]> Shamusworld >> Repos - architektonas/blob - src/base/rs_snapper.cpp
bbc306cc736098748264ea3b8091b0696b7ba217
[architektonas] / src / base / rs_snapper.cpp
1 // rs_snapper.cpp
2 //
3 // Part of the Architektonas Project
4 // Originally part of QCad Community Edition by Andrew Mustun
5 // Extensively rewritten and refactored by James L. Hammons
6 // (C) 2010 Underground Software
7 //
8 // JLH = James L. Hammons <jlhamm@acm.org>
9 //
10 // Who  When        What
11 // ---  ----------  -----------------------------------------------------------
12 // JLH  05/21/2010  Added this text. :-)
13 //
14
15 #include "rs_snapper.h"
16
17 #include "rs_dialogfactory.h"
18 #include "drawing.h"
19 #include "rs_entitycontainer.h"
20 #include "graphicview.h"
21 #include "rs_grid.h"
22 #include "rs_information.h"
23 #include "paintinterface.h"
24 #include "settings.h"
25
26 /**
27  * Constructor.
28  */
29 RS_Snapper::RS_Snapper(RS_EntityContainer & c, GraphicView & gv):
30         container(&c), graphicView(&gv), finished(false)
31 {
32         init();
33 }
34
35 RS_Snapper::RS_Snapper(void):
36         container(NULL), graphicView(NULL), finished(false)
37 {
38         init();
39 }
40
41 /**
42  * Destructor.
43  */
44 RS_Snapper::~RS_Snapper()
45 {
46 }
47
48 /**
49  * Initialize (called by all constructors)
50  */
51 void RS_Snapper::init()
52 {
53         if (graphicView)
54         {
55                 snapMode = graphicView->getDefaultSnapMode();
56                 snapRes = graphicView->getSnapRestriction();
57         }
58
59         keyEntity = NULL;
60         snapSpot = Vector(false);
61         snapCoord = Vector(false);
62         visible = false;
63         distance = 1.0;
64
65         settings.beginGroup("Snap");
66         snapRange = settings.value("Range", 20).toInt();
67         settings.endGroup();
68         settings.beginGroup("Appearance");
69         showCrosshairs = settings.value("ShowCrosshairs", true).toBool();
70         settings.endGroup();
71
72         // Sanity check
73         if (snapRange < 2)
74                 snapRange = 20;
75 }
76
77 void RS_Snapper::finish()
78 {
79         finished = true;
80 }
81
82 //bleh
83 void RS_Snapper::SetContainer(RS_EntityContainer * c)
84 {
85         container = c;
86 }
87
88 //bleh
89 void RS_Snapper::SetGraphicView(GraphicView * v)
90 {
91         graphicView = v;
92 }
93
94 /**
95  * @return Pointer to the entity which was the key entity for the last
96  * successful snapping action. If the snap mode is "end point" the key entity
97  * is the entity whos end point was caught. If the snap mode didn't require an
98  * entity (e.g. free, grid) this method will return NULL.
99  */
100 RS_Entity * RS_Snapper::getKeyEntity()
101 {
102         return keyEntity;
103 }
104
105 /** Sets a new snap mode. */
106 void RS_Snapper::setSnapMode(RS2::SnapMode snapMode)
107 {
108         this->snapMode = snapMode;
109 }
110
111 /** Sets a new snap restriction. */
112 void RS_Snapper::setSnapRestriction(RS2::SnapRestriction snapRes)
113 {
114         this->snapRes = snapRes;
115 }
116
117 /**
118 * Sets the snap range in pixels for catchEntity().
119 *
120 * @see catchEntity()
121 */
122 void RS_Snapper::setSnapRange(int r)
123 {
124         snapRange = r;
125 }
126
127 /**
128  * Snap to a coordinate in the drawing using the current snap mode.
129  *
130  * @param e A mouse event.
131  * @return The coordinates of the point or an invalid vector.
132  */
133 Vector RS_Snapper::snapPoint(QMouseEvent * e)
134 {
135         RS_DEBUG->print("RS_Snapper::snapPoint");
136         snapSpot = Vector(false);
137
138         if (!e)
139         {
140                 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Snapper::snapPoint: event is NULL");
141                 return snapSpot;
142         }
143
144         Vector mouseCoord = graphicView->toGraph(e->x(), e->y());
145
146         switch (snapMode)
147         {
148         case RS2::SnapFree:
149                 snapSpot = snapFree(mouseCoord);
150                 break;
151
152         case RS2::SnapEndpoint:
153                 snapSpot = snapEndpoint(mouseCoord);
154                 break;
155
156         case RS2::SnapGrid:
157                 snapSpot = snapGrid(mouseCoord);
158                 break;
159
160         case RS2::SnapOnEntity:
161                 snapSpot = snapOnEntity(mouseCoord);
162                 break;
163
164         case RS2::SnapCenter:
165                 snapSpot = snapCenter(mouseCoord);
166                 break;
167
168         case RS2::SnapMiddle:
169                 snapSpot = snapMiddle(mouseCoord);
170                 break;
171
172         case RS2::SnapDist:
173                 snapSpot = snapDist(mouseCoord);
174                 break;
175
176         case RS2::SnapIntersection:
177                 snapSpot = snapIntersection(mouseCoord);
178                 break;
179
180         default:
181                 break;
182         }
183
184         // Handle snap restrictions that can be activated in addition to the ones above:
185         switch (snapRes)
186         {
187         case RS2::RestrictOrthogonal:
188                 snapCoord = restrictOrthogonal(snapSpot);
189                 break;
190         case RS2::RestrictHorizontal:
191                 snapCoord = restrictHorizontal(snapSpot);
192                 break;
193         case RS2::RestrictVertical:
194                 snapCoord = restrictVertical(snapSpot);
195                 break;
196         default:
197         case RS2::RestrictNothing:
198                 snapCoord = snapSpot;
199                 break;
200         }
201
202         if (RS_DIALOGFACTORY)
203                 RS_DIALOGFACTORY->updateCoordinateWidget(snapCoord, snapCoord - graphicView->getRelativeZero());
204
205         RS_DEBUG->print("RS_Snapper::snapPoint: OK");
206
207         return snapCoord;
208 }
209
210 /**
211  * Snaps to a free coordinate.
212  *
213  * @param coord The mouse coordinate.
214  * @return The coordinates of the point or an invalid vector.
215  */
216 Vector RS_Snapper::snapFree(Vector coord)
217 {
218         keyEntity = NULL;
219         return coord;
220 }
221
222 /**
223  * Snaps to the closest endpoint.
224  *
225  * @param coord The mouse coordinate.
226  * @return The coordinates of the point or an invalid vector.
227  */
228 Vector RS_Snapper::snapEndpoint(Vector coord)
229 {
230         Vector vec(false);
231         vec = container->getNearestEndpoint(coord, NULL/*, &keyEntity*/);
232
233         return vec;
234 }
235
236 /**
237  * Snaps to a grid point.
238  *
239  * @param coord The mouse coordinate.
240  * @return The coordinates of the point or an invalid vector.
241  */
242 Vector RS_Snapper::snapGrid(Vector coord)
243 {
244         RS_DEBUG->print("RS_Snapper::snapGrid begin");
245
246         Vector vec(false);
247         double dist = 0.0;
248
249         RS_Grid * grid = graphicView->getGrid();
250
251         RS_DEBUG->print("RS_Snapper::snapGrid 001");
252
253         if (grid)
254         {
255                 RS_DEBUG->print("RS_Snapper::snapGrid 002");
256                 Vector * pts = grid->getPoints();
257                 RS_DEBUG->print("RS_Snapper::snapGrid 003");
258                 int closest = -1;
259                 dist = 32000.00;
260                 RS_DEBUG->print("RS_Snapper::snapGrid 004");
261
262                 for(int i=0; i<grid->count(); ++i)
263                 {
264                         double d = pts[i].distanceTo(coord);
265
266                         if (d < dist)
267                         {
268                                 closest = i;
269                                 dist = d;
270                         }
271                 }
272
273                 RS_DEBUG->print("RS_Snapper::snapGrid 005");
274
275                 if (closest >= 0)
276                         vec = pts[closest];
277
278                 RS_DEBUG->print("RS_Snapper::snapGrid 006");
279         }
280
281         keyEntity = NULL;
282
283         RS_DEBUG->print("RS_Snapper::snapGrid end");
284
285         return vec;
286 }
287
288 /**
289  * Snaps to a point on an entity.
290  *
291  * @param coord The mouse coordinate.
292  * @return The coordinates of the point or an invalid vector.
293  */
294 Vector RS_Snapper::snapOnEntity(Vector coord)
295 {
296         Vector vec(false);
297         vec = container->getNearestPointOnEntity(coord, true, NULL, &keyEntity);
298
299         return vec;
300 }
301
302 /**
303  * Snaps to the closest center.
304  *
305  * @param coord The mouse coordinate.
306  * @return The coordinates of the point or an invalid vector.
307  */
308 Vector RS_Snapper::snapCenter(Vector coord)
309 {
310         Vector vec(false);
311         vec = container->getNearestCenter(coord, NULL);
312
313         return vec;
314 }
315
316 /**
317  * Snaps to the closest middle.
318  *
319  * @param coord The mouse coordinate.
320  * @return The coordinates of the point or an invalid vector.
321  */
322 Vector RS_Snapper::snapMiddle(Vector coord)
323 {
324         Vector vec(false);
325         vec = container->getNearestMiddle(coord, NULL);
326
327         return vec;
328 }
329
330 /**
331  * Snaps to the closest point with a given distance to the endpoint.
332  *
333  * @param coord The mouse coordinate.
334  * @return The coordinates of the point or an invalid vector.
335  */
336 Vector RS_Snapper::snapDist(Vector coord)
337 {
338         Vector vec(false);
339         vec = container->getNearestDist(distance, coord, NULL);
340
341         return vec;
342 }
343
344 /**
345  * Snaps to the closest intersection point.
346  *
347  * @param coord The mouse coordinate.
348  * @return The coordinates of the point or an invalid vector.
349  */
350 Vector RS_Snapper::snapIntersection(Vector coord)
351 {
352         Vector vec(false);
353         vec = container->getNearestIntersection(coord, NULL);
354
355         return vec;
356 }
357
358 /**
359  * 'Corrects' the given coordinates to 0, 90, 180, 270 degrees relative to
360  * the current relative zero point.
361  *
362  * @param coord The uncorrected coordinates.
363  * @return The corrected coordinates.
364  */
365 Vector RS_Snapper::restrictOrthogonal(Vector coord)
366 {
367         Vector rz = graphicView->getRelativeZero();
368         Vector ret(coord);
369
370         Vector retx = Vector(rz.x, ret.y);
371         Vector rety = Vector(ret.x, rz.y);
372
373         if (retx.distanceTo(ret) > rety.distanceTo(ret))
374                 ret = rety;
375         else
376                 ret = retx;
377
378         return ret;
379 }
380
381 /**
382  * 'Corrects' the given coordinates to 0, 180 degrees relative to
383  * the current relative zero point.
384  *
385  * @param coord The uncorrected coordinates.
386  * @return The corrected coordinates.
387  */
388 Vector RS_Snapper::restrictHorizontal(Vector coord)
389 {
390         Vector rz = graphicView->getRelativeZero();
391
392         return Vector(coord.x, rz.y);
393 }
394
395 /**
396  * 'Corrects' the given coordinates to 90, 270 degrees relative to
397  * the current relative zero point.
398  *
399  * @param coord The uncorrected coordinates.
400  * @return The corrected coordinates.
401  */
402 Vector RS_Snapper::restrictVertical(Vector coord)
403 {
404         Vector rz = graphicView->getRelativeZero();
405
406         return Vector(rz.x, coord.y);
407 }
408
409 /**
410  * Catches an entity which is close to the given position 'pos'.
411  *
412  * @param pos A graphic coordinate.
413  * @param level The level of resolving for iterating through the entity
414  *        container
415  * @return Pointer to the entity or NULL.
416  */
417 RS_Entity * RS_Snapper::catchEntity(const Vector& pos, RS2::ResolveLevel level)
418 {
419         RS_DEBUG->print("RS_Snapper::catchEntity");
420
421         // set default distance for points inside solids
422         double dist = graphicView->toGraphDX(snapRange) * 0.9;
423
424         RS_Entity * entity = container->getNearestEntity(pos, &dist, level);
425
426         int idx = -1;
427
428         if (entity && entity->getParent())
429                 idx = entity->getParent()->findEntity(entity);
430
431         if (entity && dist <= graphicView->toGraphDX(snapRange))
432         {
433                 // highlight:
434                 RS_DEBUG->print("RS_Snapper::catchEntity: found: %d", idx);
435                 return entity;
436         }
437         else
438         {
439                 RS_DEBUG->print("RS_Snapper::catchEntity: not found");
440                 return NULL;
441         }
442
443         RS_DEBUG->print("RS_Snapper::catchEntity: OK");
444 }
445
446 /**
447  * Catches an entity which is close to the mouse cursor.
448  *
449  * @param e A mouse event.
450  * @param level The level of resolving for iterating through the entity
451  *        container
452  * @return Pointer to the entity or NULL.
453  */
454 RS_Entity * RS_Snapper::catchEntity(QMouseEvent * e, RS2::ResolveLevel level)
455 {
456     return catchEntity(Vector(graphicView->toGraphX(e->x()),
457                 graphicView->toGraphY(e->y())), level);
458 }
459
460 /**
461  * Suspends this snapper while another action takes place.
462  */
463 /*virtual*/ void RS_Snapper::suspend()
464 {
465         deleteSnapper();
466         snapSpot = snapCoord = Vector(false);
467 }
468
469 /**
470  * Resumes this snapper after it has been suspended.
471  */
472 /*virtual*/ void RS_Snapper::resume()
473 {
474         drawSnapper();
475 }
476
477 /**
478  * Hides the snapper options.
479  */
480 /*virtual*/ void RS_Snapper::hideOptions()
481 {
482         if (snapMode == RS2::SnapDist && RS_DIALOGFACTORY)
483                 RS_DIALOGFACTORY->requestSnapDistOptions(distance, false);
484 }
485
486 /**
487  * Shows the snapper options.
488  */
489 /*virtual*/ void RS_Snapper::showOptions()
490 {
491         if (snapMode == RS2::SnapDist && RS_DIALOGFACTORY)
492                 RS_DIALOGFACTORY->requestSnapDistOptions(distance, true);
493 }
494
495 /**
496  * Draws the snapper on the screen.
497  */
498 void RS_Snapper::drawSnapper()
499 {
500 printf("RS_Snapper::drawSnapper(): Using DEPRECATED function!!!\n");
501         if (!visible)
502                 xorSnapper();
503 }
504
505 /**
506  * Deletes the snapper from the screen.
507  */
508 void RS_Snapper::deleteSnapper()
509 {
510 printf("RS_Snapper::deleteSnapper(): Using DEPRECATED function!!!\n");
511         if (visible)
512         {
513                 xorSnapper();
514                 snapSpot = Vector(false);
515                 snapCoord = Vector(false);
516         }
517 }
518
519 /**
520  * Draws / deletes the current snapper spot.
521  */
522 void RS_Snapper::xorSnapper()
523 {
524 #warning "!!! RS_Snapper::xorSnapper() is DEPRECATED !!!"
525 //Not completely true...
526 //#warning "!!! xorSnapper() not working AT ALL !!!"
527 #if 0
528         if (!finished && snapSpot.valid)
529         {
530                 RS_Painter * painter = graphicView->createDirectPainter();
531                 painter->setPreviewMode();
532
533                 if (snapCoord.valid)
534                 {
535                         // snap point
536                         painter->drawCircle(graphicView->toGui(snapCoord), 4);
537
538                         // crosshairs:
539                         if (showCrosshairs == true)
540                         {
541                                 painter->setPen(RS_Pen(RS_Color(0, 255, 255), RS2::Width00, RS2::DashLine));
542                                 painter->drawLine(Vector(0, graphicView->toGuiY(snapCoord.y)),
543                                         Vector(graphicView->getWidth(), graphicView->toGuiY(snapCoord.y)));
544                                 painter->drawLine(Vector(graphicView->toGuiX(snapCoord.x), 0),
545                                         Vector(graphicView->toGuiX(snapCoord.x), graphicView->getHeight()));
546                         }
547                 }
548
549                 if (snapCoord.valid && snapCoord != snapSpot)
550                 {
551                         painter->drawLine(graphicView->toGui(snapSpot) + Vector(-5, 0),
552                                 graphicView->toGui(snapSpot) + Vector(-1, 4));
553                         painter->drawLine(graphicView->toGui(snapSpot) + Vector(0, 5),
554                                 graphicView->toGui(snapSpot) + Vector(4, 1));
555                         painter->drawLine(graphicView->toGui(snapSpot) + Vector(5, 0),
556                                 graphicView->toGui(snapSpot) + Vector(1, -4));
557                         painter->drawLine(graphicView->toGui(snapSpot) + Vector(0, -5),
558                                 graphicView->toGui(snapSpot) + Vector(-4, -1));
559                 }
560
561                 graphicView->destroyPainter();
562                 visible = !visible;
563         }
564 //#else
565         if (finished || !snapSpot.valid || !graphicView)
566                 return;
567
568         graphicView->SetSnapperDraw(true);
569         graphicView->SetSnapperVars(snapSpot, snapCoord, showCrosshairs);
570 //Apparently, this gets hit anyway by the preview code...
571 //      graphicView->redraw();
572
573         visible = !visible;
574 #endif
575 }
576
577 void RS_Snapper::SetVisible(bool visibility/*= true*/)
578 {
579         visible = visibility;
580 }
581
582 bool RS_Snapper::Visible(void)
583 {
584         return visible;
585 }
586
587 void RS_Snapper::Draw(GraphicView * view, PaintInterface * painter)
588 {
589         if (finished || !snapSpot.valid)
590                 return;
591
592 //hm, I don't like graphicView kicking around in here, especially since it now
593 //lives inside GraphicView... How to !!! FIX !!!?
594 //We'll pass it in for now...
595         if (snapCoord.valid)
596         {
597                 // snap point
598                 painter->setPen(RS_Pen(RS_Color(0, 127, 255), RS2::Width00, RS2::DashLine));
599                 painter->drawCircle(view->toGui(snapCoord), 4);
600
601                 // crosshairs
602                 if (showCrosshairs)
603                 {
604                         painter->setPen(RS_Pen(RS_Color(0, 255, 255), RS2::Width00, RS2::DashLine));
605                         painter->drawLine(Vector(0, view->toGuiY(snapCoord.y)),
606                                 Vector(view->getWidth(), view->toGuiY(snapCoord.y)));
607                         painter->drawLine(Vector(view->toGuiX(snapCoord.x), 0),
608                                 Vector(view->toGuiX(snapCoord.x), view->getHeight()));
609                 }
610         }
611
612         if (snapCoord.valid && snapCoord != snapSpot)
613         {
614                 painter->drawLine(view->toGui(snapSpot) + Vector(-5, 0),
615                         view->toGui(snapSpot) + Vector(-1, 4));
616                 painter->drawLine(view->toGui(snapSpot) + Vector(0, 5),
617                         view->toGui(snapSpot) + Vector(4, 1));
618                 painter->drawLine(view->toGui(snapSpot) + Vector(5, 0),
619                         view->toGui(snapSpot) + Vector(1, -4));
620                 painter->drawLine(view->toGui(snapSpot) + Vector(0, -5),
621                         view->toGui(snapSpot) + Vector(-4, -1));
622         }
623 }