From e831e8ed4cdf676dc9ed9e1dfd7debccb94a9dd3 Mon Sep 17 00:00:00 2001 From: cilix42 Date: Wed, 29 Aug 2007 13:43:29 +0000 Subject: [PATCH] Constrained center-dragging for 3D boxes (with Ctrl) --- src/box3d.cpp | 3 ++ src/box3d.h | 1 + src/line-geometry.cpp | 14 ++++---- src/object-edit.cpp | 77 ++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 85 insertions(+), 10 deletions(-) diff --git a/src/box3d.cpp b/src/box3d.cpp index 0cf0743db..5e67f46ab 100644 --- a/src/box3d.cpp +++ b/src/box3d.cpp @@ -1019,6 +1019,9 @@ sp_3dbox_new_midpoints (Box3D::Perspective3D *persp, Box3D::Axis axis, NR::Point // 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); diff --git a/src/box3d.h b/src/box3d.h index 27e734193..3d52c96d1 100644 --- a/src/box3d.h +++ b/src/box3d.h @@ -50,6 +50,7 @@ struct SP3DBox : public SPGroup { // FIXME: If we only allow a single box to be dragged at a time then we can save memory by storing // the old positions centrally in SP3DBoxContext (instead of in each box separately) + // Also, it may be better not to store the old corners but rather the old lines to which we want to snap NR::Point old_center; NR::Point old_corner2; NR::Point old_corner1; diff --git a/src/line-geometry.cpp b/src/line-geometry.cpp index 5d8eca8a1..7b6ba0449 100644 --- a/src/line-geometry.cpp +++ b/src/line-geometry.cpp @@ -192,9 +192,8 @@ double cross_ratio (NR::Point const &A, NR::Point const &B, NR::Point const &C, double lambda_D = line.lambda (D); if (fabs (lambda_D - lambda_A) < epsilon || fabs (lambda_C - lambda_B) < epsilon) { - // FIXME: What should we return if the cross ratio can't be computed? - return 0; - //return NR_HUGE; + // We return NR_HUGE so that we can catch this case in the calling functions + return NR_HUGE; } return (((lambda_C - lambda_A) / (lambda_D - lambda_A)) * ((lambda_D - lambda_B) / (lambda_C - lambda_B))); } @@ -204,15 +203,18 @@ double cross_ratio (VanishingPoint const &V, NR::Point const &B, NR::Point const if (V.is_finite()) { return cross_ratio (V.get_pos(), B, C, D); } else { + if (B == D) { + // catch this case so that the line BD below is non-degenerate + return 0; + } Line line (B, D); double lambda_B = line.lambda (B); double lambda_C = line.lambda (C); double lambda_D = line.lambda (D); if (fabs (lambda_C - lambda_B) < epsilon) { - // FIXME: What should we return if the cross ratio can't be computed? - return 0; - //return NR_HUGE; + // We return NR_HUGE so that we can catch this case in the calling functions + return NR_HUGE; } return (lambda_D - lambda_B) / (lambda_C - lambda_B); } diff --git a/src/object-edit.cpp b/src/object-edit.cpp index 40e38a393..fa0090c7f 100644 --- a/src/object-edit.cpp +++ b/src/object-edit.cpp @@ -555,15 +555,16 @@ static inline Box3D::Axis movement_axis_of_3dbox_corner (guint corner, guint sta */ // Should we make the threshold settable in the preferences? -static double remember_snap_threshold = 20; +static double remember_snap_threshold = 30; static guint remember_snap_index = 0; +static guint remember_snap_index_center = 0; static NR::Point snap_knot_position_3dbox (SP3DBox *box, guint corner, Box3D::Axis direction, NR::Point const &origin, NR::Point const &p, guint state) { SPDesktop * desktop = inkscape_active_desktop(); Box3D::Perspective3D *persp = sp_desktop_document (desktop)->get_persp_of_box (box); - g_return_val_if_fail (!is_single_axis_direction (direction), p); + if (is_single_axis_direction (direction)) return p; Box3D::Axis axis1 = Box3D::extract_first_axis_direction (direction); Box3D::Axis axis2 = Box3D::extract_second_axis_direction (direction); @@ -616,6 +617,67 @@ static NR::Point snap_knot_position_3dbox (SP3DBox *box, guint corner, Box3D::Ax } } +static NR::Point snap_center_position_3dbox (SP3DBox *box, NR::Point const &origin, NR::Point const &p) +{ + SPDesktop * desktop = inkscape_active_desktop(); + Box3D::Perspective3D *persp = sp_desktop_document (desktop)->get_persp_of_box (box); + + Box3D::Axis axis1 = Box3D::X; + Box3D::Axis axis2 = Box3D::Y; + + NR::Matrix const i2d (sp_item_i2d_affine (SP_ITEM (box))); + NR::Point origin_dt = origin * i2d; + NR::Point p_dt = p * i2d; + + Box3D::PerspectiveLine pl1 (origin_dt, axis1, persp); + Box3D::PerspectiveLine pl2 (origin_dt, axis2, persp); + NR::Point midpt1 = sp_3dbox_get_midpoint_in_axis_direction (box->old_corner1, box->old_corner5, Box3D::Z, persp); + NR::Point midpt2 = sp_3dbox_get_midpoint_in_axis_direction (box->old_corner3, box->old_corner7, Box3D::Z, persp); + Box3D::Line diag1 (origin_dt, midpt1); + Box3D::Line diag2 (origin_dt, midpt2); + + int num_snap_lines = 4; + NR::Point snap_pts[num_snap_lines]; + + // should we snap to the closest point or to the projection along perspective lines? + snap_pts[0] = pl1.closest_to (p_dt); + snap_pts[1] = pl2.closest_to (p_dt); + snap_pts[2] = diag1.closest_to (p_dt); + snap_pts[3] = diag2.closest_to (p_dt); + + gdouble const zoom = desktop->current_zoom(); + + double snap_dists[num_snap_lines]; + + for (int i = 0; i < num_snap_lines; ++i) { + snap_dists[i] = NR::L2 (snap_pts[i] - p_dt) * zoom; + } + + bool within_tolerance = true; + for (int i = 0; i < num_snap_lines; ++i) { + if (snap_dists[i] > remember_snap_threshold) { + within_tolerance = false; + break; + } + } + + int snap_index = -1; + double snap_dist = NR_HUGE; + for (int i = 0; i < num_snap_lines; ++i) { + if (snap_dists[i] < snap_dist) { + snap_index = i; + snap_dist = snap_dists[i]; + } + } + + if (within_tolerance) { + return snap_pts[remember_snap_index_center] * i2d.inverse(); + } else { + remember_snap_index_center = snap_index; + return snap_pts[snap_index] * i2d.inverse(); + } +} + static NR::Point sp_3dbox_knot_get(SPItem *item, guint knot_id) { g_assert(item != NULL); @@ -654,10 +716,17 @@ static void sp_3dbox_knot_center_set(SPItem *item, NR::Point const &new_pos, NR: SP3DBox *box = SP_3DBOX(item); NR::Matrix const i2d (sp_item_i2d_affine (item)); + NR::Point new_pt (new_pos); + + if ((state & GDK_CONTROL_MASK) && !(state & GDK_SHIFT_MASK)) { + // snap if Ctrl is pressed and movement isn't already constrained to a single axis + new_pt = snap_center_position_3dbox (box, origin, new_pos); + } + if (state & GDK_SHIFT_MASK) { - sp_3dbox_recompute_Z_corners_from_new_center (box, new_pos * i2d); + sp_3dbox_recompute_Z_corners_from_new_center (box, new_pt * i2d); } else { - sp_3dbox_recompute_XY_corners_from_new_center (box, new_pos * i2d); + sp_3dbox_recompute_XY_corners_from_new_center (box, new_pt * i2d); } sp_3dbox_update_curves (box); -- 2.30.2