summaryrefslogtreecommitdiffstats
path: root/src/2geom
diff options
context:
space:
mode:
authorKrzysztof Kosi??ski <tweenk.pl@gmail.com>2015-04-27 23:39:29 +0000
committerKrzysztof Kosiński <tweenk.pl@gmail.com>2015-04-27 23:39:29 +0000
commitc883d7627a479c8c5b6a9f77b9841fa5631572ad (patch)
treefba1186e26a8cc85a1b0728425bef6f2e9aeccd9 /src/2geom
parentextensions. ink2canvas.py - do not parse html comments. (Bug 1446204) (diff)
downloadinkscape-c883d7627a479c8c5b6a9f77b9841fa5631572ad.tar.gz
inkscape-c883d7627a479c8c5b6a9f77b9841fa5631572ad.zip
2Geom sync - initial commit
(bzr r14059.2.1)
Diffstat (limited to 'src/2geom')
-rw-r--r--src/2geom/2geom.h6
-rw-r--r--src/2geom/CMakeLists.txt9
-rw-r--r--src/2geom/Makefile_insert25
-rw-r--r--src/2geom/affine.cpp30
-rw-r--r--src/2geom/affine.h6
-rw-r--r--src/2geom/angle.h4
-rw-r--r--src/2geom/basic-intersection.cpp177
-rw-r--r--src/2geom/basic-intersection.h94
-rw-r--r--src/2geom/bezier-clipping.cpp345
-rw-r--r--src/2geom/bezier-curve.cpp125
-rw-r--r--src/2geom/bezier-curve.h108
-rw-r--r--src/2geom/bezier-to-sbasis.h10
-rw-r--r--src/2geom/bezier-utils.cpp6
-rw-r--r--src/2geom/bezier.cpp324
-rw-r--r--src/2geom/bezier.h505
-rw-r--r--src/2geom/cairo-path-sink.cpp123
-rw-r--r--src/2geom/cairo-path-sink.h (renamed from src/2geom/conjugate_gradient.h)59
-rw-r--r--src/2geom/circle.cpp63
-rw-r--r--src/2geom/circle.h105
-rw-r--r--src/2geom/circulator.h9
-rw-r--r--src/2geom/concepts.h37
-rw-r--r--src/2geom/conic_section_clipper.h11
-rw-r--r--src/2geom/conic_section_clipper_cr.h11
-rw-r--r--src/2geom/conic_section_clipper_impl.cpp17
-rw-r--r--src/2geom/conic_section_clipper_impl.h22
-rw-r--r--src/2geom/conicsec.cpp81
-rw-r--r--src/2geom/conicsec.h22
-rw-r--r--src/2geom/conjugate_gradient.cpp143
-rw-r--r--src/2geom/convex-cover.h204
-rw-r--r--src/2geom/convex-hull.cpp (renamed from src/2geom/convex-cover.cpp)462
-rw-r--r--src/2geom/convex-hull.h346
-rw-r--r--src/2geom/coord.cpp3688
-rw-r--r--src/2geom/coord.h79
-rw-r--r--src/2geom/crossing.cpp2
-rw-r--r--src/2geom/crossing.h27
-rw-r--r--src/2geom/curve.cpp157
-rw-r--r--src/2geom/curve.h120
-rw-r--r--src/2geom/curves.h10
-rw-r--r--src/2geom/d2-sbasis.cpp4
-rw-r--r--src/2geom/d2-sbasis.h10
-rw-r--r--src/2geom/d2.h51
-rw-r--r--src/2geom/ellipse.cpp231
-rw-r--r--src/2geom/ellipse.h149
-rw-r--r--src/2geom/elliptical-arc.cpp76
-rw-r--r--src/2geom/elliptical-arc.h31
-rw-r--r--src/2geom/exception.h4
-rw-r--r--src/2geom/forward.h16
-rw-r--r--src/2geom/generic-interval.h29
-rw-r--r--src/2geom/generic-rect.h36
-rw-r--r--src/2geom/geom.cpp13
-rw-r--r--src/2geom/geom.h42
-rw-r--r--src/2geom/hvlinesegment.h283
-rw-r--r--src/2geom/int-point.h47
-rw-r--r--src/2geom/intersection-graph.cpp296
-rw-r--r--src/2geom/intersection-graph.h106
-rw-r--r--src/2geom/intersection.h117
-rw-r--r--src/2geom/interval.h39
-rw-r--r--src/2geom/line.cpp375
-rw-r--r--src/2geom/line.h368
-rw-r--r--src/2geom/linear.h66
-rw-r--r--src/2geom/nearest-time.cpp (renamed from src/2geom/nearest-point.cpp)211
-rw-r--r--src/2geom/nearest-time.h (renamed from src/2geom/nearest-point.h)86
-rw-r--r--src/2geom/numeric/fitting-model.h4
-rw-r--r--src/2geom/numeric/matrix.h20
-rw-r--r--src/2geom/numeric/symmetric-matrix-fs-operation.h7
-rw-r--r--src/2geom/numeric/symmetric-matrix-fs-trace.h8
-rw-r--r--src/2geom/numeric/symmetric-matrix-fs.h5
-rw-r--r--src/2geom/numeric/vector.h13
-rw-r--r--src/2geom/ord.h9
-rw-r--r--src/2geom/path-intersection.cpp107
-rw-r--r--src/2geom/path-intersection.h18
-rw-r--r--src/2geom/path-sink.cpp73
-rw-r--r--src/2geom/path-sink.h98
-rw-r--r--src/2geom/path.cpp1236
-rw-r--r--src/2geom/path.h1409
-rw-r--r--src/2geom/pathvector.cpp214
-rw-r--r--src/2geom/pathvector.h318
-rw-r--r--src/2geom/piecewise.h13
-rw-r--r--src/2geom/point.cpp6
-rw-r--r--src/2geom/point.h170
-rw-r--r--src/2geom/quadtree.cpp4
-rw-r--r--src/2geom/quadtree.h6
-rw-r--r--src/2geom/ray.h4
-rw-r--r--src/2geom/rect.cpp11
-rw-r--r--src/2geom/rect.h8
-rw-r--r--src/2geom/recursive-bezier-intersection.cpp5
-rw-r--r--src/2geom/region.cpp45
-rw-r--r--src/2geom/region.h130
-rw-r--r--src/2geom/sbasis-2d.cpp2
-rw-r--r--src/2geom/sbasis-2d.h8
-rw-r--r--src/2geom/sbasis-curve.h25
-rw-r--r--src/2geom/sbasis-geometric.h44
-rw-r--r--src/2geom/sbasis-math.h11
-rw-r--r--src/2geom/sbasis-poly.h23
-rw-r--r--src/2geom/sbasis-roots.cpp52
-rw-r--r--src/2geom/sbasis-to-bezier.cpp42
-rw-r--r--src/2geom/sbasis-to-bezier.h14
-rw-r--r--src/2geom/sbasis.cpp10
-rw-r--r--src/2geom/sbasis.h115
-rw-r--r--src/2geom/shape.cpp689
-rw-r--r--src/2geom/shape.h148
-rw-r--r--src/2geom/solve-bezier-one-d.cpp30
-rw-r--r--src/2geom/solve-bezier-parametric.cpp51
-rw-r--r--src/2geom/solve-bezier.cpp57
-rw-r--r--src/2geom/solver.h11
-rw-r--r--src/2geom/svg-elliptical-arc.cpp2
-rw-r--r--src/2geom/svg-elliptical-arc.h26
-rw-r--r--src/2geom/svg-path-parser.cpp2152
-rw-r--r--src/2geom/svg-path-parser.h137
-rw-r--r--src/2geom/svg-path-writer.cpp296
-rw-r--r--src/2geom/svg-path-writer.h122
-rw-r--r--src/2geom/sweep.cpp8
-rw-r--r--src/2geom/sweep.h4
-rw-r--r--src/2geom/toposweep.cpp16
-rw-r--r--src/2geom/toposweep.h18
-rw-r--r--src/2geom/transforms.h3
-rw-r--r--src/2geom/utils.h18
-rw-r--r--src/2geom/viewbox.cpp133
-rw-r--r--src/2geom/viewbox.h102
119 files changed, 12004 insertions, 6564 deletions
diff --git a/src/2geom/2geom.h b/src/2geom/2geom.h
index 000f3423d..813e243b3 100644
--- a/src/2geom/2geom.h
+++ b/src/2geom/2geom.h
@@ -31,8 +31,8 @@
* the specific language governing rights and limitations.
*/
-#ifndef SEEN_LIB2GEOM_2GEOM_H
-#define SEEN_LIB2GEOM_2GEOM_H
+#ifndef LIB2GEOM_SEEN_2GEOM_H
+#define LIB2GEOM_SEEN_2GEOM_H
#include <2geom/forward.h>
@@ -62,7 +62,7 @@
#include <2geom/math-utils.h>
#include <2geom/utils.h>
-#endif // SEEN_LIB2GEOM_HEADER_H
+#endif // LIB2GEOM_SEEN_2GEOM_H
/*
Local Variables:
mode:c++
diff --git a/src/2geom/CMakeLists.txt b/src/2geom/CMakeLists.txt
index eeaecaa39..119c7aa71 100644
--- a/src/2geom/CMakeLists.txt
+++ b/src/2geom/CMakeLists.txt
@@ -2,23 +2,27 @@
set(2geom_SRC
affine.cpp
basic-intersection.cpp
+ bezier.cpp
bezier-clipping.cpp
bezier-curve.cpp
bezier-utils.cpp
+ cairo-path-sink.cpp
circle-circle.cpp
circle.cpp
# conic_section_clipper_impl.cpp
# conicsec.cpp
conjugate_gradient.cpp
- convex-cover.cpp
+ convex-hull.cpp
+ coord.cpp
crossing.cpp
curve.cpp
d2-sbasis.cpp
ellipse.cpp
elliptical-arc.cpp
geom.cpp
+ intersection-graph.cpp
line.cpp
- nearest-point.cpp
+ nearest-time.cpp
numeric/matrix.cpp
path-intersection.cpp
path-sink.cpp
@@ -48,6 +52,7 @@ set(2geom_SRC
toposweep.cpp
transforms.cpp
utils.cpp
+ viewbox.cpp
# -------
diff --git a/src/2geom/Makefile_insert b/src/2geom/Makefile_insert
index e77f413cb..86e64333d 100644
--- a/src/2geom/Makefile_insert
+++ b/src/2geom/Makefile_insert
@@ -15,10 +15,13 @@
2geom/bezier-clipping.cpp \
2geom/bezier-curve.cpp \
2geom/bezier-curve.h \
+ 2geom/bezier.cpp \
2geom/bezier.h \
2geom/bezier-to-sbasis.h \
2geom/bezier-utils.cpp \
2geom/bezier-utils.h \
+ 2geom/cairo-path-sink.cpp \
+ 2geom/cairo-path-sink.h \
2geom/choose.h \
2geom/circle-circle.cpp \
2geom/circle.cpp \
@@ -32,10 +35,9 @@
2geom/conic_section_clipper.h \
2geom/conic_section_clipper_impl.cpp \
2geom/conic_section_clipper_impl.h \
- 2geom/conjugate_gradient.cpp \
- 2geom/conjugate_gradient.h \
- 2geom/convex-cover.cpp \
- 2geom/convex-cover.h \
+ 2geom/convex-hull.cpp \
+ 2geom/convex-hull.h \
+ 2geom/coord.cpp \
2geom/coord.h \
2geom/crossing.cpp \
2geom/crossing.h \
@@ -56,6 +58,9 @@
2geom/geom.cpp \
2geom/geom.h \
2geom/hvlinesegment.h \
+ 2geom/intersection.h \
+ 2geom/intersection-graph.cpp \
+ 2geom/intersection-graph.h \
2geom/interval.h \
2geom/int-interval.h \
2geom/int-point.h \
@@ -64,8 +69,8 @@
2geom/line.cpp \
2geom/line.h \
2geom/math-utils.h \
- 2geom/nearest-point.cpp \
- 2geom/nearest-point.h \
+ 2geom/nearest-time.cpp \
+ 2geom/nearest-time.h \
2geom/ord.h \
2geom/path.cpp \
2geom/path.h \
@@ -88,8 +93,6 @@
2geom/rect.cpp \
2geom/rect.h \
2geom/recursive-bezier-intersection.cpp \
- 2geom/region.cpp \
- 2geom/region.h \
2geom/sbasis-2d.cpp \
2geom/sbasis-2d.h \
2geom/sbasis.cpp \
@@ -104,8 +107,6 @@
2geom/sbasis-roots.cpp \
2geom/sbasis-to-bezier.cpp \
2geom/sbasis-to-bezier.h \
- 2geom/shape.cpp \
- 2geom/shape.h \
2geom/solve-bezier.cpp \
2geom/solve-bezier-one-d.cpp \
2geom/solve-bezier-parametric.cpp \
@@ -114,6 +115,8 @@
2geom/svg-elliptical-arc.h \
2geom/svg-path-parser.cpp \
2geom/svg-path-parser.h \
+ 2geom/svg-path-writer.cpp \
+ 2geom/svg-path-writer.h \
2geom/sweep.cpp \
2geom/sweep.h \
2geom/toposweep.cpp \
@@ -122,6 +125,8 @@
2geom/transforms.h \
2geom/utils.cpp \
2geom/utils.h \
+ 2geom/viewbox.cpp \
+ 2geom/viewbox.h \
2geom/numeric/fitting-model.h \
2geom/numeric/fitting-tool.h \
2geom/numeric/linear_system.h \
diff --git a/src/2geom/affine.cpp b/src/2geom/affine.cpp
index 738d0fc48..e9ca5748e 100644
--- a/src/2geom/affine.cpp
+++ b/src/2geom/affine.cpp
@@ -222,6 +222,29 @@ bool Affine::isNonzeroRotation(Coord eps) const {
are_near(_c[0]*_c[0] + _c[1]*_c[1], 1.0, eps);
}
+/** @brief Check whether this matrix represents a non-zero rotation about any point.
+ * @param eps Numerical tolerance
+ * @return True iff the matrix is of the form
+ * \f$\left[\begin{array}{ccc}
+ a & b & 0 \\
+ -b & a & 0 \\
+ c & d & 1 \end{array}\right]\f$, \f$a^2 + b^2 = 1\f$ and \f$a \neq 1\f$. */
+bool Affine::isNonzeroNonpureRotation(Coord eps) const {
+ return !are_near(_c[0], 1.0, eps) &&
+ are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps) &&
+ are_near(_c[0]*_c[0] + _c[1]*_c[1], 1.0, eps);
+}
+
+/** @brief For a (possibly non-pure) non-zero-rotation matrix, calculate the rotation center.
+ * @pre The matrix must be a non-zero-rotation matrix to prevent division by zero, see isNonzeroNonpureRotation().
+ * @return The rotation center x, the solution to the equation
+ * \f$A x = x\f$. */
+Point Affine::rotationCenter() const {
+ Coord x = (_c[2]*_c[5]+_c[4]-_c[4]*_c[3]) / (1-_c[3]-_c[0]+_c[0]*_c[3]-_c[2]*_c[1]);
+ Coord y = (_c[1]*x + _c[5]) / (1 - _c[3]);
+ return Point(x,y);
+};
+
/** @brief Check whether this matrix represents pure horizontal shearing.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
@@ -342,8 +365,7 @@ bool Affine::preservesDistances(Coord eps) const
/** @brief Check whether this transformation flips objects.
* A transformation flips objects if it has a negative scaling component. */
bool Affine::flips() const {
- // TODO shouldn't this be det() < 0?
- return cross(xAxis(), yAxis()) > 0;
+ return det() < 0;
}
/** @brief Check whether this matrix is singular.
@@ -356,7 +378,7 @@ bool Affine::isSingular(Coord eps) const {
}
/** @brief Compute the inverse matrix.
- * Inverse is a matrix (denoted \f$A^{-1}) such that \f$AA^{-1} = A^{-1}A = I\f$.
+ * Inverse is a matrix (denoted \f$A^{-1}\f$) such that \f$AA^{-1} = A^{-1}A = I\f$.
* Singular matrices have no inverse (for example a matrix that has two of its columns equal).
* For such matrices, the identity matrix will be returned instead.
* @param eps Numerical tolerance
@@ -369,7 +391,7 @@ Affine Affine::inverse() const {
fabs(_c[2]) + fabs(_c[3])); // a random matrix norm (either l1 or linfty
if(mx > 0) {
Geom::Coord const determ = det();
- if (!rel_error_bound(determ, mx*mx)) {
+ if (!rel_error_bound(std::sqrt(fabs(determ)), mx)) {
Geom::Coord const ideterm = 1.0 / (determ);
d._c[0] = _c[3] * ideterm;
diff --git a/src/2geom/affine.h b/src/2geom/affine.h
index af7b39360..470d5fc40 100644
--- a/src/2geom/affine.h
+++ b/src/2geom/affine.h
@@ -11,8 +11,8 @@
* This code is in public domain.
*/
-#ifndef SEEN_LIB2GEOM_AFFINE_H
-#define SEEN_LIB2GEOM_AFFINE_H
+#ifndef LIB2GEOM_SEEN_AFFINE_H
+#define LIB2GEOM_SEEN_AFFINE_H
#include <boost/operators.hpp>
#include <2geom/forward.h>
@@ -150,6 +150,8 @@ public:
bool isNonzeroScale(Coord eps = EPSILON) const;
bool isNonzeroUniformScale(Coord eps = EPSILON) const;
bool isNonzeroRotation(Coord eps = EPSILON) const;
+ bool isNonzeroNonpureRotation(Coord eps = EPSILON) const;
+ Point rotationCenter() const;
bool isNonzeroHShear(Coord eps = EPSILON) const;
bool isNonzeroVShear(Coord eps = EPSILON) const;
diff --git a/src/2geom/angle.h b/src/2geom/angle.h
index 1faf63c3f..77ecd5eb4 100644
--- a/src/2geom/angle.h
+++ b/src/2geom/angle.h
@@ -69,8 +69,8 @@ class Angle
public:
Angle() : _angle(0) {} //added default constructor because of cython
Angle(Coord v) : _angle(v) { _normalize(); } // this can be called implicitly
- explicit Angle(Point p) : _angle(atan2(p)) { _normalize(); }
- Angle(Point a, Point b) : _angle(angle_between(a, b)) { _normalize(); }
+ explicit Angle(Point const &p) : _angle(atan2(p)) { _normalize(); }
+ Angle(Point const &a, Point const &b) : _angle(angle_between(a, b)) { _normalize(); }
operator Coord() const { return radians(); }
Angle &operator+=(Angle const &o) {
_angle += o._angle;
diff --git a/src/2geom/basic-intersection.cpp b/src/2geom/basic-intersection.cpp
index 379ec597c..54374e282 100644
--- a/src/2geom/basic-intersection.cpp
+++ b/src/2geom/basic-intersection.cpp
@@ -1,3 +1,38 @@
+/** @file
+ * @brief Basic intersection routines
+ *//*
+ * Authors:
+ * Nathan Hurst <njh@njhurst.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Jean-François Barraud <jf.barraud@gmail.com>
+ *
+ * Copyright 2008-2009 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
#include <2geom/basic-intersection.h>
#include <2geom/sbasis-to-bezier.h>
#include <2geom/exception.h>
@@ -33,24 +68,35 @@ namespace Geom {
//#else
namespace detail{ namespace bezier_clipping {
-void portion (std::vector<Point> & B, Interval const& I);
+void portion(std::vector<Point> &B, Interval const &I);
+void derivative(std::vector<Point> &D, std::vector<Point> const &B);
}; };
void find_intersections(std::vector<std::pair<double, double> > &xs,
+ D2<Bezier> const & A,
+ D2<Bezier> const & B,
+ double precision)
+{
+ find_intersections_bezier_clipping(xs, bezier_points(A), bezier_points(B), precision);
+}
+
+void find_intersections(std::vector<std::pair<double, double> > &xs,
D2<SBasis> const & A,
- D2<SBasis> const & B) {
+ D2<SBasis> const & B,
+ double precision)
+{
vector<Point> BezA, BezB;
sbasis_to_bezier(BezA, A);
sbasis_to_bezier(BezB, B);
-
- xs.clear();
- find_intersections_bezier_clipping(xs, BezA, BezB);
+ find_intersections_bezier_clipping(xs, BezA, BezB, precision);
}
+
void find_intersections(std::vector< std::pair<double, double> > & xs,
- std::vector<Point> const& A,
- std::vector<Point> const& B,
- double precision){
+ std::vector<Point> const& A,
+ std::vector<Point> const& B,
+ double precision)
+{
find_intersections_bezier_clipping(xs, A, B, precision);
}
@@ -61,6 +107,7 @@ void find_intersections(std::vector< std::pair<double, double> > & xs,
* Temporary storage is minimized by using part of the storage for the result
* to hold an intermediate value until it is no longer needed.
*/
+// TODO replace with Bezier method
void split(vector<Point> const &p, double t,
vector<Point> &left, vector<Point> &right) {
const unsigned sz = p.size();
@@ -88,44 +135,48 @@ void split(vector<Point> const &p, double t,
}
-void
-find_self_intersections(std::vector<std::pair<double, double> > &xs,
- D2<SBasis> const & A) {
- vector<double> dr = roots(derivative(A[X]));
+
+void find_self_intersections(std::vector<std::pair<double, double> > &xs,
+ D2<Bezier> const &A,
+ double precision)
+{
+ std::vector<double> dr = derivative(A[X]).roots();
{
- vector<double> dyr = roots(derivative(A[Y]));
+ std::vector<double> dyr = derivative(A[Y]).roots();
dr.insert(dr.begin(), dyr.begin(), dyr.end());
}
dr.push_back(0);
dr.push_back(1);
// We want to be sure that we have no empty segments
- sort(dr.begin(), dr.end());
- vector<double>::iterator new_end = unique(dr.begin(), dr.end());
+ std::sort(dr.begin(), dr.end());
+ std::vector<double>::iterator new_end = std::unique(dr.begin(), dr.end());
dr.resize( new_end - dr.begin() );
- vector<vector<Point> > pieces;
- {
- vector<Point> in, l, r;
- sbasis_to_bezier(in, A);
+ std::vector< D2<Bezier> > pieces;
+ for (unsigned i = 0; i < dr.size() - 1; ++i) {
+ pieces.push_back(portion(A, dr[i], dr[i+1]));
+ }
+ /*{
+ vector<Point> l, r, in = A;
for(unsigned i = 0; i < dr.size()-1; i++) {
split(in, (dr[i+1]-dr[i]) / (1 - dr[i]), l, r);
pieces.push_back(l);
in = r;
}
- }
+ }*/
for(unsigned i = 0; i < dr.size()-1; i++) {
for(unsigned j = i+1; j < dr.size()-1; j++) {
std::vector<std::pair<double, double> > section;
- find_intersections( section, pieces[i], pieces[j]);
+ find_intersections(section, pieces[i], pieces[j], precision);
for(unsigned k = 0; k < section.size(); k++) {
double l = section[k].first;
double r = section[k].second;
// XXX: This condition will prune out false positives, but it might create some false negatives. Todo: Confirm it is correct.
if(j == i+1)
//if((l == 1) && (r == 0))
- if( ( l > 1-1e-4 ) && (r < 1e-4) )//FIXME: what precision should be used here???
+ if( ( l > precision ) && (r < precision) )//FIXME: what precision should be used here???
continue;
xs.push_back(std::make_pair((1-l)*dr[i] + l*dr[i+1],
(1-r)*dr[j] + r*dr[j+1]));
@@ -138,6 +189,46 @@ find_self_intersections(std::vector<std::pair<double, double> > &xs,
//unique(xs.begin(), xs.end());
}
+void find_self_intersections(std::vector<std::pair<double, double> > &xs,
+ D2<SBasis> const &A,
+ double precision)
+{
+ D2<Bezier> in;
+ sbasis_to_bezier(in, A);
+ find_self_intersections(xs, in, precision);
+}
+
+
+void subdivide(D2<Bezier> const &a,
+ D2<Bezier> const &b,
+ std::vector< std::pair<double, double> > const &xs,
+ std::vector< D2<Bezier> > &av,
+ std::vector< D2<Bezier> > &bv)
+{
+ if (xs.empty()) {
+ av.push_back(a);
+ bv.push_back(b);
+ return;
+ }
+
+ std::pair<double, double> prev = std::make_pair(0., 0.);
+ for (unsigned i = 0; i < xs.size(); ++i) {
+ av.push_back(portion(a, prev.first, xs[i].first));
+ bv.push_back(portion(b, prev.second, xs[i].second));
+ av.back()[X].at0() = bv.back()[X].at0() = lerp(0.5, av.back()[X].at0(), bv.back()[X].at0());
+ av.back()[X].at1() = bv.back()[X].at1() = lerp(0.5, av.back()[X].at1(), bv.back()[X].at1());
+ av.back()[Y].at0() = bv.back()[Y].at0() = lerp(0.5, av.back()[Y].at0(), bv.back()[Y].at0());
+ av.back()[Y].at1() = bv.back()[Y].at1() = lerp(0.5, av.back()[Y].at1(), bv.back()[Y].at1());
+ prev = xs[i];
+ }
+ av.push_back(portion(a, prev.first, 1));
+ bv.push_back(portion(b, prev.second, 1));
+ av.back()[X].at0() = bv.back()[X].at0() = lerp(0.5, av.back()[X].at0(), bv.back()[X].at0());
+ av.back()[X].at1() = bv.back()[X].at1() = lerp(0.5, av.back()[X].at1(), bv.back()[X].at1());
+ av.back()[Y].at0() = bv.back()[Y].at0() = lerp(0.5, av.back()[Y].at0(), bv.back()[Y].at0());
+ av.back()[Y].at1() = bv.back()[Y].at1() = lerp(0.5, av.back()[Y].at1(), bv.back()[Y].at1());
+}
+
#ifdef HAVE_GSL
#include <gsl/gsl_multiroots.h>
@@ -304,38 +395,6 @@ void polish_intersections(std::vector<std::pair<double, double> > &xs,
B, xs[i].second);
}
-
- /**
- * Compute the Hausdorf distance from A to B only.
- */
-
-
-#if 0
-/** Compute the value of a bezier
- Todo: find a good palce for this.
- */
-// suggested by Sederberg.
-Point OldBezier::operator()(double t) const {
- int n = p.size()-1;
- double u, bc, tn, tmp;
- int i;
- Point r;
- for(int dim = 0; dim < 2; dim++) {
- u = 1.0 - t;
- bc = 1;
- tn = 1;
- tmp = p[0][dim]*u;
- for(i=1; i<n; i++){
- tn = tn*t;
- bc = bc*(n-i+1)/i;
- tmp = (tmp + tn*bc*p[i][dim])*u;
- }
- r[dim] = (tmp + tn*t*p[n][dim]);
- }
- return r;
-}
-#endif
-
/**
* Compute the Hausdorf distance from A to B only.
*/
@@ -350,7 +409,7 @@ double hausdorfl(D2<SBasis>& A, D2<SBasis> const& B,
double h_dist = 0, h_a_t = 0, h_b_t = 0;
double dist = 0;
Point Ax = A.at0();
- double t = Geom::nearest_point(Ax, B);
+ double t = Geom::nearest_time(Ax, B);
dist = Geom::distance(Ax, B(t));
if (dist > h_dist) {
h_a_t = 0;
@@ -358,7 +417,7 @@ double hausdorfl(D2<SBasis>& A, D2<SBasis> const& B,
h_dist = dist;
}
Ax = A.at1();
- t = Geom::nearest_point(Ax, B);
+ t = Geom::nearest_time(Ax, B);
dist = Geom::distance(Ax, B(t));
if (dist > h_dist) {
h_a_t = 1;
@@ -370,7 +429,7 @@ double hausdorfl(D2<SBasis>& A, D2<SBasis> const& B,
Point At = A(xs[i].first);
Point Bu = B(xs[i].second);
double distAtBu = Geom::distance(At, Bu);
- t = Geom::nearest_point(At, B);
+ t = Geom::nearest_time(At, B);
dist = Geom::distance(At, B(t));
//FIXME: we might miss it due to floating point precision...
if (dist >= distAtBu-.1 && distAtBu > h_dist) {
@@ -396,7 +455,7 @@ double hausdorf(D2<SBasis>& A, D2<SBasis> const& B,
double dist = 0;
Point Bx = B.at0();
- double t = Geom::nearest_point(Bx, A);
+ double t = Geom::nearest_time(Bx, A);
dist = Geom::distance(Bx, A(t));
if (dist > h_dist) {
if(a_t) *a_t = t;
@@ -404,7 +463,7 @@ double hausdorf(D2<SBasis>& A, D2<SBasis> const& B,
h_dist = dist;
}
Bx = B.at1();
- t = Geom::nearest_point(Bx, A);
+ t = Geom::nearest_time(Bx, A);
dist = Geom::distance(Bx, A(t));
if (dist > h_dist) {
if(a_t) *a_t = t;
diff --git a/src/2geom/basic-intersection.h b/src/2geom/basic-intersection.h
index 5a813ae99..0b0bf8930 100644
--- a/src/2geom/basic-intersection.h
+++ b/src/2geom/basic-intersection.h
@@ -1,11 +1,12 @@
-/**
- * \file
- * \brief Basic intersection routines
- *
+/** @file
+ * @brief Basic intersection routines
+ *//*
* Authors:
- * ? <?@?.?>
+ * Nathan Hurst <njh@njhurst.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Jean-François Barraud <jf.barraud@gmail.com>
*
- * Copyright ?-? authors
+ * Copyright 2008-2009 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -32,10 +33,11 @@
*
*/
-#ifndef SEEN_GEOM_BASICINTERSECTION_H
-#define SEEN_GEOM_BASICINTERSECTION_H
+#ifndef LIB2GEOM_SEEN_BASIC_INTERSECTION_H
+#define LIB2GEOM_SEEN_BASIC_INTERSECTION_H
#include <2geom/point.h>
+#include <2geom/bezier.h>
#include <2geom/sbasis.h>
#include <2geom/d2.h>
@@ -47,41 +49,29 @@
namespace Geom {
-//why not allowing precision to be set here?
-void find_intersections(std::vector<std::pair<double, double> >& xs,
- D2<SBasis> const & A,
- D2<SBasis> const & B);
+void find_intersections(std::vector<std::pair<double, double> > &xs,
+ D2<Bezier> const &A,
+ D2<Bezier> const &B,
+ double precision = EPSILON);
+
+void find_intersections(std::vector<std::pair<double, double> > &xs,
+ D2<SBasis> const &A,
+ D2<SBasis> const &B,
+ double precision = EPSILON);
+
+void find_intersections(std::vector< std::pair<double, double> > &xs,
+ std::vector<Point> const &A,
+ std::vector<Point> const &B,
+ double precision = EPSILON);
+
+void find_self_intersections(std::vector<std::pair<double, double> > &xs,
+ D2<SBasis> const &A,
+ double precision = EPSILON);
+
+void find_self_intersections(std::vector<std::pair<double, double> > &xs,
+ D2<Bezier> const &A,
+ double precision = EPSILON);
-void find_intersections(std::vector< std::pair<double, double> > & xs,
- std::vector<Point> const& A,
- std::vector<Point> const& B,
- double precision = 1e-5);
-
-//why not allowing precision to be set here?
-void find_self_intersections(std::vector<std::pair<double, double> >& xs,
- D2<SBasis> const & A);
-
-
-//--not implemented
-//void find_self_intersections(std::vector<std::pair<double, double> >& xs,
-// std::vector<Point> const & A);
-
-
-//TODO: this should be moved to .cpp, shouldn't it?
-// #ifdef USE_RECURSIVE_INTERSECTOR
-// /*
-// * find_intersection
-// *
-// * input: A, B - set of control points of two Bezier curve
-// * input: precision - required precision of computation
-// * output: xs - set of pairs of parameter values
-// * at which crossing happens
-// */
-// void find_intersections_bezier_recursive (std::vector< std::pair<double, double> > & xs,
-// std::vector<Point> const& A,
-// std::vector<Point> const& B,
-// double precision = 1e-5);
-// #else
/*
* find_intersection
*
@@ -96,10 +86,14 @@ void find_self_intersections(std::vector<std::pair<double, double> >& xs,
void find_intersections_bezier_clipping (std::vector< std::pair<double, double> > & xs,
std::vector<Point> const& A,
std::vector<Point> const& B,
- double precision = 1e-5);
+ double precision = EPSILON);
//#endif
-
+void subdivide(D2<Bezier> const &a,
+ D2<Bezier> const &b,
+ std::vector< std::pair<double, double> > const &xs,
+ std::vector< D2<Bezier> > &av,
+ std::vector< D2<Bezier> > &bv);
/*
* find_collinear_normal
@@ -115,7 +109,7 @@ void find_intersections_bezier_clipping (std::vector< std::pair<double, double>
void find_collinear_normal (std::vector< std::pair<double, double> >& xs,
std::vector<Point> const& A,
std::vector<Point> const& B,
- double precision = 1e-5);
+ double precision = EPSILON);
void polish_intersections(std::vector<std::pair<double, double> > &xs,
D2<SBasis> const &A,
@@ -125,19 +119,19 @@ void polish_intersections(std::vector<std::pair<double, double> > &xs,
/**
* Compute the Hausdorf distance from A to B only.
*/
-double hausdorfl(D2<SBasis>& A, D2<SBasis> const& B,
+double hausdorfl(D2<SBasis>& A, D2<SBasis> const &B,
double m_precision,
- double *a_t=0, double* b_t=0);
+ double *a_t=NULL, double *b_t=NULL);
/**
* Compute the symmetric Hausdorf distance.
*/
-double hausdorf(D2<SBasis>& A, D2<SBasis> const& B,
+double hausdorf(D2<SBasis> &A, D2<SBasis> const &B,
double m_precision,
- double *a_t=0, double* b_t=0);
+ double *a_t=NULL, double *b_t=NULL);
}
-#endif // !SEEN_GEOM_BASICINTERSECTION_H
+#endif // !LIB2GEOM_SEEN_BASIC_INTERSECTION_H
/*
Local Variables:
diff --git a/src/2geom/bezier-clipping.cpp b/src/2geom/bezier-clipping.cpp
index 9a055204f..be8dd5a5f 100644
--- a/src/2geom/bezier-clipping.cpp
+++ b/src/2geom/bezier-clipping.cpp
@@ -39,8 +39,9 @@
#include <2geom/point.h>
#include <2geom/interval.h>
#include <2geom/bezier.h>
-//#include <2geom/convex-cover.h>
#include <2geom/numeric/matrix.h>
+#include <2geom/convex-hull.h>
+#include <2geom/line.h>
#include <cassert>
#include <vector>
@@ -48,7 +49,7 @@
#include <utility>
//#include <iomanip>
-
+using std::swap;
#define VERBOSE 0
@@ -63,7 +64,6 @@ namespace detail { namespace bezier_clipping {
// for debugging
//
-inline
void print(std::vector<Point> const& cp, const char* msg = "")
{
std::cerr << msg << std::endl;
@@ -72,7 +72,6 @@ void print(std::vector<Point> const& cp, const char* msg = "")
}
template< class charT >
-inline
std::basic_ostream<charT> &
operator<< (std::basic_ostream<charT> & os, const Interval & I)
{
@@ -80,7 +79,6 @@ operator<< (std::basic_ostream<charT> & os, const Interval & I)
return os;
}
-inline
double angle (std::vector<Point> const& A)
{
size_t n = A.size() -1;
@@ -88,7 +86,6 @@ double angle (std::vector<Point> const& A)
return (180 * a / M_PI);
}
-inline
size_t get_precision(Interval const& I)
{
double d = I.extent();
@@ -103,7 +100,6 @@ size_t get_precision(Interval const& I)
return n;
}
-inline
void range_assertion(int k, int m, int n, const char* msg)
{
if ( k < m || k > n)
@@ -118,92 +114,11 @@ void range_assertion(int k, int m, int n, const char* msg)
////////////////////////////////////////////////////////////////////////////////
-// convex hull
-
-/*
- * return true in case the oriented polyline p0, p1, p2 is a right turn
- */
-inline
-bool is_a_right_turn (Point const& p0, Point const& p1, Point const& p2)
-{
- if (p1 == p2) return false;
- Point q1 = p1 - p0;
- Point q2 = p2 - p0;
- if (q1 == -q2) return false;
- return (cross (q1, q2) < 0);
-}
-
-/*
- * return true if p < q wrt the lexicographyc order induced by the coordinates
- */
-struct lex_less
-{
- bool operator() (Point const& p, Point const& q)
- {
- return ((p[X] < q[X]) || (p[X] == q[X] && p[Y] < q[Y]));
- }
-};
-
-/*
- * return true if p > q wrt the lexicographyc order induced by the coordinates
- */
-struct lex_greater
-{
- bool operator() (Point const& p, Point const& q)
- {
- return ((p[X] > q[X]) || (p[X] == q[X] && p[Y] > q[Y]));
- }
-};
-
-/*
- * Compute the convex hull of a set of points.
- * The implementation is based on the Andrew's scan algorithm
- * note: in the Bezier clipping for collinear normals it seems
- * to be more stable wrt the Graham's scan algorithm and in general
- * a bit quikier
- */
-void convex_hull (std::vector<Point> & P)
-{
- size_t n = P.size();
- if (n < 2) return;
- std::sort(P.begin(), P.end(), lex_less());
- if (n < 4) return;
- // upper hull
- size_t u = 2;
- for (size_t i = 2; i < n; ++i)
- {
- while (u > 1 && !is_a_right_turn(P[u-2], P[u-1], P[i]))
- {
- --u;
- }
- std::swap(P[u], P[i]);
- ++u;
- }
- std::sort(P.begin() + u, P.end(), lex_greater());
- std::rotate(P.begin(), P.begin() + 1, P.end());
- // lower hull
- size_t l = u;
- size_t k = u - 1;
- for (size_t i = l; i < n; ++i)
- {
- while (l > k && !is_a_right_turn(P[l-2], P[l-1], P[i]))
- {
- --l;
- }
- std::swap(P[l], P[i]);
- ++l;
- }
- P.resize(l);
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
// numerical routines
/*
* Compute the binomial coefficient (n, k)
*/
-inline
double binomial(unsigned int n, unsigned int k)
{
return choose<double>(n, k);
@@ -212,7 +127,6 @@ double binomial(unsigned int n, unsigned int k)
/*
* Compute the determinant of the 2x2 matrix with column the point P1, P2
*/
-inline
double det(Point const& P1, Point const& P2)
{
return P1[X]*P2[Y] - P1[Y]*P2[X];
@@ -222,7 +136,6 @@ double det(Point const& P1, Point const& P2)
* Solve the linear system [P1,P2] * P = Q
* in case there isn't exactly one solution the routine returns false
*/
-inline
bool solve(Point & P, Point const& P1, Point const& P2, Point const& Q)
{
double d = det(P1, P2);
@@ -239,27 +152,11 @@ bool solve(Point & P, Point const& P1, Point const& P2, Point const& Q)
/*
* Map the sub-interval I in [0,1] into the interval J and assign it to J
*/
-inline
void map_to(Interval & J, Interval const& I)
{
- double length = J.extent();
- J[1] = I.max() * length + J[0];
- J[0] = I.min() * length + J[0];
+ J.setEnds(J.valueAt(I.min()), J.valueAt(I.max()));
}
-/*
- * The interval [1,0] is used to represent the empty interval, this routine
- * is just an helper function for creating such an interval
- */
-inline
-Interval make_empty_interval()
-{
- Interval I(0);
- I[0] = 1;
- return I;
-}
-
-
////////////////////////////////////////////////////////////////////////////////
// bezier curve routines
@@ -267,8 +164,8 @@ Interval make_empty_interval()
* Return true if all the Bezier curve control points are near,
* false otherwise
*/
-inline
-bool is_constant(std::vector<Point> const& A, double precision = EPSILON)
+// Bezier.isConstant(precision)
+bool is_constant(std::vector<Point> const& A, double precision)
{
for (unsigned int i = 1; i < A.size(); ++i)
{
@@ -281,7 +178,7 @@ bool is_constant(std::vector<Point> const& A, double precision = EPSILON)
/*
* Compute the hodograph of the bezier curve B and return it in D
*/
-inline
+// derivative(Bezier)
void derivative(std::vector<Point> & D, std::vector<Point> const& B)
{
D.clear();
@@ -304,7 +201,7 @@ void derivative(std::vector<Point> & D, std::vector<Point> const& B)
* Compute the hodograph of the Bezier curve B rotated of 90 degree
* and return it in D; we have N(t) orthogonal to B(t) for any t
*/
-inline
+// rot90(derivative(Bezier))
void normal(std::vector<Point> & N, std::vector<Point> const& B)
{
derivative(N,B);
@@ -317,7 +214,7 @@ void normal(std::vector<Point> & N, std::vector<Point> const& B)
/*
* Compute the portion of the Bezier curve "B" wrt the interval [0,t]
*/
-inline
+// portion(Bezier, 0, t)
void left_portion(Coord t, std::vector<Point> & B)
{
size_t n = B.size();
@@ -333,7 +230,7 @@ void left_portion(Coord t, std::vector<Point> & B)
/*
* Compute the portion of the Bezier curve "B" wrt the interval [t,1]
*/
-inline
+// portion(Bezier, t, 1)
void right_portion(Coord t, std::vector<Point> & B)
{
size_t n = B.size();
@@ -349,7 +246,7 @@ void right_portion(Coord t, std::vector<Point> & B)
/*
* Compute the portion of the Bezier curve "B" wrt the interval "I"
*/
-inline
+// portion(Bezier, I)
void portion (std::vector<Point> & B , Interval const& I)
{
if (I.min() == 0)
@@ -371,9 +268,9 @@ void portion (std::vector<Point> & B , Interval const& I)
struct intersection_point_tag;
struct collinear_normal_tag;
template <typename Tag>
-void clip(Interval & dom,
- std::vector<Point> const& A,
- std::vector<Point> const& B);
+OptInterval clip(std::vector<Point> const& A,
+ std::vector<Point> const& B,
+ double precision);
template <typename Tag>
void iterate(std::vector<Interval>& domsA,
std::vector<Interval>& domsB,
@@ -392,14 +289,14 @@ void iterate(std::vector<Interval>& domsA,
* the line is returned in the output parameter "l" in the form of a 3 element
* vector : l[0] * x + l[1] * y + l[2] == 0; the line is normalized.
*/
-inline
+// Line(c[i], c[j])
void orientation_line (std::vector<double> & l,
std::vector<Point> const& c,
size_t i, size_t j)
{
l[0] = c[j][Y] - c[i][Y];
l[1] = c[i][X] - c[j][X];
- l[2] = cross(c[i], c[j]);
+ l[2] = cross(c[j], c[i]);
double length = std::sqrt(l[0] * l[0] + l[1] * l[1]);
assert (length != 0);
l[0] /= length;
@@ -411,22 +308,20 @@ void orientation_line (std::vector<double> & l,
* Pick up an orientation line for the Bezier curve "c" and return it in
* the output parameter "l"
*/
-inline
-void pick_orientation_line (std::vector<double> & l,
- std::vector<Point> const& c)
+Line pick_orientation_line (std::vector<Point> const &c, double precision)
{
size_t i = c.size();
- while (--i > 0 && are_near(c[0], c[i]))
+ while (--i > 0 && are_near(c[0], c[i], precision))
{}
- if (i == 0)
- {
- // this should never happen because when a new curve portion is created
- // we check that it is not constant;
- // however this requires that the precision used in the is_constant
- // routine has to be the same used here in the are_near test
- assert(i != 0);
- }
- orientation_line(l, c, 0, i);
+
+ // this should never happen because when a new curve portion is created
+ // we check that it is not constant;
+ // however this requires that the precision used in the is_constant
+ // routine has to be the same used here in the are_near test
+ assert(i != 0);
+
+ Line line(c[0], c[i]);
+ return line;
//std::cerr << "i = " << i << std::endl;
}
@@ -436,29 +331,25 @@ void pick_orientation_line (std::vector<double> & l,
* the line is returned in the output parameter "l" in the form of a 3 element
* vector : l[0] * x + l[1] * y + l[2] == 0; the line is normalized.
*/
-inline
-void orthogonal_orientation_line (std::vector<double> & l,
- std::vector<Point> const& c,
- Point const& p)
+Line orthogonal_orientation_line (std::vector<Point> const &c,
+ Point const &p,
+ double precision)
{
- if (is_constant(c))
- {
- // this should never happen
- assert(!is_constant(c));
- }
- std::vector<Point> ol(2);
- ol[0] = p;
- ol[1] = (c.back() - c.front()).cw() + p;
- orientation_line(l, ol, 0, 1);
+ // this should never happen
+ assert(!is_constant(c, precision));
+
+ Line line(p, (c.back() - c.front()).cw() + p);
+ return line;
}
/*
* Compute the signed distance of the point "P" from the normalized line l
*/
-inline
-double distance (Point const& P, std::vector<double> const& l)
+double signed_distance(Point const &p, Line const &l)
{
- return l[X] * P[X] + l[Y] * P[Y] + l[2];
+ Coord a, b, c;
+ l.coefficients(a, b, c);
+ return a * p[X] + b * p[Y] + c;
}
/*
@@ -466,26 +357,20 @@ double distance (Point const& P, std::vector<double> const& l)
* curve "c" from the normalized orientation line "l".
* This bounds are returned through the output Interval parameter"bound".
*/
-inline
-void fat_line_bounds (Interval& bound,
- std::vector<Point> const& c,
- std::vector<double> const& l)
+Interval fat_line_bounds (std::vector<Point> const &c,
+ Line const &l)
{
- bound[0] = 0;
- bound[1] = 0;
- for (size_t i = 0; i < c.size(); ++i)
- {
- const double d = distance(c[i], l);
- if (bound[0] > d) bound[0] = d;
- if (bound[1] < d) bound[1] = d;
+ Interval bound(0, 0);
+ for (size_t i = 0; i < c.size(); ++i) {
+ bound.expandTo(signed_distance(c[i], l));
}
+ return bound;
}
/*
* return the x component of the intersection point between the line
* passing through points p1, p2 and the line Y = "y"
*/
-inline
double intersect (Point const& p1, Point const& p2, double y)
{
// we are sure that p2[Y] != p1[Y] because this routine is called
@@ -500,23 +385,22 @@ double intersect (Point const& p1, Point const& p2, double y)
* line "l" and the interval range "bound", the new parameter interval for
* the clipped curve is returned through the output parameter "dom"
*/
-void clip_interval (Interval& dom,
- std::vector<Point> const& B,
- std::vector<double> const& l,
- Interval const& bound)
+OptInterval clip_interval (std::vector<Point> const& B,
+ Line const &l,
+ Interval const &bound)
{
double n = B.size() - 1; // number of sub-intervals
std::vector<Point> D; // distance curve control points
D.reserve (B.size());
for (size_t i = 0; i < B.size(); ++i)
{
- const double d = distance (B[i], l);
+ const double d = signed_distance(B[i], l);
D.push_back (Point(i/n, d));
}
//print(D);
- convex_hull(D);
- std::vector<Point> & p = D;
+ ConvexHull p;
+ p.swap(D);
//print(p);
bool plower, phigher;
@@ -589,8 +473,11 @@ void clip_interval (Interval& dom,
// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
}
- dom[0] = tmin;
- dom[1] = tmax;
+ if (tmin == 1 && tmax == 0) {
+ return OptInterval();
+ } else {
+ return Interval(tmin, tmax);
+ }
}
/*
@@ -599,24 +486,20 @@ void clip_interval (Interval& dom,
* is returned through the output parameter "dom"
*/
template <>
-inline
-void clip<intersection_point_tag> (Interval & dom,
- std::vector<Point> const& A,
- std::vector<Point> const& B)
+OptInterval clip<intersection_point_tag> (std::vector<Point> const& A,
+ std::vector<Point> const& B,
+ double precision)
{
- std::vector<double> bl(3);
- Interval bound;
- if (is_constant(A))
- {
+ Line bl;
+ if (is_constant(A, precision)) {
Point M = middle_point(A.front(), A.back());
- orthogonal_orientation_line(bl, B, M);
+ bl = orthogonal_orientation_line(B, M, precision);
+ } else {
+ bl = pick_orientation_line(A, precision);
}
- else
- {
- pick_orientation_line(bl, A);
- }
- fat_line_bounds(bound, A, bl);
- clip_interval(dom, B, bl, bound);
+ bl.normalize();
+ Interval bound = fat_line_bounds(A, bl);
+ return clip_interval(B, bl, bound);
}
@@ -627,7 +510,6 @@ void clip<intersection_point_tag> (Interval & dom,
* Compute a closed focus for the Bezier curve B and return it in F
* A focus is any curve through which all lines perpendicular to B(t) pass.
*/
-inline
void make_focus (std::vector<Point> & F, std::vector<Point> const& B)
{
assert (B.size() > 2);
@@ -743,9 +625,8 @@ void distance_control_points (std::vector<Point> & D,
* Clip the Bezier curve "B" wrt the focus "F"; the new parameter interval for
* the clipped curve is returned through the output parameter "dom"
*/
-void clip_interval (Interval& dom,
- std::vector<Point> const& B,
- std::vector<Point> const& F)
+OptInterval clip_interval (std::vector<Point> const& B,
+ std::vector<Point> const& F)
{
std::vector<Point> D; // distance curve control points
distance_control_points(D, B, F);
@@ -753,8 +634,8 @@ void clip_interval (Interval& dom,
// ConvexHull chD(D);
// std::vector<Point>& p = chD.boundary; // convex hull vertices
- convex_hull(D);
- std::vector<Point> & p = D;
+ ConvexHull p;
+ p.swap(D);
//print(p, "CH(D)");
bool plower, clower;
@@ -803,8 +684,11 @@ void clip_interval (Interval& dom,
// std::cerr << "0 : lower " << p[0]
// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
}
- dom[0] = tmin;
- dom[1] = tmax;
+ if (tmin == 1 && tmax == 0) {
+ return OptInterval();
+ } else {
+ return Interval(tmin, tmax);
+ }
}
/*
@@ -813,14 +697,13 @@ void clip_interval (Interval& dom,
* for the clipped curve is returned through the output parameter "dom"
*/
template <>
-inline
-void clip<collinear_normal_tag> (Interval & dom,
- std::vector<Point> const& A,
- std::vector<Point> const& B)
+OptInterval clip<collinear_normal_tag> (std::vector<Point> const& A,
+ std::vector<Point> const& B,
+ double /*precision*/)
{
std::vector<Point> F;
make_focus(F, A);
- clip_interval(dom, B, F);
+ return clip_interval(B, F);
}
@@ -828,9 +711,9 @@ void clip<collinear_normal_tag> (Interval & dom,
const double MAX_PRECISION = 1e-8;
const double MIN_CLIPPED_SIZE_THRESHOLD = 0.8;
const Interval UNIT_INTERVAL(0,1);
-const Interval EMPTY_INTERVAL = make_empty_interval();
+const OptInterval EMPTY_INTERVAL;
const Interval H1_INTERVAL(0, 0.5);
-const Interval H2_INTERVAL(0.5 + MAX_PRECISION, 1.0);
+const Interval H2_INTERVAL(nextafter(0.5, 1.0), 1.0);
/*
* iterate
@@ -884,9 +767,9 @@ void iterate<intersection_point_tag> (std::vector<Interval>& domsA,
Interval* dom1 = &dompA;
Interval* dom2 = &dompB;
- Interval dom;
+ OptInterval dom;
- if ( is_constant(A) && is_constant(B) ){
+ if ( is_constant(A, precision) && is_constant(B, precision) ){
Point M1 = middle_point(C1->front(), C1->back());
Point M2 = middle_point(C2->front(), C2->back());
if (are_near(M1,M2)){
@@ -903,10 +786,9 @@ void iterate<intersection_point_tag> (std::vector<Interval>& domsA,
#if VERBOSE
std::cerr << "iter: " << iter << std::endl;
#endif
- clip<intersection_point_tag>(dom, *C1, *C2);
+ dom = clip<intersection_point_tag>(*C1, *C2, precision);
- // [1,0] is utilized to represent an empty interval
- if (dom == EMPTY_INTERVAL)
+ if (dom.isEmpty())
{
#if VERBOSE
std::cerr << "dom: empty" << std::endl;
@@ -917,15 +799,12 @@ void iterate<intersection_point_tag> (std::vector<Interval>& domsA,
std::cerr << "dom : " << dom << std::endl;
#endif
// all other cases where dom[0] > dom[1] are invalid
- if (dom.min() > dom.max())
- {
- assert(dom.min() < dom.max());
- }
+ assert(dom->min() <= dom->max());
- map_to(*dom2, dom);
+ map_to(*dom2, *dom);
- portion(*C2, dom);
- if (is_constant(*C2) && is_constant(*C1))
+ portion(*C2, *dom);
+ if (is_constant(*C2, precision) && is_constant(*C1, precision))
{
Point M1 = middle_point(C1->front(), C1->back());
Point M2 = middle_point(C2->front(), C2->back());
@@ -945,10 +824,10 @@ void iterate<intersection_point_tag> (std::vector<Interval>& domsA,
// if we have clipped less than 20% than we need to subdive the curve
// with the largest domain into two sub-curves
- if ( dom.extent() > MIN_CLIPPED_SIZE_THRESHOLD)
+ if (dom->extent() > MIN_CLIPPED_SIZE_THRESHOLD)
{
#if VERBOSE
- std::cerr << "clipped less than 20% : " << dom.extent() << std::endl;
+ std::cerr << "clipped less than 20% : " << dom->extent() << std::endl;
std::cerr << "angle(pA) : " << angle(pA) << std::endl;
std::cerr << "angle(pB) : " << angle(pB) << std::endl;
#endif
@@ -983,8 +862,8 @@ void iterate<intersection_point_tag> (std::vector<Interval>& domsA,
return;
}
- std::swap(C1, C2);
- std::swap(dom1, dom2);
+ swap(C1, C2);
+ swap(dom1, dom2);
#if VERBOSE
std::cerr << "dom(pA) : " << dompA << std::endl;
std::cerr << "dom(pB) : " << dompB << std::endl;
@@ -1047,7 +926,7 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
Interval* dom1 = &dompA;
Interval* dom2 = &dompB;
- Interval dom;
+ OptInterval dom;
size_t iter = 0;
while (++iter < 100
@@ -1056,11 +935,9 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
#if VERBOSE
std::cerr << "iter: " << iter << std::endl;
#endif
- clip<collinear_normal_tag>(dom, *C1, *C2);
+ dom = clip<collinear_normal_tag>(*C1, *C2, precision);
- // [1,0] is utilized to represent an empty interval
- if (dom == EMPTY_INTERVAL)
- {
+ if (dom.isEmpty()) {
#if VERBOSE
std::cerr << "dom: empty" << std::endl;
#endif
@@ -1069,13 +946,9 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
#if VERBOSE
std::cerr << "dom : " << dom << std::endl;
#endif
- // all other cases where dom[0] > dom[1] are invalid
- if (dom.min() > dom.max())
- {
- assert(dom.min() < dom.max());
- }
+ assert(dom->min() <= dom->max());
- map_to(*dom2, dom);
+ map_to(*dom2, *dom);
// it's better to stop before losing computational precision
if (iter > 1 && (dom2->extent() <= MAX_PRECISION))
@@ -1086,8 +959,8 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
break;
}
- portion(*C2, dom);
- if (iter > 1 && is_constant(*C2))
+ portion(*C2, *dom);
+ if (iter > 1 && is_constant(*C2, precision))
{
#if VERBOSE
std::cerr << "new curve portion pC1 is constant" << std::endl;
@@ -1098,10 +971,10 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
// if we have clipped less than 20% than we need to subdive the curve
// with the largest domain into two sub-curves
- if ( dom.extent() > MIN_CLIPPED_SIZE_THRESHOLD)
+ if ( dom->extent() > MIN_CLIPPED_SIZE_THRESHOLD)
{
#if VERBOSE
- std::cerr << "clipped less than 20% : " << dom.extent() << std::endl;
+ std::cerr << "clipped less than 20% : " << dom->extent() << std::endl;
std::cerr << "angle(pA) : " << angle(pA) << std::endl;
std::cerr << "angle(pB) : " << angle(pB) << std::endl;
#endif
@@ -1115,7 +988,7 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
}
pC1 = pC2 = pA;
portion(pC1, H1_INTERVAL);
- if (false && is_constant(pC1))
+ if (false && is_constant(pC1, precision))
{
#if VERBOSE
std::cerr << "new curve portion pC1 is constant" << std::endl;
@@ -1123,7 +996,7 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
break;
}
portion(pC2, H2_INTERVAL);
- if (is_constant(pC2))
+ if (is_constant(pC2, precision))
{
#if VERBOSE
std::cerr << "new curve portion pC2 is constant" << std::endl;
@@ -1146,7 +1019,7 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
}
pC1 = pC2 = pB;
portion(pC1, H1_INTERVAL);
- if (is_constant(pC1))
+ if (is_constant(pC1, precision))
{
#if VERBOSE
std::cerr << "new curve portion pC1 is constant" << std::endl;
@@ -1154,7 +1027,7 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
break;
}
portion(pC2, H2_INTERVAL);
- if (is_constant(pC2))
+ if (is_constant(pC2, precision))
{
#if VERBOSE
std::cerr << "new curve portion pC2 is constant" << std::endl;
@@ -1172,8 +1045,8 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
return;
}
- std::swap(C1, C2);
- std::swap(dom1, dom2);
+ swap(C1, C2);
+ swap(dom1, dom2);
#if VERBOSE
std::cerr << "dom(pA) : " << dompA << std::endl;
std::cerr << "dom(pB) : " << dompB << std::endl;
diff --git a/src/2geom/bezier-curve.cpp b/src/2geom/bezier-curve.cpp
index 6dfb0f0b3..b81041f29 100644
--- a/src/2geom/bezier-curve.cpp
+++ b/src/2geom/bezier-curve.cpp
@@ -32,6 +32,9 @@
*/
#include <2geom/bezier-curve.h>
+#include <2geom/path-sink.h>
+#include <2geom/basic-intersection.h>
+#include <2geom/nearest-time.h>
namespace Geom
{
@@ -97,7 +100,7 @@ namespace Geom
* @class BezierCurveN
* @brief Bezier curve with compile-time specified order.
*
- * @tparam degree unsigned value indicating the order of the bezier curve
+ * @tparam degree unsigned value indicating the order of the Bezier curve
*
* @relates BezierCurve
* @ingroup Curves
@@ -105,12 +108,10 @@ namespace Geom
BezierCurve::BezierCurve(std::vector<Point> const &pts)
+ : inner(pts)
{
- inner = D2<Bezier>(Bezier::Order(pts.size() - 1), Bezier::Order(pts.size() - 1));
- for (unsigned d = 0; d < 2; ++d) {
- for (unsigned i = 0; i < pts.size(); i++) {
- inner[d][i] = pts[i][d];
- }
+ if (pts.size() < 2) {
+ THROW_RANGEERROR("Bezier curve must have at least 2 control points");
}
}
@@ -124,16 +125,87 @@ Coord BezierCurve::length(Coord tolerance) const
return distance(initialPoint(), finalPoint());
case 2:
{
- std::vector<Point> pts = points();
+ std::vector<Point> pts = controlPoints();
return bezier_length(pts[0], pts[1], pts[2], tolerance);
}
case 3:
{
- std::vector<Point> pts = points();
+ std::vector<Point> pts = controlPoints();
return bezier_length(pts[0], pts[1], pts[2], pts[3], tolerance);
}
default:
- return bezier_length(points(), tolerance);
+ return bezier_length(controlPoints(), tolerance);
+ }
+}
+
+std::vector<CurveIntersection>
+BezierCurve::intersect(Curve const &other, Coord eps) const
+{
+ std::vector<CurveIntersection> result;
+
+ // optimization for the common case of no intersections
+ if (!boundsFast().intersects(other.boundsFast())) return result;
+
+ BezierCurve const *bez = dynamic_cast<BezierCurve const *>(&other);
+ if (bez) {
+ std::vector<std::pair<double, double> > xs;
+ find_intersections(xs, inner, bez->inner, eps);
+ for (unsigned i = 0; i < xs.size(); ++i) {
+ CurveIntersection x(*this, other, xs[i].first, xs[i].second);
+ result.push_back(x);
+ }
+ } else {
+ THROW_NOTIMPLEMENTED();
+ }
+
+ return result;
+}
+
+bool BezierCurve::operator==(Curve const &c) const
+{
+ if (this == &c) return true;
+
+ BezierCurve const *other = dynamic_cast<BezierCurve const *>(&c);
+ if (!other) return false;
+ if (size() != other->size()) return false;
+
+ for (unsigned i = 0; i < size(); ++i) {
+ if (controlPoint(i) != other->controlPoint(i)) return false;
+ }
+ return true;
+}
+
+Coord BezierCurve::nearestTime(Point const &p, Coord from, Coord to) const
+{
+ return nearest_time(p, inner, from, to);
+}
+
+void BezierCurve::feed(PathSink &sink, bool moveto_initial) const
+{
+ if (size() > 4) {
+ Curve::feed(sink, moveto_initial);
+ return;
+ }
+
+ Point ip = controlPoint(0);
+ if (moveto_initial) {
+ sink.moveTo(ip);
+ }
+ switch (size()) {
+ case 2:
+ sink.lineTo(controlPoint(1));
+ break;
+ case 3:
+ sink.quadTo(controlPoint(1), controlPoint(2));
+ break;
+ case 4:
+ sink.curveTo(controlPoint(1), controlPoint(2), controlPoint(3));
+ break;
+ default:
+ // TODO: add a path sink method that accepts a vector of control points
+ // and converts to cubic spline by default
+ assert(false);
+ break;
}
}
@@ -164,9 +236,11 @@ Curve *BezierCurveN<1>::derivative() const {
}
template<>
-Coord BezierCurveN<1>::nearestPoint(Point const& p, Coord from, Coord to) const
+Coord BezierCurveN<1>::nearestTime(Point const& p, Coord from, Coord to) const
{
- if ( from > to ) std::swap(from, to);
+ using std::swap;
+
+ if ( from > to ) swap(from, to);
Point ip = pointAt(from);
Point fp = pointAt(to);
Point v = fp - ip;
@@ -178,6 +252,33 @@ Coord BezierCurveN<1>::nearestPoint(Point const& p, Coord from, Coord to) const
else return from + t*(to-from);
}
+template <>
+void BezierCurveN<1>::feed(PathSink &sink, bool moveto_initial) const
+{
+ if (moveto_initial) {
+ sink.moveTo(controlPoint(0));
+ }
+ sink.lineTo(controlPoint(1));
+}
+
+template <>
+void BezierCurveN<2>::feed(PathSink &sink, bool moveto_initial) const
+{
+ if (moveto_initial) {
+ sink.moveTo(controlPoint(0));
+ }
+ sink.quadTo(controlPoint(1), controlPoint(2));
+}
+
+template <>
+void BezierCurveN<3>::feed(PathSink &sink, bool moveto_initial) const
+{
+ if (moveto_initial) {
+ sink.moveTo(controlPoint(0));
+ }
+ sink.curveTo(controlPoint(1), controlPoint(2), controlPoint(3));
+}
+
static Coord bezier_length_internal(std::vector<Point> &v1, Coord tolerance)
{
@@ -186,7 +287,7 @@ static Coord bezier_length_internal(std::vector<Point> &v1, Coord tolerance)
* but shorter than the length of the polyline formed by its control
* points. When the difference between the two values is smaller than the
* error tolerance, we can be sure that the true value is no further than
- * 2*tolerance from their arithmetic mean. When it's larger, we recursively
+ * 0.5 * tolerance from their arithmetic mean. When it's larger, we recursively
* subdivide the Bezier curve into two parts and add their lengths.
*/
Coord lower = distance(v1.front(), v1.back());
diff --git a/src/2geom/bezier-curve.h b/src/2geom/bezier-curve.h
index d379526fa..9b08466f8 100644
--- a/src/2geom/bezier-curve.h
+++ b/src/2geom/bezier-curve.h
@@ -33,8 +33,8 @@
* the specific language governing rights and limitations.
*/
-#ifndef SEEN_LIB2GEOM_BEZIER_CURVE_H
-#define SEEN_LIB2GEOM_BEZIER_CURVE_H
+#ifndef LIB2GEOM_SEEN_BEZIER_CURVE_H
+#define LIB2GEOM_SEEN_BEZIER_CURVE_H
#include <2geom/curve.h>
#include <2geom/sbasis-curve.h> // for non-native winding method
@@ -48,25 +48,34 @@ class BezierCurve : public Curve {
protected:
D2<Bezier> inner;
BezierCurve() {}
- BezierCurve(D2<Bezier> const &b) : inner(b) {}
BezierCurve(Bezier const &x, Bezier const &y) : inner(x, y) {}
BezierCurve(std::vector<Point> const &pts);
public:
+ explicit BezierCurve(D2<Bezier> const &b) : inner(b) {}
+
/// @name Access and modify control points
/// @{
/** @brief Get the order of the Bezier curve.
* A Bezier curve has order() + 1 control points. */
unsigned order() const { return inner[X].order(); }
+ /** @brief Get the number of control points. */
+ unsigned size() const { return inner[X].order() + 1; }
+ /** @brief Access control points of the curve.
+ * @param ix The (zero-based) index of the control point. Note that the caller is responsible for checking that this value is <= order().
+ * @return The control point. No-reference return, use setPoint() to modify control points. */
+ Point controlPoint(unsigned ix) const { return Point(inner[X][ix], inner[Y][ix]); }
+ Point operator[](unsigned ix) const { return Point(inner[X][ix], inner[Y][ix]); }
/** @brief Get the control points.
* @return Vector with order() + 1 control points. */
- std::vector<Point> points() const { return bezier_points(inner); }
+ std::vector<Point> controlPoints() const { return bezier_points(inner); }
+
/** @brief Modify a control point.
* @param ix The zero-based index of the point to modify. Note that the caller is responsible for checking that this value is <= order().
* @param v The new value of the point */
- void setPoint(unsigned ix, Point v) {
- inner[X].setPoint(ix, v[X]);
- inner[Y].setPoint(ix, v[Y]);
+ void setPoint(unsigned ix, Point const &v) {
+ inner[X][ix] = v[X];
+ inner[Y][ix] = v[Y];
}
/** @brief Set new control points.
* @param ps Vector which must contain order() + 1 points.
@@ -80,23 +89,21 @@ public:
setPoint(i, ps[i]);
}
}
- /** @brief Access control points of the curve.
- * @param ix The (zero-based) index of the control point. Note that the caller is responsible for checking that this value is <= order().
- * @return The control point. No-reference return, use setPoint() to modify control points. */
- Point const operator[](unsigned ix) const { return Point(inner[X][ix], inner[Y][ix]); }
/// @}
/// @name Construct a Bezier curve with runtime-determined order.
/// @{
- /** @brief Construct a curve from a vector of control points. */
+ /** @brief Construct a curve from a vector of control points.
+ * This will construct the appropriate specialization of BezierCurve (i.e. LineSegment,
+ * QuadraticBezier or Cubic Bezier) if the number of control points in the passed vector
+ * does not exceed 4. */
static BezierCurve *create(std::vector<Point> const &pts);
/// @}
// implementation of virtual methods goes here
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
virtual Point initialPoint() const { return inner.at0(); }
virtual Point finalPoint() const { return inner.at1(); }
- virtual bool isDegenerate() const { return inner.isConstant(); }
+ virtual bool isDegenerate() const { return inner.isConstant(0); }
virtual void setInitial(Point const &v) { setPoint(0, v); }
virtual void setFinal(Point const &v) { setPoint(order(), v); }
virtual Rect boundsFast() const { return *bounds_fast(inner); }
@@ -120,19 +127,11 @@ public:
return new BezierCurve(Geom::reverse(inner));
}
- virtual Curve *transformed(Affine const &m) const {
- BezierCurve *ret = new BezierCurve();
- std::vector<Point> ps = points();
- for (unsigned i = 0; i <= order(); i++) {
- ps[i] = ps[i] * m;
+ virtual void transform(Affine const &m) {
+ for (unsigned i = 0; i < size(); ++i) {
+ setPoint(i, controlPoint(i) * m);
}
- ret->setPoints(ps);
- return ret;
}
- virtual Curve &operator*=(Translate const &m) {
- inner += m.vector();
- return *this;
- };
virtual Curve *derivative() const {
return new BezierCurve(Geom::derivative(inner[X]), Geom::derivative(inner[Y]));
@@ -143,26 +142,32 @@ public:
virtual std::vector<Coord> roots(Coord v, Dim2 d) const {
return (inner[d] - v).roots();
}
+ virtual Coord nearestTime(Point const &p, Coord from = 0, Coord to = 1) const;
virtual Coord length(Coord tolerance) const;
+ virtual std::vector<CurveIntersection> intersect(Curve const &other, Coord eps = EPSILON) const;
virtual Point pointAt(Coord t) const { return inner.valueAt(t); }
- virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const { return inner.valueAndDerivatives(t, n); }
+ virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const {
+ return inner.valueAndDerivatives(t, n);
+ }
virtual Coord valueAt(Coord t, Dim2 d) const { return inner[d].valueAt(t); }
virtual D2<SBasis> toSBasis() const {return inner.toSBasis(); }
-#endif
+ virtual bool operator==(Curve const &c) const;
+ virtual void feed(PathSink &sink, bool) const;
};
template <unsigned degree>
-class BezierCurveN : public BezierCurve {
+class BezierCurveN
+ : public BezierCurve
+{
+ template <unsigned required_degree>
+ static void assert_degree(BezierCurveN<required_degree> const *) {}
public:
- template <unsigned required_degree>
- static void assert_degree(BezierCurveN<required_degree> const *) {}
-
/// @name Construct Bezier curves
/// @{
/** @brief Construct a Bezier curve of the specified order with all points zero. */
BezierCurveN() {
- inner = D2<Bezier> (Bezier::Order(degree), Bezier::Order(degree));
+ inner = D2<Bezier>(Bezier(Bezier::Order(degree)), Bezier(Bezier::Order(degree)));
}
/** @brief Construct from 2D Bezier polynomial. */
@@ -224,7 +229,6 @@ public:
BezierCurveN(sx.second, sy.second));
}
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
virtual Curve *duplicate() const {
return new BezierCurveN(*this);
}
@@ -242,30 +246,15 @@ public:
return new BezierCurveN(Geom::reverse(inner));
}
}
- virtual Curve *transformed(Affine const &m) const {
- if (degree == 1) {
- return new BezierCurveN<1>(initialPoint() * m, finalPoint() * m);
- } else {
- BezierCurveN *ret = new BezierCurveN();
- std::vector<Point> ps = points();
- for (unsigned i = 0; i <= degree; i++) {
- ps[i] = ps[i] * m;
- }
- ret->setPoints(ps);
- return ret;
- }
- }
- virtual Curve &operator*=(Translate const &m) {
- inner += m.vector();
- return *this;
- }
virtual Curve *derivative() const;
-
- // the method below is defined so that LineSegment can specialize it
- virtual Coord nearestPoint(Point const& p, Coord from = 0, Coord to = 1) const {
- return Curve::nearestPoint(p, from, to);
+
+ virtual Coord nearestTime(Point const &p, Coord from = 0, Coord to = 1) const {
+ return BezierCurve::nearestTime(p, from, to);
+ }
+ virtual void feed(PathSink &sink, bool moveto_initial) const {
+ // call super. this is implemented only to allow specializations
+ BezierCurve::feed(sink, moveto_initial);
}
-#endif
};
// BezierCurveN<0> is meaningless; specialize it out
@@ -291,9 +280,12 @@ Curve *BezierCurveN<degree>::derivative() const {
return new BezierCurveN<degree-1>(Geom::derivative(inner[X]), Geom::derivative(inner[Y]));
}
-// optimized specializations for LineSegment
+// optimized specializations
template <> Curve *BezierCurveN<1>::derivative() const;
-template <> Coord BezierCurveN<1>::nearestPoint(Point const &, Coord, Coord) const;
+template <> Coord BezierCurveN<1>::nearestTime(Point const &, Coord, Coord) const;
+template <> void BezierCurveN<1>::feed(PathSink &sink, bool moveto_initial) const;
+template <> void BezierCurveN<2>::feed(PathSink &sink, bool moveto_initial) const;
+template <> void BezierCurveN<3>::feed(PathSink &sink, bool moveto_initial) const;
inline Point middle_point(LineSegment const& _segment) {
return ( _segment.initialPoint() + _segment.finalPoint() ) / 2;
@@ -309,7 +301,7 @@ Coord bezier_length(Point p0, Point p1, Point p2, Point p3, Coord tolerance = 0.
} // end namespace Geom
-#endif // _2GEOM_BEZIER_CURVE_H_
+#endif // LIB2GEOM_SEEN_BEZIER_CURVE_H
/*
Local Variables:
diff --git a/src/2geom/bezier-to-sbasis.h b/src/2geom/bezier-to-sbasis.h
index 8cd4bf444..73c55d9b2 100644
--- a/src/2geom/bezier-to-sbasis.h
+++ b/src/2geom/bezier-to-sbasis.h
@@ -29,8 +29,8 @@
*
*/
-#ifndef _BEZIER_TO_SBASIS
-#define _BEZIER_TO_SBASIS
+#ifndef LIB2GEOM_SEEN_BEZIER_TO_SBASIS_H
+#define LIB2GEOM_SEEN_BEZIER_TO_SBASIS_H
#include <2geom/coord.h>
#include <2geom/point.h>
@@ -79,13 +79,9 @@ D2<SBasis> handles_to_sbasis(T const& handles, unsigned order)
return sbc;
}
-
} // end namespace Geom
-
-
-
-#endif
+#endif // LIB2GEOM_SEEN_BEZIER_TO_SBASIS_H
/*
Local Variables:
mode:c++
diff --git a/src/2geom/bezier-utils.cpp b/src/2geom/bezier-utils.cpp
index ec17f6869..816bcdeb4 100644
--- a/src/2geom/bezier-utils.cpp
+++ b/src/2geom/bezier-utils.cpp
@@ -666,9 +666,9 @@ Point
bezier_pt(unsigned const degree, Point const V[], double const t)
{
/** Pascal's triangle. */
- static int const pascal[4][4] = {{1},
- {1, 1},
- {1, 2, 1},
+ static int const pascal[4][4] = {{1, 0, 0, 0},
+ {1, 1, 0, 0},
+ {1, 2, 1, 0},
{1, 3, 3, 1}};
assert( degree < 4);
double const s = 1.0 - t;
diff --git a/src/2geom/bezier.cpp b/src/2geom/bezier.cpp
new file mode 100644
index 000000000..45496b75c
--- /dev/null
+++ b/src/2geom/bezier.cpp
@@ -0,0 +1,324 @@
+/**
+ * @file
+ * @brief Bernstein-Bezier polynomial
+ *//*
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ * Michael Sloan <mgsloan@gmail.com>
+ * Nathan Hurst <njh@njhurst.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright 2007-2015 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include <2geom/bezier.h>
+#include <2geom/solver.h>
+#include <2geom/concepts.h>
+
+namespace Geom {
+
+std::vector<Coord> Bezier::valueAndDerivatives(Coord t, unsigned n_derivs) const {
+ /* This is inelegant, as it uses several extra stores. I think there might be a way to
+ * evaluate roughly in situ. */
+
+ // initialize return vector with zeroes, such that we only need to replace the non-zero derivs
+ std::vector<Coord> val_n_der(n_derivs + 1, Coord(0.0));
+
+ // initialize temp storage variables
+ std::valarray<Coord> d_(order()+1);
+ for(unsigned i = 0; i < size(); i++) {
+ d_[i] = c_[i];
+ }
+
+ unsigned nn = n_derivs + 1;
+ if(n_derivs > order()) {
+ nn = order()+1; // only calculate the non zero derivs
+ }
+ for(unsigned di = 0; di < nn; di++) {
+ //val_n_der[di] = (casteljau_subdivision(t, &d_[0], NULL, NULL, order() - di));
+ val_n_der[di] = bernstein_value_at(t, &d_[0], order() - di);
+ for(unsigned i = 0; i < order() - di; i++) {
+ d_[i] = (order()-di)*(d_[i+1] - d_[i]);
+ }
+ }
+
+ return val_n_der;
+}
+
+void Bezier::subdivide(Coord t, Bezier *left, Bezier *right) const
+{
+ if (left) {
+ left->c_.resize(size());
+ if (right) {
+ right->c_.resize(size());
+ casteljau_subdivision<double>(t, &const_cast<std::valarray<Coord>&>(c_)[0],
+ &left->c_[0], &right->c_[0], order());
+ } else {
+ casteljau_subdivision<double>(t, &const_cast<std::valarray<Coord>&>(c_)[0],
+ &left->c_[0], NULL, order());
+ }
+ } else if (right) {
+ right->c_.resize(size());
+ casteljau_subdivision<double>(t, &const_cast<std::valarray<Coord>&>(c_)[0],
+ NULL, &right->c_[0], order());
+ }
+}
+
+std::pair<Bezier, Bezier> Bezier::subdivide(Coord t) const
+{
+ std::pair<Bezier, Bezier> ret;
+ subdivide(t, &ret.first, &ret.second);
+ return ret;
+}
+
+std::vector<Coord> Bezier::roots() const
+{
+ std::vector<Coord> solutions;
+ find_bezier_roots(solutions, 0, 1);
+ std::sort(solutions.begin(), solutions.end());
+ return solutions;
+}
+
+std::vector<Coord> Bezier::roots(Interval const &ivl) const
+{
+ std::vector<Coord> solutions;
+ find_bernstein_roots(&const_cast<std::valarray<Coord>&>(c_)[0], order(), solutions, 0, ivl.min(), ivl.max());
+ std::sort(solutions.begin(), solutions.end());
+ return solutions;
+}
+
+Bezier Bezier::forward_difference(unsigned k) const
+{
+ Bezier fd(Order(order()-k));
+ unsigned n = fd.size();
+
+ for(unsigned i = 0; i < n; i++) {
+ fd[i] = 0;
+ for(unsigned j = i; j < n; j++) {
+ fd[i] += (((j)&1)?-c_[j]:c_[j])*choose<double>(n, j-i);
+ }
+ }
+ return fd;
+}
+
+Bezier Bezier::elevate_degree() const
+{
+ Bezier ed(Order(order()+1));
+ unsigned n = size();
+ ed[0] = c_[0];
+ ed[n] = c_[n-1];
+ for(unsigned i = 1; i < n; i++) {
+ ed[i] = (i*c_[i-1] + (n - i)*c_[i])/(n);
+ }
+ return ed;
+}
+
+Bezier Bezier::reduce_degree() const
+{
+ if(order() == 0) return *this;
+ Bezier ed(Order(order()-1));
+ unsigned n = size();
+ ed[0] = c_[0];
+ ed[n-1] = c_[n]; // ensure exact endpoints
+ unsigned middle = n/2;
+ for(unsigned i = 1; i < middle; i++) {
+ ed[i] = (n*c_[i] - i*ed[i-1])/(n-i);
+ }
+ for(unsigned i = n-1; i >= middle; i--) {
+ ed[i] = (n*c_[i] - i*ed[n-i])/(i);
+ }
+ return ed;
+}
+
+Bezier Bezier::elevate_to_degree(unsigned newDegree) const
+{
+ Bezier ed = *this;
+ for(unsigned i = degree(); i < newDegree; i++) {
+ ed = ed.elevate_degree();
+ }
+ return ed;
+}
+
+Bezier Bezier::deflate() const
+{
+ if(order() == 0) return *this;
+ unsigned n = order();
+ Bezier b(Order(n-1));
+ for(unsigned i = 0; i < n; i++) {
+ b[i] = (n*c_[i+1])/(i+1);
+ }
+ return b;
+}
+
+SBasis Bezier::toSBasis() const
+{
+ SBasis sb;
+ bezier_to_sbasis(sb, (*this));
+ return sb;
+ //return bezier_to_sbasis(&c_[0], order());
+}
+
+Bezier &Bezier::operator+=(Bezier const &other)
+{
+ if (c_.size() > other.size()) {
+ c_ += other.elevate_to_degree(degree()).c_;
+ } else if (c_.size() < other.size()) {
+ *this = elevate_to_degree(other.degree());
+ c_ += other.c_;
+ } else {
+ c_ += other.c_;
+ }
+ return *this;
+}
+
+Bezier &Bezier::operator-=(Bezier const &other)
+{
+ if (c_.size() > other.size()) {
+ c_ -= other.elevate_to_degree(degree()).c_;
+ } else if (c_.size() < other.size()) {
+ *this = elevate_to_degree(other.degree());
+ c_ -= other.c_;
+ } else {
+ c_ -= other.c_;
+ }
+ return *this;
+}
+
+
+
+Bezier multiply(Bezier const &f, Bezier const &g)
+{
+ unsigned m = f.order();
+ unsigned n = g.order();
+ Bezier h(Bezier::Order(m+n));
+ // h_k = sum_(i+j=k) (m i)f_i (n j)g_j / (m+n k)
+
+ for(unsigned i = 0; i <= m; i++) {
+ const double fi = choose<double>(m,i)*f[i];
+ for(unsigned j = 0; j <= n; j++) {
+ h[i+j] += fi * choose<double>(n,j)*g[j];
+ }
+ }
+ for(unsigned k = 0; k <= m+n; k++) {
+ h[k] /= choose<double>(m+n, k);
+ }
+ return h;
+}
+
+Bezier portion(Bezier const &a, double from, double to)
+{
+ Bezier ret(a);
+
+ bool reverse_result = false;
+ if (from > to) {
+ std::swap(from, to);
+ reverse_result = true;
+ }
+
+ do {
+ if (from == 0) {
+ if (to == 1) {
+ break;
+ }
+ casteljau_subdivision<double>(to, &ret.c_[0], &ret.c_[0], NULL, ret.order());
+ break;
+ }
+ casteljau_subdivision<double>(from, &ret.c_[0], NULL, &ret.c_[0], ret.order());
+ if (to == 1) break;
+ casteljau_subdivision<double>((to - from) / (1 - from), &ret.c_[0], &ret.c_[0], NULL, ret.order());
+ // to protect against numerical inaccuracy in the above expression, we manually set
+ // the last coefficient to a value evaluated directly from the original polynomial
+ ret.c_[ret.order()] = a.valueAt(to);
+ } while(0);
+
+ if (reverse_result) {
+ std::reverse(&ret.c_[0], &ret.c_[0] + ret.c_.size());
+ }
+ return ret;
+}
+
+Bezier derivative(Bezier const &a)
+{
+ //if(a.order() == 1) return Bezier(0.0);
+ if(a.order() == 1) return Bezier(a.c_[1]-a.c_[0]);
+ Bezier der(Bezier::Order(a.order()-1));
+
+ for(unsigned i = 0; i < a.order(); i++) {
+ der.c_[i] = a.order()*(a.c_[i+1] - a.c_[i]);
+ }
+ return der;
+}
+
+Bezier integral(Bezier const &a)
+{
+ Bezier inte(Bezier::Order(a.order()+1));
+
+ inte[0] = 0;
+ for(unsigned i = 0; i < inte.order(); i++) {
+ inte[i+1] = inte[i] + a[i]/(inte.order());
+ }
+ return inte;
+}
+
+OptInterval bounds_fast(Bezier const &b)
+{
+ OptInterval ret = Interval::from_array(&const_cast<Bezier&>(b).c_[0], b.size());
+ return ret;
+}
+
+OptInterval bounds_exact(Bezier const &b)
+{
+ OptInterval ret(b.at0(), b.at1());
+ std::vector<Coord> r = derivative(b).roots();
+ for (unsigned i = 0; i < r.size(); ++i) {
+ ret->expandTo(b.valueAt(r[i]));
+ }
+ return ret;
+}
+
+OptInterval bounds_local(Bezier const &b, OptInterval const &i)
+{
+ //return bounds_local(b.toSBasis(), i);
+ if (i) {
+ return bounds_fast(portion(b, i->min(), i->max()));
+ } else {
+ return OptInterval();
+ }
+}
+
+} // end namespace Geom
+
+/*
+ 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/2geom/bezier.h b/src/2geom/bezier.h
index 51d5211d9..be7df1a6b 100644
--- a/src/2geom/bezier.h
+++ b/src/2geom/bezier.h
@@ -1,13 +1,14 @@
/**
* @file
- * @brief Bezier polynomial
+ * @brief Bernstein-Bezier polynomial
*//*
* Authors:
* MenTaLguY <mental@rydia.net>
* Michael Sloan <mgsloan@gmail.com>
* Nathan Hurst <njh@njhurst.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
*
- * Copyright 2007 Authors
+ * Copyright 2007-2015 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -37,70 +38,23 @@
#ifndef LIB2GEOM_SEEN_BEZIER_H
#define LIB2GEOM_SEEN_BEZIER_H
+#include <algorithm>
#include <valarray>
#include <boost/optional.hpp>
-#include <2geom/coord.h>
#include <2geom/choose.h>
-#include <valarray>
-#include <2geom/math-utils.h>
+#include <2geom/coord.h>
#include <2geom/d2.h>
-#include <2geom/solver.h>
+#include <2geom/math-utils.h>
namespace Geom {
-inline Coord subdivideArr(Coord t, Coord const *v, Coord *left, Coord *right, unsigned order) {
-/*
- * Bernstein :
- * Evaluate a Bernstein function at a particular parameter value
- * Fill in control points for resulting sub-curves.
- *
- */
-
- unsigned N = order+1;
- std::valarray<Coord> row(N);
- for (unsigned i = 0; i < N; i++)
- row[i] = v[i];
-
- // Triangle computation
- const double omt = (1-t);
- if(left)
- left[0] = row[0];
- if(right)
- right[order] = row[order];
- for (unsigned i = 1; i < N; i++) {
- for (unsigned j = 0; j < N - i; j++) {
- row[j] = omt*row[j] + t*row[j+1];
- }
- if(left)
- left[i] = row[0];
- if(right)
- right[order-i] = row[order-i];
- }
- return (row[0]);
-/*
- Coord vtemp[order+1][order+1];
-
- // Copy control points
- std::copy(v, v+order+1, vtemp[0]);
-
- // Triangle computation
- for (unsigned i = 1; i <= order; i++) {
- for (unsigned j = 0; j <= order - i; j++) {
- vtemp[i][j] = lerp(t, vtemp[i-1][j], vtemp[i-1][j+1]);
- }
- }
- if(left != NULL)
- for (unsigned j = 0; j <= order; j++)
- left[j] = vtemp[j][0];
- if(right != NULL)
- for (unsigned j = 0; j <= order; j++)
- right[j] = vtemp[order-j][j];
-
- return (vtemp[order][0]);*/
-}
-
+/** @brief Compute the value of a Bernstein-Bezier polynomial.
+ * This method uses a Horner-like fast evaluation scheme.
+ * @param t Time value
+ * @param c_ Pointer to coefficients
+ * @param n Degree of the polynomial (number of coefficients minus one) */
template <typename T>
-inline T bernsteinValueAt(double t, T const *c_, unsigned n) {
+inline T bernstein_value_at(double t, T const *c_, unsigned n) {
double u = 1.0 - t;
double bc = 1;
double tn = 1;
@@ -113,16 +67,71 @@ inline T bernsteinValueAt(double t, T const *c_, unsigned n) {
return (tmp + tn*t*c_[n]);
}
-class Bezier {
+/** @brief Perform Casteljau subdivision of a Bezier polynomial.
+ * Given an array of coefficients and a time value, computes two new Bernstein-Bezier basis
+ * polynomials corresponding to the \f$[0, t]\f$ and \f$[t, 1]\f$ intervals of the original one.
+ * @param t Time value
+ * @param v Array of input coordinates
+ * @param left Output polynomial corresponding to \f$[0, t]\f$
+ * @param right Output polynomial corresponding to \f$[t, 1]\f$
+ * @param order Order of the input polynomial, equal to one less the number of coefficients
+ * @return Value of the polynomial at @a t */
+template <typename T>
+inline T casteljau_subdivision(double t, T const *v, T *left, T *right, unsigned order) {
+ // The Horner-like scheme gives very slightly different results, but we need
+ // the result of subdivision to match exactly with Bezier's valueAt function.
+ T val = bernstein_value_at(t, v, order);
+
+ if (!left && !right) {
+ return val;
+ }
+
+ if (!right) {
+ if (left != v) {
+ std::copy(v, v + order + 1, left);
+ }
+ for (std::size_t i = order; i > 0; --i) {
+ for (std::size_t j = i; j <= order; ++j) {
+ left[j] = lerp(t, left[j-1], left[j]);
+ }
+ }
+ left[order] = val;
+ return left[order];
+ }
+
+ if (right != v) {
+ std::copy(v, v + order + 1, right);
+ }
+ for (std::size_t i = 1; i <= order; ++i) {
+ if (left) {
+ left[i-1] = right[0];
+ }
+ for (std::size_t j = i; j > 0; --j) {
+ right[j-1] = lerp(t, right[j-1], right[j]);
+ }
+ }
+ right[0] = val;
+ if (left) {
+ left[order] = right[0];
+ }
+ return right[0];
+}
+
+/**
+ * @brief Polynomial in Bernstein-Bezier basis
+ * @ingroup Fragments
+ */
+class Bezier
+ : boost::arithmetic< Bezier, double
+ , boost::additive< Bezier
+ > >
+{
private:
std::valarray<Coord> c_;
friend Bezier portion(const Bezier & a, Coord from, Coord to);
-
friend OptInterval bounds_fast(Bezier const & b);
-
friend Bezier derivative(const Bezier & a);
-
friend class Bernstein;
void
@@ -130,13 +139,14 @@ private:
double l, double r) const;
protected:
- Bezier(Coord const c[], unsigned ord) : c_(c, ord+1){
- //std::copy(c, c+order()+1, &c_[0]);
- }
+ Bezier(Coord const c[], unsigned ord)
+ : c_(c, ord+1)
+ {}
public:
- unsigned int order() const { return c_.size()-1;}
- unsigned int size() const { return c_.size();}
+ unsigned order() const { return c_.size()-1;}
+ unsigned degree() const { return order(); }
+ unsigned size() const { return c_.size();}
Bezier() {}
Bezier(const Bezier& b) :c_(b.c_) {}
@@ -160,262 +170,145 @@ public:
assert(ord.order == order());
}
+ /// @name Construct Bezier polynomials from their control points
+ /// @{
explicit Bezier(Coord c0) : c_(0., 1) {
c_[0] = c0;
}
-
- //Construct an order-1 bezier (linear Bézier)
Bezier(Coord c0, Coord c1) : c_(0., 2) {
c_[0] = c0; c_[1] = c1;
}
-
- //Construct an order-2 bezier (quadratic Bézier)
Bezier(Coord c0, Coord c1, Coord c2) : c_(0., 3) {
c_[0] = c0; c_[1] = c1; c_[2] = c2;
}
-
- //Construct an order-3 bezier (cubic Bézier)
Bezier(Coord c0, Coord c1, Coord c2, Coord c3) : c_(0., 4) {
c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3;
}
+ Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4) : c_(0., 5) {
+ c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+ }
+ Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4,
+ Coord c5) : c_(0., 6) {
+ c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+ c_[5] = c5;
+ }
+ Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4,
+ Coord c5, Coord c6) : c_(0., 7) {
+ c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+ c_[5] = c5; c_[6] = c6;
+ }
+ Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4,
+ Coord c5, Coord c6, Coord c7) : c_(0., 8) {
+ c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+ c_[5] = c5; c_[6] = c6; c_[7] = c7;
+ }
+ Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4,
+ Coord c5, Coord c6, Coord c7, Coord c8) : c_(0., 9) {
+ c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+ c_[5] = c5; c_[6] = c6; c_[7] = c7; c_[8] = c8;
+ }
+ Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4,
+ Coord c5, Coord c6, Coord c7, Coord c8, Coord c9) : c_(0., 10) {
+ c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+ c_[5] = c5; c_[6] = c6; c_[7] = c7; c_[8] = c8; c_[9] = c9;
+ }
- void resize (unsigned int n, Coord v = 0)
- {
- c_.resize (n, v);
+ template <typename Iter>
+ Bezier(Iter first, Iter last) {
+ c_.resize(std::distance(first, last));
+ for (std::size_t i = 0; first != last; ++first, ++i) {
+ c_[i] = *first;
+ }
}
+ Bezier(std::vector<Coord> const &vec)
+ : c_(&vec[0], vec.size())
+ {}
+ /// @}
- void clear()
- {
+ void resize (unsigned int n, Coord v = 0) {
+ c_.resize (n, v);
+ }
+ void clear() {
c_.resize(0);
}
- inline unsigned degree() const { return order(); }
-
//IMPL: FragmentConcept
typedef Coord output_type;
- inline bool isZero(double eps=EPSILON) const {
+ bool isZero(double eps=EPSILON) const {
for(unsigned i = 0; i <= order(); i++) {
if( ! are_near(c_[i], 0., eps) ) return false;
}
return true;
}
- inline bool isConstant(double eps=EPSILON) const {
+ bool isConstant(double eps=EPSILON) const {
for(unsigned i = 1; i <= order(); i++) {
if( ! are_near(c_[i], c_[0], eps) ) return false;
}
return true;
}
- inline bool isFinite() const {
+ bool isFinite() const {
for(unsigned i = 0; i <= order(); i++) {
if(!IS_FINITE(c_[i])) return false;
}
return true;
}
- inline Coord at0() const { return c_[0]; }
- inline Coord at1() const { return c_[order()]; }
-
- inline Coord valueAt(double t) const {
- int n = order();
- double u, bc, tn, tmp;
- int i;
- u = 1.0 - t;
- bc = 1;
- tn = 1;
- tmp = c_[0]*u;
- for(i=1; i<n; i++){
- tn = tn*t;
- bc = bc*(n-i+1)/i;
- tmp = (tmp + tn*bc*c_[i])*u;
- }
- return (tmp + tn*t*c_[n]);
+ Coord at0() const { return c_[0]; }
+ Coord &at0() { return c_[0]; }
+ Coord at1() const { return c_[order()]; }
+ Coord &at1() { return c_[order()]; }
+
+ Coord valueAt(double t) const {
+ return bernstein_value_at(t, &c_[0], order());
}
- inline Coord operator()(double t) const { return valueAt(t); }
+ Coord operator()(double t) const { return valueAt(t); }
SBasis toSBasis() const;
- //Only mutator
- inline Coord &operator[](unsigned ix) { return c_[ix]; }
- inline Coord const &operator[](unsigned ix) const { return const_cast<std::valarray<Coord>&>(c_)[ix]; }
- //inline Coord const &operator[](unsigned ix) const { return c_[ix]; }
- inline void setPoint(unsigned ix, double val) { c_[ix] = val; }
-
- /**
- * The size of the returned vector equals n_derivs+1.
- */
- std::vector<Coord> valueAndDerivatives(Coord t, unsigned n_derivs) const {
- /* This is inelegant, as it uses several extra stores. I think there might be a way to
- * evaluate roughly in situ. */
-
- // initialize return vector with zeroes, such that we only need to replace the non-zero derivs
- std::vector<Coord> val_n_der(n_derivs + 1, Coord(0.0));
-
- // initialize temp storage variables
- std::valarray<Coord> d_(order()+1);
- for(unsigned i = 0; i < size(); i++) {
- d_[i] = c_[i];
- }
+ Coord &operator[](unsigned ix) { return c_[ix]; }
+ Coord const &operator[](unsigned ix) const { return const_cast<std::valarray<Coord>&>(c_)[ix]; }
- unsigned nn = n_derivs + 1;
- if(n_derivs > order()) {
- nn = order()+1; // only calculate the non zero derivs
- }
- for(unsigned di = 0; di < nn; di++) {
- //val_n_der[di] = (subdivideArr(t, &d_[0], NULL, NULL, order() - di));
- val_n_der[di] = bernsteinValueAt(t, &d_[0], order() - di);
- for(unsigned i = 0; i < order() - di; i++) {
- d_[i] = (order()-di)*(d_[i+1] - d_[i]);
- }
- }
+ void setCoeff(unsigned ix, double val) { c_[ix] = val; }
- return val_n_der;
- }
+ // The size of the returned vector equals n_derivs+1.
+ std::vector<Coord> valueAndDerivatives(Coord t, unsigned n_derivs) const;
- std::pair<Bezier, Bezier > subdivide(Coord t) const {
- Bezier a(Bezier::Order(*this)), b(Bezier::Order(*this));
- subdivideArr(t, &const_cast<std::valarray<Coord>&>(c_)[0], &a.c_[0], &b.c_[0], order());
- return std::pair<Bezier, Bezier >(a, b);
- }
+ void subdivide(Coord t, Bezier *left, Bezier *right) const;
+ std::pair<Bezier, Bezier> subdivide(Coord t) const;
- std::vector<double> roots() const {
- std::vector<double> solutions;
- find_bezier_roots(solutions, 0, 1);
- return solutions;
- }
- std::vector<double> roots(Interval const ivl) const {
- std::vector<double> solutions;
- find_bernstein_roots(&const_cast<std::valarray<Coord>&>(c_)[0], order(), solutions, 0, ivl.min(), ivl.max());
- return solutions;
- }
+ std::vector<Coord> roots() const;
+ std::vector<Coord> roots(Interval const &ivl) const;
- Bezier forward_difference(unsigned k) {
- Bezier fd(Order(order()-k));
- unsigned n = fd.size();
-
- for(unsigned i = 0; i < n; i++) {
- fd[i] = 0;
- for(unsigned j = i; j < n; j++) {
- fd[i] += (((j)&1)?-c_[j]:c_[j])*choose<double>(n, j-i);
- }
- }
- return fd;
- }
-
- Bezier elevate_degree() const {
- Bezier ed(Order(order()+1));
- unsigned n = size();
- ed[0] = c_[0];
- ed[n] = c_[n-1];
- for(unsigned i = 1; i < n; i++) {
- ed[i] = (i*c_[i-1] + (n - i)*c_[i])/(n);
- }
- return ed;
- }
-
- Bezier reduce_degree() const {
- if(order() == 0) return *this;
- Bezier ed(Order(order()-1));
- unsigned n = size();
- ed[0] = c_[0];
- ed[n-1] = c_[n]; // ensure exact endpoints
- unsigned middle = n/2;
- for(unsigned i = 1; i < middle; i++) {
- ed[i] = (n*c_[i] - i*ed[i-1])/(n-i);
- }
- for(unsigned i = n-1; i >= middle; i--) {
- ed[i] = (n*c_[i] - i*ed[n-i])/(i);
- }
- return ed;
- }
+ Bezier forward_difference(unsigned k) const;
+ Bezier elevate_degree() const;
+ Bezier reduce_degree() const;
+ Bezier elevate_to_degree(unsigned newDegree) const;
+ Bezier deflate() const;
- Bezier elevate_to_degree(unsigned newDegree) const {
- Bezier ed = *this;
- for(unsigned i = degree(); i < newDegree; i++) {
- ed = ed.elevate_degree();
- }
- return ed;
+ // basic arithmetic operators
+ Bezier &operator+=(double v) {
+ c_ += v;
+ return *this;
}
-
- Bezier deflate() {
- if(order() == 0) return *this;
- unsigned n = order();
- Bezier b(Order(n-1));
- for(unsigned i = 0; i < n; i++) {
- b[i] = (n*c_[i+1])/(i+1);
- }
- return b;
+ Bezier &operator-=(double v) {
+ c_ -= v;
+ return *this;
}
-};
-
-
-void bezier_to_sbasis (SBasis & sb, Bezier const& bz);
-
-inline
-Bezier multiply(Bezier const& f, Bezier const& g) {
- unsigned m = f.order();
- unsigned n = g.order();
- Bezier h(Bezier::Order(m+n));
- // h_k = sum_(i+j=k) (m i)f_i (n j)g_j / (m+n k)
-
- for(unsigned i = 0; i <= m; i++) {
- const double fi = choose<double>(m,i)*f[i];
- for(unsigned j = 0; j <= n; j++) {
- h[i+j] += fi * choose<double>(n,j)*g[j];
- }
+ Bezier &operator*=(double v) {
+ c_ *= v;
+ return *this;
}
- for(unsigned k = 0; k <= m+n; k++) {
- h[k] /= choose<double>(m+n, k);
+ Bezier &operator/=(double v) {
+ c_ /= v;
+ return *this;
}
- return h;
-}
-
-inline
-SBasis Bezier::toSBasis() const {
- SBasis sb;
- bezier_to_sbasis(sb, (*this));
- return sb;
- //return bezier_to_sbasis(&c_[0], order());
-}
-
-//TODO: implement others
-inline Bezier operator+(const Bezier & a, double v) {
- Bezier result = Bezier(Bezier::Order(a));
- for(unsigned i = 0; i <= a.order(); i++)
- result[i] = a[i] + v;
- return result;
-}
-
-inline Bezier operator-(const Bezier & a, double v) {
- Bezier result = Bezier(Bezier::Order(a));
- for(unsigned i = 0; i <= a.order(); i++)
- result[i] = a[i] - v;
- return result;
-}
-
-inline Bezier& operator+=(Bezier & a, double v) {
- for(unsigned i = 0; i <= a.order(); ++i)
- a[i] = a[i] + v;
- return a;
-}
+ Bezier &operator+=(Bezier const &other);
+ Bezier &operator-=(Bezier const &other);
+};
-inline Bezier& operator-=(Bezier & a, double v) {
- for(unsigned i = 0; i <= a.order(); ++i)
- a[i] = a[i] - v;
- return a;
-}
-inline Bezier operator*(const Bezier & a, double v) {
- Bezier result = Bezier(Bezier::Order(a));
- for(unsigned i = 0; i <= a.order(); i++)
- result[i] = a[i] * v;
- return result;
-}
+void bezier_to_sbasis (SBasis &sb, Bezier const &bz);
-inline Bezier operator/(const Bezier & a, double v) {
- Bezier result = Bezier(Bezier::Order(a));
- for(unsigned i = 0; i <= a.order(); i++)
- result[i] = a[i] / v;
- return result;
-}
+Bezier multiply(Bezier const &f, Bezier const &g);
inline Bezier reverse(const Bezier & a) {
Bezier result = Bezier(Bezier::Order(a));
@@ -424,20 +317,7 @@ inline Bezier reverse(const Bezier & a) {
return result;
}
-inline Bezier portion(const Bezier & a, double from, double to) {
- //TODO: implement better?
- std::valarray<Coord> res(a.order() + 1);
- if(from == 0) {
- if(to == 1) { return Bezier(a); }
- subdivideArr(to, &const_cast<Bezier&>(a).c_[0], &res[0], NULL, a.order());
- return Bezier(&res[0], a.order());
- }
- subdivideArr(from, &const_cast<Bezier&>(a).c_[0], NULL, &res[0], a.order());
- if(to == 1) return Bezier(&res[0], a.order());
- std::valarray<Coord> res2(a.order()+1);
- subdivideArr((to - from)/(1 - from), &res[0], &res2[0], NULL, a.order());
- return Bezier(&res2[0], a.order());
-}
+Bezier portion(const Bezier & a, double from, double to);
// XXX Todo: how to handle differing orders
inline std::vector<Point> bezier_points(const D2<Bezier > & a) {
@@ -450,52 +330,19 @@ inline std::vector<Point> bezier_points(const D2<Bezier > & a) {
return result;
}
-inline Bezier derivative(const Bezier & a) {
- //if(a.order() == 1) return Bezier(0.0);
- if(a.order() == 1) return Bezier(a.c_[1]-a.c_[0]);
- Bezier der(Bezier::Order(a.order()-1));
-
- for(unsigned i = 0; i < a.order(); i++) {
- der.c_[i] = a.order()*(a.c_[i+1] - a.c_[i]);
- }
- return der;
-}
-
-inline Bezier integral(const Bezier & a) {
- Bezier inte(Bezier::Order(a.order()+1));
-
- inte[0] = 0;
- for(unsigned i = 0; i < inte.order(); i++) {
- inte[i+1] = inte[i] + a[i]/(inte.order());
- }
- return inte;
-}
-
-inline OptInterval bounds_fast(Bezier const & b) {
- OptInterval ret = Interval::from_array(&const_cast<Bezier&>(b).c_[0], b.size());
- return ret;
-}
-
-//TODO: better bounds exact
-inline OptInterval bounds_exact(Bezier const & b) {
- return bounds_exact(b.toSBasis());
-}
-
-inline OptInterval bounds_local(Bezier const & b, OptInterval i) {
- //return bounds_local(b.toSBasis(), i);
- if (i) {
- return bounds_fast(portion(b, i->min(), i->max()));
- } else {
- return OptInterval();
- }
-}
+Bezier derivative(Bezier const &a);
+Bezier integral(Bezier const &a);
+OptInterval bounds_fast(Bezier const &b);
+OptInterval bounds_exact(Bezier const &b);
+OptInterval bounds_local(Bezier const &b, OptInterval const &i);
-inline std::ostream &operator<< (std::ostream &out_file, const Bezier & b) {
- out_file << "Bezier(";
- for(unsigned i = 0; i < b.size(); i++) {
- out_file << b[i] << ", ";
+inline std::ostream &operator<< (std::ostream &os, const Bezier & b) {
+ os << "Bezier(";
+ for(unsigned i = 0; i < b.order(); i++) {
+ os << format_coord_nice(b[i]) << ", ";
}
- return out_file << ")";
+ os << format_coord_nice(b[b.order()]) << ")";
+ return os;
}
}
diff --git a/src/2geom/cairo-path-sink.cpp b/src/2geom/cairo-path-sink.cpp
new file mode 100644
index 000000000..244a08ba4
--- /dev/null
+++ b/src/2geom/cairo-path-sink.cpp
@@ -0,0 +1,123 @@
+/**
+ * @file
+ * @brief Path sink for Cairo contexts
+ *//*
+ * Copyright 2014 Krzysztof Kosiński
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include <cairo.h>
+#include <2geom/cairo-path-sink.h>
+#include <2geom/elliptical-arc.h>
+
+namespace Geom {
+
+CairoPathSink::CairoPathSink(cairo_t *cr)
+ : _cr(cr)
+{}
+
+void CairoPathSink::moveTo(Point const &p)
+{
+ cairo_move_to(_cr, p[X], p[Y]);
+ _current_point = p;
+}
+
+void CairoPathSink::lineTo(Point const &p)
+{
+ cairo_line_to(_cr, p[X], p[Y]);
+ _current_point = p;
+}
+
+void CairoPathSink::curveTo(Point const &p1, Point const &p2, Point const &p3)
+{
+ cairo_curve_to(_cr, p1[X], p1[Y], p2[X], p2[Y], p3[X], p3[Y]);
+ _current_point = p3;
+}
+
+void CairoPathSink::quadTo(Point const &p1, Point const &p2)
+{
+ // degree-elevate to cubic Bezier, since Cairo doesn't do quad Beziers
+ // google "Bezier degree elevation" for more info
+ Point q1 = (1./3.) * _current_point + (2./3.) * p1;
+ Point q2 = (2./3.) * p1 + (1./3.) * p2;
+ // q3 = p2
+ cairo_curve_to(_cr, q1[X], q1[Y], q2[X], q2[Y], p2[X], p2[Y]);
+ _current_point = p2;
+}
+
+void CairoPathSink::arcTo(double rx, double ry, double angle,
+ bool large_arc, bool sweep, Point const &p)
+{
+ EllipticalArc arc(_current_point, rx, ry, angle, large_arc, sweep, p);
+ // Cairo only does circular arcs.
+ // To do elliptical arcs, we must use a temporary transform.
+ Affine uct = arc.unitCircleTransform();
+
+ // TODO move Cairo-2Geom matrix conversion into a common location
+ cairo_matrix_t cm;
+ cm.xx = uct[0];
+ cm.xy = uct[2];
+ cm.x0 = uct[4];
+ cm.yx = uct[1];
+ cm.yy = uct[3];
+ cm.y0 = uct[5];
+
+ cairo_save(_cr);
+ cairo_transform(_cr, &cm);
+ if (sweep) {
+ cairo_arc(_cr, 0, 0, 1, arc.initialAngle(), arc.finalAngle());
+ } else {
+ cairo_arc_negative(_cr, 0, 0, 1, arc.initialAngle(), arc.finalAngle());
+ }
+ _current_point = p;
+ cairo_restore(_cr);
+
+ /* Note that an extra linear segment will be inserted before the arc
+ * if Cairo considers the current point distinct from the initial point
+ * of the arc; we could partially alleviate this by not emitting
+ * linear segments that are followed by arc segments, but this would require
+ * buffering the input curves. */
+}
+
+void CairoPathSink::closePath()
+{
+ cairo_close_path(_cr);
+}
+
+void CairoPathSink::flush() {}
+
+} // namespace Geom
+
+/*
+ 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/2geom/conjugate_gradient.h b/src/2geom/cairo-path-sink.h
index 4f500c0e6..9fec7e0ab 100644
--- a/src/2geom/conjugate_gradient.h
+++ b/src/2geom/cairo-path-sink.h
@@ -1,8 +1,8 @@
/**
* @file
- * @brief Routines for solving a system of linear equations using the conjugate gradient method
+ * @brief Path sink for Cairo contexts
*//*
- * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Copyright 2014 Krzysztof Kosiński
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -29,29 +29,52 @@
*
*/
-#ifndef _2GEOM_CONJUGATE_GRADIENT_H
-#define _2GEOM_CONJUGATE_GRADIENT_H
+#ifndef LIB2GEOM_SEEN_CAIRO_PATH_SINK_H
+#define LIB2GEOM_SEEN_CAIRO_PATH_SINK_H
-#include <valarray>
+#include <2geom/path-sink.h>
+#include <cairo.h>
+
+namespace Geom {
-namespace Geom
-{
-double
-inner(std::valarray<double> const &x,
- std::valarray<double> const &y);
+/** @brief Output paths to a Cairo drawing context
+ *
+ * This class converts from 2Geom path representation to the Cairo representation.
+ * Use it to simplify visualizing the results of 2Geom operations with the Cairo library,
+ * for example:
+ * @code
+ * CairoPathSink sink(cr);
+ * sink.feed(pv);
+ * cairo_stroke(cr);
+ * @endcode
+ *
+ * Currently the flush method is a no-op, but this is not guaranteed
+ * to hold forever.
+ */
+class CairoPathSink
+ : public PathSink
+{
+public:
+ CairoPathSink(cairo_t *cr);
-void
-conjugate_gradient(std::valarray<double> const &A,
- std::valarray<double> &x,
- std::valarray<double> const &b,
- unsigned n, double tol,
- unsigned max_iterations, bool ortho1);
+ void moveTo(Point const &p);
+ void lineTo(Point const &p);
+ void curveTo(Point const &c0, Point const &c1, Point const &p);
+ void quadTo(Point const &c, Point const &p);
+ void arcTo(Coord rx, Coord ry, Coord angle,
+ bool large_arc, bool sweep, Point const &p);
+ void closePath();
+ void flush();
-} // namespace Geom
+private:
+ cairo_t *_cr;
+ Point _current_point;
+};
-#endif // _2GEOM_CONJUGATE_GRADIENT_H
+}
+#endif // !LIB2GEOM_SEEN_CAIRO_PATH_SINK_H
/*
Local Variables:
mode:c++
diff --git a/src/2geom/circle.cpp b/src/2geom/circle.cpp
index d021882ea..0b1dddc8e 100644
--- a/src/2geom/circle.cpp
+++ b/src/2geom/circle.cpp
@@ -1,10 +1,11 @@
-/*
- * Circle Curve
- *
+/** @file
+ * @brief Circle shape
+ *//*
* Authors:
- * Marco Cecchetti <mrcekets at gmail.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
*
- * Copyright 2008 authors
+ * Copyright 2008-2014 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -30,56 +31,54 @@
* the specific language governing rights and limitations.
*/
-
#include <2geom/circle.h>
#include <2geom/ellipse.h>
#include <2geom/svg-elliptical-arc.h>
#include <2geom/numeric/fitting-tool.h>
#include <2geom/numeric/fitting-model.h>
+namespace Geom {
-
-namespace Geom
+void Circle::setCoefficients(Coord A, Coord B, Coord C, Coord D)
{
-
-void Circle::set(double A, double B, double C, double D)
-{
- if (A == 0)
- {
+ if (A == 0) {
THROW_RANGEERROR("square term coefficient == 0");
}
//std::cerr << "B = " << B << " C = " << C << " D = " << D << std::endl;
- double b = B / A;
- double c = C / A;
- double d = D / A;
+ Coord b = B / A;
+ Coord c = C / A;
+ Coord d = D / A;
- m_centre[X] = -b/2;
- m_centre[Y] = -c/2;
- double r2 = m_centre[X] * m_centre[X] + m_centre[Y] * m_centre[Y] - d;
+ _center[X] = -b/2;
+ _center[Y] = -c/2;
+ Coord r2 = _center[X] * _center[X] + _center[Y] * _center[Y] - d;
- if (r2 < 0)
- {
+ if (r2 < 0) {
THROW_RANGEERROR("ray^2 < 0");
}
- m_ray = std::sqrt(r2);
+ _radius = std::sqrt(r2);
}
-void Circle::set(std::vector<Point> const& points)
+void Circle::fit(std::vector<Point> const& points)
{
size_t sz = points.size();
- if (sz < 3)
- {
+ if (sz < 2) {
THROW_RANGEERROR("fitting error: too few points passed");
}
+ if (sz == 2) {
+ _center = points[0] * 0.5 + points[1] * 0.5;
+ _radius = distance(points[0], points[1]) / 2;
+ return;
+ }
+
NL::LFMCircle model;
NL::least_squeares_fitter<NL::LFMCircle> fitter(model, sz);
- for (size_t i = 0; i < sz; ++i)
- {
+ for (size_t i = 0; i < sz; ++i) {
fitter.append(points[i]);
}
fitter.update();
@@ -93,11 +92,11 @@ void Circle::set(std::vector<Point> const& points)
*/
EllipticalArc *
Circle::arc(Point const& initial, Point const& inner, Point const& final,
- bool _svg_compliant)
+ bool svg_compliant)
{
// TODO native implementation!
- Ellipse e(center(X), center(Y), ray(), ray(), 0);
- return e.arc(initial, inner, final, _svg_compliant);
+ Ellipse e(_center[X], _center[Y], _radius, _radius, 0);
+ return e.arc(initial, inner, final, svg_compliant);
}
D2<SBasis> Circle::toSBasis()
@@ -108,13 +107,13 @@ D2<SBasis> Circle::toSBasis()
B[0] = cos(bo,4);
B[1] = sin(bo,4);
- B = B * m_ray + m_centre;
+ B = B * _radius + _center;
return B;
}
void
-Circle::getPath(std::vector<Path> &path_out) {
+Circle::getPath(PathVector &path_out) {
Path pb;
D2<SBasis> B = toSBasis();
diff --git a/src/2geom/circle.h b/src/2geom/circle.h
index ca9241047..3c2115b12 100644
--- a/src/2geom/circle.h
+++ b/src/2geom/circle.h
@@ -1,11 +1,11 @@
-/**
- * \file
- * \brief Circles
+/** @file
+ * @brief Circle shape
*//*
* Authors:
- * Marco Cecchetti <mrcekets at gmail.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
*
- * Copyright 2008 authors
+ * Copyright 2008-2014 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -34,83 +34,72 @@
#ifndef LIB2GEOM_SEEN_CIRCLE_H
#define LIB2GEOM_SEEN_CIRCLE_H
-#include <vector>
#include <2geom/point.h>
-#include <2geom/exception.h>
-#include <2geom/path.h>
+#include <2geom/forward.h>
+#include <2geom/transforms.h>
namespace Geom {
class EllipticalArc;
+/** @brief Set of all points at a fixed distance from the center
+ * @ingroup Shapes */
class Circle
+ : MultipliableNoncommutative< Circle, Translate
+ , MultipliableNoncommutative< Circle, Rotate
+ , MultipliableNoncommutative< Circle, Zoom
+ > > >
{
- public:
- Circle()
- {}
-
- Circle(double cx, double cy, double r)
- : m_centre(cx, cy), m_ray(r)
- {
- }
-
- Circle(Point center, double r)
- : m_centre(center), m_ray(r)
- {
- }
+ Point _center;
+ Coord _radius;
- Circle(double A, double B, double C, double D)
- {
- set(A, B, C, D);
- }
-
- Circle(std::vector<Point> const& points)
- {
- set(points);
- }
+public:
+ Circle() {}
+ Circle(Coord cx, Coord cy, Coord r)
+ : _center(cx, cy), _radius(r)
+ {}
+ Circle(Point const &center, Coord r)
+ : _center(center), _radius(r)
+ {}
- void set(double cx, double cy, double r)
- {
- m_centre[X] = cx;
- m_centre[Y] = cy;
- m_ray = r;
+ Circle(Coord A, Coord B, Coord C, Coord D) {
+ setCoefficients(A, B, C, D);
}
-
// build a circle by its implicit equation:
// Ax^2 + Ay^2 + Bx + Cy + D = 0
- void set(double A, double B, double C, double D);
+ void setCoefficients(Coord A, Coord B, Coord C, Coord D);
- // build up the best fitting circle wrt the passed points
- // prerequisite: at least 3 points must be passed
- void set(std::vector<Point> const& points);
+ /** @brief Fit the circle to the passed points using the least squares method.
+ * @param points Samples at the perimeter of the circle */
+ void fit(std::vector<Point> const &points);
EllipticalArc *
arc(Point const& initial, Point const& inner, Point const& final,
- bool _svg_compliant = true);
+ bool svg_compliant = true);
D2<SBasis> toSBasis();
- void getPath(std::vector<Path> &path_out);
+ void getPath(PathVector &path_out);
- Point center() const
- {
- return m_centre;
- }
+ Point center() const { return _center; }
+ Coord center(Dim2 d) const { return _center[d]; }
+ Coord radius() const { return _radius; }
- Coord center(Dim2 d) const
- {
- return m_centre[d];
- }
+ void setCenter(Point const &p) { _center = p; }
+ void setRadius(Coord c) { _radius = c; }
- Coord ray() const
- {
- return m_ray;
+ Circle &operator*=(Translate const &t) {
+ _center *= t;
+ return *this;
+ }
+ Circle &operator*=(Rotate const &) {
+ return *this;
+ }
+ Circle &operator*=(Zoom const &z) {
+ _center *= z;
+ _radius *= z.scale();
+ return *this;
}
-
-
- private:
- Point m_centre;
- Coord m_ray;
};
} // end namespace Geom
diff --git a/src/2geom/circulator.h b/src/2geom/circulator.h
index 9671ce4a9..06e4d2c4e 100644
--- a/src/2geom/circulator.h
+++ b/src/2geom/circulator.h
@@ -1,5 +1,4 @@
-/**
- * @file circulator.h
+/** @file
* @brief Circular iterator adapter
*//*
* Copyright 2006 MenTaLguY <mental@rydia.net>
@@ -29,8 +28,8 @@
*
*/
-#ifndef SEEN_Circulator_H
-#define SEEN_Circulator_H
+#ifndef LIB2GEOM_SEEN_CIRCULATOR_H
+#define LIB2GEOM_SEEN_CIRCULATOR_H
#include <iterator>
@@ -137,7 +136,7 @@ Geom::Circulator<T> operator+(int n, Geom::Circulator<T> const &c) {
return c + n;
}
-#endif // SEEN_Circulator_H
+#endif // LIB2GEOM_SEEN_CIRCULATOR_H
/*
Local Variables:
diff --git a/src/2geom/concepts.h b/src/2geom/concepts.h
index c89c3a224..f571ddc60 100644
--- a/src/2geom/concepts.h
+++ b/src/2geom/concepts.h
@@ -35,8 +35,9 @@
#include <2geom/interval.h>
#include <2geom/point.h>
#include <2geom/rect.h>
+#include <2geom/intersection.h>
#include <vector>
-#include <boost/concept_check.hpp>
+#include <boost/concept/assert.hpp>
#include <2geom/forward.h>
namespace Geom {
@@ -71,11 +72,13 @@ struct FragmentConcept {
SbType sb;
void constraints() {
t = T(o);
- b = t.isZero();
- b = t.isConstant();
+ b = t.isZero(d);
+ b = t.isConstant(d);
b = t.isFinite();
o = t.at0();
o = t.at1();
+ t.at0() = o;
+ t.at1() = o;
o = t.valueAt(d);
o = t(d);
v = t.valueAndDerivatives(d, u-1);
@@ -97,6 +100,34 @@ struct FragmentConcept {
};
template <typename T>
+struct ShapeConcept {
+ typedef typename ShapeTraits<T>::TimeType Time;
+ typedef typename ShapeTraits<T>::IntervalType Interval;
+ typedef typename ShapeTraits<T>::AffineClosureType AffineClosure;
+ //typedef typename ShapeTraits<T>::IntersectionType Isect;
+
+ T shape;
+ Time t;
+ Point p;
+ AffineClosure ac;
+ Affine m;
+ Coord c;
+ bool bool_;
+
+ void constraints() {
+ p = shape.pointAt(t);
+ c = shape.valueAt(t, X);
+ //ivec = shape.intersect(other);
+ t = shape.nearestTime(p);
+ ac = shape;
+ ac *= m;
+ bool_ = (shape == shape);
+ bool_ = (shape != shape);
+ bool_ = shape.isDegenerate();
+ }
+};
+
+template <typename T>
inline T portion(const T& t, const Interval& i) { return portion(t, i.min(), i.max()); }
template <typename T>
diff --git a/src/2geom/conic_section_clipper.h b/src/2geom/conic_section_clipper.h
index a02cda4d3..38bba338e 100644
--- a/src/2geom/conic_section_clipper.h
+++ b/src/2geom/conic_section_clipper.h
@@ -1,7 +1,6 @@
-/**
- * \file
- * \brief Conic section clipping with respect to a rectangle
- *
+/** @file
+ * @brief Conic section clipping with respect to a rectangle
+ *//*
* Authors:
* Marco Cecchetti <mrcekets at gmail>
*
@@ -34,8 +33,8 @@
-#ifndef _2GEOM_CONIC_SECTION_CLIPPER_H_
-#define _2GEOM_CONIC_SECTION_CLIPPER_H_
+#ifndef LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_H
+#define LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_H
#undef CLIP_WITH_CAIRO_SUPPORT
diff --git a/src/2geom/conic_section_clipper_cr.h b/src/2geom/conic_section_clipper_cr.h
index 31f5a4269..6c62494de 100644
--- a/src/2geom/conic_section_clipper_cr.h
+++ b/src/2geom/conic_section_clipper_cr.h
@@ -1,7 +1,6 @@
-/**
- * \file
- * \brief Conic section clipping with respect to a rectangle
- *
+/** @file
+ * @brief Conic section clipping with respect to a rectangle
+ *//*
* Authors:
* Marco Cecchetti <mrcekets at gmail>
*
@@ -39,8 +38,8 @@
////////////////////////////////////////////////////////////////////////////////
-#ifndef _2GEOM_CONIC_SECTION_CLIPPER_CR_H_
-#define _2GEOM_CONIC_SECTION_CLIPPER_CR_H_
+#ifndef LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_CR_H
+#define LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_CR_H
#define CLIP_WITH_CAIRO_SUPPORT
diff --git a/src/2geom/conic_section_clipper_impl.cpp b/src/2geom/conic_section_clipper_impl.cpp
index 2867e243c..c57307974 100644
--- a/src/2geom/conic_section_clipper_impl.cpp
+++ b/src/2geom/conic_section_clipper_impl.cpp
@@ -173,7 +173,7 @@ bool CLIPPER_CLASS::intersect (std::vector<Point> & crossing_points) const
cpts.size())
// remove duplicates
- std::sort (cpts.begin(), cpts.end(), Point::LexOrder<X>());
+ std::sort (cpts.begin(), cpts.end(), Point::LexLess<X>());
cpts.erase (std::unique (cpts.begin(), cpts.end()), cpts.end());
@@ -203,7 +203,7 @@ bool CLIPPER_CLASS::intersect (std::vector<Point> & crossing_points) const
inline
double signed_triangle_area (Point const& p1, Point const& p2, Point const& p3)
{
- return (cross(p3, p2) - cross(p3, p1) + cross(p2, p1));
+ return (cross(p2, p3) - cross(p1, p3) + cross(p1, p2));
}
@@ -216,6 +216,8 @@ double signed_triangle_area (Point const& p1, Point const& p2, Point const& p3)
*/
bool CLIPPER_CLASS::are_paired (Point& M, const Point & P1, const Point & P2) const
{
+ using std::swap;
+
/*
* we looks for the points on the conic whose tangent is parallel to the
* arc chord P1P2, they will be extrema of the conic arc P1P2 wrt the
@@ -257,9 +259,8 @@ bool CLIPPER_CLASS::are_paired (Point& M, const Point & P1, const Point & P2) co
if (sgn(side0) == sgn(side1))
{
- if (std::fabs(side0) > std::fabs(side1))
- {
- std::swap (extrema[0], extrema[1]);
+ if (std::fabs(side0) > std::fabs(side1)) {
+ swap(extrema[0], extrema[1]);
}
extrema.pop_back();
}
@@ -371,6 +372,8 @@ void CLIPPER_CLASS::pairing (std::vector<Point> & paired_points,
*/
bool CLIPPER_CLASS::clip (std::vector<RatQuad> & arcs)
{
+ using std::swap;
+
arcs.clear();
std::vector<Point> crossing_points;
std::vector<Point> paired_points;
@@ -454,14 +457,14 @@ bool CLIPPER_CLASS::clip (std::vector<RatQuad> & arcs)
double angle = cs.axis_angle();
Line axis1 (*c, angle);
rts = cs.roots (axis1);
- if (rts[0] > rts[1]) std::swap (rts[0], rts[1]);
+ if (rts[0] > rts[1]) swap (rts[0], rts[1]);
paired_points[0] = axis1.pointAt (rts[0]);
paired_points[1] = axis1.pointAt (rts[1]);
paired_points[2] = paired_points[1];
paired_points[3] = paired_points[0];
Line axis2 (*c, angle + M_PI/2);
rts = cs.roots (axis2);
- if (rts[0] > rts[1]) std::swap (rts[0], rts[1]);
+ if (rts[0] > rts[1]) swap (rts[0], rts[1]);
inner_points.push_back (axis2.pointAt (rts[0]));
inner_points.push_back (axis2.pointAt (rts[1]));
}
diff --git a/src/2geom/conic_section_clipper_impl.h b/src/2geom/conic_section_clipper_impl.h
index 37415df97..e38a6d416 100644
--- a/src/2geom/conic_section_clipper_impl.h
+++ b/src/2geom/conic_section_clipper_impl.h
@@ -1,7 +1,6 @@
-/**
- * \file
- * \brief Conic section clipping with respect to a rectangle
- *
+/** @file
+ * @brief Conic section clipping with respect to a rectangle
+ *//*
* Authors:
* Marco Cecchetti <mrcekets at gmail>
*
@@ -31,11 +30,8 @@
* the specific language governing rights and limitations.
*/
-
-
-
-#ifndef _2GEOM_CONIC_SECTION_CLIPPER_IMPL_H_
-#define _2GEOM_CONIC_SECTION_CLIPPER_IMPL_H_
+#ifndef LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_IMPL_H
+#define LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_IMPL_H
#include <2geom/conicsec.h>
@@ -336,13 +332,7 @@ void CLIPPER_CLASS::rsplit (std::list<Point> & points,
} // end namespace Geom
-
-
-
-#endif // _2GEOM_CONIC_SECTION_CLIPPER_IMPL_H_
-
-
-
+#endif // LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_IMPL_H
/*
Local Variables:
diff --git a/src/2geom/conicsec.cpp b/src/2geom/conicsec.cpp
index 367dc2503..889797de3 100644
--- a/src/2geom/conicsec.cpp
+++ b/src/2geom/conicsec.cpp
@@ -40,44 +40,16 @@
#include <sstream>
#include <stdexcept>
-
-
-
-
namespace Geom
{
LineSegment intersection(Line l, Rect r) {
- Point p0, p1;
- double a,b,c;
- std::vector<double> ifc = l.coefficients();
- a = ifc[0];
- b = ifc[1];
- c = ifc[2];
- if (fabs(b) > fabs(a)) {
- p0 = Point(r[0][0], (-c - a*r[0][0])/b);
- if (p0[1] < r[1][0])
- p0 = Point((-c - b*r[1][0])/a, r[1][0]);
- if (p0[1] > r[1][1])
- p0 = Point((-c - b*r[1][1])/a, r[1][1]);
- p1 = Point(r[0][1], (-c - a*r[0][1])/b);
- if (p1[1] < r[1][0])
- p1 = Point((-c - b*r[1][0])/a, r[1][0]);
- if (p1[1] > r[1][1])
- p1 = Point((-c - b*r[1][1])/a, r[1][1]);
+ boost::optional<LineSegment> seg = l.clip(r);
+ if (seg) {
+ return *seg;
} else {
- p0 = Point((-c - b*r[1][0])/a, r[1][0]);
- if (p0[0] < r[0][0])
- p0 = Point(r[0][0], (-c - a*r[0][0])/b);
- if (p0[0] > r[0][1])
- p0 = Point(r[0][1], (-c - a*r[0][1])/b);
- p1 = Point((-c - b*r[1][1])/a, r[1][1]);
- if (p1[0] < r[0][0])
- p1 = Point(r[0][0], (-c - a*r[0][0])/b);
- if (p1[0] > r[0][1])
- p1 = Point(r[0][1], (-c - a*r[0][1])/b);
+ return LineSegment(Point(0,0), Point(0,0));
}
- return LineSegment(p0, p1);
}
static double det(Point a, Point b) {
@@ -673,7 +645,7 @@ std::vector<double> xAx::roots(Line const &l) const {
Interval xAx::quad_ex(double a, double b, double c, Interval ivl) {
double cx = -b*0.5/a;
- Interval bnds((a*ivl[0]+b)*ivl[0]+c, (a*ivl[1]+b)*ivl[1]+c);
+ Interval bnds((a*ivl.min()+b)*ivl.min()+c, (a*ivl.max()+b)*ivl.max()+c);
if(ivl.contains(cx))
bnds.expandTo((a*cx+b)*cx+c);
return bnds;
@@ -714,14 +686,14 @@ Interval xAx::extrema(Rect r) const {
ext |= Interval(valueAt(r.corner(i)));
return ext;
}
- double k = r[0][0];
- Interval ext = quad_ex(c[2], c[1]*k+c[4], (c[0]*k + c[3])*k + c[5], r[1]);
- k = r[0][1];
- ext |= quad_ex(c[2], c[1]*k+c[4], (c[0]*k + c[3])*k + c[5], r[1]);
- k = r[1][0];
- ext |= quad_ex(c[0], c[1]*k+c[3], (c[2]*k + c[4])*k + c[5], r[0]);
- k = r[1][1];
- ext |= quad_ex(c[0], c[1]*k+c[3], (c[2]*k + c[4])*k + c[5], r[0]);
+ double k = r[X].min();
+ Interval ext = quad_ex(c[2], c[1]*k+c[4], (c[0]*k + c[3])*k + c[5], r[Y]);
+ k = r[X].max();
+ ext |= quad_ex(c[2], c[1]*k+c[4], (c[0]*k + c[3])*k + c[5], r[Y]);
+ k = r[Y].min();
+ ext |= quad_ex(c[0], c[1]*k+c[3], (c[2]*k + c[4])*k + c[5], r[X]);
+ k = r[Y].max();
+ ext |= quad_ex(c[0], c[1]*k+c[3], (c[2]*k + c[4])*k + c[5], r[X]);
boost::optional<Point> B0 = bottom();
if (B0 && r.contains(*B0))
ext.expandTo(0);
@@ -753,7 +725,7 @@ bool at_infinity (Point const& p)
inline
double signed_triangle_area (Point const& p1, Point const& p2, Point const& p3)
{
- return (cross(p3, p2) - cross(p3, p1) + cross(p2, p1));
+ return (cross(p2, p3) - cross(p1, p3) + cross(p1, p2));
}
@@ -801,6 +773,8 @@ void xAx::set(std::vector<Point> const& points)
*/
void xAx::set (const Point& _vertex, double _angle, double _dist1, double _dist2)
{
+ using std::swap;
+
if (_dist2 == infinity() || _dist2 == -infinity()) // parabola
{
if (_dist1 == infinity()) // degenerate to a line
@@ -842,7 +816,7 @@ void xAx::set (const Point& _vertex, double _angle, double _dist1, double _dist2
if (std::fabs(_dist1) > std::fabs(_dist2))
{
- std::swap (_dist1, _dist2);
+ swap (_dist1, _dist2);
}
if (_dist1 < 0)
{
@@ -1442,38 +1416,35 @@ Rect xAx::arc_bound (const Point & P1, const Point & Q, const Point & P2) const
if (sgn(Mside) == sgn(Qside))
{
//std::cout << "BOUND: M.size() == 1" << std::endl;
- if (M[0][dim] > B[dim][1])
- B[dim][1] = M[0][dim];
- else if (M[0][dim] < B[dim][0])
- B[dim][0] = M[0][dim];
+ B[dim].expandTo(M[0][dim]);
}
}
else if (M.size() == 2)
{
//std::cout << "BOUND: M.size() == 2" << std::endl;
if (M[0][dim] > M[1][dim])
- std::swap (M[0], M[1]);
+ swap (M[0], M[1]);
- if (M[0][dim] > B[dim][1])
+ if (M[0][dim] > B[dim].max())
{
double Mside = signed_triangle_area (P1, M[0], P2);
if (sgn(Mside) == sgn(Qside))
- B[dim][1] = M[0][dim];
+ B[dim].setMax(M[0][dim]);
}
- else if (M[1][dim] < B[dim][0])
+ else if (M[1][dim] < B[dim].min())
{
double Mside = signed_triangle_area (P1, M[1], P2);
if (sgn(Mside) == sgn(Qside))
- B[dim][0] = M[1][dim];
+ B[dim].setMin(M[1][dim]);
}
else
{
double Mside = signed_triangle_area (P1, M[0], P2);
if (sgn(Mside) == sgn(Qside))
- B[dim][0] = M[0][dim];
+ B[dim].setMin(M[0][dim]);
Mside = signed_triangle_area (P1, M[1], P2);
if (sgn(Mside) == sgn(Qside))
- B[dim][1] = M[1][dim];
+ B[dim].setMax(M[1][dim]);
}
}
}
@@ -1486,7 +1457,7 @@ Rect xAx::arc_bound (const Point & P1, const Point & Q, const Point & P2) const
*
* P: the point to compute the nearest one
*/
-std::vector<Point> xAx::allNearestPoints (const Point &P) const
+std::vector<Point> xAx::allNearestTimes (const Point &P) const
{
// TODO: manage the circle - centre case
std::vector<Point> points;
diff --git a/src/2geom/conicsec.h b/src/2geom/conicsec.h
index ec9a430d9..e9c466978 100644
--- a/src/2geom/conicsec.h
+++ b/src/2geom/conicsec.h
@@ -1,7 +1,6 @@
-/**
- * \file
- * \brief Conic Section
- *
+/** @file
+ * @brief Conic Section
+ *//*
* Authors:
* Nathan Hurst <njh@njhurst.com>
*
@@ -32,8 +31,8 @@
*/
-#ifndef _2GEOM_CONIC_SECTION_H_
-#define _2GEOM_CONIC_SECTION_H_
+#ifndef LIB2GEOM_SEEN_CONICSEC_H
+#define LIB2GEOM_SEEN_CONICSEC_H
#include <2geom/exception.h>
#include <2geom/angle.h>
@@ -474,22 +473,22 @@ public:
Rect arc_bound (const Point & P1, const Point & Q, const Point & P2) const;
- std::vector<Point> allNearestPoints (const Point &P) const;
+ std::vector<Point> allNearestTimes (const Point &P) const;
/*
* Return the point on the conic section nearest to the passed point "P".
*
* P: the point to compute the nearest one
*/
- Point nearestPoint (const Point &P) const
+ Point nearestTime (const Point &P) const
{
- std::vector<Point> points = allNearestPoints (P);
+ std::vector<Point> points = allNearestTimes (P);
if ( !points.empty() )
{
return points.front();
}
// else
- THROW_LOGICALERROR ("nearestPoint: no nearest point found");
+ THROW_LOGICALERROR ("nearestTime: no nearest point found");
return Point();
}
@@ -509,8 +508,7 @@ inline std::ostream &operator<< (std::ostream &out_file, const xAx &x) {
};
-#endif // _2GEOM_CONIC_SECTION_H_
-
+#endif // LIB2GEOM_SEEN_CONICSEC_H
/*
Local Variables:
diff --git a/src/2geom/conjugate_gradient.cpp b/src/2geom/conjugate_gradient.cpp
deleted file mode 100644
index 588513414..000000000
--- a/src/2geom/conjugate_gradient.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * conjugate_gradient.cpp
- *
- * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- *
- */
-
-#include <math.h>
-#include <stdlib.h>
-#include <valarray>
-#include <cassert>
-#include <2geom/conjugate_gradient.h>
-
-/* lifted wholely from wikipedia. */
-
-namespace Geom
-{
-
-using std::valarray;
-
-static void
-matrix_times_vector(valarray<double> const &matrix, /* m * n */
- valarray<double> const &vec, /* n */
- valarray<double> &result) /* m */
-{
- unsigned n = vec.size();
- unsigned m = result.size();
- assert(m*n == matrix.size());
- const double* mp = &const_cast<valarray<double>&>(matrix)[0];
- for (unsigned i = 0; i < m; i++) {
- double res = 0;
- for (unsigned j = 0; j < n; j++)
- res += *mp++ * vec[j];
- result[i] = res;
- }
-}
-
-/**
-// only used in commented code below
-static double Linfty(valarray<double> const &vec) {
- return std::max(vec.max(), -vec.min());
-}
-**/
-
-double
-inner(valarray<double> const &x,
- valarray<double> const &y) {
- double total = 0;
- for(unsigned i = 0; i < x.size(); i++)
- total += x[i]*y[i];
- return total;// (x*y).sum(); <- this is more concise, but ineff
-}
-
-void
-conjugate_gradient(double **A,
- double *x,
- double *b,
- unsigned n,
- double tol,
- int max_iterations,
- bool ortho1) {
- valarray<double> vA(n*n);
- valarray<double> vx(n);
- valarray<double> vb(n);
- for(unsigned i=0;i<n;i++) {
- vx[i]=x[i];
- vb[i]=b[i];
- for(unsigned j=0;j<n;j++) {
- vA[i*n+j]=A[i][j];
- }
- }
- conjugate_gradient(vA,vx,vb,n,tol,max_iterations,ortho1);
- for(unsigned i=0;i<n;i++) {
- x[i]=vx[i];
- }
-}
-void
-conjugate_gradient(valarray<double> const &A,
- valarray<double> &x,
- valarray<double> const &b,
- unsigned n, double tol,
- unsigned max_iterations, bool /*ortho1*/) {
- valarray<double> Ap(n), p(n), r(n);
- matrix_times_vector(A,x,Ap);
- r=b-Ap;
- double r_r = inner(r,r);
- unsigned k = 0;
- tol *= tol;
- while(k < max_iterations && r_r > tol) {
- k++;
- double r_r_new = r_r;
- if(k == 1)
- p = r;
- else {
- r_r_new = inner(r,r);
- p = r + (r_r_new/r_r)*p;
- }
- matrix_times_vector(A, p, Ap);
- double alpha_k = r_r_new / inner(p, Ap);
- x += alpha_k*p;
- r -= alpha_k*Ap;
- r_r = r_r_new;
- }
- //printf("njh: %d iters, Linfty = %g L2 = %g\n", k,
- //std::max(-r.min(), r.max()), sqrt(r_r));
- // x is solution
-}
-
-} // namespace Geom
-
-/*
- 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/2geom/convex-cover.h b/src/2geom/convex-cover.h
deleted file mode 100644
index d290b7e80..000000000
--- a/src/2geom/convex-cover.h
+++ /dev/null
@@ -1,204 +0,0 @@
-/**
- * \file
- * \brief Dynamic convex hull structure
- *
- * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
- * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- *
- */
-
-#ifndef GEOM_CONVEX_COVER_H
-#define GEOM_CONVEX_COVER_H
-
-#include <2geom/point.h>
-#include <vector>
-
-namespace Geom{
-
-/* A convex cover is a sequence of convex polygons that completely cover the path. For now a
- * convex hull class is included here (the convex-hull header is wrong)
- */
-
-/** ConvexHull
- * A convexhull is a convex region - every point between two points in the convex hull is also in
- * the convex hull. It is defined by a set of points travelling in a clockwise direction. We require the first point to be top most, and of the topmost, leftmost.
-
- * An empty hull has no points, we allow a single point or two points degenerate cases.
-
- * We could provide the centroid as a member for efficient direction determination. We can update the
- * centroid with all operations with the same time complexity as the operation.
- */
-
-class ConvexHull{
-public: // XXX: should be private :)
- // extracts the convex hull of boundary. internal use only
- void find_pivot();
- void angle_sort();
- void graham_scan();
- void andrew_scan();
- void graham();
-public:
- std::vector<Point> boundary;
- //Point centroid;
-
- void merge(Point p);
- bool contains_point(Point p);
- bool strict_contains_point(Point p);
-
- inline size_t size() const { return boundary.size();}
- inline Point operator[](int i) const {
-
- int l = boundary.size();
- if(l == 0) return Point();
- return boundary[i >= 0 ? i % l : (i % l) + l];
- }
-
- /*inline Point &operator[](unsigned i) {
- int l = boundary.size();
- if(l == 0) return Point();
- return boundary[i >= 0 ? i % l : i % l + l];
- }*/
-
-public:
- ConvexHull() {}
- ConvexHull(std::vector<Point> const & points) :
- boundary (points)
- {
- graham();
- }
-
- template <typename T>
- ConvexHull(T b, T e) :boundary(b,e) {}
-
- ~ConvexHull()
- {
- }
-
-public:
- /** Is the convex hull clockwise? We use the definition of clockwise from point.h
- **/
- bool is_clockwise() const;
- bool top_point_first() const;
- bool meets_invariants() const;
-
- // contains no points
- bool empty() const { return boundary.empty();}
-
- // contains exactly one point
- bool singular() const { return boundary.size() == 1;}
-
- // all points are on a line
- bool linear() const { return boundary.size() == 2;}
- bool is_degenerate() const;
-
- // area of the convex hull
- double centroid_and_area(Geom::Point& centroid) const;
- double area() const {
- Point tmp;
- return centroid_and_area(tmp);
- }
-
- // furthest point in a direction (lg time)
- Point const * furthest(Point direction) const;
-
- bool is_left(Point p, int n);
- bool is_strict_left(Point p, int n);
- int find_left(Point p);
- int find_strict_left(Point p);
- double narrowest_diameter(Point &a, Point &b, Point &c);
-
-};
-/** @brief Output operator for points.
- * Prints out all the coordinates. */
-inline std::ostream &operator<< (std::ostream &out_file, const Geom::ConvexHull &in_cvx) {
- out_file << "ConvexHull(";
- for(unsigned i = 0; i < in_cvx.size(); i++) {
- out_file << in_cvx.boundary[i] << ", ";
- }
- out_file << ")";
- return out_file;
-}
-
-// do two convex hulls intersect?
-bool intersectp(ConvexHull a, ConvexHull b);
-
-std::vector<std::pair<int, int> > bridges(ConvexHull a, ConvexHull b);
-
-// find the convex hull intersection
-ConvexHull intersection(ConvexHull a, ConvexHull b);
-ConvexHull sweepline_intersection(ConvexHull const &a, ConvexHull const &b);
-
-// find the convex hull of a set of convex hulls
-ConvexHull merge(ConvexHull a, ConvexHull b);
-
-// naive approach
-ConvexHull graham_merge(ConvexHull a, ConvexHull b);
-
-// naive approach
-ConvexHull andrew_merge(ConvexHull a, ConvexHull b);
-
-unsigned find_bottom_right(ConvexHull const &a);
-
-/*** Arbitrary transform operator.
- * Take a convex hull and apply an arbitrary convexity preserving transform.
- * we should be concerned about singular tranforms here.
- */
-template <class T> ConvexHull operator*(ConvexHull const &p, T const &m) {
- ConvexHull pr;
-
- pr.boundary.reserve(p.boundary.size());
-
- for(unsigned i = 0; i < p.boundary.size(); i++) {
- pr.boundary.push_back(p.boundary[i]*m);
- }
- return pr;
-}
-
-ConvexHull clip(ConvexHull const & ch, Point n, double d);
-
-//TODO: reinstate
-/*class ConvexCover{
-public:
- Path const* path;
- std::vector<ConvexHull> cc;
-
- ConvexCover(Path const &sp);
-};*/
-
-};
-
-#endif //2GEOM_CONVEX_COVER_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/2geom/convex-cover.cpp b/src/2geom/convex-hull.cpp
index 5e599fdde..4f5e06733 100644
--- a/src/2geom/convex-cover.cpp
+++ b/src/2geom/convex-hull.cpp
@@ -1,8 +1,11 @@
-/*
- * convex-cover.cpp
- *
- * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
- * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com>
+/** @file
+ * @brief Convex hull of a set of points
+ *//*
+ * Authors:
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Michael G. Sloan <mgsloan@gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Copyright 2006-2015 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -29,11 +32,13 @@
*
*/
-#include <2geom/convex-cover.h>
+#include <2geom/convex-hull.h>
#include <2geom/exception.h>
#include <algorithm>
#include <map>
-#include <assert.h>
+#include <iostream>
+#include <cassert>
+#include <boost/array.hpp>
/** Todo:
+ modify graham scan to work top to bottom, rather than around angles
@@ -50,9 +55,237 @@ using std::vector;
using std::map;
using std::pair;
using std::make_pair;
+using std::swap;
+
+namespace Geom {
+
+ConvexHull::ConvexHull(Point const &a, Point const &b)
+ : _boundary(2)
+ , _lower(0)
+{
+ _boundary[0] = a;
+ _boundary[1] = b;
+ std::sort(_boundary.begin(), _boundary.end(), Point::LexLess<X>());
+ _construct();
+}
+
+ConvexHull::ConvexHull(Point const &a, Point const &b, Point const &c)
+ : _boundary(3)
+ , _lower(0)
+{
+ _boundary[0] = a;
+ _boundary[1] = b;
+ _boundary[2] = c;
+ std::sort(_boundary.begin(), _boundary.end(), Point::LexLess<X>());
+ _construct();
+}
+
+ConvexHull::ConvexHull(Point const &a, Point const &b, Point const &c, Point const &d)
+ : _boundary(4)
+ , _lower(0)
+{
+ _boundary[0] = a;
+ _boundary[1] = b;
+ _boundary[2] = c;
+ _boundary[3] = d;
+ std::sort(_boundary.begin(), _boundary.end(), Point::LexLess<X>());
+ _construct();
+}
+
+ConvexHull::ConvexHull(std::vector<Point> const &pts)
+ : _lower(0)
+{
+ //if (pts.size() > 16) { // arbitrary threshold
+ // _prune(pts.begin(), pts.end(), _boundary);
+ //} else {
+ _boundary = pts;
+ std::sort(_boundary.begin(), _boundary.end(), Point::LexLess<X>());
+ //}
+ _construct();
+}
+
+bool ConvexHull::_is_clockwise_turn(Point const &a, Point const &b, Point const &c)
+{
+ if (b == c) return false;
+ return cross(b-a, c-a) > 0;
+}
+
+void ConvexHull::_construct()
+{
+ // _boundary must already be sorted in LexLess<X> order
+ if (_boundary.empty()) {
+ _lower = 0;
+ return;
+ }
+ if (_boundary.size() == 1 || (_boundary.size() == 2 && _boundary[0] == _boundary[1])) {
+ _boundary.resize(1);
+ _lower = 1;
+ return;
+ }
+ if (_boundary.size() == 2) {
+ _lower = 2;
+ return;
+ }
+
+ std::size_t k = 2;
+ for (std::size_t i = 2; i < _boundary.size(); ++i) {
+ while (k >= 2 && !_is_clockwise_turn(_boundary[k-2], _boundary[k-1], _boundary[i])) {
+ --k;
+ }
+ std::swap(_boundary[k++], _boundary[i]);
+ }
+
+ _lower = k;
+ std::sort(_boundary.begin() + k, _boundary.end(), Point::LexGreater<X>());
+ _boundary.push_back(_boundary.front());
+ for (std::size_t i = _lower; i < _boundary.size(); ++i) {
+ while (k > _lower && !_is_clockwise_turn(_boundary[k-2], _boundary[k-1], _boundary[i])) {
+ --k;
+ }
+ std::swap(_boundary[k++], _boundary[i]);
+ }
+
+ _boundary.resize(k-1);
+}
+
+double ConvexHull::area() const
+{
+ if (size() <= 2) return 0;
+
+ double a = 0;
+ for (std::size_t i = 0; i < size()-1; ++i) {
+ a += cross(_boundary[i], _boundary[i+1]);
+ }
+ a += cross(_boundary.back(), _boundary.front());
+ return fabs(a * 0.5);
+}
+
+OptRect ConvexHull::bounds() const
+{
+ OptRect ret;
+ if (empty()) return ret;
+ ret = Rect(left(), top(), right(), bottom());
+ return ret;
+}
+
+Point ConvexHull::topPoint() const
+{
+ Point ret;
+ ret[Y] = std::numeric_limits<Coord>::infinity();
+
+ for (UpperIterator i = upperHull().begin(); i != upperHull().end(); ++i) {
+ if (ret[Y] >= i->y()) {
+ ret = *i;
+ } else {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+Point ConvexHull::bottomPoint() const
+{
+ Point ret;
+ ret[Y] = -std::numeric_limits<Coord>::infinity();
+
+ for (LowerIterator j = lowerHull().begin(); j != lowerHull().end(); ++j) {
+ if (ret[Y] <= j->y()) {
+ ret = *j;
+ } else {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+template <typename Iter, typename Lex>
+bool below_x_monotonic_polyline(Point const &p, Iter first, Iter last, Lex lex)
+{
+ typename Lex::Secondary above;
+ Iter f = std::lower_bound(first, last, p, lex);
+ if (f == last) return false;
+ if (f == first) {
+ if (p == *f) return true;
+ return false;
+ }
+
+ Point a = *(f-1), b = *f;
+ if (a[X] == b[X]) {
+ if (above(p[Y], a[Y]) || above(b[Y], p[Y])) return false;
+ } else {
+ // TODO: maybe there is a more numerically stable method
+ Coord y = lerp((p[X] - a[X]) / (b[X] - a[X]), a[Y], b[Y]);
+ if (above(p[Y], y)) return false;
+ }
+ return true;
+}
+
+bool ConvexHull::contains(Point const &p) const
+{
+ if (_boundary.empty()) return false;
+ if (_boundary.size() == 1) {
+ if (_boundary[0] == p) return true;
+ return false;
+ }
+
+ // 1. verify that the point is in the relevant X range
+ if (p[X] < _boundary[0][X] || p[X] > _boundary[_lower-1][X]) return false;
+
+ // 2. check whether it is below the upper hull
+ UpperIterator ub = upperHull().begin(), ue = upperHull().end();
+ if (!below_x_monotonic_polyline(p, ub, ue, Point::LexLess<X>())) return false;
+
+ // 3. check whether it is above the lower hull
+ LowerIterator lb = lowerHull().begin(), le = lowerHull().end();
+ if (!below_x_monotonic_polyline(p, lb, le, Point::LexGreater<X>())) return false;
+
+ return true;
+}
+
+bool ConvexHull::contains(Rect const &r) const
+{
+ for (unsigned i = 0; i < 4; ++i) {
+ if (!contains(r.corner(i))) return false;
+ }
+ return true;
+}
+
+bool ConvexHull::contains(ConvexHull const &ch) const
+{
+ // TODO: requires interiorContains.
+ // We have to check all points of ch, and each point takes logarithmic time.
+ // If there are more points in ch that here, it is faster to make the check
+ // the other way around.
+ /*if (ch.size() > size()) {
+ for (iterator i = begin(); i != end(); ++i) {
+ if (ch.interiorContains(*i)) return false;
+ }
+ return true;
+ }*/
+
+ for (iterator i = ch.begin(); i != ch.end(); ++i) {
+ if (!contains(*i)) return false;
+ }
+ return true;
+}
+
+void ConvexHull::swap(ConvexHull &other)
+{
+ _boundary.swap(other._boundary);
+ std::swap(_lower, other._lower);
+}
-namespace Geom{
+void ConvexHull::swap(std::vector<Point> &pts)
+{
+ _boundary.swap(pts);
+ _lower = 0;
+ std::sort(_boundary.begin(), _boundary.end(), Point::LexLess<X>());
+ _construct();
+}
+#if 0
/*** SignedTriangleArea
* returns the area of the triangle defined by p0, p1, p2. A clockwise triangle has positive area.
*/
@@ -118,147 +351,6 @@ public:
#endif
};
-void
-ConvexHull::find_pivot() {
- // Find pivot P;
- unsigned pivot = 0;
- for (unsigned i = 1; i < boundary.size(); i++)
- if(boundary[i] <= boundary[pivot])
- pivot = i;
-
- std::swap(boundary[0], boundary[pivot]);
-}
-
-void
-ConvexHull::angle_sort() {
-// sort points by angle (resolve ties in favor of point farther from P);
-// we leave the first one in place as our pivot
- std::sort(boundary.begin()+1, boundary.end(), angle_cmp(boundary[0]));
-}
-
-
-void
-ConvexHull::graham_scan() {
- // prune out equal points. points are sorted, so equals are adjacent
- std::vector<Point>::iterator e =
- std::unique(boundary.begin(), boundary.end());
- boundary.resize(e - boundary.begin());
- for(unsigned int i = 2; i < boundary.size(); i++) {
-
- }
- if (boundary.size() < 4) {
- return;
- }
- unsigned stac = 2;
- for(unsigned int i = 2; i < boundary.size(); i++) {
- double o = SignedTriangleArea(boundary[stac-2],
- boundary[stac-1],
- boundary[i]);
- if(o == 0) { // colinear - dangerous...
- stac--;
- } else if(o < 0) { // anticlockwise
- } else { // remove concavity
- while(o >= 0 && stac > 2) {
- stac--;
- o = SignedTriangleArea(boundary[stac-2],
- boundary[stac-1],
- boundary[i]);
- }
- }
- boundary[stac++] = boundary[i];
- }
- boundary.resize(stac);
-}
-
-// following code is from marco.
-
-/*
- * return true in case the oriented polyline p0, p1, p2 is a right turn
- */
-inline
-bool is_a_right_turn (Point const& p0, Point const& p1, Point const& p2)
-{
- if (p1 == p2) return false;
- Point q1 = p1 - p0;
- Point q2 = p2 - p0;
- if (q1 == -q2) return false;
- return (cross (q1, q2) < 0);
-}
-
-/*
- * return true if p < q wrt the lexicographyc order induced by the coordinates
- */
-struct lex_less
-{
- bool operator() (Point const& p, Point const& q)
- {
- return ((p[Y] < q[Y]) || (p[Y] == q[Y] && p[X] < q[X]));
- }
-};
-
-/*
- * return true if p > q wrt the lexicographyc order induced by the coordinates
- */
-struct lex_greater
-{
- bool operator() (Point const& p, Point const& q)
- {
- return ((p[Y] > q[Y]) || (p[Y] == q[Y] && p[X] > q[X]));
- }
-};
-
-/*
- * Compute the convex hull of a set of points.
- * The implementation is based on the Andrew's scan algorithm
- * note: in the Bezier clipping for collinear normals it seems
- * to be more stable wrt the Graham's scan algorithm and in general
- * a bit quikier
- */
-void ConvexHull::andrew_scan ()
-{
- vector<Point> & P = boundary;
- size_t n = P.size();
- if (n < 2) return;
- std::sort(P.begin(), P.end(), lex_less());
- if (n < 4) return;
- // upper hull
- size_t u = 2;
- for (size_t i = 2; i < n; ++i)
- {
- while (u > 1 && !is_a_right_turn(P[u-2], P[u-1], P[i]))
- {
- --u;
- }
- std::swap(P[u], P[i]);
- ++u;
- }
- std::sort(P.begin() + u, P.end(), lex_greater());
- std::rotate(P.begin(), P.begin() + 1, P.end());
- // lower hull
- size_t l = u;
- size_t k = u - 1;
- for (size_t i = l; i < n; ++i)
- {
- while (l > k && !is_a_right_turn(P[l-2], P[l-1], P[i]))
- {
- --l;
- }
- std::swap(P[l], P[i]);
- ++l;
- }
- P.resize(l);
-}
-
-void
-ConvexHull::graham() {
- /*if(is_degenerate()) // nothing to do
- return;*/
- //find_pivot();
- //angle_sort();
- //graham_scan();
- andrew_scan();
-}
-
//Mathematically incorrect mod, but more useful.
int mod(int i, int l) {
return i >= 0 ?
@@ -266,65 +358,6 @@ int mod(int i, int l) {
}
//OPT: usages can often be replaced by conditions
-/*** ConvexHull::left
- * Tests if a point is left (outside) of a particular segment, n. */
-bool
-ConvexHull::is_left(Point p, int n) {
- return SignedTriangleArea((*this)[n], (*this)[n+1], p) > 0;
-}
-
-/*** ConvexHull::strict_left
- * Tests if a point is left (outside) of a particular segment, n. */
-bool
-ConvexHull::is_strict_left(Point p, int n) {
- return SignedTriangleArea((*this)[n], (*this)[n+1], p) >= 0;
-}
-
-/*** ConvexHull::find_left
- * May return any number n where the segment n -> n + 1 (possibly looped around) in the hull such
- * that the point is on the wrong side to be within the hull. Returns -1 if it is within the hull.*/
-int
-ConvexHull::find_left(Point p) {
- int l = boundary.size(); //Who knows if C++ is smart enough to optimize this?
- for(int i = 0; i < l; i++) {
- if(is_left(p, i)) return i;
- }
- return -1;
-}
-
-
-/*** ConvexHull::find_positive
- * May return any number n where the segment n -> n + 1 (possibly looped around) in the hull such
- * that the point is on the wrong side to be within the hull. Returns -1 if it is within the hull.*/
-int
-ConvexHull::find_strict_left(Point p) {
- int l = boundary.size(); //Who knows if C++ is smart enough to optimize this?
- for(int i = 0; i < l; i++) {
- if(is_strict_left(p, i)) return i;
- }
- return -1;
-}
-
-//OPT: do a spread iteration - quasi-random with no repeats and full coverage.
-
-/*** ConvexHull::contains_point
- * In order to test whether a point is inside a convex hull we can travel once around the outside making
- * sure that each triangle made from an edge and the point has positive area. */
-bool
-ConvexHull::contains_point(Point p) {
- if(size() == 0) return false;
- return find_left(p) == -1;
-}
-
-/*** ConvexHull::strict_contains_point
- * In order to test whether a point is strictly inside (not on the boundary) a convex hull we can travel once around the outside making
- * sure that each triangle made from an edge and the point has positive area. */
-bool
-ConvexHull::strict_contains_point(Point p) {
- if(size() == 0) return false;
- return find_strict_left(p) == -1;
-}
-
/*** ConvexHull::add_point
* to add a point we need to find whether the new point extends the boundary, and if so, what it
* obscures. Tarjan? Jarvis?*/
@@ -332,18 +365,18 @@ void
ConvexHull::merge(Point p) {
std::vector<Point> out;
- int l = boundary.size();
+ int len = boundary.size();
- if(l < 2) {
+ if(len < 2) {
if(boundary.empty() || boundary[0] != p)
boundary.push_back(p);
return;
}
bool pushed = false;
-
+
bool pre = is_left(p, -1);
- for(int i = 0; i < l; i++) {
+ for(int i = 0; i < len; i++) {
bool cur = is_left(p, i);
if(pre) {
if(cur) {
@@ -595,7 +628,7 @@ ConvexHull graham_merge(ConvexHull a, ConvexHull b) {
// we can avoid the find pivot step because of top_point_first
if(b.boundary[0] <= a.boundary[0])
- std::swap(a, b);
+ swap(a, b);
result.boundary = a.boundary;
result.boundary.insert(result.boundary.end(),
@@ -615,7 +648,7 @@ ConvexHull andrew_merge(ConvexHull a, ConvexHull b) {
// we can avoid the find pivot step because of top_point_first
if(b.boundary[0] <= a.boundary[0])
- std::swap(a, b);
+ swap(a, b);
result.boundary = a.boundary;
result.boundary.insert(result.boundary.end(),
@@ -697,6 +730,7 @@ double ConvexHull::narrowest_diameter(Point &a, Point &b, Point &c) {
}
return d;
}
+#endif
};
diff --git a/src/2geom/convex-hull.h b/src/2geom/convex-hull.h
new file mode 100644
index 000000000..4f4d10bd6
--- /dev/null
+++ b/src/2geom/convex-hull.h
@@ -0,0 +1,346 @@
+/** @file
+ * @brief Convex hull data structures
+ *//*
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_CONVEX_HULL_H
+#define LIB2GEOM_SEEN_CONVEX_HULL_H
+
+#include <2geom/point.h>
+#include <2geom/rect.h>
+#include <vector>
+#include <algorithm>
+#include <boost/operators.hpp>
+#include <boost/optional.hpp>
+#include <boost/range/iterator_range.hpp>
+
+namespace Geom {
+
+namespace {
+
+/** @brief Iterator for the lower convex hull.
+ * This iterator allows us to avoid duplicating any points in the hull
+ * boundary and still express most algorithms in a concise way. */
+class ConvexHullLowerIterator
+ : public boost::random_access_iterator_helper
+ < ConvexHullLowerIterator
+ , Point
+ , std::ptrdiff_t
+ , Point const *
+ , Point const &
+ >
+{
+public:
+ typedef ConvexHullLowerIterator Self;
+ ConvexHullLowerIterator()
+ : _data(NULL)
+ , _size(0)
+ , _x(0)
+ {}
+ ConvexHullLowerIterator(std::vector<Point> const &pts, std::size_t x)
+ : _data(&pts[0])
+ , _size(pts.size())
+ , _x(x)
+ {}
+
+ Self &operator++() {
+ *this += 1;
+ return *this;
+ }
+ Self &operator--() {
+ *this -= 1;
+ return *this;
+ }
+ Self &operator+=(std::ptrdiff_t d) {
+ _x += d;
+ return *this;
+ }
+ Self &operator-=(std::ptrdiff_t d) {
+ _x -= d;
+ return *this;
+ }
+ std::ptrdiff_t operator-(Self const &other) const {
+ return _x - other._x;
+ }
+ Point const &operator*() const {
+ if (_x < _size) {
+ return _data[_x];
+ } else {
+ return *_data;
+ }
+ }
+ bool operator==(Self const &other) const {
+ return _data == other._data && _x == other._x;
+ }
+ bool operator<(Self const &other) const {
+ return _data == other._data && _x < other._x;
+ }
+
+private:
+ Point const *_data;
+ std::size_t _size;
+ std::size_t _x;
+};
+
+} // end anonymous namespace
+
+/**
+ * @brief Convex hull based on the Andrew's monotone chain algorithm.
+ * @ingroup Shapes
+ */
+class ConvexHull {
+public:
+ typedef std::vector<Point>::const_iterator iterator;
+ typedef std::vector<Point>::const_iterator const_iterator;
+ typedef std::vector<Point>::const_iterator UpperIterator;
+ typedef ConvexHullLowerIterator LowerIterator;
+
+ /// @name Construct a convex hull.
+ /// @{
+
+ /// Create an empty convex hull.
+ ConvexHull() {}
+ /// Construct a singular convex hull.
+ explicit ConvexHull(Point const &a)
+ : _boundary(1, a)
+ , _lower(1)
+ {}
+ /// Construct a convex hull of two points.
+ ConvexHull(Point const &a, Point const &b);
+ /// Construct a convex hull of three points.
+ ConvexHull(Point const &a, Point const &b, Point const &c);
+ /// Construct a convex hull of four points.
+ ConvexHull(Point const &a, Point const &b, Point const &c, Point const &d);
+ /// Create a convex hull of a vector of points.
+ ConvexHull(std::vector<Point> const &pts);
+
+ /// Create a convex hull of a range of points.
+ template <typename Iter>
+ ConvexHull(Iter first, Iter last)
+ : _lower(0)
+ {
+ _prune(first, last, _boundary);
+ _construct();
+ }
+ /// @}
+
+ /// @name Inspect basic properties.
+ /// @{
+
+ /// Check for emptiness.
+ bool empty() const { return _boundary.empty(); }
+ /// Get the number of points in the hull.
+ size_t size() const { return _boundary.size(); }
+ /// Check whether the hull contains only one point.
+ bool isSingular() const { return _boundary.size() == 1; }
+ /// Check whether the hull is a line.
+ bool isLinear() const { return _boundary.size() == 2; }
+ /// Check whether the hull has zero area.
+ bool isDegenerate() const { return _boundary.size() < 3; }
+ /// Calculate the area of the convex hull.
+ double area() const;
+ //Point centroid() const;
+ //double areaAndCentroid(Point &c);
+ //FatLine maxDiameter() const;
+ //FatLine minDiameter() const;
+ /// @}
+
+ /// @name Inspect bounds and extreme points.
+ /// @{
+
+ /// Compute the bounding rectangle of the convex hull.
+ OptRect bounds() const;
+
+ /// Get the leftmost (minimum X) coordinate of the hull.
+ Coord left() const { return _boundary[0][X]; }
+ /// Get the rightmost (maximum X) coordinate of the hull.
+ Coord right() const { return _boundary[_lower-1][X]; }
+ /// Get the topmost (minimum Y) coordinate of the hull.
+ Coord top() const { return topPoint()[Y]; }
+ /// Get the bottommost (maximum Y) coordinate of the hull.
+ Coord bottom() const { return bottomPoint()[Y]; }
+
+ /// Get the leftmost (minimum X) point of the hull.
+ /// If the leftmost edge is vertical, the top point of the edge is returned.
+ Point leftPoint() const { return _boundary[0]; }
+ /// Get the rightmost (maximum X) point of the hull.
+ /// If the rightmost edge is vertical, the bottom point edge is returned.
+ Point rightPoint() const { return _boundary[_lower-1]; }
+ /// Get the topmost (minimum Y) point of the hull.
+ /// If the topmost edge is horizontal, the right point of the edge is returned.
+ Point topPoint() const;
+ /// Get the bottommost (maximum Y) point of the hull.
+ /// If the bottommost edge is horizontal, the left point of the edge is returned.
+ Point bottomPoint() const;
+ ///@}
+
+ /// @name Iterate over points.
+ /// @{
+ /** @brief Get the begin iterator to the points that form the hull.
+ * Points are are returned beginning the the leftmost one, going along
+ * the upper (minimum Y) side, and then along the bottom.
+ * Thus the points are always ordered clockwise. No point is
+ * repeated. */
+ iterator begin() const { return _boundary.begin(); }
+ /// Get the end iterator to the points that form the hull.
+ iterator end() const { return _boundary.end(); }
+ /// Get the first, leftmost point in the hull.
+ Point const &front() const { return _boundary.front(); }
+ /// Get the penultimate point of the lower hull.
+ Point const &back() const { return _boundary.back(); }
+ Point const &operator[](std::size_t i) const {
+ return _boundary[i];
+ }
+
+ /** @brief Get an iterator range to the upper part of the hull.
+ * This returns a range that includes the leftmost point,
+ * all points of the upper hull, and the rightmost point. */
+ boost::iterator_range<UpperIterator> upperHull() const {
+ boost::iterator_range<UpperIterator> r(_boundary.begin(), _boundary.begin() + _lower);
+ return r;
+ }
+
+ /** @brief Get an iterator range to the lower part of the hull.
+ * This returns a range that includes the leftmost point,
+ * all points of the lower hull, and the rightmost point. */
+ boost::iterator_range<LowerIterator> lowerHull() const {
+ if (_boundary.empty()) {
+ boost::iterator_range<LowerIterator> r(LowerIterator(_boundary, 0),
+ LowerIterator(_boundary, 0));
+ return r;
+ }
+ if (_boundary.size() == 1) {
+ boost::iterator_range<LowerIterator> r(LowerIterator(_boundary, 0),
+ LowerIterator(_boundary, 1));
+ return r;
+ }
+ boost::iterator_range<LowerIterator> r(LowerIterator(_boundary, _lower - 1),
+ LowerIterator(_boundary, _boundary.size() + 1));
+ return r;
+ }
+ /// @}
+
+ /// @name Check for containment and intersection.
+ /// @{
+ /** @brief Check whether the given point is inside the hull.
+ * This takes logarithmic time. */
+ bool contains(Point const &p) const;
+ /** @brief Check whether the given axis-aligned rectangle is inside the hull.
+ * A rectangle is inside the hull if all of its corners are inside. */
+ bool contains(Rect const &r) const;
+ /// Check whether the given convex hull is completely contained in this one.
+ bool contains(ConvexHull const &other) const;
+ //bool interiorContains(Point const &p) const;
+ //bool interiorContains(Rect const &r) const;
+ //bool interiorContains(ConvexHull const &other) const;
+ //bool intersects(Rect const &r) const;
+ //bool intersects(ConvexHull const &other) const;
+
+ //ConvexHull &operator|=(ConvexHull const &other);
+ //ConvexHull &operator&=(ConvexHull const &other);
+ //ConvexHull &operator*=(Affine const &m);
+
+ //ConvexHull &expand(Point const &p);
+ //void unifyWith(ConvexHull const &other);
+ //void intersectWith(ConvexHull const &other);
+ /// @}
+
+ void swap(ConvexHull &other);
+ void swap(std::vector<Point> &pts);
+
+private:
+ void _construct();
+ static bool _is_clockwise_turn(Point const &a, Point const &b, Point const &c);
+
+ /// Take a vector of points and produce a pruned sorted vector.
+ template <typename Iter>
+ static void _prune(Iter first, Iter last, std::vector<Point> &out) {
+ boost::optional<Point> ymin, ymax, xmin, xmax;
+ for (Iter i = first; i != last; ++i) {
+ Point p = *i;
+ if (!ymin || Point::LexLess<Y>()(p, *ymin)) {
+ ymin = p;
+ }
+ if (!xmin || Point::LexLess<X>()(p, *xmin)) {
+ xmin = p;
+ }
+ if (!ymax || Point::LexGreater<Y>()(p, *ymax)) {
+ ymax = p;
+ }
+ if (!xmax || Point::LexGreater<X>()(p, *xmax)) {
+ xmax = p;
+ }
+ }
+ if (!ymin) return;
+
+ ConvexHull qhull(*xmin, *xmax, *ymin, *ymax);
+ for (Iter i = first; i != last; ++i) {
+ if (qhull.contains(*i)) continue;
+ out.push_back(*i);
+ }
+
+ out.push_back(*xmin);
+ out.push_back(*xmax);
+ out.push_back(*ymin);
+ out.push_back(*ymax);
+ std::sort(out.begin(), out.end(), Point::LexLess<X>());
+ out.erase(std::unique(out.begin(), out.end()), out.end());
+ }
+
+ /// Sequence of points forming the convex hull polygon.
+ std::vector<Point> _boundary;
+ /// Index one past the rightmost point, where the lower part of the boundary starts.
+ std::size_t _lower;
+};
+
+/** @brief Output operator for convex hulls.
+ * Prints out all the coordinates. */
+inline std::ostream &operator<< (std::ostream &out_file, const Geom::ConvexHull &in_cvx) {
+ out_file << "ConvexHull(";
+ for(unsigned i = 0; i < in_cvx.size(); i++) {
+ out_file << in_cvx[i] << ", ";
+ }
+ out_file << ")";
+ return out_file;
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_CONVEX_HULL_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/2geom/coord.cpp b/src/2geom/coord.cpp
new file mode 100644
index 000000000..9ee8066f2
--- /dev/null
+++ b/src/2geom/coord.cpp
@@ -0,0 +1,3688 @@
+/** @file
+ * @brief Conversion between Coord and strings
+ *//*
+ * Authors:
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright 2014 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+// Most of the code in this file is derived from:
+// https://code.google.com/p/double-conversion/
+// The copyright notice for that code is attached below.
+//
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <2geom/coord.h>
+#include <stdint.h>
+#include <cstdlib>
+#include <cassert>
+#include <cstring>
+#include <climits>
+#include <cstdarg>
+#include <cmath>
+
+#ifndef ASSERT
+#define ASSERT(condition) \
+ assert(condition);
+#endif
+#ifndef UNIMPLEMENTED
+#define UNIMPLEMENTED() (abort())
+#endif
+#ifndef UNREACHABLE
+#define UNREACHABLE() (abort())
+#endif
+
+#define UINT64_2PART_C(a, b) (((static_cast<uint64_t>(a) << 32) + 0x##b##u))
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) \
+ ((sizeof(a) / sizeof(*(a))) / \
+ static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+#endif
+
+#ifndef DISALLOW_COPY_AND_ASSIGN
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+#endif
+
+#ifndef DISALLOW_IMPLICIT_CONSTRUCTORS
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+#endif
+
+#if defined(__GNUC__)
+#define DOUBLE_CONVERSION_UNUSED __attribute__((unused))
+#else
+#define DOUBLE_CONVERSION_UNUSED
+#endif
+
+namespace Geom {
+
+namespace {
+
+inline int StrLength(const char* string) {
+ size_t length = strlen(string);
+ ASSERT(length == static_cast<size_t>(static_cast<int>(length)));
+ return static_cast<int>(length);
+}
+
+template <typename T>
+class Vector {
+ public:
+ Vector() : start_(NULL), length_(0) {}
+ Vector(T* data, int length) : start_(data), length_(length) {
+ ASSERT(length == 0 || (length > 0 && data != NULL));
+ }
+
+ Vector<T> SubVector(int from, int to) {
+ ASSERT(to <= length_);
+ ASSERT(from < to);
+ ASSERT(0 <= from);
+ return Vector<T>(start() + from, to - from);
+ }
+ int length() const { return length_; }
+ bool is_empty() const { return length_ == 0; }
+
+ T* start() const { return start_; }
+
+ T& operator[](int index) const {
+ ASSERT(0 <= index && index < length_);
+ return start_[index];
+ }
+ T& first() { return start_[0]; }
+ T& last() { return start_[length_ - 1]; }
+
+ private:
+ T* start_;
+ int length_;
+};
+
+template <class Dest, class Source>
+inline Dest BitCast(const Source& source) {
+ DOUBLE_CONVERSION_UNUSED
+ typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1];
+ Dest dest;
+ memmove(&dest, &source, sizeof(dest));
+ return dest;
+}
+
+template <class Dest, class Source>
+inline Dest BitCast(Source* source) {
+ return BitCast<Dest>(reinterpret_cast<uintptr_t>(source));
+}
+
+// We assume that doubles and uint64_t have the same endianness.
+static uint64_t double_to_uint64(double d) { return BitCast<uint64_t>(d); }
+static double uint64_to_double(uint64_t d64) { return BitCast<double>(d64); }
+
+// This "Do It Yourself Floating Point" class
+class DiyFp {
+ public:
+ static const int kSignificandSize = 64;
+
+ DiyFp() : f_(0), e_(0) {}
+ DiyFp(uint64_t f, int e) : f_(f), e_(e) {}
+
+ void Subtract(const DiyFp& other) {
+ ASSERT(e_ == other.e_);
+ ASSERT(f_ >= other.f_);
+ f_ -= other.f_;
+ }
+
+ static DiyFp Minus(const DiyFp& a, const DiyFp& b) {
+ DiyFp result = a;
+ result.Subtract(b);
+ return result;
+ }
+
+ void Multiply(const DiyFp& other) {
+ const uint64_t kM32 = 0xFFFFFFFFU;
+ uint64_t a = f_ >> 32;
+ uint64_t b = f_ & kM32;
+ uint64_t c = other.f_ >> 32;
+ uint64_t d = other.f_ & kM32;
+ uint64_t ac = a * c;
+ uint64_t bc = b * c;
+ uint64_t ad = a * d;
+ uint64_t bd = b * d;
+ uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32);
+ // By adding 1U << 31 to tmp we round the final result.
+ // Halfway cases will be round up.
+ tmp += 1U << 31;
+ uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);
+ e_ += other.e_ + 64;
+ f_ = result_f;
+ }
+
+ static DiyFp Times(const DiyFp& a, const DiyFp& b) {
+ DiyFp result = a;
+ result.Multiply(b);
+ return result;
+ }
+
+ void Normalize() {
+ ASSERT(f_ != 0);
+ uint64_t f = f_;
+ int e = e_;
+
+ const uint64_t k10MSBits = UINT64_2PART_C(0xFFC00000, 00000000);
+ while ((f & k10MSBits) == 0) {
+ f <<= 10;
+ e -= 10;
+ }
+ while ((f & kUint64MSB) == 0) {
+ f <<= 1;
+ e--;
+ }
+ f_ = f;
+ e_ = e;
+ }
+
+ static DiyFp Normalize(const DiyFp& a) {
+ DiyFp result = a;
+ result.Normalize();
+ return result;
+ }
+
+ uint64_t f() const { return f_; }
+ int e() const { return e_; }
+
+ void set_f(uint64_t new_value) { f_ = new_value; }
+ void set_e(int new_value) { e_ = new_value; }
+
+ private:
+ static const uint64_t kUint64MSB = UINT64_2PART_C(0x80000000, 00000000);
+
+ uint64_t f_;
+ int e_;
+};
+
+class Double {
+ public:
+ static const uint64_t kSignMask = UINT64_2PART_C(0x80000000, 00000000);
+ static const uint64_t kExponentMask = UINT64_2PART_C(0x7FF00000, 00000000);
+ static const uint64_t kSignificandMask = UINT64_2PART_C(0x000FFFFF, FFFFFFFF);
+ static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000);
+ static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit.
+ static const int kSignificandSize = 53;
+
+ Double() : d64_(0) {}
+ explicit Double(double d) : d64_(double_to_uint64(d)) {}
+ explicit Double(uint64_t d64) : d64_(d64) {}
+ explicit Double(DiyFp diy_fp)
+ : d64_(DiyFpToUint64(diy_fp)) {}
+
+ DiyFp AsDiyFp() const {
+ ASSERT(Sign() > 0);
+ ASSERT(!IsSpecial());
+ return DiyFp(Significand(), Exponent());
+ }
+
+ DiyFp AsNormalizedDiyFp() const {
+ ASSERT(value() > 0.0);
+ uint64_t f = Significand();
+ int e = Exponent();
+
+ // The current double could be a denormal.
+ while ((f & kHiddenBit) == 0) {
+ f <<= 1;
+ e--;
+ }
+ // Do the final shifts in one go.
+ f <<= DiyFp::kSignificandSize - kSignificandSize;
+ e -= DiyFp::kSignificandSize - kSignificandSize;
+ return DiyFp(f, e);
+ }
+
+ uint64_t AsUint64() const {
+ return d64_;
+ }
+
+ double NextDouble() const {
+ if (d64_ == kInfinity) return Double(kInfinity).value();
+ if (Sign() < 0 && Significand() == 0) {
+ // -0.0
+ return 0.0;
+ }
+ if (Sign() < 0) {
+ return Double(d64_ - 1).value();
+ } else {
+ return Double(d64_ + 1).value();
+ }
+ }
+
+ double PreviousDouble() const {
+ if (d64_ == (kInfinity | kSignMask)) return -Double::Infinity();
+ if (Sign() < 0) {
+ return Double(d64_ + 1).value();
+ } else {
+ if (Significand() == 0) return -0.0;
+ return Double(d64_ - 1).value();
+ }
+ }
+
+ int Exponent() const {
+ if (IsDenormal()) return kDenormalExponent;
+
+ uint64_t d64 = AsUint64();
+ int biased_e =
+ static_cast<int>((d64 & kExponentMask) >> kPhysicalSignificandSize);
+ return biased_e - kExponentBias;
+ }
+
+ uint64_t Significand() const {
+ uint64_t d64 = AsUint64();
+ uint64_t significand = d64 & kSignificandMask;
+ if (!IsDenormal()) {
+ return significand + kHiddenBit;
+ } else {
+ return significand;
+ }
+ }
+
+ bool IsDenormal() const {
+ uint64_t d64 = AsUint64();
+ return (d64 & kExponentMask) == 0;
+ }
+
+ // We consider denormals not to be special.
+ // Hence only Infinity and NaN are special.
+ bool IsSpecial() const {
+ uint64_t d64 = AsUint64();
+ return (d64 & kExponentMask) == kExponentMask;
+ }
+
+ bool IsNan() const {
+ uint64_t d64 = AsUint64();
+ return ((d64 & kExponentMask) == kExponentMask) &&
+ ((d64 & kSignificandMask) != 0);
+ }
+
+ bool IsInfinite() const {
+ uint64_t d64 = AsUint64();
+ return ((d64 & kExponentMask) == kExponentMask) &&
+ ((d64 & kSignificandMask) == 0);
+ }
+
+ int Sign() const {
+ uint64_t d64 = AsUint64();
+ return (d64 & kSignMask) == 0? 1: -1;
+ }
+
+ DiyFp UpperBoundary() const {
+ ASSERT(Sign() > 0);
+ return DiyFp(Significand() * 2 + 1, Exponent() - 1);
+ }
+
+ void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const {
+ ASSERT(value() > 0.0);
+ DiyFp v = this->AsDiyFp();
+ DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1));
+ DiyFp m_minus;
+ if (LowerBoundaryIsCloser()) {
+ m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2);
+ } else {
+ m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1);
+ }
+ m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e()));
+ m_minus.set_e(m_plus.e());
+ *out_m_plus = m_plus;
+ *out_m_minus = m_minus;
+ }
+
+ bool LowerBoundaryIsCloser() const {
+ bool physical_significand_is_zero = ((AsUint64() & kSignificandMask) == 0);
+ return physical_significand_is_zero && (Exponent() != kDenormalExponent);
+ }
+
+ double value() const { return uint64_to_double(d64_); }
+
+ static int SignificandSizeForOrderOfMagnitude(int order) {
+ if (order >= (kDenormalExponent + kSignificandSize)) {
+ return kSignificandSize;
+ }
+ if (order <= kDenormalExponent) return 0;
+ return order - kDenormalExponent;
+ }
+
+ static double Infinity() {
+ return Double(kInfinity).value();
+ }
+
+ static double NaN() {
+ return Double(kNaN).value();
+ }
+
+ private:
+ static const int kExponentBias = 0x3FF + kPhysicalSignificandSize;
+ static const int kDenormalExponent = -kExponentBias + 1;
+ static const int kMaxExponent = 0x7FF - kExponentBias;
+ static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000);
+ static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000);
+
+ const uint64_t d64_;
+
+ static uint64_t DiyFpToUint64(DiyFp diy_fp) {
+ uint64_t significand = diy_fp.f();
+ int exponent = diy_fp.e();
+ while (significand > kHiddenBit + kSignificandMask) {
+ significand >>= 1;
+ exponent++;
+ }
+ if (exponent >= kMaxExponent) {
+ return kInfinity;
+ }
+ if (exponent < kDenormalExponent) {
+ return 0;
+ }
+ while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) {
+ significand <<= 1;
+ exponent--;
+ }
+ uint64_t biased_exponent;
+ if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) {
+ biased_exponent = 0;
+ } else {
+ biased_exponent = static_cast<uint64_t>(exponent + kExponentBias);
+ }
+ return (significand & kSignificandMask) |
+ (biased_exponent << kPhysicalSignificandSize);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(Double);
+};
+
+template<typename S>
+static int BitSize(S value) {
+ (void) value; // Mark variable as used.
+ return 8 * sizeof(value);
+}
+
+class Bignum {
+ public:
+ // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately.
+ // This bignum can encode much bigger numbers, since it contains an
+ // exponent.
+ static const int kMaxSignificantBits = 3584;
+
+ Bignum()
+ : bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0)
+ {
+ for (int i = 0; i < kBigitCapacity; ++i) {
+ bigits_[i] = 0;
+ }
+ }
+ void AssignUInt16(uint16_t value) {
+ ASSERT(kBigitSize >= BitSize(value));
+ Zero();
+ if (value == 0) return;
+
+ EnsureCapacity(1);
+ bigits_[0] = value;
+ used_digits_ = 1;
+ }
+ void AssignUInt64(uint64_t value) {
+ const int kUInt64Size = 64;
+
+ Zero();
+ if (value == 0) return;
+
+ int needed_bigits = kUInt64Size / kBigitSize + 1;
+ EnsureCapacity(needed_bigits);
+ for (int i = 0; i < needed_bigits; ++i) {
+ bigits_[i] = value & kBigitMask;
+ value = value >> kBigitSize;
+ }
+ used_digits_ = needed_bigits;
+ Clamp();
+ }
+ void AssignBignum(const Bignum& other) {
+ exponent_ = other.exponent_;
+ for (int i = 0; i < other.used_digits_; ++i) {
+ bigits_[i] = other.bigits_[i];
+ }
+ // Clear the excess digits (if there were any).
+ for (int i = other.used_digits_; i < used_digits_; ++i) {
+ bigits_[i] = 0;
+ }
+ used_digits_ = other.used_digits_;
+ }
+
+ void AssignDecimalString(Vector<const char> value);
+ void AssignHexString(Vector<const char> value);
+
+ void AssignPowerUInt16(uint16_t base, int exponent);
+
+ void AddUInt16(uint16_t operand);
+ void AddUInt64(uint64_t operand);
+ void AddBignum(const Bignum& other);
+ // Precondition: this >= other.
+ void SubtractBignum(const Bignum& other);
+
+ void Square();
+ void ShiftLeft(int shift_amount);
+ void MultiplyByUInt32(uint32_t factor);
+ void MultiplyByUInt64(uint64_t factor);
+ void MultiplyByPowerOfTen(int exponent);
+ void Times10() { return MultiplyByUInt32(10); }
+ // Pseudocode:
+ // int result = this / other;
+ // this = this % other;
+ // In the worst case this function is in O(this/other).
+ uint16_t DivideModuloIntBignum(const Bignum& other);
+
+ bool ToHexString(char* buffer, int buffer_size) const;
+
+ // Returns
+ // -1 if a < b,
+ // 0 if a == b, and
+ // +1 if a > b.
+ static int Compare(const Bignum& a, const Bignum& b);
+ static bool Equal(const Bignum& a, const Bignum& b) {
+ return Compare(a, b) == 0;
+ }
+ static bool LessEqual(const Bignum& a, const Bignum& b) {
+ return Compare(a, b) <= 0;
+ }
+ static bool Less(const Bignum& a, const Bignum& b) {
+ return Compare(a, b) < 0;
+ }
+ // Returns Compare(a + b, c);
+ static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c);
+ // Returns a + b == c
+ static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) {
+ return PlusCompare(a, b, c) == 0;
+ }
+ // Returns a + b <= c
+ static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) {
+ return PlusCompare(a, b, c) <= 0;
+ }
+ // Returns a + b < c
+ static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) {
+ return PlusCompare(a, b, c) < 0;
+ }
+ private:
+ typedef uint32_t Chunk;
+ typedef uint64_t DoubleChunk;
+
+ static const int kChunkSize = sizeof(Chunk) * 8;
+ static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8;
+ // With bigit size of 28 we loose some bits, but a double still fits easily
+ // into two chunks, and more importantly we can use the Comba multiplication.
+ static const int kBigitSize = 28;
+ static const Chunk kBigitMask = (1 << kBigitSize) - 1;
+ // Every instance allocates kBigitLength chunks on the stack. Bignums cannot
+ // grow. There are no checks if the stack-allocated space is sufficient.
+ static const int kBigitCapacity = kMaxSignificantBits / kBigitSize;
+
+ void EnsureCapacity(int size) {
+ if (size > kBigitCapacity) {
+ UNREACHABLE();
+ }
+ }
+ void Align(const Bignum& other);
+ void Clamp();
+ bool IsClamped() const;
+ void Zero();
+ // Requires this to have enough capacity (no tests done).
+ // Updates used_digits_ if necessary.
+ // shift_amount must be < kBigitSize.
+ void BigitsShiftLeft(int shift_amount);
+ // BigitLength includes the "hidden" digits encoded in the exponent.
+ int BigitLength() const { return used_digits_ + exponent_; }
+ Chunk BigitAt(int index) const;
+ void SubtractTimes(const Bignum& other, int factor);
+
+ Chunk bigits_buffer_[kBigitCapacity];
+ // A vector backed by bigits_buffer_. This way accesses to the array are
+ // checked for out-of-bounds errors.
+ Vector<Chunk> bigits_;
+ int used_digits_;
+ // The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize).
+ int exponent_;
+
+ DISALLOW_COPY_AND_ASSIGN(Bignum);
+};
+
+static uint64_t ReadUInt64(Vector<const char> buffer,
+ int from,
+ int digits_to_read) {
+ uint64_t result = 0;
+ for (int i = from; i < from + digits_to_read; ++i) {
+ int digit = buffer[i] - '0';
+ ASSERT(0 <= digit && digit <= 9);
+ result = result * 10 + digit;
+ }
+ return result;
+}
+
+
+void Bignum::AssignDecimalString(Vector<const char> value) {
+ // 2^64 = 18446744073709551616 > 10^19
+ const int kMaxUint64DecimalDigits = 19;
+ Zero();
+ int length = value.length();
+ int pos = 0;
+ // Let's just say that each digit needs 4 bits.
+ while (length >= kMaxUint64DecimalDigits) {
+ uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits);
+ pos += kMaxUint64DecimalDigits;
+ length -= kMaxUint64DecimalDigits;
+ MultiplyByPowerOfTen(kMaxUint64DecimalDigits);
+ AddUInt64(digits);
+ }
+ uint64_t digits = ReadUInt64(value, pos, length);
+ MultiplyByPowerOfTen(length);
+ AddUInt64(digits);
+ Clamp();
+}
+
+
+static int HexCharValue(char c) {
+ if ('0' <= c && c <= '9') return c - '0';
+ if ('a' <= c && c <= 'f') return 10 + c - 'a';
+ ASSERT('A' <= c && c <= 'F');
+ return 10 + c - 'A';
+}
+
+
+void Bignum::AssignHexString(Vector<const char> value) {
+ Zero();
+ int length = value.length();
+
+ int needed_bigits = length * 4 / kBigitSize + 1;
+ EnsureCapacity(needed_bigits);
+ int string_index = length - 1;
+ for (int i = 0; i < needed_bigits - 1; ++i) {
+ // These bigits are guaranteed to be "full".
+ Chunk current_bigit = 0;
+ for (int j = 0; j < kBigitSize / 4; j++) {
+ current_bigit += HexCharValue(value[string_index--]) << (j * 4);
+ }
+ bigits_[i] = current_bigit;
+ }
+ used_digits_ = needed_bigits - 1;
+
+ Chunk most_significant_bigit = 0; // Could be = 0;
+ for (int j = 0; j <= string_index; ++j) {
+ most_significant_bigit <<= 4;
+ most_significant_bigit += HexCharValue(value[j]);
+ }
+ if (most_significant_bigit != 0) {
+ bigits_[used_digits_] = most_significant_bigit;
+ used_digits_++;
+ }
+ Clamp();
+}
+
+
+void Bignum::AddUInt64(uint64_t operand) {
+ if (operand == 0) return;
+ Bignum other;
+ other.AssignUInt64(operand);
+ AddBignum(other);
+}
+
+
+void Bignum::AddBignum(const Bignum& other) {
+ ASSERT(IsClamped());
+ ASSERT(other.IsClamped());
+
+ Align(other);
+
+ EnsureCapacity(1 + std::max(BigitLength(), other.BigitLength()) - exponent_);
+ Chunk carry = 0;
+ int bigit_pos = other.exponent_ - exponent_;
+ ASSERT(bigit_pos >= 0);
+ for (int i = 0; i < other.used_digits_; ++i) {
+ Chunk sum = bigits_[bigit_pos] + other.bigits_[i] + carry;
+ bigits_[bigit_pos] = sum & kBigitMask;
+ carry = sum >> kBigitSize;
+ bigit_pos++;
+ }
+
+ while (carry != 0) {
+ Chunk sum = bigits_[bigit_pos] + carry;
+ bigits_[bigit_pos] = sum & kBigitMask;
+ carry = sum >> kBigitSize;
+ bigit_pos++;
+ }
+ used_digits_ = std::max(bigit_pos, used_digits_);
+ ASSERT(IsClamped());
+}
+
+
+void Bignum::SubtractBignum(const Bignum& other) {
+ ASSERT(IsClamped());
+ ASSERT(other.IsClamped());
+ // We require this to be bigger than other.
+ ASSERT(LessEqual(other, *this));
+
+ Align(other);
+
+ int offset = other.exponent_ - exponent_;
+ Chunk borrow = 0;
+ int i;
+ for (i = 0; i < other.used_digits_; ++i) {
+ ASSERT((borrow == 0) || (borrow == 1));
+ Chunk difference = bigits_[i + offset] - other.bigits_[i] - borrow;
+ bigits_[i + offset] = difference & kBigitMask;
+ borrow = difference >> (kChunkSize - 1);
+ }
+ while (borrow != 0) {
+ Chunk difference = bigits_[i + offset] - borrow;
+ bigits_[i + offset] = difference & kBigitMask;
+ borrow = difference >> (kChunkSize - 1);
+ ++i;
+ }
+ Clamp();
+}
+
+
+void Bignum::ShiftLeft(int shift_amount) {
+ if (used_digits_ == 0) return;
+ exponent_ += shift_amount / kBigitSize;
+ int local_shift = shift_amount % kBigitSize;
+ EnsureCapacity(used_digits_ + 1);
+ BigitsShiftLeft(local_shift);
+}
+
+
+void Bignum::MultiplyByUInt32(uint32_t factor) {
+ if (factor == 1) return;
+ if (factor == 0) {
+ Zero();
+ return;
+ }
+ if (used_digits_ == 0) return;
+
+ ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1);
+ DoubleChunk carry = 0;
+ for (int i = 0; i < used_digits_; ++i) {
+ DoubleChunk product = static_cast<DoubleChunk>(factor) * bigits_[i] + carry;
+ bigits_[i] = static_cast<Chunk>(product & kBigitMask);
+ carry = (product >> kBigitSize);
+ }
+ while (carry != 0) {
+ EnsureCapacity(used_digits_ + 1);
+ bigits_[used_digits_] = carry & kBigitMask;
+ used_digits_++;
+ carry >>= kBigitSize;
+ }
+}
+
+
+void Bignum::MultiplyByUInt64(uint64_t factor) {
+ if (factor == 1) return;
+ if (factor == 0) {
+ Zero();
+ return;
+ }
+ ASSERT(kBigitSize < 32);
+ uint64_t carry = 0;
+ uint64_t low = factor & 0xFFFFFFFF;
+ uint64_t high = factor >> 32;
+ for (int i = 0; i < used_digits_; ++i) {
+ uint64_t product_low = low * bigits_[i];
+ uint64_t product_high = high * bigits_[i];
+ uint64_t tmp = (carry & kBigitMask) + product_low;
+ bigits_[i] = tmp & kBigitMask;
+ carry = (carry >> kBigitSize) + (tmp >> kBigitSize) +
+ (product_high << (32 - kBigitSize));
+ }
+ while (carry != 0) {
+ EnsureCapacity(used_digits_ + 1);
+ bigits_[used_digits_] = carry & kBigitMask;
+ used_digits_++;
+ carry >>= kBigitSize;
+ }
+}
+
+
+void Bignum::MultiplyByPowerOfTen(int exponent) {
+ const uint64_t kFive27 = UINT64_2PART_C(0x6765c793, fa10079d);
+ const uint16_t kFive1 = 5;
+ const uint16_t kFive2 = kFive1 * 5;
+ const uint16_t kFive3 = kFive2 * 5;
+ const uint16_t kFive4 = kFive3 * 5;
+ const uint16_t kFive5 = kFive4 * 5;
+ const uint16_t kFive6 = kFive5 * 5;
+ const uint32_t kFive7 = kFive6 * 5;
+ const uint32_t kFive8 = kFive7 * 5;
+ const uint32_t kFive9 = kFive8 * 5;
+ const uint32_t kFive10 = kFive9 * 5;
+ const uint32_t kFive11 = kFive10 * 5;
+ const uint32_t kFive12 = kFive11 * 5;
+ const uint32_t kFive13 = kFive12 * 5;
+ const uint32_t kFive1_to_12[] =
+ { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6,
+ kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 };
+
+ ASSERT(exponent >= 0);
+ if (exponent == 0) return;
+ if (used_digits_ == 0) return;
+
+ int remaining_exponent = exponent;
+ while (remaining_exponent >= 27) {
+ MultiplyByUInt64(kFive27);
+ remaining_exponent -= 27;
+ }
+ while (remaining_exponent >= 13) {
+ MultiplyByUInt32(kFive13);
+ remaining_exponent -= 13;
+ }
+ if (remaining_exponent > 0) {
+ MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]);
+ }
+ ShiftLeft(exponent);
+}
+
+
+void Bignum::Square() {
+ ASSERT(IsClamped());
+ int product_length = 2 * used_digits_;
+ EnsureCapacity(product_length);
+
+ if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_digits_) {
+ UNIMPLEMENTED();
+ }
+ DoubleChunk accumulator = 0;
+ // First shift the digits so we don't overwrite them.
+ int copy_offset = used_digits_;
+ for (int i = 0; i < used_digits_; ++i) {
+ bigits_[copy_offset + i] = bigits_[i];
+ }
+ // We have two loops to avoid some 'if's in the loop.
+ for (int i = 0; i < used_digits_; ++i) {
+ // Process temporary digit i with power i.
+ // The sum of the two indices must be equal to i.
+ int bigit_index1 = i;
+ int bigit_index2 = 0;
+ // Sum all of the sub-products.
+ while (bigit_index1 >= 0) {
+ Chunk chunk1 = bigits_[copy_offset + bigit_index1];
+ Chunk chunk2 = bigits_[copy_offset + bigit_index2];
+ accumulator += static_cast<DoubleChunk>(chunk1) * chunk2;
+ bigit_index1--;
+ bigit_index2++;
+ }
+ bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask;
+ accumulator >>= kBigitSize;
+ }
+ for (int i = used_digits_; i < product_length; ++i) {
+ int bigit_index1 = used_digits_ - 1;
+ int bigit_index2 = i - bigit_index1;
+
+ while (bigit_index2 < used_digits_) {
+ Chunk chunk1 = bigits_[copy_offset + bigit_index1];
+ Chunk chunk2 = bigits_[copy_offset + bigit_index2];
+ accumulator += static_cast<DoubleChunk>(chunk1) * chunk2;
+ bigit_index1--;
+ bigit_index2++;
+ }
+ bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask;
+ accumulator >>= kBigitSize;
+ }
+
+ ASSERT(accumulator == 0);
+
+ used_digits_ = product_length;
+ exponent_ *= 2;
+ Clamp();
+}
+
+
+void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) {
+ ASSERT(base != 0);
+ ASSERT(power_exponent >= 0);
+ if (power_exponent == 0) {
+ AssignUInt16(1);
+ return;
+ }
+ Zero();
+ int shifts = 0;
+
+ while ((base & 1) == 0) {
+ base >>= 1;
+ shifts++;
+ }
+ int bit_size = 0;
+ int tmp_base = base;
+ while (tmp_base != 0) {
+ tmp_base >>= 1;
+ bit_size++;
+ }
+ int final_size = bit_size * power_exponent;
+
+ EnsureCapacity(final_size / kBigitSize + 2);
+
+ // Left to Right exponentiation.
+ int mask = 1;
+ while (power_exponent >= mask) mask <<= 1;
+
+ mask >>= 2;
+ uint64_t this_value = base;
+
+ bool delayed_multipliciation = false;
+ const uint64_t max_32bits = 0xFFFFFFFF;
+ while (mask != 0 && this_value <= max_32bits) {
+ this_value = this_value * this_value;
+ // Verify that there is enough space in this_value to perform the
+ // multiplication. The first bit_size bits must be 0.
+ if ((power_exponent & mask) != 0) {
+ uint64_t base_bits_mask =
+ ~((static_cast<uint64_t>(1) << (64 - bit_size)) - 1);
+ bool high_bits_zero = (this_value & base_bits_mask) == 0;
+ if (high_bits_zero) {
+ this_value *= base;
+ } else {
+ delayed_multipliciation = true;
+ }
+ }
+ mask >>= 1;
+ }
+ AssignUInt64(this_value);
+ if (delayed_multipliciation) {
+ MultiplyByUInt32(base);
+ }
+
+ // Now do the same thing as a bignum.
+ while (mask != 0) {
+ Square();
+ if ((power_exponent & mask) != 0) {
+ MultiplyByUInt32(base);
+ }
+ mask >>= 1;
+ }
+
+ // And finally add the saved shifts.
+ ShiftLeft(shifts * power_exponent);
+}
+
+
+uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) {
+ ASSERT(IsClamped());
+ ASSERT(other.IsClamped());
+ ASSERT(other.used_digits_ > 0);
+
+ if (BigitLength() < other.BigitLength()) {
+ return 0;
+ }
+
+ Align(other);
+
+ uint16_t result = 0;
+
+ while (BigitLength() > other.BigitLength()) {
+ ASSERT(other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16));
+ ASSERT(bigits_[used_digits_ - 1] < 0x10000);
+ result += static_cast<uint16_t>(bigits_[used_digits_ - 1]);
+ SubtractTimes(other, bigits_[used_digits_ - 1]);
+ }
+
+ ASSERT(BigitLength() == other.BigitLength());
+
+ Chunk this_bigit = bigits_[used_digits_ - 1];
+ Chunk other_bigit = other.bigits_[other.used_digits_ - 1];
+
+ if (other.used_digits_ == 1) {
+ int quotient = this_bigit / other_bigit;
+ bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient;
+ ASSERT(quotient < 0x10000);
+ result += static_cast<uint16_t>(quotient);
+ Clamp();
+ return result;
+ }
+
+ int division_estimate = this_bigit / (other_bigit + 1);
+ ASSERT(division_estimate < 0x10000);
+ result += static_cast<uint16_t>(division_estimate);
+ SubtractTimes(other, division_estimate);
+
+ if (other_bigit * (division_estimate + 1) > this_bigit) {
+ return result;
+ }
+
+ while (LessEqual(other, *this)) {
+ SubtractBignum(other);
+ result++;
+ }
+ return result;
+}
+
+
+template<typename S>
+static int SizeInHexChars(S number) {
+ ASSERT(number > 0);
+ int result = 0;
+ while (number != 0) {
+ number >>= 4;
+ result++;
+ }
+ return result;
+}
+
+
+static char HexCharOfValue(int value) {
+ ASSERT(0 <= value && value <= 16);
+ if (value < 10) return static_cast<char>(value + '0');
+ return static_cast<char>(value - 10 + 'A');
+}
+
+
+bool Bignum::ToHexString(char* buffer, int buffer_size) const {
+ ASSERT(IsClamped());
+ // Each bigit must be printable as separate hex-character.
+ ASSERT(kBigitSize % 4 == 0);
+ const int kHexCharsPerBigit = kBigitSize / 4;
+
+ if (used_digits_ == 0) {
+ if (buffer_size < 2) return false;
+ buffer[0] = '0';
+ buffer[1] = '\0';
+ return true;
+ }
+ // We add 1 for the terminating '\0' character.
+ int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit +
+ SizeInHexChars(bigits_[used_digits_ - 1]) + 1;
+ if (needed_chars > buffer_size) return false;
+ int string_index = needed_chars - 1;
+ buffer[string_index--] = '\0';
+ for (int i = 0; i < exponent_; ++i) {
+ for (int j = 0; j < kHexCharsPerBigit; ++j) {
+ buffer[string_index--] = '0';
+ }
+ }
+ for (int i = 0; i < used_digits_ - 1; ++i) {
+ Chunk current_bigit = bigits_[i];
+ for (int j = 0; j < kHexCharsPerBigit; ++j) {
+ buffer[string_index--] = HexCharOfValue(current_bigit & 0xF);
+ current_bigit >>= 4;
+ }
+ }
+ // And finally the last bigit.
+ Chunk most_significant_bigit = bigits_[used_digits_ - 1];
+ while (most_significant_bigit != 0) {
+ buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF);
+ most_significant_bigit >>= 4;
+ }
+ return true;
+}
+
+
+Bignum::Chunk Bignum::BigitAt(int index) const {
+ if (index >= BigitLength()) return 0;
+ if (index < exponent_) return 0;
+ return bigits_[index - exponent_];
+}
+
+
+int Bignum::Compare(const Bignum& a, const Bignum& b) {
+ ASSERT(a.IsClamped());
+ ASSERT(b.IsClamped());
+ int bigit_length_a = a.BigitLength();
+ int bigit_length_b = b.BigitLength();
+ if (bigit_length_a < bigit_length_b) return -1;
+ if (bigit_length_a > bigit_length_b) return +1;
+ for (int i = bigit_length_a - 1; i >= std::min(a.exponent_, b.exponent_); --i) {
+ Chunk bigit_a = a.BigitAt(i);
+ Chunk bigit_b = b.BigitAt(i);
+ if (bigit_a < bigit_b) return -1;
+ if (bigit_a > bigit_b) return +1;
+ }
+ return 0;
+}
+
+
+int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) {
+ ASSERT(a.IsClamped());
+ ASSERT(b.IsClamped());
+ ASSERT(c.IsClamped());
+ if (a.BigitLength() < b.BigitLength()) {
+ return PlusCompare(b, a, c);
+ }
+ if (a.BigitLength() + 1 < c.BigitLength()) return -1;
+ if (a.BigitLength() > c.BigitLength()) return +1;
+
+ if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) {
+ return -1;
+ }
+
+ Chunk borrow = 0;
+ // Starting at min_exponent all digits are == 0. So no need to compare them.
+ int min_exponent = std::min(std::min(a.exponent_, b.exponent_), c.exponent_);
+ for (int i = c.BigitLength() - 1; i >= min_exponent; --i) {
+ Chunk chunk_a = a.BigitAt(i);
+ Chunk chunk_b = b.BigitAt(i);
+ Chunk chunk_c = c.BigitAt(i);
+ Chunk sum = chunk_a + chunk_b;
+ if (sum > chunk_c + borrow) {
+ return +1;
+ } else {
+ borrow = chunk_c + borrow - sum;
+ if (borrow > 1) return -1;
+ borrow <<= kBigitSize;
+ }
+ }
+ if (borrow == 0) return 0;
+ return -1;
+}
+
+
+void Bignum::Clamp() {
+ while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) {
+ used_digits_--;
+ }
+ if (used_digits_ == 0) {
+ // Zero.
+ exponent_ = 0;
+ }
+}
+
+
+bool Bignum::IsClamped() const {
+ return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0;
+}
+
+
+void Bignum::Zero() {
+ for (int i = 0; i < used_digits_; ++i) {
+ bigits_[i] = 0;
+ }
+ used_digits_ = 0;
+ exponent_ = 0;
+}
+
+
+void Bignum::Align(const Bignum& other) {
+ if (exponent_ > other.exponent_) {
+ int zero_digits = exponent_ - other.exponent_;
+ EnsureCapacity(used_digits_ + zero_digits);
+ for (int i = used_digits_ - 1; i >= 0; --i) {
+ bigits_[i + zero_digits] = bigits_[i];
+ }
+ for (int i = 0; i < zero_digits; ++i) {
+ bigits_[i] = 0;
+ }
+ used_digits_ += zero_digits;
+ exponent_ -= zero_digits;
+ ASSERT(used_digits_ >= 0);
+ ASSERT(exponent_ >= 0);
+ }
+}
+
+
+void Bignum::BigitsShiftLeft(int shift_amount) {
+ ASSERT(shift_amount < kBigitSize);
+ ASSERT(shift_amount >= 0);
+ Chunk carry = 0;
+ for (int i = 0; i < used_digits_; ++i) {
+ Chunk new_carry = bigits_[i] >> (kBigitSize - shift_amount);
+ bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask;
+ carry = new_carry;
+ }
+ if (carry != 0) {
+ bigits_[used_digits_] = carry;
+ used_digits_++;
+ }
+}
+
+
+void Bignum::SubtractTimes(const Bignum& other, int factor) {
+ ASSERT(exponent_ <= other.exponent_);
+ if (factor < 3) {
+ for (int i = 0; i < factor; ++i) {
+ SubtractBignum(other);
+ }
+ return;
+ }
+ Chunk borrow = 0;
+ int exponent_diff = other.exponent_ - exponent_;
+ for (int i = 0; i < other.used_digits_; ++i) {
+ DoubleChunk product = static_cast<DoubleChunk>(factor) * other.bigits_[i];
+ DoubleChunk remove = borrow + product;
+ Chunk difference = bigits_[i + exponent_diff] - (remove & kBigitMask);
+ bigits_[i + exponent_diff] = difference & kBigitMask;
+ borrow = static_cast<Chunk>((difference >> (kChunkSize - 1)) +
+ (remove >> kBigitSize));
+ }
+ for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) {
+ if (borrow == 0) return;
+ Chunk difference = bigits_[i] - borrow;
+ bigits_[i] = difference & kBigitMask;
+ borrow = difference >> (kChunkSize - 1);
+ }
+ Clamp();
+}
+
+class PowersOfTenCache {
+public:
+ static const int kDecimalExponentDistance;
+
+ static const int kMinDecimalExponent;
+ static const int kMaxDecimalExponent;
+
+ static void GetCachedPowerForBinaryExponentRange(int min_exponent,
+ int max_exponent,
+ DiyFp* power,
+ int* decimal_exponent);
+
+ static void GetCachedPowerForDecimalExponent(int requested_exponent,
+ DiyFp* power,
+ int* found_exponent);
+};
+
+struct CachedPower {
+ uint64_t significand;
+ int16_t binary_exponent;
+ int16_t decimal_exponent;
+};
+
+static const CachedPower kCachedPowers[] = {
+ {UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348},
+ {UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340},
+ {UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332},
+ {UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324},
+ {UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316},
+ {UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308},
+ {UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300},
+ {UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292},
+ {UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284},
+ {UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276},
+ {UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268},
+ {UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260},
+ {UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252},
+ {UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244},
+ {UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236},
+ {UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228},
+ {UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220},
+ {UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212},
+ {UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204},
+ {UINT64_2PART_C(0xef340a98, 172aace5), -715, -196},
+ {UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188},
+ {UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180},
+ {UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172},
+ {UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164},
+ {UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156},
+ {UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148},
+ {UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140},
+ {UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132},
+ {UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124},
+ {UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116},
+ {UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108},
+ {UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100},
+ {UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92},
+ {UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84},
+ {UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76},
+ {UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68},
+ {UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60},
+ {UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52},
+ {UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44},
+ {UINT64_2PART_C(0xaa242499, 697392d3), -183, -36},
+ {UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28},
+ {UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20},
+ {UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12},
+ {UINT64_2PART_C(0xd1b71758, e219652c), -77, -4},
+ {UINT64_2PART_C(0x9c400000, 00000000), -50, 4},
+ {UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12},
+ {UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20},
+ {UINT64_2PART_C(0x813f3978, f8940984), 30, 28},
+ {UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36},
+ {UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44},
+ {UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52},
+ {UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60},
+ {UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68},
+ {UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76},
+ {UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84},
+ {UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92},
+ {UINT64_2PART_C(0x924d692c, a61be758), 269, 100},
+ {UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108},
+ {UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116},
+ {UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124},
+ {UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132},
+ {UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140},
+ {UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148},
+ {UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156},
+ {UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164},
+ {UINT64_2PART_C(0xa59bc234, db398c25), 508, 172},
+ {UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180},
+ {UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188},
+ {UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196},
+ {UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204},
+ {UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212},
+ {UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220},
+ {UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228},
+ {UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236},
+ {UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244},
+ {UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252},
+ {UINT64_2PART_C(0xd01fef10, a657842c), 800, 260},
+ {UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268},
+ {UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276},
+ {UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284},
+ {UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292},
+ {UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300},
+ {UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308},
+ {UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316},
+ {UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324},
+ {UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332},
+ {UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340},
+};
+
+static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers);
+static const int kCachedPowersOffset = 348; // -1 * the first decimal_exponent.
+static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10)
+// Difference between the decimal exponents in the table above.
+const int PowersOfTenCache::kDecimalExponentDistance = 8;
+const int PowersOfTenCache::kMinDecimalExponent = -348;
+const int PowersOfTenCache::kMaxDecimalExponent = 340;
+
+void PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
+ int min_exponent,
+ int max_exponent,
+ DiyFp* power,
+ int* decimal_exponent) {
+ int kQ = DiyFp::kSignificandSize;
+ double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10);
+ int foo = kCachedPowersOffset;
+ int index =
+ (foo + static_cast<int>(k) - 1) / kDecimalExponentDistance + 1;
+ ASSERT(0 <= index && index < kCachedPowersLength);
+ CachedPower cached_power = kCachedPowers[index];
+ ASSERT(min_exponent <= cached_power.binary_exponent);
+ (void) max_exponent; // Mark variable as used.
+ ASSERT(cached_power.binary_exponent <= max_exponent);
+ *decimal_exponent = cached_power.decimal_exponent;
+ *power = DiyFp(cached_power.significand, cached_power.binary_exponent);
+}
+
+
+void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent,
+ DiyFp* power,
+ int* found_exponent) {
+ ASSERT(kMinDecimalExponent <= requested_exponent);
+ ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance);
+ int index =
+ (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance;
+ CachedPower cached_power = kCachedPowers[index];
+ *power = DiyFp(cached_power.significand, cached_power.binary_exponent);
+ *found_exponent = cached_power.decimal_exponent;
+ ASSERT(*found_exponent <= requested_exponent);
+ ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance);
+}
+
+enum BignumDtoaMode {
+ BIGNUM_DTOA_SHORTEST,
+ BIGNUM_DTOA_FIXED,
+ BIGNUM_DTOA_PRECISION
+};
+
+static int NormalizedExponent(uint64_t significand, int exponent) {
+ ASSERT(significand != 0);
+ while ((significand & Double::kHiddenBit) == 0) {
+ significand = significand << 1;
+ exponent = exponent - 1;
+ }
+ return exponent;
+}
+
+static int EstimatePower(int exponent);
+
+static void InitialScaledStartValues(uint64_t significand,
+ int exponent,
+ bool lower_boundary_is_closer,
+ int estimated_power,
+ bool need_boundary_deltas,
+ Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus);
+
+static void FixupMultiply10(int estimated_power, bool is_even,
+ int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus);
+
+static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus,
+ bool is_even,
+ Vector<char> buffer, int* length);
+
+static void BignumToFixed(int requested_digits, int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Vector<char>(buffer), int* length);
+
+static void GenerateCountedDigits(int count, int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Vector<char>(buffer), int* length);
+
+
+void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits,
+ Vector<char> buffer, int* length, int* decimal_point) {
+ ASSERT(v > 0);
+ ASSERT(!Double(v).IsSpecial());
+ uint64_t significand;
+ int exponent;
+ bool lower_boundary_is_closer;
+
+ significand = Double(v).Significand();
+ exponent = Double(v).Exponent();
+ lower_boundary_is_closer = Double(v).LowerBoundaryIsCloser();
+
+ bool need_boundary_deltas =
+ (mode == BIGNUM_DTOA_SHORTEST);
+
+ bool is_even = (significand & 1) == 0;
+ int normalized_exponent = NormalizedExponent(significand, exponent);
+ // estimated_power might be too low by 1.
+ int estimated_power = EstimatePower(normalized_exponent);
+
+ if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) {
+ buffer[0] = '\0';
+ *length = 0;
+ *decimal_point = -requested_digits;
+ return;
+ }
+
+ Bignum numerator;
+ Bignum denominator;
+ Bignum delta_minus;
+ Bignum delta_plus;
+
+ ASSERT(Bignum::kMaxSignificantBits >= 324*4);
+ InitialScaledStartValues(significand, exponent, lower_boundary_is_closer,
+ estimated_power, need_boundary_deltas,
+ &numerator, &denominator,
+ &delta_minus, &delta_plus);
+
+ FixupMultiply10(estimated_power, is_even, decimal_point,
+ &numerator, &denominator,
+ &delta_minus, &delta_plus);
+
+ switch (mode) {
+ case BIGNUM_DTOA_SHORTEST:
+ GenerateShortestDigits(&numerator, &denominator,
+ &delta_minus, &delta_plus,
+ is_even, buffer, length);
+ break;
+ case BIGNUM_DTOA_FIXED:
+ BignumToFixed(requested_digits, decimal_point,
+ &numerator, &denominator,
+ buffer, length);
+ break;
+ case BIGNUM_DTOA_PRECISION:
+ GenerateCountedDigits(requested_digits, decimal_point,
+ &numerator, &denominator,
+ buffer, length);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ buffer[*length] = '\0';
+}
+
+static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus,
+ bool is_even,
+ Vector<char> buffer, int* length) {
+ if (Bignum::Equal(*delta_minus, *delta_plus)) {
+ delta_plus = delta_minus;
+ }
+ *length = 0;
+ for (;;) {
+ uint16_t digit;
+ digit = numerator->DivideModuloIntBignum(*denominator);
+ ASSERT(digit <= 9);
+
+ buffer[(*length)++] = static_cast<char>(digit + '0');
+
+ bool in_delta_room_minus;
+ bool in_delta_room_plus;
+ if (is_even) {
+ in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus);
+ } else {
+ in_delta_room_minus = Bignum::Less(*numerator, *delta_minus);
+ }
+ if (is_even) {
+ in_delta_room_plus =
+ Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0;
+ } else {
+ in_delta_room_plus =
+ Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0;
+ }
+ if (!in_delta_room_minus && !in_delta_room_plus) {
+ numerator->Times10();
+ delta_minus->Times10();
+
+ if (delta_minus != delta_plus) {
+ delta_plus->Times10();
+ }
+ } else if (in_delta_room_minus && in_delta_room_plus) {
+
+ int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator);
+ if (compare < 0) {
+ // Remaining digits are less than .5. -> Round down (== do nothing).
+ } else if (compare > 0) {
+ // Remaining digits are more than .5 of denominator. -> Round up.
+ ASSERT(buffer[(*length) - 1] != '9');
+ buffer[(*length) - 1]++;
+ } else {
+ if ((buffer[(*length) - 1] - '0') % 2 == 0) {
+ // Round down => Do nothing.
+ } else {
+ ASSERT(buffer[(*length) - 1] != '9');
+ buffer[(*length) - 1]++;
+ }
+ }
+ return;
+ } else if (in_delta_room_minus) {
+ return;
+ } else { // in_delta_room_plus
+ // Round up
+ ASSERT(buffer[(*length) -1] != '9');
+ buffer[(*length) - 1]++;
+ return;
+ }
+ }
+}
+
+static void GenerateCountedDigits(int count, int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Vector<char> buffer, int* length) {
+ ASSERT(count >= 0);
+ for (int i = 0; i < count - 1; ++i) {
+ uint16_t digit;
+ digit = numerator->DivideModuloIntBignum(*denominator);
+ ASSERT(digit <= 9);
+
+ buffer[i] = static_cast<char>(digit + '0');
+ numerator->Times10();
+ }
+
+ uint16_t digit;
+ digit = numerator->DivideModuloIntBignum(*denominator);
+ if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) {
+ digit++;
+ }
+ ASSERT(digit <= 10);
+ buffer[count - 1] = static_cast<char>(digit + '0');
+
+ for (int i = count - 1; i > 0; --i) {
+ if (buffer[i] != '0' + 10) break;
+ buffer[i] = '0';
+ buffer[i - 1]++;
+ }
+ if (buffer[0] == '0' + 10) {
+ buffer[0] = '1';
+ (*decimal_point)++;
+ }
+ *length = count;
+}
+
+static void BignumToFixed(int requested_digits, int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Vector<char>(buffer), int* length)
+{
+ if (-(*decimal_point) > requested_digits) {
+ *decimal_point = -requested_digits;
+ *length = 0;
+ return;
+ } else if (-(*decimal_point) == requested_digits) {
+ ASSERT(*decimal_point == -requested_digits);
+
+ denominator->Times10();
+ if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) {
+ buffer[0] = '1';
+ *length = 1;
+ (*decimal_point)++;
+ } else {
+ *length = 0;
+ }
+ return;
+ } else {
+ int needed_digits = (*decimal_point) + requested_digits;
+ GenerateCountedDigits(needed_digits, decimal_point,
+ numerator, denominator,
+ buffer, length);
+ }
+}
+
+static int EstimatePower(int exponent) {
+ const double k1Log10 = 0.30102999566398114; // 1/lg(10)
+
+ // For doubles len(f) == 53 (don't forget the hidden bit).
+ const int kSignificandSize = Double::kSignificandSize;
+ double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10);
+ return static_cast<int>(estimate);
+}
+
+static void InitialScaledStartValuesPositiveExponent(
+ uint64_t significand, int exponent,
+ int estimated_power, bool need_boundary_deltas,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus)
+{
+ ASSERT(estimated_power >= 0);
+
+ numerator->AssignUInt64(significand);
+ numerator->ShiftLeft(exponent);
+ denominator->AssignPowerUInt16(10, estimated_power);
+
+ if (need_boundary_deltas) {
+ denominator->ShiftLeft(1);
+ numerator->ShiftLeft(1);
+ delta_plus->AssignUInt16(1);
+ delta_plus->ShiftLeft(exponent);
+ delta_minus->AssignUInt16(1);
+ delta_minus->ShiftLeft(exponent);
+ }
+}
+
+static void InitialScaledStartValuesNegativeExponentPositivePower(
+ uint64_t significand, int exponent,
+ int estimated_power, bool need_boundary_deltas,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus)
+{
+ numerator->AssignUInt64(significand);
+ denominator->AssignPowerUInt16(10, estimated_power);
+ denominator->ShiftLeft(-exponent);
+
+ if (need_boundary_deltas) {
+ denominator->ShiftLeft(1);
+ numerator->ShiftLeft(1);
+ delta_plus->AssignUInt16(1);
+ delta_minus->AssignUInt16(1);
+ }
+}
+
+static void InitialScaledStartValuesNegativeExponentNegativePower(
+ uint64_t significand, int exponent,
+ int estimated_power, bool need_boundary_deltas,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus)
+{
+ Bignum* power_ten = numerator;
+ power_ten->AssignPowerUInt16(10, -estimated_power);
+
+ if (need_boundary_deltas) {
+ delta_plus->AssignBignum(*power_ten);
+ delta_minus->AssignBignum(*power_ten);
+ }
+
+ ASSERT(numerator == power_ten);
+ numerator->MultiplyByUInt64(significand);
+
+ denominator->AssignUInt16(1);
+ denominator->ShiftLeft(-exponent);
+
+ if (need_boundary_deltas) {
+ numerator->ShiftLeft(1);
+ denominator->ShiftLeft(1);
+ }
+}
+
+static void InitialScaledStartValues(uint64_t significand,
+ int exponent,
+ bool lower_boundary_is_closer,
+ int estimated_power,
+ bool need_boundary_deltas,
+ Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus)
+{
+ if (exponent >= 0) {
+ InitialScaledStartValuesPositiveExponent(
+ significand, exponent, estimated_power, need_boundary_deltas,
+ numerator, denominator, delta_minus, delta_plus);
+ } else if (estimated_power >= 0) {
+ InitialScaledStartValuesNegativeExponentPositivePower(
+ significand, exponent, estimated_power, need_boundary_deltas,
+ numerator, denominator, delta_minus, delta_plus);
+ } else {
+ InitialScaledStartValuesNegativeExponentNegativePower(
+ significand, exponent, estimated_power, need_boundary_deltas,
+ numerator, denominator, delta_minus, delta_plus);
+ }
+
+ if (need_boundary_deltas && lower_boundary_is_closer) {
+ denominator->ShiftLeft(1); // *2
+ numerator->ShiftLeft(1); // *2
+ delta_plus->ShiftLeft(1); // *2
+ }
+}
+
+static void FixupMultiply10(int estimated_power, bool is_even,
+ int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus) {
+ bool in_range;
+ if (is_even) {
+ in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0;
+ } else {
+ in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0;
+ }
+ if (in_range) {
+ *decimal_point = estimated_power + 1;
+ } else {
+ *decimal_point = estimated_power;
+ numerator->Times10();
+ if (Bignum::Equal(*delta_minus, *delta_plus)) {
+ delta_minus->Times10();
+ delta_plus->AssignBignum(*delta_minus);
+ } else {
+ delta_minus->Times10();
+ delta_plus->Times10();
+ }
+ }
+}
+
+enum FastDtoaMode {
+ FAST_DTOA_SHORTEST,
+ FAST_DTOA_PRECISION
+};
+
+static const int kFastDtoaMaximalLength = 17;
+
+bool FastDtoa(double d,
+ FastDtoaMode mode,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point);
+
+static const int kMinimalTargetExponent = -60;
+static const int kMaximalTargetExponent = -32;
+
+static bool RoundWeed(Vector<char> buffer, int length,
+ uint64_t distance_too_high_w, uint64_t unsafe_interval,
+ uint64_t rest, uint64_t ten_kappa, uint64_t unit)
+{
+ uint64_t small_distance = distance_too_high_w - unit;
+ uint64_t big_distance = distance_too_high_w + unit;
+
+ ASSERT(rest <= unsafe_interval);
+ while (rest < small_distance && // Negated condition 1
+ unsafe_interval - rest >= ten_kappa && // Negated condition 2
+ (rest + ten_kappa < small_distance || // buffer{-1} > w_high
+ small_distance - rest >= rest + ten_kappa - small_distance)) {
+ buffer[length - 1]--;
+ rest += ten_kappa;
+ }
+
+ if (rest < big_distance &&
+ unsafe_interval - rest >= ten_kappa &&
+ (rest + ten_kappa < big_distance ||
+ big_distance - rest > rest + ten_kappa - big_distance)) {
+ return false;
+ }
+
+ return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit);
+}
+
+static bool RoundWeedCounted(Vector<char> buffer, int length,
+ uint64_t rest, uint64_t ten_kappa, uint64_t unit,
+ int* kappa)
+{
+ ASSERT(rest < ten_kappa);
+
+ if (unit >= ten_kappa) return false;
+ if (ten_kappa - unit <= unit) return false;
+ if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) {
+ return true;
+ }
+
+ if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) {
+ buffer[length - 1]++;
+ for (int i = length - 1; i > 0; --i) {
+ if (buffer[i] != '0' + 10) break;
+ buffer[i] = '0';
+ buffer[i - 1]++;
+ }
+ if (buffer[0] == '0' + 10) {
+ buffer[0] = '1';
+ (*kappa) += 1;
+ }
+ return true;
+ }
+ return false;
+}
+
+static unsigned int const kSmallPowersOfTen[] =
+ {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
+ 1000000000};
+
+static void BiggestPowerTen(uint32_t number,
+ int number_bits,
+ uint32_t* power,
+ int* exponent_plus_one) {
+ ASSERT(number < (1u << (number_bits + 1)));
+
+ int exponent_plus_one_guess = ((number_bits + 1) * 1233 >> 12);
+ exponent_plus_one_guess++;
+
+ if (number < kSmallPowersOfTen[exponent_plus_one_guess]) {
+ exponent_plus_one_guess--;
+ }
+ *power = kSmallPowersOfTen[exponent_plus_one_guess];
+ *exponent_plus_one = exponent_plus_one_guess;
+}
+
+static bool DigitGen(DiyFp low, DiyFp w, DiyFp high, Vector<char> buffer,
+ int* length, int* kappa)
+{
+ ASSERT(low.e() == w.e() && w.e() == high.e());
+ ASSERT(low.f() + 1 <= high.f() - 1);
+ ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent);
+
+ uint64_t unit = 1;
+ DiyFp too_low = DiyFp(low.f() - unit, low.e());
+ DiyFp too_high = DiyFp(high.f() + unit, high.e());
+ DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low);
+ DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e());
+
+ uint32_t integrals = static_cast<uint32_t>(too_high.f() >> -one.e());
+ uint64_t fractionals = too_high.f() & (one.f() - 1);
+ uint32_t divisor;
+ int divisor_exponent_plus_one;
+ BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()),
+ &divisor, &divisor_exponent_plus_one);
+ *kappa = divisor_exponent_plus_one;
+ *length = 0;
+
+ while (*kappa > 0) {
+ int digit = integrals / divisor;
+ ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ integrals %= divisor;
+ (*kappa)--;
+
+ uint64_t rest =
+ (static_cast<uint64_t>(integrals) << -one.e()) + fractionals;
+
+ if (rest < unsafe_interval.f()) {
+ return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(),
+ unsafe_interval.f(), rest,
+ static_cast<uint64_t>(divisor) << -one.e(), unit);
+ }
+ divisor /= 10;
+ }
+
+ ASSERT(one.e() >= -60);
+ ASSERT(fractionals < one.f());
+ ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f());
+
+ for (;;) {
+ fractionals *= 10;
+ unit *= 10;
+ unsafe_interval.set_f(unsafe_interval.f() * 10);
+ // Integer division by one.
+ int digit = static_cast<int>(fractionals >> -one.e());
+ ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ fractionals &= one.f() - 1; // Modulo by one.
+ (*kappa)--;
+ if (fractionals < unsafe_interval.f()) {
+ return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit,
+ unsafe_interval.f(), fractionals, one.f(), unit);
+ }
+ }
+}
+
+static bool DigitGenCounted(DiyFp w, int requested_digits, Vector<char> buffer,
+ int* length, int* kappa)
+{
+ ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent);
+ ASSERT(kMinimalTargetExponent >= -60);
+ ASSERT(kMaximalTargetExponent <= -32);
+
+ uint64_t w_error = 1;
+ DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e());
+ uint32_t integrals = static_cast<uint32_t>(w.f() >> -one.e());
+ uint64_t fractionals = w.f() & (one.f() - 1);
+ uint32_t divisor;
+ int divisor_exponent_plus_one;
+ BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()),
+ &divisor, &divisor_exponent_plus_one);
+ *kappa = divisor_exponent_plus_one;
+ *length = 0;
+
+ while (*kappa > 0) {
+ int digit = integrals / divisor;
+ ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ requested_digits--;
+ integrals %= divisor;
+ (*kappa)--;
+ if (requested_digits == 0) break;
+ divisor /= 10;
+ }
+
+ if (requested_digits == 0) {
+ uint64_t rest =
+ (static_cast<uint64_t>(integrals) << -one.e()) + fractionals;
+ return RoundWeedCounted(buffer, *length, rest,
+ static_cast<uint64_t>(divisor) << -one.e(), w_error,
+ kappa);
+ }
+
+ ASSERT(one.e() >= -60);
+ ASSERT(fractionals < one.f());
+ ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f());
+
+ while (requested_digits > 0 && fractionals > w_error) {
+ fractionals *= 10;
+ w_error *= 10;
+ // Integer division by one.
+ int digit = static_cast<int>(fractionals >> -one.e());
+ ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ requested_digits--;
+ fractionals &= one.f() - 1; // Modulo by one.
+ (*kappa)--;
+ }
+ if (requested_digits != 0) return false;
+ return RoundWeedCounted(buffer, *length, fractionals, one.f(), w_error,
+ kappa);
+}
+
+static bool Grisu3(double v, FastDtoaMode mode, Vector<char> buffer,
+ int* length, int* decimal_exponent)
+{
+ DiyFp w = Double(v).AsNormalizedDiyFp();
+ DiyFp boundary_minus, boundary_plus;
+
+ ASSERT(mode == FAST_DTOA_SHORTEST);
+ Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus);
+
+ ASSERT(boundary_plus.e() == w.e());
+ DiyFp ten_mk; // Cached power of ten: 10^-k
+ int mk; // -k
+ int ten_mk_minimal_binary_exponent =
+ kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize);
+ int ten_mk_maximal_binary_exponent =
+ kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize);
+ PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
+ ten_mk_minimal_binary_exponent,
+ ten_mk_maximal_binary_exponent,
+ &ten_mk, &mk);
+
+ ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() +
+ DiyFp::kSignificandSize) &&
+ (kMaximalTargetExponent >= w.e() + ten_mk.e() +
+ DiyFp::kSignificandSize));
+
+ DiyFp scaled_w = DiyFp::Times(w, ten_mk);
+ ASSERT(scaled_w.e() ==
+ boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize);
+
+ DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk);
+ DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk);
+
+ int kappa;
+ bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus,
+ buffer, length, &kappa);
+ *decimal_exponent = -mk + kappa;
+ return result;
+}
+
+static bool Grisu3Counted(double v, int requested_digits, Vector<char> buffer,
+ int* length, int* decimal_exponent)
+{
+ DiyFp w = Double(v).AsNormalizedDiyFp();
+ DiyFp ten_mk; // Cached power of ten: 10^-k
+ int mk; // -k
+ int ten_mk_minimal_binary_exponent =
+ kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize);
+ int ten_mk_maximal_binary_exponent =
+ kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize);
+ PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
+ ten_mk_minimal_binary_exponent,
+ ten_mk_maximal_binary_exponent,
+ &ten_mk, &mk);
+ ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() +
+ DiyFp::kSignificandSize) &&
+ (kMaximalTargetExponent >= w.e() + ten_mk.e() +
+ DiyFp::kSignificandSize));
+
+ DiyFp scaled_w = DiyFp::Times(w, ten_mk);
+
+ int kappa;
+ bool result = DigitGenCounted(scaled_w, requested_digits,
+ buffer, length, &kappa);
+ *decimal_exponent = -mk + kappa;
+ return result;
+}
+
+
+bool FastDtoa(double v,
+ FastDtoaMode mode,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point) {
+ ASSERT(v > 0);
+ ASSERT(!Double(v).IsSpecial());
+
+ bool result = false;
+ int decimal_exponent = 0;
+ switch (mode) {
+ case FAST_DTOA_SHORTEST:
+ result = Grisu3(v, mode, buffer, length, &decimal_exponent);
+ break;
+ case FAST_DTOA_PRECISION:
+ result = Grisu3Counted(v, requested_digits,
+ buffer, length, &decimal_exponent);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ if (result) {
+ *decimal_point = *length + decimal_exponent;
+ buffer[*length] = '\0';
+ }
+ return result;
+}
+
+// Represents a 128bit type. This class should be replaced by a native type on
+// platforms that support 128bit integers.
+class UInt128 {
+ public:
+ UInt128() : high_bits_(0), low_bits_(0) { }
+ UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) { }
+
+ void Multiply(uint32_t multiplicand) {
+ uint64_t accumulator;
+
+ accumulator = (low_bits_ & kMask32) * multiplicand;
+ uint32_t part = static_cast<uint32_t>(accumulator & kMask32);
+ accumulator >>= 32;
+ accumulator = accumulator + (low_bits_ >> 32) * multiplicand;
+ low_bits_ = (accumulator << 32) + part;
+ accumulator >>= 32;
+ accumulator = accumulator + (high_bits_ & kMask32) * multiplicand;
+ part = static_cast<uint32_t>(accumulator & kMask32);
+ accumulator >>= 32;
+ accumulator = accumulator + (high_bits_ >> 32) * multiplicand;
+ high_bits_ = (accumulator << 32) + part;
+ ASSERT((accumulator >> 32) == 0);
+ }
+
+ void Shift(int shift_amount) {
+ ASSERT(-64 <= shift_amount && shift_amount <= 64);
+ if (shift_amount == 0) {
+ return;
+ } else if (shift_amount == -64) {
+ high_bits_ = low_bits_;
+ low_bits_ = 0;
+ } else if (shift_amount == 64) {
+ low_bits_ = high_bits_;
+ high_bits_ = 0;
+ } else if (shift_amount <= 0) {
+ high_bits_ <<= -shift_amount;
+ high_bits_ += low_bits_ >> (64 + shift_amount);
+ low_bits_ <<= -shift_amount;
+ } else {
+ low_bits_ >>= shift_amount;
+ low_bits_ += high_bits_ << (64 - shift_amount);
+ high_bits_ >>= shift_amount;
+ }
+ }
+
+ // Modifies *this to *this MOD (2^power).
+ // Returns *this DIV (2^power).
+ int DivModPowerOf2(int power) {
+ if (power >= 64) {
+ int result = static_cast<int>(high_bits_ >> (power - 64));
+ high_bits_ -= static_cast<uint64_t>(result) << (power - 64);
+ return result;
+ } else {
+ uint64_t part_low = low_bits_ >> power;
+ uint64_t part_high = high_bits_ << (64 - power);
+ int result = static_cast<int>(part_low + part_high);
+ high_bits_ = 0;
+ low_bits_ -= part_low << power;
+ return result;
+ }
+ }
+
+ bool IsZero() const {
+ return high_bits_ == 0 && low_bits_ == 0;
+ }
+
+ int BitAt(int position) {
+ if (position >= 64) {
+ return static_cast<int>(high_bits_ >> (position - 64)) & 1;
+ } else {
+ return static_cast<int>(low_bits_ >> position) & 1;
+ }
+ }
+
+ private:
+ static const uint64_t kMask32 = 0xFFFFFFFF;
+ // Value == (high_bits_ << 64) + low_bits_
+ uint64_t high_bits_;
+ uint64_t low_bits_;
+};
+
+
+static const int kDoubleSignificandSize = 53; // Includes the hidden bit.
+
+
+static void FillDigits32FixedLength(uint32_t number, int requested_length,
+ Vector<char> buffer, int* length) {
+ for (int i = requested_length - 1; i >= 0; --i) {
+ buffer[(*length) + i] = '0' + number % 10;
+ number /= 10;
+ }
+ *length += requested_length;
+}
+
+
+static void FillDigits32(uint32_t number, Vector<char> buffer, int* length) {
+ int number_length = 0;
+ // We fill the digits in reverse order and exchange them afterwards.
+ while (number != 0) {
+ int digit = number % 10;
+ number /= 10;
+ buffer[(*length) + number_length] = static_cast<char>('0' + digit);
+ number_length++;
+ }
+ // Exchange the digits.
+ int i = *length;
+ int j = *length + number_length - 1;
+ while (i < j) {
+ char tmp = buffer[i];
+ buffer[i] = buffer[j];
+ buffer[j] = tmp;
+ i++;
+ j--;
+ }
+ *length += number_length;
+}
+
+
+static void FillDigits64FixedLength(uint64_t number,
+ Vector<char> buffer, int* length) {
+ const uint32_t kTen7 = 10000000;
+ // For efficiency cut the number into 3 uint32_t parts, and print those.
+ uint32_t part2 = static_cast<uint32_t>(number % kTen7);
+ number /= kTen7;
+ uint32_t part1 = static_cast<uint32_t>(number % kTen7);
+ uint32_t part0 = static_cast<uint32_t>(number / kTen7);
+
+ FillDigits32FixedLength(part0, 3, buffer, length);
+ FillDigits32FixedLength(part1, 7, buffer, length);
+ FillDigits32FixedLength(part2, 7, buffer, length);
+}
+
+
+static void FillDigits64(uint64_t number, Vector<char> buffer, int* length) {
+ const uint32_t kTen7 = 10000000;
+ // For efficiency cut the number into 3 uint32_t parts, and print those.
+ uint32_t part2 = static_cast<uint32_t>(number % kTen7);
+ number /= kTen7;
+ uint32_t part1 = static_cast<uint32_t>(number % kTen7);
+ uint32_t part0 = static_cast<uint32_t>(number / kTen7);
+
+ if (part0 != 0) {
+ FillDigits32(part0, buffer, length);
+ FillDigits32FixedLength(part1, 7, buffer, length);
+ FillDigits32FixedLength(part2, 7, buffer, length);
+ } else if (part1 != 0) {
+ FillDigits32(part1, buffer, length);
+ FillDigits32FixedLength(part2, 7, buffer, length);
+ } else {
+ FillDigits32(part2, buffer, length);
+ }
+}
+
+
+static void RoundUp(Vector<char> buffer, int* length, int* decimal_point) {
+ // An empty buffer represents 0.
+ if (*length == 0) {
+ buffer[0] = '1';
+ *decimal_point = 1;
+ *length = 1;
+ return;
+ }
+
+ buffer[(*length) - 1]++;
+ for (int i = (*length) - 1; i > 0; --i) {
+ if (buffer[i] != '0' + 10) {
+ return;
+ }
+ buffer[i] = '0';
+ buffer[i - 1]++;
+ }
+
+ if (buffer[0] == '0' + 10) {
+ buffer[0] = '1';
+ (*decimal_point)++;
+ }
+}
+
+static void FillFractionals(uint64_t fractionals, int exponent,
+ int fractional_count, Vector<char> buffer,
+ int* length, int* decimal_point)
+{
+ ASSERT(-128 <= exponent && exponent <= 0);
+
+ if (-exponent <= 64) {
+ ASSERT(fractionals >> 56 == 0);
+ int point = -exponent;
+ for (int i = 0; i < fractional_count; ++i) {
+ if (fractionals == 0) break;
+ fractionals *= 5;
+ point--;
+ int digit = static_cast<int>(fractionals >> point);
+ ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ fractionals -= static_cast<uint64_t>(digit) << point;
+ }
+
+ if (((fractionals >> (point - 1)) & 1) == 1) {
+ RoundUp(buffer, length, decimal_point);
+ }
+ } else { // We need 128 bits.
+ ASSERT(64 < -exponent && -exponent <= 128);
+ UInt128 fractionals128 = UInt128(fractionals, 0);
+ fractionals128.Shift(-exponent - 64);
+ int point = 128;
+ for (int i = 0; i < fractional_count; ++i) {
+ if (fractionals128.IsZero()) break;
+ fractionals128.Multiply(5);
+ point--;
+ int digit = fractionals128.DivModPowerOf2(point);
+ ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ }
+ if (fractionals128.BitAt(point - 1) == 1) {
+ RoundUp(buffer, length, decimal_point);
+ }
+ }
+}
+
+
+// Removes leading and trailing zeros.
+// If leading zeros are removed then the decimal point position is adjusted.
+static void TrimZeros(Vector<char> buffer, int* length, int* decimal_point) {
+ while (*length > 0 && buffer[(*length) - 1] == '0') {
+ (*length)--;
+ }
+ int first_non_zero = 0;
+ while (first_non_zero < *length && buffer[first_non_zero] == '0') {
+ first_non_zero++;
+ }
+ if (first_non_zero != 0) {
+ for (int i = first_non_zero; i < *length; ++i) {
+ buffer[i - first_non_zero] = buffer[i];
+ }
+ *length -= first_non_zero;
+ *decimal_point -= first_non_zero;
+ }
+}
+
+
+bool FastFixedDtoa(double v,
+ int fractional_count,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point) {
+ const uint32_t kMaxUInt32 = 0xFFFFFFFF;
+ uint64_t significand = Double(v).Significand();
+ int exponent = Double(v).Exponent();
+
+ if (exponent > 20) return false;
+ if (fractional_count > 20) return false;
+ *length = 0;
+
+ if (exponent + kDoubleSignificandSize > 64) {
+ const uint64_t kFive17 = UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17
+ uint64_t divisor = kFive17;
+ int divisor_power = 17;
+ uint64_t dividend = significand;
+ uint32_t quotient;
+ uint64_t remainder;
+
+ if (exponent > divisor_power) {
+ dividend <<= exponent - divisor_power;
+ quotient = static_cast<uint32_t>(dividend / divisor);
+ remainder = (dividend % divisor) << divisor_power;
+ } else {
+ divisor <<= divisor_power - exponent;
+ quotient = static_cast<uint32_t>(dividend / divisor);
+ remainder = (dividend % divisor) << exponent;
+ }
+ FillDigits32(quotient, buffer, length);
+ FillDigits64FixedLength(remainder, buffer, length);
+ *decimal_point = *length;
+ } else if (exponent >= 0) {
+ // 0 <= exponent <= 11
+ significand <<= exponent;
+ FillDigits64(significand, buffer, length);
+ *decimal_point = *length;
+ } else if (exponent > -kDoubleSignificandSize) {
+ uint64_t integrals = significand >> -exponent;
+ uint64_t fractionals = significand - (integrals << -exponent);
+ if (integrals > kMaxUInt32) {
+ FillDigits64(integrals, buffer, length);
+ } else {
+ FillDigits32(static_cast<uint32_t>(integrals), buffer, length);
+ }
+ *decimal_point = *length;
+ FillFractionals(fractionals, exponent, fractional_count,
+ buffer, length, decimal_point);
+ } else if (exponent < -128) {
+ // This configuration (with at most 20 digits) means that all digits must be
+ // 0.
+ ASSERT(fractional_count <= 20);
+ buffer[0] = '\0';
+ *length = 0;
+ *decimal_point = -fractional_count;
+ } else {
+ *decimal_point = 0;
+ FillFractionals(significand, exponent, fractional_count,
+ buffer, length, decimal_point);
+ }
+ TrimZeros(buffer, length, decimal_point);
+ buffer[*length] = '\0';
+ if ((*length) == 0) {
+ *decimal_point = -fractional_count;
+ }
+ return true;
+}
+
+static const int kMaxExactDoubleIntegerDecimalDigits = 15;
+static const int kMaxUint64DecimalDigits = 19;
+
+static const int kMaxDecimalPower = 309;
+static const int kMinDecimalPower = -324;
+
+// 2^64 = 18446744073709551616
+static const uint64_t kMaxUint64 = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF);
+static const int kMaxSignificantDecimalDigits = 780;
+
+static Vector<const char> TrimLeadingZeros(Vector<const char> buffer) {
+ for (int i = 0; i < buffer.length(); i++) {
+ if (buffer[i] != '0') {
+ return buffer.SubVector(i, buffer.length());
+ }
+ }
+ return Vector<const char>(buffer.start(), 0);
+}
+
+
+static Vector<const char> TrimTrailingZeros(Vector<const char> buffer) {
+ for (int i = buffer.length() - 1; i >= 0; --i) {
+ if (buffer[i] != '0') {
+ return buffer.SubVector(0, i + 1);
+ }
+ }
+ return Vector<const char>(buffer.start(), 0);
+}
+
+
+static void CutToMaxSignificantDigits(Vector<const char> buffer,
+ int exponent,
+ char* significant_buffer,
+ int* significant_exponent) {
+ for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) {
+ significant_buffer[i] = buffer[i];
+ }
+
+ ASSERT(buffer[buffer.length() - 1] != '0');
+
+ significant_buffer[kMaxSignificantDecimalDigits - 1] = '1';
+ *significant_exponent =
+ exponent + (buffer.length() - kMaxSignificantDecimalDigits);
+}
+
+static void TrimAndCut(Vector<const char> buffer, int exponent,
+ char* buffer_copy_space, int space_size,
+ Vector<const char>* trimmed, int* updated_exponent) {
+ Vector<const char> left_trimmed = TrimLeadingZeros(buffer);
+ Vector<const char> right_trimmed = TrimTrailingZeros(left_trimmed);
+ exponent += left_trimmed.length() - right_trimmed.length();
+ if (right_trimmed.length() > kMaxSignificantDecimalDigits) {
+ (void) space_size; // Mark variable as used.
+ ASSERT(space_size >= kMaxSignificantDecimalDigits);
+ CutToMaxSignificantDigits(right_trimmed, exponent,
+ buffer_copy_space, updated_exponent);
+ *trimmed = Vector<const char>(buffer_copy_space,
+ kMaxSignificantDecimalDigits);
+ } else {
+ *trimmed = right_trimmed;
+ *updated_exponent = exponent;
+ }
+}
+
+static uint64_t ReadUint64(Vector<const char> buffer,
+ int* number_of_read_digits) {
+ uint64_t result = 0;
+ int i = 0;
+ while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) {
+ int digit = buffer[i++] - '0';
+ ASSERT(0 <= digit && digit <= 9);
+ result = 10 * result + digit;
+ }
+ *number_of_read_digits = i;
+ return result;
+}
+
+static void ReadDiyFp(Vector<const char> buffer,
+ DiyFp* result,
+ int* remaining_decimals) {
+ int read_digits;
+ uint64_t significand = ReadUint64(buffer, &read_digits);
+ if (buffer.length() == read_digits) {
+ *result = DiyFp(significand, 0);
+ *remaining_decimals = 0;
+ } else {
+ // Round the significand.
+ if (buffer[read_digits] >= '5') {
+ significand++;
+ }
+ // Compute the binary exponent.
+ int exponent = 0;
+ *result = DiyFp(significand, exponent);
+ *remaining_decimals = buffer.length() - read_digits;
+ }
+}
+
+static DiyFp AdjustmentPowerOfTen(int exponent) {
+ ASSERT(0 < exponent);
+ ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance);
+ // Simply hardcode the remaining powers for the given decimal exponent
+ // distance.
+ ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8);
+ switch (exponent) {
+ case 1: return DiyFp(UINT64_2PART_C(0xa0000000, 00000000), -60);
+ case 2: return DiyFp(UINT64_2PART_C(0xc8000000, 00000000), -57);
+ case 3: return DiyFp(UINT64_2PART_C(0xfa000000, 00000000), -54);
+ case 4: return DiyFp(UINT64_2PART_C(0x9c400000, 00000000), -50);
+ case 5: return DiyFp(UINT64_2PART_C(0xc3500000, 00000000), -47);
+ case 6: return DiyFp(UINT64_2PART_C(0xf4240000, 00000000), -44);
+ case 7: return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40);
+ default:
+ UNREACHABLE();
+ }
+}
+
+static bool DiyFpStrtod(Vector<const char> buffer,
+ int exponent,
+ double* result) {
+ DiyFp input;
+ int remaining_decimals;
+ ReadDiyFp(buffer, &input, &remaining_decimals);
+
+ const int kDenominatorLog = 3;
+ const int kDenominator = 1 << kDenominatorLog;
+ // Move the remaining decimals into the exponent.
+ exponent += remaining_decimals;
+ int error = (remaining_decimals == 0 ? 0 : kDenominator / 2);
+
+ int old_e = input.e();
+ input.Normalize();
+ error <<= old_e - input.e();
+
+ ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent);
+ if (exponent < PowersOfTenCache::kMinDecimalExponent) {
+ *result = 0.0;
+ return true;
+ }
+ DiyFp cached_power;
+ int cached_decimal_exponent;
+ PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent,
+ &cached_power,
+ &cached_decimal_exponent);
+
+ if (cached_decimal_exponent != exponent) {
+ int adjustment_exponent = exponent - cached_decimal_exponent;
+ DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent);
+ input.Multiply(adjustment_power);
+ if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) {
+ // The product of input with the adjustment power fits into a 64 bit
+ // integer.
+ ASSERT(DiyFp::kSignificandSize == 64);
+ } else {
+ // The adjustment power is exact. There is hence only an error of 0.5.
+ error += kDenominator / 2;
+ }
+ }
+
+ input.Multiply(cached_power);
+
+ int error_b = kDenominator / 2;
+ int error_ab = (error == 0 ? 0 : 1); // We round up to 1.
+ int fixed_error = kDenominator / 2;
+ error += error_b + error_ab + fixed_error;
+
+ old_e = input.e();
+ input.Normalize();
+ error <<= old_e - input.e();
+
+ int order_of_magnitude = DiyFp::kSignificandSize + input.e();
+ int effective_significand_size =
+ Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude);
+ int precision_digits_count =
+ DiyFp::kSignificandSize - effective_significand_size;
+ if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) {
+ int shift_amount = (precision_digits_count + kDenominatorLog) -
+ DiyFp::kSignificandSize + 1;
+ input.set_f(input.f() >> shift_amount);
+ input.set_e(input.e() + shift_amount);
+ error = (error >> shift_amount) + 1 + kDenominator;
+ precision_digits_count -= shift_amount;
+ }
+
+ ASSERT(DiyFp::kSignificandSize == 64);
+ ASSERT(precision_digits_count < 64);
+ uint64_t one64 = 1;
+ uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1;
+ uint64_t precision_bits = input.f() & precision_bits_mask;
+ uint64_t half_way = one64 << (precision_digits_count - 1);
+ precision_bits *= kDenominator;
+ half_way *= kDenominator;
+ DiyFp rounded_input(input.f() >> precision_digits_count,
+ input.e() + precision_digits_count);
+ if (precision_bits >= half_way + error) {
+ rounded_input.set_f(rounded_input.f() + 1);
+ }
+
+ *result = Double(rounded_input).value();
+ if (half_way - error < precision_bits && precision_bits < half_way + error) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+static int CompareBufferWithDiyFp(Vector<const char> buffer,
+ int exponent,
+ DiyFp diy_fp) {
+ ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1);
+ ASSERT(buffer.length() + exponent > kMinDecimalPower);
+ ASSERT(buffer.length() <= kMaxSignificantDecimalDigits);
+ ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits);
+
+ Bignum buffer_bignum;
+ Bignum diy_fp_bignum;
+ buffer_bignum.AssignDecimalString(buffer);
+ diy_fp_bignum.AssignUInt64(diy_fp.f());
+ if (exponent >= 0) {
+ buffer_bignum.MultiplyByPowerOfTen(exponent);
+ } else {
+ diy_fp_bignum.MultiplyByPowerOfTen(-exponent);
+ }
+ if (diy_fp.e() > 0) {
+ diy_fp_bignum.ShiftLeft(diy_fp.e());
+ } else {
+ buffer_bignum.ShiftLeft(-diy_fp.e());
+ }
+ return Bignum::Compare(buffer_bignum, diy_fp_bignum);
+}
+
+static bool ComputeGuess(Vector<const char> trimmed, int exponent,
+ double* guess)
+{
+ if (trimmed.length() == 0) {
+ *guess = 0.0;
+ return true;
+ }
+ if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) {
+ *guess = Double::Infinity();
+ return true;
+ }
+ if (exponent + trimmed.length() <= kMinDecimalPower) {
+ *guess = 0.0;
+ return true;
+ }
+
+ if (DiyFpStrtod(trimmed, exponent, guess)) {
+ return true;
+ }
+ if (*guess == Double::Infinity()) {
+ return true;
+ }
+ return false;
+}
+
+double Strtod(Vector<const char> buffer, int exponent)
+{
+ char copy_buffer[kMaxSignificantDecimalDigits];
+ Vector<const char> trimmed;
+ int updated_exponent;
+ TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits,
+ &trimmed, &updated_exponent);
+ exponent = updated_exponent;
+
+ double guess;
+ bool is_correct = ComputeGuess(trimmed, exponent, &guess);
+ if (is_correct) return guess;
+
+ DiyFp upper_boundary = Double(guess).UpperBoundary();
+ int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary);
+ if (comparison < 0) {
+ return guess;
+ } else if (comparison > 0) {
+ return Double(guess).NextDouble();
+ } else if ((Double(guess).Significand() & 1) == 0) {
+ // Round towards even.
+ return guess;
+ } else {
+ return Double(guess).NextDouble();
+ }
+}
+
+class DoubleToStringConverter {
+public:
+ static const int kMaxFixedDigitsBeforePoint = 60;
+ static const int kMaxFixedDigitsAfterPoint = 60;
+ static const int kMaxExponentialDigits = 120;
+ static const int kMinPrecisionDigits = 1;
+ static const int kMaxPrecisionDigits = 120;
+
+ enum Flags {
+ NO_FLAGS = 0,
+ EMIT_POSITIVE_EXPONENT_SIGN = 1,
+ EMIT_TRAILING_DECIMAL_POINT = 2,
+ EMIT_TRAILING_ZERO_AFTER_POINT = 4,
+ UNIQUE_ZERO = 8
+ };
+
+ DoubleToStringConverter(int flags,
+ const char* infinity_symbol,
+ const char* nan_symbol,
+ char exponent_character,
+ int decimal_in_shortest_low,
+ int decimal_in_shortest_high,
+ int max_leading_padding_zeroes_in_precision_mode,
+ int max_trailing_padding_zeroes_in_precision_mode)
+ : flags_(flags),
+ infinity_symbol_(infinity_symbol),
+ nan_symbol_(nan_symbol),
+ exponent_character_(exponent_character),
+ decimal_in_shortest_low_(decimal_in_shortest_low),
+ decimal_in_shortest_high_(decimal_in_shortest_high),
+ max_leading_padding_zeroes_in_precision_mode_(
+ max_leading_padding_zeroes_in_precision_mode),
+ max_trailing_padding_zeroes_in_precision_mode_(
+ max_trailing_padding_zeroes_in_precision_mode) {
+ // When 'trailing zero after the point' is set, then 'trailing point'
+ // must be set too.
+ ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) ||
+ !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0));
+ }
+
+ bool ToShortest(double value, std::string &s) const {
+ return ToShortestIeeeNumber(value, s, SHORTEST);
+ }
+
+ bool ToFixed(double value,
+ int requested_digits,
+ std::string &s) const;
+
+ bool ToExponential(double value,
+ int requested_digits,
+ std::string &s) const;
+
+ bool ToPrecision(double value,
+ int precision,
+ std::string &s) const;
+
+ enum DtoaMode {
+ SHORTEST,
+ FIXED, // Produce a fixed number of digits after the decimal point
+ PRECISION // Fixed number of digits (independent of the decimal point)
+ };
+
+ static const int kBase10MaximalLength = 17;
+
+ // The result should be interpreted as buffer * 10^(point-length).
+ static void DoubleToAscii(double v,
+ DtoaMode mode,
+ int requested_digits,
+ char* buffer,
+ int buffer_length,
+ bool* sign,
+ int* length,
+ int* point);
+
+ private:
+ // Implementation for ToShortest.
+ bool ToShortestIeeeNumber(double value,
+ std::string &s,
+ DtoaMode mode) const;
+
+ bool HandleSpecialValues(double value, std::string &s) const;
+
+ void CreateExponentialRepresentation(const char* decimal_digits,
+ int length,
+ int exponent,
+ std::string &s) const;
+
+ void CreateDecimalRepresentation(const char* decimal_digits,
+ int length,
+ int decimal_point,
+ int digits_after_point,
+ std::string &s) const;
+
+ const int flags_;
+ const char* const infinity_symbol_;
+ const char* const nan_symbol_;
+ const char exponent_character_;
+ const int decimal_in_shortest_low_;
+ const int decimal_in_shortest_high_;
+ const int max_leading_padding_zeroes_in_precision_mode_;
+ const int max_trailing_padding_zeroes_in_precision_mode_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter);
+};
+
+
+class StringToDoubleConverter {
+ public:
+ enum Flags {
+ NO_FLAGS = 0,
+ ALLOW_HEX = 1,
+ ALLOW_OCTALS = 2,
+ ALLOW_TRAILING_JUNK = 4,
+ ALLOW_LEADING_SPACES = 8,
+ ALLOW_TRAILING_SPACES = 16,
+ ALLOW_SPACES_AFTER_SIGN = 32
+ };
+
+ StringToDoubleConverter(int flags,
+ double empty_string_value,
+ double junk_string_value,
+ const char* infinity_symbol,
+ const char* nan_symbol)
+ : flags_(flags),
+ empty_string_value_(empty_string_value),
+ junk_string_value_(junk_string_value),
+ infinity_symbol_(infinity_symbol),
+ nan_symbol_(nan_symbol) {
+ }
+
+ double StringToDouble(const char* buffer,
+ int length,
+ int* processed_characters_count) const;
+
+ private:
+ const int flags_;
+ const double empty_string_value_;
+ const double junk_string_value_;
+ const char* const infinity_symbol_;
+ const char* const nan_symbol_;
+
+ double StringToIeee(const char *start_pointer,
+ int length,
+ int* processed_characters_count) const;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter);
+};
+
+bool DoubleToStringConverter::HandleSpecialValues(
+ double value,
+ std::string &result) const {
+ Double double_inspect(value);
+ if (double_inspect.IsInfinite()) {
+ if (infinity_symbol_ == NULL) return false;
+ if (value < 0) {
+ result += '-';
+ }
+ result += infinity_symbol_;
+ return true;
+ }
+ if (double_inspect.IsNan()) {
+ if (nan_symbol_ == NULL) return false;
+ result = nan_symbol_;
+ return true;
+ }
+ return false;
+}
+
+
+void DoubleToStringConverter::CreateExponentialRepresentation(
+ const char* decimal_digits,
+ int length,
+ int exponent,
+ std::string &result) const {
+ ASSERT(length != 0);
+ result += decimal_digits[0];
+ if (length != 1) {
+ result += '.';
+ result.append(&decimal_digits[1], length-1);
+ }
+ result += exponent_character_;
+ if (exponent < 0) {
+ result += '-';
+ exponent = -exponent;
+ } else {
+ if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) {
+ result += '+';
+ }
+ }
+ if (exponent == 0) {
+ result += '0';
+ return;
+ }
+ ASSERT(exponent < 1e4);
+ const int kMaxExponentLength = 5;
+ char buffer[kMaxExponentLength + 1];
+ buffer[kMaxExponentLength] = '\0';
+ int first_char_pos = kMaxExponentLength;
+ while (exponent > 0) {
+ buffer[--first_char_pos] = '0' + (exponent % 10);
+ exponent /= 10;
+ }
+ result.append(&buffer[first_char_pos],
+ kMaxExponentLength - first_char_pos);
+}
+
+
+void DoubleToStringConverter::CreateDecimalRepresentation(
+ const char* decimal_digits,
+ int length,
+ int decimal_point,
+ int digits_after_point,
+ std::string &result) const {
+ // Create a representation that is padded with zeros if needed.
+ if (decimal_point <= 0) {
+ // "0.00000decimal_rep".
+ result += '0';
+ if (digits_after_point > 0) {
+ result += '.';
+ result.append(-decimal_point, '0');
+ ASSERT(length <= digits_after_point - (-decimal_point));
+ result.append(decimal_digits, length);
+ int remaining_digits = digits_after_point - (-decimal_point) - length;
+ result.append(remaining_digits, '0');
+ }
+ } else if (decimal_point >= length) {
+ // "decimal_rep0000.00000" or "decimal_rep.0000"
+ result.append(decimal_digits, length);
+ result.append(decimal_point - length, '0');
+ if (digits_after_point > 0) {
+ result += '.';
+ result.append(digits_after_point, '0');
+ }
+ } else {
+ // "decima.l_rep000"
+ ASSERT(digits_after_point > 0);
+ result.append(decimal_digits, decimal_point);
+ result += '.';
+ ASSERT(length - decimal_point <= digits_after_point);
+ result.append(&decimal_digits[decimal_point], length - decimal_point);
+ int remaining_digits = digits_after_point - (length - decimal_point);
+ result.append(remaining_digits, '0');
+ }
+ if (digits_after_point == 0) {
+ if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) {
+ result += '.';
+ }
+ if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) {
+ result += '0';
+ }
+ }
+}
+
+
+bool DoubleToStringConverter::ToShortestIeeeNumber(
+ double value,
+ std::string &result,
+ DoubleToStringConverter::DtoaMode mode) const {
+ ASSERT(mode == SHORTEST);
+ if (Double(value).IsSpecial()) {
+ return HandleSpecialValues(value, result);
+ }
+
+ int decimal_point;
+ bool sign;
+ const int kDecimalRepCapacity = kBase10MaximalLength + 1;
+ char decimal_rep[kDecimalRepCapacity];
+ int decimal_rep_length;
+
+ DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity,
+ &sign, &decimal_rep_length, &decimal_point);
+
+ bool unique_zero = (flags_ & UNIQUE_ZERO) != 0;
+ if (sign && (value != 0.0 || !unique_zero)) {
+ result += '-';
+ }
+
+ int exponent = decimal_point - 1;
+ if ((decimal_in_shortest_low_ <= exponent) &&
+ (exponent < decimal_in_shortest_high_)) {
+ CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
+ std::max(0, decimal_rep_length - decimal_point),
+ result);
+ } else {
+ CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, result);
+ }
+ return true;
+}
+
+
+bool DoubleToStringConverter::ToFixed(double value,
+ int requested_digits,
+ std::string &result) const
+{
+ ASSERT(kMaxFixedDigitsBeforePoint == 60);
+ const double kFirstNonFixed = 1e60;
+
+ if (Double(value).IsSpecial()) {
+ return HandleSpecialValues(value, result);
+ }
+
+ if (requested_digits > kMaxFixedDigitsAfterPoint) return false;
+ if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false;
+
+ // Find a sufficiently precise decimal representation of n.
+ int decimal_point;
+ bool sign;
+ // Add space for the '\0' byte.
+ const int kDecimalRepCapacity =
+ kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1;
+ char decimal_rep[kDecimalRepCapacity];
+ int decimal_rep_length;
+ DoubleToAscii(value, FIXED, requested_digits,
+ decimal_rep, kDecimalRepCapacity,
+ &sign, &decimal_rep_length, &decimal_point);
+
+ bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
+ if (sign && (value != 0.0 || !unique_zero)) {
+ result += '-';
+ }
+
+ CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
+ requested_digits, result);
+ return true;
+}
+
+
+bool DoubleToStringConverter::ToExponential(
+ double value,
+ int requested_digits,
+ std::string &result) const {
+ if (Double(value).IsSpecial()) {
+ return HandleSpecialValues(value, result);
+ }
+
+ if (requested_digits < -1) return false;
+ if (requested_digits > kMaxExponentialDigits) return false;
+
+ int decimal_point;
+ bool sign;
+ // Add space for digit before the decimal point and the '\0' character.
+ const int kDecimalRepCapacity = kMaxExponentialDigits + 2;
+ ASSERT(kDecimalRepCapacity > kBase10MaximalLength);
+ char decimal_rep[kDecimalRepCapacity];
+ int decimal_rep_length;
+
+ if (requested_digits == -1) {
+ DoubleToAscii(value, SHORTEST, 0,
+ decimal_rep, kDecimalRepCapacity,
+ &sign, &decimal_rep_length, &decimal_point);
+ } else {
+ DoubleToAscii(value, PRECISION, requested_digits + 1,
+ decimal_rep, kDecimalRepCapacity,
+ &sign, &decimal_rep_length, &decimal_point);
+ ASSERT(decimal_rep_length <= requested_digits + 1);
+
+ for (int i = decimal_rep_length; i < requested_digits + 1; ++i) {
+ decimal_rep[i] = '0';
+ }
+ decimal_rep_length = requested_digits + 1;
+ }
+
+ bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
+ if (sign && (value != 0.0 || !unique_zero)) {
+ result += '-';
+ }
+
+ int exponent = decimal_point - 1;
+ CreateExponentialRepresentation(decimal_rep,
+ decimal_rep_length,
+ exponent, result);
+ return true;
+}
+
+
+bool DoubleToStringConverter::ToPrecision(double value,
+ int precision,
+ std::string &result) const {
+ if (Double(value).IsSpecial()) {
+ return HandleSpecialValues(value, result);
+ }
+
+ if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) {
+ return false;
+ }
+
+ // Find a sufficiently precise decimal representation of n.
+ int decimal_point;
+ bool sign;
+ // Add one for the terminating null character.
+ const int kDecimalRepCapacity = kMaxPrecisionDigits + 1;
+ char decimal_rep[kDecimalRepCapacity];
+ int decimal_rep_length;
+
+ DoubleToAscii(value, PRECISION, precision,
+ decimal_rep, kDecimalRepCapacity,
+ &sign, &decimal_rep_length, &decimal_point);
+ ASSERT(decimal_rep_length <= precision);
+
+ bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
+ if (sign && (value != 0.0 || !unique_zero)) {
+ result += '-';
+ }
+
+ // The exponent if we print the number as x.xxeyyy. That is with the
+ // decimal point after the first digit.
+ int exponent = decimal_point - 1;
+
+ int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0;
+ if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) ||
+ (decimal_point - precision + extra_zero >
+ max_trailing_padding_zeroes_in_precision_mode_)) {
+ for (int i = decimal_rep_length; i < precision; ++i) {
+ decimal_rep[i] = '0';
+ }
+
+ CreateExponentialRepresentation(decimal_rep,
+ precision,
+ exponent,
+ result);
+ } else {
+ CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
+ std::max(0, precision - decimal_point),
+ result);
+ }
+ return true;
+}
+
+
+static BignumDtoaMode DtoaToBignumDtoaMode(
+ DoubleToStringConverter::DtoaMode dtoa_mode) {
+ switch (dtoa_mode) {
+ case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST;
+ case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED;
+ case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void DoubleToStringConverter::DoubleToAscii(double v, DtoaMode mode, int requested_digits,
+ char* buffer, int buffer_length,
+ bool* sign, int* length, int* point)
+{
+ Vector<char> vector(buffer, buffer_length);
+ ASSERT(!Double(v).IsSpecial());
+ ASSERT(mode == SHORTEST || requested_digits >= 0);
+
+ if (Double(v).Sign() < 0) {
+ *sign = true;
+ v = -v;
+ } else {
+ *sign = false;
+ }
+
+ if (mode == PRECISION && requested_digits == 0) {
+ vector[0] = '\0';
+ *length = 0;
+ return;
+ }
+
+ if (v == 0) {
+ vector[0] = '0';
+ vector[1] = '\0';
+ *length = 1;
+ *point = 1;
+ return;
+ }
+
+ bool fast_worked;
+ switch (mode) {
+ case SHORTEST:
+ fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point);
+ break;
+ case FIXED:
+ fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point);
+ break;
+ case PRECISION:
+ fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits,
+ vector, length, point);
+ break;
+ default:
+ fast_worked = false;
+ UNREACHABLE();
+ }
+ if (fast_worked) return;
+
+ // If the fast dtoa didn't succeed use the slower bignum version.
+ BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode);
+ BignumDtoa(v, bignum_mode, requested_digits, vector, length, point);
+ vector[*length] = '\0';
+}
+
+template <class Iterator>
+static bool ConsumeSubString(Iterator* current,
+ Iterator end,
+ const char* substring) {
+ ASSERT(**current == *substring);
+ for (substring++; *substring != '\0'; substring++) {
+ ++*current;
+ if (*current == end || **current != *substring) return false;
+ }
+ ++*current;
+ return true;
+}
+
+const int kMaxSignificantDigits = 772;
+
+static const char kWhitespaceTable7[] = { 32, 13, 10, 9, 11, 12 };
+static const int kWhitespaceTable7Length = ARRAY_SIZE(kWhitespaceTable7);
+
+static bool isWhitespace(int x) {
+ if (x < 128) {
+ for (int i = 0; i < kWhitespaceTable7Length; i++) {
+ if (kWhitespaceTable7[i] == x) return true;
+ }
+ }
+ return false;
+}
+
+// Returns true if a nonspace found and false if the end has reached.
+template <class Iterator>
+static inline bool AdvanceToNonspace(Iterator* current, Iterator end) {
+ while (*current != end) {
+ if (!isWhitespace(**current)) return true;
+ ++*current;
+ }
+ return false;
+}
+
+static bool isDigit(int x, int radix) {
+ return (x >= '0' && x <= '9' && x < '0' + radix)
+ || (radix > 10 && x >= 'a' && x < 'a' + radix - 10)
+ || (radix > 10 && x >= 'A' && x < 'A' + radix - 10);
+}
+
+static double SignedZero(bool sign) {
+ return sign ? -0.0 : 0.0;
+}
+
+static bool IsDecimalDigitForRadix(int c, int radix) {
+ return '0' <= c && c <= '9' && (c - '0') < radix;
+}
+
+static bool IsCharacterDigitForRadix(int c, int radix, char a_character) {
+ return radix > 10 && c >= a_character && c < a_character + radix - 10;
+}
+
+template <int radix_log_2, class Iterator>
+static double RadixStringToIeee(Iterator* current, Iterator end,
+ bool sign, bool allow_trailing_junk, double junk_string_value,
+ bool* result_is_junk)
+{
+ ASSERT(*current != end);
+
+ const int kSignificandSize = Double::kSignificandSize;
+
+ *result_is_junk = true;
+
+ // Skip leading 0s.
+ while (**current == '0') {
+ ++(*current);
+ if (*current == end) {
+ *result_is_junk = false;
+ return SignedZero(sign);
+ }
+ }
+
+ int64_t number = 0;
+ int exponent = 0;
+ const int radix = (1 << radix_log_2);
+
+ do {
+ int digit;
+ if (IsDecimalDigitForRadix(**current, radix)) {
+ digit = static_cast<char>(**current) - '0';
+ } else if (IsCharacterDigitForRadix(**current, radix, 'a')) {
+ digit = static_cast<char>(**current) - 'a' + 10;
+ } else if (IsCharacterDigitForRadix(**current, radix, 'A')) {
+ digit = static_cast<char>(**current) - 'A' + 10;
+ } else {
+ if (allow_trailing_junk || !AdvanceToNonspace(current, end)) {
+ break;
+ } else {
+ return junk_string_value;
+ }
+ }
+
+ number = number * radix + digit;
+ int overflow = static_cast<int>(number >> kSignificandSize);
+ if (overflow != 0) {
+ // Overflow occurred. Need to determine which direction to round the
+ // result.
+ int overflow_bits_count = 1;
+ while (overflow > 1) {
+ overflow_bits_count++;
+ overflow >>= 1;
+ }
+
+ int dropped_bits_mask = ((1 << overflow_bits_count) - 1);
+ int dropped_bits = static_cast<int>(number) & dropped_bits_mask;
+ number >>= overflow_bits_count;
+ exponent = overflow_bits_count;
+
+ bool zero_tail = true;
+ for (;;) {
+ ++(*current);
+ if (*current == end || !isDigit(**current, radix)) break;
+ zero_tail = zero_tail && **current == '0';
+ exponent += radix_log_2;
+ }
+
+ if (!allow_trailing_junk && AdvanceToNonspace(current, end)) {
+ return junk_string_value;
+ }
+
+ int middle_value = (1 << (overflow_bits_count - 1));
+ if (dropped_bits > middle_value) {
+ number++; // Rounding up.
+ } else if (dropped_bits == middle_value) {
+ // Rounding to even to consistency with decimals: half-way case rounds
+ // up if significant part is odd and down otherwise.
+ if ((number & 1) != 0 || !zero_tail) {
+ number++; // Rounding up.
+ }
+ }
+
+ // Rounding up may cause overflow.
+ if ((number & ((int64_t)1 << kSignificandSize)) != 0) {
+ exponent++;
+ number >>= 1;
+ }
+ break;
+ }
+ ++(*current);
+ } while (*current != end);
+
+ ASSERT(number < ((int64_t)1 << kSignificandSize));
+ ASSERT(static_cast<int64_t>(static_cast<double>(number)) == number);
+
+ *result_is_junk = false;
+
+ if (exponent == 0) {
+ if (sign) {
+ if (number == 0) return -0.0;
+ number = -number;
+ }
+ return static_cast<double>(number);
+ }
+
+ ASSERT(number != 0);
+ return Double(DiyFp(number, exponent)).value();
+}
+
+double StringToDoubleConverter::StringToIeee(
+ const char *input,
+ int length,
+ int* processed_characters_count) const {
+ const char *current = input;
+ const char *end = input + length;
+
+ *processed_characters_count = 0;
+
+ const bool allow_trailing_junk = (flags_ & ALLOW_TRAILING_JUNK) != 0;
+ const bool allow_leading_spaces = (flags_ & ALLOW_LEADING_SPACES) != 0;
+ const bool allow_trailing_spaces = (flags_ & ALLOW_TRAILING_SPACES) != 0;
+ const bool allow_spaces_after_sign = (flags_ & ALLOW_SPACES_AFTER_SIGN) != 0;
+
+ if (current == end) return empty_string_value_;
+
+ if (allow_leading_spaces || allow_trailing_spaces) {
+ if (!AdvanceToNonspace(&current, end)) {
+ *processed_characters_count = static_cast<int>(current - input);
+ return empty_string_value_;
+ }
+ if (!allow_leading_spaces && (input != current)) {
+ return junk_string_value_;
+ }
+ }
+
+ const int kBufferSize = kMaxSignificantDigits + 10;
+ char buffer[kBufferSize]; // NOLINT: size is known at compile time.
+ int buffer_pos = 0;
+
+ int exponent = 0;
+ int significant_digits = 0;
+ int insignificant_digits = 0;
+ bool nonzero_digit_dropped = false;
+
+ bool sign = false;
+
+ if (*current == '+' || *current == '-') {
+ sign = (*current == '-');
+ ++current;
+ const char *next_non_space = current;
+
+ if (!AdvanceToNonspace(&next_non_space, end)) return junk_string_value_;
+ if (!allow_spaces_after_sign && (current != next_non_space)) {
+ return junk_string_value_;
+ }
+ current = next_non_space;
+ }
+
+ if (infinity_symbol_ != NULL) {
+ if (*current == infinity_symbol_[0]) {
+ if (!ConsumeSubString(&current, end, infinity_symbol_)) {
+ return junk_string_value_;
+ }
+
+ if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) {
+ return junk_string_value_;
+ }
+ if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) {
+ return junk_string_value_;
+ }
+
+ ASSERT(buffer_pos == 0);
+ *processed_characters_count = static_cast<int>(current - input);
+ return sign ? -Double::Infinity() : Double::Infinity();
+ }
+ }
+
+ if (nan_symbol_ != NULL) {
+ if (*current == nan_symbol_[0]) {
+ if (!ConsumeSubString(&current, end, nan_symbol_)) {
+ return junk_string_value_;
+ }
+
+ if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) {
+ return junk_string_value_;
+ }
+ if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) {
+ return junk_string_value_;
+ }
+
+ ASSERT(buffer_pos == 0);
+ *processed_characters_count = static_cast<int>(current - input);
+ return sign ? -Double::NaN() : Double::NaN();
+ }
+ }
+
+ bool leading_zero = false;
+ if (*current == '0') {
+ ++current;
+ if (current == end) {
+ *processed_characters_count = static_cast<int>(current - input);
+ return SignedZero(sign);
+ }
+
+ leading_zero = true;
+
+ // It could be hexadecimal value.
+ if ((flags_ & ALLOW_HEX) && (*current == 'x' || *current == 'X')) {
+ ++current;
+ if (current == end || !isDigit(*current, 16)) {
+ return junk_string_value_; // "0x".
+ }
+
+ bool result_is_junk;
+ double result = RadixStringToIeee<4>(&current,
+ end,
+ sign,
+ allow_trailing_junk,
+ junk_string_value_,
+ &result_is_junk);
+ if (!result_is_junk) {
+ if (allow_trailing_spaces) AdvanceToNonspace(&current, end);
+ *processed_characters_count = static_cast<int>(current - input);
+ }
+ return result;
+ }
+
+ // Ignore leading zeros in the integer part.
+ while (*current == '0') {
+ ++current;
+ if (current == end) {
+ *processed_characters_count = static_cast<int>(current - input);
+ return SignedZero(sign);
+ }
+ }
+ }
+
+ bool octal = leading_zero && (flags_ & ALLOW_OCTALS) != 0;
+
+ // Copy significant digits of the integer part (if any) to the buffer.
+ while (*current >= '0' && *current <= '9') {
+ if (significant_digits < kMaxSignificantDigits) {
+ ASSERT(buffer_pos < kBufferSize);
+ buffer[buffer_pos++] = static_cast<char>(*current);
+ significant_digits++;
+ // Will later check if it's an octal in the buffer.
+ } else {
+ insignificant_digits++; // Move the digit into the exponential part.
+ nonzero_digit_dropped = nonzero_digit_dropped || *current != '0';
+ }
+ octal = octal && *current < '8';
+ ++current;
+ if (current == end) goto parsing_done;
+ }
+
+ if (significant_digits == 0) {
+ octal = false;
+ }
+
+ if (*current == '.') {
+ if (octal && !allow_trailing_junk) return junk_string_value_;
+ if (octal) goto parsing_done;
+
+ ++current;
+ if (current == end) {
+ if (significant_digits == 0 && !leading_zero) {
+ return junk_string_value_;
+ } else {
+ goto parsing_done;
+ }
+ }
+
+ if (significant_digits == 0) {
+ // octal = false;
+ // Integer part consists of 0 or is absent. Significant digits start after
+ // leading zeros (if any).
+ while (*current == '0') {
+ ++current;
+ if (current == end) {
+ *processed_characters_count = static_cast<int>(current - input);
+ return SignedZero(sign);
+ }
+ exponent--; // Move this 0 into the exponent.
+ }
+ }
+
+ // There is a fractional part.
+ // We don't emit a '.', but adjust the exponent instead.
+ while (*current >= '0' && *current <= '9') {
+ if (significant_digits < kMaxSignificantDigits) {
+ ASSERT(buffer_pos < kBufferSize);
+ buffer[buffer_pos++] = static_cast<char>(*current);
+ significant_digits++;
+ exponent--;
+ } else {
+ // Ignore insignificant digits in the fractional part.
+ nonzero_digit_dropped = nonzero_digit_dropped || *current != '0';
+ }
+ ++current;
+ if (current == end) goto parsing_done;
+ }
+ }
+
+ if (!leading_zero && exponent == 0 && significant_digits == 0) {
+ // If leading_zeros is true then the string contains zeros.
+ // If exponent < 0 then string was [+-]\.0*...
+ // If significant_digits != 0 the string is not equal to 0.
+ // Otherwise there are no digits in the string.
+ return junk_string_value_;
+ }
+
+ // Parse exponential part.
+ if (*current == 'e' || *current == 'E') {
+ if (octal && !allow_trailing_junk) return junk_string_value_;
+ if (octal) goto parsing_done;
+ ++current;
+ if (current == end) {
+ if (allow_trailing_junk) {
+ goto parsing_done;
+ } else {
+ return junk_string_value_;
+ }
+ }
+ char sign = '+';
+ if (*current == '+' || *current == '-') {
+ sign = static_cast<char>(*current);
+ ++current;
+ if (current == end) {
+ if (allow_trailing_junk) {
+ goto parsing_done;
+ } else {
+ return junk_string_value_;
+ }
+ }
+ }
+
+ if (current == end || *current < '0' || *current > '9') {
+ if (allow_trailing_junk) {
+ goto parsing_done;
+ } else {
+ return junk_string_value_;
+ }
+ }
+
+ const int max_exponent = INT_MAX / 2;
+ ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2);
+ int num = 0;
+ do {
+ // Check overflow.
+ int digit = *current - '0';
+ if (num >= max_exponent / 10
+ && !(num == max_exponent / 10 && digit <= max_exponent % 10)) {
+ num = max_exponent;
+ } else {
+ num = num * 10 + digit;
+ }
+ ++current;
+ } while (current != end && *current >= '0' && *current <= '9');
+
+ exponent += (sign == '-' ? -num : num);
+ }
+
+ if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) {
+ return junk_string_value_;
+ }
+ if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) {
+ return junk_string_value_;
+ }
+ if (allow_trailing_spaces) {
+ AdvanceToNonspace(&current, end);
+ }
+
+ parsing_done:
+ exponent += insignificant_digits;
+
+ if (octal) {
+ double result;
+ bool result_is_junk;
+ char* start = buffer;
+ result = RadixStringToIeee<3>(&start,
+ buffer + buffer_pos,
+ sign,
+ allow_trailing_junk,
+ junk_string_value_,
+ &result_is_junk);
+ ASSERT(!result_is_junk);
+ *processed_characters_count = static_cast<int>(current - input);
+ return result;
+ }
+
+ if (nonzero_digit_dropped) {
+ buffer[buffer_pos++] = '1';
+ exponent--;
+ }
+
+ ASSERT(buffer_pos < kBufferSize);
+ buffer[buffer_pos] = '\0';
+
+ double converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent);
+ *processed_characters_count = static_cast<int>(current - input);
+ return sign? -converted: converted;
+}
+
+
+double StringToDoubleConverter::StringToDouble(
+ const char* buffer,
+ int length,
+ int* processed_characters_count) const {
+ return StringToIeee(buffer, length, processed_characters_count);
+}
+
+} // end anonymous namespace
+
+std::string format_coord_shortest(Coord x)
+{
+ char buf[20];
+ bool sign;
+ int length, point;
+
+ DoubleToStringConverter::DoubleToAscii(x, DoubleToStringConverter::SHORTEST,
+ 0, buf, 20, &sign, &length, &point);
+
+ int exponent = point - length;
+
+ std::string ret;
+ ret.reserve(32);
+
+ if (sign) {
+ ret += '-';
+ }
+
+ if (exponent == 0) {
+ // return digits without any changes
+ ret += buf;
+ } else if (point >= 0 && point <= length) {
+ // insert decimal point
+ ret.append(buf, point);
+ ret += '.';
+ ret.append(&buf[point], length - point);
+ } else if (exponent > 0 && exponent <= 2) {
+ // add trailing zeroes
+ ret += buf;
+ ret.append(exponent, '0');
+ } else if (point >= -3 && point <= -1) {
+ // add leading zeroes
+ ret += '.';
+ ret.append(-point, '0');
+ ret += buf;
+ } else {
+ // exponential form
+ ret += buf;
+ ret += 'e';
+ if (exponent < 0) {
+ ret += '-';
+ exponent = -exponent;
+ }
+
+ /* Convert exponent by hand.
+ * Using ostringstream is ~3x slower */
+ int const buflen = 6;
+ int i = 0;
+ char expdigits[buflen+1];
+ expdigits[buflen] = 0;
+
+ for (; exponent && i < buflen; ++i) {
+ expdigits[buflen - 1 - i] = '0' + (exponent % 10);
+ exponent /= 10;
+ }
+ ret.append(&expdigits[buflen - i]);
+ }
+
+ return ret;
+}
+
+std::string format_coord_nice(Coord x)
+{
+ static DoubleToStringConverter conv(
+ DoubleToStringConverter::UNIQUE_ZERO,
+ "Inf", "NaN", 'e', -6, 21, 0, 0);
+ std::string ret;
+ ret.reserve(32);
+ conv.ToShortest(x, ret);
+ return ret;
+}
+
+Coord parse_coord(std::string const &s)
+{
+ static StringToDoubleConverter conv(
+ StringToDoubleConverter::ALLOW_LEADING_SPACES |
+ StringToDoubleConverter::ALLOW_TRAILING_SPACES |
+ StringToDoubleConverter::ALLOW_SPACES_AFTER_SIGN,
+ 0.0, nan(""), "Inf", "NaN");
+ int dummy;
+ return conv.StringToDouble(s.c_str(), s.length(), &dummy);
+}
+
+} // namespace Geom
+
+/*
+ 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:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/coord.h b/src/2geom/coord.h
index 78a852f32..416e6443d 100644
--- a/src/2geom/coord.h
+++ b/src/2geom/coord.h
@@ -1,6 +1,5 @@
-/**
- * \file
- * \brief Defines the Coord "real" type with sufficient precision for coordinates.
+/** @file
+ * @brief Integral and real coordinate types and some basic utilities
*//*
* Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
*
@@ -34,14 +33,45 @@
#include <cmath>
#include <limits>
+#include <string>
+#include <functional>
#include <boost/operators.hpp>
#include <2geom/forward.h>
namespace Geom {
-/** @brief 2D axis enumeration (X or Y). */
+/// 2D axis enumeration (X or Y).
enum Dim2 { X=0, Y=1 };
+/// Get the other (perpendicular) dimension.
+inline Dim2 other_dimension(Dim2 d) { return d == Y ? X : Y; }
+
+// TODO: make a smarter implementation with C++11
+template <typename T>
+struct D2Traits {
+ typedef typename T::D1Value D1Value;
+ typedef typename T::D1Reference D1Reference;
+ typedef typename T::D1ConstReference D1ConstReference;
+};
+
+// for use with things such as transform_iterator
+template <typename T>
+struct GetX {
+ typedef typename D2Traits<T>::D1Value result_type;
+ typedef T argument_type;
+ typename D2Traits<T>::D1Value operator()(T const &a) const {
+ return a[X];
+ }
+};
+template <typename T>
+struct GetY {
+ typedef typename D2Traits<T>::D1Value result_type;
+ typedef T argument_type;
+ typename D2Traits<T>::D1Value operator()(T const &a) const {
+ return a[Y];
+ }
+};
+
/**
* @brief Floating point type used to store coordinates.
*
@@ -60,8 +90,32 @@ inline Coord infinity() { return std::numeric_limits<Coord>::infinity(); }
inline bool are_near(Coord a, Coord b, double eps=EPSILON) { return a-b <= eps && a-b >= -eps; }
inline bool rel_error_bound(Coord a, Coord b, double eps=EPSILON) { return a <= eps*b && a >= -eps*b; }
+/// Numerically stable linear interpolation.
+inline Coord lerp(Coord t, Coord a, Coord b) {
+ return (1 - t) * a + t * b;
+}
+
template <typename C>
-struct CoordTraits {};
+struct CoordTraits {
+ typedef D2<C> PointType;
+ typedef GenericInterval<C> IntervalType;
+ typedef GenericOptInterval<C> OptIntervalType;
+ typedef GenericRect<C> RectType;
+ typedef GenericOptRect<C> OptRectType;
+
+ typedef
+ boost::equality_comparable< IntervalType
+ , boost::orable< IntervalType
+ > >
+ IntervalOps;
+
+ typedef
+ boost::equality_comparable< RectType
+ , boost::orable< RectType
+ , boost::orable< RectType, OptRectType
+ > > >
+ RectOps;
+};
// NOTE: operator helpers for Rect and Interval are defined here.
// This is to avoid increasing their size through multiple inheritance.
@@ -120,6 +174,21 @@ struct CoordTraits<Coord> {
RectOps;
};
+/** @brief Convert coordinate to shortest possible string
+ * @return The shortest string that parses back to the original value. */
+std::string format_coord_shortest(Coord x);
+
+/** @brief Convert coordinate to human-readable string
+ * Unlike format_coord_shortest, this function will not omit a leading zero
+ * before a decimal point or use small negative exponents. The output format
+ * is similar to Javascript functions. */
+std::string format_coord_nice(Coord x);
+
+/** @brief Parse coordinate string
+ * When using this function in conjunction with format_coord_shortest()
+ * or format_coord_nice(), the value is guaranteed to be preserved exactly. */
+Coord parse_coord(std::string const &s);
+
} // end namespace Geom
#endif // LIB2GEOM_SEEN_COORD_H
diff --git a/src/2geom/crossing.cpp b/src/2geom/crossing.cpp
index 513327271..e27a2fc43 100644
--- a/src/2geom/crossing.cpp
+++ b/src/2geom/crossing.cpp
@@ -28,7 +28,7 @@ double wrap_dist(double from, double to, double size, bool rev) {
}
}
/*
-CrossingGraph create_crossing_graph(std::vector<Path> const &p, Crossings const &crs) {
+CrossingGraph create_crossing_graph(PathVector const &p, Crossings const &crs) {
std::vector<Point> locs;
CrossingGraph ret;
for(unsigned i = 0; i < crs.size(); i++) {
diff --git a/src/2geom/crossing.h b/src/2geom/crossing.h
index d5012ae2b..be41fe893 100644
--- a/src/2geom/crossing.h
+++ b/src/2geom/crossing.h
@@ -33,14 +33,14 @@
*
*/
-#ifndef __GEOM_CROSSING_H
-#define __GEOM_CROSSING_H
+#ifndef LIB2GEOM_SEEN_CROSSING_H
+#define LIB2GEOM_SEEN_CROSSING_H
#include <vector>
#include <2geom/rect.h>
#include <2geom/sweep.h>
#include <boost/optional/optional.hpp>
-#include <2geom/path.h>
+#include <2geom/pathvector.h>
namespace Geom {
@@ -99,7 +99,7 @@ struct TimeOrder {
};
class Path;
-CrossingGraph create_crossing_graph(std::vector<Path> const &p, Crossings const &crs);
+CrossingGraph create_crossing_graph(PathVector const &p, Crossings const &crs);
*/
/*inline bool are_near(Crossing a, Crossing b) {
@@ -143,11 +143,24 @@ std::vector<Rect> bounds(Path const &a);
inline void sort_crossings(Crossings &cr, unsigned ix) { std::sort(cr.begin(), cr.end(), CrossingOrder(ix)); }
+template <typename T>
+struct CrossingTraits {
+ typedef std::vector<T> VectorT;
+ static inline VectorT init(T const &x) { return VectorT(1, x); }
+};
+template <>
+struct CrossingTraits<Path> {
+ typedef PathVector VectorT;
+ static inline VectorT vector_one(Path const &x) { return VectorT(x); }
+};
+
template<typename T>
struct Crosser {
+ typedef typename CrossingTraits<T>::VectorT VectorT;
virtual ~Crosser() {}
- virtual Crossings crossings(T const &a, T const &b) { return crossings(std::vector<T>(1,a), std::vector<T>(1,b))[0]; }
- virtual CrossingSet crossings(std::vector<T> const &a, std::vector<T> const &b) {
+ virtual Crossings crossings(T const &a, T const &b) {
+ return crossings(CrossingTraits<T>::vector_one(a), CrossingTraits<T>::vector_one(b))[0]; }
+ virtual CrossingSet crossings(VectorT const &a, VectorT const &b) {
CrossingSet results(a.size() + b.size(), Crossings());
std::vector<std::vector<unsigned> > cull = sweep_bounds(bounds(a), bounds(b));
@@ -185,7 +198,7 @@ CrossingSet reverse_tb(CrossingSet const &cr, unsigned split, std::vector<double
void clean(Crossings &cr_a, Crossings &cr_b);
void delete_duplicates(Crossings &crs);
-}
+} // end namespace Geom
#endif
/*
diff --git a/src/2geom/curve.cpp b/src/2geom/curve.cpp
index c0f2bf883..b45228514 100644
--- a/src/2geom/curve.cpp
+++ b/src/2geom/curve.cpp
@@ -32,62 +32,26 @@
*/
#include <2geom/curve.h>
-#include <2geom/nearest-point.h>
+#include <2geom/exception.h>
+#include <2geom/nearest-time.h>
#include <2geom/sbasis-geometric.h>
+#include <2geom/sbasis-to-bezier.h>
#include <2geom/ord.h>
+#include <2geom/path-sink.h>
+
+//#include <iostream>
namespace Geom
{
-int CurveHelpers::root_winding(Curve const &c, Point p) {
- std::vector<double> ts = c.roots(p[Y], Y);
-
- if(ts.empty()) return 0;
-
- double const fudge = 0.01; //fudge factor used on first and last
-
- std::sort(ts.begin(), ts.end());
-
- // winding determined by crossings at roots
- int wind=0;
- // previous time
- double pt = ts.front() - fudge;
- for ( std::vector<double>::iterator ti = ts.begin()
- ; ti != ts.end()
- ; ++ti )
- {
- double t = *ti;
- if ( t <= 0. || t >= 1. ) continue; //skip endpoint roots
- if ( c.valueAt(t, X) > p[X] ) { // root is ray intersection
- // Get t of next:
- std::vector<double>::iterator next = ti;
- ++next;
- double nt;
- if(next == ts.end()) nt = t + fudge; else nt = *next;
-
- // Check before in time and after in time for positions
- // Currently we're using the average times between next and previous segs
- Cmp after_to_ray = cmp(c.valueAt((t + nt) / 2, Y), p[Y]);
- Cmp before_to_ray = cmp(c.valueAt((t + pt) / 2, Y), p[Y]);
- // if y is included, these will have opposite values, giving order.
- Cmp dt = cmp(after_to_ray, before_to_ray);
- if(dt != EQUAL_TO) //Should always be true, but yah never know..
- wind += dt;
- pt = t;
- }
- }
-
- return wind;
-}
-
-Coord Curve::nearestPoint(Point const& p, Coord a, Coord b) const
+Coord Curve::nearestTime(Point const& p, Coord a, Coord b) const
{
- return nearest_point(p, toSBasis(), a, b);
+ return nearest_time(p, toSBasis(), a, b);
}
-std::vector<Coord> Curve::allNearestPoints(Point const& p, Coord from, Coord to) const
+std::vector<Coord> Curve::allNearestTimes(Point const& p, Coord from, Coord to) const
{
- return all_nearest_points(p, toSBasis(), from, to);
+ return all_nearest_times(p, toSBasis(), from, to);
}
Coord Curve::length(Coord tolerance) const
@@ -95,6 +59,97 @@ Coord Curve::length(Coord tolerance) const
return ::Geom::length(toSBasis(), tolerance);
}
+int Curve::winding(Point const &p) const
+{
+ try {
+ std::vector<Coord> ts = roots(p[Y], Y);
+ if(ts.empty()) return 0;
+ std::sort(ts.begin(), ts.end());
+
+ // skip endpoint roots when they are local maxima on the Y axis
+ // this follows the convention used in other winding routines,
+ // i.e. that the bottommost coordinate is not part of the shape
+ bool ingore_0 = unitTangentAt(0)[Y] <= 0;
+ bool ignore_1 = unitTangentAt(1)[Y] >= 0;
+
+ int wind = 0;
+ for (std::size_t i = 0; i < ts.size(); ++i) {
+ Coord t = ts[i];
+ //std::cout << t << std::endl;
+ if ((t == 0 && ingore_0) || (t == 1 && ignore_1)) continue;
+ if (valueAt(t, X) > p[X]) { // root is ray intersection
+ Point tangent = unitTangentAt(t);
+ if (tangent[Y] > 0) {
+ // at the point of intersection, curve goes in +Y direction,
+ // so it winds in the direction of positive angles
+ ++wind;
+ } else if (tangent[Y] < 0) {
+ --wind;
+ }
+ }
+ }
+ return wind;
+ } catch (InfiniteSolutions const &e) {
+ // this means we encountered a line segment exactly coincident with the point
+ // skip, since this will be taken care of by endpoint roots in other segments
+ return 0;
+ }
+}
+
+std::vector<CurveIntersection> Curve::intersect(Curve const &/*other*/, Coord /*eps*/) const
+{
+ // TODO: approximate as Bezier
+ THROW_NOTIMPLEMENTED();
+}
+
+std::vector<CurveIntersection> Curve::intersectSelf(Coord eps) const
+{
+ std::vector<CurveIntersection> result;
+ // Monotonic segments cannot have self-intersections.
+ // Thus, we can split the curve at roots and intersect the portions.
+ std::vector<Coord> splits;
+ std::auto_ptr<Curve> deriv(derivative());
+ splits = deriv->roots(0, X);
+ if (splits.empty()) {
+ return result;
+ }
+ deriv.reset();
+ splits.push_back(1.);
+
+ boost::ptr_vector<Curve> parts;
+ Coord previous = 0;
+ for (unsigned i = 0; i < splits.size(); ++i) {
+ if (splits[i] == 0.) continue;
+ parts.push_back(portion(previous, splits[i]));
+ previous = splits[i];
+ }
+
+ Coord prev_i = 0;
+ for (unsigned i = 0; i < parts.size()-1; ++i) {
+ Interval dom_i(prev_i, splits[i]);
+ prev_i = splits[i];
+
+ Coord prev_j = 0;
+ for (unsigned j = i+1; j < parts.size(); ++j) {
+ Interval dom_j(prev_j, splits[j]);
+ prev_j = splits[j];
+
+ std::vector<CurveIntersection> xs = parts[i].intersect(parts[j], eps);
+ for (unsigned k = 0; k < xs.size(); ++k) {
+ // to avoid duplicated intersections, skip values at exactly 1
+ if (xs[k].first == 1. || xs[k].second == 1.) continue;
+
+ Coord ti = dom_i.valueAt(xs[k].first);
+ Coord tj = dom_j.valueAt(xs[k].second);
+
+ CurveIntersection real(ti, tj, xs[k].point());
+ result.push_back(real);
+ }
+ }
+ }
+ return result;
+}
+
Point Curve::unitTangentAt(Coord t, unsigned n) const
{
std::vector<Point> derivs = pointAndDerivatives(t, n);
@@ -108,6 +163,16 @@ Point Curve::unitTangentAt(Coord t, unsigned n) const
return Point (0,0);
};
+void Curve::feed(PathSink &sink, bool moveto_initial) const
+{
+ std::vector<Point> pts;
+ sbasis_to_bezier(pts, toSBasis(), 2); //TODO: use something better!
+ if (moveto_initial) {
+ sink.moveTo(initialPoint());
+ }
+ sink.curveTo(pts[0], pts[1], pts[2]);
+}
+
} // namespace Geom
/*
diff --git a/src/2geom/curve.h b/src/2geom/curve.h
index 172fd7ddc..893dc6bdb 100644
--- a/src/2geom/curve.h
+++ b/src/2geom/curve.h
@@ -35,27 +35,23 @@
*/
-#ifndef _2GEOM_CURVE_H_
-#define _2GEOM_CURVE_H_
+#ifndef LIB2GEOM_SEEN_CURVE_H
+#define LIB2GEOM_SEEN_CURVE_H
#include <vector>
+#include <boost/operators.hpp>
#include <2geom/coord.h>
#include <2geom/point.h>
#include <2geom/interval.h>
#include <2geom/sbasis.h>
#include <2geom/d2.h>
#include <2geom/affine.h>
+#include <2geom/intersection.h>
-namespace Geom
-{
-
-class Curve;
-
-struct CurveHelpers {
-protected:
- static int root_winding(Curve const &c, Point p);
-};
+namespace Geom {
+class PathSink;
+typedef Intersection<> CurveIntersection;
/**
* @brief Abstract continuous curve on a plane defined on [0,1].
@@ -77,7 +73,9 @@ protected:
*
* @ingroup Curves
*/
-class Curve : private CurveHelpers {
+class Curve
+ : boost::equality_comparable<Curve>
+{
public:
virtual ~Curve() {}
@@ -86,26 +84,38 @@ public:
/** @brief Retrieve the start of the curve.
* @return The point corresponding to \f$\mathbf{C}(0)\f$. */
virtual Point initialPoint() const = 0;
+
/** Retrieve the end of the curve.
* @return The point corresponding to \f$\mathbf{C}(1)\f$. */
virtual Point finalPoint() const = 0;
+
/** @brief Check whether the curve has exactly zero length.
* @return True if the curve's initial point is exactly the same as its final point, and it contains
- * no other points (its value set contains only one element).
- */
+ * no other points (its value set contains only one element). */
virtual bool isDegenerate() const = 0;
+
+ /** @brief Get the interval of allowed time values.
+ * @return \f$[0, 1]\f$ */
+ virtual Interval timeRange() const {
+ Interval tr(0, 1);
+ return tr;
+ }
+
/** @brief Evaluate the curve at a specified time value.
* @param t Time value
* @return \f$\mathbf{C}(t)\f$ */
virtual Point pointAt(Coord t) const { return pointAndDerivatives(t, 0).front(); }
+
/** @brief Evaluate one of the coordinates at the specified time value.
* @param t Time value
* @param d The dimension to evaluate
* @return The specified coordinate of \f$\mathbf{C}(t)\f$ */
virtual Coord valueAt(Coord t, Dim2 d) const { return pointAt(t)[d]; }
+
/** @brief Evaluate the function at the specified time value. Allows curves to be used
* as functors. */
virtual Point operator() (Coord t) const { return pointAt(t); }
+
/** @brief Evaluate the curve and its derivatives.
* This will return a vector that contains the value of the curve and the specified number
* of derivatives. However, the returned vector might contain less elements than specified
@@ -125,6 +135,7 @@ public:
* type.
* @param p New starting point of the curve */
virtual void setInitial(Point const &v) = 0;
+
/** @brief Change the ending point of the curve.
* After calling this method, it is guaranteed that \f$\mathbf{C}(0) = \mathbf{p}\f$,
* and the curve is still continuous. The precise new shape of the curve varies
@@ -140,12 +151,15 @@ public:
* but it might not be the smallest such rectangle. This method is usually fast.
* @return A rectangle that contains all points belonging to the curve. */
virtual Rect boundsFast() const = 0;
+
/** @brief Compute the curve's exact bounding box.
* This method can be dramatically slower than boundsExact() depending on the curve type.
* @return The smallest possible rectangle containing all of the curve's points. */
virtual Rect boundsExact() const = 0;
+
// I have no idea what the 'deg' parameter is for, so this is undocumented for now.
virtual OptRect boundsLocal(OptInterval const &i, unsigned deg) const = 0;
+
/** @brief Compute the bounding box of a part of the curve.
* Since this method returns the smallest possible bounding rectangle of the specified portion,
* it can also be rather slow.
@@ -163,21 +177,21 @@ public:
* @return Pointer to a newly allocated curve, identical to the original */
virtual Curve *duplicate() const = 0;
+ /** @brief Transform this curve by an affine transformation.
+ * Because of this method, all curve types must be closed under affine
+ * transformations.
+ * @param m Affine describing the affine transformation */
+ virtual void transform(Affine const &m) = 0;
+
/** @brief Create a curve transformed by an affine transformation.
- * This method returns a new curve instead modifying the existing one, because some curve
- * types are not closed under affine transformations. The returned curve may be of different
- * underlying type (as is the case for horizontal and vertical line segments).
+ * This method returns a new curve instead modifying the existing one.
* @param m Affine describing the affine transformation
* @return Pointer to a new, transformed curve */
- virtual Curve *transformed(Affine const &m) const = 0;
-
- /** @brief Translate the curve (i.e. displace by Point)
- * This method modifies the curve; all curve types are closed under
- * translations (the result can be expressed in its own curve type).
- * This function yields the same result as transformed(m).
- * @param p Point by which to translate the curve
- * @return reference to self */
- virtual Curve &operator*=(Translate const &m) = 0;
+ virtual Curve *transformed(Affine const &m) const {
+ Curve *ret = duplicate();
+ ret->transform(m);
+ return ret;
+ }
/** @brief Create a curve that corresponds to a part of this curve.
* For \f$a > b\f$, the returned portion will be reversed with respect to the original.
@@ -190,13 +204,16 @@ public:
* - \f$\mathbf{D}[ [0, 1] ] = \mathbf{C}[ [a?b] ]\f$,
* where \f$[a?b] = [\min(a, b), \max(a, b)]\f$ */
virtual Curve *portion(Coord a, Coord b) const = 0;
+
/** @brief A version of that accepts an Interval. */
Curve *portion(Interval const &i) const { return portion(i.min(), i.max()); }
+
/** @brief Create a reversed version of this curve.
* The result corresponds to <code>portion(1, 0)</code>, but this method might be faster.
* @return Pointer to a new curve \f$\mathbf{D}\f$ such that
* \f$\forall_{x \in [0, 1]} \mathbf{D}(x) = \mathbf{C}(1-x)\f$ */
virtual Curve *reverse() const { return portion(1, 0); }
+
/** @brief Create a derivative of this curve.
* It's best to think of the derivative in physical terms: if the curve describes
* the position of some object on the plane from time \f$t=0\f$ to \f$t=1\f$ as said in the
@@ -215,22 +232,26 @@ public:
* @param b Maximum time value to consider; \f$a < b\f$
* @return \f$q \in [a, b]: ||\mathbf{C}(q) - \mathbf{p}|| =
\inf(\{r \in \mathbb{R} : ||\mathbf{C}(r) - \mathbf{p}||\})\f$ */
- virtual Coord nearestPoint( Point const& p, Coord a = 0, Coord b = 1 ) const;
+ virtual Coord nearestTime( Point const& p, Coord a = 0, Coord b = 1 ) const;
+
/** @brief A version that takes an Interval. */
- Coord nearestPoint(Point const &p, Interval const &i) const {
- return nearestPoint(p, i.min(), i.max());
+ Coord nearestTime(Point const &p, Interval const &i) const {
+ return nearestTime(p, i.min(), i.max());
}
+
/** @brief Compute time values at which the curve comes closest to a specified point.
* @param p Query point
* @param a Minimum time value to consider
* @param b Maximum time value to consider; \f$a < b\f$
* @return Vector of points closest and equally far away from the query point */
- virtual std::vector<Coord> allNearestPoints( Point const& p, Coord from = 0,
+ virtual std::vector<Coord> allNearestTimes( Point const& p, Coord from = 0,
Coord to = 1 ) const;
+
/** @brief A version that takes an Interval. */
- std::vector<Coord> allNearestPoints(Point const &p, Interval const &i) {
- return allNearestPoints(p, i.min(), i.max());
+ std::vector<Coord> allNearestTimes(Point const &p, Interval const &i) {
+ return allNearestTimes(p, i.min(), i.max());
}
+
/** @brief Compute the arc length of this curve.
* For a curve \f$\mathbf{C}(t) = (C_x(t), C_y(t))\f$, arc length is defined for 2D curves as
* \f[ \ell = \int_{0}^{1} \sqrt { [C_x'(t)]^2 + [C_y'(t)]^2 }\, \text{d}t \f]
@@ -242,13 +263,28 @@ public:
* @param tolerance Maximum allowed error
* @return Total distance the curve's value travels on the plane when going from 0 to 1 */
virtual Coord length(Coord tolerance=0.01) const;
+
/** @brief Computes time values at which the curve intersects an axis-aligned line.
* @param v The coordinate of the line
* @param d Which axis the coordinate is on. X means a vertical line, Y a horizontal line. */
virtual std::vector<Coord> roots(Coord v, Dim2 d) const = 0;
- /** @brief Compute the winding number of the curve at the specified point.
- * @todo Does this makes sense for curves at all? */
- virtual int winding(Point const &p) const { return root_winding(*this, p); }
+
+ /** @brief Compute the partial winding number of this curve.
+ * The partial winding number is equal to the difference between the number
+ * of roots at which the curve goes in the +Y direction and the number of roots
+ * at which the curve goes in the -Y direction. This method is mainly useful
+ * for implementing path winding calculation. It will ignore roots which
+ * are local maxima on the Y axis.
+ * @param p Point where the winding number should be determined
+ * @return Winding number contribution at p */
+ virtual int winding(Point const &p) const;
+
+ /// Compute intersections with another curve.
+ virtual std::vector<CurveIntersection> intersect(Curve const &other, Coord eps = EPSILON) const;
+
+ /// Compute intersections of this curve with itself.
+ virtual std::vector<CurveIntersection> intersectSelf(Coord eps = EPSILON) const;
+
/** @brief Compute a vector tangent to the curve.
* This will return an unit vector (a Point with length() equal to 1) that denotes a vector
* tangent to the curve. This vector is defined as
@@ -266,6 +302,7 @@ public:
Point tangent = - c_reverse->unitTangentAt(0);
delete c_reverse; @endcode */
virtual Point unitTangentAt(Coord t, unsigned n = 3) const;
+
/** @brief Convert the curve to a symmetric power basis polynomial.
* Symmetric power basis polynomials (S-basis for short) are numerical representations
* of curves with excellent numerical properties. Most high level operations provided by 2Geom
@@ -281,15 +318,22 @@ public:
* of this curve. For example, for Bezier curves it returns the curve's order
* multiplied by 2. */
virtual int degreesOfFreedom() const { return 0;}
+
/** @brief Test equality of two curves.
+ * Equality means that for any time value, the evaluation of either curve will yield
+ * the same value. This means non-degenerate curves are not equal to their reverses.
+ * Note that this tests for exact equality.
* @return True if the curves are identical, false otherwise */
- virtual bool operator==(Curve const &c) const { return this == &c;}
+ virtual bool operator==(Curve const &c) const = 0;
+
+ /** @brief Feed the curve to a PathSink */
+ virtual void feed(PathSink &sink, bool moveto_initial) const;
/// @}
};
inline
-Coord nearest_point(Point const& p, Curve const& c) {
- return c.nearestPoint(p);
+Coord nearest_time(Point const& p, Curve const& c) {
+ return c.nearestTime(p);
}
// for make benefit glorious library of Boost Pointer Container
diff --git a/src/2geom/curves.h b/src/2geom/curves.h
index 319b1924d..6022c71d8 100644
--- a/src/2geom/curves.h
+++ b/src/2geom/curves.h
@@ -1,10 +1,9 @@
-/**
- * \file
- * \brief Include all curve types
+/** @file
+ * @brief Include all curve types
*//*
* Authors:
- * MenTaLguY <mental@rydia.net>
- * Marco Cecchetti <mrcekets at gmail.com>
+ * MenTaLguY <mental@rydia.net>
+ * Marco Cecchetti <mrcekets at gmail.com>
*
* Copyright 2007-2008 authors
*
@@ -38,7 +37,6 @@
#include <2geom/curve.h>
#include <2geom/sbasis-curve.h>
#include <2geom/bezier-curve.h>
-#include <2geom/hvlinesegment.h>
#include <2geom/elliptical-arc.h>
#include <2geom/svg-elliptical-arc.h>
diff --git a/src/2geom/d2-sbasis.cpp b/src/2geom/d2-sbasis.cpp
index 486ada9a2..ebec16fdd 100644
--- a/src/2geom/d2-sbasis.cpp
+++ b/src/2geom/d2-sbasis.cpp
@@ -200,7 +200,7 @@ Point unitTangentAt(D2<SBasis> const & a, Coord t, unsigned n)
static void set_first_point(Piecewise<D2<SBasis> > &f, Point a){
if ( f.empty() ){
- f.concat(Piecewise<D2<SBasis> >(D2<SBasis>(Linear(a[X]),Linear(a[Y]))));
+ f.concat(Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(Linear(a[X])), SBasis(Linear(a[Y])))));
return;
}
for (unsigned dim=0; dim<2; dim++){
@@ -213,7 +213,7 @@ static void set_first_point(Piecewise<D2<SBasis> > &f, Point a){
}
static void set_last_point(Piecewise<D2<SBasis> > &f, Point a){
if ( f.empty() ){
- f.concat(Piecewise<D2<SBasis> >(D2<SBasis>(Linear(a[X]),Linear(a[Y]))));
+ f.concat(Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(Linear(a[X])), SBasis(Linear(a[Y])))));
return;
}
for (unsigned dim=0; dim<2; dim++){
diff --git a/src/2geom/d2-sbasis.h b/src/2geom/d2-sbasis.h
index 965ec8746..a0769b314 100644
--- a/src/2geom/d2-sbasis.h
+++ b/src/2geom/d2-sbasis.h
@@ -35,11 +35,11 @@
*
*/
-#ifdef SEEN_LIB2GEOM_D2_H /*This is intentional: we don't actually want anyone to
+#ifdef LIB2GEOM_SEEN_D2_H /*This is intentional: we don't actually want anyone to
include this, other than D2.h. If somone else tries, D2
won't be defined. If it is, this will already be included. */
-#ifndef SEEN_LIB2GEOM_D2_SBASIS_H
-#define SEEN_LIB2GEOM_D2_SBASIS_H
+#ifndef LIB2GEOM_SEEN_D2_SBASIS_H
+#define LIB2GEOM_SEEN_D2_SBASIS_H
#include <2geom/sbasis.h>
#include <2geom/sbasis-2d.h>
@@ -149,8 +149,8 @@ std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector
}
-#endif
-#endif
+#endif // LIB2GEOM_SEEN_D2_SBASIS_H
+#endif // LIB2GEOM_SEEN_D2_H
/*
diff --git a/src/2geom/d2.h b/src/2geom/d2.h
index 5fc955854..714319f99 100644
--- a/src/2geom/d2.h
+++ b/src/2geom/d2.h
@@ -29,18 +29,19 @@
*
*/
-#ifndef SEEN_LIB2GEOM_D2_H
-#define SEEN_LIB2GEOM_D2_H
+#ifndef LIB2GEOM_SEEN_D2_H
+#define LIB2GEOM_SEEN_D2_H
+#include <iterator>
+#include <boost/concept_check.hpp>
+#include <boost/iterator/transform_iterator.hpp>
#include <2geom/point.h>
#include <2geom/interval.h>
#include <2geom/affine.h>
#include <2geom/rect.h>
-
-#include <boost/concept_check.hpp>
#include <2geom/concepts.h>
-namespace Geom{
+namespace Geom {
/**
* The D2 class takes two instances of a scalar data type and treats them
* like a point. All operations which make sense on a point are defined for D2.
@@ -55,6 +56,11 @@ class D2{
T f[2];
public:
+
+ typedef T D1Value;
+ typedef T &D1Reference;
+ typedef T const &D1ConstReference;
+
D2() {f[X] = f[Y] = T();}
explicit D2(Point const &a) {
f[X] = T(a[X]); f[Y] = T(a[Y]);
@@ -65,10 +71,38 @@ class D2{
f[Y] = b;
}
+ template <typename Iter>
+ D2(Iter first, Iter last) {
+ typedef typename std::iterator_traits<Iter>::value_type V;
+ typedef typename boost::transform_iterator<GetX<V>, Iter> XIter;
+ typedef typename boost::transform_iterator<GetY<V>, Iter> YIter;
+
+ XIter xfirst(first, GetX<V>()), xlast(last, GetX<V>());
+ f[X] = T(xfirst, xlast);
+ YIter yfirst(first, GetY<V>()), ylast(last, GetY<V>());
+ f[Y] = T(yfirst, ylast);
+ }
+
+ D2(std::vector<Point> const &vec) {
+ typedef Point V;
+ typedef std::vector<Point>::const_iterator Iter;
+ typedef boost::transform_iterator<GetX<V>, Iter> XIter;
+ typedef boost::transform_iterator<GetY<V>, Iter> YIter;
+
+ XIter xfirst(vec.begin(), GetX<V>()), xlast(vec.end(), GetX<V>());
+ f[X] = T(xfirst, xlast);
+ YIter yfirst(vec.begin(), GetY<V>()), ylast(vec.end(), GetY<V>());
+ f[Y] = T(yfirst, ylast);
+ }
+
//TODO: ask mental about operator= as seen in Point
T& operator[](unsigned i) { return f[i]; }
T const & operator[](unsigned i) const { return f[i]; }
+ Point point(unsigned i) const {
+ Point ret(f[X][i], f[Y][i]);
+ return ret;
+ }
//IMPL: FragmentConcept
typedef Point output_type;
@@ -429,7 +463,7 @@ inline std::ostream &operator<< (std::ostream &out_file, const Geom::D2<T> &in_d
#include <2geom/d2-sbasis.h>
-namespace Geom{
+namespace Geom {
//Some D2 Fragment implementation which requires rect:
template <typename T>
@@ -447,8 +481,10 @@ OptRect bounds_local(const D2<T> &a, const OptInterval &t) {
boost::function_requires<FragmentConcept<T> >();
return OptRect(bounds_local(a[X], t), bounds_local(a[Y], t));
}
-};
+} // end namespace Geom
+
+#endif
/*
Local Variables:
mode:c++
@@ -459,4 +495,3 @@ OptRect bounds_local(const D2<T> &a, const OptInterval &t) {
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
-#endif
diff --git a/src/2geom/ellipse.cpp b/src/2geom/ellipse.cpp
index 2686844b2..4e26707ef 100644
--- a/src/2geom/ellipse.cpp
+++ b/src/2geom/ellipse.cpp
@@ -1,10 +1,11 @@
-/*
- * Ellipse Curve
- *
+/** @file
+ * @brief Ellipse shape
+ *//*
* Authors:
- * Marco Cecchetti <mrcekets at gmail.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
*
- * Copyright 2008 authors
+ * Copyright 2008-2014 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -30,33 +31,28 @@
* the specific language governing rights and limitations.
*/
-
#include <2geom/ellipse.h>
#include <2geom/svg-elliptical-arc.h>
#include <2geom/numeric/fitting-tool.h>
#include <2geom/numeric/fitting-model.h>
-using std::swap;
+namespace Geom {
-namespace Geom
-{
-
-void Ellipse::set(double A, double B, double C, double D, double E, double F)
+void Ellipse::setCoefficients(double A, double B, double C, double D, double E, double F)
{
double den = 4*A*C - B*B;
- if ( den == 0 )
- {
- THROW_LOGICALERROR("den == 0, while computing ellipse centre");
+ if (den == 0) {
+ THROW_RANGEERROR("den == 0, while computing ellipse centre");
}
- m_centre[X] = (B*E - 2*C*D) / den;
- m_centre[Y] = (B*D - 2*A*E) / den;
+ _center[X] = (B*E - 2*C*D) / den;
+ _center[Y] = (B*D - 2*A*E) / den;
// evaluate the a coefficient of the ellipse equation in normal form
// E(x,y) = a*(x-cx)^2 + b*(x-cx)*(y-cy) + c*(y-cy)^2 = 1
// where b = a*B , c = a*C, (cx,cy) == centre
- double num = A * sqr(m_centre[X])
- + B * m_centre[X] * m_centre[Y]
- + C * sqr(m_centre[Y])
+ double num = A * sqr(_center[X])
+ + B * _center[X] * _center[Y]
+ + C * sqr(_center[Y])
- F;
@@ -64,73 +60,74 @@ void Ellipse::set(double A, double B, double C, double D, double E, double F)
double rot = std::atan2( -B, -(A - C) )/2;
// std::cerr << "rot = " << rot << std::endl;
bool swap_axes = false;
- if ( are_near(rot, 0) ) rot = 0;
- if ( are_near(rot, M_PI/2) || rot < 0 )
- {
+
+ if (rot >= M_PI/2 || rot < 0) {
swap_axes = true;
}
// evaluate the length of the ellipse rays
- double cosrot = std::cos(rot);
- double sinrot = std::sin(rot);
+ double sinrot, cosrot;
+ sincos(rot, sinrot, cosrot);
double cos2 = cosrot * cosrot;
double sin2 = sinrot * sinrot;
double cossin = cosrot * sinrot;
den = A * cos2 + B * cossin + C * sin2;
- if ( den == 0 )
- {
- THROW_LOGICALERROR("den == 0, while computing 'rx' coefficient");
+ if (den == 0) {
+ THROW_RANGEERROR("den == 0, while computing 'rx' coefficient");
}
- double rx2 = num/den;
- if ( rx2 < 0 )
- {
- THROW_LOGICALERROR("rx2 < 0, while computing 'rx' coefficient");
+ double rx2 = num / den;
+ if (rx2 < 0) {
+ THROW_RANGEERROR("rx2 < 0, while computing 'rx' coefficient");
}
double rx = std::sqrt(rx2);
den = C * cos2 - B * cossin + A * sin2;
- if ( den == 0 )
- {
- THROW_LOGICALERROR("den == 0, while computing 'ry' coefficient");
+ if (den == 0) {
+ THROW_RANGEERROR("den == 0, while computing 'ry' coefficient");
}
- double ry2 = num/den;
- if ( ry2 < 0 )
- {
- THROW_LOGICALERROR("ry2 < 0, while computing 'rx' coefficient");
+ double ry2 = num / den;
+ if (ry2 < 0) {
+ THROW_RANGEERROR("ry2 < 0, while computing 'rx' coefficient");
}
double ry = std::sqrt(ry2);
// the solution is not unique so we choose always the ellipse
// with a rotation angle between 0 and PI/2
- if ( swap_axes ) swap(rx, ry);
- if ( are_near(rot, M_PI/2)
- || are_near(rot, -M_PI/2)
- || are_near(rx, ry) )
- {
+ if (swap_axes) {
+ std::swap(rx, ry);
+ }
+
+ if (rx == ry) {
rot = 0;
}
- else if ( rot < 0 )
- {
+ if (rot < 0) {
rot += M_PI/2;
}
- m_ray[X] = rx;
- m_ray[Y] = ry;
- m_angle = rot;
+ _rays[X] = rx;
+ _rays[Y] = ry;
+ _angle = rot;
+}
+
+
+Affine Ellipse::unitCircleTransform() const
+{
+ Affine ret = Scale(ray(X), ray(Y)) * Rotate(_angle);
+ ret.setTranslation(center());
+ return ret;
}
-std::vector<double> Ellipse::implicit_form_coefficients() const
+std::vector<double> Ellipse::coefficients() const
{
- if (ray(X) == 0 || ray(Y) == 0)
- {
- THROW_LOGICALERROR("a degenerate ellipse doesn't own an implicit form");
+ if (ray(X) == 0 || ray(Y) == 0) {
+ THROW_RANGEERROR("a degenerate ellipse doesn't have an implicit form");
}
std::vector<double> coeff(6);
- double cosrot = std::cos(rot_angle());
- double sinrot = std::sin(rot_angle());
+ double cosrot, sinrot;
+ sincos(_angle, sinrot, cosrot);
double cos2 = cosrot * cosrot;
double sin2 = sinrot * sinrot;
double cossin = cosrot * sinrot;
@@ -150,18 +147,16 @@ std::vector<double> Ellipse::implicit_form_coefficients() const
}
-void Ellipse::set(std::vector<Point> const& points)
+void Ellipse::fit(std::vector<Point> const &points)
{
size_t sz = points.size();
- if (sz < 5)
- {
+ if (sz < 5) {
THROW_RANGEERROR("fitting error: too few points passed");
}
NL::LFMEllipse model;
NL::least_squeares_fitter<NL::LFMEllipse> fitter(model, sz);
- for (size_t i = 0; i < sz; ++i)
- {
+ for (size_t i = 0; i < sz; ++i) {
fitter.append(points[i]);
}
fitter.update();
@@ -172,12 +167,12 @@ void Ellipse::set(std::vector<Point> const& points)
EllipticalArc *
-Ellipse::arc(Point const& initial, Point const& inner, Point const& final,
+Ellipse::arc(Point const &ip, Point const &inner, Point const &fp,
bool _svg_compliant)
{
- Point sp_cp = initial - center();
- Point ep_cp = final - center();
- Point ip_cp = inner - center();
+ Point sp_cp = ip - center();
+ Point ep_cp = fp - center();
+ Point ip_cp = inner - center();
double angle1 = angle_between(sp_cp, ep_cp);
double angle2 = angle_between(sp_cp, ip_cp);
@@ -186,28 +181,19 @@ Ellipse::arc(Point const& initial, Point const& inner, Point const& final,
bool large_arc_flag = true;
bool sweep_flag = true;
- if ( angle1 > 0 )
- {
- if ( angle2 > 0 && angle3 > 0 )
- {
+ if (angle1 > 0) {
+ if (angle2 > 0 && angle3 > 0) {
large_arc_flag = false;
sweep_flag = true;
- }
- else
- {
+ } else {
large_arc_flag = true;
sweep_flag = false;
}
- }
- else
- {
- if ( angle2 < 0 && angle3 < 0 )
- {
+ } else {
+ if (angle2 < 0 && angle3 < 0) {
large_arc_flag = false;
sweep_flag = false;
- }
- else
- {
+ } else {
large_arc_flag = true;
sweep_flag = true;
}
@@ -215,66 +201,73 @@ Ellipse::arc(Point const& initial, Point const& inner, Point const& final,
EllipticalArc *ret_arc;
if (_svg_compliant) {
- ret_arc = new SVGEllipticalArc(initial, ray(X), ray(Y), rot_angle(),
- large_arc_flag, sweep_flag, final);
+ ret_arc = new SVGEllipticalArc(ip, ray(X), ray(Y), rotationAngle(),
+ large_arc_flag, sweep_flag, fp);
} else {
- ret_arc = new EllipticalArc(initial, ray(X), ray(Y), rot_angle(),
- large_arc_flag, sweep_flag, final);
+ ret_arc = new EllipticalArc(ip, ray(X), ray(Y), rotationAngle(),
+ large_arc_flag, sweep_flag, fp);
}
return ret_arc;
}
-Ellipse Ellipse::transformed(Affine const& m) const
+Ellipse &Ellipse::operator*=(Rotate const &r)
{
- double cosrot = std::cos(rot_angle());
- double sinrot = std::sin(rot_angle());
- Affine A( ray(X) * cosrot, ray(X) * sinrot,
- -ray(Y) * sinrot, ray(Y) * cosrot,
- 0, 0 );
- Point new_center = center() * m;
- Affine M = m.withoutTranslation();
- Affine AM = A * M;
- if ( are_near(std::sqrt(fabs(AM.det())), 0) )
- {
+ _angle += r.angle();
+ // keep the angle in the first quadrant
+ if (_angle < 0) {
+ _angle += M_PI;
+ }
+ if (_angle >= M_PI/2) {
+ std::swap(_rays[X], _rays[Y]);
+ _angle -= M_PI/2;
+ }
+ _center *= r;
+ return *this;
+}
+
+Ellipse &Ellipse::operator*=(Affine const& m)
+{
+ Rotate a(_angle);
+ Affine mwot = m.withoutTranslation();
+ Affine am = a * mwot;
+ if (are_near(am.descrim(), 0)) {
double angle;
- if (AM[0] != 0)
- {
- angle = std::atan2(AM[2], AM[0]);
- }
- else if (AM[1] != 0)
- {
- angle = std::atan2(AM[3], AM[1]);
- }
- else
- {
+ if (am[0] != 0) {
+ angle = std::atan2(am[2], am[0]);
+ } else if (am[1] != 0) {
+ angle = std::atan2(am[3], am[1]);
+ } else {
angle = M_PI/2;
}
- Point V(std::cos(angle), std::sin(angle));
- V *= AM;
- double rx = L2(V);
- angle = atan2(V);
- return Ellipse(new_center[X], new_center[Y], rx, 0, angle);
+ Point v;
+ sincos(angle, v[X], v[Y]);
+ v *= am;
+ _angle = atan2(v);
+ _center *= m;
+ _rays[X] = L2(v);
+ _rays[Y] = 0;
+ return *this;
}
- std::vector<double> coeff = implicit_form_coefficients();
- Affine Q( coeff[0], coeff[1]/2,
+ std::vector<double> coeff = coefficients();
+ Affine q( coeff[0], coeff[1]/2,
coeff[1]/2, coeff[2],
0, 0 );
- Affine invm = M.inverse();
- Q = invm * Q ;
- swap( invm[1], invm[2] );
- Q *= invm;
- Ellipse e(Q[0], 2*Q[1], Q[3], 0, 0, -1);
- e.m_centre = new_center;
+ Affine invm = mwot.inverse();
+ q = invm * q ;
+ std::swap(invm[1], invm[2]);
+ q *= invm;
+ setCoefficients(q[0], 2*q[1], q[3], 0, 0, -1);
+ _center *= m;
- return e;
+ return *this;
}
Ellipse::Ellipse(Geom::Circle const &c)
{
- m_centre = c.center();
- m_ray[X] = m_ray[Y] = c.ray();
+ _center = c.center();
+ _rays[X] = _rays[Y] = c.radius();
}
} // end namespace Geom
diff --git a/src/2geom/ellipse.h b/src/2geom/ellipse.h
index c971c6065..a67969933 100644
--- a/src/2geom/ellipse.h
+++ b/src/2geom/ellipse.h
@@ -1,9 +1,9 @@
-/**
- * \file
- * \brief Ellipse Curve
- *
+/** @file
+ * @brief Ellipse shape
+ *//*
* Authors:
- * Marco Cecchetti <mrcekets at gmail.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
*
* Copyright 2008 authors
*
@@ -32,104 +32,109 @@
*/
-#ifndef _2GEOM_ELLIPSE_H_
-#define _2GEOM_ELLIPSE_H_
+#ifndef LIB2GEOM_SEEN_ELLIPSE_H
+#define LIB2GEOM_SEEN_ELLIPSE_H
#include <vector>
-#include <2geom/point.h>
+#include <2geom/angle.h>
#include <2geom/exception.h>
-#include <2geom/affine.h>
+#include <2geom/point.h>
+#include <2geom/transforms.h>
-namespace Geom
-{
+namespace Geom {
class EllipticalArc;
class Circle;
+/** @brief Set of points with a constant sum of distances from two foci
+ * @ingroup Shapes */
class Ellipse
+ : boost::multipliable< Ellipse, Translate
+ , boost::multipliable< Ellipse, Scale
+ , boost::multipliable< Ellipse, Rotate
+ , boost::multipliable< Ellipse, Zoom
+ , boost::multipliable< Ellipse, Affine
+ > > > > >
{
- public:
- Ellipse():
- m_centre(),
- m_ray(),
- m_angle(0)
+ Point _center;
+ Point _rays;
+ Angle _angle;
+public:
+ Ellipse() {}
+ Ellipse(Point const &c, Point const &r, Coord angle)
+ : _center(c)
+ , _rays(r)
+ , _angle(angle)
{}
-
- Ellipse(double cx, double cy, double rx, double ry, double a)
- : m_centre(cx, cy), m_ray(rx, ry), m_angle(a)
- {
- }
-
- // build an ellipse by its implicit equation:
- // Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0
- Ellipse(double A, double B, double C, double D, double E, double F)
- {
- set(A, B, C, D, E, F);
- }
-
- Ellipse(std::vector<Point> const& points)
- {
- set(points);
+ Ellipse(Coord cx, Coord cy, Coord rx, Coord ry, Coord angle)
+ : _center(cx, cy)
+ , _rays(rx, ry)
+ , _angle(angle)
+ {}
+ Ellipse(double A, double B, double C, double D, double E, double F) {
+ setCoefficients(A, B, C, D, E, F);
}
-
Ellipse(Geom::Circle const &c);
- void set(double cx, double cy, double rx, double ry, double a)
- {
- m_centre[X] = cx;
- m_centre[Y] = cy;
- m_ray[X] = rx;
- m_ray[Y] = ry;
- m_angle = a;
+ void set(Point const &c, Point const &r, Coord angle) {
+ _center = c;
+ _rays = r;
+ _angle = angle;
+ }
+ void set(Coord cx, Coord cy, Coord rx, Coord ry, Coord a) {
+ _center[X] = cx;
+ _center[Y] = cy;
+ _rays[X] = rx;
+ _rays[Y] = ry;
+ _angle = a;
}
// build an ellipse by its implicit equation:
// Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0
- void set(double A, double B, double C, double D, double E, double F);
+ void setCoefficients(double A, double B, double C, double D, double E, double F);
// biuld up the best fitting ellipse wrt the passed points
// prerequisite: at least 5 points must be passed
- void set(std::vector<Point> const& points);
+ void fit(std::vector<Point> const& points);
- EllipticalArc *
- arc(Point const& initial, Point const& inner, Point const& final, bool svg_compliant = true);
+ EllipticalArc *arc(Point const &ip, Point const &inner, Point const &fp,
+ bool svg_compliant = true);
- Point center() const
- {
- return m_centre;
- }
+ Point center() const { return _center; }
+ Coord center(Dim2 d) const { return _center[d]; }
+ Point rays() const { return _rays; }
+ Coord ray(Dim2 d) const { return _rays[d]; }
+ Angle rotationAngle() const { return _angle; }
- Coord center(Dim2 d) const
- {
- return m_centre[d];
- }
+ /** @brief Compute the transform that maps the unit circle to this ellipse.
+ * Each ellipse can be interpreted as a translated, scaled and rotate unit circle.
+ * This function returns the transform that maps the unit circle to this ellipse.
+ * @return Transform from unit circle to the ellipse */
+ Affine unitCircleTransform() const;
- Coord ray(Dim2 d) const
- {
- return m_ray[d];
- }
+ std::vector<double> coefficients() const;
- Coord rot_angle() const
- {
- return m_angle;
+ Ellipse &operator*=(Translate const &t) {
+ _center *= t;
+ return *this;
}
-
- std::vector<double> implicit_form_coefficients() const;
-
- Ellipse transformed(Affine const& m) const;
-
- private:
- Point m_centre, m_ray;
- double m_angle;
+ Ellipse &operator*=(Scale const &s) {
+ _center *= s;
+ _rays *= s;
+ return *this;
+ }
+ Ellipse &operator*=(Zoom const &z) {
+ _center *= z;
+ _rays *= z.scale();
+ return *this;
+ }
+ Ellipse &operator*=(Rotate const &r);
+ Ellipse &operator*=(Affine const &m);
};
-
} // end namespace Geom
-
-
-#endif // _2GEOM_ELLIPSE_H_
-
+#endif // LIB2GEOM_SEEN_ELLIPSE_H
/*
Local Variables:
diff --git a/src/2geom/elliptical-arc.cpp b/src/2geom/elliptical-arc.cpp
index c96d5f1a6..601f0c8c8 100644
--- a/src/2geom/elliptical-arc.cpp
+++ b/src/2geom/elliptical-arc.cpp
@@ -34,11 +34,12 @@
#include <limits>
#include <memory>
-#include <2geom/elliptical-arc.h>
-#include <2geom/ellipse.h>
-#include <2geom/sbasis-geometric.h>
#include <2geom/bezier-curve.h>
+#include <2geom/ellipse.h>
+#include <2geom/elliptical-arc.h>
+#include <2geom/path-sink.h>
#include <2geom/poly.h>
+#include <2geom/sbasis-geometric.h>
#include <2geom/transforms.h>
#include <2geom/utils.h>
@@ -46,7 +47,6 @@
#include <2geom/numeric/fitting-tool.h>
#include <2geom/numeric/fitting-model.h>
-
namespace Geom
{
@@ -93,6 +93,8 @@ namespace Geom
Rect EllipticalArc::boundsExact() const
{
+ using std::swap;
+
double extremes[4];
double sinrot, cosrot;
sincos(_rot_angle, sinrot, cosrot);
@@ -109,11 +111,11 @@ Rect EllipticalArc::boundsExact() const
arc_extremes[0] = initialPoint()[X];
arc_extremes[1] = finalPoint()[X];
if ( arc_extremes[0] < arc_extremes[1] )
- std::swap(arc_extremes[0], arc_extremes[1]);
+ swap(arc_extremes[0], arc_extremes[1]);
arc_extremes[2] = initialPoint()[Y];
arc_extremes[3] = finalPoint()[Y];
if ( arc_extremes[2] < arc_extremes[3] )
- std::swap(arc_extremes[2], arc_extremes[3]);
+ swap(arc_extremes[2], arc_extremes[3]);
if ( !are_near(initialPoint(), finalPoint()) ) {
for (unsigned i = 0; i < 4; ++i) {
@@ -266,8 +268,12 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const
}
double rotx, roty;
- sincos(_rot_angle, roty, rotx); /// \todo sin and cos are calculated in many places in this function, optimize this a bit!
- if (d == X) roty = -roty;
+ if (d == X) {
+ sincos(_rot_angle, roty, rotx);
+ roty = -roty;
+ } else {
+ sincos(_rot_angle, rotx, roty);
+ }
double rxrotx = ray(X) * rotx;
double c_v = center(d) - v;
@@ -428,7 +434,7 @@ Curve *EllipticalArc::reverse() const {
}
#ifdef HAVE_GSL // GSL is required for function "solve_reals"
-std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from, double to ) const
+std::vector<double> EllipticalArc::allNearestTimes( Point const& p, double from, double to ) const
{
std::vector<double> result;
@@ -446,7 +452,7 @@ std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from
else if ( are_near(ray(X), 0) || are_near(ray(Y), 0) )
{
LineSegment seg(pointAt(from), pointAt(to));
- Point np = seg.pointAt( seg.nearestPoint(p) );
+ Point np = seg.pointAt( seg.nearestTime(p) );
if ( are_near(ray(Y), 0) )
{
if ( are_near(_rot_angle, M_PI/2)
@@ -570,7 +576,7 @@ std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from
double mindistsq1 = std::numeric_limits<double>::max();
double mindistsq2 = std::numeric_limits<double>::max();
- double dsq;
+ double dsq = 0;
unsigned int mi1 = 0, mi2 = 0;
for ( unsigned int i = 0; i < real_sol.size(); ++i )
{
@@ -887,16 +893,50 @@ D2<SBasis> EllipticalArc::toSBasis() const
return arc;
}
-
-Curve *EllipticalArc::transformed(Affine const& m) const
+void EllipticalArc::transform(Affine const& m)
{
+ if (isChord()) {
+ _initial_point *= m;
+ _final_point *= m;
+ _center = middle_point(_initial_point, _final_point);
+ _rays = Point(0,0);
+ _rot_angle = 0;
+ return;
+ }
+
+ // TODO avoid allocating a new arc here
Ellipse e(center(X), center(Y), ray(X), ray(Y), _rot_angle);
- Ellipse et = e.transformed(m);
+ e *= m;
Point inner_point = pointAt(0.5);
- return et.arc( initialPoint() * m,
- inner_point * m,
- finalPoint() * m,
- isSVGCompliant() );
+ EllipticalArc *arc = e.arc(initialPoint() * m,
+ inner_point * m,
+ finalPoint() * m,
+ isSVGCompliant());
+ *this = *arc;
+ delete arc;
+}
+
+bool EllipticalArc::operator==(Curve const &c) const
+{
+ EllipticalArc const *other = dynamic_cast<EllipticalArc const *>(&c);
+ if (!other) return false;
+ if (_initial_point != other->_initial_point) return false;
+ if (_final_point != other->_final_point) return false;
+ // TODO: all arcs with ellipse rays which are too small
+ // and fall back to a line should probably be equal
+ if (_rays != other->_rays) return false;
+ if (_rot_angle != other->_rot_angle) return false;
+ if (_large_arc != other->_large_arc) return false;
+ if (_sweep != other->_sweep) return false;
+ return true;
+}
+
+void EllipticalArc::feed(PathSink &sink, bool moveto_initial) const
+{
+ if (moveto_initial) {
+ sink.moveTo(_initial_point);
+ }
+ sink.arcTo(_rays[X], _rays[Y], _rot_angle, _large_arc, _sweep, _final_point);
}
Coord EllipticalArc::map_to_01(Coord angle) const
diff --git a/src/2geom/elliptical-arc.h b/src/2geom/elliptical-arc.h
index 5527aa6bc..342b8c8c9 100644
--- a/src/2geom/elliptical-arc.h
+++ b/src/2geom/elliptical-arc.h
@@ -34,8 +34,8 @@
* the specific language governing rights and limitations.
*/
-#ifndef _2GEOM_ELLIPTICAL_ARC_H_
-#define _2GEOM_ELLIPTICAL_ARC_H_
+#ifndef LIB2GEOM_SEEN_ELLIPTICAL_ARC_H
+#define LIB2GEOM_SEEN_ELLIPTICAL_ARC_H
#include <algorithm>
#include <2geom/angle.h>
@@ -71,9 +71,9 @@ public:
* @param sweep If true, the clockwise arc is chosen, otherwise the counter-clockwise
* arc is chosen
* @param fp Final point of the arc */
- EllipticalArc( Point ip, Coord rx, Coord ry,
+ EllipticalArc( Point const &ip, Coord rx, Coord ry,
Coord rot_angle, bool large_arc, bool sweep,
- Point fp
+ Point const &fp
)
: AngleInterval(0,0,sweep)
, _initial_point(ip)
@@ -137,7 +137,7 @@ public:
* recalculations of the center and extreme angles.
* @param ip New initial point
* @param fp New final point */
- void setExtremes(Point const &ip, Point const &fp) {
+ void setEndpoints(Point const &ip, Point const &fp) {
_initial_point = ip;
_final_point = fp;
_updateCenterAndAngles(isSVGCompliant());
@@ -182,6 +182,11 @@ public:
/** @brief Check whether the arc adheres to SVG 1.1 implementation guidelines */
virtual bool isSVGCompliant() const { return false; }
+ /// Check whether both rays are nonzero
+ bool isChord() const {
+ return _rays[0] == 0 || _rays[Y] == 0;
+ }
+
std::pair<EllipticalArc, EllipticalArc> subdivide(Coord t) const {
EllipticalArc* arc1 = static_cast<EllipticalArc*>(portion(0, t));
EllipticalArc* arc2 = static_cast<EllipticalArc*>(portion(t, 1));
@@ -193,7 +198,6 @@ public:
}
// implementation of overloads goes here
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
virtual Point initialPoint() const { return _initial_point; }
virtual Point finalPoint() const { return _final_point; }
virtual Curve* duplicate() const { return new EllipticalArc(*this); }
@@ -206,7 +210,7 @@ public:
_updateCenterAndAngles(isSVGCompliant());
}
virtual bool isDegenerate() const {
- return ( are_near(ray(X), 0) || are_near(ray(Y), 0) );
+ return _initial_point == _final_point;
}
virtual Rect boundsFast() const {
return boundsExact();
@@ -218,17 +222,17 @@ public:
}
virtual std::vector<double> roots(double v, Dim2 d) const;
#ifdef HAVE_GSL
- virtual std::vector<double> allNearestPoints( Point const& p, double from = 0, double to = 1 ) const;
+ virtual std::vector<double> allNearestTimes( Point const& p, double from = 0, double to = 1 ) const;
#endif
- virtual double nearestPoint( Point const& p, double from = 0, double to = 1 ) const {
+ virtual double nearestTime( Point const& p, double from = 0, double to = 1 ) const {
if ( are_near(ray(X), ray(Y)) && are_near(center(), p) ) {
return from;
}
- return allNearestPoints(p, from, to).front();
+ return allNearestTimes(p, from, to).front();
}
virtual int degreesOfFreedom() const { return 7; }
virtual Curve *derivative() const;
- virtual Curve *transformed(Affine const &m) const;
+ virtual void transform(Affine const &m);
virtual Curve &operator*=(Translate const &m) {
_initial_point += m.vector();
_final_point += m.vector();
@@ -251,7 +255,8 @@ public:
}
virtual Curve* portion(double f, double t) const;
virtual Curve* reverse() const;
-#endif
+ virtual bool operator==(Curve const &c) const;
+ virtual void feed(PathSink &sink, bool moveto_initial) const;
protected:
void _updateCenterAndAngles(bool svg);
@@ -267,7 +272,7 @@ private:
} // end namespace Geom
-#endif // _2GEOM_ELLIPTICAL_ARC_H_
+#endif // LIB2GEOM_SEEN_ELLIPTICAL_ARC_H
/*
Local Variables:
diff --git a/src/2geom/exception.h b/src/2geom/exception.h
index 12736d639..ab6f82142 100644
--- a/src/2geom/exception.h
+++ b/src/2geom/exception.h
@@ -38,8 +38,8 @@
*
*/
-#ifndef LIB2GEOM_EXCEPTION_HEADER
-#define LIB2GEOM_EXCEPTION_HEADER
+#ifndef LIB2GEOM_SEEN_EXCEPTION_H
+#define LIB2GEOM_SEEN_EXCEPTION_H
#include <exception>
#include <sstream>
diff --git a/src/2geom/forward.h b/src/2geom/forward.h
index 70cac1f7d..f3ab657e8 100644
--- a/src/2geom/forward.h
+++ b/src/2geom/forward.h
@@ -32,10 +32,8 @@
* the specific language governing rights and limitations.
*/
-#ifndef SEEN_GEOM_FORWARD_H
-#define SEEN_GEOM_FORWARD_H
-
-#include <vector> // include this dependency so PathVector can be defined more explicitly
+#ifndef LIB2GEOM_SEEN_FORWARD_H
+#define LIB2GEOM_SEEN_FORWARD_H
namespace Geom {
@@ -79,7 +77,10 @@ class SVGEllipticalArc;
// paths and path sequences
class Path;
-typedef std::vector<Path> PathVector;
+class PathVector;
+struct PathPosition;
+class PathInterval;
+struct PathVectorPosition;
// errors
class Exception;
@@ -103,11 +104,6 @@ class Zoom;
template <typename> class D2;
template <typename> class Piecewise;
-class Shape;
-class Region;
-class Hat;
-class Tri;
-
// misc
class SVGPathSink;
template <typename> class SVGPathGenerator;
diff --git a/src/2geom/generic-interval.h b/src/2geom/generic-interval.h
index f6d4718de..716390e57 100644
--- a/src/2geom/generic-interval.h
+++ b/src/2geom/generic-interval.h
@@ -90,13 +90,24 @@ public:
}
/// @}
- /// @name Inspect endpoints.
+ /// @name Inspect contained values.
/// @{
C min() const { return _b[0]; }
C max() const { return _b[1]; }
C extent() const { return max() - min(); }
C middle() const { return (max() + min()) / 2; }
bool isSingular() const { return min() == max(); }
+ C operator[](unsigned i) const { assert(i < 2); return _b[i]; }
+ C clamp(C val) const {
+ if (val < min()) return min();
+ if (val > max()) return max();
+ return val;
+ }
+ /// Return the closer end of the interval.
+ C nearestEnd(C val) const {
+ C dmin = std::abs(val - min()), dmax = std::abs(val - max());
+ return dmin <= dmax ? min() : max();
+ }
/// @}
/// @name Test coordinates and other intervals for inclusion.
@@ -138,6 +149,16 @@ public:
_b[1] = val;
}
}
+ /// Set both ends of the interval simultaneously
+ void setEnds(C a, C b) {
+ if (a <= b) {
+ _b[0] = a;
+ _b[1] = b;
+ } else {
+ _b[0] = b;
+ _b[1] = a;
+ }
+ }
/** @brief Extend the interval to include the given number. */
void expandTo(C val) {
if(val < _b[0]) _b[0] = val;
@@ -271,6 +292,8 @@ public:
/** @brief Check whether this interval is empty. */
bool isEmpty() { return !*this; };
+ /// Alias of isEmpty() for STL similarity.
+ bool empty() { return !*this; }
/** @brief Union with another interval, gracefully handling empty ones. */
void unionWith(GenericOptInterval<C> const &a) {
@@ -317,14 +340,14 @@ inline GenericOptInterval<C> operator&(GenericInterval<C> const &a, GenericInter
return GenericOptInterval<C>(a) & GenericOptInterval<C>(b);
}
-//#ifdef _GLIBCXX_IOSTREAM
+#ifdef _GLIBCXX_IOSTREAM
template <typename C>
inline std::ostream &operator<< (std::ostream &os,
Geom::GenericInterval<C> const &I) {
os << "Interval("<<I.min() << ", "<<I.max() << ")";
return os;
}
-//#endif
+#endif
} // namespace Geom
#endif // !LIB2GEOM_SEEN_GENERIC_INTERVAL_H
diff --git a/src/2geom/generic-rect.h b/src/2geom/generic-rect.h
index 9ad0e60b0..f7d722757 100644
--- a/src/2geom/generic-rect.h
+++ b/src/2geom/generic-rect.h
@@ -64,6 +64,10 @@ class GenericRect
protected:
CInterval f[2];
public:
+ typedef CInterval D1Value;
+ typedef CInterval &D1Reference;
+ typedef CInterval const &D1ConstReference;
+
/// @name Create rectangles.
/// @{
/** @brief Create a rectangle that contains only the point at (0,0). */
@@ -180,12 +184,34 @@ public:
/** @brief Compute rectangle's area. */
C area() const { return f[X].extent() * f[Y].extent(); }
/** @brief Check whether the rectangle has zero area. */
- bool hasZeroArea() const { return (area() == 0); }
+ bool hasZeroArea() const { return f[X].isSingular() || f[Y].isSingular(); }
/** @brief Get the larger extent (width or height) of the rectangle. */
C maxExtent() const { return std::max(f[X].extent(), f[Y].extent()); }
/** @brief Get the smaller extent (width or height) of the rectangle. */
C minExtent() const { return std::min(f[X].extent(), f[Y].extent()); }
+
+ /** @brief Clamp point to the rectangle. */
+ CPoint clamp(CPoint const &p) const {
+ CPoint result(f[X].clamp(p[X]), f[Y].clamp(p[Y]));
+ return result;
+ }
+ /** @brief Get the nearest point on the edge of the rectangle. */
+ CPoint nearestEdgePoint(CPoint const &p) const {
+ CPoint result = p;
+ if (!contains(p)) {
+ result = clamp(p);
+ } else {
+ C cx = f[X].nearestEnd(p[X]);
+ C cy = f[Y].nearestEnd(p[Y]);
+ if (std::abs(cx - p[X]) <= std::abs(cy - p[Y])) {
+ result[X] = cx;
+ } else {
+ result[Y] = cy;
+ }
+ }
+ return result;
+ }
/// @}
/// @name Test other rectangles and points for inclusion.
@@ -328,6 +354,10 @@ class GenericOptRect
typedef typename CoordTraits<C>::OptRectType OptCRect;
typedef boost::optional<CRect> Base;
public:
+ typedef CInterval D1Value;
+ typedef CInterval &D1Reference;
+ typedef CInterval const &D1ConstReference;
+
/// @name Create potentially empty rectangles.
/// @{
GenericOptRect() : Base() {}
@@ -483,13 +513,13 @@ inline bool GenericRect<C>::contains(OptCRect const &r) const {
return !r || contains(*r);
}
-//#ifdef _GLIBCXX_IOSTREAM
+#ifdef _GLIBCXX_IOSTREAM
template <typename C>
inline std::ostream &operator<<(std::ostream &out, GenericRect<C> const &r) {
out << "X: " << r[X] << " Y: " << r[Y];
return out;
}
-//#endif
+#endif
} // end namespace Geom
diff --git a/src/2geom/geom.cpp b/src/2geom/geom.cpp
index d3cf0ca73..bf9b0a721 100644
--- a/src/2geom/geom.cpp
+++ b/src/2geom/geom.cpp
@@ -10,8 +10,17 @@
#include <algorithm>
#include <2geom/rect.h>
+using std::swap;
+
namespace Geom {
+enum IntersectorKind {
+ intersects = 0,
+ parallel,
+ coincident,
+ no_intersection
+};
+
/**
* Finds the intersection of the two (infinite) lines
* defined by the points p such that dot(n0, p) == d0 and dot(n1, p) == d1.
@@ -305,7 +314,7 @@ rect_line_intersect(Geom::Point const &c0, Geom::Point const &c1,
Point dir1 (results[1] - results[0]);
Point dir2 (p1 - p0);
if (dot(dir1, dir2) < 0) {
- std::swap(results[0], results[1]);
+ swap(results[0], results[1]);
}
}
@@ -363,7 +372,7 @@ int centroid(std::vector<Geom::Point> const &p, Geom::Point& centroid, double &a
Geom::Point centroid_tmp(0,0);
double atmp = 0;
for (unsigned i = n-1, j = 0; j < n; i = j, j++) {
- const double ai = -cross(p[j], p[i]);
+ const double ai = cross(p[j], p[i]);
atmp += ai;
centroid_tmp += (p[j] + p[i])*ai; // first moment.
}
diff --git a/src/2geom/geom.h b/src/2geom/geom.h
index 5aeded23d..6ba812254 100644
--- a/src/2geom/geom.h
+++ b/src/2geom/geom.h
@@ -44,52 +44,10 @@
namespace Geom {
-enum IntersectorKind {
- intersects = 0,
- parallel,
- coincident,
- no_intersection
-};
-
-int
-intersector_ccw(const Geom::Point& p0, const Geom::Point& p1,
- const Geom::Point& p2);
-
-/* intersectors */
-
-#if 0
-// Use the new routines provided in line.h
-
-IntersectorKind
-line_intersection(Geom::Point const &n0, double const d0,
- Geom::Point const &n1, double const d1,
- Geom::Point &result);
-
-IntersectorKind
-segment_intersect(Geom::Point const &p00, Geom::Point const &p01,
- Geom::Point const &p10, Geom::Point const &p11,
- Geom::Point &result);
-
-IntersectorKind
-line_twopoint_intersect(Geom::Point const &p00, Geom::Point const &p01,
- Geom::Point const &p10, Geom::Point const &p11,
- Geom::Point &result);
-#endif
-
-#if 0
-std::vector<Geom::Point>
-rect_line_intersect(Geom::Point const &E, Geom::Point const &F,
- Geom::Point const &p0, Geom::Point const &p1);
-#endif
-
boost::optional<Geom::LineSegment>
rect_line_intersect(Geom::Rect &r,
Geom::LineSegment ls);
-boost::optional<Geom::LineSegment>
-rect_line_intersect(Geom::Rect &r,
- Geom::Line l);
-
int centroid(std::vector<Geom::Point> const &p, Geom::Point& centroid, double &area);
}
diff --git a/src/2geom/hvlinesegment.h b/src/2geom/hvlinesegment.h
deleted file mode 100644
index d2b9d6310..000000000
--- a/src/2geom/hvlinesegment.h
+++ /dev/null
@@ -1,283 +0,0 @@
-/**
- * \file
- * \brief Horizontal and vertical line segment
- *//*
- * Authors:
- * Marco Cecchetti <mrcekets at gmail.com>
- * Krzysztof Kosiński <tweenk.pl@gmail.com>
- * Copyright 2008-2011 Authors
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- */
-
-#ifndef LIB2GEOM_SEEN_HVLINESEGMENT_H
-#define LIB2GEOM_SEEN_HVLINESEGMENT_H
-
-#include <2geom/bezier-curve.h>
-
-namespace Geom
-{
-
-template <Dim2 axis>
-class AxisLineSegment : public LineSegment
-{
-public:
- static const Dim2 other_axis = static_cast<Dim2>((axis + 1) % 2);
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
- virtual void setInitial(Point const &p) {
- Point f = finalPoint();
- f[axis] = p[axis];
- LineSegment::setInitial(p);
- LineSegment::setFinal(f);
- }
- virtual void setFinal(Point const &p) {
- Point i = initialPoint();
- i[axis] = p[axis];
- LineSegment::setFinal(p);
- LineSegment::setInitial(i);
- }
- virtual Rect boundsFast() const { return boundsExact(); }
- virtual Rect boundsExact() const { Rect r(initialPoint(), finalPoint()); return r; }
- virtual int degreesOfFreedom() const { return 3; }
- virtual std::vector<Coord> roots(Coord v, Dim2 d) const {
- std::vector<Coord> result;
- if (d == axis) {
- if ( v >= initialPoint()[axis] && v <= finalPoint()[axis] ) {
- Coord t = 0;
- if (!isDegenerate())
- t = (v - initialPoint()[axis]) / (finalPoint()[axis] - initialPoint()[axis]);
- result.push_back(t);
- }
- } else {
- if (v == initialPoint()[other_axis]) {
- if (!isDegenerate())
- THROW_INFINITESOLUTIONS(0);
- result.push_back(0);
- }
- }
- return result;
- }
- virtual Coord nearestPoint( Point const &p, Coord from = 0, Coord to = 1 ) const {
- if ( from > to ) std::swap(from, to);
- Coord xfrom = valueAt(from, axis);
- Coord xto = valueAt(to, axis);
- if ( xfrom > xto ) {
- std::swap(xfrom, xto);
- std::swap(from, to);
- }
- if ( p[axis] > xfrom && p[axis] < xto ) {
- return (p[axis] - initialPoint()[axis]) / (finalPoint()[axis] - initialPoint()[axis]);
- }
- else if ( p[X] <= xfrom )
- return from;
- else
- return to;
- }
- virtual Point pointAt(Coord t) const {
- if ( t < 0 || t > 1 )
- THROW_RANGEERROR("AxisLineSegment: Time value out of range");
- Point ret = initialPoint() + t * (finalPoint() - initialPoint());
- return ret;
- }
- virtual Coord valueAt(Coord t, Dim2 d) const {
- if ( t < 0 || t > 1 )
- THROW_RANGEERROR("AxisLineSegment: Time value out of range");
- if (d != axis) return initialPoint()[other_axis];
- return initialPoint()[axis] + t * (finalPoint()[axis] - initialPoint()[axis]);
- }
- virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const {
- std::vector<Point> result;
- result.push_back(pointAt(t));
- if (n > 0) {
- Point der = finalPoint() - initialPoint();
- result.push_back( der );
- }
- if (n > 1) {
- /* higher order derivatives are zero,
- * so the other n-1 vector elements are (0,0) */
- result.insert( result.end(), n-1, Point(0, 0) );
- }
- return result;
- }
-#endif
-protected:
- AxisLineSegment(Point const &p0, Point const &p1) : LineSegment(p0, p1) {}
- AxisLineSegment() {}
-};
-
-class HLineSegment : public AxisLineSegment<X>
-{
-public:
- HLineSegment() {}
- HLineSegment(Coord x0, Coord x1, Coord y)
- : AxisLineSegment<X>(Point(x0, y), Point(x1, y))
- {}
-
- HLineSegment(Point const& p, Coord len)
- : AxisLineSegment<X>(p, Point(p[X] + len, p[Y]))
- {}
-
- HLineSegment(Point const& p0, Point const& p1)
- : AxisLineSegment<X>(p0, p1)
- {
- if ( p0[Y] != p1[Y] ) {
- THROW_RANGEERROR("HLineSegment::HLineSegment passed points should "
- "have the same Y value");
- }
- }
-
- Coord getY() { return initialPoint()[Y]; }
- void setInitialX(Coord x) {
- LineSegment::setInitial(Point(x, initialPoint()[Y]));
- }
- void setFinalX(Coord x) {
- LineSegment::setFinal(Point(x, initialPoint()[Y]));
- }
- void setY(Coord y) {
- LineSegment::setInitial( Point(initialPoint()[X], y) );
- LineSegment::setFinal( Point(finalPoint()[X], y) );
- }
- std::pair<HLineSegment, HLineSegment> subdivide(Coord t) const {
- std::pair<HLineSegment, HLineSegment> result;
- Point p = pointAt(t);
- result.first.setInitial(initialPoint());
- result.first.setFinal(p);
- result.second.setInitial(p);
- result.second.setFinal(finalPoint());
- return result;
- }
-
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
- virtual Curve* duplicate() const { return new HLineSegment(*this); }
- virtual Curve *portion(Coord f, Coord t) const {
- Point ip = pointAt(f);
- Point ep = pointAt(t);
- return new HLineSegment(ip[X], ep[X], ip[Y]);
- }
- virtual Curve *reverse() const {
- Point ip = initialPoint();
- return new HLineSegment(finalPoint()[X], ip[X], ip[Y]);
- }
- virtual Curve *transformed(Affine const & m) const {
- Point ip = initialPoint() * m;
- Point ep = finalPoint() * m;
- // cannot afford to lose precision here since it can lead to discontinuous paths
- if (m.isZoom(0)) {
- return new HLineSegment(ip[X], ep[X], ip[Y]);
- } else {
- return new LineSegment(ip, ep);
- }
- }
- virtual Curve *derivative() const {
- Coord x = finalPoint()[X] - initialPoint()[X];
- return new HLineSegment(x, x, 0);
- }
-#endif
-}; // end class HLineSegment
-
-
-class VLineSegment : public AxisLineSegment<Y>
-{
-public:
- VLineSegment() {}
-
- VLineSegment(Coord x, Coord y0, Coord y1)
- : AxisLineSegment<Y>(Point(x, y0), Point(x, y1))
- {}
-
- VLineSegment(Point const& _p, Coord _length)
- : AxisLineSegment<Y>(_p, Point(_p[X], _p[Y] + _length))
- {}
-
- VLineSegment(Point const& _p0, Point const& _p1)
- : AxisLineSegment<Y>(_p0, _p1)
- {
- if ( _p0[X] != _p1[X] ) {
- THROW_RANGEERROR("VLineSegment::VLineSegment passed points should "
- "have the same X value");
- }
- }
-
- Coord getX() { return initialPoint()[X]; }
- void setInitialY(Coord _y) {
- LineSegment::setInitial( Point(initialPoint()[X], _y) );
- }
- void setFinalY(Coord _y) {
- LineSegment::setFinal( Point(finalPoint()[Y], _y) );
- }
- void setX(Coord _x) {
- LineSegment::setInitial( Point(_x, initialPoint()[Y]) );
- LineSegment::setFinal( Point(_x, finalPoint()[Y]) );
- }
- std::pair<VLineSegment, VLineSegment> subdivide(Coord t) const {
- std::pair<VLineSegment, VLineSegment> result;
- Point p = pointAt(t);
- result.first.setInitial(initialPoint());
- result.first.setFinal(p);
- result.second.setInitial(p);
- result.second.setFinal(finalPoint());
- return result;
- }
-
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
- virtual Curve *duplicate() const { return new VLineSegment(*this); }
- virtual Curve *portion(Coord f, Coord t) const {
- Point ip = pointAt(f);
- Coord epy = valueAt(t, Y);
- return new VLineSegment(ip[X], ip[Y], epy);
- }
- virtual Curve *reverse() const {
- Point ip = initialPoint();
- return new VLineSegment(ip[X], finalPoint()[Y], ip[Y]);
- }
- virtual Curve *transformed(Affine const & m) const {
- Point ip = initialPoint() * m;
- Point ep = finalPoint() * m;
- if (m.isZoom()) {
- return new VLineSegment(ip[X], ip[Y], ep[Y]);
- } else {
- return new LineSegment(ip, ep);
- }
- }
- virtual Curve* derivative() const {
- Coord y = finalPoint()[Y] - initialPoint()[Y];
- return new VLineSegment(0, y, y);
- }
-#endif
-}; // end class VLineSegment
-
-} // end namespace Geom
-
-#endif // LIB2GEOM_SEEN_HVLINESEGMENT_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/2geom/int-point.h b/src/2geom/int-point.h
index 1a16ecb7a..7c737b1d7 100644
--- a/src/2geom/int-point.h
+++ b/src/2geom/int-point.h
@@ -120,30 +120,53 @@ public:
}
/// @}
- /** @brief Lexicographical ordering functor. */
- template <Dim2 d> struct LexOrder;
+ /** @brief Lexicographical ordering functor.
+ * @param d The more significant dimension */
+ template <Dim2 d> struct LexLess;
+ /** @brief Lexicographical ordering functor.
+ * @param d The more significant dimension */
+ template <Dim2 d> struct LexGreater;
/** @brief Lexicographical ordering functor with runtime dimension. */
- class LexOrderRt {
- public:
- LexOrderRt(Dim2 d) : dim(d) {}
- inline bool operator()(IntPoint const &a, IntPoint const &b);
+ struct LexLessRt {
+ LexLessRt(Dim2 d) : dim(d) {}
+ inline bool operator()(IntPoint const &a, IntPoint const &b) const;
+ private:
+ Dim2 dim;
+ };
+ /** @brief Lexicographical ordering functor with runtime dimension. */
+ struct LexGreaterRt {
+ LexGreaterRt(Dim2 d) : dim(d) {}
+ inline bool operator()(IntPoint const &a, IntPoint const &b) const;
private:
Dim2 dim;
};
};
-template<> struct IntPoint::LexOrder<X> {
- bool operator()(IntPoint const &a, IntPoint const &b) {
+template<> struct IntPoint::LexLess<X> {
+ bool operator()(IntPoint const &a, IntPoint const &b) const {
return a[X] < b[X] || (a[X] == b[X] && a[Y] < b[Y]);
}
};
-template<> struct IntPoint::LexOrder<Y> {
- bool operator()(IntPoint const &a, IntPoint const &b) {
+template<> struct IntPoint::LexLess<Y> {
+ bool operator()(IntPoint const &a, IntPoint const &b) const {
return a[Y] < b[Y] || (a[Y] == b[Y] && a[X] < b[X]);
}
};
-inline bool IntPoint::LexOrderRt::operator()(IntPoint const &a, IntPoint const &b) {
- return dim ? IntPoint::LexOrder<Y>()(a, b) : IntPoint::LexOrder<X>()(a, b);
+template<> struct IntPoint::LexGreater<X> {
+ bool operator()(IntPoint const &a, IntPoint const &b) const {
+ return a[X] > b[X] || (a[X] == b[X] && a[Y] > b[Y]);
+ }
+};
+template<> struct IntPoint::LexGreater<Y> {
+ bool operator()(IntPoint const &a, IntPoint const &b) const {
+ return a[Y] > b[Y] || (a[Y] == b[Y] && a[X] > b[X]);
+ }
+};
+inline bool IntPoint::LexLessRt::operator()(IntPoint const &a, IntPoint const &b) const {
+ return dim ? IntPoint::LexLess<Y>()(a, b) : IntPoint::LexLess<X>()(a, b);
+}
+inline bool IntPoint::LexGreaterRt::operator()(IntPoint const &a, IntPoint const &b) const {
+ return dim ? IntPoint::LexGreater<Y>()(a, b) : IntPoint::LexGreater<X>()(a, b);
}
} // namespace Geom
diff --git a/src/2geom/intersection-graph.cpp b/src/2geom/intersection-graph.cpp
new file mode 100644
index 000000000..abe462e3f
--- /dev/null
+++ b/src/2geom/intersection-graph.cpp
@@ -0,0 +1,296 @@
+/**
+ * \file
+ * \brief Intersection graph for Boolean operations
+ *//*
+ * Authors:
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright 2015 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#include <2geom/intersection-graph.h>
+#include <2geom/path.h>
+#include <2geom/pathvector.h>
+#include <iostream>
+
+namespace Geom {
+
+struct IntersectionVertexLess {
+ bool operator()(IntersectionVertex const &a, IntersectionVertex const &b) const {
+ return a.pos < b.pos;
+ }
+};
+
+/** @class PathIntersectionGraph
+ * @brief Intermediate data for computing Boolean operations on paths.
+ *
+ * This class implements the Greiner-Hormann clipping algorithm,
+ * with improvements by Foster and Overfelt.
+ *
+ * @ingroup Paths
+ */
+
+PathIntersectionGraph::PathIntersectionGraph(PathVector const &a, PathVector const &b, Coord precision)
+ : _a(a)
+ , _b(b)
+{
+ if (a.empty() || b.empty()) return;
+
+ // all paths must be closed, otherwise we will miss some intersections
+ for (std::size_t i = 0; i < a.size(); ++i) {
+ _a[i].close();
+ }
+ for (std::size_t i = 0; i < b.size(); ++i) {
+ _b[i].close();
+ }
+
+ std::vector<PVIntersection> pxs = _a.intersect(_b, precision);
+ if (pxs.empty()) return;
+ if (pxs.size() % 2) return;
+
+ // prepare intersection lists for each path component
+ for (std::size_t i = 0; i < _a.size(); ++i) {
+ _xalists.push_back(new IntersectionList());
+ }
+ for (std::size_t i = 0; i < _b.size(); ++i) {
+ _xblists.push_back(new IntersectionList());
+ }
+
+ for (std::size_t i = 0; i < pxs.size(); ++i) {
+ IntersectionVertex *xa, *xb;
+ xa = new IntersectionVertex();
+ xb = new IntersectionVertex();
+ xa->processed = xb->processed = false;
+ xa->pos = pxs[i].first;
+ xb->pos = pxs[i].second;
+ xa->p = xb->p = pxs[i].point();
+ xa->neighbor = xb;
+ xb->neighbor = xa;
+ _xs.push_back(xa);
+ _xs.push_back(xb);
+ _xalists[xa->pos.path_index].push_back(*xa);
+ _xblists[xb->pos.path_index].push_back(*xb);
+ }
+
+ for (std::size_t i = 0; i < _xalists.size(); ++i) {
+ _xalists[i].sort(IntersectionVertexLess());
+ }
+ for (std::size_t i = 0; i < _xblists.size(); ++i) {
+ _xblists[i].sort(IntersectionVertexLess());
+ }
+
+ typedef IntersectionList::iterator Iter;
+
+ // determine in/out/on flags using winding
+ for (unsigned npv = 0; npv < 2; ++npv) {
+ boost::ptr_vector<IntersectionList> &ls = npv ? _xblists : _xalists;
+ PathVector const &pv = npv ? b : a;
+ PathVector const &other = npv ? a : b;
+
+ for (unsigned li = 0; li < ls.size(); ++li) {
+ for (Iter i = ls[li].begin(); i != ls[li].end(); ++i) {
+ Iter n = boost::next(i);
+ if (n == ls[li].end()) {
+ n = ls[li].begin();
+ }
+ std::size_t pi = i->pos.path_index;
+
+ PathInterval ival = forward_interval(i->pos, n->pos, pv[pi].size());
+ PathPosition mid = ival.inside(precision);
+
+ // TODO check for degenerate cases
+ // requires changes in the winding routine
+ int w = other.winding(pv[pi].pointAt(mid));
+ if (w % 2) {
+ i->next = POINT_INSIDE;
+ n->previous = POINT_INSIDE;
+ } else {
+ i->next = POINT_OUTSIDE;
+ n->previous = POINT_OUTSIDE;
+ }
+ }
+
+ // assign exit / entry flags
+ for (Iter i = ls[li].begin(); i != ls[li].end(); ++i) {
+ i->entry = ((i->next == POINT_INSIDE) && (i->previous == POINT_OUTSIDE));
+ }
+ }
+ }
+}
+
+PathVector PathIntersectionGraph::getUnion()
+{
+ PathVector result = _getResult(false, false);
+ _handleNonintersectingPaths(result, 0, false);
+ _handleNonintersectingPaths(result, 1, false);
+ return result;
+}
+
+PathVector PathIntersectionGraph::getIntersection()
+{
+ PathVector result = _getResult(true, true);
+ _handleNonintersectingPaths(result, 0, true);
+ _handleNonintersectingPaths(result, 1, true);
+ return result;
+}
+
+PathVector PathIntersectionGraph::_getResult(bool enter_a, bool enter_b)
+{
+ typedef IntersectionList::iterator Iter;
+ PathVector result;
+ if (_xs.empty()) return result;
+
+ // reset processed status
+ for (unsigned npv = 0; npv < 2; ++npv) {
+ boost::ptr_vector<IntersectionList> &ls = npv ? _xblists : _xalists;
+ for (std::size_t li = 0; li < ls.size(); ++li) {
+ for (Iter k = ls[li].begin(); k != ls[li].end(); ++k) {
+ k->processed = false;
+ }
+ }
+ }
+
+ unsigned n_processed = 0;
+
+ while (true) {
+ PathVector const *cur = &_a, *other = &_b;
+ boost::ptr_vector<IntersectionList> *lscur = &_xalists, *lsother = &_xblists;
+
+ // find unprocessed intersection
+ Iter i;
+ if (!_findUnprocessed(i)) break;
+
+ result.push_back(Path(i->p));
+ result.back().setStitching(true);
+
+ while (!i->processed) {
+ Iter prev = i;
+ std::size_t pi = i->pos.path_index;
+ // determine which direction to go
+ // union: always go outside
+ // intersection: always go inside
+ // a minus b: go inside in b, outside in a
+ // b minus a: go inside in a, outside in b
+ bool reverse = false;
+ if (cur == &_a) {
+ reverse = i->entry ^ enter_a;
+ } else {
+ reverse = i->entry ^ enter_b;
+ }
+
+ // get next intersection
+ if (reverse) {
+ if (i == (*lscur)[pi].begin()) {
+ i = (*lscur)[pi].end();
+ }
+ --i;
+ } else {
+ ++i;
+ if (i == (*lscur)[pi].end()) {
+ i = (*lscur)[pi].begin();
+ }
+ }
+
+ // append portion of path
+ PathInterval ival = PathInterval::from_direction(
+ prev->pos.asPathPosition(), i->pos.asPathPosition(),
+ reverse, (*cur)[pi].size());
+
+ (*cur)[pi].appendPortionTo(result.back(), ival, prev->p, i->p);
+
+ // mark both vertices as processed
+ prev->processed = true;
+ i->processed = true;
+ n_processed += 2;
+
+ // switch to the other path
+ i = (*lsother)[i->neighbor->pos.path_index].iterator_to(*i->neighbor);
+ std::swap(lscur, lsother);
+ std::swap(cur, other);
+ }
+
+ assert(!result.back().empty());
+ }
+
+ assert(n_processed == _xs.size());
+
+ return result;
+}
+
+void PathIntersectionGraph::_handleNonintersectingPaths(PathVector &result, int which, bool inside)
+{
+ /* Every component that has any intersections will be processed by _getResult.
+ * Here we take care of paths that don't have any intersections. They are either
+ * completely inside or completely outside the other pathvector. We test this by
+ * evaluating the winding rule at the initial point. If inside is true and
+ * the path is inside, we add it to the result.
+ */
+ boost::ptr_vector<IntersectionList> const &ls = which ? _xblists : _xalists;
+ PathVector const &cur = which ? _b : _a;
+ PathVector const &other = which ? _a : _b;
+
+ for (std::size_t i = 0; i < cur.size(); ++i) {
+ if (!ls.empty() && !ls[i].empty()) continue;
+
+ int w = other.winding(cur[i].initialPoint());
+ bool path_inside = w % 2 != 0;
+ if (path_inside == inside) {
+ result.push_back(cur[i]);
+ }
+ }
+}
+
+bool PathIntersectionGraph::_findUnprocessed(IntersectionList::iterator &result)
+{
+ typedef IntersectionList::iterator Iter;
+
+ Iter it;
+
+ for (std::size_t k = 0; k < _xalists.size(); ++k) {
+ it = _xalists[k].begin();
+ for (; it != _xalists[k].end(); ++it) {
+ if (!it->processed) {
+ result = it;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+} // namespace Geom
+
+/*
+ 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:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/intersection-graph.h b/src/2geom/intersection-graph.h
new file mode 100644
index 000000000..2fe858c5d
--- /dev/null
+++ b/src/2geom/intersection-graph.h
@@ -0,0 +1,106 @@
+/**
+ * \file
+ * \brief Path intersection graph
+ *//*
+ * Authors:
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright 2015 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef SEEN_LIB2GEOM_INTERSECTION_GRAPH_H
+#define SEEN_LIB2GEOM_INTERSECTION_GRAPH_H
+
+#include <set>
+#include <vector>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <boost/intrusive/list.hpp>
+#include <2geom/forward.h>
+#include <2geom/pathvector.h>
+
+namespace Geom {
+
+enum InOutFlag {
+ POINT_INSIDE,
+ POINT_OUTSIDE,
+ POINT_ON_EDGE
+};
+
+struct IntersectionVertex {
+ boost::intrusive::list_member_hook<> _hook;
+ PathVectorPosition pos;
+ Point p; // guarantees that endpoints are exact
+ IntersectionVertex *neighbor;
+ bool entry; // going in +t direction enters the other path
+ InOutFlag previous;
+ InOutFlag next;
+ bool processed; // TODO: use intrusive unprocessed list instead
+};
+
+typedef boost::intrusive::list
+ < IntersectionVertex
+ , boost::intrusive::member_hook
+ < IntersectionVertex
+ , boost::intrusive::list_member_hook<>
+ , &IntersectionVertex::_hook
+ >
+ > IntersectionList;
+
+
+class PathIntersectionGraph
+{
+ // this is called PathIntersectionGraph so that we can also have a class for polygons,
+ // e.g. PolygonIntersectionGraph, which is going to be significantly faster
+public:
+ PathIntersectionGraph(PathVector const &a, PathVector const &b, Coord precision = EPSILON);
+
+ PathVector getUnion();
+ PathVector getIntersection();
+
+private:
+ PathVector _getResult(bool enter_a, bool enter_b);
+ void _handleNonintersectingPaths(PathVector &result, int which, bool inside);
+ bool _findUnprocessed(IntersectionList::iterator &result);
+
+ PathVector _a, _b;
+ boost::ptr_vector<IntersectionVertex> _xs;
+ boost::ptr_vector<IntersectionList> _xalists;
+ boost::ptr_vector<IntersectionList> _xblists;
+};
+
+} // namespace Geom
+
+#endif // SEEN_LIB2GEOM_PATH_GRAPH_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:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/intersection.h b/src/2geom/intersection.h
new file mode 100644
index 000000000..848797682
--- /dev/null
+++ b/src/2geom/intersection.h
@@ -0,0 +1,117 @@
+/**
+ * \file
+ * \brief Intersection utilities
+ *//*
+ * Authors:
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright 2015 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef SEEN_LIB2GEOM_INTERSECTION_H
+#define SEEN_LIB2GEOM_INTERSECTION_H
+
+#include <2geom/coord.h>
+#include <2geom/point.h>
+
+namespace Geom {
+
+
+/** @brief Intersection between two shapes.
+ */
+template <typename TimeA = Coord, typename TimeB = TimeA>
+class Intersection
+{
+public:
+ /** @brief Construct from shape references and time values.
+ * By default, the intersection point will be halfway between the evaluated
+ * points on the two shapes. */
+ template <typename TA, typename TB>
+ Intersection(TA const &sa, TB const &sb, TimeA const &ta, TimeB const &tb)
+ : first(ta)
+ , second(tb)
+ , _point(lerp(0.5, sa.pointAt(ta), sb.pointAt(tb)))
+ {}
+
+ /// Additionally report the intersection point.
+ Intersection(TimeA const &ta, TimeB const &tb, Point const &p)
+ : first(ta)
+ , second(tb)
+ , _point(p)
+ {}
+
+ /// Intersection point, as calculated by the intersection algorithm.
+ Point point() const {
+ return _point;
+ }
+ /// Implicit conversion to Point.
+ operator Point() const {
+ return _point;
+ }
+
+ friend inline void swap(Intersection &a, Intersection &b) {
+ using std::swap;
+ swap(a.first, b.first);
+ swap(a.second, b.second);
+ swap(a._point, b._point);
+ }
+
+public:
+ /// First shape and time value.
+ TimeA first;
+ /// Second shape and time value.
+ TimeB second;
+private:
+ // Recalculation of the intersection point from the time values is in many cases
+ // less precise than the value obtained directly from the intersection algorithm,
+ // so we need to store it.
+ Point _point;
+};
+
+
+// TODO: move into new header
+template <typename T>
+struct ShapeTraits {
+ typedef Coord TimeType;
+ typedef Interval IntervalType;
+ typedef T AffineClosureType;
+ typedef Intersection<> IntersectionType;
+};
+
+
+} // namespace Geom
+
+#endif // SEEN_LIB2GEOM_INTERSECTION_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:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/interval.h b/src/2geom/interval.h
index 1714435be..09ea46923 100644
--- a/src/2geom/interval.h
+++ b/src/2geom/interval.h
@@ -91,19 +91,23 @@ public:
}
/// @}
- /// @name Inspect endpoints.
+ /// @name Inspect contained values.
/// @{
- /** @brief Access endpoints by value.
- * @deprecated Use min() and max() instead */
- Coord operator[](unsigned i) const { return _b[i]; }
- /** @brief Access endpoints by reference.
- * @deprecated Use min() and max() instead
- * @todo Remove Interval index operator, which can be used to break the invariant */
- Coord& operator[](unsigned i) { return _b[i]; }
-
+ /** @brief Check whether both endpoints are finite. */
bool isFinite() const {
return IS_FINITE(min()) && IS_FINITE(max());
}
+ /** @brief Map the interval [0,1] onto this one.
+ * This method simply performs 1D linear interpolation between endpoints. */
+ Coord valueAt(Coord t) {
+ return lerp(t, min(), max());
+ }
+ /** @brief Find closest time in [0,1] that maps to the given value. */
+ Coord nearestTime(Coord t) {
+ if (t < min()) return 0;
+ if (t > max()) return 1;
+ return (t - min()) / extent();
+ }
/// @}
/// @name Test coordinates and other intervals for inclusion.
@@ -114,7 +118,16 @@ public:
/** @brief Check whether the interior of the interval includes the given interval.
* Interior means all numbers in the interval except its ends. */
bool interiorContains(Interval const &val) const { return min() < val.min() && val.max() < max(); }
- /** @brief Check whether the interiors of the intervals have any common elements. A single point in common is not considered an intersection. */
+ /// Check whether the number is contained in the union of the interior and the lower boundary.
+ bool lowerContains(Coord val) { return min() <= val && val < max(); }
+ /// Check whether the given interval is contained in the union of the interior and the lower boundary.
+ bool lowerContains(Interval const &val) const { return min() <= val.min() && val.max() < max(); }
+ /// Check whether the number is contained in the union of the interior and the upper boundary.
+ bool upperContains(Coord val) { return min() < val && val <= max(); }
+ /// Check whether the given interval is contained in the union of the interior and the upper boundary.
+ bool upperContains(Interval const &val) const { return min() < val.min() && val.max() <= max(); }
+ /** @brief Check whether the interiors of the intervals have any common elements.
+ * A single point in common is not considered an intersection. */
bool interiorIntersects(Interval const &val) const {
return std::max(min(), val.min()) < std::min(max(), val.max());
}
@@ -125,16 +138,18 @@ public:
// IMPL: ScalableConcept
/** @brief Scale an interval */
Interval &operator*=(Coord s) {
+ using std::swap;
_b[0] *= s;
_b[1] *= s;
- if(s < 0) std::swap(_b[0], _b[1]);
+ if(s < 0) swap(_b[0], _b[1]);
return *this;
}
/** @brief Scale an interval by the inverse of the specified value */
Interval &operator/=(Coord s) {
+ using std::swap;
_b[0] /= s;
_b[1] /= s;
- if(s < 0) std::swap(_b[0], _b[1]);
+ if(s < 0) swap(_b[0], _b[1]);
return *this;
}
/** @brief Multiply two intervals.
diff --git a/src/2geom/line.cpp b/src/2geom/line.cpp
index 097365245..35cf2d379 100644
--- a/src/2geom/line.cpp
+++ b/src/2geom/line.cpp
@@ -28,11 +28,9 @@
* the specific language governing rights and limitations.
*/
-
-#include <2geom/line.h>
-
#include <algorithm>
-
+#include <2geom/line.h>
+#include <2geom/math-utils.h>
namespace Geom
{
@@ -41,12 +39,18 @@ namespace Geom
* @class Line
* @brief Infinite line on a plane.
*
- * Every line in 2Geom has a special point on it, called the origin. The direction of the line
- * is stored as a unit vector (versor). This way a line can be interpreted as a function
- * \f$ f: (-\infty, \infty) \to \mathbb{R}^2\f$. Zero corresponds to the origin point,
- * positive values to the points in the direction of the unit vector, and negative values
- * to points in the opposite direction.
- *
+ * A line is specified as two points through which it passes. Lines can be interpreted as functions
+ * \f$ f: (-\infty, \infty) \to \mathbb{R}^2\f$. Zero corresponds to the first (origin) point,
+ * one corresponds to the second (final) point. All other points are computed as a linear
+ * interpolation between those two: \f$p = (1-t) a + t b\f$. Many such functions have the same
+ * image and therefore represent the same lines; for example, adding \f$b-a\f$ to both points
+ * yields the same line.
+ *
+ * 2Geom can represent the same line in many ways by design: using a different representation
+ * would lead to precision loss. For example, a line from (1e30, 1e30) to (10,0) would actually
+ * evaluate to (0,0) at time 1 if it was stored as origin and normalized versor,
+ * or origin and angle.
+ *
* @ingroup Primitives
*/
@@ -54,35 +58,50 @@ namespace Geom
* A line is a set of points that satisfies the line equation
* \f$Ax + By + C = 0\f$. This function changes the line so that its points
* satisfy the line equation with the given coefficients. */
-void Line::setCoefficients (double a, double b, double c) {
+void Line::setCoefficients (Coord a, Coord b, Coord c)
+{
if (a == 0 && b == 0) {
if (c != 0) {
THROW_LOGICALERROR("the passed coefficients gives the empty set");
}
- m_versor = Point(0,0);
- m_origin = Point(0,0);
- } else {
- double l = hypot(a,b);
- a /= l;
- b /= l;
- c /= l;
- Point N(a, b);
- m_versor = N.ccw();
- m_origin = -c * N;
- }
+ _initial = Point(0,0);
+ _final = Point(0,0);
+ return;
+ }
+ if (a == 0) {
+ // b must be nonzero
+ _initial = Point(0, c / b);
+ _final = _initial;
+ _final[X] = 1;
+ return;
+ }
+ if (b == 0) {
+ _initial = Point(c / a, 0);
+ _final = _initial;
+ _final[Y] = 1;
+ return;
+ }
+
+ _initial = Point(c / a, 0);
+ _final = Point(0, c / b);
+}
+
+void Line::coefficients(Coord &a, Coord &b, Coord &c) const
+{
+ Point v = versor().cw();
+ a = v[X];
+ b = v[Y];
+ c = cross(_initial, _final);
}
/** @brief Get the line equation coefficients of this line.
* @return Vector with three values corresponding to the A, B and C
* coefficients of the line equation for this line. */
-std::vector<double> Line::coefficients() const {
- std::vector<double> coeff;
- coeff.reserve(3);
- Point N = versor().cw();
- coeff.push_back (N[X]);
- coeff.push_back (N[Y]);
- double d = - dot (N, origin());
- coeff.push_back (d);
+std::vector<Coord> Line::coefficients() const
+{
+ Coord c[3];
+ coefficients(c[0], c[1], c[2]);
+ std::vector<Coord> coeff(c, c+3);
return coeff;
}
@@ -91,87 +110,153 @@ std::vector<double> Line::coefficients() const {
* @param d Which axis the coordinate is on. X means a vertical line, Y means a horizontal line.
* @return Time values at which this line intersects the query line. */
std::vector<Coord> Line::roots(Coord v, Dim2 d) const {
- if (d < 0 || d > 1)
- THROW_RANGEERROR("Line::roots, dimension argument out of range");
std::vector<Coord> result;
- if ( m_versor[d] != 0 )
- {
- result.push_back( (v - m_origin[d]) / m_versor[d] );
+ Coord r = root(v, d);
+ if (IS_FINITE(r)) {
+ result.push_back(r);
}
- // TODO: else ?
return result;
}
+Coord Line::root(Coord v, Dim2 d) const
+{
+ assert(d == X || d == Y);
+ Point vs = versor();
+ if (vs[d] != 0) {
+ return (v - _initial[d]) / vs[d];
+ } else {
+ return nan("");
+ }
+}
+
+boost::optional<LineSegment> Line::clip(Rect const &r) const
+{
+ Point v = versor();
+ // handle horizontal and vertical lines first,
+ // since the root-based code below will break for them
+ for (unsigned i = 0; i < 2; ++i) {
+ Dim2 d = (Dim2) i;
+ Dim2 o = other_dimension(d);
+ if (v[d] != 0) continue;
+ if (r[d].contains(_initial[d])) {
+ Point a, b;
+ a[o] = r[o].min();
+ b[o] = r[o].max();
+ a[d] = b[d] = _initial[d];
+ if (v[o] > 0) {
+ return LineSegment(a, b);
+ } else {
+ return LineSegment(b, a);
+ }
+ } else {
+ return boost::none;
+ }
+ }
+
+ Interval xpart(root(r[X].min(), X), root(r[X].max(), X));
+ Interval ypart(root(r[Y].min(), Y), root(r[Y].max(), Y));
+ if (!xpart.isFinite() || !ypart.isFinite()) {
+ return boost::none;
+ }
+
+ OptInterval common = xpart & ypart;
+ if (common) {
+ Point p1 = pointAt(common->min()), p2 = pointAt(common->max());
+ LineSegment result(r.clamp(p1), r.clamp(p2));
+ return result;
+ } else {
+ return boost::none;
+ }
+
+ /* old implementation using coefficients:
+
+ if (fabs(b) > fabs(a)) {
+ p0 = Point(r[X].min(), (-c - a*r[X].min())/b);
+ if (p0[Y] < r[Y].min())
+ p0 = Point((-c - b*r[Y].min())/a, r[Y].min());
+ if (p0[Y] > r[Y].max())
+ p0 = Point((-c - b*r[Y].max())/a, r[Y].max());
+ p1 = Point(r[X].max(), (-c - a*r[X].max())/b);
+ if (p1[Y] < r[Y].min())
+ p1 = Point((-c - b*r[Y].min())/a, r[Y].min());
+ if (p1[Y] > r[Y].max())
+ p1 = Point((-c - b*r[Y].max())/a, r[Y].max());
+ } else {
+ p0 = Point((-c - b*r[Y].min())/a, r[Y].min());
+ if (p0[X] < r[X].min())
+ p0 = Point(r[X].min(), (-c - a*r[X].min())/b);
+ if (p0[X] > r[X].max())
+ p0 = Point(r[X].max(), (-c - a*r[X].max())/b);
+ p1 = Point((-c - b*r[Y].max())/a, r[Y].max());
+ if (p1[X] < r[X].min())
+ p1 = Point(r[X].min(), (-c - a*r[X].min())/b);
+ if (p1[X] > r[X].max())
+ p1 = Point(r[X].max(), (-c - a*r[X].max())/b);
+ }
+ return LineSegment(p0, p1); */
+}
+
/** @brief Get a time value corresponding to a point.
* @param p Point on the line. If the point is not on the line,
* the returned value will be meaningless.
* @return Time value t such that \f$f(t) = p\f$.
* @see timeAtProjection */
-Coord Line::timeAt(Point const& _point) const {
- Coord t;
- if ( m_versor[X] != 0 ) {
- t = (_point[X] - m_origin[X]) / m_versor[X];
- }
- else if ( m_versor[Y] != 0 ) {
- t = (_point[Y] - m_origin[Y]) / m_versor[Y];
+Coord Line::timeAt(Point const &p) const
+{
+ Point v = versor();
+ // degenerate case
+ if (v[X] == 0 && v[Y] == 0) {
+ return 0;
}
- else { // degenerate case
- t = 0;
+
+ // use the coordinate that will give better precision
+ if (fabs(v[X]) > fabs(v[Y])) {
+ return (p[X] - _initial[X]) / v[X];
+ } else {
+ return (p[Y] - _initial[Y]) / v[Y];
}
- return t;
}
namespace detail
{
inline
-OptCrossing intersection_impl(Point const& V1, Point const O1,
- Point const& V2, Point const O2 )
+OptCrossing intersection_impl(Point const &v1, Point const &o1,
+ Point const &v2, Point const &o2)
{
- double detV1V2 = V1[X] * V2[Y] - V2[X] * V1[Y];
- if (are_near(detV1V2, 0)) return OptCrossing();
+ Coord cp = cross(v1, v2);
+ if (cp == 0) return OptCrossing();
- Point B = O2 - O1;
- double detBV2 = B[X] * V2[Y] - V2[X] * B[Y];
- double detV1B = B[X] * V1[Y] - V1[X] * B[Y];
- double inv_detV1V2 = 1 / detV1V2;
+ Point odiff = o2 - o1;
Crossing c;
- c.ta = detBV2 * inv_detV1V2;
- c.tb = detV1B * inv_detV1V2;
-// std::cerr << "ta = " << c.ta << std::endl;
-// std::cerr << "tb = " << c.tb << std::endl;
- return OptCrossing(c);
+ c.ta = cross(odiff, v2) / cp;
+ c.tb = cross(odiff, v1) / cp;
+ return c;
}
OptCrossing intersection_impl(Ray const& r1, Line const& l2, unsigned int i)
{
+ using std::swap;
+
OptCrossing crossing =
intersection_impl(r1.versor(), r1.origin(),
l2.versor(), l2.origin() );
- if (crossing)
- {
- if (crossing->ta < 0)
- {
+ if (crossing) {
+ if (crossing->ta < 0) {
return OptCrossing();
- }
- else
- {
- if (i != 0)
- {
- std::swap(crossing->ta, crossing->tb);
+ } else {
+ if (i != 0) {
+ swap(crossing->ta, crossing->tb);
}
return crossing;
}
}
- if (are_near(r1.origin(), l2))
- {
+ if (are_near(r1.origin(), l2)) {
THROW_INFINITESOLUTIONS();
- }
- else
- {
+ } else {
return OptCrossing();
}
}
@@ -181,34 +266,29 @@ OptCrossing intersection_impl( LineSegment const& ls1,
Line const& l2,
unsigned int i )
{
+ using std::swap;
+
OptCrossing crossing =
intersection_impl(ls1.finalPoint() - ls1.initialPoint(),
ls1.initialPoint(),
l2.versor(),
l2.origin() );
- if (crossing)
- {
+ if (crossing) {
if ( crossing->getTime(0) < 0
|| crossing->getTime(0) > 1 )
{
return OptCrossing();
- }
- else
- {
- if (i != 0)
- {
- std::swap((*crossing).ta, (*crossing).tb);
+ } else {
+ if (i != 0) {
+ swap((*crossing).ta, (*crossing).tb);
}
return crossing;
}
}
- if (are_near(ls1.initialPoint(), l2))
- {
+ if (are_near(ls1.initialPoint(), l2)) {
THROW_INFINITESOLUTIONS();
- }
- else
- {
+ } else {
return OptCrossing();
}
}
@@ -218,6 +298,8 @@ OptCrossing intersection_impl( LineSegment const& ls1,
Ray const& r2,
unsigned int i )
{
+ using std::swap;
+
Point direction = ls1.finalPoint() - ls1.initialPoint();
OptCrossing crossing =
intersection_impl( direction,
@@ -225,57 +307,40 @@ OptCrossing intersection_impl( LineSegment const& ls1,
r2.versor(),
r2.origin() );
- if (crossing)
- {
+ if (crossing) {
if ( (crossing->getTime(0) < 0)
|| (crossing->getTime(0) > 1)
|| (crossing->getTime(1) < 0) )
{
return OptCrossing();
- }
- else
- {
- if (i != 0)
- {
- std::swap(crossing->ta, crossing->tb);
+ } else {
+ if (i != 0) {
+ swap(crossing->ta, crossing->tb);
}
return crossing;
}
}
- if ( are_near(r2.origin(), ls1) )
- {
+ if ( are_near(r2.origin(), ls1) ) {
bool eqvs = (dot(direction, r2.versor()) > 0);
- if ( are_near(ls1.initialPoint(), r2.origin()) && !eqvs )
- {
+ if ( are_near(ls1.initialPoint(), r2.origin()) && !eqvs) {
crossing->ta = crossing->tb = 0;
return crossing;
- }
- else if ( are_near(ls1.finalPoint(), r2.origin()) && eqvs )
- {
- if (i == 0)
- {
+ } else if ( are_near(ls1.finalPoint(), r2.origin()) && eqvs) {
+ if (i == 0) {
crossing->ta = 1;
crossing->tb = 0;
- }
- else
- {
+ } else {
crossing->ta = 0;
crossing->tb = 1;
}
return crossing;
- }
- else
- {
+ } else {
THROW_INFINITESOLUTIONS();
}
- }
- else if ( are_near(ls1.initialPoint(), r2) )
- {
+ } else if ( are_near(ls1.initialPoint(), r2) ) {
THROW_INFINITESOLUTIONS();
- }
- else
- {
+ } else {
OptCrossing no_crossing;
return no_crossing;
}
@@ -287,24 +352,16 @@ OptCrossing intersection_impl( LineSegment const& ls1,
OptCrossing intersection(Line const& l1, Line const& l2)
{
- OptCrossing crossing =
- detail::intersection_impl( l1.versor(), l1.origin(),
- l2.versor(), l2.origin() );
- if (crossing)
- {
- return crossing;
- }
- if (are_near(l1.origin(), l2))
- {
+ OptCrossing c = detail::intersection_impl(
+ l1.versor(), l1.origin(),
+ l2.versor(), l2.origin());
+
+ if (!c && distance(l1.origin(), l2) == 0) {
THROW_INFINITESOLUTIONS();
}
- else
- {
- return crossing;
- }
+ return c;
}
-
OptCrossing intersection(Ray const& r1, Ray const& r2)
{
OptCrossing crossing =
@@ -416,64 +473,6 @@ OptCrossing intersection( LineSegment const& ls1, LineSegment const& ls2 )
}
}
-
-boost::optional<LineSegment> clip (Line const& l, Rect const& r)
-{
- typedef boost::optional<LineSegment> opt_linesegment;
- LineSegment result;
- //size_t index = 0;
- std::vector<Point> points;
- LineSegment ls (r.corner(0), r.corner(1));
- try
- {
- OptCrossing oc = intersection (ls, l);
- if (oc)
- {
- points.push_back (l.pointAt (oc->tb));
- }
- }
- catch (InfiniteSolutions const &e)
- {
- return opt_linesegment(ls);
- }
-
- for (size_t i = 2; i < 5; ++i)
- {
- ls.setInitial (ls[1]);
- ls.setFinal (r.corner(i));
- try
- {
- OptCrossing oc = intersection (ls, l);
- if (oc)
- {
- points.push_back (l.pointAt (oc->tb));
- if (points.size() > 1)
- {
- size_t sz = points.size();
- if (!are_near (points[sz - 2], points[sz - 1], 1e-10))
- {
- result.setInitial (points[sz - 2]);
- result.setFinal (points[sz - 1]);
- return opt_linesegment(result);
- }
- }
- }
- }
- catch (InfiniteSolutions const &e)
- {
- return opt_linesegment(ls);
- }
- }
- if ( !points.empty() )
- {
- result.setInitial (points[0]);
- result.setFinal (points[0]);
- return opt_linesegment(result);
- }
- return opt_linesegment();
-}
-
-
Line make_angle_bisector_line(Line const& l1, Line const& l2)
{
OptCrossing crossing;
diff --git a/src/2geom/line.h b/src/2geom/line.h
index cbd68fa08..2c47b4486 100644
--- a/src/2geom/line.h
+++ b/src/2geom/line.h
@@ -35,7 +35,6 @@
#define LIB2GEOM_SEEN_LINE_H
#include <cmath>
-#include <iostream>
#include <boost/optional.hpp>
#include <2geom/bezier-curve.h> // for LineSegment
#include <2geom/rect.h>
@@ -43,36 +42,48 @@
#include <2geom/exception.h>
#include <2geom/ray.h>
#include <2geom/angle.h>
+#include <2geom/intersection.h>
namespace Geom
{
-class Line {
+// class docs in cpp file
+class Line
+ : boost::equality_comparable1<Line
+ , MultipliableNoncommutative<Line, Affine
+ > >
+{
private:
- Point m_origin;
- Point m_versor;
+ Point _initial;
+ Point _final;
public:
/// @name Creating lines.
/// @{
- /** @brief Create a default horizontal line. */
+ /** @brief Create a default horizontal line.
+ * Creates a line with unit speed going in +X direction. */
Line()
- : m_origin(0,0), m_versor(1,0)
+ : _initial(0,0), _final(1,0)
{}
/** @brief Create a line with the specified inclination.
- * @param _origin One of the points on the line
+ * @param origin One of the points on the line
* @param angle Angle of the line in mathematical convention */
- Line(Point const& _origin, Coord angle )
- : m_origin(_origin)
+ Line(Point const &origin, Coord angle)
+ : _initial(origin)
{
- sincos(angle, m_versor[Y], m_versor[X]);
+ Point v;
+ sincos(angle, v[Y], v[X]);
+ _final = _initial + v;
}
/** @brief Create a line going through two points.
- * @param A First point
- * @param B Second point */
- Line(Point const& A, Point const& B) {
- setPoints(A, B);
- }
+ * The first point will be at time 0, while the second one
+ * will be at time 1.
+ * @param a Initial point
+ * @param b First point */
+ Line(Point const &a, Point const &b)
+ : _initial(a)
+ , _final(b)
+ {}
/** @brief Create a line based on the coefficients of its equation.
@see Line::setCoefficients() */
@@ -80,30 +91,30 @@ public:
setCoefficients(a, b, c);
}
- /** @brief Create a line by extending a line segment. */
- explicit Line(LineSegment const& _segment) {
- setPoints(_segment.initialPoint(), _segment.finalPoint());
- }
+ /// Create a line by extending a line segment.
+ explicit Line(LineSegment const &seg)
+ : _initial(seg.initialPoint())
+ , _final(seg.finalPoint())
+ {}
- /** @brief Create a line by extending a ray. */
- explicit Line(Ray const& _ray)
- : m_origin(_ray.origin()), m_versor(_ray.versor())
+ /// Create a line by extending a ray.
+ explicit Line(Ray const &r)
+ : _initial(r.origin())
+ , _final(r.origin() + r.versor())
{}
- // huh?
- static Line from_normal_distance(Point n, double c) {
- Point P = n * c / dot(n,n);
- Line l(P, P+rot90(n));
+ /// Create a line normal to a vector at a specified distance from origin.
+ static Line from_normal_distance(Point const &n, Coord c) {
+ Point start = c * n.normalized();
+ Line l(start, start + rot90(n));
return l;
}
/** @brief Create a line from origin and unit vector.
* Note that each line direction has two possible unit vectors.
* @param o Point through which the line will pass
* @param v Unit vector of the line's direction */
- static Line from_origin_and_versor(Point o, Point v) {
- Line l;
- l.m_origin = o;
- l.m_versor = v;
+ static Line from_origin_and_versor(Point const &o, Point const &v) {
+ Line l(o, o + v);
return l;
}
@@ -114,63 +125,99 @@ public:
/// @name Retrieve and set the line's parameters.
/// @{
- /** @brief Get the line's origin point. */
- Point origin() const { return m_origin; }
- /** @brief Get the line's direction unit vector. */
- Point versor() const { return m_versor; }
- // return the angle described by rotating the X-axis in cw direction
- // until it overlaps the line
- // the returned value is in the interval [0, PI[
+
+ /// Get the line's origin point.
+ Point origin() const { return _initial; }
+ /** @brief Get the line's direction vector.
+ * Note that the retrieved vector is not normalized to unit length. */
+ Point versor() const { return _final - _initial; }
+ /// Angle the line makes with the X axis, in mathematical convention.
Coord angle() const {
- double a = std::atan2(m_versor[Y], m_versor[X]);
+ Point d = _final - _initial;
+ double a = std::atan2(d[Y], d[X]);
if (a < 0) a += M_PI;
if (a == M_PI) a = 0;
return a;
}
- void setOrigin(Point const& _point) {
- m_origin = _point;
+ /** @brief Set the point at zero time.
+ * The orientation remains unchanged, modulo numeric errors during addition. */
+ void setOrigin(Point const &p) {
+ Point d = p - _initial;
+ _initial = p;
+ _final += d;
}
- void setVersor(Point const& _versor) {
- m_versor = _versor;
+ /** @brief Set the speed of the line.
+ * Origin remains unchanged. */
+ void setVersor(Point const &v) {
+ _final = _initial + v;
}
- void setAngle(Coord _angle) {
- sincos(_angle, m_versor[Y], m_versor[X]);
+ /** @brief Set the angle the line makes with the X axis.
+ * Origin remains unchanged. */
+ void setAngle(Coord angle) {
+ Point v;
+ sincos(angle, v[Y], v[X]);
+ v *= distance(_initial, _final);
+ _final = _initial + v;
}
- /** @brief Set a line based on two points it should pass through. */
- void setPoints(Point const& A, Point const& B) {
- m_origin = A;
- if ( are_near(A, B) )
- m_versor = Point(0,0);
- else
- m_versor = B - A;
- m_versor.normalize();
+ /// Set a line based on two points it should pass through.
+ void setPoints(Point const &a, Point const &b) {
+ _initial = a;
+ _final = b;
}
- void setCoefficients (double a, double b, double c);
+ /** @brief Set the coefficients of the line equation.
+ * The line equation is: \f$ax + by = c\f$. Points that satisfy the equation
+ * are on the line. */
+ void setCoefficients(double a, double b, double c);
+
+ /** @brief Get the coefficients of the line equation as a vector.
+ * @return STL vector @a v such that @a v[0] contains \f$a\f$, @a v[1] contains \f$b\f$,
+ * and @a v[2] contains \f$c\f$. */
std::vector<double> coefficients() const;
- /** @brief Check if the line has any points.
+ /// Get the coefficients of the line equation by reference.
+ void coefficients(Coord &a, Coord &b, Coord &c) const;
+
+ /** @brief Check if the line has more than one point.
* A degenerate line can be created if the line is created from a line equation
* that has no solutions.
- * @return True if the line has no points */
+ * @return True if the line has no points or exactly one point */
bool isDegenerate() const {
- return ( m_versor[X] == 0 && m_versor[Y] == 0 );
+ return _initial == _final;
+ }
+
+ /** @brief Reparametrize the line so that it has unit speed. */
+ void normalize() {
+ Point v = _final - _initial;
+ v.normalize();
+ _final = _initial + v;
+ }
+ /** @brief Return a new line reparametrized for unit speed. */
+ Line normalized() const {
+ Point v = _final - _initial;
+ v.normalize();
+ Line ret(_initial, _initial + v);
+ return ret;
}
/// @}
/// @name Evaluate the line as a function.
///@{
+ Point initialPoint() const {
+ return _initial;
+ }
+ Point finalPoint() const {
+ return _final;
+ }
Point pointAt(Coord t) const {
- return m_origin + m_versor * t;
+ return lerp(t, _initial, _final);;
}
Coord valueAt(Coord t, Dim2 d) const {
- if (d < 0 || d > 1)
- THROW_RANGEERROR("Line::valueAt, dimension argument out of range");
- return m_origin[d] + m_versor[d] * t;
+ return lerp(t, _initial[d], _final[d]);
}
Coord timeAt(Point const &p) const;
@@ -180,27 +227,29 @@ public:
* @return Time value corresponding to a point closest to @c p. */
Coord timeAtProjection(Point const& p) const {
if ( isDegenerate() ) return 0;
- return dot( p - m_origin, m_versor );
+ Point v = versor();
+ return dot(p - _initial, v) / dot(v, v);
}
/** @brief Find a point on the line closest to the query point.
* This is an alias for timeAtProjection(). */
- Coord nearestPoint(Point const& _point) const {
- return timeAtProjection(_point);
+ Coord nearestTime(Point const &p) const {
+ return timeAtProjection(p);
}
std::vector<Coord> roots(Coord v, Dim2 d) const;
+ Coord root(Coord v, Dim2 d) const;
/// @}
/// @name Create other objects based on this line.
/// @{
- /** @brief Create a line containing the same points, but with negated time values.
- * @return Line \f$g\f$ such that \f$g(t) = f(-t)\f$ */
- Line reverse() const
- {
- Line result;
- result.setOrigin(m_origin);
- result.setVersor(-m_versor);
+ void reverse() {
+ std::swap(_final, _initial);
+ }
+ /** @brief Create a line containing the same points, but in opposite direction.
+ * @return Line \f$g\f$ such that \f$g(t) = f(1-t)\f$ */
+ Line reversed() const {
+ Line result(_final, _initial);
return result;
}
@@ -219,6 +268,9 @@ public:
return LineSegment(pointAt(f), pointAt(t));
}
+ /// Return the portion of the line that is inside the given rectangle
+ boost::optional<LineSegment> clip(Rect const &r) const;
+
/** @brief Create a ray starting at the specified time value.
* The created ray will go in the direction of the line's versor (in the direction
* of increasing time values).
@@ -227,7 +279,7 @@ public:
Ray ray(Coord t) {
Ray result;
result.setOrigin(pointAt(t));
- result.setVersor(m_versor);
+ result.setVersor(versor());
return result;
}
@@ -235,88 +287,126 @@ public:
* The new line will always be degenerate. Its origin will be equal to this
* line's versor. */
Line derivative() const {
- Line result;
- result.setOrigin(m_versor);
- result.setVersor(Point(0,0));
+ Point v = versor();
+ Line result(v, v);
return result;
}
- /** @brief Create a line transformed by an affine transformation. */
+ /// Create a line transformed by an affine transformation.
Line transformed(Affine const& m) const {
- return Line(m_origin * m, (m_origin + m_versor) * m);
+ Line l(_initial * m, _final * m);
+ return l;
}
- /** @brief Get a vector normal to the line.
+ /** @brief Get a unit vector normal to the line.
* If Y grows upwards, then this is the left normal. If Y grows downwards,
* then this is the right normal. */
Point normal() const {
- return rot90(m_versor);
+ return rot90(versor()).normalized();
}
// what does this do?
Point normalAndDist(double & dist) const {
Point n = normal();
- dist = -dot(n, m_origin);
+ dist = -dot(n, _initial);
return n;
}
- friend inline std::ostream &operator<< (std::ostream &out_file, const Geom::Line &in_line);
-/// @}
-}; // end class Line
+ /// Compute an affine matrix representing a reflection about the line.
+ Affine reflection() const {
+ Point v = versor().normalized();
+ Coord x2 = v[X]*v[X], y2 = v[Y]*v[Y], xy = v[X]*v[Y];
+ Affine m(x2-y2, 2.*xy,
+ 2.*xy, y2-x2,
+ _initial[X], _initial[Y]);
+ m = Translate(-_initial) * m;
+ return m;
+ }
-/** @brief Output operator for lines.
- * Prints out representation (point + versor)
- */
-inline std::ostream &operator<< (std::ostream &out_file, const Geom::Line &in_line) {
- out_file << "X: " << in_line.m_origin[X] << " Y: " << in_line.m_origin[Y]
- << " dX: " << in_line.m_versor[X] << " dY: " << in_line.m_versor[Y];
- return out_file;
-}
+ /** @brief Compute an affine which transforms all points on the line to zero X or Y coordinate.
+ * This operation is useful in reducing intersection problems to root-finding problems.
+ * There are many affines which do this transformation. This function returns one that
+ * preserves angles, areas and distances - a rotation combined with a translation, and
+ * additionaly moves the initial point of the line to (0,0). This way it works without
+ * problems even for lines perpendicular to the target, though may in some cases have
+ * lower precision than e.g. a shear transform.
+ * @param d Which coordinate of points on the line should be zero after the transformation */
+ Affine rotationToZero(Dim2 d) const {
+ Point v = versor();
+ if (d == X) {
+ std::swap(v[X], v[Y]);
+ } else {
+ v[Y] = -v[Y];
+ }
+ Affine m = Translate(-_initial) * Rotate(v);
+ return m;
+ }
+ /** @brief Compute a rotation affine which transforms the line to one of the axes.
+ * @param d Which line should be the axis */
+ Affine rotationToAxis(Dim2 d) const {
+ Affine m = rotationToZero(other_dimension(d));
+ return m;
+ }
+ /// @}
-inline
-double distance(Point const& _point, Line const& _line)
-{
- if ( _line.isDegenerate() )
- {
- return ::Geom::distance( _point, _line.origin() );
+ //std::vector<LineIntersection> intersect(Line const &other, Coord precision = EPSILON) const;
+
+ Line &operator*=(Affine const &m) {
+ _initial *= m;
+ _final *= m;
+ return *this;
}
- else
- {
- return fabs( dot(_point - _line.origin(), _line.versor().ccw()) );
+ bool operator==(Line const &other) const {
+ if (distance(pointAt(nearestTime(other._initial)), other._initial) != 0) return false;
+ if (distance(pointAt(nearestTime(other._final)), other._final) != 0) return false;
+ return true;
}
-}
+}; // end class Line
+/// @brief Compute distance from point to line.
+/// @relates Line
inline
-bool are_near(Point const& _point, Line const& _line, double eps = EPSILON)
+double distance(Point const &p, Line const &line)
{
- return are_near(distance(_point, _line), 0, eps);
+ if (line.isDegenerate()) {
+ return ::Geom::distance(p, line.initialPoint());
+ } else {
+ Coord t = line.nearestTime(p);
+ return ::Geom::distance(line.pointAt(t), p);
+ }
}
inline
-bool are_parallel(Line const& l1, Line const& l2, double eps = EPSILON)
+bool are_near(Point const &p, Line const &line, double eps = EPSILON)
{
- return ( are_near(l1.versor(), l2.versor(), eps)
- || are_near(l1.versor(), -l2.versor(), eps) );
+ return are_near(distance(p, line), 0, eps);
}
inline
-bool are_same(Line const& l1, Line const& l2, double eps = EPSILON)
+bool are_parallel(Line const &l1, Line const &l2, double eps = EPSILON)
{
- return are_parallel(l1, l2, eps) && are_near(l1.origin(), l2, eps);
+ return are_near(cross(l1.versor(), l2.versor()), 0, eps);
}
+/** @brief Test whether two lines are approximately the same.
+ * This tests for being parallel and the origin of one line being close to the other,
+ * so it tests whether the images of the lines are similar, not whether the same time values
+ * correspond to similar points. For example a line from (1,1) to (2,2) and a line from
+ * (-1,-1) to (0,0) will the the same, because their images match, even though there is
+ * no time value for which the lines give similar points.
+ * @relates Line */
inline
-bool are_orthogonal(Line const& l1, Line const& l2, double eps = EPSILON)
+bool are_same(Line const &l1, Line const &l2, double eps = EPSILON)
{
- return ( are_near(l1.versor(), l2.versor().cw(), eps)
- || are_near(l1.versor(), l2.versor().ccw(), eps) );
+ return are_parallel(l1, l2, eps) && are_near(l1.origin(), l2, eps);
}
+/// Test whether two lines are perpendicular.
+/// @relates Line
inline
-bool are_collinear(Point const& p1, Point const& p2, Point const& p3,
- double eps = EPSILON)
+bool are_orthogonal(Line const &l1, Line const &l2, double eps = EPSILON)
{
- return are_near( cross(p3, p2) - cross(p3, p1) + cross(p2, p1), 0, eps);
+ return are_near(dot(l1.versor(), l2.versor()), 0, eps);
}
// evaluate the angle between l1 and l2 rotating l1 in cw direction
@@ -332,36 +422,34 @@ double angle_between(Line const& l1, Line const& l2)
}
inline
-double distance(Point const& _point, LineSegment const& _segment)
+double distance(Point const &p, LineSegment const &seg)
{
- double t = _segment.nearestPoint(_point);
- return L2(_point - _segment.pointAt(t));
+ double t = seg.nearestTime(p);
+ return distance(p, seg.pointAt(t));
}
inline
-bool are_near(Point const& _point, LineSegment const& _segment,
- double eps = EPSILON)
+bool are_near(Point const &p, LineSegment const &seg, double eps = EPSILON)
{
- return are_near(distance(_point, _segment), 0, eps);
+ return are_near(distance(p, seg), 0, eps);
}
// build a line passing by _point and orthogonal to _line
inline
-Line make_orthogonal_line(Point const& _point, Line const& _line)
+Line make_orthogonal_line(Point const &p, Line const &line)
{
- Line l;
- l.setOrigin(_point);
- l.setVersor(_line.versor().cw());
+ Point d = line.versor().cw();
+ Line l(p, p + d);
return l;
}
// build a line passing by _point and parallel to _line
inline
-Line make_parallel_line(Point const& _point, Line const& _line)
+Line make_parallel_line(Point const &p, Line const &line)
{
- Line l(_line);
- l.setOrigin(_point);
- return l;
+ Line result(line);
+ result.setOrigin(p);
+ return result;
}
// build a line passing by the middle point of _segment and orthogonal to it.
@@ -373,31 +461,31 @@ Line make_bisector_line(LineSegment const& _segment)
// build the bisector line of the angle between ray(O,A) and ray(O,B)
inline
-Line make_angle_bisector_line(Point const& A, Point const& O, Point const& B)
+Line make_angle_bisector_line(Point const &A, Point const &O, Point const &B)
{
- Point M = middle_point(A,B);
- if (are_near(O,M)) {
- Line l(A,B);
- M += (make_orthogonal_line(O,l)).versor();
- }
- return Line(O,M);
+ AngleInterval ival(Angle(A-O), Angle(B-O));
+ Angle bisect = ival.angleAt(0.5);
+ return Line(O, bisect);
}
// prj(P) = rot(v, Point( rot(-v, P-O)[X], 0 )) + O
inline
-Point projection(Point const& _point, Line const& _line)
+Point projection(Point const &p, Line const &line)
{
- return _line.pointAt( _line.nearestPoint(_point) );
+ return line.pointAt(line.nearestTime(p));
}
inline
-LineSegment projection(LineSegment const& _segment, Line const& _line)
+LineSegment projection(LineSegment const &seg, Line const &line)
{
- return _line.segment( _line.nearestPoint(_segment.initialPoint()),
- _line.nearestPoint(_segment.finalPoint()) );
+ return line.segment(line.nearestTime(seg.initialPoint()),
+ line.nearestTime(seg.finalPoint()));
}
-boost::optional<LineSegment> clip (Line const& l, Rect const& r);
+inline
+boost::optional<LineSegment> clip(Line const &l, Rect const &r) {
+ return l.clip(r);
+}
namespace detail
diff --git a/src/2geom/linear.h b/src/2geom/linear.h
index 8c154364e..cd7108835 100644
--- a/src/2geom/linear.h
+++ b/src/2geom/linear.h
@@ -1,7 +1,7 @@
/**
* \file
* \brief Linear fragment function class
- *
+ *//*
* Authors:
* Nathan Hurst <njh@mail.csse.monash.edu.au>
* Michael Sloan <mgsloan@gmail.com>
@@ -32,12 +32,12 @@
* the specific language governing rights and limitations.
*/
-#ifndef SEEN_LINEAR_H
-#define SEEN_LINEAR_H
+#ifndef LIB2GEOM_SEEN_LINEAR_H
+#define LIB2GEOM_SEEN_LINEAR_H
+
#include <2geom/interval.h>
#include <2geom/math-utils.h>
-
//#define USE_SBASIS_OF
#ifdef USE_SBASIS_OF
@@ -46,48 +46,60 @@
#else
-namespace Geom{
-
-inline double lerp(double t, double a, double b) { return a*(1-t) + b*t; }
+namespace Geom {
class SBasis;
-class Linear{
+/**
+ * @brief Linear function fragment
+ * @ingroup Fragments
+ */
+class Linear {
public:
double a[2];
Linear() {a[0]=0; a[1]=0;}
Linear(double aa, double b) {a[0] = aa; a[1] = b;}
Linear(double aa) {a[0] = aa; a[1] = aa;}
- double operator[](const int i) const {
- assert(i >= 0);
+ double operator[](unsigned i) const {
assert(i < 2);
return a[i];
}
- double& operator[](const int i) {
- assert(i >= 0);
+ double &operator[](unsigned i) {
assert(i < 2);
return a[i];
}
//IMPL: FragmentConcept
typedef double output_type;
- inline bool isZero(double eps=EPSILON) const { return are_near(a[0], 0., eps) && are_near(a[1], 0., eps); }
- inline bool isConstant(double eps=EPSILON) const { return are_near(a[0], a[1], eps); }
- inline bool isFinite() const { return IS_FINITE(a[0]) && IS_FINITE(a[1]); }
-
- inline double at0() const { return a[0]; }
- inline double at1() const { return a[1]; }
-
- inline double valueAt(double t) const { return lerp(t, a[0], a[1]); }
- inline double operator()(double t) const { return valueAt(t); }
+ bool isZero(double eps=EPSILON) const { return are_near(a[0], 0., eps) && are_near(a[1], 0., eps); }
+ bool isConstant(double eps=EPSILON) const { return are_near(a[0], a[1], eps); }
+ bool isFinite() const { return IS_FINITE(a[0]) && IS_FINITE(a[1]); }
+
+ Coord at0() const { return a[0]; }
+ Coord &at0() { return a[0]; }
+ Coord at1() const { return a[1]; }
+ Coord &at1() { return a[1]; }
+
+ double valueAt(double t) const { return lerp(t, a[0], a[1]); }
+ double operator()(double t) const { return valueAt(t); }
+
+ // not very useful, but required for ShapeConcept
+ std::vector<Coord> valueAndDerivatives(Coord t, unsigned n) {
+ std::vector<Coord> result(n+1, 0.0);
+ result[0] = valueAt(t);
+ if (n >= 1) {
+ result[1] = a[1] - a[0];
+ }
+ return result;
+ }
//defined in sbasis.h
inline SBasis toSBasis() const;
- inline OptInterval bounds_exact() const { return Interval(a[0], a[1]); }
- inline OptInterval bounds_fast() const { return bounds_exact(); }
- inline OptInterval bounds_local(double u, double v) const { return Interval(valueAt(u), valueAt(v)); }
+ OptInterval bounds_exact() const { return Interval(a[0], a[1]); }
+ OptInterval bounds_fast() const { return bounds_exact(); }
+ OptInterval bounds_local(double u, double v) const { return Interval(valueAt(u), valueAt(v)); }
double tri() const {
return a[1] - a[0];
@@ -98,6 +110,10 @@ public:
};
inline Linear reverse(Linear const &a) { return Linear(a[1], a[0]); }
+inline Linear portion(Linear const &a, Coord from, Coord to) {
+ Linear result(a.valueAt(from), a.valueAt(to));
+ return result;
+}
//IMPL: AddableConcept
inline Linear operator+(Linear const & a, Linear const & b) {
@@ -158,7 +174,7 @@ inline Linear operator/=(Linear & a, double b) {
}
#endif
-#endif //SEEN_LINEAR_H
+#endif //LIB2GEOM_SEEN_LINEAR_H
/*
Local Variables:
diff --git a/src/2geom/nearest-point.cpp b/src/2geom/nearest-time.cpp
index c5dfc133c..0b21e51a2 100644
--- a/src/2geom/nearest-point.cpp
+++ b/src/2geom/nearest-time.cpp
@@ -1,9 +1,8 @@
-/*
- * nearest point routines for D2<SBasis> and Piecewise<D2<SBasis>>
- *
+/** @file
+ * @brief Nearest time routines for D2<SBasis> and Piecewise<D2<SBasis>>
+ *//*
* Authors:
- *
- * Marco Cecchetti <mrcekets at gmail.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
*
* Copyright 2007-2008 authors
*
@@ -32,27 +31,79 @@
*/
-#include <2geom/nearest-point.h>
+#include <2geom/nearest-time.h>
#include <algorithm>
-
namespace Geom
{
+Coord nearest_time(Point const &p, D2<Bezier> const &input, Coord from, Coord to)
+{
+ Interval domain(from, to);
+ bool partial = false;
+
+ if (domain.min() < 0 || domain.max() > 1) {
+ THROW_RANGEERROR("[from,to] interval out of bounds");
+ }
+
+ if (input.isConstant(0)) return from;
+
+ D2<Bezier> bez;
+ if (domain.min() != 0 || domain.max() != 1) {
+ bez = portion(input, domain) - p;
+ partial = true;
+ } else {
+ bez = input - p;
+ }
+
+ // find extrema of the function x(t)^2 + y(t)^2
+ // use the fact that (f^2)' = 2 f f'
+ // this reduces the order of the distance function by 1
+ D2<Bezier> deriv = derivative(bez);
+ std::vector<Coord> ts = (multiply(bez[X], deriv[X]) + multiply(bez[Y], deriv[Y])).roots();
+
+ Coord t = -1, mind = infinity();
+ for (unsigned i = 0; i < ts.size(); ++i) {
+ Coord droot = L2sq(bez.valueAt(ts[i]));
+ if (droot < mind) {
+ mind = droot;
+ t = ts[i];
+ }
+ }
+
+ // also check endpoints
+ Coord dinitial = L2sq(bez.at0());
+ Coord dfinal = L2sq(bez.at1());
+
+ if (dinitial < mind) {
+ mind = dinitial;
+ t = 0;
+ }
+ if (dfinal < mind) {
+ mind = dfinal;
+ t = 1;
+ }
+
+ if (partial) {
+ t = domain.valueAt(t);
+ }
+ return t;
+}
+
////////////////////////////////////////////////////////////////////////////////
// D2<SBasis> versions
/*
- * Return the parameter t of a nearest point on the portion of the curve "c",
+ * Return the parameter t of the nearest time value on the portion of the curve "c",
* related to the interval [from, to], to the point "p".
* The needed curve derivative "dc" is passed as parameter.
- * The function return the first nearest point to "p" that is found.
+ * The function return the first nearest time value to "p" that is found.
*/
-double nearest_point( Point const& p,
- D2<SBasis> const& c,
- D2<SBasis> const& dc,
- double from, double to )
+double nearest_time(Point const& p,
+ D2<SBasis> const& c,
+ D2<SBasis> const& dc,
+ double from, double to )
{
if ( from > to ) std::swap(from, to);
if ( from < 0 || to > 1 )
@@ -88,21 +139,20 @@ double nearest_point( Point const& p,
*/
std::vector<double>
-all_nearest_points( Point const& p,
- D2<SBasis> const& c,
- D2<SBasis> const& dc,
- double from, double to )
+all_nearest_times(Point const &p,
+ D2<SBasis> const &c,
+ D2<SBasis> const &dc,
+ double from, double to)
{
- std::swap(from, to);
- if ( from > to ) std::swap(from, to);
- if ( from < 0 || to > 1 )
- {
+ if (from > to) {
+ std::swap(from, to);
+ }
+ if (from < 0 || to > 1) {
THROW_RANGEERROR("[from,to] interval out of bounds");
}
std::vector<double> result;
- if (c.isConstant())
- {
+ if (c.isConstant()) {
result.push_back(from);
return result;
}
@@ -115,24 +165,19 @@ all_nearest_points( Point const& p,
candidates.push_back(to);
std::vector<double> distsq;
distsq.reserve(candidates.size());
- for ( unsigned int i = 0; i < candidates.size(); ++i )
- {
- distsq.push_back( L2sq(c(candidates[i]) - p) );
+ for (unsigned i = 0; i < candidates.size(); ++i) {
+ distsq.push_back(L2sq(c(candidates[i]) - p));
}
- unsigned int closest = 0;
+ unsigned closest = 0;
double dsq = distsq[0];
- for ( unsigned int i = 1; i < candidates.size(); ++i )
- {
- if ( dsq > distsq[i] )
- {
+ for (unsigned i = 1; i < candidates.size(); ++i) {
+ if (dsq > distsq[i]) {
closest = i;
dsq = distsq[i];
}
}
- for ( unsigned int i = 0; i < candidates.size(); ++i )
- {
- if( distsq[closest] == distsq[i] )
- {
+ for (unsigned i = 0; i < candidates.size(); ++i) {
+ if (distsq[closest] == distsq[i]) {
result.push_back(candidates[i]);
}
}
@@ -144,39 +189,37 @@ all_nearest_points( Point const& p,
// Piecewise< D2<SBasis> > versions
-double nearest_point( Point const& p,
- Piecewise< D2<SBasis> > const& c,
- double from, double to )
+double nearest_time(Point const &p,
+ Piecewise< D2<SBasis> > const &c,
+ double from, double to)
{
- if ( from > to ) std::swap(from, to);
- if ( from < c.cuts[0] || to > c.cuts[c.size()] )
- {
+ if (from > to) std::swap(from, to);
+ if (from < c.cuts[0] || to > c.cuts[c.size()]) {
THROW_RANGEERROR("[from,to] interval out of bounds");
}
- unsigned int si = c.segN(from);
- unsigned int ei = c.segN(to);
- if ( si == ei )
- {
- double nearest=
- nearest_point(p, c[si], c.segT(from, si), c.segT(to, si));
+ unsigned si = c.segN(from);
+ unsigned ei = c.segN(to);
+ if (si == ei) {
+ double nearest =
+ nearest_time(p, c[si], c.segT(from, si), c.segT(to, si));
return c.mapToDomain(nearest, si);
}
+
double t;
- double nearest = nearest_point(p, c[si], c.segT(from, si));
+ double nearest = nearest_time(p, c[si], c.segT(from, si));
unsigned int ni = si;
double dsq;
double mindistsq = distanceSq(p, c[si](nearest));
- Rect bb(Geom::Point(0,0),Geom::Point(0,0));
- for ( unsigned int i = si + 1; i < ei; ++i )
- {
+ Rect bb;
+ for (unsigned i = si + 1; i < ei; ++i) {
bb = *bounds_fast(c[i]);
dsq = distanceSq(p, bb);
if ( mindistsq <= dsq ) continue;
- t = nearest_point(p, c[i]);
+
+ t = nearest_time(p, c[i]);
dsq = distanceSq(p, c[i](t));
- if ( mindistsq > dsq )
- {
+ if (mindistsq > dsq) {
nearest = t;
ni = i;
mindistsq = dsq;
@@ -184,12 +227,10 @@ double nearest_point( Point const& p,
}
bb = *bounds_fast(c[ei]);
dsq = distanceSq(p, bb);
- if ( mindistsq > dsq )
- {
- t = nearest_point(p, c[ei], 0, c.segT(to, ei));
+ if (mindistsq > dsq) {
+ t = nearest_time(p, c[ei], 0, c.segT(to, ei));
dsq = distanceSq(p, c[ei](t));
- if ( mindistsq > dsq )
- {
+ if (mindistsq > dsq) {
nearest = t;
ni = ei;
}
@@ -198,22 +239,23 @@ double nearest_point( Point const& p,
}
std::vector<double>
-all_nearest_points( Point const& p,
- Piecewise< D2<SBasis> > const& c,
- double from, double to )
+all_nearest_times(Point const &p,
+ Piecewise< D2<SBasis> > const &c,
+ double from, double to)
{
- if ( from > to ) std::swap(from, to);
- if ( from < c.cuts[0] || to > c.cuts[c.size()] )
- {
+ if (from > to) {
+ std::swap(from, to);
+ }
+ if (from < c.cuts[0] || to > c.cuts[c.size()]) {
THROW_RANGEERROR("[from,to] interval out of bounds");
}
- unsigned int si = c.segN(from);
- unsigned int ei = c.segN(to);
+ unsigned si = c.segN(from);
+ unsigned ei = c.segN(to);
if ( si == ei )
{
std::vector<double> all_nearest =
- all_nearest_points(p, c[si], c.segT(from, si), c.segT(to, si));
+ all_nearest_times(p, c[si], c.segT(from, si), c.segT(to, si));
for ( unsigned int i = 0; i < all_nearest.size(); ++i )
{
all_nearest[i] = c.mapToDomain(all_nearest[i], si);
@@ -222,18 +264,18 @@ all_nearest_points( Point const& p,
}
std::vector<double> all_t;
std::vector< std::vector<double> > all_np;
- all_np.push_back( all_nearest_points(p, c[si], c.segT(from, si)) );
- std::vector<unsigned int> ni;
+ all_np.push_back( all_nearest_times(p, c[si], c.segT(from, si)) );
+ std::vector<unsigned> ni;
ni.push_back(si);
double dsq;
double mindistsq = distanceSq( p, c[si](all_np.front().front()) );
- Rect bb(Geom::Point(0,0),Geom::Point(0,0));
- for ( unsigned int i = si + 1; i < ei; ++i )
- {
+ Rect bb;
+
+ for (unsigned i = si + 1; i < ei; ++i) {
bb = *bounds_fast(c[i]);
dsq = distanceSq(p, bb);
if ( mindistsq < dsq ) continue;
- all_t = all_nearest_points(p, c[i]);
+ all_t = all_nearest_times(p, c[i]);
dsq = distanceSq( p, c[i](all_t.front()) );
if ( mindistsq > dsq )
{
@@ -251,29 +293,22 @@ all_nearest_points( Point const& p,
}
bb = *bounds_fast(c[ei]);
dsq = distanceSq(p, bb);
- if ( mindistsq >= dsq )
- {
- all_t = all_nearest_points(p, c[ei], 0, c.segT(to, ei));
+ if (mindistsq >= dsq) {
+ all_t = all_nearest_times(p, c[ei], 0, c.segT(to, ei));
dsq = distanceSq( p, c[ei](all_t.front()) );
- if ( mindistsq > dsq )
- {
- for ( unsigned int i = 0; i < all_t.size(); ++i )
- {
+ if (mindistsq > dsq) {
+ for (unsigned int i = 0; i < all_t.size(); ++i) {
all_t[i] = c.mapToDomain(all_t[i], ei);
}
return all_t;
- }
- else if ( mindistsq == dsq )
- {
+ } else if (mindistsq == dsq) {
all_np.push_back(all_t);
ni.push_back(ei);
}
}
std::vector<double> all_nearest;
- for ( unsigned int i = 0; i < all_np.size(); ++i )
- {
- for ( unsigned int j = 0; j < all_np[i].size(); ++j )
- {
+ for (unsigned i = 0; i < all_np.size(); ++i) {
+ for (unsigned int j = 0; j < all_np[i].size(); ++j) {
all_nearest.push_back( c.mapToDomain(all_np[i][j], ni[i]) );
}
}
diff --git a/src/2geom/nearest-point.h b/src/2geom/nearest-time.h
index 19485242c..007cd27ba 100644
--- a/src/2geom/nearest-point.h
+++ b/src/2geom/nearest-time.h
@@ -1,10 +1,8 @@
-/**
- * \file
- * \brief nearest point routines for D2<SBasis> and Piecewise<D2<SBasis>>
- *
+/** @file
+ * @brief Nearest time routines for D2<SBasis> and Piecewise<D2<SBasis>>
+ *//*
* Authors:
- *
- * Marco Cecchetti <mrcekets at gmail.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
*
* Copyright 2007-2008 authors
*
@@ -33,8 +31,8 @@
*/
-#ifndef _NEAREST_POINT_H_
-#define _NEAREST_POINT_H_
+#ifndef LIB2GEOM_SEEN_NEAREST_TIME_H
+#define LIB2GEOM_SEEN_NEAREST_TIME_H
#include <vector>
@@ -42,7 +40,7 @@
#include <2geom/d2.h>
#include <2geom/piecewise.h>
#include <2geom/exception.h>
-
+#include <2geom/bezier.h>
namespace Geom
@@ -53,81 +51,91 @@ namespace Geom
* return the point on L nearest to p. Note that the returned value
* is with respect to the _normalized_ direction of v!
*/
-inline double nearest_point(Point const &p, Point const &A, Point const &v)
+inline double nearest_time(Point const &p, Point const &A, Point const &v)
{
Point d(p - A);
return d[0] * v[0] + d[1] * v[1];
}
+Coord nearest_time(Point const &p, D2<Bezier> const &bez, Coord from = 0, Coord to = 1);
+
////////////////////////////////////////////////////////////////////////////////
// D2<SBasis> versions
/*
* Return the parameter t of a nearest point on the portion of the curve "c",
* related to the interval [from, to], to the point "p".
- * The needed curve derivative "dc" is passed as parameter.
+ * The needed curve derivative "deriv" is passed as parameter.
* The function return the first nearest point to "p" that is found.
*/
-double nearest_point( Point const& p,
- D2<SBasis> const& c, D2<SBasis> const& dc,
- double from = 0, double to = 1 );
+double nearest_time(Point const &p,
+ D2<SBasis> const &c, D2<SBasis> const &deriv,
+ double from = 0, double to = 1);
inline
-double nearest_point( Point const& p,
- D2<SBasis> const& c,
- double from = 0, double to = 1 )
+double nearest_time(Point const &p,
+ D2<SBasis> const &c,
+ double from = 0, double to = 1 )
{
- return nearest_point(p, c, Geom::derivative(c), from, to);
+ return nearest_time(p, c, Geom::derivative(c), from, to);
}
/*
- * Return the parameters t of all the nearest points on the portion of
+ * Return the parameters t of all the nearest times on the portion of
* the curve "c", related to the interval [from, to], to the point "p".
* The needed curve derivative "dc" is passed as parameter.
*/
std::vector<double>
-all_nearest_points( Point const& p,
- D2<SBasis> const& c, D2<SBasis> const& dc,
- double from = 0, double to = 1 );
+all_nearest_times(Point const& p,
+ D2<SBasis> const& c, D2<SBasis> const& dc,
+ double from = 0, double to = 1 );
inline
std::vector<double>
-all_nearest_points( Point const& p,
- D2<SBasis> const& c,
- double from = 0, double to = 1 )
+all_nearest_times(Point const &p,
+ D2<SBasis> const &c,
+ double from = 0, double to = 1)
{
- return all_nearest_points(p, c, Geom::derivative(c), from, to);
+ return all_nearest_times(p, c, Geom::derivative(c), from, to);
}
////////////////////////////////////////////////////////////////////////////////
// Piecewise< D2<SBasis> > versions
-double nearest_point( Point const& p,
- Piecewise< D2<SBasis> > const& c,
- double from, double to );
+double nearest_time(Point const &p,
+ Piecewise< D2<SBasis> > const &c,
+ double from, double to);
inline
-double nearest_point( Point const& p, Piecewise< D2<SBasis> > const& c )
+double nearest_time(Point const& p, Piecewise< D2<SBasis> > const &c)
{
- return nearest_point(p, c, c.cuts[0], c.cuts[c.size()]);
+ return nearest_time(p, c, c.cuts[0], c.cuts[c.size()]);
}
std::vector<double>
-all_nearest_points( Point const& p,
- Piecewise< D2<SBasis> > const& c,
- double from, double to );
+all_nearest_times(Point const &p,
+ Piecewise< D2<SBasis> > const &c,
+ double from, double to);
inline
std::vector<double>
-all_nearest_points( Point const& p, Piecewise< D2<SBasis> > const& c )
+all_nearest_times( Point const& p, Piecewise< D2<SBasis> > const& c )
{
- return all_nearest_points(p, c, c.cuts[0], c.cuts[c.size()]);
+ return all_nearest_times(p, c, c.cuts[0], c.cuts[c.size()]);
}
} // end namespace Geom
-
-
-#endif /*_NEAREST_POINT_H_*/
+#endif // LIB2GEOM_SEEN_NEAREST_TIME_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/2geom/numeric/fitting-model.h b/src/2geom/numeric/fitting-model.h
index a44c1ddac..5cc6dde7a 100644
--- a/src/2geom/numeric/fitting-model.h
+++ b/src/2geom/numeric/fitting-model.h
@@ -298,7 +298,7 @@ class LFMEllipse
public:
void instance(Ellipse & e, ConstVectorView const& coeff) const
{
- e.set(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]);
+ e.setCoefficients(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]);
}
};
@@ -333,7 +333,7 @@ class LFMCircle
public:
void instance(Circle & c, ConstVectorView const& coeff) const
{
- c.set(1, coeff[0], coeff[1], coeff[2]);
+ c.setCoefficients(1, coeff[0], coeff[1], coeff[2]);
}
};
diff --git a/src/2geom/numeric/matrix.h b/src/2geom/numeric/matrix.h
index a130bd748..ddca35cd6 100644
--- a/src/2geom/numeric/matrix.h
+++ b/src/2geom/numeric/matrix.h
@@ -432,16 +432,17 @@ class Matrix: public detail::MatrixImpl
inline
void swap(Matrix & m1, Matrix & m2)
{
- assert( m1.rows() == m2.rows() && m1.columns() == m2.columns() );
- std::swap(m1.m_matrix, m2.m_matrix);
+ assert(m1.rows() == m2.rows() && m1.columns() == m2.columns());
+ using std::swap;
+ swap(m1.m_matrix, m2.m_matrix);
}
-inline
-void swap_any(Matrix & m1, Matrix & m2)
+inline void swap_any(Matrix &m1, Matrix &m2)
{
- std::swap(m1.m_matrix, m2.m_matrix);
- std::swap(m1.m_rows, m2.m_rows);
- std::swap(m1.m_columns, m2.m_columns);
+ using std::swap;
+ swap(m1.m_matrix, m2.m_matrix);
+ swap(m1.m_rows, m2.m_rows);
+ swap(m1.m_columns, m2.m_columns);
}
@@ -569,8 +570,9 @@ class MatrixView : public detail::MatrixImpl
inline
void swap_view(MatrixView & m1, MatrixView & m2)
{
- assert( m1.rows() == m2.rows() && m1.columns() == m2.columns() );
- std::swap(m1.m_matrix_view, m2.m_matrix_view);
+ assert(m1.rows() == m2.rows() && m1.columns() == m2.columns());
+ using std::swap;
+ swap(m1.m_matrix_view, m2.m_matrix_view);
}
Vector operator*( detail::BaseMatrixImpl const& A,
diff --git a/src/2geom/numeric/symmetric-matrix-fs-operation.h b/src/2geom/numeric/symmetric-matrix-fs-operation.h
index 37ece56ae..c5aaa724b 100644
--- a/src/2geom/numeric/symmetric-matrix-fs-operation.h
+++ b/src/2geom/numeric/symmetric-matrix-fs-operation.h
@@ -43,12 +43,7 @@
namespace Geom { namespace NL {
template <size_t N>
-inline
-SymmetricMatrix<N> adj(const ConstBaseSymmetricMatrix<N> & /*S*/)
-{
- THROW_NOTIMPLEMENTED();
- return SymmetricMatrix<N>();
-}
+SymmetricMatrix<N> adj(const ConstBaseSymmetricMatrix<N> & S);
template <>
inline
diff --git a/src/2geom/numeric/symmetric-matrix-fs-trace.h b/src/2geom/numeric/symmetric-matrix-fs-trace.h
index dbabecf6e..eff3dd24d 100644
--- a/src/2geom/numeric/symmetric-matrix-fs-trace.h
+++ b/src/2geom/numeric/symmetric-matrix-fs-trace.h
@@ -73,13 +73,7 @@ bool abs_less (double x, double y)
template <size_t K, size_t N>
struct trace
{
- static
- double evaluate (const ConstBaseSymmetricMatrix<N> & /*S*/)
- {
- THROW_NOTIMPLEMENTED();
- return K;
- }
-
+ static double evaluate(const ConstBaseSymmetricMatrix<N> &S);
};
template <size_t N>
diff --git a/src/2geom/numeric/symmetric-matrix-fs.h b/src/2geom/numeric/symmetric-matrix-fs.h
index c1de27afd..2fadd6915 100644
--- a/src/2geom/numeric/symmetric-matrix-fs.h
+++ b/src/2geom/numeric/symmetric-matrix-fs.h
@@ -339,7 +339,10 @@ class BaseSymmetricMatrix : public ConstBaseSymmetricMatrix<N>
{
}
- using base_type::operator();
+ double operator() (size_t i, size_t j) const
+ {
+ return m_data[base_type::get_index(i,j)];
+ }
double& operator() (size_t i, size_t j)
{
diff --git a/src/2geom/numeric/vector.h b/src/2geom/numeric/vector.h
index 6ab898f29..f28289f0f 100644
--- a/src/2geom/numeric/vector.h
+++ b/src/2geom/numeric/vector.h
@@ -348,15 +348,17 @@ class Vector : public detail::VectorImpl
inline
void swap(Vector & v1, Vector & v2)
{
- assert( v1.size() == v2.size() );
- std::swap(v1.m_vector, v2.m_vector);
+ assert(v1.size() == v2.size());
+ using std::swap;
+ swap(v1.m_vector, v2.m_vector);
}
inline
void swap_any(Vector & v1, Vector & v2)
{
- std::swap(v1.m_vector, v2.m_vector);
- std::swap(v1.m_size, v2.m_size);
+ using std::swap;
+ swap(v1.m_vector, v2.m_vector);
+ swap(v1.m_size, v2.m_size);
}
@@ -552,7 +554,8 @@ inline
void swap_view(VectorView & v1, VectorView & v2)
{
assert( v1.size() == v2.size() );
- std::swap(v1.m_vector_view, v2.m_vector_view); // not swap m_vector too
+ using std::swap;
+ swap(v1.m_vector_view, v2.m_vector_view); // not swap m_vector too
}
inline
diff --git a/src/2geom/ord.h b/src/2geom/ord.h
index 0add83da4..e190a4a1e 100644
--- a/src/2geom/ord.h
+++ b/src/2geom/ord.h
@@ -1,6 +1,5 @@
-/**
- * \file
- * \brief Comparator template
+/** @file
+ * @brief Comparator template
*//*
* Authors:
* ? <?@?.?>
@@ -32,8 +31,8 @@
*
*/
-#ifndef __2GEOM_ORD__
-#define __2GEOM_ORD__
+#ifndef LIB2GEOM_SEEN_ORD_H
+#define LIB2GEOM_SEEN_ORD_H
namespace {
diff --git a/src/2geom/path-intersection.cpp b/src/2geom/path-intersection.cpp
index 63a29423d..07e38ba9e 100644
--- a/src/2geom/path-intersection.cpp
+++ b/src/2geom/path-intersection.cpp
@@ -12,98 +12,9 @@
namespace Geom {
-/**
- * This function computes the winding of the path, given a reference point.
- * Positive values correspond to counter-clockwise in the mathematical coordinate system,
- * and clockwise in screen coordinates. This particular implementation casts a ray in
- * the positive x direction. It iterates the path, checking for intersection with the
- * bounding boxes. If an intersection is found, the initial/final Y value of the curve is
- * used to derive a delta on the winding value. If the point is within the bounding box,
- * the curve specific winding function is called.
- */
-int winding(Path const &path, Point p) {
- //start on a segment which is not a horizontal line with y = p[y]
- Path::const_iterator start;
- for(Path::const_iterator iter = path.begin(); ; ++iter) {
- if(iter == path.end_closed()) { return 0; }
- if(iter->initialPoint()[Y]!=p[Y]) { start = iter; break; }
- if(iter->finalPoint()[Y]!=p[Y]) { start = iter; break; }
- if(iter->boundsFast().height()!=0.){ start = iter; break; }
- }
- int wind = 0;
- unsigned cnt = 0;
- bool starting = true;
- for (Path::const_iterator iter = start; iter != start || starting
- ; ++iter, iter = (iter == path.end_closed()) ? path.begin() : iter )
- {
- cnt++;
- if(cnt > path.size()) return wind; //some bug makes this required
- starting = false;
- Rect bounds = (iter->boundsFast());
- Coord x = p[X], y = p[Y];
-
- if(x > bounds.right() || !bounds[Y].contains(y)) continue; //ray doesn't intersect box
-
- Point final = iter->finalPoint();
- Point initial = iter->initialPoint();
- Cmp final_to_ray = cmp(final[Y], y);
- Cmp initial_to_ray = cmp(initial[Y], y);
-
- // if y is included, these will have opposite values, giving order.
- Cmp c = cmp(final_to_ray, initial_to_ray);
- if(x < bounds.left()) {
- // ray goes through bbox
- // winding delta determined by position of endpoints
- if(final_to_ray != EQUAL_TO) {
- wind += int(c); // GT = counter-clockwise = 1; LT = clockwise = -1; EQ = not-included = 0
- //std::cout << int(c) << " ";
- goto cont;
- }
- } else {
- //inside bbox, use custom per-curve winding thingie
- int delt = iter->winding(p);
- wind += delt;
- //std::cout << "n" << delt << " ";
- }
- //Handling the special case of an endpoint on the ray:
- if(final[Y] == y) {
- //Traverse segments until it breaks away from y
- //99.9% of the time this will happen the first go
- Path::const_iterator next = iter;
- ++next;
- for(; ; ++next) {
- if(next == path.end_closed()) next = path.begin();
- Rect bnds = (next->boundsFast());
- //TODO: X considerations
- if(bnds.height() > 0) {
- //It has diverged
- if(bnds.contains(p)) {
- const double fudge = 0.01;
- if(cmp(y, next->valueAt(fudge, Y)) == initial_to_ray) {
- wind += int(c);
- //std::cout << "!!!!!" << int(c) << " ";
- }
- iter = next; // No increment, as the rest of the thing hasn't been counted.
- } else {
- Coord ny = next->initialPoint()[Y];
- if(cmp(y, ny) == initial_to_ray) {
- //Is a continuation through the ray, so counts windingwise
- wind += int(c);
- //std::cout << "!!!!!" << int(c) << " ";
- }
- iter = ++next;
- }
- goto cont;
- }
- if(next==start) return wind;
- }
- //Looks like it looped, which means everything's flat
- return 0;
- }
-
- cont:(void)0;
- }
- return wind;
+/// Compute winding number of the path at the specified point
+int winding(Path const &path, Point const &p) {
+ return path.winding(p);
}
/**
@@ -162,7 +73,7 @@ void append(T &a, T const &b) {
* indicates if the time values are within their proper range on the line segments.
*/
bool
-linear_intersect(Point A0, Point A1, Point B0, Point B1,
+linear_intersect(Point const &A0, Point const &A1, Point const &B0, Point const &B1,
double &tA, double &tB, double &det) {
bool both_lines_non_zero = (!are_near(A0, A1)) && (!are_near(B0, B1));
@@ -521,7 +432,7 @@ std::vector<double> path_mono_splits(Path const &p) {
* Applies path_mono_splits to multiple paths, and returns the results such that
* time-set i corresponds to Path i.
*/
-std::vector<std::vector<double> > paths_mono_splits(std::vector<Path> const &ps) {
+std::vector<std::vector<double> > paths_mono_splits(PathVector const &ps) {
std::vector<std::vector<double> > ret;
for(unsigned i = 0; i < ps.size(); i++)
ret.push_back(path_mono_splits(ps[i]));
@@ -533,7 +444,7 @@ std::vector<std::vector<double> > paths_mono_splits(std::vector<Path> const &ps)
* Each entry i corresponds to path i of the input. The number of rects in each entry is guaranteed to be the
* number of splits for that path, subtracted by one.
*/
-std::vector<std::vector<Rect> > split_bounds(std::vector<Path> const &p, std::vector<std::vector<double> > splits) {
+std::vector<std::vector<Rect> > split_bounds(PathVector const &p, std::vector<std::vector<double> > splits) {
std::vector<std::vector<Rect> > ret;
for(unsigned i = 0; i < p.size(); i++) {
std::vector<Rect> res;
@@ -553,7 +464,7 @@ std::vector<std::vector<Rect> > split_bounds(std::vector<Path> const &p, std::ve
* This function does two sweeps, one on the bounds of each path, and after that cull, one on the curves within.
* This leads to a certain amount of code complexity, however, most of that is factored into the above functions
*/
-CrossingSet MonoCrosser::crossings(std::vector<Path> const &a, std::vector<Path> const &b) {
+CrossingSet MonoCrosser::crossings(PathVector const &a, PathVector const &b) {
if(b.empty()) return CrossingSet(a.size(), Crossings());
CrossingSet results(a.size() + b.size(), Crossings());
if(a.empty()) return results;
@@ -596,7 +507,7 @@ CrossingSet MonoCrosser::crossings(std::vector<Path> const &a, std::vector<Path>
/* This function is similar codewise to the MonoCrosser, the main difference is that it deals with
* only one set of paths and includes self intersection
-CrossingSet crossings_among(std::vector<Path> const &p) {
+CrossingSet crossings_among(PathVector const &p) {
CrossingSet results(p.size(), Crossings());
if(p.empty()) return results;
@@ -780,7 +691,7 @@ void flip_crossings(Crossings &crs) {
crs[i] = Crossing(crs[i].tb, crs[i].ta, crs[i].b, crs[i].a, !crs[i].dir);
}
-CrossingSet crossings_among(std::vector<Path> const &p) {
+CrossingSet crossings_among(PathVector const &p) {
CrossingSet results(p.size(), Crossings());
if(p.empty()) return results;
diff --git a/src/2geom/path-intersection.h b/src/2geom/path-intersection.h
index 512c31167..e3a8d1de3 100644
--- a/src/2geom/path-intersection.h
+++ b/src/2geom/path-intersection.h
@@ -32,8 +32,8 @@
*
*/
-#ifndef __GEOM_PATH_INTERSECTION_H
-#define __GEOM_PATH_INTERSECTION_H
+#ifndef LIB2GEOM_SEEN_PATH_INTERSECTION_H
+#define LIB2GEOM_SEEN_PATH_INTERSECTION_H
#include <2geom/path.h>
@@ -43,10 +43,10 @@
namespace Geom {
-int winding(Path const &path, Point p);
+int winding(Path const &path, Point const &p);
bool path_direction(Path const &p);
-inline bool contains(Path const & p, Point i, bool evenodd = true) {
+inline bool contains(Path const & p, Point const &i, bool evenodd = true) {
return (evenodd ? winding(p, i) % 2 : winding(p, i)) != 0;
}
@@ -74,19 +74,19 @@ Crossings mono_intersect(Curve const & A, Interval const &Ad,
struct SimpleCrosser : public Crosser<Path> {
Crossings crossings(Curve const &a, Curve const &b);
Crossings crossings(Path const &a, Path const &b) { return curve_sweep<SimpleCrosser>(a, b); }
- CrossingSet crossings(std::vector<Path> const &a, std::vector<Path> const &b) { return Crosser<Path>::crossings(a, b); }
+ CrossingSet crossings(PathVector const &a, PathVector const &b) { return Crosser<Path>::crossings(a, b); }
};
struct MonoCrosser : public Crosser<Path> {
- Crossings crossings(Path const &a, Path const &b) { return crossings(std::vector<Path>(1,a), std::vector<Path>(1,b))[0]; }
- CrossingSet crossings(std::vector<Path> const &a, std::vector<Path> const &b);
+ Crossings crossings(Path const &a, Path const &b) { return crossings(PathVector(a), PathVector(b))[0]; }
+ CrossingSet crossings(PathVector const &a, PathVector const &b);
};
typedef SimpleCrosser DefaultCrosser;
std::vector<double> path_mono_splits(Path const &p);
-CrossingSet crossings_among(std::vector<Path> const & p);
+CrossingSet crossings_among(PathVector const & p);
Crossings self_crossings(Path const & a);
inline Crossings crossings(Curve const & a, Curve const & b) {
@@ -99,7 +99,7 @@ inline Crossings crossings(Path const & a, Path const & b) {
return c.crossings(a, b);
}
-inline CrossingSet crossings(std::vector<Path> const & a, std::vector<Path> const & b) {
+inline CrossingSet crossings(PathVector const & a, PathVector const & b) {
DefaultCrosser c = DefaultCrosser();
return c.crossings(a, b);
}
diff --git a/src/2geom/path-sink.cpp b/src/2geom/path-sink.cpp
index 6acd9508c..6ccc21e7b 100644
--- a/src/2geom/path-sink.cpp
+++ b/src/2geom/path-sink.cpp
@@ -34,77 +34,40 @@
namespace Geom {
-void output(Curve const &curve, PathSink &sink) {
- std::vector<Point> pts;
- sbasis_to_bezier(pts, curve.toSBasis(), 2); //TODO: use something better!
- sink.curveTo(pts[0], pts[1], pts[2]);
+void PathSink::feed(Curve const &c, bool moveto_initial)
+{
+ c.feed(*this, moveto_initial);
}
-void output(HLineSegment const &curve, PathSink &sink) {
- sink.hlineTo(curve.finalPoint()[X]);
-}
-
-void output(VLineSegment const &curve, PathSink &sink) {
- sink.vlineTo(curve.finalPoint()[Y]);
-}
-
-void output(LineSegment const &curve, PathSink &sink) {
- sink.lineTo(curve[1]);
-}
-
-void output(CubicBezier const &curve, PathSink &sink) {
- sink.curveTo(curve[1], curve[2], curve[3]);
-}
-
-void output(QuadraticBezier const &curve, PathSink &sink) {
- sink.quadTo(curve[1], curve[2]);
-}
-
-void output(SVGEllipticalArc const &curve, PathSink &sink) {
- sink.arcTo( curve.ray(X), curve.ray(Y), curve.rotationAngle(),
- curve.largeArc(), curve.sweep(),
- curve.finalPoint() );
-}
-
-template <typename T>
-bool output_as(Curve const &curve, PathSink &sink) {
- T const *t = dynamic_cast<T const *>(&curve);
- if (t) {
- output(*t, sink);
- return true;
- } else {
- return false;
- }
-}
-
-void PathSink::path(Path const &path) {
+void PathSink::feed(Path const &path) {
flush();
moveTo(path.front().initialPoint());
- Path::const_iterator iter;
- for (iter = path.begin(); iter != path.end(); ++iter) {
- output_as<HLineSegment>(*iter, *this) ||
- output_as<VLineSegment>(*iter, *this) ||
- output_as<LineSegment>(*iter, *this) ||
- output_as<CubicBezier>(*iter, *this) ||
- output_as<QuadraticBezier>(*iter, *this) ||
- output_as<SVGEllipticalArc>(*iter, *this) ||
- output_as<Curve>(*iter, *this);
+ // never output the closing segment to the sink
+ Path::const_iterator iter = path.begin(), last = path.end_open();
+ for (; iter != last; ++iter) {
+ iter->feed(*this, false);
}
-
if (path.closed()) {
closePath();
}
flush();
}
-void PathSink::pathvector(PathVector const &pv) {
- flush();
+void PathSink::feed(PathVector const &pv) {
for (PathVector::const_iterator i = pv.begin(); i != pv.end(); ++i) {
- path(*i);
+ feed(*i);
}
}
+void PathSink::feed(Rect const &r) {
+ moveTo(r.corner(0));
+ lineTo(r.corner(1));
+ lineTo(r.corner(2));
+ lineTo(r.corner(3));
+ closePath();
+}
+
}
/*
diff --git a/src/2geom/path-sink.h b/src/2geom/path-sink.h
index 949369b80..5913593d2 100644
--- a/src/2geom/path-sink.h
+++ b/src/2geom/path-sink.h
@@ -29,8 +29,8 @@
*
*/
-#ifndef SEEN_SVG_PATH_H
-#define SEEN_SVG_PATH_H
+#ifndef LIB2GEOM_SEEN_PATH_SINK_H
+#define LIB2GEOM_SEEN_PATH_SINK_H
#include <2geom/pathvector.h>
#include <2geom/curves.h>
@@ -44,58 +44,69 @@ namespace Geom {
* PathSink provides an interface that allows one to easily write
* code which processes path data, for instance when converting
* between path formats used by different graphics libraries.
+ * It is also useful for writing algorithms which must do something
+ * for each curve in the path.
*
* To store a path in a new format, implement the virtual methods
- * for segments in a derived class and call path() or pathvector().
+ * for segments in a derived class and call feed().
+ *
+ * @ingroup Paths
*/
class PathSink {
public:
- /** Move to a different point without creating a segment.
+ /** @brief Move to a different point without creating a segment.
* Usually starts a new subpath. */
virtual void moveTo(Point const &p) = 0;
- /// Output a horizontal line segment. Only the X coordinate of the final point is given.
- virtual void hlineTo(Coord v) = 0;
- /// Output a vertical line segment. Only the Y coordinate of the final point is given.
- virtual void vlineTo(Coord v) = 0;
/// Output a line segment.
virtual void lineTo(Point const &p) = 0;
/// Output a quadratic Bezier segment.
virtual void curveTo(Point const &c0, Point const &c1, Point const &p) = 0;
/// Output a cubic Bezier segment.
virtual void quadTo(Point const &c, Point const &p) = 0;
- /** Output an elliptical arc segment.
+ /** @brief Output an elliptical arc segment.
* See the EllipticalArc class for the documentation of parameters. */
- virtual void arcTo(double rx, double ry, double angle,
+ virtual void arcTo(Coord rx, Coord ry, Coord angle,
bool large_arc, bool sweep, Point const &p) = 0;
/// Close the current path with a line segment.
virtual void closePath() = 0;
- /** Flush any internal state of the generator.
- *
+ /** @brief Flush any internal state of the generator.
* This call should implicitly finish the current subpath.
* Calling this method should be idempotent, because the default
- * implementations of path() and pathvector() will be call it
+ * implementations of path() and pathvector() will call it
* multiple times in a row. */
virtual void flush() = 0;
+ // Get the current point, e.g. where the initial point of the next segment will be.
+ //virtual Point currentPoint() const = 0;
- /** Undo the last segment.
+ /** @brief Undo the last segment.
* This method is optional.
* @return true true if a segment was erased, false otherwise. */
virtual bool backspace() { return false; }
// these have a default implementation
- /** Output a subpath.
- * Calls the appropriate segment methods according to the contents
- * of the passed subpath. You can override this function. */
- virtual void path(Path const &p);
- /** Output a path.
+ virtual void feed(Curve const &c, bool moveto_initial = true);
+ /** @brief Output a subpath.
* Calls the appropriate segment methods according to the contents
- * of the passed path. You can override this function. */
- virtual void pathvector(PathVector const &v);
+ * of the passed subpath. You can override this function.
+ * NOTE: if you override only some of the feed() functions,
+ * always write this in the derived class:
+ * @code
+ using PathSink::feed;
+ @endcode
+ * Otherwise the remaining methods will be hidden. */
+ virtual void feed(Path const &p);
+ /** @brief Output a path.
+ * Calls feed() on each path in the vector. You can override this function. */
+ virtual void feed(PathVector const &v);
+ /// Output an axis-aligned rectangle, using moveTo, lineTo and closePath.
+ virtual void feed(Rect const &);
virtual ~PathSink() {}
};
+/** @brief Store paths to an output iterator
+ * @ingroup Paths */
template <typename OutputIterator>
class PathIteratorSink : public PathSink {
public:
@@ -110,22 +121,6 @@ public:
}
//TODO: what if _in_path = false?
- void hlineTo(Coord v) {
- // check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z"
- if (!_in_path) {
- moveTo(_start_p);
- }
- _path.template appendNew<HLineSegment>(Point(v, _path.finalPoint()[Y]));
- }
-
- void vlineTo(Coord v) {
- // check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z"
- if (!_in_path) {
- moveTo(_start_p);
- }
- _path.template appendNew<VLineSegment>(Point(_path.finalPoint()[X], v));
- }
-
void lineTo(Point const &p) {
// check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z"
if (!_in_path) {
@@ -150,7 +145,7 @@ public:
_path.template appendNew<CubicBezier>(c0, c1, p);
}
- void arcTo(double rx, double ry, double angle,
+ void arcTo(Coord rx, Coord ry, Coord angle,
bool large_arc, bool sweep, Point const &p)
{
// check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z"
@@ -170,12 +165,12 @@ public:
return false;
}
- void append(Path const &other, Path::Stitching stitching = Path::NO_STITCHING)
+ void append(Path const &other)
{
if (!_in_path) {
moveTo(other.initialPoint());
}
- _path.append(other, stitching);
+ _path.append(other);
}
void closePath() {
@@ -188,11 +183,15 @@ public:
_in_path = false;
*_out++ = _path;
_path.clear();
- _path.close(false);
}
}
+
+ void setStitching(bool s) {
+ _path.setStitching(s);
+ }
- void path(Path const &other)
+ using PathSink::feed;
+ void feed(Path const &other)
{
flush();
*_out++ = other;
@@ -205,14 +204,19 @@ protected:
Point _start_p;
};
-typedef std::back_insert_iterator<std::vector<Path> > iter;
+typedef std::back_insert_iterator<PathVector> SubpathInserter;
-class PathBuilder : public PathIteratorSink<iter> {
+/** @brief Store paths to a PathVector
+ * @ingroup Paths */
+class PathBuilder : public PathIteratorSink<SubpathInserter> {
private:
- std::vector<Path> _pathset;
+ PathVector _pathset;
public:
- PathBuilder() : PathIteratorSink<iter>(iter(_pathset)) {}
- std::vector<Path> const &peek() const {return _pathset;}
+ PathBuilder() : PathIteratorSink<SubpathInserter>(SubpathInserter(_pathset)) {}
+ /// Retrieve the path
+ PathVector const &peek() const {return _pathset;}
+ /// Clear the stored path vector
+ void clear() { _pathset.clear(); }
};
}
diff --git a/src/2geom/path.cpp b/src/2geom/path.cpp
index 3558af3b3..cf8b15d60 100644
--- a/src/2geom/path.cpp
+++ b/src/2geom/path.cpp
@@ -1,11 +1,12 @@
-/*
- * Path - Series of continuous curves
- *
+/** @file
+ * @brief Path - a sequence of contiguous curves (implementation file)
+ *//*
* Authors:
- * MenTaLguY <mental@rydia.net>
- * Marco Cecchetti <mrcekets at gmail.com>
+ * MenTaLguY <mental@rydia.net>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
*
- * Copyright 2007-2008 authors
+ * Copyright 2007-2014 authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -31,414 +32,861 @@
* the specific language governing rights and limitations.
*/
-
-
-
#include <2geom/path.h>
+#include <2geom/pathvector.h>
#include <2geom/transforms.h>
+#include <2geom/convex-hull.h>
#include <algorithm>
+#include <limits>
using std::swap;
using namespace Geom::PathInternal;
-namespace Geom
+namespace Geom {
+
+// this represents an empty interval
+PathInterval::PathInterval()
+ : _from(0, 0.0)
+ , _to(0, 0.0)
+ , _path_size(1)
+ , _cross_start(false)
+ , _reverse(false)
+{}
+
+PathInterval::PathInterval(Position const &from, Position const &to, bool cross_start, size_type path_size)
+ : _from(from)
+ , _to(to)
+ , _path_size(path_size)
+ , _cross_start(cross_start)
+ , _reverse(cross_start ? to >= from : to < from)
+{
+ if (_reverse) {
+ _to.normalizeForward(_path_size);
+ if (_from != _to) {
+ _from.normalizeBackward(_path_size);
+ }
+ } else {
+ _from.normalizeForward(_path_size);
+ if (_from != _to) {
+ _to.normalizeBackward(_path_size);
+ }
+ }
+
+ if (_from == _to) {
+ _reverse = false;
+ _cross_start = false;
+ }
+}
+
+bool PathInterval::contains(Position const &pos) const {
+ if (_cross_start) {
+ if (_reverse) {
+ return pos >= _to || _from >= pos;
+ } else {
+ return pos >= _from || _to >= pos;
+ }
+ } else {
+ if (_reverse) {
+ return _to <= pos && pos <= _from;
+ } else {
+ return _from <= pos && pos <= _to;
+ }
+ }
+}
+
+PathPosition PathInterval::inside(Coord min_dist) const
+{
+ // If there is some node further than min_dist (in time coord) from the ends,
+ // return that node. Otherwise, return the middle.
+ PathPosition result(0, 0.0);
+
+ if (!_cross_start && _from.curve_index == _to.curve_index) {
+ PathPosition result(_from.curve_index, lerp(0.5, _from.t, _to.t));
+ return result;
+ }
+ // If _cross_start, then we can be sure that at least one node is in the domain.
+ // If dcurve == 0, it actually means that all curves are included in the domain
+
+ if (_reverse) {
+ size_type dcurve = (_path_size + _from.curve_index - _to.curve_index) % _path_size;
+ bool from_close = _from.t < min_dist;
+ bool to_close = _to.t > 1 - min_dist;
+
+ if (dcurve == 0) {
+ dcurve = _path_size;
+ }
+
+ if (dcurve == 1) {
+ if (from_close || to_close) {
+ result.curve_index = _from.curve_index;
+ Coord tmid = _from.t - ((1 - _to.t) + _from.t) * 0.5;
+ if (tmid < 0) {
+ result.curve_index = (_path_size + result.curve_index - 1) % _path_size;
+ tmid += 1;
+ }
+ result.t = tmid;
+ return result;
+ }
+
+ result.curve_index = _from.curve_index;
+ return result;
+ }
+
+ result.curve_index = (_to.curve_index + 1) % _path_size;
+ if (to_close) {
+ if (dcurve == 2) {
+ result.t = 0.5;
+ } else {
+ result.curve_index = (result.curve_index + 1) % _path_size;
+ }
+ }
+ return result;
+ } else {
+ size_type dcurve = (_path_size + _to.curve_index - _from.curve_index) % _path_size;
+ bool from_close = _from.t > 1 - min_dist;
+ bool to_close = _to.t < min_dist;
+
+ if (dcurve == 0) {
+ dcurve = _path_size;
+ }
+
+ if (dcurve == 1) {
+ if (from_close || to_close) {
+ result.curve_index = _from.curve_index;
+ Coord tmid = ((1 - _from.t) + _to.t) * 0.5 + _from.t;
+ if (tmid >= 1) {
+ result.curve_index = (result.curve_index + 1) % _path_size;
+ tmid -= 1;
+ }
+ result.t = tmid;
+ return result;
+ }
+
+ result.curve_index = _to.curve_index;
+ return result;
+ }
+
+ result.curve_index = (_from.curve_index + 1) % _path_size;
+ if (from_close) {
+ if (dcurve == 2) {
+ result.t = 0.5;
+ } else {
+ result.curve_index = (result.curve_index + 1) % _path_size;
+ }
+ }
+ return result;
+ }
+
+ result.curve_index = _reverse ? _from.curve_index : _to.curve_index;
+ return result;
+}
+
+PathInterval PathInterval::from_direction(Position const &from, Position const &to, bool reversed, size_type path_size)
+{
+ PathInterval result;
+ result._from = from;
+ result._to = to;
+ result._path_size = path_size;
+
+ if (reversed) {
+ result._to.normalizeForward(path_size);
+ if (result._from != result._to) {
+ result._from.normalizeBackward(path_size);
+ }
+ } else {
+ result._from.normalizeForward(path_size);
+ if (result._from != result._to) {
+ result._to.normalizeBackward(path_size);
+ }
+ }
+
+ if (result._from == result._to) {
+ result._reverse = false;
+ result._cross_start = false;
+ } else {
+ result._reverse = reversed;
+ if (reversed) {
+ result._cross_start = from < to;
+ } else {
+ result._cross_start = to < from;
+ }
+ }
+ return result;
+}
+
+
+Path::Path(ConvexHull const &ch)
+ : _curves(new Sequence())
+ , _closing_seg(new ClosingSegment(Point(), Point()))
+ , _closed(false)
+ , _exception_on_stitch(true)
+{
+ if (ch.empty()) {
+ _curves->push_back(_closing_seg);
+ return;
+ }
+
+ _closing_seg->setInitial(ch.back());
+ _closing_seg->setFinal(ch.front());
+
+ Point last = ch.front();
+
+ for (std::size_t i = 1; i < ch.size(); ++i) {
+ _curves->push_back(new LineSegment(last, ch[i]));
+ last = ch[i];
+ }
+
+ _curves->push_back(_closing_seg);
+ _closed = true;
+}
+
+void Path::clear()
+{
+ _unshare();
+ _curves->pop_back().release();
+ _curves->clear();
+ _closing_seg->setInitial(Point(0, 0));
+ _closing_seg->setFinal(Point(0, 0));
+ _curves->push_back(_closing_seg);
+ _closed = false;
+}
+
+OptRect Path::boundsFast() const
{
+ OptRect bounds;
+ if (empty())
+ return bounds;
+ bounds = front().boundsFast();
+ const_iterator iter = begin();
+ // the closing path segment can be ignored, because it will always lie within the bbox of the rest of the path
+ if (iter != end()) {
+ for (++iter; iter != end(); ++iter) {
+ bounds.unionWith(iter->boundsFast());
+ }
+ }
+ return bounds;
+}
-OptRect Path::boundsFast() const {
- OptRect bounds;
- if (empty()) return bounds;
- bounds = front().boundsFast();
- const_iterator iter = begin();
- // the closing path segment can be ignored, because it will always lie within the bbox of the rest of the path
- if ( iter != end() ) {
- for ( ++iter; iter != end() ; ++iter ) {
- bounds.unionWith(iter->boundsFast());
+OptRect Path::boundsExact() const
+{
+ OptRect bounds;
+ if (empty())
+ return bounds;
+ bounds = front().boundsExact();
+ const_iterator iter = begin();
+ // the closing path segment can be ignored, because it will always lie within the bbox of the rest of the path
+ if (iter != end()) {
+ for (++iter; iter != end(); ++iter) {
+ bounds.unionWith(iter->boundsExact());
+ }
}
- }
- return bounds;
+ return bounds;
}
-OptRect Path::boundsExact() const {
- OptRect bounds;
- if (empty()) return bounds;
- bounds = front().boundsExact();
- const_iterator iter = begin();
- // the closing path segment can be ignored, because it will always lie within the bbox of the rest of the path
- if ( iter != end() ) {
- for ( ++iter; iter != end() ; ++iter ) {
- bounds.unionWith(iter->boundsExact());
+Piecewise<D2<SBasis> > Path::toPwSb() const
+{
+ Piecewise<D2<SBasis> > ret;
+ ret.push_cut(0);
+ unsigned i = 1;
+ bool degenerate = true;
+ // pw<d2<>> is always open. so if path is closed, add closing segment as well to pwd2.
+ for (const_iterator it = begin(); it != end_default(); ++it) {
+ if (!it->isDegenerate()) {
+ ret.push(it->toSBasis(), i++);
+ degenerate = false;
+ }
}
- }
- return bounds;
+ if (degenerate) {
+ // if path only contains degenerate curves, no second cut is added
+ // so we need to create at least one segment manually
+ ret = Piecewise<D2<SBasis> >(initialPoint());
+ }
+ return ret;
}
-template<typename iter>
+template <typename iter>
iter inc(iter const &x, unsigned n) {
- iter ret = x;
- for(unsigned i = 0; i < n; i++)
- ret++;
- return ret;
-}
-
-Path &Path::operator*=(Affine const &m) {
- unshare();
- Sequence::iterator last = get_curves().end() - 1;
- Sequence::iterator it;
- Point prev;
- for (it = get_curves().begin() ; it != last ; ++it) {
- *it = boost::shared_ptr<Curve>((*it)->transformed(m));
- if ( it != get_curves().begin() && (*it)->initialPoint() != prev ) {
- THROW_CONTINUITYERROR();
- }
- prev = (*it)->finalPoint();
- }
- for ( int i = 0 ; i < 2 ; ++i ) {
- final_->setPoint(i, (*final_)[i] * m);
- }
- if (get_curves().size() > 1) {
- if ( front().initialPoint() != initialPoint() || back().finalPoint() != finalPoint() ) {
- THROW_CONTINUITYERROR();
- }
- }
- return *this;
-}
-
-Path &Path::operator*=(Translate const &m) {
-/* Somehow there is something wrong here, LPE Construct grid fails with this code
- unshare();
- Sequence::iterator last = get_curves().end() - 1;
- Sequence::iterator it;
- Point prev;
- for (it = get_curves().begin() ; it != last ; ++it) {
- // *(const_cast<Curve*>(&**it)) *= m;
- const_cast<Curve*>(it->get())->operator*=(m);
- if ( it != get_curves().begin() && (*it)->initialPoint() != prev ) {
- THROW_CONTINUITYERROR();
- }
- prev = (*it)->finalPoint();
- }
- for ( int i = 0 ; i < 2 ; ++i ) {
- final_->setPoint(i, (*final_)[i] + m.vector());
- }
- if (get_curves().size() > 1) {
- if ( front().initialPoint() != initialPoint() || back().finalPoint() != finalPoint() ) {
- THROW_CONTINUITYERROR();
- }
- }
- return *this;
-*/
- return this->operator*=(static_cast<Affine>(m));
-}
-
-std::vector<double>
-Path::allNearestPoints(Point const& _point, double from, double to) const
-{
- using std::swap;
-
- if ( from > to ) swap(from, to);
- const Path& _path = *this;
- unsigned int sz = _path.size();
- if ( _path.closed() ) ++sz;
- if ( from < 0 || to > sz )
- {
- THROW_RANGEERROR("[from,to] interval out of bounds");
- }
- double sif, st = modf(from, &sif);
- double eif, et = modf(to, &eif);
- unsigned int si = static_cast<unsigned int>(sif);
- unsigned int ei = static_cast<unsigned int>(eif);
- if ( si == sz )
- {
- --si;
- st = 1;
- }
- if ( ei == sz )
- {
- --ei;
- et = 1;
- }
- if ( si == ei )
- {
- std::vector<double> all_nearest =
- _path[si].allNearestPoints(_point, st, et);
- for ( unsigned int i = 0; i < all_nearest.size(); ++i )
- {
- all_nearest[i] = si + all_nearest[i];
- }
- return all_nearest;
- }
- std::vector<double> all_t;
- std::vector< std::vector<double> > all_np;
- all_np.push_back( _path[si].allNearestPoints(_point, st) );
- std::vector<unsigned int> ni;
- ni.push_back(si);
- double dsq;
- double mindistsq
- = distanceSq( _point, _path[si].pointAt( all_np.front().front() ) );
- Rect bb(Geom::Point(0,0),Geom::Point(0,0));
- for ( unsigned int i = si + 1; i < ei; ++i )
- {
- bb = (_path[i].boundsFast());
- dsq = distanceSq(_point, bb);
- if ( mindistsq < dsq ) continue;
- all_t = _path[i].allNearestPoints(_point);
- dsq = distanceSq( _point, _path[i].pointAt( all_t.front() ) );
- if ( mindistsq > dsq )
- {
- all_np.clear();
- all_np.push_back(all_t);
- ni.clear();
- ni.push_back(i);
- mindistsq = dsq;
- }
- else if ( mindistsq == dsq )
- {
- all_np.push_back(all_t);
- ni.push_back(i);
- }
- }
- bb = (_path[ei].boundsFast());
- dsq = distanceSq(_point, bb);
- if ( mindistsq >= dsq )
- {
- all_t = _path[ei].allNearestPoints(_point, 0, et);
- dsq = distanceSq( _point, _path[ei].pointAt( all_t.front() ) );
- if ( mindistsq > dsq )
- {
- for ( unsigned int i = 0; i < all_t.size(); ++i )
- {
- all_t[i] = ei + all_t[i];
- }
- return all_t;
- }
- else if ( mindistsq == dsq )
- {
- all_np.push_back(all_t);
- ni.push_back(ei);
- }
- }
- std::vector<double> all_nearest;
- for ( unsigned int i = 0; i < all_np.size(); ++i )
- {
- for ( unsigned int j = 0; j < all_np[i].size(); ++j )
- {
- all_nearest.push_back( ni[i] + all_np[i][j] );
- }
- }
- all_nearest.erase(std::unique(all_nearest.begin(), all_nearest.end()),
- all_nearest.end());
- return all_nearest;
-}
-
-std::vector<double>
-Path::nearestPointPerCurve(Point const& _point) const
-{
- //return a single nearest point for each curve in this path
- std::vector<double> np;
- for (const_iterator it = begin() ; it != end_default() ; ++it)
- //for (std::vector<Path>::const_iterator it = _path.begin(); it != _path.end(), ++it){
- {
- np.push_back(it->nearestPoint(_point));
- }
- return np;
-}
-
-double Path::nearestPoint(Point const &_point, double from, double to, double *distance_squared) const
-{
- using std::swap;
-
- if ( from > to ) swap(from, to);
- const Path& _path = *this;
- unsigned int sz = _path.size();
- if ( _path.closed() ) ++sz;
- if ( from < 0 || to > sz )
- {
- THROW_RANGEERROR("[from,to] interval out of bounds");
- }
- double sif, st = modf(from, &sif);
- double eif, et = modf(to, &eif);
- unsigned int si = static_cast<unsigned int>(sif);
- unsigned int ei = static_cast<unsigned int>(eif);
- if(sz == 0) {// naked moveto
- if (distance_squared != NULL)
- *distance_squared = distanceSq(_point, _path.initialPoint());
- return 0;
- }
- if ( si == sz )
- {
- --si;
- st = 1;
- }
- if ( ei == sz )
- {
- --ei;
- et = 1;
- }
- if ( si == ei )
- {
- double nearest = _path[si].nearestPoint(_point, st, et);
- if (distance_squared != NULL)
- *distance_squared = distanceSq(_point, _path[si].pointAt(nearest));
- return si + nearest;
- }
-
- double t;
- double nearest = _path[si].nearestPoint(_point, st);
- unsigned int ni = si;
- double dsq;
- double mindistsq = distanceSq(_point, _path[si].pointAt(nearest));
- for ( unsigned int i = si + 1; i < ei; ++i )
- {
- Rect bb = (_path[i].boundsFast());
- dsq = distanceSq(_point, bb);
- if ( mindistsq <= dsq ) continue;
- t = _path[i].nearestPoint(_point);
- dsq = distanceSq(_point, _path[i].pointAt(t));
- if ( mindistsq > dsq )
- {
- nearest = t;
- ni = i;
- mindistsq = dsq;
- }
- }
- Rect bb = (_path[ei].boundsFast());
- dsq = distanceSq(_point, bb);
- if ( mindistsq > dsq )
- {
- t = _path[ei].nearestPoint(_point, 0, et);
- dsq = distanceSq(_point, _path[ei].pointAt(t));
- if ( mindistsq > dsq )
- {
- nearest = t;
- ni = ei;
- mindistsq = dsq;
- }
- }
-
- if (distance_squared != NULL)
- *distance_squared = mindistsq;
-
- return ni + nearest;
-}
-
-void Path::appendPortionTo(Path &ret, double from, double to) const {
- if (!(from >= 0 && to >= 0)) {
- THROW_RANGEERROR("from and to must be >=0 in Path::appendPortionTo");
- }
- if(to == 0) to = size()+0.999999;
- if(from == to) { return; }
- double fi, ti;
- double ff = modf(from, &fi), tf = modf(to, &ti);
- if(tf == 0) { ti--; tf = 1; }
- const_iterator fromi = inc(begin(), (unsigned)fi);
- if(fi == ti && from < to) {
- Curve *v = fromi->portion(ff, tf);
- ret.append(*v, STITCH_DISCONTINUOUS);
- delete v;
- return;
- }
- const_iterator toi = inc(begin(), (unsigned)ti);
- if(ff != 1.) {
- Curve *fromv = fromi->portion(ff, 1.);
- //fromv->setInitial(ret.finalPoint());
- ret.append(*fromv, STITCH_DISCONTINUOUS);
- delete fromv;
- }
- if(from >= to) {
- const_iterator ender = end();
- if(ender->initialPoint() == ender->finalPoint()) ++ender;
- ret.insert(ret.end(), ++fromi, ender, STITCH_DISCONTINUOUS);
- ret.insert(ret.end(), begin(), toi, STITCH_DISCONTINUOUS);
- } else {
- ret.insert(ret.end(), ++fromi, toi, STITCH_DISCONTINUOUS);
- }
- Curve *tov = toi->portion(0., tf);
- ret.append(*tov, STITCH_DISCONTINUOUS);
- delete tov;
-}
-
-void Path::do_update(Sequence::iterator first_replaced,
- Sequence::iterator last_replaced,
- Sequence::iterator first,
- Sequence::iterator last)
-{
- // note: modifies the contents of [first,last)
- check_continuity(first_replaced, last_replaced, first, last);
- if ( ( last - first ) == ( last_replaced - first_replaced ) ) {
- std::copy(first, last, first_replaced);
- } else {
- // this approach depends on std::vector's behavior WRT iterator stability
- get_curves().erase(first_replaced, last_replaced);
- get_curves().insert(first_replaced, first, last);
- }
-
- if ( get_curves().front().get() != final_ ) {
- final_->setPoint(0, back().finalPoint());
- final_->setPoint(1, front().initialPoint());
- }
-}
-
-void Path::do_append(Curve *c) {
- if ( get_curves().front().get() == final_ ) {
- final_->setPoint(1, c->initialPoint());
- } else {
- if (c->initialPoint() != finalPoint()) {
- THROW_CONTINUITYERROR();
- }
- }
- get_curves().insert(get_curves().end()-1, boost::shared_ptr<Curve>(c));
- final_->setPoint(0, c->finalPoint());
-}
-
-void Path::stitch(Sequence::iterator first_replaced,
- Sequence::iterator last_replaced,
- Sequence &source)
-{
- if (!source.empty()) {
- if ( first_replaced != get_curves().begin() ) {
- if ( (*first_replaced)->initialPoint() != source.front()->initialPoint() ) {
- Curve *stitch = new StitchSegment((*first_replaced)->initialPoint(),
- source.front()->initialPoint());
- source.insert(source.begin(), boost::shared_ptr<Curve>(stitch));
- }
- }
- if ( last_replaced != (get_curves().end()-1) ) {
- if ( (*last_replaced)->finalPoint() != source.back()->finalPoint() ) {
- Curve *stitch = new StitchSegment(source.back()->finalPoint(),
- (*last_replaced)->finalPoint());
- source.insert(source.end(), boost::shared_ptr<Curve>(stitch));
- }
- }
- } else if ( first_replaced != last_replaced && first_replaced != get_curves().begin() && last_replaced != get_curves().end()-1) {
- if ( (*first_replaced)->initialPoint() != (*(last_replaced-1))->finalPoint() ) {
- Curve *stitch = new StitchSegment((*(last_replaced-1))->finalPoint(),
- (*first_replaced)->initialPoint());
- source.insert(source.begin(), boost::shared_ptr<Curve>(stitch));
- }
- }
-}
-
-void Path::check_continuity(Sequence::iterator first_replaced,
- Sequence::iterator last_replaced,
- Sequence::iterator first,
- Sequence::iterator last)
-{
- if ( first != last ) {
- if ( first_replaced != get_curves().begin() ) {
- if ( (*first_replaced)->initialPoint() != (*first)->initialPoint() ) {
- THROW_CONTINUITYERROR();
- }
+ iter ret = x;
+ for (unsigned i = 0; i < n; i++)
+ ret++;
+ return ret;
+}
+
+bool Path::operator==(Path const &other) const
+{
+ if (this == &other)
+ return true;
+ if (_closed != other._closed)
+ return false;
+ return *_curves == *other._curves;
+}
+
+Path &Path::operator*=(Affine const &m)
+{
+ _unshare();
+ Sequence::iterator last = _curves->end() - 1;
+ Sequence::iterator it;
+
+ for (it = _curves->begin(); it != last; ++it) {
+ it->transform(m);
+ }
+ _closing_seg->transform(m);
+ checkContinuity();
+ return *this;
+}
+
+void Path::start(Point const &p) {
+ if (_curves->size() > 1) {
+ clear();
+ }
+ _closing_seg->setInitial(p);
+ _closing_seg->setFinal(p);
+}
+
+Interval Path::timeRange() const
+{
+ Interval ret(0, size_default());
+ return ret;
+}
+
+Curve const &Path::curveAt(Coord t, Coord *rest) const
+{
+ Position pos = _getPosition(t);
+ if (rest) {
+ *rest = pos.t;
+ }
+ return at(pos.curve_index);
+}
+
+Point Path::pointAt(Coord t) const
+{
+ return pointAt(_getPosition(t));
+}
+
+Coord Path::valueAt(Coord t, Dim2 d) const
+{
+ return valueAt(_getPosition(t), d);
+}
+
+Curve const &Path::curveAt(Position const &pos) const
+{
+ return at(pos.curve_index);
+}
+Point Path::pointAt(Position const &pos) const
+{
+ return at(pos.curve_index).pointAt(pos.t);
+}
+Coord Path::valueAt(Position const &pos, Dim2 d) const
+{
+ return at(pos.curve_index).valueAt(pos.t, d);
+}
+
+std::vector<PathPosition> Path::roots(Coord v, Dim2 d) const
+{
+ std::vector<PathPosition> res;
+ for (unsigned i = 0; i <= size(); i++) {
+ std::vector<Coord> temp = (*this)[i].roots(v, d);
+ for (unsigned j = 0; j < temp.size(); j++)
+ res.push_back(PathPosition(i, temp[j]));
+ }
+ return res;
+}
+
+std::vector<PathIntersection> Path::intersect(Path const &other, Coord precision) const
+{
+ std::vector<PathIntersection> result;
+
+ // TODO: sweepline optimization
+ // TODO: remove multiple intersections within precision of each other?
+ for (size_type i = 0; i < size(); ++i) {
+ for (size_type j = 0; j < other.size(); ++j) {
+ std::vector<CurveIntersection> cx = (*this)[i].intersect(other[j], precision);
+ for (std::size_t ci = 0; ci < cx.size(); ++ci) {
+ PathPosition a(i, cx[ci].first), b(j, cx[ci].second);
+ PathIntersection px(a, b, cx[ci].point());
+ result.push_back(px);
+ }
+ }
+ }
+
+ return result;
+}
+
+int Path::winding(Point const &p) const {
+ int wind = 0;
+
+ /* To handle all the edge cases, we consider the maximum Y edge of the bounding box
+ * as not included in box. This way paths that contain linear horizontal
+ * segments will be treated correctly. */
+ for (const_iterator i = begin(); i != end_closed(); ++i) {
+ Rect bounds = i->boundsFast();
+
+ if (bounds.height() == 0) continue;
+ if (p[X] > bounds.right() || !bounds[Y].lowerContains(p[Y])) {
+ // Ray doesn't intersect bbox, so we ignore this segment
+ continue;
+ }
+
+ if (p[X] < bounds.left()) {
+ /* Ray intersects the curve's bbox, but the point is outside it.
+ * The winding contribution is exactly the same as that
+ * of a linear segment with the same initial and final points. */
+ Point ip = i->initialPoint();
+ Point fp = i->finalPoint();
+ Rect eqbox(ip, fp);
+
+ if (eqbox[Y].lowerContains(p[Y])) {
+ /* The ray intersects the equivalent linear segment.
+ * Determine winding contribution based on its derivative. */
+ if (ip[Y] < fp[Y]) {
+ wind += 1;
+ } else if (ip[Y] > fp[Y]) {
+ wind -= 1;
+ } else {
+ // should never happen, because bounds.height() was not zero
+ assert(false);
+ }
+ }
+ } else {
+ // point is inside bbox
+ wind += i->winding(p);
+ }
}
- if ( last_replaced != (get_curves().end()-1) ) {
- if ( (*(last_replaced-1))->finalPoint() != (*(last-1))->finalPoint() ) {
+ return wind;
+}
+
+std::vector<double> Path::allNearestTimes(Point const &_point, double from, double to) const
+{
+ // TODO from and to are not used anywhere.
+ // rewrite this to simplify.
+ using std::swap;
+
+ if (from > to)
+ swap(from, to);
+ const Path &_path = *this;
+ unsigned int sz = _path.size();
+ if (_path.closed())
+ ++sz;
+ if (from < 0 || to > sz) {
+ THROW_RANGEERROR("[from,to] interval out of bounds");
+ }
+ double sif, st = modf(from, &sif);
+ double eif, et = modf(to, &eif);
+ unsigned int si = static_cast<unsigned int>(sif);
+ unsigned int ei = static_cast<unsigned int>(eif);
+ if (si == sz) {
+ --si;
+ st = 1;
+ }
+ if (ei == sz) {
+ --ei;
+ et = 1;
+ }
+ if (si == ei) {
+ std::vector<double> all_nearest = _path[si].allNearestTimes(_point, st, et);
+ for (unsigned int i = 0; i < all_nearest.size(); ++i) {
+ all_nearest[i] = si + all_nearest[i];
+ }
+ return all_nearest;
+ }
+ std::vector<double> all_t;
+ std::vector<std::vector<double> > all_np;
+ all_np.push_back(_path[si].allNearestTimes(_point, st));
+ std::vector<unsigned int> ni;
+ ni.push_back(si);
+ double dsq;
+ double mindistsq = distanceSq(_point, _path[si].pointAt(all_np.front().front()));
+ Rect bb(Geom::Point(0, 0), Geom::Point(0, 0));
+ for (unsigned int i = si + 1; i < ei; ++i) {
+ bb = (_path[i].boundsFast());
+ dsq = distanceSq(_point, bb);
+ if (mindistsq < dsq)
+ continue;
+ all_t = _path[i].allNearestTimes(_point);
+ dsq = distanceSq(_point, _path[i].pointAt(all_t.front()));
+ if (mindistsq > dsq) {
+ all_np.clear();
+ all_np.push_back(all_t);
+ ni.clear();
+ ni.push_back(i);
+ mindistsq = dsq;
+ } else if (mindistsq == dsq) {
+ all_np.push_back(all_t);
+ ni.push_back(i);
+ }
+ }
+ bb = (_path[ei].boundsFast());
+ dsq = distanceSq(_point, bb);
+ if (mindistsq >= dsq) {
+ all_t = _path[ei].allNearestTimes(_point, 0, et);
+ dsq = distanceSq(_point, _path[ei].pointAt(all_t.front()));
+ if (mindistsq > dsq) {
+ for (unsigned int i = 0; i < all_t.size(); ++i) {
+ all_t[i] = ei + all_t[i];
+ }
+ return all_t;
+ } else if (mindistsq == dsq) {
+ all_np.push_back(all_t);
+ ni.push_back(ei);
+ }
+ }
+ std::vector<double> all_nearest;
+ for (unsigned int i = 0; i < all_np.size(); ++i) {
+ for (unsigned int j = 0; j < all_np[i].size(); ++j) {
+ all_nearest.push_back(ni[i] + all_np[i][j]);
+ }
+ }
+ all_nearest.erase(std::unique(all_nearest.begin(), all_nearest.end()), all_nearest.end());
+ return all_nearest;
+}
+
+std::vector<Coord> Path::nearestTimePerCurve(Point const &p) const
+{
+ // return a single nearest time for each curve in this path
+ std::vector<Coord> np;
+ for (const_iterator it = begin(); it != end_default(); ++it) {
+ np.push_back(it->nearestTime(p));
+ }
+ return np;
+}
+
+Coord Path::nearestTime(Point const &p, Coord *dist) const
+{
+ Position pos = nearestPosition(p, dist);
+ return pos.curve_index + pos.t;
+}
+
+PathPosition Path::nearestPosition(Point const &p, Coord *dist) const
+{
+ Coord mindist = std::numeric_limits<Coord>::max();
+ Position ret;
+
+ if (_curves->size() == 1) {
+ // naked moveto
+ ret.curve_index = 0;
+ ret.t = 0;
+ if (dist) {
+ *dist = distance(_closing_seg->initialPoint(), p);
+ }
+ return ret;
+ }
+
+ for (size_type i = 0; i < size_default(); ++i) {
+ Curve const &c = at(i);
+ if (distance(p, c.boundsFast()) >= mindist) continue;
+
+ Coord t = c.nearestTime(p);
+ Coord d = distance(c.pointAt(t), p);
+ if (d < mindist) {
+ mindist = d;
+ ret.curve_index = i;
+ ret.t = t;
+ }
+ }
+ if (dist) {
+ *dist = mindist;
+ }
+
+ return ret;
+}
+
+void Path::appendPortionTo(Path &ret, double from, double to) const
+{
+ if (!(from >= 0 && to >= 0)) {
+ THROW_RANGEERROR("from and to must be >=0 in Path::appendPortionTo");
+ }
+ if (to == 0)
+ to = size() + 0.999999;
+ if (from == to) {
+ return;
+ }
+ double fi, ti;
+ double ff = modf(from, &fi), tf = modf(to, &ti);
+ if (tf == 0) {
+ ti--;
+ tf = 1;
+ }
+ const_iterator fromi = inc(begin(), (unsigned)fi);
+ if (fi == ti && from < to) {
+ ret.append(fromi->portion(ff, tf));
+ return;
+ }
+ const_iterator toi = inc(begin(), (unsigned)ti);
+ if (ff != 1.) {
+ // fromv->setInitial(ret.finalPoint());
+ ret.append(fromi->portion(ff, 1.));
+ }
+ if (from >= to) {
+ const_iterator ender = end();
+ if (ender->initialPoint() == ender->finalPoint())
+ ++ender;
+ ret.insert(ret.end(), ++fromi, ender);
+ ret.insert(ret.end(), begin(), toi);
+ } else {
+ ret.insert(ret.end(), ++fromi, toi);
+ }
+ ret.append(toi->portion(0., tf));
+}
+
+void Path::appendPortionTo(Path &target, PathInterval const &ival,
+ boost::optional<Point> const &p_from, boost::optional<Point> const &p_to) const
+{
+ assert(ival.pathSize() == size_closed());
+
+ if (ival.isDegenerate()) {
+ Point stitch_to = p_from ? *p_from : pointAt(ival.from());
+ target.stitchTo(stitch_to);
+ return;
+ }
+
+ Position const &from = ival.from(), &to = ival.to();
+
+ bool reverse = ival.reverse();
+ int di = reverse ? -1 : 1;
+ size_type s = size_closed();
+
+ if (!ival.crossesStart() && from.curve_index == to.curve_index) {
+ Curve *c = (*this)[from.curve_index].portion(from.t, to.t);
+ if (p_from) {
+ c->setInitial(*p_from);
+ }
+ if (p_to) {
+ c->setFinal(*p_to);
+ }
+ target.append(c);
+ } else {
+ Curve *c_first = (*this)[from.curve_index].portion(from.t, reverse ? 0 : 1);
+ if (p_from) {
+ c_first->setInitial(*p_from);
+ }
+ target.append(c_first);
+
+ for (size_type i = (from.curve_index + s + di) % s; i != to.curve_index;
+ i = (i + s + di) % s)
+ {
+ if (reverse) {
+ target.append((*this)[i].reverse());
+ } else {
+ target.append((*this)[i].duplicate());
+ }
+ }
+
+ Curve *c_last = (*this)[to.curve_index].portion(reverse ? 1 : 0, to.t);
+ if (p_to) {
+ c_last->setFinal(*p_to);
+ }
+ target.append(c_last);
+ }
+}
+
+Path Path::reversed() const
+{
+ typedef std::reverse_iterator<Sequence::const_iterator> RIter;
+
+ Path ret;
+ ret._curves->pop_back();
+ RIter iter(_curves->end()), rend(_curves->begin());
+ for (; iter != rend; ++iter) {
+ ret._curves->push_back(iter->reverse());
+ }
+ ret._closing_seg = static_cast<ClosingSegment *>(ret._closing_seg->reverse());
+ ret._curves->push_back(ret._closing_seg);
+ return ret;
+}
+
+
+void Path::insert(iterator pos, Curve const &curve)
+{
+ _unshare();
+ Sequence::iterator seq_pos(seq_iter(pos));
+ Sequence source;
+ source.push_back(curve.duplicate());
+ do_update(seq_pos, seq_pos, source);
+}
+
+void Path::erase(iterator pos)
+{
+ _unshare();
+ Sequence::iterator seq_pos(seq_iter(pos));
+
+ Sequence stitched;
+ do_update(seq_pos, seq_pos + 1, stitched);
+}
+
+void Path::erase(iterator first, iterator last)
+{
+ _unshare();
+ Sequence::iterator seq_first = seq_iter(first);
+ Sequence::iterator seq_last = seq_iter(last);
+
+ Sequence stitched;
+ do_update(seq_first, seq_last, stitched);
+}
+
+void Path::stitchTo(Point const &p)
+{
+ if (!empty() && _closing_seg->initialPoint() != p) {
+ if (_exception_on_stitch) {
+ THROW_CONTINUITYERROR();
+ }
+ _unshare();
+ do_append(new StitchSegment(_closing_seg->initialPoint(), p));
+ }
+}
+
+void Path::replace(iterator replaced, Curve const &curve)
+{
+ replace(replaced, replaced + 1, curve);
+}
+
+void Path::replace(iterator first_replaced, iterator last_replaced, Curve const &curve)
+{
+ _unshare();
+ Sequence::iterator seq_first_replaced(seq_iter(first_replaced));
+ Sequence::iterator seq_last_replaced(seq_iter(last_replaced));
+ Sequence source(1);
+ source.push_back(curve.duplicate());
+
+ do_update(seq_first_replaced, seq_last_replaced, source);
+}
+
+void Path::replace(iterator replaced, Path const &path)
+{
+ replace(replaced, path.begin(), path.end());
+}
+
+void Path::replace(iterator first, iterator last, Path const &path)
+{
+ replace(first, last, path.begin(), path.end());
+}
+
+// replace curves between first and last with contents of source,
+//
+void Path::do_update(Sequence::iterator first, Sequence::iterator last, Sequence &source)
+{
+ // TODO: handle cases where first > last in closed paths?
+ bool last_beyond_closing_segment = (last == _curves->end());
+
+ // special case:
+ // if do_update replaces the closing segment, we have to regenerate it
+ if (source.empty()) {
+ if (first == last) return; // nothing to do
+
+ // only removing some segments
+ if ((!_closed && first == _curves->begin()) || (!_closed && last == _curves->end() - 1) || last_beyond_closing_segment) {
+ // just adjust the closing segment
+ // do nothing
+ } else if (first->initialPoint() != (last - 1)->finalPoint()) {
+ if (_exception_on_stitch) {
+ THROW_CONTINUITYERROR();
+ }
+ source.push_back(new StitchSegment(first->initialPoint(), (last - 1)->finalPoint()));
+ }
+ } else {
+ // replacing
+ if (first == _curves->begin() && last == _curves->end()) {
+ // special case: replacing everything should work the same in open and closed curves
+ _curves->erase(_curves->begin(), _curves->end() - 1);
+ _closing_seg->setFinal(source.front().initialPoint());
+ _closing_seg->setInitial(source.back().finalPoint());
+ _curves->transfer(_curves->begin(), source.begin(), source.end(), source);
+ return;
+ }
+
+ // stitch in front
+ if (!_closed && first == _curves->begin()) {
+ // not necessary to stitch in front
+ } else if (first->initialPoint() != source.front().initialPoint()) {
+ if (_exception_on_stitch) {
+ THROW_CONTINUITYERROR();
+ }
+ source.insert(source.begin(), new StitchSegment(first->initialPoint(), source.front().initialPoint()));
+ }
+
+ // stitch at the end
+ if ((!_closed && last == _curves->end() - 1) || last_beyond_closing_segment) {
+ // repurpose the closing segment as the stitch segment
+ // do nothing
+ } else if (source.back().finalPoint() != (last - 1)->finalPoint()) {
+ if (_exception_on_stitch) {
+ THROW_CONTINUITYERROR();
+ }
+ source.push_back(new StitchSegment(source.back().finalPoint(), (last - 1)->finalPoint()));
+ }
+ }
+
+ // do not erase the closing segment, adjust it instead
+ if (last_beyond_closing_segment) {
+ --last;
+ }
+ _curves->erase(first, last);
+ _curves->transfer(first, source.begin(), source.end(), source);
+
+ // adjust closing segment
+ if (size_open() == 0) {
+ _closing_seg->setFinal(_closing_seg->initialPoint());
+ } else {
+ _closing_seg->setInitial(back_open().finalPoint());
+ _closing_seg->setFinal(front().initialPoint());
+ }
+
+ checkContinuity();
+}
+
+void Path::do_append(Curve *c)
+{
+ if (&_curves->front() == _closing_seg) {
+ _closing_seg->setFinal(c->initialPoint());
+ } else {
+ // if we can't freely move the closing segment, we check whether
+ // the new curve connects with the last non-closing curve
+ if (c->initialPoint() != _closing_seg->initialPoint()) {
+ THROW_CONTINUITYERROR();
+ }
+ }
+ _curves->insert(_curves->end() - 1, c);
+ _closing_seg->setInitial(c->finalPoint());
+}
+
+void Path::checkContinuity() const
+{
+ Sequence::const_iterator i = _curves->begin(), j = _curves->begin();
+ ++j;
+ for (; j != _curves->end(); ++i, ++j) {
+ if (i->finalPoint() != j->initialPoint()) {
+ THROW_CONTINUITYERROR();
+ }
+ }
+ if (_curves->front().initialPoint() != _curves->back().finalPoint()) {
THROW_CONTINUITYERROR();
- }
}
- } else if ( first_replaced != last_replaced && first_replaced != get_curves().begin() && last_replaced != get_curves().end()-1) {
- if ( (*first_replaced)->initialPoint() != (*(last_replaced-1))->finalPoint() ) {
- THROW_CONTINUITYERROR();
+}
+
+PathPosition Path::_getPosition(Coord t) const
+{
+ size_type sz = size_default();
+ if (t < 0 || t > sz) {
+ THROW_RANGEERROR("parameter t out of bounds");
+ }
+
+ Position ret;
+ Coord k;
+ ret.t = modf(t, &k);
+ ret.curve_index = k;
+ if (ret.curve_index == sz) {
+ --ret.curve_index;
+ ret.t = 1;
+ }
+ return ret;
+}
+
+Piecewise<D2<SBasis> > paths_to_pw(PathVector const &paths)
+{
+ Piecewise<D2<SBasis> > ret = paths[0].toPwSb();
+ for (unsigned i = 1; i < paths.size(); i++) {
+ ret.concat(paths[i].toPwSb());
}
- }
+ return ret;
}
} // end namespace Geom
diff --git a/src/2geom/path.h b/src/2geom/path.h
index 28d2a25e4..1940aa580 100644
--- a/src/2geom/path.h
+++ b/src/2geom/path.h
@@ -1,43 +1,47 @@
-/**
- * \file
- * \brief Path - Series of continuous curves
+/** @file
+ * @brief Path - a sequence of contiguous curves
*//*
- * Authors:
- * MenTaLguY <mental@rydia.net>
- * Marco Cecchetti <mrcekets at gmail.com>
- *
- * Copyright 2007-2008 Authors
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- */
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright 2007-2014 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
#ifndef LIB2GEOM_SEEN_PATH_H
#define LIB2GEOM_SEEN_PATH_H
#include <iterator>
#include <algorithm>
+#include <iostream>
+#include <boost/operators.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/shared_ptr.hpp>
+#include <2geom/intersection.h>
#include <2geom/curve.h>
#include <2geom/bezier-curve.h>
#include <2geom/transforms.h>
@@ -45,656 +49,757 @@
namespace Geom {
class Path;
+class ConvexHull;
namespace PathInternal {
-typedef std::vector<boost::shared_ptr<Curve const> > Sequence;
+typedef boost::ptr_vector<Curve> Sequence;
+
+template <typename P>
+class BaseIterator
+ : public boost::random_access_iterator_helper
+ < BaseIterator<P>
+ , Curve const
+ , std::ptrdiff_t
+ , Curve const *
+ , Curve const &
+ >
+{
+ protected:
+ BaseIterator(P &p, unsigned i) : path(&p), index(i) {}
+ // default copy, default assign
+ typedef BaseIterator<P> Self;
-template <typename C, typename P>
-class BaseIterator {
-protected:
- BaseIterator() : path(NULL), index(0) {}
- BaseIterator(P *p, unsigned i) : path(p), index(i) {}
- // default copy, default assign
+ public:
+ BaseIterator() : path(NULL), index(0) {}
-public:
- bool operator==(BaseIterator const &other) {
- return path == other.path && index == other.index;
- }
- bool operator!=(BaseIterator const &other) {
- return path != other.path || index != other.index;
- }
-
- Curve const &operator*() const { return (*path)[index]; }
- Curve const *operator->() const { return &(*path)[index]; }
- boost::shared_ptr<Curve const> get_ref() const {
- return path->get_ref_at_index(index);
- }
-
- C &operator++() {
- ++index;
- return static_cast<C &>(*this);
- }
- C operator++(int) {
- C old(static_cast<C &>(*this));
- ++(*this);
- return old;
- }
-
- C &operator--() {
- --index;
- return static_cast<C &>(*this);
- }
- C operator--(int) {
- C old(static_cast<C &>(*this));
- --(*this);
- return old;
- }
+ bool operator<(BaseIterator const &other) const {
+ return path == other.path && index < other.index;
+ }
+ bool operator==(BaseIterator const &other) const {
+ return path == other.path && index == other.index;
+ }
+ Curve const &operator*() const {
+ return (*path)[index];
+ }
-private:
- P *path;
- unsigned index;
+ Self &operator++() {
+ ++index;
+ return *this;
+ }
+ Self &operator--() {
+ --index;
+ return *this;
+ }
+ Self &operator+=(std::ptrdiff_t d) {
+ index += d;
+ return *this;
+ }
+ Self &operator-=(std::ptrdiff_t d) {
+ index -= d;
+ return *this;
+ }
- friend class ::Geom::Path;
+ private:
+ P *path;
+ unsigned index;
+
+ friend class ::Geom::Path;
};
-class ConstIterator : public BaseIterator<ConstIterator, Path const> {
-public:
- typedef BaseIterator<ConstIterator, Path const> Base;
+}
- ConstIterator() : Base() {}
- // default copy, default assign
+/** @brief Position (generalized time value) in the path.
+ *
+ * This class exists because when mapping the range of multiple curves onto the same interval
+ * as the curve index, we lose some precision. For instance, a path with 16 curves will
+ * have 4 bits less precision than a path with 1 curve. If you need high precision results
+ * in long paths, either use this class and related methods instead of the standard methods
+ * pointAt(), nearestTime() and so on, or use curveAt() to first obtain the curve, then
+ * call the method again to obtain a high precision result.
+ *
+ * @ingroup Paths */
+struct PathPosition
+ : boost::totally_ordered<PathPosition>
+{
+ typedef PathInternal::Sequence::size_type size_type;
-private:
- ConstIterator(Path const &p, unsigned i) : Base(&p, i) {}
- friend class ::Geom::Path;
+ Coord t; ///< Time value in the curve
+ size_type curve_index; ///< Index of the curve in the path
+
+ PathPosition() : t(0), curve_index(0) {}
+ PathPosition(size_type idx, Coord tval) : t(tval), curve_index(idx) {}
+
+ bool operator<(PathPosition const &other) const {
+ if (curve_index < other.curve_index) return true;
+ if (curve_index == other.curve_index) {
+ return t < other.t;
+ }
+ return false;
+ }
+ bool operator==(PathPosition const &other) const {
+ return curve_index == other.curve_index && t == other.t;
+ }
+ /// Convert positions at or beyond 1 to 0 on the next curve.
+ void normalizeForward(size_type path_size) {
+ if (t >= 1) {
+ curve_index = (curve_index + 1) % path_size;
+ t = 0;
+ }
+ }
+ /// Convert positions at or before 0 to 1 on the previous curve.
+ void normalizeBackward(size_type path_size) {
+ if (t <= 0) {
+ curve_index = (curve_index - 1) % path_size;
+ t = 1;
+ }
+ }
};
-class Iterator : public BaseIterator<Iterator, Path> {
-public:
- typedef BaseIterator<Iterator, Path> Base;
+inline std::ostream &operator<<(std::ostream &os, PathPosition const &pos) {
+ os << pos.curve_index << ": " << pos.t;
+ return os;
+}
- Iterator() : Base() {}
- // default copy, default assign
- operator ConstIterator const &() const {
- return reinterpret_cast<ConstIterator const &>(*this);
- }
+/** @brief Contiguous subset of the path's parameter domain.
+ * This is a directed interval, which allows one to specify any contiguous subset
+ * of the path's domain, including subsets that wrap around the initial point
+ * of the path.
+ * @ingroup Paths */
+class PathInterval {
+public:
+ typedef PathPosition Position;
+ typedef PathInternal::Sequence::size_type size_type;
+
+ /** @brief Default interval.
+ * Default-constructed PathInterval includes only the initial point of the initial segment. */
+ PathInterval();
+
+ /** @brief Construct an interval in the path's parameter domain.
+ * @param from Initial position
+ * @param to Final position
+ * @param cross_start If true, the interval will proceed from the initial to final
+ * position through the initial point of the path, wrapping around the closing segment;
+ * otherwise it will not wrap around the closing segment.
+ * @param path_size Size of the path to which this interval applies, required
+ * to clean up degenerate cases */
+ PathInterval(Position const &from, Position const &to, bool cross_start, size_type path_size);
+
+ /// Get the position of the initial point.
+ Position const &initialPosition() const { return _from; }
+ /// Get the position of the final point.
+ Position const &finalPosition() const { return _to; }
+
+ Position const &from() const { return _from; }
+ Position const &to() const { return _to; }
+
+ /// Check whether the interval has only one point.
+ bool isDegenerate() const { return _from == _to; }
+ /// True if the interval goes in the direction of decreasing time values.
+ bool reverse() const { return _reverse; }
+ /// True if the interior of the interval contains the initial point of the path.
+ bool crossesStart() const { return _cross_start; }
+
+ /// Test a path position for inclusion.
+ bool contains(Position const &pos) const;
+
+ /// Get a position at least @a min_dist away in parameter space from the ends.
+ /// If no such position exists, the middle point between these positions is returned.
+ Position inside(Coord min_dist = EPSILON) const;
+
+ /// Select one of two intervals with given endpoints by parameter direction.
+ static PathInterval from_direction(Position const &from, Position const &to,
+ bool reversed, size_type path_size);
+
+ /// Select one of two intervals with given endpoints by whether it includes the initial point.
+ static PathInterval from_start_crossing(Position const &from, Position const &to,
+ bool cross_start, size_type path_size) {
+ PathInterval result(from, to, cross_start, path_size);
+ return result;
+ }
+
+ size_type pathSize() const { return _path_size; }
private:
- Iterator(Path &p, unsigned i) : Base(&p, i) {}
- friend class ::Geom::Path;
+ Position _from, _to;
+ size_type _path_size;
+ bool _cross_start, _reverse;
};
+/// Create an interval in the direction of increasing time value.
+/// @relates PathInterval
+inline PathInterval forward_interval(PathPosition const &from, PathPosition const &to,
+ PathInterval::size_type path_size)
+{
+ PathInterval result = PathInterval::from_direction(from, to, false, path_size);
+ return result;
+}
+
+/// Create an interval in the direction of decreasing time value.
+/// @relates PathInterval
+inline PathInterval backward_interval(PathPosition const &from, PathPosition const &to,
+ PathInterval::size_type path_size)
+{
+ PathInterval result = PathInterval::from_direction(from, to, true, path_size);
+ return result;
}
-/*
- * Open and closed paths: all paths, whether open or closed, store a final
- * segment which connects the initial and final endpoints of the "real"
- * path data. While similar to the "z" in an SVG path, it exists for
- * both open and closed paths, and is not considered part of the "normal"
- * path data, which is always covered by the range [begin(), end_open()).
- * Conversely, the range [begin(), end_closed()) always contains the "extra"
- * closing segment.
+/// Output an interval in the path's domain.
+/// @relates PathInterval
+inline std::ostream &operator<<(std::ostream &os, PathInterval const &ival) {
+ os << "PathInterval[";
+ if (ival.crossesStart()) {
+ os << ival.from() << " -> 0: 0.0 -> " << ival.to();
+ } else {
+ os << ival.from() << " -> " << ival.to();
+ }
+ os << "]";
+ return os;
+}
+
+typedef Intersection<PathPosition> PathIntersection;
+
+template <>
+struct ShapeTraits<Path> {
+ typedef PathPosition TimeType;
+ typedef PathInterval IntervalType;
+ typedef Path AffineClosureType;
+ typedef PathIntersection IntersectionType;
+};
+
+/** @brief Sequence of contiguous curves, aka spline.
+ *
+ * Path represents a sequence of contiguous curves, also known as a spline.
+ * It corresponds to a "subpath" in SVG terminology. It can represent both
+ * open and closed subpaths. The final point of each curve is exactly
+ * equal to the initial point of the next curve.
+ *
+ * The path always contains a linear closing segment that connects
+ * the final point of the last "real" curve to the initial point of the
+ * first curve. This way the curves form a closed loop even for open paths.
+ * If the closing segment has nonzero length and the path is closed, it is
+ * considered a normal part of the path data. There are three distinct sets
+ * of end iterators one can use to iterate over the segments:
+ *
+ * - Iterating between @a begin() and @a end() will iterate over segments
+ * which are part of the path.
+ * - Iterating between @a begin() and @a end_closed()
+ * will always iterate over a closed loop of segments.
+ * - Iterating between @a begin() and @a end_open() will always skip
+ * the closing segment.
*
- * The only difference between a closed and an open path is whether
- * end_default() returns end_closed() or end_open(). The idea behind this
- * is to let any path be stroked using [begin(), end_default()), and filled
- * using [begin(), end_closed()), without requiring a separate "filled" version
- * of the path to use for filling.
+ * Normally, an exception will be thrown when you try to insert a curve
+ * that makes the path non-continuous. If you are working with unsanitized
+ * curve data, you can call setStitching(true), which will insert line segments
+ * to make the path continuous.
*
- * \invariant : curves_ always contains at least one segment. The last segment
- * is always of type ClosingSegment. All constructors take care of this.
- (curves_.size() > 0) && dynamic_cast<ClosingSegment>(curves_.back())
- */
-class Path {
+ * Internally, Path uses copy-on-write data. This is done for two reasons: first,
+ * copying a Curve requires calling a virtual function, so it's a little more expensive
+ * that normal copying; and second, it reduces the memory cost of copying the path.
+ * Therefore you can return Path and PathVector from functions without worrying
+ * about temporary copies.
+ *
+ * Note that this class cannot represent arbitrary shapes, which may contain holes.
+ * To do that, use PathVector, which is more generic.
+ *
+ * It's not very convenient to create a Path directly. To construct paths more easily,
+ * use PathBuilder.
+ *
+ * @ingroup Paths */
+class Path
+ : boost::equality_comparable1< Path
+ , MultipliableNoncommutative< Path, Affine
+ > >
+{
public:
- typedef PathInternal::Sequence Sequence;
- typedef PathInternal::Iterator iterator;
- typedef PathInternal::ConstIterator const_iterator;
- typedef Sequence::size_type size_type;
- typedef Sequence::difference_type difference_type;
+ typedef PathPosition Position;
+ typedef PathInternal::Sequence Sequence;
+ typedef PathInternal::BaseIterator<Path> iterator;
+ typedef PathInternal::BaseIterator<Path const> const_iterator;
+ typedef Sequence::size_type size_type;
+ typedef Sequence::difference_type difference_type;
+
+ class ClosingSegment : public LineSegment {
+ public:
+ ClosingSegment() : LineSegment() {}
+ ClosingSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
+ virtual Curve *duplicate() const { return new ClosingSegment(*this); }
+ virtual Curve *reverse() const { return new ClosingSegment((*this)[1], (*this)[0]); }
+ };
+
+ class StitchSegment : public LineSegment {
+ public:
+ StitchSegment() : LineSegment() {}
+ StitchSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
+ virtual Curve *duplicate() const { return new StitchSegment(*this); }
+ virtual Curve *reverse() const { return new StitchSegment((*this)[1], (*this)[0]); }
+ };
+
+ // Path(Path const &other) - use default copy constructor
+
+ /// Construct an empty path starting at the specified point.
+ explicit Path(Point const &p = Point())
+ : _curves(new Sequence())
+ , _closing_seg(new ClosingSegment(p, p))
+ , _closed(false)
+ , _exception_on_stitch(true)
+ {
+ _curves->push_back(_closing_seg);
+ }
- class ClosingSegment : public LineSegment {
- public:
- ClosingSegment() : LineSegment() {}
- ClosingSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
- virtual Curve *duplicate() const { return new ClosingSegment(*this); }
- virtual Curve *reverse() const { return new ClosingSegment((*this)[1], (*this)[0]); }
- };
-
- enum Stitching {
- NO_STITCHING=0,
- STITCH_DISCONTINUOUS
- };
-
- class StitchSegment : public LineSegment {
- public:
- StitchSegment() : LineSegment() {}
- StitchSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
- virtual Curve *duplicate() const { return new StitchSegment(*this); }
- virtual Curve *reverse() const { return new StitchSegment((*this)[1], (*this)[0]); }
- };
-
- // Path(Path const &other) - use default copy constructor
-
- explicit Path(Point p=Point())
- : curves_(boost::shared_ptr<Sequence>(new Sequence(1, boost::shared_ptr<Curve>()))),
- final_(new ClosingSegment(p, p)),
- closed_(false)
- {
- get_curves().back() = boost::shared_ptr<Curve>(final_);
- }
-
- Path(const_iterator const &first,
- const_iterator const &last,
- bool closed=false)
- : curves_(boost::shared_ptr<Sequence>(new Sequence(seq_iter(first),
- seq_iter(last)))),
- closed_(closed)
- {
- if (!get_curves().empty()) {
- final_ = new ClosingSegment(get_curves().back()->finalPoint(),
- get_curves().front()->initialPoint());
- } else {
- final_ = new ClosingSegment();
- }
- get_curves().push_back(boost::shared_ptr<Curve>(final_));
- }
-
- virtual ~Path() {}
-
- // Path &operator=(Path const &other) - use default assignment operator
-
- /// \todo Add noexcept specifiers for C++11
- void swap(Path &other) {
- using std::swap;
- swap(other.curves_, curves_);
- swap(other.final_, final_);
- swap(other.closed_, closed_);
- }
- friend inline void swap(Path &a, Path &b) { a.swap(b); }
-
- Curve const &operator[](unsigned i) const { return *get_curves()[i]; }
- Curve const &at_index(unsigned i) const { return *get_curves()[i]; }
- boost::shared_ptr<Curve const> get_ref_at_index(unsigned i) {
- return get_curves()[i];
- }
-
- Curve const &front() const { return *get_curves()[0]; }
- Curve const &back() const { return back_open(); }
- Curve const &back_open() const {
- if (empty()) { THROW_RANGEERROR("Path contains not enough segments"); }
- return *get_curves()[get_curves().size()-2];
- }
- Curve const &back_closed() const { return *get_curves()[get_curves().size()-1]; }
- Curve const &back_default() const {
- return ( closed_ ? back_closed() : back_open() );
- }
-
- const_iterator begin() const { return const_iterator(*this, 0); }
- const_iterator end() const { return const_iterator(*this, size()); }
- iterator begin() { return iterator(*this, 0); }
- iterator end() { return iterator(*this, size()); }
-
- const_iterator end_open() const { return const_iterator(*this, size()); }
- const_iterator end_closed() const { return const_iterator(*this, size()+1); }
- const_iterator end_default() const {
- return ( closed_ ? end_closed() : end_open() );
- }
-
- size_type size_open() const { return get_curves().size()-1; }
- size_type size_closed() const { return get_curves().size(); }
- size_type size_default() const {
- return ( closed_ ? size_closed() : size_open() );
- }
- size_type size() const { return size_open(); }
-
- size_type max_size() const { return get_curves().max_size()-1; }
-
- bool empty() const { return (get_curves().size() == 1); }
- bool closed() const { return closed_; }
- void close(bool closed=true) { closed_ = closed; }
-
- OptRect boundsFast() const;
- OptRect boundsExact() const;
-
- Piecewise<D2<SBasis> > toPwSb() const {
- Piecewise<D2<SBasis> > ret;
- ret.push_cut(0);
- unsigned i = 1;
- bool degenerate = true;
- // pw<d2<>> is always open. so if path is closed, add closing segment as well to pwd2.
- for(const_iterator it = begin(); it != end_default(); ++it) {
- if (!it->isDegenerate()) {
- ret.push(it->toSBasis(), i++);
- degenerate = false;
- }
- }
- if (degenerate) {
- // if path only contains degenerate curves, no second cut is added
- // so we need to create at least one segment manually
- ret = Piecewise<D2<SBasis> >(initialPoint());
- }
- return ret;
- }
-
- bool operator==(Path const &other) const {
- if (this == &other) return true;
- if (closed_ != other.closed_) return false;
- return get_curves() == other.get_curves();
- }
- bool operator!=(Path const &other) const {
- return !( *this == other );
- }
-
- Path operator*(Affine const &m) const {
- Path ret(*this);
- ret *= m;
- return ret;
- }
- Path operator*(Translate const &m) const { // specialization over Affine, for faster computation
- Path ret(*this);
- ret *= m;
- return ret;
- }
-
- Path &operator*=(Affine const &m);
- Path &operator*=(Translate const &m); // specialization over Affine, for faster computation
-
- Point pointAt(double t) const
- {
- unsigned int sz = size();
- if ( closed() ) ++sz;
- if ( t < 0 || t > sz )
- {
- THROW_RANGEERROR("parameter t out of bounds");
- }
- if ( empty() ) return initialPoint(); // naked moveto
- double k, lt = modf(t, &k);
- unsigned int i = static_cast<unsigned int>(k);
- if ( i == sz )
- {
- --i;
- lt = 1;
- }
- return (*this)[i].pointAt(lt);
- }
-
- double valueAt(double t, Dim2 d) const
- {
- unsigned int sz = size();
- if ( closed() ) ++sz;
- if ( t < 0 || t > sz )
- {
- THROW_RANGEERROR("parameter t out of bounds");
- }
- if ( empty() ) return initialPoint()[d]; // naked moveto
- double k, lt = modf(t, &k);
- unsigned int i = static_cast<unsigned int>(k);
- if ( i == sz )
- {
- --i;
- lt = 1;
- }
- return (*this)[i].valueAt(lt, d);
- }
-
-
- Point operator() (double t) const
- {
- return pointAt(t);
- }
-
- std::vector<double> roots(double v, Dim2 d) const {
- std::vector<double> res;
- for(unsigned i = 0; i <= size(); i++) {
- std::vector<double> temp = (*this)[i].roots(v, d);
- for(unsigned j = 0; j < temp.size(); j++)
- res.push_back(temp[j] + i);
- }
- return res;
- }
-
- std::vector<double>
- allNearestPoints(Point const& _point, double from, double to) const;
-
- std::vector<double>
- allNearestPoints(Point const& _point) const
- {
- unsigned int sz = size();
- if ( closed() ) ++sz;
- return allNearestPoints(_point, 0, sz);
- }
-
- std::vector<double>
- nearestPointPerCurve(Point const& _point) const;
-
- double nearestPoint(Point const& _point, double from, double to, double *distance_squared = NULL) const;
-
- double nearestPoint(Point const& _point, double *distance_squared = NULL) const
- {
- unsigned int sz = size();
- if ( closed() ) ++sz;
- return nearestPoint(_point, 0, sz, distance_squared);
- }
-
- void appendPortionTo(Path &p, double f, double t) const;
-
- Path portion(double f, double t) const {
- Path ret;
- ret.close(false);
- appendPortionTo(ret, f, t);
- return ret;
- }
- Path portion(Interval i) const { return portion(i.min(), i.max()); }
-
- Path reverse() const {
- Path ret(*this);
- ret.unshare();
- for ( Sequence::iterator iter = ret.get_curves().begin() ;
- iter != ret.get_curves().end()-1 ; ++iter )
+ /// Construct a path containing a range of curves.
+ template <typename Iter>
+ Path(Iter first, Iter last, bool closed = false, bool stitch = false)
+ : _curves(new Sequence())
+ , _closed(closed)
+ , _exception_on_stitch(!stitch)
{
- *iter = boost::shared_ptr<Curve>((*iter)->reverse());
- }
- std::reverse(ret.get_curves().begin(), ret.get_curves().end()-1);
- ret.final_ = static_cast<ClosingSegment *>(ret.final_->reverse());
- ret.get_curves().back() = boost::shared_ptr<Curve>(ret.final_);
- return ret;
- }
-
- void insert(iterator const &pos,
- Curve const &curve, Stitching stitching=NO_STITCHING)
- {
- unshare();
- Sequence::iterator seq_pos(seq_iter(pos));
- Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate()));
- if (stitching) stitch(seq_pos, seq_pos, source);
- do_update(seq_pos, seq_pos, source.begin(), source.end());
- }
-
- void insert(iterator const &pos,
- const_iterator const &first,
- const_iterator const &last,
- Stitching stitching=NO_STITCHING)
- {
- unshare();
- Sequence::iterator seq_pos(seq_iter(pos));
- Sequence source(seq_iter(first), seq_iter(last));
- if (stitching) stitch(seq_pos, seq_pos, source);
- do_update(seq_pos, seq_pos, source.begin(), source.end());
- }
-
- void clear() {
- unshare();
- do_update(get_curves().begin(), get_curves().end()-1,
- get_curves().begin(), get_curves().begin());
- }
-
- void erase(iterator const &pos, Stitching stitching=NO_STITCHING) {
- unshare();
- Sequence::iterator seq_pos(seq_iter(pos));
- if (stitching) {
- Sequence stitched;
- stitch(seq_pos, seq_pos+1, stitched);
- do_update(seq_pos, seq_pos+1, stitched.begin(), stitched.end());
- } else {
- do_update(seq_pos, seq_pos+1, get_curves().begin(), get_curves().begin());
- }
- }
-
- void erase(iterator const &first,
- iterator const &last,
- Stitching stitching=NO_STITCHING)
- {
- unshare();
- Sequence::iterator seq_first=seq_iter(first);
- Sequence::iterator seq_last=seq_iter(last);
- if (stitching) {
- Sequence stitched;
- stitch(seq_first, seq_last, stitched);
- do_update(seq_first, seq_last, stitched.begin(), stitched.end());
- } else {
- do_update(seq_first, seq_last,
- get_curves().begin(), get_curves().begin());
- }
- }
-
- // erase last segment of path
- void erase_last() {
- erase(iterator(*this, size()-1));
- }
-
- void replace(iterator const &replaced,
- Curve const &curve,
- Stitching stitching=NO_STITCHING)
- {
- unshare();
- Sequence::iterator seq_replaced(seq_iter(replaced));
- Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate()));
- if (stitching) stitch(seq_replaced, seq_replaced+1, source);
- do_update(seq_replaced, seq_replaced+1, source.begin(), source.end());
- }
-
- void replace(iterator const &first_replaced,
- iterator const &last_replaced,
- Curve const &curve, Stitching stitching=NO_STITCHING)
- {
- unshare();
- Sequence::iterator seq_first_replaced(seq_iter(first_replaced));
- Sequence::iterator seq_last_replaced(seq_iter(last_replaced));
- Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate()));
- if (stitching) stitch(seq_first_replaced, seq_last_replaced, source);
- do_update(seq_first_replaced, seq_last_replaced,
- source.begin(), source.end());
- }
-
- void replace(iterator const &replaced,
- const_iterator const &first,
- const_iterator const &last,
- Stitching stitching=NO_STITCHING)
- {
- unshare();
- Sequence::iterator seq_replaced(seq_iter(replaced));
- Sequence source(seq_iter(first), seq_iter(last));
- if (stitching) stitch(seq_replaced, seq_replaced+1, source);
- do_update(seq_replaced, seq_replaced+1, source.begin(), source.end());
- }
-
- void replace(iterator const &first_replaced,
- iterator const &last_replaced,
- const_iterator const &first,
- const_iterator const &last,
- Stitching stitching=NO_STITCHING)
- {
- unshare();
- Sequence::iterator seq_first_replaced(seq_iter(first_replaced));
- Sequence::iterator seq_last_replaced(seq_iter(last_replaced));
- Sequence source(seq_iter(first), seq_iter(last));
- if (stitching) stitch(seq_first_replaced, seq_last_replaced, source);
- do_update(seq_first_replaced, seq_last_replaced,
- source.begin(), source.end());
- }
-
- void start(Point p) {
- clear();
- final_->setPoint(0, p);
- final_->setPoint(1, p);
- }
-
- Point initialPoint() const { return (*final_)[1]; }
- Point finalPoint() const { return (*final_)[0]; }
-
- void setInitial(Point const& p)
- {
- if ( empty() ) return;
- unshare();
- boost::shared_ptr<Curve> head(front().duplicate());
- head->setInitial(p);
- Sequence::iterator replaced = get_curves().begin();
- Sequence source(1, head);
- do_update(replaced, replaced + 1, source.begin(), source.end());
- }
-
- void setFinal(Point const& p)
- {
- if ( empty() ) return;
- unshare();
- boost::shared_ptr<Curve> tail(back().duplicate());
- tail->setFinal(p);
- Sequence::iterator replaced = get_curves().end() - 2;
- Sequence source(1, tail);
- do_update(replaced, replaced + 1, source.begin(), source.end());
- }
-
- void append(Curve const &curve, Stitching stitching=NO_STITCHING) {
- unshare();
- if (stitching) stitchTo(curve.initialPoint());
- do_append(curve.duplicate());
- }
- void append(D2<SBasis> const &curve, Stitching stitching=NO_STITCHING) {
- unshare();
- if (stitching) stitchTo(Point(curve[X][0][0], curve[Y][0][0]));
- do_append(new SBasisCurve(curve));
- }
- void append(Path const &other, Stitching stitching=NO_STITCHING) {
- insert(end(), other.begin(), other.end(), stitching);
- }
-
- void stitchTo(Point const &p) {
- if (!empty() && finalPoint() != p) {
- unshare();
- do_append(new StitchSegment(finalPoint(), p));
- }
- }
-
-
- /**
- * It is important to note that the coordinates passed to appendNew should be finite!
- * If one of the coordinates is infinite, 2geom will throw a ContinuityError exception.
- */
-
- template <typename CurveType, typename A>
- void appendNew(A a) {
- unshare();
- do_append(new CurveType(finalPoint(), a));
- }
-
- template <typename CurveType, typename A, typename B>
- void appendNew(A a, B b) {
- unshare();
- do_append(new CurveType(finalPoint(), a, b));
- }
-
- template <typename CurveType, typename A, typename B, typename C>
- void appendNew(A a, B b, C c) {
- unshare();
- do_append(new CurveType(finalPoint(), a, b, c));
- }
-
- template <typename CurveType, typename A, typename B, typename C,
- typename D>
- void appendNew(A a, B b, C c, D d) {
- unshare();
- do_append(new CurveType(finalPoint(), a, b, c, d));
- }
-
- template <typename CurveType, typename A, typename B, typename C,
- typename D, typename E>
- void appendNew(A a, B b, C c, D d, E e) {
- unshare();
- do_append(new CurveType(finalPoint(), a, b, c, d, e));
- }
-
- template <typename CurveType, typename A, typename B, typename C,
- typename D, typename E, typename F>
- void appendNew(A a, B b, C c, D d, E e, F f) {
- unshare();
- do_append(new CurveType(finalPoint(), a, b, c, d, e, f));
- }
-
- template <typename CurveType, typename A, typename B, typename C,
- typename D, typename E, typename F,
- typename G>
- void appendNew(A a, B b, C c, D d, E e, F f, G g) {
- unshare();
- do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g));
- }
-
- template <typename CurveType, typename A, typename B, typename C,
- typename D, typename E, typename F,
- typename G, typename H>
- void appendNew(A a, B b, C c, D d, E e, F f, G g, H h) {
- unshare();
- do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h));
- }
-
- template <typename CurveType, typename A, typename B, typename C,
- typename D, typename E, typename F,
- typename G, typename H, typename I>
- void appendNew(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
- unshare();
- do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h, i));
- }
+ for (Iter i = first; i != last; ++i) {
+ _curves->push_back(i->duplicate());
+ }
+ if (!_curves->empty()) {
+ _closing_seg = new ClosingSegment(_curves->back().finalPoint(),
+ _curves->front().initialPoint());
+ } else {
+ _closing_seg = new ClosingSegment();
+ }
+ _curves->push_back(_closing_seg);
+ }
+
+ /// Construct a path from a convex hull.
+ Path(ConvexHull const &);
+
+ virtual ~Path() {}
+
+ // Path &operator=(Path const &other) - use default assignment operator
+
+ /** @brief Swap contents with another path
+ * @todo Add noexcept specifiers for C++11 */
+ void swap(Path &other) throw() {
+ using std::swap;
+ swap(other._curves, _curves);
+ swap(other._closing_seg, _closing_seg);
+ swap(other._closed, _closed);
+ }
+ /** @brief Swap contents of two paths.
+ * @relates Path */
+ friend inline void swap(Path &a, Path &b) throw() { a.swap(b); }
+
+ /** @brief Access a curve by index */
+ Curve const &operator[](size_type i) const { return (*_curves)[i]; }
+ /** @brief Access a curve by index */
+ Curve const &at(size_type i) const { return _curves->at(i); }
+
+ /** @brief Access the first curve in the path.
+ * Since the curve always contains at least a degenerate closing segment,
+ * it is always safe to use this method. */
+ Curve const &front() const { return _curves->front(); }
+ /** @brief Access the last curve in the path. */
+ Curve const &back() const { return back_default(); }
+ Curve const &back_open() const {
+ if (empty()) return _curves->back();
+ return (*_curves)[_curves->size() - 2];
+ }
+ Curve const &back_closed() const {
+ return _closing_seg->isDegenerate()
+ ? (*_curves)[_curves->size() - 2]
+ : (*_curves)[_curves->size() - 1];
+ }
+ Curve const &back_default() const { return (_closed ? back_closed() : back_open()); }
+
+ const_iterator begin() const { return const_iterator(*this, 0); }
+ const_iterator end() const { return end_default(); }
+ const_iterator end_default() const { return const_iterator(*this, size_default()); }
+ const_iterator end_open() const { return const_iterator(*this, size_open()); }
+ const_iterator end_closed() const { return const_iterator(*this, size_closed()); }
+ iterator begin() { return iterator(*this, 0); }
+ iterator end() { return end_default(); }
+ iterator end_default() { return iterator(*this, size_default()); }
+ iterator end_open() { return iterator(*this, size_open()); }
+ iterator end_closed() { return iterator(*this, size_closed()); }
+
+ /// Size without the closing segment, even if the path is closed.
+ size_type size_open() const { return _curves->size() - 1; }
+
+ /** @brief Size with the closing segment, if it makes a difference.
+ * If the closing segment is degenerate, i.e. its initial and final points
+ * are exactly equal, then it is not included in this size. */
+ size_type size_closed() const {
+ return _closing_seg->isDegenerate() ? _curves->size() - 1 : _curves->size();
+ }
+
+ /// Natural size of the path.
+ size_type size_default() const {
+ return _includesClosingSegment() ? size_closed() : size_open();
+ }
+ /// Natural size of the path.
+ size_type size() const { return size_default(); }
+
+ size_type max_size() const { return _curves->max_size() - 1; }
+
+ /** @brief Check whether path is empty.
+ * The path is empty if it contains only the closing segment, which according
+ * to the continuity invariant must be degenerate. Note that unlike standard
+ * containers, two empty paths are not necessarily identical, because the
+ * degenerate closing segment may be at a different point, affecting the operation
+ * of methods such as appendNew(). */
+ bool empty() const { return (_curves->size() == 1); }
+
+ /// Check whether the path is closed.
+ bool closed() const { return _closed; }
+
+ /// Set whether the path is closed.
+ void close(bool closed = true) { _closed = closed; }
+
+ /** @brief Remove all curves from the path.
+ * The initial and final points of the closing segment are set to (0,0).
+ * The stitching flag remains unchanged. */
+ void clear();
+
+ /** @brief Get the approximate bounding box.
+ * The rectangle returned by this method will contain all the curves, but it's not
+ * guaranteed to be the smallest possible one */
+ OptRect boundsFast() const;
+
+ /** @brief Get a tight-fitting bounding box.
+ * This will return the smallest possible axis-aligned rectangle containing
+ * all the curves in the path. */
+ OptRect boundsExact() const;
+
+ Piecewise<D2<SBasis> > toPwSb() const;
+
+ /// Test paths for exact equality.
+ bool operator==(Path const &other) const;
+
+ /// Apply an affine transform.
+ Path &operator*=(Affine const &m);
+
+ /** @brief Get the allowed range of time values.
+ * @return Values for which pointAt() and valueAt() yield valid results. */
+ Interval timeRange() const;
+
+ /** Get the curve at the specified time value.
+ * @param t Time value
+ * @param rest Optional storage for the corresponding time value in the curve */
+ Curve const &curveAt(Coord t, Coord *rest = NULL) const;
+
+ /// Get the closing segment of the path.
+ LineSegment const &closingSegment() const { return *_closing_seg; }
+
+ /** @brief Get the point at the specified time value.
+ * Note that this method has reduced precision with respect to calling pointAt()
+ * directly on the curve. If you want high precision results, use the version
+ * that takes a Position parameter.
+ *
+ * Allowed time values range from zero to the number of curves; you can retrieve
+ * the allowed range of values with timeRange(). */
+ Point pointAt(Coord t) const;
+
+ /// Get one coordinate (X or Y) at the specified time value.
+ Coord valueAt(Coord t, Dim2 d) const;
+
+ /// Get the curve at the specified position.
+ Curve const &curveAt(Position const &pos) const;
+ /// Get the point at the specified position.
+ Point pointAt(Position const &pos) const;
+ /// Get one coordinate at the specified position.
+ Coord valueAt(Position const &pos, Dim2 d) const;
+
+ Point operator()(Coord t) const { return pointAt(t); }
+
+ /// Compute intersections with axis-aligned line.
+ std::vector<Position> roots(Coord v, Dim2 d) const;
+
+ /// Compute intersections with another path.
+ std::vector<PathIntersection> intersect(Path const &other, Coord precision = EPSILON) const;
+
+ /** @brief Determine the winding number at the specified point.
+ *
+ * The winding number is the number of full turns made by a ray that connects the passed
+ * point and the path's value (i.e. the result of the pointAt() method) as the time increases
+ * from 0 to the maximum valid value. Positive numbers indicate turns in the direction
+ * of increasing angles.
+ *
+ * Winding numbers are often used as the definition of what is considered "inside"
+ * the shape. Typically points with either nonzero winding or odd winding are
+ * considered to be inside the path. */
+ int winding(Point const &p) const;
+
+ std::vector<Coord> allNearestTimes(Point const &p, Coord from, Coord to) const;
+ std::vector<Coord> allNearestTimes(Point const &p) const {
+ return allNearestTimes(p, 0, size_default());
+ }
+
+ Coord nearestTime(Point const &p, Coord *dist = NULL) const;
+ std::vector<Coord> nearestTimePerCurve(Point const &p) const;
+ Position nearestPosition(Point const &p, Coord *dist = NULL) const;
+
+ void appendPortionTo(Path &p, Coord f, Coord t) const;
+
+ /** @brief Append a subset of this path to another path.
+ * An extra stitching segment will be inserted if the start point of the portion
+ * and the final point of the target path do not match exactly.
+ * The closing segment of the target path will be modified. */
+ void appendPortionTo(Path &p, Position const &from, Position const &to, bool cross_start = false) const {
+ PathInterval ival(from, to, cross_start, size_closed());
+ appendPortionTo(p, ival, boost::none, boost::none);
+ }
+
+ /** @brief Append a subset of this path to another path.
+ * This version allows you to explicitly pass a PathInterval. */
+ void appendPortionTo(Path &p, PathInterval const &ival) const {
+ appendPortionTo(p, ival, boost::none, boost::none);
+ }
+
+ /** @brief Append a subset of this path to another path, specifying endpoints.
+ * This method is for use in situations where endpoints of the portion segments
+ * have to be set exactly, for instance when computing Boolean operations. */
+ void appendPortionTo(Path &p, PathInterval const &ival,
+ boost::optional<Point> const &p_from, boost::optional<Point> const &p_to) const;
+
+ Path portion(Coord f, Coord t) const {
+ Path ret;
+ ret.close(false);
+ appendPortionTo(ret, f, t);
+ return ret;
+ }
+
+ Path portion(Interval const &i) const { return portion(i.min(), i.max()); }
+
+ /** @brief Get a subset of the current path with full precision.
+ * When @a from is larger (later in the path) than @a to, the returned portion
+ * will be reversed. If @a cross_start is true, the portion will be reversed
+ * and will cross the initial point of the path. Therefore, when @a from is larger
+ * than @a to and @a cross_start is true, the returned portion will not be reversed,
+ * but will "wrap around" the end of the path. */
+ Path portion(Position const &from, Position const &to, bool cross_start = false) const {
+ Path ret;
+ ret.close(false);
+ appendPortionTo(ret, from, to, cross_start);
+ return ret;
+ }
+
+ /** @brief Get a subset of the current path with full precision.
+ * This version allows you to explicitly pass a PathInterval. */
+ Path portion(PathInterval const &ival) const {
+ Path ret;
+ ret.close(false);
+ appendPortionTo(ret, ival);
+ return ret;
+ }
+
+ /** @brief Obtain a reversed version of the current path.
+ * The final point of the current path will become the initial point
+ * of the reversed path, unless it is closed and has a non-degenerate
+ * closing segment. In that case, the new initial point will be the final point
+ * of the last "real" segment. */
+ Path reversed() const;
+
+ void insert(iterator pos, Curve const &curve);
+
+ template <typename Iter>
+ void insert(iterator pos, Iter first, Iter last) {
+ _unshare();
+ Sequence::iterator seq_pos(seq_iter(pos));
+ Sequence source;
+ for (; first != last; ++first) {
+ source.push_back(first->duplicate());
+ }
+ do_update(seq_pos, seq_pos, source);
+ }
+
+ void erase(iterator pos);
+ void erase(iterator first, iterator last);
+
+ // erase last segment of path
+ void erase_last() { erase(iterator(*this, size() - 1)); }
+
+ void start(Point const &p);
+
+ /** @brief Get the first point in the path. */
+ Point initialPoint() const { return (*_closing_seg)[1]; }
+
+ /** @brief Get the last point in the path.
+ * If the path is closed, this is always the same as the initial point. */
+ Point finalPoint() const { return (*_closing_seg)[_closed ? 1 : 0]; }
+
+ void setInitial(Point const &p) {
+ _unshare();
+ _closed = false;
+ _curves->front().setInitial(p);
+ _closing_seg->setFinal(p);
+ }
+ void setFinal(Point const &p) {
+ _unshare();
+ _closed = false;
+ (*_curves)[size_open() - 1].setFinal(p);
+ _closing_seg->setInitial(p);
+ }
+
+ /** @brief Add a new curve to the end of the path.
+ * This inserts the new curve right before the closing segment.
+ * The path takes ownership of the passed pointer, which should not be freed. */
+ void append(Curve *curve) {
+ _unshare();
+ stitchTo(curve->initialPoint());
+ do_append(curve);
+ }
+
+ void append(Curve const &curve) {
+ _unshare();
+ stitchTo(curve.initialPoint());
+ do_append(curve.duplicate());
+ }
+ void append(D2<SBasis> const &curve) {
+ _unshare();
+ stitchTo(Point(curve[X][0][0], curve[Y][0][0]));
+ do_append(new SBasisCurve(curve));
+ }
+ void append(Path const &other) {
+ replace(end_open(), other.begin(), other.end());
+ }
+
+ void replace(iterator replaced, Curve const &curve);
+ void replace(iterator first, iterator last, Curve const &curve);
+ void replace(iterator replaced, Path const &path);
+ void replace(iterator first, iterator last, Path const &path);
+
+ template <typename Iter>
+ void replace(iterator replaced, Iter first, Iter last) {
+ replace(replaced, replaced + 1, first, last);
+ }
+
+ template <typename Iter>
+ void replace(iterator first_replaced, iterator last_replaced, Iter first, Iter last) {
+ _unshare();
+ Sequence::iterator seq_first_replaced(seq_iter(first_replaced));
+ Sequence::iterator seq_last_replaced(seq_iter(last_replaced));
+ Sequence source;
+ for (; first != last; ++first) {
+ source.push_back(first->duplicate());
+ }
+ do_update(seq_first_replaced, seq_last_replaced, source);
+ }
+
+ /** @brief Append a new curve to the path.
+ *
+ * This family of methods will automaticaly use the current final point of the path
+ * as the first argument of the new curve's constructor. To call this method,
+ * you'll need to write e.g.:
+ * @code
+ path.template appendNew<CubicBezier>(control1, control2, end_point);
+ @endcode
+ * It is important to note that the coordinates passed to appendNew should be finite!
+ * If one of the coordinates is infinite, 2geom will throw a ContinuityError exception.
+ */
+ template <typename CurveType, typename A>
+ void appendNew(A a) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a));
+ }
+
+ template <typename CurveType, typename A, typename B>
+ void appendNew(A a, B b) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a, b));
+ }
+
+ template <typename CurveType, typename A, typename B, typename C>
+ void appendNew(A a, B b, C c) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a, b, c));
+ }
+
+ template <typename CurveType, typename A, typename B, typename C, typename D>
+ void appendNew(A a, B b, C c, D d) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a, b, c, d));
+ }
+
+ template <typename CurveType, typename A, typename B, typename C, typename D, typename E>
+ void appendNew(A a, B b, C c, D d, E e) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a, b, c, d, e));
+ }
+
+ template <typename CurveType, typename A, typename B, typename C, typename D, typename E, typename F>
+ void appendNew(A a, B b, C c, D d, E e, F f) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a, b, c, d, e, f));
+ }
+
+ template <typename CurveType, typename A, typename B, typename C, typename D, typename E, typename F, typename G>
+ void appendNew(A a, B b, C c, D d, E e, F f, G g) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g));
+ }
+
+ template <typename CurveType, typename A, typename B, typename C, typename D, typename E, typename F, typename G,
+ typename H>
+ void appendNew(A a, B b, C c, D d, E e, F f, G g, H h) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h));
+ }
+
+ template <typename CurveType, typename A, typename B, typename C, typename D, typename E, typename F, typename G,
+ typename H, typename I>
+ void appendNew(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h, i));
+ }
+
+ /// Append a stitching segment ending at the specified point.
+ void stitchTo(Point const &p);
+
+ /** @brief Verify the continuity invariant.
+ * If the path is not contiguous, this will throw a CountinuityError. */
+ void checkContinuity() const;
+
+ /** @brief Enable or disable the throwing of exceptions when stitching discontinuities.
+ * Normally stitching will cause exceptions, but when you are working with unsanitized
+ * curve data, you can disable these exceptions. */
+ void setStitching(bool x) {
+ _exception_on_stitch = !x;
+ }
private:
- static Sequence::iterator seq_iter(iterator const &iter) {
- return iter.path->get_curves().begin() + iter.index;
- }
- static Sequence::const_iterator seq_iter(const_iterator const &iter) {
- return iter.path->get_curves().begin() + iter.index;
- }
-
- Sequence &get_curves() { return *curves_; }
- Sequence const &get_curves() const { return *curves_; }
-
- void unshare() {
- if (!curves_.unique()) {
- curves_ = boost::shared_ptr<Sequence>(new Sequence(*curves_));
- }
- if (!get_curves().back().unique()) {
- final_ = static_cast<ClosingSegment *>(final_->duplicate());
- get_curves().back() = boost::shared_ptr<Curve>(final_);
- }
- }
-
- void stitch(Sequence::iterator first_replaced,
- Sequence::iterator last_replaced,
- Sequence &sequence);
-
- void do_update(Sequence::iterator first_replaced,
- Sequence::iterator last_replaced,
- Sequence::iterator first,
- Sequence::iterator last);
-
- // n.b. takes ownership of curve object
- void do_append(Curve *curve);
-
- void check_continuity(Sequence::iterator first_replaced,
- Sequence::iterator last_replaced,
- Sequence::iterator first,
- Sequence::iterator last);
-
- boost::shared_ptr<Sequence> curves_;
- ClosingSegment *final_;
- bool closed_;
-}; // end class Path
-
-inline static Piecewise<D2<SBasis> > paths_to_pw(std::vector<Path> paths) {
- Piecewise<D2<SBasis> > ret = paths[0].toPwSb();
- for(unsigned i = 1; i < paths.size(); i++) {
- ret.concat(paths[i].toPwSb());
- }
- return ret;
-}
+ static Sequence::iterator seq_iter(iterator const &iter) {
+ return iter.path->_curves->begin() + iter.index;
+ }
+ static Sequence::const_iterator seq_iter(const_iterator const &iter) {
+ return iter.path->_curves->begin() + iter.index;
+ }
-inline
-Coord nearest_point(Point const& p, Path const& c)
-{
- return c.nearestPoint(p);
-}
+ // whether the closing segment is part of the path
+ bool _includesClosingSegment() const {
+ return _closed && !_closing_seg->isDegenerate();
+ }
+ void _unshare() {
+ if (!_curves.unique()) {
+ _curves.reset(new Sequence(*_curves));
+ _closing_seg = static_cast<ClosingSegment*>(&_curves->back());
+ }
+ }
+ Position _getPosition(Coord t) const;
+
+ void stitch(Sequence::iterator first_replaced, Sequence::iterator last_replaced, Sequence &sequence);
+ void do_update(Sequence::iterator first, Sequence::iterator last, Sequence &source);
+
+ // n.b. takes ownership of curve object
+ void do_append(Curve *curve);
+
+ boost::shared_ptr<Sequence> _curves;
+ ClosingSegment *_closing_seg;
+ bool _closed;
+ bool _exception_on_stitch;
+}; // end class Path
+
+Piecewise<D2<SBasis> > paths_to_pw(PathVector const &paths);
+
+inline Coord nearest_time(Point const &p, Path const &c) { return c.nearestTime(p); }
-} // end namespace Geom
+} // end namespace Geom
#endif // LIB2GEOM_SEEN_PATH_H
diff --git a/src/2geom/pathvector.cpp b/src/2geom/pathvector.cpp
index fc0ad75c4..36364de9b 100644
--- a/src/2geom/pathvector.cpp
+++ b/src/2geom/pathvector.cpp
@@ -1,12 +1,11 @@
-/*
- * PathVector - std::vector containing Geom::Path
- * This file provides a set of operations that can be performed on PathVector,
- * e.g. an affine transform.
- *
+/** @file
+ * @brief PathVector - a sequence of subpaths
+ *//*
* Authors:
- * Johan Engelen <goejendaagh@zonnet.nl>
+ * Johan Engelen <goejendaagh@zonnet.nl>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
*
- * Copyright 2008 authors
+ * Copyright 2008-2014 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -32,9 +31,6 @@
* the specific language governing rights and limitations.
*/
-#ifndef SEEN_GEOM_PATHVECTOR_CPP
-#define SEEN_GEOM_PATHVECTOR_CPP
-
#include <2geom/pathvector.h>
#include <2geom/path.h>
@@ -42,104 +38,184 @@
namespace Geom {
-// TODO: see which of these functions can be inlined for optimization
+//PathVector &PathVector::operator+=(PathVector const &other);
+
+PathVector::size_type PathVector::curveCount() const
+{
+ size_type n = 0;
+ for (const_iterator it = begin(); it != end(); ++it) {
+ n += it->size_default();
+ }
+ return n;
+}
+
+void PathVector::reverse(bool reverse_paths)
+{
+ if (reverse_paths) {
+ std::reverse(begin(), end());
+ }
+ for (iterator i = begin(); i != end(); ++i) {
+ *i = i->reversed();
+ }
+}
-/**
- * Reverses all Paths and the order of paths in the vector as well
- **/
-PathVector reverse_paths_and_order (PathVector const & path_in)
+PathVector PathVector::reversed(bool reverse_paths) const
{
- PathVector path_out;
- for (PathVector::const_reverse_iterator it = path_in.rbegin(); it != path_in.rend(); ++it) {
- path_out.push_back( (*it).reverse() );
+ PathVector ret;
+ for (const_iterator i = begin(); i != end(); ++i) {
+ ret.push_back(i->reversed());
+ }
+ if (reverse_paths) {
+ std::reverse(ret.begin(), ret.end());
}
- return path_out;
+ return ret;
}
-OptRect bounds_fast( PathVector const& pv )
+Path &PathVector::pathAt(Coord t, Coord *rest)
+{
+ return const_cast<Path &>(static_cast<PathVector const*>(this)->pathAt(t, rest));
+}
+Path const &PathVector::pathAt(Coord t, Coord *rest) const
+{
+ Position pos = _getPosition(t);
+ if (rest) {
+ *rest = Coord(pos.curve_index) + pos.t;
+ }
+ return at(pos.path_index);
+}
+Curve const &PathVector::curveAt(Coord t, Coord *rest) const
+{
+ Position pos = _getPosition(t);
+ if (rest) {
+ *rest = pos.t;
+ }
+ return at(pos.path_index).at(pos.curve_index);
+}
+Coord PathVector::valueAt(Coord t, Dim2 d) const
+{
+ Position pos = _getPosition(t);
+ return at(pos.path_index).at(pos.curve_index).valueAt(pos.t, d);
+}
+Point PathVector::pointAt(Coord t) const
+{
+ Position pos = _getPosition(t);
+ return at(pos.path_index).at(pos.curve_index).pointAt(pos.t);
+}
+
+OptRect PathVector::boundsFast() const
{
- typedef PathVector::const_iterator const_iterator;
-
OptRect bound;
- if (pv.empty()) return bound;
-
- bound = (pv.begin())->boundsFast();
- for (const_iterator it = ++(pv.begin()); it != pv.end(); ++it)
- {
+ if (empty()) return bound;
+
+ bound = front().boundsFast();
+ for (const_iterator it = ++begin(); it != end(); ++it) {
bound.unionWith(it->boundsFast());
}
return bound;
}
-OptRect bounds_exact( PathVector const& pv )
+OptRect PathVector::boundsExact() const
{
- typedef PathVector::const_iterator const_iterator;
-
OptRect bound;
- if (pv.empty()) return bound;
-
- bound = (pv.begin())->boundsExact();
- for (const_iterator it = ++(pv.begin()); it != pv.end(); ++it)
- {
+ if (empty()) return bound;
+
+ bound = front().boundsExact();
+ for (const_iterator it = ++begin(); it != end(); ++it) {
bound.unionWith(it->boundsExact());
}
return bound;
}
-/* Note: undefined for empty pathvectors or pathvectors with empty paths.
- * */
-boost::optional<PathVectorPosition> nearestPoint(PathVector const & path_in, Point const& _point, double *distance_squared)
+std::vector<PVIntersection> PathVector::intersect(PathVector const &other, Coord precision) const
{
- boost::optional<PathVectorPosition> retval;
-
- double mindsq = infinity();
- unsigned int i = 0;
- for (Geom::PathVector::const_iterator pit = path_in.begin(); pit != path_in.end(); ++pit) {
- double dsq;
- double t = pit->nearestPoint(_point, &dsq);
- //std::cout << t << "," << dsq << std::endl;
- if (dsq < mindsq) {
- mindsq = dsq;
- retval = PathVectorPosition(i, t);
+ typedef PathVectorPosition PVPos;
+ std::vector<PVIntersection> result;
+ for (std::size_t i = 0; i < size(); ++i) {
+ for (std::size_t j = 0; j < other.size(); ++j) {
+ std::vector<PathIntersection> xs = (*this)[i].intersect(other[j]);
+ for (std::size_t k = 0; k < xs.size(); ++k) {
+ PVIntersection pvx(PVPos(i, xs[k].first), PVPos(j, xs[k].second), xs[k].point());
+ result.push_back(pvx);
+ }
}
+ }
+ return result;
+}
- ++i;
+int PathVector::winding(Point const &p) const
+{
+ int wind = 0;
+ for (const_iterator i = begin(); i != end(); ++i) {
+ wind += i->winding(p);
}
+ return wind;
+}
- if (distance_squared) {
- *distance_squared = mindsq;
+boost::optional<PathVectorPosition> PathVector::nearestPosition(Point const &p, Coord *dist) const
+{
+ boost::optional<Position> retval;
+
+ Coord mindist = infinity();
+ for (size_type i = 0; i < size(); ++i) {
+ Coord d;
+ PathPosition pos = (*this)[i].nearestPosition(p, &d);
+ if (d < mindist) {
+ mindist = d;
+ retval = Position(i, pos.curve_index, pos.t);
+ }
+ }
+
+ if (dist) {
+ *dist = mindist;
}
return retval;
}
-std::vector<PathVectorPosition> allNearestPoints(PathVector const & path_in, Point const& _point, double *distance_squared)
+std::vector<PathVectorPosition> PathVector::allNearestPositions(Point const &p, Coord *dist) const
{
- std::vector<PathVectorPosition> retval;
-
- double mindsq = infinity();
- unsigned int i = 0;
- for (Geom::PathVector::const_iterator pit = path_in.begin(); pit != path_in.end(); ++pit) {
- double dsq;
- double t = pit->nearestPoint(_point, &dsq);
- if (dsq < mindsq) {
- mindsq = dsq;
- retval.push_back(PathVectorPosition(i, t));
+ std::vector<Position> retval;
+
+ Coord mindist = infinity();
+ for (size_type i = 0; i < size(); ++i) {
+ Coord d;
+ PathPosition pos = (*this)[i].nearestPosition(p, &d);
+ if (d < mindist) {
+ mindist = d;
+ retval.clear();
+ }
+ if (d <= mindist) {
+ retval.push_back(Position(i, pos.curve_index, pos.t));
}
-
- ++i;
}
- if (distance_squared) {
- *distance_squared = mindsq;
+ if (dist) {
+ *dist = mindist;
}
return retval;
+}
+PathVectorPosition PathVector::_getPosition(Coord t) const
+{
+ Position ret;
+ Coord rest = 0;
+ ret.t = modf(t, &rest);
+ ret.curve_index = rest;
+ for (; ret.path_index < size(); ++ret.path_index) {
+ unsigned s = _data.at(ret.path_index).size_default();
+ if (s > ret.curve_index) break;
+ // special case for the last point
+ if (s == ret.curve_index && ret.path_index + 1 == size()) {
+ --ret.curve_index;
+ ret.t = 1;
+ break;
+ }
+ ret.curve_index -= s;
+ }
+ return ret;
}
} // namespace Geom
-#endif // SEEN_GEOM_PATHVECTOR_CPP
-
/*
Local Variables:
mode:c++
diff --git a/src/2geom/pathvector.h b/src/2geom/pathvector.h
index e875e915f..6765d6bc0 100644
--- a/src/2geom/pathvector.h
+++ b/src/2geom/pathvector.h
@@ -1,13 +1,11 @@
-/**
- * \file
- * \brief PathVector - std::vector containing Geom::Path.
- * This file provides a set of operations that can be performed on PathVector,
- * e.g. an affine transform.
+/** @file
+ * @brief PathVector - a sequence of subpaths
*//*
* Authors:
- * Johan Engelen <goejendaagh@zonnet.nl>
+ * Johan Engelen <j.b.c.engelen@alumnus.utwente.nl>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
*
- * Copyright 2008 authors
+ * Copyright 2008-2014 authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -36,93 +34,247 @@
#ifndef LIB2GEOM_SEEN_PATHVECTOR_H
#define LIB2GEOM_SEEN_PATHVECTOR_H
+#include <boost/concept/requires.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/range/algorithm/equal.hpp>
#include <2geom/forward.h>
#include <2geom/path.h>
#include <2geom/transforms.h>
namespace Geom {
-typedef std::vector<Geom::Path> PathVector;
-
-/* general path transformation: */
-inline
-void operator*= (PathVector & path_in, Affine const &m) {
- for(PathVector::iterator it = path_in.begin(); it != path_in.end(); ++it) {
- (*it) *= m;
- }
-}
-inline
-PathVector operator*(PathVector const & path_in, Affine const &m) {
- PathVector ret(path_in);
- ret *= m;
- return ret;
-}
-
-/* specific path transformations: Translation:
- * This makes it possible to make optimized implementations for Translate transforms */
-inline
-void operator*= (PathVector & path_in, Translate const &m) {
- for(PathVector::iterator it = path_in.begin(); it != path_in.end(); ++it) {
- (*it) *= m;
- }
-}
-inline
-PathVector operator*(PathVector const & path_in, Translate const &m) {
- PathVector ret(path_in);
- ret *= m;
- return ret;
-}
-
-/* user friendly approach to Translate transforms: just add an offset Point to the whole path */
-inline
-void operator+=(PathVector &path_in, Point const &p) {
- for(PathVector::iterator it = path_in.begin(); it != path_in.end(); ++it) {
- (*it) *= Translate(p);
- }
-}
-inline
-PathVector operator+(PathVector const &path_in, Point const &p) {
- PathVector ret(path_in);
- ret *= Translate(p);
- return ret;
-}
-
-inline
-Geom::Point initialPoint(PathVector const &path_in)
+/** @brief Position (generalized time value) in the path vector.
+ *
+ * This class exists because mapping the range of multiple curves onto the same interval
+ * as the curve index, we lose some precision. For instance, a path with 16 curves will
+ * have 4 bits less precision than a path with 1 curve. If you need high precision results
+ * in long paths, use this class and related methods instead of the standard methods
+ * pointAt(), nearestTime() and so on.
+ *
+ * @ingroup Paths */
+struct PathVectorPosition
+ : public PathPosition
+ , boost::totally_ordered<PathVectorPosition>
{
- return path_in.front().initialPoint();
-}
+ size_type path_index; ///< Index of the path in the vector
-inline
-Geom::Point finalPoint(PathVector const &path_in)
-{
- return path_in.back().finalPoint();
-}
-
-PathVector reverse_paths_and_order (PathVector const & path_in);
-
-OptRect bounds_fast( PathVector const & pv );
-OptRect bounds_exact( PathVector const & pv );
-
-struct PathVectorPosition {
- // pathvector[path_nr].pointAt(t) is the position
- unsigned int path_nr;
- double t;
- PathVectorPosition() :
- path_nr(0),
- t(0)
- {}
- PathVectorPosition(unsigned int path_nr,
- double t) : path_nr(path_nr), t(t) {}
+ PathVectorPosition() : PathPosition(0, 0), path_index(0) {}
+ PathVectorPosition(size_type _i, size_type _c, Coord _t)
+ : PathPosition(_c, _t), path_index(_i) {}
+ PathVectorPosition(size_type _i, PathPosition const &pos)
+ : PathPosition(pos), path_index(_i) {}
+
+ bool operator<(PathVectorPosition const &other) const {
+ if (path_index < other.path_index) return true;
+ if (path_index == other.path_index) {
+ return static_cast<PathPosition const &>(*this) < static_cast<PathPosition const &>(other);
+ }
+ return false;
+ }
+ bool operator==(PathVectorPosition const &other) const {
+ return path_index == other.path_index
+ && static_cast<PathPosition const &>(*this) == static_cast<PathPosition const &>(other);
+ }
+
+ PathPosition const &asPathPosition() const {
+ return *static_cast<PathPosition const *>(this);
+ }
+};
+
+typedef Intersection<PathVectorPosition> PathVectorIntersection;
+typedef PathVectorIntersection PVIntersection; ///< Alias to save typing
+
+template <>
+struct ShapeTraits<PathVector> {
+ typedef PathVectorPosition TimeType;
+ //typedef PathVectorInterval IntervalType;
+ typedef PathVector AffineClosureType;
+ typedef PathVectorIntersection IntersectionType;
};
-boost::optional<PathVectorPosition> nearestPoint(PathVector const & path_in, Point const& _point, double *distance_squared = NULL);
-std::vector<PathVectorPosition> allNearestPoints(PathVector const & path_in, Point const& _point, double *distance_squared = NULL);
+/** @brief Sequence of subpaths.
+ *
+ * This class corresponds to the SVG notion of a path:
+ * a sequence of any number of open or closed contiguous subpaths.
+ * Unlike Path, this class is closed under boolean operations.
+ *
+ * If you want to represent an arbitrary shape, this is the best class to use.
+ * Shapes with a boundary that is composed of only a single contiguous
+ * component can be represented with Path instead.
+ *
+ * @ingroup Paths
+ */
+class PathVector
+ : MultipliableNoncommutative< PathVector, Affine
+ , MultipliableNoncommutative< PathVector, Translate
+ , MultipliableNoncommutative< PathVector, Scale
+ , MultipliableNoncommutative< PathVector, Rotate
+ , MultipliableNoncommutative< PathVector, HShear
+ , MultipliableNoncommutative< PathVector, VShear
+ , MultipliableNoncommutative< PathVector, Zoom
+ , boost::addable< PathVector
+ , boost::equality_comparable< PathVector
+ > > > > > > > > >
+{
+ typedef std::vector<Path> Sequence;
+public:
+ typedef PathVectorPosition Position;
+ typedef Sequence::iterator iterator;
+ typedef Sequence::const_iterator const_iterator;
+ typedef Sequence::size_type size_type;
+ typedef Path value_type;
+ typedef Path &reference;
+ typedef Path const &const_reference;
+ typedef Path *pointer;
+ typedef std::ptrdiff_t difference_type;
+
+ PathVector() {}
+ PathVector(Path const &p)
+ : _data(1, p)
+ {}
+ template <typename InputIter>
+ PathVector(InputIter first, InputIter last)
+ : _data(first, last)
+ {}
+
+ /// Check whether the vector contains any paths.
+ bool empty() const { return _data.empty(); }
+ /// Get the number of paths in the vector.
+ size_type size() const { return _data.size(); }
+ /// Get the total number of curves in the vector.
+ size_type curveCount() const;
+
+ iterator begin() { return _data.begin(); }
+ iterator end() { return _data.end(); }
+ const_iterator begin() const { return _data.begin(); }
+ const_iterator end() const { return _data.end(); }
+ Path &operator[](size_type index) {
+ return _data[index];
+ }
+ Path const &operator[](size_type index) const {
+ return _data[index];
+ }
+ Path &at(size_type index) {
+ return _data.at(index);
+ }
+ Path const &at(size_type index) const {
+ return _data.at(index);
+ }
+ Path &front() { return _data.front(); }
+ Path const &front() const { return _data.front(); }
+ Path &back() { return _data.back(); }
+ Path const &back() const { return _data.back(); }
+ /// Append a path at the end.
+ void push_back(Path const &path) {
+ _data.push_back(path);
+ }
+ /// Remove the last path.
+ void pop_back() {
+ _data.pop_back();
+ }
+ iterator insert(iterator pos, Path const &p) {
+ return _data.insert(pos, p);
+ }
+ template <typename InputIter>
+ void insert(iterator out, InputIter first, InputIter last) {
+ _data.insert(out, first, last);
+ }
+ /// Remove a path from the vector.
+ iterator erase(iterator i) {
+ return _data.erase(i);
+ }
+ /// Remove a range of paths from the vector.
+ iterator erase(iterator first, iterator last) {
+ return _data.erase(first, last);
+ }
+ /// Remove all paths from the vector.
+ void clear() { _data.clear(); }
+ /** @brief Change the number of paths.
+ * If the vector size increases, it is passed with paths that contain only
+ * a degenerate closing segment at (0,0). */
+ void resize(size_type n) { _data.resize(n); }
+ /** @brief Reverse the direction of paths in the vector.
+ * @param reverse_paths If this is true, the order of paths is reversed as well;
+ * otherwise each path is reversed, but their order in the
+ * PathVector stays the same */
+ void reverse(bool reverse_paths = true);
+ /** @brief Get a new vector with reversed direction of paths.
+ * @param reverse_paths If this is true, the order of paths is reversed as well;
+ * otherwise each path is reversed, but their order in the
+ * PathVector stays the same */
+ PathVector reversed(bool reverse_paths = true) const;
+
+ /// Get the range of allowed time values.
+ Interval timeRange() const {
+ Interval ret(0, curveCount()); return ret;
+ }
+ /** @brief Get the first point in the first path of the vector.
+ * This method will throw an exception if the vector doesn't contain any paths. */
+ Point initialPoint() const {
+ return _data.front().initialPoint();
+ }
+ /** @brief Get the last point in the last path of the vector.
+ * This method will throw an exception if the vector doesn't contain any paths. */
+ Point finalPoint() const {
+ return _data.back().finalPoint();
+ }
+ Path &pathAt(Coord t, Coord *rest = NULL);
+ Path const &pathAt(Coord t, Coord *rest = NULL) const;
+ Curve const &curveAt(Coord t, Coord *rest = NULL) const;
+ Coord valueAt(Coord t, Dim2 d) const;
+ Point pointAt(Coord t) const;
+
+ Path &pathAt(Position const &pos) {
+ return const_cast<Path &>(static_cast<PathVector const*>(this)->pathAt(pos));
+ }
+ Path const &pathAt(Position const &pos) const {
+ return at(pos.path_index);
+ }
+ Curve const &curveAt(Position const &pos) const {
+ return at(pos.path_index).at(pos.curve_index);
+ }
+ Point pointAt(Position const &pos) const {
+ return at(pos.path_index).at(pos.curve_index).pointAt(pos.t);
+ }
+ Coord valueAt(Position const &pos, Dim2 d) const {
+ return at(pos.path_index).at(pos.curve_index).valueAt(pos.t, d);
+ }
+
+ OptRect boundsFast() const;
+ OptRect boundsExact() const;
+
+ template <typename T>
+ BOOST_CONCEPT_REQUIRES(((TransformConcept<T>)), (PathVector &))
+ operator*=(T const &t) {
+ if (empty()) return *this;
+ for (iterator i = begin(); i != end(); ++i) {
+ *i *= t;
+ }
+ return *this;
+ }
+
+ bool operator==(PathVector const &other) const {
+ return boost::range::equal(_data, other._data);
+ }
+
+ std::vector<PVIntersection> intersect(PathVector const &other, Coord precision = EPSILON) const;
+
+ /** @brief Determine the winding number at the specified point.
+ * This is simply the sum of winding numbers for constituent paths. */
+ int winding(Point const &p) const;
+
+ Coord nearestTime(Point const &p) const;
+ boost::optional<Position> nearestPosition(Point const &p, Coord *dist = NULL) const;
+ std::vector<Position> allNearestPositions(Point const &p, Coord *dist = NULL) const;
+
+private:
+ Position _getPosition(Coord t) const;
+
+ Sequence _data;
+};
-inline
-Point pointAt(PathVector const & path_in, PathVectorPosition const &pvp) {
- return path_in[pvp.path_nr].pointAt(pvp.t);
-}
+inline OptRect bounds_fast(PathVector const &pv) { return pv.boundsFast(); }
+inline OptRect bounds_exact(PathVector const &pv) { return pv.boundsExact(); }
} // end namespace Geom
diff --git a/src/2geom/piecewise.h b/src/2geom/piecewise.h
index ab8417254..981d67144 100644
--- a/src/2geom/piecewise.h
+++ b/src/2geom/piecewise.h
@@ -1,7 +1,6 @@
-/**
- * \file
- * \brief Piecewise function class
- *
+/** @file
+ * @brief Piecewise function class
+ *//*
* Copyright 2007 Michael Sloan <mgsloan@gmail.com>
*
* This library is free software; you can redistribute it and/or
@@ -29,8 +28,8 @@
*
*/
-#ifndef SEEN_GEOM_PW_SB_H
-#define SEEN_GEOM_PW_SB_H
+#ifndef LIB2GEOM_SEEN_PIECEWISE_H
+#define LIB2GEOM_SEEN_PIECEWISE_H
#include <vector>
#include <map>
@@ -933,7 +932,7 @@ Piecewise<T> lerp(double t, Piecewise<T> const &a, Piecewise<T> b) {
}
}
-#endif //SEEN_GEOM_PW_SB_H
+#endif //LIB2GEOM_SEEN_PIECEWISE_H
/*
Local Variables:
mode:c++
diff --git a/src/2geom/point.cpp b/src/2geom/point.cpp
index b0b00b5da..e2662becd 100644
--- a/src/2geom/point.cpp
+++ b/src/2geom/point.cpp
@@ -146,8 +146,8 @@ bool is_zero(Point const &p) {
/** @brief True if the point has a length near 1. The are_near() function is used.
* @relates Point */
-bool is_unit_vector(Point const &p) {
- return are_near(L2(p), 1.0);
+bool is_unit_vector(Point const &p, Coord eps) {
+ return are_near(L2(p), 1.0, eps);
}
/** @brief Return the angle between the point and the +X axis.
* @return Angle in \f$(-\pi, \pi]\f$.
@@ -161,7 +161,7 @@ Coord atan2(Point const &p) {
* @return Angle in \f$(-\pi, \pi]\f$.
* @relates Point */
Coord angle_between(Point const &a, Point const &b) {
- return std::atan2(cross(b,a), dot(b,a));
+ return std::atan2(cross(a,b), dot(a,b));
}
/** @brief Create a normalized version of a point.
diff --git a/src/2geom/point.h b/src/2geom/point.h
index 23dcfd54f..3e56ae358 100644
--- a/src/2geom/point.h
+++ b/src/2geom/point.h
@@ -1,6 +1,5 @@
-/**
- * \file
- * \brief Cartesian point / 2D vector and related operations
+/** @file
+ * @brief Cartesian point / 2D vector and related operations
*//*
* Authors:
* Michael G. Sloan <mgsloan@gmail.com>
@@ -33,8 +32,8 @@
* the specific language governing rights and limitations.
*/
-#ifndef SEEN_Geom_POINT_H
-#define SEEN_Geom_POINT_H
+#ifndef LIB2GEOM_SEEN_POINT_H
+#define LIB2GEOM_SEEN_POINT_H
#include "config.h"
#include <iostream>
@@ -59,10 +58,14 @@ class Point
, MultipliableNoncommutative< Point, HShear
, MultipliableNoncommutative< Point, VShear
, MultipliableNoncommutative< Point, Zoom
- > > > > > > > > > > // this uses chaining so it looks weird, but works
+ > > > > > > > > > > // base class chaining, see documentation for Boost.Operator
{
Coord _pt[2];
public:
+ typedef Coord D1Value;
+ typedef Coord &D1Reference;
+ typedef Coord const &D1ConstReference;
+
/// @name Create points
/// @{
/** Construct a point on the origin. */
@@ -116,6 +119,11 @@ public:
* @return Length of the vector from origin to this point */
Coord length() const { return hypot(_pt[0], _pt[1]); }
void normalize();
+ Point normalized() const {
+ Point ret(*this);
+ ret.normalize();
+ return ret;
+ }
/** @brief Return a point like this point but rotated -90 degrees.
* If the y axis grows downwards and the x axis grows to the
@@ -223,16 +231,25 @@ public:
}
/// @}
- /** @brief Lexicographical ordering functor. */
- template <Dim2 d> struct LexOrder;
+ /** @brief Lexicographical ordering functor.
+ * @param d The dimension with higher significance */
+ template <Dim2 DIM> struct LexLess;
+ template <Dim2 DIM> struct LexGreater;
+ //template <Dim2 DIM, typename First = std::less<Coord>, typename Second = std::less<Coord> > LexOrder;
/** @brief Lexicographical ordering functor with runtime dimension. */
- class LexOrderRt {
- public:
- LexOrderRt(Dim2 d) : dim(d) {}
- inline bool operator()(Point const &a, Point const &b);
+ struct LexLessRt {
+ LexLessRt(Dim2 d) : dim(d) {}
+ inline bool operator()(Point const &a, Point const &b) const;
+ private:
+ Dim2 dim;
+ };
+ struct LexGreaterRt {
+ LexGreaterRt(Dim2 d) : dim(d) {}
+ inline bool operator()(Point const &a, Point const &b) const;
private:
Dim2 dim;
};
+ //template <typename First = std::less<Coord>, typename Second = std::less<Coord> > LexOrder
friend inline std::ostream &operator<< (std::ostream &out_file, const Geom::Point &in_pnt);
};
@@ -245,18 +262,47 @@ inline std::ostream &operator<< (std::ostream &out_file, const Geom::Point &in_p
return out_file;
}
-template<> struct Point::LexOrder<X> {
- bool operator()(Point const &a, Point const &b) {
+template<> struct Point::LexLess<X> {
+ typedef std::less<Coord> Primary;
+ typedef std::less<Coord> Secondary;
+ typedef std::less<Coord> XOrder;
+ typedef std::less<Coord> YOrder;
+ bool operator()(Point const &a, Point const &b) const {
return a[X] < b[X] || (a[X] == b[X] && a[Y] < b[Y]);
}
};
-template<> struct Point::LexOrder<Y> {
- bool operator()(Point const &a, Point const &b) {
+template<> struct Point::LexLess<Y> {
+ typedef std::less<Coord> Primary;
+ typedef std::less<Coord> Secondary;
+ typedef std::less<Coord> XOrder;
+ typedef std::less<Coord> YOrder;
+ bool operator()(Point const &a, Point const &b) const {
return a[Y] < b[Y] || (a[Y] == b[Y] && a[X] < b[X]);
}
};
-inline bool Point::LexOrderRt::operator()(Point const &a, Point const &b) {
- return dim ? Point::LexOrder<Y>()(a, b) : Point::LexOrder<X>()(a, b);
+template<> struct Point::LexGreater<X> {
+ typedef std::greater<Coord> Primary;
+ typedef std::greater<Coord> Secondary;
+ typedef std::greater<Coord> XOrder;
+ typedef std::greater<Coord> YOrder;
+ bool operator()(Point const &a, Point const &b) const {
+ return a[X] > b[X] || (a[X] == b[X] && a[Y] > b[Y]);
+ }
+};
+template<> struct Point::LexGreater<Y> {
+ typedef std::greater<Coord> Primary;
+ typedef std::greater<Coord> Secondary;
+ typedef std::greater<Coord> XOrder;
+ typedef std::greater<Coord> YOrder;
+ bool operator()(Point const &a, Point const &b) const {
+ return a[Y] > b[Y] || (a[Y] == b[Y] && a[X] > b[X]);
+ }
+};
+inline bool Point::LexLessRt::operator()(Point const &a, Point const &b) const {
+ return dim ? Point::LexLess<Y>()(a, b) : Point::LexLess<X>()(a, b);
+}
+inline bool Point::LexGreaterRt::operator()(Point const &a, Point const &b) const {
+ return dim ? Point::LexGreater<Y>()(a, b) : Point::LexGreater<X>()(a, b);
}
/** @brief Compute the second (Euclidean) norm of @a p.
@@ -265,8 +311,7 @@ inline bool Point::LexOrderRt::operator()(Point const &a, Point const &b) {
* in a <code>double</code>.
* @return \f$\sqrt{p_X^2 + p_Y^2}\f$
* @relates Point */
-inline Coord L2(Point const &p)
-{
+inline Coord L2(Point const &p) {
return p.length();
}
@@ -274,28 +319,10 @@ inline Coord L2(Point const &p)
* Warning: this can overflow where L2 won't.
* @return \f$p_X^2 + p_Y^2\f$
* @relates Point */
-inline Coord L2sq(Point const &p)
-{
+inline Coord L2sq(Point const &p) {
return p[0]*p[0] + p[1]*p[1];
}
-//IMPL: NearConcept
-/** @brief Nearness predicate for points.
- * True if neither coordinate of @a a is further than @a eps from the corresponding
- * coordinate of @a b.
- * @relates Point */
-inline bool are_near(Point const &a, Point const &b, double const eps=EPSILON)
-{
- return ( are_near(a[X],b[X],eps) && are_near(a[Y],b[Y],eps) );
-}
-
-/** @brief Return a point halfway between the specified ones.
- * @relates Point */
-inline Point middle_point(Point const& P1, Point const& P2)
-{
- return (P1 + P2) / 2;
-}
-
/** @brief Returns p * Geom::rotate_degrees(90), but more efficient.
*
* Angle direction in 2Geom: If you use the traditional mathematics convention that y
@@ -305,8 +332,7 @@ inline Point middle_point(Point const& P1, Point const& P2)
*
* There is no function to rotate by -90 degrees: use -rot90(p) instead.
* @relates Point */
-inline Point rot90(Point const &p)
-{
+inline Point rot90(Point const &p) {
return Point(-p[Y], p[X]);
}
@@ -317,9 +343,14 @@ inline Point rot90(Point const &p)
* @return Point on a line between a and b. The ratio of its distance from a
* and the distance between a and b will be equal to t.
* @relates Point */
-inline Point lerp(double const t, Point const &a, Point const &b)
-{
- return (a * (1 - t) + b * t);
+inline Point lerp(Coord t, Point const &a, Point const &b) {
+ return (1 - t) * a + t * b;
+}
+
+/** @brief Return a point halfway between the specified ones.
+ * @relates Point */
+inline Point middle_point(Point const &p1, Point const &p2) {
+ return lerp(0.5, p1, p2);
}
/** @brief Compute the dot product of a and b.
@@ -328,45 +359,60 @@ inline Point lerp(double const t, Point const &a, Point const &b)
* and the sign depends on whether they point in the same direction (+) or opposite ones (-).
* @return \f$a \cdot b = a_X b_X + a_Y b_Y\f$.
* @relates Point */
-inline Coord dot(Point const &a, Point const &b)
-{
- return a[0] * b[0] + a[1] * b[1];
+inline Coord dot(Point const &a, Point const &b) {
+ return a[X] * b[X] + a[Y] * b[Y];
}
/** @brief Compute the 2D cross product.
- * Defined as dot(a, b.cw()). This means it will be zero for parallel vectors,
- * and its absolute value highest for perpendicular vectors.
+ * This is also known as "perp dot product". It will be zero for parallel vectors,
+ * and the absolute value will be highest for perpendicular vectors.
+ * @return \f$a \times b = a_X b_Y - a_Y b_X\f$.
* @relates Point*/
inline Coord cross(Point const &a, Point const &b)
{
- return dot(a, b.cw());
+ // equivalent implementation:
+ // return dot(a, b.ccw());
+ return a[X] * b[Y] - a[Y] * b[X];
}
-/** @brief Compute the (Euclidean) distance between points.
- * @relates Point */
-inline Coord distance (Point const &a, Point const &b)
-{
- return L2(a - b);
+/// Compute the (Euclidean) distance between points.
+/// @relates Point
+inline Coord distance (Point const &a, Point const &b) {
+ return (a - b).length();
}
-/** @brief Compute the square of the distance between points.
- * @relates Point */
-inline Coord distanceSq (Point const &a, Point const &b)
-{
+/// Compute the square of the distance between points.
+/// @relates Point
+inline Coord distanceSq (Point const &a, Point const &b) {
return L2sq(a - b);
}
+//IMPL: NearConcept
+/// Test whether two points are no further apart than some threshold.
+/// @relates Point
+inline bool are_near(Point const &a, Point const &b, double eps = EPSILON) {
+ return are_near(distance(a, b), 0, eps);
+}
+
+/// Test whether three points lie approximately on the same line.
+/// @relates Point
+inline bool are_collinear(Point const& p1, Point const& p2, Point const& p3,
+ double eps = EPSILON)
+{
+ return are_near( cross(p3, p2) - cross(p3, p1) + cross(p2, p1), 0, eps);
+}
+
Point unit_vector(Point const &a);
Coord L1(Point const &p);
Coord LInfty(Point const &p);
bool is_zero(Point const &p);
-bool is_unit_vector(Point const &p);
+bool is_unit_vector(Point const &p, Coord eps = EPSILON);
double atan2(Point const &p);
double angle_between(Point const &a, Point const &b);
Point abs(Point const &b);
Point constrain_angle(Point const &A, Point const &B, unsigned int n = 4, Geom::Point const &dir = Geom::Point(1,0));
-} /* namespace Geom */
+} // end namespace Geom
// This is required to fix a bug in GCC 4.3.3 (and probably others) that causes the compiler
// to try to instantiate the iterator_traits template and fail. Probably it thinks that Point
@@ -375,7 +421,7 @@ namespace std {
template <> class iterator_traits<Geom::Point> {};
}
-#endif /* !SEEN_Geom_POINT_H */
+#endif // LIB2GEOM_SEEN_POINT_H
/*
Local Variables:
diff --git a/src/2geom/quadtree.cpp b/src/2geom/quadtree.cpp
index 98030c424..76dc68774 100644
--- a/src/2geom/quadtree.cpp
+++ b/src/2geom/quadtree.cpp
@@ -211,10 +211,6 @@ void QuadTree::insert(double x0, double y0, double x1, double y1, int shape) {
// check if rect's bounding box has size 1x1. This means that rect is defined by 2 points
// that are in the same place.
if( ( fabs(bxx0 - bxx1) < 1.0 ) && ( fabs(byy0 - byy1) < 1.0 )){
- bxx0 = floor(bxx0);
- bxx1 = floor(bxx1);
- byy0 = floor(byy0);
- byy1 = floor(byy1);
break;
}
diff --git a/src/2geom/quadtree.h b/src/2geom/quadtree.h
index 949a9b898..62f2b8321 100644
--- a/src/2geom/quadtree.h
+++ b/src/2geom/quadtree.h
@@ -32,6 +32,9 @@
*
*/
+#ifndef LIB2GEOM_SEEN_QUADTREE_H
+#define LIB2GEOM_SEEN_QUADTREE_H
+
#include <vector>
#include <cassert>
@@ -64,6 +67,8 @@ public:
* this case should be never reached */
assert(false);
}
+ assert(false);
+ return Rect();
}
};
@@ -87,6 +92,7 @@ private:
};
+#endif
/*
Local Variables:
mode:c++
diff --git a/src/2geom/ray.h b/src/2geom/ray.h
index 2fda06ff5..a336e3132 100644
--- a/src/2geom/ray.h
+++ b/src/2geom/ray.h
@@ -98,7 +98,7 @@ public:
}
return result;
}
- Coord nearestPoint(Point const& point) const {
+ Coord nearestTime(Point const& point) const {
if ( isDegenerate() ) return 0;
double t = dot(point - _origin, _versor);
if (t < 0) t = 0;
@@ -123,7 +123,7 @@ public:
inline
double distance(Point const& _point, Ray const& _ray) {
- double t = _ray.nearestPoint(_point);
+ double t = _ray.nearestTime(_point);
return ::Geom::distance(_point, _ray.pointAt(t));
}
diff --git a/src/2geom/rect.cpp b/src/2geom/rect.cpp
index 0cb842d29..383e72c8e 100644
--- a/src/2geom/rect.cpp
+++ b/src/2geom/rect.cpp
@@ -84,6 +84,17 @@ Coord distance(Point const &p, Rect const &rect)
return hypot(dx, dy);
}
+Coord distanceSq(Point const &p, OptRect const &rect)
+{
+ if (!rect) return std::numeric_limits<Coord>::max();
+ return distanceSq(p, *rect);
+}
+Coord distance(Point const &p, OptRect const &rect)
+{
+ if (!rect) return std::numeric_limits<Coord>::max();
+ return distance(p, *rect);
+}
+
} // namespace Geom
/*
diff --git a/src/2geom/rect.h b/src/2geom/rect.h
index 2516bcfa6..51daf6b5a 100644
--- a/src/2geom/rect.h
+++ b/src/2geom/rect.h
@@ -75,6 +75,10 @@ public:
* @param eps Maximum value of the area to consider empty
* @return True if rectangle has an area smaller than tolerance, false otherwise */
bool hasZeroArea(Coord eps = EPSILON) const { return (area() <= eps); }
+ /// Check whether the rectangle has finite area
+ bool isFinite() const { return (*this)[X].isFinite() && (*this)[Y].isFinite(); }
+ /// Calculate the diameter of the smallest circle that would contain the rectangle.
+ Coord diameter() const { return distance(corner(0), corner(2)); }
/// @}
/// @name Test other rectangles and points for inclusion.
@@ -153,6 +157,10 @@ public:
Coord distanceSq(Point const &p, Rect const &rect);
Coord distance(Point const &p, Rect const &rect);
+/// Minimum square of distance to rectangle, or infinity if empty.
+Coord distanceSq(Point const &p, OptRect const &rect);
+/// Minimum distance to rectangle, or infinity if empty.
+Coord distance(Point const &p, OptRect const &rect);
inline bool Rect::interiorContains(OptRect const &r) const {
return !r || interiorContains(static_cast<Rect const &>(*r));
diff --git a/src/2geom/recursive-bezier-intersection.cpp b/src/2geom/recursive-bezier-intersection.cpp
index 548065196..e86192f54 100644
--- a/src/2geom/recursive-bezier-intersection.cpp
+++ b/src/2geom/recursive-bezier-intersection.cpp
@@ -13,7 +13,6 @@
unsigned intersect_steps = 0;
using std::vector;
-using std::swap;
namespace Geom {
@@ -33,7 +32,7 @@ public:
minax = p[0][X]; // These are the most likely to be extremal
maxax = p.back()[X];
if( minax > maxax )
- swap(minax, maxax);
+ std::swap(minax, maxax);
for(unsigned i = 1; i < p.size()-1; i++) {
if( p[i][X] < minax )
minax = p[i][X];
@@ -44,7 +43,7 @@ public:
minay = p[0][Y]; // These are the most likely to be extremal
maxay = p.back()[Y];
if( minay > maxay )
- swap(minay, maxay);
+ std::swap(minay, maxay);
for(unsigned i = 1; i < p.size()-1; i++) {
if( p[i][Y] < minay )
minay = p[i][Y];
diff --git a/src/2geom/region.cpp b/src/2geom/region.cpp
deleted file mode 100644
index 8cfb1c68c..000000000
--- a/src/2geom/region.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-#include <2geom/region.h>
-#include <2geom/utils.h>
-
-#include <2geom/shape.h>
-
-namespace Geom {
-
-Region Region::operator*(Affine const &m) const {
- Region r((m.flips() ? boundary.reverse() : boundary) * m, fill);
- if(box && m.isZoom()) r.box = (*box) * m;
- return r;
-}
-
-bool Region::invariants() const {
- return self_crossings(boundary).empty();
-}
-
-unsigned outer_index(Regions const &ps) {
- if(ps.size() <= 1 || ps[0].contains(ps[1])) {
- return 0;
- } else {
- /* Since we've already shown that chunks[0] is not outside
- it can be used as an exemplar inner. */
- Point exemplar = Path(ps[0]).initialPoint();
- for(unsigned i = 1; i < ps.size(); i++) {
- if(ps[i].contains(exemplar)) {
- return i;
- }
- }
- }
- return ps.size();
-}
-
-}
-
-/*
- 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/2geom/region.h b/src/2geom/region.h
deleted file mode 100644
index 06a4f63e9..000000000
--- a/src/2geom/region.h
+++ /dev/null
@@ -1,130 +0,0 @@
-/**
- * \file
- * \brief Uncrossed path for boolean algorithms
- *//*
- * Authors:
- * ? <?@?.?>
- *
- * Copyright ?-? authors
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- *
- */
-
-#ifndef __2GEOM_REGION_H
-#define __2GEOM_REGION_H
-
-#include <2geom/path.h>
-#include <2geom/path-intersection.h>
-
-namespace Geom {
-
-class Shape;
-
-class Region {
- friend Crossings crossings(Region const &a, Region const &b);
- friend class Shape;
- friend Shape shape_boolean(bool rev, Shape const & a, Shape const & b, CrossingSet const & crs);
-
- Path boundary;
- mutable OptRect box;
- bool fill;
- public:
- Region() : fill(true) {}
- explicit Region(Path const &p) : boundary(p) { fill = path_direction(p); }
- Region(Path const &p, bool dir) : boundary(p), fill(dir) {}
- Region(Path const &p, OptRect const &b) : boundary(p), box(b) { fill = path_direction(p); }
- Region(Path const &p, OptRect const &b, bool dir) : boundary(p), box(b), fill(dir) {}
-
- unsigned size() const { return boundary.size(); }
-
- bool isFill() const { return fill; }
- Region asFill() const { if(fill) return Region(*this); else return inverse(); }
- Region asHole() const { if(fill) return inverse(); else return Region(*this); }
-
- operator Path() const { return boundary; }
- Rect boundsFast() const {
- if(!box) box = boundary.boundsFast(); /// \todo this doesn't look right at all...
- return *box;
- }
-
- bool contains(Point const &p) const {
- if(box && !box->contains(p)) return false;
- return Geom::contains(boundary, p);
- }
- bool contains(Region const &other) const { return contains(other.boundary.initialPoint()); }
-
- bool includes(Point const &p) const {
- return logical_xor(!fill, contains(p));
- }
-
- Region inverse() const { return Region(boundary.reverse(), box, !fill); }
-
- Region operator*(Affine const &m) const;
-
- bool invariants() const;
-};
-
-typedef std::vector<Region> Regions;
-
-unsigned outer_index(Regions const &ps);
-
-//assumes they're already sanitized somewhat
-inline Regions regions_from_paths(std::vector<Path> const &ps) {
- Regions res;
- for(unsigned i = 0; i < ps.size(); i++)
- res.push_back(Region(ps[i]));
- return res;
-}
-
-inline std::vector<Path> paths_from_regions(Regions const &rs) {
- std::vector<Path> res;
- for(unsigned i = 0; i < rs.size(); i++)
- res.push_back(rs[i]);
- return res;
-}
-
-Regions sanitize_path(Path const &p);
-
-Regions region_boolean(bool rev, Region const & a, Region const & b, Crossings const &cr);
-Regions region_boolean(bool rev, Region const & a, Region const & b, Crossings const & cr_a, Crossings const & cr_b);
-
-inline Regions region_boolean(bool rev, Region const & a, Region const & b) {
- return region_boolean(rev, a, b, crossings(a, b));
-}
-
-}
-
-#endif
-
-/*
- 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/2geom/sbasis-2d.cpp b/src/2geom/sbasis-2d.cpp
index aa5018e9e..53b09cd35 100644
--- a/src/2geom/sbasis-2d.cpp
+++ b/src/2geom/sbasis-2d.cpp
@@ -169,7 +169,7 @@ sb2d_cubic_solve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B){
std::vector<D2<SBasis> > candidates = cubics_fitting_curvature(A,B,V0,V1,D2fVV0,D2fVV1);
if (candidates.empty()) {
- return D2<SBasis>(Linear(A[X],B[X]),Linear(A[Y],B[Y]));
+ return D2<SBasis>(SBasis(Linear(A[X],B[X])), SBasis(Linear(A[Y],B[Y])));
}
//TODO: I'm sure std algorithm could do that for me...
double error = -1;
diff --git a/src/2geom/sbasis-2d.h b/src/2geom/sbasis-2d.h
index 00429e259..c7d9b000a 100644
--- a/src/2geom/sbasis-2d.h
+++ b/src/2geom/sbasis-2d.h
@@ -33,8 +33,8 @@
*
*/
-#ifndef SEEN_SBASIS_2D_H
-#define SEEN_SBASIS_2D_H
+#ifndef LIB2GEOM_SEEN_SBASIS_2D_H
+#define LIB2GEOM_SEEN_SBASIS_2D_H
#include <vector>
#include <cassert>
#include <algorithm>
@@ -356,8 +356,9 @@ sb2dsolve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B, unsigne
D2<SBasis>
sb2d_cubic_solve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B);
-};
+} // end namespace Geom
+#endif
/*
Local Variables:
mode:c++
@@ -368,4 +369,3 @@ sb2d_cubic_solve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B);
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
-#endif
diff --git a/src/2geom/sbasis-curve.h b/src/2geom/sbasis-curve.h
index a5c3f2ca7..a7b74dcee 100644
--- a/src/2geom/sbasis-curve.h
+++ b/src/2geom/sbasis-curve.h
@@ -37,7 +37,7 @@
#define LIB2GEOM_SEEN_SBASIS_CURVE_H
#include <2geom/curve.h>
-#include <2geom/nearest-point.h>
+#include <2geom/nearest-time.h>
#include <2geom/sbasis-geometric.h>
#include <2geom/transforms.h>
@@ -84,11 +84,10 @@ public:
explicit SBasisCurve(D2<SBasis> const &sb) : inner(sb) {}
explicit SBasisCurve(Curve const &other) : inner(other.toSBasis()) {}
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
virtual Curve *duplicate() const { return new SBasisCurve(*this); }
virtual Point initialPoint() const { return inner.at0(); }
virtual Point finalPoint() const { return inner.at1(); }
- virtual bool isDegenerate() const { return inner.isConstant(); }
+ virtual bool isDegenerate() const { return inner.isConstant(0); }
virtual Point pointAt(Coord t) const { return inner.valueAt(t); }
virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const {
return inner.valueAndDerivatives(t, n);
@@ -106,33 +105,33 @@ public:
return bounds_local(inner, i, deg);
}
virtual std::vector<Coord> roots(Coord v, Dim2 d) const { return Geom::roots(inner[d] - v); }
- virtual Coord nearestPoint( Point const& p, Coord from = 0, Coord to = 1 ) const {
- return nearest_point(p, inner, from, to);
+ virtual Coord nearestTime( Point const& p, Coord from = 0, Coord to = 1 ) const {
+ return nearest_time(p, inner, from, to);
}
- virtual std::vector<Coord> allNearestPoints( Point const& p, Coord from = 0,
+ virtual std::vector<Coord> allNearestTimes( Point const& p, Coord from = 0,
Coord to = 1 ) const
{
- return all_nearest_points(p, inner, from, to);
+ return all_nearest_times(p, inner, from, to);
}
virtual Coord length(Coord tolerance) const { return ::Geom::length(inner, tolerance); }
virtual Curve *portion(Coord f, Coord t) const {
return new SBasisCurve(Geom::portion(inner, f, t));
}
- virtual Curve *transformed(Affine const &m) const { return new SBasisCurve(inner * m); }
- virtual Curve &operator*=(Translate const &m) {
- inner += m.vector();
- return *this;
- };
+ virtual void transform(Affine const &m) { inner = inner * m; }
virtual Curve *derivative() const {
return new SBasisCurve(Geom::derivative(inner));
}
virtual D2<SBasis> toSBasis() const { return inner; }
+ virtual bool operator==(Curve const &c) const {
+ SBasisCurve const *other = dynamic_cast<SBasisCurve const *>(&c);
+ if (!other) return false;
+ return inner == other->inner;
+ }
virtual int degreesOfFreedom() const {
return inner[0].degreesOfFreedom() + inner[1].degreesOfFreedom();
}
-#endif
};
} // end namespace Geom
diff --git a/src/2geom/sbasis-geometric.h b/src/2geom/sbasis-geometric.h
index 43c624fb4..7f1e8aaba 100644
--- a/src/2geom/sbasis-geometric.h
+++ b/src/2geom/sbasis-geometric.h
@@ -1,15 +1,6 @@
-#ifndef _SBASIS_GEOMETRIC
-#define _SBASIS_GEOMETRIC
-#include <2geom/d2.h>
-#include <2geom/piecewise.h>
-#include <vector>
-
/**
* \file
* \brief two-dimensional geometric operators.
- *
- * Copyright 2007, JFBarraud
- * Copyright 2007, njh
*
* These operators are built on a more 'polynomially robust'
* transformation to map a function that takes a [0,1] parameter to a
@@ -21,9 +12,42 @@
* from the various tangent directions at each end (angular jet). As
* a result, the final path has a convergence behaviour derived from
* that of the sin and cos series. -- njh
+ *//*
+ * Copyright 2007, JFBarraud
+ * Copyright 2007, njh
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
*/
-namespace Geom{
+#ifndef LIB2GEOM_SEEN_SBASIS_GEOMETRIC_H
+#define LIB2GEOM_SEEN_SBASIS_GEOMETRIC_H
+
+#include <2geom/d2.h>
+#include <2geom/piecewise.h>
+#include <vector>
+
+namespace Geom {
Piecewise<D2<SBasis> >
cutAtRoots(Piecewise<D2<SBasis> > const &M, double tol=1e-4);
diff --git a/src/2geom/sbasis-math.h b/src/2geom/sbasis-math.h
index e6d40a3de..e191dae62 100644
--- a/src/2geom/sbasis-math.h
+++ b/src/2geom/sbasis-math.h
@@ -1,7 +1,6 @@
-/**
- * \file
- * \brief some std functions to work with (pw)s-basis
- *
+/** @file
+ * @brief some std functions to work with (pw)s-basis
+ *//*
* Authors:
* Jean-Francois Barraud
*
@@ -36,8 +35,8 @@
//TODO: in all these functions, compute 'order' according to 'tol'.
//TODO: use template to define the pw version automatically from the sb version?
-#ifndef SEEN_GEOM_SB_CALCULS_H
-#define SEEN_GEOM_SB_CALCULS_H
+#ifndef LIB2GEOM_SEEN_SBASIS_MATH_H
+#define LIB2GEOM_SEEN_SBASIS_MATH_H
#include <2geom/sbasis.h>
diff --git a/src/2geom/sbasis-poly.h b/src/2geom/sbasis-poly.h
index e0bef9333..81eeca72c 100644
--- a/src/2geom/sbasis-poly.h
+++ b/src/2geom/sbasis-poly.h
@@ -1,13 +1,6 @@
-#ifndef _SBASIS_TO_POLY
-#define _SBASIS_TO_POLY
-
-#include <2geom/poly.h>
-#include <2geom/sbasis.h>
-
-/**
- * \file
- * \brief Conversion between SBasis and Poly. Not recommended for general use due to instability.
- *
+/** @file
+ * @brief Conversion between SBasis and Poly. Not recommended for general use due to instability.
+ *//*
* Authors:
* ? <?@?.?>
*
@@ -35,9 +28,14 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
- *
*/
+#ifndef LIB2GEOM_SEEN_SBASIS_POLY_H
+#define LIB2GEOM_SEEN_SBASIS_POLY_H
+
+#include <2geom/poly.h>
+#include <2geom/sbasis.h>
+
namespace Geom{
SBasis poly_to_sbasis(Poly const & p);
@@ -45,6 +43,7 @@ Poly sbasis_to_poly(SBasis const & s);
};
+#endif
/*
Local Variables:
mode:c++
@@ -55,5 +54,3 @@ Poly sbasis_to_poly(SBasis const & s);
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
-
-#endif
diff --git a/src/2geom/sbasis-roots.cpp b/src/2geom/sbasis-roots.cpp
index acf4e1abc..57bef4c0f 100644
--- a/src/2geom/sbasis-roots.cpp
+++ b/src/2geom/sbasis-roots.cpp
@@ -1,7 +1,38 @@
-/** root finding for sbasis functions.
- * Copyright 2006 N Hurst
- * Copyright 2007 JF Barraud
+/**
+ * @file
+ * @brief Root finding for sbasis functions.
+ *//*
+ * Authors:
+ * Nathan Hurst <njh@njhurst.com>
+ * JF Barraud
+ * Copyright 2006-2007 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
*
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+ /*
* It is more efficient to find roots of f(t) = c_0, c_1, ... all at once, rather than iterating.
*
* Todo/think about:
@@ -98,20 +129,20 @@ OptInterval bounds_fast(const SBasis &sb, int order) {
double b=sb[j][1];
double v, t = 0;
- v = res[0];
+ v = res.min();
if (v<0) t = ((b-a)/v+1)*0.5;
if (v>=0 || t<0 || t>1) {
- res[0] = std::min(a,b);
- }else{
- res[0]=lerp(t, a+v*t, b);
+ res.setMin(std::min(a,b));
+ } else {
+ res.setMin(lerp(t, a+v*t, b));
}
- v = res[1];
+ v = res.max();
if (v>0) t = ((b-a)/v+1)*0.5;
if (v<=0 || t<0 || t>1) {
- res[1] = std::max(a,b);
+ res.setMax(std::max(a,b));
}else{
- res[1]=lerp(t, a+v*t, b);
+ res.setMax(lerp(t, a+v*t, b));
}
}
if (order>0) res*=std::pow(.25,order);
@@ -576,6 +607,7 @@ std::vector<double> roots1(SBasis const & s, Interval const ivl) {
/** Find all t s.t s(t) = 0
\param a sbasis function
+ \see Bezier::roots
\returns vector of zeros (roots)
*/
diff --git a/src/2geom/sbasis-to-bezier.cpp b/src/2geom/sbasis-to-bezier.cpp
index a2e4253d2..fe7bbc91c 100644
--- a/src/2geom/sbasis-to-bezier.cpp
+++ b/src/2geom/sbasis-to-bezier.cpp
@@ -37,7 +37,7 @@
#include <2geom/choose.h>
#include <2geom/path-sink.h>
#include <2geom/exception.h>
-#include <2geom/convex-cover.h>
+#include <2geom/convex-hull.h>
#include <iostream>
@@ -153,6 +153,15 @@ void sbasis_to_bezier (Bezier & bz, SBasis const& sb, size_t sz)
bz[n] = sb[0][1];
}
+void sbasis_to_bezier(D2<Bezier> &bz, D2<SBasis> const &sb, size_t sz)
+{
+ if (sz == 0) {
+ sz = std::max(sb[X].size(), sb[Y].size())*2;
+ }
+ sbasis_to_bezier(bz[X], sb[X], sz);
+ sbasis_to_bezier(bz[Y], sb[Y], sz);
+}
+
/** Changes the basis of p to be Bernstein.
\param p the D2 Symmetric basis polynomial
\returns the D2 Bernstein basis polynomial
@@ -161,24 +170,9 @@ void sbasis_to_bezier (Bezier & bz, SBasis const& sb, size_t sz)
*/
void sbasis_to_bezier (std::vector<Point> & bz, D2<SBasis> const& sb, size_t sz)
{
- Bezier bzx, bzy;
- if(sz == 0) {
- sz = std::max(sb[X].size(), sb[Y].size())*2;
- }
- sbasis_to_bezier(bzx, sb[X], sz);
- sbasis_to_bezier(bzy, sb[Y], sz);
- assert(bzx.size() == bzy.size());
- size_t n = (bzx.size() >= bzy.size()) ? bzx.size() : bzy.size();
-
- bz.resize(n, Point(0,0));
- for (size_t i = 0; i < bzx.size(); ++i)
- {
- bz[i][X] = bzx[i];
- }
- for (size_t i = 0; i < bzy.size(); ++i)
- {
- bz[i][Y] = bzy[i];
- }
+ D2<Bezier> bez;
+ sbasis_to_bezier(bez, sb, sz);
+ bz = bezier_points(bez);
}
/** Changes the basis of p to be Bernstein.
@@ -238,7 +232,7 @@ void sbasis_to_cubic_bezier (std::vector<Point> & bz, D2<SBasis> const& sb)
// is midpoint in hull: if not, the solution will be ill-conditioned, LP Bug 1428683
- if (!bezhull.contains_point(Geom::Point(midx, midy)))
+ if (!bezhull.contains(Geom::Point(midx, midy)))
return;
// calculate Bezier control arms
@@ -264,11 +258,11 @@ void sbasis_to_cubic_bezier (std::vector<Point> & bz, D2<SBasis> const& sb)
dely[1] = 0;
} else if (std::abs(xprime[1]*yprime[0] - yprime[1]*xprime[0]) > // general case : fit mid fxn value
0.002 * std::abs(xprime[1]*xprime[0] + yprime[1]*yprime[0])) { // approx. 0.1 degree of angle
- denom = xprime[1]*yprime[0] - yprime[1]*xprime[0];
+ denom = 3.0*(xprime[1]*yprime[0] - yprime[1]*xprime[0]);
for (int i = 0; i < 2; ++i) {
numer = xprime[1 - i]*midy - yprime[1 - i]*midx;
- delx[i] = xprime[i]*numer/denom/3;
- dely[i] = yprime[i]*numer/denom/3;
+ delx[i] = xprime[i]*numer/denom;
+ dely[i] = yprime[i]*numer/denom;
}
} else if ((xprime[0]*xprime[1] < 0) || (yprime[0]*yprime[1] < 0)) { // symmetric case : use distance of closest approach
numer = midx*xprime[0] + midy*yprime[0];
@@ -514,7 +508,7 @@ path_from_sbasis(D2<SBasis> const &B, double tol, bool only_cubicbeziers) {
If only_cubicbeziers is true, the resulting path may only contain CubicBezier curves.
TODO: some of this logic should be lifted into svg-path
*/
-std::vector<Geom::Path>
+PathVector
path_from_piecewise(Geom::Piecewise<Geom::D2<Geom::SBasis> > const &B, double tol, bool only_cubicbeziers) {
Geom::PathBuilder pb;
if(B.size() == 0) return pb.peek();
diff --git a/src/2geom/sbasis-to-bezier.h b/src/2geom/sbasis-to-bezier.h
index 07511f4a4..eadb47bff 100644
--- a/src/2geom/sbasis-to-bezier.h
+++ b/src/2geom/sbasis-to-bezier.h
@@ -32,11 +32,11 @@
*
*/
-#ifndef _SBASIS_TO_BEZIER
-#define _SBASIS_TO_BEZIER
+#ifndef LIB2GEOM_SEEN_SBASIS_TO_BEZIER_H
+#define LIB2GEOM_SEEN_SBASIS_TO_BEZIER_H
#include <2geom/d2.h>
-#include <2geom/path.h>
+#include <2geom/pathvector.h>
#include <vector>
@@ -44,7 +44,8 @@ namespace Geom {
class PathBuilder;
-void sbasis_to_bezier (Bezier & bz, SBasis const& sb, size_t sz = 0);
+void sbasis_to_bezier (Bezier &bz, SBasis const &sb, size_t sz = 0);
+void sbasis_to_bezier (D2<Bezier> &bz, D2<SBasis> const &sb, size_t sz = 0);
void sbasis_to_bezier (std::vector<Point> & bz, D2<SBasis> const& sb, size_t sz = 0);
void sbasis_to_cubic_bezier (std::vector<Point> & bz, D2<SBasis> const& sb);
void bezier_to_sbasis (SBasis & sb, Bezier const& bz);
@@ -65,7 +66,7 @@ sbasis_to_bezier(D2<SBasis> const &B, unsigned q = 0);
#endif
-std::vector<Path> path_from_piecewise(Piecewise<D2<SBasis> > const &B, double tol, bool only_cubicbeziers = false);
+PathVector path_from_piecewise(Piecewise<D2<SBasis> > const &B, double tol, bool only_cubicbeziers = false);
Path path_from_sbasis(D2<SBasis> const &B, double tol, bool only_cubicbeziers = false);
inline Path cubicbezierpath_from_sbasis(D2<SBasis> const &B, double tol)
@@ -73,10 +74,7 @@ inline Path cubicbezierpath_from_sbasis(D2<SBasis> const &B, double tol)
} // end namespace Geom
-
-
#endif
-
/*
Local Variables:
mode:c++
diff --git a/src/2geom/sbasis.cpp b/src/2geom/sbasis.cpp
index b56e03c74..4f1df621e 100644
--- a/src/2geom/sbasis.cpp
+++ b/src/2geom/sbasis.cpp
@@ -466,6 +466,15 @@ SBasis compose(SBasis const &a, SBasis const &b, unsigned k) {
return r;
}
+SBasis portion(const SBasis &t, double from, double to) {
+ double fv = t.valueAt(from);
+ double tv = t.valueAt(to);
+ SBasis ret = compose(t, Linear(from, to));
+ ret.at0() = fv;
+ ret.at1() = tv;
+ return ret;
+}
+
/*
Inversion algorithm. The notation is certainly very misleading. The
pseudocode should say:
@@ -632,7 +641,6 @@ SBasis compose_inverse(SBasis const &f, SBasis const &g, unsigned order, double
//TODO: handle det~0!!
if (fabs(det)<zero){
- det = zero;
a=b=0;
}else{
a=( q01*r10-q10*r01)/det;
diff --git a/src/2geom/sbasis.h b/src/2geom/sbasis.h
index ca864ac7c..787e8b722 100644
--- a/src/2geom/sbasis.h
+++ b/src/2geom/sbasis.h
@@ -1,7 +1,6 @@
-/**
- * \file
- * \brief Defines S-power basis function class
- *
+/** @file
+ * @brief Polynomial in symmetric power basis (S-basis)
+ *//*
* Authors:
* Nathan Hurst <njh@mail.csse.monash.edu.au>
* Michael Sloan <mgsloan@gmail.com>
@@ -32,8 +31,8 @@
* the specific language governing rights and limitations.
*/
-#ifndef SEEN_SBASIS_H
-#define SEEN_SBASIS_H
+#ifndef LIB2GEOM_SEEN_SBASIS_H
+#define LIB2GEOM_SEEN_SBASIS_H
#include <vector>
#include <cassert>
#include <iostream>
@@ -61,13 +60,13 @@ class SBasis : public SBasisN<1>;
};
#else
-namespace Geom{
+namespace Geom {
/**
-* \brief S-power basis function class
-*
-* An empty SBasis is identically 0. */
-class SBasis{
+ * @brief Polynomial in symmetric power basis
+ * @ingroup Fragments
+ */
+class SBasis {
std::vector<Linear> d;
void push_back(Linear const&l) { d.push_back(l); }
@@ -101,11 +100,17 @@ public:
SBasis() {}
- explicit SBasis(double a) {
- push_back(Linear(a,a));
+ explicit SBasis(double a)
+ : d(1)
+ {
+ d[0][0] = a;
+ d[0][1] = a;
}
- explicit SBasis(double a, double b) {
- push_back(Linear(a,b));
+ explicit SBasis(double a, double b)
+ : d(1)
+ {
+ d[0][0] = a;
+ d[0][1] = b;
}
SBasis(SBasis const & a) :
d(a.d)
@@ -117,10 +122,69 @@ public:
push_back(bo);
}
SBasis(Linear* bo) {
- push_back(*bo);
+ if (bo) {
+ push_back(*bo);
+ }
}
explicit SBasis(size_t n, Linear const&l) : d(n, l) {}
+ SBasis(Coord c0, Coord c1, Coord c2, Coord c3)
+ : d(2)
+ {
+ d[0][0] = c0;
+ d[1][0] = c1;
+ d[1][1] = c2;
+ d[0][1] = c3;
+ }
+ SBasis(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5)
+ : d(3)
+ {
+ d[0][0] = c0;
+ d[1][0] = c1;
+ d[2][0] = c2;
+ d[2][1] = c3;
+ d[1][1] = c4;
+ d[0][1] = c5;
+ }
+ SBasis(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5,
+ Coord c6, Coord c7)
+ : d(4)
+ {
+ d[0][0] = c0;
+ d[1][0] = c1;
+ d[2][0] = c2;
+ d[3][0] = c3;
+ d[3][1] = c4;
+ d[2][1] = c5;
+ d[1][1] = c6;
+ d[0][1] = c7;
+ }
+ SBasis(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5,
+ Coord c6, Coord c7, Coord c8, Coord c9)
+ : d(5)
+ {
+ d[0][0] = c0;
+ d[1][0] = c1;
+ d[2][0] = c2;
+ d[3][0] = c3;
+ d[4][0] = c4;
+ d[4][1] = c5;
+ d[3][1] = c6;
+ d[2][1] = c7;
+ d[1][1] = c8;
+ d[0][1] = c9;
+ }
+
+ // construct from a sequence of coefficients
+ template <typename Iter>
+ SBasis(Iter first, Iter last) {
+ assert(std::distance(first, last) % 2 == 0);
+ for (; first != last; ++first) {
+ --last;
+ push_back(Linear(*first, *last));
+ }
+ }
+
//IMPL: FragmentConcept
typedef double output_type;
inline bool isZero(double eps=EPSILON) const {
@@ -140,12 +204,10 @@ public:
}
bool isFinite() const;
- inline double at0() const {
- if(empty()) return 0; else return (*this)[0][0];
- }
- inline double at1() const{
- if(empty()) return 0; else return (*this)[0][1];
- }
+ inline Coord at0() const { return (*this)[0][0]; }
+ inline Coord &at0() { return (*this)[0][0]; }
+ inline Coord at1() const { return (*this)[0][1]; }
+ inline Coord &at1() { return (*this)[0][1]; }
int degreesOfFreedom() const { return size()*2;}
@@ -348,8 +410,8 @@ SBasis compose_inverse(SBasis const &f, SBasis const &g, unsigned order=2, doubl
\return sbasis
\relates SBasis
*/
-inline SBasis portion(const SBasis &t, double from, double to) { return compose(t, Linear(from, to)); }
-inline SBasis portion(const SBasis &t, Interval ivl) { return compose(t, Linear(ivl.min(), ivl.max())); }
+SBasis portion(const SBasis &t, double from, double to);
+inline SBasis portion(const SBasis &t, Interval const &ivl) { return portion(t, ivl.min(), ivl.max()); }
// compute f(g)
inline SBasis
@@ -364,7 +426,10 @@ inline std::ostream &operator<< (std::ostream &out_file, const Linear &bo) {
inline std::ostream &operator<< (std::ostream &out_file, const SBasis & p) {
for(unsigned i = 0; i < p.size(); i++) {
- out_file << p[i] << "s^" << i << " + ";
+ if (i != 0) {
+ out_file << " + ";
+ }
+ out_file << p[i] << "s^" << i;
}
return out_file;
}
diff --git a/src/2geom/shape.cpp b/src/2geom/shape.cpp
deleted file mode 100644
index e9f5e55dc..000000000
--- a/src/2geom/shape.cpp
+++ /dev/null
@@ -1,689 +0,0 @@
-/**
- * \brief Shapes are special paths on which boolops can be performed
- *
- * Authors:
- * Michael G. Sloan <mgsloan@gmail.com>
- * Nathan Hurst <njh@mail.csse.monash.edu.au>
- * MenTaLguY <mental@rydia.net>
- *
- * Copyright 2007-2009 Authors
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- *
- */
-
-#include <2geom/shape.h>
-
-#include <2geom/utils.h>
-#include <2geom/sweep.h>
-#include <2geom/ord.h>
-
-#include <iostream>
-#include <algorithm>
-#include <cstdlib>
-
-//#define SHAPE_DEBUG // turns on debug outputting to cout.
-
-namespace Geom {
-
-// A little sugar for appending a list to another
-template<typename T>
-void append(T &a, T const &b) {
- a.insert(a.end(), b.begin(), b.end());
-}
-
-//Orders a list of indices according to their containment within eachother.
-struct ContainmentOrder {
- std::vector<Region> const *rs;
- explicit ContainmentOrder(std::vector<Region> const *r) : rs(r) {}
- bool operator()(unsigned a, unsigned b) const { return (*rs)[b].contains((*rs)[a]); }
-};
-
-//Returns the list of regions containing a particular point. Useful in tandem with ContainmentOrder
-std::vector<unsigned> Shape::containment_list(Point p) const {
- std::vector<Rect> pnt;
- pnt.push_back(Rect(p, p));
- std::vector<std::vector<unsigned> > cull = sweep_bounds(pnt, bounds(*this));
- std::vector<unsigned> containers;
- if(cull[0].size() == 0) return containers;
- for(unsigned i = 0; i < cull[0].size(); i++)
- if(content[cull[0][i]].contains(p)) containers.push_back(cull[0][i]);
- return containers;
-}
-
-/* Used within shape_boolean and related functions, as the name describes, finds the
- * first false within the list of lists of booleans.
- */
-void first_false(std::vector<std::vector<bool> > visited, unsigned &i, unsigned &j) {
- for(i = 0, j = 0; i < visited.size(); i++) {
- std::vector<bool>::iterator unvisited = std::find(visited[i].begin(), visited[i].end(), false);
- if(unvisited != visited[i].end()) {
- j = unvisited - visited[i].begin();
- break;
- }
- }
-}
-
-// Finds a crossing in a list of them, given the sorting index.
-unsigned find_crossing(Crossings const &cr, Crossing x, unsigned i) {
- return std::lower_bound(cr.begin(), cr.end(), x, CrossingOrder(i)) - cr.begin();
-}
-
-/* This function handles boolean ops on shapes. The first parameter is a bool
- * which determines its behavior in each combination of cases. For proper
- * fill information and noncrossing behavior, the fill data of the regions
- * must be correct. The boolean parameter determines whether the operation
- * is a union or a subtraction. Reversed paths represent inverse regions,
- * where everything is included in the fill except for the insides.
- *
- * Here is a chart of the behavior under various circumstances:
- *
- * rev = false (union)
- * A
- * F H
- * F A+B -> F A-B -> H
- *B
- * H B-A -> H AxB -> H
- *
- * rev = true (intersect)
- * A
- * F H
- * F AxB -> F B-A -> F
- *B
- * H A-B -> F A+B -> H
- *
- * F/H = Fill outer / Hole outer
- * A/B specify operands
- * + = union, - = subtraction, x = intersection
- * -> read as "produces"
- *
- * This is the main function of boolops, yet its operation isn't very complicated.
- * It traverses the crossings, and uses the crossing direction to decide whether
- * the next segment should be taken from A or from B. The second half of the
- * function deals with figuring out what to do with bits that have no intersection.
- */
-Shape shape_boolean(bool rev, Shape const & a, Shape const & b, CrossingSet const & crs) {
- const Regions ac = a.content, bc = b.content;
-
- //Keep track of which crossings we've hit.
- std::vector<std::vector<bool> > visited;
- for(unsigned i = 0; i < crs.size(); i++)
- visited.push_back(std::vector<bool>(crs[i].size(), false));
-
- //Traverse the crossings, creating chunks
- Regions chunks;
- while(true) {
- unsigned i, j;
- first_false(visited, i, j);
- if(i == visited.size()) break;
-
- Path res;
- do {
- Crossing cur = crs[i][j];
- visited[i][j] = true;
-
- //get indices of the dual:
- unsigned io = cur.getOther(i), jo = find_crossing(crs[io], cur, io);
- if(jo < visited[io].size()) visited[io][jo] = true;
-
- //main driving logic
- if(logical_xor(cur.dir, rev)) {
- if(i >= ac.size()) { i = io; j = jo; }
- j++;
- if(j >= crs[i].size()) j = 0;
- Crossing next = crs[i][j];
- ac[next.a].boundary.appendPortionTo(res, cur.ta, next.ta);
- } else {
- if(i < ac.size()) { i = io; j = jo; }
- j++;
- if(j >= crs[i].size()) j = 0;
- Crossing next = crs[i][j];
- bc[next.b - ac.size()].boundary.appendPortionTo(res, cur.tb, next.tb);
- }
- } while (!visited[i][j]);
- if(res.size() > 0) chunks.push_back(Region(res));
- }
-
- //If true, then we are on the 'subtraction diagonal'
- bool const on_sub = logical_xor(a.fill, b.fill);
- //If true, outer paths are filled
- bool const res_fill = rev ? (on_sub || (a.fill && b.fill)) : (a.fill && b.fill);
-
- //Handle unintersecting portions
- for(unsigned i = 0; i < crs.size(); i++) {
- if(crs[i].size() == 0) {
- bool env;
- bool on_a = i < ac.size();
- Region const & r(on_a ? ac[i] : bc[i - ac.size()]);
- Shape const & other(on_a ? b : a);
-
- std::vector<unsigned> containers = other.containment_list(r.boundary.initialPoint());
- if(containers.empty()) {
- //not included in any container, the environment fill is the opposite of the outer fill
- env = !res_fill;
- if(on_sub && logical_xor(other.fill, res_fill)) env = !env; //If on the subtractor, invert the environment fill
- } else {
- //environment fill is the same as the inner-most container
- std::vector<unsigned>::iterator cit = std::min_element(containers.begin(), containers.end(), ContainmentOrder(&other.content));
- env = other[*cit].isFill();
- }
- if(!logical_xor(rev, env)) chunks.push_back(r); //When unioning, environment must be hole for inclusion, when intersecting, it must be filled
- }
- }
-
- return Shape(chunks, res_fill);
-}
-
-// Just a convenience wrapper for shape_boolean, which handles the crossings
-Shape shape_boolean(bool rev, Shape const & a, Shape const & b) {
- CrossingSet crs = crossings_between(a, b);
-
- return shape_boolean(rev, a, b, crs);
-}
-
-
-// Some utility functions for boolop:
-
-std::vector<double> region_sizes(Shape const &a) {
- std::vector<double> ret;
- for(unsigned i = 0; i < a.size(); i++) {
- ret.push_back(double(a[i].size()));
- }
- return ret;
-}
-
-Shape shape_boolean_ra(bool rev, Shape const &a, Shape const &b, CrossingSet const &crs) {
- return shape_boolean(rev, a.inverse(), b, reverse_ta(crs, a.size(), region_sizes(a)));
-}
-
-Shape shape_boolean_rb(bool rev, Shape const &a, Shape const &b, CrossingSet const &crs) {
- return shape_boolean(rev, a, b.inverse(), reverse_tb(crs, a.size(), region_sizes(b)));
-}
-
-/* This is a function based on shape_boolean which allows boolean operations
- * to be specified as a logic table. This logic table is 4 bit-flags, which
- * correspond to the elements of the 'truth table' for a particular operation.
- * These flags are defined with the enums starting with BOOLOP_ .
- *
- * NOTE: currently doesn't work, as the CrossingSet reversal functions crash
- */
-Shape boolop(Shape const &a, Shape const &b, unsigned flags, CrossingSet const &crs) {
- THROW_NOTIMPLEMENTED();
- flags &= 15;
- if(flags <= BOOLOP_UNION) {
- switch(flags) {
- case BOOLOP_INTERSECT: return shape_boolean(true, a, b, crs);
- case BOOLOP_SUBTRACT_A_B: return shape_boolean_rb(true, a, b, crs);
- case BOOLOP_IDENTITY_A: return a;
- case BOOLOP_SUBTRACT_B_A: return shape_boolean_ra(true, a, b, crs);
- case BOOLOP_IDENTITY_B: return b;
- case BOOLOP_EXCLUSION: {
- Shape res = shape_boolean_rb(true, a, b, crs);
- append(res.content, shape_boolean_ra(true, a, b, crs).content);
- return res;
- }
- case BOOLOP_UNION: return shape_boolean(false, a, b);
- }
- } else {
- flags = ~flags & 15;
- switch(flags - BOOLOP_NEITHER) {
- case BOOLOP_SUBTRACT_A_B: return shape_boolean_ra(false, a, b, crs);
- case BOOLOP_SUBTRACT_B_A: return shape_boolean_rb(false, a, b, crs);
- case BOOLOP_EXCLUSION: {
- Shape res = shape_boolean_ra(false, a, b, CrossingSet(crs));
- append(res.content, shape_boolean_rb(false, a, b, CrossingSet(crs)).content);
- return res;
- }
- }
- return boolop(a, b, flags, crs).inverse();
- }
- return Shape();
-}
-
-/* This version of the boolop function doesn't require a set of crossings, as
- * it computes them for you. This is more efficient in some cases, as the
- * shape can be inverted before finding crossings. In the special case of
- * exclusion it uses the other version of boolop.
- */
-Shape boolop(Shape const &a, Shape const &b, unsigned flags) {
- flags &= 15;
- if(flags <= BOOLOP_UNION) {
- switch(flags) {
- case BOOLOP_INTERSECT: return shape_boolean(true, a, b);
- case BOOLOP_SUBTRACT_A_B: return shape_boolean(true, a, b.inverse());
- case BOOLOP_IDENTITY_A: return a;
- case BOOLOP_SUBTRACT_B_A: return shape_boolean(true, b, a.inverse());
- case BOOLOP_IDENTITY_B: return b;
- case BOOLOP_EXCLUSION: {
- Shape res = shape_boolean(true, a, b.inverse());
- append(res.content, shape_boolean(true, b, a.inverse()).content);
- return res;
- } //return boolop(a, b, flags, crossings_between(a, b));
- case BOOLOP_UNION: return shape_boolean(false, a, b);
- }
- } else {
- flags = ~flags & 15;
- switch(flags) {
- case BOOLOP_SUBTRACT_A_B: return shape_boolean(false, b, a.inverse());
- case BOOLOP_SUBTRACT_B_A: return shape_boolean(false, a, b.inverse());
- case BOOLOP_EXCLUSION: {
- Shape res = shape_boolean(false, a, b.inverse());
- append(res.content, shape_boolean(false, b, a.inverse()).content);
- return res;
- } //return boolop(a, b, flags, crossings_between(a, b));
- }
- return boolop(a, b, flags).inverse();
- }
- return Shape();
-}
-
-int paths_winding(std::vector<Path> const &ps, Point p) {
- int ret = 0;
- for(unsigned i = 0; i < ps.size(); i++)
- ret += winding(ps[i], p);
- return ret;
-}
-
-void add_to_shape(Shape &s, Path const &p, bool fill) {
- if(fill)
- s.content.push_back(Region(p).asFill());
- else
- s.content.push_back(Region(p).asHole());
-}
-
-int inner_winding(Path const & p, std::vector<Path> const &ps) {
- Point pnt = p.initialPoint();
- return paths_winding(ps, pnt) - winding(p, pnt) + 1;
-}
-
-double fudgerize(double d, bool rev) {
- double ret = rev ? d - 0.01 : d + 0.01;
- if(ret < 0) ret = 0;
- return ret;
-}
-
-unsigned pick_coincident(unsigned ix, unsigned jx, bool &rev, std::vector<Path> const &ps, CrossingSet const &crs) {
- unsigned ex_jx = jx;
- unsigned oix = crs[ix][jx].getOther(ix);
- double otime = crs[ix][jx].getTime(oix);
- Point cross_point = ps[oix].pointAt(otime),
- along = ps[oix].pointAt(fudgerize(otime, rev)) - cross_point,
- prev = -along;
- bool ex_dir = rev;
- for(unsigned k = jx; k < crs[ix].size(); k++) {
- unsigned koix = crs[ix][k].getOther(ix);
- if(koix == oix) {
- if(!are_near(otime, crs[ix][k].getTime(oix))) break;
- for(unsigned dir = 0; dir < 2; dir++) {
- Point val = ps[ix].pointAt(fudgerize(crs[ix][k].getTime(ix), dir)) - cross_point;
- Cmp to_prev = cmp(cross(val, prev), 0);
- Cmp from_along = cmp(cross(along, val), 0);
- Cmp c = cmp(from_along, to_prev);
- if(c == EQUAL_TO && from_along == LESS_THAN) {
- ex_jx = k;
- prev = val;
- ex_dir = dir;
- }
- }
- }
- }
- rev = ex_dir;
- return ex_jx;
-}
-
-unsigned crossing_along(double t, unsigned ix, unsigned jx, bool dir, Crossings const & crs) {
- Crossing cur = Crossing(t, t, ix, ix, false);
- if(jx < crs.size()) {
- double ct = crs[jx].getTime(ix);
- if(t == ct) {
- cur = crs[jx];
- if(cur.a == cur.b) {
- if(jx+1 <= crs.size() && crs[jx+1].getOther(ix) == ix) return jx+1;
- if(jx > 0 && crs[jx-1].getOther(ix) == ix) return jx-1;
- }
- }
- }
- if(!dir) {
- jx = std::upper_bound(crs.begin(), crs.end(), cur, CrossingOrder(ix)) - crs.begin();
- } else {
- jx = std::lower_bound(crs.begin(), crs.end(), cur, CrossingOrder(ix)) - crs.begin();
- if(jx == 0) jx = crs.size() - 1; else jx--;
- jx = std::lower_bound(crs.begin(), crs.end(), crs[jx], CrossingOrder(ix)) - crs.begin();
- }
- if(jx >= crs.size()) jx = 0;
- return jx;
-}
-
-void crossing_dual(unsigned &i, unsigned &j, CrossingSet const & crs) {
- Crossing cur = crs[i][j];
- i = cur.getOther(i);
-#ifdef SHAPE_DEBUG
- std::cout << i << "\n";
-#endif
- if(crs[i].empty())
- j = 0;
- else
- j = std::lower_bound(crs[i].begin(), crs[i].end(), cur, CrossingOrder(i)) - crs[i].begin();
-}
-
-//locate a crossing on the outside, by casting a ray through the middle of the bbox
-void outer_crossing(unsigned &ix, unsigned &jx, bool & dir, std::vector<Path> const & ps, CrossingSet const & crs) {
- Rect bounds = *(ps[ix].boundsFast());
- double ry = bounds[Y].middle();
- double max_val = bounds.left(), max_t = 0;
- ix = ps.size();
- for(unsigned i = 0; i < ps.size(); i++) {
- if(!crs[i].empty()) {
- std::vector<double> rts = ps[i].roots(ry, Y);
- for(unsigned j = 0; j < rts.size(); j++) {
- double val = ps[i].valueAt(rts[j], X);
- if(val > max_val) {
- ix = i;
- max_val = val;
- max_t = rts[j];
- }
- }
- }
- }
- if(ix != ps.size()) {
- dir = ps[ix].valueAt(max_t + 0.01, Y) >
- ps[ix].valueAt(max_t - 0.01, Y);
- jx = crossing_along(max_t, ix, jx, dir, crs[ix]);
- }
-}
-
-std::vector<Path> inner_sanitize(std::vector<Path> const & ps) {
- CrossingSet crs(crossings_among(ps));
-
- Regions chunks;
-
- std::vector<bool> used_path(ps.size(), false);
- std::vector<std::vector<bool> > visited;
- for(unsigned i = 0; i < crs.size(); i++)
- visited.push_back(std::vector<bool>(crs[i].size(), false));
-
- std::vector<Path> result_paths;
-
- while(true) {
- unsigned ix = 0, jx = 0;
- bool dir = false;
-
- //find an outer crossing by trying various paths and checking if the crossings are used
- for(; ix < crs.size(); ix++) {
- //TODO: optimize so it doesn't unecessarily check stuff
- bool cont = true;
- for(unsigned j = 0; j < crs[ix].size(); j++) {
- if(!visited[ix][j]) { cont = false; break; }
- }
- if(cont) continue;
- unsigned rix = ix, rjx = jx;
- outer_crossing(rix, rjx, dir, ps, crs);
- if(rix >= crs.size() || visited[rix][rjx]) continue;
- ix = rix; jx = rjx;
- break;
- }
- if(ix == crs.size()) break;
- crossing_dual(ix, jx, crs);
-
- dir = !dir;
-
- Path res;
- do {
- visited[ix][jx] = true;
- //unsigned nix = ix, njx = jx;
- //crossing_dual(nix, njx, crs);
- //visited[nix][njx] = true;
- unsigned fix = ix, fjx = jx;
-
- bool new_dir = dir;
-
- jx = crossing_along(crs[ix][jx].getTime(ix), ix, jx, dir, crs[ix]);
- if(crs[ix][jx].a != crs[ix][jx].b) crossing_dual(ix, jx, crs); else new_dir = !new_dir;
- jx = pick_coincident(ix, jx, new_dir, ps, crs);
-
- //unsigned nix = ix, njx = jx;
- //crossing_dual(nix, njx, crs);
-
- Crossing from = crs[fix][fjx],
- to = crs[ix][jx];
- if(dir) {
- // backwards
-#ifdef SHAPE_DEBUG
- std::cout << "r" << ix << "[" << from.getTime(ix) << ", " << to.getTime(ix) << "]\n";
-#endif
- Path p = ps[ix].portion(from.getTime(ix), to.getTime(ix)).reverse();
- for(unsigned i = 0; i < p.size(); i++)
- res.append(p[i], Path::STITCH_DISCONTINUOUS);
- } else {
- // forwards
-#ifdef SHAPE_DEBUG
- std::cout << "f" << ix << "[" << from.getTime(ix) << ", " << to.getTime(ix) << "]\n";
-#endif
- ps[ix].appendPortionTo(res, from.getTime(ix), to.getTime(ix));
- }
- dir = new_dir;
- } while(!visited[ix][jx]);
-#ifdef SHAPE_DEBUG
- std::cout << "added " << res.size() << "\n";
-#endif
- result_paths.push_back(res);
- }
- for(unsigned i = 0; i < crs.size(); i++) {
- if(crs[i].empty() && !used_path[i])
- result_paths.push_back(ps[i]);
- }
- return result_paths;
-}
-
-Shape sanitize(std::vector<Path> const & ps) {
- std::vector<Path> res;
- for(unsigned i = 0; i < ps.size(); i++) {
- append(res, inner_sanitize(std::vector<Path>(1, ps[i])));
- }
- return stopgap_cleaner(res);
-}
-
-/* WIP sanitizer:
-unsigned pick_coincident(unsigned ix, unsigned jx, bool pref, bool &rev, std::vector<Path> const &ps, CrossingSet const &crs) {
- unsigned ex_jx = jx;
- unsigned oix = crs[ix][jx].getOther(ix);
- double otime = crs[ix][jx].getTime(oix);
- Point cross_point = ps[oix].pointAt(otime),
- along = ps[oix].pointAt(otime + (rev ? -0.01 : 0.01)) - cross_point,
- prev = -along;
- bool ex_dir = rev;
- for(unsigned k = jx; k < crs[ix].size(); k++) {
- unsigned koix = crs[ix][k].getOther(ix);
- if(koix == oix) {
- if(!are_near(otime, crs[ix][k].getTime(oix))) break;
- for(unsigned dir = 0; dir < 2; dir++) {
- Point val = ps[ix].pointAt(crs[ix][k].getTime(ix) + (dir ? -0.01 : 0.01)) - cross_point;
- Cmp to_prev = cmp(cross(val, prev), 0);
- Cmp from_along = cmp(cross(along, val), 0);
- Cmp c = cmp(from_along, to_prev);
- if(c == EQUAL_TO && (from_along == LESS_THAN) == pref) {
- ex_jx = k;
- prev = val;
- ex_dir = dir;
- }
- }
- }
- }
- rev = ex_dir;
- return ex_jx;
-}
-
-unsigned corner_index(unsigned &i) {
- div_t div_res = div(i, 4);
- i = div_res.quot;
- return div_res.rem;
-}
-
-bool corner_direction(unsigned ix, unsigned jc, unsigned corner, CrossingSet const &crs) {
- if(crs[ix][jc].a == ix) return corner > 1; else return corner %2 == 1;
-}
-
-Shape sanitize(std::vector<Path> const & ps) {
- CrossingSet crs = crossings_among(ps);
-
- //Keep track of which CORNERS we've hit.
- // FF FR RF RR, first is A dir, second B dir
- std::vector<std::vector<bool> > visited;
- for(unsigned i = 0; i < crs.size(); i++)
- visited.push_back(std::vector<bool>(crs[i].size()*4, false));
-
- Regions chunks;
- while(true) {
- unsigned i, j;
- first_false(visited, i, j);
- unsigned corner = corner_index(j);
-
- if(i == visited.size()) break;
-
- bool dir = corner_direction(i, j, corner, crs);
-
- //Figure out whether we hug the path cw or ccw, based on the orientation of the initial corner:
- unsigned oix = crs[i][j].getOther(i);
- double otime = crs[i][j].getTime(oix);
- bool odir = (oix == crs[i][j].a) ? corner > 1 : corner % 2 == 1;
- Point cross_point = ps[oix].pointAt(otime),
- along = ps[oix].pointAt(otime + (odir ? -0.01 : 0.01)) - cross_point,
- val = ps[i].pointAt(crs[i][j].getTime(i) + (dir ? -0.01 : 0.01)) - cross_point;
-
- Cmp from_along = cmp(cross(along, val), 0);
- bool cw = from_along == LESS_THAN;
- std::cout << "cw = " << cw << "\n";
- Path res;
- do {
- Crossing cur = crs[i][j];
- visited[i][j*4+corner] = true;
-
- unsigned fix = i, fjx = j;
- crossing_dual(i, j, crs);
- visited[i][j*4+corner] = true;
- i = fix; j = fjx;
-
- j = crossing_along(crs[i][j].getTime(i), i, j, dir, crs[i]);
-
- crossing_dual(i, j, crs);
-
- bool new_dir = dir;
- pick_coincident(i, j, cw, new_dir, ps, crs);
-
- Crossing from = crs[fix][fjx],
- to = crs[i][j];
- if(dir) {
- // backwards
- std::cout << "r" << i << "[" << to.getTime(i) << ", " << from.getTime(i) << "]\n";
- Path p = ps[i].portion(to.getTime(i) + 0.001, from.getTime(i)).reverse();
- for(unsigned k = 0; k < p.size(); k++)
- res.append(p[k]);
- } else {
- // forwards
- std::cout << "f" << i << "[" << from.getTime(i) << ", " << to.getTime(i) << "]\n";
- ps[i].appendPortionTo(res, from.getTime(i) + 0.001, to.getTime(i));
- }
- if(i == to.a)
- corner = (new_dir ? 2 : 0) + (dir ? 1 : 0);
- else
- corner = (new_dir ? 1 : 0) + (dir ? 2 : 0);
- dir = new_dir;
- } while(!visited[i][j*4+corner]);
- chunks.push_back(Region(res));
-// if(use) {
-// chunks.push_back(Region(res, true));
-// }
- }
- return Shape(chunks);
-// return ret;
-} */
-
-/* This transforms a shape by a matrix. In the case that the matrix flips
- * the shape, it reverses the paths in order to preserve the fill.
- */
-Shape Shape::operator*(Affine const &m) const {
- Shape ret;
- for(unsigned i = 0; i < size(); i++)
- ret.content.push_back(content[i] * m);
- ret.fill = fill;
- return ret;
-}
-
-// Inverse is a boolean not, and simply reverses all the paths & fill flags
-Shape Shape::inverse() const {
- Shape ret;
- for(unsigned i = 0; i < size(); i++)
- ret.content.push_back(content[i].inverse());
- ret.fill = !fill;
- return ret;
-}
-
-bool Shape::contains(Point const &p) const {
- std::vector<unsigned> containers = containment_list(p);
- if(containers.empty()) return !isFill();
- unsigned ix = *min_element(containers.begin(), containers.end(), ContainmentOrder(&content));
- return content[ix].isFill();
-}
-
-Shape stopgap_cleaner(std::vector<Path> const &ps) {
- if(ps.empty()) return Shape(false);
- Shape ret;
- for(unsigned i = 0; i < ps.size(); i++)
- add_to_shape(ret, ps[i], inner_winding(ps[i], ps) % 2 != 0);
- return ret;
-}
-
-bool Shape::inside_invariants() const { //semi-slow & easy to violate
- for(unsigned i = 0; i < size(); i++)
- if( logical_xor(content[i].isFill(), contains(content[i].boundary.initialPoint())) ) return false;
- return true;
-}
-bool Shape::region_invariants() const { //semi-slow
- for(unsigned i = 0; i < size(); i++)
- if(!content[i].invariants()) return false;
- return true;
-}
-bool Shape::cross_invariants() const { //slow
- CrossingSet crs; // = crossings_among(paths_from_regions(content));
- for(unsigned i = 0; i < crs.size(); i++)
- if(!crs[i].empty()) return false;
- return true;
-}
-
-bool Shape::invariants() const {
- return inside_invariants() && region_invariants() && cross_invariants();
-}
-
-}
-
-/*
- 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/2geom/shape.h b/src/2geom/shape.h
deleted file mode 100644
index 0a7ee9709..000000000
--- a/src/2geom/shape.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/**
- * \brief Shapes are special paths on which boolops can be performed
- *
- * Authors:
- * Michael G. Sloan <mgsloan@gmail.com>
- * Nathan Hurst <njh@mail.csse.monash.edu.au>
- * MenTaLguY <mental@rydia.net>
- *
- * Copyright 2007-2009 Authors
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- *
- */
-
-#ifndef __2GEOM_SHAPE_H
-#define __2GEOM_SHAPE_H
-
-#include <vector>
-#include <set>
-
-#include <2geom/region.h>
-
-//TODO: BBOX optimizations
-
-namespace Geom {
-
-enum {
- BOOLOP_JUST_A = 1,
- BOOLOP_JUST_B = 2,
- BOOLOP_BOTH = 4,
- BOOLOP_NEITHER = 8
-};
-
-enum {
- BOOLOP_NULL = 0,
- BOOLOP_INTERSECT = BOOLOP_BOTH,
- BOOLOP_SUBTRACT_A_B = BOOLOP_JUST_B,
- BOOLOP_IDENTITY_A = BOOLOP_JUST_A | BOOLOP_BOTH,
- BOOLOP_SUBTRACT_B_A = BOOLOP_JUST_A,
- BOOLOP_IDENTITY_B = BOOLOP_JUST_B | BOOLOP_BOTH,
- BOOLOP_EXCLUSION = BOOLOP_JUST_A | BOOLOP_JUST_B,
- BOOLOP_UNION = BOOLOP_JUST_A | BOOLOP_JUST_B | BOOLOP_BOTH
-};
-
-class Shape {
- Regions content;
- mutable bool fill;
- //friend Shape shape_region_boolean(bool rev, Shape const & a, Region const & b);
- friend CrossingSet crossings_between(Shape const &a, Shape const &b);
- friend Shape shape_boolean(bool rev, Shape const &, Shape const &, CrossingSet const &);
- friend Shape boolop(Shape const &a, Shape const &b, unsigned);
- friend Shape boolop(Shape const &a, Shape const &b, unsigned, CrossingSet const &);
- friend void add_to_shape(Shape &s, Path const &p, bool);
- public:
- Shape() : fill(true) {}
- explicit Shape(Region const & r) {
- content = Regions(1, r);
- fill = r.fill;
- }
- explicit Shape(Regions const & r) : content(r) { update_fill(); }
- explicit Shape(bool f) : fill(f) {}
- Shape(Regions const & r, bool f) : content(r), fill(f) {}
-
- Regions getContent() const { return content; }
- bool isFill() const { return fill; }
-
- unsigned size() const { return content.size(); }
- const Region &operator[](unsigned ix) const { return content[ix]; }
-
- Shape inverse() const;
- Shape operator*(Affine const &m) const;
-
- bool contains(Point const &p) const;
-
- bool inside_invariants() const; //semi-slow & easy to violate : checks that the insides are inside, the outsides are outside
- bool region_invariants() const; //semi-slow : checks for self crossing
- bool cross_invariants() const; //slow : checks that everything is disjoint
- bool invariants() const; //vera slow (combo, checks the above)
-
- private:
- std::vector<unsigned> containment_list(Point p) const;
- void update_fill() const {
- unsigned ix = outer_index(content);
- if(ix < size())
- fill = content[ix].fill;
- else if(size() > 0)
- fill = content.front().fill;
- else
- fill = true;
- }
-};
-
-inline CrossingSet crossings_between(Shape const &a, Shape const &b) { return crossings(paths_from_regions(a.content), paths_from_regions(b.content)); }
-
-Shape shape_boolean(bool rev, Shape const &, Shape const &, CrossingSet const &);
-Shape shape_boolean(bool rev, Shape const &, Shape const &);
-
-//unsigned pick_coincident(unsigned ix, unsigned jx, bool &rev, std::vector<Path> const &ps, CrossingSet const &crs);
-//void outer_crossing(unsigned &ix, unsigned &jx, bool & dir, std::vector<Path> const & ps, CrossingSet const & crs);
-void crossing_dual(unsigned &i, unsigned &j, CrossingSet const & crs);
-unsigned crossing_along(double t, unsigned ix, unsigned jx, bool dir, Crossings const & crs);
-
-Shape boolop(Shape const &, Shape const &, unsigned flags);
-Shape boolop(Shape const &, Shape const &, unsigned flags, CrossingSet &);
-
-Shape sanitize(std::vector<Path> const &ps);
-
-Shape stopgap_cleaner(std::vector<Path> const &ps);
-
-inline std::vector<Path> desanitize(Shape const & s) {
- return paths_from_regions(s.getContent());
-}
-
-}
-
-#endif
-
-/*
- 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/2geom/solve-bezier-one-d.cpp b/src/2geom/solve-bezier-one-d.cpp
index 3d87d4926..3a25e9808 100644
--- a/src/2geom/solve-bezier-one-d.cpp
+++ b/src/2geom/solve-bezier-one-d.cpp
@@ -27,7 +27,7 @@ static int SGN(t x) { return (x > 0 ? 1 : (x < 0 ? -1 : 0)); }
**/
class Bernsteins{
public:
- static const size_t MAX_DEPTH = 22;
+ static const size_t MAX_DEPTH = 53;
size_t degree, N;
std::vector<double> &solutions;
//std::vector<double> bc;
@@ -38,17 +38,9 @@ public:
{
}
- void subdivide(double const *V,
- double t,
- double *Left,
- double *Right);
-
unsigned
control_poly_flat_enough(double const *V);
- double horner(const double *b, double t);
-
-
void
find_bernstein_roots(double const *w, /* The control points */
unsigned depth, /* The depth of the recursion */
@@ -134,7 +126,7 @@ void Bernsteins::find_bernstein_roots(double const *w, /* The control points */
r = (fs*t - ft*s) / (fs - ft);
if (fabs(t-s) < e * fabs(t+s)) break;
- double fr = horner(w, r);
+ double fr = bernstein_value_at(r, w, degree);
if (fr * ft > 0)
{
@@ -191,24 +183,6 @@ void Bernsteins::find_bernstein_roots(double const *w, /* The control points */
delete[] LR;
}
-
-// suggested by Sederberg.
-double Bernsteins::horner(const double *b, double t)
-{
- double u, tn, tmp;
- u = 1.0 - t;
- tn = 1.0;
- tmp = b[0] * u;
- for(size_t i = 1; i < degree; ++i)
- {
- tn *= t;
- tmp = (tmp + tn*bc[i]*b[i]) * u;
- }
- return (tmp + tn*t*b[degree]);
-}
-
-
-
#if 0
/*
* control_poly_flat_enough :
diff --git a/src/2geom/solve-bezier-parametric.cpp b/src/2geom/solve-bezier-parametric.cpp
index 9b0feaee4..2fb3f41da 100644
--- a/src/2geom/solve-bezier-parametric.cpp
+++ b/src/2geom/solve-bezier-parametric.cpp
@@ -1,8 +1,9 @@
-#include <2geom/solver.h>
+#include <2geom/bezier.h>
#include <2geom/point.h>
+#include <2geom/solver.h>
#include <algorithm>
-namespace Geom{
+namespace Geom {
/*** Find the zeros of the parametric function in 2d defined by two beziers X(t), Y(t). The code subdivides until it happy with the linearity of the bezier. This requires an n^2 subdivision for each step, even when there is only one solution.
*
@@ -14,13 +15,6 @@ namespace Geom{
/*
* Forward declarations
*/
-static Geom::Point
-Bezier(Geom::Point const *V,
- unsigned degree,
- double t,
- Geom::Point *Left,
- Geom::Point *Right);
-
unsigned
crossing_count(Geom::Point const *V, unsigned degree);
static unsigned
@@ -74,7 +68,7 @@ find_parametric_bezier_roots(Geom::Point const *w, /* The control points */
// Right[degree+1]; /* control polygons */
std::vector<Geom::Point> Left( degree+1 ), Right(degree+1);
- Bezier(w, degree, 0.5, Left.data(), Right.data());
+ casteljau_subdivision(0.5, w, Left.data(), Right.data(), degree);
total_subs ++;
find_parametric_bezier_roots(Left.data(), degree, solutions, depth+1);
find_parametric_bezier_roots(Right.data(), degree, solutions, depth+1);
@@ -181,43 +175,6 @@ compute_x_intercept(Geom::Point const *V, /* Control points */
return (A[Geom::X]*V[0][Geom::Y] - A[Geom::Y]*V[0][Geom::X]) / -A[Geom::Y];
}
-
-/*
- * Bezier :
- * Evaluate a Bezier curve at a particular parameter value
- * Fill in control points for resulting sub-curves.
- *
- */
-static Geom::Point
-Bezier(Geom::Point const *V, /* Control pts */
- unsigned degree, /* Degree of bezier curve */
- double t, /* Parameter value */
- Geom::Point *Left, /* RETURN left half ctl pts */
- Geom::Point *Right) /* RETURN right half ctl pts */
-{
- //Geom::Point Vtemp[degree+1][degree+1];
- std::vector<std::vector<Geom::Point> > Vtemp(degree+1);
- for ( size_t i = 0; i < degree + 1; ++i )
- Vtemp.reserve(degree+1);
-
- /* Copy control points */
- std::copy(V, V+degree+1, Vtemp[0].begin());
-
- /* Triangle computation */
- for (unsigned i = 1; i <= degree; i++) {
- for (unsigned j = 0; j <= degree - i; j++) {
- Vtemp[i][j] = lerp(t, Vtemp[i-1][j], Vtemp[i-1][j+1]);
- }
- }
-
- for (unsigned j = 0; j <= degree; j++)
- Left[j] = Vtemp[j][0];
- for (unsigned j = 0; j <= degree; j++)
- Right[j] = Vtemp[degree-j][j];
-
- return (Vtemp[degree][0]);
-}
-
};
/*
diff --git a/src/2geom/solve-bezier.cpp b/src/2geom/solve-bezier.cpp
index adf3c9ac0..4732169cb 100644
--- a/src/2geom/solve-bezier.cpp
+++ b/src/2geom/solve-bezier.cpp
@@ -37,12 +37,10 @@ public:
double *Left,
double *Right);
- double secant(Bezier bz);
+ double secant(Bezier const &bz);
- double horner(Bezier bz, double t);
-
- void find_bernstein_roots(Bezier bz, unsigned depth,
+ void find_bernstein_roots(Bezier const &bz, unsigned depth,
double left_t, double right_t);
};
@@ -55,27 +53,7 @@ inline std::ostream &operator<< (std::ostream &out_file, const std::vector<T> &
return out_file << "]";
}
-Bezier subRight(Bezier bz, double t) {
- unsigned order = bz.order();
- unsigned N = order+1;
- std::valarray<Coord> row(N);
- for (unsigned i = 0; i < N; i++)
- row[i] = bz[i];
-
- // Triangle computation
- const double omt = (1-t);
- Bezier Right = bz;
- Right[order] = row[order];
- for (unsigned i = 1; i < N; i++) {
- for (unsigned j = 0; j < N - i; j++) {
- row[j] = omt*row[j] + t*row[j+1];
- }
- Right[order-i] = row[order-i];
- }
- return Right;
-}
-
-void convex_hull_marching(Bezier src_bz, Bezier bz,
+void convex_hull_marching(Bezier const &src_bz, Bezier bz,
std::vector<double> &solutions,
double left_t,
double right_t)
@@ -111,7 +89,7 @@ void convex_hull_marching(Bezier src_bz, Bezier bz,
<< " = " << bz(left_bound) << std::endl;
double new_left_t = left_bound * (right_t - left_t) + left_t;
std::cout << "new_left_t = " << new_left_t << std::endl;
- Bezier bzr = subRight(src_bz, new_left_t);
+ Bezier bzr = portion(src_bz, new_left_t, 1);
while(bzr.order() > 0 && bzr[0] == 0) {
std::cout << "deflate\n";
bzr = bzr.deflate();
@@ -171,7 +149,7 @@ Bezier::find_bezier_roots(std::vector<double> &solutions,
//std::cout << solutions << std::endl;
}
-void Bernsteins::find_bernstein_roots(Bezier bz,
+void Bernsteins::find_bernstein_roots(Bezier const &bz,
unsigned depth,
double left_t,
double right_t)
@@ -194,6 +172,11 @@ void Bernsteins::find_bernstein_roots(Bezier bz,
old_sign = sign;
}
}
+ // if last control point is zero, that counts as crossing too
+ if (SGN(bz[bz.size()-1]) == 0) {
+ ++n_crossings;
+ }
+
//std::cout << "n_crossings = " << n_crossings << std::endl;
if (n_crossings == 0) return; // no solutions here
@@ -271,23 +254,7 @@ void Bernsteins::find_bernstein_roots(Bezier bz,
}
}
-
-// suggested by Sederberg.
-double Bernsteins::horner(Bezier bz, double t)
-{
- double u, tn, tmp;
- u = 1.0 - t;
- tn = 1.0;
- tmp = bz.at0() * u;
- for(size_t i = 1; i < bz.degree(); ++i)
- {
- tn *= t;
- tmp = (tmp + tn*choose<double>(bz.order(), (unsigned)i)*bz[i]) * u;
- }
- return (tmp + tn*t*bz.at1());
-}
-
-double Bernsteins::secant(Bezier bz) {
+double Bernsteins::secant(Bezier const &bz) {
double s = 0, t = 1;
double e = 1e-14;
int side = 0;
@@ -303,7 +270,7 @@ double Bernsteins::secant(Bezier bz) {
return r;
}
- double fr = horner(bz, r);
+ double fr = bz.valueAt(r);
if (fr * ft > 0)
{
diff --git a/src/2geom/solver.h b/src/2geom/solver.h
index 793939b2a..5b082cb83 100644
--- a/src/2geom/solver.h
+++ b/src/2geom/solver.h
@@ -32,16 +32,14 @@
*
*/
-#ifndef _SOLVE_SBASIS_H
-#define _SOLVE_SBASIS_H
+#ifndef LIB2GEOM_SEEN_SOLVER_H
+#define LIB2GEOM_SEEN_SOLVER_H
+
#include <2geom/point.h>
#include <2geom/sbasis.h>
-
-
#include <vector>
-
-namespace Geom{
+namespace Geom {
class Point;
class Bezier;
@@ -78,7 +76,6 @@ find_bernstein_roots(std::vector<double> &solutions, /* RETURN candidate t-value
double left_t, double right_t);
#endif
-
/*
Local Variables:
mode:c++
diff --git a/src/2geom/svg-elliptical-arc.cpp b/src/2geom/svg-elliptical-arc.cpp
index 96a4f99d8..3a5154d08 100644
--- a/src/2geom/svg-elliptical-arc.cpp
+++ b/src/2geom/svg-elliptical-arc.cpp
@@ -207,7 +207,7 @@ bool make_elliptical_arc::make_elliptiarc()
Ellipse e;
try
{
- e.set(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]);
+ e.setCoefficients(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]);
}
catch(LogicalError const &exc)
{
diff --git a/src/2geom/svg-elliptical-arc.h b/src/2geom/svg-elliptical-arc.h
index ba0a18257..90e16a999 100644
--- a/src/2geom/svg-elliptical-arc.h
+++ b/src/2geom/svg-elliptical-arc.h
@@ -70,59 +70,57 @@ public:
_updateCenterAndAngles(true);
}
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
virtual Curve *duplicate() const {
return new SVGEllipticalArc(*this);
}
virtual Coord valueAt(Coord t, Dim2 d) const {
- if (isDegenerate()) return chord().valueAt(t, d);
+ if (isChord()) return chord().valueAt(t, d);
return EllipticalArc::valueAt(t, d);
}
virtual Point pointAt(Coord t) const {
- if (isDegenerate()) return chord().pointAt(t);
+ if (isChord()) return chord().pointAt(t);
return EllipticalArc::pointAt(t);
}
virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned int n) const {
- if (isDegenerate()) return chord().pointAndDerivatives(t, n);
+ if (isChord()) return chord().pointAndDerivatives(t, n);
return EllipticalArc::pointAndDerivatives(t, n);
}
virtual Rect boundsExact() const {
- if (isDegenerate()) return chord().boundsExact();
+ if (isChord()) return chord().boundsExact();
return EllipticalArc::boundsExact();
}
virtual OptRect boundsLocal(OptInterval const &i, unsigned int deg) const {
- if (isDegenerate()) return chord().boundsLocal(i, deg);
+ if (isChord()) return chord().boundsLocal(i, deg);
return EllipticalArc::boundsLocal(i, deg);
}
virtual Curve *derivative() const {
- if (isDegenerate()) return chord().derivative();
+ if (isChord()) return chord().derivative();
return EllipticalArc::derivative();
}
virtual std::vector<Coord> roots(Coord v, Dim2 d) const {
- if (isDegenerate()) return chord().roots(v, d);
+ if (isChord()) return chord().roots(v, d);
return EllipticalArc::roots(v, d);
}
#ifdef HAVE_GSL
- virtual std::vector<Coord> allNearestPoints( Point const& p, double from = 0, double to = 1 ) const {
- if (isDegenerate()) {
+ virtual std::vector<Coord> allNearestTimes( Point const& p, double from = 0, double to = 1 ) const {
+ if (isChord()) {
std::vector<Coord> result;
- result.push_back(chord().nearestPoint(p, from, to));
+ result.push_back(chord().nearestTime(p, from, to));
return result;
}
- return EllipticalArc::allNearestPoints(p, from, to);
+ return EllipticalArc::allNearestTimes(p, from, to);
}
#endif
virtual D2<SBasis> toSBasis() const {
- if (isDegenerate()) return chord().toSBasis();
+ if (isChord()) return chord().toSBasis();
return EllipticalArc::toSBasis();
}
virtual bool isSVGCompliant() const { return true; }
// TODO move SVG-specific behavior here.
//protected:
//virtual void _updateCenterAndAngles();
-#endif
}; // end class SVGEllipticalArc
/*
diff --git a/src/2geom/svg-path-parser.cpp b/src/2geom/svg-path-parser.cpp
index 932f95829..89a912695 100644
--- a/src/2geom/svg-path-parser.cpp
+++ b/src/2geom/svg-path-parser.cpp
@@ -1,3 +1,5 @@
+
+#line 1 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
/**
* \file
* \brief parse SVG path specifications
@@ -30,7 +32,7 @@
*
*/
-
+#include <cstdio>
#include <cmath>
#include <vector>
#include <glib.h>
@@ -41,229 +43,108 @@
namespace Geom {
-namespace {
-
-class Parser {
-public:
- Parser(PathSink &sink) : _absolute(false), _sink(sink) {}
-
- void parse(char const *str) throw(SVGPathParseError);
-
-private:
- bool _absolute;
- Point _current;
- Point _initial;
- Point _cubic_tangent;
- Point _quad_tangent;
- std::vector<double> _params;
- PathSink &_sink;
-
- void _reset() {
- _absolute = false;
- _current = _initial = Point(0, 0);
- _quad_tangent = _cubic_tangent = Point(0, 0);
- _params.clear();
- }
-
- void _push(double value) {
- _params.push_back(value);
- }
-
- double _pop() {
- double value = _params.back();
- _params.pop_back();
- return value;
- }
-
- bool _pop_flag() {
- return _pop() != 0.0;
- }
-
- double _pop_coord(Geom::Dim2 axis) {
- if (_absolute) {
- return _pop();
- } else {
- return _pop() + _current[axis];
- }
- }
-
- Point _pop_point() {
- double y = _pop_coord(Geom::Y);
- double x = _pop_coord(Geom::X);
- return Point(x, y);
- }
-
- void _moveTo(Point p) {
- _quad_tangent = _cubic_tangent = _current = _initial = p;
- _sink.moveTo(p);
- }
-
- void _hlineTo(Point p) {
- _quad_tangent = _cubic_tangent = _current = p;
- _sink.hlineTo(p[Geom::X]);
- }
-
- void _vlineTo(Point p) {
- _quad_tangent = _cubic_tangent = _current = p;
- _sink.vlineTo(p[Geom::Y]);
- }
-
- void _lineTo(Point p) {
- _quad_tangent = _cubic_tangent = _current = p;
- _sink.lineTo(p);
- }
-
- void _curveTo(Point c0, Point c1, Point p) {
- _quad_tangent = _current = p;
- _cubic_tangent = p + ( p - c1 );
- _sink.curveTo(c0, c1, p);
- }
-
- void _quadTo(Point c, Point p) {
- _cubic_tangent = _current = p;
- _quad_tangent = p + ( p - c );
- _sink.quadTo(c, p);
- }
-
- void _arcTo(double rx, double ry, double angle,
- bool large_arc, bool sweep, Point p)
- {
- if (are_near(_current, p)) {
- return;
- }
- _quad_tangent = _cubic_tangent = _current = p;
- _sink.arcTo(rx, ry, angle, large_arc, sweep, p);
- }
-
- void _closePath() {
- _quad_tangent = _cubic_tangent = _current = _initial;
- _sink.closePath();
- }
-};
-
+#line 48 "D:/lib2geom/trunk/src/2geom/svg-path-parser.cpp"
static const char _svg_path_actions[] = {
0, 1, 0, 1, 1, 1, 2, 1,
- 3, 1, 4, 1, 5, 1, 15, 1,
- 16, 2, 1, 0, 2, 1, 6, 2,
- 1, 7, 2, 1, 8, 2, 1, 9,
- 2, 1, 10, 2, 1, 11, 2, 1,
- 12, 2, 1, 13, 2, 1, 14, 2,
- 4, 0, 2, 5, 0, 2, 15, 16,
- 3, 1, 6, 0, 3, 1, 6, 16,
- 3, 1, 7, 0, 3, 1, 7, 16,
- 3, 1, 8, 0, 3, 1, 8, 16,
- 3, 1, 9, 0, 3, 1, 9, 16,
- 3, 1, 10, 0, 3, 1, 10, 16,
- 3, 1, 11, 0, 3, 1, 11, 16,
- 3, 1, 12, 0, 3, 1, 12, 16,
- 3, 1, 13, 0, 3, 1, 13, 16,
- 3, 1, 14, 0, 3, 1, 14, 16
-
+ 3, 1, 4, 1, 5, 1, 15, 2,
+ 1, 0, 2, 1, 6, 2, 1, 7,
+ 2, 1, 8, 2, 1, 9, 2, 1,
+ 10, 2, 1, 11, 2, 1, 12, 2,
+ 1, 13, 2, 1, 14, 2, 2, 0,
+ 2, 3, 0, 2, 4, 0, 2, 5,
+ 0, 3, 1, 6, 0, 3, 1, 7,
+ 0, 3, 1, 8, 0, 3, 1, 9,
+ 0, 3, 1, 10, 0, 3, 1, 11,
+ 0, 3, 1, 12, 0, 3, 1, 13,
+ 0, 3, 1, 14, 0
};
static const short _svg_path_key_offsets[] = {
- 0, 0, 7, 16, 25, 28, 30, 42,
- 52, 55, 57, 90, 121, 124, 126, 138,
- 148, 151, 153, 186, 195, 207, 216, 249,
- 256, 263, 265, 275, 283, 290, 292, 304,
- 314, 317, 319, 328, 335, 341, 346, 353,
- 359, 364, 374, 377, 379, 391, 401, 404,
- 406, 437, 466, 476, 480, 482, 490, 499,
- 508, 511, 513, 525, 535, 538, 540, 552,
- 562, 565, 567, 579, 589, 592, 594, 606,
- 616, 619, 621, 633, 643, 646, 648, 681,
- 712, 724, 733, 745, 754, 766, 775, 787,
- 796, 808, 817, 850, 854, 856, 887, 896,
- 905, 908, 910, 943, 974, 1007, 1011, 1013,
- 1044, 1053, 1062, 1071, 1074, 1076, 1088, 1098,
- 1101, 1103, 1115, 1125, 1128, 1130, 1142, 1152,
- 1155, 1157, 1190, 1221, 1233, 1242, 1254, 1263,
- 1275, 1284, 1317, 1321, 1323, 1354, 1363, 1372,
- 1375, 1377, 1389, 1399, 1402, 1404, 1416, 1426,
- 1429, 1431, 1443, 1453, 1456, 1458, 1491, 1522,
- 1534, 1543, 1555, 1564, 1576, 1585, 1618, 1622,
- 1624, 1655, 1664, 1673, 1676, 1678, 1690, 1700,
- 1703, 1705, 1738, 1769, 1781, 1790, 1823, 1827,
- 1829, 1860, 1869, 1878, 1881, 1883, 1916, 1947,
- 1980, 1984, 1986, 2017, 2042, 2067, 2074, 2083,
- 2092, 2101, 2110, 2122, 2131, 2164, 2168, 2170,
- 2201, 2210, 2219, 2228, 2237, 2241, 2243, 2253,
- 2257, 2259, 2269, 2273, 2275, 2285, 2289, 2291,
- 2301, 2305, 2307, 2317, 2321, 2323, 2333, 2337,
- 2339, 2349, 2353, 2355, 2365, 2369, 2371, 2381,
- 2385, 2387, 2397, 2401, 2403, 2413, 2417, 2419,
- 2429, 2433, 2435, 2445, 2449, 2451, 2480, 2511,
- 2520, 2524, 2526, 2536, 2548, 2557, 2562, 2567,
- 2571, 2573, 2580, 2590, 2599, 2603, 2605, 2615,
- 2627, 2631, 2633, 2664, 2668, 2670, 2680
+ 0, 0, 9, 18, 21, 23, 35, 45,
+ 48, 50, 53, 55, 67, 77, 80, 82,
+ 91, 103, 112, 119, 126, 128, 138, 146,
+ 153, 155, 167, 177, 180, 182, 191, 198,
+ 204, 211, 218, 224, 234, 244, 247, 249,
+ 261, 271, 274, 276, 286, 290, 292, 300,
+ 309, 318, 321, 323, 335, 345, 348, 350,
+ 362, 372, 375, 377, 389, 399, 402, 404,
+ 416, 426, 429, 431, 443, 453, 456, 458,
+ 470, 479, 491, 500, 512, 521, 533, 542,
+ 554, 563, 567, 569, 578, 587, 590, 592,
+ 596, 598, 607, 616, 625, 628, 630, 642,
+ 652, 655, 657, 669, 679, 682, 684, 696,
+ 706, 709, 711, 723, 732, 744, 753, 765,
+ 774, 778, 780, 789, 798, 801, 803, 815,
+ 825, 828, 830, 842, 852, 855, 857, 869,
+ 879, 882, 884, 896, 905, 917, 926, 938,
+ 947, 951, 953, 962, 971, 974, 976, 988,
+ 998, 1001, 1003, 1015, 1024, 1028, 1030, 1039,
+ 1048, 1051, 1053, 1057, 1059, 1066, 1075, 1084,
+ 1093, 1102, 1114, 1123, 1127, 1129, 1138, 1147,
+ 1156, 1165, 1169, 1171, 1181, 1185, 1187, 1197,
+ 1201, 1203, 1213, 1217, 1219, 1229, 1233, 1235,
+ 1245, 1249, 1251, 1261, 1265, 1267, 1277, 1281,
+ 1283, 1293, 1297, 1299, 1309, 1313, 1315, 1325,
+ 1329, 1331, 1341, 1345, 1347, 1357, 1361, 1363,
+ 1373, 1377, 1379, 1388, 1392, 1394, 1404, 1416,
+ 1425, 1435, 1442, 1446, 1448, 1455, 1465, 1474,
+ 1478, 1480, 1490, 1502, 1506, 1508, 1512, 1514,
+ 1524, 1530, 1562, 1592, 1624, 1656, 1686, 1714,
+ 1746, 1776, 1808, 1838, 1870, 1900, 1932, 1962,
+ 1994, 2024, 2056, 2086, 2118, 2148, 2180, 2210,
+ 2242, 2272, 2304, 2334, 2366, 2396, 2428, 2458,
+ 2482, 2506, 2538, 2568, 2596, 2626
};
static const char _svg_path_trans_keys[] = {
- 0, 13, 32, 77, 109, 9, 10, 13,
- 32, 43, 45, 46, 9, 10, 48, 57,
13, 32, 43, 45, 46, 9, 10, 48,
- 57, 46, 48, 57, 48, 57, 13, 32,
- 44, 46, 69, 101, 9, 10, 43, 45,
- 48, 57, 13, 32, 44, 46, 9, 10,
- 43, 45, 48, 57, 46, 48, 57, 48,
- 57, 0, 13, 32, 44, 46, 65, 67,
- 69, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 101, 104, 108, 109, 113,
- 115, 116, 118, 122, 9, 10, 43, 45,
- 48, 57, 0, 13, 32, 44, 46, 65,
- 67, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 104, 108, 109, 113, 115,
- 116, 118, 122, 9, 10, 43, 45, 48,
- 57, 46, 48, 57, 48, 57, 13, 32,
- 44, 46, 69, 101, 9, 10, 43, 45,
- 48, 57, 13, 32, 44, 46, 9, 10,
- 43, 45, 48, 57, 46, 48, 57, 48,
- 57, 0, 13, 32, 44, 46, 65, 67,
- 69, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 101, 104, 108, 109, 113,
- 115, 116, 118, 122, 9, 10, 43, 45,
+ 57, 13, 32, 43, 45, 46, 9, 10,
+ 48, 57, 46, 48, 57, 48, 57, 13,
+ 32, 44, 46, 69, 101, 9, 10, 43,
+ 45, 48, 57, 13, 32, 44, 46, 9,
+ 10, 43, 45, 48, 57, 46, 48, 57,
+ 48, 57, 46, 48, 57, 48, 57, 13,
+ 32, 44, 46, 69, 101, 9, 10, 43,
+ 45, 48, 57, 13, 32, 44, 46, 9,
+ 10, 43, 45, 48, 57, 46, 48, 57,
48, 57, 13, 32, 43, 45, 46, 9,
10, 48, 57, 13, 32, 44, 46, 69,
101, 9, 10, 43, 45, 48, 57, 13,
32, 43, 45, 46, 9, 10, 48, 57,
- 0, 13, 32, 44, 46, 65, 67, 69,
- 72, 76, 77, 81, 83, 84, 86, 90,
- 97, 99, 101, 104, 108, 109, 113, 115,
- 116, 118, 122, 9, 10, 43, 45, 48,
- 57, 13, 32, 46, 9, 10, 48, 57,
- 13, 32, 46, 9, 10, 48, 57, 48,
+ 13, 32, 46, 9, 10, 48, 57, 13,
+ 32, 46, 9, 10, 48, 57, 48, 57,
+ 13, 32, 44, 46, 69, 101, 9, 10,
+ 48, 57, 13, 32, 44, 46, 9, 10,
+ 48, 57, 13, 32, 46, 9, 10, 48,
+ 57, 48, 57, 13, 32, 44, 46, 69,
+ 101, 9, 10, 43, 45, 48, 57, 13,
+ 32, 44, 46, 9, 10, 43, 45, 48,
+ 57, 46, 48, 57, 48, 57, 13, 32,
+ 44, 69, 101, 9, 10, 48, 57, 13,
+ 32, 44, 48, 49, 9, 10, 13, 32,
+ 48, 49, 9, 10, 13, 32, 44, 48,
+ 49, 9, 10, 13, 32, 44, 48, 49,
+ 9, 10, 13, 32, 48, 49, 9, 10,
+ 13, 32, 44, 46, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 9, 10,
+ 43, 45, 48, 57, 46, 48, 57, 48,
57, 13, 32, 44, 46, 69, 101, 9,
- 10, 48, 57, 13, 32, 44, 46, 9,
- 10, 48, 57, 13, 32, 46, 9, 10,
+ 10, 43, 45, 48, 57, 13, 32, 44,
+ 46, 9, 10, 43, 45, 48, 57, 46,
48, 57, 48, 57, 13, 32, 44, 46,
- 69, 101, 9, 10, 43, 45, 48, 57,
- 13, 32, 44, 46, 9, 10, 43, 45,
- 48, 57, 46, 48, 57, 48, 57, 13,
- 32, 44, 69, 101, 9, 10, 48, 57,
- 13, 32, 44, 48, 49, 9, 10, 13,
- 32, 48, 49, 9, 10, 13, 32, 44,
- 9, 10, 13, 32, 44, 48, 49, 9,
- 10, 13, 32, 48, 49, 9, 10, 13,
- 32, 44, 9, 10, 13, 32, 44, 46,
- 9, 10, 43, 45, 48, 57, 46, 48,
+ 69, 101, 9, 10, 48, 57, 43, 45,
+ 48, 57, 48, 57, 13, 32, 44, 46,
+ 9, 10, 48, 57, 13, 32, 43, 45,
+ 46, 9, 10, 48, 57, 13, 32, 43,
+ 45, 46, 9, 10, 48, 57, 46, 48,
57, 48, 57, 13, 32, 44, 46, 69,
101, 9, 10, 43, 45, 48, 57, 13,
32, 44, 46, 9, 10, 43, 45, 48,
- 57, 46, 48, 57, 48, 57, 0, 13,
- 32, 44, 46, 65, 67, 69, 72, 76,
- 77, 81, 83, 84, 86, 90, 97, 99,
- 101, 104, 108, 109, 113, 115, 116, 118,
- 122, 9, 10, 48, 57, 0, 13, 32,
- 44, 46, 65, 67, 72, 76, 77, 81,
- 83, 84, 86, 90, 97, 99, 104, 108,
- 109, 113, 115, 116, 118, 122, 9, 10,
- 48, 57, 13, 32, 44, 46, 69, 101,
- 9, 10, 48, 57, 43, 45, 48, 57,
+ 57, 46, 48, 57, 48, 57, 13, 32,
+ 44, 46, 69, 101, 9, 10, 43, 45,
48, 57, 13, 32, 44, 46, 9, 10,
- 48, 57, 13, 32, 43, 45, 46, 9,
- 10, 48, 57, 13, 32, 43, 45, 46,
- 9, 10, 48, 57, 46, 48, 57, 48,
+ 43, 45, 48, 57, 46, 48, 57, 48,
57, 13, 32, 44, 46, 69, 101, 9,
10, 43, 45, 48, 57, 13, 32, 44,
46, 9, 10, 43, 45, 48, 57, 46,
@@ -276,102 +157,29 @@ static const char _svg_path_trans_keys[] = {
10, 43, 45, 48, 57, 46, 48, 57,
48, 57, 13, 32, 44, 46, 69, 101,
9, 10, 43, 45, 48, 57, 13, 32,
- 44, 46, 9, 10, 43, 45, 48, 57,
- 46, 48, 57, 48, 57, 13, 32, 44,
- 46, 69, 101, 9, 10, 43, 45, 48,
- 57, 13, 32, 44, 46, 9, 10, 43,
- 45, 48, 57, 46, 48, 57, 48, 57,
- 0, 13, 32, 44, 46, 65, 67, 69,
- 72, 76, 77, 81, 83, 84, 86, 90,
- 97, 99, 101, 104, 108, 109, 113, 115,
- 116, 118, 122, 9, 10, 43, 45, 48,
- 57, 0, 13, 32, 44, 46, 65, 67,
- 72, 76, 77, 81, 83, 84, 86, 90,
- 97, 99, 104, 108, 109, 113, 115, 116,
- 118, 122, 9, 10, 43, 45, 48, 57,
- 13, 32, 44, 46, 69, 101, 9, 10,
- 43, 45, 48, 57, 13, 32, 43, 45,
- 46, 9, 10, 48, 57, 13, 32, 44,
- 46, 69, 101, 9, 10, 43, 45, 48,
- 57, 13, 32, 43, 45, 46, 9, 10,
- 48, 57, 13, 32, 44, 46, 69, 101,
- 9, 10, 43, 45, 48, 57, 13, 32,
43, 45, 46, 9, 10, 48, 57, 13,
32, 44, 46, 69, 101, 9, 10, 43,
45, 48, 57, 13, 32, 43, 45, 46,
9, 10, 48, 57, 13, 32, 44, 46,
69, 101, 9, 10, 43, 45, 48, 57,
13, 32, 43, 45, 46, 9, 10, 48,
- 57, 0, 13, 32, 44, 46, 65, 67,
- 69, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 101, 104, 108, 109, 113,
- 115, 116, 118, 122, 9, 10, 43, 45,
- 48, 57, 43, 45, 48, 57, 48, 57,
- 0, 13, 32, 44, 46, 65, 67, 72,
- 76, 77, 81, 83, 84, 86, 90, 97,
- 99, 104, 108, 109, 113, 115, 116, 118,
- 122, 9, 10, 43, 45, 48, 57, 13,
- 32, 43, 45, 46, 9, 10, 48, 57,
- 13, 32, 43, 45, 46, 9, 10, 48,
- 57, 46, 48, 57, 48, 57, 0, 13,
- 32, 44, 46, 65, 67, 69, 72, 76,
- 77, 81, 83, 84, 86, 90, 97, 99,
- 101, 104, 108, 109, 113, 115, 116, 118,
- 122, 9, 10, 43, 45, 48, 57, 0,
- 13, 32, 44, 46, 65, 67, 72, 76,
- 77, 81, 83, 84, 86, 90, 97, 99,
- 104, 108, 109, 113, 115, 116, 118, 122,
- 9, 10, 43, 45, 48, 57, 0, 13,
- 32, 44, 46, 65, 67, 69, 72, 76,
- 77, 81, 83, 84, 86, 90, 97, 99,
- 101, 104, 108, 109, 113, 115, 116, 118,
- 122, 9, 10, 43, 45, 48, 57, 43,
- 45, 48, 57, 48, 57, 0, 13, 32,
- 44, 46, 65, 67, 72, 76, 77, 81,
- 83, 84, 86, 90, 97, 99, 104, 108,
- 109, 113, 115, 116, 118, 122, 9, 10,
- 43, 45, 48, 57, 13, 32, 43, 45,
- 46, 9, 10, 48, 57, 13, 32, 43,
+ 57, 13, 32, 44, 46, 69, 101, 9,
+ 10, 43, 45, 48, 57, 13, 32, 43,
45, 46, 9, 10, 48, 57, 13, 32,
- 43, 45, 46, 9, 10, 48, 57, 46,
- 48, 57, 48, 57, 13, 32, 44, 46,
- 69, 101, 9, 10, 43, 45, 48, 57,
- 13, 32, 44, 46, 9, 10, 43, 45,
- 48, 57, 46, 48, 57, 48, 57, 13,
- 32, 44, 46, 69, 101, 9, 10, 43,
- 45, 48, 57, 13, 32, 44, 46, 9,
- 10, 43, 45, 48, 57, 46, 48, 57,
- 48, 57, 13, 32, 44, 46, 69, 101,
- 9, 10, 43, 45, 48, 57, 13, 32,
- 44, 46, 9, 10, 43, 45, 48, 57,
- 46, 48, 57, 48, 57, 0, 13, 32,
- 44, 46, 65, 67, 69, 72, 76, 77,
- 81, 83, 84, 86, 90, 97, 99, 101,
- 104, 108, 109, 113, 115, 116, 118, 122,
- 9, 10, 43, 45, 48, 57, 0, 13,
- 32, 44, 46, 65, 67, 72, 76, 77,
- 81, 83, 84, 86, 90, 97, 99, 104,
- 108, 109, 113, 115, 116, 118, 122, 9,
- 10, 43, 45, 48, 57, 13, 32, 44,
- 46, 69, 101, 9, 10, 43, 45, 48,
+ 44, 46, 69, 101, 9, 10, 43, 45,
+ 48, 57, 13, 32, 43, 45, 46, 9,
+ 10, 48, 57, 43, 45, 48, 57, 48,
57, 13, 32, 43, 45, 46, 9, 10,
- 48, 57, 13, 32, 44, 46, 69, 101,
- 9, 10, 43, 45, 48, 57, 13, 32,
- 43, 45, 46, 9, 10, 48, 57, 13,
- 32, 44, 46, 69, 101, 9, 10, 43,
- 45, 48, 57, 13, 32, 43, 45, 46,
- 9, 10, 48, 57, 0, 13, 32, 44,
- 46, 65, 67, 69, 72, 76, 77, 81,
- 83, 84, 86, 90, 97, 99, 101, 104,
- 108, 109, 113, 115, 116, 118, 122, 9,
- 10, 43, 45, 48, 57, 43, 45, 48,
- 57, 48, 57, 0, 13, 32, 44, 46,
- 65, 67, 72, 76, 77, 81, 83, 84,
- 86, 90, 97, 99, 104, 108, 109, 113,
- 115, 116, 118, 122, 9, 10, 43, 45,
48, 57, 13, 32, 43, 45, 46, 9,
- 10, 48, 57, 13, 32, 43, 45, 46,
- 9, 10, 48, 57, 46, 48, 57, 48,
+ 10, 48, 57, 46, 48, 57, 48, 57,
+ 43, 45, 48, 57, 48, 57, 13, 32,
+ 43, 45, 46, 9, 10, 48, 57, 13,
+ 32, 43, 45, 46, 9, 10, 48, 57,
+ 13, 32, 43, 45, 46, 9, 10, 48,
+ 57, 46, 48, 57, 48, 57, 13, 32,
+ 44, 46, 69, 101, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 9, 10,
+ 43, 45, 48, 57, 46, 48, 57, 48,
57, 13, 32, 44, 46, 69, 101, 9,
10, 43, 45, 48, 57, 13, 32, 44,
46, 9, 10, 43, 45, 48, 57, 46,
@@ -380,85 +188,49 @@ static const char _svg_path_trans_keys[] = {
13, 32, 44, 46, 9, 10, 43, 45,
48, 57, 46, 48, 57, 48, 57, 13,
32, 44, 46, 69, 101, 9, 10, 43,
- 45, 48, 57, 13, 32, 44, 46, 9,
- 10, 43, 45, 48, 57, 46, 48, 57,
- 48, 57, 0, 13, 32, 44, 46, 65,
- 67, 69, 72, 76, 77, 81, 83, 84,
- 86, 90, 97, 99, 101, 104, 108, 109,
- 113, 115, 116, 118, 122, 9, 10, 43,
- 45, 48, 57, 0, 13, 32, 44, 46,
- 65, 67, 72, 76, 77, 81, 83, 84,
- 86, 90, 97, 99, 104, 108, 109, 113,
- 115, 116, 118, 122, 9, 10, 43, 45,
- 48, 57, 13, 32, 44, 46, 69, 101,
- 9, 10, 43, 45, 48, 57, 13, 32,
- 43, 45, 46, 9, 10, 48, 57, 13,
- 32, 44, 46, 69, 101, 9, 10, 43,
45, 48, 57, 13, 32, 43, 45, 46,
9, 10, 48, 57, 13, 32, 44, 46,
69, 101, 9, 10, 43, 45, 48, 57,
13, 32, 43, 45, 46, 9, 10, 48,
- 57, 0, 13, 32, 44, 46, 65, 67,
- 69, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 101, 104, 108, 109, 113,
- 115, 116, 118, 122, 9, 10, 43, 45,
- 48, 57, 43, 45, 48, 57, 48, 57,
- 0, 13, 32, 44, 46, 65, 67, 72,
- 76, 77, 81, 83, 84, 86, 90, 97,
- 99, 104, 108, 109, 113, 115, 116, 118,
- 122, 9, 10, 43, 45, 48, 57, 13,
- 32, 43, 45, 46, 9, 10, 48, 57,
- 13, 32, 43, 45, 46, 9, 10, 48,
+ 57, 13, 32, 44, 46, 69, 101, 9,
+ 10, 43, 45, 48, 57, 13, 32, 43,
+ 45, 46, 9, 10, 48, 57, 43, 45,
+ 48, 57, 48, 57, 13, 32, 43, 45,
+ 46, 9, 10, 48, 57, 13, 32, 43,
+ 45, 46, 9, 10, 48, 57, 46, 48,
+ 57, 48, 57, 13, 32, 44, 46, 69,
+ 101, 9, 10, 43, 45, 48, 57, 13,
+ 32, 44, 46, 9, 10, 43, 45, 48,
57, 46, 48, 57, 48, 57, 13, 32,
44, 46, 69, 101, 9, 10, 43, 45,
48, 57, 13, 32, 44, 46, 9, 10,
43, 45, 48, 57, 46, 48, 57, 48,
- 57, 0, 13, 32, 44, 46, 65, 67,
- 69, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 101, 104, 108, 109, 113,
- 115, 116, 118, 122, 9, 10, 43, 45,
- 48, 57, 0, 13, 32, 44, 46, 65,
- 67, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 104, 108, 109, 113, 115,
- 116, 118, 122, 9, 10, 43, 45, 48,
+ 57, 13, 32, 44, 46, 69, 101, 9,
+ 10, 43, 45, 48, 57, 13, 32, 44,
+ 46, 9, 10, 43, 45, 48, 57, 46,
+ 48, 57, 48, 57, 13, 32, 44, 46,
+ 69, 101, 9, 10, 43, 45, 48, 57,
+ 13, 32, 43, 45, 46, 9, 10, 48,
57, 13, 32, 44, 46, 69, 101, 9,
10, 43, 45, 48, 57, 13, 32, 43,
- 45, 46, 9, 10, 48, 57, 0, 13,
- 32, 44, 46, 65, 67, 69, 72, 76,
- 77, 81, 83, 84, 86, 90, 97, 99,
- 101, 104, 108, 109, 113, 115, 116, 118,
- 122, 9, 10, 43, 45, 48, 57, 43,
- 45, 48, 57, 48, 57, 0, 13, 32,
- 44, 46, 65, 67, 72, 76, 77, 81,
- 83, 84, 86, 90, 97, 99, 104, 108,
- 109, 113, 115, 116, 118, 122, 9, 10,
- 43, 45, 48, 57, 13, 32, 43, 45,
- 46, 9, 10, 48, 57, 13, 32, 43,
- 45, 46, 9, 10, 48, 57, 46, 48,
- 57, 48, 57, 0, 13, 32, 44, 46,
- 65, 67, 69, 72, 76, 77, 81, 83,
- 84, 86, 90, 97, 99, 101, 104, 108,
- 109, 113, 115, 116, 118, 122, 9, 10,
- 43, 45, 48, 57, 0, 13, 32, 44,
- 46, 65, 67, 72, 76, 77, 81, 83,
- 84, 86, 90, 97, 99, 104, 108, 109,
- 113, 115, 116, 118, 122, 9, 10, 43,
- 45, 48, 57, 0, 13, 32, 44, 46,
- 65, 67, 69, 72, 76, 77, 81, 83,
- 84, 86, 90, 97, 99, 101, 104, 108,
- 109, 113, 115, 116, 118, 122, 9, 10,
- 43, 45, 48, 57, 43, 45, 48, 57,
- 48, 57, 0, 13, 32, 44, 46, 65,
- 67, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 104, 108, 109, 113, 115,
- 116, 118, 122, 9, 10, 43, 45, 48,
- 57, 0, 13, 32, 65, 67, 72, 76,
- 77, 81, 83, 84, 86, 90, 97, 99,
- 104, 108, 109, 113, 115, 116, 118, 122,
- 9, 10, 0, 13, 32, 65, 67, 72,
- 76, 77, 81, 83, 84, 86, 90, 97,
- 99, 104, 108, 109, 113, 115, 116, 118,
- 122, 9, 10, 13, 32, 46, 9, 10,
+ 45, 46, 9, 10, 48, 57, 13, 32,
+ 44, 46, 69, 101, 9, 10, 43, 45,
+ 48, 57, 13, 32, 43, 45, 46, 9,
+ 10, 48, 57, 43, 45, 48, 57, 48,
+ 57, 13, 32, 43, 45, 46, 9, 10,
+ 48, 57, 13, 32, 43, 45, 46, 9,
+ 10, 48, 57, 46, 48, 57, 48, 57,
+ 13, 32, 44, 46, 69, 101, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 9, 10, 43, 45, 48, 57, 46, 48,
+ 57, 48, 57, 13, 32, 44, 46, 69,
+ 101, 9, 10, 43, 45, 48, 57, 13,
+ 32, 43, 45, 46, 9, 10, 48, 57,
+ 43, 45, 48, 57, 48, 57, 13, 32,
+ 43, 45, 46, 9, 10, 48, 57, 13,
+ 32, 43, 45, 46, 9, 10, 48, 57,
+ 46, 48, 57, 48, 57, 43, 45, 48,
+ 57, 48, 57, 13, 32, 46, 9, 10,
48, 57, 13, 32, 43, 45, 46, 9,
10, 48, 57, 13, 32, 43, 45, 46,
9, 10, 48, 57, 13, 32, 43, 45,
@@ -466,15 +238,7 @@ static const char _svg_path_trans_keys[] = {
45, 46, 9, 10, 48, 57, 13, 32,
44, 46, 69, 101, 9, 10, 43, 45,
48, 57, 13, 32, 43, 45, 46, 9,
- 10, 48, 57, 0, 13, 32, 44, 46,
- 65, 67, 69, 72, 76, 77, 81, 83,
- 84, 86, 90, 97, 99, 101, 104, 108,
- 109, 113, 115, 116, 118, 122, 9, 10,
- 43, 45, 48, 57, 43, 45, 48, 57,
- 48, 57, 0, 13, 32, 44, 46, 65,
- 67, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 104, 108, 109, 113, 115,
- 116, 118, 122, 9, 10, 43, 45, 48,
+ 10, 48, 57, 43, 45, 48, 57, 48,
57, 13, 32, 43, 45, 46, 9, 10,
48, 57, 13, 32, 43, 45, 46, 9,
10, 48, 57, 13, 32, 43, 45, 46,
@@ -506,661 +270,944 @@ static const char _svg_path_trans_keys[] = {
10, 43, 45, 48, 57, 43, 45, 48,
57, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 43, 45, 48,
- 57, 48, 57, 0, 13, 32, 44, 46,
+ 57, 48, 57, 13, 32, 43, 45, 46,
+ 9, 10, 48, 57, 43, 45, 48, 57,
+ 48, 57, 13, 32, 44, 46, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 69, 101, 9, 10, 43, 45, 48, 57,
+ 13, 32, 43, 45, 46, 9, 10, 48,
+ 57, 13, 32, 44, 46, 9, 10, 43,
+ 45, 48, 57, 13, 32, 44, 48, 49,
+ 9, 10, 43, 45, 48, 57, 48, 57,
+ 13, 32, 44, 9, 10, 48, 57, 13,
+ 32, 44, 46, 69, 101, 9, 10, 48,
+ 57, 13, 32, 43, 45, 46, 9, 10,
+ 48, 57, 43, 45, 48, 57, 48, 57,
+ 13, 32, 44, 46, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 69, 101,
+ 9, 10, 43, 45, 48, 57, 43, 45,
+ 48, 57, 48, 57, 43, 45, 48, 57,
+ 48, 57, 13, 32, 44, 46, 9, 10,
+ 43, 45, 48, 57, 13, 32, 77, 109,
+ 9, 10, 13, 32, 44, 46, 65, 67,
+ 69, 72, 76, 77, 81, 83, 84, 86,
+ 90, 97, 99, 101, 104, 108, 109, 113,
+ 115, 116, 118, 122, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 65, 67,
+ 72, 76, 77, 81, 83, 84, 86, 90,
+ 97, 99, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 69, 72,
+ 76, 77, 81, 83, 84, 86, 90, 97,
+ 99, 101, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 69, 72,
+ 76, 77, 81, 83, 84, 86, 90, 97,
+ 99, 101, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 69, 72,
+ 76, 77, 81, 83, 84, 86, 90, 97,
+ 99, 101, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 48, 57, 13, 32,
+ 44, 46, 65, 67, 72, 76, 77, 81,
+ 83, 84, 86, 90, 97, 99, 104, 108,
+ 109, 113, 115, 116, 118, 122, 9, 10,
+ 48, 57, 13, 32, 44, 46, 65, 67,
+ 69, 72, 76, 77, 81, 83, 84, 86,
+ 90, 97, 99, 101, 104, 108, 109, 113,
+ 115, 116, 118, 122, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 65, 67,
+ 72, 76, 77, 81, 83, 84, 86, 90,
+ 97, 99, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 69, 72,
+ 76, 77, 81, 83, 84, 86, 90, 97,
+ 99, 101, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 72, 76,
+ 77, 81, 83, 84, 86, 90, 97, 99,
+ 104, 108, 109, 113, 115, 116, 118, 122,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 44, 46, 65, 67, 69, 72, 76, 77,
+ 81, 83, 84, 86, 90, 97, 99, 101,
+ 104, 108, 109, 113, 115, 116, 118, 122,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 44, 46, 65, 67, 72, 76, 77, 81,
+ 83, 84, 86, 90, 97, 99, 104, 108,
+ 109, 113, 115, 116, 118, 122, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 65, 67, 69, 72, 76, 77, 81, 83,
+ 84, 86, 90, 97, 99, 101, 104, 108,
+ 109, 113, 115, 116, 118, 122, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
65, 67, 72, 76, 77, 81, 83, 84,
86, 90, 97, 99, 104, 108, 109, 113,
- 115, 116, 118, 122, 9, 10, 48, 57,
- 0, 13, 32, 44, 46, 65, 67, 69,
+ 115, 116, 118, 122, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 65, 67,
+ 69, 72, 76, 77, 81, 83, 84, 86,
+ 90, 97, 99, 101, 104, 108, 109, 113,
+ 115, 116, 118, 122, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 65, 67,
72, 76, 77, 81, 83, 84, 86, 90,
- 97, 99, 101, 104, 108, 109, 113, 115,
- 116, 118, 122, 9, 10, 48, 57, 13,
- 32, 43, 45, 46, 9, 10, 48, 57,
- 43, 45, 48, 57, 48, 57, 13, 32,
- 44, 46, 9, 10, 43, 45, 48, 57,
- 13, 32, 44, 46, 69, 101, 9, 10,
- 43, 45, 48, 57, 13, 32, 43, 45,
- 46, 9, 10, 48, 57, 13, 32, 44,
- 9, 10, 13, 32, 44, 9, 10, 43,
- 45, 48, 57, 48, 57, 13, 32, 44,
+ 97, 99, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 69, 72,
+ 76, 77, 81, 83, 84, 86, 90, 97,
+ 99, 101, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 72, 76,
+ 77, 81, 83, 84, 86, 90, 97, 99,
+ 104, 108, 109, 113, 115, 116, 118, 122,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 44, 46, 65, 67, 69, 72, 76, 77,
+ 81, 83, 84, 86, 90, 97, 99, 101,
+ 104, 108, 109, 113, 115, 116, 118, 122,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 44, 46, 65, 67, 72, 76, 77, 81,
+ 83, 84, 86, 90, 97, 99, 104, 108,
+ 109, 113, 115, 116, 118, 122, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 65, 67, 69, 72, 76, 77, 81, 83,
+ 84, 86, 90, 97, 99, 101, 104, 108,
+ 109, 113, 115, 116, 118, 122, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 65, 67, 72, 76, 77, 81, 83, 84,
+ 86, 90, 97, 99, 104, 108, 109, 113,
+ 115, 116, 118, 122, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 65, 67,
+ 69, 72, 76, 77, 81, 83, 84, 86,
+ 90, 97, 99, 101, 104, 108, 109, 113,
+ 115, 116, 118, 122, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 65, 67,
+ 72, 76, 77, 81, 83, 84, 86, 90,
+ 97, 99, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 69, 72,
+ 76, 77, 81, 83, 84, 86, 90, 97,
+ 99, 101, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 72, 76,
+ 77, 81, 83, 84, 86, 90, 97, 99,
+ 104, 108, 109, 113, 115, 116, 118, 122,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 44, 46, 65, 67, 69, 72, 76, 77,
+ 81, 83, 84, 86, 90, 97, 99, 101,
+ 104, 108, 109, 113, 115, 116, 118, 122,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 44, 46, 65, 67, 72, 76, 77, 81,
+ 83, 84, 86, 90, 97, 99, 104, 108,
+ 109, 113, 115, 116, 118, 122, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 65, 67, 69, 72, 76, 77, 81, 83,
+ 84, 86, 90, 97, 99, 101, 104, 108,
+ 109, 113, 115, 116, 118, 122, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 65, 67, 72, 76, 77, 81, 83, 84,
+ 86, 90, 97, 99, 104, 108, 109, 113,
+ 115, 116, 118, 122, 9, 10, 43, 45,
+ 48, 57, 13, 32, 65, 67, 72, 76,
+ 77, 81, 83, 84, 86, 90, 97, 99,
+ 104, 108, 109, 113, 115, 116, 118, 122,
+ 9, 10, 13, 32, 65, 67, 72, 76,
+ 77, 81, 83, 84, 86, 90, 97, 99,
+ 104, 108, 109, 113, 115, 116, 118, 122,
+ 9, 10, 13, 32, 44, 46, 65, 67,
+ 69, 72, 76, 77, 81, 83, 84, 86,
+ 90, 97, 99, 101, 104, 108, 109, 113,
+ 115, 116, 118, 122, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 65, 67,
+ 72, 76, 77, 81, 83, 84, 86, 90,
+ 97, 99, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 72, 76,
+ 77, 81, 83, 84, 86, 90, 97, 99,
+ 104, 108, 109, 113, 115, 116, 118, 122,
9, 10, 48, 57, 13, 32, 44, 46,
- 69, 101, 9, 10, 48, 57, 13, 32,
- 43, 45, 46, 9, 10, 48, 57, 43,
- 45, 48, 57, 48, 57, 13, 32, 44,
- 46, 9, 10, 43, 45, 48, 57, 13,
- 32, 44, 46, 69, 101, 9, 10, 43,
- 45, 48, 57, 43, 45, 48, 57, 48,
- 57, 0, 13, 32, 44, 46, 65, 67,
+ 65, 67, 69, 72, 76, 77, 81, 83,
+ 84, 86, 90, 97, 99, 101, 104, 108,
+ 109, 113, 115, 116, 118, 122, 9, 10,
+ 48, 57, 13, 32, 44, 46, 65, 67,
72, 76, 77, 81, 83, 84, 86, 90,
97, 99, 104, 108, 109, 113, 115, 116,
118, 122, 9, 10, 43, 45, 48, 57,
- 43, 45, 48, 57, 48, 57, 13, 32,
- 44, 46, 9, 10, 43, 45, 48, 57,
0
};
static const char _svg_path_single_lengths[] = {
+ 0, 5, 5, 1, 0, 6, 4, 1,
+ 0, 1, 0, 6, 4, 1, 0, 5,
+ 6, 5, 3, 3, 0, 6, 4, 3,
+ 0, 6, 4, 1, 0, 5, 5, 4,
+ 5, 5, 4, 4, 4, 1, 0, 6,
+ 4, 1, 0, 6, 2, 0, 4, 5,
+ 5, 1, 0, 6, 4, 1, 0, 6,
+ 4, 1, 0, 6, 4, 1, 0, 6,
+ 4, 1, 0, 6, 4, 1, 0, 6,
+ 5, 6, 5, 6, 5, 6, 5, 6,
+ 5, 2, 0, 5, 5, 1, 0, 2,
0, 5, 5, 5, 1, 0, 6, 4,
- 1, 0, 27, 25, 1, 0, 6, 4,
- 1, 0, 27, 5, 6, 5, 27, 3,
- 3, 0, 6, 4, 3, 0, 6, 4,
- 1, 0, 5, 5, 4, 3, 5, 4,
- 3, 4, 1, 0, 6, 4, 1, 0,
- 27, 25, 6, 2, 0, 4, 5, 5,
1, 0, 6, 4, 1, 0, 6, 4,
+ 1, 0, 6, 5, 6, 5, 6, 5,
+ 2, 0, 5, 5, 1, 0, 6, 4,
1, 0, 6, 4, 1, 0, 6, 4,
- 1, 0, 6, 4, 1, 0, 27, 25,
- 6, 5, 6, 5, 6, 5, 6, 5,
- 6, 5, 27, 2, 0, 25, 5, 5,
- 1, 0, 27, 25, 27, 2, 0, 25,
- 5, 5, 5, 1, 0, 6, 4, 1,
- 0, 6, 4, 1, 0, 6, 4, 1,
- 0, 27, 25, 6, 5, 6, 5, 6,
- 5, 27, 2, 0, 25, 5, 5, 1,
- 0, 6, 4, 1, 0, 6, 4, 1,
- 0, 6, 4, 1, 0, 27, 25, 6,
- 5, 6, 5, 6, 5, 27, 2, 0,
- 25, 5, 5, 1, 0, 6, 4, 1,
- 0, 27, 25, 6, 5, 27, 2, 0,
- 25, 5, 5, 1, 0, 27, 25, 27,
- 2, 0, 25, 23, 23, 3, 5, 5,
- 5, 5, 6, 5, 27, 2, 0, 25,
- 5, 5, 5, 5, 2, 0, 4, 2,
+ 1, 0, 6, 5, 6, 5, 6, 5,
+ 2, 0, 5, 5, 1, 0, 6, 4,
+ 1, 0, 6, 5, 2, 0, 5, 5,
+ 1, 0, 2, 0, 3, 5, 5, 5,
+ 5, 6, 5, 2, 0, 5, 5, 5,
+ 5, 2, 0, 4, 2, 0, 4, 2,
0, 4, 2, 0, 4, 2, 0, 4,
2, 0, 4, 2, 0, 4, 2, 0,
4, 2, 0, 4, 2, 0, 4, 2,
0, 4, 2, 0, 4, 2, 0, 4,
- 2, 0, 4, 2, 0, 25, 27, 5,
- 2, 0, 4, 6, 5, 3, 3, 2,
- 0, 3, 6, 5, 2, 0, 4, 6,
- 2, 0, 25, 2, 0, 4, 0
+ 2, 0, 5, 2, 0, 4, 6, 5,
+ 4, 5, 2, 0, 3, 6, 5, 2,
+ 0, 4, 6, 2, 0, 2, 0, 4,
+ 4, 26, 24, 26, 26, 26, 24, 26,
+ 24, 26, 24, 26, 24, 26, 24, 26,
+ 24, 26, 24, 26, 24, 26, 24, 26,
+ 24, 26, 24, 26, 24, 26, 24, 22,
+ 22, 26, 24, 24, 26, 24
};
static const char _svg_path_range_lengths[] = {
- 0, 1, 2, 2, 1, 1, 3, 3,
+ 0, 2, 2, 1, 1, 3, 3, 1,
+ 1, 1, 1, 3, 3, 1, 1, 2,
+ 3, 2, 2, 2, 1, 2, 2, 2,
+ 1, 3, 3, 1, 1, 2, 1, 1,
+ 1, 1, 1, 3, 3, 1, 1, 3,
+ 3, 1, 1, 2, 1, 1, 2, 2,
+ 2, 1, 1, 3, 3, 1, 1, 3,
+ 3, 1, 1, 3, 3, 1, 1, 3,
+ 3, 1, 1, 3, 3, 1, 1, 3,
+ 2, 3, 2, 3, 2, 3, 2, 3,
+ 2, 1, 1, 2, 2, 1, 1, 1,
+ 1, 2, 2, 2, 1, 1, 3, 3,
1, 1, 3, 3, 1, 1, 3, 3,
1, 1, 3, 2, 3, 2, 3, 2,
- 2, 1, 2, 2, 2, 1, 3, 3,
- 1, 1, 2, 1, 1, 1, 1, 1,
- 1, 3, 1, 1, 3, 3, 1, 1,
- 2, 2, 2, 1, 1, 2, 2, 2,
+ 1, 1, 2, 2, 1, 1, 3, 3,
1, 1, 3, 3, 1, 1, 3, 3,
- 1, 1, 3, 3, 1, 1, 3, 3,
- 1, 1, 3, 3, 1, 1, 3, 3,
- 3, 2, 3, 2, 3, 2, 3, 2,
- 3, 2, 3, 1, 1, 3, 2, 2,
- 1, 1, 3, 3, 3, 1, 1, 3,
- 2, 2, 2, 1, 1, 3, 3, 1,
- 1, 3, 3, 1, 1, 3, 3, 1,
- 1, 3, 3, 3, 2, 3, 2, 3,
- 2, 3, 1, 1, 3, 2, 2, 1,
- 1, 3, 3, 1, 1, 3, 3, 1,
- 1, 3, 3, 1, 1, 3, 3, 3,
- 2, 3, 2, 3, 2, 3, 1, 1,
- 3, 2, 2, 1, 1, 3, 3, 1,
- 1, 3, 3, 3, 2, 3, 1, 1,
- 3, 2, 2, 1, 1, 3, 3, 3,
- 1, 1, 3, 1, 1, 2, 2, 2,
- 2, 2, 3, 2, 3, 1, 1, 3,
- 2, 2, 2, 2, 1, 1, 3, 1,
+ 1, 1, 3, 2, 3, 2, 3, 2,
+ 1, 1, 2, 2, 1, 1, 3, 3,
+ 1, 1, 3, 2, 1, 1, 2, 2,
+ 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 3, 2, 1, 1, 2, 2, 2,
+ 2, 1, 1, 3, 1, 1, 3, 1,
1, 3, 1, 1, 3, 1, 1, 3,
1, 1, 3, 1, 1, 3, 1, 1,
3, 1, 1, 3, 1, 1, 3, 1,
1, 3, 1, 1, 3, 1, 1, 3,
- 1, 1, 3, 1, 1, 2, 2, 2,
- 1, 1, 3, 3, 2, 1, 1, 1,
- 1, 2, 2, 2, 1, 1, 3, 3,
- 1, 1, 3, 1, 1, 3, 0
+ 1, 1, 2, 1, 1, 3, 3, 2,
+ 3, 1, 1, 1, 2, 2, 2, 1,
+ 1, 3, 3, 1, 1, 1, 1, 3,
+ 1, 3, 3, 3, 3, 2, 2, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 1,
+ 1, 3, 3, 2, 2, 3
};
static const short _svg_path_index_offsets[] = {
- 0, 0, 7, 15, 23, 26, 28, 38,
- 46, 49, 51, 82, 111, 114, 116, 126,
- 134, 137, 139, 170, 178, 188, 196, 227,
- 233, 239, 241, 250, 257, 263, 265, 275,
- 283, 286, 288, 296, 303, 309, 314, 321,
- 327, 332, 340, 343, 345, 355, 363, 366,
- 368, 398, 426, 435, 439, 441, 448, 456,
- 464, 467, 469, 479, 487, 490, 492, 502,
- 510, 513, 515, 525, 533, 536, 538, 548,
- 556, 559, 561, 571, 579, 582, 584, 615,
- 644, 654, 662, 672, 680, 690, 698, 708,
- 716, 726, 734, 765, 769, 771, 800, 808,
- 816, 819, 821, 852, 881, 912, 916, 918,
- 947, 955, 963, 971, 974, 976, 986, 994,
- 997, 999, 1009, 1017, 1020, 1022, 1032, 1040,
- 1043, 1045, 1076, 1105, 1115, 1123, 1133, 1141,
- 1151, 1159, 1190, 1194, 1196, 1225, 1233, 1241,
- 1244, 1246, 1256, 1264, 1267, 1269, 1279, 1287,
- 1290, 1292, 1302, 1310, 1313, 1315, 1346, 1375,
- 1385, 1393, 1403, 1411, 1421, 1429, 1460, 1464,
- 1466, 1495, 1503, 1511, 1514, 1516, 1526, 1534,
- 1537, 1539, 1570, 1599, 1609, 1617, 1648, 1652,
- 1654, 1683, 1691, 1699, 1702, 1704, 1735, 1764,
- 1795, 1799, 1801, 1830, 1855, 1880, 1886, 1894,
- 1902, 1910, 1918, 1928, 1936, 1967, 1971, 1973,
- 2002, 2010, 2018, 2026, 2034, 2038, 2040, 2048,
- 2052, 2054, 2062, 2066, 2068, 2076, 2080, 2082,
- 2090, 2094, 2096, 2104, 2108, 2110, 2118, 2122,
- 2124, 2132, 2136, 2138, 2146, 2150, 2152, 2160,
- 2164, 2166, 2174, 2178, 2180, 2188, 2192, 2194,
- 2202, 2206, 2208, 2216, 2220, 2222, 2250, 2280,
- 2288, 2292, 2294, 2302, 2312, 2320, 2325, 2330,
- 2334, 2336, 2342, 2351, 2359, 2363, 2365, 2373,
- 2383, 2387, 2389, 2418, 2422, 2424, 2432
+ 0, 0, 8, 16, 19, 21, 31, 39,
+ 42, 44, 47, 49, 59, 67, 70, 72,
+ 80, 90, 98, 104, 110, 112, 121, 128,
+ 134, 136, 146, 154, 157, 159, 167, 174,
+ 180, 187, 194, 200, 208, 216, 219, 221,
+ 231, 239, 242, 244, 253, 257, 259, 266,
+ 274, 282, 285, 287, 297, 305, 308, 310,
+ 320, 328, 331, 333, 343, 351, 354, 356,
+ 366, 374, 377, 379, 389, 397, 400, 402,
+ 412, 420, 430, 438, 448, 456, 466, 474,
+ 484, 492, 496, 498, 506, 514, 517, 519,
+ 523, 525, 533, 541, 549, 552, 554, 564,
+ 572, 575, 577, 587, 595, 598, 600, 610,
+ 618, 621, 623, 633, 641, 651, 659, 669,
+ 677, 681, 683, 691, 699, 702, 704, 714,
+ 722, 725, 727, 737, 745, 748, 750, 760,
+ 768, 771, 773, 783, 791, 801, 809, 819,
+ 827, 831, 833, 841, 849, 852, 854, 864,
+ 872, 875, 877, 887, 895, 899, 901, 909,
+ 917, 920, 922, 926, 928, 934, 942, 950,
+ 958, 966, 976, 984, 988, 990, 998, 1006,
+ 1014, 1022, 1026, 1028, 1036, 1040, 1042, 1050,
+ 1054, 1056, 1064, 1068, 1070, 1078, 1082, 1084,
+ 1092, 1096, 1098, 1106, 1110, 1112, 1120, 1124,
+ 1126, 1134, 1138, 1140, 1148, 1152, 1154, 1162,
+ 1166, 1168, 1176, 1180, 1182, 1190, 1194, 1196,
+ 1204, 1208, 1210, 1218, 1222, 1224, 1232, 1242,
+ 1250, 1258, 1265, 1269, 1271, 1277, 1286, 1294,
+ 1298, 1300, 1308, 1318, 1322, 1324, 1328, 1330,
+ 1338, 1344, 1374, 1402, 1432, 1462, 1491, 1518,
+ 1548, 1576, 1606, 1634, 1664, 1692, 1722, 1750,
+ 1780, 1808, 1838, 1866, 1896, 1924, 1954, 1982,
+ 2012, 2040, 2070, 2098, 2128, 2156, 2186, 2214,
+ 2238, 2262, 2292, 2320, 2347, 2376
};
static const short _svg_path_indicies[] = {
- 0, 2, 2, 3, 4, 2, 1, 5,
- 5, 6, 6, 7, 5, 8, 1, 9,
- 9, 10, 10, 11, 9, 12, 1, 13,
- 14, 1, 15, 1, 16, 16, 18, 19,
- 20, 20, 16, 17, 15, 1, 21, 21,
- 23, 24, 21, 22, 25, 1, 26, 27,
- 1, 28, 1, 29, 30, 30, 32, 33,
- 34, 35, 36, 37, 38, 39, 40, 41,
- 42, 43, 44, 45, 46, 36, 47, 48,
- 49, 50, 51, 52, 53, 44, 30, 31,
- 28, 1, 0, 54, 54, 56, 57, 59,
- 60, 61, 62, 3, 63, 64, 65, 66,
- 67, 68, 69, 70, 71, 4, 72, 73,
- 74, 75, 67, 54, 55, 58, 1, 76,
- 77, 1, 78, 1, 79, 79, 81, 82,
- 83, 83, 79, 80, 78, 1, 84, 84,
- 86, 87, 84, 85, 88, 1, 89, 90,
- 1, 91, 1, 92, 93, 93, 95, 96,
- 97, 98, 99, 100, 101, 102, 103, 104,
- 105, 106, 107, 108, 109, 99, 110, 111,
- 112, 113, 114, 115, 116, 107, 93, 94,
- 91, 1, 56, 56, 55, 55, 57, 56,
- 58, 1, 79, 79, 81, 78, 83, 83,
- 79, 80, 77, 1, 86, 86, 85, 85,
- 87, 86, 88, 1, 92, 93, 93, 95,
- 91, 97, 98, 99, 100, 101, 102, 103,
- 104, 105, 106, 107, 108, 109, 99, 110,
- 111, 112, 113, 114, 115, 116, 107, 93,
- 94, 90, 1, 117, 117, 118, 117, 119,
- 1, 120, 120, 121, 120, 122, 1, 123,
- 1, 124, 124, 125, 126, 127, 127, 124,
- 123, 1, 128, 128, 129, 130, 128, 131,
- 1, 129, 129, 130, 129, 131, 1, 132,
- 1, 133, 133, 135, 136, 137, 137, 133,
- 134, 132, 1, 138, 138, 140, 141, 138,
- 139, 142, 1, 143, 144, 1, 145, 1,
- 146, 146, 147, 148, 148, 146, 145, 1,
- 149, 149, 150, 151, 152, 149, 1, 150,
- 150, 151, 152, 150, 1, 153, 153, 154,
- 153, 1, 155, 155, 156, 157, 158, 155,
- 1, 156, 156, 157, 158, 156, 1, 159,
- 159, 160, 159, 1, 161, 161, 163, 164,
- 161, 162, 165, 1, 166, 167, 1, 168,
- 1, 169, 169, 171, 172, 173, 173, 169,
- 170, 168, 1, 174, 174, 176, 177, 174,
- 175, 178, 1, 179, 180, 1, 181, 1,
- 182, 183, 183, 184, 185, 186, 187, 188,
- 189, 190, 191, 192, 193, 194, 195, 196,
- 197, 198, 188, 199, 200, 201, 202, 203,
- 204, 205, 196, 183, 181, 1, 0, 206,
- 206, 120, 121, 59, 60, 61, 62, 3,
- 63, 64, 65, 66, 67, 68, 69, 70,
- 71, 4, 72, 73, 74, 75, 67, 206,
- 122, 1, 124, 124, 125, 123, 127, 127,
- 124, 207, 1, 208, 208, 209, 1, 209,
- 1, 124, 124, 125, 126, 124, 209, 1,
- 210, 210, 211, 211, 212, 210, 213, 1,
- 214, 214, 215, 215, 216, 214, 217, 1,
- 218, 219, 1, 220, 1, 221, 221, 223,
- 224, 225, 225, 221, 222, 220, 1, 226,
- 226, 228, 229, 226, 227, 230, 1, 231,
- 232, 1, 233, 1, 234, 234, 236, 237,
- 238, 238, 234, 235, 233, 1, 239, 239,
- 241, 242, 239, 240, 243, 1, 244, 245,
- 1, 246, 1, 247, 247, 249, 250, 251,
- 251, 247, 248, 246, 1, 252, 252, 254,
- 255, 252, 253, 256, 1, 257, 258, 1,
- 259, 1, 260, 260, 262, 263, 264, 264,
- 260, 261, 259, 1, 265, 265, 267, 268,
- 265, 266, 269, 1, 270, 271, 1, 272,
- 1, 273, 273, 275, 276, 277, 277, 273,
- 274, 272, 1, 278, 278, 280, 281, 278,
- 279, 282, 1, 283, 284, 1, 285, 1,
- 286, 287, 287, 289, 290, 291, 292, 293,
- 294, 295, 296, 297, 298, 299, 300, 301,
- 302, 303, 293, 304, 305, 306, 307, 308,
- 309, 310, 301, 287, 288, 285, 1, 0,
- 311, 311, 214, 216, 59, 60, 61, 62,
- 3, 63, 64, 65, 66, 67, 68, 69,
- 70, 71, 4, 72, 73, 74, 75, 67,
- 311, 215, 217, 1, 221, 221, 223, 220,
- 225, 225, 221, 222, 219, 1, 228, 228,
- 227, 227, 229, 228, 230, 1, 234, 234,
- 236, 233, 238, 238, 234, 235, 232, 1,
- 241, 241, 240, 240, 242, 241, 243, 1,
- 247, 247, 249, 246, 251, 251, 247, 248,
- 245, 1, 254, 254, 253, 253, 255, 254,
- 256, 1, 260, 260, 262, 259, 264, 264,
- 260, 261, 258, 1, 267, 267, 266, 266,
- 268, 267, 269, 1, 273, 273, 275, 272,
- 277, 277, 273, 274, 271, 1, 280, 280,
- 279, 279, 281, 280, 282, 1, 286, 287,
- 287, 289, 285, 291, 292, 293, 294, 295,
- 296, 297, 298, 299, 300, 301, 302, 303,
- 293, 304, 305, 306, 307, 308, 309, 310,
- 301, 287, 288, 284, 1, 312, 312, 313,
- 1, 313, 1, 286, 287, 287, 289, 290,
- 291, 292, 294, 295, 296, 297, 298, 299,
- 300, 301, 302, 303, 304, 305, 306, 307,
- 308, 309, 310, 301, 287, 288, 313, 1,
- 314, 314, 315, 315, 316, 314, 317, 1,
- 318, 318, 319, 319, 320, 318, 321, 1,
- 322, 323, 1, 324, 1, 325, 326, 326,
- 328, 329, 330, 331, 332, 333, 334, 335,
- 336, 337, 338, 339, 340, 341, 342, 332,
- 343, 344, 345, 346, 347, 348, 349, 340,
- 326, 327, 324, 1, 0, 350, 350, 318,
- 320, 59, 60, 61, 62, 3, 63, 64,
- 65, 66, 67, 68, 69, 70, 71, 4,
- 72, 73, 74, 75, 67, 350, 319, 321,
- 1, 325, 326, 326, 328, 324, 330, 331,
- 332, 333, 334, 335, 336, 337, 338, 339,
- 340, 341, 342, 332, 343, 344, 345, 346,
- 347, 348, 349, 340, 326, 327, 323, 1,
- 351, 351, 352, 1, 352, 1, 325, 326,
- 326, 328, 329, 330, 331, 333, 334, 335,
- 336, 337, 338, 339, 340, 341, 342, 343,
- 344, 345, 346, 347, 348, 349, 340, 326,
- 327, 352, 1, 353, 353, 354, 354, 355,
- 353, 356, 1, 357, 357, 358, 358, 359,
- 357, 360, 1, 361, 361, 362, 362, 363,
- 361, 364, 1, 365, 366, 1, 367, 1,
- 368, 368, 370, 371, 372, 372, 368, 369,
- 367, 1, 373, 373, 375, 376, 373, 374,
- 377, 1, 378, 379, 1, 380, 1, 381,
- 381, 383, 384, 385, 385, 381, 382, 380,
- 1, 386, 386, 388, 389, 386, 387, 390,
- 1, 391, 392, 1, 393, 1, 394, 394,
- 396, 397, 398, 398, 394, 395, 393, 1,
- 399, 399, 401, 402, 399, 400, 403, 1,
- 404, 405, 1, 406, 1, 407, 408, 408,
- 410, 411, 412, 413, 414, 415, 416, 417,
- 418, 419, 420, 421, 422, 423, 424, 414,
- 425, 426, 427, 428, 429, 430, 431, 422,
- 408, 409, 406, 1, 0, 432, 432, 361,
- 363, 59, 60, 61, 62, 3, 63, 64,
- 65, 66, 67, 68, 69, 70, 71, 4,
- 72, 73, 74, 75, 67, 432, 362, 364,
- 1, 368, 368, 370, 367, 372, 372, 368,
- 369, 366, 1, 375, 375, 374, 374, 376,
- 375, 377, 1, 381, 381, 383, 380, 385,
- 385, 381, 382, 379, 1, 388, 388, 387,
- 387, 389, 388, 390, 1, 394, 394, 396,
- 393, 398, 398, 394, 395, 392, 1, 401,
- 401, 400, 400, 402, 401, 403, 1, 407,
- 408, 408, 410, 406, 412, 413, 414, 415,
- 416, 417, 418, 419, 420, 421, 422, 423,
- 424, 414, 425, 426, 427, 428, 429, 430,
- 431, 422, 408, 409, 405, 1, 433, 433,
- 434, 1, 434, 1, 407, 408, 408, 410,
- 411, 412, 413, 415, 416, 417, 418, 419,
- 420, 421, 422, 423, 424, 425, 426, 427,
- 428, 429, 430, 431, 422, 408, 409, 434,
- 1, 435, 435, 436, 436, 437, 435, 438,
- 1, 439, 439, 440, 440, 441, 439, 442,
- 1, 443, 444, 1, 445, 1, 446, 446,
- 448, 449, 450, 450, 446, 447, 445, 1,
- 451, 451, 453, 454, 451, 452, 455, 1,
- 456, 457, 1, 458, 1, 459, 459, 461,
- 462, 463, 463, 459, 460, 458, 1, 464,
- 464, 466, 467, 464, 465, 468, 1, 469,
- 470, 1, 471, 1, 472, 472, 474, 475,
- 476, 476, 472, 473, 471, 1, 477, 477,
- 479, 480, 477, 478, 481, 1, 482, 483,
- 1, 484, 1, 485, 486, 486, 488, 489,
- 490, 491, 492, 493, 494, 495, 496, 497,
- 498, 499, 500, 501, 502, 492, 503, 504,
- 505, 506, 507, 508, 509, 500, 486, 487,
- 484, 1, 0, 510, 510, 439, 441, 59,
- 60, 61, 62, 3, 63, 64, 65, 66,
- 67, 68, 69, 70, 71, 4, 72, 73,
- 74, 75, 67, 510, 440, 442, 1, 446,
- 446, 448, 445, 450, 450, 446, 447, 444,
- 1, 453, 453, 452, 452, 454, 453, 455,
- 1, 459, 459, 461, 458, 463, 463, 459,
- 460, 457, 1, 466, 466, 465, 465, 467,
- 466, 468, 1, 472, 472, 474, 471, 476,
- 476, 472, 473, 470, 1, 479, 479, 478,
- 478, 480, 479, 481, 1, 485, 486, 486,
- 488, 484, 490, 491, 492, 493, 494, 495,
- 496, 497, 498, 499, 500, 501, 502, 492,
- 503, 504, 505, 506, 507, 508, 509, 500,
- 486, 487, 483, 1, 511, 511, 512, 1,
- 512, 1, 485, 486, 486, 488, 489, 490,
- 491, 493, 494, 495, 496, 497, 498, 499,
- 500, 501, 502, 503, 504, 505, 506, 507,
- 508, 509, 500, 486, 487, 512, 1, 513,
- 513, 514, 514, 515, 513, 516, 1, 517,
- 517, 518, 518, 519, 517, 520, 1, 521,
- 522, 1, 523, 1, 524, 524, 526, 527,
- 528, 528, 524, 525, 523, 1, 529, 529,
- 531, 532, 529, 530, 533, 1, 534, 535,
- 1, 536, 1, 537, 538, 538, 540, 541,
+ 0, 0, 2, 2, 3, 0, 4, 1,
+ 5, 5, 6, 6, 7, 5, 8, 1,
+ 9, 10, 1, 11, 1, 12, 12, 14,
+ 15, 16, 16, 12, 13, 11, 1, 17,
+ 17, 19, 20, 17, 18, 21, 1, 22,
+ 23, 1, 24, 1, 25, 26, 1, 27,
+ 1, 28, 28, 30, 31, 32, 32, 28,
+ 29, 27, 1, 33, 33, 35, 36, 33,
+ 34, 37, 1, 38, 39, 1, 40, 1,
+ 41, 41, 42, 42, 43, 41, 44, 1,
+ 28, 28, 30, 27, 32, 32, 28, 29,
+ 26, 1, 35, 35, 34, 34, 36, 35,
+ 37, 1, 45, 45, 46, 45, 47, 1,
+ 48, 48, 49, 48, 50, 1, 51, 1,
+ 52, 52, 53, 54, 55, 55, 52, 51,
+ 1, 56, 56, 57, 58, 56, 59, 1,
+ 57, 57, 58, 57, 59, 1, 60, 1,
+ 61, 61, 63, 64, 65, 65, 61, 62,
+ 60, 1, 66, 66, 68, 69, 66, 67,
+ 70, 1, 71, 72, 1, 73, 1, 74,
+ 74, 75, 76, 76, 74, 73, 1, 77,
+ 77, 78, 79, 80, 77, 1, 78, 78,
+ 79, 80, 78, 1, 81, 81, 82, 83,
+ 84, 81, 1, 85, 85, 86, 87, 88,
+ 85, 1, 86, 86, 87, 88, 86, 1,
+ 89, 89, 91, 92, 89, 90, 93, 1,
+ 94, 94, 96, 97, 94, 95, 98, 1,
+ 99, 100, 1, 101, 1, 102, 102, 104,
+ 105, 106, 106, 102, 103, 101, 1, 107,
+ 107, 109, 110, 107, 108, 111, 1, 112,
+ 113, 1, 114, 1, 52, 52, 53, 51,
+ 55, 55, 52, 115, 1, 116, 116, 117,
+ 1, 117, 1, 52, 52, 53, 54, 52,
+ 117, 1, 118, 118, 119, 119, 120, 118,
+ 121, 1, 122, 122, 123, 123, 124, 122,
+ 125, 1, 126, 127, 1, 128, 1, 129,
+ 129, 131, 132, 133, 133, 129, 130, 128,
+ 1, 134, 134, 136, 137, 134, 135, 138,
+ 1, 139, 140, 1, 141, 1, 142, 142,
+ 144, 145, 146, 146, 142, 143, 141, 1,
+ 147, 147, 149, 150, 147, 148, 151, 1,
+ 152, 153, 1, 154, 1, 155, 155, 157,
+ 158, 159, 159, 155, 156, 154, 1, 160,
+ 160, 162, 163, 160, 161, 164, 1, 165,
+ 166, 1, 167, 1, 168, 168, 170, 171,
+ 172, 172, 168, 169, 167, 1, 173, 173,
+ 175, 176, 173, 174, 177, 1, 178, 179,
+ 1, 180, 1, 181, 181, 183, 184, 185,
+ 185, 181, 182, 180, 1, 186, 186, 188,
+ 189, 186, 187, 190, 1, 191, 192, 1,
+ 193, 1, 129, 129, 131, 128, 133, 133,
+ 129, 130, 127, 1, 136, 136, 135, 135,
+ 137, 136, 138, 1, 142, 142, 144, 141,
+ 146, 146, 142, 143, 140, 1, 149, 149,
+ 148, 148, 150, 149, 151, 1, 155, 155,
+ 157, 154, 159, 159, 155, 156, 153, 1,
+ 162, 162, 161, 161, 163, 162, 164, 1,
+ 168, 168, 170, 167, 172, 172, 168, 169,
+ 166, 1, 175, 175, 174, 174, 176, 175,
+ 177, 1, 181, 181, 183, 180, 185, 185,
+ 181, 182, 179, 1, 188, 188, 187, 187,
+ 189, 188, 190, 1, 194, 194, 195, 1,
+ 195, 1, 196, 196, 197, 197, 198, 196,
+ 199, 1, 200, 200, 201, 201, 202, 200,
+ 203, 1, 204, 205, 1, 206, 1, 207,
+ 207, 208, 1, 208, 1, 209, 209, 210,
+ 210, 211, 209, 212, 1, 213, 213, 214,
+ 214, 215, 213, 216, 1, 217, 217, 218,
+ 218, 219, 217, 220, 1, 221, 222, 1,
+ 223, 1, 224, 224, 226, 227, 228, 228,
+ 224, 225, 223, 1, 229, 229, 231, 232,
+ 229, 230, 233, 1, 234, 235, 1, 236,
+ 1, 237, 237, 239, 240, 241, 241, 237,
+ 238, 236, 1, 242, 242, 244, 245, 242,
+ 243, 246, 1, 247, 248, 1, 249, 1,
+ 250, 250, 252, 253, 254, 254, 250, 251,
+ 249, 1, 255, 255, 257, 258, 255, 256,
+ 259, 1, 260, 261, 1, 262, 1, 224,
+ 224, 226, 223, 228, 228, 224, 225, 222,
+ 1, 231, 231, 230, 230, 232, 231, 233,
+ 1, 237, 237, 239, 236, 241, 241, 237,
+ 238, 235, 1, 244, 244, 243, 243, 245,
+ 244, 246, 1, 250, 250, 252, 249, 254,
+ 254, 250, 251, 248, 1, 257, 257, 256,
+ 256, 258, 257, 259, 1, 263, 263, 264,
+ 1, 264, 1, 265, 265, 266, 266, 267,
+ 265, 268, 1, 269, 269, 270, 270, 271,
+ 269, 272, 1, 273, 274, 1, 275, 1,
+ 276, 276, 278, 279, 280, 280, 276, 277,
+ 275, 1, 281, 281, 283, 284, 281, 282,
+ 285, 1, 286, 287, 1, 288, 1, 289,
+ 289, 291, 292, 293, 293, 289, 290, 288,
+ 1, 294, 294, 296, 297, 294, 295, 298,
+ 1, 299, 300, 1, 301, 1, 302, 302,
+ 304, 305, 306, 306, 302, 303, 301, 1,
+ 307, 307, 309, 310, 307, 308, 311, 1,
+ 312, 313, 1, 314, 1, 276, 276, 278,
+ 275, 280, 280, 276, 277, 274, 1, 283,
+ 283, 282, 282, 284, 283, 285, 1, 289,
+ 289, 291, 288, 293, 293, 289, 290, 287,
+ 1, 296, 296, 295, 295, 297, 296, 298,
+ 1, 302, 302, 304, 301, 306, 306, 302,
+ 303, 300, 1, 309, 309, 308, 308, 310,
+ 309, 311, 1, 315, 315, 316, 1, 316,
+ 1, 317, 317, 318, 318, 319, 317, 320,
+ 1, 321, 321, 322, 322, 323, 321, 324,
+ 1, 325, 326, 1, 327, 1, 328, 328,
+ 330, 331, 332, 332, 328, 329, 327, 1,
+ 333, 333, 335, 336, 333, 334, 337, 1,
+ 338, 339, 1, 340, 1, 328, 328, 330,
+ 327, 332, 332, 328, 329, 326, 1, 335,
+ 335, 334, 334, 336, 335, 337, 1, 341,
+ 341, 342, 1, 342, 1, 343, 343, 344,
+ 344, 345, 343, 346, 1, 347, 347, 348,
+ 348, 349, 347, 350, 1, 351, 352, 1,
+ 353, 1, 354, 354, 355, 1, 355, 1,
+ 356, 356, 357, 356, 358, 1, 359, 359,
+ 360, 360, 361, 359, 362, 1, 363, 363,
+ 364, 364, 365, 363, 366, 1, 367, 367,
+ 368, 368, 369, 367, 370, 1, 371, 371,
+ 372, 372, 373, 371, 374, 1, 12, 12,
+ 14, 11, 16, 16, 12, 13, 10, 1,
+ 19, 19, 18, 18, 20, 19, 21, 1,
+ 375, 375, 376, 1, 376, 1, 377, 377,
+ 378, 378, 379, 377, 380, 1, 381, 381,
+ 382, 382, 383, 381, 384, 1, 385, 385,
+ 386, 386, 387, 385, 388, 1, 389, 389,
+ 390, 390, 391, 389, 392, 1, 393, 393,
+ 394, 1, 394, 1, 12, 12, 14, 15,
+ 12, 13, 394, 1, 395, 395, 396, 1,
+ 396, 1, 328, 328, 330, 331, 328, 329,
+ 396, 1, 397, 397, 398, 1, 398, 1,
+ 302, 302, 304, 305, 302, 303, 398, 1,
+ 399, 399, 400, 1, 400, 1, 289, 289,
+ 291, 292, 289, 290, 400, 1, 401, 401,
+ 402, 1, 402, 1, 276, 276, 278, 279,
+ 276, 277, 402, 1, 403, 403, 404, 1,
+ 404, 1, 250, 250, 252, 253, 250, 251,
+ 404, 1, 405, 405, 406, 1, 406, 1,
+ 237, 237, 239, 240, 237, 238, 406, 1,
+ 407, 407, 408, 1, 408, 1, 224, 224,
+ 226, 227, 224, 225, 408, 1, 409, 409,
+ 410, 1, 410, 1, 181, 181, 183, 184,
+ 181, 182, 410, 1, 411, 411, 412, 1,
+ 412, 1, 168, 168, 170, 171, 168, 169,
+ 412, 1, 413, 413, 414, 1, 414, 1,
+ 155, 155, 157, 158, 155, 156, 414, 1,
+ 415, 415, 416, 1, 416, 1, 142, 142,
+ 144, 145, 142, 143, 416, 1, 417, 417,
+ 418, 1, 418, 1, 129, 129, 131, 132,
+ 129, 130, 418, 1, 419, 419, 420, 1,
+ 420, 1, 109, 109, 108, 108, 110, 109,
+ 111, 1, 421, 421, 422, 1, 422, 1,
+ 102, 102, 104, 105, 102, 103, 422, 1,
+ 102, 102, 104, 101, 106, 106, 102, 103,
+ 100, 1, 96, 96, 95, 95, 97, 96,
+ 98, 1, 423, 423, 425, 426, 423, 424,
+ 427, 1, 428, 428, 429, 430, 431, 428,
+ 1, 432, 432, 433, 1, 433, 1, 74,
+ 74, 75, 74, 433, 1, 74, 74, 75,
+ 73, 76, 76, 74, 72, 1, 68, 68,
+ 67, 67, 69, 68, 70, 1, 434, 434,
+ 435, 1, 435, 1, 61, 61, 63, 64,
+ 61, 62, 435, 1, 61, 61, 63, 60,
+ 65, 65, 61, 62, 436, 1, 437, 437,
+ 438, 1, 438, 1, 439, 439, 440, 1,
+ 440, 1, 28, 28, 30, 31, 28, 29,
+ 440, 1, 441, 441, 442, 443, 441, 1,
+ 444, 444, 446, 447, 448, 449, 450, 451,
+ 452, 453, 454, 455, 456, 457, 458, 459,
+ 460, 450, 461, 462, 463, 464, 465, 466,
+ 467, 458, 444, 445, 24, 1, 468, 468,
+ 41, 43, 469, 470, 471, 472, 442, 473,
+ 474, 475, 476, 477, 478, 479, 480, 481,
+ 443, 482, 483, 484, 485, 477, 468, 42,
+ 44, 1, 486, 486, 488, 489, 490, 491,
+ 492, 493, 494, 495, 496, 497, 498, 499,
+ 500, 501, 502, 492, 503, 504, 505, 506,
+ 507, 508, 509, 500, 486, 487, 40, 1,
+ 486, 486, 488, 40, 490, 491, 492, 493,
+ 494, 495, 496, 497, 498, 499, 500, 501,
+ 502, 492, 503, 504, 505, 506, 507, 508,
+ 509, 500, 486, 487, 39, 1, 510, 510,
+ 511, 512, 513, 514, 515, 516, 517, 518,
+ 519, 520, 521, 522, 523, 524, 525, 515,
+ 526, 527, 528, 529, 530, 531, 532, 523,
+ 510, 114, 1, 533, 533, 48, 49, 469,
+ 470, 471, 472, 442, 473, 474, 475, 476,
+ 477, 478, 479, 480, 481, 443, 482, 483,
+ 484, 485, 477, 533, 50, 1, 534, 534,
+ 536, 537, 538, 539, 540, 541, 542, 543,
+ 544, 545, 546, 547, 548, 549, 550, 540,
+ 551, 552, 553, 554, 555, 556, 557, 548,
+ 534, 535, 193, 1, 558, 558, 122, 124,
+ 469, 470, 471, 472, 442, 473, 474, 475,
+ 476, 477, 478, 479, 480, 481, 443, 482,
+ 483, 484, 485, 477, 558, 123, 125, 1,
+ 534, 534, 536, 193, 538, 539, 540, 541,
542, 543, 544, 545, 546, 547, 548, 549,
- 550, 551, 552, 553, 554, 544, 555, 556,
- 557, 558, 559, 560, 561, 552, 538, 539,
- 536, 1, 0, 562, 562, 517, 519, 59,
- 60, 61, 62, 3, 63, 64, 65, 66,
- 67, 68, 69, 70, 71, 4, 72, 73,
- 74, 75, 67, 562, 518, 520, 1, 524,
- 524, 526, 523, 528, 528, 524, 525, 522,
- 1, 531, 531, 530, 530, 532, 531, 533,
- 1, 537, 538, 538, 540, 536, 542, 543,
- 544, 545, 546, 547, 548, 549, 550, 551,
- 552, 553, 554, 544, 555, 556, 557, 558,
- 559, 560, 561, 552, 538, 539, 535, 1,
- 563, 563, 564, 1, 564, 1, 537, 538,
- 538, 540, 541, 542, 543, 545, 546, 547,
- 548, 549, 550, 551, 552, 553, 554, 555,
- 556, 557, 558, 559, 560, 561, 552, 538,
- 539, 564, 1, 565, 565, 566, 566, 567,
- 565, 568, 1, 569, 569, 570, 570, 571,
- 569, 572, 1, 573, 574, 1, 575, 1,
- 576, 577, 577, 579, 580, 581, 582, 583,
- 584, 585, 586, 587, 588, 589, 590, 591,
- 592, 593, 583, 594, 595, 596, 597, 598,
- 599, 600, 591, 577, 578, 575, 1, 0,
- 601, 601, 569, 571, 59, 60, 61, 62,
- 3, 63, 64, 65, 66, 67, 68, 69,
- 70, 71, 4, 72, 73, 74, 75, 67,
- 601, 570, 572, 1, 576, 577, 577, 579,
- 575, 581, 582, 583, 584, 585, 586, 587,
- 588, 589, 590, 591, 592, 593, 583, 594,
- 595, 596, 597, 598, 599, 600, 591, 577,
- 578, 574, 1, 602, 602, 603, 1, 603,
- 1, 576, 577, 577, 579, 580, 581, 582,
- 584, 585, 586, 587, 588, 589, 590, 591,
+ 550, 540, 551, 552, 553, 554, 555, 556,
+ 557, 548, 534, 535, 192, 1, 534, 534,
+ 536, 537, 538, 539, 541, 542, 543, 544,
+ 545, 546, 547, 548, 549, 550, 551, 552,
+ 553, 554, 555, 556, 557, 548, 534, 535,
+ 195, 1, 559, 559, 561, 562, 563, 564,
+ 565, 566, 567, 568, 569, 570, 571, 572,
+ 573, 574, 575, 565, 576, 577, 578, 579,
+ 580, 581, 582, 573, 559, 560, 206, 1,
+ 583, 583, 200, 202, 469, 470, 471, 472,
+ 442, 473, 474, 475, 476, 477, 478, 479,
+ 480, 481, 443, 482, 483, 484, 485, 477,
+ 583, 201, 203, 1, 559, 559, 561, 206,
+ 563, 564, 565, 566, 567, 568, 569, 570,
+ 571, 572, 573, 574, 575, 565, 576, 577,
+ 578, 579, 580, 581, 582, 573, 559, 560,
+ 205, 1, 559, 559, 561, 562, 563, 564,
+ 566, 567, 568, 569, 570, 571, 572, 573,
+ 574, 575, 576, 577, 578, 579, 580, 581,
+ 582, 573, 559, 560, 208, 1, 584, 584,
+ 586, 587, 588, 589, 590, 591, 592, 593,
+ 594, 595, 596, 597, 598, 599, 600, 590,
+ 601, 602, 603, 604, 605, 606, 607, 598,
+ 584, 585, 262, 1, 608, 608, 217, 219,
+ 469, 470, 471, 472, 442, 473, 474, 475,
+ 476, 477, 478, 479, 480, 481, 443, 482,
+ 483, 484, 485, 477, 608, 218, 220, 1,
+ 584, 584, 586, 262, 588, 589, 590, 591,
592, 593, 594, 595, 596, 597, 598, 599,
- 600, 591, 577, 578, 603, 1, 604, 605,
- 605, 606, 607, 608, 609, 610, 611, 612,
+ 600, 590, 601, 602, 603, 604, 605, 606,
+ 607, 598, 584, 585, 261, 1, 584, 584,
+ 586, 587, 588, 589, 591, 592, 593, 594,
+ 595, 596, 597, 598, 599, 600, 601, 602,
+ 603, 604, 605, 606, 607, 598, 584, 585,
+ 264, 1, 609, 609, 611, 612, 613, 614,
+ 615, 616, 617, 618, 619, 620, 621, 622,
+ 623, 624, 625, 615, 626, 627, 628, 629,
+ 630, 631, 632, 623, 609, 610, 314, 1,
+ 633, 633, 269, 271, 469, 470, 471, 472,
+ 442, 473, 474, 475, 476, 477, 478, 479,
+ 480, 481, 443, 482, 483, 484, 485, 477,
+ 633, 270, 272, 1, 609, 609, 611, 314,
613, 614, 615, 616, 617, 618, 619, 620,
- 621, 622, 623, 624, 615, 605, 1, 0,
- 625, 625, 59, 60, 61, 62, 3, 63,
- 64, 65, 66, 67, 68, 69, 70, 71,
- 4, 72, 73, 74, 75, 67, 625, 1,
- 626, 626, 627, 626, 628, 1, 629, 629,
- 630, 630, 631, 629, 632, 1, 633, 633,
- 634, 634, 635, 633, 636, 1, 637, 637,
- 638, 638, 639, 637, 640, 1, 641, 641,
- 642, 642, 643, 641, 644, 1, 16, 16,
- 18, 15, 20, 20, 16, 17, 14, 1,
- 23, 23, 22, 22, 24, 23, 25, 1,
- 29, 30, 30, 32, 28, 34, 35, 36,
- 37, 38, 39, 40, 41, 42, 43, 44,
- 45, 46, 36, 47, 48, 49, 50, 51,
- 52, 53, 44, 30, 31, 27, 1, 645,
- 645, 646, 1, 646, 1, 29, 30, 30,
- 32, 33, 34, 35, 37, 38, 39, 40,
- 41, 42, 43, 44, 45, 46, 47, 48,
- 49, 50, 51, 52, 53, 44, 30, 31,
- 646, 1, 647, 647, 648, 648, 649, 647,
- 650, 1, 651, 651, 652, 652, 653, 651,
- 654, 1, 655, 655, 656, 656, 657, 655,
- 658, 1, 659, 659, 660, 660, 661, 659,
- 662, 1, 663, 663, 664, 1, 664, 1,
- 16, 16, 18, 19, 16, 17, 664, 1,
- 665, 665, 666, 1, 666, 1, 524, 524,
- 526, 527, 524, 525, 666, 1, 667, 667,
- 668, 1, 668, 1, 472, 472, 474, 475,
- 472, 473, 668, 1, 669, 669, 670, 1,
- 670, 1, 459, 459, 461, 462, 459, 460,
- 670, 1, 671, 671, 672, 1, 672, 1,
- 446, 446, 448, 449, 446, 447, 672, 1,
- 673, 673, 674, 1, 674, 1, 394, 394,
- 396, 397, 394, 395, 674, 1, 675, 675,
- 676, 1, 676, 1, 381, 381, 383, 384,
- 381, 382, 676, 1, 677, 677, 678, 1,
- 678, 1, 368, 368, 370, 371, 368, 369,
- 678, 1, 679, 679, 680, 1, 680, 1,
- 273, 273, 275, 276, 273, 274, 680, 1,
- 681, 681, 682, 1, 682, 1, 260, 260,
- 262, 263, 260, 261, 682, 1, 683, 683,
- 684, 1, 684, 1, 247, 247, 249, 250,
- 247, 248, 684, 1, 685, 685, 686, 1,
- 686, 1, 234, 234, 236, 237, 234, 235,
- 686, 1, 687, 687, 688, 1, 688, 1,
- 221, 221, 223, 224, 221, 222, 688, 1,
- 689, 689, 690, 1, 690, 1, 182, 183,
- 183, 184, 185, 186, 187, 189, 190, 191,
- 192, 193, 194, 195, 196, 197, 198, 199,
- 200, 201, 202, 203, 204, 205, 196, 183,
- 690, 1, 182, 183, 183, 184, 181, 186,
- 187, 188, 189, 190, 191, 192, 193, 194,
- 195, 196, 197, 198, 188, 199, 200, 201,
- 202, 203, 204, 205, 196, 183, 180, 1,
- 176, 176, 175, 175, 177, 176, 178, 1,
- 691, 691, 692, 1, 692, 1, 169, 169,
- 171, 172, 169, 170, 692, 1, 169, 169,
- 171, 168, 173, 173, 169, 170, 167, 1,
- 163, 163, 162, 162, 164, 163, 165, 1,
- 693, 693, 694, 693, 1, 695, 695, 696,
- 695, 1, 697, 697, 698, 1, 698, 1,
- 146, 146, 147, 146, 698, 1, 146, 146,
- 147, 145, 148, 148, 146, 144, 1, 140,
- 140, 139, 139, 141, 140, 142, 1, 699,
- 699, 700, 1, 700, 1, 133, 133, 135,
- 136, 133, 134, 700, 1, 133, 133, 135,
- 132, 137, 137, 133, 134, 701, 1, 702,
- 702, 703, 1, 703, 1, 92, 93, 93,
- 95, 96, 97, 98, 100, 101, 102, 103,
- 104, 105, 106, 107, 108, 109, 110, 111,
- 112, 113, 114, 115, 116, 107, 93, 94,
- 703, 1, 704, 704, 705, 1, 705, 1,
- 79, 79, 81, 82, 79, 80, 705, 1,
- 1, 0
+ 621, 622, 623, 624, 625, 615, 626, 627,
+ 628, 629, 630, 631, 632, 623, 609, 610,
+ 313, 1, 609, 609, 611, 612, 613, 614,
+ 616, 617, 618, 619, 620, 621, 622, 623,
+ 624, 625, 626, 627, 628, 629, 630, 631,
+ 632, 623, 609, 610, 316, 1, 634, 634,
+ 636, 637, 638, 639, 640, 641, 642, 643,
+ 644, 645, 646, 647, 648, 649, 650, 640,
+ 651, 652, 653, 654, 655, 656, 657, 648,
+ 634, 635, 340, 1, 658, 658, 321, 323,
+ 469, 470, 471, 472, 442, 473, 474, 475,
+ 476, 477, 478, 479, 480, 481, 443, 482,
+ 483, 484, 485, 477, 658, 322, 324, 1,
+ 634, 634, 636, 340, 638, 639, 640, 641,
+ 642, 643, 644, 645, 646, 647, 648, 649,
+ 650, 640, 651, 652, 653, 654, 655, 656,
+ 657, 648, 634, 635, 339, 1, 634, 634,
+ 636, 637, 638, 639, 641, 642, 643, 644,
+ 645, 646, 647, 648, 649, 650, 651, 652,
+ 653, 654, 655, 656, 657, 648, 634, 635,
+ 342, 1, 659, 659, 661, 662, 663, 664,
+ 665, 666, 667, 668, 669, 670, 671, 672,
+ 673, 674, 675, 665, 676, 677, 678, 679,
+ 680, 681, 682, 673, 659, 660, 353, 1,
+ 683, 683, 347, 349, 469, 470, 471, 472,
+ 442, 473, 474, 475, 476, 477, 478, 479,
+ 480, 481, 443, 482, 483, 484, 485, 477,
+ 683, 348, 350, 1, 659, 659, 661, 353,
+ 663, 664, 665, 666, 667, 668, 669, 670,
+ 671, 672, 673, 674, 675, 665, 676, 677,
+ 678, 679, 680, 681, 682, 673, 659, 660,
+ 352, 1, 659, 659, 661, 662, 663, 664,
+ 666, 667, 668, 669, 670, 671, 672, 673,
+ 674, 675, 676, 677, 678, 679, 680, 681,
+ 682, 673, 659, 660, 355, 1, 684, 684,
+ 685, 686, 687, 688, 689, 690, 691, 692,
+ 693, 694, 695, 696, 697, 698, 699, 700,
+ 701, 702, 703, 694, 684, 1, 704, 704,
+ 469, 470, 471, 472, 442, 473, 474, 475,
+ 476, 477, 478, 479, 480, 481, 443, 482,
+ 483, 484, 485, 477, 704, 1, 444, 444,
+ 446, 24, 448, 449, 450, 451, 452, 453,
+ 454, 455, 456, 457, 458, 459, 460, 450,
+ 461, 462, 463, 464, 465, 466, 467, 458,
+ 444, 445, 23, 1, 444, 444, 446, 447,
+ 448, 449, 451, 452, 453, 454, 455, 456,
+ 457, 458, 459, 460, 461, 462, 463, 464,
+ 465, 466, 467, 458, 444, 445, 376, 1,
+ 510, 510, 511, 512, 513, 514, 516, 517,
+ 518, 519, 520, 521, 522, 523, 524, 525,
+ 526, 527, 528, 529, 530, 531, 532, 523,
+ 510, 420, 1, 510, 510, 511, 114, 513,
+ 514, 515, 516, 517, 518, 519, 520, 521,
+ 522, 523, 524, 525, 515, 526, 527, 528,
+ 529, 530, 531, 532, 523, 510, 113, 1,
+ 486, 486, 488, 489, 490, 491, 493, 494,
+ 495, 496, 497, 498, 499, 500, 501, 502,
+ 503, 504, 505, 506, 507, 508, 509, 500,
+ 486, 487, 438, 1, 0
};
static const short _svg_path_trans_targs[] = {
- 270, 0, 1, 2, 193, 3, 4, 5,
- 194, 3, 4, 5, 194, 5, 194, 6,
- 7, 8, 195, 9, 204, 7, 8, 195,
- 9, 196, 9, 196, 10, 270, 11, 12,
- 19, 13, 23, 54, 197, 94, 104, 2,
- 105, 133, 161, 177, 187, 189, 190, 191,
- 192, 193, 200, 201, 202, 203, 11, 12,
- 19, 13, 20, 23, 54, 94, 104, 105,
- 133, 161, 177, 187, 189, 190, 191, 192,
- 200, 201, 202, 203, 13, 20, 14, 15,
- 16, 21, 17, 267, 15, 16, 21, 17,
- 22, 17, 22, 18, 270, 11, 12, 19,
- 13, 23, 54, 264, 94, 104, 2, 105,
- 133, 161, 177, 187, 189, 190, 191, 192,
- 193, 200, 201, 202, 203, 24, 25, 50,
- 24, 25, 50, 26, 27, 28, 29, 51,
- 27, 28, 29, 263, 30, 31, 32, 259,
- 33, 260, 31, 32, 259, 33, 258, 33,
- 258, 34, 35, 36, 255, 35, 36, 37,
- 254, 38, 39, 38, 39, 40, 253, 41,
- 252, 41, 42, 252, 43, 251, 43, 251,
- 44, 45, 46, 247, 47, 248, 45, 46,
- 247, 47, 246, 47, 246, 48, 270, 49,
- 24, 25, 23, 54, 243, 94, 104, 2,
- 105, 133, 161, 177, 187, 189, 190, 191,
- 192, 193, 200, 201, 202, 203, 49, 50,
- 52, 53, 55, 56, 57, 80, 55, 56,
- 57, 80, 57, 80, 58, 59, 60, 81,
- 61, 240, 59, 60, 81, 61, 82, 61,
- 82, 62, 63, 64, 83, 65, 237, 63,
- 64, 83, 65, 84, 65, 84, 66, 67,
- 68, 85, 69, 234, 67, 68, 85, 69,
- 86, 69, 86, 70, 71, 72, 87, 73,
- 231, 71, 72, 87, 73, 88, 73, 88,
- 74, 75, 76, 89, 77, 228, 75, 76,
- 89, 77, 90, 77, 90, 78, 270, 79,
- 56, 55, 57, 23, 54, 91, 94, 104,
- 2, 105, 133, 161, 177, 187, 189, 190,
- 191, 192, 193, 200, 201, 202, 203, 79,
- 92, 93, 95, 96, 97, 100, 95, 96,
- 97, 100, 97, 100, 98, 270, 99, 96,
- 95, 97, 23, 54, 101, 94, 104, 2,
- 105, 133, 161, 177, 187, 189, 190, 191,
- 192, 193, 200, 201, 202, 203, 99, 102,
- 103, 19, 12, 13, 20, 106, 107, 108,
- 123, 106, 107, 108, 123, 108, 123, 109,
- 110, 111, 124, 112, 225, 110, 111, 124,
- 112, 125, 112, 125, 113, 114, 115, 126,
- 116, 222, 114, 115, 126, 116, 127, 116,
- 127, 117, 118, 119, 128, 120, 219, 118,
- 119, 128, 120, 129, 120, 129, 121, 270,
- 122, 107, 106, 108, 23, 54, 130, 94,
- 104, 2, 105, 133, 161, 177, 187, 189,
- 190, 191, 192, 193, 200, 201, 202, 203,
- 122, 131, 132, 134, 135, 136, 151, 134,
- 135, 136, 151, 136, 151, 137, 138, 139,
- 152, 140, 216, 138, 139, 152, 140, 153,
- 140, 153, 141, 142, 143, 154, 144, 213,
- 142, 143, 154, 144, 155, 144, 155, 145,
- 146, 147, 156, 148, 210, 146, 147, 156,
- 148, 157, 148, 157, 149, 270, 150, 135,
- 134, 136, 23, 54, 158, 94, 104, 2,
- 105, 133, 161, 177, 187, 189, 190, 191,
- 192, 193, 200, 201, 202, 203, 150, 159,
- 160, 162, 163, 164, 171, 162, 163, 164,
- 171, 164, 171, 165, 166, 167, 172, 168,
- 207, 166, 167, 172, 168, 173, 168, 173,
- 169, 270, 170, 163, 162, 164, 23, 54,
- 174, 94, 104, 2, 105, 133, 161, 177,
- 187, 189, 190, 191, 192, 193, 200, 201,
- 202, 203, 170, 175, 176, 178, 179, 180,
- 183, 178, 179, 180, 183, 180, 183, 181,
- 270, 182, 179, 178, 180, 23, 54, 184,
- 94, 104, 2, 105, 133, 161, 177, 187,
- 189, 190, 191, 192, 193, 200, 201, 202,
- 203, 182, 185, 186, 270, 188, 23, 54,
- 94, 104, 2, 105, 133, 161, 177, 187,
- 189, 190, 191, 192, 193, 200, 201, 202,
- 203, 188, 24, 25, 50, 55, 56, 57,
- 80, 95, 96, 97, 100, 19, 12, 13,
- 20, 3, 4, 5, 194, 198, 199, 106,
- 107, 108, 123, 134, 135, 136, 151, 162,
- 163, 164, 171, 178, 179, 180, 183, 205,
- 206, 208, 209, 211, 212, 214, 215, 217,
- 218, 220, 221, 223, 224, 226, 227, 229,
- 230, 232, 233, 235, 236, 238, 239, 241,
- 242, 244, 245, 249, 250, 41, 252, 38,
- 39, 256, 257, 261, 262, 263, 265, 266,
- 268, 269
+ 2, 0, 3, 4, 161, 2, 3, 4,
+ 161, 4, 161, 5, 6, 7, 162, 8,
+ 169, 6, 7, 162, 8, 265, 8, 265,
+ 233, 10, 16, 11, 12, 13, 17, 14,
+ 229, 12, 13, 17, 14, 236, 14, 236,
+ 235, 15, 9, 10, 16, 19, 20, 43,
+ 19, 20, 43, 21, 22, 23, 24, 44,
+ 22, 23, 24, 226, 25, 26, 27, 222,
+ 28, 223, 26, 27, 222, 28, 221, 28,
+ 221, 29, 30, 31, 218, 30, 31, 32,
+ 217, 33, 34, 35, 216, 33, 34, 35,
+ 216, 36, 37, 215, 38, 214, 36, 37,
+ 215, 38, 214, 38, 214, 39, 40, 41,
+ 210, 42, 211, 40, 41, 210, 42, 268,
+ 42, 268, 237, 43, 45, 46, 48, 49,
+ 50, 71, 48, 49, 50, 71, 50, 71,
+ 51, 52, 53, 72, 54, 205, 52, 53,
+ 72, 54, 73, 54, 73, 55, 56, 57,
+ 74, 58, 202, 56, 57, 74, 58, 75,
+ 58, 75, 59, 60, 61, 76, 62, 199,
+ 60, 61, 76, 62, 77, 62, 77, 63,
+ 64, 65, 78, 66, 196, 64, 65, 78,
+ 66, 79, 66, 79, 67, 68, 69, 80,
+ 70, 193, 68, 69, 80, 70, 241, 70,
+ 241, 239, 82, 242, 84, 85, 86, 245,
+ 84, 85, 86, 245, 86, 245, 243, 88,
+ 246, 15, 9, 10, 16, 91, 92, 93,
+ 106, 91, 92, 93, 106, 93, 106, 94,
+ 95, 96, 107, 97, 190, 95, 96, 107,
+ 97, 108, 97, 108, 98, 99, 100, 109,
+ 101, 187, 99, 100, 109, 101, 110, 101,
+ 110, 102, 103, 104, 111, 105, 184, 103,
+ 104, 111, 105, 249, 105, 249, 247, 113,
+ 250, 115, 116, 117, 130, 115, 116, 117,
+ 130, 117, 130, 118, 119, 120, 131, 121,
+ 181, 119, 120, 131, 121, 132, 121, 132,
+ 122, 123, 124, 133, 125, 178, 123, 124,
+ 133, 125, 134, 125, 134, 126, 127, 128,
+ 135, 129, 175, 127, 128, 135, 129, 253,
+ 129, 253, 251, 137, 254, 139, 140, 141,
+ 146, 139, 140, 141, 146, 141, 146, 142,
+ 143, 144, 147, 145, 172, 143, 144, 147,
+ 145, 257, 145, 257, 255, 149, 258, 151,
+ 152, 153, 261, 151, 152, 153, 261, 153,
+ 261, 259, 155, 262, 19, 20, 43, 48,
+ 49, 50, 71, 84, 85, 86, 245, 15,
+ 9, 10, 16, 2, 3, 4, 161, 164,
+ 266, 91, 92, 93, 106, 115, 116, 117,
+ 130, 139, 140, 141, 146, 151, 152, 153,
+ 261, 170, 171, 173, 174, 176, 177, 179,
+ 180, 182, 183, 185, 186, 188, 189, 191,
+ 192, 194, 195, 197, 198, 200, 201, 203,
+ 204, 206, 207, 209, 267, 212, 213, 36,
+ 37, 215, 38, 214, 33, 34, 35, 216,
+ 219, 220, 224, 225, 226, 228, 269, 230,
+ 231, 232, 1, 160, 234, 9, 15, 10,
+ 18, 47, 163, 83, 89, 1, 90, 114,
+ 138, 150, 263, 156, 157, 158, 159, 160,
+ 165, 166, 167, 168, 234, 18, 47, 83,
+ 89, 90, 114, 138, 150, 263, 156, 157,
+ 158, 159, 165, 166, 167, 168, 234, 9,
+ 15, 10, 18, 47, 227, 83, 89, 1,
+ 90, 114, 138, 150, 263, 156, 157, 158,
+ 159, 160, 165, 166, 167, 168, 238, 19,
+ 20, 18, 47, 208, 83, 89, 1, 90,
+ 114, 138, 150, 263, 156, 157, 158, 159,
+ 160, 165, 166, 167, 168, 238, 240, 49,
+ 48, 50, 18, 47, 81, 83, 89, 1,
+ 90, 114, 138, 150, 263, 156, 157, 158,
+ 159, 160, 165, 166, 167, 168, 240, 244,
+ 85, 84, 86, 18, 47, 87, 83, 89,
+ 1, 90, 114, 138, 150, 263, 156, 157,
+ 158, 159, 160, 165, 166, 167, 168, 244,
+ 248, 92, 91, 93, 18, 47, 112, 83,
+ 89, 1, 90, 114, 138, 150, 263, 156,
+ 157, 158, 159, 160, 165, 166, 167, 168,
+ 248, 252, 116, 115, 117, 18, 47, 136,
+ 83, 89, 1, 90, 114, 138, 150, 263,
+ 156, 157, 158, 159, 160, 165, 166, 167,
+ 168, 252, 256, 140, 139, 141, 18, 47,
+ 148, 83, 89, 1, 90, 114, 138, 150,
+ 263, 156, 157, 158, 159, 160, 165, 166,
+ 167, 168, 256, 260, 152, 151, 153, 18,
+ 47, 154, 83, 89, 1, 90, 114, 138,
+ 150, 263, 156, 157, 158, 159, 160, 165,
+ 166, 167, 168, 260, 264, 18, 47, 83,
+ 89, 1, 90, 114, 138, 150, 263, 156,
+ 157, 158, 159, 160, 165, 166, 167, 168,
+ 264
};
static const char _svg_path_trans_actions[] = {
- 15, 0, 0, 0, 0, 9, 47, 47,
- 47, 0, 1, 1, 1, 0, 0, 0,
- 3, 17, 3, 17, 0, 0, 1, 0,
- 1, 1, 0, 0, 0, 60, 20, 56,
- 20, 56, 20, 20, 0, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 0, 1,
- 0, 1, 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 3,
- 17, 3, 17, 0, 0, 1, 0, 1,
- 1, 0, 0, 0, 68, 23, 64, 23,
- 64, 23, 23, 0, 23, 23, 23, 23,
- 23, 23, 23, 23, 23, 23, 23, 23,
- 23, 23, 23, 23, 23, 9, 47, 47,
- 0, 1, 1, 0, 3, 3, 17, 0,
- 0, 0, 1, 1, 0, 3, 17, 3,
- 17, 0, 0, 1, 0, 1, 1, 0,
- 0, 0, 3, 3, 0, 0, 0, 0,
- 0, 7, 7, 0, 0, 0, 0, 7,
- 7, 0, 1, 0, 1, 1, 0, 0,
- 0, 3, 17, 3, 17, 0, 0, 1,
- 0, 1, 1, 0, 0, 0, 124, 44,
- 44, 120, 44, 44, 0, 44, 44, 44,
- 44, 44, 44, 44, 44, 44, 44, 44,
- 44, 44, 44, 44, 44, 44, 0, 0,
- 0, 0, 9, 47, 47, 47, 0, 1,
- 1, 1, 0, 0, 0, 3, 17, 3,
- 17, 0, 0, 1, 0, 1, 1, 0,
- 0, 0, 3, 17, 3, 17, 0, 0,
- 1, 0, 1, 1, 0, 0, 0, 3,
- 17, 3, 17, 0, 0, 1, 0, 1,
- 1, 0, 0, 0, 3, 17, 3, 17,
+ 9, 0, 51, 51, 51, 0, 1, 1,
+ 1, 0, 0, 0, 3, 15, 3, 15,
0, 0, 1, 0, 1, 1, 0, 0,
- 0, 3, 17, 3, 17, 0, 0, 1,
- 0, 1, 1, 0, 0, 0, 92, 32,
- 88, 32, 88, 32, 32, 0, 32, 32,
- 32, 32, 32, 32, 32, 32, 32, 32,
- 32, 32, 32, 32, 32, 32, 32, 0,
- 0, 0, 9, 47, 47, 47, 0, 1,
- 1, 1, 0, 0, 0, 76, 26, 72,
- 26, 72, 26, 26, 0, 26, 26, 26,
- 26, 26, 26, 26, 26, 26, 26, 26,
- 26, 26, 26, 26, 26, 26, 0, 0,
- 0, 9, 47, 47, 47, 9, 47, 47,
- 47, 0, 1, 1, 1, 0, 0, 0,
- 3, 17, 3, 17, 0, 0, 1, 0,
- 1, 1, 0, 0, 0, 3, 17, 3,
- 17, 0, 0, 1, 0, 1, 1, 0,
- 0, 0, 3, 17, 3, 17, 0, 0,
- 1, 0, 1, 1, 0, 0, 0, 108,
- 38, 104, 38, 104, 38, 38, 0, 38,
- 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38,
- 0, 0, 0, 9, 47, 47, 47, 0,
- 1, 1, 1, 0, 0, 0, 3, 17,
- 3, 17, 0, 0, 1, 0, 1, 1,
- 0, 0, 0, 3, 17, 3, 17, 0,
+ 0, 0, 0, 0, 3, 15, 3, 15,
+ 0, 0, 1, 0, 1, 1, 0, 0,
+ 0, 0, 1, 1, 1, 9, 51, 51,
+ 0, 1, 1, 0, 3, 3, 15, 0,
+ 0, 0, 1, 1, 0, 3, 15, 3,
+ 15, 0, 0, 1, 0, 1, 1, 0,
+ 0, 0, 3, 3, 0, 0, 0, 0,
+ 0, 7, 7, 7, 7, 0, 0, 0,
+ 0, 7, 48, 7, 48, 48, 0, 1,
+ 0, 1, 1, 0, 0, 0, 3, 15,
+ 3, 15, 0, 0, 1, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 9, 51,
+ 51, 51, 0, 1, 1, 1, 0, 0,
+ 0, 3, 15, 3, 15, 0, 0, 1,
+ 0, 1, 1, 0, 0, 0, 3, 15,
+ 3, 15, 0, 0, 1, 0, 1, 1,
+ 0, 0, 0, 3, 15, 3, 15, 0,
0, 1, 0, 1, 1, 0, 0, 0,
- 3, 17, 3, 17, 0, 0, 1, 0,
- 1, 1, 0, 0, 0, 100, 35, 96,
- 35, 96, 35, 35, 0, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 0, 0,
- 0, 9, 47, 47, 47, 0, 1, 1,
- 1, 0, 0, 0, 3, 17, 3, 17,
+ 3, 15, 3, 15, 0, 0, 1, 0,
+ 1, 1, 0, 0, 0, 3, 15, 3,
+ 15, 0, 0, 1, 0, 1, 1, 0,
+ 0, 0, 0, 0, 9, 51, 51, 51,
+ 0, 1, 1, 1, 0, 0, 0, 0,
+ 0, 9, 51, 51, 51, 9, 51, 51,
+ 51, 0, 1, 1, 1, 0, 0, 0,
+ 3, 15, 3, 15, 0, 0, 1, 0,
+ 1, 1, 0, 0, 0, 3, 15, 3,
+ 15, 0, 0, 1, 0, 1, 1, 0,
+ 0, 0, 3, 15, 3, 15, 0, 0,
+ 1, 0, 1, 1, 0, 0, 0, 0,
+ 0, 9, 51, 51, 51, 0, 1, 1,
+ 1, 0, 0, 0, 3, 15, 3, 15,
0, 0, 1, 0, 1, 1, 0, 0,
- 0, 116, 41, 112, 41, 112, 41, 41,
- 0, 41, 41, 41, 41, 41, 41, 41,
- 41, 41, 41, 41, 41, 41, 41, 41,
- 41, 41, 0, 0, 0, 9, 47, 47,
- 47, 0, 1, 1, 1, 0, 0, 0,
- 84, 29, 80, 29, 80, 29, 29, 0,
- 29, 29, 29, 29, 29, 29, 29, 29,
- 29, 29, 29, 29, 29, 29, 29, 29,
- 29, 0, 0, 0, 53, 13, 13, 13,
+ 0, 3, 15, 3, 15, 0, 0, 1,
+ 0, 1, 1, 0, 0, 0, 3, 15,
+ 3, 15, 0, 0, 1, 0, 1, 1,
+ 0, 0, 0, 0, 0, 9, 51, 51,
+ 51, 0, 1, 1, 1, 0, 0, 0,
+ 3, 15, 3, 15, 0, 0, 1, 0,
+ 1, 1, 0, 0, 0, 0, 0, 9,
+ 51, 51, 51, 0, 1, 1, 1, 0,
+ 0, 0, 0, 0, 11, 54, 54, 11,
+ 54, 54, 54, 11, 54, 54, 54, 11,
+ 54, 54, 54, 11, 54, 54, 54, 0,
+ 0, 11, 54, 54, 54, 11, 54, 54,
+ 54, 11, 54, 54, 54, 11, 54, 54,
+ 54, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 5,
+ 45, 5, 45, 45, 5, 5, 5, 5,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 18, 57, 18, 57,
+ 18, 18, 0, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 21, 61,
+ 21, 61, 21, 21, 0, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 42, 42,
+ 89, 42, 42, 0, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 0, 30, 73,
+ 30, 73, 30, 30, 0, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 0, 24,
+ 65, 24, 65, 24, 24, 0, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 0,
+ 36, 81, 36, 81, 36, 36, 0, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36,
+ 0, 33, 77, 33, 77, 33, 33, 0,
+ 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 0, 39, 85, 39, 85, 39, 39,
+ 0, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 0, 27, 69, 27, 69, 27,
+ 27, 0, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 0, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13,
- 13, 0, 11, 50, 50, 11, 50, 50,
- 50, 11, 50, 50, 50, 11, 50, 50,
- 50, 11, 50, 50, 50, 0, 0, 11,
- 50, 50, 50, 11, 50, 50, 50, 11,
- 50, 50, 50, 11, 50, 50, 50, 0,
+ 0
+};
+
+static const char _svg_path_eof_actions[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 5, 5, 5,
- 5, 0, 0, 0, 0, 0, 0, 0,
- 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 18, 0, 21, 21, 42, 0, 30,
+ 0, 30, 30, 24, 0, 24, 24, 36,
+ 0, 36, 36, 33, 0, 33, 33, 39,
+ 0, 39, 39, 27, 0, 27, 27, 13,
+ 0, 18, 18, 42, 42, 21
};
-static const int svg_path_start = 1;
-static const int svg_path_first_final = 270;
+static const int svg_path_start = 232;
+static const int svg_path_first_final = 232;
+
+static const int svg_path_en_main = 232;
-//static const int svg_path_en_main = 1;
+#line 47 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
-void Parser::parse(char const *str)
-throw(SVGPathParseError)
+
+SVGPathParser::SVGPathParser(PathSink &sink)
+ : _absolute(false)
+ , _sink(sink)
{
- char const *p = str;
- char const *start = NULL;
- int cs;
+ reset();
+}
- _reset();
+void SVGPathParser::reset() {
+ _absolute = false;
+ _current = _initial = Point(0, 0);
+ _quad_tangent = _cubic_tangent = Point(0, 0);
+ _params.clear();
+
+#line 1096 "D:/lib2geom/trunk/src/2geom/svg-path-parser.cpp"
{
cs = svg_path_start;
}
+#line 64 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+
+}
+
+void SVGPathParser::parse(char const *str, int len)
+{
+ if (len < 0) {
+ len = std::strlen(str);
+ }
+ _parse(str, str + len, true);
+}
+
+void SVGPathParser::parse(std::string const &s)
+{
+ _parse(s.c_str(), s.c_str() + s.size(), true);
+}
+
+void SVGPathParser::feed(char const *str, int len)
+{
+ if (len < 0) {
+ len = std::strlen(str);
+ }
+ _parse(str, str + len, false);
+}
+
+void SVGPathParser::feed(std::string const &s)
+{
+ _parse(s.c_str(), s.c_str() + s.size(), false);
+}
+void SVGPathParser::finish()
+{
+ char const *empty = "";
+ _parse(empty, empty, true);
+}
+
+void SVGPathParser::_push(Coord value) {
+ _params.push_back(value);
+}
+
+Coord SVGPathParser::_pop() {
+ Coord value = _params.back();
+ _params.pop_back();
+ return value;
+}
+
+bool SVGPathParser::_pop_flag() {
+ return _pop() != 0.0;
+}
+
+Coord SVGPathParser::_pop_coord(Dim2 axis) {
+ if (_absolute) {
+ return _pop();
+ } else {
+ return _pop() + _current[axis];
+ }
+}
+
+Point SVGPathParser::_pop_point() {
+ Coord y = _pop_coord(Y);
+ Coord x = _pop_coord(X);
+ return Point(x, y);
+}
+
+void SVGPathParser::_moveTo(Point const &p) {
+ _sink.moveTo(p);
+ _quad_tangent = _cubic_tangent = _current = _initial = p;
+}
+
+void SVGPathParser::_lineTo(Point const &p) {
+ _sink.lineTo(p);
+ _quad_tangent = _cubic_tangent = _current = p;
+}
+
+void SVGPathParser::_curveTo(Point const &c0, Point const &c1, Point const &p) {
+ _sink.curveTo(c0, c1, p);
+ _quad_tangent = _current = p;
+ _cubic_tangent = p + ( p - c1 );
+}
+
+void SVGPathParser::_quadTo(Point const &c, Point const &p) {
+ _sink.quadTo(c, p);
+ _cubic_tangent = _current = p;
+ _quad_tangent = p + ( p - c );
+}
+
+void SVGPathParser::_arcTo(Coord rx, Coord ry, Coord angle,
+ bool large_arc, bool sweep, Point const &p)
+{
+ if (are_near(_current, p)) {
+ return; // ignore invalid (ambiguous) arc segments where start and end point are the same (per SVG spec)
+ }
+
+ _sink.arcTo(rx, ry, angle, large_arc, sweep, p);
+ _quad_tangent = _cubic_tangent = _current = p;
+}
+
+void SVGPathParser::_closePath() {
+ _sink.closePath();
+ _quad_tangent = _cubic_tangent = _current = _initial;
+}
+
+void SVGPathParser::_parse(char const *str, char const *strend, bool finish)
+{
+ char const *p = str;
+ char const *pe = strend;
+ char const *eof = finish ? pe : NULL;
+ char const *start = NULL;
+
+
+#line 1211 "D:/lib2geom/trunk/src/2geom/svg-path-parser.cpp"
{
int _klen;
unsigned int _trans;
@@ -1168,6 +1215,8 @@ throw(SVGPathParseError)
unsigned int _nacts;
const char *_keys;
+ if ( p == pe )
+ goto _test_eof;
if ( cs == 0 )
goto _out;
_resume:
@@ -1233,63 +1282,75 @@ _match:
switch ( *_acts++ )
{
case 0:
+#line 173 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
{
start = p;
}
break;
case 1:
-
+#line 177 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
{
- char const *end=p;
- std::string buf(start, end);
- _push(g_ascii_strtod(buf.c_str(), NULL));
- start = NULL;
+ if (start) {
+ std::string buf(start, p);
+ _push(g_ascii_strtod(buf.c_str(), NULL));
+ start = NULL;
+ } else {
+ std::string buf(str, p);
+ _push(g_ascii_strtod((_number_part + buf).c_str(), NULL));
+ _number_part.clear();
+ }
}
break;
case 2:
-
+#line 189 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
{
_push(1.0);
}
break;
case 3:
-
+#line 193 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
{
_push(0.0);
}
break;
case 4:
-
+#line 197 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
{
_absolute = true;
}
break;
case 5:
+#line 201 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
{
_absolute = false;
}
break;
case 6:
+#line 205 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
{
_moveTo(_pop_point());
}
break;
case 7:
+#line 209 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
{
_lineTo(_pop_point());
}
break;
case 8:
+#line 213 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
{
- _hlineTo(Point(_pop_coord(X), _current[Y]));
+ _lineTo(Point(_pop_coord(X), _current[Y]));
}
break;
case 9:
+#line 217 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
{
- _vlineTo(Point(_current[X], _pop_coord(Y)));
+ _lineTo(Point(_current[X], _pop_coord(Y)));
}
break;
case 10:
+#line 221 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c1 = _pop_point();
@@ -1298,6 +1359,7 @@ _match:
}
break;
case 11:
+#line 228 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c1 = _pop_point();
@@ -1305,6 +1367,7 @@ _match:
}
break;
case 12:
+#line 234 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c = _pop_point();
@@ -1312,12 +1375,14 @@ _match:
}
break;
case 13:
+#line 240 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
_quadTo(_quad_tangent, p);
}
break;
case 14:
+#line 245 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
{
Point point = _pop_point();
bool sweep = _pop_flag();
@@ -1330,42 +1395,167 @@ _match:
}
break;
case 15:
+#line 256 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
{
_closePath();
}
break;
- case 16:
- {{p++; goto _out; }}
- break;
+#line 1404 "D:/lib2geom/trunk/src/2geom/svg-path-parser.cpp"
}
}
_again:
if ( cs == 0 )
goto _out;
- p += 1;
- goto _resume;
+ if ( ++p != pe )
+ goto _resume;
+ _test_eof: {}
+ if ( p == eof )
+ {
+ const char *__acts = _svg_path_actions + _svg_path_eof_actions[cs];
+ unsigned int __nacts = (unsigned int) *__acts++;
+ while ( __nacts-- > 0 ) {
+ switch ( *__acts++ ) {
+ case 1:
+#line 177 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+ {
+ if (start) {
+ std::string buf(start, p);
+ _push(g_ascii_strtod(buf.c_str(), NULL));
+ start = NULL;
+ } else {
+ std::string buf(str, p);
+ _push(g_ascii_strtod((_number_part + buf).c_str(), NULL));
+ _number_part.clear();
+ }
+ }
+ break;
+ case 6:
+#line 205 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+ {
+ _moveTo(_pop_point());
+ }
+ break;
+ case 7:
+#line 209 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+ {
+ _lineTo(_pop_point());
+ }
+ break;
+ case 8:
+#line 213 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+ {
+ _lineTo(Point(_pop_coord(X), _current[Y]));
+ }
+ break;
+ case 9:
+#line 217 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+ {
+ _lineTo(Point(_current[X], _pop_coord(Y)));
+ }
+ break;
+ case 10:
+#line 221 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+ {
+ Point p = _pop_point();
+ Point c1 = _pop_point();
+ Point c0 = _pop_point();
+ _curveTo(c0, c1, p);
+ }
+ break;
+ case 11:
+#line 228 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+ {
+ Point p = _pop_point();
+ Point c1 = _pop_point();
+ _curveTo(_cubic_tangent, c1, p);
+ }
+ break;
+ case 12:
+#line 234 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+ {
+ Point p = _pop_point();
+ Point c = _pop_point();
+ _quadTo(c, p);
+ }
+ break;
+ case 13:
+#line 240 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+ {
+ Point p = _pop_point();
+ _quadTo(_quad_tangent, p);
+ }
+ break;
+ case 14:
+#line 245 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+ {
+ Point point = _pop_point();
+ bool sweep = _pop_flag();
+ bool large_arc = _pop_flag();
+ double angle = deg_to_rad(_pop());
+ double ry = _pop();
+ double rx = _pop();
+
+ _arcTo(rx, ry, angle, large_arc, sweep, point);
+ }
+ break;
+ case 15:
+#line 256 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+ {
+ _closePath();
+ }
+ break;
+#line 1509 "D:/lib2geom/trunk/src/2geom/svg-path-parser.cpp"
+ }
+ }
+ }
+
_out: {}
}
+#line 398 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
- if ( cs < svg_path_first_final ) {
- throw SVGPathParseError();
+
+ if (finish) {
+ if (cs < svg_path_first_final) {
+ throw SVGPathParseError();
+ }
+ } else if (start != NULL) {
+ _number_part = std::string(start, pe);
}
-}
+ if (finish) {
+ _sink.flush();
+ reset();
+ }
}
void parse_svg_path(char const *str, PathSink &sink)
-throw(SVGPathParseError)
{
- Parser parser(sink);
+ SVGPathParser parser(sink);
parser.parse(str);
- sink.flush();
}
+void parse_svg_path_file(FILE *fi, PathSink &sink)
+{
+ static const size_t BUFFER_SIZE = 4096;
+ char buffer[BUFFER_SIZE];
+ size_t bytes_read;
+ SVGPathParser parser(sink);
+
+ while (true) {
+ bytes_read = fread(buffer, 1, BUFFER_SIZE, fi);
+ if (bytes_read < BUFFER_SIZE) {
+ parser.parse(buffer, bytes_read);
+ break;
+ } else {
+ parser.feed(buffer, bytes_read);
+ }
+ }
}
+} // namespace Geom
+
/*
Local Variables:
mode:c++
diff --git a/src/2geom/svg-path-parser.h b/src/2geom/svg-path-parser.h
index 163fbe5c4..6478ba468 100644
--- a/src/2geom/svg-path-parser.h
+++ b/src/2geom/svg-path-parser.h
@@ -30,48 +30,143 @@
*
*/
-#ifndef SEEN_SVG_PATH_PARSER_H
-#define SEEN_SVG_PATH_PARSER_H
+#ifndef LIB2GEOM_SEEN_SVG_PATH_PARSER_H
+#define LIB2GEOM_SEEN_SVG_PATH_PARSER_H
-#include <vector>
+#include <iostream>
#include <iterator>
#include <stdexcept>
+#include <vector>
#include <2geom/exception.h>
#include <2geom/point.h>
#include <2geom/path-sink.h>
namespace Geom {
-void parse_svg_path(char const *str, PathSink &sink) throw(SVGPathParseError);
+/** @brief Read SVG path data and feed it to a PathSink
+ *
+ * This class provides an interface to an SVG path data parser written in Ragel.
+ * It supports parsing the path data either at once or block-by-block.
+ * Use the parse() functions to parse complete data and the feed() and finish()
+ * functions to parse partial data.
+ *
+ * The parser will call the appropriate methods on the PathSink supplied
+ * at construction. To store the path in memory as a PathVector, pass
+ * a PathBuilder. You can also use one of the freestanding helper functions
+ * if you don't need to parse data block-by-block.
+ *
+ * @ingroup Paths
+ */
+class SVGPathParser {
+public:
+ SVGPathParser(PathSink &sink);
+
+ /** @brief Reset internal state.
+ * Discards the internal state associated with partially parsed data,
+ * letting you start from scratch. Note that any partial data written
+ * to the path sink is not affected - you need to clear it yourself. */
+ void reset();
+
+ /** @brief Parse a C-style string.
+ * The path sink is flushed and the internal state is reset after this call.
+ * Note that the state is not reset before this method, so you can use it to
+ * process the last block of partial data.
+ * @param str String to parse
+ * @param len Length of string or -1 if null-terminated */
+ void parse(char const *str, int len = -1);
+ /** @brief Parse an STL string. */
+ void parse(std::string const &s);
+
+ /** @brief Parse a part of path data stored in a C-style string.
+ * This method does not reset internal state, so it can be called multiple
+ * times to parse successive blocks of a longer SVG path data string.
+ * To finish parsing, call finish() after the final block or call parse()
+ * with the last block of data.
+ * @param str String to parse
+ * @param len Length of string or -1 if null-terminated */
+ void feed(char const *str, int len = -1);
+ /** @brief Parse a part of path data stored in an STL string. */
+ void feed(std::string const &s);
-inline std::vector<Path> parse_svg_path(char const *str) throw(SVGPathParseError) {
- typedef std::vector<Path> Subpaths;
- typedef std::back_insert_iterator<Subpaths> Inserter;
-
- Subpaths subpaths;
- Inserter iter(subpaths);
- PathIteratorSink<Inserter> generator(iter);
+ /** @brief Finalize parsing.
+ * After the last block of data was submitted with feed(), call this method
+ * to finalize parsing, flush the path sink and reset internal state.
+ * You should not call this after parse(). */
+ void finish();
+
+private:
+ bool _absolute;
+ Point _current;
+ Point _initial;
+ Point _cubic_tangent;
+ Point _quad_tangent;
+ std::vector<Coord> _params;
+ PathSink &_sink;
+
+ int cs;
+ std::string _number_part;
+
+ void _push(Coord value);
+ Coord _pop();
+ bool _pop_flag();
+ Coord _pop_coord(Geom::Dim2 axis);
+ Point _pop_point();
+ void _moveTo(Point const &p);
+ void _lineTo(Point const &p);
+ void _curveTo(Point const &c0, Point const &c1, Point const &p);
+ void _quadTo(Point const &c, Point const &p);
+ void _arcTo(double rx, double ry, double angle,
+ bool large_arc, bool sweep, Point const &p);
+ void _closePath();
+
+ void _parse(char const *str, char const *strend, bool finish);
+};
+
+/** @brief Feed SVG path data to the specified sink
+ * @ingroup Paths */
+void parse_svg_path(char const *str, PathSink &sink);
+/** @brief Feed SVG path data to the specified sink
+ * @ingroup Paths */
+inline void parse_svg_path(std::string const &str, PathSink &sink) {
+ parse_svg_path(str.c_str(), sink);
+}
+/** Feed SVG path data from a C stream to the specified sink
+ * @ingroup Paths */
+void parse_svg_path_file(FILE *fi, PathSink &sink);
+
+/** @brief Create path vector from SVG path data stored in a C string
+ * @ingroup Paths */
+inline PathVector parse_svg_path(char const *str) {
+ PathVector ret;
+ SubpathInserter iter(ret);
+ PathIteratorSink<SubpathInserter> generator(iter);
parse_svg_path(str, generator);
- return subpaths;
+ return ret;
}
-inline std::vector<Path> read_svgd_f(FILE * fi) throw(SVGPathParseError) {
- /// @bug The 10kB length limit should be removed
- char input[1024 * 10];
- fgets(input, 1024 * 10, fi);
- return parse_svg_path(input);
+/** @brief Create path vector from a C stream with SVG path data
+ * @ingroup Paths */
+inline PathVector read_svgd_f(FILE * fi) {
+ PathVector ret;
+ SubpathInserter iter(ret);
+ PathIteratorSink<SubpathInserter> generator(iter);
+
+ parse_svg_path_file(fi, generator);
+ return ret;
}
-inline std::vector<Path> read_svgd(char const * name) throw(SVGPathParseError) {
- FILE* fi = fopen(name, "r");
+/** @brief Create path vector from SVG path data stored in a file
+ * @ingroup Paths */
+inline PathVector read_svgd(char const *filename) {
+ FILE* fi = fopen(filename, "r");
if(fi == NULL) throw(std::runtime_error("Error opening file"));
- std::vector<Path> out = read_svgd_f(fi);
+ PathVector out = read_svgd_f(fi);
fclose(fi);
return out;
}
-}
+} // end namespace Geom
#endif
/*
diff --git a/src/2geom/svg-path-writer.cpp b/src/2geom/svg-path-writer.cpp
new file mode 100644
index 000000000..e8083a8d1
--- /dev/null
+++ b/src/2geom/svg-path-writer.cpp
@@ -0,0 +1,296 @@
+/** @file
+ * @brief Path sink which writes an SVG-compatible command string
+ *//*
+ * Authors:
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright 2014 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#include <cmath>
+#include <iomanip>
+#include <2geom/coord.h>
+#include <2geom/svg-path-writer.h>
+#include <glib.h>
+
+namespace Geom {
+
+static inline bool is_digit(char c) {
+ return c >= '0' && c <= '9';
+}
+
+SVGPathWriter::SVGPathWriter()
+ : _epsilon(0)
+ , _precision(-1)
+ , _optimize(false)
+ , _use_shorthands(true)
+ , _command(0)
+{
+ // always use C locale for number formatting
+ _ns.imbue(std::locale::classic());
+ _ns.unsetf(std::ios::floatfield);
+}
+
+void SVGPathWriter::moveTo(Point const &p)
+{
+ _setCommand('M');
+ _current_pars.push_back(p[X]);
+ _current_pars.push_back(p[Y]);
+
+ _current = _subpath_start = _quad_tangent = _cubic_tangent = p;
+ if (!_optimize) {
+ flush();
+ }
+}
+
+void SVGPathWriter::lineTo(Point const &p)
+{
+ // The weird setting of _current is to avoid drift with many almost-aligned segments
+ // The additional conditions ensure that the smaller dimension is rounded to zero
+ bool written = false;
+ if (_use_shorthands) {
+ Point r = _current - p;
+ if (are_near(p[X], _current[X], _epsilon) && std::abs(r[X]) < std::abs(r[Y])) {
+ // emit vlineto
+ _setCommand('V');
+ _current_pars.push_back(p[Y]);
+ _current[Y] = p[Y];
+ written = true;
+ } else if (are_near(p[Y], _current[Y], _epsilon) && std::abs(r[Y]) < std::abs(r[X])) {
+ // emit hlineto
+ _setCommand('H');
+ _current_pars.push_back(p[X]);
+ _current[X] = p[X];
+ written = true;
+ }
+ }
+
+ if (!written) {
+ // emit normal lineto
+ if (_command != 'M' && _command != 'L') {
+ _setCommand('L');
+ }
+ _current_pars.push_back(p[X]);
+ _current_pars.push_back(p[Y]);
+ _current = p;
+ }
+
+ _cubic_tangent = _quad_tangent = _current;
+ if (!_optimize) {
+ flush();
+ }
+}
+
+void SVGPathWriter::quadTo(Point const &c, Point const &p)
+{
+ bool shorthand = _use_shorthands && are_near(c, _quad_tangent, _epsilon);
+
+ _setCommand(shorthand ? 'T' : 'Q');
+ if (!shorthand) {
+ _current_pars.push_back(c[X]);
+ _current_pars.push_back(c[Y]);
+ }
+ _current_pars.push_back(p[X]);
+ _current_pars.push_back(p[Y]);
+
+ _current = _cubic_tangent = p;
+ _quad_tangent = p + (p - c);
+ if (!_optimize) {
+ flush();
+ }
+}
+
+void SVGPathWriter::curveTo(Point const &p1, Point const &p2, Point const &p3)
+{
+ bool shorthand = _use_shorthands && are_near(p1, _cubic_tangent, _epsilon);
+
+ _setCommand(shorthand ? 'S' : 'C');
+ if (!shorthand) {
+ _current_pars.push_back(p1[X]);
+ _current_pars.push_back(p1[Y]);
+ }
+ _current_pars.push_back(p2[X]);
+ _current_pars.push_back(p2[Y]);
+ _current_pars.push_back(p3[X]);
+ _current_pars.push_back(p3[Y]);
+
+ _current = _quad_tangent = p3;
+ _cubic_tangent = p3 + (p3 - p2);
+ if (!_optimize) {
+ flush();
+ }
+}
+
+void SVGPathWriter::arcTo(double rx, double ry, double angle,
+ bool large_arc, bool sweep, Point const &p)
+{
+ _setCommand('A');
+ _current_pars.push_back(rx);
+ _current_pars.push_back(ry);
+ _current_pars.push_back(angle);
+ _current_pars.push_back(large_arc ? 1. : 0.);
+ _current_pars.push_back(sweep ? 1. : 0.);
+ _current_pars.push_back(p[X]);
+ _current_pars.push_back(p[Y]);
+
+ _current = _quad_tangent = _cubic_tangent = p;
+ if (!_optimize) {
+ flush();
+ }
+}
+
+void SVGPathWriter::closePath()
+{
+ flush();
+ if (_optimize) {
+ _s << "z";
+ } else {
+ _s << " z";
+ }
+ _current = _quad_tangent = _cubic_tangent = _subpath_start;
+}
+
+void SVGPathWriter::flush()
+{
+ if (_command == 0 || _current_pars.empty()) return;
+
+ if (_optimize) {
+ _s << _command;
+ } else {
+ if (_s.tellp() != 0) {
+ _s << ' ';
+ }
+ _s << _command;
+ }
+
+ char lastchar = _command;
+ bool contained_dot = false;
+
+ for (unsigned i = 0; i < _current_pars.size(); ++i) {
+ // TODO: optimize the use of absolute / relative coords
+ std::string cs = _formatCoord(_current_pars[i]);
+
+ // Separator handling logic.
+ // Floating point values can end with a digit or dot
+ // and start with a digit, a plus or minus sign, or a dot.
+ // The following cases require a separator:
+ // * digit-digit
+ // * digit-dot (only if the previous number didn't contain a dot)
+ // * dot-digit
+ if (_optimize) {
+ // C++11: change to front()
+ char firstchar = cs[0];
+ if (is_digit(lastchar)) {
+ if (is_digit(firstchar)) {
+ _s << " ";
+ } else if (firstchar == '.' && !contained_dot) {
+ _s << " ";
+ }
+ } else if (lastchar == '.' && is_digit(firstchar)) {
+ _s << " ";
+ }
+ _s << cs;
+
+ // C++11: change to back()
+ lastchar = cs[cs.length()-1];
+ contained_dot = cs.find('.') != std::string::npos;
+ } else {
+ _s << " " << cs;
+ }
+ }
+ _current_pars.clear();
+ _command = 0;
+}
+
+void SVGPathWriter::clear()
+{
+ _s.clear();
+ _s.str("");
+ _ns.clear();
+ _ns.str("");
+ _command = 0;
+ _current_pars.clear();
+ _current = Point(0,0);
+ _subpath_start = Point(0,0);
+}
+
+void SVGPathWriter::setPrecision(int prec)
+{
+ _precision = prec;
+ if (prec < 0) {
+ _epsilon = 0;
+ } else {
+ _epsilon = std::pow(10., -prec);
+ _ns << std::setprecision(_precision);
+ }
+}
+
+void SVGPathWriter::_setCommand(char cmd)
+{
+ if (_command != 0 && _command != cmd) {
+ flush();
+ }
+ _command = cmd;
+}
+
+std::string SVGPathWriter::_formatCoord(Coord par)
+{
+ std::string ret;
+ if (_precision < 0) {
+ ret = format_coord_shortest(par);
+ } else {
+ _ns << par;
+ ret = _ns.str();
+ _ns.clear();
+ _ns.str("");
+ }
+ return ret;
+}
+
+
+std::string write_svg_path(PathVector const &pv, int prec, bool optimize, bool shorthands)
+{
+ SVGPathWriter writer;
+ writer.setPrecision(prec);
+ writer.setOptimize(optimize);
+ writer.setUseShorthands(shorthands);
+
+ writer.feed(pv);
+ return writer.str();
+}
+
+} // namespace Geom
+
+/*
+ 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:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/svg-path-writer.h b/src/2geom/svg-path-writer.h
new file mode 100644
index 000000000..e639541ab
--- /dev/null
+++ b/src/2geom/svg-path-writer.h
@@ -0,0 +1,122 @@
+/** @file
+ * @brief Path sink which writes an SVG-compatible command string
+ *//*
+ * Authors:
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright 2014 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_SVG_PATH_WRITER_H
+#define LIB2GEOM_SEEN_SVG_PATH_WRITER_H
+
+#include <2geom/path-sink.h>
+#include <sstream>
+
+namespace Geom {
+
+/** @brief Serialize paths to SVG path data strings.
+ * You can access the generated string by calling the str() method.
+ * @ingroup Paths
+ */
+class SVGPathWriter
+ : public PathSink
+{
+public:
+ SVGPathWriter();
+ ~SVGPathWriter() {}
+
+ void moveTo(Point const &p);
+ void lineTo(Point const &p);
+ void quadTo(Point const &c, Point const &p);
+ void curveTo(Point const &c0, Point const &c1, Point const &p);
+ void arcTo(double rx, double ry, double angle,
+ bool large_arc, bool sweep, Point const &p);
+ void closePath();
+ void flush();
+
+ /// Clear any path data written so far.
+ void clear();
+
+ /** @brief Set output precision.
+ * When the parameter is negative, the path writer enters a verbatim mode
+ * which preserves all values exactly. */
+ void setPrecision(int prec);
+
+ /** @brief Enable or disable length optimization.
+ *
+ * When set to true, the path writer will optimize the generated path data
+ * for minimum length. However, this will make the data less readable,
+ * because spaces between commands and coordinates will be omitted where
+ * unnecessary for correct parsing.
+ *
+ * When set to false, the string will be a straightforward, partially redundant
+ * representation of the passed commands, optimized for readability.
+ * Commands and coordinates will always be separated by spaces and the command
+ * symbol will not be omitted for multiple consecutive commands of the same type.
+ *
+ * Length optimization is turned off by default. */
+ void setOptimize(bool opt) { _optimize = opt; }
+
+ /** @brief Enable or disable the use of V, H, T and S commands where possible.
+ * Shorthands are turned on by default. */
+ void setUseShorthands(bool use) { _use_shorthands = use; }
+
+ /// Retrieve the generated path data string.
+ std::string str() const { return _s.str(); }
+
+private:
+ void _setCommand(char cmd);
+ std::string _formatCoord(Coord par);
+
+ std::ostringstream _s, _ns;
+ std::vector<Coord> _current_pars;
+ Point _subpath_start;
+ Point _current;
+ Point _quad_tangent;
+ Point _cubic_tangent;
+ Coord _epsilon;
+ int _precision;
+ bool _optimize;
+ bool _use_shorthands;
+ char _command;
+};
+
+std::string write_svg_path(PathVector const &pv, int prec = -1, bool optimize = false, bool shorthands = true);
+
+} // namespace Geom
+
+#endif // LIB2GEOM_SEEN_SVG_PATH_WRITER_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:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/sweep.cpp b/src/2geom/sweep.cpp
index f25894282..6910d5d02 100644
--- a/src/2geom/sweep.cpp
+++ b/src/2geom/sweep.cpp
@@ -19,8 +19,8 @@ std::vector<std::vector<unsigned> > sweep_bounds(std::vector<Rect> rs, Dim2 d) {
std::vector<std::vector<unsigned> > pairs(rs.size());
for(unsigned i = 0; i < rs.size(); i++) {
- events.push_back(Event(rs[i][d][0], i, false));
- events.push_back(Event(rs[i][d][1], i, true));
+ events.push_back(Event(rs[i][d].min(), i, false));
+ events.push_back(Event(rs[i][d].max(), i, true));
}
std::sort(events.begin(), events.end());
@@ -66,8 +66,8 @@ std::vector<std::vector<unsigned> > sweep_bounds(std::vector<Rect> a, std::vecto
events[n].reserve(sz*2);
for(unsigned i = 0; i < sz; i++) {
Rect r = n ? b[i] : a[i];
- events[n].push_back(Event(r[d][0], i, false));
- events[n].push_back(Event(r[d][1], i, true));
+ events[n].push_back(Event(r[d].min(), i, false));
+ events[n].push_back(Event(r[d].max(), i, true));
}
std::sort(events[n].begin(), events[n].end());
}
diff --git a/src/2geom/sweep.h b/src/2geom/sweep.h
index 91371e6fb..9c012958b 100644
--- a/src/2geom/sweep.h
+++ b/src/2geom/sweep.h
@@ -32,8 +32,8 @@
*
*/
-#ifndef __2GEOM_SWEEP_H__
-#define __2GEOM_SWEEP_H__
+#ifndef LIB2GEOM_SEEN_SWEEP_H
+#define LIB2GEOM_SEEN_SWEEP_H
#include <vector>
#include <2geom/d2.h>
diff --git a/src/2geom/toposweep.cpp b/src/2geom/toposweep.cpp
index 4da3f6922..3cb231142 100644
--- a/src/2geom/toposweep.cpp
+++ b/src/2geom/toposweep.cpp
@@ -189,7 +189,7 @@ bool SectionSorter::operator()(Section const &a, Section const &b) const {
}
}
- return Point::LexOrderRt(dim)(a.fp, b.fp);
+ return Point::LexLessRt(dim)(a.fp, b.fp);
}
// splits a section into pieces, as specified by an array of doubles, mutating the section to
@@ -203,7 +203,7 @@ std::vector<boost::shared_ptr<Section> > split_section(boost::shared_ptr<Section
s->t = cuts[1];
s->tp = s->curve.get(ps)(cuts[1]);
- assert(Point::LexOrderRt(d)(s->fp, s->tp));
+ assert(Point::LexLessRt(d)(s->fp, s->tp));
ret.reserve(cuts.size() - 2);
for(int i = cuts.size() - 1; i > 1; i--) ret.push_back(boost::shared_ptr<Section>(new Section(s->curve, cuts[i-1], cuts[i], ps, d)));
@@ -380,7 +380,7 @@ TopoGraph::TopoGraph(PathVector const &ps, Dim2 d, double t) : dim(d), tol(t) {
//find all sections to remove
for(int i = context.size() - 1; i >= 0; i--) {
boost::shared_ptr<Section> sec = context[i].section;
- if(Point::LexOrderRt(d)(lim, sec->tp)) {
+ if(Point::LexLessRt(d)(lim, sec->tp)) {
//sec->tp is less than or equal to lim
if(context[i].to_vert == -1) {
//we need to create a new vertex; add everything that enters it
@@ -639,6 +639,7 @@ void remove_area_whiskers(Areas &areas) {
Path area_to_path(PathVector const &ps, Area const &area) {
Path ret;
+ ret.setStitching(true);
if(area.size() == 0) return ret;
Point prev = area[0]->fp;
for(unsigned i = 0; i < area.size(); i++) {
@@ -646,17 +647,18 @@ Path area_to_path(PathVector const &ps, Area const &area) {
Curve *curv = area[i]->curve.get(ps).portion(
forward ? area[i]->f : area[i]->t,
forward ? area[i]->t : area[i]->f);
- ret.append(*curv, Path::STITCH_DISCONTINUOUS);
+ ret.append(*curv);
delete curv;
prev = forward ? area[i]->tp : area[i]->fp;
}
+ ret.setStitching(false);
return ret;
}
PathVector areas_to_paths(PathVector const &ps, Areas const &areas) {
- std::vector<Path> ret;
- ret.reserve(areas.size());
- for(unsigned i = 0; i < areas.size(); i++)
+ PathVector ret;
+ //ret.reserve(areas.size());
+ for(unsigned i = 0; i < areas.size(); ++i)
ret.push_back(area_to_path(ps, areas[i]));
return ret;
}
diff --git a/src/2geom/toposweep.h b/src/2geom/toposweep.h
index b6a55b154..7faf890e1 100644
--- a/src/2geom/toposweep.h
+++ b/src/2geom/toposweep.h
@@ -1,8 +1,7 @@
-
/**
* \file
* \brief TopoSweep - topology / graph representation of a PathVector, for boolean operations and related tasks
- *
+ *//*
* Authors:
* Michael Sloan <mgsloan at gmail.com>
* Nathan Hurst <njhurst at njhurst.com>
@@ -33,8 +32,8 @@
* the specific language governing rights and limitations.
*/
-#ifndef SEEN_GEOM_TOPOSWEEP_H
-#define SEEN_GEOM_TOPOSWEEP_H
+#ifndef LIB2GEOM_SEEN_TOPOSWEEP_H
+#define LIB2GEOM_SEEN_TOPOSWEEP_H
#include <2geom/coord.h>
#include <2geom/point.h>
@@ -69,10 +68,11 @@ struct Section {
Section(CurveIx cix, double fd, double td, Point fdp, Point tdp) : curve(cix), f(fd), t(td), fp(fdp), tp(tdp) { }
Section(CurveIx cix, double fd, double td, PathVector ps, Dim2 d) : curve(cix), f(fd), t(td) {
fp = curve.get(ps).pointAt(f), tp = curve.get(ps).pointAt(t);
- if (Point::LexOrderRt(d)(tp, fp)) {
+ if (Point::LexLessRt(d)(tp, fp)) {
//swap from and to, since tp is left or above fp
- std::swap(f, t);
- std::swap(fp, tp);
+ using std::swap;
+ swap(f, t);
+ swap(fp, tp);
}
}
Rect bbox() const { return Rect(fp, tp); }
@@ -167,7 +167,7 @@ struct SweepSorter {
Dim2 dim;
SweepSorter(Dim2 d) : dim(d) {}
bool operator()(const Section &a, const Section &b) const {
- return Point::LexOrderRt(dim)(a.fp, b.fp);
+ return Point::LexLessRt(dim)(a.fp, b.fp);
}
};
@@ -208,7 +208,7 @@ Areas filter_areas(PathVector const &ps, Areas const & areas, Z const &z) {
} // end namespace Geom
-#endif // SEEN_GEOM_TOPOSWEEP_H
+#endif // LIB2GEOM_SEEN_TOPOSWEEP_H
/*
Local Variables:
diff --git a/src/2geom/transforms.h b/src/2geom/transforms.h
index 7f5635747..2e805786d 100644
--- a/src/2geom/transforms.h
+++ b/src/2geom/transforms.h
@@ -199,6 +199,7 @@ public:
/** @brief Get the characteristic vector of the rotation.
* @return A vector that would be obtained by applying this transform to the X versor. */
Point vector() const { return vec; }
+ Coord angle() const { return atan2(vec); }
Coord operator[](Dim2 dim) const { return vec[dim]; }
Coord operator[](unsigned dim) const { return vec[dim]; }
Rotate &operator*=(Rotate const &o) { vec *= o; return *this; }
@@ -343,6 +344,8 @@ inline Translate pow(Translate const &t, int n) {
/** @brief Reflects objects about line.
* The line, defined by a vector along the line and a point on it, acts as a mirror.
+ * @ingroup Transforms
+ * @see Line::reflection()
*/
Affine reflection(Point const & vector, Point const & origin);
diff --git a/src/2geom/utils.h b/src/2geom/utils.h
index fe955dd41..bc0ad74b8 100644
--- a/src/2geom/utils.h
+++ b/src/2geom/utils.h
@@ -30,11 +30,12 @@
*
*/
-#ifndef SEEN_LIB2GEOM_UTILS_H
-#define SEEN_LIB2GEOM_UTILS_H
+#ifndef LIB2GEOM_SEEN_UTILS_H
+#define LIB2GEOM_SEEN_UTILS_H
#include <cstddef>
#include <vector>
+#include <boost/operators.hpp>
namespace Geom {
@@ -59,9 +60,20 @@ struct MultipliableNoncommutative : B
}
};
+/** @brief Null output iterator
+ * Use this if you want to discard a result returned through an output iterator. */
+struct NullIterator
+ : public boost::output_iterator_helper<NullIterator>
+{
+ NullIterator() {}
+
+ template <typename T>
+ void operator=(T const &v) {}
+};
+
} // end namespace Geom
-#endif // SEEN_LIB2GEOM_UTILS_H
+#endif // LIB2GEOM_SEEN_UTILS_H
/*
Local Variables:
diff --git a/src/2geom/viewbox.cpp b/src/2geom/viewbox.cpp
new file mode 100644
index 000000000..69bd0c487
--- /dev/null
+++ b/src/2geom/viewbox.cpp
@@ -0,0 +1,133 @@
+/**
+ * \file
+ * \brief Convenience class for SVG viewBox handling
+ *//*
+ * Authors:
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2013 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#include <2geom/transforms.h>
+#include <2geom/viewbox.h>
+
+namespace Geom {
+
+/** Convert an align specification to coordinate fractions. */
+Point align_factors(Align g) {
+ Point p;
+ switch (g) {
+ case ALIGN_XMIN_YMIN:
+ p[X] = 0.0;
+ p[Y] = 0.0;
+ break;
+ case ALIGN_XMID_YMIN:
+ p[X] = 0.5;
+ p[Y] = 0.0;
+ break;
+ case ALIGN_XMAX_YMIN:
+ p[X] = 1.0;
+ p[Y] = 0.0;
+ break;
+ case ALIGN_XMIN_YMID:
+ p[X] = 0.0;
+ p[Y] = 0.5;
+ break;
+ case ALIGN_XMID_YMID:
+ p[X] = 0.5;
+ p[Y] = 0.5;
+ break;
+ case ALIGN_XMAX_YMID:
+ p[X] = 1.0;
+ p[Y] = 0.5;
+ break;
+ case ALIGN_XMIN_YMAX:
+ p[X] = 0.0;
+ p[Y] = 1.0;
+ break;
+ case ALIGN_XMID_YMAX:
+ p[X] = 0.5;
+ p[Y] = 1.0;
+ break;
+ case ALIGN_XMAX_YMAX:
+ p[X] = 1.0;
+ p[Y] = 1.0;
+ break;
+ default:
+ break;
+ }
+ return p;
+}
+
+/** Obtain transformation from the viewbox to the specified viewport. */
+Affine ViewBox::transformTo(Geom::Rect const &viewport) const
+{
+ if (!_box) {
+ return Geom::Affine::identity();
+ }
+
+ // 1. translate viewbox to origin
+ Geom::Affine total = Translate(-_box->min());
+
+ // 2. compute scale
+ Geom::Point vdims = viewport.dimensions();
+ Geom::Point bdims = _box->dimensions();
+ Geom::Scale scale(vdims[X] / bdims[X], vdims[Y] / bdims[Y]);
+
+ if (_align == ALIGN_NONE) {
+ // apply non-uniform scale
+ // = Scale(_box->dimensions()).inverse() * Scale(viewport.dimensions())
+ total *= scale * Translate(viewport.min());
+ } else {
+ double uscale = 0;
+ if (_expansion == EXPANSION_MEET) {
+ uscale = std::min(scale[X], scale[Y]);
+ } else {
+ uscale = std::max(scale[X], scale[Y]);
+ }
+ scale = Scale(uscale);
+
+ // compute offset for align
+ Geom::Point offset = bdims * scale - vdims;
+ offset *= Scale(align_factors(_align));
+ total *= Translate(-offset);
+ }
+
+ return total;
+}
+
+} // namespace Geom
+
+/*
+ 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/2geom/viewbox.h b/src/2geom/viewbox.h
new file mode 100644
index 000000000..81f59ee36
--- /dev/null
+++ b/src/2geom/viewbox.h
@@ -0,0 +1,102 @@
+/**
+ * \file
+ * \brief Convenience class for SVG viewBox handling
+ *//*
+ * Authors:
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2013 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_VIEWBOX_H
+#define LIB2GEOM_SEEN_VIEWBOX_H
+
+#include <2geom/affine.h>
+#include <2geom/rect.h>
+
+namespace Geom {
+
+/** Values for the <align> parameter of preserveAspectRatio.
+ * See: http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute */
+enum Align {
+ ALIGN_NONE,
+ ALIGN_XMIN_YMIN,
+ ALIGN_XMID_YMIN,
+ ALIGN_XMAX_YMIN,
+ ALIGN_XMIN_YMID,
+ ALIGN_XMID_YMID,
+ ALIGN_XMAX_YMID,
+ ALIGN_XMIN_YMAX,
+ ALIGN_XMID_YMAX,
+ ALIGN_XMAX_YMAX
+};
+
+/** Values for the <meetOrSlice> parameter of preserveAspectRatio.
+ * See: http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute */
+enum Expansion {
+ EXPANSION_MEET,
+ EXPANSION_SLICE
+};
+
+Point align_factors(Align align);
+
+class ViewBox {
+ OptRect _box;
+ Align _align;
+ Expansion _expansion;
+
+public:
+ explicit ViewBox(OptRect const &r = OptRect(), Align a = ALIGN_XMID_YMID, Expansion ex = EXPANSION_MEET)
+ : _box(r)
+ , _align(a)
+ , _expansion(ex)
+ {}
+
+ void setBox(OptRect const &r) { _box = r; }
+ void setAlign(Align a) { _align = a; }
+ void setExpansion(Expansion ex) { _expansion = ex; }
+ OptRect const &box() const { return _box; }
+ Align align() const { return _align; }
+ Expansion expansion() const { return _expansion; }
+
+ /** Obtain transformation from the viewbox to the specified viewport. */
+ Affine transformTo(Geom::Rect const &viewport) const;
+};
+
+} // namespace Geom
+
+#endif // !LIB2GEOM_SEEN_VIEWBOX_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 :