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