Code

Draw perspective lines; provide shortcuts to toggle their visibility and the corners...
authorcilix42 <cilix42@users.sourceforge.net>
Mon, 6 Aug 2007 08:01:03 +0000 (08:01 +0000)
committercilix42 <cilix42@users.sourceforge.net>
Mon, 6 Aug 2007 08:01:03 +0000 (08:01 +0000)
src/box3d-context.cpp
src/box3d.cpp
src/box3d.h
src/object-edit.cpp
src/perspective3d.cpp
src/vanishing-point.cpp
src/vanishing-point.h

index 3096ad17985c268d9e3ed897a8d62cb037082be4..a753f5f5004ede9b660ef773870b9d2c885e73b9 100644 (file)
@@ -440,13 +440,67 @@ static gint sp_3dbox_context_root_handler(SPEventContext *event_context, GdkEven
             ret = true;
             break;
 
+        case GDK_L:
+        case GDK_l:
+            bc->_vpdrag->show_lines = !bc->_vpdrag->show_lines;
+            bc->_vpdrag->updateLines();
+            ret = true;
+            break;
+
+        case GDK_A:
+        case GDK_a:
+            if (MOD__CTRL) break; // Don't catch Ctrl+A ("select all")
+            if (bc->_vpdrag->show_lines) {
+                bc->_vpdrag->front_or_rear_lines = bc->_vpdrag->front_or_rear_lines ^ 0x2; // toggle rear PLs
+            }
+            bc->_vpdrag->updateLines();
+            ret = true;
+            break;
+
         case GDK_x:
         case GDK_X:
-            if (MOD__ALT_ONLY) {
-                // desktop->setToolboxFocusTo ("altx-rect");
-                ret = TRUE;
+        {
+            if (MOD__CTRL) break; // Don't catch Ctrl+X ('cut') and Ctrl+Shift+X ('open XML editor')
+            // FIXME: Shouldn't we access _vpdrag->selection instead?
+            Inkscape::Selection *selection = sp_desktop_selection (bc->_vpdrag->desktop);
+            for (GSList const *i = selection->itemList(); i != NULL; i = i->next) {
+                if (!SP_IS_3DBOX (i->data)) continue;
+                sp_3dbox_switch_front_face (SP_3DBOX (i->data), Box3D::X);
+             }
+            bc->_vpdrag->updateLines();
+            ret = true;
+            break;
+        }
+        case GDK_y:
+        case GDK_Y:
+        {
+            if (MOD__CTRL) break; // Don't catch Ctrl+Y ("redo")
+            // FIXME: Shouldn't we access _vpdrag->selection instead?
+            Inkscape::Selection *selection = sp_desktop_selection (bc->_vpdrag->desktop);
+            for (GSList const *i = selection->itemList(); i != NULL; i = i->next) {
+                if (!SP_IS_3DBOX (i->data)) continue;
+                sp_3dbox_switch_front_face (SP_3DBOX (i->data), Box3D::Y);
+            }
+            bc->_vpdrag->updateLines();
+            ret = true;
+            break;
+        }
+
+        case GDK_z:
+        case GDK_Z:
+        {
+            if (MOD__CTRL) break; // Don't catch Ctrl+Z ("undo")
+            // FIXME: Shouldn't we access _vpdrag->selection instead?
+            Inkscape::Selection *selection = sp_desktop_selection (bc->_vpdrag->desktop);
+            for (GSList const *i = selection->itemList(); i != NULL; i = i->next) {
+                if (!SP_IS_3DBOX (i->data)) continue;
+                sp_3dbox_switch_front_face (SP_3DBOX (i->data), Box3D::Z);
             }
+            bc->_vpdrag->updateLines();
+            ret = true;
             break;
+        }
 
         case GDK_Escape:
             sp_desktop_selection(desktop)->clear();
index 455a7889c16192adb209f90ddc463e30845b671a..e4bda5d9c5a4a30ed0830b9f6badacf151e465f3 100644 (file)
@@ -103,6 +103,8 @@ sp_3dbox_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr
 
     box->my_counter = counter++;
 
+    box->front_bits = 0x0;
+
     if (repr->attribute ("inkscape:perspective") == NULL) {
         // we are creating a new box; link it to the current perspective
         Box3D::Perspective3D::current_perspective->add_box (box);
@@ -295,6 +297,15 @@ void sp_3dbox_set_ratios (SP3DBox *box, Box3D::Axis axes)
     }
 }
 
+void
+sp_3dbox_switch_front_face (SP3DBox *box, Box3D::Axis axis)
+{
+    if (Box3D::get_persp_of_box (box)->get_vanishing_point (axis)->is_finite()) {
+        box->front_bits = box->front_bits ^ axis;
+    }
+}
+
+
 void
 sp_3dbox_position_set (SP3DBoxContext &bc)
 {
@@ -566,6 +577,83 @@ sp_3dbox_get_svg_descr_of_persp (Box3D::Perspective3D *persp)
     return g_strdup(os.str().c_str());
 }
 
+void sp_3dbox_update_perspective_lines()
+{
+    SPEventContext *ec = inkscape_active_event_context();
+    if (!SP_IS_3DBOX_CONTEXT (ec))
+        return;
+
+    SP_3DBOX_CONTEXT (ec)->_vpdrag->updateLines();
+}
+
+/*
+ * Manipulates corner1 through corner4 to contain the indices of the corners
+ * from which the perspective lines in the direction of 'axis' emerge
+ */
+void sp_3dbox_corners_for_perspective_lines (const SP3DBox * box, Box3D::Axis axis, 
+                                            NR::Point &corner1, NR::Point &corner2, NR::Point &corner3, NR::Point &corner4)
+{
+    // along which axis to switch when takint
+    Box3D::Axis switch_axis;
+    if (axis == Box3D::X || axis == Box3D::Y) {
+        switch_axis = (box->front_bits & axis) ? Box3D::Z : Box3D::NONE;
+    } else {
+        switch_axis = (box->front_bits & axis) ? Box3D::X : Box3D::NONE;
+    }
+
+    switch (axis) {
+        case Box3D::X:
+            corner1 = sp_3dbox_get_corner_along_edge (box, 0 ^ switch_axis, axis, Box3D::REAR);
+            corner2 = sp_3dbox_get_corner_along_edge (box, 2 ^ switch_axis, axis, Box3D::REAR);
+            corner3 = sp_3dbox_get_corner_along_edge (box, 4 ^ switch_axis, axis, Box3D::REAR);
+            corner4 = sp_3dbox_get_corner_along_edge (box, 6 ^ switch_axis, axis, Box3D::REAR);
+            break;
+        case Box3D::Y:
+            corner1 = sp_3dbox_get_corner_along_edge (box, 0 ^ switch_axis, axis, Box3D::REAR);
+            corner2 = sp_3dbox_get_corner_along_edge (box, 1 ^ switch_axis, axis, Box3D::REAR);
+            corner3 = sp_3dbox_get_corner_along_edge (box, 4 ^ switch_axis, axis, Box3D::REAR);
+            corner4 = sp_3dbox_get_corner_along_edge (box, 5 ^ switch_axis, axis, Box3D::REAR);
+            break;
+        case Box3D::Z:
+            corner1 = sp_3dbox_get_corner_along_edge (box, 1 ^ switch_axis, axis, Box3D::REAR);
+            corner2 = sp_3dbox_get_corner_along_edge (box, 3 ^ switch_axis, axis, Box3D::REAR);
+            corner3 = sp_3dbox_get_corner_along_edge (box, 0 ^ switch_axis, axis, Box3D::REAR);
+            corner4 = sp_3dbox_get_corner_along_edge (box, 2 ^ switch_axis, axis, Box3D::REAR);
+            break;
+    }            
+}
+
+/**
+ * Returns the id of the corner on the edge along 'axis' and passing through 'corner' that
+ * lies on the front/rear face in this direction.
+ */
+guint
+sp_3dbox_get_corner_id_along_edge (const SP3DBox *box, guint corner, Box3D::Axis axis, Box3D::FrontOrRear rel_pos)
+{
+    guint result;
+    guint other_corner = corner ^ axis;
+    Box3D::VanishingPoint *vp = Box3D::get_persp_of_box (box)->get_vanishing_point(axis);
+    if (vp->is_finite()) {
+        result = (  NR::L2 (vp->get_pos() - box->corners[corner])
+                  < NR::L2 (vp->get_pos() - box->corners[other_corner]) ? other_corner : corner);
+    } else {
+        // clear the axis bit and switch to the appropriate corner along axis, depending on the value of front_bits
+        result = ((corner & (0xF ^ axis)) ^ (box->front_bits & axis));
+    }
+
+    if (rel_pos == Box3D::FRONT) {
+        return result;
+    } else {
+        return result ^ axis;
+    }
+}
+
+NR::Point
+sp_3dbox_get_corner_along_edge (const SP3DBox *box, guint corner, Box3D::Axis axis, Box3D::FrontOrRear rel_pos)
+{
+    return box->corners[sp_3dbox_get_corner_id_along_edge (box, corner, axis, rel_pos)];
+}
+
 // auxiliary functions
 static void
 sp_3dbox_update_corner_with_value_from_svg (SPObject *object, guint corner_id, const gchar *value)
index d72c6eb9c8900c66dc48aea14bb9bb7f32933fe3..ebf2a68bbc1292985d1ea8ae46774c63d6042d10 100644 (file)
@@ -43,6 +43,8 @@ struct SP3DBox : public SPGroup {
     double ratio_y;
     double ratio_z;
 
+    guint front_bits; /* used internally to determine which of two parallel faces is supposed to be the front face */
+
     gint my_counter; // for testing only
 };
 
@@ -58,11 +60,17 @@ void sp_3dbox_recompute_corners (SP3DBox *box, NR::Point const pt1, NR::Point co
 void sp_3dbox_update_curves (SP3DBox *box);
 void sp_3dbox_link_to_existing_paths (SP3DBox *box, Inkscape::XML::Node *repr);
 void sp_3dbox_set_ratios (SP3DBox *box, Box3D::Axis axes = Box3D::XYZ);
+void sp_3dbox_switch_front_face (SP3DBox *box, Box3D::Axis axis);
 void sp_3dbox_move_corner_in_XY_plane (SP3DBox *box, guint id, NR::Point pt, Box3D::Axis axes = Box3D::XY);
 void sp_3dbox_move_corner_in_Z_direction (SP3DBox *box, guint id, NR::Point pt, bool constrained = true);
 NR::Maybe<NR::Point> sp_3dbox_get_center (SP3DBox *box);
 NR::Maybe<NR::Point> sp_3dbox_get_midpoint_between_corners (SP3DBox *box, guint id_corner1, guint id_corner2);
 
+void sp_3dbox_update_perspective_lines();
+void sp_3dbox_corners_for_perspective_lines (const SP3DBox * box, Box3D::Axis axis, NR::Point &corner1, NR::Point &corner2, NR::Point &corner3, NR::Point &corner4);
+guint sp_3dbox_get_corner_id_along_edge (const SP3DBox *box, guint corner, Box3D::Axis axis, Box3D::FrontOrRear rel_pos);
+NR::Point sp_3dbox_get_corner_along_edge (const SP3DBox *box, guint corner, Box3D::Axis axis, Box3D::FrontOrRear rel_pos);
+
 gchar * sp_3dbox_get_svg_descr_of_persp (Box3D::Perspective3D *persp);
 
 inline NR::Point sp_3dbox_get_corner (SP3DBox *box, guint id) { return box->corners[id]; }
index e8390da20b9fb1b9ae4315df559df827a1826033..f9e5c64fb9591fedb706dc8f774c7de12e72841d 100644 (file)
@@ -547,6 +547,7 @@ static void sp_3dbox_knot_set(SPItem *item, guint knot_id, Box3D::Axis direction
     }
     sp_3dbox_update_curves (box);
     sp_3dbox_set_ratios (box);
+    sp_3dbox_update_perspective_lines ();
 }
 
 static NR::Point sp_3dbox_knot_get(SPItem *item, guint knot_id)
@@ -617,6 +618,7 @@ static void sp_3dbox_knot_set_uniformly(SPItem *item, guint knot_id, Box3D::Axis
     }
     sp_3dbox_update_curves (box);
     sp_3dbox_set_ratios (box);
+    sp_3dbox_update_perspective_lines ();
 }
 
 static void sp_3dbox_knot0_set_uniformly(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
index 2137449373735c8a62dfec0f67fb6b95b37692f1..d37c9f3a0100be2e15debc5bab8791e66573b8d8 100644 (file)
@@ -19,7 +19,6 @@
 
 // can probably be removed later
 #include "inkscape.h"
-#include "knotholder.h"
 
 namespace Box3D {
 
@@ -312,13 +311,6 @@ Perspective3D::reshape_boxes (Box3D::Axis axes)
         }                
 
         sp_3dbox_set_shape (box, true);
-        // FIXME: Is there a way update the knots without accessing the
-        //        statically linked function knotholder_update_knots?
-        SPEventContext *ec = inkscape_active_event_context();
-        g_assert (ec != NULL);
-        if (ec->shape_knot_holder != NULL) {
-            knotholder_update_knots(ec->shape_knot_holder, (SPItem *) box);
-        }
     }
 }
 
index fa77d755fcef9959d29cdd5f7d35a8ea1e0084e1..ea3626dacb44ab1858f88a722d18b5791841296f 100644 (file)
@@ -19,6 +19,8 @@
 #include "desktop-handles.h"
 #include "box3d.h"
 
+#include "knotholder.h" // FIXME: can we avoid direct access to knotholder_update_knots?
+
 namespace Box3D {
 
 #define VP_KNOT_COLOR_NORMAL 0xffffff00
@@ -131,7 +133,7 @@ vp_drag_sel_changed(Inkscape::Selection *selection, gpointer data)
 {
     VPDrag *drag = (VPDrag *) data;
     drag->updateDraggers ();
-    //drag->updateLines ();
+    drag->updateLines ();
 }
 
 static void
@@ -145,7 +147,7 @@ vp_drag_sel_modified (Inkscape::Selection *selection, guint flags, gpointer data
         drag->updateDraggers ();
     }
     ***/
-    //drag->updateLines ();
+    drag->updateLines ();
 }
 
 // auxiliary function
@@ -225,9 +227,11 @@ vp_knot_moved_handler (SPKnot *knot, NR::Point const *ppointer, guint state, gpo
                 //d_new->updateKnotShape ();
                 //d_new->updateTip ();
 
-                d_new->reshapeBoxes (p, Box3D::XYZ);
+                d_new->reshapeBoxes (d_new->point, Box3D::XYZ);
                 d_new->updateBoxReprs ();
 
+                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.
@@ -245,7 +249,7 @@ vp_knot_moved_handler (SPKnot *knot, NR::Point const *ppointer, guint state, gpo
     dragger->reshapeBoxes (p, Box3D::XYZ);
     dragger->updateBoxReprs ();
 
-    //dragger->parent->updateLines ();
+    drag->updateLines ();
 
     //drag->local_change = false;
 }
@@ -530,6 +534,7 @@ VPDragger::reshapeBoxes (NR::Point const &p, Box3D::Axis axes)
         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
     }
+    parent->updateBoxHandles();
 }
 
 void
@@ -543,9 +548,16 @@ VPDragger::updateBoxReprs ()
 VPDrag::VPDrag (SPDesktop *desktop)
 {
     this->desktop = desktop;
-    this->draggers = NULL;
     this->selection = sp_desktop_selection(desktop);
 
+    this->draggers = NULL;
+    this->lines = NULL;
+    this->show_lines = true;
+    this->front_or_rear_lines = 0x1;
+
+    //this->selected = NULL;
+    this->local_change = false;
+
     this->sel_changed_connection = this->selection->connectChanged(
         sigc::bind (
             sigc::ptr_fun(&vp_drag_sel_changed),
@@ -559,7 +571,7 @@ VPDrag::VPDrag (SPDesktop *desktop)
         );
 
     this->updateDraggers ();
-    //this->updateLines ();
+    this->updateLines ();
 }
 
 VPDrag::~VPDrag()
@@ -572,6 +584,12 @@ VPDrag::~VPDrag()
     }
     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;
 }
 
 /**
@@ -636,6 +654,95 @@ VPDrag::updateDraggers ()
     }
 }
 
+/**
+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;
+
+    // do nothing if perspective lines are currently disabled
+    if (this->show_lines == 0) return;
+
+    g_return_if_fail (this->selection != NULL);
+
+    for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
+        if (!SP_IS_3DBOX(i->data)) continue;
+        SP3DBox *box = SP_3DBOX (i->data);
+
+        this->drawLinesForFace (box, Box3D::X);
+        this->drawLinesForFace (box, Box3D::Y);
+        this->drawLinesForFace (box, Box3D::Z);
+    }
+}
+
+void
+VPDrag::updateBoxHandles ()
+{
+    // FIXME: Is there a way to update the knots without accessing the
+    //        statically linked function knotholder_update_knots?
+
+    GSList *sel = (GSList *) selection->itemList();
+    if (g_slist_length (sel) > 1) {
+        // Currently we only show handles if a single box is selected
+        return;
+    }
+
+    if (!SP_IS_3DBOX (sel->data))
+        return;
+
+    SPEventContext *ec = inkscape_active_event_context();
+    g_assert (ec != NULL);
+    if (ec->shape_knot_holder != NULL) {
+        knotholder_update_knots(ec->shape_knot_holder, (SPItem *) sel->data);
+    }
+}
+
+/**
+ * Depending on the value of all_lines, draw the front and/or rear perspective lines starting from the given corners.
+ */
+void
+VPDrag::drawLinesForFace (const SP3DBox *box, Box3D::Axis axis) //, guint corner1, guint corner2, guint corner3, guint corner4)
+{
+    guint color;
+    switch (axis) {
+        // TODO: Make color selectable by user
+        case Box3D::X: color = VP_LINE_COLOR_STROKE_X; break;
+        case Box3D::Y: color = VP_LINE_COLOR_STROKE_Y; break;
+        case Box3D::Z: color = VP_LINE_COLOR_STROKE_Z; break;
+        default: g_assert_not_reached();
+    }
+
+    NR::Point corner1, corner2, corner3, corner4;
+    sp_3dbox_corners_for_perspective_lines (box, axis, corner1, corner2, corner3, corner4);
+
+    //VanishingPoint *vp = box->perspective->get_vanishing_point (axis);
+    VanishingPoint *vp = Box3D::get_persp_of_box (box)->get_vanishing_point (axis);
+    if (vp->is_finite()) {
+        NR::Point pt = vp->get_pos();
+        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 {
+        // TODO: Draw infinite PLs
+        g_warning ("Perspective lines for infinite vanishing points are not supported yet.\n");
+    }
+
+}
 
 /**
  * Returns true if all boxes that are linked to a VP in the dragger are selected
@@ -693,6 +800,20 @@ VPDrag::addDragger (VanishingPoint *vp)
     this->draggers = g_list_append (this->draggers, new_dragger);
 }
 
+/**
+Create a line from p1 to p2 and add it to the lines list
+ */
+void
+VPDrag::addLine (NR::Point p1, NR::Point p2, guint32 rgba)
+{
+    SPCanvasItem *line = sp_canvas_item_new(sp_desktop_controls(this->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 
  
 /*
index 76299541ab7e7a11fc57b60183a595867542ed56..98788f51247a0f6aa6c969d9e7530f2d00b69ffa 100644 (file)
@@ -118,10 +118,16 @@ public:
 
     SPDesktop *desktop;
     GList *draggers;
-    //GSList *lines;
+    GSList *lines;
 
     void updateDraggers ();
-    //void updateLines ();
+    void updateLines ();
+    void updateBoxHandles ();
+    void drawLinesForFace (const SP3DBox *box, Box3D::Axis axis); //, guint corner1, guint corner2, guint corner3, guint corner4);
+    bool show_lines; /* whether perspective lines are drawn at all */
+    guint front_or_rear_lines; /* whether we draw perspective lines from all corners or only the
+                                  front/rear corners (indicated by the first/second bit, respectively  */
+
 
     inline bool hasEmptySelection() { return this->selection->isEmpty(); }
     bool allBoxesAreSelected (VPDragger *dragger);
@@ -134,7 +140,7 @@ public:
 private:
     //void deselect_all();
 
-    //void addLine (NR::Point p1, NR::Point p2, guint32 rgba);
+    void addLine (NR::Point p1, NR::Point p2, guint32 rgba);
 
     Inkscape::Selection *selection;
     sigc::connection sel_changed_connection;