From d4432a4aaf3fb615d9808f53557a4283436bf035 Mon Sep 17 00:00:00 2001 From: Stefano Facchini Date: Sun, 1 Oct 2017 15:27:03 +0200 Subject: Fix the editing of clipping and masking objects By passing along an edit_transform matrix, as done for clipping paths. Cleanups: - remove some unused methods - use member initialization --- src/knot-holder-entity.cpp | 6 +++--- src/knotholder.cpp | 13 ++++++++++--- src/knotholder.h | 6 +++++- src/ui/shape-editor.cpp | 19 +++++++------------ src/ui/shape-editor.h | 4 ++-- src/ui/tools/node-tool.cpp | 3 ++- 6 files changed, 29 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/knot-holder-entity.cpp b/src/knot-holder-entity.cpp index 8add35d46..bb7ef6eb3 100644 --- a/src/knot-holder-entity.cpp +++ b/src/knot-holder-entity.cpp @@ -84,7 +84,7 @@ KnotHolderEntity::update_knot() { Geom::Point knot_pos(knot_get()); if (knot_pos.isFinite()) { - Geom::Point dp(knot_pos * item->i2dt_affine()); + Geom::Point dp(knot_pos * parent_holder->getEditTranform() * item->i2dt_affine()); _moved_connection.block(); knot->setPosition(dp, SP_KNOT_STATE_NORMAL); @@ -102,7 +102,7 @@ KnotHolderEntity::snap_knot_position(Geom::Point const &p, guint state) return p; } - Geom::Affine const i2dt (item->i2dt_affine()); + Geom::Affine const i2dt (parent_holder->getEditTranform() * item->i2dt_affine()); Geom::Point s = p * i2dt; SnapManager &m = desktop->namedview->snap_manager; @@ -120,7 +120,7 @@ KnotHolderEntity::snap_knot_position_constrained(Geom::Point const &p, Inkscape: return p; } - Geom::Affine const i2d (item->i2dt_affine()); + Geom::Affine const i2d (parent_holder->getEditTranform() * item->i2dt_affine()); Geom::Point s = p * i2d; SnapManager &m = desktop->namedview->snap_manager; diff --git a/src/knotholder.cpp b/src/knotholder.cpp index 29054bcd9..5fbccf52d 100644 --- a/src/knotholder.cpp +++ b/src/knotholder.cpp @@ -56,7 +56,8 @@ KnotHolder::KnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFun sizeUpdatedConn(), released(relhandler), local_change(FALSE), - dragging(false) + dragging(false), + _edit_transform(Geom::identity()) { if (!desktop || !item) { g_print ("Error! Throw an exception, please!\n"); @@ -79,6 +80,12 @@ KnotHolder::~KnotHolder() { sizeUpdatedConn.disconnect(); } +void +KnotHolder::setEditTransform(Geom::Affine edit_transform) +{ + _edit_transform = edit_transform; +} + void KnotHolder::updateControlSizes() { ControlManager &mgr = ControlManager::getManager(); @@ -230,8 +237,8 @@ KnotHolder::knot_moved_handler(SPKnot *knot, Geom::Point const &p, guint state) for(std::list::iterator i = this->entity.begin(); i != this->entity.end(); ++i) { KnotHolderEntity *e = *i; if (e->knot == knot) { - Geom::Point const q = p * item->i2dt_affine().inverse(); - e->knot_set(q, e->knot->drag_origin * item->i2dt_affine().inverse(), state); + Geom::Point const q = p * item->i2dt_affine().inverse() * _edit_transform.inverse(); + e->knot_set(q, e->knot->drag_origin * item->i2dt_affine().inverse() * _edit_transform.inverse(), state); break; } } diff --git a/src/knotholder.h b/src/knotholder.h index d683b53ac..3c535cc31 100644 --- a/src/knotholder.h +++ b/src/knotholder.h @@ -18,6 +18,7 @@ */ #include <2geom/forward.h> +#include <2geom/affine.h> #include #include @@ -58,7 +59,8 @@ public: void add_pattern_knotholder(); - const SPItem *getItem() { return item; } + void setEditTransform(Geom::Affine edit_transform); + Geom::Affine getEditTranform() const { return _edit_transform; } bool knot_mouseover() const; @@ -83,6 +85,8 @@ protected: bool dragging; + Geom::Affine _edit_transform; + private: KnotHolder(); // declared but not defined }; diff --git a/src/ui/shape-editor.cpp b/src/ui/shape-editor.cpp index 6a8f5e931..2c0e662ee 100644 --- a/src/ui/shape-editor.cpp +++ b/src/ui/shape-editor.cpp @@ -31,10 +31,12 @@ namespace UI { bool ShapeEditor::_blockSetItem = false; -ShapeEditor::ShapeEditor(SPDesktop *dt) { - this->desktop = dt; - this->knotholder = NULL; - this->knotholder_listener_attached_for = NULL; +ShapeEditor::ShapeEditor(SPDesktop *dt, Geom::Affine edit_transform) : + desktop(dt), + knotholder(nullptr), + knotholder_listener_attached_for(nullptr), + _edit_transform(edit_transform) +{ } ShapeEditor::~ShapeEditor() { @@ -76,14 +78,6 @@ void ShapeEditor::decrement_local_change() { } } -const SPItem *ShapeEditor::get_item() { - const SPItem *item = NULL; - if (this->has_knotholder()) { - item = this->knotholder->getItem(); - } - return item; -} - void ShapeEditor::event_attr_changed(Inkscape::XML::Node * node, gchar const *name, gchar const *, gchar const *, bool, void *data) { g_assert(data); @@ -129,6 +123,7 @@ void ShapeEditor::set_item(SPItem *item, bool keep_knotholder) { this->knotholder = createKnotHolder(item, desktop); } if (this->knotholder) { + this->knotholder->setEditTransform(_edit_transform); this->knotholder->update_knots(); // setting new listener repr = this->knotholder->repr; diff --git a/src/ui/shape-editor.h b/src/ui/shape-editor.h index 7f435efca..e30b2d60b 100644 --- a/src/ui/shape-editor.h +++ b/src/ui/shape-editor.h @@ -23,7 +23,7 @@ namespace UI { class ShapeEditor { public: - ShapeEditor(SPDesktop *desktop); + ShapeEditor(SPDesktop *desktop, Geom::Affine edit_transform = Geom::identity()); ~ShapeEditor(); void set_item(SPItem *item, bool keep_knotholder = false); @@ -42,11 +42,11 @@ public: char const * /*new_value*/, bool /*is_interactive*/, void *data); private: void reset_item (bool keep_knotholder = true); - const SPItem *get_item(); static bool _blockSetItem; SPDesktop *desktop; Inkscape::XML::Node *knotholder_listener_attached_for; + Geom::Affine _edit_transform; }; } // namespace UI diff --git a/src/ui/tools/node-tool.cpp b/src/ui/tools/node-tool.cpp index d508a16f2..e7825e302 100644 --- a/src/ui/tools/node-tool.cpp +++ b/src/ui/tools/node-tool.cpp @@ -358,6 +358,7 @@ void NodeTool::set(const Inkscape::Preferences::Entry& value) { } /** Recursively collect ShapeRecords */ +static void gather_items(NodeTool *nt, SPItem *base, SPObject *obj, Inkscape::UI::ShapeRole role, std::set &s) { @@ -437,7 +438,7 @@ void NodeTool::selection_changed(Inkscape::Selection *sel) { if ((SP_IS_SHAPE(r.item) || SP_IS_TEXT(r.item) || SP_IS_GROUP(r.item) || SP_IS_OBJECTGROUP(r.item)) && this->_shape_editors.find(r.item) == this->_shape_editors.end()) { - ShapeEditor *si = new ShapeEditor(this->desktop); + ShapeEditor *si = new ShapeEditor(this->desktop, r.edit_transform); si->set_item(r.item); this->_shape_editors.insert(const_cast(r.item), si); } -- cgit v1.2.3 From 1d7b7827cef0755aa92387e48bdde4d2a9c06318 Mon Sep 17 00:00:00 2001 From: Stefano Facchini Date: Sun, 1 Oct 2017 15:47:50 +0200 Subject: Rename object-edit.cpp to what it really is --- src/ui/CMakeLists.txt | 3 +- src/ui/object-edit.cpp | 1618 ---------------------------------- src/ui/object-edit.h | 84 -- src/ui/shape-editor-knotholders.cpp | 1666 +++++++++++++++++++++++++++++++++++ src/ui/shape-editor.cpp | 4 +- 5 files changed, 1669 insertions(+), 1706 deletions(-) delete mode 100644 src/ui/object-edit.cpp delete mode 100644 src/ui/object-edit.h create mode 100644 src/ui/shape-editor-knotholders.cpp (limited to 'src') diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index efdb279b4..be13d9b1b 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -5,10 +5,10 @@ set(ui_SRC dialog-events.cpp draw-anchor.cpp interface.cpp - object-edit.cpp previewholder.cpp selected-color.cpp shape-editor.cpp + shape-editor-knotholders.cpp tool-factory.cpp tools-switch.cpp uxmanager.cpp @@ -185,7 +185,6 @@ set(ui_SRC event-debug.h icon-names.h interface.h - object-edit.h previewable.h previewfillable.h previewholder.h diff --git a/src/ui/object-edit.cpp b/src/ui/object-edit.cpp deleted file mode 100644 index a2b6a2de0..000000000 --- a/src/ui/object-edit.cpp +++ /dev/null @@ -1,1618 +0,0 @@ -/* - * Node editing extension to objects - * - * Authors: - * Lauris Kaplinski - * Mitsuru Oka - * Maximilian Albert - * Abhishek Sharma - * Jon A. Cruz - * - * Licensed under GNU GPL - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "sp-item.h" -#include "sp-rect.h" -#include "box3d.h" -#include "sp-ellipse.h" -#include "sp-star.h" -#include "sp-spiral.h" -#include "sp-offset.h" -#include "sp-flowtext.h" -#include "preferences.h" -#include "style.h" -#include "desktop.h" - -#include "sp-namedview.h" -#include "live_effects/effect.h" -#include "sp-pattern.h" -#include -#include "ui/object-edit.h" -#include "knot-holder-entity.h" - -#define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m))) - -namespace { - -static KnotHolder *sp_lpe_knot_holder(SPLPEItem *item, SPDesktop *desktop) -{ - KnotHolder *knot_holder = new KnotHolder(desktop, item, NULL); - - Inkscape::LivePathEffect::Effect *effect = item->getCurrentLPE(); - effect->addHandles(knot_holder, item); - - return knot_holder; -} - -} // namespace - -namespace Inkscape { -namespace UI { - -KnotHolder *createKnotHolder(SPItem *item, SPDesktop *desktop) -{ - KnotHolder *knotholder = NULL; - - SPLPEItem *lpe = dynamic_cast(item); - if (lpe && - lpe->getCurrentLPE() && - lpe->getCurrentLPE()->isVisible() && - lpe->getCurrentLPE()->providesKnotholder()) { - knotholder = sp_lpe_knot_holder(lpe, desktop); - } else if (dynamic_cast(item)) { - knotholder = new RectKnotHolder(desktop, item, NULL); - } else if (dynamic_cast(item)) { - knotholder = new Box3DKnotHolder(desktop, item, NULL); - } else if (dynamic_cast(item)) { - knotholder = new ArcKnotHolder(desktop, item, NULL); - } else if (dynamic_cast(item)) { - knotholder = new StarKnotHolder(desktop, item, NULL); - } else if (dynamic_cast(item)) { - knotholder = new SpiralKnotHolder(desktop, item, NULL); - } else if (dynamic_cast(item)) { - knotholder = new OffsetKnotHolder(desktop, item, NULL); - } else { - SPFlowtext *flowtext = dynamic_cast(item); - if (flowtext && flowtext->has_internal_frame()) { - knotholder = new FlowtextKnotHolder(desktop, flowtext->get_frame(NULL), NULL); - } else if ((item->style->fill.isPaintserver() && dynamic_cast(item->style->getFillPaintServer())) || - (item->style->stroke.isPaintserver() && dynamic_cast(item->style->getStrokePaintServer()))) { - knotholder = new KnotHolder(desktop, item, NULL); - knotholder->add_pattern_knotholder(); - } - } - - return knotholder; -} - -} -} // namespace Inkscape - -/* SPRect */ - -/* handle for horizontal rounding radius */ -class RectKnotHolderEntityRX : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); - virtual void knot_click(unsigned int state); -}; - -/* handle for vertical rounding radius */ -class RectKnotHolderEntityRY : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); - virtual void knot_click(unsigned int state); -}; - -/* handle for width/height adjustment */ -class RectKnotHolderEntityWH : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); - -protected: - void set_internal(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -/* handle for x/y adjustment */ -class RectKnotHolderEntityXY : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -/* handle for position */ -class RectKnotHolderEntityCenter : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -Geom::Point -RectKnotHolderEntityRX::knot_get() const -{ - SPRect *rect = dynamic_cast(item); - g_assert(rect != NULL); - - return Geom::Point(rect->x.computed + rect->width.computed - rect->rx.computed, rect->y.computed); -} - -void -RectKnotHolderEntityRX::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) -{ - SPRect *rect = dynamic_cast(item); - g_assert(rect != NULL); - - //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)), state); - - if (state & GDK_CONTROL_MASK) { - gdouble temp = MIN(rect->height.computed, rect->width.computed) / 2.0; - rect->rx = rect->ry = CLAMP(rect->x.computed + rect->width.computed - s[Geom::X], 0.0, temp); - } else { - rect->rx = CLAMP(rect->x.computed + rect->width.computed - s[Geom::X], 0.0, rect->width.computed / 2.0); - } - - update_knot(); - - rect->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - -void -RectKnotHolderEntityRX::knot_click(unsigned int state) -{ - SPRect *rect = dynamic_cast(item); - g_assert(rect != NULL); - - if (state & GDK_SHIFT_MASK) { - /* remove rounding from rectangle */ - rect->getRepr()->setAttribute("rx", NULL); - rect->getRepr()->setAttribute("ry", NULL); - } else if (state & GDK_CONTROL_MASK) { - /* Ctrl-click sets the vertical rounding to be the same as the horizontal */ - rect->getRepr()->setAttribute("ry", rect->getRepr()->attribute("rx")); - } - -} - -Geom::Point -RectKnotHolderEntityRY::knot_get() const -{ - SPRect *rect = dynamic_cast(item); - g_assert(rect != NULL); - - return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->ry.computed); -} - -void -RectKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) -{ - SPRect *rect = dynamic_cast(item); - g_assert(rect != NULL); - - //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)), state); - - 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 = rect->ry = CLAMP(s[Geom::Y] - rect->y.computed, 0.0, temp); - } else { - if (!rect->rx._set || rect->rx.computed == 0) { - rect->ry = CLAMP(s[Geom::Y] - rect->y.computed, - 0.0, - MIN(rect->height.computed / 2.0, rect->width.computed / 2.0)); - } else { - rect->ry = CLAMP(s[Geom::Y] - rect->y.computed, - 0.0, - rect->height.computed / 2.0); - } - } - - update_knot(); - - rect->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - -void -RectKnotHolderEntityRY::knot_click(unsigned int state) -{ - SPRect *rect = dynamic_cast(item); - g_assert(rect != NULL); - - if (state & GDK_SHIFT_MASK) { - /* remove rounding */ - rect->getRepr()->setAttribute("rx", NULL); - rect->getRepr()->setAttribute("ry", NULL); - } else if (state & GDK_CONTROL_MASK) { - /* Ctrl-click sets the vertical rounding to be the same as the horizontal */ - rect->getRepr()->setAttribute("rx", rect->getRepr()->attribute("ry")); - } -} - -#define SGN(x) ((x)>0?1:((x)<0?-1:0)) - -static void sp_rect_clamp_radii(SPRect *rect) -{ - // clamp rounding radii so that they do not exceed width/height - if (2 * rect->rx.computed > rect->width.computed) { - rect->rx = 0.5 * rect->width.computed; - } - if (2 * rect->ry.computed > rect->height.computed) { - rect->ry = 0.5 * rect->height.computed; - } -} - -Geom::Point -RectKnotHolderEntityWH::knot_get() const -{ - SPRect *rect = dynamic_cast(item); - g_assert(rect != NULL); - - return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed); -} - -void -RectKnotHolderEntityWH::set_internal(Geom::Point const &p, Geom::Point const &origin, unsigned int state) -{ - SPRect *rect = dynamic_cast(item); - g_assert(rect != NULL); - - Geom::Point s = p; - - if (state & GDK_CONTROL_MASK) { - // original width/height when drag started - gdouble const w_orig = (origin[Geom::X] - rect->x.computed); - gdouble const h_orig = (origin[Geom::Y] - rect->y.computed); - - //original ratio - gdouble ratio = (w_orig / h_orig); - - // mouse displacement since drag started - gdouble minx = p[Geom::X] - origin[Geom::X]; - gdouble miny = p[Geom::Y] - origin[Geom::Y]; - - 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 - 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)), state); - minx = s[Geom::X] - origin[Geom::X]; - // Dead assignment: Value stored to 'miny' is never read - //miny = s[Geom::Y] - origin[Geom::Y]; - rect->height = 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)), state); - minx = s[Geom::X] - origin[Geom::X]; - // Dead assignment: Value stored to 'miny' is never read - //miny = s[Geom::Y] - origin[Geom::Y]; - rect->height = MAX(h_orig, 0); - } - rect->width = MAX(w_orig + minx, 0); - - } else { - // snap to vertical or diagonal - 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)), state); - // Dead assignment: Value stored to 'minx' is never read - //minx = s[Geom::X] - origin[Geom::X]; - miny = s[Geom::Y] - origin[Geom::Y]; - rect->width = 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)), state); - // Dead assignment: Value stored to 'minx' is never read - //minx = s[Geom::X] - origin[Geom::X]; - miny = s[Geom::Y] - origin[Geom::Y]; - rect->width = MAX(w_orig, 0); - } - rect->height = MAX(h_orig + miny, 0); - - } - - } else { - // move freely - s = snap_knot_position(p, state); - rect->width = MAX(s[Geom::X] - rect->x.computed, 0); - rect->height = MAX(s[Geom::Y] - rect->y.computed, 0); - } - - sp_rect_clamp_radii(rect); - - rect->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - -void -RectKnotHolderEntityWH::knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) -{ - set_internal(p, origin, state); - update_knot(); -} - -Geom::Point -RectKnotHolderEntityXY::knot_get() const -{ - SPRect *rect = dynamic_cast(item); - g_assert(rect != NULL); - - return Geom::Point(rect->x.computed, rect->y.computed); -} - -void -RectKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) -{ - SPRect *rect = dynamic_cast(item); - g_assert(rect != NULL); - - // opposite corner (unmoved) - gdouble opposite_x = (rect->x.computed + rect->width.computed); - gdouble opposite_y = (rect->y.computed + rect->height.computed); - - // original width/height when drag started - gdouble w_orig = opposite_x - origin[Geom::X]; - gdouble h_orig = opposite_y - origin[Geom::Y]; - - Geom::Point s = p; - Geom::Point p_handle(rect->x.computed, rect->y.computed); - - // mouse displacement since drag started - 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 - 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)), state); - minx = s[Geom::X] - origin[Geom::X]; - // Dead assignment: Value stored to 'miny' is never read - //miny = s[Geom::Y] - origin[Geom::Y]; - rect->y = MIN(origin[Geom::Y] + minx / ratio, opposite_y); - rect->height = 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)), state); - minx = s[Geom::X] - origin[Geom::X]; - // Dead assignment: Value stored to 'miny' is never read - //miny = s[Geom::Y] - origin[Geom::Y]; - rect->y = MIN(origin[Geom::Y], opposite_y); - rect->height = MAX(h_orig, 0); - } - rect->x = MIN(s[Geom::X], opposite_x); - rect->width = MAX(w_orig - minx, 0); - } else { - // snap to vertical or diagonal - 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)), state); - // Dead assignment: Value stored to 'minx' is never read - //minx = s[Geom::X] - origin[Geom::X]; - miny = s[Geom::Y] - origin[Geom::Y]; - rect->x = MIN(origin[Geom::X] + miny * ratio, opposite_x); - rect->width = 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)), state); - // Dead assignment: Value stored to 'minx' is never read - //minx = s[Geom::X] - origin[Geom::X]; - miny = s[Geom::Y] - origin[Geom::Y]; - rect->x = MIN(origin[Geom::X], opposite_x); - rect->width = MAX(w_orig, 0); - } - rect->y = MIN(s[Geom::Y], opposite_y); - rect->height = MAX(h_orig - miny, 0); - } - - } else { - // move freely - s = snap_knot_position(p, state); - minx = s[Geom::X] - origin[Geom::X]; - miny = s[Geom::Y] - origin[Geom::Y]; - - rect->x = MIN(s[Geom::X], opposite_x); - rect->y = MIN(s[Geom::Y], opposite_y); - rect->width = MAX(w_orig - minx, 0); - rect->height = MAX(h_orig - miny, 0); - } - - sp_rect_clamp_radii(rect); - - update_knot(); - - rect->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - -Geom::Point -RectKnotHolderEntityCenter::knot_get() const -{ - SPRect *rect = dynamic_cast(item); - g_assert(rect != NULL); - - return Geom::Point(rect->x.computed + (rect->width.computed / 2.), rect->y.computed + (rect->height.computed / 2.)); -} - -void -RectKnotHolderEntityCenter::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) -{ - SPRect *rect = dynamic_cast(item); - g_assert(rect != NULL); - - Geom::Point const s = snap_knot_position(p, state); - - rect->x = s[Geom::X] - (rect->width.computed / 2.); - rect->y = s[Geom::Y] - (rect->height.computed / 2.); - - // No need to call sp_rect_clamp_radii(): width and height haven't changed. - // No need to call update_knot(): the knot is set directly by the user. - - rect->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - -RectKnotHolder::RectKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : - KnotHolder(desktop, item, relhandler) -{ - RectKnotHolderEntityRX *entity_rx = new RectKnotHolderEntityRX(); - RectKnotHolderEntityRY *entity_ry = new RectKnotHolderEntityRY(); - RectKnotHolderEntityWH *entity_wh = new RectKnotHolderEntityWH(); - RectKnotHolderEntityXY *entity_xy = new RectKnotHolderEntityXY(); - RectKnotHolderEntityCenter *entity_center = new RectKnotHolderEntityCenter(); - - entity_rx->create(desktop, item, this, Inkscape::CTRL_TYPE_ROTATE, - _("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, Inkscape::CTRL_TYPE_ROTATE, - _("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, Inkscape::CTRL_TYPE_SIZER, - _("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, Inkscape::CTRL_TYPE_SIZER, - _("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_center->create(desktop, item, this, Inkscape::CTRL_TYPE_POINT, - _("Drag to move the rectangle"), - SP_KNOT_SHAPE_CROSS); - - entity.push_back(entity_rx); - entity.push_back(entity_ry); - entity.push_back(entity_wh); - entity.push_back(entity_xy); - entity.push_back(entity_center); - - add_pattern_knotholder(); -} - -/* Box3D (= the new 3D box structure) */ - -class Box3DKnotHolderEntity : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const = 0; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) = 0; - - Geom::Point knot_get_generic(SPItem *item, unsigned int knot_id) const; - void knot_set_generic(SPItem *item, unsigned int knot_id, Geom::Point const &p, unsigned int state); -}; - -Geom::Point -Box3DKnotHolderEntity::knot_get_generic(SPItem *item, unsigned int knot_id) const -{ - SPBox3D *box = dynamic_cast(item); - if (box) { - return box3d_get_corner_screen(box, knot_id); - } else { - return Geom::Point(); // TODO investigate proper fallback - } -} - -void -Box3DKnotHolderEntity::knot_set_generic(SPItem *item, unsigned int knot_id, Geom::Point const &new_pos, unsigned int state) -{ - Geom::Point const s = snap_knot_position(new_pos, state); - - g_assert(item != NULL); - SPBox3D *box = dynamic_cast(item); - g_assert(box != NULL); - Geom::Affine const i2dt (item->i2dt_affine ()); - - Box3D::Axis movement; - if ((knot_id < 4) != (state & GDK_SHIFT_MASK)) { - movement = Box3D::XY; - } else { - movement = Box3D::Z; - } - - box3d_set_corner (box, knot_id, s * i2dt, movement, (state & GDK_CONTROL_MASK)); - box3d_set_z_orders(box); - box3d_position_set(box); -} - -class Box3DKnotHolderEntity0 : public Box3DKnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -class Box3DKnotHolderEntity1 : public Box3DKnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -class Box3DKnotHolderEntity2 : public Box3DKnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -class Box3DKnotHolderEntity3 : public Box3DKnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -class Box3DKnotHolderEntity4 : public Box3DKnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -class Box3DKnotHolderEntity5 : public Box3DKnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -class Box3DKnotHolderEntity6 : public Box3DKnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -class Box3DKnotHolderEntity7 : public Box3DKnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -class Box3DKnotHolderEntityCenter : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -Geom::Point -Box3DKnotHolderEntity0::knot_get() const -{ - return knot_get_generic(item, 0); -} - -Geom::Point -Box3DKnotHolderEntity1::knot_get() const -{ - return knot_get_generic(item, 1); -} - -Geom::Point -Box3DKnotHolderEntity2::knot_get() const -{ - return knot_get_generic(item, 2); -} - -Geom::Point -Box3DKnotHolderEntity3::knot_get() const -{ - return knot_get_generic(item, 3); -} - -Geom::Point -Box3DKnotHolderEntity4::knot_get() const -{ - return knot_get_generic(item, 4); -} - -Geom::Point -Box3DKnotHolderEntity5::knot_get() const -{ - return knot_get_generic(item, 5); -} - -Geom::Point -Box3DKnotHolderEntity6::knot_get() const -{ - return knot_get_generic(item, 6); -} - -Geom::Point -Box3DKnotHolderEntity7::knot_get() const -{ - return knot_get_generic(item, 7); -} - -Geom::Point -Box3DKnotHolderEntityCenter::knot_get() const -{ - SPBox3D *box = dynamic_cast(item); - if (box) { - return box3d_get_center_screen(box); - } else { - return Geom::Point(); // TODO investigate proper fallback - } -} - -void -Box3DKnotHolderEntity0::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) -{ - knot_set_generic(item, 0, new_pos, state); -} - -void -Box3DKnotHolderEntity1::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) -{ - knot_set_generic(item, 1, new_pos, state); -} - -void -Box3DKnotHolderEntity2::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) -{ - knot_set_generic(item, 2, new_pos, state); -} - -void -Box3DKnotHolderEntity3::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) -{ - knot_set_generic(item, 3, new_pos, state); -} - -void -Box3DKnotHolderEntity4::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) -{ - knot_set_generic(item, 4, new_pos, state); -} - -void -Box3DKnotHolderEntity5::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) -{ - knot_set_generic(item, 5, new_pos, state); -} - -void -Box3DKnotHolderEntity6::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) -{ - knot_set_generic(item, 6, new_pos, state); -} - -void -Box3DKnotHolderEntity7::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) -{ - knot_set_generic(item, 7, new_pos, state); -} - -void -Box3DKnotHolderEntityCenter::knot_set(Geom::Point const &new_pos, Geom::Point const &origin, unsigned int state) -{ - Geom::Point const s = snap_knot_position(new_pos, state); - - SPBox3D *box = dynamic_cast(item); - g_assert(box != NULL); - Geom::Affine const i2dt (item->i2dt_affine ()); - - box3d_set_center(box, s * i2dt, origin * i2dt, !(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, Inkscape::CTRL_TYPE_SHAPER, - _("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, Inkscape::CTRL_TYPE_SHAPER, - _("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, Inkscape::CTRL_TYPE_SHAPER, - _("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, Inkscape::CTRL_TYPE_SHAPER, - _("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, Inkscape::CTRL_TYPE_SHAPER, - _("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, Inkscape::CTRL_TYPE_SHAPER, - _("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, Inkscape::CTRL_TYPE_SHAPER, - _("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, Inkscape::CTRL_TYPE_SHAPER, - _("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, Inkscape::CTRL_TYPE_POINT, - _("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() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); - virtual void knot_click(unsigned int state); -}; - -class ArcKnotHolderEntityEnd : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); - virtual void knot_click(unsigned int state); -}; - -class ArcKnotHolderEntityRX : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); - virtual void knot_click(unsigned int state); -}; - -class ArcKnotHolderEntityRY : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); - virtual void knot_click(unsigned int state); -}; - -class ArcKnotHolderEntityCenter : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -/* - * return values: - * 1 : inside - * 0 : on the curves - * -1 : outside - */ -static gint -sp_genericellipse_side(SPGenericEllipse *ellipse, Geom::Point const &p) -{ - 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; - // We add a bit of a buffer, so there's a decent chance the user will - // be able to adjust the arc without the closed status flipping between - // open and closed during micro mouse movements. - if (s < 0.75) return 1; - if (s > 1.25) return -1; - return 0; -} - -void -ArcKnotHolderEntityStart::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) -{ - int snaps = Inkscape::Preferences::get()->getInt("/options/rotationsnapsperpi/value", 12); - - SPGenericEllipse *arc = dynamic_cast(item); - g_assert(arc != NULL); - - gint side = sp_genericellipse_side(arc, p); - if(side != 0) { arc->setArcType( (side == -1) ? - SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE : - SP_GENERIC_ELLIPSE_ARC_TYPE_ARC); } - - Geom::Point delta = p - Geom::Point(arc->cx.computed, arc->cy.computed); - Geom::Scale sc(arc->rx.computed, arc->ry.computed); - - double offset = arc->start - atan2(delta * sc.inverse()); - arc->start -= offset; - - if ((state & GDK_CONTROL_MASK) && snaps) { - arc->start = sp_round(arc->start, M_PI / snaps); - } - if (state & GDK_SHIFT_MASK) { - arc->end -= offset; - } - - arc->normalize(); - arc->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - -Geom::Point -ArcKnotHolderEntityStart::knot_get() const -{ - SPGenericEllipse const *ge = dynamic_cast(item); - g_assert(ge != NULL); - - return ge->getPointAtAngle(ge->start); -} - -void -ArcKnotHolderEntityStart::knot_click(unsigned int state) -{ - SPGenericEllipse *ge = dynamic_cast(item); - g_assert(ge != NULL); - - if (state & GDK_SHIFT_MASK) { - ge->end = ge->start = 0; - ge->updateRepr(); - } -} - -void -ArcKnotHolderEntityEnd::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) -{ - int snaps = Inkscape::Preferences::get()->getInt("/options/rotationsnapsperpi/value", 12); - - SPGenericEllipse *arc = dynamic_cast(item); - g_assert(arc != NULL); - - gint side = sp_genericellipse_side(arc, p); - if(side != 0) { arc->setArcType( (side == -1) ? - SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE : - SP_GENERIC_ELLIPSE_ARC_TYPE_ARC); } - - Geom::Point delta = p - Geom::Point(arc->cx.computed, arc->cy.computed); - Geom::Scale sc(arc->rx.computed, arc->ry.computed); - - double offset = arc->end - atan2(delta * sc.inverse()); - arc->end -= offset; - - if ((state & GDK_CONTROL_MASK) && snaps) { - arc->end = sp_round(arc->end, M_PI/snaps); - } - if (state & GDK_SHIFT_MASK) { - arc->start -= offset; - } - - arc->normalize(); - arc->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - -Geom::Point -ArcKnotHolderEntityEnd::knot_get() const -{ - SPGenericEllipse const *ge = dynamic_cast(item); - g_assert(ge != NULL); - - return ge->getPointAtAngle(ge->end); -} - - -void -ArcKnotHolderEntityEnd::knot_click(unsigned int state) -{ - SPGenericEllipse *ge = dynamic_cast(item); - g_assert(ge != NULL); - - if (state & GDK_SHIFT_MASK) { - ge->end = ge->start = 0; - ge->updateRepr(); - } -} - - -void -ArcKnotHolderEntityRX::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) -{ - SPGenericEllipse *ge = dynamic_cast(item); - g_assert(ge != NULL); - - Geom::Point const s = snap_knot_position(p, state); - - ge->rx = fabs( ge->cx.computed - s[Geom::X] ); - - if ( state & GDK_CONTROL_MASK ) { - ge->ry = ge->rx.computed; - } - - item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - -Geom::Point -ArcKnotHolderEntityRX::knot_get() const -{ - SPGenericEllipse const *ge = dynamic_cast(item); - g_assert(ge != NULL); - - return (Geom::Point(ge->cx.computed, ge->cy.computed) - Geom::Point(ge->rx.computed, 0)); -} - -void -ArcKnotHolderEntityRX::knot_click(unsigned int state) -{ - SPGenericEllipse *ge = dynamic_cast(item); - g_assert(ge != NULL); - - if (state & GDK_CONTROL_MASK) { - ge->ry = ge->rx.computed; - ge->updateRepr(); - } -} - -void -ArcKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) -{ - SPGenericEllipse *ge = dynamic_cast(item); - g_assert(ge != NULL); - - Geom::Point const s = snap_knot_position(p, state); - - ge->ry = fabs( ge->cy.computed - s[Geom::Y] ); - - if ( state & GDK_CONTROL_MASK ) { - ge->rx = ge->ry.computed; - } - - item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - -Geom::Point -ArcKnotHolderEntityRY::knot_get() const -{ - SPGenericEllipse const *ge = dynamic_cast(item); - g_assert(ge != NULL); - - return (Geom::Point(ge->cx.computed, ge->cy.computed) - Geom::Point(0, ge->ry.computed)); -} - -void -ArcKnotHolderEntityRY::knot_click(unsigned int state) -{ - SPGenericEllipse *ge = dynamic_cast(item); - g_assert(ge != NULL); - - if (state & GDK_CONTROL_MASK) { - ge->rx = ge->ry.computed; - ge->updateRepr(); - } -} - -void -ArcKnotHolderEntityCenter::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) -{ - SPGenericEllipse *ge = dynamic_cast(item); - g_assert(ge != NULL); - - Geom::Point const s = snap_knot_position(p, state); - - ge->cx = s[Geom::X]; - ge->cy = s[Geom::Y]; - - item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - -Geom::Point -ArcKnotHolderEntityCenter::knot_get() const -{ - SPGenericEllipse const *ge = dynamic_cast(item); - g_assert(ge != NULL); - - return Geom::Point(ge->cx.computed, ge->cy.computed); -} - - -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(); - ArcKnotHolderEntityCenter *entity_center = new ArcKnotHolderEntityCenter(); - - entity_rx->create(desktop, item, this, Inkscape::CTRL_TYPE_SIZER, - _("Adjust ellipse width, with Ctrl to make circle"), - SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR); - - entity_ry->create(desktop, item, this, Inkscape::CTRL_TYPE_SIZER, - _("Adjust ellipse height, with Ctrl to make circle"), - SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR); - - entity_start->create(desktop, item, this, Inkscape::CTRL_TYPE_ROTATE, - _("Position the start point of the arc or segment; with Shift to move " - "with end point; 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, Inkscape::CTRL_TYPE_ROTATE, - _("Position the end point of the arc or segment; with Shift to move " - "with start point; with Ctrl to snap angle; drag inside the " - "ellipse for arc, outside for segment"), - SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR); - - entity_center->create(desktop, item, this, Inkscape::CTRL_TYPE_POINT, - _("Drag to move the ellipse"), - SP_KNOT_SHAPE_CROSS); - - entity.push_back(entity_rx); - entity.push_back(entity_ry); - entity.push_back(entity_start); - entity.push_back(entity_end); - entity.push_back(entity_center); - - add_pattern_knotholder(); -} - -/* SPStar */ - -class StarKnotHolderEntity1 : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); - virtual void knot_click(unsigned int state); -}; - -class StarKnotHolderEntity2 : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); - virtual void knot_click(unsigned int state); -}; - -class StarKnotHolderEntityCenter : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -void -StarKnotHolderEntity1::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) -{ - SPStar *star = dynamic_cast(item); - g_assert(star != NULL); - - Geom::Point const s = snap_knot_position(p, state); - - Geom::Point d = s - star->center; - - double arg1 = atan2(d); - double darg1 = arg1 - star->arg[0]; - - if (state & GDK_MOD1_MASK) { - star->randomized = darg1/(star->arg[0] - star->arg[1]); - } else if (state & GDK_SHIFT_MASK) { - star->rounded = darg1/(star->arg[0] - star->arg[1]); - } else if (state & GDK_CONTROL_MASK) { - star->r[0] = L2(d); - } else { - star->r[0] = L2(d); - star->arg[0] = arg1; - star->arg[1] += darg1; - } - star->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - -void -StarKnotHolderEntity2::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) -{ - SPStar *star = dynamic_cast(item); - g_assert(star != NULL); - - Geom::Point const s = snap_knot_position(p, state); - - if (star->flatsided == false) { - Geom::Point d = s - star->center; - - double arg1 = atan2(d); - double darg1 = arg1 - star->arg[1]; - - if (state & GDK_MOD1_MASK) { - star->randomized = darg1/(star->arg[0] - star->arg[1]); - } else if (state & GDK_SHIFT_MASK) { - star->rounded = fabs(darg1/(star->arg[0] - star->arg[1])); - } else if (state & GDK_CONTROL_MASK) { - star->r[1] = L2(d); - star->arg[1] = star->arg[0] + M_PI / star->sides; - } - else { - star->r[1] = L2(d); - star->arg[1] = atan2(d); - } - star->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - } -} - -void -StarKnotHolderEntityCenter::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) -{ - SPStar *star = dynamic_cast(item); - g_assert(star != NULL); - - star->center = snap_knot_position(p, state); - - item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - -Geom::Point -StarKnotHolderEntity1::knot_get() const -{ - g_assert(item != NULL); - - SPStar const *star = dynamic_cast(item); - g_assert(star != NULL); - - return sp_star_get_xy(star, SP_STAR_POINT_KNOT1, 0); - -} - -Geom::Point -StarKnotHolderEntity2::knot_get() const -{ - g_assert(item != NULL); - - SPStar const *star = dynamic_cast(item); - g_assert(star != NULL); - - return sp_star_get_xy(star, SP_STAR_POINT_KNOT2, 0); -} - -Geom::Point -StarKnotHolderEntityCenter::knot_get() const -{ - g_assert(item != NULL); - - SPStar const *star = dynamic_cast(item); - g_assert(star != NULL); - - return star->center; -} - -static void -sp_star_knot_click(SPItem *item, unsigned int state) -{ - SPStar *star = dynamic_cast(item); - g_assert(star != NULL); - - if (state & GDK_MOD1_MASK) { - star->randomized = 0; - star->updateRepr(); - } else if (state & GDK_SHIFT_MASK) { - star->rounded = 0; - star->updateRepr(); - } else if (state & GDK_CONTROL_MASK) { - star->arg[1] = star->arg[0] + M_PI / star->sides; - star->updateRepr(); - } -} - -void -StarKnotHolderEntity1::knot_click(unsigned int state) -{ - sp_star_knot_click(item, state); -} - -void -StarKnotHolderEntity2::knot_click(unsigned int state) -{ - sp_star_knot_click(item, state); -} - -StarKnotHolder::StarKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : - KnotHolder(desktop, item, relhandler) -{ - SPStar *star = dynamic_cast(item); - g_assert(item != NULL); - - StarKnotHolderEntity1 *entity1 = new StarKnotHolderEntity1(); - entity1->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, - _("Adjust the tip radius of the star or polygon; " - "with Shift to round; with Alt to randomize")); - - entity.push_back(entity1); - - if (star->flatsided == false) { - StarKnotHolderEntity2 *entity2 = new StarKnotHolderEntity2(); - entity2->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, - _("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); - } - - StarKnotHolderEntityCenter *entity_center = new StarKnotHolderEntityCenter(); - entity_center->create(desktop, item, this, Inkscape::CTRL_TYPE_POINT, - _("Drag to move the star"), - SP_KNOT_SHAPE_CROSS); - entity.push_back(entity_center); - - add_pattern_knotholder(); -} - -/* SPSpiral */ - -class SpiralKnotHolderEntityInner : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); - virtual void knot_click(unsigned int state); -}; - -class SpiralKnotHolderEntityOuter : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -class SpiralKnotHolderEntityCenter : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int 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 - */ -void -SpiralKnotHolderEntityInner::knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) -{ - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); - - SPSpiral *spiral = dynamic_cast(item); - g_assert(spiral != NULL); - - 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 - 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; - spiral->getPolar(spiral->t0, NULL, &arg_t0); - - gdouble arg_tmp = atan2(dy, dx) - arg_t0; - gdouble arg_t0_new = arg_tmp - floor((arg_tmp+M_PI)/(2.0*M_PI))*2.0*M_PI + arg_t0; - spiral->t0 = (arg_t0_new - spiral->arg) / (2.0*M_PI*spiral->revo); - - /* round inner arg per PI/snaps, if CTRL is pressed */ - if ( ( state & GDK_CONTROL_MASK ) - && ( fabs(spiral->revo) > SP_EPSILON_2 ) - && ( snaps != 0 ) ) { - gdouble arg = 2.0*M_PI*spiral->revo*spiral->t0 + spiral->arg; - spiral->t0 = (sp_round(arg, M_PI/snaps) - spiral->arg)/(2.0*M_PI*spiral->revo); - } - - spiral->t0 = CLAMP(spiral->t0, 0.0, 0.999); - } - - spiral->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - -/* - * set attributes via outer (t=1) knot point: - * [default] increase/decrease revolution factor - * [control] constrain inner arg to round per PI/4 - */ -void -SpiralKnotHolderEntityOuter::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) -{ - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); - - SPSpiral *spiral = dynamic_cast(item); - g_assert(spiral != NULL); - - 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; - if (!(state & GDK_MOD1_MASK)) { - // if alt not pressed, change also rad; otherwise it is locked - spiral->rad = MAX(hypot(dx, dy), 0.001); - } - if ( ( state & GDK_CONTROL_MASK ) - && snaps ) { - spiral->arg = sp_round(spiral->arg, M_PI/snaps); - } - } else { // roll/unroll - // arg of the spiral outer end - double arg_1; - spiral->getPolar(1, NULL, &arg_1); - - // its fractional part after the whole turns are subtracted - double arg_r = arg_1 - sp_round(arg_1, 2.0*M_PI); - - // arg of the mouse point relative to spiral center - double mouse_angle = atan2(dy, dx); - if (mouse_angle < 0) - mouse_angle += 2*M_PI; - - // snap if ctrl - if ( ( state & GDK_CONTROL_MASK ) && snaps ) { - mouse_angle = sp_round(mouse_angle, M_PI/snaps); - } - - // by how much we want to rotate the outer point - double diff = mouse_angle - arg_r; - if (diff > M_PI) - diff -= 2*M_PI; - else if (diff < -M_PI) - diff += 2*M_PI; - - // calculate the new rad; - // the value of t corresponding to the angle arg_1 + diff: - double t_temp = ((arg_1 + diff) - spiral->arg)/(2*M_PI*spiral->revo); - // the rad at that t: - double rad_new = 0; - if (t_temp > spiral->t0) - spiral->getPolar(t_temp, &rad_new, NULL); - - // change the revo (converting diff from radians to the number of turns) - spiral->revo += diff/(2*M_PI); - if (spiral->revo < 1e-3) - spiral->revo = 1e-3; - - // if alt not pressed and the values are sane, change the rad - if (!(state & GDK_MOD1_MASK) && rad_new > 1e-3 && rad_new/spiral->rad < 2) { - // adjust t0 too so that the inner point stays unmoved - double r0; - spiral->getPolar(spiral->t0, &r0, NULL); - spiral->rad = rad_new; - spiral->t0 = pow(r0 / spiral->rad, 1.0/spiral->exp); - } - if (!IS_FINITE(spiral->t0)) spiral->t0 = 0.0; - spiral->t0 = CLAMP(spiral->t0, 0.0, 0.999); - } - - spiral->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - -void -SpiralKnotHolderEntityCenter::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) -{ - SPSpiral *spiral = dynamic_cast(item); - g_assert(spiral != NULL); - - Geom::Point const s = snap_knot_position(p, state); - - spiral->cx = s[Geom::X]; - spiral->cy = s[Geom::Y]; - - spiral->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - -Geom::Point -SpiralKnotHolderEntityInner::knot_get() const -{ - SPSpiral const *spiral = dynamic_cast(item); - g_assert(spiral != NULL); - - return spiral->getXY(spiral->t0); -} - -Geom::Point -SpiralKnotHolderEntityOuter::knot_get() const -{ - SPSpiral const *spiral = dynamic_cast(item); - g_assert(spiral != NULL); - - return spiral->getXY(1.0); -} - -Geom::Point -SpiralKnotHolderEntityCenter::knot_get() const -{ - SPSpiral const *spiral = dynamic_cast(item); - g_assert(spiral != NULL); - - return Geom::Point(spiral->cx, spiral->cy); -} - -void -SpiralKnotHolderEntityInner::knot_click(unsigned int state) -{ - SPSpiral *spiral = dynamic_cast(item); - g_assert(spiral != NULL); - - if (state & GDK_MOD1_MASK) { - spiral->exp = 1; - spiral->updateRepr(); - } else if (state & GDK_SHIFT_MASK) { - spiral->t0 = 0; - spiral->updateRepr(); - } -} - -SpiralKnotHolder::SpiralKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : - KnotHolder(desktop, item, relhandler) -{ - SpiralKnotHolderEntityCenter *entity_center = new SpiralKnotHolderEntityCenter(); - SpiralKnotHolderEntityInner *entity_inner = new SpiralKnotHolderEntityInner(); - SpiralKnotHolderEntityOuter *entity_outer = new SpiralKnotHolderEntityOuter(); - - // NOTE: entity_central and entity_inner can overlap. - // - // In that case it would be a problem if the center control point was ON - // TOP because it would steal the mouse focus and the user would loose the - // ability to access the inner control point using only the mouse. - // - // However if the inner control point is ON TOP, taking focus, the - // situation is a lot better: the user can still move the inner control - // point with the mouse to regain access to the center control point. - // - // So, create entity_inner AFTER entity_center; this ensures that - // entity_inner gets rendered ON TOP. - entity_center->create(desktop, item, this, Inkscape::CTRL_TYPE_POINT, - _("Drag to move the spiral"), - SP_KNOT_SHAPE_CROSS); - - entity_inner->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, - _("Roll/unroll the spiral from inside; with Ctrl to snap angle; " - "with Alt to converge/diverge")); - - entity_outer->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, - _("Roll/unroll the spiral from outside; with Ctrl to snap angle; " - "with Shift to scale/rotate; with Alt to lock radius")); - - entity.push_back(entity_center); - entity.push_back(entity_inner); - entity.push_back(entity_outer); - - add_pattern_knotholder(); -} - -/* SPOffset */ - -class OffsetKnotHolderEntity : public KnotHolderEntity { -public: - virtual Geom::Point knot_get() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -void -OffsetKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) -{ - SPOffset *offset = dynamic_cast(item); - g_assert(offset != NULL); - - Geom::Point const p_snapped = snap_knot_position(p, state); - - offset->rad = sp_offset_distance_to_original(offset, p_snapped); - offset->knot = p_snapped; - offset->knotSet = true; - - offset->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} - - -Geom::Point -OffsetKnotHolderEntity::knot_get() const -{ - SPOffset const *offset = dynamic_cast(item); - g_assert(offset != NULL); - - Geom::Point np; - sp_offset_top_point(offset,&np); - return np; -} - -OffsetKnotHolder::OffsetKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : - KnotHolder(desktop, item, relhandler) -{ - OffsetKnotHolderEntity *entity_offset = new OffsetKnotHolderEntity(); - entity_offset->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, - _("Adjust the offset distance")); - entity.push_back(entity_offset); - - add_pattern_knotholder(); -} - -// 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() const; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); -}; - -Geom::Point -FlowtextKnotHolderEntity::knot_get() const -{ - SPRect const *rect = dynamic_cast(item); - g_assert(rect != NULL); - - return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed); -} - -void -FlowtextKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) -{ - set_internal(p, origin, state); -} - -FlowtextKnotHolder::FlowtextKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : - KnotHolder(desktop, item, relhandler) -{ - g_assert(item != NULL); - - FlowtextKnotHolderEntity *entity_flowtext = new FlowtextKnotHolderEntity(); - entity_flowtext->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, - _("Drag to resize the flowed text frame")); - entity.push_back(entity_flowtext); -} - -/* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: -*/ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/object-edit.h b/src/ui/object-edit.h deleted file mode 100644 index 75f3ce12b..000000000 --- a/src/ui/object-edit.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef OBJECT_EDIT_H_SEEN -#define OBJECT_EDIT_H_SEEN - -/* - * Node editing extension to objects - * - * Authors: - * Lauris Kaplinski - * Mitsuru Oka - * Jon A. Cruz - * - * Licensed under GNU GPL - */ - -#include "knotholder.h" - -namespace Inkscape { -namespace UI { - -KnotHolder *createKnotHolder(SPItem *item, SPDesktop *desktop); - -} -} - -class RectKnotHolder : public KnotHolder { -public: - RectKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); - virtual ~RectKnotHolder() {}; -}; - -class Box3DKnotHolder : public KnotHolder { -public: - Box3DKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); - virtual ~Box3DKnotHolder() {}; -}; - -class ArcKnotHolder : public KnotHolder { -public: - ArcKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); - virtual ~ArcKnotHolder() {}; -}; - -class StarKnotHolder : public KnotHolder { -public: - StarKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); - virtual ~StarKnotHolder() {}; -}; - -class SpiralKnotHolder : public KnotHolder { -public: - SpiralKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); - virtual ~SpiralKnotHolder() {}; -}; - -class OffsetKnotHolder : public KnotHolder { -public: - OffsetKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); - virtual ~OffsetKnotHolder() {}; -}; - -class FlowtextKnotHolder : public KnotHolder { -public: - FlowtextKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); - virtual ~FlowtextKnotHolder() {}; -}; - -class MiscKnotHolder : public KnotHolder { -public: - MiscKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); - virtual ~MiscKnotHolder() {}; -}; - -#endif // OBJECT_EDIT_H_SEEN - -/* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: -*/ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/shape-editor-knotholders.cpp b/src/ui/shape-editor-knotholders.cpp new file mode 100644 index 000000000..b2e41bac4 --- /dev/null +++ b/src/ui/shape-editor-knotholders.cpp @@ -0,0 +1,1666 @@ +/* + * Node editing extension to objects + * + * Authors: + * Lauris Kaplinski + * Mitsuru Oka + * Maximilian Albert + * Abhishek Sharma + * Jon A. Cruz + * + * Licensed under GNU GPL + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "sp-item.h" +#include "sp-rect.h" +#include "box3d.h" +#include "sp-ellipse.h" +#include "sp-star.h" +#include "sp-spiral.h" +#include "sp-offset.h" +#include "sp-flowtext.h" +#include "preferences.h" +#include "style.h" +#include "desktop.h" + +#include "sp-namedview.h" +#include "live_effects/effect.h" +#include "sp-pattern.h" +#include +#include "knotholder.h" +#include "knot-holder-entity.h" + +#define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m))) + +class RectKnotHolder : public KnotHolder { +public: + RectKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); + virtual ~RectKnotHolder() {}; +}; + +class Box3DKnotHolder : public KnotHolder { +public: + Box3DKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); + virtual ~Box3DKnotHolder() {}; +}; + +class ArcKnotHolder : public KnotHolder { +public: + ArcKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); + virtual ~ArcKnotHolder() {}; +}; + +class StarKnotHolder : public KnotHolder { +public: + StarKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); + virtual ~StarKnotHolder() {}; +}; + +class SpiralKnotHolder : public KnotHolder { +public: + SpiralKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); + virtual ~SpiralKnotHolder() {}; +}; + +class OffsetKnotHolder : public KnotHolder { +public: + OffsetKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); + virtual ~OffsetKnotHolder() {}; +}; + +class FlowtextKnotHolder : public KnotHolder { +public: + FlowtextKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); + virtual ~FlowtextKnotHolder() {}; +}; + +class MiscKnotHolder : public KnotHolder { +public: + MiscKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler); + virtual ~MiscKnotHolder() {}; +}; + +namespace { + +static KnotHolder *sp_lpe_knot_holder(SPLPEItem *item, SPDesktop *desktop) +{ + KnotHolder *knot_holder = new KnotHolder(desktop, item, NULL); + + Inkscape::LivePathEffect::Effect *effect = item->getCurrentLPE(); + effect->addHandles(knot_holder, item); + + return knot_holder; +} + +} // namespace + +namespace Inkscape { +namespace UI { + +KnotHolder *createKnotHolder(SPItem *item, SPDesktop *desktop) +{ + KnotHolder *knotholder = NULL; + + SPLPEItem *lpe = dynamic_cast(item); + if (lpe && + lpe->getCurrentLPE() && + lpe->getCurrentLPE()->isVisible() && + lpe->getCurrentLPE()->providesKnotholder()) { + knotholder = sp_lpe_knot_holder(lpe, desktop); + } else if (dynamic_cast(item)) { + knotholder = new RectKnotHolder(desktop, item, NULL); + } else if (dynamic_cast(item)) { + knotholder = new Box3DKnotHolder(desktop, item, NULL); + } else if (dynamic_cast(item)) { + knotholder = new ArcKnotHolder(desktop, item, NULL); + } else if (dynamic_cast(item)) { + knotholder = new StarKnotHolder(desktop, item, NULL); + } else if (dynamic_cast(item)) { + knotholder = new SpiralKnotHolder(desktop, item, NULL); + } else if (dynamic_cast(item)) { + knotholder = new OffsetKnotHolder(desktop, item, NULL); + } else { + SPFlowtext *flowtext = dynamic_cast(item); + if (flowtext && flowtext->has_internal_frame()) { + knotholder = new FlowtextKnotHolder(desktop, flowtext->get_frame(NULL), NULL); + } else if ((item->style->fill.isPaintserver() && dynamic_cast(item->style->getFillPaintServer())) || + (item->style->stroke.isPaintserver() && dynamic_cast(item->style->getStrokePaintServer()))) { + knotholder = new KnotHolder(desktop, item, NULL); + knotholder->add_pattern_knotholder(); + } + } + + return knotholder; +} + +} +} // namespace Inkscape + +/* SPRect */ + +/* handle for horizontal rounding radius */ +class RectKnotHolderEntityRX : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +/* handle for vertical rounding radius */ +class RectKnotHolderEntityRY : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +/* handle for width/height adjustment */ +class RectKnotHolderEntityWH : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + +protected: + void set_internal(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +/* handle for x/y adjustment */ +class RectKnotHolderEntityXY : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +/* handle for position */ +class RectKnotHolderEntityCenter : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +Geom::Point +RectKnotHolderEntityRX::knot_get() const +{ + SPRect *rect = dynamic_cast(item); + g_assert(rect != NULL); + + return Geom::Point(rect->x.computed + rect->width.computed - rect->rx.computed, rect->y.computed); +} + +void +RectKnotHolderEntityRX::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPRect *rect = dynamic_cast(item); + g_assert(rect != NULL); + + //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)), state); + + if (state & GDK_CONTROL_MASK) { + gdouble temp = MIN(rect->height.computed, rect->width.computed) / 2.0; + rect->rx = rect->ry = CLAMP(rect->x.computed + rect->width.computed - s[Geom::X], 0.0, temp); + } else { + rect->rx = CLAMP(rect->x.computed + rect->width.computed - s[Geom::X], 0.0, rect->width.computed / 2.0); + } + + update_knot(); + + rect->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void +RectKnotHolderEntityRX::knot_click(unsigned int state) +{ + SPRect *rect = dynamic_cast(item); + g_assert(rect != NULL); + + if (state & GDK_SHIFT_MASK) { + /* remove rounding from rectangle */ + rect->getRepr()->setAttribute("rx", NULL); + rect->getRepr()->setAttribute("ry", NULL); + } else if (state & GDK_CONTROL_MASK) { + /* Ctrl-click sets the vertical rounding to be the same as the horizontal */ + rect->getRepr()->setAttribute("ry", rect->getRepr()->attribute("rx")); + } + +} + +Geom::Point +RectKnotHolderEntityRY::knot_get() const +{ + SPRect *rect = dynamic_cast(item); + g_assert(rect != NULL); + + return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->ry.computed); +} + +void +RectKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPRect *rect = dynamic_cast(item); + g_assert(rect != NULL); + + //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)), state); + + 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 = rect->ry = CLAMP(s[Geom::Y] - rect->y.computed, 0.0, temp); + } else { + if (!rect->rx._set || rect->rx.computed == 0) { + rect->ry = CLAMP(s[Geom::Y] - rect->y.computed, + 0.0, + MIN(rect->height.computed / 2.0, rect->width.computed / 2.0)); + } else { + rect->ry = CLAMP(s[Geom::Y] - rect->y.computed, + 0.0, + rect->height.computed / 2.0); + } + } + + update_knot(); + + rect->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void +RectKnotHolderEntityRY::knot_click(unsigned int state) +{ + SPRect *rect = dynamic_cast(item); + g_assert(rect != NULL); + + if (state & GDK_SHIFT_MASK) { + /* remove rounding */ + rect->getRepr()->setAttribute("rx", NULL); + rect->getRepr()->setAttribute("ry", NULL); + } else if (state & GDK_CONTROL_MASK) { + /* Ctrl-click sets the vertical rounding to be the same as the horizontal */ + rect->getRepr()->setAttribute("rx", rect->getRepr()->attribute("ry")); + } +} + +#define SGN(x) ((x)>0?1:((x)<0?-1:0)) + +static void sp_rect_clamp_radii(SPRect *rect) +{ + // clamp rounding radii so that they do not exceed width/height + if (2 * rect->rx.computed > rect->width.computed) { + rect->rx = 0.5 * rect->width.computed; + } + if (2 * rect->ry.computed > rect->height.computed) { + rect->ry = 0.5 * rect->height.computed; + } +} + +Geom::Point +RectKnotHolderEntityWH::knot_get() const +{ + SPRect *rect = dynamic_cast(item); + g_assert(rect != NULL); + + return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed); +} + +void +RectKnotHolderEntityWH::set_internal(Geom::Point const &p, Geom::Point const &origin, unsigned int state) +{ + SPRect *rect = dynamic_cast(item); + g_assert(rect != NULL); + + Geom::Point s = p; + + if (state & GDK_CONTROL_MASK) { + // original width/height when drag started + gdouble const w_orig = (origin[Geom::X] - rect->x.computed); + gdouble const h_orig = (origin[Geom::Y] - rect->y.computed); + + //original ratio + gdouble ratio = (w_orig / h_orig); + + // mouse displacement since drag started + gdouble minx = p[Geom::X] - origin[Geom::X]; + gdouble miny = p[Geom::Y] - origin[Geom::Y]; + + 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 + 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)), state); + minx = s[Geom::X] - origin[Geom::X]; + // Dead assignment: Value stored to 'miny' is never read + //miny = s[Geom::Y] - origin[Geom::Y]; + rect->height = 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)), state); + minx = s[Geom::X] - origin[Geom::X]; + // Dead assignment: Value stored to 'miny' is never read + //miny = s[Geom::Y] - origin[Geom::Y]; + rect->height = MAX(h_orig, 0); + } + rect->width = MAX(w_orig + minx, 0); + + } else { + // snap to vertical or diagonal + 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)), state); + // Dead assignment: Value stored to 'minx' is never read + //minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->width = 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)), state); + // Dead assignment: Value stored to 'minx' is never read + //minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->width = MAX(w_orig, 0); + } + rect->height = MAX(h_orig + miny, 0); + + } + + } else { + // move freely + s = snap_knot_position(p, state); + rect->width = MAX(s[Geom::X] - rect->x.computed, 0); + rect->height = MAX(s[Geom::Y] - rect->y.computed, 0); + } + + sp_rect_clamp_radii(rect); + + rect->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void +RectKnotHolderEntityWH::knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) +{ + set_internal(p, origin, state); + update_knot(); +} + +Geom::Point +RectKnotHolderEntityXY::knot_get() const +{ + SPRect *rect = dynamic_cast(item); + g_assert(rect != NULL); + + return Geom::Point(rect->x.computed, rect->y.computed); +} + +void +RectKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) +{ + SPRect *rect = dynamic_cast(item); + g_assert(rect != NULL); + + // opposite corner (unmoved) + gdouble opposite_x = (rect->x.computed + rect->width.computed); + gdouble opposite_y = (rect->y.computed + rect->height.computed); + + // original width/height when drag started + gdouble w_orig = opposite_x - origin[Geom::X]; + gdouble h_orig = opposite_y - origin[Geom::Y]; + + Geom::Point s = p; + Geom::Point p_handle(rect->x.computed, rect->y.computed); + + // mouse displacement since drag started + 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 + 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)), state); + minx = s[Geom::X] - origin[Geom::X]; + // Dead assignment: Value stored to 'miny' is never read + //miny = s[Geom::Y] - origin[Geom::Y]; + rect->y = MIN(origin[Geom::Y] + minx / ratio, opposite_y); + rect->height = 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)), state); + minx = s[Geom::X] - origin[Geom::X]; + // Dead assignment: Value stored to 'miny' is never read + //miny = s[Geom::Y] - origin[Geom::Y]; + rect->y = MIN(origin[Geom::Y], opposite_y); + rect->height = MAX(h_orig, 0); + } + rect->x = MIN(s[Geom::X], opposite_x); + rect->width = MAX(w_orig - minx, 0); + } else { + // snap to vertical or diagonal + 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)), state); + // Dead assignment: Value stored to 'minx' is never read + //minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->x = MIN(origin[Geom::X] + miny * ratio, opposite_x); + rect->width = 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)), state); + // Dead assignment: Value stored to 'minx' is never read + //minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->x = MIN(origin[Geom::X], opposite_x); + rect->width = MAX(w_orig, 0); + } + rect->y = MIN(s[Geom::Y], opposite_y); + rect->height = MAX(h_orig - miny, 0); + } + + } else { + // move freely + s = snap_knot_position(p, state); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + + rect->x = MIN(s[Geom::X], opposite_x); + rect->y = MIN(s[Geom::Y], opposite_y); + rect->width = MAX(w_orig - minx, 0); + rect->height = MAX(h_orig - miny, 0); + } + + sp_rect_clamp_radii(rect); + + update_knot(); + + rect->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +Geom::Point +RectKnotHolderEntityCenter::knot_get() const +{ + SPRect *rect = dynamic_cast(item); + g_assert(rect != NULL); + + return Geom::Point(rect->x.computed + (rect->width.computed / 2.), rect->y.computed + (rect->height.computed / 2.)); +} + +void +RectKnotHolderEntityCenter::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPRect *rect = dynamic_cast(item); + g_assert(rect != NULL); + + Geom::Point const s = snap_knot_position(p, state); + + rect->x = s[Geom::X] - (rect->width.computed / 2.); + rect->y = s[Geom::Y] - (rect->height.computed / 2.); + + // No need to call sp_rect_clamp_radii(): width and height haven't changed. + // No need to call update_knot(): the knot is set directly by the user. + + rect->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +RectKnotHolder::RectKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) +{ + RectKnotHolderEntityRX *entity_rx = new RectKnotHolderEntityRX(); + RectKnotHolderEntityRY *entity_ry = new RectKnotHolderEntityRY(); + RectKnotHolderEntityWH *entity_wh = new RectKnotHolderEntityWH(); + RectKnotHolderEntityXY *entity_xy = new RectKnotHolderEntityXY(); + RectKnotHolderEntityCenter *entity_center = new RectKnotHolderEntityCenter(); + + entity_rx->create(desktop, item, this, Inkscape::CTRL_TYPE_ROTATE, + _("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, Inkscape::CTRL_TYPE_ROTATE, + _("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, Inkscape::CTRL_TYPE_SIZER, + _("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, Inkscape::CTRL_TYPE_SIZER, + _("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_center->create(desktop, item, this, Inkscape::CTRL_TYPE_POINT, + _("Drag to move the rectangle"), + SP_KNOT_SHAPE_CROSS); + + entity.push_back(entity_rx); + entity.push_back(entity_ry); + entity.push_back(entity_wh); + entity.push_back(entity_xy); + entity.push_back(entity_center); + + add_pattern_knotholder(); +} + +/* Box3D (= the new 3D box structure) */ + +class Box3DKnotHolderEntity : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const = 0; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) = 0; + + Geom::Point knot_get_generic(SPItem *item, unsigned int knot_id) const; + void knot_set_generic(SPItem *item, unsigned int knot_id, Geom::Point const &p, unsigned int state); +}; + +Geom::Point +Box3DKnotHolderEntity::knot_get_generic(SPItem *item, unsigned int knot_id) const +{ + SPBox3D *box = dynamic_cast(item); + if (box) { + return box3d_get_corner_screen(box, knot_id); + } else { + return Geom::Point(); // TODO investigate proper fallback + } +} + +void +Box3DKnotHolderEntity::knot_set_generic(SPItem *item, unsigned int knot_id, Geom::Point const &new_pos, unsigned int state) +{ + Geom::Point const s = snap_knot_position(new_pos, state); + + g_assert(item != NULL); + SPBox3D *box = dynamic_cast(item); + g_assert(box != NULL); + Geom::Affine const i2dt (item->i2dt_affine ()); + + Box3D::Axis movement; + if ((knot_id < 4) != (state & GDK_SHIFT_MASK)) { + movement = Box3D::XY; + } else { + movement = Box3D::Z; + } + + box3d_set_corner (box, knot_id, s * i2dt, movement, (state & GDK_CONTROL_MASK)); + box3d_set_z_orders(box); + box3d_position_set(box); +} + +class Box3DKnotHolderEntity0 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class Box3DKnotHolderEntity1 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class Box3DKnotHolderEntity2 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class Box3DKnotHolderEntity3 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class Box3DKnotHolderEntity4 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class Box3DKnotHolderEntity5 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class Box3DKnotHolderEntity6 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class Box3DKnotHolderEntity7 : public Box3DKnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class Box3DKnotHolderEntityCenter : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +Geom::Point +Box3DKnotHolderEntity0::knot_get() const +{ + return knot_get_generic(item, 0); +} + +Geom::Point +Box3DKnotHolderEntity1::knot_get() const +{ + return knot_get_generic(item, 1); +} + +Geom::Point +Box3DKnotHolderEntity2::knot_get() const +{ + return knot_get_generic(item, 2); +} + +Geom::Point +Box3DKnotHolderEntity3::knot_get() const +{ + return knot_get_generic(item, 3); +} + +Geom::Point +Box3DKnotHolderEntity4::knot_get() const +{ + return knot_get_generic(item, 4); +} + +Geom::Point +Box3DKnotHolderEntity5::knot_get() const +{ + return knot_get_generic(item, 5); +} + +Geom::Point +Box3DKnotHolderEntity6::knot_get() const +{ + return knot_get_generic(item, 6); +} + +Geom::Point +Box3DKnotHolderEntity7::knot_get() const +{ + return knot_get_generic(item, 7); +} + +Geom::Point +Box3DKnotHolderEntityCenter::knot_get() const +{ + SPBox3D *box = dynamic_cast(item); + if (box) { + return box3d_get_center_screen(box); + } else { + return Geom::Point(); // TODO investigate proper fallback + } +} + +void +Box3DKnotHolderEntity0::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) +{ + knot_set_generic(item, 0, new_pos, state); +} + +void +Box3DKnotHolderEntity1::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) +{ + knot_set_generic(item, 1, new_pos, state); +} + +void +Box3DKnotHolderEntity2::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) +{ + knot_set_generic(item, 2, new_pos, state); +} + +void +Box3DKnotHolderEntity3::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) +{ + knot_set_generic(item, 3, new_pos, state); +} + +void +Box3DKnotHolderEntity4::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) +{ + knot_set_generic(item, 4, new_pos, state); +} + +void +Box3DKnotHolderEntity5::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) +{ + knot_set_generic(item, 5, new_pos, state); +} + +void +Box3DKnotHolderEntity6::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) +{ + knot_set_generic(item, 6, new_pos, state); +} + +void +Box3DKnotHolderEntity7::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, unsigned int state) +{ + knot_set_generic(item, 7, new_pos, state); +} + +void +Box3DKnotHolderEntityCenter::knot_set(Geom::Point const &new_pos, Geom::Point const &origin, unsigned int state) +{ + Geom::Point const s = snap_knot_position(new_pos, state); + + SPBox3D *box = dynamic_cast(item); + g_assert(box != NULL); + Geom::Affine const i2dt (item->i2dt_affine ()); + + box3d_set_center(box, s * i2dt, origin * i2dt, !(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, Inkscape::CTRL_TYPE_SHAPER, + _("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, Inkscape::CTRL_TYPE_SHAPER, + _("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, Inkscape::CTRL_TYPE_SHAPER, + _("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, Inkscape::CTRL_TYPE_SHAPER, + _("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, Inkscape::CTRL_TYPE_SHAPER, + _("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, Inkscape::CTRL_TYPE_SHAPER, + _("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, Inkscape::CTRL_TYPE_SHAPER, + _("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, Inkscape::CTRL_TYPE_SHAPER, + _("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, Inkscape::CTRL_TYPE_POINT, + _("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() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +class ArcKnotHolderEntityEnd : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +class ArcKnotHolderEntityRX : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +class ArcKnotHolderEntityRY : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +class ArcKnotHolderEntityCenter : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +/* + * return values: + * 1 : inside + * 0 : on the curves + * -1 : outside + */ +static gint +sp_genericellipse_side(SPGenericEllipse *ellipse, Geom::Point const &p) +{ + 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; + // We add a bit of a buffer, so there's a decent chance the user will + // be able to adjust the arc without the closed status flipping between + // open and closed during micro mouse movements. + if (s < 0.75) return 1; + if (s > 1.25) return -1; + return 0; +} + +void +ArcKnotHolderEntityStart::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + int snaps = Inkscape::Preferences::get()->getInt("/options/rotationsnapsperpi/value", 12); + + SPGenericEllipse *arc = dynamic_cast(item); + g_assert(arc != NULL); + + gint side = sp_genericellipse_side(arc, p); + if(side != 0) { arc->setArcType( (side == -1) ? + SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE : + SP_GENERIC_ELLIPSE_ARC_TYPE_ARC); } + + Geom::Point delta = p - Geom::Point(arc->cx.computed, arc->cy.computed); + Geom::Scale sc(arc->rx.computed, arc->ry.computed); + + double offset = arc->start - atan2(delta * sc.inverse()); + arc->start -= offset; + + if ((state & GDK_CONTROL_MASK) && snaps) { + arc->start = sp_round(arc->start, M_PI / snaps); + } + if (state & GDK_SHIFT_MASK) { + arc->end -= offset; + } + + arc->normalize(); + arc->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +Geom::Point +ArcKnotHolderEntityStart::knot_get() const +{ + SPGenericEllipse const *ge = dynamic_cast(item); + g_assert(ge != NULL); + + return ge->getPointAtAngle(ge->start); +} + +void +ArcKnotHolderEntityStart::knot_click(unsigned int state) +{ + SPGenericEllipse *ge = dynamic_cast(item); + g_assert(ge != NULL); + + if (state & GDK_SHIFT_MASK) { + ge->end = ge->start = 0; + ge->updateRepr(); + } +} + +void +ArcKnotHolderEntityEnd::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + int snaps = Inkscape::Preferences::get()->getInt("/options/rotationsnapsperpi/value", 12); + + SPGenericEllipse *arc = dynamic_cast(item); + g_assert(arc != NULL); + + gint side = sp_genericellipse_side(arc, p); + if(side != 0) { arc->setArcType( (side == -1) ? + SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE : + SP_GENERIC_ELLIPSE_ARC_TYPE_ARC); } + + Geom::Point delta = p - Geom::Point(arc->cx.computed, arc->cy.computed); + Geom::Scale sc(arc->rx.computed, arc->ry.computed); + + double offset = arc->end - atan2(delta * sc.inverse()); + arc->end -= offset; + + if ((state & GDK_CONTROL_MASK) && snaps) { + arc->end = sp_round(arc->end, M_PI/snaps); + } + if (state & GDK_SHIFT_MASK) { + arc->start -= offset; + } + + arc->normalize(); + arc->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +Geom::Point +ArcKnotHolderEntityEnd::knot_get() const +{ + SPGenericEllipse const *ge = dynamic_cast(item); + g_assert(ge != NULL); + + return ge->getPointAtAngle(ge->end); +} + + +void +ArcKnotHolderEntityEnd::knot_click(unsigned int state) +{ + SPGenericEllipse *ge = dynamic_cast(item); + g_assert(ge != NULL); + + if (state & GDK_SHIFT_MASK) { + ge->end = ge->start = 0; + ge->updateRepr(); + } +} + + +void +ArcKnotHolderEntityRX::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPGenericEllipse *ge = dynamic_cast(item); + g_assert(ge != NULL); + + Geom::Point const s = snap_knot_position(p, state); + + ge->rx = fabs( ge->cx.computed - s[Geom::X] ); + + if ( state & GDK_CONTROL_MASK ) { + ge->ry = ge->rx.computed; + } + + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +Geom::Point +ArcKnotHolderEntityRX::knot_get() const +{ + SPGenericEllipse const *ge = dynamic_cast(item); + g_assert(ge != NULL); + + return (Geom::Point(ge->cx.computed, ge->cy.computed) - Geom::Point(ge->rx.computed, 0)); +} + +void +ArcKnotHolderEntityRX::knot_click(unsigned int state) +{ + SPGenericEllipse *ge = dynamic_cast(item); + g_assert(ge != NULL); + + if (state & GDK_CONTROL_MASK) { + ge->ry = ge->rx.computed; + ge->updateRepr(); + } +} + +void +ArcKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPGenericEllipse *ge = dynamic_cast(item); + g_assert(ge != NULL); + + Geom::Point const s = snap_knot_position(p, state); + + ge->ry = fabs( ge->cy.computed - s[Geom::Y] ); + + if ( state & GDK_CONTROL_MASK ) { + ge->rx = ge->ry.computed; + } + + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +Geom::Point +ArcKnotHolderEntityRY::knot_get() const +{ + SPGenericEllipse const *ge = dynamic_cast(item); + g_assert(ge != NULL); + + return (Geom::Point(ge->cx.computed, ge->cy.computed) - Geom::Point(0, ge->ry.computed)); +} + +void +ArcKnotHolderEntityRY::knot_click(unsigned int state) +{ + SPGenericEllipse *ge = dynamic_cast(item); + g_assert(ge != NULL); + + if (state & GDK_CONTROL_MASK) { + ge->rx = ge->ry.computed; + ge->updateRepr(); + } +} + +void +ArcKnotHolderEntityCenter::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPGenericEllipse *ge = dynamic_cast(item); + g_assert(ge != NULL); + + Geom::Point const s = snap_knot_position(p, state); + + ge->cx = s[Geom::X]; + ge->cy = s[Geom::Y]; + + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +Geom::Point +ArcKnotHolderEntityCenter::knot_get() const +{ + SPGenericEllipse const *ge = dynamic_cast(item); + g_assert(ge != NULL); + + return Geom::Point(ge->cx.computed, ge->cy.computed); +} + + +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(); + ArcKnotHolderEntityCenter *entity_center = new ArcKnotHolderEntityCenter(); + + entity_rx->create(desktop, item, this, Inkscape::CTRL_TYPE_SIZER, + _("Adjust ellipse width, with Ctrl to make circle"), + SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR); + + entity_ry->create(desktop, item, this, Inkscape::CTRL_TYPE_SIZER, + _("Adjust ellipse height, with Ctrl to make circle"), + SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR); + + entity_start->create(desktop, item, this, Inkscape::CTRL_TYPE_ROTATE, + _("Position the start point of the arc or segment; with Shift to move " + "with end point; 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, Inkscape::CTRL_TYPE_ROTATE, + _("Position the end point of the arc or segment; with Shift to move " + "with start point; with Ctrl to snap angle; drag inside the " + "ellipse for arc, outside for segment"), + SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR); + + entity_center->create(desktop, item, this, Inkscape::CTRL_TYPE_POINT, + _("Drag to move the ellipse"), + SP_KNOT_SHAPE_CROSS); + + entity.push_back(entity_rx); + entity.push_back(entity_ry); + entity.push_back(entity_start); + entity.push_back(entity_end); + entity.push_back(entity_center); + + add_pattern_knotholder(); +} + +/* SPStar */ + +class StarKnotHolderEntity1 : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +class StarKnotHolderEntity2 : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +class StarKnotHolderEntityCenter : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +void +StarKnotHolderEntity1::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPStar *star = dynamic_cast(item); + g_assert(star != NULL); + + Geom::Point const s = snap_knot_position(p, state); + + Geom::Point d = s - star->center; + + double arg1 = atan2(d); + double darg1 = arg1 - star->arg[0]; + + if (state & GDK_MOD1_MASK) { + star->randomized = darg1/(star->arg[0] - star->arg[1]); + } else if (state & GDK_SHIFT_MASK) { + star->rounded = darg1/(star->arg[0] - star->arg[1]); + } else if (state & GDK_CONTROL_MASK) { + star->r[0] = L2(d); + } else { + star->r[0] = L2(d); + star->arg[0] = arg1; + star->arg[1] += darg1; + } + star->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void +StarKnotHolderEntity2::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPStar *star = dynamic_cast(item); + g_assert(star != NULL); + + Geom::Point const s = snap_knot_position(p, state); + + if (star->flatsided == false) { + Geom::Point d = s - star->center; + + double arg1 = atan2(d); + double darg1 = arg1 - star->arg[1]; + + if (state & GDK_MOD1_MASK) { + star->randomized = darg1/(star->arg[0] - star->arg[1]); + } else if (state & GDK_SHIFT_MASK) { + star->rounded = fabs(darg1/(star->arg[0] - star->arg[1])); + } else if (state & GDK_CONTROL_MASK) { + star->r[1] = L2(d); + star->arg[1] = star->arg[0] + M_PI / star->sides; + } + else { + star->r[1] = L2(d); + star->arg[1] = atan2(d); + } + star->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } +} + +void +StarKnotHolderEntityCenter::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPStar *star = dynamic_cast(item); + g_assert(star != NULL); + + star->center = snap_knot_position(p, state); + + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +Geom::Point +StarKnotHolderEntity1::knot_get() const +{ + g_assert(item != NULL); + + SPStar const *star = dynamic_cast(item); + g_assert(star != NULL); + + return sp_star_get_xy(star, SP_STAR_POINT_KNOT1, 0); + +} + +Geom::Point +StarKnotHolderEntity2::knot_get() const +{ + g_assert(item != NULL); + + SPStar const *star = dynamic_cast(item); + g_assert(star != NULL); + + return sp_star_get_xy(star, SP_STAR_POINT_KNOT2, 0); +} + +Geom::Point +StarKnotHolderEntityCenter::knot_get() const +{ + g_assert(item != NULL); + + SPStar const *star = dynamic_cast(item); + g_assert(star != NULL); + + return star->center; +} + +static void +sp_star_knot_click(SPItem *item, unsigned int state) +{ + SPStar *star = dynamic_cast(item); + g_assert(star != NULL); + + if (state & GDK_MOD1_MASK) { + star->randomized = 0; + star->updateRepr(); + } else if (state & GDK_SHIFT_MASK) { + star->rounded = 0; + star->updateRepr(); + } else if (state & GDK_CONTROL_MASK) { + star->arg[1] = star->arg[0] + M_PI / star->sides; + star->updateRepr(); + } +} + +void +StarKnotHolderEntity1::knot_click(unsigned int state) +{ + sp_star_knot_click(item, state); +} + +void +StarKnotHolderEntity2::knot_click(unsigned int state) +{ + sp_star_knot_click(item, state); +} + +StarKnotHolder::StarKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) +{ + SPStar *star = dynamic_cast(item); + g_assert(item != NULL); + + StarKnotHolderEntity1 *entity1 = new StarKnotHolderEntity1(); + entity1->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Adjust the tip radius of the star or polygon; " + "with Shift to round; with Alt to randomize")); + + entity.push_back(entity1); + + if (star->flatsided == false) { + StarKnotHolderEntity2 *entity2 = new StarKnotHolderEntity2(); + entity2->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("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); + } + + StarKnotHolderEntityCenter *entity_center = new StarKnotHolderEntityCenter(); + entity_center->create(desktop, item, this, Inkscape::CTRL_TYPE_POINT, + _("Drag to move the star"), + SP_KNOT_SHAPE_CROSS); + entity.push_back(entity_center); + + add_pattern_knotholder(); +} + +/* SPSpiral */ + +class SpiralKnotHolderEntityInner : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); + virtual void knot_click(unsigned int state); +}; + +class SpiralKnotHolderEntityOuter : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +class SpiralKnotHolderEntityCenter : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int 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 + */ +void +SpiralKnotHolderEntityInner::knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); + + SPSpiral *spiral = dynamic_cast(item); + g_assert(spiral != NULL); + + 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 + 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; + spiral->getPolar(spiral->t0, NULL, &arg_t0); + + gdouble arg_tmp = atan2(dy, dx) - arg_t0; + gdouble arg_t0_new = arg_tmp - floor((arg_tmp+M_PI)/(2.0*M_PI))*2.0*M_PI + arg_t0; + spiral->t0 = (arg_t0_new - spiral->arg) / (2.0*M_PI*spiral->revo); + + /* round inner arg per PI/snaps, if CTRL is pressed */ + if ( ( state & GDK_CONTROL_MASK ) + && ( fabs(spiral->revo) > SP_EPSILON_2 ) + && ( snaps != 0 ) ) { + gdouble arg = 2.0*M_PI*spiral->revo*spiral->t0 + spiral->arg; + spiral->t0 = (sp_round(arg, M_PI/snaps) - spiral->arg)/(2.0*M_PI*spiral->revo); + } + + spiral->t0 = CLAMP(spiral->t0, 0.0, 0.999); + } + + spiral->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +/* + * set attributes via outer (t=1) knot point: + * [default] increase/decrease revolution factor + * [control] constrain inner arg to round per PI/4 + */ +void +SpiralKnotHolderEntityOuter::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); + + SPSpiral *spiral = dynamic_cast(item); + g_assert(spiral != NULL); + + 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; + if (!(state & GDK_MOD1_MASK)) { + // if alt not pressed, change also rad; otherwise it is locked + spiral->rad = MAX(hypot(dx, dy), 0.001); + } + if ( ( state & GDK_CONTROL_MASK ) + && snaps ) { + spiral->arg = sp_round(spiral->arg, M_PI/snaps); + } + } else { // roll/unroll + // arg of the spiral outer end + double arg_1; + spiral->getPolar(1, NULL, &arg_1); + + // its fractional part after the whole turns are subtracted + double arg_r = arg_1 - sp_round(arg_1, 2.0*M_PI); + + // arg of the mouse point relative to spiral center + double mouse_angle = atan2(dy, dx); + if (mouse_angle < 0) + mouse_angle += 2*M_PI; + + // snap if ctrl + if ( ( state & GDK_CONTROL_MASK ) && snaps ) { + mouse_angle = sp_round(mouse_angle, M_PI/snaps); + } + + // by how much we want to rotate the outer point + double diff = mouse_angle - arg_r; + if (diff > M_PI) + diff -= 2*M_PI; + else if (diff < -M_PI) + diff += 2*M_PI; + + // calculate the new rad; + // the value of t corresponding to the angle arg_1 + diff: + double t_temp = ((arg_1 + diff) - spiral->arg)/(2*M_PI*spiral->revo); + // the rad at that t: + double rad_new = 0; + if (t_temp > spiral->t0) + spiral->getPolar(t_temp, &rad_new, NULL); + + // change the revo (converting diff from radians to the number of turns) + spiral->revo += diff/(2*M_PI); + if (spiral->revo < 1e-3) + spiral->revo = 1e-3; + + // if alt not pressed and the values are sane, change the rad + if (!(state & GDK_MOD1_MASK) && rad_new > 1e-3 && rad_new/spiral->rad < 2) { + // adjust t0 too so that the inner point stays unmoved + double r0; + spiral->getPolar(spiral->t0, &r0, NULL); + spiral->rad = rad_new; + spiral->t0 = pow(r0 / spiral->rad, 1.0/spiral->exp); + } + if (!IS_FINITE(spiral->t0)) spiral->t0 = 0.0; + spiral->t0 = CLAMP(spiral->t0, 0.0, 0.999); + } + + spiral->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void +SpiralKnotHolderEntityCenter::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPSpiral *spiral = dynamic_cast(item); + g_assert(spiral != NULL); + + Geom::Point const s = snap_knot_position(p, state); + + spiral->cx = s[Geom::X]; + spiral->cy = s[Geom::Y]; + + spiral->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +Geom::Point +SpiralKnotHolderEntityInner::knot_get() const +{ + SPSpiral const *spiral = dynamic_cast(item); + g_assert(spiral != NULL); + + return spiral->getXY(spiral->t0); +} + +Geom::Point +SpiralKnotHolderEntityOuter::knot_get() const +{ + SPSpiral const *spiral = dynamic_cast(item); + g_assert(spiral != NULL); + + return spiral->getXY(1.0); +} + +Geom::Point +SpiralKnotHolderEntityCenter::knot_get() const +{ + SPSpiral const *spiral = dynamic_cast(item); + g_assert(spiral != NULL); + + return Geom::Point(spiral->cx, spiral->cy); +} + +void +SpiralKnotHolderEntityInner::knot_click(unsigned int state) +{ + SPSpiral *spiral = dynamic_cast(item); + g_assert(spiral != NULL); + + if (state & GDK_MOD1_MASK) { + spiral->exp = 1; + spiral->updateRepr(); + } else if (state & GDK_SHIFT_MASK) { + spiral->t0 = 0; + spiral->updateRepr(); + } +} + +SpiralKnotHolder::SpiralKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) +{ + SpiralKnotHolderEntityCenter *entity_center = new SpiralKnotHolderEntityCenter(); + SpiralKnotHolderEntityInner *entity_inner = new SpiralKnotHolderEntityInner(); + SpiralKnotHolderEntityOuter *entity_outer = new SpiralKnotHolderEntityOuter(); + + // NOTE: entity_central and entity_inner can overlap. + // + // In that case it would be a problem if the center control point was ON + // TOP because it would steal the mouse focus and the user would loose the + // ability to access the inner control point using only the mouse. + // + // However if the inner control point is ON TOP, taking focus, the + // situation is a lot better: the user can still move the inner control + // point with the mouse to regain access to the center control point. + // + // So, create entity_inner AFTER entity_center; this ensures that + // entity_inner gets rendered ON TOP. + entity_center->create(desktop, item, this, Inkscape::CTRL_TYPE_POINT, + _("Drag to move the spiral"), + SP_KNOT_SHAPE_CROSS); + + entity_inner->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Roll/unroll the spiral from inside; with Ctrl to snap angle; " + "with Alt to converge/diverge")); + + entity_outer->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Roll/unroll the spiral from outside; with Ctrl to snap angle; " + "with Shift to scale/rotate; with Alt to lock radius")); + + entity.push_back(entity_center); + entity.push_back(entity_inner); + entity.push_back(entity_outer); + + add_pattern_knotholder(); +} + +/* SPOffset */ + +class OffsetKnotHolderEntity : public KnotHolderEntity { +public: + virtual Geom::Point knot_get() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +void +OffsetKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, unsigned int state) +{ + SPOffset *offset = dynamic_cast(item); + g_assert(offset != NULL); + + Geom::Point const p_snapped = snap_knot_position(p, state); + + offset->rad = sp_offset_distance_to_original(offset, p_snapped); + offset->knot = p_snapped; + offset->knotSet = true; + + offset->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + + +Geom::Point +OffsetKnotHolderEntity::knot_get() const +{ + SPOffset const *offset = dynamic_cast(item); + g_assert(offset != NULL); + + Geom::Point np; + sp_offset_top_point(offset,&np); + return np; +} + +OffsetKnotHolder::OffsetKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) +{ + OffsetKnotHolderEntity *entity_offset = new OffsetKnotHolderEntity(); + entity_offset->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Adjust the offset distance")); + entity.push_back(entity_offset); + + add_pattern_knotholder(); +} + +// 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() const; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state); +}; + +Geom::Point +FlowtextKnotHolderEntity::knot_get() const +{ + SPRect const *rect = dynamic_cast(item); + g_assert(rect != NULL); + + return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed); +} + +void +FlowtextKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) +{ + set_internal(p, origin, state); +} + +FlowtextKnotHolder::FlowtextKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) : + KnotHolder(desktop, item, relhandler) +{ + g_assert(item != NULL); + + FlowtextKnotHolderEntity *entity_flowtext = new FlowtextKnotHolderEntity(); + entity_flowtext->create(desktop, item, this, Inkscape::CTRL_TYPE_SHAPER, + _("Drag to resize the flowed text frame")); + entity.push_back(entity_flowtext); +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/shape-editor.cpp b/src/ui/shape-editor.cpp index 2c0e662ee..4851c413f 100644 --- a/src/ui/shape-editor.cpp +++ b/src/ui/shape-editor.cpp @@ -20,15 +20,15 @@ #include "sp-shape.h" #include "sp-path.h" #include "inkscape.h" -#include "ui/object-edit.h" #include "ui/shape-editor.h" #include "xml/node-event-vector.h" -//using Inkscape::createKnotHolder; namespace Inkscape { namespace UI { +KnotHolder *createKnotHolder(SPItem *item, SPDesktop *desktop); + bool ShapeEditor::_blockSetItem = false; ShapeEditor::ShapeEditor(SPDesktop *dt, Geom::Affine edit_transform) : -- cgit v1.2.3