1 /**
2  * \file
3  * \brief  callback interface for SVG path data
4  *//*
5  * Copyright 2007 MenTaLguY <mental@rydia.net>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it either under the terms of the GNU Lesser General Public
9  * License version 2.1 as published by the Free Software Foundation
10  * (the "LGPL") or, at your option, under the terms of the Mozilla
11  * Public License Version 1.1 (the "MPL"). If you do not alter this
12  * notice, a recipient may use your version of this file under either
13  * the MPL or the LGPL.
14  *
15  * You should have received a copy of the LGPL along with this library
16  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  * You should have received a copy of the MPL along with this library
19  * in the file COPYING-MPL-1.1
20  *
21  * The contents of this file are subject to the Mozilla Public License
22  * Version 1.1 (the "License"); you may not use this file except in
23  * compliance with the License. You may obtain a copy of the License at
24  * http://www.mozilla.org/MPL/
25  *
26  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
27  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
28  * the specific language governing rights and limitations.
29  *
30  */
31 
32 #ifndef LIB2GEOM_SEEN_PATH_SINK_H
33 #define LIB2GEOM_SEEN_PATH_SINK_H
34 
35 #include <2geom/forward.h>
36 #include <2geom/pathvector.h>
37 #include <2geom/curves.h>
38 #include <iterator>
39 
40 namespace Geom {
41 
42 
43 /** @brief Callback interface for processing path data.
44  *
45  * PathSink provides an interface that allows one to easily write
46  * code which processes path data, for instance when converting
47  * between path formats used by different graphics libraries.
48  * It is also useful for writing algorithms which must do something
49  * for each curve in the path.
50  *
51  * To store a path in a new format, implement the virtual methods
52  * for segments in a derived class and call feed().
53  *
54  * @ingroup Paths
55  */
56 class PathSink {
57 public:
58     /** @brief Move to a different point without creating a segment.
59      * Usually starts a new subpath. */
60     virtual void moveTo(Point const &p) = 0;
61     /// Output a line segment.
62     virtual void lineTo(Point const &p) = 0;
63     /// Output a quadratic Bezier segment.
64     virtual void curveTo(Point const &c0, Point const &c1, Point const &p) = 0;
65     /// Output a cubic Bezier segment.
66     virtual void quadTo(Point const &c, Point const &p) = 0;
67     /** @brief Output an elliptical arc segment.
68      * See the EllipticalArc class for the documentation of parameters. */
69     virtual void arcTo(Coord rx, Coord ry, Coord angle,
70                        bool large_arc, bool sweep, Point const &p) = 0;
71 
72     /// Close the current path with a line segment.
73     virtual void closePath() = 0;
74     /** @brief Flush any internal state of the generator.
75      * This call should implicitly finish the current subpath.
76      * Calling this method should be idempotent, because the default
77      * implementations of path() and pathvector() will call it
78      * multiple times in a row. */
79     virtual void flush() = 0;
80     // Get the current point, e.g. where the initial point of the next segment will be.
81     //virtual Point currentPoint() const = 0;
82 
83     /** @brief Undo the last segment.
84      * This method is optional.
85      * @return true true if a segment was erased, false otherwise. */
backspace()86     virtual bool backspace() { return false; }
87 
88     // these have a default implementation
89     virtual void feed(Curve const &c, bool moveto_initial = true);
90     /** @brief Output a subpath.
91      * Calls the appropriate segment methods according to the contents
92      * of the passed subpath. You can override this function.
93      * NOTE: if you override only some of the feed() functions,
94      * always write this in the derived class:
95      * @code
96        using PathSink::feed;
97        @endcode
98      * Otherwise the remaining methods will be hidden. */
99     virtual void feed(Path const &p);
100     /** @brief Output a path.
101      * Calls feed() on each path in the vector. You can override this function. */
102     virtual void feed(PathVector const &v);
103     /// Output an axis-aligned rectangle, using moveTo, lineTo and closePath.
104     virtual void feed(Rect const &);
105     /// Output a circle as two elliptical arcs.
106     virtual void feed(Circle const &e);
107     /// Output an ellipse as two elliptical arcs.
108     virtual void feed(Ellipse const &e);
109 
~PathSink()110     virtual ~PathSink() {}
111 };
112 
113 /** @brief Store paths to an output iterator
114  * @ingroup Paths */
115 template <typename OutputIterator>
116 class PathIteratorSink : public PathSink {
117 public:
PathIteratorSink(OutputIterator out)118     explicit PathIteratorSink(OutputIterator out)
119     : _in_path(false), _out(out) {}
120 
moveTo(Point const & p)121     void moveTo(Point const &p) override {
122         flush();
123         _path.start(p);
124         _start_p = p;
125         _in_path = true;
126     }
127 //TODO: what if _in_path = false?
128 
129     /** @brief Detect if the builder is in a path and thus will NOT
130                create a new moveTo command when given the next line
131         @return true if the builder is inside a subpath.
132      */
inPath()133     bool inPath() const {
134         return _in_path;
135     }
136 
lineTo(Point const & p)137     void lineTo(Point const &p) override {
138         // check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z"
139         if (!_in_path) {
140             moveTo(_start_p);
141         }
142         _path.template appendNew<LineSegment>(p);
143     }
144 
quadTo(Point const & c,Point const & p)145     void quadTo(Point const &c, Point const &p) override {
146         // check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z"
147         if (!_in_path) {
148             moveTo(_start_p);
149         }
150         _path.template appendNew<QuadraticBezier>(c, p);
151     }
152 
curveTo(Point const & c0,Point const & c1,Point const & p)153     void curveTo(Point const &c0, Point const &c1, Point const &p) override {
154         // check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z"
155         if (!_in_path) {
156             moveTo(_start_p);
157         }
158         _path.template appendNew<CubicBezier>(c0, c1, p);
159     }
160 
arcTo(Coord rx,Coord ry,Coord angle,bool large_arc,bool sweep,Point const & p)161     void arcTo(Coord rx, Coord ry, Coord angle,
162                bool large_arc, bool sweep, Point const &p) override
163     {
164         // check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z"
165         if (!_in_path) {
166             moveTo(_start_p);
167         }
168         _path.template appendNew<EllipticalArc>(rx, ry, angle,
169                                                 large_arc, sweep, p);
170     }
171 
backspace()172     bool backspace() override
173     {
174         if (_in_path && _path.size() > 0) {
175             _path.erase_last();
176             return true;
177         }
178         return false;
179     }
180 
append(Path const & other)181     void append(Path const &other)
182     {
183         if (!_in_path) {
184             moveTo(other.initialPoint());
185         }
186         _path.append(other);
187     }
188 
closePath()189     void closePath() override {
190         if (_in_path) {
191             _path.close();
192             flush();
193         }
194     }
195 
flush()196     void flush() override {
197         if (_in_path) {
198             _in_path = false;
199             *_out++ = _path;
200             _path.clear();
201         }
202     }
203 
setStitching(bool s)204     void setStitching(bool s) {
205         _path.setStitching(s);
206     }
207 
208     using PathSink::feed;
feed(Path const & other)209     void feed(Path const &other) override
210     {
211         flush();
212         *_out++ = other;
213     }
214 
215 protected:
216     bool _in_path;
217     OutputIterator _out;
218     Path _path;
219     Point _start_p;
220 };
221 
222 typedef std::back_insert_iterator<PathVector> SubpathInserter;
223 
224 /** @brief Store paths to a PathVector
225  * @ingroup Paths */
226 class PathBuilder : public PathIteratorSink<SubpathInserter> {
227 private:
228     PathVector _pathset;
229 public:
230     /// Create a builder that outputs to an internal pathvector.
PathBuilder()231     PathBuilder() : PathIteratorSink<SubpathInserter>(SubpathInserter(_pathset)) {}
232     /// Create a builder that outputs to pathvector given by reference.
PathBuilder(PathVector & pv)233     PathBuilder(PathVector &pv) : PathIteratorSink<SubpathInserter>(SubpathInserter(pv)) {}
234 
235     /// Retrieve the path
peek()236     PathVector const &peek() const {return _pathset;}
237     /// Clear the stored path vector
clear()238     void clear() { _pathset.clear(); }
239 };
240 
241 }
242 
243 #endif
244 /*
245   Local Variables:
246   mode:c++
247   c-file-style:"stroustrup"
248   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
249   indent-tabs-mode:nil
250   fill-column:99
251   End:
252 */
253 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
254