index edf769c176388f9c6164836167639bc3fda1ad44..f8e991623ded868fcd43c373feff2f8311bd04b2 100644 (file)
--- a/src/vanishing-point.cpp
+++ b/src/vanishing-point.cpp
-#define __VANISHING_POINT_C__
-
/*
* Vanishing point for 3D perspectives
*
* Authors:
/*
* Vanishing point for 3D perspectives
*
* Authors:
+ * bulia byak <buliabyak@users.sf.net>
+ * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
* Maximilian Albert <Anhalter42@gmx.de>
* Maximilian Albert <Anhalter42@gmx.de>
+ * Abhishek Sharma
*
*
- * Copyright (C) 2007 authors
+ * Copyright (C) 2005-2007 authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+#include <glibmm/i18n.h>
+
#include "vanishing-point.h"
#include "desktop-handles.h"
#include "vanishing-point.h"
#include "desktop-handles.h"
-#include "box3d.h"
+#include "desktop.h"
+#include "event-context.h"
+#include "xml/repr.h"
+#include "perspective-line.h"
+#include "shape-editor.h"
+#include "snap.h"
+#include "sp-namedview.h"
+
+using Inkscape::DocumentUndo;
namespace Box3D {
namespace Box3D {
SP_KNOT_SHAPE_CIRCLE //VP_INFINITE
};
SP_KNOT_SHAPE_CIRCLE //VP_INFINITE
};
-// FIXME: We should always require to have both the point (for finite VPs)
-// and the direction (for infinite VPs) set. Otherwise toggling
-// shows very unexpected behaviour.
-// Later on we can maybe infer the infinite direction from the finite point
-// and a suitable center of the scene. How to go in the other direction?
-VanishingPoint::VanishingPoint(NR::Point const &pt, NR::Point const &inf_dir, VPState st)
- : NR::Point (pt), state (st), v_dir (inf_dir) {}
-
-VanishingPoint::VanishingPoint(NR::Point const &pt)
- : NR::Point (pt), state (VP_FINITE), v_dir (0.0, 0.0) {}
-
-VanishingPoint::VanishingPoint(NR::Point const &pt, NR::Point const &direction)
- : NR::Point (pt), state (VP_INFINITE), v_dir (direction) {}
-
-VanishingPoint::VanishingPoint(NR::Coord x, NR::Coord y)
- : NR::Point(x, y), state(VP_FINITE), v_dir(0.0, 0.0) {}
-
-VanishingPoint::VanishingPoint(NR::Coord dir_x, NR::Coord dir_y, VPState st)
- : NR::Point(0.0, 0.0), state(st), v_dir(dir_x, dir_y) {}
-
-VanishingPoint::VanishingPoint(NR::Coord x, NR::Coord y, NR::Coord dir_x, NR::Coord dir_y)
- : NR::Point(x, y), state(VP_INFINITE), v_dir(dir_x, dir_y) {}
-
-VanishingPoint::VanishingPoint(VanishingPoint const &rhs) : NR::Point (rhs)
-{
- this->state = rhs.state;
- //this->ref_pt = rhs.ref_pt;
- this->v_dir = rhs.v_dir;
-}
-
-VanishingPoint::~VanishingPoint () {}
-
-bool VanishingPoint::operator== (VanishingPoint const &other)
-{
- // Should we compare the parent perspectives, too? Probably not.
- if ((*this)[NR::X] == other[NR::X] && (*this)[NR::Y] == other[NR::Y]
- && this->state == other.state && this->v_dir == other.v_dir) {
- return true;
- }
- return false;
-}
-
-bool VanishingPoint::is_finite() const
-{
- return this->state == VP_FINITE;
-}
-
-VPState VanishingPoint::toggle_parallel()
-{
- if (this->state == VP_FINITE) {
- this->state = VP_INFINITE;
- } else {
- this->state = VP_FINITE;
- }
-
- return this->state;
-}
-
-void VanishingPoint::draw(Box3D::Axis const axis)
+static void
+vp_drag_sel_changed(Inkscape::Selection */*selection*/, gpointer data)
{
{
- switch (axis) {
- case X:
- if (state == VP_FINITE)
- create_canvas_point(*this, 6.0, 0xff000000);
- else
- create_canvas_point(*this, 6.0, 0xffffff00);
- break;
- case Y:
- if (state == VP_FINITE)
- create_canvas_point(*this, 6.0, 0x0000ff00);
- else
- create_canvas_point(*this, 6.0, 0xffffff00);
- break;
- case Z:
- if (state == VP_FINITE)
- create_canvas_point(*this, 6.0, 0x00770000);
- else
- create_canvas_point(*this, 6.0, 0xffffff00);
- break;
- default:
- g_assert_not_reached();
- break;
- }
+ VPDrag *drag = (VPDrag *) data;
+ drag->updateDraggers();
+ drag->updateLines();
+ drag->updateBoxReprs();
}
static void
}
static void
-vp_drag_sel_changed(Inkscape::Selection *selection, gpointer data)
+vp_drag_sel_modified (Inkscape::Selection */*selection*/, guint /*flags*/, gpointer data)
{
VPDrag *drag = (VPDrag *) data;
{
VPDrag *drag = (VPDrag *) data;
+ drag->updateLines ();
+ //drag->updateBoxReprs();
+ drag->updateBoxHandles (); // FIXME: Only update the handles of boxes on this dragger (not on all)
drag->updateDraggers ();
drag->updateDraggers ();
- //drag->updateLines ();
}
}
-static void
-vp_drag_sel_modified (Inkscape::Selection *selection, guint flags, gpointer data)
+static bool
+have_VPs_of_same_perspective (VPDragger *dr1, VPDragger *dr2)
{
{
- VPDrag *drag = (VPDrag *) data;
- /***
- if (drag->local_change) {
- drag->local_change = false;
- } else {
- drag->updateDraggers ();
+ for (std::list<VanishingPoint>::iterator i = dr1->vps.begin(); i != dr1->vps.end(); ++i) {
+ if (dr2->hasPerspective ((*i).get_perspective())) {
+ return true;
+ }
}
}
- ***/
- //drag->updateLines ();
+ return false;
}
static void
}
static void
-vp_knot_moved_handler (SPKnot *knot, NR::Point const *ppointer, guint state, gpointer data)
+vp_knot_moved_handler (SPKnot *knot, Geom::Point const *ppointer, guint state, gpointer data)
{
{
- g_warning ("Please implement vp_knot_moved_handler.\n");
VPDragger *dragger = (VPDragger *) data;
VPDragger *dragger = (VPDragger *) data;
- //VPDrag *drag = dragger->parent;
+ VPDrag *drag = dragger->parent;
+
+ Geom::Point p = *ppointer;
+
+ // FIXME: take from prefs
+ double snap_dist = SNAP_DIST / inkscape_active_desktop()->current_zoom();
+
+ /*
+ * We use dragging_started to indicate if we have already checked for the need to split Draggers up.
+ * This only has the purpose of avoiding costly checks in the routine below.
+ */
+ if (!dragger->dragging_started && (state & GDK_SHIFT_MASK)) {
+ /* with Shift; if there is more than one box linked to this VP
+ we need to split it and create a new perspective */
+ if (dragger->numberOfBoxes() > 1) { // FIXME: Don't do anything if *all* boxes of a VP are selected
+ std::set<VanishingPoint*, less_ptr> sel_vps = dragger->VPsOfSelectedBoxes();
+
+ std::list<SPBox3D *> sel_boxes;
+ for (std::set<VanishingPoint*, less_ptr>::iterator vp = sel_vps.begin(); vp != sel_vps.end(); ++vp) {
+ // for each VP that has selected boxes:
+ Persp3D *old_persp = (*vp)->get_perspective();
+ sel_boxes = (*vp)->selectedBoxes(sp_desktop_selection(inkscape_active_desktop()));
+
+ // we create a new perspective ...
+ Persp3D *new_persp = persp3d_create_xml_element (dragger->parent->document, old_persp->perspective_impl);
+
+ /* ... unlink the boxes from the old one and
+ FIXME: We need to unlink the _un_selected boxes of each VP so that
+ the correct boxes are kept with the VP being moved */
+ std::list<SPBox3D *> bx_lst = persp3d_list_of_boxes(old_persp);
+ for (std::list<SPBox3D *>::iterator i = bx_lst.begin(); i != bx_lst.end(); ++i) {
+ if (std::find(sel_boxes.begin(), sel_boxes.end(), *i) == sel_boxes.end()) {
+ /* if a box in the VP is unselected, move it to the
+ newly created perspective so that it doesn't get dragged **/
+ box3d_switch_perspectives(*i, old_persp, new_persp);
+ }
+ }
+ }
+ // FIXME: Do we need to create a new dragger as well?
+ dragger->updateZOrders ();
+ DocumentUndo::done(sp_desktop_document (inkscape_active_desktop()), SP_VERB_CONTEXT_3DBOX,
+ _("Split vanishing points"));
+ return;
+ }
+ }
+
+ if (!(state & GDK_SHIFT_MASK)) {
+ // without Shift; see if we need to snap to another dragger
+ for (GList *di = dragger->parent->draggers; di != NULL; di = di->next) {
+ VPDragger *d_new = (VPDragger *) di->data;
+ if ((d_new != dragger) && (Geom::L2 (d_new->point - p) < snap_dist)) {
+ if (have_VPs_of_same_perspective (dragger, d_new)) {
+ // this would result in degenerate boxes, which we disallow for the time being
+ continue;
+ }
+
+ // update positions ... (this is needed so that the perspectives are detected as identical)
+ // FIXME: This is called a bit too often, isn't it?
+ for (std::list<VanishingPoint>::iterator j = dragger->vps.begin(); j != dragger->vps.end(); ++j) {
+ (*j).set_pos(d_new->point);
+ }
- NR::Point p = *ppointer;
+ // ... join lists of VPs ...
+ d_new->vps.merge(dragger->vps);
+
+ // ... delete old dragger ...
+ drag->draggers = g_list_remove (drag->draggers, dragger);
+ delete dragger;
+ dragger = NULL;
+
+ // ... and merge any duplicate perspectives
+ d_new->mergePerspectives();
+
+ // TODO: Update the new merged dragger
+ d_new->updateTip();
+
+ d_new->parent->updateBoxDisplays (); // FIXME: Only update boxes in current dragger!
+ d_new->updateZOrders ();
+
+ drag->updateLines ();
+
+ // TODO: Undo machinery; this doesn't work yet because perspectives must be created and
+ // deleted according to changes in the svg representation, not based on any user input
+ // as is currently the case.
+
+ DocumentUndo::done(sp_desktop_document (inkscape_active_desktop()), SP_VERB_CONTEXT_3DBOX,
+ _("Merge vanishing points"));
+
+ return;
+ }
+ }
+
+ // We didn't snap to another dragger, so we'll try a regular snap
+ SPDesktop *desktop = inkscape_active_desktop();
+ SnapManager &m = desktop->namedview->snap_manager;
+ m.setup(desktop);
+ Inkscape::SnappedPoint s = m.freeSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_OTHER_HANDLE));
+ m.unSetup();
+ if (s.getSnapped()) {
+ p = s.getPoint();
+ sp_knot_moveto(knot, p);
+ }
+ }
- dragger->point = p;
+ dragger->point = p; // FIXME: Is dragger->point being used at all?
- dragger->reshapeBoxes (p, Box3D::XYZ);
- //dragger->parent->updateLines ();
+ dragger->updateVPs(p);
+ dragger->updateBoxDisplays();
+ dragger->parent->updateBoxHandles (); // FIXME: Only update the handles of boxes on this dragger (not on all)
+ dragger->updateZOrders();
- //drag->local_change = false;
+ drag->updateLines();
+
+ dragger->dragging_started = true;
}
}
-static void
-vp_knot_grabbed_handler (SPKnot *knot, unsigned int state, gpointer data)
+void
+vp_knot_grabbed_handler (SPKnot */*knot*/, unsigned int /*state*/, gpointer data)
{
VPDragger *dragger = (VPDragger *) data;
{
VPDragger *dragger = (VPDragger *) data;
+ VPDrag *drag = dragger->parent;
- //sp_canvas_force_full_redraw_after_interruptions(dragger->parent->desktop->canvas, 5);
+ drag->dragging = true;
}
static void
}
static void
-vp_knot_ungrabbed_handler (SPKnot *knot, guint state, gpointer data)
+vp_knot_ungrabbed_handler (SPKnot *knot, guint /*state*/, gpointer data)
{
{
- g_warning ("Please fully implement vp_knot_ungrabbed_handler.\n");
-
VPDragger *dragger = (VPDragger *) data;
VPDragger *dragger = (VPDragger *) data;
- //VPDrag *drag = dragger->parent;
-
- //sp_canvas_end_forced_full_redraws(dragger->parent->desktop->canvas);
dragger->point_original = dragger->point = knot->pos;
dragger->point_original = dragger->point = knot->pos;
- /***
- VanishingPoint *vp;
- for (GSList *i = dragger->vps; i != NULL; i = i->next) {
- vp = (VanishingPoint *) i->data;
- vp->set_pos (knot->pos);
+ dragger->dragging_started = false;
+
+ for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
+ (*i).set_pos (knot->pos);
+ (*i).updateBoxReprs();
+ (*i).updatePerspRepr();
}
}
- ***/
dragger->parent->updateDraggers ();
dragger->parent->updateDraggers ();
- dragger->updateBoxReprs ();
+ dragger->parent->updateLines ();
+ dragger->parent->updateBoxHandles ();
// TODO: Update box's paths and svg representation
// TODO: Update box's paths and svg representation
+ dragger->parent->dragging = false;
+
// TODO: Undo machinery!!
// TODO: Undo machinery!!
+ g_return_if_fail (dragger->parent);
+ g_return_if_fail (dragger->parent->document);
+ DocumentUndo::done(dragger->parent->document, SP_VERB_CONTEXT_3DBOX,
+ _("3D box: Move vanishing point"));
}
}
-VPDragger::VPDragger(VPDrag *parent, NR::Point p, VanishingPoint *vp)
-{
- if (vp == NULL) {
- g_print ("VP used to create the VPDragger is NULL. This can happen when shift-dragging knots.\n");
- g_print ("How to correctly handle this? Should we just ignore it, as we currently do?\n");
- //g_assert (vp != NULL);
+unsigned int VanishingPoint::global_counter = 0;
+
+// FIXME: Rename to something more meaningful!
+void
+VanishingPoint::set_pos(Proj::Pt2 const &pt) {
+ g_return_if_fail (_persp);
+ _persp->perspective_impl->tmat.set_image_pt (_axis, pt);
+}
+
+std::list<SPBox3D *>
+VanishingPoint::selectedBoxes(Inkscape::Selection *sel) {
+ std::list<SPBox3D *> sel_boxes;
+ for (GSList const* i = sel->itemList(); i != NULL; i = i->next) {
+ if (!SP_IS_BOX3D(i->data))
+ continue;
+ SPBox3D *box = SP_BOX3D(i->data);
+ if (this->hasBox(box)) {
+ sel_boxes.push_back (box);
+ }
}
}
- this->vps = NULL;
+ return sel_boxes;
+}
+VPDragger::VPDragger(VPDrag *parent, Geom::Point p, VanishingPoint &vp)
+{
this->parent = parent;
this->point = p;
this->point_original = p;
this->parent = parent;
this->point = p;
this->point_original = p;
- // create the knot
- this->knot = sp_knot_new (parent->desktop, NULL);
- this->knot->setMode(SP_KNOT_MODE_XOR);
- this->knot->setFill(VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL);
- this->knot->setStroke(0x000000ff, 0x000000ff, 0x000000ff);
- sp_knot_update_ctrl(this->knot);
-
- // move knot to the given point
- sp_knot_set_position (this->knot, &this->point, SP_KNOT_STATE_NORMAL);
- sp_knot_show (this->knot);
-
- // connect knot's signals
- g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (vp_knot_moved_handler), this);
- /***
- g_signal_connect (G_OBJECT (this->knot), "clicked", G_CALLBACK (vp_knot_clicked_handler), this);
- ***/
- g_signal_connect (G_OBJECT (this->knot), "grabbed", G_CALLBACK (vp_knot_grabbed_handler), this);
- g_signal_connect (G_OBJECT (this->knot), "ungrabbed", G_CALLBACK (vp_knot_ungrabbed_handler), this);
- /***
- g_signal_connect (G_OBJECT (this->knot), "doubleclicked", G_CALLBACK (vp_knot_doubleclicked_handler), this);
- ***/
-
- // add the initial VP (which may be NULL!)
- this->addVP (vp);
- //updateKnotShape();
+ this->dragging_started = false;
+
+ if (vp.is_finite()) {
+ // create the knot
+ this->knot = sp_knot_new (inkscape_active_desktop(), NULL);
+ this->knot->setMode(SP_KNOT_MODE_XOR);
+ this->knot->setFill(VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL, VP_KNOT_COLOR_NORMAL);
+ this->knot->setStroke(0x000000ff, 0x000000ff, 0x000000ff);
+ sp_knot_update_ctrl(this->knot);
+
+ // move knot to the given point
+ sp_knot_set_position (this->knot, this->point, SP_KNOT_STATE_NORMAL);
+ sp_knot_show (this->knot);
+
+ // connect knot's signals
+ g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (vp_knot_moved_handler), this);
+ g_signal_connect (G_OBJECT (this->knot), "grabbed", G_CALLBACK (vp_knot_grabbed_handler), this);
+ g_signal_connect (G_OBJECT (this->knot), "ungrabbed", G_CALLBACK (vp_knot_ungrabbed_handler), this);
+
+ // add the initial VP (which may be NULL!)
+ this->addVP (vp);
+ }
}
VPDragger::~VPDragger()
{
}
VPDragger::~VPDragger()
{
- // unselect if it was selected
- //this->parent->setDeselected(this);
-
// disconnect signals
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_moved_handler), this);
// disconnect signals
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_moved_handler), this);
- /***
- g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_clicked_handler), this);
- ***/
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_grabbed_handler), this);
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_ungrabbed_handler), this);
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_grabbed_handler), this);
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_ungrabbed_handler), this);
- /***
- g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_doubleclicked_handler), this);
- ***/
-
/* unref should call destroy */
g_object_unref (G_OBJECT (this->knot));
/* unref should call destroy */
g_object_unref (G_OBJECT (this->knot));
+}
+
+/**
+Updates the statusbar tip of the dragger knot, based on its draggables
+ */
+void
+VPDragger::updateTip ()
+{
+ if (this->knot && this->knot->tip) {
+ g_free (this->knot->tip);
+ this->knot->tip = NULL;
+ }
- g_slist_free (this->vps);
- this->vps = NULL;
+ guint num = this->numberOfBoxes();
+ if (this->vps.size() == 1) {
+ if (this->vps.front().is_finite()) {
+ this->knot->tip = g_strdup_printf (ngettext("<b>Finite</b> vanishing point shared by <b>%d</b> box",
+ "<b>Finite</b> vanishing point shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
+ num),
+ num);
+ } else {
+ // This won't make sense any more when infinite VPs are not shown on the canvas,
+ // but currently we update the status message anyway
+ this->knot->tip = g_strdup_printf (ngettext("<b>Infinite</b> vanishing point shared by <b>%d</b> box",
+ "<b>Infinite</b> vanishing point shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
+ num),
+ num);
+ }
+ } else {
+ int length = this->vps.size();
+ char *desc1 = g_strdup_printf ("Collection of <b>%d</b> vanishing points ", length);
+ char *desc2 = g_strdup_printf (ngettext("shared by <b>%d</b> box; drag with <b>Shift</b> to separate selected box(es)",
+ "shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
+ num),
+ num);
+ this->knot->tip = g_strconcat(desc1, desc2, NULL);
+ g_free (desc1);
+ g_free (desc2);
+ }
}
/**
}
/**
- * Adds a vanishing point to the dragger (also updates the position)
+ * Adds a vanishing point to the dragger (also updates the position if necessary);
+ * the perspective is stored separately, too, for efficiency in updating boxes.
*/
void
*/
void
-VPDragger::addVP (VanishingPoint *vp)
+VPDragger::addVP (VanishingPoint &vp, bool update_pos)
{
{
- if (vp == NULL) {
- g_print ("No VP present in addVP. We return without adding a new VP to the list.\n");
+ if (!vp.is_finite() || std::find (vps.begin(), vps.end(), vp) != vps.end()) {
+ // don't add infinite VPs; don't add the same VP twice
return;
}
return;
}
- vp->set_pos (this->point);
- this->vps = g_slist_prepend (this->vps, vp);
- //this->updateTip();
+ if (update_pos) {
+ vp.set_pos (this->point);
+ }
+ this->vps.push_front (vp);
+
+ this->updateTip();
}
void
}
void
-VPDragger::removeVP (VanishingPoint *vp)
+VPDragger::removeVP (VanishingPoint const &vp)
{
{
- if (vp == NULL) {
- g_print ("NULL vanishing point will not be removed.\n");
- return;
+ std::list<VanishingPoint>::iterator i = std::find (this->vps.begin(), this->vps.end(), vp);
+ if (i != this->vps.end()) {
+ this->vps.erase (i);
+ }
+ this->updateTip();
+}
+
+VanishingPoint *
+VPDragger::findVPWithBox (SPBox3D *box) {
+ for (std::list<VanishingPoint>::iterator vp = vps.begin(); vp != vps.end(); ++vp) {
+ if ((*vp).hasBox(box)) {
+ return &(*vp);
+ }
+ }
+ return NULL;
+}
+
+std::set<VanishingPoint*, less_ptr>
+VPDragger::VPsOfSelectedBoxes() {
+ std::set<VanishingPoint*, less_ptr> sel_vps;
+ VanishingPoint *vp;
+ // FIXME: Should we take the selection from the parent VPDrag? I guess it shouldn't make a difference.
+ Inkscape::Selection *sel = sp_desktop_selection(inkscape_active_desktop());
+ for (GSList const* i = sel->itemList(); i != NULL; i = i->next) {
+ if (!SP_IS_BOX3D(i->data))
+ continue;
+ SPBox3D *box = SP_BOX3D(i->data);
+ vp = this->findVPWithBox(box);
+ if (vp) {
+ sel_vps.insert (vp);
+ }
+ }
+ return sel_vps;
+}
+
+guint
+VPDragger::numberOfBoxes ()
+{
+ guint num = 0;
+ for (std::list<VanishingPoint>::iterator vp = vps.begin(); vp != vps.end(); ++vp) {
+ num += (*vp).numberOfBoxes();
+ }
+ return num;
+}
+
+bool
+VPDragger::hasPerspective (const Persp3D *persp)
+{
+ for (std::list<VanishingPoint>::iterator i = vps.begin(); i != vps.end(); ++i) {
+ if (persp3d_perspectives_coincide(persp, (*i).get_perspective())) {
+ return true;
+ }
}
}
- g_assert (this->vps != NULL);
- this->vps = g_slist_remove (this->vps, vp);
+ return false;
+}
+
+void
+VPDragger::mergePerspectives ()
+{
+ Persp3D *persp1, *persp2;
+ for (std::list<VanishingPoint>::iterator i = vps.begin(); i != vps.end(); ++i) {
+ persp1 = (*i).get_perspective();
+ for (std::list<VanishingPoint>::iterator j = i; j != vps.end(); ++j) {
+ persp2 = (*j).get_perspective();
+ if (persp1 == persp2) {
+ /* don't merge a perspective with itself */
+ continue;
+ }
+ if (persp3d_perspectives_coincide(persp1,persp2)) {
+ /* if perspectives coincide but are not the same, merge them */
+ persp3d_absorb(persp1, persp2);
+
+ this->parent->swap_perspectives_of_VPs(persp2, persp1);
- //this->updateTip();
+ SP_OBJECT(persp2)->deleteObject(false);
+ }
+ }
+ }
+}
+
+void
+VPDragger::updateBoxDisplays ()
+{
+ for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
+ (*i).updateBoxDisplays();
+ }
}
void
}
void
-VPDragger::reshapeBoxes (NR::Point const &p, Box3D::Axis axes)
+VPDragger::updateVPs (Geom::Point const &pt)
{
{
- Perspective3D *persp;
- for (GSList const* i = this->vps; i != NULL; i = i->next) {
- VanishingPoint *vp = (VanishingPoint *) i->data;
- // TODO: We can extract the VP directly from the box's perspective. Is that vanishing point identical to 'vp'?
- // Or is there duplicated information? If so, remove it and simplify the whole construction!
- vp->set_pos(p);
- persp = get_persp_of_VP (vp);
- Box3D::Axis axis = persp->get_axis_of_VP (vp);
- get_persp_of_VP (vp)->reshape_boxes (axis); // FIXME: we should only update the direction of the VP
+ for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
+ (*i).set_pos (pt);
}
}
void
}
}
void
-VPDragger::updateBoxReprs ()
+VPDragger::updateZOrders ()
{
{
- for (GSList *i = this->vps; i != NULL; i = i->next) {
- Box3D::get_persp_of_VP ((VanishingPoint *) i->data)->update_box_reprs ();
+ for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
+ persp3d_update_z_orders((*i).get_perspective());
}
}
}
}
-VPDrag::VPDrag (SPDesktop *desktop)
+void
+VPDragger::printVPs() {
+ g_print ("VPDragger at position (%f, %f):\n", point[Geom::X], point[Geom::Y]);
+ for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) {
+ g_print (" VP %s\n", (*i).axisString());
+ }
+}
+
+VPDrag::VPDrag (SPDocument *document)
{
{
- this->desktop = desktop;
+ this->document = document;
+ this->selection = sp_desktop_selection(inkscape_active_desktop());
+
this->draggers = NULL;
this->draggers = NULL;
- this->selection = sp_desktop_selection(desktop);
+ this->lines = NULL;
+ this->show_lines = true;
+ this->front_or_rear_lines = 0x1;
+
+ this->dragging = false;
this->sel_changed_connection = this->selection->connectChanged(
sigc::bind (
this->sel_changed_connection = this->selection->connectChanged(
sigc::bind (
);
this->updateDraggers ();
);
this->updateDraggers ();
- //this->updateLines ();
+ this->updateLines ();
}
VPDrag::~VPDrag()
}
VPDrag::~VPDrag()
}
g_list_free (this->draggers);
this->draggers = NULL;
}
g_list_free (this->draggers);
this->draggers = NULL;
+
+ for (GSList const *i = this->lines; i != NULL; i = i->next) {
+ gtk_object_destroy( GTK_OBJECT (i->data));
+ }
+ g_slist_free (this->lines);
+ this->lines = NULL;
}
/**
}
/**
{
for (GList const* i = this->draggers; i != NULL; i = i->next) {
VPDragger *dragger = (VPDragger *) i->data;
{
for (GList const* i = this->draggers; i != NULL; i = i->next) {
VPDragger *dragger = (VPDragger *) i->data;
- for (GSList const* j = dragger->vps; j != NULL; j = j->next) {
- VanishingPoint *vp2 = (VanishingPoint *) j->data;
- g_assert (vp2 != NULL);
-
+ for (std::list<VanishingPoint>::iterator j = dragger->vps.begin(); j != dragger->vps.end(); ++j) {
// TODO: Should we compare the pointers or the VPs themselves!?!?!?!
// TODO: Should we compare the pointers or the VPs themselves!?!?!?!
- //if ((*vp2) == vp) {
- if (vp2 == &vp) {
+ if (*j == vp) {
return (dragger);
}
}
return (dragger);
}
}
return NULL;
}
return NULL;
}
+void
+VPDrag::printDraggers ()
+{
+ g_print ("=== VPDrag info: =================================\n");
+ for (GList const* i = this->draggers; i != NULL; i = i->next) {
+ ((VPDragger *) i->data)->printVPs();
+ g_print ("========\n");
+ }
+ g_print ("=================================================\n");
+}
+
/**
* Regenerates the draggers list from the current selection; is called when selection is changed or modified
*/
void
VPDrag::updateDraggers ()
{
/**
* Regenerates the draggers list from the current selection; is called when selection is changed or modified
*/
void
VPDrag::updateDraggers ()
{
- /***
- while (selected) {
- selected = g_list_remove(selected, selected->data);
- }
- ***/
+ if (this->dragging)
+ return;
// delete old draggers
for (GList const* i = this->draggers; i != NULL; i = i->next) {
delete ((VPDragger *) i->data);
// delete old draggers
for (GList const* i = this->draggers; i != NULL; i = i->next) {
delete ((VPDragger *) i->data);
for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
SPItem *item = SP_ITEM(i->data);
for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
SPItem *item = SP_ITEM(i->data);
- //SPStyle *style = SP_OBJECT_STYLE (item);
+ if (!SP_IS_BOX3D (item)) continue;
+ SPBox3D *box = SP_BOX3D (item);
+
+ VanishingPoint vp;
+ for (int i = 0; i < 3; ++i) {
+ vp.set(box3d_get_perspective(box), Proj::axes[i]);
+ addDragger (vp);
+ }
+ }
+}
+
+/**
+Regenerates the lines list from the current selection; is called on each move
+of a dragger, so that lines are always in sync with the actual perspective
+*/
+void
+VPDrag::updateLines ()
+{
+ // delete old lines
+ for (GSList const *i = this->lines; i != NULL; i = i->next) {
+ gtk_object_destroy( GTK_OBJECT (i->data));
+ }
+ g_slist_free (this->lines);
+ this->lines = NULL;
- if (!SP_IS_3DBOX (item)) continue;
- SP3DBox *box = SP_3DBOX (item);
+ // do nothing if perspective lines are currently disabled
+ if (this->show_lines == 0) return;
- // FIXME: Get the VPs from the selection!!!!
- //addDragger (Box3D::Perspective3D::current_perspective->get_vanishing_point(Box3D::X));
- //addDragger (Box3D::Perspective3D::current_perspective->get_vanishing_point(Box3D::Y));
- //addDragger (Box3D::Perspective3D::current_perspective->get_vanishing_point(Box3D::Z));
+ g_return_if_fail (this->selection != NULL);
+
+ for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
+ if (!SP_IS_BOX3D(i->data)) continue;
+ SPBox3D *box = SP_BOX3D (i->data);
+
+ this->drawLinesForFace (box, Proj::X);
+ this->drawLinesForFace (box, Proj::Y);
+ this->drawLinesForFace (box, Proj::Z);
+ }
+}
+
+void
+VPDrag::updateBoxHandles ()
+{
+ // FIXME: Is there a way to update the knots without accessing the
+ // (previously) statically linked function KnotHolder::update_knots?
+
+ GSList *sel = (GSList *) selection->itemList();
+ if (!sel)
+ return; // no selection
+
+ if (g_slist_length (sel) > 1) {
+ // Currently we only show handles if a single box is selected
+ return;
+ }
- //Box3D::Perspective3D *persp = box->perspective;
- Box3D::Perspective3D *persp = Box3D::get_persp_of_box (box);
- addDragger (persp->get_vanishing_point(Box3D::X));
- addDragger (persp->get_vanishing_point(Box3D::Y));
- addDragger (persp->get_vanishing_point(Box3D::Z));
+ SPEventContext *ec = inkscape_active_event_context();
+ g_assert (ec != NULL);
+ if (ec->shape_editor != NULL) {
+ ec->shape_editor->update_knotholder();
+ }
+}
+
+void
+VPDrag::updateBoxReprs ()
+{
+ for (GList *i = this->draggers; i != NULL; i = i->next) {
+ VPDragger *dragger = (VPDragger *) i->data;
+ for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
+ (*i).updateBoxReprs();
+ }
+ }
+}
+
+void
+VPDrag::updateBoxDisplays ()
+{
+ for (GList *i = this->draggers; i != NULL; i = i->next) {
+ VPDragger *dragger = (VPDragger *) i->data;
+ for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) {
+ (*i).updateBoxDisplays();
+ }
+ }
+}
+
+
+/**
+ * Depending on the value of all_lines, draw the front and/or rear perspective lines starting from the given corners.
+ */
+void
+VPDrag::drawLinesForFace (const SPBox3D *box, Proj::Axis axis) //, guint corner1, guint corner2, guint corner3, guint corner4)
+{
+ guint color;
+ switch (axis) {
+ // TODO: Make color selectable by user
+ case Proj::X: color = VP_LINE_COLOR_STROKE_X; break;
+ case Proj::Y: color = VP_LINE_COLOR_STROKE_Y; break;
+ case Proj::Z: color = VP_LINE_COLOR_STROKE_Z; break;
+ default: g_assert_not_reached();
+ }
+
+ Geom::Point corner1, corner2, corner3, corner4;
+ box3d_corners_for_PLs (box, axis, corner1, corner2, corner3, corner4);
+
+ g_return_if_fail (box3d_get_perspective(box));
+ Proj::Pt2 vp = persp3d_get_VP (box3d_get_perspective(box), axis);
+ if (vp.is_finite()) {
+ // draw perspective lines for finite VPs
+ Geom::Point pt = vp.affine();
+ if (this->front_or_rear_lines & 0x1) {
+ // draw 'front' perspective lines
+ this->addLine (corner1, pt, color);
+ this->addLine (corner2, pt, color);
+ }
+ if (this->front_or_rear_lines & 0x2) {
+ // draw 'rear' perspective lines
+ this->addLine (corner3, pt, color);
+ this->addLine (corner4, pt, color);
+ }
+ } else {
+ // draw perspective lines for infinite VPs
+ boost::optional<Geom::Point> pt1, pt2, pt3, pt4;
+ Persp3D *persp = box3d_get_perspective(box);
+ SPDesktop *desktop = inkscape_active_desktop (); // FIXME: Store the desktop in VPDrag
+ Box3D::PerspectiveLine pl (corner1, axis, persp);
+ pt1 = pl.intersection_with_viewbox(desktop);
+
+ pl = Box3D::PerspectiveLine (corner2, axis, persp);
+ pt2 = pl.intersection_with_viewbox(desktop);
+
+ pl = Box3D::PerspectiveLine (corner3, axis, persp);
+ pt3 = pl.intersection_with_viewbox(desktop);
+
+ pl = Box3D::PerspectiveLine (corner4, axis, persp);
+ pt4 = pl.intersection_with_viewbox(desktop);
+
+ if (!pt1 || !pt2 || !pt3 || !pt4) {
+ // some perspective lines s are outside the canvas; currently we don't draw any of them
+ return;
+ }
+ if (this->front_or_rear_lines & 0x1) {
+ // draw 'front' perspective lines
+ this->addLine (corner1, *pt1, color);
+ this->addLine (corner2, *pt2, color);
+ }
+ if (this->front_or_rear_lines & 0x2) {
+ // draw 'rear' perspective lines
+ this->addLine (corner3, *pt3, color);
+ this->addLine (corner4, *pt4, color);
+ }
}
}
/**
* If there already exists a dragger within MERGE_DIST of p, add the VP to it;
* otherwise create new dragger and add it to draggers list
}
}
/**
* If there already exists a dragger within MERGE_DIST of p, add the VP to it;
* otherwise create new dragger and add it to draggers list
+ * We also store the corresponding perspective in case it is not already present.
*/
void
*/
void
-VPDrag::addDragger (VanishingPoint *vp)
+VPDrag::addDragger (VanishingPoint &vp)
{
{
- if (vp == NULL) {
- g_print ("Warning: The VP in addDragger is already NULL. Aborting.\n)");
- g_assert (vp != NULL);
+ if (!vp.is_finite()) {
+ // don't create draggers for infinite vanishing points
+ return;
}
}
- NR::Point p = vp->get_pos();
+ Geom::Point p = vp.get_pos();
for (GList *i = this->draggers; i != NULL; i = i->next) {
VPDragger *dragger = (VPDragger *) i->data;
for (GList *i = this->draggers; i != NULL; i = i->next) {
VPDragger *dragger = (VPDragger *) i->data;
- if (NR::L2 (dragger->point - p) < MERGE_DIST) {
+ if (Geom::L2 (dragger->point - p) < MERGE_DIST) {
// distance is small, merge this draggable into dragger, no need to create new dragger
dragger->addVP (vp);
// distance is small, merge this draggable into dragger, no need to create new dragger
dragger->addVP (vp);
- //dragger->updateKnotShape();
return;
}
}
return;
}
}
this->draggers = g_list_append (this->draggers, new_dragger);
}
this->draggers = g_list_append (this->draggers, new_dragger);
}
-} // namespace Box3D
-
+void
+VPDrag::swap_perspectives_of_VPs(Persp3D *persp2, Persp3D *persp1)
+{
+ // iterate over all VP in all draggers and replace persp2 with persp1
+ for (GList *i = this->draggers; i != NULL; i = i->next) {
+ for (std::list<VanishingPoint>::iterator j = ((VPDragger *) (i->data))->vps.begin();
+ j != ((VPDragger *) (i->data))->vps.end(); ++j) {
+ if ((*j).get_perspective() == persp2) {
+ (*j).set_perspective(persp1);
+ }
+ }
+ }
+}
+
+/**
+Create a line from p1 to p2 and add it to the lines list
+ */
+void
+VPDrag::addLine (Geom::Point p1, Geom::Point p2, guint32 rgba)
+{
+ SPCanvasItem *line = sp_canvas_item_new(sp_desktop_controls(inkscape_active_desktop()), SP_TYPE_CTRLLINE, NULL);
+ sp_ctrlline_set_coords(SP_CTRLLINE(line), p1, p2);
+ if (rgba != VP_LINE_COLOR_FILL) // fill is the default, so don't set color for it to speed up redraw
+ sp_ctrlline_set_rgba32 (SP_CTRLLINE(line), rgba);
+ sp_canvas_item_show (line);
+ this->lines = g_slist_append (this->lines, line);
+}
+
+} // namespace Box3D
+
/*
Local Variables:
mode:c++
/*
Local Variables:
mode:c++
fill-column:99
End:
*/
fill-column:99
End:
*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :