diff options
| author | vanntile <valentin.ionita1201@gmail.com> | 2019-08-02 16:23:28 +0000 |
|---|---|---|
| committer | vanntile <valentin.ionita1201@gmail.com> | 2019-08-17 15:33:45 +0000 |
| commit | 56d76b4861d034dad8b4eeb25e86308e233202e4 (patch) | |
| tree | 9edc93e1ac61c7e176fdb7050594f0c8ee43ad57 /src | |
| parent | Hatch knot editing initial commit (diff) | |
| download | inkscape-56d76b4861d034dad8b4eeb25e86308e233202e4.tar.gz inkscape-56d76b4861d034dad8b4eeb25e86308e233202e4.zip | |
Added knot holder entities
Diffstat (limited to 'src')
| -rw-r--r-- | src/knot-holder-entity.cpp | 96 | ||||
| -rw-r--r-- | src/knot-holder-entity.h | 62 | ||||
| -rw-r--r-- | src/knotholder.cpp | 61 | ||||
| -rw-r--r-- | src/object/sp-hatch.cpp | 70 | ||||
| -rw-r--r-- | src/object/sp-hatch.h | 10 | ||||
| -rw-r--r-- | src/object/sp-item.cpp | 32 | ||||
| -rw-r--r-- | src/object/sp-item.h | 17 |
7 files changed, 316 insertions, 32 deletions
diff --git a/src/knot-holder-entity.cpp b/src/knot-holder-entity.cpp index 75f36089c..afa51a9ad 100644 --- a/src/knot-holder-entity.cpp +++ b/src/knot-holder-entity.cpp @@ -26,6 +26,7 @@ #include "style.h" #include "include/macros.h" +#include "object/sp-hatch.h" #include "object/sp-item.h" #include "object/sp-namedview.h" #include "object/sp-pattern.h" @@ -260,6 +261,100 @@ PatternKnotHolderEntityScale::knot_set(Geom::Point const &p, Geom::Point const & item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } +/* Hatch manipulation */ +bool HatchKnotHolderEntity::knot_missing() const +{ + SPHatch *hatch = _hatch(); + return (hatch == nullptr); +} + +SPHatch *HatchKnotHolderEntity::_hatch() const +{ + return _fill ? SP_HATCH(item->style->getFillPaintServer()) : SP_HATCH(item->style->getStrokePaintServer()); +} + +static Geom::Point sp_hatch_knot_get(SPHatch const *hatch, gdouble x, gdouble y) +{ + return Geom::Point(x, y) * hatch->hatchTransform(); +} + +Geom::Point HatchKnotHolderEntityXY::knot_get() const +{ + SPHatch *hatch = _hatch(); + return sp_hatch_knot_get(hatch, 0, 0); +} + +Geom::Point HatchKnotHolderEntityAngle::knot_get() const +{ + SPHatch *hatch = _hatch(); + return sp_hatch_knot_get(hatch, hatch->pitch(), 0); +} + +Geom::Point HatchKnotHolderEntityScale::knot_get() const +{ + SPHatch *hatch = _hatch(); + return sp_hatch_knot_get(hatch, hatch->pitch(), hatch->pitch()); +} + +void HatchKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) +{ + Geom::Point p_snapped = snap_knot_position(p, state); + + if (state & GDK_CONTROL_MASK) { + if (fabs((p - origin)[Geom::X]) > fabs((p - origin)[Geom::Y])) { + p_snapped[Geom::Y] = origin[Geom::Y]; + } else { + p_snapped[Geom::X] = origin[Geom::X]; + } + } + + if (state) { + Geom::Point const q = p_snapped - knot_get(); + item->adjust_hatch(Geom::Translate(q), false, _fill ? TRANSFORM_FILL : TRANSFORM_STROKE); + } + + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void HatchKnotHolderEntityAngle::knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); + + SPHatch *hatch = _hatch(); + + // get the angle from hatch 0,0 to the cursor pos + Geom::Point transform_origin = sp_hatch_knot_get(hatch, 0, 0); + gdouble theta = atan2(p - transform_origin); + gdouble theta_old = atan2(knot_get() - transform_origin); + + if (state & GDK_CONTROL_MASK) { + theta = sp_round(theta, M_PI / snaps); + } + + Geom::Affine rot = + Geom::Translate(-transform_origin) * Geom::Rotate(theta - theta_old) * Geom::Translate(transform_origin); + item->adjust_hatch(rot, false, _fill ? TRANSFORM_FILL : TRANSFORM_STROKE); + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void HatchKnotHolderEntityScale::knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) +{ + SPHatch *hatch = _hatch(); + + // FIXME: this snapping should be done together with knowing whether control was pressed. + // If GDK_CONTROL_MASK, then constrained snapping should be used. + Geom::Point p_snapped = snap_knot_position(p, state); + + // Get the new scale from the position of the knotholder + Geom::Affine transform = hatch->hatchTransform(); + Geom::Affine transform_inverse = transform.inverse(); + Geom::Point d = p_snapped * transform_inverse; + Geom::Point d_origin = origin * transform_inverse; + Geom::Point origin_dt; + + // TODO ??? +} /* Filter manipulation */ void FilterKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) @@ -301,7 +396,6 @@ void FilterKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &o } item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - } Geom::Point FilterKnotHolderEntity::knot_get() const diff --git a/src/knot-holder-entity.h b/src/knot-holder-entity.h index 92c4d5536..c7c035c6b 100644 --- a/src/knot-holder-entity.h +++ b/src/knot-holder-entity.h @@ -21,6 +21,7 @@ #include "knot.h" #include "snapper.h" +class SPHatch; class SPItem; class SPKnot; class SPDesktop; @@ -59,7 +60,7 @@ public: SPKnotShapeType shape = SP_KNOT_SHAPE_DIAMOND, SPKnotModeType mode = SP_KNOT_MODE_XOR, guint32 color = 0xffffff00); - + /* the get/set/click handlers are virtual functions; each handler class for a knot should be derived from KnotHolderEntity and override these functions */ virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) = 0; @@ -108,12 +109,13 @@ protected: /* pattern manipulation */ class PatternKnotHolderEntity : public KnotHolderEntity { -public: + public: PatternKnotHolderEntity(bool fill) : KnotHolderEntity(), _fill(fill) {} bool knot_missing() const override; - void knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state) override {}; -protected: - // true if the entity tracks fill, false for stroke + void knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state) override{}; + + protected: + // true if the entity tracks fill, false for stroke bool _fill; SPPattern *_pattern() const; }; @@ -139,9 +141,57 @@ public: void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) override; }; +/* Hatch manipulation */ +class HatchKnotHolderEntity : public KnotHolderEntity { + public: + HatchKnotHolderEntity(bool fill) + : KnotHolderEntity() + , _fill(fill) + { + } + bool knot_missing() const override; + void knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state) override{}; + + protected: + // true if the entity tracks fill, false for stroke + bool _fill; + SPHatch *_hatch() const; +}; + +class HatchKnotHolderEntityXY : public HatchKnotHolderEntity { + public: + HatchKnotHolderEntityXY(bool fill) + : HatchKnotHolderEntity(fill) + { + } + Geom::Point knot_get() const override; + void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) override; +}; + +class HatchKnotHolderEntityAngle : public HatchKnotHolderEntity { + public: + HatchKnotHolderEntityAngle(bool fill) + : HatchKnotHolderEntity(fill) + { + } + Geom::Point knot_get() const override; + void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) override; +}; + +class HatchKnotHolderEntityScale : public HatchKnotHolderEntity { + public: + HatchKnotHolderEntityScale(bool fill) + : HatchKnotHolderEntity(fill) + { + } + Geom::Point knot_get() const override; + void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned int state) override; +}; + + /* Filter manipulation */ class FilterKnotHolderEntity : public KnotHolderEntity { -public: + public: FilterKnotHolderEntity(bool topleft) : KnotHolderEntity(), _topleft(topleft) {} Geom::Point knot_get() const override; void knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state) override {}; diff --git a/src/knotholder.cpp b/src/knotholder.cpp index 7d6fa776e..7f65cec2b 100644 --- a/src/knotholder.cpp +++ b/src/knotholder.cpp @@ -354,16 +354,15 @@ void KnotHolder::add_pattern_knotholder() PatternKnotHolderEntityScale *entity_scale = new PatternKnotHolderEntityScale(true); entity_xy->create(desktop, item, this, Inkscape::CTRL_TYPE_POINT, // TRANSLATORS: This refers to the pattern that's inside the object - _("<b>Move</b> the pattern fill inside the object"), - SP_KNOT_SHAPE_CROSS); + _("<b>Move</b> the pattern fill inside the object"), SP_KNOT_SHAPE_CROSS); entity_scale->create(desktop, item, this, Inkscape::CTRL_TYPE_SIZER, - _("<b>Scale</b> the pattern fill; uniformly if with <b>Ctrl</b>"), - SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR); + _("<b>Scale</b> the pattern fill; uniformly if with <b>Ctrl</b>"), SP_KNOT_SHAPE_SQUARE, + SP_KNOT_MODE_XOR); entity_angle->create(desktop, item, this, Inkscape::CTRL_TYPE_ROTATE, - _("<b>Rotate</b> the pattern fill; with <b>Ctrl</b> to snap angle"), - SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR); + _("<b>Rotate</b> the pattern fill; with <b>Ctrl</b> to snap angle"), SP_KNOT_SHAPE_CIRCLE, + SP_KNOT_MODE_XOR); entity.push_back(entity_xy); entity.push_back(entity_angle); @@ -376,15 +375,14 @@ void KnotHolder::add_pattern_knotholder() PatternKnotHolderEntityScale *entity_scale = new PatternKnotHolderEntityScale(false); entity_xy->create(desktop, item, this, Inkscape::CTRL_TYPE_POINT, // TRANSLATORS: This refers to the pattern that's inside the object - _("<b>Move</b> the pattern fill inside the object"), - SP_KNOT_SHAPE_CROSS); + _("<b>Move</b> the pattern stroke inside the object"), SP_KNOT_SHAPE_CROSS); entity_scale->create(desktop, item, this, Inkscape::CTRL_TYPE_SIZER, - _("<b>Scale</b> the pattern fill; uniformly if with <b>Ctrl</b>"), - SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR); + _("<b>Scale</b> the pattern stroke; uniformly if with <b>Ctrl</b>"), SP_KNOT_SHAPE_SQUARE, + SP_KNOT_MODE_XOR); entity_angle->create(desktop, item, this, Inkscape::CTRL_TYPE_ROTATE, - _("<b>Rotate</b> the pattern fill; with <b>Ctrl</b> to snap angle"), + _("<b>Rotate</b> the pattern stroke; with <b>Ctrl</b> to snap angle"), SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR); entity.push_back(entity_xy); @@ -397,8 +395,47 @@ void KnotHolder::add_pattern_knotholder() void KnotHolder::add_hatch_knotholder() { if ((item->style->fill.isPaintserver()) && dynamic_cast<SPHatch *>(item->style->getFillPaintServer())) { - std::cout << "add_hatch_knotholder: activated" << std::endl; + HatchKnotHolderEntityXY *entity_xy = new HatchKnotHolderEntityXY(true); + HatchKnotHolderEntityAngle *entity_angle = new HatchKnotHolderEntityAngle(true); + HatchKnotHolderEntityScale *entity_scale = new HatchKnotHolderEntityScale(true); + entity_xy->create(desktop, item, this, Inkscape::CTRL_TYPE_POINT, + // TRANSLATORS: This refers to the hatch that's inside the object + _("<b>Move</b> the hatch fill inside the object"), SP_KNOT_SHAPE_CROSS); + + entity_scale->create(desktop, item, this, Inkscape::CTRL_TYPE_SIZER, + _("<b>Scale</b> the hatch fill; uniformly if with <b>Ctrl</b>"), SP_KNOT_SHAPE_SQUARE, + SP_KNOT_MODE_XOR); + + entity_angle->create(desktop, item, this, Inkscape::CTRL_TYPE_ROTATE, + _("<b>Rotate</b> the hatch fill; with <b>Ctrl</b> to snap angle"), SP_KNOT_SHAPE_CIRCLE, + SP_KNOT_MODE_XOR); + + entity.push_back(entity_xy); + entity.push_back(entity_angle); + entity.push_back(entity_scale); } + + if ((item->style->stroke.isPaintserver()) && dynamic_cast<SPHatch *>(item->style->getStrokePaintServer())) { + HatchKnotHolderEntityXY *entity_xy = new HatchKnotHolderEntityXY(false); + HatchKnotHolderEntityAngle *entity_angle = new HatchKnotHolderEntityAngle(false); + HatchKnotHolderEntityScale *entity_scale = new HatchKnotHolderEntityScale(false); + entity_xy->create(desktop, item, this, Inkscape::CTRL_TYPE_POINT, + // TRANSLATORS: This refers to the pattern that's inside the object + _("<b>Move</b> the hatch stroke inside the object"), SP_KNOT_SHAPE_CROSS); + + entity_scale->create(desktop, item, this, Inkscape::CTRL_TYPE_SIZER, + _("<b>Scale</b> the hatch stroke; uniformly if with <b>Ctrl</b>"), SP_KNOT_SHAPE_SQUARE, + SP_KNOT_MODE_XOR); + + entity_angle->create(desktop, item, this, Inkscape::CTRL_TYPE_ROTATE, + _("<b>Rotate</b> the hatch stroke; with <b>Ctrl</b> to snap angle"), SP_KNOT_SHAPE_CIRCLE, + SP_KNOT_MODE_XOR); + + entity.push_back(entity_xy); + entity.push_back(entity_angle); + entity.push_back(entity_scale); + } + updateControlSizes(); } void KnotHolder::add_filter_knotholder() { diff --git a/src/object/sp-hatch.cpp b/src/object/sp-hatch.cpp index ae91b4dd2..3c5da9bd9 100644 --- a/src/object/sp-hatch.cpp +++ b/src/object/sp-hatch.cpp @@ -31,7 +31,9 @@ #include "display/drawing.h" #include "display/drawing-pattern.h" +#include "sp-defs.h" #include "sp-hatch-path.h" +#include "sp-item.h" #include "svg/svg.h" @@ -482,6 +484,74 @@ gdouble SPHatch::rotate() const return val; } +guint SPHatch::_countHrefs(SPObject *o) const +{ + if (!o) + return 1; + + guint i = 0; + + SPStyle *style = o->style; + if (style && style->fill.isPaintserver() && SP_IS_HATCH(SP_STYLE_FILL_SERVER(style)) && + SP_HATCH(SP_STYLE_FILL_SERVER(style)) == this) { + i++; + } + if (style && style->stroke.isPaintserver() && SP_IS_HATCH(SP_STYLE_STROKE_SERVER(style)) && + SP_HATCH(SP_STYLE_STROKE_SERVER(style)) == this) { + i++; + } + + for (auto &child : o->children) { + i += _countHrefs(&child); + } + + return i; +} + +SPHatch *SPHatch::clone_if_necessary(SPItem *item, const gchar *property) +{ + SPHatch *hatch = this; + if (hatch->href.empty() || hatch->hrefcount > _countHrefs(item)) { + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + Inkscape::XML::Node *defsrepr = document->getDefs()->getRepr(); + + Inkscape::XML::Node *repr = xml_doc->createElement("svg:hatch"); + repr->setAttribute("inkscape:collect", "always"); + Glib::ustring parent_ref = Glib::ustring::compose("#%1", getRepr()->attribute("id")); + repr->setAttribute("xlink:href", parent_ref); + + defsrepr->addChild(repr, nullptr); + const gchar *child_id = repr->attribute("id"); + SPObject *child = document->getObjectById(child_id); + g_assert(SP_IS_HATCH(child)); + + hatch = SP_HATCH(child); + + Glib::ustring href = Glib::ustring::compose("url(#%1)", hatch->getRepr()->attribute("id")); + + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property(css, property, href.c_str()); + sp_repr_css_change_recursive(item->getRepr(), css, "style"); + } + + return hatch; +} + +void SPHatch::transform_multiply(Geom::Affine postmul, bool set) +{ + if (set) { + _hatchTransform = postmul; + } else { + _hatchTransform = hatchTransform() * postmul; + } + + _hatchTransform_set = true; + + gchar *c = sp_svg_transform_write(_hatchTransform); + setAttribute("transform", c); + g_free(c); +} + bool SPHatch::isValid() const { bool valid = false; diff --git a/src/object/sp-hatch.h b/src/object/sp-hatch.h index 446be0357..f548163a1 100644 --- a/src/object/sp-hatch.h +++ b/src/object/sp-hatch.h @@ -42,6 +42,9 @@ class Node; } } +#define SP_HATCH(obj) (dynamic_cast<SPHatch *>((SPObject *)obj)) +#define SP_IS_HATCH(obj) (dynamic_cast<const SPHatch *>((SPObject *)obj) != NULL) + class SPHatch : public SPPaintServer { public: enum HatchUnits { @@ -82,6 +85,9 @@ public: std::vector<SPHatchPath *> hatchPaths(); std::vector<SPHatchPath const *> hatchPaths() const; + SPHatch *clone_if_necessary(SPItem *item, const gchar *property); + void transform_multiply(Geom::Affine postmul, bool set); + bool isValid() const override; Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptRect bbox) override; @@ -124,6 +130,10 @@ private: RenderInfo _calculateRenderInfo(View const &view) const; Geom::OptInterval _calculateStripExtents(Geom::OptRect const &bbox) const; + /** + Count how many times hatch is used by the styles of o and its descendants + */ + guint _countHrefs(SPObject *o) const; /** * Gets called when the hatch is reattached to another <hatch> diff --git a/src/object/sp-item.cpp b/src/object/sp-item.cpp index dfc482267..003562269 100644 --- a/src/object/sp-item.cpp +++ b/src/object/sp-item.cpp @@ -34,11 +34,12 @@ #include "sp-clippath.h" #include "sp-desc.h" #include "sp-guide.h" +#include "sp-hatch.h" #include "sp-item-rm-unsatisfied-cns.h" #include "sp-mask.h" #include "sp-pattern.h" -#include "sp-root.h" #include "sp-rect.h" +#include "sp-root.h" #include "sp-switch.h" #include "sp-text.h" #include "sp-textpath.h" @@ -730,7 +731,7 @@ Inkscape::XML::Node* SPItem::write(Inkscape::XML::Document *xml_doc, Inkscape::X } } } - + gchar *c = sp_svg_transform_write(item->transform); repr->setAttribute("transform", c); g_free(c); @@ -825,7 +826,7 @@ Geom::OptRect SPItem::visualBounds(Geom::Affine const &transform) const double len_x = bbox ? bbox->width() : 0; double len_y = bbox ? bbox->height() : 0; - + x.update(12, 6, len_x); y.update(12, 6, len_y); w.update(12, 6, len_x); @@ -1233,7 +1234,7 @@ void SPItem::invoke_hide(unsigned key) // Adjusters -void SPItem::adjust_pattern(Geom::Affine const &postmul, bool set, PatternTransform pt) +void SPItem::adjust_pattern(Geom::Affine const &postmul, bool set, PaintServerTransform pt) { bool fill = (pt == TRANSFORM_FILL || pt == TRANSFORM_BOTH); if (fill && style && (style->fill.isPaintserver())) { @@ -1256,6 +1257,29 @@ void SPItem::adjust_pattern(Geom::Affine const &postmul, bool set, PatternTransf } } +void SPItem::adjust_hatch(Geom::Affine const &postmul, bool set, PaintServerTransform pt) +{ + bool fill = (pt == TRANSFORM_FILL || pt == TRANSFORM_BOTH); + if (fill && style && (style->fill.isPaintserver())) { + SPObject *server = style->getFillPaintServer(); + SPHatch *serverHatch = dynamic_cast<SPHatch *>(server); + if (serverHatch) { + SPHatch *hatch = serverHatch->clone_if_necessary(this, "fill"); + hatch->transform_multiply(postmul, set); + } + } + + bool stroke = (pt == TRANSFORM_STROKE || pt == TRANSFORM_BOTH); + if (stroke && style && (style->stroke.isPaintserver())) { + SPObject *server = style->getStrokePaintServer(); + SPHatch *serverHatch = dynamic_cast<SPHatch *>(server); + if (serverHatch) { + SPHatch *hatch = serverHatch->clone_if_necessary(this, "stroke"); + hatch->transform_multiply(postmul, set); + } + } +} + void SPItem::adjust_gradient( Geom::Affine const &postmul, bool set ) { if ( style && style->fill.isPaintserver() ) { diff --git a/src/object/sp-item.h b/src/object/sp-item.h index 8a4eb7e78..04f0f5fd7 100644 --- a/src/object/sp-item.h +++ b/src/object/sp-item.h @@ -55,11 +55,7 @@ class SVGViewWidget; // TODO make a completely new function that transforms either the fill or // stroke of any SPItem without adding an extra parameter to adjust_pattern. -enum PatternTransform { - TRANSFORM_BOTH, - TRANSFORM_FILL, - TRANSFORM_STROKE -}; +enum PaintServerTransform { TRANSFORM_BOTH, TRANSFORM_FILL, TRANSFORM_STROKE }; /** * Event structure. @@ -180,9 +176,9 @@ public: bool isHighlightSet() const; guint32 highlight_color() const; - + void setHighlightColor(guint32 color); - + void unsetHighlightColor(); //==================== @@ -306,7 +302,7 @@ public: /** * Allocates unique integer keys. - * + * * @param numkeys Number of keys required. * @return First allocated key; hence if the returned key is n * you can use n, n + 1, ..., n + (numkeys - 1) @@ -319,7 +315,10 @@ public: void invoke_hide(unsigned int key); void getSnappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs=nullptr) const; - void adjust_pattern(/* Geom::Affine const &premul, */ Geom::Affine const &postmul, bool set = false, PatternTransform = TRANSFORM_BOTH); + void adjust_pattern(/* Geom::Affine const &premul, */ Geom::Affine const &postmul, bool set = false, + PaintServerTransform = TRANSFORM_BOTH); + void adjust_hatch(/* Geom::Affine const &premul, */ Geom::Affine const &postmul, bool set = false, + PaintServerTransform = TRANSFORM_BOTH); void adjust_gradient(/* Geom::Affine const &premul, */ Geom::Affine const &postmul, bool set = false); void adjust_stroke(double ex); |
