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 
28 #include<cmath>
29 #include<QMouseEvent>
30 #include "rs_snapper.h"
31 
32 #include "rs_point.h"
33 #include "rs_circle.h"
34 #include "rs_line.h"
35 #include "rs_dialogfactory.h"
36 #include "rs_graphicview.h"
37 #include "rs_grid.h"
38 #include "rs_settings.h"
39 #include "rs_overlayline.h"
40 #include "rs_coordinateevent.h"
41 #include "rs_entitycontainer.h"
42 #include "rs_pen.h"
43 #include "rs_debug.h"
44 
45 /**
46   * Disable all snapping.
47   *
48   * This effectively puts the object into free snap mode.
49   *
50   * @returns A reference to itself.
51   */
clear()52 RS_SnapMode const & RS_SnapMode::clear()
53 {
54     snapIntersection    = false;
55     snapOnEntity        = false;
56     snapCenter          = false;
57     snapDistance        = false;
58     snapMiddle          = false;
59     snapEndpoint        = false;
60     snapGrid            = false;
61     snapFree            = false;
62     snapAngle           = false;
63 
64     restriction = RS2::RestrictNothing;
65 
66     return *this;
67 }
68 
operator ==(RS_SnapMode const & rhs) const69 bool RS_SnapMode::operator ==(RS_SnapMode const& rhs) const
70 {
71     return snapIntersection == rhs.snapIntersection
72             && snapOnEntity == rhs.snapOnEntity
73             && snapCenter   == rhs.snapCenter
74             && snapDistance == rhs.snapDistance
75             && snapMiddle   == rhs.snapMiddle
76             && snapEndpoint == rhs.snapEndpoint
77             && snapGrid     == rhs.snapGrid
78             && snapFree     == rhs.snapFree
79             && restriction  == rhs.restriction
80             && snapAngle    == rhs.snapAngle;
81 }
82 
83 
84 /**
85   * snap mode to a flag integer
86   */
toInt(const RS_SnapMode & s)87 uint RS_SnapMode::toInt(const RS_SnapMode& s)
88 {
89     uint ret {0};
90 
91     if (s.snapIntersection) ret |= RS_SnapMode::SnapIntersection;
92     if (s.snapOnEntity)     ret |= RS_SnapMode::SnapOnEntity;
93     if (s.snapCenter)       ret |= RS_SnapMode::SnapCenter;
94     if (s.snapDistance)     ret |= RS_SnapMode::SnapDistance;
95     if (s.snapMiddle)       ret |= RS_SnapMode::SnapMiddle;
96     if (s.snapEndpoint)     ret |= RS_SnapMode::SnapEndpoint;
97     if (s.snapGrid)         ret |= RS_SnapMode::SnapGrid;
98     if (s.snapFree)         ret |= RS_SnapMode::SnapFree;
99     if (s.snapAngle)        ret |= RS_SnapMode::SnapAngle;
100 
101     switch (s.restriction) {
102     case RS2::RestrictHorizontal:
103         ret |= RS_SnapMode::RestrictHorizontal;
104         break;
105     case RS2::RestrictVertical:
106         ret |= RS_SnapMode::RestrictVertical;
107         break;
108     case RS2::RestrictOrthogonal:
109         ret |= RS_SnapMode::RestrictOrthogonal;
110         break;
111     default:
112         break;
113     }
114 
115     return ret;
116 }
117 
118 /**
119   * integer flag to snapMode
120   */
fromInt(unsigned int ret)121 RS_SnapMode RS_SnapMode::fromInt(unsigned int ret)
122 {
123     RS_SnapMode s;
124 
125     if (RS_SnapMode::SnapIntersection   & ret) s.snapIntersection = true;
126     if (RS_SnapMode::SnapOnEntity       & ret) s.snapOnEntity = true;
127     if (RS_SnapMode::SnapCenter         & ret) s.snapCenter = true;
128     if (RS_SnapMode::SnapDistance       & ret) s.snapDistance = true;
129     if (RS_SnapMode::SnapMiddle         & ret) s.snapMiddle = true;
130     if (RS_SnapMode::SnapEndpoint       & ret) s.snapEndpoint = true;
131     if (RS_SnapMode::SnapGrid           & ret) s.snapGrid = true;
132     if (RS_SnapMode::SnapFree           & ret) s.snapFree = true;
133     if (RS_SnapMode::SnapAngle          & ret) s.snapAngle = true;
134 
135     switch (RS_SnapMode::RestrictOrthogonal & ret) {
136     case RS_SnapMode::RestrictHorizontal:
137         s.restriction = RS2::RestrictHorizontal;
138         break;
139     case RS_SnapMode::RestrictVertical:
140         s.restriction = RS2::RestrictVertical;
141         break;
142     case RS_SnapMode::RestrictOrthogonal:
143         s.restriction = RS2::RestrictOrthogonal;
144         break;
145     default:
146         s.restriction = RS2::RestrictNothing;
147         break;
148     }
149 
150     return s;
151 }
152 
153 /**
154   * Methods and structs for class RS_Snapper
155   */
156 struct RS_Snapper::Indicator
157 {
158     bool lines_state;
159     QString lines_type;
160     RS_Pen lines_pen;
161 
162     bool shape_state;
163     QString shape_type;
164     RS_Pen shape_pen;
165 };
166 
167 struct RS_Snapper::ImpData {
168 RS_Vector snapCoord;
169 RS_Vector snapSpot;
170 };
171 
172 /**
173  * Constructor.
174  */
RS_Snapper(RS_EntityContainer & container,RS_GraphicView & graphicView)175 RS_Snapper::RS_Snapper(RS_EntityContainer& container, RS_GraphicView& graphicView)
176     :container(&container)
177     ,graphicView(&graphicView)
178 	,pImpData(new ImpData{})
179     ,snap_indicator(new Indicator{})
180 {}
181 
~RS_Snapper()182 RS_Snapper::~RS_Snapper()
183 {
184     delete snap_indicator;
185 }
186 
187 /**
188  * Initialize (called by all constructors)
189  */
init()190 void RS_Snapper::init()
191 {
192     snapMode = graphicView->getDefaultSnapMode();
193 	keyEntity = nullptr;
194 	pImpData->snapSpot = RS_Vector{false};
195 	pImpData->snapCoord = RS_Vector{false};
196 	m_SnapDistance = 1.0;
197 
198     RS_SETTINGS->beginGroup("/Appearance");
199     snap_indicator->lines_state = RS_SETTINGS->readNumEntry("/indicator_lines_state", 1);
200     snap_indicator->lines_type = RS_SETTINGS->readEntry("/indicator_lines_type", "Crosshair");
201     snap_indicator->shape_state = RS_SETTINGS->readNumEntry("/indicator_shape_state", 1);
202     snap_indicator->shape_type = RS_SETTINGS->readEntry("/indicator_shape_type", "Circle");
203     RS_SETTINGS->endGroup();
204 
205     RS_SETTINGS->beginGroup("Colors");
206     QString snap_color = RS_SETTINGS->readEntry("/snap_indicator", Colors::snap_indicator);
207     RS_SETTINGS->endGroup();
208 
209 	snap_indicator->lines_pen = RS_Pen(RS_Color(snap_color), RS2::Width00, RS2::DashLine2);
210 	snap_indicator->shape_pen = RS_Pen(RS_Color(snap_color), RS2::Width00, RS2::SolidLine);
211 	snap_indicator->shape_pen.setScreenWidth(1);
212 
213     snapRange=getSnapRange();
214 }
215 
216 
finish()217 void RS_Snapper::finish() {
218     finished = true;
219     deleteSnapper();
220 }
221 
222 
setSnapMode(const RS_SnapMode & snapMode)223 void RS_Snapper::setSnapMode(const RS_SnapMode& snapMode) {
224     this->snapMode = snapMode;
225 	RS_DIALOGFACTORY->requestSnapDistOptions(m_SnapDistance, snapMode.snapDistance);
226     RS_DIALOGFACTORY->requestSnapMiddleOptions(middlePoints, snapMode.snapMiddle);
227 //std::cout<<"RS_Snapper::setSnapMode(): middlePoints="<<middlePoints<<std::endl;
228 }
229 
230 
getSnapMode() const231 RS_SnapMode const* RS_Snapper::getSnapMode() const{
232 	return &(this->snapMode);
233 }
234 
getSnapMode()235 RS_SnapMode* RS_Snapper::getSnapMode() {
236 	return &(this->snapMode);
237 }
238 
239 //get current mouse coordinates
snapFree(QMouseEvent * e)240 RS_Vector RS_Snapper::snapFree(QMouseEvent* e) {
241 	if (!e) {
242                 RS_DEBUG->print(RS_Debug::D_WARNING,
243 						"RS_Snapper::snapFree: event is nullptr");
244         return RS_Vector(false);
245     }
246 	pImpData->snapSpot=graphicView->toGraph(e->x(), e->y());
247 	pImpData->snapCoord=pImpData->snapSpot;
248     snap_indicator->lines_state=true;
249 	return pImpData->snapCoord;
250 }
251 
252 /**
253  * Snap to a coordinate in the drawing using the current snap mode.
254  *
255  * @param e A mouse event.
256  * @return The coordinates of the point or an invalid vector.
257  */
snapPoint(QMouseEvent * e)258 RS_Vector RS_Snapper::snapPoint(QMouseEvent* e)
259 {
260 	pImpData->snapSpot = RS_Vector(false);
261     RS_Vector t(false);
262 
263 	if (!e) {
264                 RS_DEBUG->print(RS_Debug::D_WARNING,
265 						"RS_Snapper::snapPoint: event is nullptr");
266 		return pImpData->snapSpot;
267     }
268 
269     RS_Vector mouseCoord = graphicView->toGraph(e->x(), e->y());
270     double ds2Min=RS_MAXDOUBLE*RS_MAXDOUBLE;
271 
272     if (snapMode.snapEndpoint) {
273         t = snapEndpoint(mouseCoord);
274 		double ds2=mouseCoord.squaredTo(t);
275 
276         if (ds2 < ds2Min){
277             ds2Min=ds2;
278 			pImpData->snapSpot = t;
279         }
280     }
281     if (snapMode.snapCenter) {
282         t = snapCenter(mouseCoord);
283 		double ds2=mouseCoord.squaredTo(t);
284         if (ds2 < ds2Min){
285             ds2Min=ds2;
286 			pImpData->snapSpot = t;
287         }
288     }
289     if (snapMode.snapMiddle) {
290         //this is still brutal force
291         //todo: accept value from widget QG_SnapMiddleOptions
292 		RS_DIALOGFACTORY->requestSnapMiddleOptions(middlePoints, snapMode.snapMiddle);
293         t = snapMiddle(mouseCoord);
294 		double ds2=mouseCoord.squaredTo(t);
295         if (ds2 < ds2Min){
296             ds2Min=ds2;
297 			pImpData->snapSpot = t;
298         }
299     }
300     if (snapMode.snapDistance) {
301         //this is still brutal force
302         //todo: accept value from widget QG_SnapDistOptions
303 		RS_DIALOGFACTORY->requestSnapDistOptions(m_SnapDistance, snapMode.snapDistance);
304         t = snapDist(mouseCoord);
305 		double ds2=mouseCoord.squaredTo(t);
306         if (ds2 < ds2Min){
307             ds2Min=ds2;
308 			pImpData->snapSpot = t;
309         }
310     }
311     if (snapMode.snapIntersection) {
312         t = snapIntersection(mouseCoord);
313 		double ds2=mouseCoord.squaredTo(t);
314         if (ds2 < ds2Min){
315             ds2Min=ds2;
316 			pImpData->snapSpot = t;
317         }
318     }
319 
320     if (snapMode.snapOnEntity &&
321 		pImpData->snapSpot.distanceTo(mouseCoord) > snapMode.distance) {
322         t = snapOnEntity(mouseCoord);
323 		double ds2=mouseCoord.squaredTo(t);
324         if (ds2 < ds2Min){
325             ds2Min=ds2;
326 			pImpData->snapSpot = t;
327         }
328     }
329 
330     if (snapMode.snapGrid) {
331         t = snapGrid(mouseCoord);
332 		double ds2=mouseCoord.squaredTo(t);
333         if (ds2 < ds2Min){
334 //            ds2Min=ds2;
335 			pImpData->snapSpot = t;
336         }
337     }
338 
339 	if( !pImpData->snapSpot.valid ) {
340 		pImpData->snapSpot=mouseCoord; //default to snapFree
341     } else {
342 //        std::cout<<"mouseCoord.distanceTo(snapSpot)="<<mouseCoord.distanceTo(snapSpot)<<std::endl;
343         //        std::cout<<"snapRange="<<snapRange<<std::endl;
344 
345         //retreat to snapFree when distance is more than half grid
346         if(snapMode.snapFree){
347 			RS_Vector const& ds=mouseCoord - pImpData->snapSpot;
348 			RS_Vector const& grid=graphicView->getGrid()->getCellVector()*0.5;
349 			if( fabs(ds.x) > fabs(grid.x) ||  fabs(ds.y) > fabs(grid.y) ) pImpData->snapSpot = mouseCoord;
350         }
351 
352         //another choice is to keep snapRange in GUI coordinates instead of graph coordinates
353 //        if (mouseCoord.distanceTo(snapSpot) > snapRange ) snapSpot = mouseCoord;
354     }
355     //if (snapSpot.distanceTo(mouseCoord) > snapMode.distance) {
356     // handle snap restrictions that can be activated in addition
357     //   to the ones above:
358     //apply restriction
359     RS_Vector rz = graphicView->getRelativeZero();
360 	RS_Vector vpv(rz.x, pImpData->snapSpot.y);
361 	RS_Vector vph(pImpData->snapSpot.x,rz.y);
362     switch (snapMode.restriction) {
363     case RS2::RestrictOrthogonal:
364 		pImpData->snapCoord= ( mouseCoord.distanceTo(vpv)< mouseCoord.distanceTo(vph))?
365                     vpv:vph;
366         break;
367     case RS2::RestrictHorizontal:
368 		pImpData->snapCoord = vph;
369         break;
370     case RS2::RestrictVertical:
371 		pImpData->snapCoord = vpv;
372         break;
373 
374     //case RS2::RestrictNothing:
375     default:
376 		pImpData->snapCoord = pImpData->snapSpot;
377         break;
378     }
379     //}
380     //else snapCoord = snapSpot;
381 
382 	snapPoint(pImpData->snapSpot, false);
383 
384 	return pImpData->snapCoord;
385 }
386 
387 
388 /**manually set snapPoint*/
snapPoint(const RS_Vector & coord,bool setSpot)389 RS_Vector RS_Snapper::snapPoint(const RS_Vector& coord, bool setSpot)
390 {
391     if(coord.valid){
392 		pImpData->snapSpot=coord;
393 		if(setSpot) pImpData->snapCoord = coord;
394 		drawSnapper();
395 		RS_DIALOGFACTORY->updateCoordinateWidget(
396 					pImpData->snapCoord,
397 					pImpData->snapCoord - graphicView->getRelativeZero());
398     }
399     return coord;
400 }
401 
402 
getSnapRange() const403 double RS_Snapper::getSnapRange() const
404 {
405     if (graphicView) {
406         return (graphicView->getGrid()->getCellVector() * 0.5).magnitude();
407     }
408 
409     return 20.;
410 }
411 
412 /**
413  * Snaps to a free coordinate.
414  *
415  * @param coord The mouse coordinate.
416  * @return The coordinates of the point or an invalid vector.
417  */
snapFree(const RS_Vector & coord)418 RS_Vector RS_Snapper::snapFree(const RS_Vector& coord) {
419 	keyEntity = nullptr;
420     return coord;
421 }
422 
423 
424 
425 /**
426  * Snaps to the closest endpoint.
427  *
428  * @param coord The mouse coordinate.
429  * @return The coordinates of the point or an invalid vector.
430  */
snapEndpoint(const RS_Vector & coord)431 RS_Vector RS_Snapper::snapEndpoint(const RS_Vector& coord) {
432     RS_Vector vec(false);
433 
434     vec = container->getNearestEndpoint(coord,
435 										nullptr/*, &keyEntity*/);
436     return vec;
437 }
438 
439 
440 
441 /**
442  * Snaps to a grid point.
443  *
444  * @param coord The mouse coordinate.
445  * @return The coordinates of the point or an invalid vector.
446  */
snapGrid(const RS_Vector & coord)447 RS_Vector RS_Snapper::snapGrid(const RS_Vector& coord) {
448 
449 //    RS_DEBUG->print("RS_Snapper::snapGrid begin");
450 
451 //    std::cout<<__FILE__<<" : "<<__func__<<" : line "<<__LINE__<<std::endl;
452 //    std::cout<<" mouse: = "<<coord<<std::endl;
453 //    std::cout<<" snapGrid: = "<<graphicView->getGrid()->snapGrid(coord)<<std::endl;
454     return  graphicView->getGrid()->snapGrid(coord);
455 }
456 
457 
458 
459 /**
460  * Snaps to a point on an entity.
461  *
462  * @param coord The mouse coordinate.
463  * @return The coordinates of the point or an invalid vector.
464  */
snapOnEntity(const RS_Vector & coord)465 RS_Vector RS_Snapper::snapOnEntity(const RS_Vector& coord) {
466 
467 	RS_Vector vec{};
468 	vec = container->getNearestPointOnEntity(coord, true, nullptr, &keyEntity);
469     return vec;
470 }
471 
472 
473 
474 /**
475  * Snaps to the closest center.
476  *
477  * @param coord The mouse coordinate.
478  * @return The coordinates of the point or an invalid vector.
479  */
snapCenter(const RS_Vector & coord)480 RS_Vector RS_Snapper::snapCenter(const RS_Vector& coord) {
481 	RS_Vector vec{};
482 
483 	vec = container->getNearestCenter(coord, nullptr);
484     return vec;
485 }
486 
487 
488 
489 /**
490  * Snaps to the closest middle.
491  *
492  * @param coord The mouse coordinate.
493  * @return The coordinates of the point or an invalid vector.
494  */
snapMiddle(const RS_Vector & coord)495 RS_Vector RS_Snapper::snapMiddle(const RS_Vector& coord) {
496 //std::cout<<"RS_Snapper::snapMiddle(): middlePoints="<<middlePoints<<std::endl;
497 	return container->getNearestMiddle(coord,static_cast<double *>(nullptr),middlePoints);
498 }
499 
500 
501 
502 /**
503  * Snaps to the closest point with a given distance to the endpoint.
504  *
505  * @param coord The mouse coordinate.
506  * @return The coordinates of the point or an invalid vector.
507  */
snapDist(const RS_Vector & coord)508 RS_Vector RS_Snapper::snapDist(const RS_Vector& coord) {
509     RS_Vector vec;
510 
511 //std::cout<<" RS_Snapper::snapDist(RS_Vector coord): distance="<<distance<<std::endl;
512 	vec = container->getNearestDist(m_SnapDistance,
513                                     coord,
514 									nullptr);
515     return vec;
516 }
517 
518 
519 
520 /**
521  * Snaps to the closest intersection point.
522  *
523  * @param coord The mouse coordinate.
524  * @return The coordinates of the point or an invalid vector.
525  */
snapIntersection(const RS_Vector & coord)526 RS_Vector RS_Snapper::snapIntersection(const RS_Vector& coord) {
527 	RS_Vector vec{};
528 
529     vec = container->getNearestIntersection(coord,
530 											nullptr);
531     return vec;
532 }
533 
534 
535 
536 /**
537  * 'Corrects' the given coordinates to 0, 90, 180, 270 degrees relative to
538  * the current relative zero point.
539  *
540  * @param coord The uncorrected coordinates.
541  * @return The corrected coordinates.
542  */
restrictOrthogonal(const RS_Vector & coord)543 RS_Vector RS_Snapper::restrictOrthogonal(const RS_Vector& coord) {
544     RS_Vector rz = graphicView->getRelativeZero();
545     RS_Vector ret(coord);
546 
547     RS_Vector retx = RS_Vector(rz.x, ret.y);
548     RS_Vector rety = RS_Vector(ret.x, rz.y);
549 
550     if (retx.distanceTo(ret) > rety.distanceTo(ret)) {
551         ret = rety;
552     } else {
553         ret = retx;
554     }
555 
556     return ret;
557 }
558 
559 /**
560  * 'Corrects' the given coordinates to 0, 180 degrees relative to
561  * the current relative zero point.
562  *
563  * @param coord The uncorrected coordinates.
564  * @return The corrected coordinates.
565  */
restrictHorizontal(const RS_Vector & coord)566 RS_Vector RS_Snapper::restrictHorizontal(const RS_Vector& coord) {
567     RS_Vector rz = graphicView->getRelativeZero();
568     RS_Vector ret = RS_Vector(coord.x, rz.y);
569     return ret;
570 }
571 
572 
573 /**
574  * 'Corrects' the given coordinates to 90, 270 degrees relative to
575  * the current relative zero point.
576  *
577  * @param coord The uncorrected coordinates.
578  * @return The corrected coordinates.
579  */
restrictVertical(const RS_Vector & coord)580 RS_Vector RS_Snapper::restrictVertical(const RS_Vector& coord) {
581     RS_Vector rz = graphicView->getRelativeZero();
582     RS_Vector ret = RS_Vector(rz.x, coord.y);
583     return ret;
584 }
585 
586 
587 /**
588  * Catches an entity which is close to the given position 'pos'.
589  *
590  * @param pos A graphic coordinate.
591  * @param level The level of resolving for iterating through the entity
592  *        container
593  * @return Pointer to the entity or nullptr.
594  */
catchEntity(const RS_Vector & pos,RS2::ResolveLevel level)595 RS_Entity* RS_Snapper::catchEntity(const RS_Vector& pos,
596                                    RS2::ResolveLevel level) {
597 
598     RS_DEBUG->print("RS_Snapper::catchEntity");
599 
600         // set default distance for points inside solids
601     double dist (0.);
602 //    std::cout<<"getSnapRange()="<<getSnapRange()<<"\tsnap distance = "<<dist<<std::endl;
603 
604     RS_Entity* entity = container->getNearestEntity(pos, &dist, level);
605 
606         int idx = -1;
607 		if (entity && entity->getParent()) {
608                 idx = entity->getParent()->findEntity(entity);
609         }
610 
611 	if (entity && dist<=getSnapRange()) {
612         // highlight:
613         RS_DEBUG->print("RS_Snapper::catchEntity: found: %d", idx);
614         return entity;
615     } else {
616         RS_DEBUG->print("RS_Snapper::catchEntity: not found");
617 		return nullptr;
618     }
619     RS_DEBUG->print("RS_Snapper::catchEntity: OK");
620 }
621 
622 
623 /**
624  * Catches an entity which is close to the given position 'pos'.
625  *
626  * @param pos A graphic coordinate.
627  * @param level The level of resolving for iterating through the entity
628  *        container
629  * @enType, only search for a particular entity type
630  * @return Pointer to the entity or nullptr.
631  */
catchEntity(const RS_Vector & pos,RS2::EntityType enType,RS2::ResolveLevel level)632 RS_Entity* RS_Snapper::catchEntity(const RS_Vector& pos, RS2::EntityType enType,
633                                    RS2::ResolveLevel level) {
634 
635     RS_DEBUG->print("RS_Snapper::catchEntity");
636 //                    std::cout<<"RS_Snapper::catchEntity(): enType= "<<enType<<std::endl;
637 
638     // set default distance for points inside solids
639 	RS_EntityContainer ec(nullptr,false);
640 	//isContainer
641 	bool isContainer{false};
642 	switch(enType){
643 	case RS2::EntityPolyline:
644 	case RS2::EntityContainer:
645 	case RS2::EntitySpline:
646 		isContainer=true;
647 		break;
648 	default:
649 		break;
650 	}
651 
652 	for(RS_Entity* en= container->firstEntity(level);en;en=container->nextEntity(level)){
653         if(en->isVisible()==false) continue;
654 		if(en->rtti() != enType && isContainer){
655             //whether this entity is a member of member of the type enType
656             RS_Entity* parent(en->getParent());
657 			bool matchFound{false};
658 			while(parent ) {
659 //                    std::cout<<"RS_Snapper::catchEntity(): parent->rtti()="<<parent->rtti()<<" enType= "<<enType<<std::endl;
660                 if(parent->rtti() == enType) {
661                     matchFound=true;
662                     ec.addEntity(en);
663                     break;
664                 }
665                 parent=parent->getParent();
666             }
667 			if(!matchFound) continue;
668         }
669         if (en->rtti() == enType){
670             ec.addEntity(en);
671         }
672     }
673 	if (ec.count() == 0 ) return nullptr;
674     double dist(0.);
675 
676     RS_Entity* entity = ec.getNearestEntity(pos, &dist, RS2::ResolveNone);
677 
678         int idx = -1;
679 		if (entity && entity->getParent()) {
680                 idx = entity->getParent()->findEntity(entity);
681         }
682 
683 	if (entity && dist<=getSnapRange()) {
684         // highlight:
685         RS_DEBUG->print("RS_Snapper::catchEntity: found: %d", idx);
686         return entity;
687     } else {
688         RS_DEBUG->print("RS_Snapper::catchEntity: not found");
689 		return nullptr;
690     }
691 }
692 
693 
694 /**
695  * Catches an entity which is close to the mouse cursor.
696  *
697  * @param e A mouse event.
698  * @param level The level of resolving for iterating through the entity
699  *        container
700  * @return Pointer to the entity or nullptr.
701  */
catchEntity(QMouseEvent * e,RS2::ResolveLevel level)702 RS_Entity* RS_Snapper::catchEntity(QMouseEvent* e,
703                                    RS2::ResolveLevel level) {
704 
705     return catchEntity(
706                RS_Vector(graphicView->toGraphX(e->x()),
707                          graphicView->toGraphY(e->y())),
708                level);
709 }
710 
711 
712 /**
713  * Catches an entity which is close to the mouse cursor.
714  *
715  * @param e A mouse event.
716  * @param level The level of resolving for iterating through the entity
717  *        container
718  * @enType, only search for a particular entity type
719  * @return Pointer to the entity or nullptr.
720  */
catchEntity(QMouseEvent * e,RS2::EntityType enType,RS2::ResolveLevel level)721 RS_Entity* RS_Snapper::catchEntity(QMouseEvent* e, RS2::EntityType enType,
722                                    RS2::ResolveLevel level) {
723     return catchEntity(
724 			   {graphicView->toGraphX(e->x()), graphicView->toGraphY(e->y())},
725 				enType,
726 				level);
727 }
728 
catchEntity(QMouseEvent * e,const EntityTypeList & enTypeList,RS2::ResolveLevel level)729 RS_Entity* RS_Snapper::catchEntity(QMouseEvent* e, const EntityTypeList& enTypeList,
730                                    RS2::ResolveLevel level) {
731 	RS_Entity* pten = nullptr;
732 	RS_Vector coord{graphicView->toGraphX(e->x()), graphicView->toGraphY(e->y())};
733     switch(enTypeList.size()) {
734     case 0:
735         return catchEntity(coord, level);
736     default:
737     {
738 
739 		RS_EntityContainer ec(nullptr,false);
740 		for( auto t0: enTypeList){
741 			RS_Entity* en=catchEntity(coord, t0, level);
742 			if(en) ec.addEntity(en);
743 //			if(en) {
744 //            std::cout<<__FILE__<<" : "<<__func__<<" : lines "<<__LINE__<<std::endl;
745 //            std::cout<<"caught id= "<<en->getId()<<std::endl;
746 //            }
747         }
748         if(ec.count()>0){
749             ec.getDistanceToPoint(coord, &pten, RS2::ResolveNone);
750             return pten;
751         }
752     }
753 
754     }
755 	return nullptr;
756 }
757 
suspend()758 void RS_Snapper::suspend() {
759 			// RVT Don't delete the snapper here!
760 	// RVT_PORT (can be deleted)();
761 	pImpData->snapSpot = pImpData->snapCoord = RS_Vector{false};
762 }
763 
764 /**
765  * Hides the snapper options. Default implementation does nothing.
766  */
hideOptions()767 void RS_Snapper::hideOptions() {
768     //not used any more, will be removed
769 }
770 
771 /**
772  * Shows the snapper options. Default implementation does nothing.
773  */
showOptions()774 void RS_Snapper::showOptions() {
775     //not used any more, will be removed
776 }
777 
778 
779 /**
780  * Deletes the snapper from the screen.
781  */
deleteSnapper()782 void RS_Snapper::deleteSnapper()
783 {
784     graphicView->getOverlayContainer(RS2::Snapper)->clear();
785     graphicView->redraw(RS2::RedrawOverlay); // redraw will happen in the mouse movement event
786 }
787 
788 
789 
790 /**
791  * creates the snap indicator
792  */
drawSnapper()793 void RS_Snapper::drawSnapper()
794 {
795     // We could properly speed this up by calling the draw function of this snapper within the paint event
796     // this will avoid creating/deletion of the lines
797 
798     graphicView->getOverlayContainer(RS2::Snapper)->clear();
799 	if (!finished && pImpData->snapSpot.valid)
800     {
801         RS_EntityContainer *container=graphicView->getOverlayContainer(RS2::Snapper);
802 
803         if (snap_indicator->lines_state)
804         {
805             QString type = snap_indicator->lines_type;
806 
807             if (type == "Crosshair")
808             {
809                 RS_OverlayLine *line = new RS_OverlayLine(nullptr,
810                     {{0., graphicView->toGuiY(pImpData->snapCoord.y)},
811                     {double(graphicView->getWidth()),
812                     graphicView->toGuiY(pImpData->snapCoord.y)}});
813 
814                 line->setPen(snap_indicator->lines_pen);
815                 container->addEntity(line);
816 
817                 line = new RS_OverlayLine(nullptr,
818                     {{graphicView->toGuiX(pImpData->snapCoord.x),0.},
819                     {graphicView->toGuiX(pImpData->snapCoord.x),
820                     double(graphicView->getHeight())}});
821 
822                 line->setPen(snap_indicator->lines_pen);
823                 container->addEntity(line);
824             }
825             else if (type == "Crosshair2")
826             {
827                 double xenoRadius=16;
828 
829                 double snapX=graphicView->toGuiX(pImpData->snapCoord.x);
830                 double snapY=graphicView->toGuiY(pImpData->snapCoord.y);
831 
832                 double viewWidth=double(graphicView->getWidth());
833                 double viewHeight=double(graphicView->getHeight());
834 
835                 RS_OverlayLine *line;
836 
837                 // ----O     (Left)
838                 line=new RS_OverlayLine(nullptr, {
839                     {0., snapY},
840                     {snapX-xenoRadius, snapY}
841                 });
842                 {
843                     line->setPen(snap_indicator->lines_pen);
844                     container->addEntity(line);
845                 }
846 
847                 //     O---- (Right)
848                 line=new RS_OverlayLine(nullptr, {
849                     {snapX+xenoRadius, snapY},
850                     {viewWidth, snapY}
851                 });
852                 {
853                     line->setPen(snap_indicator->lines_pen);
854                     container->addEntity(line);
855                 }
856 
857                 // (Top)
858                 line=new RS_OverlayLine(nullptr, {
859                     {snapX, 0.},
860                     {snapX, snapY-xenoRadius}
861                 });
862                 {
863                     line->setPen(snap_indicator->lines_pen);
864                     container->addEntity(line);
865                 }
866 
867                 // (Bottom)
868                 line=new RS_OverlayLine(nullptr, {
869                     {snapX, snapY+xenoRadius},
870                     {snapX, viewHeight}
871                 });
872                 {
873                     line->setPen(snap_indicator->lines_pen);
874                     container->addEntity(line);
875                 }
876             }
877             else if (type == "Isometric")
878             {
879                 //isometric crosshair
880                 RS2::CrosshairType chType=graphicView->getCrosshairType();
881                 RS_Vector direction1;
882                 RS_Vector direction2(0.,1.);
883                 double l=graphicView->getWidth()+graphicView->getHeight();
884                 switch(chType){
885                 case RS2::RightCrosshair:
886                     direction1=RS_Vector(M_PI*5./6.)*l;
887                     direction2*=l;
888                     break;
889                 case RS2::LeftCrosshair:
890                     direction1=RS_Vector(M_PI*1./6.)*l;
891                     direction2*=l;
892                     break;
893                 default:
894                     direction1=RS_Vector(M_PI*1./6.)*l;
895                     direction2=RS_Vector(M_PI*5./6.)*l;
896                 }
897                 RS_Vector center(graphicView->toGui(pImpData->snapCoord));
898                 RS_OverlayLine *line=new RS_OverlayLine(container,
899                 {center-direction1,center+direction1});
900                 line->setPen(snap_indicator->lines_pen);
901                 container->addEntity(line);
902                 line=new RS_OverlayLine(nullptr,
903                 {center-direction2,center+direction2});
904                 line->setPen(snap_indicator->lines_pen);
905                 container->addEntity(line);
906             }
907             else if (type == "Spiderweb")
908             {
909                 RS_OverlayLine* line;
910                 RS_Vector point1;
911                 RS_Vector point2;
912 
913                 point1 = RS_Vector{0, 0};
914                 point2 = RS_Vector{graphicView->toGuiX(pImpData->snapCoord.x),
915                                    graphicView->toGuiY(pImpData->snapCoord.y)};
916                 line=new RS_OverlayLine{nullptr, {point1, point2}};
917                 line->setPen(snap_indicator->lines_pen);
918                 container->addEntity(line);
919 
920                 point1 = RS_Vector(0, graphicView->getHeight());
921                 line = new RS_OverlayLine{nullptr, {point1, point2}};
922                 line->setPen(snap_indicator->lines_pen);
923                 container->addEntity(line);
924 
925                 point1 = RS_Vector(graphicView->getWidth(), 0);
926                 line = new RS_OverlayLine(nullptr, {point1, point2});
927                 line->setPen(snap_indicator->lines_pen);
928                 container->addEntity(line);
929 
930                 point1 = RS_Vector(graphicView->getWidth(), graphicView->getHeight());
931                 line = new RS_OverlayLine(nullptr, {point1, point2});
932                 line->setPen(snap_indicator->lines_pen);
933                 container->addEntity(line);
934             }
935         }
936         if (snap_indicator->shape_state)
937         {
938             QString type = snap_indicator->shape_type;
939 
940             if (type == "Circle")
941             {
942                 RS_Circle *circle=new RS_Circle(container,
943                     {pImpData->snapCoord, 4./graphicView->getFactor().x});
944                 circle->setPen(snap_indicator->shape_pen);
945                 container->addEntity(circle);
946             }
947             else if (type == "Point")
948             {
949                 RS_Point *point=new RS_Point(container, pImpData->snapCoord);
950                 point->setPen(snap_indicator->shape_pen);
951                 container->addEntity(point);
952             }
953             else if (type == "Square")
954             {
955                 RS_Vector snap_point{graphicView->toGuiX(pImpData->snapCoord.x),
956                                      graphicView->toGuiY(pImpData->snapCoord.y)};
957 
958                 double a = 6.0;
959                 RS_Vector p1 = snap_point + RS_Vector(-a, a);
960                 RS_Vector p2 = snap_point + RS_Vector(a, a);
961                 RS_Vector p3 = snap_point + RS_Vector(a, -a);
962                 RS_Vector p4 = snap_point + RS_Vector(-a, -a);
963 
964                 RS_OverlayLine* line;
965                 line=new RS_OverlayLine{nullptr, {p1, p2}};
966                 line->setPen(snap_indicator->shape_pen);
967                 container->addEntity(line);
968 
969                 line = new RS_OverlayLine{nullptr, {p2, p3}};
970                 line->setPen(snap_indicator->shape_pen);
971                 container->addEntity(line);
972 
973                 line = new RS_OverlayLine(nullptr, {p3, p4});
974                 line->setPen(snap_indicator->shape_pen);
975                 container->addEntity(line);
976 
977                 line = new RS_OverlayLine(nullptr, {p4, p1});
978                 line->setPen(snap_indicator->shape_pen);
979                 container->addEntity(line);
980             }
981         }
982         graphicView->redraw(RS2::RedrawOverlay); // redraw will happen in the mouse movement event
983     }
984 }
985 
snapToAngle(const RS_Vector & currentCoord,const RS_Vector & referenceCoord,const double angularResolution)986 RS_Vector RS_Snapper::snapToAngle(const RS_Vector &currentCoord, const RS_Vector &referenceCoord, const double angularResolution)
987 {
988 
989     if(snapMode.restriction != RS2::RestrictNothing || snapMode.snapGrid)
990     {
991         return currentCoord;
992     }
993 
994     double angle = referenceCoord.angleTo(currentCoord)*180.0/M_PI;
995     angle -= remainder(angle,angularResolution);
996     angle *= M_PI/180.;
997     RS_Vector res = RS_Vector::polar(referenceCoord.distanceTo(currentCoord),angle);
998     res += referenceCoord;
999 
1000     if (snapMode.snapOnEntity)
1001     {
1002         RS_Vector t(false);
1003         //RS_Vector mouseCoord = graphicView->toGraph(currentCoord.x(), currentCoord.y());
1004         t = container->getNearestVirtualIntersection(res,angle,nullptr);
1005 
1006         pImpData->snapSpot = t;
1007         snapPoint(pImpData->snapSpot, true);
1008         return t;
1009     }
1010     else
1011     {
1012         snapPoint(res, true);
1013         return res;
1014     }
1015 }
1016 
1017