Code

If necessary, split up perspectives when applying transformations to boxes; moreover...
[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"
25 #include "knotholder.h" // FIXME: can we avoid direct access to knotholder_update_knots?
27 namespace Box3D {
29 #define VP_KNOT_COLOR_NORMAL 0xffffff00
30 #define VP_KNOT_COLOR_SELECTED 0x0000ff00
32 #define VP_LINE_COLOR_FILL 0x0000ff7f
33 #define VP_LINE_COLOR_STROKE_X 0xff00007f
34 #define VP_LINE_COLOR_STROKE_Y 0x0000ff7f
35 #define VP_LINE_COLOR_STROKE_Z 0xffff007f
37 // screen pixels between knots when they snap:
38 #define SNAP_DIST 5
40 // absolute distance between gradient points for them to become a single dragger when the drag is created:
41 #define MERGE_DIST 0.1
43 // knot shapes corresponding to GrPointType enum
44 SPKnotShapeType vp_knot_shapes [] = {
45         SP_KNOT_SHAPE_SQUARE, // VP_FINITE
46         SP_KNOT_SHAPE_CIRCLE  //VP_INFINITE
47 };
49 static void
50 vp_drag_sel_changed(Inkscape::Selection *selection, gpointer data)
51 {
52     VPDrag *drag = (VPDrag *) data;
53     drag->updateDraggers();
54     drag->updateLines();
55     drag->updateBoxReprs();
56 }
58 static void
59 vp_drag_sel_modified (Inkscape::Selection *selection, guint flags, gpointer data)
60 {
61     VPDrag *drag = (VPDrag *) data;
62     drag->updateLines ();
63     //drag->updateBoxReprs();
64     drag->updateBoxHandles (); // FIXME: Only update the handles of boxes on this dragger (not on all)
65     drag->updateDraggers ();
66 }
68 static bool
69 have_VPs_of_same_perspective (VPDragger *dr1, VPDragger *dr2)
70 {
71     for (std::list<VanishingPoint>::iterator i = dr1->vps.begin(); i != dr1->vps.end(); ++i) {
72         if (dr2->hasPerspective ((*i).get_perspective())) {
73             return true;
74         }
75     }
76     return false;
77 }
79 static void
80 vp_knot_moved_handler (SPKnot *knot, NR::Point const *ppointer, guint state, gpointer data)
81 {
82     VPDragger *dragger = (VPDragger *) data;
83     VPDrag *drag = dragger->parent;
85     NR::Point p = *ppointer;
87     // FIXME: take from prefs
88     double snap_dist = SNAP_DIST / inkscape_active_desktop()->current_zoom();
90     /*
91      * We use dragging_started to indicate if we have already checked for the need to split Draggers up.
92      * This only has the purpose of avoiding costly checks in the routine below.
93      */
94     if (!dragger->dragging_started && (state & GDK_SHIFT_MASK)) {
95         /* with Shift; if there is more than one box linked to this VP
96            we need to split it and create a new perspective */
97         //g_print ("Number of boxes in dragger: %d\n", dragger->numberOfBoxes());
98         if (dragger->numberOfBoxes() > 1) { // FIXME: Don't do anything if *all* boxes of a VP are selected
99             //g_print ("We need to split the VPDragger\n");
100             std::set<VanishingPoint*, less_ptr> sel_vps = dragger->VPsOfSelectedBoxes();
101             /**
102             g_print ("===== VPs of selected boxes: ===========================\n");
103             for (std::set<VanishingPoint*, less_ptr>::iterator i = sel_vps.begin(); i != sel_vps.end(); ++i) {
104                 (*i)->printPt();
105             }
106             g_print ("========================================================\n");
107             **/
109             std::list<SPBox3D *> sel_boxes;
110             for (std::set<VanishingPoint*, less_ptr>::iterator vp = sel_vps.begin(); vp != sel_vps.end(); ++vp) {
111                 // for each VP that has selected boxes:
112                 Persp3D *old_persp = (*vp)->get_perspective();
113                 sel_boxes = (*vp)->selectedBoxes(sp_desktop_selection(inkscape_active_desktop()));
115                 // we create a new perspective ...
116                 Persp3D *new_persp = persp3d_create_xml_element (dragger->parent->document, old_persp);
118                 /* ... unlink the boxes from the old one and
119                    FIXME: We need to unlink the _un_selected boxes of each VP so that
120                           the correct boxes are kept with the VP being moved */
121                 std::list<SPBox3D *> bx_lst = persp3d_list_of_boxes(old_persp);
122                 for (std::list<SPBox3D *>::iterator i = bx_lst.begin(); i != bx_lst.end(); ++i) {
123                     //g_print ("Iterating over box #%d\n", (*i)->my_counter);
124                     if (std::find(sel_boxes.begin(), sel_boxes.end(), *i) == sel_boxes.end()) {
125                         /* if a box in the VP is unselected, move it to the
126                            newly created perspective so that it doesn't get dragged **/
127                         //g_print ("   switching box #%d to new perspective.\n", (*i)->my_counter);
128                         box3d_switch_perspectives(*i, old_persp, new_persp);
129                     }
130                 }
131             }
132             // FIXME: Do we need to create a new dragger as well?
133             dragger->updateZOrders ();
134             sp_document_done (sp_desktop_document (inkscape_active_desktop()), SP_VERB_CONTEXT_3DBOX,
135                               _("Split vanishing points"));
136             return;
137         }
138     }
140     if (!(state & GDK_SHIFT_MASK)) {
141         // without Shift; see if we need to snap to another dragger
142         for (GList *di = dragger->parent->draggers; di != NULL; di = di->next) {
143             VPDragger *d_new = (VPDragger *) di->data;
144             if ((d_new != dragger) && (NR::L2 (d_new->point - p) < snap_dist)) {
145                 if (have_VPs_of_same_perspective (dragger, d_new)) {
146                     // this would result in degenerate boxes, which we disallow for the time being
147                     continue;
148                 }
150                 // update positions ... (this is needed so that the perspectives are detected as identical)
151                 // FIXME: This is called a bit too often, isn't it?
152                 for (std::list<VanishingPoint>::iterator j = dragger->vps.begin(); j != dragger->vps.end(); ++j) {
153                     (*j).set_pos(d_new->point);
154                 }
156                 // ... join lists of VPs ...
157                 d_new->vps.merge(dragger->vps);
159                 // ... delete old dragger ...
160                 drag->draggers = g_list_remove (drag->draggers, dragger);
161                 delete dragger;
162                 dragger = NULL;
164                 // ... and merge any duplicate perspectives
165                 d_new->mergePerspectives();
166                     
167                 // TODO: Update the new merged dragger
168                 //d_new->updateKnotShape ();
169                 d_new->updateTip();
171                 d_new->parent->updateBoxDisplays (); // FIXME: Only update boxes in current dragger!
172                 d_new->updateZOrders ();
174                 drag->updateLines ();
176                 // TODO: Undo machinery; this doesn't work yet because perspectives must be created and
177                 //       deleted according to changes in the svg representation, not based on any user input
178                 //       as is currently the case.
180                 sp_document_done (sp_desktop_document (inkscape_active_desktop()), SP_VERB_CONTEXT_3DBOX,
181                                   _("Merge vanishing points"));
183                 return;
184             }
185         }
186     }
189     dragger->point = p; // FIXME: Brauchen wir dragger->point überhaupt?
191     dragger->updateVPs(p);
192     dragger->updateBoxDisplays();
193     dragger->parent->updateBoxHandles (); // FIXME: Only update the handles of boxes on this dragger (not on all)
194     dragger->updateZOrders();
196     drag->updateLines();
198     dragger->dragging_started = true;
201 /* helpful for debugging */
202 static void
203 vp_knot_clicked_handler(SPKnot *knot, guint state, gpointer data)
205     VPDragger *dragger = (VPDragger *) data;
206     g_print ("\nVPDragger contains the following VPs: ");
207     for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
208         g_print("%d (%d)  ", (*i).my_counter, (*i).get_perspective()->my_counter);
209     }
210     g_print("\n");
213 void
214 vp_knot_grabbed_handler (SPKnot *knot, unsigned int state, gpointer data)
216     VPDragger *dragger = (VPDragger *) data;
217     VPDrag *drag = dragger->parent;
219     drag->dragging = true;
222 static void
223 vp_knot_ungrabbed_handler (SPKnot *knot, guint state, gpointer data)
225     VPDragger *dragger = (VPDragger *) data;
227     //sp_canvas_end_forced_full_redraws(dragger->parent->desktop->canvas);
229     dragger->point_original = dragger->point = knot->pos;
231     dragger->dragging_started = false;
233     for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
234         (*i).set_pos (knot->pos);
235         (*i).updateBoxReprs();
236         (*i).updatePerspRepr();
237     }
239     dragger->parent->updateDraggers ();
240     //dragger->updateBoxReprs ();
241     dragger->parent->updateLines ();
242     dragger->parent->updateBoxHandles ();
244     // TODO: Update box's paths and svg representation
246     dragger->parent->dragging = false;
248     // TODO: Undo machinery!!
249     g_return_if_fail (dragger->parent);
250     g_return_if_fail (dragger->parent->document);
251     sp_document_done(dragger->parent->document, SP_VERB_CONTEXT_3DBOX,
252                      _("3D box: Move vanishing point"));
255 unsigned int VanishingPoint::global_counter = 0;
257 // FIXME: Rename to something more meaningful!
258 void
259 VanishingPoint::set_pos(Proj::Pt2 const &pt) {
260     g_return_if_fail (_persp);
261     _persp->tmat.set_image_pt (_axis, pt);
264 std::list<SPBox3D *>
265 VanishingPoint::selectedBoxes(Inkscape::Selection *sel) {
266     std::list<SPBox3D *> sel_boxes;
267     for (GSList const* i = sel->itemList(); i != NULL; i = i->next) {
268         if (!SP_IS_BOX3D(i->data))
269             continue;
270         SPBox3D *box = SP_BOX3D(i->data);
271         if (this->hasBox(box)) {
272             sel_boxes.push_back (box);
273         }
274     }
275     return sel_boxes;
278 VPDragger::VPDragger(VPDrag *parent, NR::Point p, VanishingPoint &vp)
280     //this->vps = NULL;
282     this->parent = parent;
284     this->point = p;
285     this->point_original = p;
287     this->dragging_started = false;
289     if (vp.is_finite()) {
290         // create the knot
291         this->knot = sp_knot_new (inkscape_active_desktop(), NULL);
292         this->knot->setMode(SP_KNOT_MODE_XOR);
293         this->knot->setFill(VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL);
294         this->knot->setStroke(0x000000ff, 0x000000ff, 0x000000ff);
295         sp_knot_update_ctrl(this->knot);
297         // move knot to the given point
298         sp_knot_set_position (this->knot, &this->point, SP_KNOT_STATE_NORMAL);
299         sp_knot_show (this->knot);
301         // connect knot's signals
302         g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (vp_knot_moved_handler), this);
303         g_signal_connect (G_OBJECT (this->knot), "clicked", G_CALLBACK (vp_knot_clicked_handler), this);
304         g_signal_connect (G_OBJECT (this->knot), "grabbed", G_CALLBACK (vp_knot_grabbed_handler), this);
305         g_signal_connect (G_OBJECT (this->knot), "ungrabbed", G_CALLBACK (vp_knot_ungrabbed_handler), this);
306         /***
307         g_signal_connect (G_OBJECT (this->knot), "doubleclicked", G_CALLBACK (vp_knot_doubleclicked_handler), this);
308         ***/
310         // add the initial VP (which may be NULL!)
311         this->addVP (vp);
312         //updateKnotShape();
313     }
316 VPDragger::~VPDragger()
318     // unselect if it was selected
319     //this->parent->setDeselected(this);
321     // disconnect signals
322     g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_moved_handler), this);
323     g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_clicked_handler), this);
324     g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_grabbed_handler), this);
325     g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_ungrabbed_handler), this);
326     /***
327     g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_doubleclicked_handler), this);
328     ***/
330     /* unref should call destroy */
331     g_object_unref (G_OBJECT (this->knot));
333     //g_slist_free (this->vps);
334     //this->vps = NULL;
337 /**
338 Updates the statusbar tip of the dragger knot, based on its draggables
339  */
340 void
341 VPDragger::updateTip ()
343     if (this->knot && this->knot->tip) {
344         g_free (this->knot->tip);
345         this->knot->tip = NULL;
346     }
348     guint num = this->numberOfBoxes();
349     if (this->vps.size() == 1) {
350         if (this->vps.front().is_finite()) {
351             this->knot->tip = g_strdup_printf (ngettext("<b>Finite</b> vanishing point shared by <b>%d</b> box",
352                                                         "<b>Finite</b> vanishing point shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
353                                                         num),
354                                                num);
355         } else {
356             // This won't make sense any more when infinite VPs are not shown on the canvas,
357             // but currently we update the status message anyway
358             this->knot->tip = g_strdup_printf (ngettext("<b>Infinite</b> vanishing point shared by <b>%d</b> box",
359                                                         "<b>Infinite</b> vanishing point shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
360                                                         num),
361                                                num);
362         }
363     } else {
364         int length = this->vps.size();
365         char *desc1 = g_strdup_printf ("Collection of <b>%d</b> vanishing points ", length);
366         char *desc2 = g_strdup_printf (ngettext("shared by <b>%d</b> box; drag with <b>Shift</b> to separate selected box(es)",
367                                                 "shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
368                                                 num),
369                                        num);
370         this->knot->tip = g_strconcat(desc1, desc2, NULL);
371         g_free (desc1);
372         g_free (desc2);
373     }
376 /**
377  * Adds a vanishing point to the dragger (also updates the position if necessary);
378  * the perspective is stored separately, too, for efficiency in updating boxes.
379  */
380 void
381 VPDragger::addVP (VanishingPoint &vp, bool update_pos)
383     //if (!vp.is_finite() || g_slist_find (this->vps, vp)) {
384     if (!vp.is_finite() || std::find (vps.begin(), vps.end(), vp) != vps.end()) {
385         // don't add infinite VPs; don't add the same VP twice
386         return;
387     }
389     if (update_pos) {
390         vp.set_pos (this->point);
391     }
392     //this->vps = g_slist_prepend (this->vps, vp);
393     this->vps.push_front (vp);
394     //this->persps.include (vp.get_perspective());
396     this->updateTip();
399 void
400 VPDragger::removeVP (VanishingPoint const &vp)
402     std::list<VanishingPoint>::iterator i = std::find (this->vps.begin(), this->vps.end(), vp);
403     if (i != this->vps.end()) {
404         this->vps.erase (i);
405     }
406     this->updateTip();
409 VanishingPoint *
410 VPDragger::findVPWithBox (SPBox3D *box) {
411     for (std::list<VanishingPoint>::iterator vp = vps.begin(); vp != vps.end(); ++vp) {
412         if ((*vp).hasBox(box)) {
413             return &(*vp);
414         }
415     }
416     return NULL;
419 std::set<VanishingPoint*, less_ptr>
420 VPDragger::VPsOfSelectedBoxes() {
421     std::set<VanishingPoint*, less_ptr> sel_vps;
422     VanishingPoint *vp;
423     // FIXME: Should we take the selection from the parent VPDrag? I guess it shouldn't make a difference.
424     Inkscape::Selection *sel = sp_desktop_selection(inkscape_active_desktop());
425     for (GSList const* i = sel->itemList(); i != NULL; i = i->next) {
426         if (!SP_IS_BOX3D(i->data))
427             continue;
428         SPBox3D *box = SP_BOX3D(i->data);
429         vp = this->findVPWithBox(box);
430         if (vp) {
431             sel_vps.insert (vp);
432         }
433     }
434     return sel_vps;
437 guint
438 VPDragger::numberOfBoxes ()
440     guint num = 0;
441     for (std::list<VanishingPoint>::iterator vp = vps.begin(); vp != vps.end(); ++vp) {
442         num += (*vp).numberOfBoxes();
443     }
444     return num;
447 bool
448 VPDragger::hasPerspective (const Persp3D *persp)
450     for (std::list<VanishingPoint>::iterator i = vps.begin(); i != vps.end(); ++i) {
451         if (persp3d_perspectives_coincide(persp, (*i).get_perspective())) {
452             return true;
453         }        
454     }
455     return false;
458 void
459 VPDragger::mergePerspectives ()
461     Persp3D *persp1, *persp2;
462     for (std::list<VanishingPoint>::iterator i = vps.begin(); i != vps.end(); ++i) {
463         persp1 = (*i).get_perspective();
464         for (std::list<VanishingPoint>::iterator j = i; j != vps.end(); ++j) {
465             persp2 = (*j).get_perspective();
466             if (persp1 == persp2) {
467                 /* don't merge a perspective with itself */
468                 continue;
469             }
470             if (persp3d_perspectives_coincide(persp1,persp2)) {
471                 /* if perspectives coincide but are not the same, merge them */
472                 persp3d_absorb(persp1, persp2);
474                 this->parent->swap_perspectives_of_VPs(persp2, persp1);
476                 SP_OBJECT(persp2)->deleteObject(false);
477             }
478         }
479     }
482 void
483 VPDragger::updateBoxDisplays ()
485     for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
486         (*i).updateBoxDisplays();
487     }
490 void
491 VPDragger::updateVPs (NR::Point const &pt)
493     for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
494         (*i).set_pos (pt);
495     }
498 void
499 VPDragger::updateZOrders ()
501     for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
502         persp3d_update_z_orders((*i).get_perspective());
503     }
506 void
507 VPDragger::printVPs() {
508     g_print ("VPDragger at position (%f, %f):\n", point[NR::X], point[NR::Y]);
509     for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
510         g_print ("    VP %s\n", (*i).axisString());
511     }
514 VPDrag::VPDrag (SPDocument *document)
516     this->document = document;
517     this->selection = sp_desktop_selection(inkscape_active_desktop());
519     this->draggers = NULL;
520     this->lines = NULL;
521     this->show_lines = true;
522     this->front_or_rear_lines = 0x1;
524     //this->selected = NULL;
525     this->dragging = false;
527     this->sel_changed_connection = this->selection->connectChanged(
528         sigc::bind (
529             sigc::ptr_fun(&vp_drag_sel_changed),
530             (gpointer)this )
532         );
533     this->sel_modified_connection = this->selection->connectModified(
534         sigc::bind(
535             sigc::ptr_fun(&vp_drag_sel_modified),
536             (gpointer)this )
537         );
539     this->updateDraggers ();
540     this->updateLines ();
543 VPDrag::~VPDrag()
545     this->sel_changed_connection.disconnect();
546     this->sel_modified_connection.disconnect();
548     for (GList *l = this->draggers; l != NULL; l = l->next) {
549         delete ((VPDragger *) l->data);
550     }
551     g_list_free (this->draggers);
552     this->draggers = NULL;
554     for (GSList const *i = this->lines; i != NULL; i = i->next) {
555         gtk_object_destroy( GTK_OBJECT (i->data));
556     }
557     g_slist_free (this->lines);
558     this->lines = NULL;
561 /**
562  * Select the dragger that has the given VP.
563  */
564 VPDragger *
565 VPDrag::getDraggerFor (VanishingPoint const &vp)
567     for (GList const* i = this->draggers; i != NULL; i = i->next) {
568         VPDragger *dragger = (VPDragger *) i->data;
569         for (std::list<VanishingPoint>::iterator j = dragger->vps.begin(); j != dragger->vps.end(); ++j) {
570             // TODO: Should we compare the pointers or the VPs themselves!?!?!?!
571             if (*j == vp) {
572                 return (dragger);
573             }
574         }
575     }
576     return NULL;
579 void
580 VPDrag::printDraggers ()
582     g_print ("=== VPDrag info: =================================\n");
583     for (GList const* i = this->draggers; i != NULL; i = i->next) {
584         ((VPDragger *) i->data)->printVPs();
585         g_print ("========\n");
586     }
587     g_print ("=================================================\n");
590 /**
591  * Regenerates the draggers list from the current selection; is called when selection is changed or modified
592  */
593 void
594 VPDrag::updateDraggers ()
596     if (this->dragging)
597         return;
598     // delete old draggers
599     for (GList const* i = this->draggers; i != NULL; i = i->next) {
600         delete ((VPDragger *) i->data);
601     }
602     g_list_free (this->draggers);
603     this->draggers = NULL;
605     g_return_if_fail (this->selection != NULL);
607     for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
608         SPItem *item = SP_ITEM(i->data);
609         if (!SP_IS_BOX3D (item)) continue;
610         SPBox3D *box = SP_BOX3D (item);
612         VanishingPoint vp;
613         for (int i = 0; i < 3; ++i) {
614             vp.set (box->persp_ref->getObject(), Proj::axes[i]);
615             addDragger (vp);
616         }
617     }
620 /**
621 Regenerates the lines list from the current selection; is called on each move
622 of a dragger, so that lines are always in sync with the actual perspective
623 */
624 void
625 VPDrag::updateLines ()
627     // delete old lines
628     for (GSList const *i = this->lines; i != NULL; i = i->next) {
629         gtk_object_destroy( GTK_OBJECT (i->data));
630     }
631     g_slist_free (this->lines);
632     this->lines = NULL;
634     // do nothing if perspective lines are currently disabled
635     if (this->show_lines == 0) return;
637     g_return_if_fail (this->selection != NULL);
639     for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
640         if (!SP_IS_BOX3D(i->data)) continue;
641         SPBox3D *box = SP_BOX3D (i->data);
643         this->drawLinesForFace (box, Proj::X);
644         this->drawLinesForFace (box, Proj::Y);
645         this->drawLinesForFace (box, Proj::Z);
646     }
649 void
650 VPDrag::updateBoxHandles ()
652     // FIXME: Is there a way to update the knots without accessing the
653     //        (previously) statically linked function knotholder_update_knots?
655     GSList *sel = (GSList *) selection->itemList();
656     if (!sel)
657         return; // no selection
659     if (g_slist_length (sel) > 1) {
660         // Currently we only show handles if a single box is selected
661         return;
662     }
664     SPEventContext *ec = inkscape_active_event_context();
665     g_assert (ec != NULL);
666     if (ec->shape_knot_holder != NULL) {
667         knotholder_update_knots(ec->shape_knot_holder, (SPItem *) sel->data);
668     }
671 void
672 VPDrag::updateBoxReprs ()
674     for (GList *i = this->draggers; i != NULL; i = i->next) {
675         VPDragger *dragger = (VPDragger *) i->data;
676         for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
677             (*i).updateBoxReprs();
678         }
679     }
682 void
683 VPDrag::updateBoxDisplays ()
685     for (GList *i = this->draggers; i != NULL; i = i->next) {
686         VPDragger *dragger = (VPDragger *) i->data;
687         for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
688             (*i).updateBoxDisplays();
689         }
690     }
694 /**
695  * Depending on the value of all_lines, draw the front and/or rear perspective lines starting from the given corners.
696  */
697 void
698 VPDrag::drawLinesForFace (const SPBox3D *box, Proj::Axis axis) //, guint corner1, guint corner2, guint corner3, guint corner4)
700     guint color;
701     switch (axis) {
702         // TODO: Make color selectable by user
703         case Proj::X: color = VP_LINE_COLOR_STROKE_X; break;
704         case Proj::Y: color = VP_LINE_COLOR_STROKE_Y; break;
705         case Proj::Z: color = VP_LINE_COLOR_STROKE_Z; break;
706         default: g_assert_not_reached();
707     }
709     NR::Point corner1, corner2, corner3, corner4;
710     box3d_corners_for_PLs (box, axis, corner1, corner2, corner3, corner4);
712     g_return_if_fail (box->persp_ref->getObject());
713     Proj::Pt2 vp = persp3d_get_VP (box->persp_ref->getObject(), axis);
714     if (vp.is_finite()) {
715         // draw perspective lines for finite VPs
716         NR::Point pt = vp.affine();
717         if (this->front_or_rear_lines & 0x1) {
718             // draw 'front' perspective lines
719             this->addLine (corner1, pt, color);
720             this->addLine (corner2, pt, color);
721         }
722         if (this->front_or_rear_lines & 0x2) {
723             // draw 'rear' perspective lines
724             this->addLine (corner3, pt, color);
725             this->addLine (corner4, pt, color);
726         }
727     } else {
728         // draw perspective lines for infinite VPs
729         NR::Maybe<NR::Point> pt1, pt2, pt3, pt4;
730         Persp3D *persp = box->persp_ref->getObject();
731         SPDesktop *desktop = inkscape_active_desktop (); // FIXME: Store the desktop in VPDrag
732         Box3D::PerspectiveLine pl (corner1, axis, persp);
733         pt1 = pl.intersection_with_viewbox(desktop);
735         pl = Box3D::PerspectiveLine (corner2, axis, persp);
736         pt2 = pl.intersection_with_viewbox(desktop);
738         pl = Box3D::PerspectiveLine (corner3, axis, persp);
739         pt3 = pl.intersection_with_viewbox(desktop);
741         pl = Box3D::PerspectiveLine (corner4, axis, persp);
742         pt4 = pl.intersection_with_viewbox(desktop);
744         if (!pt1 || !pt2 || !pt3 || !pt4) {
745             // some perspective lines s are outside the canvas; currently we don't draw any of them
746             return;
747         }
748         if (this->front_or_rear_lines & 0x1) {
749             // draw 'front' perspective lines
750             this->addLine (corner1, *pt1, color);
751             this->addLine (corner2, *pt2, color);
752         }
753         if (this->front_or_rear_lines & 0x2) {
754             // draw 'rear' perspective lines
755             this->addLine (corner3, *pt3, color);
756             this->addLine (corner4, *pt4, color);
757         }
758     }
761 /**
762  * If there already exists a dragger within MERGE_DIST of p, add the VP to it;
763  * otherwise create new dragger and add it to draggers list
764  * We also store the corresponding perspective in case it is not already present.
765  */
766 void
767 VPDrag::addDragger (VanishingPoint &vp)
769     if (!vp.is_finite()) {
770         // don't create draggers for infinite vanishing points
771         return;
772     }
773     NR::Point p = vp.get_pos();
775     for (GList *i = this->draggers; i != NULL; i = i->next) {
776         VPDragger *dragger = (VPDragger *) i->data;
777         if (NR::L2 (dragger->point - p) < MERGE_DIST) {
778             // distance is small, merge this draggable into dragger, no need to create new dragger
779             dragger->addVP (vp);
780             //dragger->updateKnotShape();
781             return;
782         }
783     }
785     VPDragger *new_dragger = new VPDragger(this, p, vp);
786     // fixme: draggers should be added AFTER the last one: this way tabbing through them will be from begin to end.
787     this->draggers = g_list_append (this->draggers, new_dragger);
790 void
791 VPDrag::swap_perspectives_of_VPs(Persp3D *persp2, Persp3D *persp1)
793     // iterate over all VP in all draggers and replace persp2 with persp1
794     for (GList *i = this->draggers; i != NULL; i = i->next) {
795         for (std::list<VanishingPoint>::iterator j = ((VPDragger *) (i->data))->vps.begin();
796              j != ((VPDragger *) (i->data))->vps.end(); ++j) {
797             if ((*j).get_perspective() == persp2) {
798                 (*j).set_perspective(persp1);
799             }
800         }
801     }
804 /**
805 Create a line from p1 to p2 and add it to the lines list
806  */
807 void
808 VPDrag::addLine (NR::Point p1, NR::Point p2, guint32 rgba)
810     SPCanvasItem *line = sp_canvas_item_new(sp_desktop_controls(inkscape_active_desktop()), SP_TYPE_CTRLLINE, NULL);
811     sp_ctrlline_set_coords(SP_CTRLLINE(line), p1, p2);
812     if (rgba != VP_LINE_COLOR_FILL) // fill is the default, so don't set color for it to speed up redraw
813         sp_ctrlline_set_rgba32 (SP_CTRLLINE(line), rgba);
814     sp_canvas_item_show (line);
815     this->lines = g_slist_append (this->lines, line);
818 } // namespace Box3D 
819  
820 /*
821   Local Variables:
822   mode:c++
823   c-file-style:"stroustrup"
824   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
825   indent-tabs-mode:nil
826   fill-column:99
827   End:
828 */
829 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :