Code

switch to using shape_editor, instead of separate knotholders and listeners; fixes...
[inkscape.git] / src / vanishing-point.cpp
1 #define __VANISHING_POINT_C__
3 /*
4  * Vanishing point for 3D perspectives
5  *
6  * Authors:
7  *   bulia byak <buliabyak@users.sf.net>
8  *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
9  *   Maximilian Albert <Anhalter42@gmx.de>
10  *
11  * Copyright (C) 2005-2007 authors
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #include <glibmm/i18n.h>
18 #include "vanishing-point.h"
19 #include "desktop-handles.h"
20 #include "desktop.h"
21 #include "event-context.h"
22 #include "xml/repr.h"
23 #include "perspective-line.h"
24 #include "shape-editor.h"
26 #include "knotholder.h" // FIXME: can we avoid direct access to KnotHolder::update_knots?
28 namespace Box3D {
30 #define VP_KNOT_COLOR_NORMAL 0xffffff00
31 #define VP_KNOT_COLOR_SELECTED 0x0000ff00
33 #define VP_LINE_COLOR_FILL 0x0000ff7f
34 #define VP_LINE_COLOR_STROKE_X 0xff00007f
35 #define VP_LINE_COLOR_STROKE_Y 0x0000ff7f
36 #define VP_LINE_COLOR_STROKE_Z 0xffff007f
38 // screen pixels between knots when they snap:
39 #define SNAP_DIST 5
41 // absolute distance between gradient points for them to become a single dragger when the drag is created:
42 #define MERGE_DIST 0.1
44 // knot shapes corresponding to GrPointType enum
45 SPKnotShapeType vp_knot_shapes [] = {
46         SP_KNOT_SHAPE_SQUARE, // VP_FINITE
47         SP_KNOT_SHAPE_CIRCLE  //VP_INFINITE
48 };
50 static void
51 vp_drag_sel_changed(Inkscape::Selection */*selection*/, gpointer data)
52 {
53     VPDrag *drag = (VPDrag *) data;
54     drag->updateDraggers();
55     drag->updateLines();
56     drag->updateBoxReprs();
57 }
59 static void
60 vp_drag_sel_modified (Inkscape::Selection */*selection*/, guint /*flags*/, gpointer data)
61 {
62     VPDrag *drag = (VPDrag *) data;
63     drag->updateLines ();
64     //drag->updateBoxReprs();
65     drag->updateBoxHandles (); // FIXME: Only update the handles of boxes on this dragger (not on all)
66     drag->updateDraggers ();
67 }
69 static bool
70 have_VPs_of_same_perspective (VPDragger *dr1, VPDragger *dr2)
71 {
72     for (std::list<VanishingPoint>::iterator i = dr1->vps.begin(); i != dr1->vps.end(); ++i) {
73         if (dr2->hasPerspective ((*i).get_perspective())) {
74             return true;
75         }
76     }
77     return false;
78 }
80 static void
81 vp_knot_moved_handler (SPKnot */*knot*/, Geom::Point const *ppointer, guint state, gpointer data)
82 {
83     VPDragger *dragger = (VPDragger *) data;
84     VPDrag *drag = dragger->parent;
86     Geom::Point p = *ppointer;
88     // FIXME: take from prefs
89     double snap_dist = SNAP_DIST / inkscape_active_desktop()->current_zoom();
91     /*
92      * We use dragging_started to indicate if we have already checked for the need to split Draggers up.
93      * This only has the purpose of avoiding costly checks in the routine below.
94      */
95     if (!dragger->dragging_started && (state & GDK_SHIFT_MASK)) {
96         /* with Shift; if there is more than one box linked to this VP
97            we need to split it and create a new perspective */
98         if (dragger->numberOfBoxes() > 1) { // FIXME: Don't do anything if *all* boxes of a VP are selected
99             std::set<VanishingPoint*, less_ptr> sel_vps = dragger->VPsOfSelectedBoxes();
101             std::list<SPBox3D *> sel_boxes;
102             for (std::set<VanishingPoint*, less_ptr>::iterator vp = sel_vps.begin(); vp != sel_vps.end(); ++vp) {
103                 // for each VP that has selected boxes:
104                 Persp3D *old_persp = (*vp)->get_perspective();
105                 sel_boxes = (*vp)->selectedBoxes(sp_desktop_selection(inkscape_active_desktop()));
107                 // we create a new perspective ...
108                 Persp3D *new_persp = persp3d_create_xml_element (dragger->parent->document, old_persp);
110                 /* ... unlink the boxes from the old one and
111                    FIXME: We need to unlink the _un_selected boxes of each VP so that
112                           the correct boxes are kept with the VP being moved */
113                 std::list<SPBox3D *> bx_lst = persp3d_list_of_boxes(old_persp);
114                 for (std::list<SPBox3D *>::iterator i = bx_lst.begin(); i != bx_lst.end(); ++i) {
115                     if (std::find(sel_boxes.begin(), sel_boxes.end(), *i) == sel_boxes.end()) {
116                         /* if a box in the VP is unselected, move it to the
117                            newly created perspective so that it doesn't get dragged **/
118                         box3d_switch_perspectives(*i, old_persp, new_persp);
119                     }
120                 }
121             }
122             // FIXME: Do we need to create a new dragger as well?
123             dragger->updateZOrders ();
124             sp_document_done (sp_desktop_document (inkscape_active_desktop()), SP_VERB_CONTEXT_3DBOX,
125                               _("Split vanishing points"));
126             return;
127         }
128     }
130     if (!(state & GDK_SHIFT_MASK)) {
131         // without Shift; see if we need to snap to another dragger
132         for (GList *di = dragger->parent->draggers; di != NULL; di = di->next) {
133             VPDragger *d_new = (VPDragger *) di->data;
134             if ((d_new != dragger) && (Geom::L2 (d_new->point - p) < snap_dist)) {
135                 if (have_VPs_of_same_perspective (dragger, d_new)) {
136                     // this would result in degenerate boxes, which we disallow for the time being
137                     continue;
138                 }
140                 // update positions ... (this is needed so that the perspectives are detected as identical)
141                 // FIXME: This is called a bit too often, isn't it?
142                 for (std::list<VanishingPoint>::iterator j = dragger->vps.begin(); j != dragger->vps.end(); ++j) {
143                     (*j).set_pos(d_new->point);
144                 }
146                 // ... join lists of VPs ...
147                 d_new->vps.merge(dragger->vps);
149                 // ... delete old dragger ...
150                 drag->draggers = g_list_remove (drag->draggers, dragger);
151                 delete dragger;
152                 dragger = NULL;
154                 // ... and merge any duplicate perspectives
155                 d_new->mergePerspectives();
157                 // TODO: Update the new merged dragger
158                 d_new->updateTip();
160                 d_new->parent->updateBoxDisplays (); // FIXME: Only update boxes in current dragger!
161                 d_new->updateZOrders ();
163                 drag->updateLines ();
165                 // TODO: Undo machinery; this doesn't work yet because perspectives must be created and
166                 //       deleted according to changes in the svg representation, not based on any user input
167                 //       as is currently the case.
169                 sp_document_done (sp_desktop_document (inkscape_active_desktop()), SP_VERB_CONTEXT_3DBOX,
170                                   _("Merge vanishing points"));
172                 return;
173             }
174         }
175     }
178     dragger->point = p; // FIXME: Brauchen wir dragger->point überhaupt?
180     dragger->updateVPs(p);
181     dragger->updateBoxDisplays();
182     dragger->parent->updateBoxHandles (); // FIXME: Only update the handles of boxes on this dragger (not on all)
183     dragger->updateZOrders();
185     drag->updateLines();
187     dragger->dragging_started = true;
190 void
191 vp_knot_grabbed_handler (SPKnot */*knot*/, unsigned int /*state*/, gpointer data)
193     VPDragger *dragger = (VPDragger *) data;
194     VPDrag *drag = dragger->parent;
196     drag->dragging = true;
199 static void
200 vp_knot_ungrabbed_handler (SPKnot *knot, guint /*state*/, gpointer data)
202     VPDragger *dragger = (VPDragger *) data;
204     dragger->point_original = dragger->point = knot->pos;
206     dragger->dragging_started = false;
208     for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
209         (*i).set_pos (knot->pos);
210         (*i).updateBoxReprs();
211         (*i).updatePerspRepr();
212     }
214     dragger->parent->updateDraggers ();
215     dragger->parent->updateLines ();
216     dragger->parent->updateBoxHandles ();
218     // TODO: Update box's paths and svg representation
220     dragger->parent->dragging = false;
222     // TODO: Undo machinery!!
223     g_return_if_fail (dragger->parent);
224     g_return_if_fail (dragger->parent->document);
225     sp_document_done(dragger->parent->document, SP_VERB_CONTEXT_3DBOX,
226                      _("3D box: Move vanishing point"));
229 unsigned int VanishingPoint::global_counter = 0;
231 // FIXME: Rename to something more meaningful!
232 void
233 VanishingPoint::set_pos(Proj::Pt2 const &pt) {
234     g_return_if_fail (_persp);
235     _persp->tmat.set_image_pt (_axis, pt);
238 std::list<SPBox3D *>
239 VanishingPoint::selectedBoxes(Inkscape::Selection *sel) {
240     std::list<SPBox3D *> sel_boxes;
241     for (GSList const* i = sel->itemList(); i != NULL; i = i->next) {
242         if (!SP_IS_BOX3D(i->data))
243             continue;
244         SPBox3D *box = SP_BOX3D(i->data);
245         if (this->hasBox(box)) {
246             sel_boxes.push_back (box);
247         }
248     }
249     return sel_boxes;
252 VPDragger::VPDragger(VPDrag *parent, Geom::Point p, VanishingPoint &vp)
254     this->parent = parent;
256     this->point = p;
257     this->point_original = p;
259     this->dragging_started = false;
261     if (vp.is_finite()) {
262         // create the knot
263         this->knot = sp_knot_new (inkscape_active_desktop(), NULL);
264         this->knot->setMode(SP_KNOT_MODE_XOR);
265         this->knot->setFill(VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL);
266         this->knot->setStroke(0x000000ff, 0x000000ff, 0x000000ff);
267         sp_knot_update_ctrl(this->knot);
269         // move knot to the given point
270         sp_knot_set_position (this->knot, this->point, SP_KNOT_STATE_NORMAL);
271         sp_knot_show (this->knot);
273         // connect knot's signals
274         g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (vp_knot_moved_handler), this);
275         g_signal_connect (G_OBJECT (this->knot), "grabbed", G_CALLBACK (vp_knot_grabbed_handler), this);
276         g_signal_connect (G_OBJECT (this->knot), "ungrabbed", G_CALLBACK (vp_knot_ungrabbed_handler), this);
278         // add the initial VP (which may be NULL!)
279         this->addVP (vp);
280     }
283 VPDragger::~VPDragger()
285     // disconnect signals
286     g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_moved_handler), this);
287     g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_grabbed_handler), this);
288     g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_ungrabbed_handler), this);
289     /* unref should call destroy */
290     g_object_unref (G_OBJECT (this->knot));
293 /**
294 Updates the statusbar tip of the dragger knot, based on its draggables
295  */
296 void
297 VPDragger::updateTip ()
299     if (this->knot && this->knot->tip) {
300         g_free (this->knot->tip);
301         this->knot->tip = NULL;
302     }
304     guint num = this->numberOfBoxes();
305     if (this->vps.size() == 1) {
306         if (this->vps.front().is_finite()) {
307             this->knot->tip = g_strdup_printf (ngettext("<b>Finite</b> vanishing point shared by <b>%d</b> box",
308                                                         "<b>Finite</b> vanishing point shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
309                                                         num),
310                                                num);
311         } else {
312             // This won't make sense any more when infinite VPs are not shown on the canvas,
313             // but currently we update the status message anyway
314             this->knot->tip = g_strdup_printf (ngettext("<b>Infinite</b> vanishing point shared by <b>%d</b> box",
315                                                         "<b>Infinite</b> vanishing point shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
316                                                         num),
317                                                num);
318         }
319     } else {
320         int length = this->vps.size();
321         char *desc1 = g_strdup_printf ("Collection of <b>%d</b> vanishing points ", length);
322         char *desc2 = g_strdup_printf (ngettext("shared by <b>%d</b> box; drag with <b>Shift</b> to separate selected box(es)",
323                                                 "shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
324                                                 num),
325                                        num);
326         this->knot->tip = g_strconcat(desc1, desc2, NULL);
327         g_free (desc1);
328         g_free (desc2);
329     }
332 /**
333  * Adds a vanishing point to the dragger (also updates the position if necessary);
334  * the perspective is stored separately, too, for efficiency in updating boxes.
335  */
336 void
337 VPDragger::addVP (VanishingPoint &vp, bool update_pos)
339     if (!vp.is_finite() || std::find (vps.begin(), vps.end(), vp) != vps.end()) {
340         // don't add infinite VPs; don't add the same VP twice
341         return;
342     }
344     if (update_pos) {
345         vp.set_pos (this->point);
346     }
347     this->vps.push_front (vp);
349     this->updateTip();
352 void
353 VPDragger::removeVP (VanishingPoint const &vp)
355     std::list<VanishingPoint>::iterator i = std::find (this->vps.begin(), this->vps.end(), vp);
356     if (i != this->vps.end()) {
357         this->vps.erase (i);
358     }
359     this->updateTip();
362 VanishingPoint *
363 VPDragger::findVPWithBox (SPBox3D *box) {
364     for (std::list<VanishingPoint>::iterator vp = vps.begin(); vp != vps.end(); ++vp) {
365         if ((*vp).hasBox(box)) {
366             return &(*vp);
367         }
368     }
369     return NULL;
372 std::set<VanishingPoint*, less_ptr>
373 VPDragger::VPsOfSelectedBoxes() {
374     std::set<VanishingPoint*, less_ptr> sel_vps;
375     VanishingPoint *vp;
376     // FIXME: Should we take the selection from the parent VPDrag? I guess it shouldn't make a difference.
377     Inkscape::Selection *sel = sp_desktop_selection(inkscape_active_desktop());
378     for (GSList const* i = sel->itemList(); i != NULL; i = i->next) {
379         if (!SP_IS_BOX3D(i->data))
380             continue;
381         SPBox3D *box = SP_BOX3D(i->data);
382         vp = this->findVPWithBox(box);
383         if (vp) {
384             sel_vps.insert (vp);
385         }
386     }
387     return sel_vps;
390 guint
391 VPDragger::numberOfBoxes ()
393     guint num = 0;
394     for (std::list<VanishingPoint>::iterator vp = vps.begin(); vp != vps.end(); ++vp) {
395         num += (*vp).numberOfBoxes();
396     }
397     return num;
400 bool
401 VPDragger::hasPerspective (const Persp3D *persp)
403     for (std::list<VanishingPoint>::iterator i = vps.begin(); i != vps.end(); ++i) {
404         if (persp3d_perspectives_coincide(persp, (*i).get_perspective())) {
405             return true;
406         }
407     }
408     return false;
411 void
412 VPDragger::mergePerspectives ()
414     Persp3D *persp1, *persp2;
415     for (std::list<VanishingPoint>::iterator i = vps.begin(); i != vps.end(); ++i) {
416         persp1 = (*i).get_perspective();
417         for (std::list<VanishingPoint>::iterator j = i; j != vps.end(); ++j) {
418             persp2 = (*j).get_perspective();
419             if (persp1 == persp2) {
420                 /* don't merge a perspective with itself */
421                 continue;
422             }
423             if (persp3d_perspectives_coincide(persp1,persp2)) {
424                 /* if perspectives coincide but are not the same, merge them */
425                 persp3d_absorb(persp1, persp2);
427                 this->parent->swap_perspectives_of_VPs(persp2, persp1);
429                 SP_OBJECT(persp2)->deleteObject(false);
430             }
431         }
432     }
435 void
436 VPDragger::updateBoxDisplays ()
438     for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
439         (*i).updateBoxDisplays();
440     }
443 void
444 VPDragger::updateVPs (Geom::Point const &pt)
446     for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
447         (*i).set_pos (pt);
448     }
451 void
452 VPDragger::updateZOrders ()
454     for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
455         persp3d_update_z_orders((*i).get_perspective());
456     }
459 void
460 VPDragger::printVPs() {
461     g_print ("VPDragger at position (%f, %f):\n", point[Geom::X], point[Geom::Y]);
462     for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
463         g_print ("    VP %s\n", (*i).axisString());
464     }
467 VPDrag::VPDrag (SPDocument *document)
469     this->document = document;
470     this->selection = sp_desktop_selection(inkscape_active_desktop());
472     this->draggers = NULL;
473     this->lines = NULL;
474     this->show_lines = true;
475     this->front_or_rear_lines = 0x1;
477     this->dragging = false;
479     this->sel_changed_connection = this->selection->connectChanged(
480         sigc::bind (
481             sigc::ptr_fun(&vp_drag_sel_changed),
482             (gpointer)this )
484         );
485     this->sel_modified_connection = this->selection->connectModified(
486         sigc::bind(
487             sigc::ptr_fun(&vp_drag_sel_modified),
488             (gpointer)this )
489         );
491     this->updateDraggers ();
492     this->updateLines ();
495 VPDrag::~VPDrag()
497     this->sel_changed_connection.disconnect();
498     this->sel_modified_connection.disconnect();
500     for (GList *l = this->draggers; l != NULL; l = l->next) {
501         delete ((VPDragger *) l->data);
502     }
503     g_list_free (this->draggers);
504     this->draggers = NULL;
506     for (GSList const *i = this->lines; i != NULL; i = i->next) {
507         gtk_object_destroy( GTK_OBJECT (i->data));
508     }
509     g_slist_free (this->lines);
510     this->lines = NULL;
513 /**
514  * Select the dragger that has the given VP.
515  */
516 VPDragger *
517 VPDrag::getDraggerFor (VanishingPoint const &vp)
519     for (GList const* i = this->draggers; i != NULL; i = i->next) {
520         VPDragger *dragger = (VPDragger *) i->data;
521         for (std::list<VanishingPoint>::iterator j = dragger->vps.begin(); j != dragger->vps.end(); ++j) {
522             // TODO: Should we compare the pointers or the VPs themselves!?!?!?!
523             if (*j == vp) {
524                 return (dragger);
525             }
526         }
527     }
528     return NULL;
531 void
532 VPDrag::printDraggers ()
534     g_print ("=== VPDrag info: =================================\n");
535     for (GList const* i = this->draggers; i != NULL; i = i->next) {
536         ((VPDragger *) i->data)->printVPs();
537         g_print ("========\n");
538     }
539     g_print ("=================================================\n");
542 /**
543  * Regenerates the draggers list from the current selection; is called when selection is changed or modified
544  */
545 void
546 VPDrag::updateDraggers ()
548     if (this->dragging)
549         return;
550     // delete old draggers
551     for (GList const* i = this->draggers; i != NULL; i = i->next) {
552         delete ((VPDragger *) i->data);
553     }
554     g_list_free (this->draggers);
555     this->draggers = NULL;
557     g_return_if_fail (this->selection != NULL);
559     for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
560         SPItem *item = SP_ITEM(i->data);
561         if (!SP_IS_BOX3D (item)) continue;
562         SPBox3D *box = SP_BOX3D (item);
564         VanishingPoint vp;
565         for (int i = 0; i < 3; ++i) {
566             vp.set(box3d_get_perspective(box), Proj::axes[i]);
567             addDragger (vp);
568         }
569     }
572 /**
573 Regenerates the lines list from the current selection; is called on each move
574 of a dragger, so that lines are always in sync with the actual perspective
575 */
576 void
577 VPDrag::updateLines ()
579     // delete old lines
580     for (GSList const *i = this->lines; i != NULL; i = i->next) {
581         gtk_object_destroy( GTK_OBJECT (i->data));
582     }
583     g_slist_free (this->lines);
584     this->lines = NULL;
586     // do nothing if perspective lines are currently disabled
587     if (this->show_lines == 0) return;
589     g_return_if_fail (this->selection != NULL);
591     for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
592         if (!SP_IS_BOX3D(i->data)) continue;
593         SPBox3D *box = SP_BOX3D (i->data);
595         this->drawLinesForFace (box, Proj::X);
596         this->drawLinesForFace (box, Proj::Y);
597         this->drawLinesForFace (box, Proj::Z);
598     }
601 void
602 VPDrag::updateBoxHandles ()
604     // FIXME: Is there a way to update the knots without accessing the
605     //        (previously) statically linked function KnotHolder::update_knots?
607     GSList *sel = (GSList *) selection->itemList();
608     if (!sel)
609         return; // no selection
611     if (g_slist_length (sel) > 1) {
612         // Currently we only show handles if a single box is selected
613         return;
614     }
616     SPEventContext *ec = inkscape_active_event_context();
617     g_assert (ec != NULL);
618     if (ec->shape_editor != NULL) {
619         ec->shape_editor->update_knotholder();
620     }
623 void
624 VPDrag::updateBoxReprs ()
626     for (GList *i = this->draggers; i != NULL; i = i->next) {
627         VPDragger *dragger = (VPDragger *) i->data;
628         for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
629             (*i).updateBoxReprs();
630         }
631     }
634 void
635 VPDrag::updateBoxDisplays ()
637     for (GList *i = this->draggers; i != NULL; i = i->next) {
638         VPDragger *dragger = (VPDragger *) i->data;
639         for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
640             (*i).updateBoxDisplays();
641         }
642     }
646 /**
647  * Depending on the value of all_lines, draw the front and/or rear perspective lines starting from the given corners.
648  */
649 void
650 VPDrag::drawLinesForFace (const SPBox3D *box, Proj::Axis axis) //, guint corner1, guint corner2, guint corner3, guint corner4)
652     guint color;
653     switch (axis) {
654         // TODO: Make color selectable by user
655         case Proj::X: color = VP_LINE_COLOR_STROKE_X; break;
656         case Proj::Y: color = VP_LINE_COLOR_STROKE_Y; break;
657         case Proj::Z: color = VP_LINE_COLOR_STROKE_Z; break;
658         default: g_assert_not_reached();
659     }
661     Geom::Point corner1, corner2, corner3, corner4;
662     box3d_corners_for_PLs (box, axis, corner1, corner2, corner3, corner4);
664     g_return_if_fail (box3d_get_perspective(box));
665     Proj::Pt2 vp = persp3d_get_VP (box3d_get_perspective(box), axis);
666     if (vp.is_finite()) {
667         // draw perspective lines for finite VPs
668         Geom::Point pt = vp.affine();
669         if (this->front_or_rear_lines & 0x1) {
670             // draw 'front' perspective lines
671             this->addLine (corner1, pt, color);
672             this->addLine (corner2, pt, color);
673         }
674         if (this->front_or_rear_lines & 0x2) {
675             // draw 'rear' perspective lines
676             this->addLine (corner3, pt, color);
677             this->addLine (corner4, pt, color);
678         }
679     } else {
680         // draw perspective lines for infinite VPs
681         boost::optional<Geom::Point> pt1, pt2, pt3, pt4;
682         Persp3D *persp = box3d_get_perspective(box);
683         SPDesktop *desktop = inkscape_active_desktop (); // FIXME: Store the desktop in VPDrag
684         Box3D::PerspectiveLine pl (corner1, axis, persp);
685         pt1 = pl.intersection_with_viewbox(desktop);
687         pl = Box3D::PerspectiveLine (corner2, axis, persp);
688         pt2 = pl.intersection_with_viewbox(desktop);
690         pl = Box3D::PerspectiveLine (corner3, axis, persp);
691         pt3 = pl.intersection_with_viewbox(desktop);
693         pl = Box3D::PerspectiveLine (corner4, axis, persp);
694         pt4 = pl.intersection_with_viewbox(desktop);
696         if (!pt1 || !pt2 || !pt3 || !pt4) {
697             // some perspective lines s are outside the canvas; currently we don't draw any of them
698             return;
699         }
700         if (this->front_or_rear_lines & 0x1) {
701             // draw 'front' perspective lines
702             this->addLine (corner1, *pt1, color);
703             this->addLine (corner2, *pt2, color);
704         }
705         if (this->front_or_rear_lines & 0x2) {
706             // draw 'rear' perspective lines
707             this->addLine (corner3, *pt3, color);
708             this->addLine (corner4, *pt4, color);
709         }
710     }
713 /**
714  * If there already exists a dragger within MERGE_DIST of p, add the VP to it;
715  * otherwise create new dragger and add it to draggers list
716  * We also store the corresponding perspective in case it is not already present.
717  */
718 void
719 VPDrag::addDragger (VanishingPoint &vp)
721     if (!vp.is_finite()) {
722         // don't create draggers for infinite vanishing points
723         return;
724     }
725     Geom::Point p = vp.get_pos();
727     for (GList *i = this->draggers; i != NULL; i = i->next) {
728         VPDragger *dragger = (VPDragger *) i->data;
729         if (Geom::L2 (dragger->point - p) < MERGE_DIST) {
730             // distance is small, merge this draggable into dragger, no need to create new dragger
731             dragger->addVP (vp);
732             return;
733         }
734     }
736     VPDragger *new_dragger = new VPDragger(this, p, vp);
737     // fixme: draggers should be added AFTER the last one: this way tabbing through them will be from begin to end.
738     this->draggers = g_list_append (this->draggers, new_dragger);
741 void
742 VPDrag::swap_perspectives_of_VPs(Persp3D *persp2, Persp3D *persp1)
744     // iterate over all VP in all draggers and replace persp2 with persp1
745     for (GList *i = this->draggers; i != NULL; i = i->next) {
746         for (std::list<VanishingPoint>::iterator j = ((VPDragger *) (i->data))->vps.begin();
747              j != ((VPDragger *) (i->data))->vps.end(); ++j) {
748             if ((*j).get_perspective() == persp2) {
749                 (*j).set_perspective(persp1);
750             }
751         }
752     }
755 /**
756 Create a line from p1 to p2 and add it to the lines list
757  */
758 void
759 VPDrag::addLine (Geom::Point p1, Geom::Point p2, guint32 rgba)
761     SPCanvasItem *line = sp_canvas_item_new(sp_desktop_controls(inkscape_active_desktop()), SP_TYPE_CTRLLINE, NULL);
762     sp_ctrlline_set_coords(SP_CTRLLINE(line), p1, p2);
763     if (rgba != VP_LINE_COLOR_FILL) // fill is the default, so don't set color for it to speed up redraw
764         sp_ctrlline_set_rgba32 (SP_CTRLLINE(line), rgba);
765     sp_canvas_item_show (line);
766     this->lines = g_slist_append (this->lines, line);
769 } // namespace Box3D
771 /*
772   Local Variables:
773   mode:c++
774   c-file-style:"stroustrup"
775   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
776   indent-tabs-mode:nil
777   fill-column:99
778   End:
779 */
780 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :