diff options
| author | Tomasz Boczkowski <penginsbacon@gmail.com> | 2014-10-14 11:01:49 +0000 |
|---|---|---|
| committer | Tomasz Boczkowski <penginsbacon@gmail.com> | 2014-10-14 11:01:49 +0000 |
| commit | f3b182a2facfd52c7bbfee67f207ad765f536beb (patch) | |
| tree | b43f0be1ab2cbf4f3f385582c1825a4e266aadd2 /src | |
| parent | Merged src/sp-item.cpp from svg-paints-support branch (diff) | |
| download | inkscape-f3b182a2facfd52c7bbfee67f207ad765f536beb.tar.gz inkscape-f3b182a2facfd52c7bbfee67f207ad765f536beb.zip | |
Merged hatch rendering code
(bzr r13611.1.8)
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/Makefile_insert | 2 | ||||
| -rw-r--r-- | src/attributes.cpp | 5 | ||||
| -rw-r--r-- | src/attributes.h | 5 | ||||
| -rw-r--r-- | src/display/drawing-pattern.h | 2 | ||||
| -rw-r--r-- | src/sp-hatch-path.cpp | 290 | ||||
| -rw-r--r-- | src/sp-hatch-path.h | 86 | ||||
| -rw-r--r-- | src/sp-hatch.cpp | 616 | ||||
| -rw-r--r-- | src/sp-hatch.h | 158 | ||||
| -rw-r--r-- | src/sp-item.cpp | 70 | ||||
| -rw-r--r-- | src/sp-paint-server.h | 57 | ||||
| -rw-r--r-- | src/style-internal.h | 3 | ||||
| -rw-r--r-- | src/svg/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/svg/Makefile_insert | 2 | ||||
| -rw-r--r-- | src/svg/svg-angle.cpp | 136 | ||||
| -rw-r--r-- | src/svg/svg-angle.h | 69 |
16 files changed, 1471 insertions, 36 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4a792af6b..25556e3bc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,8 @@ set(sp_SRC sp-gradient-reference.cpp sp-gradient.cpp sp-guide.cpp + sp-hatch.cpp + sp-hatch-path.cpp sp-image.cpp sp-item-group.cpp sp-item-notify-moveto.cpp @@ -115,6 +117,8 @@ set(sp_SRC sp-guide-attachment.h sp-guide-constraint.h sp-guide.h + sp-hatch.h + sp-hatch-path.h sp-image.h sp-item-group.h sp-item-notify-moveto.h diff --git a/src/Makefile_insert b/src/Makefile_insert index f191839a1..66188f502 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -158,6 +158,8 @@ ink_common_sources += \ sp-guide-attachment.h \ sp-guide-constraint.h \ sp-guide.cpp sp-guide.h \ + sp-hatch.cpp sp-hatch.h \ + sp-hatch-path.cpp sp-hatch-path.h \ sp-image.cpp sp-image.h \ sp-item.cpp sp-item.h \ sp-item-group.cpp sp-item-group.h \ diff --git a/src/attributes.cpp b/src/attributes.cpp index fec5d3af4..75e78957f 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -292,6 +292,11 @@ static SPStyleProp const props[] = { {SP_ATTR_PATTERNUNITS, "patternUnits"}, {SP_ATTR_PATTERNCONTENTUNITS, "patternContentUnits"}, {SP_ATTR_PATTERNTRANSFORM, "patternTransform"}, + /* SPHatch */ + {SP_ATTR_HATCHUNITS, "hatchUnits"}, + {SP_ATTR_HATCHCONTENTUNITS, "hatchContentUnits"}, + {SP_ATTR_HATCHTRANSFORM, "hatchTransform"}, + {SP_ATTR_PITCH, "pitch"}, /* SPClipPath */ {SP_ATTR_CLIPPATHUNITS, "clipPathUnits"}, /* SPMask */ diff --git a/src/attributes.h b/src/attributes.h index 3397e4034..8d3c6399b 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -293,6 +293,11 @@ enum SPAttributeEnum { SP_ATTR_PATTERNUNITS, SP_ATTR_PATTERNCONTENTUNITS, SP_ATTR_PATTERNTRANSFORM, + /* SPHatch */ + SP_ATTR_HATCHUNITS, + SP_ATTR_HATCHCONTENTUNITS, + SP_ATTR_HATCHTRANSFORM, + SP_ATTR_PITCH, /* SPClipPath */ SP_ATTR_CLIPPATHUNITS, /* SPMask */ diff --git a/src/display/drawing-pattern.h b/src/display/drawing-pattern.h index 487da51b7..b614785dc 100644 --- a/src/display/drawing-pattern.h +++ b/src/display/drawing-pattern.h @@ -59,6 +59,7 @@ public: protected: virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset); + Geom::IntPoint _patternDimentions(); Geom::Affine *_pattern_to_user; Geom::Affine _overflow_initial_transform; @@ -66,7 +67,6 @@ protected: int _overflow_steps; Geom::OptRect _tile_rect; bool _debug; - Geom::IntPoint _pattern_resolution; }; diff --git a/src/sp-hatch-path.cpp b/src/sp-hatch-path.cpp new file mode 100644 index 000000000..bdefd32d8 --- /dev/null +++ b/src/sp-hatch-path.cpp @@ -0,0 +1,290 @@ +/** @file + * SVG <hatchPath> implementation + *//* + * Author: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Tomasz Boczkowski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cstring> +#include <string> +#include <2geom/path.h> +#include <2geom/transforms.h> + +#include "svg/svg.h" +#include "display/cairo-utils.h" +#include "display/curve.h" +#include "display/drawing-context.h" +#include "display/drawing-surface.h" +#include "display/drawing.h" +#include "display/drawing-shape.h" +#include "attributes.h" +#include "document-private.h" +#include "uri.h" +#include "style.h" +#include "sp-hatch-path.h" +#include "svg/css-ostringstream.h" +#include "xml/repr.h" + +#include "sp-factory.h" + +namespace { +SPObject* createHatchPath() { + return new SPHatchPath(); +} + +bool hatchRegistered = SPFactory::instance().registerObject("svg:hatchPath", createHatchPath); +} + +SPHatchPath::SPHatchPath() + : _curve(NULL) +{ + offset.unset(); +} + +SPHatchPath::~SPHatchPath() { +} + +void SPHatchPath::setCurve(SPCurve *new_curve, bool owner) { + if (_curve) { + _curve = _curve->unref(); + } + + if (new_curve) { + if (owner) { + _curve = new_curve->ref(); + } else { + _curve = new_curve->copy(); + } + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void SPHatchPath::build(SPDocument* doc, Inkscape::XML::Node* repr) { + SPObject::build(doc, repr); + + this->readAttr("d"); + this->readAttr("offset"); + this->readAttr( "style" ); + + style->fill.setNone(); +} + +void SPHatchPath::release() { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + delete iter->arenaitem; + iter->arenaitem = NULL; + } + + SPObject::release(); +} + +void SPHatchPath::set(unsigned int key, const gchar* value) { + switch (key) { + case SP_ATTR_D: + if (value) { + Geom::PathVector pv; + _readHatchPathVector(value, pv, _continuous); + SPCurve *curve = new SPCurve(pv); + + if (curve) { + this->setCurve(curve, true); + curve->unref(); + } + } else { + this->setCurve(NULL, true); + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_OFFSET: + offset.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + default: + if (SP_ATTRIBUTE_IS_CSS(key)) { + sp_style_read_from_object(this->style, this); + requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + } else { + SPObject::set(key, value); + } + break; + } +} + + +void SPHatchPath::update(SPCtx* ctx, unsigned int flags) { + + if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { + flags &= ~SP_OBJECT_USER_MODIFIED_FLAG_B; + } + + if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { + if (this->style->stroke_width.unit == SP_CSS_UNIT_PERCENT) { + SPItemCtx *ictx = (SPItemCtx *) ctx; + double const aw = 1.0 / ictx->i2vp.descrim(); + this->style->stroke_width.computed = this->style->stroke_width.value * aw; + + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + iter->arenaitem->setStyle(this->style); + } + } + } + + if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + _updateView(*iter); + } + } +} + +bool SPHatchPath::isValid() const { + if (_curve && (_repeatLength() <= 0)) { + return false; + } + return true; +} + +Inkscape::DrawingItem *SPHatchPath::show(Inkscape::Drawing &drawing, unsigned int key) { + Inkscape::DrawingShape *s = new Inkscape::DrawingShape(drawing); + _display.push_front(View(s, key)); + + _updateView(_display.front()); + + return s; +} + +void SPHatchPath::hide(unsigned int key) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + delete iter->arenaitem; + _display.erase(iter); + return; + } + } + + g_assert_not_reached(); +} + +void SPHatchPath::setStripExtents(unsigned int key, Geom::OptInterval const &extents) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + iter->extents = extents; + break; + } + } +} + +gdouble SPHatchPath::_repeatLength() const { + if (!_curve) { + return 0; + } + + if (!_curve->last_point()) { + return 0; + } + + return _curve->last_point()->y(); +} + +void SPHatchPath::_updateView(View &view) { + SPCurve *calculated_curve = new SPCurve; + + if (!view.extents) { + return; + } + + if (!_curve) { + calculated_curve->moveto(0, view.extents->min()); + calculated_curve->lineto(0, view.extents->max()); + //TODO: if hatch has a dasharray defined, adjust line ends + } else { + gdouble repeatLength = _repeatLength(); + if (repeatLength > 0) { + gdouble initial_y = floor(view.extents->min() / repeatLength) * repeatLength; + int segment_cnt = ceil((view.extents->extent()) / repeatLength) + 1; + + SPCurve *segment =_curve->copy(); + segment->transform(Geom::Translate(0, initial_y)); + + Geom::Affine step_transform = Geom::Translate(0, repeatLength); + for (int i = 0; i < segment_cnt; i++) { + if (_continuous) { + calculated_curve->append_continuous(segment, 0.0625); + } else { + calculated_curve->append(segment, false); + } + segment->transform(step_transform); + } + + segment->unref(); + } + } + + Geom::Affine offset_transform = Geom::Translate(offset.computed, 0); + view.arenaitem->setTransform(offset_transform); + style->fill.setNone(); + view.arenaitem->setStyle(this->style); + view.arenaitem->setPath(calculated_curve); + + calculated_curve->unref(); +} + +void SPHatchPath::_readHatchPathVector(char const *str, Geom::PathVector &pathv, bool &continous_join) { + if (!str) { + return; + } + + pathv = sp_svg_read_pathv(str); + + if (!pathv.empty()) { + continous_join = false; + } else { + Glib::ustring str2 = Glib::ustring::compose("M0,0 %1", str); + pathv = sp_svg_read_pathv(str2.c_str()); + if (pathv.empty()) { + return; + } + + gdouble last_point_x = pathv.back().finalPoint().x(); + Inkscape::CSSOStringStream stream; + stream << last_point_x; + Glib::ustring str3 = Glib::ustring::compose("M%1,0 %2", stream.str(), str); + Geom::PathVector pathv3 = sp_svg_read_pathv(str3.c_str()); + + //Path can be composed of relative commands only. In this case final point + //coordinates would depend on first point position. If this happens, fall + //back to using 0,0 as first path point + if (pathv3.back().finalPoint().y() == pathv.back().finalPoint().y()) { + pathv = pathv3; + } + continous_join = true; + } +} + +SPHatchPath::View::View(Inkscape::DrawingShape *arenaitem, int key) + : arenaitem(arenaitem), key(key) +{ +} + + +/* + 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/sp-hatch-path.h b/src/sp-hatch-path.h new file mode 100644 index 000000000..ff091c6f9 --- /dev/null +++ b/src/sp-hatch-path.h @@ -0,0 +1,86 @@ +/** @file + * SVG <hatchPath> implementation + *//* + * Author: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Tomasz Boczkowski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_SP_HATCH_PATH_H +#define SEEN_SP_HATCH_PATH_H + +#include <list> +#include <stddef.h> +#include <glibmm/ustring.h> +#include <sigc++/connection.h> + +#include "svg/svg-length.h" + +namespace Inkscape { + +class Drawing; +class DrawingShape; + +} + +#define SP_HATCH_PATH(obj) (dynamic_cast<SPHatchPath*>((SPObject*)obj)) +#define SP_IS_HATCH_PATH(obj) (dynamic_cast<const SPHatchPath*>((SPObject*)obj) != NULL) + +class SPHatchPath : public SPObject { +public: + SPHatchPath(); + virtual ~SPHatchPath(); + + SVGLength offset; + + void setCurve(SPCurve *curve, bool owner); + + bool isValid() const; + + Inkscape::DrawingItem *show(Inkscape::Drawing &drawing, unsigned int key); + void hide(unsigned int key); + + void setStripExtents(unsigned int key, Geom::OptInterval const &extents); + +protected: + virtual void build(SPDocument* doc, Inkscape::XML::Node* repr); + virtual void release(); + virtual void set(unsigned int key, const gchar* value); + virtual void update(SPCtx* ctx, unsigned int flags); + +private: + struct View { + View(Inkscape::DrawingShape *arenaitem, int key); + //Do not delete arenaitem in destructor. + + Inkscape::DrawingShape *arenaitem; + Geom::OptInterval extents; + unsigned int key; + }; + typedef std::list<SPHatchPath::View>::iterator ViewIterator; + std::list<View> _display; + + gdouble _repeatLength() const; + void _updateView(View &view); + + void _readHatchPathVector(char const *str, Geom::PathVector &pathv, bool &continous_join); + + SPCurve *_curve; + bool _continuous; +}; + +#endif // SEEN_SP_HATCH_PATH_H + +/* + 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/sp-hatch.cpp b/src/sp-hatch.cpp new file mode 100644 index 000000000..cde6c86cb --- /dev/null +++ b/src/sp-hatch.cpp @@ -0,0 +1,616 @@ +/** @file + * SVG <hatch> implementation + *//* + * Author: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Tomasz Boczkowski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cstring> +#include <string> +#include <2geom/transforms.h> +#include <sigc++/functors/mem_fun.h> + +#include "svg/svg.h" +#include "display/cairo-utils.h" +#include "display/drawing-context.h" +#include "display/drawing-surface.h" +#include "display/drawing.h" +#include "display/drawing-pattern.h" +#include "attributes.h" +#include "document-private.h" +#include "uri.h" +#include "style.h" +#include "sp-hatch.h" +#include "sp-hatch-path.h" +#include "xml/repr.h" + +#include "sp-factory.h" + +namespace { +SPObject* createHatch() { + return new SPHatch(); +} + +bool hatchRegistered = SPFactory::instance().registerObject("svg:hatch", createHatch); +} + +SPHatch::SPHatch() + : SPPaintServer() +{ + this->ref = new SPHatchReference(this); + this->ref->changedSignal().connect(sigc::mem_fun(this, &SPHatch::_onRefChanged)); + + this->_hatchUnits = UNITS_OBJECTBOUNDINGBOX; + this->_hatchUnits_set = false; + + this->_hatchContentUnits = UNITS_USERSPACEONUSE; + this->_hatchContentUnits_set = false; + + this->_hatchTransform = Geom::identity(); + this->_hatchTransform_set = false; + + this->_x.unset(); + this->_y.unset(); + this->_pitch.unset(); + this->_rotate.unset(); +} + +SPHatch::~SPHatch() { +} + +void SPHatch::build(SPDocument* doc, Inkscape::XML::Node* repr) { + SPPaintServer::build(doc, repr); + + this->readAttr("hatchUnits"); + this->readAttr("hatchContentUnits"); + this->readAttr("hatchTransform"); + this->readAttr("x"); + this->readAttr("y"); + this->readAttr("pitch"); + this->readAttr("rotate"); + this->readAttr("xlink:href"); + + /* Register ourselves */ + doc->addResource("hatch", this); +} + +void SPHatch::release() { + if (this->document) { + // Unregister ourselves + this->document->removeResource("hatch", this); + } + + std::vector<SPHatchPath *> children; + _children(children); + for (ViewIterator view_iter = _display.begin(); view_iter != _display.end(); view_iter++) { + for (ChildIterator child_iter = children.begin(); child_iter != children.end(); + child_iter++) { + SPHatchPath *child = *child_iter; + child->hide(view_iter->key); + } + delete view_iter->arenaitem; + view_iter->arenaitem = NULL; + } + + if (this->ref) { + this->_modified_connection.disconnect(); + this->ref->detach(); + delete this->ref; + this->ref = NULL; + } + + SPPaintServer::release(); +} + +void SPHatch::child_added(Inkscape::XML::Node* child, Inkscape::XML::Node* ref) { + SPObject::child_added(child, ref); + + SPHatchPath *path_child = SP_HATCH_PATH(this->document->getObjectByRepr(child)); + + if (path_child) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + Inkscape::DrawingItem *ac = path_child->show(iter->arenaitem->drawing(), iter->key); + + Geom::OptInterval strip_extents = _calculateStripExtents(iter->bbox); + path_child->setStripExtents(iter->key, strip_extents); + path_child->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + if (ac) { + iter->arenaitem->prependChild(ac); + } + } + } + //FIXME: notify all hatches that refer to this child set +} + +void SPHatch::set(unsigned int key, const gchar* value) { + switch (key) { + case SP_ATTR_HATCHUNITS: + if (value) { + if (!strcmp(value, "userSpaceOnUse")) { + this->_hatchUnits = UNITS_USERSPACEONUSE; + } else { + this->_hatchUnits = UNITS_OBJECTBOUNDINGBOX; + } + + this->_hatchUnits_set = true; + } else { + this->_hatchUnits_set = false; + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_HATCHCONTENTUNITS: + if (value) { + if (!strcmp(value, "userSpaceOnUse")) { + this->_hatchContentUnits = UNITS_USERSPACEONUSE; + } else { + this->_hatchContentUnits = UNITS_OBJECTBOUNDINGBOX; + } + + this->_hatchContentUnits_set = true; + } else { + this->_hatchContentUnits_set = false; + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_HATCHTRANSFORM: { + Geom::Affine t; + + if (value && sp_svg_transform_read(value, &t)) { + this->_hatchTransform = t; + this->_hatchTransform_set = true; + } else { + this->_hatchTransform = Geom::identity(); + this->_hatchTransform_set = false; + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + } + case SP_ATTR_X: + this->_x.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_Y: + this->_y.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_PITCH: + this->_pitch.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_ROTATE: + this->_rotate.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_XLINK_HREF: + if (value && this->href == value) { + /* Href unchanged, do nothing. */ + } else { + this->href.clear(); + + if (value) { + // First, set the href field; it's only used in the "unchanged" check above. + this->href = value; + // Now do the attaching, which emits the changed signal. + if (value) { + try { + this->ref->attach(Inkscape::URI(value)); + } catch (Inkscape::BadURIException &e) { + g_warning("%s", e.what()); + this->ref->detach(); + } + } else { + this->ref->detach(); + } + } + } + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + default: + SPPaintServer::set(key, value); + break; + } +} + +bool SPHatch::_hasHatchPatchChildren(SPHatch const *hatch) { + for (SPObject const *child = hatch->firstChild(); child; child = child->getNext() ) { + if (SP_IS_HATCH_PATH(child)) { + return true; + } + } + return false; +} + +void SPHatch::_children(std::vector<SPHatchPath*>& l) { + SPHatch *src = chase_hrefs<SPHatch>(this, sigc::ptr_fun(&_hasHatchPatchChildren)); + + if (src) { + for (SPObject *child = src->firstChild(); child; child = child->getNext()) { + if (SP_IS_HATCH_PATH(child)) { + l.push_back(SP_HATCH_PATH(child)); + } + } + } +} + +void SPHatch::_children(std::vector<SPHatchPath const*>& l) const { + SPHatch const *src = chase_hrefs<SPHatch const>(this, sigc::ptr_fun(&_hasHatchPatchChildren)); + + if (src) { + for (SPObject const *child = src->firstChild(); child; child = child->getNext()) { + if (SP_IS_HATCH_PATH(child)) { + l.push_back(SP_HATCH_PATH(child)); + } + } + } +} + +/* TODO: ::remove_child and ::order_changed handles - see SPPattern */ + + +void SPHatch::update(SPCtx* ctx, unsigned int flags) { + typedef std::list<SPHatch::View>::iterator ViewIterator; + + if (flags & SP_OBJECT_MODIFIED_FLAG) { + flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + } + + flags &= SP_OBJECT_MODIFIED_CASCADE; + + std::vector<SPHatchPath *> children; + _children(children); + + for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath* child = *iter; + + sp_object_ref(child, NULL); + + for (ViewIterator view_iter = _display.begin(); view_iter != _display.end(); view_iter++) { + Geom::OptInterval strip_extents = _calculateStripExtents(view_iter->bbox); + child->setStripExtents(view_iter->key, strip_extents); + } + + if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { + + child->updateDisplay(ctx, flags); + } + + sp_object_unref(child, NULL); + } + + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + _updateView(*iter); + } +} + +void SPHatch::modified(unsigned int flags) { + if (flags & SP_OBJECT_MODIFIED_FLAG) { + flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + } + + flags &= SP_OBJECT_MODIFIED_CASCADE; + + std::vector<SPHatchPath *> children; + _children(children); + + for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPObject *child = *iter; + + sp_object_ref(child, NULL); + + if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { + child->emitModified(flags); + } + + sp_object_unref(child, NULL); + } +} + +void SPHatch::_onRefChanged(SPObject *old_ref, SPObject *ref) { + typedef std::list<SPHatch::View>::iterator ViewIterator; + + if (old_ref) { + _modified_connection.disconnect(); + } + + if (SP_IS_HATCH(ref)) { + _modified_connection = ref->connectModified(sigc::mem_fun(this, &SPHatch::_onRefModified)); + } + + if (!_hasHatchPatchChildren(this)) { + SPHatch *old_shown = NULL; + SPHatch *new_shown = NULL; + std::vector<SPHatchPath *> old_children; + std::vector<SPHatchPath *> new_children; + if (SP_IS_HATCH(old_ref)) { + old_shown = SP_HATCH(old_ref)->rootHatch(); + old_shown->_children(old_children); + } + if (SP_IS_HATCH(ref)) { + new_shown = SP_HATCH(ref)->rootHatch(); + new_shown->_children(new_children); + } + if (old_shown != new_shown) { + + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + Geom::OptInterval extents = _calculateStripExtents(iter->bbox); + + for (ChildIterator child_iter = old_children.begin(); child_iter != old_children.end(); child_iter++) { + SPHatchPath *child = *child_iter; + child->hide(iter->key); + } + for (ChildIterator child_iter = new_children.begin(); child_iter != new_children.end(); child_iter++) { + SPHatchPath *child = *child_iter; + Inkscape::DrawingItem *cai = child->show(iter->arenaitem->drawing(), iter->key); + child->setStripExtents(iter->key, extents); + child->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + if (cai) { + iter->arenaitem->appendChild(cai); + } + + } + } + } + } + + _onRefModified(ref, 0); +} + +void SPHatch::_onRefModified(SPObject */*ref*/, guint /*flags*/) { + requestModified(SP_OBJECT_MODIFIED_FLAG); + // Conditional to avoid causing infinite loop if there's a cycle in the href chain. +} + + +SPHatch *SPHatch::rootHatch() { + SPHatch *src = chase_hrefs<SPHatch>(this, sigc::ptr_fun(&_hasHatchPatchChildren)); + return src ? src : this; // document is broken, we can't get to root; but at least we can return pat which is supposedly a valid hatch +} + +// Access functions that look up fields up the chain of referenced hatchs and return the first one which is set +// FIXME: all of them must use chase_hrefs as children() and rootHatch() + +SPHatch::HatchUnits SPHatch::hatchUnits() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_hatchUnits_set) + return pat_i->_hatchUnits; + } + return _hatchUnits; +} + +SPHatch::HatchUnits SPHatch::hatchContentUnits() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_hatchContentUnits_set) + return pat_i->_hatchContentUnits; + } + return _hatchContentUnits; +} + +Geom::Affine const &SPHatch::hatchTransform() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_hatchTransform_set) + return pat_i->_hatchTransform; + } + return _hatchTransform; +} + +gdouble SPHatch::x() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_x._set) + return pat_i->_x.computed; + } + return 0; +} + +gdouble SPHatch::y() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_y._set) + return pat_i->_y.computed; + } + return 0; +} + +gdouble SPHatch::pitch() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_pitch._set) + return pat_i->_pitch.computed; + } + return 0; +} + +gdouble SPHatch::rotate() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_rotate._set) + return pat_i->_rotate.computed; + } + return 0; +} + +bool SPHatch::isValid() const { + double strip_pitch = pitch(); + if (strip_pitch <= 0) { + return false; + } + + std::vector<SPHatchPath const *> children; + _children(children); + if (children.empty()) { + return false; + } + for (ConstChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath const *child = *iter; + if (!child->isValid()) { + return false; + } + } + + return true; +} + +Inkscape::DrawingPattern *SPHatch::show(Inkscape::Drawing &drawing, unsigned int key) { + Inkscape::DrawingPattern *ai = new Inkscape::DrawingPattern(drawing); + //TODO: set some debug flag to see DrawingPattern + _display.push_front(View(ai, key)); + + std::vector<SPHatchPath *> children; + _children(children); + + for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath *child = *iter; + Inkscape::DrawingItem *cai = child->show(drawing, key); + if (cai) { + ai->appendChild(cai); + } + } + + View& view = _display.front(); + _updateView(view); + + return ai; +} + +void SPHatch::hide(unsigned int key) { + std::vector<SPHatchPath *> children; + _children(children); + + for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath *child = *iter; + child->hide(key); + } + + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + delete iter->arenaitem; + _display.erase(iter); + return; + } + } + + g_assert_not_reached(); +} + +void SPHatch::_updateView(View &view) { + Geom::OptInterval extents = _calculateStripExtents(view.bbox); + if (!extents) { + return; + } + + double tile_x = x(); + double tile_y = y(); + double tile_width = pitch(); + double tile_height = extents->max() - extents->min(); + double tile_rotate = rotate(); + double tile_render_y = extents->min(); + + if (view.bbox && (hatchUnits() == UNITS_OBJECTBOUNDINGBOX)) { + tile_x *= view.bbox->width(); + tile_y *= view.bbox->height(); + tile_width *= view.bbox->width(); + tile_height *= view.bbox->height(); + tile_render_y *= view.bbox->height(); + } + + // Pattern size in hatch space + Geom::Rect hatch_tile = Geom::Rect::from_xywh(0, tile_render_y, tile_width, tile_height); + // Content to bbox + Geom::Affine content2ps; + if (view.bbox && (hatchContentUnits() == UNITS_OBJECTBOUNDINGBOX)) { + content2ps = Geom::Affine(view.bbox->width(), 0.0, 0.0, view.bbox->height(), 0, 0); + } + + // Tile (hatch space) to user. + Geom::Affine ps2user = Geom::Translate(tile_x, tile_y) * Geom::Rotate::from_degrees(tile_rotate) * hatchTransform(); + + view.arenaitem->setChildTransform(content2ps); + view.arenaitem->setPatternToUserTransform(ps2user); + view.arenaitem->setTileRect(hatch_tile); + view.arenaitem->setStyle(this->style); +} + +//calculates strip extents in content space +Geom::OptInterval SPHatch::_calculateStripExtents(Geom::OptRect bbox) { + if (!bbox || (bbox->area() == 0)) { + return Geom::OptInterval(); + } + + double tile_x = x(); + double tile_y = y(); + double tile_rotate = rotate(); + + Geom::Affine ps2user = Geom::Translate(tile_x, tile_y) * Geom::Rotate::from_degrees(tile_rotate) * hatchTransform(); + Geom::Affine user2ps = ps2user.inverse(); + + Geom::Interval extents; + for (int i = 0; i < 4; i++) { + Geom::Point corner = bbox->corner(i); + Geom::Point corner_ps = corner * user2ps; + if (i == 0 || corner_ps.y() < extents.min()) { + extents.setMin(corner_ps.y()); + } + if (i == 0 || corner_ps.y() > extents.max()) { + extents.setMax(corner_ps.y()); + } + } + + if (hatchUnits() == UNITS_OBJECTBOUNDINGBOX) { + extents /= bbox->height(); + } + + return extents; +} + +cairo_pattern_t* SPHatch::pattern_new(cairo_t *base_ct, Geom::OptRect const &bbox, double opacity) { + //this code should not be used + //it is however required by the fact that SPPaintServer::hatch_new is pure virtual + return cairo_pattern_create_rgb(0.5, 0.5, 1.0); +} + +void SPHatch::setBBox(unsigned int key, Geom::OptRect const &bbox) { + typedef std::list<View>::iterator ViewIter; + + for (ViewIter iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + iter->bbox = bbox; + break; + } + } +} + +SPHatch::View::View(Inkscape::DrawingPattern *arenaitem, int key) + : arenaitem(arenaitem), key(key) +{ +} +/* + 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/sp-hatch.h b/src/sp-hatch.h new file mode 100644 index 000000000..170f68a7a --- /dev/null +++ b/src/sp-hatch.h @@ -0,0 +1,158 @@ +/** @file + * SVG <hatch> implementation + *//* + * Author: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Tomasz Boczkowski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_SP_HATCH_H +#define SEEN_SP_HATCH_H + +#include <list> +#include <stddef.h> +#include <glibmm/ustring.h> +#include <sigc++/connection.h> + +#include "svg/svg-length.h" +#include "svg/svg-angle.h" +#include "sp-paint-server.h" +#include "uri-references.h" + +class SPHatchReference; +class SPHatchPath; +class SPItem; + +namespace Inkscape { + +class Drawing; +class DrawingPattern; + +namespace XML { + +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 { + UNITS_USERSPACEONUSE, + UNITS_OBJECTBOUNDINGBOX + }; + + SPHatch(); + virtual ~SPHatch(); + + /* Reference (href) */ + Glib::ustring href; + SPHatchReference *ref; + + gdouble x() const; + gdouble y() const; + gdouble pitch() const; + gdouble rotate() const; + HatchUnits hatchUnits() const; + HatchUnits hatchContentUnits() const; + Geom::Affine const &hatchTransform() const; + SPHatch *rootHatch(); //TODO: const + + bool isValid() const; + + Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key); + void hide(unsigned int key); + virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity); + + void setBBox(unsigned int key, Geom::OptRect const &bbox); + +protected: + virtual void build(SPDocument* doc, Inkscape::XML::Node* repr); + virtual void release(); + virtual void child_added(Inkscape::XML::Node* child, Inkscape::XML::Node* ref); + virtual void set(unsigned int key, const gchar* value); + virtual void update(SPCtx* ctx, unsigned int flags); + virtual void modified(unsigned int flags); + +private: + struct View { + View(Inkscape::DrawingPattern *arenaitem, int key); + //Do not delete arenaitem in destructor. + + Inkscape::DrawingPattern *arenaitem; + Geom::OptRect bbox; + unsigned int key; + }; + typedef std::vector<SPHatchPath *>::iterator ChildIterator; + typedef std::vector<SPHatchPath const *>::const_iterator ConstChildIterator; + typedef std::list<View>::iterator ViewIterator; + void _updateView(View &view); + + static bool _hasHatchPatchChildren(SPHatch const* hatch); + + void _children(std::vector<SPHatchPath*>& l); + void _children(std::vector<SPHatchPath const *>& l) const; + + Geom::OptInterval _calculateStripExtents(Geom::OptRect bbox); + + /** + Gets called when the hatch is reattached to another <hatch> + */ + void _onRefChanged(SPObject *old_ref, SPObject *ref); + + /** + Gets called when the referenced <hatch> is changed + */ + void _onRefModified(SPObject *ref, guint flags); + + /* patternUnits and patternContentUnits attribute */ + HatchUnits _hatchUnits : 1; + bool _hatchUnits_set : 1; + HatchUnits _hatchContentUnits : 1; + bool _hatchContentUnits_set : 1; + /* hatchTransform attribute */ + Geom::Affine _hatchTransform; + bool _hatchTransform_set : 1; + /* Strip */ + SVGLength _x; + SVGLength _y; + SVGLength _pitch; + SVGAngle _rotate; + + sigc::connection _modified_connection; + + std::list<View> _display; +}; + + +class SPHatchReference : public Inkscape::URIReference { +public: + SPHatchReference (SPObject *obj) : URIReference(obj) {} + SPHatch *getObject() const { + return reinterpret_cast<SPHatch *>(URIReference::getObject()); + } + +protected: + virtual bool _acceptObject(SPObject *obj) const { + return SP_IS_HATCH (obj); + } +}; + +#endif // SEEN_SP_HATCH_H + +/* + 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/sp-item.cpp b/src/sp-item.cpp index edf758e7d..da6e6ea58 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -445,14 +445,14 @@ void SPItem::release() { SPObject::release(); - SPPattern *fill_pattern = SP_PATTERN(style->getFillPaintServer()); - SPPattern *stroke_pattern = SP_PATTERN(style->getStrokePaintServer()); + SPPaintServer *fill_ps = style->getFillPaintServer(); + SPPaintServer *stroke_ps = style->getStrokePaintServer(); while (item->display) { - if (fill_pattern) { - fill_pattern->hide(item->display->arenaitem->key()); + if (fill_ps) { + fill_ps->hide(item->display->arenaitem->key()); } - if (stroke_pattern) { - stroke_pattern->hide(item->display->arenaitem->key()); + if (stroke_ps) { + stroke_ps->hide(item->display->arenaitem->key()); } item->display = sp_item_view_list_remove(item->display, item->display); } @@ -600,25 +600,27 @@ void SPItem::mask_ref_changed(SPObject *old_mask, SPObject *mask, SPItem *item) } void SPItem::fill_ps_ref_changed(SPObject *old_ps, SPObject *ps, SPItem *item) { - SPPattern *old_fill_pattern = SP_PATTERN(old_ps); - if (old_fill_pattern) { + SPPaintServer *old_fill_ps = SP_PAINT_SERVER(old_ps); + if (old_fill_ps) { for (SPItemView *v =item->display; v != NULL; v = v->next) { - old_fill_pattern->hide(v->arenaitem->key()); + old_fill_ps->hide(v->arenaitem->key()); } } - SPPattern *new_fill_pattern = SP_PATTERN(ps); - if (new_fill_pattern) { + SPPaintServer *new_fill_ps = SP_PAINT_SERVER(ps); + if (new_fill_ps) { Geom::OptRect bbox = item->geometricBounds(); for (SPItemView *v = item->display; v != NULL; v = v->next) { if (!v->arenaitem->key()) { v->arenaitem->setKey(SPItem::display_key_new(3)); } - Inkscape::DrawingPattern *pi = new_fill_pattern->show( + Inkscape::DrawingPattern *pi = new_fill_ps->show( v->arenaitem->drawing(), v->arenaitem->key()); v->arenaitem->setFillPattern(pi); - new_fill_pattern->setBBox(v->arenaitem->key(), bbox); - new_fill_pattern->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + new_fill_ps->setBBox(v->arenaitem->key(), bbox); + if (pi) { + new_fill_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } } } } @@ -1133,29 +1135,33 @@ Inkscape::DrawingItem *SPItem::invoke_show(Inkscape::Drawing &drawing, unsigned mask->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } - SPPattern *fill_pattern = SP_PATTERN(style->getFillPaintServer()); - if (fill_pattern) { + SPPaintServer *fill_ps = style->getFillPaintServer(); + if (fill_ps) { if (!display->arenaitem->key()) { display->arenaitem->setKey(display_key_new(3)); } int fill_key = display->arenaitem->key(); - Inkscape::DrawingPattern *ac = fill_pattern->show(drawing, fill_key); - ai->setFillPattern(ac); - fill_pattern->setBBox(fill_key, item_bbox); - fill_pattern->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + Inkscape::DrawingPattern *ap = fill_ps->show(drawing, fill_key); + ai->setFillPattern(ap); + fill_ps->setBBox(fill_key, item_bbox); + if (ap) { + fill_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } } - SPPattern *stroke_pattern = SP_PATTERN(style->getStrokePaintServer()); - if (stroke_pattern) { + SPPaintServer *stroke_ps = style->getStrokePaintServer(); + if (stroke_ps) { if (!display->arenaitem->key()) { display->arenaitem->setKey(display_key_new(3)); } int stroke_key = display->arenaitem->key(); - Inkscape::DrawingPattern *ac = stroke_pattern->show(drawing, stroke_key); - ai->setStrokePattern(ac); - stroke_pattern->setBBox(stroke_key, item_bbox); - stroke_pattern->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + Inkscape::DrawingPattern *ap = stroke_ps->show(drawing, stroke_key); + ai->setStrokePattern(ap); + stroke_ps->setBBox(stroke_key, item_bbox); + if (ap) { + stroke_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } } ai->setData(this); ai->setItemBounds(geometricBounds()); @@ -1186,13 +1192,13 @@ void SPItem::invoke_hide(unsigned key) mask_ref->getObject()->sp_mask_hide(v->arenaitem->key()); v->arenaitem->setMask(NULL); } - SPPattern *fill_pattern = SP_PATTERN(style->getFillPaintServer()); - if (fill_pattern) { - fill_pattern->hide(v->arenaitem->key()); + SPPaintServer *fill_ps = style->getFillPaintServer(); + if (fill_ps) { + fill_ps->hide(v->arenaitem->key()); } - SPPattern *stroke_pattern = SP_PATTERN(style->getStrokePaintServer()); - if (stroke_pattern) { - stroke_pattern->hide(v->arenaitem->key()); + SPPaintServer *stroke_ps = style->getStrokePaintServer(); + if (stroke_ps) { + stroke_ps->hide(v->arenaitem->key()); } if (!ref) { display = v->next; diff --git a/src/sp-paint-server.h b/src/sp-paint-server.h index c1c8d651e..91d292424 100644 --- a/src/sp-paint-server.h +++ b/src/sp-paint-server.h @@ -17,8 +17,16 @@ #include <cairo.h> #include <2geom/rect.h> +#include <sigc++/slot.h> #include "sp-object.h" +namespace Inkscape { + +class Drawing; +class DrawingPattern; + +} + #define SP_PAINT_SERVER(obj) (dynamic_cast<SPPaintServer*>((SPObject*)obj)) #define SP_IS_PAINT_SERVER(obj) (dynamic_cast<const SPPaintServer*>((SPObject*)obj) != NULL) @@ -31,12 +39,59 @@ public: bool isSolid() const; virtual bool isValid() const; - virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity) = 0; + //There are two ways to render a paint. The simple one is to create cairo_pattern_t structure + //on demand by pattern_new method. It is used for gradients. The other one is to add elements + //representing PaintServer in NR tree. It is used by hatches and patterns. + //Either pattern new or all three methods show, hide, setBBox need to be implemented + virtual Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key) {return NULL;} + virtual void hide(unsigned int key) {}; + virtual void setBBox(unsigned int key, Geom::OptRect const &bbox) {}; + + virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity) {return NULL;} protected: bool swatch; }; +/** + * Returns the first of {src, src-\>ref-\>getObject(), + * src-\>ref-\>getObject()-\>ref-\>getObject(),...} + * for which \a match is true, or NULL if none found. + * + * The raison d'être of this routine is that it correctly handles cycles in the href chain (e.g., if + * a gradient gives itself as its href, or if each of two gradients gives the other as its href). + * + * \pre SP_IS_GRADIENT(src). + */ +template <class PaintServer> +PaintServer *chase_hrefs(PaintServer *src, sigc::slot<bool, PaintServer const *> match) { + /* Use a pair of pointers for detecting loops: p1 advances half as fast as p2. If there is a + loop, then once p1 has entered the loop, we'll detect it the next time the distance between + p1 and p2 is a multiple of the loop size. */ + PaintServer *p1 = src, *p2 = src; + bool do1 = false; + for (;;) { + if (match(p2)) { + return p2; + } + + p2 = p2->ref->getObject(); + if (!p2) { + return p2; + } + if (do1) { + p1 = p1->ref->getObject(); + } + do1 = !do1; + + if ( p2 == p1 ) { + /* We've been here before, so return NULL to indicate that no matching gradient found + * in the chain. */ + return NULL; + } + } +} + #endif // SEEN_SP_PAINT_SERVER_H /* Local Variables: diff --git a/src/style-internal.h b/src/style-internal.h index 69784199c..0b2b40001 100644 --- a/src/style-internal.h +++ b/src/style-internal.h @@ -505,8 +505,7 @@ class SPIPaint : public SPIBase { void setColor( float r, float g, float b ) {value.color.set( r, g, b ); colorSet = true;} void setColor( guint32 val ) {value.color.set( val ); colorSet = true;} void setColor( SPColor const& color ) {value.color = color; colorSet = true;} - - + void setNone() {noneSet = true; colorSet=false;} // To do: make private public: diff --git a/src/svg/CMakeLists.txt b/src/svg/CMakeLists.txt index 968287895..f9d0bc52d 100644 --- a/src/svg/CMakeLists.txt +++ b/src/svg/CMakeLists.txt @@ -7,6 +7,7 @@ set(svg_SRC strip-trailing-zeros.cpp svg-affine.cpp svg-color.cpp + svg-angle.cpp svg-length.cpp svg-path.cpp # test-stubs.cpp @@ -24,6 +25,7 @@ set(svg_SRC svg-color-test.h svg-color.h svg-icc-color.h + svg-angle.h svg-length-test.h svg-length.h svg-path-geom-test.h diff --git a/src/svg/Makefile_insert b/src/svg/Makefile_insert index cf9bf3fbb..4f82bdd76 100644 --- a/src/svg/Makefile_insert +++ b/src/svg/Makefile_insert @@ -13,6 +13,8 @@ ink_common_sources += \ svg/svg-color.cpp \ svg/svg-color.h \ svg/svg-icc-color.h \ + svg/svg-angle.cpp \ + svg/svg-angle.h \ svg/svg-length.cpp \ svg/svg-length.h \ svg/svg-path.cpp \ diff --git a/src/svg/svg-angle.cpp b/src/svg/svg-angle.cpp new file mode 100644 index 000000000..63152368e --- /dev/null +++ b/src/svg/svg-angle.cpp @@ -0,0 +1,136 @@ +/** + * \file src/svg/svg-angle.cpp + * \brief SVG angle type + */ +/* + * Authors: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 1999-2002 Lauris Kaplinski + * Copyright (C) 2000-2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cstring> +#include <string> +#include <math.h> +#include <glib.h> + +#include "svg.h" +#include "stringstream.h" +#include "svg/svg-angle.h" +#include "util/units.h" + + +static bool sp_svg_angle_read_lff(gchar const *str, SVGAngle::Unit &unit, float &val, float &computed); + +SVGAngle::SVGAngle() + : _set(false) + , unit(NONE) + , value(0) + , computed(0) +{ +} + +/* Angle */ + +bool SVGAngle::read(gchar const *str) +{ + if (!str) { + return false; + } + + SVGAngle::Unit u; + float v; + float c; + if (!sp_svg_angle_read_lff(str, u, v, c)) { + return false; + } + + _set = true; + unit = u; + value = v; + computed = c; + + return true; +} + +void SVGAngle::unset(SVGAngle::Unit u, float v, float c) { + _set = false; + unit = u; + value = v; + computed = c; +} + +void SVGAngle::readOrUnset(gchar const *str, Unit u, float v, float c) { + if (!read(str)) { + unset(u, v, c); + } +} + +static bool sp_svg_angle_read_lff(gchar const *str, SVGAngle::Unit &unit, float &val, float &computed) +{ + if (!str) { + return false; + } + + gchar const *e; + float const v = g_ascii_strtod(str, (char **) &e); + if (e == str) { + return false; + } + + if (!e[0]) { + /* Unitless (defaults to degrees)*/ + unit = SVGAngle::NONE; + val = v; + computed = v; + return true; + } else if (!g_ascii_isalnum(e[0])) { + if (g_ascii_isspace(e[0]) && e[1] && g_ascii_isalpha(e[1])) { + return false; // spaces between value and unit are not allowed + } else { + /* Unitless (defaults to degrees)*/ + unit = SVGAngle::NONE; + val = v; + computed = v; + return true; + } + } else { + if (strncmp(e, "deg", 3) == 0) { + unit = SVGAngle::DEG; + computed = v; + } else if (strncmp(e, "grad", 4) == 0) { + unit = SVGAngle::GRAD; + computed = Inkscape::Util::Quantity::convert(v, "grad", "°"); + } else if (strncmp(e, "rad", 3) == 0) { + unit = SVGAngle::RAD; + computed = Inkscape::Util::Quantity::convert(v, "rad", "°"); + } else if (strncmp(e, "turn", 4) == 0) { + unit = SVGAngle::TURN; + computed = Inkscape::Util::Quantity::convert(v, "turn", "°"); + } else { + return false; + } + return true; + } + + /* Invalid */ + return false; +} + +/* + 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/svg/svg-angle.h b/src/svg/svg-angle.h new file mode 100644 index 000000000..966491174 --- /dev/null +++ b/src/svg/svg-angle.h @@ -0,0 +1,69 @@ +#ifndef SEEN_SP_SVG_ANGLE_H +#define SEEN_SP_SVG_ANGLE_H + +/** + * \file src/svg/svg-angle.h + * \brief SVG angle type + */ +/* + * Authors: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 1999-2002 Lauris Kaplinski + * Copyright (C) 2000-2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <glib.h> + +class SVGAngle +{ +public: + SVGAngle(); + + enum Unit { + NONE, + DEG, + GRAD, + RAD, + TURN, + LAST_UNIT = TURN + }; + + // The object's value is valid / exists in SVG. + bool _set; + + // The unit of value. + Unit unit; + + // The value of this SVGAngle as found in the SVG. + float value; + + // The value in degrees. + float computed; + + float operator=(float v) { + _set = true; + unit = NONE; + value = computed = v; + return v; + } + + bool read(gchar const *str); + void unset(Unit u = NONE, float v = 0, float c = 0); + void readOrUnset(gchar const *str, Unit u = NONE, float v = 0, float c = 0); +}; + +#endif // SEEN_SP_SVG_ANGLE_H + +/* + 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 : |
