Code

Set z-orders of 3D box faces during dragging/resizing according to the perspective
authorcilix42 <cilix42@users.sourceforge.net>
Tue, 7 Aug 2007 06:56:36 +0000 (06:56 +0000)
committercilix42 <cilix42@users.sourceforge.net>
Tue, 7 Aug 2007 06:56:36 +0000 (06:56 +0000)
src/axis-manip.h
src/box3d-context.cpp
src/box3d-face.h
src/box3d.cpp
src/box3d.h
src/object-edit.cpp
src/perspective3d.cpp
src/perspective3d.h
src/vanishing-point.cpp
src/vanishing-point.h

index 7461678d5321f2dd4814e64eb799b02c09dd3958..5690eedcb79f564739f27cf2007f4af41bab7b70 100644 (file)
@@ -66,6 +66,10 @@ inline gint face_to_int (guint face_id) {
     }
 }
 
+inline guint opposite_face (guint face_id) {
+    return face_id + ((face_id % 2 == 0) ? 1 : -1);
+}
+
 inline bool is_single_axis_direction (Box3D::Axis dir) {
     // tests whether dir is nonzero and a power of 2
     return (!(dir & (dir - 1)) && dir);
index a753f5f5004ede9b660ef773870b9d2c885e73b9..388e2037d89578bf91877e5d6202fbef818a53c7 100644 (file)
@@ -581,6 +581,8 @@ static void sp_3dbox_drag(SP3DBoxContext &bc, guint state)
         }
 
         bc.item->updateRepr();
+        sp_3dbox_set_z_orders (SP_3DBOX (bc.item));
+
         // 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();
@@ -603,6 +605,7 @@ static void sp_3dbox_drag(SP3DBoxContext &bc, guint state)
     NR::Point origin_w(ec->xp, ec->yp);
     NR::Point origin(desktop->w2d(origin_w));
     sp_3dbox_position_set(bc);
+    sp_3dbox_set_z_orders (SP_3DBOX (bc.item));
 
     // status text
     //GString *Ax = SP_PX_TO_METRIC_STRING(origin[NR::X], desktop->namedview->getDefaultMetric());
index 61c13432d7bad100f1a7dfc498acdcbfe101e7b8..0e911cccfdbe84a1d18878ac63586d47767441e7 100644 (file)
@@ -45,6 +45,8 @@ public:
     void hook_path_to_3dbox(SPPath * existing_path = NULL);
     void set_path_repr();
     void set_curve();
+    inline void lower_to_bottom() { SP_ITEM (path)->lowerToBottom(); }
+    inline void raise_to_top() { SP_ITEM (path)->raiseToTop(); }
     gchar * axes_string();
     gchar * svg_repr_string();
 
index e445ba3d06cfe2092b8456a11644f7fd78472863..7f04efde65ef116cc53d21976f7ddc733edb4264 100644 (file)
@@ -103,6 +103,11 @@ sp_3dbox_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr
 
     box->my_counter = counter++;
 
+    /* we initialize the z-orders to zero so that they are updated during dragging */
+    for (int i = 0; i < 6; ++i) {
+        box->z_orders[i] = 0;
+    }
+
     box->front_bits = 0x0;
 
     if (repr->attribute ("inkscape:perspective") == NULL) {
@@ -368,6 +373,110 @@ void sp_3dbox_recompute_corners (SP3DBox *box, NR::Point const A, NR::Point cons
     sp_3dbox_move_corner_in_Z_direction (box, 5, C);
 }
 
+inline static double
+normalized_angle (double angle) {
+    if (angle < -M_PI) {
+        return angle + 2*M_PI;
+    } else if (angle > M_PI) {
+        return angle - 2*M_PI;
+    }
+    return angle;
+}
+
+static gdouble
+sp_3dbox_corner_angle_to_VP (SP3DBox *box, Box3D::Axis axis, guint extreme_corner)
+{
+    Box3D::VanishingPoint *vp = Box3D::get_persp_of_box (box)->get_vanishing_point (axis);
+    NR::Point dir;
+
+    if (vp->is_finite()) {
+        dir = NR::unit_vector (vp->get_pos() - box->corners[extreme_corner]);
+    } else {
+        dir = NR::unit_vector (vp->v_dir);
+    }
+
+    return atan2 (dir[NR::Y], dir[NR::X]);
+}
+
+
+bool sp_3dbox_recompute_z_orders (SP3DBox *box)
+{
+    guint new_z_orders[6];
+
+    Box3D::Perspective3D *persp = Box3D::get_persp_of_box (box);
+
+    // TODO: Determine the front corner depending on the distance from VPs and/or the user presets
+    guint front_corner = 1;
+
+    gdouble dir_1x = sp_3dbox_corner_angle_to_VP (box, Box3D::X, front_corner);
+    gdouble dir_3x = sp_3dbox_corner_angle_to_VP (box, Box3D::X, front_corner ^ Box3D::Y);
+
+    gdouble dir_1y = sp_3dbox_corner_angle_to_VP (box, Box3D::Y, front_corner);
+    gdouble dir_0y = sp_3dbox_corner_angle_to_VP (box, Box3D::Y, front_corner ^ Box3D::X);
+
+    gdouble dir_1z = sp_3dbox_corner_angle_to_VP (box, Box3D::Z, front_corner);
+    gdouble dir_3z = sp_3dbox_corner_angle_to_VP (box, Box3D::Z, front_corner ^ Box3D::Y);
+
+    // Still not perfect, but only fails in some rather degenerate cases.
+    // I suspect that there is a more elegant model, though. :)
+    new_z_orders[0] = Box3D::face_to_int (Box3D::XY ^ Box3D::FRONT);
+    if (normalized_angle (dir_1y - dir_1z) > 0) {
+        new_z_orders[1] = Box3D::face_to_int (Box3D::YZ ^ Box3D::REAR);
+        if (normalized_angle (dir_1x - dir_1z) > 0) {
+            new_z_orders[2] = Box3D::face_to_int (Box3D::XZ ^ Box3D::REAR);
+        } else {
+            new_z_orders[2] = Box3D::face_to_int (Box3D::XZ ^ Box3D::FRONT);
+        }
+    } else {
+        if (normalized_angle (dir_3x - dir_3z) > 0) {
+            new_z_orders[1] = Box3D::face_to_int (Box3D::XZ ^ Box3D::REAR);
+            new_z_orders[2] = Box3D::face_to_int (Box3D::YZ ^ Box3D::FRONT);
+        } else {
+            if (normalized_angle (dir_1x - dir_1z) > 0) {
+                new_z_orders[1] = Box3D::face_to_int (Box3D::YZ ^ Box3D::FRONT);
+                new_z_orders[2] = Box3D::face_to_int (Box3D::XZ ^ Box3D::FRONT);
+            } else {
+                new_z_orders[1] = Box3D::face_to_int (Box3D::XZ ^ Box3D::FRONT);
+                new_z_orders[2] = Box3D::face_to_int (Box3D::YZ ^ Box3D::FRONT);
+            }
+        }
+    }
+
+    new_z_orders[3] = Box3D::opposite_face (new_z_orders[2]);
+    new_z_orders[4] = Box3D::opposite_face (new_z_orders[1]);
+    new_z_orders[5] = Box3D::opposite_face (new_z_orders[0]);
+
+    /* We only need to look for changes among the topmost three faces because the order
+       of the other ones is just inverted. */
+    // Currently we can even skip the first test since the front face is always in the XY plane.
+    if (// (box->z_orders[0] != new_z_orders[0]) ||
+        (box->z_orders[1] != new_z_orders[1]) ||
+        (box->z_orders[2] != new_z_orders[2]))
+    {
+        for (int i = 0; i < 6; ++i) {
+            box->z_orders[i] = new_z_orders[i];
+        }
+        return true;
+    }
+
+    return false;
+}
+
+void sp_3dbox_set_z_orders (SP3DBox *box)
+{
+    GSList *items = sp_item_group_item_list(SP_GROUP(box));
+
+    // For efficiency reasons, we only set the new z-orders if something really changed
+    if (sp_3dbox_recompute_z_orders (box)) {
+        box->faces[box->z_orders[0]]->lower_to_bottom ();
+        box->faces[box->z_orders[1]]->lower_to_bottom ();
+        box->faces[box->z_orders[2]]->lower_to_bottom ();
+        box->faces[box->z_orders[3]]->lower_to_bottom ();
+        box->faces[box->z_orders[4]]->lower_to_bottom ();
+        box->faces[box->z_orders[5]]->lower_to_bottom ();
+    }
+}
+
 void
 sp_3dbox_update_curves (SP3DBox *box) {
     for (int i = 0; i < 6; ++i) {
index ebf2a68bbc1292985d1ea8ae46774c63d6042d10..bca319e678c6c01d461fa9fce6ace1429cd81190 100644 (file)
@@ -36,6 +36,7 @@
 struct SP3DBox : public SPGroup {
     NR::Point corners[8];
     Box3DFace *faces[6];
+    guint z_orders[6]; // z_orders[i] holds the ID of the face at position #i in the group (from top to bottom)
 
     // TODO: Keeping/updating the ratios works reasonably well but is still an ad hoc implementation.
     //       Use a mathematically correct model to update the boxes.
@@ -57,6 +58,9 @@ GType sp_3dbox_get_type (void);
 void sp_3dbox_position_set (SP3DBoxContext &bc);
 void sp_3dbox_set_shape(SP3DBox *box3d, bool use_previous_corners = false);
 void sp_3dbox_recompute_corners (SP3DBox *box, NR::Point const pt1, NR::Point const pt2, NR::Point const pt3);
+bool sp_3dbox_recompute_z_orders (SP3DBox *box); /* returns true if there was a change in the z-orders
+                                                   (which triggers an update of the repr) */
+void sp_3dbox_set_z_orders (SP3DBox *box);
 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);
index f9e5c64fb9591fedb706dc8f774c7de12e72841d..a0a8d42185ca3989bf5053c27a1a7c9d87a33de1 100644 (file)
@@ -548,6 +548,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 ();
+    sp_3dbox_set_z_orders (box);
 }
 
 static NR::Point sp_3dbox_knot_get(SPItem *item, guint knot_id)
@@ -619,6 +620,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 ();
+    sp_3dbox_set_z_orders (box);
 }
 
 static void sp_3dbox_knot0_set_uniformly(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state)
index d37c9f3a0100be2e15debc5bab8791e66573b8d8..bb4963d119952f831efc4024b009ea0162453786 100644 (file)
@@ -322,6 +322,14 @@ Perspective3D::update_box_reprs ()
     }
 }
 
+void
+Perspective3D::update_z_orders ()
+{
+    for (GSList *i = this->boxes; i != NULL; i = i->next) {
+        sp_3dbox_set_z_orders (SP_3DBOX (i->data));
+    }
+}
+
 // swallow the list of boxes from the other perspective and delete it
 void
 Perspective3D::absorb (Perspective3D *other)
index 4134b3fc1ffc59d970fa7914a26f6510efbc6bf8..6c7e774469fa0bc8cac0819017d2112ee587166e 100644 (file)
@@ -41,6 +41,7 @@ public:
     inline guint number_of_boxes () { return g_slist_length (boxes); }
     void reshape_boxes (Box3D::Axis axes);
     void update_box_reprs ();
+    void update_z_orders ();
 
     /* convenience functions for interaction with dragging machinery: */
     bool all_boxes_occur_in_list (GSList *boxes_to_do);
index ea3626dacb44ab1858f88a722d18b5791841296f..0b4140a8bc95a37dab705832202209a3a5d12aec 100644 (file)
@@ -229,6 +229,7 @@ vp_knot_moved_handler (SPKnot *knot, NR::Point const *ppointer, guint state, gpo
 
                 d_new->reshapeBoxes (d_new->point, Box3D::XYZ);
                 d_new->updateBoxReprs ();
+                d_new->updateZOrders ();
 
                 drag->updateLines ();
 
@@ -248,6 +249,7 @@ vp_knot_moved_handler (SPKnot *knot, NR::Point const *ppointer, guint state, gpo
 
     dragger->reshapeBoxes (p, Box3D::XYZ);
     dragger->updateBoxReprs ();
+    dragger->updateZOrders ();
 
     drag->updateLines ();
 
@@ -545,6 +547,14 @@ VPDragger::updateBoxReprs ()
     }
 }
 
+void
+VPDragger::updateZOrders ()
+{
+    for (GSList *i = this->vps; i != NULL; i = i->next) {
+        Box3D::get_persp_of_VP ((VanishingPoint *) i->data)->update_z_orders ();
+    }
+}
+
 VPDrag::VPDrag (SPDesktop *desktop)
 {
     this->desktop = desktop;
index 98788f51247a0f6aa6c969d9e7530f2d00b69ffa..516b4a51b2f2d0a2bcb749c01aff42557b5c98c7 100644 (file)
@@ -103,6 +103,7 @@ public:
 
     void reshapeBoxes(NR::Point const &p, Box3D::Axis axes);
     void updateBoxReprs();
+    void updateZOrders();
 };
 
 struct VPDrag {