X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Fobject-edit.cpp;h=27e75afab1340151367e3dd5db22cdc65f5446ef;hb=42382dfb4164b919ffcdf03766da9f8be4e43b42;hp=c597f3a265f7b748912b3e55a938386af825f698;hpb=9d0ce7b32d7ef50eb5398355676a6db57a688a15;p=inkscape.git diff --git a/src/object-edit.cpp b/src/object-edit.cpp index c597f3a26..27e75afab 100644 --- a/src/object-edit.cpp +++ b/src/object-edit.cpp @@ -1,11 +1,11 @@ -#define __SP_OBJECT_EDIT_C__ - /* * Node editing extension to objects * * Authors: * Lauris Kaplinski * Mitsuru Oka + * Maximilian Albert + * Abhishek Sharma * * Licensed under GNU GPL */ @@ -24,13 +24,12 @@ #include "sp-spiral.h" #include "sp-offset.h" #include "sp-flowtext.h" -#include "prefs-utils.h" -#include "inkscape.h" -#include "snap.h" -#include "desktop-affine.h" -#include +#include "preferences.h" +#include "style.h" #include "desktop.h" +#include "desktop-handles.h" #include "sp-namedview.h" +#include "live_effects/effect.h" #include "sp-pattern.h" #include "sp-path.h" @@ -41,247 +40,169 @@ #include - #include "xml/repr.h" -#include "isnan.h" +#include "2geom/isnan.h" #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_flowtext_knot_holder(SPItem *item, SPDesktop *desktop); -static void sp_pat_knot_holder(SPItem *item, SPKnotHolder *knot_holder); - -SPKnotHolder * +static KnotHolder *sp_lpe_knot_holder(SPItem *item, SPDesktop *desktop) +{ + KnotHolder *knot_holder = new KnotHolder(desktop, item, NULL); + + Inkscape::LivePathEffect::Effect *effect = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item)); + effect->addHandles(knot_holder, desktop, item); + + return knot_holder; +} + +KnotHolder * 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); + KnotHolder *knotholder = NULL; + + if (SP_IS_LPE_ITEM(item) && + sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item)) && + sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))->isVisible() && + sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))->providesKnotholder()) { + knotholder = sp_lpe_knot_holder(item, desktop); + } else if (SP_IS_RECT(item)) { + knotholder = new RectKnotHolder(desktop, item, NULL); + } else if (SP_IS_BOX3D(item)) { + knotholder = new Box3DKnotHolder(desktop, item, NULL); } else if (SP_IS_ARC(item)) { - return sp_arc_knot_holder(item, desktop); + knotholder = new ArcKnotHolder(desktop, item, NULL); } else if (SP_IS_STAR(item)) { - return sp_star_knot_holder(item, desktop); + knotholder = new StarKnotHolder(desktop, item, NULL); } else if (SP_IS_SPIRAL(item)) { - return sp_spiral_knot_holder(item, desktop); + knotholder = new SpiralKnotHolder(desktop, item, NULL); } 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); + knotholder = new OffsetKnotHolder(desktop, item, NULL); } else if (SP_IS_FLOWTEXT(item) && SP_FLOWTEXT(item)->has_internal_frame()) { - return sp_flowtext_knot_holder(item, desktop); + knotholder = new FlowtextKnotHolder(desktop, SP_FLOWTEXT(item)->get_frame(NULL), NULL); + } else if ((SP_OBJECT(item)->style->fill.isPaintserver()) + && SP_IS_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style))) { + knotholder = new KnotHolder(desktop, item, NULL); + knotholder->add_pattern_knotholder(); } - return NULL; -} - - -/* Pattern manipulation */ - -static gdouble sp_pattern_extract_theta(SPPattern *pat, gdouble scale) -{ - gdouble theta = asin(pat->patternTransform[1] / scale); - if (pat->patternTransform[0] < 0) theta = M_PI - theta ; - return theta; -} - -static gdouble sp_pattern_extract_scale(SPPattern *pat) -{ - gdouble s = pat->patternTransform[1]; - gdouble c = pat->patternTransform[0]; - gdouble xscale = sqrt(c * c + s * s); - return xscale; -} - -static NR::Point sp_pattern_extract_trans(SPPattern const *pat) -{ - return NR::Point(pat->patternTransform[4], pat->patternTransform[5]); -} - -static void -sp_pattern_xy_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) -{ - SPPattern *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style)); - - NR::Point p_snapped = p; - - if ( state & GDK_CONTROL_MASK ) { - if (fabs((p - origin)[NR::X]) > fabs((p - origin)[NR::Y])) { - p_snapped[NR::Y] = origin[NR::Y]; - } else { - p_snapped[NR::X] = origin[NR::X]; - } - } - - if (state) { - NR::Point const q = p_snapped - sp_pattern_extract_trans(pat); - sp_item_adjust_pattern(item, NR::Matrix(NR::translate(q))); - } - - item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - - -static NR::Point sp_pattern_xy_get(SPItem *item) -{ - SPPattern const *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style)); - return sp_pattern_extract_trans(pat); -} - -static NR::Point sp_pattern_angle_get(SPItem *item) -{ - SPPattern *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style)); - - gdouble x = (pattern_width(pat)*0.5); - gdouble y = 0; - NR::Point delta = NR::Point(x,y); - gdouble scale = sp_pattern_extract_scale(pat); - gdouble theta = sp_pattern_extract_theta(pat, scale); - delta = delta * NR::Matrix(NR::rotate(theta))*NR::Matrix(NR::scale(scale,scale)); - delta = delta + sp_pattern_extract_trans(pat); - return delta; -} - -static void -sp_pattern_angle_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) -{ - int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12); - - SPPattern *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style)); - - // get the angle from pattern 0,0 to the cursor pos - NR::Point delta = p - sp_pattern_extract_trans(pat); - gdouble theta = atan2(delta); - - if ( state & GDK_CONTROL_MASK ) { - theta = sp_round(theta, M_PI/snaps); - } - - // get the scale from the current transform so we can keep it. - gdouble scl = sp_pattern_extract_scale(pat); - NR::Matrix rot = NR::Matrix(NR::rotate(theta)) * NR::Matrix(NR::scale(scl,scl)); - NR::Point const t = sp_pattern_extract_trans(pat); - rot[4] = t[NR::X]; - rot[5] = t[NR::Y]; - sp_item_adjust_pattern(item, rot, true); - item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - -static void -sp_pattern_scale_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) -{ - SPPattern *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style)); - - // Get the scale from the position of the knotholder, - NR::Point d = p - sp_pattern_extract_trans(pat); - gdouble s = NR::L2(d); - gdouble pat_x = pattern_width(pat) * 0.5; - gdouble pat_y = pattern_height(pat) * 0.5; - gdouble pat_h = hypot(pat_x, pat_y); - gdouble scl = s / pat_h; - - // get angle from current transform, (need get current scale first to calculate angle) - gdouble oldscale = sp_pattern_extract_scale(pat); - gdouble theta = sp_pattern_extract_theta(pat,oldscale); - - NR::Matrix rot = NR::Matrix(NR::rotate(theta)) * NR::Matrix(NR::scale(scl,scl)); - NR::Point const t = sp_pattern_extract_trans(pat); - rot[4] = t[NR::X]; - rot[5] = t[NR::Y]; - sp_item_adjust_pattern(item, rot, true); - item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - - -static NR::Point sp_pattern_scale_get(SPItem *item) -{ - SPPattern *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style)); - - gdouble x = pattern_width(pat)*0.5; - gdouble y = pattern_height(pat)*0.5; - NR::Point delta = NR::Point(x,y); - NR::Matrix a = pat->patternTransform; - a[4] = 0; - a[5] = 0; - delta = delta * a; - delta = delta + sp_pattern_extract_trans(pat); - return delta; + return knotholder; } /* SPRect */ -static NR::Point snap_knot_position(SPItem *item, NR::Point const &p) -{ - SPDesktop const *desktop = inkscape_active_desktop(); - 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 s * i2d.inverse(); -} - -static NR::Point sp_rect_rx_get(SPItem *item) +/* handle for horizontal rounding radius */ +class RectKnotHolderEntityRX : public KnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); + virtual void knot_click(guint state); +}; + +/* handle for vertical rounding radius */ +class RectKnotHolderEntityRY : public KnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); + virtual void knot_click(guint state); +}; + +/* handle for width/height adjustment */ +class RectKnotHolderEntityWH : public KnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); + +protected: + void set_internal(Geom::Point const &p, Geom::Point const &origin, guint state); +}; + +/* handle for x/y adjustment */ +class RectKnotHolderEntityXY : public KnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +}; + +Geom::Point +RectKnotHolderEntityRX::knot_get() { SPRect *rect = SP_RECT(item); - return NR::Point(rect->x.computed + rect->width.computed - rect->rx.computed, rect->y.computed); + return Geom::Point(rect->x.computed + rect->width.computed - rect->rx.computed, rect->y.computed); } -static void sp_rect_rx_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) +void +RectKnotHolderEntityRX::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) { SPRect *rect = SP_RECT(item); - + //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 + Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(-1, 0))); 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 - p[NR::X], 0.0, temp); + rect->rx.computed = rect->ry.computed = CLAMP(rect->x.computed + rect->width.computed - s[Geom::X], 0.0, temp); rect->rx._set = rect->ry._set = true; } else { - rect->rx.computed = CLAMP(rect->x.computed + rect->width.computed - p[NR::X], 0.0, rect->width.computed / 2.0); + rect->rx.computed = CLAMP(rect->x.computed + rect->width.computed - s[Geom::X], 0.0, rect->width.computed / 2.0); rect->rx._set = true; } + update_knot(); + ((SPObject*)rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } +void +RectKnotHolderEntityRX::knot_click(guint state) +{ + SPRect *rect = SP_RECT(item); -static NR::Point sp_rect_ry_get(SPItem *item) + if (state & GDK_SHIFT_MASK) { + /* remove rounding from rectangle */ + SP_OBJECT_REPR(rect)->setAttribute("rx", NULL); + SP_OBJECT_REPR(rect)->setAttribute("ry", NULL); + } else if (state & GDK_CONTROL_MASK) { + /* Ctrl-click sets the vertical rounding to be the same as the horizontal */ + SP_OBJECT_REPR(rect)->setAttribute("ry", SP_OBJECT_REPR(rect)->attribute("rx")); + } + +} + +Geom::Point +RectKnotHolderEntityRY::knot_get() { SPRect *rect = SP_RECT(item); - return NR::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->ry.computed); + return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->ry.computed); } -static void sp_rect_ry_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) +void +RectKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) { SPRect *rect = SP_RECT(item); - + //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 + Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(0, 1))); - if (state & GDK_CONTROL_MASK) { + if (state & GDK_CONTROL_MASK) { // When holding control then rx will be kept equal to ry, + // resulting in a perfect circle (and not an ellipse) gdouble temp = MIN(rect->height.computed, rect->width.computed) / 2.0; - rect->rx.computed = rect->ry.computed = CLAMP(p[NR::Y] - rect->y.computed, 0.0, temp); + rect->rx.computed = rect->ry.computed = CLAMP(s[Geom::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(p[NR::Y] - rect->y.computed, + rect->ry.computed = CLAMP(s[Geom::Y] - rect->y.computed, 0.0, MIN(rect->height.computed / 2.0, rect->width.computed / 2.0)); } else { - rect->ry.computed = CLAMP(p[NR::Y] - rect->y.computed, + rect->ry.computed = CLAMP(s[Geom::Y] - rect->y.computed, 0.0, rect->height.computed / 2.0); } @@ -289,42 +210,20 @@ static void sp_rect_ry_set(SPItem *item, NR::Point const &p, NR::Point const &or rect->ry._set = true; } - ((SPObject *)rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} + update_knot(); -/** - * Remove rounding from a rectangle. - */ -static void rect_remove_rounding(SPRect *rect) -{ - SP_OBJECT_REPR(rect)->setAttribute("rx", NULL); - SP_OBJECT_REPR(rect)->setAttribute("ry", NULL); -} - -/** - * Called when the horizontal rounding radius knot is clicked. - */ -static void sp_rect_rx_knot_click(SPItem *item, guint state) -{ - SPRect *rect = SP_RECT(item); - - if (state & GDK_SHIFT_MASK) { - rect_remove_rounding(rect); - } else if (state & GDK_CONTROL_MASK) { - /* Ctrl-click sets the vertical rounding to be the same as the horizontal */ - SP_OBJECT_REPR(rect)->setAttribute("ry", SP_OBJECT_REPR(rect)->attribute("rx")); - } + ((SPObject *)rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } -/** - * Called when the vertical rounding radius knot is clicked. - */ -static void sp_rect_ry_knot_click(SPItem *item, guint state) +void +RectKnotHolderEntityRY::knot_click(guint state) { SPRect *rect = SP_RECT(item); if (state & GDK_SHIFT_MASK) { - rect_remove_rounding(rect); + /* remove rounding */ + SP_OBJECT_REPR(rect)->setAttribute("rx", NULL); + SP_OBJECT_REPR(rect)->setAttribute("ry", NULL); } else if (state & GDK_CONTROL_MASK) { /* Ctrl-click sets the vertical rounding to be the same as the horizontal */ SP_OBJECT_REPR(rect)->setAttribute("rx", SP_OBJECT_REPR(rect)->attribute("ry")); @@ -346,59 +245,78 @@ static void sp_rect_clamp_radii(SPRect *rect) } } -static NR::Point sp_rect_wh_get(SPItem *item) +Geom::Point +RectKnotHolderEntityWH::knot_get() { SPRect *rect = SP_RECT(item); - return NR::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed); + return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed); } -static void sp_rect_wh_set_internal(SPRect *rect, NR::Point const &p, NR::Point const &origin, guint state) +void +RectKnotHolderEntityWH::set_internal(Geom::Point const &p, Geom::Point const &origin, guint state) { - NR::Point const s = snap_knot_position(rect, p); + SPRect *rect = SP_RECT(item); + + Geom::Point s = p; if (state & GDK_CONTROL_MASK) { // original width/height when drag started - gdouble const w_orig = (origin[NR::X] - rect->x.computed); - gdouble const h_orig = (origin[NR::Y] - rect->y.computed); + gdouble const w_orig = (origin[Geom::X] - rect->x.computed); + gdouble const h_orig = (origin[Geom::Y] - rect->y.computed); //original ratio - gdouble const ratio = (w_orig / h_orig); + gdouble ratio = (w_orig / h_orig); // mouse displacement since drag started - gdouble const minx = s[NR::X] - origin[NR::X]; - gdouble const miny = s[NR::Y] - origin[NR::Y]; + gdouble minx = p[Geom::X] - origin[Geom::X]; + gdouble miny = p[Geom::Y] - origin[Geom::Y]; - if (fabs(minx) > fabs(miny)) { + Geom::Point p_handle(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed); + if (fabs(minx) > fabs(miny)) { // snap to horizontal or diagonal - rect->width.computed = MAX(w_orig + minx, 0); if (minx != 0 && fabs(miny/minx) > 0.5 * 1/ratio && (SGN(minx) == SGN(miny))) { // closer to the diagonal and in same-sign quarters, change both using ratio + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1))); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; rect->height.computed = MAX(h_orig + minx / ratio, 0); } else { // closer to the horizontal, change only width, height is h_orig + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-1, 0))); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; rect->height.computed = MAX(h_orig, 0); } + rect->width.computed = MAX(w_orig + minx, 0); } else { // snap to vertical or diagonal - rect->height.computed = MAX(h_orig + miny, 0); if (miny != 0 && fabs(minx/miny) > 0.5 * ratio && (SGN(minx) == SGN(miny))) { // closer to the diagonal and in same-sign quarters, change both using ratio + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1))); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; rect->width.computed = MAX(w_orig + miny * ratio, 0); } else { // closer to the vertical, change only height, width is w_orig + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(0, -1))); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; rect->width.computed = MAX(w_orig, 0); } + rect->height.computed = MAX(h_orig + miny, 0); + } rect->width._set = rect->height._set = true; } else { // move freely - rect->width.computed = MAX(s[NR::X] - rect->x.computed, 0); - rect->height.computed = MAX(s[NR::Y] - rect->y.computed, 0); + s = snap_knot_position(p); + rect->width.computed = MAX(s[Geom::X] - rect->x.computed, 0); + rect->height.computed = MAX(s[Geom::Y] - rect->y.computed, 0); rect->width._set = rect->height._set = true; } @@ -407,21 +325,23 @@ static void sp_rect_wh_set_internal(SPRect *rect, NR::Point const &p, NR::Point ((SPObject *)rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } -static void sp_rect_wh_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) +void +RectKnotHolderEntityWH::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) { - SPRect *rect = SP_RECT(item); - - sp_rect_wh_set_internal(rect, p, origin, state); + set_internal(p, origin, state); + update_knot(); } -static NR::Point sp_rect_xy_get(SPItem *item) +Geom::Point +RectKnotHolderEntityXY::knot_get() { SPRect *rect = SP_RECT(item); - return NR::Point(rect->x.computed, rect->y.computed); + return Geom::Point(rect->x.computed, rect->y.computed); } -static void sp_rect_xy_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) +void +RectKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) { SPRect *rect = SP_RECT(item); @@ -430,181 +350,405 @@ static void sp_rect_xy_set(SPItem *item, NR::Point const &p, NR::Point const &or gdouble opposite_y = (rect->y.computed + rect->height.computed); // original width/height when drag started - gdouble w_orig = opposite_x - origin[NR::X]; - gdouble h_orig = opposite_y - origin[NR::Y]; + gdouble w_orig = opposite_x - origin[Geom::X]; + gdouble h_orig = opposite_y - origin[Geom::Y]; - NR::Point const s = snap_knot_position(rect, p); + Geom::Point s = p; + Geom::Point p_handle(rect->x.computed, rect->y.computed); // mouse displacement since drag started - gdouble minx = s[NR::X] - origin[NR::X]; - gdouble miny = s[NR::Y] - origin[NR::Y]; + gdouble minx = p[Geom::X] - origin[Geom::X]; + gdouble miny = p[Geom::Y] - origin[Geom::Y]; if (state & GDK_CONTROL_MASK) { //original ratio gdouble ratio = (w_orig / h_orig); if (fabs(minx) > fabs(miny)) { - // snap to horizontal or diagonal - rect->x.computed = MIN(s[NR::X], opposite_x); - rect->width.computed = MAX(w_orig - minx, 0); if (minx != 0 && fabs(miny/minx) > 0.5 * 1/ratio && (SGN(minx) == SGN(miny))) { // closer to the diagonal and in same-sign quarters, change both using ratio - rect->y.computed = MIN(origin[NR::Y] + minx / ratio, opposite_y); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1))); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->y.computed = MIN(origin[Geom::Y] + minx / ratio, opposite_y); rect->height.computed = MAX(h_orig - minx / ratio, 0); } else { // closer to the horizontal, change only width, height is h_orig - rect->y.computed = MIN(origin[NR::Y], opposite_y); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-1, 0))); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->y.computed = MIN(origin[Geom::Y], opposite_y); rect->height.computed = MAX(h_orig, 0); } - + rect->x.computed = MIN(s[Geom::X], opposite_x); + rect->width.computed = MAX(w_orig - minx, 0); } else { - // snap to vertical or diagonal - rect->y.computed = MIN(s[NR::Y], opposite_y); - rect->height.computed = MAX(h_orig - miny, 0); if (miny != 0 && fabs(minx/miny) > 0.5 *ratio && (SGN(minx) == SGN(miny))) { // closer to the diagonal and in same-sign quarters, change both using ratio - rect->x.computed = MIN(origin[NR::X] + miny * ratio, opposite_x); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1))); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->x.computed = MIN(origin[Geom::X] + miny * ratio, opposite_x); rect->width.computed = MAX(w_orig - miny * ratio, 0); } else { // closer to the vertical, change only height, width is w_orig - rect->x.computed = MIN(origin[NR::X], opposite_x); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(0, -1))); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->x.computed = MIN(origin[Geom::X], opposite_x); rect->width.computed = MAX(w_orig, 0); } - + rect->y.computed = MIN(s[Geom::Y], opposite_y); + rect->height.computed = MAX(h_orig - miny, 0); } rect->width._set = rect->height._set = rect->x._set = rect->y._set = true; } else { // move freely - rect->x.computed = MIN(s[NR::X], opposite_x); + s = snap_knot_position(p); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + + rect->x.computed = MIN(s[Geom::X], opposite_x); rect->width.computed = MAX(w_orig - minx, 0); - rect->y.computed = MIN(s[NR::Y], opposite_y); + rect->y.computed = MIN(s[Geom::Y], opposite_y); rect->height.computed = MAX(h_orig - miny, 0); rect->width._set = rect->height._set = rect->x._set = rect->y._set = true; } sp_rect_clamp_radii(rect); + update_knot(); + ((SPObject *)rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } -static SPKnotHolder *sp_rect_knot_holder(SPItem *item, SPDesktop *desktop) +RectKnotHolder::RectKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) { - SPKnotHolder *knot_holder = sp_knot_holder_new(desktop, item, NULL); + RectKnotHolderEntityRX *entity_rx = new RectKnotHolderEntityRX(); + RectKnotHolderEntityRY *entity_ry = new RectKnotHolderEntityRY(); + RectKnotHolderEntityWH *entity_wh = new RectKnotHolderEntityWH(); + RectKnotHolderEntityXY *entity_xy = new RectKnotHolderEntityXY(); + entity_rx->create(desktop, item, this, + _("Adjust the horizontal rounding radius; with Ctrl " + "to make the vertical radius the same"), + SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR); + entity_ry->create(desktop, item, this, + _("Adjust the vertical rounding radius; with Ctrl " + "to make the horizontal radius the same"), + SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR); + entity_wh->create(desktop, item, this, + _("Adjust the width and height of the rectangle; with Ctrl " + "to lock ratio or stretch in one dimension only"), + SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR); + entity_xy->create(desktop, item, this, + _("Adjust the width and height of the rectangle; with Ctrl " + "to lock ratio or stretch in one dimension only"), + SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR); + entity.push_back(entity_rx); + entity.push_back(entity_ry); + entity.push_back(entity_wh); + entity.push_back(entity_xy); - sp_knot_holder_add_full( - knot_holder, sp_rect_rx_set, sp_rect_rx_get, sp_rect_rx_knot_click, - SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR, - _("Adjust the horizontal rounding radius; with Ctrl to make the vertical " - "radius the same")); + add_pattern_knotholder(); +} - sp_knot_holder_add_full( - knot_holder, sp_rect_ry_set, sp_rect_ry_get, sp_rect_ry_knot_click, - SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR, - _("Adjust the vertical rounding radius; with Ctrl to make the horizontal " - "radius the same") - ); +/* Box3D (= the new 3D box structure) */ - sp_knot_holder_add_full( - knot_holder, sp_rect_wh_set, sp_rect_wh_get, NULL, - SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR, - _("Adjust the width and height of the rectangle; with Ctrl to lock ratio " - "or stretch in one dimension only") - ); +class Box3DKnotHolderEntity : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() = 0; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) = 0; - sp_knot_holder_add_full( - knot_holder, sp_rect_xy_set, sp_rect_xy_get, NULL, - SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR, - _("Adjust the width and height of the rectangle; with Ctrl to lock ratio " - "or stretch in one dimension only") - ); + Geom::Point knot_get_generic(SPItem *item, unsigned int knot_id); + void knot_set_generic(SPItem *item, unsigned int knot_id, Geom::Point const &p, guint state); +}; - sp_pat_knot_holder(item, knot_holder); - return knot_holder; +Geom::Point +Box3DKnotHolderEntity::knot_get_generic(SPItem *item, unsigned int knot_id) +{ + return box3d_get_corner_screen(SP_BOX3D(item), knot_id); } -/* 3D Box */ - -static void sp_3dbox_knot_set(SPItem *item, guint knot_id, Box3D::Axis direction, - NR::Point const &new_pos, NR::Point const &origin, guint state) +void +Box3DKnotHolderEntity::knot_set_generic(SPItem *item, unsigned int knot_id, Geom::Point const &new_pos, guint state) { - g_assert(item != NULL); - SP3DBox *box = SP_3DBOX(item); + Geom::Point const s = snap_knot_position(new_pos); - // FIXME: Why must the coordinates be flipped vertically??? - SPDocument *doc = SP_OBJECT_DOCUMENT(box); - gdouble height = sp_document_height(doc); + g_assert(item != NULL); + SPBox3D *box = SP_BOX3D(item); + Geom::Matrix const i2d (item->i2d_affine ()); - if (direction == Box3D::Z) { - sp_3dbox_move_corner_in_constrained_Z_direction (box, knot_id, - NR::Point (new_pos[NR::X], height - new_pos[NR::Y])); + Box3D::Axis movement; + if ((knot_id < 4) != (state & GDK_SHIFT_MASK)) { + movement = Box3D::XY; } else { - sp_3dbox_move_corner_in_XY_plane (box, knot_id, NR::Point (new_pos[NR::X], height - new_pos[NR::Y])); + movement = Box3D::Z; } - sp_3dbox_update_curves (box); + + box3d_set_corner (box, knot_id, s * i2d, movement, (state & GDK_CONTROL_MASK)); + box3d_set_z_orders(box); + box3d_position_set(box); } -static NR::Point sp_3dbox_knot_get(SPItem *item, guint knot_id) +class Box3DKnotHolderEntity0 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +}; + +class Box3DKnotHolderEntity1 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +}; + +class Box3DKnotHolderEntity2 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +}; + +class Box3DKnotHolderEntity3 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +}; + +class Box3DKnotHolderEntity4 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +}; + +class Box3DKnotHolderEntity5 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +}; + +class Box3DKnotHolderEntity6 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +}; + +class Box3DKnotHolderEntity7 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +}; + +class Box3DKnotHolderEntityCenter : public KnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +}; + +Geom::Point +Box3DKnotHolderEntity0::knot_get() { - g_assert(item != NULL); - SP3DBox *box = SP_3DBOX(item); + return knot_get_generic(item, 0); +} - // FIXME: Why must the coordinates be flipped vertically??? - SPDocument *doc = SP_OBJECT_DOCUMENT(box); - gdouble height = sp_document_height(doc); +Geom::Point +Box3DKnotHolderEntity1::knot_get() +{ + return knot_get_generic(item, 1); +} - return NR::Point(sp_3dbox_get_corner(box, knot_id)[NR::X], height - sp_3dbox_get_corner(box, knot_id)[NR::Y]); +Geom::Point +Box3DKnotHolderEntity2::knot_get() +{ + return knot_get_generic(item, 2); } -static void sp_3dbox_knot1_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state) +Geom::Point +Box3DKnotHolderEntity3::knot_get() { - sp_3dbox_knot_set (item, 1, Box3D::XY, new_pos, origin, state); + return knot_get_generic(item, 3); } -static NR::Point sp_3dbox_knot1_get(SPItem *item) +Geom::Point +Box3DKnotHolderEntity4::knot_get() { - return sp_3dbox_knot_get(item, 1); + return knot_get_generic(item, 4); } -static void sp_3dbox_knot2_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state) +Geom::Point +Box3DKnotHolderEntity5::knot_get() { - sp_3dbox_knot_set (item, 2, Box3D::XY, new_pos, origin, state); + return knot_get_generic(item, 5); } -static NR::Point sp_3dbox_knot2_get(SPItem *item) +Geom::Point +Box3DKnotHolderEntity6::knot_get() { - return sp_3dbox_knot_get(item, 2); + return knot_get_generic(item, 6); } -static void sp_3dbox_knot5_set(SPItem *item, NR::Point const &new_pos, NR::Point const &origin, guint state) +Geom::Point +Box3DKnotHolderEntity7::knot_get() { - sp_3dbox_knot_set (item, 5, Box3D::Z, new_pos, origin, state); + return knot_get_generic(item, 7); } -static NR::Point sp_3dbox_knot5_get(SPItem *item) +Geom::Point +Box3DKnotHolderEntityCenter::knot_get() { - return sp_3dbox_knot_get(item, 5); + return box3d_get_center_screen(SP_BOX3D(item)); } -static SPKnotHolder * -sp_3dbox_knot_holder(SPItem *item, SPDesktop *desktop) +void +Box3DKnotHolderEntity0::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state) { - g_assert(item != NULL); - SPKnotHolder *knot_holder = sp_knot_holder_new(desktop, item, NULL); + knot_set_generic(item, 0, new_pos, state); +} - sp_knot_holder_add(knot_holder, sp_3dbox_knot1_set, sp_3dbox_knot1_get, NULL,_("Resize box in X/Y direction")); - sp_knot_holder_add(knot_holder, sp_3dbox_knot2_set, sp_3dbox_knot2_get, NULL,_("Resize box in X/Y direction")); - sp_knot_holder_add(knot_holder, sp_3dbox_knot5_set, sp_3dbox_knot5_get, NULL,_("Resize box in Z direction")); - sp_pat_knot_holder(item, knot_holder); +void +Box3DKnotHolderEntity1::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state) +{ + knot_set_generic(item, 1, new_pos, state); +} - return knot_holder; +void +Box3DKnotHolderEntity2::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state) +{ + knot_set_generic(item, 2, new_pos, state); +} + +void +Box3DKnotHolderEntity3::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state) +{ + knot_set_generic(item, 3, new_pos, state); +} + +void +Box3DKnotHolderEntity4::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state) +{ + knot_set_generic(item, 4, new_pos, state); +} + +void +Box3DKnotHolderEntity5::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state) +{ + knot_set_generic(item, 5, new_pos, state); +} + +void +Box3DKnotHolderEntity6::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state) +{ + knot_set_generic(item, 6, new_pos, state); +} + +void +Box3DKnotHolderEntity7::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state) +{ + knot_set_generic(item, 7, new_pos, state); +} + +void +Box3DKnotHolderEntityCenter::knot_set(Geom::Point const &new_pos, Geom::Point const &origin, guint state) +{ + Geom::Point const s = snap_knot_position(new_pos); + + SPBox3D *box = SP_BOX3D(item); + Geom::Matrix const i2d (item->i2d_affine ()); + + box3d_set_center (SP_BOX3D(item), s * i2d, origin * i2d, !(state & GDK_SHIFT_MASK) ? Box3D::XY : Box3D::Z, + state & GDK_CONTROL_MASK); + + box3d_set_z_orders(box); + box3d_position_set(box); +} + +Box3DKnotHolder::Box3DKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) +{ + Box3DKnotHolderEntity0 *entity_corner0 = new Box3DKnotHolderEntity0(); + Box3DKnotHolderEntity1 *entity_corner1 = new Box3DKnotHolderEntity1(); + Box3DKnotHolderEntity2 *entity_corner2 = new Box3DKnotHolderEntity2(); + Box3DKnotHolderEntity3 *entity_corner3 = new Box3DKnotHolderEntity3(); + Box3DKnotHolderEntity4 *entity_corner4 = new Box3DKnotHolderEntity4(); + Box3DKnotHolderEntity5 *entity_corner5 = new Box3DKnotHolderEntity5(); + Box3DKnotHolderEntity6 *entity_corner6 = new Box3DKnotHolderEntity6(); + Box3DKnotHolderEntity7 *entity_corner7 = new Box3DKnotHolderEntity7(); + Box3DKnotHolderEntityCenter *entity_center = new Box3DKnotHolderEntityCenter(); + + entity_corner0->create(desktop, item, this, + _("Resize box in X/Y direction; with Shift along the Z axis; " + "with Ctrl to constrain to the directions of edges or diagonals")); + entity_corner1->create(desktop, item, this, + _("Resize box in X/Y direction; with Shift along the Z axis; " + "with Ctrl to constrain to the directions of edges or diagonals")); + entity_corner2->create(desktop, item, this, + _("Resize box in X/Y direction; with Shift along the Z axis; " + "with Ctrl to constrain to the directions of edges or diagonals")); + entity_corner3->create(desktop, item, this, + _("Resize box in X/Y direction; with Shift along the Z axis; " + "with Ctrl to constrain to the directions of edges or diagonals")); + entity_corner4->create(desktop, item, this, + _("Resize box along the Z axis; with Shift in X/Y direction; " + "with Ctrl to constrain to the directions of edges or diagonals")); + entity_corner5->create(desktop, item, this, + _("Resize box along the Z axis; with Shift in X/Y direction; " + "with Ctrl to constrain to the directions of edges or diagonals")); + entity_corner6->create(desktop, item, this, + _("Resize box along the Z axis; with Shift in X/Y direction; " + "with Ctrl to constrain to the directions of edges or diagonals")); + entity_corner7->create(desktop, item, this, + _("Resize box along the Z axis; with Shift in X/Y direction; " + "with Ctrl to constrain to the directions of edges or diagonals")); + entity_center->create(desktop, item, this, + _("Move the box in perspective"), + SP_KNOT_SHAPE_CROSS); + + entity.push_back(entity_corner0); + entity.push_back(entity_corner1); + entity.push_back(entity_corner2); + entity.push_back(entity_corner3); + entity.push_back(entity_corner4); + entity.push_back(entity_corner5); + entity.push_back(entity_corner6); + entity.push_back(entity_corner7); + entity.push_back(entity_center); + + add_pattern_knotholder(); } /* SPArc */ +class ArcKnotHolderEntityStart : public KnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +}; + +class ArcKnotHolderEntityEnd : public KnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); + virtual void knot_click(guint state); +}; + +class ArcKnotHolderEntityRX : public KnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); + virtual void knot_click(guint state); +}; + +class ArcKnotHolderEntityRY : public KnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); + virtual void knot_click(guint state); +}; + /* * return values: * 1 : inside @@ -612,10 +756,10 @@ sp_3dbox_knot_holder(SPItem *item, SPDesktop *desktop) * -1 : outside */ static gint -sp_genericellipse_side(SPGenericEllipse *ellipse, NR::Point const &p) +sp_genericellipse_side(SPGenericEllipse *ellipse, Geom::Point const &p) { - gdouble dx = (p[NR::X] - ellipse->cx.computed) / ellipse->rx.computed; - gdouble dy = (p[NR::Y] - ellipse->cy.computed) / ellipse->ry.computed; + gdouble dx = (p[Geom::X] - ellipse->cx.computed) / ellipse->rx.computed; + gdouble dy = (p[Geom::Y] - ellipse->cy.computed) / ellipse->ry.computed; gdouble s = dx * dx + dy * dy; if (s < 1.0) return 1; @@ -623,18 +767,19 @@ sp_genericellipse_side(SPGenericEllipse *ellipse, NR::Point const &p) return 0; } -static void -sp_arc_start_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) +void +ArcKnotHolderEntityStart::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) { - int snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); SPGenericEllipse *ge = SP_GENERICELLIPSE(item); SPArc *arc = SP_ARC(item); ge->closed = (sp_genericellipse_side(ge, p) == -1) ? TRUE : FALSE; - NR::Point delta = p - NR::Point(ge->cx.computed, ge->cy.computed); - NR::scale sc(ge->rx.computed, ge->ry.computed); + Geom::Point delta = p - Geom::Point(ge->cx.computed, ge->cy.computed); + Geom::Scale sc(ge->rx.computed, ge->ry.computed); ge->start = atan2(delta * sc.inverse()); if ( ( state & GDK_CONTROL_MASK ) && snaps ) @@ -645,7 +790,8 @@ sp_arc_start_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guin ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } -static NR::Point sp_arc_start_get(SPItem *item) +Geom::Point +ArcKnotHolderEntityStart::knot_get() { SPGenericEllipse *ge = SP_GENERICELLIPSE(item); SPArc *arc = SP_ARC(item); @@ -653,18 +799,19 @@ static NR::Point sp_arc_start_get(SPItem *item) return sp_arc_get_xy(arc, ge->start); } -static void -sp_arc_end_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) +void +ArcKnotHolderEntityEnd::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) { - int snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); SPGenericEllipse *ge = SP_GENERICELLIPSE(item); SPArc *arc = SP_ARC(item); ge->closed = (sp_genericellipse_side(ge, p) == -1) ? TRUE : FALSE; - NR::Point delta = p - NR::Point(ge->cx.computed, ge->cy.computed); - NR::scale sc(ge->rx.computed, ge->ry.computed); + Geom::Point delta = p - Geom::Point(ge->cx.computed, ge->cy.computed); + Geom::Scale sc(ge->rx.computed, ge->ry.computed); ge->end = atan2(delta * sc.inverse()); if ( ( state & GDK_CONTROL_MASK ) && snaps ) @@ -675,7 +822,8 @@ sp_arc_end_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } -static NR::Point sp_arc_end_get(SPItem *item) +Geom::Point +ArcKnotHolderEntityEnd::knot_get() { SPGenericEllipse *ge = SP_GENERICELLIPSE(item); SPArc *arc = SP_ARC(item); @@ -683,8 +831,9 @@ static NR::Point sp_arc_end_get(SPItem *item) return sp_arc_get_xy(arc, ge->end); } -static void -sp_arc_startend_click(SPItem *item, guint state) + +void +ArcKnotHolderEntityEnd::knot_click(guint state) { SPGenericEllipse *ge = SP_GENERICELLIPSE(item); @@ -695,67 +844,67 @@ sp_arc_startend_click(SPItem *item, guint state) } -static void -sp_arc_rx_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) +void +ArcKnotHolderEntityRX::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) { SPGenericEllipse *ge = SP_GENERICELLIPSE(item); - SPArc *arc = SP_ARC(item); - - NR::Point const s = snap_knot_position(arc, p); - ge->rx.computed = fabs( ge->cx.computed - s[NR::X] ); + Geom::Point const s = snap_knot_position(p); + + ge->rx.computed = fabs( ge->cx.computed - s[Geom::X] ); if ( state & GDK_CONTROL_MASK ) { ge->ry.computed = ge->rx.computed; } - ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + ((SPObject *)item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } -static NR::Point sp_arc_rx_get(SPItem *item) +Geom::Point +ArcKnotHolderEntityRX::knot_get() { SPGenericEllipse *ge = SP_GENERICELLIPSE(item); - return (NR::Point(ge->cx.computed, ge->cy.computed) - NR::Point(ge->rx.computed, 0)); + return (Geom::Point(ge->cx.computed, ge->cy.computed) - Geom::Point(ge->rx.computed, 0)); } -static void -sp_arc_ry_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) +void +ArcKnotHolderEntityRX::knot_click(guint state) { SPGenericEllipse *ge = SP_GENERICELLIPSE(item); - SPArc *arc = SP_ARC(item); - - NR::Point const s = snap_knot_position(arc, p); - ge->ry.computed = fabs( ge->cy.computed - s[NR::Y] ); - - if ( state & GDK_CONTROL_MASK ) { - ge->rx.computed = ge->ry.computed; + if (state & GDK_CONTROL_MASK) { + ge->ry.computed = ge->rx.computed; + ((SPObject *)ge)->updateRepr(); } - - ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } -static NR::Point sp_arc_ry_get(SPItem *item) +void +ArcKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) { SPGenericEllipse *ge = SP_GENERICELLIPSE(item); - return (NR::Point(ge->cx.computed, ge->cy.computed) - NR::Point(0, ge->ry.computed)); + Geom::Point const s = snap_knot_position(p); + + ge->ry.computed = fabs( ge->cy.computed - s[Geom::Y] ); + + if ( state & GDK_CONTROL_MASK ) { + ge->rx.computed = ge->ry.computed; + } + + ((SPObject *)item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } -static void -sp_arc_rx_click(SPItem *item, guint state) +Geom::Point +ArcKnotHolderEntityRY::knot_get() { SPGenericEllipse *ge = SP_GENERICELLIPSE(item); - if (state & GDK_CONTROL_MASK) { - ge->ry.computed = ge->rx.computed; - ((SPObject *)ge)->updateRepr(); - } + return (Geom::Point(ge->cx.computed, ge->cy.computed) - Geom::Point(0, ge->ry.computed)); } -static void -sp_arc_ry_click(SPItem *item, guint state) +void +ArcKnotHolderEntityRY::knot_click(guint state) { SPGenericEllipse *ge = SP_GENERICELLIPSE(item); @@ -765,39 +914,59 @@ sp_arc_ry_click(SPItem *item, guint state) } } -static SPKnotHolder * -sp_arc_knot_holder(SPItem *item, SPDesktop *desktop) -{ - SPKnotHolder *knot_holder = sp_knot_holder_new(desktop, item, NULL); - - sp_knot_holder_add_full(knot_holder, sp_arc_rx_set, sp_arc_rx_get, sp_arc_rx_click, - SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR, - _("Adjust ellipse width, with Ctrl to make circle")); - sp_knot_holder_add_full(knot_holder, sp_arc_ry_set, sp_arc_ry_get, sp_arc_ry_click, - SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR, - _("Adjust ellipse height, with Ctrl to make circle")); - sp_knot_holder_add_full(knot_holder, sp_arc_start_set, sp_arc_start_get, sp_arc_startend_click, - SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR, - _("Position the start point of the arc or segment; with Ctrl to snap angle; drag inside the ellipse for arc, outside for segment")); - sp_knot_holder_add_full(knot_holder, sp_arc_end_set, sp_arc_end_get, sp_arc_startend_click, - SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR, - _("Position the end point of the arc or segment; with Ctrl to snap angle; drag inside the ellipse for arc, outside for segment")); - - sp_pat_knot_holder(item, knot_holder); - - return knot_holder; +ArcKnotHolder::ArcKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) +{ + ArcKnotHolderEntityRX *entity_rx = new ArcKnotHolderEntityRX(); + ArcKnotHolderEntityRY *entity_ry = new ArcKnotHolderEntityRY(); + ArcKnotHolderEntityStart *entity_start = new ArcKnotHolderEntityStart(); + ArcKnotHolderEntityEnd *entity_end = new ArcKnotHolderEntityEnd(); + entity_rx->create(desktop, item, this, + _("Adjust ellipse width, with Ctrl to make circle"), + SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR); + entity_ry->create(desktop, item, this, + _("Adjust ellipse height, with Ctrl to make circle"), + SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR); + entity_start->create(desktop, item, this, + _("Position the start point of the arc or segment; with Ctrl " + "to snap angle; drag inside the ellipse for arc, outside for segment"), + SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR); + entity_end->create(desktop, item, this, + _("Position the end point of the arc or segment; with Ctrl to snap angle; " + "drag inside the ellipse for arc, outside for segment"), + SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR); + entity.push_back(entity_rx); + entity.push_back(entity_ry); + entity.push_back(entity_start); + entity.push_back(entity_end); + + add_pattern_knotholder(); } /* SPStar */ -static void -sp_star_knot1_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) +class StarKnotHolderEntity1 : public KnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); + virtual void knot_click(guint state); +}; + +class StarKnotHolderEntity2 : public KnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); + virtual void knot_click(guint state); +}; + +void +StarKnotHolderEntity1::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) { SPStar *star = SP_STAR(item); - - NR::Point const s = snap_knot_position(star, p); - NR::Point d = s - star->center; + Geom::Point const s = snap_knot_position(p); + + Geom::Point d = s - to_2geom(star->center); double arg1 = atan2(d); double darg1 = arg1 - star->arg[0]; @@ -816,15 +985,15 @@ sp_star_knot1_set(SPItem *item, NR::Point const &p, NR::Point const &origin, gui ((SPObject *)star)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } -static void -sp_star_knot2_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) +void +StarKnotHolderEntity2::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) { SPStar *star = SP_STAR(item); - - NR::Point const s = snap_knot_position(star, p); - + + Geom::Point const s = snap_knot_position(p); + if (star->flatsided == false) { - NR::Point d = s - star->center; + Geom::Point d = s - to_2geom(star->center); double arg1 = atan2(d); double darg1 = arg1 - star->arg[1]; @@ -845,7 +1014,8 @@ sp_star_knot2_set(SPItem *item, NR::Point const &p, NR::Point const &origin, gui } } -static NR::Point sp_star_knot1_get(SPItem *item) +Geom::Point +StarKnotHolderEntity1::knot_get() { g_assert(item != NULL); @@ -855,7 +1025,8 @@ static NR::Point sp_star_knot1_get(SPItem *item) } -static NR::Point sp_star_knot2_get(SPItem *item) +Geom::Point +StarKnotHolderEntity2::knot_get() { g_assert(item != NULL); @@ -881,48 +1052,83 @@ sp_star_knot_click(SPItem *item, guint state) } } -static SPKnotHolder * -sp_star_knot_holder(SPItem *item, SPDesktop *desktop) +void +StarKnotHolderEntity1::knot_click(guint state) { - /* we don't need to get parent knot_holder */ - SPKnotHolder *knot_holder = sp_knot_holder_new(desktop, item, NULL); - g_assert(item != NULL); + return sp_star_knot_click(item, state); +} + +void +StarKnotHolderEntity2::knot_click(guint state) +{ + return sp_star_knot_click(item, state); +} +StarKnotHolder::StarKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) +{ SPStar *star = SP_STAR(item); - sp_knot_holder_add(knot_holder, sp_star_knot1_set, sp_star_knot1_get, sp_star_knot_click, - _("Adjust the tip radius of the star or polygon; with Shift to round; with Alt to randomize")); - if (star->flatsided == false) - sp_knot_holder_add(knot_holder, sp_star_knot2_set, sp_star_knot2_get, sp_star_knot_click, - _("Adjust the base radius of the star; with Ctrl to keep star rays radial (no skew); with Shift to round; with Alt to randomize")); + StarKnotHolderEntity1 *entity1 = new StarKnotHolderEntity1(); + entity1->create(desktop, item, this, + _("Adjust the tip radius of the star or polygon; " + "with Shift to round; with Alt to randomize")); + entity.push_back(entity1); - sp_pat_knot_holder(item, knot_holder); + if (star->flatsided == false) { + StarKnotHolderEntity2 *entity2 = new StarKnotHolderEntity2(); + entity2->create(desktop, item, this, + _("Adjust the base radius of the star; with Ctrl to keep star rays " + "radial (no skew); with Shift to round; with Alt to randomize")); + entity.push_back(entity2); + } - return knot_holder; + add_pattern_knotholder(); } /* SPSpiral */ +class SpiralKnotHolderEntityInner : public KnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); + virtual void knot_click(guint state); +}; + +class SpiralKnotHolderEntityOuter : public KnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +}; + + /* * set attributes via inner (t=t0) knot point: * [default] increase/decrease inner point * [shift] increase/decrease inner and outer arg synchronizely * [control] constrain inner arg to round per PI/4 */ -static void -sp_spiral_inner_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) +void +SpiralKnotHolderEntityInner::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) { - int snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); SPSpiral *spiral = SP_SPIRAL(item); - gdouble dx = p[NR::X] - spiral->cx; - gdouble dy = p[NR::Y] - spiral->cy; + gdouble dx = p[Geom::X] - spiral->cx; + gdouble dy = p[Geom::Y] - spiral->cy; + + gdouble moved_y = p[Geom::Y] - origin[Geom::Y]; if (state & GDK_MOD1_MASK) { // adjust divergence by vertical drag, relative to rad - double new_exp = (spiral->rad + dy)/(spiral->rad); - spiral->exp = new_exp > 0? new_exp : 0; + if (spiral->rad > 0) { + double exp_delta = 0.1*moved_y/(spiral->rad); // arbitrary multiplier to slow it down + spiral->exp += exp_delta; + if (spiral->exp < 1e-3) + spiral->exp = 1e-3; + } } else { // roll/unroll from inside gdouble arg_t0; @@ -951,15 +1157,16 @@ sp_spiral_inner_set(SPItem *item, NR::Point const &p, NR::Point const &origin, g * [default] increase/decrease revolution factor * [control] constrain inner arg to round per PI/4 */ -static void -sp_spiral_outer_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) +void +SpiralKnotHolderEntityOuter::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) { - int snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); SPSpiral *spiral = SP_SPIRAL(item); - gdouble dx = p[NR::X] - spiral->cx; - gdouble dy = p[NR::Y] - spiral->cy; + gdouble dx = p[Geom::X] - spiral->cx; + gdouble dy = p[Geom::Y] - spiral->cy; if (state & GDK_SHIFT_MASK) { // rotate without roll/unroll spiral->arg = atan2(dy, dx) - 2.0*M_PI*spiral->revo; @@ -1017,29 +1224,31 @@ sp_spiral_outer_set(SPItem *item, NR::Point const &p, NR::Point const &origin, g spiral->rad = rad_new; spiral->t0 = pow(r0 / spiral->rad, 1.0/spiral->exp); } - if (!isFinite(spiral->t0)) spiral->t0 = 0.0; + if (!IS_FINITE(spiral->t0)) spiral->t0 = 0.0; spiral->t0 = CLAMP(spiral->t0, 0.0, 0.999); } ((SPObject *)spiral)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } -static NR::Point sp_spiral_inner_get(SPItem *item) +Geom::Point +SpiralKnotHolderEntityInner::knot_get() { SPSpiral *spiral = SP_SPIRAL(item); return sp_spiral_get_xy(spiral, spiral->t0); } -static NR::Point sp_spiral_outer_get(SPItem *item) +Geom::Point +SpiralKnotHolderEntityOuter::knot_get() { SPSpiral *spiral = SP_SPIRAL(item); return sp_spiral_get_xy(spiral, 1.0); } -static void -sp_spiral_inner_click(SPItem *item, guint state) +void +SpiralKnotHolderEntityInner::knot_click(guint state) { SPSpiral *spiral = SP_SPIRAL(item); @@ -1052,25 +1261,33 @@ sp_spiral_inner_click(SPItem *item, guint state) } } -static SPKnotHolder * -sp_spiral_knot_holder(SPItem *item, SPDesktop *desktop) +SpiralKnotHolder::SpiralKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) { - SPKnotHolder *knot_holder = sp_knot_holder_new(desktop, item, NULL); - - sp_knot_holder_add(knot_holder, sp_spiral_inner_set, sp_spiral_inner_get, sp_spiral_inner_click, - _("Roll/unroll the spiral from inside; with Ctrl to snap angle; with Alt to converge/diverge")); - sp_knot_holder_add(knot_holder, sp_spiral_outer_set, sp_spiral_outer_get, NULL, - _("Roll/unroll the spiral from outside; with Ctrl to snap angle; with Shift to scale/rotate")); + SpiralKnotHolderEntityInner *entity_inner = new SpiralKnotHolderEntityInner(); + SpiralKnotHolderEntityOuter *entity_outer = new SpiralKnotHolderEntityOuter(); + entity_inner->create(desktop, item, this, + _("Roll/unroll the spiral from inside; with Ctrl to snap angle; " + "with Alt to converge/diverge")); + entity_outer->create(desktop, item, this, + _("Roll/unroll the spiral from outside; with Ctrl to snap angle; " + "with Shift to scale/rotate")); + entity.push_back(entity_inner); + entity.push_back(entity_outer); - sp_pat_knot_holder(item, knot_holder); - - return knot_holder; + add_pattern_knotholder(); } /* SPOffset */ -static void -sp_offset_offset_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) +class OffsetKnotHolderEntity : public KnotHolderEntity { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +}; + +void +OffsetKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint /*state*/) { SPOffset *offset = SP_OFFSET(item); @@ -1082,86 +1299,61 @@ sp_offset_offset_set(SPItem *item, NR::Point const &p, NR::Point const &origin, } -static NR::Point sp_offset_offset_get(SPItem *item) +Geom::Point +OffsetKnotHolderEntity::knot_get() { SPOffset *offset = SP_OFFSET(item); - NR::Point np; + Geom::Point np; sp_offset_top_point(offset,&np); return np; } -static SPKnotHolder * -sp_offset_knot_holder(SPItem *item, SPDesktop *desktop) -{ - SPKnotHolder *knot_holder = sp_knot_holder_new(desktop, item, NULL); - - sp_knot_holder_add(knot_holder, sp_offset_offset_set, sp_offset_offset_get, NULL, - _("Adjust the offset distance")); - - sp_pat_knot_holder(item, knot_holder); - - return knot_holder; -} - -static SPKnotHolder * -sp_path_knot_holder(SPItem *item, SPDesktop *desktop) // FIXME: eliminate, instead make a pattern-drag similar to gradient-drag +OffsetKnotHolder::OffsetKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) { - if ((SP_OBJECT(item)->style->fill.type == SP_PAINT_TYPE_PAINTSERVER) - && SP_IS_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style))) - { - SPKnotHolder *knot_holder = sp_knot_holder_new(desktop, item, NULL); + OffsetKnotHolderEntity *entity_offset = new OffsetKnotHolderEntity(); + entity_offset->create(desktop, item, this, + _("Adjust the offset distance")); + entity.push_back(entity_offset); - sp_pat_knot_holder(item, knot_holder); - - return knot_holder; - } - return NULL; + add_pattern_knotholder(); } -static void -sp_pat_knot_holder(SPItem *item, SPKnotHolder *knot_holder) -{ - if ((SP_OBJECT(item)->style->fill.type == SP_PAINT_TYPE_PAINTSERVER) - && 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, - // TRANSLATORS: This refers to the pattern that's inside the object - _("Move the pattern fill inside the object")); - sp_knot_holder_add_full(knot_holder, sp_pattern_scale_set, sp_pattern_scale_get, NULL, SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR, - _("Scale the pattern fill uniformly")); - sp_knot_holder_add_full(knot_holder, sp_pattern_angle_set, sp_pattern_angle_get, NULL, SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR, - _("Rotate the pattern fill; with Ctrl to snap angle")); - } -} +// TODO: this is derived from RectKnotHolderEntityWH because it used the same static function +// set_internal as the latter before KnotHolderEntity was C++ified. Check whether this also makes +// sense logically. +class FlowtextKnotHolderEntity : public RectKnotHolderEntityWH { +public: + virtual Geom::Point knot_get(); + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +}; -static NR::Point sp_flowtext_corner_get(SPItem *item) +Geom::Point +FlowtextKnotHolderEntity::knot_get() { SPRect *rect = SP_RECT(item); - return NR::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed); + return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed); } -static void -sp_flowtext_corner_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) +void +FlowtextKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) { - SPRect *rect = SP_RECT(item); - - sp_rect_wh_set_internal(rect, p, origin, state); + set_internal(p, origin, state); } -static SPKnotHolder * -sp_flowtext_knot_holder(SPItem *item, SPDesktop *desktop) +FlowtextKnotHolder::FlowtextKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) { - SPKnotHolder *knot_holder = sp_knot_holder_new(desktop, SP_FLOWTEXT(item)->get_frame(NULL), NULL); - - sp_knot_holder_add(knot_holder, sp_flowtext_corner_set, sp_flowtext_corner_get, NULL, - _("Drag to resize the flowed text frame")); + g_assert(item != NULL); - return knot_holder; + FlowtextKnotHolderEntity *entity_flowtext = new FlowtextKnotHolderEntity(); + entity_flowtext->create(desktop, item, this, + _("Drag to resize the flowed text frame")); + entity.push_back(entity_flowtext); } - /* Local Variables: mode:c++ @@ -1171,4 +1363,4 @@ sp_flowtext_knot_holder(SPItem *item, SPDesktop *desktop) fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :