X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Fobject-edit.cpp;h=63ccf827e740cf4b55c1a632686031a9f104bdcc;hb=b7d95f994b4858eeab9ce480b04933b5e94d40eb;hp=14539417b8f01b9bb3662fa1c66c00b321c184ef;hpb=f0c8d6ccf3bb136f2e417ba468aad0969f48fcde;p=inkscape.git diff --git a/src/object-edit.cpp b/src/object-edit.cpp index 14539417b..63ccf827e 100644 --- a/src/object-edit.cpp +++ b/src/object-edit.cpp @@ -18,6 +18,7 @@ #include "sp-item.h" #include "sp-rect.h" +#include "box3d.h" #include "sp-ellipse.h" #include "sp-star.h" #include "sp-spiral.h" @@ -29,6 +30,7 @@ #include "desktop-affine.h" #include #include "desktop.h" +#include "desktop-handles.h" #include "sp-namedview.h" #include "sp-pattern.h" @@ -48,11 +50,13 @@ #define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m))) static SPKnotHolder *sp_rect_knot_holder(SPItem *item, SPDesktop *desktop); +//static +SPKnotHolder *sp_3dbox_knot_holder(SPItem *item, SPDesktop *desktop); static SPKnotHolder *sp_arc_knot_holder(SPItem *item, SPDesktop *desktop); static SPKnotHolder *sp_star_knot_holder(SPItem *item, SPDesktop *desktop); static SPKnotHolder *sp_spiral_knot_holder(SPItem *item, SPDesktop *desktop); static SPKnotHolder *sp_offset_knot_holder(SPItem *item, SPDesktop *desktop); -static SPKnotHolder *sp_path_knot_holder(SPItem *item, SPDesktop *desktop); +static SPKnotHolder *sp_misc_knot_holder(SPItem *item, SPDesktop *desktop); static SPKnotHolder *sp_flowtext_knot_holder(SPItem *item, SPDesktop *desktop); static void sp_pat_knot_holder(SPItem *item, SPKnotHolder *knot_holder); @@ -61,6 +65,8 @@ sp_item_knot_holder(SPItem *item, SPDesktop *desktop) { if (SP_IS_RECT(item)) { return sp_rect_knot_holder(item, desktop); + } else if (SP_IS_3DBOX(item)) { + return sp_3dbox_knot_holder(item, desktop); } else if (SP_IS_ARC(item)) { return sp_arc_knot_holder(item, desktop); } else if (SP_IS_STAR(item)) { @@ -69,10 +75,10 @@ sp_item_knot_holder(SPItem *item, SPDesktop *desktop) return sp_spiral_knot_holder(item, desktop); } else if (SP_IS_OFFSET(item)) { return sp_offset_knot_holder(item, desktop); - } else if (SP_IS_PATH(item)) { - return sp_path_knot_holder(item, desktop); } else if (SP_IS_FLOWTEXT(item) && SP_FLOWTEXT(item)->has_internal_frame()) { return sp_flowtext_knot_holder(item, desktop); + } else { + return sp_misc_knot_holder(item, desktop); } return NULL; @@ -216,10 +222,11 @@ static NR::Point sp_pattern_scale_get(SPItem *item) static NR::Point snap_knot_position(SPItem *item, NR::Point const &p) { SPDesktop const *desktop = inkscape_active_desktop(); - NR::Point s = sp_desktop_dt2root_xy_point(desktop, p); + NR::Matrix const i2d (sp_item_i2d_affine (item)); + NR::Point s = p * i2d; SnapManager const &m = desktop->namedview->snap_manager; - s = m.freeSnap(Inkscape::Snapper::BBOX_POINT | Inkscape::Snapper::SNAP_POINT, s, item).getPoint(); - return sp_desktop_root2dt_xy_point(desktop, s); + s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, s, item).getPoint(); + return s * i2d.inverse(); } static NR::Point sp_rect_rx_get(SPItem *item) @@ -233,15 +240,17 @@ static void sp_rect_rx_set(SPItem *item, NR::Point const &p, NR::Point const &or { SPRect *rect = SP_RECT(item); - NR::Point const s = snap_knot_position(rect, p); + //In general we cannot just snap this radius to an arbitrary point, as we have only a single + //degree of freedom. For snapping to an arbitrary point we need two DOF. If we're going to snap + //the radius then we should have a constrained snap. snap_knot_position() is unconstrained if (state & GDK_CONTROL_MASK) { gdouble temp = MIN(rect->height.computed, rect->width.computed) / 2.0; - rect->rx.computed = rect->ry.computed = CLAMP(rect->x.computed + rect->width.computed - s[NR::X], 0.0, temp); + rect->rx.computed = rect->ry.computed = CLAMP(rect->x.computed + rect->width.computed - p[NR::X], 0.0, temp); rect->rx._set = rect->ry._set = true; } else { - rect->rx.computed = CLAMP(rect->x.computed + rect->width.computed - s[NR::X], 0.0, rect->width.computed / 2.0); + rect->rx.computed = CLAMP(rect->x.computed + rect->width.computed - p[NR::X], 0.0, rect->width.computed / 2.0); rect->rx._set = true; } @@ -260,19 +269,21 @@ static void sp_rect_ry_set(SPItem *item, NR::Point const &p, NR::Point const &or { SPRect *rect = SP_RECT(item); - NR::Point const s = snap_knot_position(rect, p); + //In general we cannot just snap this radius to an arbitrary point, as we have only a single + //degree of freedom. For snapping to an arbitrary point we need two DOF. If we're going to snap + //the radius then we should have a constrained snap. snap_knot_position() is unconstrained if (state & GDK_CONTROL_MASK) { gdouble temp = MIN(rect->height.computed, rect->width.computed) / 2.0; - rect->rx.computed = rect->ry.computed = CLAMP(s[NR::Y] - rect->y.computed, 0.0, temp); + rect->rx.computed = rect->ry.computed = CLAMP(p[NR::Y] - rect->y.computed, 0.0, temp); rect->ry._set = rect->rx._set = true; } else { if (!rect->rx._set || rect->rx.computed == 0) { - rect->ry.computed = CLAMP(s[NR::Y] - rect->y.computed, + rect->ry.computed = CLAMP(p[NR::Y] - rect->y.computed, 0.0, MIN(rect->height.computed / 2.0, rect->width.computed / 2.0)); } else { - rect->ry.computed = CLAMP(s[NR::Y] - rect->y.computed, + rect->ry.computed = CLAMP(p[NR::Y] - rect->y.computed, 0.0, rect->height.computed / 2.0); } @@ -517,6 +528,333 @@ static SPKnotHolder *sp_rect_knot_holder(SPItem *item, SPDesktop *desktop) return knot_holder; } +/* 3D Box */ + +static inline Box3D::Axis movement_axis_of_3dbox_corner (guint corner, guint state) +{ + // this function has the purpose to simplify a change in the resizing behaviour of boxes + switch (corner) { + case 0: + case 1: + case 2: + case 3: + return ((state & GDK_SHIFT_MASK) ? Box3D::Z : Box3D::XY); + case 4: + case 5: + case 6: + case 7: + return ((state & GDK_SHIFT_MASK) ? Box3D::XY : Box3D::Z); + } + return Box3D::NONE; +} + +/* + * To keep the snappoint from jumping randomly between the two lines when the mouse pointer is close to + * their intersection, we remember the last snapped line and keep snapping to this specific line as long + * as the distance from the intersection to the mouse pointer is less than remember_snap_threshold. + */ + +// Should we make the threshold settable in the preferences? +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); + + 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); + + 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); + Box3D::Line diag1 (origin_dt, box->corners[corner ^ Box3D::XY]); + + int num_snap_lines = 3; + NR::Point snap_pts[num_snap_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); + + 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] * i2d.inverse(); + } else { + remember_snap_index = snap_index; + return snap_pts[snap_index] * i2d.inverse(); + } +} + +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); + SP3DBox *box = SP_3DBOX(item); + + NR::Matrix const i2d (sp_item_i2d_affine (item)); + return sp_3dbox_get_corner(box, knot_id) * i2d; +} + +static void sp_3dbox_knot_set(SPItem *item, guint knot_id, NR::Point const &new_pos, NR::Point const &origin, guint state) +{ + g_assert(item != NULL); + SP3DBox *box = SP_3DBOX(item); + + NR::Matrix const i2d (sp_item_i2d_affine (item)); + Box3D::Axis direction = movement_axis_of_3dbox_corner (knot_id, state); + if ((state & GDK_CONTROL_MASK) && !is_single_axis_direction (direction)) { + // snap if Ctrl is pressed and movement isn't already constrained to a single axis + NR::Point const s = snap_knot_position_3dbox (box, knot_id, direction, origin, new_pos, state); + sp_3dbox_move_corner_in_Z_direction (box, knot_id, s * i2d, false); + } else { + if (direction == Box3D::Z) { + sp_3dbox_move_corner_in_Z_direction (box, knot_id, new_pos * i2d, true); + } else { + sp_3dbox_move_corner_in_Z_direction (box, knot_id, new_pos * i2d, false); + } + } + sp_3dbox_update_curves (box); + sp_3dbox_set_ratios (box); + sp_3dbox_update_perspective_lines (); + sp_3dbox_set_z_orders_later_on (box); +} + +static void sp_3dbox_knot_center_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state) +{ + 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_pt * i2d); + } else { + sp_3dbox_recompute_XY_corners_from_new_center (box, new_pt * i2d); + } + + sp_3dbox_update_curves (box); + sp_3dbox_set_z_orders_later_on (box); +} + +static NR::Point sp_3dbox_knot_center_get(SPItem *item) +{ + NR::Maybe center = sp_3dbox_get_center(SP_3DBOX(item)); + if (!center) return NR::Point (0, 0); + NR::Matrix const i2d (sp_item_i2d_affine (item)); + return (*center) * i2d; +} + +static void sp_3dbox_knot0_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state) +{ + sp_3dbox_knot_set(item, 0, new_pos, origin, state); +} + +static void sp_3dbox_knot1_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state) +{ + sp_3dbox_knot_set(item, 1, new_pos, origin, state); +} + +static void sp_3dbox_knot2_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state) +{ + sp_3dbox_knot_set(item, 2, new_pos, origin, state); +} + +static void sp_3dbox_knot3_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state) +{ + sp_3dbox_knot_set(item, 3, new_pos, origin, state); +} + +static void sp_3dbox_knot4_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state) +{ + sp_3dbox_knot_set(item, 4, new_pos, origin, state); +} + +static void sp_3dbox_knot5_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state) +{ + sp_3dbox_knot_set(item, 5, new_pos, origin, state); +} + +static void sp_3dbox_knot6_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state) +{ + sp_3dbox_knot_set(item, 6, new_pos, origin, state); +} + +static void sp_3dbox_knot7_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state) +{ + sp_3dbox_knot_set(item, 7, new_pos, origin, state); +} + +static NR::Point sp_3dbox_knot0_get(SPItem *item) +{ + return sp_3dbox_knot_get(item, 0); +} + +static NR::Point sp_3dbox_knot1_get(SPItem *item) +{ + return sp_3dbox_knot_get(item, 1); +} + +static NR::Point sp_3dbox_knot2_get(SPItem *item) +{ + return sp_3dbox_knot_get(item, 2); +} + +static NR::Point sp_3dbox_knot3_get(SPItem *item) +{ + return sp_3dbox_knot_get(item, 3); +} + +static NR::Point sp_3dbox_knot4_get(SPItem *item) +{ + return sp_3dbox_knot_get(item, 4); +} + +static NR::Point sp_3dbox_knot5_get(SPItem *item) +{ + return sp_3dbox_knot_get(item, 5); +} + +static NR::Point sp_3dbox_knot6_get(SPItem *item) +{ + return sp_3dbox_knot_get(item, 6); +} + +static NR::Point sp_3dbox_knot7_get(SPItem *item) +{ + return sp_3dbox_knot_get(item, 7); +} + + +//static +SPKnotHolder * +sp_3dbox_knot_holder(SPItem *item, SPDesktop *desktop) +{ + g_assert(item != NULL); + SPKnotHolder *knot_holder = sp_knot_holder_new(desktop, item, NULL); + + sp_knot_holder_add(knot_holder, sp_3dbox_knot0_set, sp_3dbox_knot0_get, NULL, + _("Resize box in X/Y direction; with Shift along the Z axis; with Ctrl to constrain to the directions of edges or diagonals")); + sp_knot_holder_add(knot_holder, sp_3dbox_knot1_set, sp_3dbox_knot1_get, NULL, + _("Resize box in X/Y direction; with Shift along the Z axis; with Ctrl to constrain to the directions of edges or diagonals")); + sp_knot_holder_add(knot_holder, sp_3dbox_knot2_set, sp_3dbox_knot2_get, NULL, + _("Resize box in X/Y direction; with Shift along the Z axis; with Ctrl to constrain to the directions of edges or diagonals")); + sp_knot_holder_add(knot_holder, sp_3dbox_knot3_set, sp_3dbox_knot3_get, NULL, + _("Resize box in X/Y direction; with Shift along the Z axis; with Ctrl to constrain to the directions of edges or diagonals")); + sp_knot_holder_add(knot_holder, sp_3dbox_knot4_set, sp_3dbox_knot4_get, NULL, + _("Resize box along the Z axis; with Shift in X/Y direction; with Ctrl to constrain to the directions of edges or diagonals")); + sp_knot_holder_add(knot_holder, sp_3dbox_knot5_set, sp_3dbox_knot5_get, NULL, + _("Resize box along the Z axis; with Shift in X/Y direction; with Ctrl to constrain to the directions of edges or diagonals")); + sp_knot_holder_add(knot_holder, sp_3dbox_knot6_set, sp_3dbox_knot6_get, NULL, + _("Resize box along the Z axis; with Shift in X/Y direction; with Ctrl to constrain to the directions of edges or diagonals")); + sp_knot_holder_add(knot_holder, sp_3dbox_knot7_set, sp_3dbox_knot7_get, NULL, + _("Resize box along the Z axis; with Shift in X/Y direction; with Ctrl to constrain to the directions of edges or diagonals")); + + // center dragging + sp_knot_holder_add_full(knot_holder, sp_3dbox_knot_center_set, sp_3dbox_knot_center_get, NULL, + SP_KNOT_SHAPE_CROSS, SP_KNOT_MODE_XOR,_("Move the box in perspective.")); + + sp_pat_knot_holder(item, knot_holder); + + return knot_holder; +} + /* SPArc */ /* @@ -1019,9 +1357,9 @@ sp_offset_knot_holder(SPItem *item, SPDesktop *desktop) } static SPKnotHolder * -sp_path_knot_holder(SPItem *item, SPDesktop *desktop) // FIXME: eliminate, instead make a pattern-drag similar to gradient-drag +sp_misc_knot_holder(SPItem *item, SPDesktop *desktop) // FIXME: eliminate, instead make a pattern-drag similar to gradient-drag { - if ((SP_OBJECT(item)->style->fill.type == SP_PAINT_TYPE_PAINTSERVER) + if ((SP_OBJECT(item)->style->fill.isPaintserver()) && SP_IS_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style))) { SPKnotHolder *knot_holder = sp_knot_holder_new(desktop, item, NULL); @@ -1036,7 +1374,7 @@ sp_path_knot_holder(SPItem *item, SPDesktop *desktop) // FIXME: eliminate, inste static void sp_pat_knot_holder(SPItem *item, SPKnotHolder *knot_holder) { - if ((SP_OBJECT(item)->style->fill.type == SP_PAINT_TYPE_PAINTSERVER) + if ((SP_OBJECT(item)->style->fill.isPaintserver()) && SP_IS_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style))) { sp_knot_holder_add_full(knot_holder, sp_pattern_xy_set, sp_pattern_xy_get, NULL, SP_KNOT_SHAPE_CROSS, SP_KNOT_MODE_XOR,