]> Shamusworld >> Repos - architektonas/blob - src/base/rs_snapper.cpp
Check in prior to mucking around with preview/snapper rendering...
[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 "settings.h"
24
25 /**
26  * Constructor.
27  */
28 RS_Snapper::RS_Snapper(RS_EntityContainer & c, 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)
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         vec = container->getNearestEndpoint(coord, NULL/*, &keyEntity*/);
216
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
283         return vec;
284 }
285
286 /**
287  * Snaps to the closest center.
288  *
289  * @param coord The mouse coordinate.
290  * @return The coordinates of the point or an invalid vector.
291  */
292 Vector RS_Snapper::snapCenter(Vector coord)
293 {
294         Vector vec(false);
295         vec = container->getNearestCenter(coord, NULL);
296
297         return vec;
298 }
299
300 /**
301  * Snaps to the closest middle.
302  *
303  * @param coord The mouse coordinate.
304  * @return The coordinates of the point or an invalid vector.
305  */
306 Vector RS_Snapper::snapMiddle(Vector coord)
307 {
308         Vector vec(false);
309         vec = container->getNearestMiddle(coord, NULL);
310
311         return vec;
312 }
313
314 /**
315  * Snaps to the closest point with a given distance to the endpoint.
316  *
317  * @param coord The mouse coordinate.
318  * @return The coordinates of the point or an invalid vector.
319  */
320 Vector RS_Snapper::snapDist(Vector coord)
321 {
322         Vector vec(false);
323         vec = container->getNearestDist(distance, coord, NULL);
324
325         return vec;
326 }
327
328 /**
329  * Snaps to the closest intersection point.
330  *
331  * @param coord The mouse coordinate.
332  * @return The coordinates of the point or an invalid vector.
333  */
334 Vector RS_Snapper::snapIntersection(Vector coord)
335 {
336         Vector vec(false);
337         vec = container->getNearestIntersection(coord, NULL);
338
339         return vec;
340 }
341
342 /**
343  * 'Corrects' the given coordinates to 0, 90, 180, 270 degrees relative to
344  * the current relative zero point.
345  *
346  * @param coord The uncorrected coordinates.
347  * @return The corrected coordinates.
348  */
349 Vector RS_Snapper::restrictOrthogonal(Vector coord)
350 {
351         Vector rz = graphicView->getRelativeZero();
352         Vector ret(coord);
353
354         Vector retx = Vector(rz.x, ret.y);
355         Vector rety = Vector(ret.x, rz.y);
356
357         if (retx.distanceTo(ret) > rety.distanceTo(ret))
358                 ret = rety;
359         else
360                 ret = retx;
361
362         return ret;
363 }
364
365 /**
366  * 'Corrects' the given coordinates to 0, 180 degrees relative to
367  * the current relative zero point.
368  *
369  * @param coord The uncorrected coordinates.
370  * @return The corrected coordinates.
371  */
372 Vector RS_Snapper::restrictHorizontal(Vector coord)
373 {
374         Vector rz = graphicView->getRelativeZero();
375         Vector ret = Vector(coord.x, rz.y);
376         return ret;
377 }
378
379 /**
380  * 'Corrects' the given coordinates to 90, 270 degrees relative to
381  * the current relative zero point.
382  *
383  * @param coord The uncorrected coordinates.
384  * @return The corrected coordinates.
385  */
386 Vector RS_Snapper::restrictVertical(Vector coord)
387 {
388         Vector rz = graphicView->getRelativeZero();
389         Vector ret = Vector(rz.x, coord.y);
390         return ret;
391 }
392
393 /**
394  * Catches an entity which is close to the given position 'pos'.
395  *
396  * @param pos A graphic coordinate.
397  * @param level The level of resolving for iterating through the entity
398  *        container
399  * @return Pointer to the entity or NULL.
400  */
401 RS_Entity * RS_Snapper::catchEntity(const Vector& pos, RS2::ResolveLevel level)
402 {
403         RS_DEBUG->print("RS_Snapper::catchEntity");
404
405         // set default distance for points inside solids
406         double dist = graphicView->toGraphDX(snapRange) * 0.9;
407
408         RS_Entity * entity = container->getNearestEntity(pos, &dist, level);
409
410         int idx = -1;
411
412         if (entity && entity->getParent())
413                 idx = entity->getParent()->findEntity(entity);
414
415         if (entity && dist <= graphicView->toGraphDX(snapRange))
416         {
417                 // highlight:
418                 RS_DEBUG->print("RS_Snapper::catchEntity: found: %d", idx);
419                 return entity;
420         }
421         else
422         {
423                 RS_DEBUG->print("RS_Snapper::catchEntity: not found");
424                 return NULL;
425         }
426
427         RS_DEBUG->print("RS_Snapper::catchEntity: OK");
428 }
429
430 /**
431  * Catches an entity which is close to the mouse cursor.
432  *
433  * @param e A mouse event.
434  * @param level The level of resolving for iterating through the entity
435  *        container
436  * @return Pointer to the entity or NULL.
437  */
438 RS_Entity * RS_Snapper::catchEntity(QMouseEvent * e, RS2::ResolveLevel level)
439 {
440     return catchEntity(Vector(graphicView->toGraphX(e->x()),
441                 graphicView->toGraphY(e->y())), level);
442 }
443
444 /**
445  * Suspends this snapper while another action takes place.
446  */
447 void RS_Snapper::suspend()
448 {
449         deleteSnapper();
450         snapSpot = snapCoord = Vector(false);
451 }
452
453 /**
454  * Resumes this snapper after it has been suspended.
455  */
456 void RS_Snapper::resume()
457 {
458         drawSnapper();
459 }
460
461 /**
462  * Hides the snapper options. Default implementation does nothing.
463  */
464 void RS_Snapper::hideOptions()
465 {
466         if (snapMode == RS2::SnapDist && RS_DIALOGFACTORY)
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 && RS_DIALOGFACTORY)
476                 RS_DIALOGFACTORY->requestSnapDistOptions(distance, true);
477 }
478
479 /**
480  * Draws the snapper on the screen.
481  */
482 void RS_Snapper::drawSnapper()
483 {
484 printf("RS_Snapper::drawSnapper(): Using DEPRECATED function!!!\n");
485         if (!visible)
486                 xorSnapper();
487 }
488
489 /**
490  * Deletes the snapper from the screen.
491  */
492 void RS_Snapper::deleteSnapper()
493 {
494 printf("RS_Snapper::deleteSnapper(): Using DEPRECATED function!!!\n");
495         if (visible)
496         {
497                 xorSnapper();
498                 snapSpot = Vector(false);
499                 snapCoord = Vector(false);
500         }
501 }
502
503 /**
504  * Draws / deletes the current snapper spot.
505  */
506 void RS_Snapper::xorSnapper()
507 {
508 //Not completely true...
509 //#warning "!!! xorSnapper() not working AT ALL !!!"
510 #if 0
511         if (!finished && snapSpot.valid)
512         {
513                 RS_Painter * painter = graphicView->createDirectPainter();
514                 painter->setPreviewMode();
515
516                 if (snapCoord.valid)
517                 {
518                         // snap point
519                         painter->drawCircle(graphicView->toGui(snapCoord), 4);
520
521                         // crosshairs:
522                         if (showCrosshairs == true)
523                         {
524                                 painter->setPen(RS_Pen(RS_Color(0, 255, 255), RS2::Width00, RS2::DashLine));
525                                 painter->drawLine(Vector(0, graphicView->toGuiY(snapCoord.y)),
526                                         Vector(graphicView->getWidth(), graphicView->toGuiY(snapCoord.y)));
527                                 painter->drawLine(Vector(graphicView->toGuiX(snapCoord.x), 0),
528                                         Vector(graphicView->toGuiX(snapCoord.x), graphicView->getHeight()));
529                         }
530                 }
531
532                 if (snapCoord.valid && snapCoord != snapSpot)
533                 {
534                         painter->drawLine(graphicView->toGui(snapSpot) + Vector(-5, 0),
535                                 graphicView->toGui(snapSpot) + Vector(-1, 4));
536                         painter->drawLine(graphicView->toGui(snapSpot) + Vector(0, 5),
537                                 graphicView->toGui(snapSpot) + Vector(4, 1));
538                         painter->drawLine(graphicView->toGui(snapSpot) + Vector(5, 0),
539                                 graphicView->toGui(snapSpot) + Vector(1, -4));
540                         painter->drawLine(graphicView->toGui(snapSpot) + Vector(0, -5),
541                                 graphicView->toGui(snapSpot) + Vector(-4, -1));
542                 }
543
544                 graphicView->destroyPainter();
545                 visible = !visible;
546         }
547 #else
548         if (finished || !snapSpot.valid || graphicView == NULL)
549                 return;
550
551         graphicView->SetSnapperDraw(true);
552         graphicView->SetSnapperVars(snapSpot, snapCoord, showCrosshairs);
553 //Apparently, this gets hit anyway by the preview code...
554 //      graphicView->redraw();
555
556         visible = !visible;
557 #endif
558 }
559
560 void RS_Snapper::SetSnapperVisible(bool visibility/*= true*/)
561 {
562         graphicView->SetSnapperDraw(visibility);
563 }
564
565 //Hmm, not sure this is necessary...
566 #warning "!!! Not sure this is necessary... !!!"
567 void RS_Snapper::SetSnapperCoords(Vector snapCoord, Vector snapSpot)
568 {
569         graphicView->SetSnapperVars(snapSpot, snapCoord, showCrosshairs);
570 }