| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** @file | ||
| 2 | * @brief PathVector - a sequence of subpaths | ||
| 3 | *//* | ||
| 4 | * Authors: | ||
| 5 | * Johan Engelen <j.b.c.engelen@alumnus.utwente.nl> | ||
| 6 | * Krzysztof KosiĆski <tweenk.pl@gmail.com> | ||
| 7 | * | ||
| 8 | * Copyright 2008-2014 authors | ||
| 9 | * | ||
| 10 | * This library is free software; you can redistribute it and/or | ||
| 11 | * modify it either under the terms of the GNU Lesser General Public | ||
| 12 | * License version 2.1 as published by the Free Software Foundation | ||
| 13 | * (the "LGPL") or, at your option, under the terms of the Mozilla | ||
| 14 | * Public License Version 1.1 (the "MPL"). If you do not alter this | ||
| 15 | * notice, a recipient may use your version of this file under either | ||
| 16 | * the MPL or the LGPL. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the LGPL along with this library | ||
| 19 | * in the file COPYING-LGPL-2.1; if not, write to the Free Software | ||
| 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 21 | * You should have received a copy of the MPL along with this library | ||
| 22 | * in the file COPYING-MPL-1.1 | ||
| 23 | * | ||
| 24 | * The contents of this file are subject to the Mozilla Public License | ||
| 25 | * Version 1.1 (the "License"); you may not use this file except in | ||
| 26 | * compliance with the License. You may obtain a copy of the License at | ||
| 27 | * http://www.mozilla.org/MPL/ | ||
| 28 | * | ||
| 29 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY | ||
| 30 | * OF ANY KIND, either express or implied. See the LGPL or the MPL for | ||
| 31 | * the specific language governing rights and limitations. | ||
| 32 | */ | ||
| 33 | |||
| 34 | #ifndef LIB2GEOM_SEEN_PATHVECTOR_H | ||
| 35 | #define LIB2GEOM_SEEN_PATHVECTOR_H | ||
| 36 | |||
| 37 | #include <optional> | ||
| 38 | #include <boost/concept/requires.hpp> | ||
| 39 | #include <boost/range/algorithm/equal.hpp> | ||
| 40 | #include <2geom/forward.h> | ||
| 41 | #include <2geom/path.h> | ||
| 42 | #include <2geom/transforms.h> | ||
| 43 | |||
| 44 | namespace Geom { | ||
| 45 | |||
| 46 | /** @brief Generalized time value in the path vector. | ||
| 47 | * | ||
| 48 | * This class exists because mapping the range of multiple curves onto the same interval | ||
| 49 | * as the curve index, we lose some precision. For instance, a path with 16 curves will | ||
| 50 | * have 4 bits less precision than a path with 1 curve. If you need high precision results | ||
| 51 | * in long paths, use this class and related methods instead of the standard methods | ||
| 52 | * pointAt(), nearestTime() and so on. | ||
| 53 | * | ||
| 54 | * @ingroup Paths */ | ||
| 55 | struct PathVectorTime | ||
| 56 | : public PathTime | ||
| 57 | , boost::totally_ordered<PathVectorTime> | ||
| 58 | { | ||
| 59 | size_type path_index; ///< Index of the path in the vector | ||
| 60 | |||
| 61 | 44 | PathVectorTime() : PathTime(0, 0), path_index(0) {} | |
| 62 | ✗ | PathVectorTime(size_type _i, size_type _c, Coord _t) | |
| 63 | ✗ | : PathTime(_c, _t), path_index(_i) {} | |
| 64 | 64 | PathVectorTime(size_type _i, PathTime const &pos) | |
| 65 | 64 | : PathTime(pos), path_index(_i) {} | |
| 66 | |||
| 67 | 111 | bool operator<(PathVectorTime const &other) const { | |
| 68 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 111 times.
|
111 | if (path_index < other.path_index) return true; |
| 69 |
1/2✓ Branch 0 taken 111 times.
✗ Branch 1 not taken.
|
111 | if (path_index == other.path_index) { |
| 70 | 111 | return static_cast<PathTime const &>(*this) < static_cast<PathTime const &>(other); | |
| 71 | } | ||
| 72 | ✗ | return false; | |
| 73 | } | ||
| 74 | 44 | bool operator==(PathVectorTime const &other) const { | |
| 75 | 44 | return path_index == other.path_index | |
| 76 |
2/4✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 44 times.
|
44 | && static_cast<PathTime const &>(*this) == static_cast<PathTime const &>(other); |
| 77 | } | ||
| 78 | |||
| 79 | 104 | PathTime const &asPathTime() const { | |
| 80 | 104 | return *static_cast<PathTime const *>(this); | |
| 81 | } | ||
| 82 | }; | ||
| 83 | |||
| 84 | ✗ | inline std::ostream &operator<<(std::ostream &os, PathVectorTime const &pvt) { | |
| 85 | ✗ | os << pvt.path_index << ": " << pvt.asPathTime(); | |
| 86 | ✗ | return os; | |
| 87 | } | ||
| 88 | |||
| 89 | typedef Intersection<PathVectorTime> PathVectorIntersection; | ||
| 90 | typedef PathVectorIntersection PVIntersection; ///< Alias to save typing | ||
| 91 | |||
| 92 | template <> | ||
| 93 | struct ShapeTraits<PathVector> { | ||
| 94 | typedef PathVectorTime TimeType; | ||
| 95 | //typedef PathVectorInterval IntervalType; | ||
| 96 | typedef PathVector AffineClosureType; | ||
| 97 | typedef PathVectorIntersection IntersectionType; | ||
| 98 | }; | ||
| 99 | |||
| 100 | /** @brief Sequence of subpaths. | ||
| 101 | * | ||
| 102 | * This class corresponds to the SVG notion of a path: | ||
| 103 | * a sequence of any number of open or closed contiguous subpaths. | ||
| 104 | * Unlike Path, this class is closed under boolean operations. | ||
| 105 | * | ||
| 106 | * If you want to represent an arbitrary shape, this is the best class to use. | ||
| 107 | * Shapes with a boundary that is composed of only a single contiguous | ||
| 108 | * component can be represented with Path instead. | ||
| 109 | * | ||
| 110 | * @ingroup Paths | ||
| 111 | */ | ||
| 112 | class PathVector | ||
| 113 | : MultipliableNoncommutative< PathVector, Affine | ||
| 114 | , MultipliableNoncommutative< PathVector, Translate | ||
| 115 | , MultipliableNoncommutative< PathVector, Scale | ||
| 116 | , MultipliableNoncommutative< PathVector, Rotate | ||
| 117 | , MultipliableNoncommutative< PathVector, HShear | ||
| 118 | , MultipliableNoncommutative< PathVector, VShear | ||
| 119 | , MultipliableNoncommutative< PathVector, Zoom | ||
| 120 | , boost::equality_comparable< PathVector | ||
| 121 | > > > > > > > > | ||
| 122 | { | ||
| 123 | typedef std::vector<Path> Sequence; | ||
| 124 | public: | ||
| 125 | typedef PathVectorTime Position; | ||
| 126 | typedef Sequence::iterator iterator; | ||
| 127 | typedef Sequence::const_iterator const_iterator; | ||
| 128 | typedef Sequence::size_type size_type; | ||
| 129 | typedef Path value_type; | ||
| 130 | typedef Path &reference; | ||
| 131 | typedef Path const &const_reference; | ||
| 132 | typedef Path *pointer; | ||
| 133 | typedef std::ptrdiff_t difference_type; | ||
| 134 | |||
| 135 | 1215 | PathVector() {} | |
| 136 | ✗ | PathVector(Path const &p) | |
| 137 | ✗ | : _data(1, p) | |
| 138 | ✗ | {} | |
| 139 | template <typename InputIter> | ||
| 140 | ✗ | PathVector(InputIter first, InputIter last) | |
| 141 | ✗ | : _data(first, last) | |
| 142 | ✗ | {} | |
| 143 | |||
| 144 | /// Check whether the vector contains any paths. | ||
| 145 | 18054 | bool empty() const { return _data.empty(); } | |
| 146 | /// Get the number of paths in the vector. | ||
| 147 | ✗ | size_type size() const { return _data.size(); } | |
| 148 | /// Get the total number of curves in the vector. | ||
| 149 | size_type curveCount() const; | ||
| 150 | |||
| 151 | 18914 | iterator begin() { return _data.begin(); } | |
| 152 | 18914 | iterator end() { return _data.end(); } | |
| 153 | 951 | const_iterator begin() const { return _data.begin(); } | |
| 154 | 923 | const_iterator end() const { return _data.end(); } | |
| 155 | 6 | Path &operator[](size_type index) { | |
| 156 | 6 | return _data[index]; | |
| 157 | } | ||
| 158 | 12 | Path const &operator[](size_type index) const { | |
| 159 | 12 | return _data[index]; | |
| 160 | } | ||
| 161 | Path &at(size_type index) { | ||
| 162 | return _data.at(index); | ||
| 163 | } | ||
| 164 | ✗ | Path const &at(size_type index) const { | |
| 165 | ✗ | return _data.at(index); | |
| 166 | } | ||
| 167 | Path &front() { return _data.front(); } | ||
| 168 | 28 | Path const &front() const { return _data.front(); } | |
| 169 | 157 | Path &back() { return _data.back(); } | |
| 170 | Path const &back() const { return _data.back(); } | ||
| 171 | /// Append a path at the end. | ||
| 172 | 165561 | void push_back(Path const &path) { | |
| 173 | 165561 | _data.push_back(path); | |
| 174 | 165561 | } | |
| 175 | /// Remove the last path. | ||
| 176 | void pop_back() { | ||
| 177 | _data.pop_back(); | ||
| 178 | } | ||
| 179 | ✗ | iterator insert(iterator pos, Path const &p) { | |
| 180 | ✗ | return _data.insert(pos, p); | |
| 181 | } | ||
| 182 | template <typename InputIter> | ||
| 183 | ✗ | void insert(iterator out, InputIter first, InputIter last) { | |
| 184 | ✗ | _data.insert(out, first, last); | |
| 185 | ✗ | } | |
| 186 | /// Remove a path from the vector. | ||
| 187 | ✗ | iterator erase(iterator i) { | |
| 188 | ✗ | return _data.erase(i); | |
| 189 | } | ||
| 190 | /// Remove a range of paths from the vector. | ||
| 191 | ✗ | iterator erase(iterator first, iterator last) { | |
| 192 | ✗ | return _data.erase(first, last); | |
| 193 | } | ||
| 194 | /// Remove all paths from the vector. | ||
| 195 | void clear() { _data.clear(); } | ||
| 196 | /** @brief Change the number of paths. | ||
| 197 | * If the vector size increases, it is passed with paths that contain only | ||
| 198 | * a degenerate closing segment at (0,0). */ | ||
| 199 | void resize(size_type n) { _data.resize(n); } | ||
| 200 | /** @brief Reverse the direction of paths in the vector. | ||
| 201 | * @param reverse_paths If this is true, the order of paths is reversed as well; | ||
| 202 | * otherwise each path is reversed, but their order in the | ||
| 203 | * PathVector stays the same */ | ||
| 204 | void reverse(bool reverse_paths = true); | ||
| 205 | /** @brief Get a new vector with reversed direction of paths. | ||
| 206 | * @param reverse_paths If this is true, the order of paths is reversed as well; | ||
| 207 | * otherwise each path is reversed, but their order in the | ||
| 208 | * PathVector stays the same */ | ||
| 209 | PathVector reversed(bool reverse_paths = true) const; | ||
| 210 | |||
| 211 | /// Get the range of allowed time values. | ||
| 212 | Interval timeRange() const { | ||
| 213 | Interval ret(0, curveCount()); return ret; | ||
| 214 | } | ||
| 215 | /** @brief Get the first point in the first path of the vector. | ||
| 216 | * This method will throw an exception if the vector doesn't contain any paths. */ | ||
| 217 | Point initialPoint() const { | ||
| 218 | return _data.front().initialPoint(); | ||
| 219 | } | ||
| 220 | /** @brief Get the last point in the last path of the vector. | ||
| 221 | * This method will throw an exception if the vector doesn't contain any paths. */ | ||
| 222 | Point finalPoint() const { | ||
| 223 | return _data.back().finalPoint(); | ||
| 224 | } | ||
| 225 | /** @brief Get all intersections of the path-vector with itself. This includes both | ||
| 226 | * self-intersections of constituent paths and intersections between different paths. */ | ||
| 227 | std::vector<PathVectorIntersection> intersectSelf(Coord precision = EPSILON) const; | ||
| 228 | Path &pathAt(Coord t, Coord *rest = NULL); | ||
| 229 | Path const &pathAt(Coord t, Coord *rest = NULL) const; | ||
| 230 | Curve const &curveAt(Coord t, Coord *rest = NULL) const; | ||
| 231 | Coord valueAt(Coord t, Dim2 d) const; | ||
| 232 | Point pointAt(Coord t) const; | ||
| 233 | |||
| 234 | Path &pathAt(PathVectorTime const &pos) { | ||
| 235 | return const_cast<Path &>(static_cast<PathVector const*>(this)->pathAt(pos)); | ||
| 236 | } | ||
| 237 | Path const &pathAt(PathVectorTime const &pos) const { | ||
| 238 | return at(pos.path_index); | ||
| 239 | } | ||
| 240 | Curve const &curveAt(PathVectorTime const &pos) const { | ||
| 241 | return at(pos.path_index).at(pos.curve_index); | ||
| 242 | } | ||
| 243 | Point pointAt(PathVectorTime const &pos) const { | ||
| 244 | return at(pos.path_index).at(pos.curve_index).pointAt(pos.t); | ||
| 245 | } | ||
| 246 | Coord valueAt(PathVectorTime const &pos, Dim2 d) const { | ||
| 247 | return at(pos.path_index).at(pos.curve_index).valueAt(pos.t, d); | ||
| 248 | } | ||
| 249 | |||
| 250 | OptRect boundsFast() const; | ||
| 251 | OptRect boundsExact() const; | ||
| 252 | |||
| 253 | template <typename T> | ||
| 254 | BOOST_CONCEPT_REQUIRES(((TransformConcept<T>)), (PathVector &)) | ||
| 255 | 18000 | operator*=(T const &t) { | |
| 256 |
0/2✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
18000 | if (empty()) return *this; |
| 257 |
0/2✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
36000 | for (auto & i : *this) { |
| 258 |
0/2✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
18000 | i *= t; |
| 259 | } | ||
| 260 | 18000 | return *this; | |
| 261 | } | ||
| 262 | |||
| 263 | bool operator==(PathVector const &other) const { | ||
| 264 | return boost::range::equal(_data, other._data); | ||
| 265 | } | ||
| 266 | |||
| 267 | void snapEnds(Coord precision = EPSILON); | ||
| 268 | |||
| 269 | std::vector<PVIntersection> intersect(PathVector const &other, Coord precision = EPSILON) const; | ||
| 270 | |||
| 271 | /** @brief Determine the winding number at the specified point. | ||
| 272 | * This is simply the sum of winding numbers for constituent paths. */ | ||
| 273 | int winding(Point const &p) const; | ||
| 274 | |||
| 275 | std::optional<PathVectorTime> nearestTime(Point const &p, Coord *dist = NULL) const; | ||
| 276 | std::vector<PathVectorTime> allNearestTimes(Point const &p, Coord *dist = NULL) const; | ||
| 277 | |||
| 278 | std::vector<Point> nodes() const; | ||
| 279 | |||
| 280 | private: | ||
| 281 | PathVectorTime _factorTime(Coord t) const; | ||
| 282 | |||
| 283 | Sequence _data; | ||
| 284 | }; | ||
| 285 | |||
| 286 | inline OptRect bounds_fast(PathVector const &pv) { return pv.boundsFast(); } | ||
| 287 | inline OptRect bounds_exact(PathVector const &pv) { return pv.boundsExact(); } | ||
| 288 | |||
| 289 | std::ostream &operator<<(std::ostream &out, PathVector const &pv); | ||
| 290 | |||
| 291 | } // end namespace Geom | ||
| 292 | |||
| 293 | #endif // LIB2GEOM_SEEN_PATHVECTOR_H | ||
| 294 | |||
| 295 | /* | ||
| 296 | Local Variables: | ||
| 297 | mode:c++ | ||
| 298 | c-file-style:"stroustrup" | ||
| 299 | c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) | ||
| 300 | indent-tabs-mode:nil | ||
| 301 | fill-column:99 | ||
| 302 | End: | ||
| 303 | */ | ||
| 304 | // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : | ||
| 305 |