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