Code

First stage of draggable vanishing points (no snapping/unsnapping yet)
authorcilix42 <cilix42@users.sourceforge.net>
Mon, 6 Aug 2007 07:37:24 +0000 (07:37 +0000)
committercilix42 <cilix42@users.sourceforge.net>
Mon, 6 Aug 2007 07:37:24 +0000 (07:37 +0000)
src/box3d-context.cpp
src/box3d-context.h
src/object-edit.cpp
src/perspective3d.cpp
src/perspective3d.h
src/vanishing-point.cpp
src/vanishing-point.h

index 5f1029576a6420e68a545fb61aa691f424f3a47f..3096ad17985c268d9e3ed897a8d62cb037082be4 100644 (file)
@@ -113,6 +113,8 @@ static void sp_3dbox_context_init(SP3DBoxContext *box3d_context)
     box3d_context->ctrl_dragged = false;
     box3d_context->extruded = false;
     
+    box3d_context->_vpdrag = NULL;
+
     new (&box3d_context->sel_changed_connection) sigc::connection();
 }
 
@@ -123,6 +125,9 @@ static void sp_3dbox_context_dispose(GObject *object)
 
     ec->enableGrDrag(false);
 
+    delete (bc->_vpdrag);
+    bc->_vpdrag = NULL;
+
     bc->sel_changed_connection.disconnect();
     bc->sel_changed_connection.~connection();
 
@@ -213,6 +218,8 @@ static void sp_3dbox_context_setup(SPEventContext *ec)
         sigc::bind(sigc::ptr_fun(&sp_3dbox_context_selection_changed), (gpointer)bc)
     );
 
+    bc->_vpdrag = new Box3D::VPDrag(ec->desktop);
+
     if (prefs_get_int_attribute("tools.shapes", "selcue", 0) != 0) {
         ec->enableSelectionCue();
     }
@@ -520,6 +527,9 @@ static void sp_3dbox_drag(SP3DBoxContext &bc, guint state)
         }
 
         bc.item->updateRepr();
+        // TODO: It would be nice to show the VPs during dragging, but since there is no selection
+        //       at this point (only after finishing the box), we must do this "manually"
+        bc._vpdrag->updateDraggers();
 
         sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 5);
     }
index bf359ebe7ff58884e498a0ed041c1cd42aff7963..6d2e560d45f610ad4ca03ef06e50e571237c3480 100644 (file)
@@ -48,6 +48,8 @@ struct SP3DBoxContext : public SPEventContext {
     bool ctrl_dragged; /* whether we are ctrl-dragging */
     bool extruded; /* whether shift-dragging already occured (i.e. the box is already extruded) */
 
+    Box3D::VPDrag * _vpdrag;
+
     /* temporary member until the precise behaviour is sorted out */
     static guint number_of_handles;
 
index 6eff465f8ab308b5bf19a3ce15c41f7be4fd7fd0..e8390da20b9fb1b9ae4315df559df827a1826033 100644 (file)
@@ -546,6 +546,7 @@ static void sp_3dbox_knot_set(SPItem *item, guint knot_id, Box3D::Axis direction
         sp_3dbox_move_corner_in_XY_plane (box, knot_id, new_pos * i2d, (state & GDK_SHIFT_MASK) ? direction : Box3D::XY);
     }
     sp_3dbox_update_curves (box);
+    sp_3dbox_set_ratios (box);
 }
 
 static NR::Point sp_3dbox_knot_get(SPItem *item, guint knot_id)
@@ -615,6 +616,7 @@ static void sp_3dbox_knot_set_uniformly(SPItem *item, guint knot_id, Box3D::Axis
         sp_3dbox_move_corner_in_Z_direction (box, knot_id, new_pos * i2d,  (state & GDK_SHIFT_MASK));
     }
     sp_3dbox_update_curves (box);
+    sp_3dbox_set_ratios (box);
 }
 
 static void sp_3dbox_knot0_set_uniformly(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
index dd05ec7c7cef6ca7563e75782a2a78ce25200908..e5b1f62126f25983d026002cc3a8e5d3fefc3bbc 100644 (file)
@@ -38,6 +38,23 @@ get_persp_of_box (const SP3DBox *box)
     g_assert_not_reached();
 }
 
+Perspective3D *
+get_persp_of_VP (const VanishingPoint *vp)
+{
+    Perspective3D *persp;
+    for (GSList *p = Perspective3D::perspectives; p != NULL; p = p->next) {
+        persp = (Perspective3D *) p->data;
+        // we compare the pointers, not the position/state of the VPs; is this correct?
+        if (persp->get_vanishing_point (Box3D::X) == vp ||
+            persp->get_vanishing_point (Box3D::Y) == vp ||
+            persp->get_vanishing_point (Box3D::Z) == vp)
+            return persp;
+    }
+
+    g_warning ("Stray vanishing point!\n");
+    g_assert_not_reached();
+}
+
 /**
  * Computes the intersection of the two perspective lines from pt1 and pt2 to the respective
  * vanishing points in the given directions.
@@ -88,6 +105,37 @@ Perspective3D::~Perspective3D ()
 {
     Perspective3D::remove_perspective (this);
 
+    // Remove the VPs from their draggers
+    SPEventContext *ec = inkscape_active_event_context();
+    if (SP_IS_3DBOX_CONTEXT (ec)) {
+        SP3DBoxContext *bc = SP_3DBOX_CONTEXT (ec);
+        // we need to check if there are any draggers because the selection
+        // is temporarily empty during duplication of boxes, e.g.
+        if (bc->_vpdrag->draggers != NULL) {
+            /***
+            g_assert (bc->_vpdrag->getDraggerFor (*vp_x) != NULL);
+            g_assert (bc->_vpdrag->getDraggerFor (*vp_y) != NULL);
+            g_assert (bc->_vpdrag->getDraggerFor (*vp_z) != NULL);
+            bc->_vpdrag->getDraggerFor (*vp_x)->removeVP (vp_x);
+            bc->_vpdrag->getDraggerFor (*vp_y)->removeVP (vp_y);
+            bc->_vpdrag->getDraggerFor (*vp_z)->removeVP (vp_z);
+            ***/
+            // TODO: the temporary perspective created when building boxes is not linked to any dragger, hence
+            //       we need to do the following checks. Maybe it would be better to not create a temporary
+            //       perspective at all but simply compare the VPs manually in sp_3dbox_build.
+            VPDragger * dragger;
+            dragger = bc->_vpdrag->getDraggerFor (*vp_x);
+            if (dragger)
+                dragger->removeVP (vp_x);
+            dragger = bc->_vpdrag->getDraggerFor (*vp_y);
+            if (dragger)
+                dragger->removeVP (vp_y);
+            dragger = bc->_vpdrag->getDraggerFor (*vp_z);
+            if (dragger)
+                dragger->removeVP (vp_z);
+        }
+    }
+
     delete vp_x;
     delete vp_y;
     delete vp_z;
@@ -145,6 +193,17 @@ Perspective3D::set_vanishing_point (Box3D::Axis const dir, VanishingPoint const
     }
 }
 
+Axis
+Perspective3D::get_axis_of_VP (VanishingPoint *vp)
+{
+    if (vp == vp_x) return X;
+    if (vp == vp_y) return Y;
+    if (vp == vp_z) return Z;
+
+    g_warning ("Vanishing point not present in the perspective.\n");
+    return NONE;
+}
+
 void
 Perspective3D::set_vanishing_point (Box3D::Axis const dir, gdouble pt_x, gdouble pt_y, gdouble dir_x, gdouble dir_y, VPState st)
 {
index 43b0ca3d3776fb56c0ad7eeb603292a6d1d69671..c3e07b23b3c0b357ee83d69de6e46c8b48444e35 100644 (file)
@@ -30,6 +30,7 @@ public:
     bool operator== (Perspective3D const &other);
 
     VanishingPoint *get_vanishing_point (Box3D::Axis const dir);
+    Axis get_axis_of_VP (VanishingPoint *vp);
     void set_vanishing_point (Box3D::Axis const dir, VanishingPoint const &pt);
     void set_vanishing_point (Box3D::Axis const dir, gdouble pt_x, gdouble pt_y, gdouble dir_x, gdouble dir_y, VPState st);
     void add_box (SP3DBox *box);
@@ -57,6 +58,7 @@ private:
 };
 
 Perspective3D * get_persp_of_box (const SP3DBox *box);
+Perspective3D * get_persp_of_VP (const VanishingPoint *vp);
 
 NR::Point perspective_intersection (NR::Point pt1, Box3D::Axis dir1, NR::Point pt2, Box3D::Axis dir2, Perspective3D *persp);
 NR::Point perspective_line_snap (NR::Point pt, Box3D::Axis dir, NR::Point ext_pt, Perspective3D *persp);
index 5f40936cf5ca637da07e98c252eb15b223274a43..edf769c176388f9c6164836167639bc3fda1ad44 100644 (file)
  */
 
 #include "vanishing-point.h"
+#include "desktop-handles.h"
+#include "box3d.h"
 
 namespace Box3D {
 
+#define VP_KNOT_COLOR_NORMAL 0xffffff00
+#define VP_KNOT_COLOR_SELECTED 0x0000ff00
+
+#define VP_LINE_COLOR_FILL 0x0000ff7f
+#define VP_LINE_COLOR_STROKE_X 0xff00007f
+#define VP_LINE_COLOR_STROKE_Y 0x0000ff7f
+#define VP_LINE_COLOR_STROKE_Z 0xffff007f
+
+// screen pixels between knots when they snap:
+#define SNAP_DIST 5
+
+// absolute distance between gradient points for them to become a single dragger when the drag is created:
+#define MERGE_DIST 0.1
+
+// knot shapes corresponding to GrPointType enum
+SPKnotShapeType vp_knot_shapes [] = {
+        SP_KNOT_SHAPE_SQUARE, // VP_FINITE
+        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.
@@ -45,6 +67,8 @@ VanishingPoint::VanishingPoint(VanishingPoint const &rhs) : NR::Point (rhs)
     this->v_dir = rhs.v_dir;
 }
 
+VanishingPoint::~VanishingPoint () {}
+
 bool VanishingPoint::operator== (VanishingPoint const &other)
 {
     // Should we compare the parent perspectives, too? Probably not.
@@ -98,6 +122,321 @@ void VanishingPoint::draw(Box3D::Axis const axis)
     }
 }
 
+static void
+vp_drag_sel_changed(Inkscape::Selection *selection, gpointer data)
+{
+    VPDrag *drag = (VPDrag *) data;
+    drag->updateDraggers ();
+    //drag->updateLines ();
+}
+
+static void
+vp_drag_sel_modified (Inkscape::Selection *selection, guint flags, gpointer data)
+{
+    VPDrag *drag = (VPDrag *) data;
+    /***
+    if (drag->local_change) {
+        drag->local_change = false;
+    } else {
+        drag->updateDraggers ();
+    }
+    ***/
+    //drag->updateLines ();
+}
+
+static void
+vp_knot_moved_handler (SPKnot *knot, NR::Point const *ppointer, guint state, gpointer data)
+{
+    g_warning ("Please implement vp_knot_moved_handler.\n");
+    VPDragger *dragger = (VPDragger *) data;
+    //VPDrag *drag = dragger->parent;
+
+    NR::Point p = *ppointer;
+
+    dragger->point = p;
+
+    dragger->reshapeBoxes (p, Box3D::XYZ);
+    //dragger->parent->updateLines ();
+
+    //drag->local_change = false;
+}
+
+static void
+vp_knot_grabbed_handler (SPKnot *knot, unsigned int state, gpointer data)
+{
+    VPDragger *dragger = (VPDragger *) data;
+
+    //sp_canvas_force_full_redraw_after_interruptions(dragger->parent->desktop->canvas, 5);
+}
+
+static void
+vp_knot_ungrabbed_handler (SPKnot *knot, guint state, gpointer data)
+{
+    g_warning ("Please fully implement vp_knot_ungrabbed_handler.\n");
+    
+    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;
+
+    /***
+    VanishingPoint *vp;
+    for (GSList *i = dragger->vps; i != NULL; i = i->next) {
+        vp = (VanishingPoint *) i->data;
+        vp->set_pos (knot->pos);
+    }
+    ***/
+
+    dragger->parent->updateDraggers ();
+    dragger->updateBoxReprs ();
+
+    // TODO: Update box's paths and svg representation
+
+    // TODO: Undo machinery!!
+}
+
+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);
+    }
+    this->vps = NULL;
+
+    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();
+}
+
+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);
+    /***
+    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_doubleclicked_handler), this);
+    ***/
+
+    /* unref should call destroy */
+    g_object_unref (G_OBJECT (this->knot));
+
+    g_slist_free (this->vps);
+    this->vps = NULL;
+}
+
+/**
+ * Adds a vanishing point to the dragger (also updates the position)
+ */
+void
+VPDragger::addVP (VanishingPoint *vp)
+{
+    if (vp == NULL) {
+        g_print ("No VP present in addVP. We return without adding a new VP to the list.\n");
+        return;
+    }
+    vp->set_pos (this->point);
+    this->vps = g_slist_prepend (this->vps, vp);
+
+    //this->updateTip();
+}
+
+void
+VPDragger::removeVP (VanishingPoint *vp)
+{
+    if (vp == NULL) {
+        g_print ("NULL vanishing point will not be removed.\n");
+        return;
+    }
+    g_assert (this->vps != NULL);
+    this->vps = g_slist_remove (this->vps, vp);
+
+    //this->updateTip();
+}
+
+void
+VPDragger::reshapeBoxes (NR::Point const &p, Box3D::Axis axes)
+{
+    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
+    }
+}
+
+void
+VPDragger::updateBoxReprs ()
+{
+    for (GSList *i = this->vps; i != NULL; i = i->next) {
+        Box3D::get_persp_of_VP ((VanishingPoint *) i->data)->update_box_reprs ();
+    }
+}
+
+VPDrag::VPDrag (SPDesktop *desktop)
+{
+    this->desktop = desktop;
+    this->draggers = NULL;
+    this->selection = sp_desktop_selection(desktop);
+
+    this->sel_changed_connection = this->selection->connectChanged(
+        sigc::bind (
+            sigc::ptr_fun(&vp_drag_sel_changed),
+            (gpointer)this )
+
+        );
+    this->sel_modified_connection = this->selection->connectModified(
+        sigc::bind(
+            sigc::ptr_fun(&vp_drag_sel_modified),
+            (gpointer)this )
+        );
+
+    this->updateDraggers ();
+    //this->updateLines ();
+}
+
+VPDrag::~VPDrag()
+{
+    this->sel_changed_connection.disconnect();
+    this->sel_modified_connection.disconnect();
+
+    for (GList *l = this->draggers; l != NULL; l = l->next) {
+        delete ((VPDragger *) l->data);
+    }
+    g_list_free (this->draggers);
+    this->draggers = NULL;
+}
+
+/**
+ * Select the dragger that has the given VP.
+ */
+VPDragger *
+VPDrag::getDraggerFor (VanishingPoint const &vp)
+{
+    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);
+
+            // TODO: Should we compare the pointers or the VPs themselves!?!?!?!
+            //if ((*vp2) == vp) {
+            if (vp2 == &vp) {
+                return (dragger);
+            }
+        }
+    }
+    return NULL;
+}
+
+/**
+ * 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);
+    }
+    ***/
+    // delete old draggers
+    for (GList const* i = this->draggers; i != NULL; i = i->next) {
+        delete ((VPDragger *) i->data);
+    }
+    g_list_free (this->draggers);
+    this->draggers = NULL;
+
+    g_return_if_fail (this->selection != NULL);
+
+    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_3DBOX (item)) continue;
+        SP3DBox *box = SP_3DBOX (item);
+
+        // 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));
+
+        //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));
+    }
+}
+
+/**
+ * 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
+ */
+void
+VPDrag::addDragger (VanishingPoint *vp)
+{
+    if (vp == NULL) {
+        g_print ("Warning: The VP in addDragger is already NULL. Aborting.\n)");
+        g_assert (vp != NULL);
+    }
+    NR::Point p = vp->get_pos();
+
+    for (GList *i = this->draggers; i != NULL; i = i->next) {
+        VPDragger *dragger = (VPDragger *) i->data;
+        if (NR::L2 (dragger->point - p) < MERGE_DIST) {
+            // distance is small, merge this draggable into dragger, no need to create new dragger
+            dragger->addVP (vp);
+            //dragger->updateKnotShape();
+            return;
+        }
+    }
+
+    VPDragger *new_dragger = new VPDragger(this, p, vp);
+    // fixme: draggers should be added AFTER the last one: this way tabbing through them will be from begin to end.
+    this->draggers = g_list_append (this->draggers, new_dragger);
+}
+
 } // namespace Box3D 
  
 /*
index 918b27fc76876c79bc20bec4507af2d2d18c0b92..58b3b142727c8fd93b8f1b8b28d8547e5879e818 100644 (file)
@@ -13,6 +13,8 @@
 #define SEEN_VANISHING_POINT_H
 
 #include "libnr/nr-point.h"
+#include "knot.h"
+#include "selection.h"
 #include "axis-manip.h"
 
 #include "line-geometry.h" // TODO: Remove this include as soon as we don't need create_canvas_(point|line) any more.
@@ -46,6 +48,7 @@ public:
     VanishingPoint(NR::Coord x, NR::Coord y, VPState const state);
     VanishingPoint(NR::Coord x, NR::Coord y, NR::Coord dir_x, NR::Coord dir_y);
     VanishingPoint(VanishingPoint const &rhs);
+    ~VanishingPoint();
 
     bool operator== (VanishingPoint const &other);
 
@@ -67,6 +70,58 @@ public:
 private:
 };
 
+class VPDrag;
+
+struct VPDragger {
+public:
+    VPDragger(VPDrag *parent, NR::Point p, VanishingPoint *vp);
+    ~VPDragger();
+
+    VPDrag *parent;
+    SPKnot *knot;
+
+    // position of the knot, desktop coords
+    NR::Point point;
+    // position of the knot before it began to drag; updated when released
+    NR::Point point_original;
+
+    GSList *vps; // the list of vanishing points
+
+    void addVP(VanishingPoint *vp);
+    void removeVP(VanishingPoint *vp);
+
+    void reshapeBoxes(NR::Point const &p, Box3D::Axis axes);
+    void updateBoxReprs();
+};
+
+struct VPDrag {
+public:
+    VPDrag(SPDesktop *desktop);
+    ~VPDrag();
+
+    VPDragger *getDraggerFor (VanishingPoint const &vp);
+
+    //void grabKnot (VanishingPoint const &vp, gint x, gint y, guint32 etime);
+
+    bool local_change;
+
+    SPDesktop *desktop;
+    GList *draggers;
+    //GSList *lines;
+
+    void updateDraggers ();
+    //void updateLines ();
+
+private:
+    //void deselect_all();
+
+    //void addLine (NR::Point p1, NR::Point p2, guint32 rgba);
+    void addDragger (VanishingPoint *vp);
+
+    Inkscape::Selection *selection;
+    sigc::connection sel_changed_connection;
+    sigc::connection sel_modified_connection;
+};
 
 } // namespace Box3D