]> Shamusworld >> Repos - architektonas/blob - src/base/rs_snapper.cpp
Fixed problem with dimensions not showing up.
[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 RS2::SnapMode RS_Snapper::getSnapMode(void)
118 {
119         return snapMode;
120 }
121
122 RS2::SnapRestriction RS_Snapper::getSnapRestriction(void)
123 {
124         return snapRes;
125 }
126
127 /**
128  * Sets the snap range in pixels for catchEntity().
129  *
130  * @see catchEntity()
131  */
132 void RS_Snapper::setSnapRange(int r)
133 {
134         snapRange = r;
135 }
136
137 /**
138  * Snap to a coordinate in the drawing using the current snap mode.
139  *
140  * @param e A mouse event.
141  * @return The coordinates of the point or an invalid vector.
142  */
143 Vector RS_Snapper::snapPoint(QMouseEvent * e)
144 {
145         RS_DEBUG->print("RS_Snapper::snapPoint");
146         snapSpot = Vector(false);
147
148         if (!e)
149         {
150                 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Snapper::snapPoint: event is NULL");
151                 return snapSpot;
152         }
153
154         Vector mouseCoord = graphicView->toGraph(e->x(), e->y());
155
156         switch (snapMode)
157         {
158         case RS2::SnapFree:
159                 snapSpot = snapFree(mouseCoord);
160                 break;
161
162         case RS2::SnapEndpoint:
163                 snapSpot = snapEndpoint(mouseCoord);
164                 break;
165
166         case RS2::SnapGrid:
167                 snapSpot = snapGrid(mouseCoord);
168                 break;
169
170         case RS2::SnapOnEntity:
171                 snapSpot = snapOnEntity(mouseCoord);
172                 break;
173
174         case RS2::SnapCenter:
175                 snapSpot = snapCenter(mouseCoord);
176                 break;
177
178         case RS2::SnapMiddle:
179                 snapSpot = snapMiddle(mouseCoord);
180                 break;
181
182         case RS2::SnapDist:
183                 snapSpot = snapDist(mouseCoord);
184                 break;
185
186         case RS2::SnapIntersection:
187                 snapSpot = snapIntersection(mouseCoord);
188                 break;
189
190         default:
191                 break;
192         }
193
194         // Handle snap restrictions that can be activated in addition to the ones above:
195         switch (snapRes)
196         {
197         case RS2::RestrictOrthogonal:
198                 snapCoord = restrictOrthogonal(snapSpot);
199                 break;
200         case RS2::RestrictHorizontal:
201                 snapCoord = restrictHorizontal(snapSpot);
202                 break;
203         case RS2::RestrictVertical:
204                 snapCoord = restrictVertical(snapSpot);
205                 break;
206         default:
207         case RS2::RestrictNothing:
208                 snapCoord = snapSpot;
209                 break;
210         }
211
212         if (RS_DIALOGFACTORY)
213                 RS_DIALOGFACTORY->updateCoordinateWidget(snapCoord, snapCoord - graphicView->getRelativeZero());
214
215         RS_DEBUG->print("RS_Snapper::snapPoint: OK");
216
217         return snapCoord;
218 }
219
220 /**
221  * Snaps to a free coordinate.
222  *
223  * @param coord The mouse coordinate.
224  * @return The coordinates of the point or an invalid vector.
225  */
226 Vector RS_Snapper::snapFree(Vector coord)
227 {
228         keyEntity = NULL;
229         return coord;
230 }
231
232 /**
233  * Snaps to the closest endpoint.
234  *
235  * @param coord The mouse coordinate.
236  * @return The coordinates of the point or an invalid vector.
237  */
238 Vector RS_Snapper::snapEndpoint(Vector coord)
239 {
240         Vector vec(false);
241         vec = container->getNearestEndpoint(coord, NULL);
242
243         return vec;
244 }
245
246 /**
247  * Snaps to a grid point.
248  *
249  * @param coord The mouse coordinate.
250  * @return The coordinates of the point or an invalid vector.
251  */
252 Vector RS_Snapper::snapGrid(Vector coord)
253 {
254         RS_DEBUG->print("RS_Snapper::snapGrid begin");
255
256         Vector vec(false);
257         double dist = 0.0;
258
259         RS_Grid * grid = graphicView->getGrid();
260
261         RS_DEBUG->print("RS_Snapper::snapGrid 001");
262
263         if (grid)
264         {
265                 RS_DEBUG->print("RS_Snapper::snapGrid 002");
266                 Vector * pts = grid->getPoints();
267                 RS_DEBUG->print("RS_Snapper::snapGrid 003");
268                 int closest = -1;
269                 dist = 32000.00;
270                 RS_DEBUG->print("RS_Snapper::snapGrid 004");
271
272                 for(int i=0; i<grid->count(); ++i)
273                 {
274                         double d = pts[i].distanceTo(coord);
275
276                         if (d < dist)
277                         {
278                                 closest = i;
279                                 dist = d;
280                         }
281                 }
282
283                 RS_DEBUG->print("RS_Snapper::snapGrid 005");
284
285                 if (closest >= 0)
286                         vec = pts[closest];
287
288                 RS_DEBUG->print("RS_Snapper::snapGrid 006");
289         }
290
291         keyEntity = NULL;
292
293         RS_DEBUG->print("RS_Snapper::snapGrid end");
294
295         return vec;
296 }
297
298 /**
299  * Snaps to a point on an entity.
300  *
301  * @param coord The mouse coordinate.
302  * @return The coordinates of the point or an invalid vector.
303  */
304 Vector RS_Snapper::snapOnEntity(Vector coord)
305 {
306         Vector vec(false);
307         vec = container->getNearestPointOnEntity(coord, true, NULL, &keyEntity);
308
309         return vec;
310 }
311
312 /**
313  * Snaps to the closest center.
314  *
315  * @param coord The mouse coordinate.
316  * @return The coordinates of the point or an invalid vector.
317  */
318 Vector RS_Snapper::snapCenter(Vector coord)
319 {
320         Vector vec(false);
321         vec = container->getNearestCenter(coord, NULL);
322
323         return vec;
324 }
325
326 /**
327  * Snaps to the closest middle.
328  *
329  * @param coord The mouse coordinate.
330  * @return The coordinates of the point or an invalid vector.
331  */
332 Vector RS_Snapper::snapMiddle(Vector coord)
333 {
334         Vector vec(false);
335         vec = container->getNearestMiddle(coord, NULL);
336
337         return vec;
338 }
339
340 /**
341  * Snaps to the closest point with a given distance to the endpoint.
342  *
343  * @param coord The mouse coordinate.
344  * @return The coordinates of the point or an invalid vector.
345  */
346 Vector RS_Snapper::snapDist(Vector coord)
347 {
348         Vector vec(false);
349         vec = container->getNearestDist(distance, coord, NULL);
350
351         return vec;
352 }
353
354 /**
355  * Snaps to the closest intersection point.
356  *
357  * @param coord The mouse coordinate.
358  * @return The coordinates of the point or an invalid vector.
359  */
360 Vector RS_Snapper::snapIntersection(Vector coord)
361 {
362         Vector vec(false);
363         vec = container->getNearestIntersection(coord, NULL);
364
365         return vec;
366 }
367
368 /**
369  * 'Corrects' the given coordinates to 0, 90, 180, 270 degrees relative to
370  * the current relative zero point.
371  *
372  * @param coord The uncorrected coordinates.
373  * @return The corrected coordinates.
374  */
375 Vector RS_Snapper::restrictOrthogonal(Vector coord)
376 {
377         Vector rz = graphicView->getRelativeZero();
378         Vector ret(coord);
379
380         Vector retx = Vector(rz.x, ret.y);
381         Vector rety = Vector(ret.x, rz.y);
382
383         if (retx.distanceTo(ret) > rety.distanceTo(ret))
384                 ret = rety;
385         else
386                 ret = retx;
387
388         return ret;
389 }
390
391 /**
392  * 'Corrects' the given coordinates to 0, 180 degrees relative to
393  * the current relative zero point.
394  *
395  * @param coord The uncorrected coordinates.
396  * @return The corrected coordinates.
397  */
398 Vector RS_Snapper::restrictHorizontal(Vector coord)
399 {
400         Vector rz = graphicView->getRelativeZero();
401
402         return Vector(coord.x, rz.y);
403 }
404
405 /**
406  * 'Corrects' the given coordinates to 90, 270 degrees relative to
407  * the current relative zero point.
408  *
409  * @param coord The uncorrected coordinates.
410  * @return The corrected coordinates.
411  */
412 Vector RS_Snapper::restrictVertical(Vector coord)
413 {
414         Vector rz = graphicView->getRelativeZero();
415
416         return Vector(rz.x, coord.y);
417 }
418
419 /**
420  * Catches an entity which is close to the given position 'pos'.
421  *
422  * @param pos A graphic coordinate.
423  * @param level The level of resolving for iterating through the entity
424  *        container
425  * @return Pointer to the entity or NULL.
426  */
427 RS_Entity * RS_Snapper::catchEntity(const Vector& pos, RS2::ResolveLevel level)
428 {
429         RS_DEBUG->print("RS_Snapper::catchEntity");
430
431         // set default distance for points inside solids
432         double dist = graphicView->toGraphDX(snapRange) * 0.9;
433
434         RS_Entity * entity = container->getNearestEntity(pos, &dist, level);
435
436         int idx = -1;
437
438         if (entity && entity->getParent())
439                 idx = entity->getParent()->findEntity(entity);
440
441         if (entity && dist <= graphicView->toGraphDX(snapRange))
442         {
443                 // highlight:
444                 RS_DEBUG->print("RS_Snapper::catchEntity: found: %d", idx);
445                 return entity;
446         }
447         else
448         {
449                 RS_DEBUG->print("RS_Snapper::catchEntity: not found");
450                 return NULL;
451         }
452
453         RS_DEBUG->print("RS_Snapper::catchEntity: OK");
454 }
455
456 /**
457  * Catches an entity which is close to the mouse cursor.
458  *
459  * @param e A mouse event.
460  * @param level The level of resolving for iterating through the entity
461  *        container
462  * @return Pointer to the entity or NULL.
463  */
464 RS_Entity * RS_Snapper::catchEntity(QMouseEvent * e, RS2::ResolveLevel level)
465 {
466     return catchEntity(Vector(graphicView->toGraphX(e->x()),
467                 graphicView->toGraphY(e->y())), level);
468 }
469
470 /**
471  * Suspends this snapper while another action takes place.
472  */
473 /*virtual*/ void RS_Snapper::suspend()
474 {
475 #warning "!!! This may need to have SetVisibility() called !!!"
476 //      deleteSnapper();
477         snapSpot = snapCoord = Vector(false);
478 }
479
480 /**
481  * Resumes this snapper after it has been suspended.
482  */
483 /*virtual*/ void RS_Snapper::resume()
484 {
485 #warning "!!! This may need to have SetVisibility() called !!!"
486 //      drawSnapper();
487 }
488
489 /**
490  * Hides the snapper options.
491  */
492 /*virtual*/ void RS_Snapper::hideOptions()
493 {
494         if (snapMode == RS2::SnapDist && RS_DIALOGFACTORY)
495                 RS_DIALOGFACTORY->requestSnapDistOptions(distance, false);
496 }
497
498 /**
499  * Shows the snapper options.
500  */
501 /*virtual*/ void RS_Snapper::showOptions()
502 {
503         if (snapMode == RS2::SnapDist && RS_DIALOGFACTORY)
504                 RS_DIALOGFACTORY->requestSnapDistOptions(distance, true);
505 }
506
507 void RS_Snapper::SetVisible(bool visibility/*= true*/)
508 {
509         visible = visibility;
510 }
511
512 bool RS_Snapper::Visible(void)
513 {
514         return visible;
515 }
516
517 void RS_Snapper::Draw(GraphicView * view, PaintInterface * painter)
518 {
519         if (finished || !snapSpot.valid)
520                 return;
521
522 //hm, I don't like graphicView kicking around in here, especially since it now
523 //lives inside GraphicView... How to !!! FIX !!!?
524 //We'll pass it in for now...
525         if (snapCoord.valid)
526         {
527                 // snap point
528                 painter->setPen(RS_Pen(RS_Color(0, 127, 255), RS2::Width00, RS2::DashLine));
529                 painter->drawCircle(view->toGui(snapCoord), 4);
530
531                 // crosshairs
532                 if (showCrosshairs)
533                 {
534                         painter->setPen(RS_Pen(RS_Color(0, 255, 255), RS2::Width00, RS2::DashLine));
535                         painter->drawLine(Vector(0, view->toGuiY(snapCoord.y)),
536                                 Vector(view->getWidth(), view->toGuiY(snapCoord.y)));
537                         painter->drawLine(Vector(view->toGuiX(snapCoord.x), 0),
538                                 Vector(view->toGuiX(snapCoord.x), view->getHeight()));
539                 }
540         }
541
542         if (snapCoord.valid && snapCoord != snapSpot)
543         {
544                 painter->drawLine(view->toGui(snapSpot) + Vector(-5, 0),
545                         view->toGui(snapSpot) + Vector(-1, 4));
546                 painter->drawLine(view->toGui(snapSpot) + Vector(0, 5),
547                         view->toGui(snapSpot) + Vector(4, 1));
548                 painter->drawLine(view->toGui(snapSpot) + Vector(5, 0),
549                         view->toGui(snapSpot) + Vector(1, -4));
550                 painter->drawLine(view->toGui(snapSpot) + Vector(0, -5),
551                         view->toGui(snapSpot) + Vector(-4, -1));
552         }
553 }