]> Shamusworld >> Repos - architektonas/blob - src/base/rs_snapper.cpp
Beginnings of new render path (Snapper)...
[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 "paintintf.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 void RS_Snapper::SetContainer(RS_EntityContainer * c)
83 {
84         container = c;
85 }
86
87 //bleh
88 void RS_Snapper::SetGraphicView(GraphicView * v)
89 {
90         graphicView = v;
91 }
92
93 /**
94  * @return Pointer to the entity which was the key entity for the
95  * last successful snapping action. If the snap mode is "end point"
96  * the key entity is the entity whos end point was caught.
97  * If the snap mode didn't require an entity (e.g. free, grid) this
98  * 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 /**
118 * Sets the snap range in pixels for catchEntity().
119 *
120 * @see catchEntity()
121 */
122 void RS_Snapper::setSnapRange(int r)
123 {
124         snapRange = r;
125 }
126
127 /**
128  * Snap to a coordinate in the drawing using the current snap mode.
129  *
130  * @param e A mouse event.
131  * @return The coordinates of the point or an invalid vector.
132  */
133 Vector RS_Snapper::snapPoint(QMouseEvent * e)
134 {
135         RS_DEBUG->print("RS_Snapper::snapPoint");
136
137 //meh   deleteSnapper();
138         snapSpot = Vector(false);
139
140         if (!e)
141         {
142                 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Snapper::snapPoint: event is NULL");
143                 return snapSpot;
144         }
145
146         Vector mouseCoord = graphicView->toGraph(e->x(), e->y());
147
148         switch (snapMode)
149         {
150         case RS2::SnapFree:
151                 snapSpot = snapFree(mouseCoord);
152                 break;
153
154         case RS2::SnapEndpoint:
155                 snapSpot = snapEndpoint(mouseCoord);
156                 break;
157
158         case RS2::SnapGrid:
159                 snapSpot = snapGrid(mouseCoord);
160                 break;
161
162         case RS2::SnapOnEntity:
163                 snapSpot = snapOnEntity(mouseCoord);
164                 break;
165
166         case RS2::SnapCenter:
167                 snapSpot = snapCenter(mouseCoord);
168                 break;
169
170         case RS2::SnapMiddle:
171                 snapSpot = snapMiddle(mouseCoord);
172                 break;
173
174         case RS2::SnapDist:
175                 snapSpot = snapDist(mouseCoord);
176                 break;
177
178         case RS2::SnapIntersection:
179                 snapSpot = snapIntersection(mouseCoord);
180                 break;
181
182         default:
183                 break;
184         }
185
186         // handle snap restrictions that can be activated in addition
187         //   to the ones above:
188         switch (snapRes)
189         {
190         case RS2::RestrictOrthogonal:
191                 snapCoord = restrictOrthogonal(snapSpot);
192                 break;
193         case RS2::RestrictHorizontal:
194                 snapCoord = restrictHorizontal(snapSpot);
195                 break;
196         case RS2::RestrictVertical:
197                 snapCoord = restrictVertical(snapSpot);
198                 break;
199         default:
200         case RS2::RestrictNothing:
201                 snapCoord = snapSpot;
202                 break;
203         }
204
205 //#warning "!!! THIS IS WHERE THE SNAPPER IS BEING DRAWN... !!!"
206 //      drawSnapper();
207
208         if (RS_DIALOGFACTORY)
209                 RS_DIALOGFACTORY->updateCoordinateWidget(snapCoord, snapCoord - graphicView->getRelativeZero());
210
211         RS_DEBUG->print("RS_Snapper::snapPoint: OK");
212
213         return snapCoord;
214 }
215
216 /**
217  * Snaps to a free coordinate.
218  *
219  * @param coord The mouse coordinate.
220  * @return The coordinates of the point or an invalid vector.
221  */
222 Vector RS_Snapper::snapFree(Vector coord)
223 {
224         keyEntity = NULL;
225         return coord;
226 }
227
228 /**
229  * Snaps to the closest endpoint.
230  *
231  * @param coord The mouse coordinate.
232  * @return The coordinates of the point or an invalid vector.
233  */
234 Vector RS_Snapper::snapEndpoint(Vector coord)
235 {
236         Vector vec(false);
237         vec = container->getNearestEndpoint(coord, NULL/*, &keyEntity*/);
238
239         return vec;
240 }
241
242 /**
243  * Snaps to a grid point.
244  *
245  * @param coord The mouse coordinate.
246  * @return The coordinates of the point or an invalid vector.
247  */
248 Vector RS_Snapper::snapGrid(Vector coord)
249 {
250         RS_DEBUG->print("RS_Snapper::snapGrid begin");
251
252         Vector vec(false);
253         double dist = 0.0;
254
255         RS_Grid * grid = graphicView->getGrid();
256
257         RS_DEBUG->print("RS_Snapper::snapGrid 001");
258
259         if (grid)
260         {
261                 RS_DEBUG->print("RS_Snapper::snapGrid 002");
262                 Vector * pts = grid->getPoints();
263                 RS_DEBUG->print("RS_Snapper::snapGrid 003");
264                 int closest = -1;
265                 dist = 32000.00;
266                 RS_DEBUG->print("RS_Snapper::snapGrid 004");
267
268                 for(int i=0; i<grid->count(); ++i)
269                 {
270                         double d = pts[i].distanceTo(coord);
271
272                         if (d < dist)
273                         {
274                                 closest = i;
275                                 dist = d;
276                         }
277                 }
278
279                 RS_DEBUG->print("RS_Snapper::snapGrid 005");
280
281                 if (closest >= 0)
282                         vec = pts[closest];
283
284                 RS_DEBUG->print("RS_Snapper::snapGrid 006");
285         }
286
287         keyEntity = NULL;
288
289         RS_DEBUG->print("RS_Snapper::snapGrid end");
290
291         return vec;
292 }
293
294 /**
295  * Snaps to a point on an entity.
296  *
297  * @param coord The mouse coordinate.
298  * @return The coordinates of the point or an invalid vector.
299  */
300 Vector RS_Snapper::snapOnEntity(Vector coord)
301 {
302         Vector vec(false);
303         vec = container->getNearestPointOnEntity(coord, true, NULL, &keyEntity);
304
305         return vec;
306 }
307
308 /**
309  * Snaps to the closest center.
310  *
311  * @param coord The mouse coordinate.
312  * @return The coordinates of the point or an invalid vector.
313  */
314 Vector RS_Snapper::snapCenter(Vector coord)
315 {
316         Vector vec(false);
317         vec = container->getNearestCenter(coord, NULL);
318
319         return vec;
320 }
321
322 /**
323  * Snaps to the closest middle.
324  *
325  * @param coord The mouse coordinate.
326  * @return The coordinates of the point or an invalid vector.
327  */
328 Vector RS_Snapper::snapMiddle(Vector coord)
329 {
330         Vector vec(false);
331         vec = container->getNearestMiddle(coord, NULL);
332
333         return vec;
334 }
335
336 /**
337  * Snaps to the closest point with a given distance to the endpoint.
338  *
339  * @param coord The mouse coordinate.
340  * @return The coordinates of the point or an invalid vector.
341  */
342 Vector RS_Snapper::snapDist(Vector coord)
343 {
344         Vector vec(false);
345         vec = container->getNearestDist(distance, coord, NULL);
346
347         return vec;
348 }
349
350 /**
351  * Snaps to the closest intersection point.
352  *
353  * @param coord The mouse coordinate.
354  * @return The coordinates of the point or an invalid vector.
355  */
356 Vector RS_Snapper::snapIntersection(Vector coord)
357 {
358         Vector vec(false);
359         vec = container->getNearestIntersection(coord, NULL);
360
361         return vec;
362 }
363
364 /**
365  * 'Corrects' the given coordinates to 0, 90, 180, 270 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::restrictOrthogonal(Vector coord)
372 {
373         Vector rz = graphicView->getRelativeZero();
374         Vector ret(coord);
375
376         Vector retx = Vector(rz.x, ret.y);
377         Vector rety = Vector(ret.x, rz.y);
378
379         if (retx.distanceTo(ret) > rety.distanceTo(ret))
380                 ret = rety;
381         else
382                 ret = retx;
383
384         return ret;
385 }
386
387 /**
388  * 'Corrects' the given coordinates to 0, 180 degrees relative to
389  * the current relative zero point.
390  *
391  * @param coord The uncorrected coordinates.
392  * @return The corrected coordinates.
393  */
394 Vector RS_Snapper::restrictHorizontal(Vector coord)
395 {
396         Vector rz = graphicView->getRelativeZero();
397 //      Vector ret = Vector(coord.x, rz.y);
398 //      return ret;
399         return Vector(coord.x, rz.y);
400 }
401
402 /**
403  * 'Corrects' the given coordinates to 90, 270 degrees relative to
404  * the current relative zero point.
405  *
406  * @param coord The uncorrected coordinates.
407  * @return The corrected coordinates.
408  */
409 Vector RS_Snapper::restrictVertical(Vector coord)
410 {
411         Vector rz = graphicView->getRelativeZero();
412 //      Vector ret = Vector(rz.x, coord.y);
413 //      return ret;
414         return Vector(rz.x, coord.y);
415 }
416
417 /**
418  * Catches an entity which is close to the given position 'pos'.
419  *
420  * @param pos A graphic coordinate.
421  * @param level The level of resolving for iterating through the entity
422  *        container
423  * @return Pointer to the entity or NULL.
424  */
425 RS_Entity * RS_Snapper::catchEntity(const Vector& pos, RS2::ResolveLevel level)
426 {
427         RS_DEBUG->print("RS_Snapper::catchEntity");
428
429         // set default distance for points inside solids
430         double dist = graphicView->toGraphDX(snapRange) * 0.9;
431
432         RS_Entity * entity = container->getNearestEntity(pos, &dist, level);
433
434         int idx = -1;
435
436         if (entity && entity->getParent())
437                 idx = entity->getParent()->findEntity(entity);
438
439         if (entity && dist <= graphicView->toGraphDX(snapRange))
440         {
441                 // highlight:
442                 RS_DEBUG->print("RS_Snapper::catchEntity: found: %d", idx);
443                 return entity;
444         }
445         else
446         {
447                 RS_DEBUG->print("RS_Snapper::catchEntity: not found");
448                 return NULL;
449         }
450
451         RS_DEBUG->print("RS_Snapper::catchEntity: OK");
452 }
453
454 /**
455  * Catches an entity which is close to the mouse cursor.
456  *
457  * @param e A mouse event.
458  * @param level The level of resolving for iterating through the entity
459  *        container
460  * @return Pointer to the entity or NULL.
461  */
462 RS_Entity * RS_Snapper::catchEntity(QMouseEvent * e, RS2::ResolveLevel level)
463 {
464     return catchEntity(Vector(graphicView->toGraphX(e->x()),
465                 graphicView->toGraphY(e->y())), level);
466 }
467
468 /**
469  * Suspends this snapper while another action takes place.
470  */
471 /*virtual*/ void RS_Snapper::suspend()
472 {
473         deleteSnapper();
474         snapSpot = snapCoord = Vector(false);
475 }
476
477 /**
478  * Resumes this snapper after it has been suspended.
479  */
480 /*virtual*/ void RS_Snapper::resume()
481 {
482         drawSnapper();
483 }
484
485 /**
486  * Hides the snapper options.
487  */
488 /*virtual*/ void RS_Snapper::hideOptions()
489 {
490         if (snapMode == RS2::SnapDist && RS_DIALOGFACTORY)
491                 RS_DIALOGFACTORY->requestSnapDistOptions(distance, false);
492 }
493
494 /**
495  * Shows the snapper options.
496  */
497 /*virtual*/ void RS_Snapper::showOptions()
498 {
499         if (snapMode == RS2::SnapDist && RS_DIALOGFACTORY)
500                 RS_DIALOGFACTORY->requestSnapDistOptions(distance, true);
501 }
502
503 /**
504  * Draws the snapper on the screen.
505  */
506 void RS_Snapper::drawSnapper()
507 {
508 printf("RS_Snapper::drawSnapper(): Using DEPRECATED function!!!\n");
509         if (!visible)
510                 xorSnapper();
511 }
512
513 /**
514  * Deletes the snapper from the screen.
515  */
516 void RS_Snapper::deleteSnapper()
517 {
518 printf("RS_Snapper::deleteSnapper(): Using DEPRECATED function!!!\n");
519         if (visible)
520         {
521                 xorSnapper();
522                 snapSpot = Vector(false);
523                 snapCoord = Vector(false);
524         }
525 }
526
527 /**
528  * Draws / deletes the current snapper spot.
529  */
530 void RS_Snapper::xorSnapper()
531 {
532 //Not completely true...
533 //#warning "!!! xorSnapper() not working AT ALL !!!"
534 #if 0
535         if (!finished && snapSpot.valid)
536         {
537                 RS_Painter * painter = graphicView->createDirectPainter();
538                 painter->setPreviewMode();
539
540                 if (snapCoord.valid)
541                 {
542                         // snap point
543                         painter->drawCircle(graphicView->toGui(snapCoord), 4);
544
545                         // crosshairs:
546                         if (showCrosshairs == true)
547                         {
548                                 painter->setPen(RS_Pen(RS_Color(0, 255, 255), RS2::Width00, RS2::DashLine));
549                                 painter->drawLine(Vector(0, graphicView->toGuiY(snapCoord.y)),
550                                         Vector(graphicView->getWidth(), graphicView->toGuiY(snapCoord.y)));
551                                 painter->drawLine(Vector(graphicView->toGuiX(snapCoord.x), 0),
552                                         Vector(graphicView->toGuiX(snapCoord.x), graphicView->getHeight()));
553                         }
554                 }
555
556                 if (snapCoord.valid && snapCoord != snapSpot)
557                 {
558                         painter->drawLine(graphicView->toGui(snapSpot) + Vector(-5, 0),
559                                 graphicView->toGui(snapSpot) + Vector(-1, 4));
560                         painter->drawLine(graphicView->toGui(snapSpot) + Vector(0, 5),
561                                 graphicView->toGui(snapSpot) + Vector(4, 1));
562                         painter->drawLine(graphicView->toGui(snapSpot) + Vector(5, 0),
563                                 graphicView->toGui(snapSpot) + Vector(1, -4));
564                         painter->drawLine(graphicView->toGui(snapSpot) + Vector(0, -5),
565                                 graphicView->toGui(snapSpot) + Vector(-4, -1));
566                 }
567
568                 graphicView->destroyPainter();
569                 visible = !visible;
570         }
571 #else
572         if (finished || !snapSpot.valid || !graphicView)
573                 return;
574
575         graphicView->SetSnapperDraw(true);
576         graphicView->SetSnapperVars(snapSpot, snapCoord, showCrosshairs);
577 //Apparently, this gets hit anyway by the preview code...
578 //      graphicView->redraw();
579
580         visible = !visible;
581 #endif
582 }
583
584 void RS_Snapper::SetVisible(bool visibility/*= true*/)
585 {
586 //      graphicView->SetSnapperDraw(visibility);
587         visible = visibility;
588 }
589
590 bool RS_Snapper::Visible(void)
591 {
592 //      graphicView->SetSnapperDraw(visibility);
593         return visible;
594 }
595
596 void RS_Snapper::Draw(GraphicView * view, PaintInterface * painter)
597 {
598 //printf("RS_Snapper::Draw()...");
599         if (finished || !snapSpot.valid)
600                 return;
601 //printf("{D}\n");
602
603 //meh   painter->setPreviewMode();
604
605 //hm, I don't like graphicView kicking around in here, especially since it now
606 //lives inside GraphicView... How to !!! FIX !!!?
607 //We'll pass it in for now...
608         if (snapCoord.valid)
609         {
610                 // snap point
611                 painter->drawCircle(view->toGui(snapCoord), 4);
612
613                 // crosshairs:
614                 if (showCrosshairs)
615                 {
616                         painter->setPen(RS_Pen(RS_Color(0, 255, 255), RS2::Width00, RS2::DashLine));
617                         painter->drawLine(Vector(0, view->toGuiY(snapCoord.y)),
618                                 Vector(view->getWidth(), view->toGuiY(snapCoord.y)));
619                         painter->drawLine(Vector(view->toGuiX(snapCoord.x), 0),
620                                 Vector(view->toGuiX(snapCoord.x), view->getHeight()));
621                 }
622         }
623
624         if (snapCoord.valid && snapCoord != snapSpot)
625         {
626                 painter->drawLine(view->toGui(snapSpot) + Vector(-5, 0),
627                         view->toGui(snapSpot) + Vector(-1, 4));
628                 painter->drawLine(view->toGui(snapSpot) + Vector(0, 5),
629                         view->toGui(snapSpot) + Vector(4, 1));
630                 painter->drawLine(view->toGui(snapSpot) + Vector(5, 0),
631                         view->toGui(snapSpot) + Vector(1, -4));
632                 painter->drawLine(view->toGui(snapSpot) + Vector(0, -5),
633                         view->toGui(snapSpot) + Vector(-4, -1));
634         }
635 }