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 namespace Box3D {
28 #define VP_KNOT_COLOR_NORMAL 0xffffff00
29 #define VP_KNOT_COLOR_SELECTED 0x0000ff00
31 #define VP_LINE_COLOR_FILL 0x0000ff7f
32 #define VP_LINE_COLOR_STROKE_X 0xff00007f
33 #define VP_LINE_COLOR_STROKE_Y 0x0000ff7f
34 #define VP_LINE_COLOR_STROKE_Z 0xffff007f
36 // screen pixels between knots when they snap:
37 #define SNAP_DIST 5
39 // absolute distance between gradient points for them to become a single dragger when the drag is created:
40 #define MERGE_DIST 0.1
42 // knot shapes corresponding to GrPointType enum
43 SPKnotShapeType vp_knot_shapes [] = {
44 SP_KNOT_SHAPE_SQUARE, // VP_FINITE
45 SP_KNOT_SHAPE_CIRCLE //VP_INFINITE
46 };
48 static void
49 vp_drag_sel_changed(Inkscape::Selection */*selection*/, gpointer data)
50 {
51 VPDrag *drag = (VPDrag *) data;
52 drag->updateDraggers();
53 drag->updateLines();
54 drag->updateBoxReprs();
55 }
57 static void
58 vp_drag_sel_modified (Inkscape::Selection */*selection*/, guint /*flags*/, gpointer data)
59 {
60 VPDrag *drag = (VPDrag *) data;
61 drag->updateLines ();
62 //drag->updateBoxReprs();
63 drag->updateBoxHandles (); // FIXME: Only update the handles of boxes on this dragger (not on all)
64 drag->updateDraggers ();
65 }
67 static bool
68 have_VPs_of_same_perspective (VPDragger *dr1, VPDragger *dr2)
69 {
70 for (std::list<VanishingPoint>::iterator i = dr1->vps.begin(); i != dr1->vps.end(); ++i) {
71 if (dr2->hasPerspective ((*i).get_perspective())) {
72 return true;
73 }
74 }
75 return false;
76 }
78 static void
79 vp_knot_moved_handler (SPKnot */*knot*/, Geom::Point const *ppointer, guint state, gpointer data)
80 {
81 VPDragger *dragger = (VPDragger *) data;
82 VPDrag *drag = dragger->parent;
84 Geom::Point p = *ppointer;
86 // FIXME: take from prefs
87 double snap_dist = SNAP_DIST / inkscape_active_desktop()->current_zoom();
89 /*
90 * We use dragging_started to indicate if we have already checked for the need to split Draggers up.
91 * This only has the purpose of avoiding costly checks in the routine below.
92 */
93 if (!dragger->dragging_started && (state & GDK_SHIFT_MASK)) {
94 /* with Shift; if there is more than one box linked to this VP
95 we need to split it and create a new perspective */
96 if (dragger->numberOfBoxes() > 1) { // FIXME: Don't do anything if *all* boxes of a VP are selected
97 std::set<VanishingPoint*, less_ptr> sel_vps = dragger->VPsOfSelectedBoxes();
99 std::list<SPBox3D *> sel_boxes;
100 for (std::set<VanishingPoint*, less_ptr>::iterator vp = sel_vps.begin(); vp != sel_vps.end(); ++vp) {
101 // for each VP that has selected boxes:
102 Persp3D *old_persp = (*vp)->get_perspective();
103 sel_boxes = (*vp)->selectedBoxes(sp_desktop_selection(inkscape_active_desktop()));
105 // we create a new perspective ...
106 Persp3D *new_persp = persp3d_create_xml_element (dragger->parent->document, old_persp);
108 /* ... unlink the boxes from the old one and
109 FIXME: We need to unlink the _un_selected boxes of each VP so that
110 the correct boxes are kept with the VP being moved */
111 std::list<SPBox3D *> bx_lst = persp3d_list_of_boxes(old_persp);
112 for (std::list<SPBox3D *>::iterator i = bx_lst.begin(); i != bx_lst.end(); ++i) {
113 if (std::find(sel_boxes.begin(), sel_boxes.end(), *i) == sel_boxes.end()) {
114 /* if a box in the VP is unselected, move it to the
115 newly created perspective so that it doesn't get dragged **/
116 box3d_switch_perspectives(*i, old_persp, new_persp);
117 }
118 }
119 }
120 // FIXME: Do we need to create a new dragger as well?
121 dragger->updateZOrders ();
122 sp_document_done (sp_desktop_document (inkscape_active_desktop()), SP_VERB_CONTEXT_3DBOX,
123 _("Split vanishing points"));
124 return;
125 }
126 }
128 if (!(state & GDK_SHIFT_MASK)) {
129 // without Shift; see if we need to snap to another dragger
130 for (GList *di = dragger->parent->draggers; di != NULL; di = di->next) {
131 VPDragger *d_new = (VPDragger *) di->data;
132 if ((d_new != dragger) && (Geom::L2 (d_new->point - p) < snap_dist)) {
133 if (have_VPs_of_same_perspective (dragger, d_new)) {
134 // this would result in degenerate boxes, which we disallow for the time being
135 continue;
136 }
138 // update positions ... (this is needed so that the perspectives are detected as identical)
139 // FIXME: This is called a bit too often, isn't it?
140 for (std::list<VanishingPoint>::iterator j = dragger->vps.begin(); j != dragger->vps.end(); ++j) {
141 (*j).set_pos(d_new->point);
142 }
144 // ... join lists of VPs ...
145 d_new->vps.merge(dragger->vps);
147 // ... delete old dragger ...
148 drag->draggers = g_list_remove (drag->draggers, dragger);
149 delete dragger;
150 dragger = NULL;
152 // ... and merge any duplicate perspectives
153 d_new->mergePerspectives();
155 // TODO: Update the new merged dragger
156 d_new->updateTip();
158 d_new->parent->updateBoxDisplays (); // FIXME: Only update boxes in current dragger!
159 d_new->updateZOrders ();
161 drag->updateLines ();
163 // TODO: Undo machinery; this doesn't work yet because perspectives must be created and
164 // deleted according to changes in the svg representation, not based on any user input
165 // as is currently the case.
167 sp_document_done (sp_desktop_document (inkscape_active_desktop()), SP_VERB_CONTEXT_3DBOX,
168 _("Merge vanishing points"));
170 return;
171 }
172 }
173 }
176 dragger->point = p; // FIXME: Brauchen wir dragger->point überhaupt?
178 dragger->updateVPs(p);
179 dragger->updateBoxDisplays();
180 dragger->parent->updateBoxHandles (); // FIXME: Only update the handles of boxes on this dragger (not on all)
181 dragger->updateZOrders();
183 drag->updateLines();
185 dragger->dragging_started = true;
186 }
188 void
189 vp_knot_grabbed_handler (SPKnot */*knot*/, unsigned int /*state*/, gpointer data)
190 {
191 VPDragger *dragger = (VPDragger *) data;
192 VPDrag *drag = dragger->parent;
194 drag->dragging = true;
195 }
197 static void
198 vp_knot_ungrabbed_handler (SPKnot *knot, guint /*state*/, gpointer data)
199 {
200 VPDragger *dragger = (VPDragger *) data;
202 dragger->point_original = dragger->point = knot->pos;
204 dragger->dragging_started = false;
206 for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
207 (*i).set_pos (knot->pos);
208 (*i).updateBoxReprs();
209 (*i).updatePerspRepr();
210 }
212 dragger->parent->updateDraggers ();
213 dragger->parent->updateLines ();
214 dragger->parent->updateBoxHandles ();
216 // TODO: Update box's paths and svg representation
218 dragger->parent->dragging = false;
220 // TODO: Undo machinery!!
221 g_return_if_fail (dragger->parent);
222 g_return_if_fail (dragger->parent->document);
223 sp_document_done(dragger->parent->document, SP_VERB_CONTEXT_3DBOX,
224 _("3D box: Move vanishing point"));
225 }
227 unsigned int VanishingPoint::global_counter = 0;
229 // FIXME: Rename to something more meaningful!
230 void
231 VanishingPoint::set_pos(Proj::Pt2 const &pt) {
232 g_return_if_fail (_persp);
233 _persp->tmat.set_image_pt (_axis, pt);
234 }
236 std::list<SPBox3D *>
237 VanishingPoint::selectedBoxes(Inkscape::Selection *sel) {
238 std::list<SPBox3D *> sel_boxes;
239 for (GSList const* i = sel->itemList(); i != NULL; i = i->next) {
240 if (!SP_IS_BOX3D(i->data))
241 continue;
242 SPBox3D *box = SP_BOX3D(i->data);
243 if (this->hasBox(box)) {
244 sel_boxes.push_back (box);
245 }
246 }
247 return sel_boxes;
248 }
250 VPDragger::VPDragger(VPDrag *parent, Geom::Point p, VanishingPoint &vp)
251 {
252 this->parent = parent;
254 this->point = p;
255 this->point_original = p;
257 this->dragging_started = false;
259 if (vp.is_finite()) {
260 // create the knot
261 this->knot = sp_knot_new (inkscape_active_desktop(), NULL);
262 this->knot->setMode(SP_KNOT_MODE_XOR);
263 this->knot->setFill(VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL);
264 this->knot->setStroke(0x000000ff, 0x000000ff, 0x000000ff);
265 sp_knot_update_ctrl(this->knot);
267 // move knot to the given point
268 sp_knot_set_position (this->knot, this->point, SP_KNOT_STATE_NORMAL);
269 sp_knot_show (this->knot);
271 // connect knot's signals
272 g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (vp_knot_moved_handler), this);
273 g_signal_connect (G_OBJECT (this->knot), "grabbed", G_CALLBACK (vp_knot_grabbed_handler), this);
274 g_signal_connect (G_OBJECT (this->knot), "ungrabbed", G_CALLBACK (vp_knot_ungrabbed_handler), this);
276 // add the initial VP (which may be NULL!)
277 this->addVP (vp);
278 }
279 }
281 VPDragger::~VPDragger()
282 {
283 // disconnect signals
284 g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_moved_handler), this);
285 g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_grabbed_handler), this);
286 g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_ungrabbed_handler), this);
287 /* unref should call destroy */
288 g_object_unref (G_OBJECT (this->knot));
289 }
291 /**
292 Updates the statusbar tip of the dragger knot, based on its draggables
293 */
294 void
295 VPDragger::updateTip ()
296 {
297 if (this->knot && this->knot->tip) {
298 g_free (this->knot->tip);
299 this->knot->tip = NULL;
300 }
302 guint num = this->numberOfBoxes();
303 if (this->vps.size() == 1) {
304 if (this->vps.front().is_finite()) {
305 this->knot->tip = g_strdup_printf (ngettext("<b>Finite</b> vanishing point shared by <b>%d</b> box",
306 "<b>Finite</b> vanishing point shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
307 num),
308 num);
309 } else {
310 // This won't make sense any more when infinite VPs are not shown on the canvas,
311 // but currently we update the status message anyway
312 this->knot->tip = g_strdup_printf (ngettext("<b>Infinite</b> vanishing point shared by <b>%d</b> box",
313 "<b>Infinite</b> vanishing point shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
314 num),
315 num);
316 }
317 } else {
318 int length = this->vps.size();
319 char *desc1 = g_strdup_printf ("Collection of <b>%d</b> vanishing points ", length);
320 char *desc2 = g_strdup_printf (ngettext("shared by <b>%d</b> box; drag with <b>Shift</b> to separate selected box(es)",
321 "shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
322 num),
323 num);
324 this->knot->tip = g_strconcat(desc1, desc2, NULL);
325 g_free (desc1);
326 g_free (desc2);
327 }
328 }
330 /**
331 * Adds a vanishing point to the dragger (also updates the position if necessary);
332 * the perspective is stored separately, too, for efficiency in updating boxes.
333 */
334 void
335 VPDragger::addVP (VanishingPoint &vp, bool update_pos)
336 {
337 if (!vp.is_finite() || std::find (vps.begin(), vps.end(), vp) != vps.end()) {
338 // don't add infinite VPs; don't add the same VP twice
339 return;
340 }
342 if (update_pos) {
343 vp.set_pos (this->point);
344 }
345 this->vps.push_front (vp);
347 this->updateTip();
348 }
350 void
351 VPDragger::removeVP (VanishingPoint const &vp)
352 {
353 std::list<VanishingPoint>::iterator i = std::find (this->vps.begin(), this->vps.end(), vp);
354 if (i != this->vps.end()) {
355 this->vps.erase (i);
356 }
357 this->updateTip();
358 }
360 VanishingPoint *
361 VPDragger::findVPWithBox (SPBox3D *box) {
362 for (std::list<VanishingPoint>::iterator vp = vps.begin(); vp != vps.end(); ++vp) {
363 if ((*vp).hasBox(box)) {
364 return &(*vp);
365 }
366 }
367 return NULL;
368 }
370 std::set<VanishingPoint*, less_ptr>
371 VPDragger::VPsOfSelectedBoxes() {
372 std::set<VanishingPoint*, less_ptr> sel_vps;
373 VanishingPoint *vp;
374 // FIXME: Should we take the selection from the parent VPDrag? I guess it shouldn't make a difference.
375 Inkscape::Selection *sel = sp_desktop_selection(inkscape_active_desktop());
376 for (GSList const* i = sel->itemList(); i != NULL; i = i->next) {
377 if (!SP_IS_BOX3D(i->data))
378 continue;
379 SPBox3D *box = SP_BOX3D(i->data);
380 vp = this->findVPWithBox(box);
381 if (vp) {
382 sel_vps.insert (vp);
383 }
384 }
385 return sel_vps;
386 }
388 guint
389 VPDragger::numberOfBoxes ()
390 {
391 guint num = 0;
392 for (std::list<VanishingPoint>::iterator vp = vps.begin(); vp != vps.end(); ++vp) {
393 num += (*vp).numberOfBoxes();
394 }
395 return num;
396 }
398 bool
399 VPDragger::hasPerspective (const Persp3D *persp)
400 {
401 for (std::list<VanishingPoint>::iterator i = vps.begin(); i != vps.end(); ++i) {
402 if (persp3d_perspectives_coincide(persp, (*i).get_perspective())) {
403 return true;
404 }
405 }
406 return false;
407 }
409 void
410 VPDragger::mergePerspectives ()
411 {
412 Persp3D *persp1, *persp2;
413 for (std::list<VanishingPoint>::iterator i = vps.begin(); i != vps.end(); ++i) {
414 persp1 = (*i).get_perspective();
415 for (std::list<VanishingPoint>::iterator j = i; j != vps.end(); ++j) {
416 persp2 = (*j).get_perspective();
417 if (persp1 == persp2) {
418 /* don't merge a perspective with itself */
419 continue;
420 }
421 if (persp3d_perspectives_coincide(persp1,persp2)) {
422 /* if perspectives coincide but are not the same, merge them */
423 persp3d_absorb(persp1, persp2);
425 this->parent->swap_perspectives_of_VPs(persp2, persp1);
427 SP_OBJECT(persp2)->deleteObject(false);
428 }
429 }
430 }
431 }
433 void
434 VPDragger::updateBoxDisplays ()
435 {
436 for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
437 (*i).updateBoxDisplays();
438 }
439 }
441 void
442 VPDragger::updateVPs (Geom::Point const &pt)
443 {
444 for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
445 (*i).set_pos (pt);
446 }
447 }
449 void
450 VPDragger::updateZOrders ()
451 {
452 for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
453 persp3d_update_z_orders((*i).get_perspective());
454 }
455 }
457 void
458 VPDragger::printVPs() {
459 g_print ("VPDragger at position (%f, %f):\n", point[Geom::X], point[Geom::Y]);
460 for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
461 g_print (" VP %s\n", (*i).axisString());
462 }
463 }
465 VPDrag::VPDrag (SPDocument *document)
466 {
467 this->document = document;
468 this->selection = sp_desktop_selection(inkscape_active_desktop());
470 this->draggers = NULL;
471 this->lines = NULL;
472 this->show_lines = true;
473 this->front_or_rear_lines = 0x1;
475 this->dragging = false;
477 this->sel_changed_connection = this->selection->connectChanged(
478 sigc::bind (
479 sigc::ptr_fun(&vp_drag_sel_changed),
480 (gpointer)this )
482 );
483 this->sel_modified_connection = this->selection->connectModified(
484 sigc::bind(
485 sigc::ptr_fun(&vp_drag_sel_modified),
486 (gpointer)this )
487 );
489 this->updateDraggers ();
490 this->updateLines ();
491 }
493 VPDrag::~VPDrag()
494 {
495 this->sel_changed_connection.disconnect();
496 this->sel_modified_connection.disconnect();
498 for (GList *l = this->draggers; l != NULL; l = l->next) {
499 delete ((VPDragger *) l->data);
500 }
501 g_list_free (this->draggers);
502 this->draggers = NULL;
504 for (GSList const *i = this->lines; i != NULL; i = i->next) {
505 gtk_object_destroy( GTK_OBJECT (i->data));
506 }
507 g_slist_free (this->lines);
508 this->lines = NULL;
509 }
511 /**
512 * Select the dragger that has the given VP.
513 */
514 VPDragger *
515 VPDrag::getDraggerFor (VanishingPoint const &vp)
516 {
517 for (GList const* i = this->draggers; i != NULL; i = i->next) {
518 VPDragger *dragger = (VPDragger *) i->data;
519 for (std::list<VanishingPoint>::iterator j = dragger->vps.begin(); j != dragger->vps.end(); ++j) {
520 // TODO: Should we compare the pointers or the VPs themselves!?!?!?!
521 if (*j == vp) {
522 return (dragger);
523 }
524 }
525 }
526 return NULL;
527 }
529 void
530 VPDrag::printDraggers ()
531 {
532 g_print ("=== VPDrag info: =================================\n");
533 for (GList const* i = this->draggers; i != NULL; i = i->next) {
534 ((VPDragger *) i->data)->printVPs();
535 g_print ("========\n");
536 }
537 g_print ("=================================================\n");
538 }
540 /**
541 * Regenerates the draggers list from the current selection; is called when selection is changed or modified
542 */
543 void
544 VPDrag::updateDraggers ()
545 {
546 if (this->dragging)
547 return;
548 // delete old draggers
549 for (GList const* i = this->draggers; i != NULL; i = i->next) {
550 delete ((VPDragger *) i->data);
551 }
552 g_list_free (this->draggers);
553 this->draggers = NULL;
555 g_return_if_fail (this->selection != NULL);
557 for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
558 SPItem *item = SP_ITEM(i->data);
559 if (!SP_IS_BOX3D (item)) continue;
560 SPBox3D *box = SP_BOX3D (item);
562 VanishingPoint vp;
563 for (int i = 0; i < 3; ++i) {
564 vp.set(box3d_get_perspective(box), Proj::axes[i]);
565 addDragger (vp);
566 }
567 }
568 }
570 /**
571 Regenerates the lines list from the current selection; is called on each move
572 of a dragger, so that lines are always in sync with the actual perspective
573 */
574 void
575 VPDrag::updateLines ()
576 {
577 // delete old lines
578 for (GSList const *i = this->lines; i != NULL; i = i->next) {
579 gtk_object_destroy( GTK_OBJECT (i->data));
580 }
581 g_slist_free (this->lines);
582 this->lines = NULL;
584 // do nothing if perspective lines are currently disabled
585 if (this->show_lines == 0) return;
587 g_return_if_fail (this->selection != NULL);
589 for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
590 if (!SP_IS_BOX3D(i->data)) continue;
591 SPBox3D *box = SP_BOX3D (i->data);
593 this->drawLinesForFace (box, Proj::X);
594 this->drawLinesForFace (box, Proj::Y);
595 this->drawLinesForFace (box, Proj::Z);
596 }
597 }
599 void
600 VPDrag::updateBoxHandles ()
601 {
602 // FIXME: Is there a way to update the knots without accessing the
603 // (previously) statically linked function KnotHolder::update_knots?
605 GSList *sel = (GSList *) selection->itemList();
606 if (!sel)
607 return; // no selection
609 if (g_slist_length (sel) > 1) {
610 // Currently we only show handles if a single box is selected
611 return;
612 }
614 SPEventContext *ec = inkscape_active_event_context();
615 g_assert (ec != NULL);
616 if (ec->shape_editor != NULL) {
617 ec->shape_editor->update_knotholder();
618 }
619 }
621 void
622 VPDrag::updateBoxReprs ()
623 {
624 for (GList *i = this->draggers; i != NULL; i = i->next) {
625 VPDragger *dragger = (VPDragger *) i->data;
626 for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
627 (*i).updateBoxReprs();
628 }
629 }
630 }
632 void
633 VPDrag::updateBoxDisplays ()
634 {
635 for (GList *i = this->draggers; i != NULL; i = i->next) {
636 VPDragger *dragger = (VPDragger *) i->data;
637 for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
638 (*i).updateBoxDisplays();
639 }
640 }
641 }
644 /**
645 * Depending on the value of all_lines, draw the front and/or rear perspective lines starting from the given corners.
646 */
647 void
648 VPDrag::drawLinesForFace (const SPBox3D *box, Proj::Axis axis) //, guint corner1, guint corner2, guint corner3, guint corner4)
649 {
650 guint color;
651 switch (axis) {
652 // TODO: Make color selectable by user
653 case Proj::X: color = VP_LINE_COLOR_STROKE_X; break;
654 case Proj::Y: color = VP_LINE_COLOR_STROKE_Y; break;
655 case Proj::Z: color = VP_LINE_COLOR_STROKE_Z; break;
656 default: g_assert_not_reached();
657 }
659 Geom::Point corner1, corner2, corner3, corner4;
660 box3d_corners_for_PLs (box, axis, corner1, corner2, corner3, corner4);
662 g_return_if_fail (box3d_get_perspective(box));
663 Proj::Pt2 vp = persp3d_get_VP (box3d_get_perspective(box), axis);
664 if (vp.is_finite()) {
665 // draw perspective lines for finite VPs
666 Geom::Point pt = vp.affine();
667 if (this->front_or_rear_lines & 0x1) {
668 // draw 'front' perspective lines
669 this->addLine (corner1, pt, color);
670 this->addLine (corner2, pt, color);
671 }
672 if (this->front_or_rear_lines & 0x2) {
673 // draw 'rear' perspective lines
674 this->addLine (corner3, pt, color);
675 this->addLine (corner4, pt, color);
676 }
677 } else {
678 // draw perspective lines for infinite VPs
679 boost::optional<Geom::Point> pt1, pt2, pt3, pt4;
680 Persp3D *persp = box3d_get_perspective(box);
681 SPDesktop *desktop = inkscape_active_desktop (); // FIXME: Store the desktop in VPDrag
682 Box3D::PerspectiveLine pl (corner1, axis, persp);
683 pt1 = pl.intersection_with_viewbox(desktop);
685 pl = Box3D::PerspectiveLine (corner2, axis, persp);
686 pt2 = pl.intersection_with_viewbox(desktop);
688 pl = Box3D::PerspectiveLine (corner3, axis, persp);
689 pt3 = pl.intersection_with_viewbox(desktop);
691 pl = Box3D::PerspectiveLine (corner4, axis, persp);
692 pt4 = pl.intersection_with_viewbox(desktop);
694 if (!pt1 || !pt2 || !pt3 || !pt4) {
695 // some perspective lines s are outside the canvas; currently we don't draw any of them
696 return;
697 }
698 if (this->front_or_rear_lines & 0x1) {
699 // draw 'front' perspective lines
700 this->addLine (corner1, *pt1, color);
701 this->addLine (corner2, *pt2, color);
702 }
703 if (this->front_or_rear_lines & 0x2) {
704 // draw 'rear' perspective lines
705 this->addLine (corner3, *pt3, color);
706 this->addLine (corner4, *pt4, color);
707 }
708 }
709 }
711 /**
712 * If there already exists a dragger within MERGE_DIST of p, add the VP to it;
713 * otherwise create new dragger and add it to draggers list
714 * We also store the corresponding perspective in case it is not already present.
715 */
716 void
717 VPDrag::addDragger (VanishingPoint &vp)
718 {
719 if (!vp.is_finite()) {
720 // don't create draggers for infinite vanishing points
721 return;
722 }
723 Geom::Point p = vp.get_pos();
725 for (GList *i = this->draggers; i != NULL; i = i->next) {
726 VPDragger *dragger = (VPDragger *) i->data;
727 if (Geom::L2 (dragger->point - p) < MERGE_DIST) {
728 // distance is small, merge this draggable into dragger, no need to create new dragger
729 dragger->addVP (vp);
730 return;
731 }
732 }
734 VPDragger *new_dragger = new VPDragger(this, p, vp);
735 // fixme: draggers should be added AFTER the last one: this way tabbing through them will be from begin to end.
736 this->draggers = g_list_append (this->draggers, new_dragger);
737 }
739 void
740 VPDrag::swap_perspectives_of_VPs(Persp3D *persp2, Persp3D *persp1)
741 {
742 // iterate over all VP in all draggers and replace persp2 with persp1
743 for (GList *i = this->draggers; i != NULL; i = i->next) {
744 for (std::list<VanishingPoint>::iterator j = ((VPDragger *) (i->data))->vps.begin();
745 j != ((VPDragger *) (i->data))->vps.end(); ++j) {
746 if ((*j).get_perspective() == persp2) {
747 (*j).set_perspective(persp1);
748 }
749 }
750 }
751 }
753 /**
754 Create a line from p1 to p2 and add it to the lines list
755 */
756 void
757 VPDrag::addLine (Geom::Point p1, Geom::Point p2, guint32 rgba)
758 {
759 SPCanvasItem *line = sp_canvas_item_new(sp_desktop_controls(inkscape_active_desktop()), SP_TYPE_CTRLLINE, NULL);
760 sp_ctrlline_set_coords(SP_CTRLLINE(line), p1, p2);
761 if (rgba != VP_LINE_COLOR_FILL) // fill is the default, so don't set color for it to speed up redraw
762 sp_ctrlline_set_rgba32 (SP_CTRLLINE(line), rgba);
763 sp_canvas_item_show (line);
764 this->lines = g_slist_append (this->lines, line);
765 }
767 } // namespace Box3D
769 /*
770 Local Variables:
771 mode:c++
772 c-file-style:"stroustrup"
773 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
774 indent-tabs-mode:nil
775 fill-column:99
776 End:
777 */
778 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :