1 /****************************************************************************
2 **
3 ** This file is part of the LibreCAD project, a 2D CAD program
4 **
5 ** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
6 ** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
7 **
8 **
9 ** This file may be distributed and/or modified under the terms of the
10 ** GNU General Public License version 2 as published by the Free Software
11 ** Foundation and appearing in the file gpl-2.0.txt included in the
12 ** packaging of this file.
13 **
14 ** This program is distributed in the hope that it will be useful,
15 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ** GNU General Public License for more details.
18 **
19 ** You should have received a copy of the GNU General Public License
20 ** along with this program; if not, write to the Free Software
21 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 **
23 ** This copyright notice MUST APPEAR in all copies of the script!
24 **
25 **********************************************************************/
26
27 #include <iostream>
28 #include <cmath>
29 #include <set>
30 #include <QObject>
31
32 #include "rs_dialogfactory.h"
33 #include "qg_dialogfactory.h"
34 #include "rs_entitycontainer.h"
35
36 #include "rs_debug.h"
37 #include "rs_dimension.h"
38 #include "rs_layer.h"
39 #include "rs_arc.h"
40 #include "rs_ellipse.h"
41 #include "rs_line.h"
42 #include "rs_insert.h"
43 #include "rs_spline.h"
44 #include "rs_solid.h"
45 #include "rs_information.h"
46 #include "rs_graphicview.h"
47 #include "rs_constructionline.h"
48
49 bool RS_EntityContainer::autoUpdateBorders = true;
50
51 /**
52 * Default constructor.
53 *
54 * @param owner True if we own and also delete the entities.
55 */
RS_EntityContainer(RS_EntityContainer * parent,bool owner)56 RS_EntityContainer::RS_EntityContainer(RS_EntityContainer* parent,
57 bool owner)
58 : RS_Entity(parent) {
59
60 autoDelete=owner;
61 // RS_DEBUG->print("RS_EntityContainer::RS_EntityContainer: "
62 // "owner: %d", (int)owner);
63 subContainer = nullptr;
64 //autoUpdateBorders = true;
65 entIdx = -1;
66 }
67
68
69 /**
70 * Copy constructor. Makes a deep copy of all entities.
71 */
72 /*
73 RS_EntityContainer::RS_EntityContainer(const RS_EntityContainer& ec)
74 : RS_Entity(ec) {
75
76 }
77 */
78
79
80
81 /**
82 * Destructor.
83 */
~RS_EntityContainer()84 RS_EntityContainer::~RS_EntityContainer() {
85 if (autoDelete) {
86 while (!entities.isEmpty())
87 delete entities.takeFirst();
88 } else
89 entities.clear();
90 }
91
92
93
clone() const94 RS_Entity* RS_EntityContainer::clone() const{
95 RS_DEBUG->print("RS_EntityContainer::clone: ori autoDel: %d",
96 autoDelete);
97
98 RS_EntityContainer* ec = new RS_EntityContainer(*this);
99 ec->setOwner(autoDelete);
100
101 RS_DEBUG->print("RS_EntityContainer::clone: clone autoDel: %d",
102 ec->isOwner());
103
104 ec->detach();
105 ec->initId();
106
107 return ec;
108 }
109
110
111
112 /**
113 * Detaches shallow copies and creates deep copies of all subentities.
114 * This is called after cloning entity containers.
115 */
detach()116 void RS_EntityContainer::detach() {
117 QList<RS_Entity*> tmp;
118 bool autoDel = isOwner();
119 RS_DEBUG->print("RS_EntityContainer::detach: autoDel: %d",
120 (int)autoDel);
121 setOwner(false);
122
123 // make deep copies of all entities:
124 for(auto e: entities){
125 if (!e->getFlag(RS2::FlagTemp)) {
126 tmp.append(e->clone());
127 }
128 }
129
130 // clear shared pointers:
131 entities.clear();
132 setOwner(autoDel);
133
134 // point to new deep copies:
135 for(auto e: tmp){
136 entities.append(e);
137 e->reparent(this);
138 }
139 }
140
141
142
reparent(RS_EntityContainer * parent)143 void RS_EntityContainer::reparent(RS_EntityContainer* parent) {
144 RS_Entity::reparent(parent);
145
146 // All sub-entities:
147
148 for(auto e: entities){
149 e->reparent(parent);
150 }
151 }
152
153
154
setVisible(bool v)155 void RS_EntityContainer::setVisible(bool v) {
156 // RS_DEBUG->print("RS_EntityContainer::setVisible: %d", v);
157 RS_Entity::setVisible(v);
158
159 // All sub-entities:
160
161 for(auto e: entities){
162 // RS_DEBUG->print("RS_EntityContainer::setVisible: subentity: %d", v);
163 e->setVisible(v);
164 }
165 }
166
167
168
169 /**
170 * @return Total length of all entities in this container.
171 */
getLength() const172 double RS_EntityContainer::getLength() const {
173 double ret = 0.0;
174
175 for(auto e: entities){
176 if (e->isVisible()) {
177 double l = e->getLength();
178 if (l<0.0) {
179 ret = -1.0;
180 break;
181 } else {
182 ret += l;
183 }
184 }
185 }
186
187 return ret;
188 }
189
190
191 /**
192 * Selects this entity.
193 */
setSelected(bool select)194 bool RS_EntityContainer::setSelected(bool select) {
195 // This entity's select:
196 if (RS_Entity::setSelected(select)) {
197
198 // All sub-entity's select:
199 for(auto e: entities){
200 if (e->isVisible()) {
201 e->setSelected(select);
202 }
203 }
204 return true;
205 } else {
206 return false;
207 }
208 }
209
210
211
212 /**
213 * Toggles select on this entity.
214 */
toggleSelected()215 bool RS_EntityContainer::toggleSelected() {
216 // Toggle this entity's select:
217 if (RS_Entity::toggleSelected()) {
218
219 // Toggle all sub-entity's select:
220 /*for (RS_Entity* e=firstEntity(RS2::ResolveNone);
221 e;
222 e=nextEntity(RS2::ResolveNone)) {
223 e->toggleSelected();
224 }*/
225 return true;
226 } else {
227 return false;
228 }
229 }
230
231
232
233 /**
234 * Selects all entities within the given area.
235 *
236 * @param select True to select, False to deselect the entities.
237 */
selectWindow(RS_Vector v1,RS_Vector v2,bool select,bool cross)238 void RS_EntityContainer::selectWindow(RS_Vector v1, RS_Vector v2,
239 bool select, bool cross) {
240
241 bool included;
242
243 for(auto e: entities){
244
245 included = false;
246
247 if (e->isVisible()) {
248 if (e->isInWindow(v1, v2)) {
249 //e->setSelected(select);
250 included = true;
251 } else if (cross) {
252 RS_EntityContainer l;
253 l.addRectangle(v1, v2);
254 RS_VectorSolutions sol;
255
256 if (e->isContainer()) {
257 RS_EntityContainer* ec = (RS_EntityContainer*)e;
258 for (RS_Entity* se=ec->firstEntity(RS2::ResolveAll);
259 se && included==false;
260 se=ec->nextEntity(RS2::ResolveAll)) {
261
262 if (se->rtti() == RS2::EntitySolid){
263 included = static_cast<RS_Solid*>(se)->isInCrossWindow(v1,v2);
264 } else {
265 for (auto line: l) {
266 sol = RS_Information::getIntersection(
267 se, line, true);
268 if (sol.hasValid()) {
269 included = true;
270 break;
271 }
272 }
273 }
274 }
275 } else if (e->rtti() == RS2::EntitySolid){
276 included = static_cast<RS_Solid*>(e)->isInCrossWindow(v1,v2);
277 } else {
278 for (auto line: l) {
279 sol = RS_Information::getIntersection(e, line, true);
280 if (sol.hasValid()) {
281 included = true;
282 break;
283 }
284 }
285 }
286 }
287 }
288
289 if (included) {
290 e->setSelected(select);
291 }
292 }
293 }
294
295
296
297 /**
298 * Adds a entity to this container and updates the borders of this
299 * entity-container if autoUpdateBorders is true.
300 */
addEntity(RS_Entity * entity)301 void RS_EntityContainer::addEntity(RS_Entity* entity) {
302 /*
303 if (isDocument()) {
304 RS_LayerList* lst = getDocument()->getLayerList();
305 if (lst) {
306 RS_Layer* l = lst->getActive();
307 if (l && l->isLocked()) {
308 return;
309 }
310 }
311 }
312 */
313
314 if (!entity) return;
315
316 if (entity->rtti()==RS2::EntityImage ||
317 entity->rtti()==RS2::EntityHatch) {
318 entities.prepend(entity);
319 } else {
320 entities.append(entity);
321 }
322 if (autoUpdateBorders) {
323 adjustBorders(entity);
324 }
325 }
326
327
328 /**
329 * Insert a entity at the end of entities list and updates the
330 * borders of this entity-container if autoUpdateBorders is true.
331 */
appendEntity(RS_Entity * entity)332 void RS_EntityContainer::appendEntity(RS_Entity* entity){
333 if (!entity)
334 return;
335 entities.append(entity);
336 if (autoUpdateBorders)
337 adjustBorders(entity);
338 }
339
340 /**
341 * Insert a entity at the start of entities list and updates the
342 * borders of this entity-container if autoUpdateBorders is true.
343 */
prependEntity(RS_Entity * entity)344 void RS_EntityContainer::prependEntity(RS_Entity* entity){
345 if (!entity) return;
346 entities.prepend(entity);
347 if (autoUpdateBorders)
348 adjustBorders(entity);
349 }
350
351 /**
352 * Move a entity list in this container at the given position,
353 * the borders of this entity-container if autoUpdateBorders is true.
354 */
moveEntity(int index,QList<RS_Entity * > & entList)355 void RS_EntityContainer::moveEntity(int index, QList<RS_Entity *>& entList){
356 if (entList.isEmpty()) return;
357 int ci = 0; //current index for insert without invert order
358 bool ret, into = false;
359 RS_Entity *mid = nullptr;
360 if (index < 1) {
361 ci = 0;
362 } else if (index >= entities.size() ) {
363 ci = entities.size() - entList.size();
364 } else {
365 into = true;
366 mid = entities.at(index);
367 }
368
369 for (int i = 0; i < entList.size(); ++i) {
370 RS_Entity *e = entList.at(i);
371 ret = entities.removeOne(e);
372 //if e not exist in entities list remove from entList
373 if (!ret) {
374 entList.removeAt(i);
375 }
376 }
377 if (into) {
378 ci = entities.indexOf(mid);
379 }
380
381 for(auto e: entList){
382 entities.insert(ci++, e);
383 }
384 }
385
386 /**
387 * Inserts a entity to this container at the given position and updates
388 * the borders of this entity-container if autoUpdateBorders is true.
389 */
insertEntity(int index,RS_Entity * entity)390 void RS_EntityContainer::insertEntity(int index, RS_Entity* entity) {
391 if (!entity) return;
392
393 entities.insert(index, entity);
394
395 if (autoUpdateBorders) {
396 adjustBorders(entity);
397 }
398 }
399
400
401
402 /**
403 * Replaces the entity at the given index with the given entity
404 * and updates the borders of this entity-container if autoUpdateBorders is true.
405 */
406 /*RLZ unused function
407 void RS_EntityContainer::replaceEntity(int index, RS_Entity* entity) {
408 //RLZ TODO: is needed to delete the old entity? not documented in Q3PtrList
409 // investigate in qt3support code if reactivate this function.
410 if (!entity) {
411 return;
412 }
413
414 entities.replace(index, entity);
415
416 if (autoUpdateBorders) {
417 adjustBorders(entity);
418 }
419 }RLZ*/
420
421
422
423 /**
424 * Removes an entity from this container and updates the borders of
425 * this entity-container if autoUpdateBorders is true.
426 */
removeEntity(RS_Entity * entity)427 bool RS_EntityContainer::removeEntity(RS_Entity* entity) {
428 //RLZ TODO: in Q3PtrList if 'entity' is nullptr remove the current item-> at.(entIdx)
429 // and sets 'entIdx' in next() or last() if 'entity' is the last item in the list.
430 // in LibreCAD is never called with nullptr
431 bool ret;
432 ret = entities.removeOne(entity);
433
434 if (autoDelete && ret) {
435 delete entity;
436 }
437 if (autoUpdateBorders) {
438 calculateBorders();
439 }
440 return ret;
441 }
442
443
444
445 /**
446 * Erases all entities in this container and resets the borders..
447 */
clear()448 void RS_EntityContainer::clear() {
449 if (autoDelete) {
450 while (!entities.isEmpty())
451 delete entities.takeFirst();
452 } else
453 entities.clear();
454 resetBorders();
455 }
456
count() const457 unsigned int RS_EntityContainer::count() const{
458 return entities.size();
459 }
460
461
462 /**
463 * Counts all entities (leaves of the tree).
464 */
countDeep() const465 unsigned int RS_EntityContainer::countDeep() const{
466 unsigned int c=0;
467 for(auto t: *this){
468 c += t->countDeep();
469 }
470 return c;
471 }
472
473
474
475 /**
476 * Counts the selected entities in this container.
477 */
countSelected(bool deep,std::initializer_list<RS2::EntityType> const & types)478 unsigned int RS_EntityContainer::countSelected(bool deep, std::initializer_list<RS2::EntityType> const& types) {
479 unsigned int c=0;
480 std::set<RS2::EntityType> type = types;
481
482 for (RS_Entity* t: entities){
483
484 if (t->isSelected())
485 if (!types.size() || type.count(t->rtti()))
486 c++;
487
488 if (t->isContainer())
489 c += static_cast<RS_EntityContainer*>(t)->countSelected(deep);
490 }
491
492 return c;
493 }
494
495 /**
496 * Counts the selected entities in this container.
497 */
totalSelectedLength()498 double RS_EntityContainer::totalSelectedLength() {
499 double ret(0.0);
500 for (RS_Entity* e: entities){
501
502 if (e->isVisible() && e->isSelected()) {
503 double l = e->getLength();
504 if (l>=0.) {
505 ret += l;
506 }
507 }
508 }
509 return ret;
510 }
511
512
513 /**
514 * Adjusts the borders of this graphic (max/min values)
515 */
adjustBorders(RS_Entity * entity)516 void RS_EntityContainer::adjustBorders(RS_Entity* entity) {
517 //RS_DEBUG->print("RS_EntityContainer::adjustBorders");
518 //resetBorders();
519
520 if (entity) {
521 // make sure a container is not empty (otherwise the border
522 // would get extended to 0/0):
523 if (!entity->isContainer() || entity->count()>0) {
524 minV = RS_Vector::minimum(entity->getMin(),minV);
525 maxV = RS_Vector::maximum(entity->getMax(),maxV);
526 }
527
528 // Notify parents. The border for the parent might
529 // also change TODO: Check for efficiency
530 //if(parent) {
531 //parent->adjustBorders(this);
532 //}
533 }
534 }
535
536
537 /**
538 * Recalculates the borders of this entity container.
539 */
calculateBorders()540 void RS_EntityContainer::calculateBorders() {
541 RS_DEBUG->print("RS_EntityContainer::calculateBorders");
542
543 resetBorders();
544 for (RS_Entity* e: entities){
545
546 RS_Layer* layer = e->getLayer();
547
548 // RS_DEBUG->print("RS_EntityContainer::calculateBorders: "
549 // "isVisible: %d", (int)e->isVisible());
550
551 if (e->isVisible() && !(layer && layer->isFrozen())) {
552 e->calculateBorders();
553 adjustBorders(e);
554 }
555 }
556
557 RS_DEBUG->print("RS_EntityContainer::calculateBorders: size 1: %f,%f",
558 getSize().x, getSize().y);
559
560 // needed for correcting corrupt data (PLANS.dxf)
561 if (minV.x>maxV.x || minV.x>RS_MAXDOUBLE || maxV.x>RS_MAXDOUBLE
562 || minV.x<RS_MINDOUBLE || maxV.x<RS_MINDOUBLE) {
563
564 minV.x = 0.0;
565 maxV.x = 0.0;
566 }
567 if (minV.y>maxV.y || minV.y>RS_MAXDOUBLE || maxV.y>RS_MAXDOUBLE
568 || minV.y<RS_MINDOUBLE || maxV.y<RS_MINDOUBLE) {
569
570 minV.y = 0.0;
571 maxV.y = 0.0;
572 }
573
574 RS_DEBUG->print("RS_EntityContainer::calculateBorders: size: %f,%f",
575 getSize().x, getSize().y);
576
577 //RS_DEBUG->print(" borders: %f/%f %f/%f", minV.x, minV.y, maxV.x, maxV.y);
578
579 //printf("borders: %lf/%lf %lf/%lf\n", minV.x, minV.y, maxV.x, maxV.y);
580 //RS_Entity::calculateBorders();
581 }
582
583 //namespace {
584 //bool isBoundingBoxValid(RS_Entity* e) {
585 // if (!(e->getMin() && e->getMax())) return false;
586 // if (!(e->getMin().x <= e->getMax().x)) return false;
587 // if (!(e->getMin().y <= e->getMax().y)) return false;
588 // if ((e->getMin() - e->getMax()).magnitude() > RS_MAXDOUBLE) return false;
589 // return true;
590 //}
591 //}
592
593 /**
594 * Recalculates the borders of this entity container including
595 * invisible entities.
596 */
forcedCalculateBorders()597 void RS_EntityContainer::forcedCalculateBorders() {
598 //RS_DEBUG->print("RS_EntityContainer::calculateBorders");
599
600 resetBorders();
601 for (RS_Entity* e: entities){
602
603 //RS_Layer* layer = e->getLayer();
604
605 if (e->isContainer()) {
606 ((RS_EntityContainer*)e)->forcedCalculateBorders();
607 } else {
608 e->calculateBorders();
609 }
610 adjustBorders(e);
611 }
612
613 // needed for correcting corrupt data (PLANS.dxf)
614 if (minV.x>maxV.x || minV.x>RS_MAXDOUBLE || maxV.x>RS_MAXDOUBLE
615 || minV.x<RS_MINDOUBLE || maxV.x<RS_MINDOUBLE) {
616
617 minV.x = 0.0;
618 maxV.x = 0.0;
619 }
620 if (minV.y>maxV.y || minV.y>RS_MAXDOUBLE || maxV.y>RS_MAXDOUBLE
621 || minV.y<RS_MINDOUBLE || maxV.y<RS_MINDOUBLE) {
622
623 minV.y = 0.0;
624 maxV.y = 0.0;
625 }
626
627 //RS_DEBUG->print(" borders: %f/%f %f/%f", minV.x, minV.y, maxV.x, maxV.y);
628
629 //printf("borders: %lf/%lf %lf/%lf\n", minV.x, minV.y, maxV.x, maxV.y);
630 //RS_Entity::calculateBorders();
631 }
632
633 /**
634 * Updates all Dimension entities in this container and / or
635 * reposition their labels.
636 *
637 * @param autoText Automatically reposition the text label bool autoText=true
638 */
updateDimensions(bool autoText)639 void RS_EntityContainer::updateDimensions(bool autoText) {
640
641 RS_DEBUG->print("RS_EntityContainer::updateDimensions()");
642
643 //for (RS_Entity* e=firstEntity(RS2::ResolveNone);
644 // e;
645 // e=nextEntity(RS2::ResolveNone)) {
646
647 for (RS_Entity* e: entities){
648 if (RS_Information::isDimension(e->rtti())) {
649 // update and reposition label:
650 ((RS_Dimension*)e)->updateDim(autoText);
651 } else if(e->rtti()==RS2::EntityDimLeader)
652 e->update();
653 else if (e->isContainer()) {
654 ((RS_EntityContainer*)e)->updateDimensions(autoText);
655 }
656 }
657
658 RS_DEBUG->print("RS_EntityContainer::updateDimensions() OK");
659 }
660
661
662
663 /**
664 * Updates all Insert entities in this container.
665 */
updateInserts()666 void RS_EntityContainer::updateInserts() {
667
668 RS_DEBUG->print("RS_EntityContainer::updateInserts() ID/type: %d/%d", getId(), rtti());
669
670 for (RS_Entity* e: entities){
671 //// Only update our own inserts and not inserts of inserts
672 if (e->rtti()==RS2::EntityInsert /*&& e->getParent()==this*/) {
673 ((RS_Insert*)e)->update();
674 RS_DEBUG->print("RS_EntityContainer::updateInserts: updated ID/type: %d/%d", e->getId(), e->rtti());
675 } else if (e->isContainer()) {
676 if (e->rtti()==RS2::EntityHatch) {
677 RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_EntityContainer::updateInserts: skip hatch ID/type: %d/%d", e->getId(), e->rtti());
678 } else {
679 RS_DEBUG->print("RS_EntityContainer::updateInserts: update container ID/type: %d/%d", e->getId(), e->rtti());
680 ((RS_EntityContainer*)e)->updateInserts();
681 }
682 } else {
683 RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_EntityContainer::updateInserts: skip entity ID/type: %d/%d", e->getId(), e->rtti());
684 }
685 }
686 RS_DEBUG->print("RS_EntityContainer::updateInserts() ID/type: %d/%d OK", getId(), rtti());
687 }
688
689
690
691 /**
692 * Renames all inserts with name 'oldName' to 'newName'. This is
693 * called after a block was rename to update the inserts.
694 */
renameInserts(const QString & oldName,const QString & newName)695 void RS_EntityContainer::renameInserts(const QString& oldName,
696 const QString& newName) {
697 RS_DEBUG->print("RS_EntityContainer::renameInserts()");
698
699 //for (RS_Entity* e=firstEntity(RS2::ResolveNone);
700 // e;
701 // e=nextEntity(RS2::ResolveNone)) {
702
703 for (RS_Entity* e: entities){
704 if (e->rtti()==RS2::EntityInsert) {
705 RS_Insert* i = ((RS_Insert*)e);
706 if (i->getName()==oldName) {
707 i->setName(newName);
708 }
709 } else if (e->isContainer()) {
710 ((RS_EntityContainer*)e)->renameInserts(oldName, newName);
711 }
712 }
713
714 RS_DEBUG->print("RS_EntityContainer::renameInserts() OK");
715
716 }
717
718 /**
719 * Updates all Spline entities in this container.
720 */
updateSplines()721 void RS_EntityContainer::updateSplines() {
722
723 RS_DEBUG->print("RS_EntityContainer::updateSplines()");
724
725 for (RS_Entity* e: entities){
726 //// Only update our own inserts and not inserts of inserts
727 if (e->rtti()==RS2::EntitySpline /*&& e->getParent()==this*/) {
728 ((RS_Spline*)e)->update();
729 } else if (e->isContainer() && e->rtti()!=RS2::EntityHatch) {
730 ((RS_EntityContainer*)e)->updateSplines();
731 }
732 }
733
734 RS_DEBUG->print("RS_EntityContainer::updateSplines() OK");
735 }
736
737
738 /**
739 * Updates the sub entities of this container.
740 */
update()741 void RS_EntityContainer::update() {
742 for (RS_Entity* e: entities){
743 e->update();
744 }
745 }
746
addRectangle(RS_Vector const & v0,RS_Vector const & v1)747 void RS_EntityContainer::addRectangle(RS_Vector const& v0, RS_Vector const& v1)
748 {
749 addEntity(new RS_Line{this, v0, {v1.x, v0.y}});
750 addEntity(new RS_Line{this, {v1.x, v0.y}, v1});
751 addEntity(new RS_Line{this, v1, {v0.x, v1.y}});
752 addEntity(new RS_Line{this, {v0.x, v1.y}, v0});
753 }
754
755 /**
756 * Returns the first entity or nullptr if this graphic is empty.
757 * @param level
758 */
firstEntity(RS2::ResolveLevel level)759 RS_Entity* RS_EntityContainer::firstEntity(RS2::ResolveLevel level) {
760 RS_Entity* e = nullptr;
761 entIdx = -1;
762 switch (level) {
763 case RS2::ResolveNone:
764 if (!entities.isEmpty()) {
765 entIdx = 0;
766 return entities.first();
767 }
768 break;
769
770 case RS2::ResolveAllButInserts: {
771 subContainer=nullptr;
772 if (!entities.isEmpty()) {
773 entIdx = 0;
774 e = entities.first();
775 }
776 if (e && e->isContainer() && e->rtti()!=RS2::EntityInsert) {
777 subContainer = (RS_EntityContainer*)e;
778 e = ((RS_EntityContainer*)e)->firstEntity(level);
779 // empty container:
780 if (!e) {
781 subContainer = nullptr;
782 e = nextEntity(level);
783 }
784 }
785 return e;
786 }
787 break;
788
789 case RS2::ResolveAllButTextImage:
790 case RS2::ResolveAllButTexts: {
791 subContainer=nullptr;
792 if (!entities.isEmpty()) {
793 entIdx = 0;
794 e = entities.first();
795 }
796 if (e && e->isContainer() && e->rtti()!=RS2::EntityText && e->rtti()!=RS2::EntityMText) {
797 subContainer = (RS_EntityContainer*)e;
798 e = ((RS_EntityContainer*)e)->firstEntity(level);
799 // empty container:
800 if (!e) {
801 subContainer = nullptr;
802 e = nextEntity(level);
803 }
804 }
805 return e;
806 }
807 break;
808
809 case RS2::ResolveAll: {
810 subContainer=nullptr;
811 if (!entities.isEmpty()) {
812 entIdx = 0;
813 e = entities.first();
814 }
815 if (e && e->isContainer()) {
816 subContainer = (RS_EntityContainer*)e;
817 e = ((RS_EntityContainer*)e)->firstEntity(level);
818 // empty container:
819 if (!e) {
820 subContainer = nullptr;
821 e = nextEntity(level);
822 }
823 }
824 return e;
825 }
826 break;
827 }
828
829 return nullptr;
830 }
831
832
833
834 /**
835 * Returns the last entity or \p nullptr if this graphic is empty.
836 *
837 * @param level \li \p 0 Groups are not resolved
838 * \li \p 1 (default) only Groups are resolved
839 * \li \p 2 all Entity Containers are resolved
840 */
lastEntity(RS2::ResolveLevel level)841 RS_Entity* RS_EntityContainer::lastEntity(RS2::ResolveLevel level) {
842 RS_Entity* e = nullptr;
843 if(!entities.size()) return nullptr;
844 entIdx = entities.size()-1;
845 switch (level) {
846 case RS2::ResolveNone:
847 if (!entities.isEmpty())
848 return entities.last();
849 break;
850
851 case RS2::ResolveAllButInserts: {
852 if (!entities.isEmpty())
853 e = entities.last();
854 subContainer = nullptr;
855 if (e && e->isContainer() && e->rtti()!=RS2::EntityInsert) {
856 subContainer = (RS_EntityContainer*)e;
857 e = ((RS_EntityContainer*)e)->lastEntity(level);
858 }
859 return e;
860 }
861 break;
862 case RS2::ResolveAllButTextImage:
863 case RS2::ResolveAllButTexts: {
864 if (!entities.isEmpty())
865 e = entities.last();
866 subContainer = nullptr;
867 if (e && e->isContainer() && e->rtti()!=RS2::EntityText && e->rtti()!=RS2::EntityMText) {
868 subContainer = (RS_EntityContainer*)e;
869 e = ((RS_EntityContainer*)e)->lastEntity(level);
870 }
871 return e;
872 }
873 break;
874
875 case RS2::ResolveAll: {
876 if (!entities.isEmpty())
877 e = entities.last();
878 subContainer = nullptr;
879 if (e && e->isContainer()) {
880 subContainer = (RS_EntityContainer*)e;
881 e = ((RS_EntityContainer*)e)->lastEntity(level);
882 }
883 return e;
884 }
885 break;
886 }
887
888 return nullptr;
889 }
890
891
892 /**
893 * Returns the next entity or container or \p nullptr if the last entity
894 * returned by \p next() was the last entity in the container.
895 */
nextEntity(RS2::ResolveLevel level)896 RS_Entity* RS_EntityContainer::nextEntity(RS2::ResolveLevel level) {
897
898 //set entIdx pointing in next entity and check if is out of range
899 ++entIdx;
900 switch (level) {
901 case RS2::ResolveNone:
902 if ( entIdx < entities.size() )
903 return entities.at(entIdx);
904 break;
905
906 case RS2::ResolveAllButInserts: {
907 RS_Entity* e=nullptr;
908 if (subContainer) {
909 e = subContainer->nextEntity(level);
910 if (e) {
911 --entIdx; //return a sub-entity, index not advanced
912 return e;
913 } else {
914 if ( entIdx < entities.size() )
915 e = entities.at(entIdx);
916 }
917 } else {
918 if ( entIdx < entities.size() )
919 e = entities.at(entIdx);
920 }
921 if (e && e->isContainer() && e->rtti()!=RS2::EntityInsert) {
922 subContainer = (RS_EntityContainer*)e;
923 e = ((RS_EntityContainer*)e)->firstEntity(level);
924 // empty container:
925 if (!e) {
926 subContainer = nullptr;
927 e = nextEntity(level);
928 }
929 }
930 return e;
931 }
932 break;
933
934 case RS2::ResolveAllButTextImage:
935 case RS2::ResolveAllButTexts: {
936 RS_Entity* e=nullptr;
937 if (subContainer) {
938 e = subContainer->nextEntity(level);
939 if (e) {
940 --entIdx; //return a sub-entity, index not advanced
941 return e;
942 } else {
943 if ( entIdx < entities.size() )
944 e = entities.at(entIdx);
945 }
946 } else {
947 if ( entIdx < entities.size() )
948 e = entities.at(entIdx);
949 }
950 if (e && e->isContainer() && e->rtti()!=RS2::EntityText && e->rtti()!=RS2::EntityMText ) {
951 subContainer = (RS_EntityContainer*)e;
952 e = ((RS_EntityContainer*)e)->firstEntity(level);
953 // empty container:
954 if (!e) {
955 subContainer = nullptr;
956 e = nextEntity(level);
957 }
958 }
959 return e;
960 }
961 break;
962
963 case RS2::ResolveAll: {
964 RS_Entity* e=nullptr;
965 if (subContainer) {
966 e = subContainer->nextEntity(level);
967 if (e) {
968 --entIdx; //return a sub-entity, index not advanced
969 return e;
970 } else {
971 if ( entIdx < entities.size() )
972 e = entities.at(entIdx);
973 }
974 } else {
975 if ( entIdx < entities.size() )
976 e = entities.at(entIdx);
977 }
978 if (e && e->isContainer()) {
979 subContainer = (RS_EntityContainer*)e;
980 e = ((RS_EntityContainer*)e)->firstEntity(level);
981 // empty container:
982 if (!e) {
983 subContainer = nullptr;
984 e = nextEntity(level);
985 }
986 }
987 return e;
988 }
989 break;
990 }
991 return nullptr;
992 }
993
994
995
996 /**
997 * Returns the prev entity or container or \p nullptr if the last entity
998 * returned by \p prev() was the first entity in the container.
999 */
prevEntity(RS2::ResolveLevel level)1000 RS_Entity* RS_EntityContainer::prevEntity(RS2::ResolveLevel level) {
1001 //set entIdx pointing in prev entity and check if is out of range
1002 --entIdx;
1003 switch (level) {
1004
1005 case RS2::ResolveNone:
1006 if (entIdx >= 0)
1007 return entities.at(entIdx);
1008 break;
1009
1010 case RS2::ResolveAllButInserts: {
1011 RS_Entity* e=nullptr;
1012 if (subContainer) {
1013 e = subContainer->prevEntity(level);
1014 if (e) {
1015 return e;
1016 } else {
1017 if (entIdx >= 0)
1018 e = entities.at(entIdx);
1019 }
1020 } else {
1021 if (entIdx >= 0)
1022 e = entities.at(entIdx);
1023 }
1024 if (e && e->isContainer() && e->rtti()!=RS2::EntityInsert) {
1025 subContainer = (RS_EntityContainer*)e;
1026 e = ((RS_EntityContainer*)e)->lastEntity(level);
1027 // empty container:
1028 if (!e) {
1029 subContainer = nullptr;
1030 e = prevEntity(level);
1031 }
1032 }
1033 return e;
1034 }
1035
1036 case RS2::ResolveAllButTextImage:
1037 case RS2::ResolveAllButTexts: {
1038 RS_Entity* e=nullptr;
1039 if (subContainer) {
1040 e = subContainer->prevEntity(level);
1041 if (e) {
1042 return e;
1043 } else {
1044 if (entIdx >= 0)
1045 e = entities.at(entIdx);
1046 }
1047 } else {
1048 if (entIdx >= 0)
1049 e = entities.at(entIdx);
1050 }
1051 if (e && e->isContainer() && e->rtti()!=RS2::EntityText && e->rtti()!=RS2::EntityMText) {
1052 subContainer = (RS_EntityContainer*)e;
1053 e = ((RS_EntityContainer*)e)->lastEntity(level);
1054 // empty container:
1055 if (!e) {
1056 subContainer = nullptr;
1057 e = prevEntity(level);
1058 }
1059 }
1060 return e;
1061 }
1062
1063 case RS2::ResolveAll: {
1064 RS_Entity* e=nullptr;
1065 if (subContainer) {
1066 e = subContainer->prevEntity(level);
1067 if (e) {
1068 ++entIdx; //return a sub-entity, index not advanced
1069 return e;
1070 } else {
1071 if (entIdx >= 0)
1072 e = entities.at(entIdx);
1073 }
1074 } else {
1075 if (entIdx >= 0)
1076 e = entities.at(entIdx);
1077 }
1078 if (e && e->isContainer()) {
1079 subContainer = (RS_EntityContainer*)e;
1080 e = ((RS_EntityContainer*)e)->lastEntity(level);
1081 // empty container:
1082 if (!e) {
1083 subContainer = nullptr;
1084 e = prevEntity(level);
1085 }
1086 }
1087 return e;
1088 }
1089 }
1090 return nullptr;
1091 }
1092
1093
1094
1095 /**
1096 * @return Entity at the given index or nullptr if the index is out of range.
1097 */
entityAt(int index)1098 RS_Entity* RS_EntityContainer::entityAt(int index) {
1099 if (entities.size() > index && index >= 0)
1100 return entities.at(index);
1101 else
1102 return nullptr;
1103 }
1104
setEntityAt(int index,RS_Entity * en)1105 void RS_EntityContainer::setEntityAt(int index,RS_Entity* en){
1106 if(autoDelete && entities.at(index)) {
1107 delete entities.at(index);
1108 }
1109 entities[index] = en;
1110 }
1111
1112 /**
1113 * @return Current index.
1114 */
1115 /*RLZ unused
1116 int RS_EntityContainer::entityAt() {
1117 return entIdx;
1118 } RLZ unused*/
1119
1120
1121 /**
1122 * Finds the given entity and makes it the current entity if found.
1123 */
findEntity(RS_Entity const * const entity)1124 int RS_EntityContainer::findEntity(RS_Entity const* const entity) {
1125 entIdx = entities.indexOf(const_cast<RS_Entity*>(entity));
1126 return entIdx;
1127 }
1128
1129 /**
1130 * @return The point which is closest to 'coord'
1131 * (one of the vertices)
1132 */
getNearestEndpoint(const RS_Vector & coord,double * dist) const1133 RS_Vector RS_EntityContainer::getNearestEndpoint(const RS_Vector& coord,
1134 double* dist )const {
1135
1136 double minDist = RS_MAXDOUBLE; // minimum measured distance
1137 double curDist; // currently measured distance
1138 RS_Vector closestPoint(false); // closest found endpoint
1139 RS_Vector point; // endpoint found
1140
1141 for (RS_Entity* en: entities){
1142
1143 if (en->isVisible()
1144 && !en->getParent()->ignoredOnModification()
1145 ){//no end point for Insert, text, Dim
1146 point = en->getNearestEndpoint(coord, &curDist);
1147 if (point.valid && curDist<minDist) {
1148 closestPoint = point;
1149 minDist = curDist;
1150 if (dist) {
1151 *dist = minDist;
1152 }
1153 }
1154 }
1155 }
1156
1157 return closestPoint;
1158 }
1159
1160
1161
1162 /**
1163 * @return The point which is closest to 'coord'
1164 * (one of the vertices)
1165 */
getNearestEndpoint(const RS_Vector & coord,double * dist,RS_Entity ** pEntity) const1166 RS_Vector RS_EntityContainer::getNearestEndpoint(const RS_Vector& coord,
1167 double* dist, RS_Entity** pEntity)const {
1168
1169 double minDist = RS_MAXDOUBLE; // minimum measured distance
1170 double curDist; // currently measured distance
1171 RS_Vector closestPoint(false); // closest found endpoint
1172 RS_Vector point; // endpoint found
1173
1174 //QListIterator<RS_Entity> it = createIterator();
1175 //RS_Entity* en;
1176 //while ( (en = it.current()) ) {
1177 // ++it;
1178
1179 unsigned i0=0;
1180 for(auto en: entities){
1181 if (!en->getParent()->ignoredOnModification() ){//no end point for Insert, text, Dim
1182 // std::cout<<"find nearest for entity "<<i0<<std::endl;
1183 point = en->getNearestEndpoint(coord, &curDist);
1184 if (point.valid && curDist<minDist) {
1185 closestPoint = point;
1186 minDist = curDist;
1187 if (dist) {
1188 *dist = minDist;
1189 }
1190 if(pEntity){
1191 *pEntity=en;
1192 }
1193 }
1194 }
1195 i0++;
1196 }
1197
1198 // std::cout<<__FILE__<<" : "<<__func__<<" : line "<<__LINE__<<std::endl;
1199 // std::cout<<"count()="<<const_cast<RS_EntityContainer*>(this)->count()<<"\tminDist= "<<minDist<<"\tclosestPoint="<<closestPoint;
1200 // if(pEntity ) std::cout<<"\t*pEntity="<<*pEntity;
1201 // std::cout<<std::endl;
1202 return closestPoint;
1203 }
1204
1205
1206
getNearestPointOnEntity(const RS_Vector & coord,bool onEntity,double * dist,RS_Entity ** entity) const1207 RS_Vector RS_EntityContainer::getNearestPointOnEntity(const RS_Vector& coord,
1208 bool onEntity, double* dist, RS_Entity** entity)const {
1209
1210 RS_Vector point(false);
1211
1212 RS_Entity* en = getNearestEntity(coord, dist, RS2::ResolveNone);
1213
1214 if (en && en->isVisible()
1215 && !en->getParent()->ignoredSnap()
1216 ){
1217 point = en->getNearestPointOnEntity(coord, onEntity, dist, entity);
1218 }
1219
1220 return point;
1221 }
1222
1223
1224
getNearestCenter(const RS_Vector & coord,double * dist) const1225 RS_Vector RS_EntityContainer::getNearestCenter(const RS_Vector& coord,
1226 double* dist) const{
1227 double minDist = RS_MAXDOUBLE; // minimum measured distance
1228 double curDist = RS_MAXDOUBLE; // currently measured distance
1229 RS_Vector closestPoint(false); // closest found endpoint
1230 RS_Vector point; // endpoint found
1231
1232 for(auto en: entities){
1233
1234 if (en->isVisible()
1235 && !en->getParent()->ignoredSnap()
1236 ){//no center point for spline, text, Dim
1237 point = en->getNearestCenter(coord, &curDist);
1238 if (point.valid && curDist<minDist) {
1239 closestPoint = point;
1240 minDist = curDist;
1241 }
1242 }
1243 }
1244 if (dist) {
1245 *dist = minDist;
1246 }
1247
1248 return closestPoint;
1249 }
1250
1251 /** @return the nearest of equidistant middle points of the line. */
1252
getNearestMiddle(const RS_Vector & coord,double * dist,int middlePoints) const1253 RS_Vector RS_EntityContainer::getNearestMiddle(const RS_Vector& coord,
1254 double* dist,
1255 int middlePoints
1256 ) const{
1257 double minDist = RS_MAXDOUBLE; // minimum measured distance
1258 double curDist = RS_MAXDOUBLE; // currently measured distance
1259 RS_Vector closestPoint(false); // closest found endpoint
1260 RS_Vector point; // endpoint found
1261
1262 for(auto en: entities){
1263
1264 if (en->isVisible()
1265 && !en->getParent()->ignoredSnap()
1266 ){//no midle point for spline, text, Dim
1267 point = en->getNearestMiddle(coord, &curDist, middlePoints);
1268 if (point.valid && curDist<minDist) {
1269 closestPoint = point;
1270 minDist = curDist;
1271 }
1272 }
1273 }
1274 if (dist) {
1275 *dist = minDist;
1276 }
1277
1278 return closestPoint;
1279 }
1280
1281
1282
getNearestDist(double distance,const RS_Vector & coord,double * dist) const1283 RS_Vector RS_EntityContainer::getNearestDist(double distance,
1284 const RS_Vector& coord,
1285 double* dist) const{
1286
1287 RS_Vector point(false);
1288 RS_Entity* closestEntity;
1289
1290 closestEntity = getNearestEntity(coord, nullptr, RS2::ResolveNone);
1291
1292 if (closestEntity) {
1293 point = closestEntity->getNearestDist(distance, coord, dist);
1294 }
1295
1296 return point;
1297 }
1298
1299
1300
1301 /**
1302 * @return The intersection which is closest to 'coord'
1303 */
getNearestIntersection(const RS_Vector & coord,double * dist)1304 RS_Vector RS_EntityContainer::getNearestIntersection(const RS_Vector& coord,
1305 double* dist) {
1306
1307 double minDist = RS_MAXDOUBLE; // minimum measured distance
1308 double curDist = RS_MAXDOUBLE; // currently measured distance
1309 RS_Vector closestPoint(false); // closest found endpoint
1310 RS_Vector point; // endpoint found
1311 RS_VectorSolutions sol;
1312 RS_Entity* closestEntity;
1313
1314 closestEntity = getNearestEntity(coord, nullptr, RS2::ResolveAllButTextImage);
1315
1316 if (closestEntity) {
1317 for (RS_Entity* en = firstEntity(RS2::ResolveAllButTextImage);
1318 en;
1319 en = nextEntity(RS2::ResolveAllButTextImage)) {
1320 if (
1321 !en->isVisible()
1322 || en->getParent()->ignoredSnap()
1323 ){
1324 continue;
1325 }
1326
1327 sol = RS_Information::getIntersection(closestEntity,
1328 en,
1329 true);
1330
1331 point=sol.getClosest(coord,&curDist,nullptr);
1332 if(sol.getNumber()>0 && curDist<minDist){
1333 closestPoint=point;
1334 minDist=curDist;
1335 }
1336
1337 }
1338 }
1339 if(dist && closestPoint.valid) {
1340 *dist = minDist;
1341 }
1342
1343 return closestPoint;
1344 }
1345
getNearestVirtualIntersection(const RS_Vector & coord,const double & angle,double * dist)1346 RS_Vector RS_EntityContainer::getNearestVirtualIntersection(const RS_Vector& coord,
1347 const double& angle,
1348 double* dist)
1349 {
1350
1351 RS_Vector point; // endpoint found
1352 RS_VectorSolutions sol;
1353 RS_Entity* closestEntity;
1354 RS_Vector second_coord;
1355
1356 second_coord.set(angle);
1357 closestEntity = getNearestEntity(coord, nullptr, RS2::ResolveAllButTextImage);
1358
1359 if (closestEntity)
1360 {
1361 RS_ConstructionLineData data(coord,coord + second_coord);
1362 auto line = new RS_ConstructionLine(this,data);
1363
1364 sol = RS_Information::getIntersection(closestEntity,line,true);
1365 if (sol.getVector().empty())
1366 {
1367 return coord;
1368 }
1369 else
1370 {
1371 point=sol.getClosest(coord,dist,nullptr);
1372 return point;
1373 }
1374 }
1375 else
1376 {
1377 return coord;
1378 }
1379
1380
1381 }
1382
1383
getNearestRef(const RS_Vector & coord,double * dist) const1384 RS_Vector RS_EntityContainer::getNearestRef(const RS_Vector& coord,
1385 double* dist) const{
1386
1387 double minDist = RS_MAXDOUBLE; // minimum measured distance
1388 double curDist; // currently measured distance
1389 RS_Vector closestPoint(false); // closest found endpoint
1390 RS_Vector point; // endpoint found
1391
1392 for(auto en: entities){
1393
1394 if (en->isVisible()) {
1395 point = en->getNearestRef(coord, &curDist);
1396 if (point.valid && curDist<minDist) {
1397 closestPoint = point;
1398 minDist = curDist;
1399 if (dist) {
1400 *dist = minDist;
1401 }
1402 }
1403 }
1404 }
1405
1406 return closestPoint;
1407 }
1408
1409
getNearestSelectedRef(const RS_Vector & coord,double * dist) const1410 RS_Vector RS_EntityContainer::getNearestSelectedRef(const RS_Vector& coord,
1411 double* dist) const{
1412
1413 double minDist = RS_MAXDOUBLE; // minimum measured distance
1414 double curDist; // currently measured distance
1415 RS_Vector closestPoint(false); // closest found endpoint
1416 RS_Vector point; // endpoint found
1417
1418 for(auto en: entities){
1419
1420 if (en->isVisible() && en->isSelected() && !en->isParentSelected()) {
1421 point = en->getNearestSelectedRef(coord, &curDist);
1422 if (point.valid && curDist<minDist) {
1423 closestPoint = point;
1424 minDist = curDist;
1425 if (dist) {
1426 *dist = minDist;
1427 }
1428 }
1429 }
1430 }
1431
1432 return closestPoint;
1433 }
1434
1435
getDistanceToPoint(const RS_Vector & coord,RS_Entity ** entity,RS2::ResolveLevel level,double solidDist) const1436 double RS_EntityContainer::getDistanceToPoint(const RS_Vector& coord,
1437 RS_Entity** entity,
1438 RS2::ResolveLevel level,
1439 double solidDist) const{
1440
1441 RS_DEBUG->print("RS_EntityContainer::getDistanceToPoint");
1442
1443
1444 double minDist = RS_MAXDOUBLE; // minimum measured distance
1445 double curDist; // currently measured distance
1446 RS_Entity* closestEntity = nullptr; // closest entity found
1447 RS_Entity* subEntity = nullptr;
1448
1449 for(auto e: entities){
1450
1451 if (e->isVisible()) {
1452 RS_DEBUG->print("entity: getDistanceToPoint");
1453 RS_DEBUG->print("entity: %d", e->rtti());
1454 // bug#426, need to ignore Images to find nearest intersections
1455 if(level==RS2::ResolveAllButTextImage && e->rtti()==RS2::EntityImage) continue;
1456 curDist = e->getDistanceToPoint(coord, &subEntity, level, solidDist);
1457
1458 RS_DEBUG->print("entity: getDistanceToPoint: OK");
1459
1460 /*
1461 * By using '<=', we will prefer the *last* item in the container if there are multiple
1462 * entities that are *exactly* the same distance away, which should tend to be the one
1463 * drawn most recently, and the one most likely to be visible (as it is also the order
1464 * that the software draws the entities). This makes a difference when one entity is
1465 * drawn directly over top of another, and it's reasonable to assume that humans will
1466 * tend to want to reference entities that they see or have recently drawn as opposed
1467 * to deeper more forgotten and invisible ones...
1468 */
1469 if (curDist<=minDist)
1470 {
1471 switch(level){
1472 case RS2::ResolveAll:
1473 case RS2::ResolveAllButTextImage:
1474 closestEntity = subEntity;
1475 break;
1476 default:
1477 closestEntity = e;
1478 }
1479 minDist = curDist;
1480 }
1481 }
1482 }
1483
1484 if (entity) {
1485 *entity = closestEntity;
1486 }
1487 RS_DEBUG->print("RS_EntityContainer::getDistanceToPoint: OK");
1488
1489 return minDist;
1490 }
1491
1492
1493
getNearestEntity(const RS_Vector & coord,double * dist,RS2::ResolveLevel level) const1494 RS_Entity* RS_EntityContainer::getNearestEntity(const RS_Vector& coord,
1495 double* dist,
1496 RS2::ResolveLevel level) const{
1497
1498 RS_DEBUG->print("RS_EntityContainer::getNearestEntity");
1499
1500 RS_Entity* e = nullptr;
1501
1502 // distance for points inside solids:
1503 double solidDist = RS_MAXDOUBLE;
1504 if (dist) {
1505 solidDist = *dist;
1506 }
1507
1508 double d = getDistanceToPoint(coord, &e, level, solidDist);
1509
1510 if (e && e->isVisible()==false) {
1511 e = nullptr;
1512 }
1513
1514 // if d is negative, use the default distance (used for points inside solids)
1515 if (dist) {
1516 *dist = d;
1517 }
1518 RS_DEBUG->print("RS_EntityContainer::getNearestEntity: OK");
1519
1520 return e;
1521 }
1522
1523
1524
1525 /**
1526 * Rearranges the atomic entities in this container in a way that connected
1527 * entities are stored in the right order and direction.
1528 * Non-recoursive. Only affects atomic entities in this container.
1529 *
1530 * @retval true all contours were closed
1531 * @retval false at least one contour is not closed
1532
1533 * to do: find closed contour by flood-fill
1534 */
optimizeContours()1535 bool RS_EntityContainer::optimizeContours() {
1536 // std::cout<<"RS_EntityContainer::optimizeContours: begin"<<std::endl;
1537
1538 // DEBUG_HEADER
1539 // std::cout<<"loop with count()="<<count()<<std::endl;
1540 RS_DEBUG->print("RS_EntityContainer::optimizeContours");
1541
1542 RS_EntityContainer tmp;
1543 tmp.setAutoUpdateBorders(false);
1544 bool closed=true;
1545
1546 /** accept all full circles **/
1547 QList<RS_Entity*> enList;
1548 for(auto e1: entities){
1549 if (!e1->isEdge() || e1->isContainer() ) {
1550 enList<<e1;
1551 continue;
1552 }
1553
1554 //detect circles and whole ellipses
1555 switch(e1->rtti()){
1556 case RS2::EntityEllipse:
1557 if(static_cast<RS_Ellipse*>(e1)->isEllipticArc())
1558 continue;
1559 // fall-through
1560 case RS2::EntityCircle:
1561 //directly detect circles, bug#3443277
1562 tmp.addEntity(e1->clone());
1563 enList<<e1;
1564 // fall-through
1565 default:
1566 continue;
1567 }
1568
1569 }
1570 // std::cout<<"RS_EntityContainer::optimizeContours: 1"<<std::endl;
1571
1572 /** remove unsupported entities */
1573 for(RS_Entity* it: enList)
1574 removeEntity(it);
1575
1576 /** check and form a closed contour **/
1577 // std::cout<<"RS_EntityContainer::optimizeContours: 2"<<std::endl;
1578 /** the first entity **/
1579 RS_Entity* current(nullptr);
1580 if(count()>0) {
1581 current=entityAt(0)->clone();
1582 tmp.addEntity(current);
1583 removeEntity(entityAt(0));
1584 }else {
1585 if(tmp.count()==0) return false;
1586 }
1587 // std::cout<<"RS_EntityContainer::optimizeContours: 3"<<std::endl;
1588 RS_Vector vpStart;
1589 RS_Vector vpEnd;
1590 if(current){
1591 vpStart=current->getStartpoint();
1592 vpEnd=current->getEndpoint();
1593 }
1594 RS_Entity* next(nullptr);
1595 // std::cout<<"RS_EntityContainer::optimizeContours: 4"<<std::endl;
1596 /** connect entities **/
1597 const QString errMsg=QObject::tr("Hatch failed due to a gap=%1 between (%2, %3) and (%4, %5)");
1598
1599 while(count()>0) {
1600 double dist(0.);
1601 RS_Vector&& vpTmp=getNearestEndpoint(vpEnd,&dist,&next);
1602 if(dist>1e-8) {
1603 if(vpEnd.squaredTo(vpStart) < 1e-8) {
1604 RS_Entity* e2=entityAt(0);
1605 tmp.addEntity(e2->clone());
1606 vpStart=e2->getStartpoint();
1607 vpEnd=e2->getEndpoint();
1608 removeEntity(e2);
1609 continue;
1610 }
1611 else {
1612 QG_DIALOGFACTORY->commandMessage(
1613 errMsg.arg(dist).arg(vpTmp.x).arg(vpTmp.y).arg(vpEnd.x).arg(vpEnd.y)
1614 );
1615 RS_DEBUG->print(RS_Debug::D_ERROR, "RS_EntityContainer::optimizeContours: hatch failed due to a gap");
1616 closed=false;
1617 break;
1618 }
1619 }
1620 if(!next) { //workaround if next is nullptr
1621 // std::cout<<"RS_EntityContainer::optimizeContours: next is nullptr" <<std::endl;
1622 RS_DEBUG->print("RS_EntityContainer::optimizeContours: next is nullptr");
1623 // closed=false; //workaround if next is nullptr
1624 break; //workaround if next is nullptr
1625 } //workaround if next is nullptr
1626 if(closed) {
1627 next->setProcessed(true);
1628 RS_Entity* eTmp = next->clone();
1629 if(vpEnd.squaredTo(eTmp->getStartpoint())>vpEnd.squaredTo(eTmp->getEndpoint()))
1630 eTmp->revertDirection();
1631 vpEnd=eTmp->getEndpoint();
1632 tmp.addEntity(eTmp);
1633 removeEntity(next);
1634 }
1635 }
1636 // DEBUG_HEADER
1637 // if(vpEnd.valid && vpEnd.squaredTo(vpStart) > 1e-8) {
1638 // QG_DIALOGFACTORY->commandMessage(errMsg.arg(vpEnd.distanceTo(vpStart))
1639 // .arg(vpStart.x).arg(vpStart.y).arg(vpEnd.x).arg(vpEnd.y));
1640 // RS_DEBUG->print("RS_EntityContainer::optimizeContours: hatch failed due to a gap");
1641 // closed=false;
1642 // }
1643 // std::cout<<"RS_EntityContainer::optimizeContours: 5"<<std::endl;
1644
1645
1646 // add new sorted entities:
1647 for(auto en: tmp){
1648 en->setProcessed(false);
1649 addEntity(en->clone());
1650 }
1651 // std::cout<<"RS_EntityContainer::optimizeContours: 6"<<std::endl;
1652
1653 if(closed) {
1654 RS_DEBUG->print("RS_EntityContainer::optimizeContours: OK");
1655 }
1656 else {
1657 RS_DEBUG->print("RS_EntityContainer::optimizeContours: bad");
1658 }
1659 // std::cout<<"RS_EntityContainer::optimizeContours: end: count()="<<count()<<std::endl;
1660 // std::cout<<"RS_EntityContainer::optimizeContours: closed="<<closed<<std::endl;
1661 return closed;
1662 }
1663
1664
hasEndpointsWithinWindow(const RS_Vector & v1,const RS_Vector & v2)1665 bool RS_EntityContainer::hasEndpointsWithinWindow(const RS_Vector& v1, const RS_Vector& v2) {
1666 for(auto e: entities){
1667 if (e->hasEndpointsWithinWindow(v1, v2)) {
1668 return true;
1669 }
1670 }
1671
1672 return false;
1673 }
1674
1675
move(const RS_Vector & offset)1676 void RS_EntityContainer::move(const RS_Vector& offset) {
1677 for(auto e: entities){
1678
1679 e->move(offset);
1680 if (autoUpdateBorders) {
1681 e->moveBorders(offset);
1682 }
1683 }
1684 if (autoUpdateBorders) {
1685 moveBorders(offset);
1686 }
1687 }
1688
1689
1690
rotate(const RS_Vector & center,const double & angle)1691 void RS_EntityContainer::rotate(const RS_Vector& center, const double& angle) {
1692 RS_Vector angleVector(angle);
1693
1694 for(auto e: entities){
1695 e->rotate(center, angleVector);
1696 }
1697 if (autoUpdateBorders) {
1698 calculateBorders();
1699 }
1700 }
1701
1702
rotate(const RS_Vector & center,const RS_Vector & angleVector)1703 void RS_EntityContainer::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
1704
1705 for(auto e: entities){
1706 e->rotate(center, angleVector);
1707 }
1708 if (autoUpdateBorders) {
1709 calculateBorders();
1710 }
1711 }
1712
1713
scale(const RS_Vector & center,const RS_Vector & factor)1714 void RS_EntityContainer::scale(const RS_Vector& center, const RS_Vector& factor) {
1715 if (fabs(factor.x)>RS_TOLERANCE && fabs(factor.y)>RS_TOLERANCE) {
1716
1717 for(auto e: entities){
1718 e->scale(center, factor);
1719 }
1720 }
1721 if (autoUpdateBorders) {
1722 calculateBorders();
1723 }
1724 }
1725
1726
1727
mirror(const RS_Vector & axisPoint1,const RS_Vector & axisPoint2)1728 void RS_EntityContainer::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
1729 if (axisPoint1.distanceTo(axisPoint2)>RS_TOLERANCE) {
1730
1731 for(auto e: entities){
1732 e->mirror(axisPoint1, axisPoint2);
1733 }
1734 }
1735 }
1736
1737
stretch(const RS_Vector & firstCorner,const RS_Vector & secondCorner,const RS_Vector & offset)1738 void RS_EntityContainer::stretch(const RS_Vector& firstCorner,
1739 const RS_Vector& secondCorner,
1740 const RS_Vector& offset) {
1741
1742 if (getMin().isInWindow(firstCorner, secondCorner) &&
1743 getMax().isInWindow(firstCorner, secondCorner)) {
1744
1745 move(offset);
1746 } else {
1747
1748 for(auto e: entities){
1749 e->stretch(firstCorner, secondCorner, offset);
1750 }
1751 }
1752
1753 // some entitiycontainers might need an update (e.g. RS_Leader):
1754 update();
1755 }
1756
1757
1758
moveRef(const RS_Vector & ref,const RS_Vector & offset)1759 void RS_EntityContainer::moveRef(const RS_Vector& ref,
1760 const RS_Vector& offset) {
1761
1762
1763 for(auto e: entities){
1764 e->moveRef(ref, offset);
1765 }
1766 if (autoUpdateBorders) {
1767 calculateBorders();
1768 }
1769 }
1770
1771
moveSelectedRef(const RS_Vector & ref,const RS_Vector & offset)1772 void RS_EntityContainer::moveSelectedRef(const RS_Vector& ref,
1773 const RS_Vector& offset) {
1774
1775
1776 for(auto e: entities){
1777 e->moveSelectedRef(ref, offset);
1778 }
1779 if (autoUpdateBorders) {
1780 calculateBorders();
1781 }
1782 }
1783
revertDirection()1784 void RS_EntityContainer::revertDirection() {
1785 for(int k = 0; k < entities.size() / 2; ++k) {
1786 entities.swap(k, entities.size() - 1 - k);
1787 }
1788
1789 for(RS_Entity*const entity: entities) {
1790 entity->revertDirection();
1791 }
1792 }
1793
1794 /**
1795 * @brief RS_EntityContainer::draw() draw entities in order
1796 * @param painter
1797 * @param view
1798 */
draw(RS_Painter * painter,RS_GraphicView * view,double &)1799 void RS_EntityContainer::draw(RS_Painter* painter, RS_GraphicView* view,
1800 double& /*patternOffset*/) {
1801
1802 if (!(painter && view)) {
1803 return;
1804 }
1805
1806 foreach (auto e, entities)
1807 {
1808 view->drawEntity(painter, e);
1809 }
1810 }
1811
1812 /**
1813 * @brief areaLineIntegral, line integral for contour area calculation by Green's Theorem
1814 * Contour Area =\oint x dy
1815 * @return line integral \oint x dy along the entity
1816 */
areaLineIntegral() const1817 double RS_EntityContainer::areaLineIntegral() const
1818 {
1819 //TODO make sure all contour integral is by counter-clockwise
1820 double contourArea=0.;
1821 //closed area is always positive
1822 double closedArea=0.;
1823
1824 // edges:
1825
1826 for(auto e: entities){
1827 e->setLayer(getLayer());
1828 switch (e->rtti()) {
1829 case RS2::EntityLine:
1830 case RS2::EntityArc:
1831 contourArea += e->areaLineIntegral();
1832 break;
1833 case RS2::EntityCircle:
1834 closedArea += e->areaLineIntegral();
1835 break;
1836 case RS2::EntityEllipse:
1837 if(static_cast<RS_Ellipse*>(e)->isArc())
1838 contourArea += e->areaLineIntegral();
1839 else
1840 closedArea += e->areaLineIntegral();
1841 default:
1842 break;
1843 }
1844 }
1845 return fabs(contourArea)+closedArea;
1846 }
1847
ignoredOnModification() const1848 bool RS_EntityContainer::ignoredOnModification() const
1849 {
1850 switch(rtti()){
1851 // commented out Insert to allow snapping on block, bug#523
1852 // case RS2::EntityInsert: /**Insert*/
1853 case RS2::EntitySpline:
1854 case RS2::EntityMText: /**< Text 15*/
1855 case RS2::EntityText: /**< Text 15*/
1856 case RS2::EntityDimAligned: /**< Aligned Dimension */
1857 case RS2::EntityDimLinear: /**< Linear Dimension */
1858 case RS2::EntityDimRadial: /**< Radial Dimension */
1859 case RS2::EntityDimDiametric: /**< Diametric Dimension */
1860 case RS2::EntityDimAngular: /**< Angular Dimension */
1861 case RS2::EntityDimLeader: /**< Leader Dimension */
1862 case RS2::EntityHatch:
1863 return true;
1864 default:
1865 return false;
1866 }
1867 }
1868
ignoredSnap() const1869 bool RS_EntityContainer::ignoredSnap() const
1870 {
1871 // issue #652 , disable snap for hatch
1872 // TODO, should snapping on hatch be a feature enabled by settings?
1873 if (getParent() && getParent()->rtti() == RS2::EntityHatch)
1874 return true;
1875 return ignoredOnModification();
1876 }
1877
begin() const1878 QList<RS_Entity *>::const_iterator RS_EntityContainer::begin() const
1879 {
1880 return entities.begin();
1881 }
1882
end() const1883 QList<RS_Entity *>::const_iterator RS_EntityContainer::end() const
1884 {
1885 return entities.end();
1886 }
1887
begin()1888 QList<RS_Entity *>::iterator RS_EntityContainer::begin()
1889 {
1890 return entities.begin();
1891 }
1892
end()1893 QList<RS_Entity *>::iterator RS_EntityContainer::end()
1894 {
1895 return entities.end();
1896 }
1897
1898 /**
1899 * Dumps the entities to stdout.
1900 */
operator <<(std::ostream & os,RS_EntityContainer & ec)1901 std::ostream& operator << (std::ostream& os, RS_EntityContainer& ec) {
1902
1903 static int indent = 0;
1904
1905 char* tab = new char[indent*2+1];
1906 for(int i=0; i<indent*2; ++i) {
1907 tab[i] = ' ';
1908 }
1909 tab[indent*2] = '\0';
1910
1911 ++indent;
1912
1913 unsigned long int id = ec.getId();
1914
1915 os << tab << "EntityContainer[" << id << "]: \n";
1916 os << tab << "Borders[" << id << "]: "
1917 << ec.minV << " - " << ec.maxV << "\n";
1918 //os << tab << "Unit[" << id << "]: "
1919 //<< RS_Units::unit2string (ec.unit) << "\n";
1920 if (ec.getLayer()) {
1921 os << tab << "Layer[" << id << "]: "
1922 << ec.getLayer()->getName().toLatin1().data() << "\n";
1923 } else {
1924 os << tab << "Layer[" << id << "]: <nullptr>\n";
1925 }
1926 //os << ec.layerList << "\n";
1927
1928 os << tab << " Flags[" << id << "]: "
1929 << (ec.getFlag(RS2::FlagVisible) ? "RS2::FlagVisible" : "");
1930 os << (ec.getFlag(RS2::FlagUndone) ? " RS2::FlagUndone" : "");
1931 os << (ec.getFlag(RS2::FlagSelected) ? " RS2::FlagSelected" : "");
1932 os << "\n";
1933
1934
1935 os << tab << "Entities[" << id << "]: \n";
1936 for(auto t: ec){
1937
1938
1939 switch (t->rtti()) {
1940 case RS2::EntityInsert:
1941 os << tab << *((RS_Insert*)t);
1942 os << tab << *((RS_Entity*)t);
1943 os << tab << *((RS_EntityContainer*)t);
1944 break;
1945 default:
1946 if (t->isContainer()) {
1947 os << tab << *((RS_EntityContainer*)t);
1948 } else {
1949 os << tab << *t;
1950 }
1951 break;
1952 }
1953 }
1954 os << tab << "\n\n";
1955 --indent;
1956
1957 delete[] tab;
1958 return os;
1959 }
1960
1961
first() const1962 RS_Entity* RS_EntityContainer::first() const
1963 {
1964 return entities.first();
1965 }
1966
last() const1967 RS_Entity* RS_EntityContainer::last() const
1968 {
1969 return entities.last();
1970 }
1971
getEntityList()1972 const QList<RS_Entity*>& RS_EntityContainer::getEntityList()
1973 {
1974 return entities;
1975 }
1976