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 ¤tCoord, 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