Code

Refactoring SPColor to C++ and removing legacy CMYK implementation
[inkscape.git] / src / box3d.cpp
index dad0ae88cd7df11059a199153ae126dae9a9f9cb..ff00a795c57505d50c8475fa83ed835cd16e4740 100644 (file)
@@ -19,6 +19,7 @@
 #include "attributes.h"
 #include "svg/stringstream.h"
 #include "box3d.h"
+#include "desktop-handles.h"
 
 static void sp_3dbox_class_init(SP3DBoxClass *klass);
 static void sp_3dbox_init(SP3DBox *box3d);
@@ -169,6 +170,18 @@ sp_3dbox_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr
     sp_3dbox_link_to_existing_paths (box, repr);
 
     sp_3dbox_set_ratios (box, Box3D::XYZ);
+
+    // Store the center (if it already exists) and certain corners for later use during center-dragging
+    NR::Maybe<NR::Point> cen = sp_3dbox_get_center (box);
+    if (cen) {
+        box->old_center = *cen;
+    }
+    box->old_corner2 = box->corners[2];
+    box->old_corner1 = box->corners[1];
+    box->old_corner0 = box->corners[0];
+    box->old_corner3 = box->corners[3];
+    box->old_corner5 = box->corners[5];
+    box->old_corner7 = box->corners[7];
 }
 
 static void
@@ -221,7 +234,15 @@ sp_3dbox_update(SPObject *object, SPCtx *ctx, guint flags)
 {
     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
         SP3DBox *box = SP_3DBOX(object);
-        sp_3dbox_link_to_existing_paths (box, SP_OBJECT_REPR(object));
+        Inkscape::XML::Node *repr = SP_OBJECT_REPR(object);
+        sp_3dbox_link_to_existing_paths (box, repr);
+        SPEventContext *ec = inkscape_active_event_context();
+        if (SP_IS_3DBOX_CONTEXT (ec)) {
+            SP_3DBOX_CONTEXT (ec)->_vpdrag->updateDraggers();
+            // FIXME: Should we update the corners here, too? Maybe this is the reason why the handles
+            //        are off after an undo/redo! On the other hand, if we do so we get warnings about
+            //        updates occuring while other updats are in progress ...
+        }
     }
 
     /* Invoke parent method */
@@ -229,8 +250,6 @@ sp_3dbox_update(SPObject *object, SPCtx *ctx, guint flags)
         ((SPObjectClass *) (parent_class))->update(object, ctx, flags);
 }
 
-
-
 static Inkscape::XML::Node *sp_3dbox_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
 {
     SP3DBox *box = SP_3DBOX(object);
@@ -269,6 +288,18 @@ static Inkscape::XML::Node *sp_3dbox_write(SPObject *object, Inkscape::XML::Node
         sp_3dbox_set_ratios (box);
 
         g_free ((void *) str);
+
+        /* store center and construction-corners for later use during center-dragging */
+        NR::Maybe<NR::Point> cen = sp_3dbox_get_center (box);
+        if (cen) {
+            box->old_center = *cen;
+        }
+        box->old_corner2 = box->corners[2];
+        box->old_corner1 = box->corners[1];
+        box->old_corner0 = box->corners[0];
+        box->old_corner3 = box->corners[3];
+        box->old_corner5 = box->corners[5];
+        box->old_corner7 = box->corners[7];
     }
 
     if (((SPObjectClass *) (parent_class))->write) {
@@ -337,37 +368,45 @@ sp_3dbox_position_set (SP3DBoxContext &bc)
     ***/
 }
 
-//static
+static void
+sp_3dbox_set_shape_from_points (SP3DBox *box, NR::Point const &cornerA, NR::Point const &cornerB, NR::Point const &cornerC)
+{
+    sp_3dbox_recompute_corners (box, cornerA, cornerB, cornerC);
+
+    // FIXME: How to handle other contexts???
+    // FIXME: Is tools_isactive(..) more recommended to check for the current context/tool?
+    if (!SP_IS_3DBOX_CONTEXT(inkscape_active_event_context()))
+        return;
+    SP3DBoxContext *bc = SP_3DBOX_CONTEXT(inkscape_active_event_context());
+
+    if (bc->extruded) {
+        box->faces[0]->set_corners (box->corners[0], box->corners[4], box->corners[6], box->corners[2]);
+        box->faces[1]->set_corners (box->corners[1], box->corners[5], box->corners[7], box->corners[3]);
+        box->faces[2]->set_corners (box->corners[0], box->corners[1], box->corners[5], box->corners[4]);
+        box->faces[3]->set_corners (box->corners[2], box->corners[3], box->corners[7], box->corners[6]);
+        box->faces[5]->set_corners (box->corners[4], box->corners[5], box->corners[7], box->corners[6]);
+    }
+    box->faces[4]->set_corners (box->corners[0], box->corners[1], box->corners[3], box->corners[2]);
+
+    sp_3dbox_update_curves (box);
+}
+
 void
 // FIXME: Note that this is _not_ the virtual set_shape() method inherited from SPShape,
 //        since SP3DBox is inherited from SPGroup. The following method is "artificially"
 //        called from sp_3dbox_update().
 //sp_3dbox_set_shape(SPShape *shape)
-sp_3dbox_set_shape(SP3DBox *box3d, bool use_previous_corners)
+sp_3dbox_set_shape(SP3DBox *box, bool use_previous_corners)
 {
-    // FIXME: How to handle other contexts???
-    // FIXME: Is tools_isactive(..) more recommended to check for the current context/tool?
     if (!SP_IS_3DBOX_CONTEXT(inkscape_active_event_context()))
         return;
     SP3DBoxContext *bc = SP_3DBOX_CONTEXT(inkscape_active_event_context());
 
-    /* Only update the curves during dragging; setting the svg representations 
-       is expensive and only done once at the end */
     if (!use_previous_corners) {
-        sp_3dbox_recompute_corners (box3d, bc->drag_origin, bc->drag_ptB, bc->drag_ptC);
+        sp_3dbox_set_shape_from_points (box, bc->drag_origin, bc->drag_ptB, bc->drag_ptC);
     } else {
-        sp_3dbox_recompute_corners (box3d, box3d->corners[2], box3d->corners[1], box3d->corners[5]);
-    }
-    if (bc->extruded) {
-        box3d->faces[0]->set_corners (box3d->corners[0], box3d->corners[4], box3d->corners[6], box3d->corners[2]);
-        box3d->faces[1]->set_corners (box3d->corners[1], box3d->corners[5], box3d->corners[7], box3d->corners[3]);
-        box3d->faces[2]->set_corners (box3d->corners[0], box3d->corners[1], box3d->corners[5], box3d->corners[4]);
-        box3d->faces[3]->set_corners (box3d->corners[2], box3d->corners[3], box3d->corners[7], box3d->corners[6]);
-        box3d->faces[5]->set_corners (box3d->corners[4], box3d->corners[5], box3d->corners[7], box3d->corners[6]);
+        sp_3dbox_set_shape_from_points (box, box->corners[2], box->corners[1], box->corners[5]);
     }
-    box3d->faces[4]->set_corners (box3d->corners[0], box3d->corners[1], box3d->corners[3], box3d->corners[2]);
-
-    sp_3dbox_update_curves (box3d);
 }
 
 
@@ -406,9 +445,7 @@ sp_3dbox_corner_angle_to_VP (SP3DBox *box, Box3D::Axis axis, guint extreme_corne
 
 bool sp_3dbox_recompute_z_orders (SP3DBox *box)
 {
-    guint new_z_orders[6];
-
-    Box3D::Perspective3D *persp = SP_OBJECT_DOCUMENT (G_OBJECT (box))->get_persp_of_box (box);
+    gint new_z_orders[6];
 
     // TODO: Determine the front corner depending on the distance from VPs and/or the user presets
     guint front_corner = sp_3dbox_get_front_corner_id (box);
@@ -417,7 +454,7 @@ bool sp_3dbox_recompute_z_orders (SP3DBox *box)
     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_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);
@@ -466,10 +503,237 @@ bool sp_3dbox_recompute_z_orders (SP3DBox *box)
     return false;
 }
 
-void sp_3dbox_set_z_orders (SP3DBox *box)
+// convenience
+static bool sp_3dbox_is_subset_or_superset (std::vector<gint> const &list1, std::vector<gint> const &list2)
 {
-    GSList *items = sp_item_group_item_list(SP_GROUP(box));
+    return (std::includes (list1.begin(), list1.end(), list2.begin(), list2.end()) ||
+            std::includes (list2.begin(), list2.end(), list1.begin(), list1.end()));
+}
+
+static bool sp_3dbox_differ_by_opposite_faces (std::vector<gint> const &list1, std::vector<gint> const &list2)
+{
+    std::vector<gint> diff1;
+    std::vector<gint> diff2;
+    std::set_difference (list1.begin(), list1.end(), list2.begin(), list2.end(),
+                         std::insert_iterator<std::vector<gint> >(diff1, diff1.begin()));
+    std::set_difference (list2.begin(), list2.end(), list1.begin(), list1.end(),
+                         std::insert_iterator<std::vector<gint> >(diff2, diff2.begin()));
+
+    if (diff1.size() == 3 || diff1.size() != diff2.size())
+        return false;
+
+    for (guint i = 0; i < diff1.size(); ++i) {
+        if (std::find (diff2.begin(), diff2.end(), Box3D::opposite_face (diff1[i])) == diff2.end()) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static gint
+sp_3dbox_face_containing_diagonal_corners (guint corner1, guint corner2)
+{
+    Box3D::Axis plane = (Box3D::Axis) (corner1 ^ corner2);
+    if (!Box3D::is_plane (plane)) {
+        g_warning ("Corners %d and %d should span a plane.\n", corner1, corner2);
+        return 0;
+    }
+
+    return Box3D::face_containing_corner (plane, corner1);
+}
+
+static std::vector<gint> sp_3dbox_adjacent_faces_of_edge (guint corner1, guint corner2) {
+    std::vector<gint> adj_faces;
+    Box3D::Axis edge = (Box3D::Axis) (corner1 ^ corner2);
+    if (!Box3D::is_single_axis_direction (edge)) {
+        return adj_faces;
+    }
+
+    Box3D::Axis plane = Box3D::orth_plane_or_axis (edge);
+    Box3D::Axis axis1 = Box3D::extract_first_axis_direction (plane);
+    Box3D::Axis axis2 = Box3D::extract_second_axis_direction (plane);
+    adj_faces.push_back (Box3D::face_containing_corner ((Box3D::Axis) (edge ^ axis1), corner1));
+    adj_faces.push_back (Box3D::face_containing_corner ((Box3D::Axis) (edge ^ axis2), corner1));
+    return adj_faces;
+}
+
+static std::vector<gint> sp_3dbox_faces_meeting_in_corner (guint corner) {
+    std::vector<gint> faces;
+    for (int i = 0; i < 3; ++i) {
+        faces.push_back (sp_3dbox_face_containing_diagonal_corners (corner, corner ^ Box3D::planes[i]));
+    }
+    return faces;
+}
+
+static void sp_3dbox_remaining_faces (std::vector<gint> const &faces, std::vector<gint> &rem_faces)
+{
+    rem_faces.clear();
+    for (gint i = 0; i < 6; ++i) {
+        if (std::find (faces.begin(), faces.end(), i) == faces.end()) {
+            rem_faces.push_back (i);
+        }
+    }
+}
+
+/*
+ * Given two adjacent edges (\a c2,\a c1) and (\a c2, \a c3) of \a box (with common corner \a c2),
+ * check whether both lie on the convex hull of the point configuration given by \a box's corners.
+ */
+static bool
+sp_3dbox_is_border_edge_pair (SP3DBox *box, guint const c1, guint const c2, guint const c3)
+{
+    Box3D::Axis edge21 = (Box3D::Axis) (c2 ^ c1);
+    Box3D::Axis edge23 = (Box3D::Axis) (c2 ^ c3);
+    Box3D::Axis rear_axis = Box3D::orth_plane_or_axis ((Box3D::Axis) (edge21 ^ edge23));
+    NR::Point corner2 = box->corners[c2];
+    NR::Point dir21 = box->corners[c1] - corner2;
+    NR::Point dir23 = box->corners[c3] - corner2;
+
+    if (!Box3D::lies_in_sector (dir21, dir23, box->corners[c2 ^ edge21 ^ edge23] - corner2) ||
+        !Box3D::lies_in_sector (dir21, dir23, box->corners[c2 ^ rear_axis] - corner2) ||
+        !Box3D::lies_in_sector (dir21, dir23, box->corners[c2 ^ rear_axis ^ edge21] - corner2) ||
+        !Box3D::lies_in_sector (dir21, dir23, box->corners[c2 ^ rear_axis ^ edge21 ^ edge23] - corner2) ||
+        !Box3D::lies_in_sector (dir21, dir23, box->corners[c2 ^ rear_axis ^ edge23] - corner2)) {
+        // corner triple c1, c2, c3 doesn't bound the convex hull
+        return false;
+    }
+    // corner triple c1, c2, c3 bounds the convex hull
+    return true;    
+}
+
+/*
+ * Test whether there are any adjacent corners of \a corner (i.e., connected with it along one of the axes)
+ * such that the corresponding edges bound the convex hull of the box (as a point configuration in the plane)
+ * If this is the case, return the corresponding two adjacent corners; otherwise return (-1, -1).
+ */
+static Box3D::Axis
+sp_3dbox_axis_pair_bounding_convex_hull (SP3DBox *box, guint corner)
+ {
+    guint adj1 = corner ^ Box3D::X;
+    guint adj2 = corner ^ Box3D::Y;
+    guint adj3 = corner ^ Box3D::Z;
+
+    if (sp_3dbox_is_border_edge_pair (box, adj1, corner, adj2)) {
+        return Box3D::XY;
+    }
+    if (sp_3dbox_is_border_edge_pair (box, adj1, corner, adj3)) {
+        return Box3D::XZ;
+    }
+    if (sp_3dbox_is_border_edge_pair (box, adj2, corner, adj3)) {
+        return Box3D::YZ;
+    }
+    return Box3D::NONE;
+}
+
+// inside_hull is modified 'in place' by the following function
+static void sp_3dbox_corner_configuration (SP3DBox *box, std::vector<gint> &on_hull, std::vector<gint> &inside_hull)
+{
+    for (int i = 0; i < 8; ++i) {
+        Box3D::Axis bounding_edges = sp_3dbox_axis_pair_bounding_convex_hull (box, i);
+        if (bounding_edges != Box3D::NONE) {
+            on_hull.push_back (i);
+        } else {
+            inside_hull.push_back (i);
+        }
+    }
+}
+
+/* returns true if there was a change in the z-orders (which triggers an update of the repr) */
+static bool sp_3dbox_recompute_z_orders_by_corner_configuration (SP3DBox *box)
+{
+    gint new_z_orders[6];
+    Box3D::Axis front_rear_axis = Box3D::Z;
+
+    std::vector<gint> on_hull;
+    std::vector<gint> inside_hull;
+    std::vector<gint> visible_faces;
+
+    sp_3dbox_corner_configuration (box, on_hull, inside_hull);
+
+    switch (on_hull.size()) {
+        case 4:
+            {
+                // the following works because on_hull is sorted
+                gint front_face = sp_3dbox_face_containing_diagonal_corners (on_hull[0], on_hull[3]);
+                visible_faces.push_back (front_face);
+            }
+            break;
+
+        case 6:
+        {
+            guint c1 = inside_hull[0] ^ Box3D::XYZ;
+            guint c2 = inside_hull[1] ^ Box3D::XYZ;
+            Box3D::Axis edge = (Box3D::Axis) (c1 ^ c2);
+            if (Box3D::is_single_axis_direction (edge)) {
+                visible_faces = sp_3dbox_adjacent_faces_of_edge (c1, c2);
+            } else if (c1 == c2 ^ Box3D::XYZ) {
+                guint c_cmp = sp_3dbox_get_corner_id_along_edge (box, 0, front_rear_axis, Box3D::FRONT);
+                guint visible_front_corner = (((c_cmp & front_rear_axis) == (c1 & front_rear_axis)) ? c1 : c2);
+                visible_faces = sp_3dbox_faces_meeting_in_corner (visible_front_corner);
+            } else {
+                /* Under what conditions do we end up here? Can we safely ignore this case? */
+                return false;
+            }
+            break;
+        }
+
+        default:
+            /* Under what conditions do we end up here? Can we safely ignore this case? */
+            return false;
+    }
+
+    /* catch weird corner configurations; these should be theoretically impossible, but maybe
+       occur in (almost) degenerate cases due to rounding errors, for example */
+    if (std::find (visible_faces.begin(), visible_faces.end(), -1) != visible_faces.end()) {
+        return false;
+    }
+
+    /* sort the list of visible faces for later use (although it may be already sorted anyway) */
+    std::sort (visible_faces.begin(), visible_faces.end());
+
+    std::vector<gint> invisible_faces;
+    sp_3dbox_remaining_faces (visible_faces, invisible_faces);
+
+
+    if (!sp_3dbox_is_subset_or_superset (visible_faces, box->currently_visible_faces) &&
+        !sp_3dbox_differ_by_opposite_faces (visible_faces, box->currently_visible_faces)) {
+        std::swap (visible_faces, invisible_faces);
+        if (!sp_3dbox_is_subset_or_superset (visible_faces, box->currently_visible_faces) &&
+            !sp_3dbox_differ_by_opposite_faces (visible_faces, box->currently_visible_faces)) {
+            /* Hopefully this case is only caused by rounding errors or something similar;
+               does it need further investigation? */
+            return false;
+        }
+    }
+
+    box->currently_visible_faces = visible_faces;
+
+    // set new z-orders according to the visible/invisible faces
+    guint vis_size = visible_faces.size();
+    for (guint i = 0; i < vis_size; ++i) {
+        new_z_orders[i] = visible_faces[i];
+    }
+    for (guint i = 0; i < invisible_faces.size(); ++i) {
+        new_z_orders[vis_size + i] = invisible_faces[i];
+    }
+
+    // test whether any z-orders actually changed and indicate this in the return status
+    for (int i = 0; i < 6; ++i) {
+        if (box->z_orders[i] != new_z_orders[i]) {
+            // we update the z-orders starting from the index where the change occurs
+            for (int j = i; j < 6; ++j) {
+                box->z_orders[j] = new_z_orders[j];
+            }
+            return true;
+        }
+    }
+    return false;
+}
 
+// FIXME: Can we unify this and the next function for setting the z-orders?
+void sp_3dbox_set_z_orders_in_the_first_place (SP3DBox *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 ();
@@ -481,6 +745,19 @@ void sp_3dbox_set_z_orders (SP3DBox *box)
     }
 }
 
+void sp_3dbox_set_z_orders_later_on (SP3DBox *box)
+{
+    // For efficiency reasons, we only set the new z-orders if something really changed
+    if (sp_3dbox_recompute_z_orders_by_corner_configuration (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) {
@@ -529,6 +806,23 @@ sp_3dbox_link_to_existing_paths (SP3DBox *box, Inkscape::XML::Node *repr) {
     }
 }
 
+void
+sp_3dbox_reshape_after_VP_rotation (SP3DBox *box, Box3D::Axis axis)
+{
+    Box3D::Perspective3D *persp = inkscape_active_document()->get_persp_of_box (box);
+    Box3D::VanishingPoint *vp = persp->get_vanishing_point (axis);
+
+    guint c1 = (axis == Box3D::Z) ? 1 : sp_3dbox_get_front_corner_id (box); // hack
+    guint c2 = c1 ^ axis;
+    NR::Point v = box->corners[c1] - box->corners[c2];
+    double dist = NR::L2 (v) * ((NR::dot (v, vp->v_dir) < 0) ? 1 : -1); // "directed" distance
+
+    Box3D::PerspectiveLine pline (box->corners[c1], axis, persp);
+    NR::Point pt = pline.point_from_lambda (dist);
+
+    sp_3dbox_move_corner_in_Z_direction (box, c2, pt, axis == Box3D::Z);
+}
+
 void
 sp_3dbox_move_corner_in_XY_plane (SP3DBox *box, guint id, NR::Point pt, Box3D::Axis axes)
 {
@@ -633,6 +927,13 @@ sp_3dbox_get_center (SP3DBox *box)
     return sp_3dbox_get_midpoint_between_corners (box, 0, 7);
 }
 
+NR::Point
+sp_3dbox_get_midpoint_in_axis_direction (NR::Point const &C, NR::Point const &D, Box3D::Axis axis, Box3D::Perspective3D *persp)
+{
+    Box3D::PerspectiveLine pl (D, axis, persp);
+    return pl.pt_with_given_cross_ratio (C, D, -1.0);
+}
+
 // TODO: The following function can probably be rewritten in a much more elegant and robust way
 //        by using projective coordinates for all points and using the cross ratio.
 NR::Maybe<NR::Point>
@@ -733,6 +1034,133 @@ sp_3dbox_get_svg_descr_of_persp (Box3D::Perspective3D *persp)
     return g_strdup(os.str().c_str());
 }
 
+// auxiliary function
+static std::pair<NR::Point, NR::Point>
+sp_3dbox_new_midpoints (Box3D::Perspective3D *persp, Box3D::Axis axis, NR::Point const &M0, NR::Point const &M, NR::Point const &A, NR::Point const &B)
+{
+    double cr1 = Box3D::cross_ratio (*persp->get_vanishing_point (axis), M0, M, A);
+    double cr2 = Box3D::cross_ratio (*persp->get_vanishing_point (axis), M, B, M0);
+    if (fabs (cr1 - 1) < Box3D::epsilon) {
+        // FIXME: cr == 1 is a degenerate case; how should we deal with it?
+        return std::make_pair (NR::Point (0,0), NR::Point (0,0));
+    }
+    if (cr1 == NR_HUGE) {
+        return std::make_pair (A, B);
+    }
+    Box3D::PerspectiveLine pl (M0, axis, persp);
+    NR::Point B_new = pl.pt_with_given_cross_ratio (M0, M, cr1 / (cr1 - 1));
+    NR::Point A_new = pl.pt_with_given_cross_ratio (M0, M, 1 - cr2);
+    return std::make_pair (A_new, B_new);
+}
+
+void sp_3dbox_recompute_Z_corners_from_new_center (SP3DBox *box, NR::Point const new_center)
+{
+    // TODO: Clean this function up
+
+    Box3D::Perspective3D *persp = sp_desktop_document (inkscape_active_desktop())->get_persp_of_box (box);
+    NR::Point old_center = box->old_center;
+
+    Box3D::PerspectiveLine aux_line1 (old_center, Box3D::Z, persp);
+    Box3D::PerspectiveLine aux_line2 (new_center, Box3D::Y, persp);
+    NR::Point Z1 = aux_line1.meet (aux_line2);
+
+    NR::Point A0 (sp_3dbox_get_midpoint_in_axis_direction (box->old_corner2, box->old_corner0, Box3D::Y, persp));
+    NR::Point B0 (sp_3dbox_get_midpoint_in_axis_direction (box->old_corner7, box->old_corner5, Box3D::Y, persp));
+    Box3D::PerspectiveLine aux_line3 (A0, Box3D::X, persp);
+    Box3D::PerspectiveLine aux_line4 (B0, Box3D::X, persp);
+
+    NR::Point C0 = aux_line3.meet (aux_line1);
+    NR::Point D0 = aux_line4.meet (aux_line1);
+
+    std::pair<NR::Point, NR::Point> new_midpts = sp_3dbox_new_midpoints (persp, Box3D::Z, old_center, Z1, C0, D0);
+    NR::Point C1 (new_midpts.first);
+    NR::Point D1 (new_midpts.second);
+    Box3D::PerspectiveLine aux_line5 (C1, Box3D::X, persp);
+    Box3D::PerspectiveLine aux_line6 (D1, Box3D::X, persp);
+
+    Box3D::PerspectiveLine aux_line7 (A0, Box3D::Z, persp);
+    Box3D::PerspectiveLine aux_line8 (B0, Box3D::Z, persp);
+
+    NR::Point A1 = aux_line5.meet (aux_line7);
+    NR::Point B1 = aux_line6.meet (aux_line8);
+
+    Box3D::PerspectiveLine aux_line9  (box->old_corner2, Box3D::Z, persp);
+    Box3D::PerspectiveLine aux_line10 (box->old_corner5, Box3D::Z, persp);
+
+    Box3D::PerspectiveLine aux_line11 (A1, Box3D::Y, persp);
+    Box3D::PerspectiveLine aux_line12 (B1, Box3D::Y, persp);
+
+    NR::Point new_corner2 = aux_line9.meet (aux_line11);
+    NR::Point new_corner5 = aux_line10.meet (aux_line12);
+
+    Box3D::PerspectiveLine aux_line13 (A1, Box3D::X, persp);
+    NR::Point E1 = aux_line13.meet (aux_line8);
+    Box3D::PerspectiveLine aux_line14 (E1, Box3D::Y, persp);
+
+    NR::Point new_corner1 = aux_line10.meet (aux_line14);
+
+    sp_3dbox_set_shape_from_points (box, new_corner2, new_corner1, new_corner5);
+}
+
+void sp_3dbox_recompute_XY_corners_from_new_center (SP3DBox *box, NR::Point const new_center)
+{
+    // TODO: Clean this function up
+
+    Box3D::Perspective3D *persp = sp_desktop_document (inkscape_active_desktop())->get_persp_of_box (box);
+    NR::Point old_center = box->old_center;
+
+    NR::Point A0 (sp_3dbox_get_midpoint_in_axis_direction (box->old_corner2, box->old_corner0, Box3D::Y, persp));
+    NR::Point B0 (sp_3dbox_get_midpoint_in_axis_direction (box->old_corner1, box->old_corner3, Box3D::Y, persp));
+
+    /* we first move the box along the X-axis ... */
+    Box3D::PerspectiveLine aux_line1 (old_center, Box3D::X, persp);
+    Box3D::PerspectiveLine aux_line2 (new_center, Box3D::Y, persp);
+    NR::Point Z1 = aux_line1.meet (aux_line2);
+
+    Box3D::PerspectiveLine ref_line (B0, Box3D::X, persp);
+    Box3D::PerspectiveLine pline2 (old_center, Box3D::Z, persp);
+    Box3D::PerspectiveLine pline3 (Z1, Box3D::Z, persp);
+    NR::Point M0 = ref_line.meet (pline2);
+    NR::Point M1 = ref_line.meet (pline3);
+
+    std::pair<NR::Point, NR::Point> new_midpts = sp_3dbox_new_midpoints (persp, Box3D::X, M0, M1, A0, B0);
+    NR::Point A1 (new_midpts.first);
+    NR::Point B1 (new_midpts.second);
+
+    /* ... and then along the Y-axis */
+    Box3D::PerspectiveLine pline4 (box->old_corner1, Box3D::X, persp);
+    Box3D::PerspectiveLine pline5 (box->old_corner3, Box3D::X, persp);
+    Box3D::PerspectiveLine aux_line3 (M1, Box3D::Y, persp);
+    NR::Point C1 = aux_line3.meet (pline4);
+    NR::Point D1 = aux_line3.meet (pline5);
+
+    Box3D::PerspectiveLine aux_line4 (new_center, Box3D::Z, persp);
+    NR::Point M2 = aux_line4.meet (aux_line3);
+
+    std::pair<NR::Point, NR::Point> other_new_midpts = sp_3dbox_new_midpoints (persp, Box3D::Y, M1, M2, C1, D1);
+    NR::Point C2 (other_new_midpts.first);
+    NR::Point D2 (other_new_midpts.second);
+
+    Box3D::PerspectiveLine plXC (C2, Box3D::X, persp);
+    Box3D::PerspectiveLine plXD (D2, Box3D::X, persp);
+    Box3D::PerspectiveLine plYA (A1, Box3D::Y, persp);
+    Box3D::PerspectiveLine plYB (B1, Box3D::Y, persp);
+
+    NR::Point new_corner2 (plXD.meet (plYA));
+    NR::Point new_corner1 (plXC.meet (plYB));
+
+    NR::Point tmp_corner1 (pline4.meet (plYB));
+    Box3D::PerspectiveLine pline6 (box->old_corner5, Box3D::X, persp);
+    Box3D::PerspectiveLine pline7 (tmp_corner1, Box3D::Z, persp);
+    NR::Point tmp_corner5 (pline6.meet (pline7));
+
+    Box3D::PerspectiveLine pline8 (tmp_corner5, Box3D::Y, persp);
+    Box3D::PerspectiveLine pline9 (new_corner1, Box3D::Z, persp);
+    NR::Point new_corner5 (pline8.meet (pline9));
+
+    sp_3dbox_set_shape_from_points (box, new_corner2, new_corner1, new_corner5);
+}
+
 void sp_3dbox_update_perspective_lines()
 {
     SPEventContext *ec = inkscape_active_event_context();
@@ -776,6 +1204,9 @@ void sp_3dbox_corners_for_perspective_lines (const SP3DBox * box, Box3D::Axis ax
             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;
+        default:
+            // do nothing
+            break;
     }            
 }