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