From b9314c4c1c56471487e07aa368f0e311d29bee58 Mon Sep 17 00:00:00 2001 From: cilix42 Date: Tue, 7 Aug 2007 06:56:36 +0000 Subject: [PATCH] Set z-orders of 3D box faces during dragging/resizing according to the perspective --- src/axis-manip.h | 4 ++ src/box3d-context.cpp | 3 ++ src/box3d-face.h | 2 + src/box3d.cpp | 109 ++++++++++++++++++++++++++++++++++++++++ src/box3d.h | 4 ++ src/object-edit.cpp | 2 + src/perspective3d.cpp | 8 +++ src/perspective3d.h | 1 + src/vanishing-point.cpp | 10 ++++ src/vanishing-point.h | 1 + 10 files changed, 144 insertions(+) diff --git a/src/axis-manip.h b/src/axis-manip.h index 7461678d5..5690eedcb 100644 --- a/src/axis-manip.h +++ b/src/axis-manip.h @@ -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); diff --git a/src/box3d-context.cpp b/src/box3d-context.cpp index a753f5f50..388e2037d 100644 --- a/src/box3d-context.cpp +++ b/src/box3d-context.cpp @@ -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()); diff --git a/src/box3d-face.h b/src/box3d-face.h index 61c13432d..0e911cccf 100644 --- a/src/box3d-face.h +++ b/src/box3d-face.h @@ -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(); diff --git a/src/box3d.cpp b/src/box3d.cpp index e445ba3d0..7f04efde6 100644 --- a/src/box3d.cpp +++ b/src/box3d.cpp @@ -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) { diff --git a/src/box3d.h b/src/box3d.h index ebf2a68bb..bca319e67 100644 --- a/src/box3d.h +++ b/src/box3d.h @@ -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); diff --git a/src/object-edit.cpp b/src/object-edit.cpp index f9e5c64fb..a0a8d4218 100644 --- a/src/object-edit.cpp +++ b/src/object-edit.cpp @@ -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) diff --git a/src/perspective3d.cpp b/src/perspective3d.cpp index d37c9f3a0..bb4963d11 100644 --- a/src/perspective3d.cpp +++ b/src/perspective3d.cpp @@ -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) diff --git a/src/perspective3d.h b/src/perspective3d.h index 4134b3fc1..6c7e77446 100644 --- a/src/perspective3d.h +++ b/src/perspective3d.h @@ -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); diff --git a/src/vanishing-point.cpp b/src/vanishing-point.cpp index ea3626dac..0b4140a8b 100644 --- a/src/vanishing-point.cpp +++ b/src/vanishing-point.cpp @@ -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; diff --git a/src/vanishing-point.h b/src/vanishing-point.h index 98788f512..516b4a51b 100644 --- a/src/vanishing-point.h +++ b/src/vanishing-point.h @@ -103,6 +103,7 @@ public: void reshapeBoxes(NR::Point const &p, Box3D::Axis axes); void updateBoxReprs(); + void updateZOrders(); }; struct VPDrag { -- 2.30.2