summaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--include/2geom/2geom.h75
-rw-r--r--include/2geom/affine.h244
-rw-r--r--include/2geom/angle.h408
-rw-r--r--include/2geom/basic-intersection.h151
-rw-r--r--include/2geom/bezier-curve.h366
-rw-r--r--include/2geom/bezier-to-sbasis.h94
-rw-r--r--include/2geom/bezier-utils.h99
-rw-r--r--include/2geom/bezier.h394
-rw-r--r--include/2geom/cairo-path-sink.h91
-rw-r--r--include/2geom/choose.h147
-rw-r--r--include/2geom/circle.h165
-rw-r--r--include/2geom/concepts.h209
-rw-r--r--include/2geom/conic_section_clipper.h58
-rw-r--r--include/2geom/conic_section_clipper_cr.h64
-rw-r--r--include/2geom/conic_section_clipper_impl.h346
-rw-r--r--include/2geom/conicsec.h537
-rw-r--r--include/2geom/convex-hull.h346
-rw-r--r--include/2geom/coord.h208
-rw-r--r--include/2geom/crossing.h213
-rw-r--r--include/2geom/curve.h375
-rw-r--r--include/2geom/curves.h54
-rw-r--r--include/2geom/d2.h564
-rw-r--r--include/2geom/ellipse.h260
-rw-r--r--include/2geom/elliptical-arc.h344
-rw-r--r--include/2geom/exception.h157
-rw-r--r--include/2geom/forward.h127
-rw-r--r--include/2geom/generic-interval.h374
-rw-r--r--include/2geom/generic-rect.h547
-rw-r--r--include/2geom/geom.h66
-rw-r--r--include/2geom/int-interval.h63
-rw-r--r--include/2geom/int-point.h202
-rw-r--r--include/2geom/int-rect.h75
-rw-r--r--include/2geom/intersection-graph.h259
-rw-r--r--include/2geom/intersection.h147
-rw-r--r--include/2geom/interval.h245
-rw-r--r--include/2geom/intervaltree/interval_tree.h126
-rw-r--r--include/2geom/line.h605
-rw-r--r--include/2geom/linear.h167
-rw-r--r--include/2geom/math-utils.h140
-rw-r--r--include/2geom/nearest-time.h141
-rw-r--r--include/2geom/numeric/fitting-model.h521
-rw-r--r--include/2geom/numeric/fitting-tool.h562
-rw-r--r--include/2geom/numeric/linear_system.h138
-rw-r--r--include/2geom/numeric/matrix.h603
-rw-r--r--include/2geom/numeric/symmetric-matrix-fs-operation.h102
-rw-r--r--include/2geom/numeric/symmetric-matrix-fs-trace.h427
-rw-r--r--include/2geom/numeric/symmetric-matrix-fs.h733
-rw-r--r--include/2geom/numeric/vector.h594
-rw-r--r--include/2geom/ord.h80
-rw-r--r--include/2geom/orphan-code/arc-length.h58
-rw-r--r--include/2geom/orphan-code/chebyshev.h30
-rw-r--r--include/2geom/orphan-code/intersection-by-smashing.h78
-rw-r--r--include/2geom/orphan-code/linear-of.h269
-rw-r--r--include/2geom/orphan-code/linearN.h363
-rw-r--r--include/2geom/orphan-code/redblacktree.h121
-rw-r--r--include/2geom/orphan-code/rtree.h241
-rw-r--r--include/2geom/orphan-code/sbasis-of.h638
-rw-r--r--include/2geom/orphan-code/sbasisN.h1123
-rw-r--r--include/2geom/parallelogram.h83
-rw-r--r--include/2geom/path-intersection.h118
-rw-r--r--include/2geom/path-sink.h253
-rw-r--r--include/2geom/path.h917
-rw-r--r--include/2geom/pathvector.h304
-rw-r--r--include/2geom/piecewise.h945
-rw-r--r--include/2geom/point.h449
-rw-r--r--include/2geom/polynomial.h264
-rw-r--r--include/2geom/ray.h192
-rw-r--r--include/2geom/rect.h263
-rw-r--r--include/2geom/sbasis-2d.h371
-rw-r--r--include/2geom/sbasis-curve.h160
-rw-r--r--include/2geom/sbasis-geometric.h146
-rw-r--r--include/2geom/sbasis-math.h99
-rw-r--r--include/2geom/sbasis-poly.h56
-rw-r--r--include/2geom/sbasis-to-bezier.h87
-rw-r--r--include/2geom/sbasis.h530
-rw-r--r--include/2geom/solver.h88
-rw-r--r--include/2geom/svg-path-parser.h199
-rw-r--r--include/2geom/svg-path-writer.h122
-rw-r--r--include/2geom/sweep-bounds.h62
-rw-r--r--include/2geom/sweeper.h189
-rw-r--r--include/2geom/symbolic/determinant-minor.h175
-rw-r--r--include/2geom/symbolic/implicit.h353
-rw-r--r--include/2geom/symbolic/matrix.h265
-rw-r--r--include/2geom/symbolic/multi-index.h169
-rw-r--r--include/2geom/symbolic/multipoly.h684
-rw-r--r--include/2geom/symbolic/mvpoly-tools.h690
-rw-r--r--include/2geom/symbolic/polynomial.h569
-rw-r--r--include/2geom/symbolic/unity-builder.h102
-rw-r--r--include/2geom/transforms.h370
-rw-r--r--include/2geom/utils.h114
-rw-r--r--include/toys/lpe-framework.h77
-rw-r--r--include/toys/path-cairo.h57
-rw-r--r--include/toys/toy-framework-2.h451
93 files changed, 25907 insertions, 0 deletions
diff --git a/include/2geom/2geom.h b/include/2geom/2geom.h
new file mode 100644
index 0000000..7bf36ae
--- /dev/null
+++ b/include/2geom/2geom.h
@@ -0,0 +1,75 @@
+/**
+ * \file
+ * \brief Include everything
+ *//*
+ * Authors:
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright 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_2GEOM_H
+#define LIB2GEOM_SEEN_2GEOM_H
+
+#include <2geom/forward.h>
+
+// primitives
+#include <2geom/coord.h>
+#include <2geom/point.h>
+#include <2geom/interval.h>
+#include <2geom/rect.h>
+#include <2geom/angle.h>
+#include <2geom/ray.h>
+#include <2geom/line.h>
+#include <2geom/affine.h>
+#include <2geom/transforms.h>
+
+// curves and paths
+#include <2geom/curves.h>
+#include <2geom/path.h>
+#include <2geom/pathvector.h>
+
+// fragments
+#include <2geom/d2.h>
+#include <2geom/linear.h>
+#include <2geom/bezier.h>
+#include <2geom/sbasis.h>
+
+// others
+#include <2geom/math-utils.h>
+#include <2geom/utils.h>
+
+#endif // LIB2GEOM_SEEN_2GEOM_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/include/2geom/affine.h b/include/2geom/affine.h
new file mode 100644
index 0000000..470d5fc
--- /dev/null
+++ b/include/2geom/affine.h
@@ -0,0 +1,244 @@
+/**
+ * \file
+ * \brief 3x3 affine transformation matrix.
+ *//*
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com> (Original NRAffine definition and related macros)
+ * Nathan Hurst <njh@mail.csse.monash.edu.au> (Geom::Affine class version of the above)
+ * Michael G. Sloan <mgsloan@gmail.com> (reorganization and additions)
+ * Krzysztof Kosiński <tweenk.pl@gmail.com> (removal of boilerplate, docs)
+ *
+ * This code is in public domain.
+ */
+
+#ifndef LIB2GEOM_SEEN_AFFINE_H
+#define LIB2GEOM_SEEN_AFFINE_H
+
+#include <boost/operators.hpp>
+#include <2geom/forward.h>
+#include <2geom/point.h>
+#include <2geom/utils.h>
+
+namespace Geom {
+
+/**
+ * @brief 3x3 matrix representing an affine transformation.
+ *
+ * Affine transformations on elements of a vector space are transformations which can be
+ * expressed in terms of matrix multiplication followed by addition
+ * (\f$x \mapsto A x + b\f$). They can be thought of as generalizations of linear functions
+ * (\f$y = a x + b\f$) to vector spaces. Affine transformations of points on a 2D plane preserve
+ * the following properties:
+ *
+ * - Colinearity: if three points lie on the same line, they will still be colinear after
+ * an affine transformation.
+ * - Ratios of distances between points on the same line are preserved
+ * - Parallel lines remain parallel.
+ *
+ * All affine transformations on 2D points can be written as combinations of scaling, rotation,
+ * shearing and translation. They can be represented as a combination of a vector and a 2x2 matrix,
+ * but this form is inconvenient to work with. A better solution is to represent all affine
+ * transformations on the 2D plane as 3x3 matrices, where the last column has fixed values.
+ * \f[ A = \left[ \begin{array}{ccc}
+ c_0 & c_1 & 0 \\
+ c_2 & c_3 & 0 \\
+ c_4 & c_5 & 1 \end{array} \right]\f]
+ *
+ * We then interpret points as row vectors of the form \f$[p_X, p_Y, 1]\f$. Applying a
+ * transformation to a point can be written as right-multiplication by a 3x3 matrix
+ * (\f$p' = pA\f$). This subset of matrices is closed under multiplication - combination
+ * of any two transforms can be expressed as the multiplication of their matrices.
+ * In this representation, the \f$c_4\f$ and \f$c_5\f$ coefficients represent
+ * the translation component of the transformation.
+ *
+ * Matrices can be multiplied by other more specific transformations. When multiplying,
+ * the transformations are applied from left to right, so for example <code>m = a * b</code>
+ * means: @a m first transforms by a, then by b.
+ *
+ * @ingroup Transforms
+ */
+class Affine
+ : boost::equality_comparable< Affine // generates operator!= from operator==
+ , boost::multipliable1< Affine
+ , MultipliableNoncommutative< Affine, Translate
+ , MultipliableNoncommutative< Affine, Scale
+ , MultipliableNoncommutative< Affine, Rotate
+ , MultipliableNoncommutative< Affine, HShear
+ , MultipliableNoncommutative< Affine, VShear
+ , MultipliableNoncommutative< Affine, Zoom
+ > > > > > > > >
+{
+ Coord _c[6];
+public:
+ Affine() {
+ _c[0] = _c[3] = 1;
+ _c[1] = _c[2] = _c[4] = _c[5] = 0;
+ }
+
+ /** @brief Create a matrix from its coefficient values.
+ * It's rather inconvenient to directly create matrices in this way. Use transform classes
+ * if your transformation has a geometric interpretation.
+ * @see Translate
+ * @see Scale
+ * @see Rotate
+ * @see HShear
+ * @see VShear
+ * @see Zoom */
+ Affine(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5) {
+ _c[0] = c0; _c[1] = c1;
+ _c[2] = c2; _c[3] = c3;
+ _c[4] = c4; _c[5] = c5;
+ }
+
+ /** @brief Access a coefficient by its index. */
+ inline Coord operator[](unsigned i) const { return _c[i]; }
+ inline Coord &operator[](unsigned i) { return _c[i]; }
+
+ /// @name Combine with other transformations
+ /// @{
+ Affine &operator*=(Affine const &m);
+ // implemented in transforms.cpp
+ Affine &operator*=(Translate const &t);
+ Affine &operator*=(Scale const &s);
+ Affine &operator*=(Rotate const &r);
+ Affine &operator*=(HShear const &h);
+ Affine &operator*=(VShear const &v);
+ Affine &operator*=(Zoom const &);
+ /// @}
+
+ bool operator==(Affine const &o) const {
+ for(unsigned i = 0; i < 6; ++i) {
+ if ( _c[i] != o._c[i] ) return false;
+ }
+ return true;
+ }
+
+ /// @name Get the parameters of the matrix's transform
+ /// @{
+ Point xAxis() const;
+ Point yAxis() const;
+ Point translation() const;
+ Coord expansionX() const;
+ Coord expansionY() const;
+ Point expansion() const { return Point(expansionX(), expansionY()); }
+ /// @}
+
+ /// @name Modify the matrix
+ /// @{
+ void setXAxis(Point const &vec);
+ void setYAxis(Point const &vec);
+
+ void setTranslation(Point const &loc);
+
+ void setExpansionX(Coord val);
+ void setExpansionY(Coord val);
+ void setIdentity();
+ /// @}
+
+ /// @name Inspect the matrix's transform
+ /// @{
+ bool isIdentity(Coord eps = EPSILON) const;
+
+ bool isTranslation(Coord eps = EPSILON) const;
+ bool isScale(Coord eps = EPSILON) const;
+ bool isUniformScale(Coord eps = EPSILON) const;
+ bool isRotation(Coord eps = EPSILON) const;
+ bool isHShear(Coord eps = EPSILON) const;
+ bool isVShear(Coord eps = EPSILON) const;
+
+ bool isNonzeroTranslation(Coord eps = EPSILON) const;
+ 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;
+
+ bool isZoom(Coord eps = EPSILON) const;
+ bool preservesArea(Coord eps = EPSILON) const;
+ bool preservesAngles(Coord eps = EPSILON) const;
+ bool preservesDistances(Coord eps = EPSILON) const;
+ bool flips() const;
+
+ bool isSingular(Coord eps = EPSILON) const;
+ /// @}
+
+ /// @name Compute other matrices
+ /// @{
+ Affine withoutTranslation() const {
+ Affine ret(*this);
+ ret.setTranslation(Point(0,0));
+ return ret;
+ }
+ Affine inverse() const;
+ /// @}
+
+ /// @name Compute scalar values
+ /// @{
+ Coord det() const;
+ Coord descrim2() const;
+ Coord descrim() const;
+ /// @}
+ inline static Affine identity();
+};
+
+/** @brief Print out the Affine (for debugging).
+ * @relates Affine */
+inline std::ostream &operator<< (std::ostream &out_file, const Geom::Affine &m) {
+ out_file << "A: " << m[0] << " C: " << m[2] << " E: " << m[4] << "\n";
+ out_file << "B: " << m[1] << " D: " << m[3] << " F: " << m[5] << "\n";
+ return out_file;
+}
+
+// Affine factories
+Affine from_basis(const Point &x_basis, const Point &y_basis, const Point &offset=Point(0,0));
+Affine elliptic_quadratic_form(Affine const &m);
+
+/** Given a matrix (ignoring the translation) this returns the eigen
+ * values and vectors. */
+class Eigen{
+public:
+ Point vectors[2];
+ double values[2];
+ Eigen(Affine const &m);
+ Eigen(double M[2][2]);
+};
+
+/** @brief Create an identity matrix.
+ * This is a convenience function identical to Affine::identity(). */
+inline Affine identity() {
+ Affine ret(Affine::identity());
+ return ret; // allow NRVO
+}
+
+/** @brief Create an identity matrix.
+ * @return The matrix
+ * \f$\left[\begin{array}{ccc}
+ 1 & 0 & 0 \\
+ 0 & 1 & 0 \\
+ 0 & 0 & 1 \end{array}\right]\f$.
+ * @relates Affine */
+inline Affine Affine::identity() {
+ Affine ret(1.0, 0.0,
+ 0.0, 1.0,
+ 0.0, 0.0);
+ return ret; // allow NRVO
+}
+
+bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON);
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_AFFINE_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/include/2geom/angle.h b/include/2geom/angle.h
new file mode 100644
index 0000000..f0caaba
--- /dev/null
+++ b/include/2geom/angle.h
@@ -0,0 +1,408 @@
+/**
+ * \file
+ * \brief Various trigoniometric helper functions
+ *//*
+ * Authors:
+ * Johan Engelen <goejendaagh@zonnet.nl>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2007-2010 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_ANGLE_H
+#define LIB2GEOM_SEEN_ANGLE_H
+
+#include <cmath>
+#include <boost/operators.hpp>
+#include <2geom/exception.h>
+#include <2geom/coord.h>
+#include <2geom/point.h>
+
+namespace Geom {
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846
+#endif
+#ifndef M_1_2PI
+# define M_1_2PI 0.159154943091895335768883763373
+#endif
+
+/** @brief Wrapper for angular values.
+ *
+ * This class is a convenience wrapper that implements the behavior generally expected of angles,
+ * like addition modulo \f$2\pi\f$. The value returned from the default conversion
+ * to <tt>double</tt> is in the range \f$[-\pi, \pi)\f$ - the convention used by C's
+ * math library.
+ *
+ * This class holds only a single floating point value, so passing it by value will generally
+ * be faster than passing it by const reference.
+ *
+ * @ingroup Primitives
+ */
+class Angle
+ : boost::additive< Angle
+ , boost::additive< Angle, Coord
+ , boost::equality_comparable< Angle
+ , boost::equality_comparable< Angle, Coord
+ > > > >
+{
+public:
+ Angle() : _angle(0) {}
+ Angle(Coord v) : _angle(v) { _normalize(); } // this can be called implicitly
+ 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 o) {
+ _angle += o._angle;
+ _normalize();
+ return *this;
+ }
+ Angle &operator-=(Angle o) {
+ _angle -= o._angle;
+ _normalize();
+ return *this;
+ }
+ Angle &operator+=(Coord a) {
+ *this += Angle(a);
+ return *this;
+ }
+ Angle &operator-=(Coord a) {
+ *this -= Angle(a);
+ return *this;
+ }
+ bool operator==(Angle o) const {
+ return _angle == o._angle;
+ }
+ bool operator==(Coord c) const {
+ return _angle == Angle(c)._angle;
+ }
+
+ /** @brief Get the angle as radians.
+ * @return Number in range \f$[-\pi, \pi)\f$. */
+ Coord radians() const {
+ return _angle >= M_PI ? _angle - 2*M_PI : _angle;
+ }
+ /** @brief Get the angle as positive radians.
+ * @return Number in range \f$[0, 2\pi)\f$. */
+ Coord radians0() const {
+ return _angle;
+ }
+ /** @brief Get the angle as degrees in math convention.
+ * @return Number in range [-180, 180) obtained by scaling the result of radians()
+ * by \f$180/\pi\f$. */
+ Coord degrees() const { return radians() * (180.0 / M_PI); }
+ /** @brief Get the angle as degrees in clock convention.
+ * This method converts the angle to the "clock convention": angles start from the +Y axis
+ * and grow clockwise. This means that 0 corresponds to \f$\pi/2\f$ radians,
+ * 90 to 0 radians, 180 to \f$-\pi/2\f$ radians, and 270 to \f$\pi\f$ radians.
+ * @return A number in the range [0, 360).
+ */
+ Coord degreesClock() const {
+ Coord ret = 90.0 - _angle * (180.0 / M_PI);
+ if (ret < 0) ret += 360;
+ return ret;
+ }
+ /** @brief Create an angle from its measure in radians. */
+ static Angle from_radians(Coord d) {
+ Angle a(d);
+ return a;
+ }
+ /** @brief Create an angle from its measure in degrees. */
+ static Angle from_degrees(Coord d) {
+ Angle a(d * (M_PI / 180.0));
+ return a;
+ }
+ /** @brief Create an angle from its measure in degrees in clock convention.
+ * @see Angle::degreesClock() */
+ static Angle from_degrees_clock(Coord d) {
+ // first make sure d is in [0, 360)
+ d = std::fmod(d, 360.0);
+ if (d < 0) d += 360.0;
+ Coord rad = M_PI/2 - d * (M_PI / 180.0);
+ if (rad < 0) rad += 2*M_PI;
+ Angle a;
+ a._angle = rad;
+ return a;
+ }
+private:
+
+ void _normalize() {
+ _angle = std::fmod(_angle, 2*M_PI);
+ if (_angle < 0) _angle += 2*M_PI;
+ //_angle -= floor(_angle * (1.0/(2*M_PI))) * 2*M_PI;
+ }
+ Coord _angle; // this is always in [0, 2pi)
+ friend class AngleInterval;
+};
+
+inline Angle distance(Angle const &a, Angle const &b) {
+ // the distance cannot be larger than M_PI.
+ Coord ac = a.radians0();
+ Coord bc = b.radians0();
+ Coord d = fabs(ac - bc);
+ return Angle(d > M_PI ? 2*M_PI - d : d);
+}
+
+/** @brief Directed angular interval.
+ *
+ * Wrapper for directed angles with defined start and end values. Useful e.g. for representing
+ * the portion of an ellipse in an elliptical arc. Both extreme angles are contained
+ * in the interval (it is a closed interval). Angular intervals can also be interptered
+ * as functions \f$f: [0, 1] \to [-\pi, \pi)\f$, which return the start angle for 0,
+ * the end angle for 1, and interpolate linearly for other values. Note that such functions
+ * are not continuous if the interval crosses the angle \f$\pi\f$.
+ *
+ * This class can represent all directed angular intervals, including empty ones.
+ * However, not all possible intervals can be created with the constructors.
+ * For full control, use the setInitial(), setFinal() and setAngles() methods.
+ *
+ * @ingroup Primitives
+ */
+class AngleInterval
+ : boost::equality_comparable< AngleInterval >
+{
+public:
+ AngleInterval() {}
+ /** @brief Create an angular interval from two angles and direction.
+ * If the initial and final angle are the same, a degenerate interval
+ * (containing only one angle) will be created.
+ * @param s Starting angle
+ * @param e Ending angle
+ * @param cw Which direction the interval goes. True means that it goes
+ * in the direction of increasing angles, while false means in the direction
+ * of decreasing angles. */
+ AngleInterval(Angle s, Angle e, bool cw = false)
+ : _start_angle(s), _end_angle(e), _sweep(cw), _full(false)
+ {}
+ AngleInterval(double s, double e, bool cw = false)
+ : _start_angle(s), _end_angle(e), _sweep(cw), _full(false)
+ {}
+ /** @brief Create an angular interval from three angles.
+ * If the inner angle is exactly equal to initial or final angle,
+ * the sweep flag will be set to true, i.e. the interval will go
+ * in the direction of increasing angles.
+ *
+ * If the initial and final angle are the same, but the inner angle
+ * is different, a full angle in the direction of increasing angles
+ * will be created.
+ *
+ * @param s Initial angle
+ * @param inner Angle contained in the interval
+ * @param e Final angle */
+ AngleInterval(Angle s, Angle inner, Angle e)
+ : _start_angle(s)
+ , _end_angle(e)
+ , _sweep((inner-s).radians0() <= (e-s).radians0())
+ , _full(s == e && s != inner)
+ {
+ if (_full) {
+ _sweep = true;
+ }
+ }
+
+ /// Get the start angle.
+ Angle initialAngle() const { return _start_angle; }
+ /// Get the end angle.
+ Angle finalAngle() const { return _end_angle; }
+ /// Check whether the interval goes in the direction of increasing angles.
+ bool sweep() const { return _sweep; }
+ /// Check whether the interval contains only a single angle.
+ bool isDegenerate() const {
+ return _start_angle == _end_angle && !_full;
+ }
+ /// Check whether the interval contains all angles.
+ bool isFull() const {
+ return _start_angle == _end_angle && _full;
+ }
+
+ /** @brief Set the initial angle.
+ * @param a Angle to set
+ * @param prefer_full Whether to set a full angular interval when
+ * the initial angle is set to the final angle */
+ void setInitial(Angle a, bool prefer_full = false) {
+ _start_angle = a;
+ _full = prefer_full && a == _end_angle;
+ }
+
+ /** @brief Set the final angle.
+ * @param a Angle to set
+ * @param prefer_full Whether to set a full angular interval when
+ * the initial angle is set to the final angle */
+ void setFinal(Angle a, bool prefer_full = false) {
+ _end_angle = a;
+ _full = prefer_full && a == _start_angle;
+ }
+ /** @brief Set both angles at once.
+ * The direction (sweep flag) is left unchanged.
+ * @param s Initial angle
+ * @param e Final angle
+ * @param prefer_full Whether to set a full interval when the passed
+ * initial and final angle are the same */
+ void setAngles(Angle s, Angle e, bool prefer_full = false) {
+ _start_angle = s;
+ _end_angle = e;
+ _full = prefer_full && s == e;
+ }
+ /// Set whether the interval goes in the direction of increasing angles.
+ void setSweep(bool s) { _sweep = s; }
+
+ /// Reverse the direction of the interval while keeping contained values the same.
+ void reverse() {
+ using std::swap;
+ swap(_start_angle, _end_angle);
+ _sweep = !_sweep;
+ }
+ /// Get a new interval with reversed direction.
+ AngleInterval reversed() const {
+ AngleInterval result(*this);
+ result.reverse();
+ return result;
+ }
+
+ /// Get an angle corresponding to the specified time value.
+ Angle angleAt(Coord t) const {
+ Coord span = extent();
+ Angle ret = _start_angle.radians0() + span * (_sweep ? t : -t);
+ return ret;
+ }
+ Angle operator()(Coord t) const { return angleAt(t); }
+
+ /** @brief Compute a time value that would evaluate to the given angle.
+ * If the start and end angle are exactly the same, NaN will be returned.
+ * Negative values will be returned for angles between the initial angle
+ * and the angle exactly opposite the midpoint of the interval. */
+ Coord timeAtAngle(Angle a) const {
+ if (_full) {
+ Angle ta = _sweep ? a - _start_angle : _start_angle - a;
+ return ta.radians0() / (2*M_PI);
+ }
+ Coord ex = extent();
+ Coord outex = 2*M_PI - ex;
+ if (_sweep) {
+ Angle midout = _start_angle - outex / 2;
+ Angle acmp = a - midout, scmp = _start_angle - midout;
+ if (acmp.radians0() >= scmp.radians0()) {
+ return (a - _start_angle).radians0() / ex;
+ } else {
+ return -(_start_angle - a).radians0() / ex;
+ }
+ } else {
+ Angle midout = _start_angle + outex / 2;
+ Angle acmp = a - midout, scmp = _start_angle - midout;
+ if (acmp.radians0() <= scmp.radians0()) {
+ return (_start_angle - a).radians0() / ex;
+ } else {
+ return -(a - _start_angle).radians0() / ex;
+ }
+ }
+ }
+
+ /// Check whether the interval includes the given angle.
+ bool contains(Angle a) const {
+ if (_full) return true;
+ Coord s = _start_angle.radians0();
+ Coord e = _end_angle.radians0();
+ Coord x = a.radians0();
+ if (_sweep) {
+ if (s < e) return x >= s && x <= e;
+ return x >= s || x <= e;
+ } else {
+ if (s > e) return x <= s && x >= e;
+ return x <= s || x >= e;
+ }
+ }
+ /** @brief Extent of the angle interval.
+ * Equivalent to the absolute value of the sweep angle.
+ * @return Extent in range \f$[0, 2\pi)\f$. */
+ Coord extent() const {
+ if (_full) return 2*M_PI;
+ return _sweep
+ ? (_end_angle - _start_angle).radians0()
+ : (_start_angle - _end_angle).radians0();
+ }
+ /** @brief Get the sweep angle of the interval.
+ * This is the value you need to add to the initial angle to get the final angle.
+ * It is positive when sweep is true. Denoted as \f$\Delta\theta\f$ in the SVG
+ * elliptical arc implementation notes. */
+ Coord sweepAngle() const {
+ if (_full) return _sweep ? 2*M_PI : -2*M_PI;
+ Coord sa = _end_angle.radians0() - _start_angle.radians0();
+ if (_sweep && sa < 0) sa += 2*M_PI;
+ if (!_sweep && sa > 0) sa -= 2*M_PI;
+ return sa;
+ }
+
+ /// Check another interval for equality.
+ bool operator==(AngleInterval const &other) const {
+ if (_start_angle != other._start_angle) return false;
+ if (_end_angle != other._end_angle) return false;
+ if (_sweep != other._sweep) return false;
+ if (_full != other._full) return false;
+ return true;
+ }
+
+ static AngleInterval create_full(Angle start, bool sweep = true) {
+ AngleInterval result;
+ result._start_angle = result._end_angle = start;
+ result._sweep = sweep;
+ result._full = true;
+ return result;
+ }
+
+private:
+ Angle _start_angle;
+ Angle _end_angle;
+ bool _sweep;
+ bool _full;
+};
+
+/** @brief Given an angle in degrees, return radians
+ * @relates Angle */
+inline Coord rad_from_deg(Coord deg) { return deg*M_PI/180.0;}
+/** @brief Given an angle in radians, return degrees
+ * @relates Angle */
+inline Coord deg_from_rad(Coord rad) { return rad*180.0/M_PI;}
+
+} // end namespace Geom
+
+namespace std {
+template <> class iterator_traits<Geom::Angle> {};
+}
+
+#endif // LIB2GEOM_SEEN_ANGLE_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/basic-intersection.h b/include/2geom/basic-intersection.h
new file mode 100644
index 0000000..2d0c00d
--- /dev/null
+++ b/include/2geom/basic-intersection.h
@@ -0,0 +1,151 @@
+/** @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.
+ *
+ */
+
+#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>
+
+#include <vector>
+#include <utility>
+
+#define USE_RECURSIVE_INTERSECTOR 0
+
+
+namespace Geom {
+
+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);
+
+/*
+ * 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
+ *
+ * This routine is based on the Bezier Clipping Algorithm,
+ * see: Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping
+ */
+void find_intersections_bezier_clipping (std::vector< std::pair<double, double> > & xs,
+ std::vector<Point> const& A,
+ std::vector<Point> const& B,
+ 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
+ *
+ * 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 there are collinear normals
+ *
+ * This routine is based on the Bezier Clipping Algorithm,
+ * see: Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping
+ */
+void find_collinear_normal (std::vector< std::pair<double, double> >& xs,
+ std::vector<Point> const& A,
+ std::vector<Point> const& B,
+ double precision = EPSILON);
+
+void polish_intersections(std::vector<std::pair<double, double> > &xs,
+ D2<SBasis> const &A,
+ D2<SBasis> const &B);
+
+
+/**
+ * Compute the Hausdorf distance from A to B only.
+ */
+double hausdorfl(D2<SBasis>& A, D2<SBasis> const &B,
+ double m_precision,
+ double *a_t=NULL, double *b_t=NULL);
+
+/**
+ * Compute the symmetric Hausdorf distance.
+ */
+double hausdorf(D2<SBasis> &A, D2<SBasis> const &B,
+ double m_precision,
+ double *a_t=NULL, double *b_t=NULL);
+
+/**
+ * Check if two line segments intersect. If they are collinear, the result is undefined.
+ * @return True if line segments AB and CD intersect
+ */
+bool non_collinear_segments_intersect(const Point &A, const Point &B, const Point &C, const Point &D);
+}
+
+#endif // !LIB2GEOM_SEEN_BASIC_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:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/bezier-curve.h b/include/2geom/bezier-curve.h
new file mode 100644
index 0000000..754c9cc
--- /dev/null
+++ b/include/2geom/bezier-curve.h
@@ -0,0 +1,366 @@
+/**
+ * \file
+ * \brief Bezier curve
+ *//*
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright 2007-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_BEZIER_CURVE_H
+#define LIB2GEOM_SEEN_BEZIER_CURVE_H
+
+#include <2geom/curve.h>
+#include <2geom/sbasis-curve.h> // for non-native winding method
+#include <2geom/bezier.h>
+#include <2geom/transforms.h>
+
+namespace Geom
+{
+
+class BezierCurve : public Curve {
+protected:
+ D2<Bezier> inner;
+ BezierCurve() {}
+ 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> controlPoints() const { return bezier_points(inner); }
+ D2<Bezier> const &fragment() const { return 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 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.
+ * Note that the caller is responsible for checking the size of this vector.
+ * @throws LogicalError Thrown when the size of the vector does not match the order. */
+ virtual void setPoints(std::vector<Point> const &ps) {
+ // must be virtual, because HLineSegment will need to redefine it
+ if (ps.size() != order() + 1)
+ THROW_LOGICALERROR("BezierCurve::setPoints: incorrect number of points in vector");
+ for(unsigned i = 0; i <= order(); i++) {
+ setPoint(i, ps[i]);
+ }
+ }
+ /// @}
+
+ /// @name Construct a Bezier curve with runtime-determined order.
+ /// @{
+ /** @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
+ Point initialPoint() const override { return inner.at0(); }
+ Point finalPoint() const override { return inner.at1(); }
+ bool isDegenerate() const override;
+ bool isLineSegment() const override;
+ void setInitial(Point const &v) override { setPoint(0, v); }
+ void setFinal(Point const &v) override { setPoint(order(), v); }
+ Rect boundsFast() const override { return *bounds_fast(inner); }
+ Rect boundsExact() const override { return *bounds_exact(inner); }
+ void expandToTransformed(Rect &bbox, Affine const &transform) const override;
+ OptRect boundsLocal(OptInterval const &i, unsigned deg) const override {
+ if (!i) return OptRect();
+ if(i->min() == 0 && i->max() == 1) return boundsFast();
+ if(deg == 0) return bounds_local(inner, i);
+ // TODO: UUUUUUGGGLLY
+ if(deg == 1 && order() > 1) return OptRect(bounds_local(Geom::derivative(inner[X]), i),
+ bounds_local(Geom::derivative(inner[Y]), i));
+ return OptRect();
+ }
+ Curve *duplicate() const override {
+ return new BezierCurve(*this);
+ }
+
+ Curve *portion(Coord f, Coord t) const override;
+
+ Curve *reverse() const override {
+ return new BezierCurve(Geom::reverse(inner));
+ }
+
+ using Curve::operator*=;
+ void operator*=(Translate const &tr) override {
+ for (unsigned i = 0; i < size(); ++i) {
+ inner[X][i] += tr[X];
+ inner[Y][i] += tr[Y];
+ }
+ }
+ void operator*=(Scale const &s) override {
+ for (unsigned i = 0; i < size(); ++i) {
+ inner[X][i] *= s[X];
+ inner[Y][i] *= s[Y];
+ }
+ }
+ void operator*=(Affine const &m) override {
+ for (unsigned i = 0; i < size(); ++i) {
+ setPoint(i, controlPoint(i) * m);
+ }
+ }
+
+ Curve *derivative() const override {
+ return new BezierCurve(Geom::derivative(inner[X]), Geom::derivative(inner[Y]));
+ }
+ int degreesOfFreedom() const override {
+ return 2 * (order() + 1);
+ }
+ std::vector<Coord> roots(Coord v, Dim2 d) const override {
+ return (inner[d] - v).roots();
+ }
+ Coord nearestTime(Point const &p, Coord from = 0, Coord to = 1) const override;
+ Coord length(Coord tolerance) const override;
+ std::vector<CurveIntersection> intersect(Curve const &other, Coord eps = EPSILON) const override;
+ Point pointAt(Coord t) const override { return inner.pointAt(t); }
+ std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const override {
+ return inner.valueAndDerivatives(t, n);
+ }
+ Coord valueAt(Coord t, Dim2 d) const override { return inner[d].valueAt(t); }
+ D2<SBasis> toSBasis() const override {return inner.toSBasis(); }
+ bool isNear(Curve const &c, Coord precision) const override;
+ bool operator==(Curve const &c) const override;
+ void feed(PathSink &sink, bool) const override;
+};
+
+template <unsigned degree>
+class BezierCurveN
+ : public BezierCurve
+{
+ template <unsigned required_degree>
+ static void assert_degree(BezierCurveN<required_degree> const *) {}
+
+public:
+ /// @name Construct Bezier curves
+ /// @{
+ /** @brief Construct a Bezier curve of the specified order with all points zero. */
+ BezierCurveN() {
+ inner = D2<Bezier>(Bezier(Bezier::Order(degree)), Bezier(Bezier::Order(degree)));
+ }
+
+ /** @brief Construct from 2D Bezier polynomial. */
+ explicit BezierCurveN(D2<Bezier > const &x) {
+ inner = x;
+ }
+
+ /** @brief Construct from two 1D Bezier polynomials of the same order. */
+ BezierCurveN(Bezier x, Bezier y) {
+ inner = D2<Bezier > (x,y);
+ }
+
+ /** @brief Construct a Bezier curve from a vector of its control points. */
+ BezierCurveN(std::vector<Point> const &points) {
+ unsigned ord = points.size() - 1;
+ if (ord != degree) THROW_LOGICALERROR("BezierCurve<degree> does not match number of points");
+ for (unsigned d = 0; d < 2; ++d) {
+ inner[d] = Bezier(Bezier::Order(ord));
+ for(unsigned i = 0; i <= ord; i++)
+ inner[d][i] = points[i][d];
+ }
+ }
+
+ /** @brief Construct a linear segment from its endpoints. */
+ BezierCurveN(Point c0, Point c1) {
+ assert_degree<1>(this);
+ for(unsigned d = 0; d < 2; d++)
+ inner[d] = Bezier(c0[d], c1[d]);
+ }
+
+ /** @brief Construct a quadratic Bezier curve from its control points. */
+ BezierCurveN(Point c0, Point c1, Point c2) {
+ assert_degree<2>(this);
+ for(unsigned d = 0; d < 2; d++)
+ inner[d] = Bezier(c0[d], c1[d], c2[d]);
+ }
+
+ /** @brief Construct a cubic Bezier curve from its control points. */
+ BezierCurveN(Point c0, Point c1, Point c2, Point c3) {
+ assert_degree<3>(this);
+ for(unsigned d = 0; d < 2; d++)
+ inner[d] = Bezier(c0[d], c1[d], c2[d], c3[d]);
+ }
+
+ // default copy
+ // default assign
+
+ /// @}
+
+ /** @brief Divide a Bezier curve into two curves
+ * @param t Time value
+ * @return Pair of Bezier curves \f$(\mathbf{D}, \mathbf{E})\f$ such that
+ * \f$\mathbf{D}[ [0,1] ] = \mathbf{C}[ [0,t] ]\f$ and
+ * \f$\mathbf{E}[ [0,1] ] = \mathbf{C}[ [t,1] ]\f$ */
+ std::pair<BezierCurveN, BezierCurveN> subdivide(Coord t) const {
+ std::pair<Bezier, Bezier> sx = inner[X].subdivide(t), sy = inner[Y].subdivide(t);
+ return std::make_pair(
+ BezierCurveN(sx.first, sy.first),
+ BezierCurveN(sx.second, sy.second));
+ }
+
+ bool isDegenerate() const override {
+ return BezierCurve::isDegenerate();
+ }
+
+ bool isLineSegment() const override {
+ if constexpr (degree == 1) {
+ return true;
+ } else {
+ return BezierCurve::isLineSegment();
+ }
+ }
+
+ Curve *duplicate() const override {
+ return new BezierCurveN(*this);
+ }
+ Curve *portion(Coord f, Coord t) const override {
+ if (degree == 1) {
+ return new BezierCurveN<1>(pointAt(f), pointAt(t));
+ } else {
+ return new BezierCurveN(Geom::portion(inner, f, t));
+ }
+ }
+ Curve *reverse() const override {
+ if (degree == 1) {
+ return new BezierCurveN<1>(finalPoint(), initialPoint());
+ } else {
+ return new BezierCurveN(Geom::reverse(inner));
+ }
+ }
+ Curve *derivative() const override;
+
+ Coord nearestTime(Point const &p, Coord from = 0, Coord to = 1) const override {
+ return BezierCurve::nearestTime(p, from, to);
+ }
+ std::vector<CurveIntersection> intersect(Curve const &other, Coord eps = EPSILON) const override {
+ // call super. this is implemented only to allow specializations
+ return BezierCurve::intersect(other, eps);
+ }
+ int winding(Point const &p) const override {
+ return Curve::winding(p);
+ }
+ void feed(PathSink &sink, bool moveto_initial) const override {
+ // call super. this is implemented only to allow specializations
+ BezierCurve::feed(sink, moveto_initial);
+ }
+ void expandToTransformed(Rect &bbox, Affine const &transform) const override {
+ // call super. this is implemented only to allow specializations
+ BezierCurve::expandToTransformed(bbox, transform);
+ }
+};
+
+// BezierCurveN<0> is meaningless; specialize it out
+template<> class BezierCurveN<0> : public BezierCurveN<1> { private: BezierCurveN();};
+
+/** @brief Line segment.
+ * Line segments are Bezier curves of order 1. They have only two control points,
+ * the starting point and the ending point.
+ * @ingroup Curves */
+typedef BezierCurveN<1> LineSegment;
+
+/** @brief Quadratic (order 2) Bezier curve.
+ * @ingroup Curves */
+typedef BezierCurveN<2> QuadraticBezier;
+
+/** @brief Cubic (order 3) Bezier curve.
+ * @ingroup Curves */
+typedef BezierCurveN<3> CubicBezier;
+
+template <unsigned degree>
+inline
+Curve *BezierCurveN<degree>::derivative() const {
+ return new BezierCurveN<degree-1>(Geom::derivative(inner[X]), Geom::derivative(inner[Y]));
+}
+
+// optimized specializations
+template <> inline bool BezierCurveN<1>::isDegenerate() const {
+ return inner[X][0] == inner[X][1] && inner[Y][0] == inner[Y][1];
+}
+template <> inline bool BezierCurveN<1>::isLineSegment() const { return true; }
+template <> Curve *BezierCurveN<1>::derivative() const;
+template <> Coord BezierCurveN<1>::nearestTime(Point const &, Coord, Coord) const;
+template <> std::vector<CurveIntersection> BezierCurveN<1>::intersect(Curve const &, Coord) const;
+template <> std::vector<CurveIntersection> BezierCurveN<2>::intersect(Curve const &, Coord) const;
+template <> std::vector<CurveIntersection> BezierCurveN<3>::intersect(Curve const &, Coord) const;
+template <> int BezierCurveN<1>::winding(Point const &) 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;
+template <> void BezierCurveN<1>::expandToTransformed(Rect &bbox, Affine const &transform) const;
+template <> void BezierCurveN<2>::expandToTransformed(Rect &bbox, Affine const &transform) const;
+template <> void BezierCurveN<3>::expandToTransformed(Rect &bbox, Affine const &transform) const;
+
+inline Point middle_point(LineSegment const& _segment) {
+ return ( _segment.initialPoint() + _segment.finalPoint() ) / 2;
+}
+
+inline Coord length(LineSegment const& seg) {
+ return distance(seg.initialPoint(), seg.finalPoint());
+}
+
+Coord bezier_length(std::vector<Point> const &points, Coord tolerance = 0.01);
+Coord bezier_length(Point p0, Point p1, Point p2, Coord tolerance = 0.01);
+Coord bezier_length(Point p0, Point p1, Point p2, Point p3, Coord tolerance = 0.01);
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_BEZIER_CURVE_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/include/2geom/bezier-to-sbasis.h b/include/2geom/bezier-to-sbasis.h
new file mode 100644
index 0000000..73c55d9
--- /dev/null
+++ b/include/2geom/bezier-to-sbasis.h
@@ -0,0 +1,94 @@
+/**
+ * \file
+ * \brief Conversion between Bezier control points and SBasis curves
+ *//*
+ * 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.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_BEZIER_TO_SBASIS_H
+#define LIB2GEOM_SEEN_BEZIER_TO_SBASIS_H
+
+#include <2geom/coord.h>
+#include <2geom/point.h>
+#include <2geom/d2.h>
+#include <2geom/sbasis-to-bezier.h>
+
+namespace Geom
+{
+
+#if 0
+inline SBasis bezier_to_sbasis(Coord const *handles, unsigned order) {
+ if(order == 0)
+ return Linear(handles[0]);
+ else if(order == 1)
+ return Linear(handles[0], handles[1]);
+ else
+ return multiply(Linear(1, 0), bezier_to_sbasis(handles, order-1)) +
+ multiply(Linear(0, 1), bezier_to_sbasis(handles+1, order-1));
+}
+
+
+template <typename T>
+inline D2<SBasis> handles_to_sbasis(T const &handles, unsigned order)
+{
+ double v[2][order+1];
+ for(unsigned i = 0; i <= order; i++)
+ for(unsigned j = 0; j < 2; j++)
+ v[j][i] = handles[i][j];
+ return D2<SBasis>(bezier_to_sbasis(v[0], order),
+ bezier_to_sbasis(v[1], order));
+}
+#endif
+
+
+template <typename T>
+inline
+D2<SBasis> handles_to_sbasis(T const& handles, unsigned order)
+{
+ D2<SBasis> sbc;
+ size_t sz = order + 1;
+ std::vector<Point> v;
+ v.reserve(sz);
+ for (size_t i = 0; i < sz; ++i)
+ v.push_back(handles[i]);
+ bezier_to_sbasis(sbc, v);
+ return sbc;
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_BEZIER_TO_SBASIS_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/include/2geom/bezier-utils.h b/include/2geom/bezier-utils.h
new file mode 100644
index 0000000..3e56e6e
--- /dev/null
+++ b/include/2geom/bezier-utils.h
@@ -0,0 +1,99 @@
+/**
+ * \file
+ * \brief Bezier fitting algorithms
+ *//*
+ * An Algorithm for Automatically Fitting Digitized Curves
+ * by Philip J. Schneider
+ * from "Graphics Gems", Academic Press, 1990
+ *
+ * Authors:
+ * Philip J. Schneider
+ * Lauris Kaplinski <lauris@ximian.com>
+ *
+ * Copyright (C) 1990 Philip J. Schneider
+ * Copyright (C) 2001 Lauris Kaplinski and Ximian, Inc.
+ *
+ * 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_BEZIER_UTILS_H
+#define LIB2GEOM_SEEN_BEZIER_UTILS_H
+
+#include <2geom/point.h>
+
+namespace Geom {
+
+Point bezier_pt(unsigned degree, Point const V[], double t);
+
+int bezier_fit_cubic(Point bezier[], Point const data[], int len, double error);
+
+int bezier_fit_cubic_r(Point bezier[], Point const data[], int len, double error,
+ unsigned max_beziers);
+
+int bezier_fit_cubic_full(Point bezier[], int split_points[], Point const data[], int len,
+ Point const &tHat1, Point const &tHat2,
+ double error, unsigned max_beziers);
+
+Point darray_left_tangent(Point const d[], unsigned const len);
+Point darray_left_tangent(Point const d[], unsigned const len, double const tolerance_sq);
+Point darray_right_tangent(Point const d[], unsigned const length, double const tolerance_sq);
+
+template <typename iterator>
+static void
+cubic_bezier_poly_coeff(iterator b, Point *pc) {
+ double c[10] = {1,
+ -3, 3,
+ 3, -6, 3,
+ -1, 3, -3, 1};
+
+ int cp = 0;
+
+ for(int i = 0; i < 4; i++) {
+ pc[i] = Point(0,0);
+ ++b;
+ }
+ for(int i = 0; i < 4; i++) {
+ --b;
+ for(int j = 0; j <= i; j++) {
+ pc[3 - j] += c[cp]*(*b);
+ cp++;
+ }
+ }
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_BEZIER_UTILS_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/include/2geom/bezier.h b/include/2geom/bezier.h
new file mode 100644
index 0000000..d65b3bd
--- /dev/null
+++ b/include/2geom/bezier.h
@@ -0,0 +1,394 @@
+/**
+ * @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.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_BEZIER_H
+#define LIB2GEOM_SEEN_BEZIER_H
+
+#include <algorithm>
+#include <valarray>
+#include <2geom/coord.h>
+#include <2geom/d2.h>
+#include <2geom/math-utils.h>
+
+namespace Geom {
+
+/** @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 bernstein_value_at(double t, T const *c_, unsigned n) {
+ double u = 1.0 - t;
+ double bc = 1;
+ double tn = 1;
+ T tmp = c_[0]*u;
+ for(unsigned 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]);
+}
+
+/** @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
+ find_bezier_roots(std::vector<double> & solutions,
+ double l, double r) const;
+
+protected:
+ Bezier(Coord const c[], unsigned ord)
+ : c_(c, ord+1)
+ {}
+
+public:
+ 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_) {}
+ Bezier &operator=(Bezier const &other) {
+ if ( c_.size() != other.c_.size() ) {
+ c_.resize(other.c_.size());
+ }
+ c_ = other.c_;
+ return *this;
+ }
+
+ bool operator==(Bezier const &other) const
+ {
+ if (degree() != other.degree()) {
+ return false;
+ }
+
+ for (size_t i = 0; i < c_.size(); i++) {
+ if (c_[i] != other.c_[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool operator!=(Bezier const &other) const
+ {
+ return !(*this == other);
+ }
+
+ struct Order {
+ unsigned order;
+ explicit Order(Bezier const &b) : order(b.order()) {}
+ explicit Order(unsigned o) : order(o) {}
+ operator unsigned() const { return order; }
+ };
+
+ //Construct an arbitrary order bezier
+ Bezier(Order ord) : c_(0., ord.order+1) {
+ assert(ord.order == order());
+ }
+
+ /// @name Construct Bezier polynomials from their control points
+ /// @{
+ explicit Bezier(Coord c0) : c_(0., 1) {
+ c_[0] = c0;
+ }
+ Bezier(Coord c0, Coord c1) : c_(0., 2) {
+ c_[0] = c0; c_[1] = c1;
+ }
+ Bezier(Coord c0, Coord c1, Coord c2) : c_(0., 3) {
+ c_[0] = c0; c_[1] = c1; c_[2] = c2;
+ }
+ 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;
+ }
+
+ 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 resize (unsigned int n, Coord v = 0) {
+ c_.resize (n, v);
+ }
+ void clear() {
+ c_.resize(0);
+ }
+
+ //IMPL: FragmentConcept
+ typedef Coord output_type;
+ bool isZero(double eps=EPSILON) const {
+ for(unsigned i = 0; i <= order(); i++) {
+ if( ! are_near(c_[i], 0., eps) ) return false;
+ }
+ return true;
+ }
+ 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;
+ }
+ bool isFinite() const {
+ for(unsigned i = 0; i <= order(); i++) {
+ if(!std::isfinite(c_[i])) return false;
+ }
+ return true;
+ }
+ 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());
+ }
+ Coord operator()(double t) const { return valueAt(t); }
+
+ SBasis toSBasis() const;
+
+ Coord &operator[](unsigned ix) { return c_[ix]; }
+ Coord const &operator[](unsigned ix) const { return const_cast<std::valarray<Coord>&>(c_)[ix]; }
+
+ void setCoeff(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;
+
+ void subdivide(Coord t, Bezier *left, Bezier *right) const;
+ std::pair<Bezier, Bezier> subdivide(Coord t) const;
+
+ std::vector<Coord> roots() const;
+ std::vector<Coord> roots(Interval const &ivl) const;
+
+ Bezier forward_difference(unsigned k) const;
+ Bezier elevate_degree() const;
+ Bezier reduce_degree() const;
+ Bezier elevate_to_degree(unsigned newDegree) const;
+ Bezier deflate() const;
+
+ // basic arithmetic operators
+ Bezier &operator+=(double v) {
+ c_ += v;
+ return *this;
+ }
+ Bezier &operator-=(double v) {
+ c_ -= v;
+ return *this;
+ }
+ Bezier &operator*=(double v) {
+ c_ *= v;
+ return *this;
+ }
+ Bezier &operator/=(double v) {
+ c_ /= v;
+ return *this;
+ }
+ Bezier &operator+=(Bezier const &other);
+ Bezier &operator-=(Bezier const &other);
+
+ /// Unary minus
+ Bezier operator-() const
+ {
+ Bezier result;
+ result.c_ = -c_;
+ return result;
+ }
+};
+
+
+void bezier_to_sbasis (SBasis &sb, Bezier const &bz);
+
+Bezier operator*(Bezier const &f, Bezier const &g);
+inline Bezier multiply(Bezier const &f, Bezier const &g) {
+ Bezier result = f * g;
+ return result;
+}
+
+inline Bezier reverse(const Bezier & a) {
+ Bezier result = Bezier(Bezier::Order(a));
+ for(unsigned i = 0; i <= a.order(); i++)
+ result[i] = a[a.order() - i];
+ return result;
+}
+
+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) {
+ std::vector<Point> result;
+ for(unsigned i = 0; i <= a[0].order(); i++) {
+ Point p;
+ for(unsigned d = 0; d < 2; d++) p[d] = a[d][i];
+ result.push_back(p);
+ }
+ return result;
+}
+
+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);
+
+/// Expand an interval to the image of a Bézier-Bernstein polynomial, assuming it already contains the initial point x0.
+void bezier_expand_to_image(Interval &range, Coord x0, Coord x1, Coord x2);
+void bezier_expand_to_image(Interval &range, Coord x0, Coord x1, Coord x2, Coord x3);
+
+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]) << ", ";
+ }
+ os << format_coord_nice(b[b.order()]) << ")";
+ return os;
+}
+
+} // namespace Geom
+
+#endif // LIB2GEOM_SEEN_BEZIER_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/include/2geom/cairo-path-sink.h b/include/2geom/cairo-path-sink.h
new file mode 100644
index 0000000..3f7a044
--- /dev/null
+++ b/include/2geom/cairo-path-sink.h
@@ -0,0 +1,91 @@
+/**
+ * @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, 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_CAIRO_PATH_SINK_H
+#define LIB2GEOM_SEEN_CAIRO_PATH_SINK_H
+
+#ifdef HAVE_CAIRO
+
+#include <2geom/path-sink.h>
+#include <cairo.h>
+
+namespace Geom {
+
+
+/** @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 moveTo(Point const &p) override;
+ void lineTo(Point const &p) override;
+ void curveTo(Point const &c0, Point const &c1, Point const &p) override;
+ void quadTo(Point const &c, Point const &p) override;
+ void arcTo(Coord rx, Coord ry, Coord angle,
+ bool large_arc, bool sweep, Point const &p) override;
+ void closePath() override;
+ void flush() override;
+
+private:
+ cairo_t *_cr;
+ Point _current_point;
+};
+
+}
+
+#endif
+
+#endif // !LIB2GEOM_SEEN_CAIRO_PATH_SINK_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/include/2geom/choose.h b/include/2geom/choose.h
new file mode 100644
index 0000000..106d04f
--- /dev/null
+++ b/include/2geom/choose.h
@@ -0,0 +1,147 @@
+/**
+ * \file
+ * \brief Calculation of binomial cefficients
+ *//*
+ * 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.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_CHOOSE_H
+#define LIB2GEOM_SEEN_CHOOSE_H
+
+#include <vector>
+
+namespace Geom {
+
+/**
+ * @brief Given a multiple of binomial(n, k), modify it to the same multiple of binomial(n + 1, k).
+ */
+template <typename T>
+constexpr void binomial_increment_n(T &b, int n, int k)
+{
+ b = b * (n + 1) / (n + 1 - k);
+}
+
+/**
+ * @brief Given a multiple of binomial(n, k), modify it to the same multiple of binomial(n - 1, k).
+ */
+template <typename T>
+constexpr void binomial_decrement_n(T &b, int n, int k)
+{
+ b = b * (n - k) / n;
+}
+
+/**
+ * @brief Given a multiple of binomial(n, k), modify it to the same multiple of binomial(n, k + 1).
+ */
+template <typename T>
+constexpr void binomial_increment_k(T &b, int n, int k)
+{
+ b = b * (n - k) / (k + 1);
+}
+
+/**
+ * @brief Given a multiple of binomial(n, k), modify it to the same multiple of binomial(n, k - 1).
+ */
+template <typename T>
+constexpr void binomial_decrement_k(T &b, int n, int k)
+{
+ b = b * k / (n + 1 - k);
+}
+
+/**
+ * @brief Calculate the (n, k)th binomial coefficient.
+ */
+template <typename T>
+constexpr T choose(unsigned n, unsigned k)
+{
+ if (k > n) {
+ return 0;
+ }
+ T b = 1;
+ int max = std::min(k, n - k);
+ for (int i = 0; i < max; i++) {
+ binomial_increment_k(b, n, i);
+ }
+ return b;
+}
+
+/**
+ * @brief Class for calculating and accessing a row of Pascal's triangle.
+ */
+template <typename ValueType>
+class BinomialCoefficient
+{
+public:
+ using value_type = ValueType;
+ using container_type = std::vector<value_type>;
+
+ BinomialCoefficient(unsigned int _n)
+ : n(_n)
+ {
+ coefficients.reserve(n / 2 + 1);
+ coefficients.emplace_back(1);
+ value_type b = 1;
+ for (int i = 0; i < n / 2; i++) {
+ binomial_increment_k(b, n, i);
+ coefficients.emplace_back(b);
+ }
+ }
+
+ unsigned int size() const
+ {
+ return degree() + 1;
+ }
+
+ unsigned int degree() const
+ {
+ return n;
+ }
+
+ value_type operator[](unsigned int k) const
+ {
+ return coefficients[std::min(k, n - k)];
+ }
+
+private:
+ int const n;
+ container_type coefficients;
+};
+
+} // namespace Geom
+
+#endif // LIB2GEOM_SEEN_CHOOSE_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/include/2geom/circle.h b/include/2geom/circle.h
new file mode 100644
index 0000000..a4d5f20
--- /dev/null
+++ b/include/2geom/circle.h
@@ -0,0 +1,165 @@
+/** @file
+ * @brief Circle shape
+ *//*
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * 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
+ * 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_CIRCLE_H
+#define LIB2GEOM_SEEN_CIRCLE_H
+
+#include <2geom/forward.h>
+#include <2geom/intersection.h>
+#include <2geom/point.h>
+#include <2geom/rect.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
+ : boost::equality_comparable1< Circle
+ , MultipliableNoncommutative< Circle, Translate
+ , MultipliableNoncommutative< Circle, Rotate
+ , MultipliableNoncommutative< Circle, Zoom
+ > > > >
+{
+ Point _center;
+ Coord _radius;
+
+public:
+ Circle() {}
+ Circle(Coord cx, Coord cy, Coord r)
+ : _center(cx, cy), _radius(r)
+ {}
+ Circle(Point const &center, Coord r)
+ : _center(center), _radius(r)
+ {}
+
+ Circle(Coord A, Coord B, Coord C, Coord D) {
+ setCoefficients(A, B, C, D);
+ }
+
+ // Construct the unique circle passing through three points.
+ //Circle(Point const &a, Point const &b, Point const &c);
+
+ Point center() const { return _center; }
+ Coord center(Dim2 d) const { return _center[d]; }
+ Coord radius() const { return _radius; }
+ Coord area() const { return M_PI * _radius * _radius; }
+ bool isDegenerate() const { return _radius == 0; }
+
+ void setCenter(Point const &p) { _center = p; }
+ void setRadius(Coord c) { _radius = c; }
+
+ Rect boundsFast() const;
+ Rect boundsExact() const { return boundsFast(); }
+
+ Point initialPoint() const;
+ Point finalPoint() const { return initialPoint(); }
+ Point pointAt(Coord t) const;
+ Coord valueAt(Coord t, Dim2 d) const;
+ Coord timeAt(Point const &p) const;
+ Coord nearestTime(Point const &p) const;
+
+ bool contains(Point const &p) const { return distance(p, _center) <= _radius; }
+ bool contains(Rect const &other) const;
+ bool contains(Circle const &other) const;
+
+ bool intersects(Line const &l) const;
+ bool intersects(LineSegment const &l) const;
+ bool intersects(Circle const &other) const;
+
+ std::vector<ShapeIntersection> intersect(Line const &other) const;
+ std::vector<ShapeIntersection> intersect(LineSegment const &other) const;
+ std::vector<ShapeIntersection> intersect(Circle const &other) const;
+
+ // build a circle by its implicit equation:
+ // Ax^2 + Ay^2 + Bx + Cy + D = 0
+ void setCoefficients(Coord A, Coord B, Coord C, Coord D);
+ void coefficients(Coord &A, Coord &B, Coord &C, Coord &D) const;
+ std::vector<Coord> coefficients() const;
+
+ Zoom unitCircleTransform() const;
+ Zoom inverseUnitCircleTransform() const;
+
+ EllipticalArc *
+ arc(Point const& initial, Point const& inner, Point const& final) const;
+
+ D2<SBasis> toSBasis() const;
+
+ 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;
+ }
+
+ bool operator==(Circle const &other) const;
+
+ /** @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);
+};
+
+bool are_near(Circle const &a, Circle const &b, Coord eps=EPSILON);
+
+std::ostream &operator<<(std::ostream &out, Circle const &c);
+
+template <>
+struct ShapeTraits<Circle> {
+ typedef Coord TimeType;
+ typedef Interval IntervalType;
+ typedef Ellipse AffineClosureType;
+ typedef Intersection<> IntersectionType;
+};
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_CIRCLE_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/include/2geom/concepts.h b/include/2geom/concepts.h
new file mode 100644
index 0000000..de76d0f
--- /dev/null
+++ b/include/2geom/concepts.h
@@ -0,0 +1,209 @@
+/**
+ * \file
+ * \brief Template concepts used by 2Geom
+ *//*
+ * Copyright 2007 Michael 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, 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.
+ */
+
+#ifndef LIB2GEOM_SEEN_CONCEPTS_H
+#define LIB2GEOM_SEEN_CONCEPTS_H
+
+#include <2geom/sbasis.h>
+#include <2geom/interval.h>
+#include <2geom/point.h>
+#include <2geom/rect.h>
+#include <2geom/intersection.h>
+#include <vector>
+#include <boost/concept/assert.hpp>
+#include <2geom/forward.h>
+#include <2geom/transforms.h>
+
+namespace Geom {
+
+//forward decls
+template <typename T> struct ResultTraits;
+
+template <> struct ResultTraits<double> {
+ typedef OptInterval bounds_type;
+ typedef SBasis sb_type;
+};
+
+template <> struct ResultTraits<Point> {
+ typedef OptRect bounds_type;
+ typedef D2<SBasis> sb_type;
+};
+
+//A concept for one-dimensional functions defined on [0,1]
+template <typename T>
+struct FragmentConcept {
+ typedef typename T::output_type OutputType;
+ typedef typename ResultTraits<OutputType>::bounds_type BoundsType;
+ typedef typename ResultTraits<OutputType>::sb_type SbType;
+ T t;
+ double d;
+ OutputType o;
+ bool b;
+ BoundsType i;
+ Interval dom;
+ std::vector<OutputType> v;
+ unsigned u;
+ SbType sb;
+ void constraints() {
+ t = T(o);
+ 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);
+ //Is a pure derivative (ignoring others) accessor ever much faster?
+ //u = number of values returned. first val is value.
+ sb = t.toSBasis();
+ t = reverse(t);
+ i = bounds_fast(t);
+ i = bounds_exact(t);
+ i = bounds_local(t, dom);
+ /*With portion, Interval makes some sense, but instead I'm opting for
+ doubles, for the following reasons:
+ A) This way a reversed portion may be specified
+ B) Performance might be a bit better for piecewise and such
+ C) Interval version provided below
+ */
+ t = portion(t, d, d);
+ }
+};
+
+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, other;
+ Time t;
+ Point p;
+ AffineClosure ac;
+ Affine m;
+ Translate tr;
+ Coord c;
+ bool bool_;
+ std::vector<Isect> ivec;
+
+ void constraints() {
+ p = shape.pointAt(t);
+ c = shape.valueAt(t, X);
+ ivec = shape.intersect(other);
+ t = shape.nearestTime(p);
+ shape *= tr;
+ ac = shape;
+ ac *= m;
+ bool_ = (shape == shape);
+ bool_ = (shape != other);
+ bool_ = shape.isDegenerate();
+ //bool_ = are_near(shape, other, c);
+ }
+};
+
+template <typename T>
+inline T portion(const T& t, const Interval& i) { return portion(t, i.min(), i.max()); }
+
+template <typename T>
+struct EqualityComparableConcept {
+ T a, b;
+ bool bool_;
+ void constraints() {
+ bool_ = (a == b);
+ bool_ = (a != b);
+ }
+};
+
+template <typename T>
+struct NearConcept {
+ T a, b;
+ double tol;
+ bool res;
+ void constraints() {
+ res = are_near(a, b, tol);
+ }
+};
+
+template <typename T>
+struct OffsetableConcept {
+ T t;
+ typename T::output_type d;
+ void constraints() {
+ t = t + d; t += d;
+ t = t - d; t -= d;
+ }
+};
+
+template <typename T>
+struct ScalableConcept {
+ T t;
+ typename T::output_type d;
+ void constraints() {
+ t = -t;
+ t = t * d; t *= d;
+ t = t / d; t /= d;
+ }
+};
+
+template <typename T>
+struct AddableConcept {
+ T i, j;
+ void constraints() {
+ i += j; i = i + j;
+ i -= j; i = i - j;
+ }
+};
+
+template <typename T>
+struct MultiplicableConcept {
+ T i, j;
+ void constraints() {
+ i *= j; i = i * j;
+ }
+};
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_CONCEPTS_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/include/2geom/conic_section_clipper.h b/include/2geom/conic_section_clipper.h
new file mode 100644
index 0000000..38bba33
--- /dev/null
+++ b/include/2geom/conic_section_clipper.h
@@ -0,0 +1,58 @@
+/** @file
+ * @brief Conic section clipping with respect to a rectangle
+ *//*
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail>
+ *
+ * Copyright 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 LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_H
+#define LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_H
+
+
+#undef CLIP_WITH_CAIRO_SUPPORT
+#include <2geom/conic_section_clipper_impl.h>
+
+
+#endif // _2GEOM_CONIC_SECTION_CLIPPER_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/include/2geom/conic_section_clipper_cr.h b/include/2geom/conic_section_clipper_cr.h
new file mode 100644
index 0000000..6c62494
--- /dev/null
+++ b/include/2geom/conic_section_clipper_cr.h
@@ -0,0 +1,64 @@
+/** @file
+ * @brief Conic section clipping with respect to a rectangle
+ *//*
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail>
+ *
+ * Copyright 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.
+ */
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// This header should be used for graphical debugging purpuse only. //
+////////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_CR_H
+#define LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_CR_H
+
+
+#define CLIP_WITH_CAIRO_SUPPORT
+#include "conic_section_clipper_impl.h"
+#include "conic_section_clipper_impl.cpp"
+
+
+#endif // _2GEOM_CONIC_SECTION_CLIPPER_CR_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/include/2geom/conic_section_clipper_impl.h b/include/2geom/conic_section_clipper_impl.h
new file mode 100644
index 0000000..ee67df1
--- /dev/null
+++ b/include/2geom/conic_section_clipper_impl.h
@@ -0,0 +1,346 @@
+/** @file
+ * @brief Conic section clipping with respect to a rectangle
+ *//*
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail>
+ *
+ * Copyright 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 LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_IMPL_H
+#define LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_IMPL_H
+
+
+#include <2geom/conicsec.h>
+#include <2geom/line.h>
+
+#include <list>
+#include <map>
+
+
+
+#ifdef CLIP_WITH_CAIRO_SUPPORT
+ #include <2geom/toys/path-cairo.h>
+ #define CLIPPER_CLASS clipper_cr
+#else
+ #define CLIPPER_CLASS clipper
+#endif
+
+//#define CLIPDBG
+
+#ifdef CLIPDBG
+#include <2geom/toys/path-cairo.h>
+#define DBGINFO(msg) \
+ std::cerr << msg << std::endl;
+#define DBGPRINT(msg, var) \
+ std::cerr << msg << var << std::endl;
+#define DBGPRINTIF(cond, msg, var) \
+ if (cond) \
+ std::cerr << msg << var << std::endl;
+
+#define DBGPRINT2(msg1, var1, msg2, var2) \
+ std::cerr << msg1 << var1 << msg2 << var2 << std::endl;
+
+#define DBGPRINTCOLL(msg, coll) \
+ if (coll.size() != 0) \
+ std::cerr << msg << ":\n"; \
+ for (size_t i = 0; i < coll.size(); ++i) \
+ { \
+ std::cerr << i << ": " << coll[i] << "\n"; \
+ }
+
+#else
+#define DBGINFO(msg)
+#define DBGPRINT(msg, var)
+#define DBGPRINTIF(cond, msg, var)
+#define DBGPRINT2(msg1, var1, msg2, var2)
+#define DBGPRINTCOLL(msg, coll)
+#endif
+
+
+
+
+namespace Geom
+{
+
+class CLIPPER_CLASS
+{
+
+ public:
+
+#ifdef CLIP_WITH_CAIRO_SUPPORT
+ clipper_cr (cairo_t* _cr, const xAx & _cs, const Rect & _R)
+ : cr(_cr), cs(_cs), R(_R)
+ {
+ DBGPRINT ("CLIP: right side: ", R.right())
+ DBGPRINT ("CLIP: top side: ", R.top())
+ DBGPRINT ("CLIP: left side: ", R.left())
+ DBGPRINT ("CLIP: bottom side: ", R.bottom())
+ }
+#else
+ clipper (const xAx & _cs, const Rect & _R)
+ : cs(_cs), R(_R)
+ {
+ }
+#endif
+
+ bool clip (std::vector<RatQuad> & arcs);
+
+ bool found_any_isolated_point() const
+ {
+ return ( !single_points.empty() );
+ }
+
+ const std::vector<Point> & isolated_points() const
+ {
+ return single_points;
+ }
+
+
+ private:
+ bool intersect (std::vector<Point> & crossing_points) const;
+
+ bool are_paired (Point & M, const Point & P1, const Point & P2) const;
+ void pairing (std::vector<Point> & paired_points,
+ std::vector<Point> & inner_points,
+ const std::vector<Point> & crossing_points);
+
+ Point find_inner_point_by_bisector_line (const Point & P,
+ const Point & Q) const;
+ Point find_inner_point (const Point & P, const Point & Q) const;
+
+ std::list<Point>::iterator split (std::list<Point> & points,
+ std::list<Point>::iterator sp,
+ std::list<Point>::iterator fp) const;
+ void rsplit (std::list<Point> & points,
+ std::list<Point>::iterator sp,
+ std::list<Point>::iterator fp,
+ size_t k) const;
+
+ void rsplit (std::list<Point> & points,
+ std::list<Point>::iterator sp,
+ std::list<Point>::iterator fp,
+ double length) const;
+
+ private:
+#ifdef CLIP_WITH_CAIRO_SUPPORT
+ cairo_t* cr;
+#endif
+ const xAx & cs;
+ const Rect & R;
+ std::vector<Point> single_points;
+};
+
+
+
+
+/*
+ * Given two point "P", "Q" on the conic section the method computes
+ * a third point inner to the arc with end-point "P", "Q".
+ * The new point is found by intersecting the conic with the bisector line
+ * of the PQ line segment.
+ */
+inline
+Point CLIPPER_CLASS::find_inner_point_by_bisector_line (const Point & P,
+ const Point & Q) const
+{
+ DBGPRINT ("CLIP: find_inner_point_by_bisector_line: P = ", P)
+ DBGPRINT ("CLIP: find_inner_point_by_bisector_line: Q = ", Q)
+ Line bl = make_bisector_line (LineSegment (P, Q));
+ std::vector<double> rts = cs.roots (bl);
+ //DBGPRINT ("CLIP: find_inner_point: rts.size = ", rts.size())
+ double t;
+ if (rts.size() == 0)
+ {
+ THROW_LOGICALERROR ("clipper::find_inner_point_by_bisector_line: "
+ "no conic-bisector line intersection point");
+ }
+ if (rts.size() == 2)
+ {
+ // we suppose that the searched point is the nearest
+ // to the line segment PQ
+ t = (std::fabs(rts[0]) < std::fabs(rts[1])) ? rts[0] : rts[1];
+ }
+ else
+ {
+ t = rts[0];
+ }
+ return bl.pointAt (t);
+}
+
+
+/*
+ * Given two point "P", "Q" on the conic section the method computes
+ * a third point inner to the arc with end-point "P", "Q".
+ * The new point is found by intersecting the conic with the line
+ * passing through the middle point of the PQ line segment and
+ * the intersection point of the tangent lines at points P and Q.
+ */
+inline
+Point CLIPPER_CLASS::find_inner_point (const Point & P, const Point & Q) const
+{
+
+ Line l1 = cs.tangent (P);
+ Line l2 = cs.tangent (Q);
+ Line l;
+ // in case we fail to find a crossing point we fall back to the bisector
+ // method
+ try
+ {
+ OptCrossing oc = intersection(l1, l2);
+ if (!oc)
+ {
+ return find_inner_point_by_bisector_line (P, Q);
+ }
+ l.setPoints (l1.pointAt (oc->ta), middle_point (P, Q));
+ }
+ catch (Geom::InfiniteSolutions const &e)
+ {
+ return find_inner_point_by_bisector_line (P, Q);
+ }
+
+ std::vector<double> rts = cs.roots (l);
+ double t;
+ if (rts.size() == 0)
+ {
+ return find_inner_point_by_bisector_line (P, Q);
+ }
+ // the line "l" origin is set to the tangent crossing point so in case
+ // we find two intersection points only the nearest belongs to the given arc
+ // pay attention: in case we are dealing with an hyperbola (remember that
+ // end points are on the same branch, because they are paired) the tangent
+ // crossing point belongs to the angle delimited by hyperbola asymptotes
+ // and containing the given hyperbola branch, so the previous statement is
+ // still true
+ if (rts.size() == 2)
+ {
+ t = (std::fabs(rts[0]) < std::fabs(rts[1])) ? rts[0] : rts[1];
+ }
+ else
+ {
+ t = rts[0];
+ }
+ return l.pointAt (t);
+}
+
+
+/*
+ * Given a list of points on the conic section, and given two consecutive
+ * points belonging to the list and passed by two list iterators, the method
+ * finds a new point that is inner to the conic arc which has the two passed
+ * points as initial and final point. This new point is inserted into the list
+ * between the two passed points and an iterator pointing to the new point
+ * is returned.
+ */
+inline
+std::list<Point>::iterator CLIPPER_CLASS::split (std::list<Point> & points,
+ std::list<Point>::iterator sp,
+ std::list<Point>::iterator fp) const
+{
+ Point new_point = find_inner_point (*sp, *fp);
+ std::list<Point>::iterator ip = points.insert (fp, new_point);
+ //std::cerr << "CLIP: split: [" << *sp << ", " << *ip << ", "
+ // << *fp << "]" << std::endl;
+ return ip;
+}
+
+
+/*
+ * Given a list of points on the conic section, and given two consecutive
+ * points belonging to the list and passed by two list iterators, the method
+ * recursively finds new points that are inner to the conic arc which has
+ * the two passed points as initial and final point. The recursion stop after
+ * "k" recursive calls. These new points are inserted into the list between
+ * the two passed points, and in the order we cross them going from
+ * the initial to the final arc point.
+ */
+inline
+void CLIPPER_CLASS::rsplit (std::list<Point> & points,
+ std::list<Point>::iterator sp,
+ std::list<Point>::iterator fp,
+ size_t k) const
+{
+ if (k == 0)
+ {
+ //DBGINFO("CLIP: split: no further split")
+ return;
+ }
+
+ std::list<Point>::iterator ip = split (points, sp, fp);
+ --k;
+ rsplit (points, sp, ip, k);
+ rsplit (points, ip, fp, k);
+}
+
+
+/*
+ * Given a list of points on the conic section, and given two consecutive
+ * points belonging to the list and passed by two list iterators, the method
+ * recursively finds new points that are inner to the conic arc which has
+ * the two passed points as initial and final point. The recursion stop when
+ * the max distance between the new computed inner point and the two passed
+ * arc end-points is less then the value specified by the "length" parameter.
+ * These new points are inserted into the list between the two passed points,
+ * and in the order we cross them going from the initial to the final arc point.
+ */
+inline
+void CLIPPER_CLASS::rsplit (std::list<Point> & points,
+ std::list<Point>::iterator sp,
+ std::list<Point>::iterator fp,
+ double length) const
+{
+ std::list<Point>::iterator ip = split (points, sp, fp);
+ double d1 = distance (*sp, *ip);
+ double d2 = distance (*ip, *fp);
+ double mdist = std::max (d1, d2);
+
+ if (mdist < length)
+ {
+ //DBGINFO("CLIP: split: no further split")
+ return;
+ }
+
+ // they have to be called both to keep the number of points in the list
+ // in the form 2k+1 where k are the sub-arcs the initial arc is split in.
+ rsplit (points, sp, ip, length);
+ rsplit (points, ip, fp, length);
+}
+
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_IMPL_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/include/2geom/conicsec.h b/include/2geom/conicsec.h
new file mode 100644
index 0000000..bfd5f36
--- /dev/null
+++ b/include/2geom/conicsec.h
@@ -0,0 +1,537 @@
+/** @file
+ * @brief Conic Section
+ *//*
+ * Authors:
+ * Nathan Hurst <njh@njhurst.com>
+ *
+ * Copyright 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 LIB2GEOM_SEEN_CONICSEC_H
+#define LIB2GEOM_SEEN_CONICSEC_H
+
+#include <2geom/exception.h>
+#include <2geom/angle.h>
+#include <2geom/rect.h>
+#include <2geom/affine.h>
+#include <2geom/point.h>
+#include <2geom/line.h>
+#include <2geom/bezier-curve.h>
+#include <2geom/numeric/linear_system.h>
+#include <2geom/numeric/symmetric-matrix-fs.h>
+#include <2geom/numeric/symmetric-matrix-fs-operation.h>
+
+#include <optional>
+
+#include <string>
+#include <vector>
+#include <ostream>
+
+
+
+
+namespace Geom
+{
+
+class RatQuad{
+ /**
+ * A curve of the form B02*A + B12*B*w + B22*C/(B02 + B12*w + B22)
+ * These curves can exactly represent a piece conic section less than a certain angle (find out)
+ *
+ **/
+
+public:
+ Point P[3];
+ double w;
+ RatQuad() {}
+ RatQuad(Point a, Point b, Point c, double w) : w(w) {
+ P[0] = a;
+ P[1] = b;
+ P[2] = c;
+ }
+ double lambda() const;
+
+ static RatQuad fromPointsTangents(Point P0, Point dP0,
+ Point P,
+ Point P2, Point dP2);
+ static RatQuad circularArc(Point P0, Point P1, Point P2);
+
+ CubicBezier toCubic() const;
+ CubicBezier toCubic(double lam) const;
+
+ Point pointAt(double t) const;
+ Point at0() const {return P[0];}
+ Point at1() const {return P[2];}
+
+ void split(RatQuad &a, RatQuad &b) const;
+
+ D2<SBasis> hermite() const;
+ std::vector<SBasis> homogeneous() const;
+};
+
+
+
+
+class xAx{
+public:
+ double c[6];
+
+ enum kind_t
+ {
+ PARABOLA,
+ CIRCLE,
+ REAL_ELLIPSE,
+ IMAGINARY_ELLIPSE,
+ RECTANGULAR_HYPERBOLA,
+ HYPERBOLA,
+ DOUBLE_LINE,
+ TWO_REAL_PARALLEL_LINES,
+ TWO_IMAGINARY_PARALLEL_LINES,
+ TWO_REAL_CROSSING_LINES,
+ TWO_IMAGINARY_CROSSING_LINES, // crossing at a real point
+ SINGLE_POINT = TWO_IMAGINARY_CROSSING_LINES,
+ UNKNOWN
+ };
+
+
+ xAx() {}
+
+ /*
+ * Define the conic section by its algebraic equation coefficients
+ *
+ * c0, .., c5: equation coefficients
+ */
+ xAx (double c0, double c1, double c2, double c3, double c4, double c5)
+ {
+ set (c0, c1, c2, c3, c4, c5);
+ }
+
+ /*
+ * Define a conic section by its related symmetric matrix
+ */
+ xAx (const NL::ConstSymmetricMatrixView<3> & C)
+ {
+ set(C);
+ }
+
+ /*
+ * Define a conic section by computing the one that fits better with
+ * N points.
+ *
+ * points: points to fit
+ *
+ * precondition: there must be at least 5 non-overlapping points
+ */
+ xAx (std::vector<Point> const& points)
+ {
+ set (points);
+ }
+
+ /*
+ * Define a section conic by providing the coordinates of one of its
+ * vertex,the major axis inclination angle and the coordinates of its foci
+ * with respect to the unidimensional system defined by the major axis with
+ * origin set at the provided vertex.
+ *
+ * _vertex : section conic vertex V
+ * _angle : section conic major axis angle
+ * _dist1: +/-distance btw V and nearest focus
+ * _dist2: +/-distance btw V and farest focus
+ *
+ * prerequisite: _dist1 <= _dist2
+ */
+ xAx (const Point& _vertex, double _angle, double _dist1, double _dist2)
+ {
+ set (_vertex, _angle, _dist1, _dist2);
+ }
+
+ /*
+ * Define a conic section by providing one of its vertex and its foci.
+ *
+ * _vertex: section conic vertex
+ * _focus1: section conic focus
+ * _focus2: section conic focus
+ */
+ xAx (const Point& _vertex, const Point& _focus1, const Point& _focus2)
+ {
+ set(_vertex, _focus1, _focus2);
+ }
+
+ /*
+ * Define a conic section by passing a focus, the related directrix,
+ * and the eccentricity (e)
+ * (e < 1 -> ellipse; e = 1 -> parabola; e > 1 -> hyperbola)
+ *
+ * _focus: a focus of the conic section
+ * _directrix: the directrix related to the given focus
+ * _eccentricity: the eccentricity parameter of the conic section
+ */
+ xAx (const Point & _focus, const Line & _directrix, double _eccentricity)
+ {
+ set (_focus, _directrix, _eccentricity);
+ }
+
+ /*
+ * Made up a degenerate conic section as a pair of lines
+ *
+ * l1, l2: lines that made up the conic section
+ */
+ xAx (const Line& l1, const Line& l2)
+ {
+ set (l1, l2);
+ }
+
+ /*
+ * Define the conic section by its algebraic equation coefficients
+ * c0, ..., c5: equation coefficients
+ */
+ void set (double c0, double c1, double c2, double c3, double c4, double c5)
+ {
+ c[0] = c0; c[1] = c1; c[2] = c2; // xx, xy, yy
+ c[3] = c3; c[4] = c4; // x, y
+ c[5] = c5; // 1
+ }
+
+ /*
+ * Define a conic section by its related symmetric matrix
+ */
+ void set (const NL::ConstSymmetricMatrixView<3> & C)
+ {
+ set(C(0,0), 2*C(1,0), C(1,1), 2*C(2,0), 2*C(2,1), C(2,2));
+ }
+
+ void set (std::vector<Point> const& points);
+
+ void set (const Point& _vertex, double _angle, double _dist1, double _dist2);
+
+ void set (const Point& _vertex, const Point& _focus1, const Point& _focus2);
+
+ void set (const Point & _focus, const Line & _directrix, double _eccentricity);
+
+ void set (const Line& l1, const Line& l2);
+
+
+ static xAx fromPoint(Point p);
+ static xAx fromDistPoint(Point p, double d);
+ static xAx fromLine(Point n, double d);
+ static xAx fromLine(Line l);
+ static xAx fromPoints(std::vector<Point> const &pts);
+
+
+ template<typename T>
+ T evaluate_at(T x, T y) const {
+ return c[0]*x*x + c[1]*x*y + c[2]*y*y + c[3]*x + c[4]*y + c[5];
+ }
+
+ double valueAt(Point P) const;
+
+ std::vector<double> implicit_form_coefficients() const {
+ return std::vector<double>(c, c+6);
+ }
+
+ template<typename T>
+ T evaluate_at(T x, T y, T w) const {
+ return c[0]*x*x + c[1]*x*y + c[2]*y*y + c[3]*x*w + c[4]*y*w + c[5]*w*w;
+ }
+
+ xAx scale(double sx, double sy) const;
+
+ Point gradient(Point p) const;
+
+ xAx operator-(xAx const &b) const;
+ xAx operator+(xAx const &b) const;
+ xAx operator+(double const &b) const;
+ xAx operator*(double const &b) const;
+
+ std::vector<Point> crossings(Rect r) const;
+ std::optional<RatQuad> toCurve(Rect const & bnd) const;
+ std::vector<double> roots(Point d, Point o) const;
+
+ std::vector<double> roots(Line const &l) const;
+
+ static Interval quad_ex(double a, double b, double c, Interval ivl);
+
+ Geom::Affine hessian() const;
+
+ std::optional<Point> bottom() const;
+
+ Interval extrema(Rect r) const;
+
+
+ /*
+ * Return the symmetric matrix related to the conic section.
+ * Modifying the matrix does not modify the conic section
+ */
+ NL::SymmetricMatrix<3> get_matrix() const
+ {
+ NL::SymmetricMatrix<3> C(c);
+ C(1,0) *= 0.5; C(2,0) *= 0.5; C(2,1) *= 0.5;
+ return C;
+ }
+
+ /*
+ * Return the i-th coefficient of the conic section algebraic equation
+ * Modifying the returned value does not modify the conic section coefficient
+ */
+ double coeff (size_t i) const
+ {
+ return c[i];
+ }
+
+ /*
+ * Return the i-th coefficient of the conic section algebraic equation
+ * Modifying the returned value modifies the conic section coefficient
+ */
+ double& coeff (size_t i)
+ {
+ return c[i];
+ }
+
+ kind_t kind () const;
+
+ std::string categorise() const;
+
+ /*
+ * Return true if the equation:
+ * c0*x^2 + c1*xy + c2*y^2 + c3*x + c4*y +c5 == 0
+ * really defines a conic, false otherwise
+ */
+ bool is_quadratic() const
+ {
+ return (coeff(0) != 0 || coeff(1) != 0 || coeff(2) != 0);
+ }
+
+ /*
+ * Return true if the conic is degenerate, i.e. if the related matrix
+ * determinant is null, false otherwise
+ */
+ bool isDegenerate() const
+ {
+ return (det_sgn (get_matrix()) == 0);
+ }
+
+ /*
+ * Compute the centre of symmetry of the conic section when it exists,
+ * else it return an uninitialized std::optional<Point> instance.
+ */
+ std::optional<Point> centre() const
+ {
+ typedef std::optional<Point> opt_point_t;
+
+ double d = coeff(1) * coeff(1) - 4 * coeff(0) * coeff(2);
+ if (are_near (d, 0)) return opt_point_t();
+ NL::Matrix Q(2, 2);
+ Q(0,0) = coeff(0);
+ Q(1,1) = coeff(2);
+ Q(0,1) = Q(1,0) = coeff(1) * 0.5;
+ NL::Vector T(2);
+ T[0] = - coeff(3) * 0.5;
+ T[1] = - coeff(4) * 0.5;
+
+ NL::LinearSystem ls (Q, T);
+ NL::Vector sol = ls.SV_solve();
+ Point C;
+ C[0] = sol[0];
+ C[1] = sol[1];
+
+ return opt_point_t(C);
+ }
+
+ double axis_angle() const;
+
+ void roots (std::vector<double>& sol, Coord v, Dim2 d) const;
+
+ xAx translate (const Point & _offset) const;
+
+ xAx rotate (double angle) const;
+
+ /*
+ * Rotate the conic section by the given angle wrt the provided point.
+ *
+ * _rot_centre: the rotation centre
+ * _angle: the rotation angle
+ */
+ xAx rotate (const Point & _rot_centre, double _angle) const
+ {
+ xAx result
+ = translate (-_rot_centre).rotate (_angle).translate (_rot_centre);
+ return result;
+ }
+
+ /*
+ * Compute the tangent line of the conic section at the provided point
+ *
+ * _point: the conic section point the tangent line pass through
+ */
+ Line tangent (const Point & _point) const
+ {
+ NL::Vector pp(3);
+ pp[0] = _point[0]; pp[1] = _point[1]; pp[2] = 1;
+ NL::SymmetricMatrix<3> C = get_matrix();
+ NL::Vector line = C * pp;
+ return Line(line[0], line[1], line[2]);
+ }
+
+ /*
+ * For a non degenerate conic compute the dual conic.
+ * TODO: investigate degenerate case
+ */
+ xAx dual () const
+ {
+ //assert (! isDegenerate());
+ NL::SymmetricMatrix<3> C = get_matrix();
+ NL::SymmetricMatrix<3> D = adj(C);
+ xAx dc(D);
+ return dc;
+ }
+
+ bool decompose (Line& l1, Line& l2) const;
+
+ /**
+ * @brief Division-free decomposition of a degenerate conic section, without
+ * degeneration test.
+ *
+ * When the conic is degenerate, it consists of 0, 1 or 2 lines in the xy-plane.
+ * This function returns these lines. But it does not check whether the conic
+ * is really degenerate, so calling it on a non-degenerate conic produces a
+ * meaningless result.
+ *
+ * If the number of lines is less than two, the trailing lines in the returned
+ * array will be degenerate. Use Line::isDegenerate() to test for that.
+ *
+ * This version of the decomposition is division-free, which improves numerical
+ * stability compared to decompose().
+ *
+ * @param epsilon The numerical threshold for floating point comparison of discriminants.
+ */
+ std::array<Line, 2> decompose_df(Coord epsilon = EPSILON) const;
+
+ /*
+ * Generate a RatQuad object from a conic arc.
+ *
+ * p0: the initial point of the arc
+ * p1: the inner point of the arc
+ * p2: the final point of the arc
+ */
+ RatQuad toRatQuad (const Point & p0,
+ const Point & p1,
+ const Point & p2) const
+ {
+ Point dp0 = gradient (p0);
+ Point dp2 = gradient (p2);
+ return
+ RatQuad::fromPointsTangents (p0, rot90 (dp0), p1, p2, rot90 (dp2));
+ }
+
+ /*
+ * Return the angle related to the normal gradient computed at the passed
+ * point.
+ *
+ * _point: the point at which computes the angle
+ *
+ * prerequisite: the passed point must lie on the conic
+ */
+ double angle_at (const Point & _point) const
+ {
+ double angle = atan2 (gradient (_point));
+ if (angle < 0) angle += (2*M_PI);
+ return angle;
+ }
+
+ /*
+ * Return true if the given point is contained in the conic arc determined
+ * by the passed points.
+ *
+ * _point: the point to be tested
+ * _initial: the initial point of the arc
+ * _inner: an inner point of the arc
+ * _final: the final point of the arc
+ *
+ * prerequisite: the passed points must lie on the conic, the inner point
+ * has to be strictly contained in the arc, except when the
+ * initial and final points are equal: in such a case if the
+ * inner point is also equal to them, then they define an arc
+ * made up by a single point.
+ *
+ */
+ bool arc_contains (const Point & _point, const Point & _initial,
+ const Point & _inner, const Point & _final) const
+ {
+ AngleInterval ai(angle_at(_initial), angle_at(_inner), angle_at(_final));
+ return ai.contains(angle_at(_point));
+ }
+
+ Rect arc_bound (const Point & P1, const Point & Q, const Point & P2) 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 nearestTime (const Point &P) const
+ {
+ std::vector<Point> points = allNearestTimes (P);
+ if ( !points.empty() )
+ {
+ return points.front();
+ }
+ // else
+ THROW_LOGICALERROR ("nearestTime: no nearest point found");
+ return Point();
+ }
+
+};
+
+std::vector<Point> intersect(const xAx & C1, const xAx & C2);
+
+bool clip (std::vector<RatQuad> & rq, const xAx & cs, const Rect & R);
+
+inline std::ostream &operator<< (std::ostream &out_file, const xAx &x) {
+ for(double i : x.c) {
+ out_file << i << ", ";
+ }
+ return out_file;
+}
+
+};
+
+
+#endif // LIB2GEOM_SEEN_CONICSEC_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/include/2geom/convex-hull.h b/include/2geom/convex-hull.h
new file mode 100644
index 0000000..4dff9c2
--- /dev/null
+++ b/include/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 <optional>
+#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 returned beginning with 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) {
+ std::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(auto i : in_cvx) {
+ out_file << 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/include/2geom/coord.h b/include/2geom/coord.h
new file mode 100644
index 0000000..40db84e
--- /dev/null
+++ b/include/2geom/coord.h
@@ -0,0 +1,208 @@
+/** @file
+ * @brief Integral and real coordinate types and some basic utilities
+ *//*
+ * Authors:
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * 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
+ * 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_COORD_H
+#define LIB2GEOM_SEEN_COORD_H
+
+#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).
+ * @ingroup Primitives */
+enum Dim2 { X=0, Y=1 };
+
+/** @brief Get the other (perpendicular) dimension.
+ * @ingroup Primitives */
+inline constexpr Dim2 other_dimension(Dim2 d) { return Dim2(int(d) ^ 1); }
+
+// TODO: make a smarter implementation with C++11
+template <typename T>
+struct D2Traits {
+ using D1Value = typename T::D1Value;
+ using D1Reference = typename T::D1Reference;
+ using D1ConstReference = typename T::D1ConstReference;
+};
+
+/** @brief Axis extraction functor.
+ * For use with things such as Boost's transform_iterator.
+ * @ingroup Utilities */
+template <Dim2 D, typename T>
+struct GetAxis {
+ using result_type = typename D2Traits<T>::D1Value;
+ using argument_type = T;
+ typename D2Traits<T>::D1Value operator()(T const &a) const {
+ return a[D];
+ }
+};
+
+/** @brief Floating point type used to store coordinates.
+ * @ingroup Primitives */
+using Coord = double;
+
+/** @brief Type used for integral coordinates.
+ * @ingroup Primitives */
+using IntCoord = int;
+
+/** @brief Default "acceptably small" value.
+ * @ingroup Primitives */
+constexpr Coord EPSILON = 1e-6;
+
+/** @brief Get a value representing infinity.
+ * @ingroup Primitives */
+inline constexpr Coord infinity() { return std::numeric_limits<Coord>::infinity(); }
+
+/** @brief Nearness predicate for values.
+ * @ingroup Primitives */
+inline constexpr bool are_near(Coord a, Coord b, double eps=EPSILON) { return std::abs(a-b) <= eps; }
+inline constexpr bool rel_error_bound(Coord a, Coord b, double eps=EPSILON) { return std::abs(a) <= eps*b; }
+
+/** @brief Numerically stable linear interpolation.
+ * @ingroup Primitives */
+inline constexpr Coord lerp(Coord t, Coord a, Coord b) {
+ return (1 - t) * a + t * b;
+}
+
+/** @brief Traits class used with coordinate types.
+ * Defines point, interval and rectangle types for the given coordinate type.
+ * @ingroup Utilities */
+template <typename C>
+struct CoordTraits {
+ using PointType = D2<C>;
+ using IntervalType = GenericInterval<C>;
+ using OptIntervalType = GenericOptInterval<C>;
+ using RectType = GenericRect<C>;
+ using OptRectType = GenericOptRect<C>;
+
+ using IntervalOps =
+ boost::equality_comparable< IntervalType
+ , boost::orable< IntervalType
+ >>;
+
+ using RectOps =
+ boost::equality_comparable< RectType
+ , boost::orable< RectType
+ , boost::orable< RectType, OptRectType
+ >>>;
+};
+
+// NOTE: operator helpers for Rect and Interval are defined here.
+// This is to avoid increasing their size through multiple inheritance.
+
+template<>
+struct CoordTraits<IntCoord> {
+ using PointType = IntPoint;
+ using IntervalType = IntInterval;
+ using OptIntervalType = OptIntInterval;
+ using RectType = IntRect;
+ using OptRectType = OptIntRect;
+
+ using IntervalOps =
+ boost::equality_comparable< IntInterval
+ , boost::additive< IntInterval
+ , boost::additive< IntInterval, IntCoord
+ , boost::orable< IntInterval
+ >>>>;
+
+ using RectOps =
+ boost::equality_comparable< IntRect
+ , boost::orable< IntRect
+ , boost::orable< IntRect, OptIntRect
+ , boost::additive< IntRect, IntPoint
+ >>>>;
+};
+
+template<>
+struct CoordTraits<Coord> {
+ using PointType = Point;
+ using IntervalType = Interval;
+ using OptIntervalType = OptInterval;
+ using RectType = Rect;
+ using OptRectType = OptRect;
+
+ using IntervalOps =
+ boost::equality_comparable< Interval
+ , boost::equality_comparable< Interval, IntInterval
+ , boost::additive< Interval
+ , boost::multipliable< Interval
+ , boost::orable< Interval
+ , boost::arithmetic< Interval, Coord
+ >>>>>>;
+
+ using RectOps =
+ boost::equality_comparable< Rect
+ , boost::equality_comparable< Rect, IntRect
+ , boost::orable< Rect
+ , boost::orable< Rect, OptRect
+ , boost::additive< Rect, Point
+ , boost::multipliable< Rect, Affine
+ >>>>>>;
+};
+
+/** @brief Convert coordinate to shortest possible string.
+ * @return The shortest string that parses back to the original value.
+ * @relates Coord */
+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.
+ * @relates Coord */
+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.
+ * @relates Coord */
+Coord parse_coord(std::string const &s);
+
+} // namespace Geom
+
+#endif // LIB2GEOM_SEEN_COORD_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/include/2geom/crossing.h b/include/2geom/crossing.h
new file mode 100644
index 0000000..7ca273b
--- /dev/null
+++ b/include/2geom/crossing.h
@@ -0,0 +1,213 @@
+/**
+ * @file
+ * @brief Structure representing the intersection of two curves
+ *//*
+ * Authors:
+ * Michael Sloan <mgsloan@gmail.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2006-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.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_CROSSING_H
+#define LIB2GEOM_SEEN_CROSSING_H
+
+#include <vector>
+#include <2geom/rect.h>
+#include <2geom/sweep-bounds.h>
+#include <optional>
+#include <2geom/pathvector.h>
+
+namespace Geom {
+
+//Crossing between one or two paths
+struct Crossing {
+ bool dir; //True: along a, a becomes outside.
+ double ta, tb; //time on a and b of crossing
+ unsigned a, b; //storage of indices
+ Crossing() : dir(false), ta(0), tb(1), a(0), b(1) {}
+ Crossing(double t_a, double t_b, bool direction) : dir(direction), ta(t_a), tb(t_b), a(0), b(1) {}
+ Crossing(double t_a, double t_b, unsigned ai, unsigned bi, bool direction) : dir(direction), ta(t_a), tb(t_b), a(ai), b(bi) {}
+ bool operator==(const Crossing & other) const { return a == other.a && b == other.b && dir == other.dir && ta == other.ta && tb == other.tb; }
+ bool operator!=(const Crossing & other) const { return !(*this == other); }
+
+ unsigned getOther(unsigned cur) const { return a == cur ? b : a; }
+ double getTime(unsigned cur) const { return a == cur ? ta : tb; }
+ double getOtherTime(unsigned cur) const { return a == cur ? tb : ta; }
+ bool onIx(unsigned ix) const { return a == ix || b == ix; }
+};
+
+typedef std::optional<Crossing> OptCrossing;
+
+
+/*
+struct Edge {
+ unsigned node, path;
+ double time;
+ bool reverse;
+ Edge(unsigned p, double t, bool r) : path(p), time(t), reverse(r) {}
+ bool operator==(Edge const &other) const { return other.path == path && other.time == time && other.reverse == reverse; }
+};
+
+struct CrossingNode {
+ std::vector<Edge> edges;
+ CrossingNode() : edges(std::vector<Edge>()) {}
+ explicit CrossingNode(std::vector<Edge> es) : edges(es) {}
+ void add_edge(Edge const &e) {
+ if(std::find(edges.begin(), edges.end(), e) == edges.end())
+ edges.push_back(e);
+ }
+ double time_on(unsigned p) {
+ for(unsigned i = 0; i < edges.size(); i++)
+ if(edges[i].path == p) return edges[i].time;
+ std::cout << "CrossingNode time_on failed\n";
+ return 0;
+ }
+};
+
+
+typedef std::vector<CrossingNode> CrossingGraph;
+
+struct TimeOrder {
+ bool operator()(Edge a, Edge b) {
+ return a.time < b.time;
+ }
+};
+
+class Path;
+CrossingGraph create_crossing_graph(PathVector const &p, Crossings const &crs);
+*/
+
+/*inline bool are_near(Crossing a, Crossing b) {
+ return are_near(a.ta, b.ta) && are_near(a.tb, b.tb);
+}
+
+struct NearF { bool operator()(Crossing a, Crossing b) { return are_near(a, b); } };
+*/
+
+struct CrossingOrder {
+ unsigned ix;
+ bool rev;
+ CrossingOrder(unsigned i, bool r = false) : ix(i), rev(r) {}
+ bool operator()(Crossing a, Crossing b) {
+ if(rev)
+ return (ix == a.a ? a.ta : a.tb) <
+ (ix == b.a ? b.ta : b.tb);
+ else
+ return (ix == a.a ? a.ta : a.tb) >
+ (ix == b.a ? b.ta : b.tb);
+ }
+};
+
+typedef std::vector<Crossing> Crossings;
+
+typedef std::vector<Crossings> CrossingSet;
+
+template<typename C>
+std::vector<Rect> bounds(C const &a) {
+ std::vector<Rect> rs;
+ for (unsigned i = 0; i < a.size(); i++) {
+ OptRect bb = a[i].boundsFast();
+ if (bb) {
+ rs.push_back(*bb);
+ }
+ }
+ return rs;
+}
+// provide specific method for Paths because paths can be closed or open. Path::size() is named somewhat wrong...
+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(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));
+ for(unsigned i = 0; i < cull.size(); i++) {
+ for(unsigned jx = 0; jx < cull[i].size(); jx++) {
+ unsigned j = cull[i][jx];
+ unsigned jc = j + a.size();
+ Crossings cr = crossings(a[i], b[j]);
+ for(auto & k : cr) { k.a = i; k.b = jc; }
+
+ //Sort & add A-sorted crossings
+ sort_crossings(cr, i);
+ Crossings n(results[i].size() + cr.size());
+ std::merge(results[i].begin(), results[i].end(), cr.begin(), cr.end(), n.begin(), CrossingOrder(i));
+ results[i] = n;
+
+ //Sort & add B-sorted crossings
+ sort_crossings(cr, jc);
+ n.resize(results[jc].size() + cr.size());
+ std::merge(results[jc].begin(), results[jc].end(), cr.begin(), cr.end(), n.begin(), CrossingOrder(jc));
+ results[jc] = n;
+ }
+ }
+ return results;
+ }
+};
+void merge_crossings(Crossings &a, Crossings &b, unsigned i);
+void offset_crossings(Crossings &cr, double a, double b);
+
+Crossings reverse_ta(Crossings const &cr, std::vector<double> max);
+Crossings reverse_tb(Crossings const &cr, unsigned split, std::vector<double> max);
+CrossingSet reverse_ta(CrossingSet const &cr, unsigned split, std::vector<double> max);
+CrossingSet reverse_tb(CrossingSet const &cr, unsigned split, std::vector<double> max);
+
+void clean(Crossings &cr_a, Crossings &cr_b);
+void delete_duplicates(Crossings &crs);
+
+} // end namespace Geom
+
+#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/include/2geom/curve.h b/include/2geom/curve.h
new file mode 100644
index 0000000..9519144
--- /dev/null
+++ b/include/2geom/curve.h
@@ -0,0 +1,375 @@
+/**
+ * \file
+ * \brief Abstract curve type
+ *
+ *//*
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * 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 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 PathSink;
+typedef Intersection<> CurveIntersection;
+
+/**
+ * @brief Abstract continuous curve on a plane defined on [0,1].
+ *
+ * Formally, a curve in 2Geom is defined as a function
+ * \f$\mathbf{C}: [0, 1] \to \mathbb{R}^2\f$
+ * (a function that maps the unit interval to points on a 2D plane). Its image (the set of points
+ * the curve passes through) will be denoted \f$\mathcal{C} = \mathbf{C}[ [0, 1] ]\f$.
+ * All curve types available in 2Geom are continuous and differentiable on their
+ * interior, e.g. \f$(0, 1)\f$. Sometimes the curve's image (value set) is referred to as the curve
+ * itself for simplicity, but keep in mind that it's not strictly correct.
+ *
+ * It is common to think of the parameter as time. The curve can then be interpreted as
+ * describing the position of some moving object from time \f$t=0\f$ to \f$t=1\f$.
+ * Because of this, the parameter is frequently called the time value.
+ *
+ * Some methods return pointers to newly allocated curves. They are expected to be freed
+ * by the caller when no longer used. Default implementations are provided for some methods.
+ *
+ * @ingroup Curves
+ */
+class Curve
+ : boost::equality_comparable<Curve>
+{
+public:
+ virtual ~Curve() {}
+
+ /// @name Evaluate the curve
+ /// @{
+ /** @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). */
+ virtual bool isDegenerate() const = 0;
+
+ /// Check whether the curve is a line segment.
+ virtual bool isLineSegment() const { return false; }
+
+ /** @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
+ * if some derivatives do not exist.
+ * @param t Time value
+ * @param n The number of derivatives to compute
+ * @return Vector of at most \f$n+1\f$ elements of the form \f$[\mathbf{C}(t),
+ \mathbf{C}'(t), \mathbf{C}''(t), \ldots]\f$ */
+ virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const = 0;
+ /// @}
+
+ /// @name Change the curve's endpoints
+ /// @{
+ /** @brief Change the starting 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 with curve
+ * 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
+ * with curve type.
+ * @param p New ending point of the curve */
+ virtual void setFinal(Point const &v) = 0;
+ /// @}
+
+ /// @name Compute the bounding box
+ /// @{
+ /** @brief Quickly compute the curve's approximate bounding box.
+ * The resulting rectangle is guaranteed to contain all points belonging to the curve,
+ * 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 boundsFast() depending on the curve type.
+ * @return The smallest possible rectangle containing all of the curve's points. */
+ virtual Rect boundsExact() const = 0;
+
+ /** @brief Expand the given rectangle to include the transformed curve,
+ * assuming it already contains its initial point.
+ * @param bbox[in,out] bbox The rectangle to expand; it is assumed to already contain (initialPoint() * transform).
+ * @param transform The transform to apply to the curve before taking its bounding box. */
+ virtual void expandToTransformed(Rect &bbox, Affine const &transform) 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.
+ * @param a An interval specifying a part of the curve, or nothing.
+ * If \f$[0, 1] \subseteq a\f$, then the bounding box for the entire curve
+ * is calculated.
+ * @return The smallest possible rectangle containing all points in \f$\mathbf{C}[a]\f$,
+ * or nothing if the supplied interval is empty. */
+ OptRect boundsLocal(OptInterval const &a) const { return boundsLocal(a, 0); }
+ /// @}
+
+ /// @name Create new curves based on this one
+ /// @{
+ /** @brief Create an exact copy of this curve.
+ * @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 */
+ void transform(Affine const &m) {
+ *this *= m;
+ }
+
+ virtual void operator*=(Translate const &tr) { *this *= Affine(tr); }
+ virtual void operator*=(Scale const &s) { *this *= Affine(s); }
+ virtual void operator*=(Rotate const &r) { *this *= Affine(r); }
+ virtual void operator*=(HShear const &hs) { *this *= Affine(hs); }
+ virtual void operator*=(VShear const &vs) { *this *= Affine(vs); }
+ virtual void operator*=(Zoom const &z) { *this *= Affine(z); }
+ virtual void operator*=(Affine const &m) = 0;
+
+ /** @brief Create a curve transformed by an affine transformation.
+ * 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 {
+ 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.
+ * The returned curve will always be of the same type.
+ * @param a Beginning of the interval specifying the portion of the curve
+ * @param b End of the interval
+ * @return New curve \f$\mathbf{D}\f$ such that:
+ * - \f$\mathbf{D}(0) = \mathbf{C}(a)\f$
+ * - \f$\mathbf{D}(1) = \mathbf{C}(b)\f$
+ * - \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
+ * introduction, then the curve's derivative describes that object's speed at the same times.
+ * The second derivative refers to its acceleration, the third to jerk, etc.
+ * @return New curve \f$\mathbf{D} = \mathbf{C}'\f$. */
+ virtual Curve *derivative() const = 0;
+ /// @}
+
+ /// @name Advanced operations
+ /// @{
+ /** @brief Compute a time value at which the curve comes closest to a specified point.
+ * The first value with the smallest distance is returned if there are multiple such points.
+ * @param p Query point
+ * @param a Minimum time value to consider
+ * @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 nearestTime( Point const& p, Coord a = 0, Coord b = 1 ) const;
+
+ /** @brief A version that takes an Interval. */
+ 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> allNearestTimes( Point const& p, Coord from = 0,
+ Coord to = 1 ) const;
+
+ /** @brief A version that takes an Interval. */
+ 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]
+ * In other words, we divide the curve into infinitely small linear segments
+ * and add together their lengths. Of course we can't subdivide the curve into
+ * infinitely many segments on a computer, so this method returns an approximation.
+ * Not that there is usually no closed form solution to such integrals, so this
+ * method might be slow.
+ * @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 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
+ * \f$ \mathbf{v}(t) = \frac{\mathbf{C}'(t)}{||\mathbf{C}'(t)||} \f$. It is pointed
+ * in the direction of increasing \f$t\f$, at the specified time value. The method uses
+ * l'Hopital's rule when the derivative is zero. A zero vector is returned if no non-zero
+ * derivative could be found.
+ * @param t Time value
+ * @param n The maximum order of derivative to consider
+ * @return Unit tangent vector \f$\mathbf{v}(t)\f$ */
+ 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
+ * are implemented in terms of S-basis operations, so every curve has to provide a method
+ * to convert it to an S-basis polynomial on two variables. See SBasis class reference
+ * for more information. */
+ virtual D2<SBasis> toSBasis() const = 0;
+ /// @}
+
+ /// @name Miscellaneous
+ /// @{
+ /** Return the number of independent parameters required to represent all variations
+ * 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 = 0;
+
+ /** @brief Test whether two curves are approximately the same. */
+ virtual bool isNear(Curve const &c, Coord precision) const = 0;
+
+ /** @brief Feed the curve to a PathSink */
+ virtual void feed(PathSink &sink, bool moveto_initial) const;
+ /// @}
+};
+
+inline
+Coord nearest_time(Point const& p, Curve const& c) {
+ return c.nearestTime(p);
+}
+
+// for make benefit glorious library of Boost Pointer Container
+inline
+Curve *new_clone(Curve const &c) {
+ return c.duplicate();
+}
+
+} // end namespace Geom
+
+
+#endif // _2GEOM_CURVE_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/include/2geom/curves.h b/include/2geom/curves.h
new file mode 100644
index 0000000..46fb6d9
--- /dev/null
+++ b/include/2geom/curves.h
@@ -0,0 +1,54 @@
+/** @file
+ * @brief Include all curve types
+ *//*
+ * 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.
+ */
+
+#ifndef LIB2GEOM_SEEN_CURVES_H
+#define LIB2GEOM_SEEN_CURVES_H
+
+#include <2geom/curve.h>
+#include <2geom/sbasis-curve.h>
+#include <2geom/bezier-curve.h>
+#include <2geom/elliptical-arc.h>
+
+#endif // LIB2GEOM_SEEN_CURVES_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/include/2geom/d2.h b/include/2geom/d2.h
new file mode 100644
index 0000000..45f036b
--- /dev/null
+++ b/include/2geom/d2.h
@@ -0,0 +1,564 @@
+/**
+ * \file
+ * \brief Lifts one dimensional objects into 2D
+ *//*
+ * Authors:
+ * Michael Sloan <mgsloan@gmail.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, 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.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_D2_H
+#define LIB2GEOM_SEEN_D2_H
+
+#include <iterator>
+#include <boost/concept/assert.hpp>
+#include <boost/iterator/transform_iterator.hpp>
+#include <2geom/point.h>
+#include <2geom/interval.h>
+#include <2geom/affine.h>
+#include <2geom/rect.h>
+#include <2geom/concepts.h>
+
+namespace Geom {
+/**
+ * @brief Adaptor that creates 2D functions from 1D ones.
+ * @ingroup Fragments
+ */
+template <typename T>
+class D2
+{
+private:
+ 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]);
+ }
+
+ D2(T const &a, T const &b) {
+ f[X] = a;
+ 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<GetAxis<X,V>, Iter> XIter;
+ typedef typename boost::transform_iterator<GetAxis<Y,V>, Iter> YIter;
+
+ XIter xfirst(first, GetAxis<X,V>()), xlast(last, GetAxis<X,V>());
+ f[X] = T(xfirst, xlast);
+ YIter yfirst(first, GetAxis<Y,V>()), ylast(last, GetAxis<Y,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<GetAxis<X,V>, Iter> XIter;
+ typedef boost::transform_iterator<GetAxis<Y,V>, Iter> YIter;
+
+ XIter xfirst(vec.begin(), GetAxis<X,V>()), xlast(vec.end(), GetAxis<X,V>());
+ f[X] = T(xfirst, xlast);
+ YIter yfirst(vec.begin(), GetAxis<Y,V>()), ylast(vec.end(), GetAxis<Y,V>());
+ f[Y] = T(yfirst, ylast);
+ }
+
+ //TODO: ask MenTaLguY 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;
+ bool isZero(double eps=EPSILON) const {
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+ return f[X].isZero(eps) && f[Y].isZero(eps);
+ }
+ bool isConstant(double eps=EPSILON) const {
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+ return f[X].isConstant(eps) && f[Y].isConstant(eps);
+ }
+ bool isFinite() const {
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+ return f[X].isFinite() && f[Y].isFinite();
+ }
+ Point at0() const {
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+ return Point(f[X].at0(), f[Y].at0());
+ }
+ Point at1() const {
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+ return Point(f[X].at1(), f[Y].at1());
+ }
+ Point pointAt(double t) const {
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+ return (*this)(t);
+ }
+ Point valueAt(double t) const {
+ // TODO: remove this alias
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+ return (*this)(t);
+ }
+ std::vector<Point > valueAndDerivatives(double t, unsigned n) const {
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+ std::vector<Coord> x = f[X].valueAndDerivatives(t, n),
+ y = f[Y].valueAndDerivatives(t, n); // always returns a vector of size n+1
+ std::vector<Point> res(n+1);
+ for(unsigned i = 0; i <= n; i++) {
+ res[i] = Point(x[i], y[i]);
+ }
+ return res;
+ }
+ D2<SBasis> toSBasis() const {
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+ return D2<SBasis>(f[X].toSBasis(), f[Y].toSBasis());
+ }
+
+ Point operator()(double t) const;
+ Point operator()(double x, double y) const;
+};
+template <typename T>
+inline D2<T> reverse(const D2<T> &a) {
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+ return D2<T>(reverse(a[X]), reverse(a[Y]));
+}
+
+template <typename T>
+inline D2<T> portion(const D2<T> &a, Coord f, Coord t) {
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+ return D2<T>(portion(a[X], f, t), portion(a[Y], f, t));
+}
+
+template <typename T>
+inline D2<T> portion(const D2<T> &a, Interval i) {
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+ return D2<T>(portion(a[X], i), portion(a[Y], i));
+}
+
+//IMPL: EqualityComparableConcept
+template <typename T>
+inline bool
+operator==(D2<T> const &a, D2<T> const &b) {
+ BOOST_CONCEPT_ASSERT((EqualityComparableConcept<T>));
+ return a[0]==b[0] && a[1]==b[1];
+}
+template <typename T>
+inline bool
+operator!=(D2<T> const &a, D2<T> const &b) {
+ BOOST_CONCEPT_ASSERT((EqualityComparableConcept<T>));
+ return a[0]!=b[0] || a[1]!=b[1];
+}
+
+//IMPL: NearConcept
+template <typename T>
+inline bool
+are_near(D2<T> const &a, D2<T> const &b, double tol) {
+ BOOST_CONCEPT_ASSERT((NearConcept<T>));
+ return are_near(a[0], b[0], tol) && are_near(a[1], b[1], tol);
+}
+
+//IMPL: AddableConcept
+template <typename T>
+inline D2<T>
+operator+(D2<T> const &a, D2<T> const &b) {
+ BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+
+ D2<T> r;
+ for(unsigned i = 0; i < 2; i++)
+ r[i] = a[i] + b[i];
+ return r;
+}
+template <typename T>
+inline D2<T>
+operator-(D2<T> const &a, D2<T> const &b) {
+ BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+
+ D2<T> r;
+ for(unsigned i = 0; i < 2; i++)
+ r[i] = a[i] - b[i];
+ return r;
+}
+template <typename T>
+inline D2<T>
+operator+=(D2<T> &a, D2<T> const &b) {
+ BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+
+ for(unsigned i = 0; i < 2; i++)
+ a[i] += b[i];
+ return a;
+}
+template <typename T>
+inline D2<T>
+operator-=(D2<T> &a, D2<T> const & b) {
+ BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+
+ for(unsigned i = 0; i < 2; i++)
+ a[i] -= b[i];
+ return a;
+}
+
+//IMPL: ScalableConcept
+template <typename T>
+inline D2<T>
+operator-(D2<T> const & a) {
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+ D2<T> r;
+ for(unsigned i = 0; i < 2; i++)
+ r[i] = -a[i];
+ return r;
+}
+template <typename T>
+inline D2<T>
+operator*(D2<T> const & a, Point const & b) {
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+
+ D2<T> r;
+ for(unsigned i = 0; i < 2; i++)
+ r[i] = a[i] * b[i];
+ return r;
+}
+template <typename T>
+inline D2<T>
+operator/(D2<T> const & a, Point const & b) {
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+ //TODO: b==0?
+ D2<T> r;
+ for(unsigned i = 0; i < 2; i++)
+ r[i] = a[i] / b[i];
+ return r;
+}
+template <typename T>
+inline D2<T>
+operator*=(D2<T> &a, Point const & b) {
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+
+ for(unsigned i = 0; i < 2; i++)
+ a[i] *= b[i];
+ return a;
+}
+template <typename T>
+inline D2<T>
+operator/=(D2<T> &a, Point const & b) {
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+ //TODO: b==0?
+ for(unsigned i = 0; i < 2; i++)
+ a[i] /= b[i];
+ return a;
+}
+
+template <typename T>
+inline D2<T> operator*(D2<T> const & a, double b) { return D2<T>(a[0]*b, a[1]*b); }
+template <typename T>
+inline D2<T> operator*=(D2<T> & a, double b) { a[0] *= b; a[1] *= b; return a; }
+template <typename T>
+inline D2<T> operator/(D2<T> const & a, double b) { return D2<T>(a[0]/b, a[1]/b); }
+template <typename T>
+inline D2<T> operator/=(D2<T> & a, double b) { a[0] /= b; a[1] /= b; return a; }
+
+template<typename T>
+D2<T> operator*(D2<T> const &v, Affine const &m) {
+ BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+ D2<T> ret;
+ for(unsigned i = 0; i < 2; i++)
+ ret[i] = v[X] * m[i] + v[Y] * m[i + 2] + m[i + 4];
+ return ret;
+}
+
+//IMPL: MultiplicableConcept
+template <typename T>
+inline D2<T>
+operator*(D2<T> const & a, T const & b) {
+ BOOST_CONCEPT_ASSERT((MultiplicableConcept<T>));
+ D2<T> ret;
+ for(unsigned i = 0; i < 2; i++)
+ ret[i] = a[i] * b;
+ return ret;
+}
+
+//IMPL:
+
+//IMPL: OffsetableConcept
+template <typename T>
+inline D2<T>
+operator+(D2<T> const & a, Point b) {
+ BOOST_CONCEPT_ASSERT((OffsetableConcept<T>));
+ D2<T> r;
+ for(unsigned i = 0; i < 2; i++)
+ r[i] = a[i] + b[i];
+ return r;
+}
+template <typename T>
+inline D2<T>
+operator-(D2<T> const & a, Point b) {
+ BOOST_CONCEPT_ASSERT((OffsetableConcept<T>));
+ D2<T> r;
+ for(unsigned i = 0; i < 2; i++)
+ r[i] = a[i] - b[i];
+ return r;
+}
+template <typename T>
+inline D2<T>
+operator+=(D2<T> & a, Point b) {
+ BOOST_CONCEPT_ASSERT((OffsetableConcept<T>));
+ for(unsigned i = 0; i < 2; i++)
+ a[i] += b[i];
+ return a;
+}
+template <typename T>
+inline D2<T>
+operator-=(D2<T> & a, Point b) {
+ BOOST_CONCEPT_ASSERT((OffsetableConcept<T>));
+ for(unsigned i = 0; i < 2; i++)
+ a[i] -= b[i];
+ return a;
+}
+
+template <typename T>
+inline T
+dot(D2<T> const & a, D2<T> const & b) {
+ BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+ BOOST_CONCEPT_ASSERT((MultiplicableConcept<T>));
+
+ T r;
+ for(unsigned i = 0; i < 2; i++)
+ r += a[i] * b[i];
+ return r;
+}
+
+/** @brief Calculates the 'dot product' or 'inner product' of \c a and \c b
+ * @return \f$a \bullet b = a_X b_X + a_Y b_Y\f$.
+ * @relates D2 */
+template <typename T>
+inline T
+dot(D2<T> const & a, Point const & b) {
+ BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+
+ T r;
+ for(unsigned i = 0; i < 2; i++) {
+ r += a[i] * b[i];
+ }
+ return r;
+}
+
+/** @brief Calculates the 'cross product' or 'outer product' of \c a and \c b
+ * @return \f$a \times b = a_Y b_X - a_X b_Y\f$.
+ * @relates D2 */
+template <typename T>
+inline T
+cross(D2<T> const & a, D2<T> const & b) {
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+ BOOST_CONCEPT_ASSERT((MultiplicableConcept<T>));
+
+ return a[1] * b[0] - a[0] * b[1];
+}
+
+
+//equivalent to cw/ccw, for use in situations where rotation direction doesn't matter.
+template <typename T>
+inline D2<T>
+rot90(D2<T> const & a) {
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+ return D2<T>(-a[Y], a[X]);
+}
+
+//TODO: concepterize the following functions
+template <typename T>
+inline D2<T>
+compose(D2<T> const & a, T const & b) {
+ D2<T> r;
+ for(unsigned i = 0; i < 2; i++)
+ r[i] = compose(a[i],b);
+ return r;
+}
+
+template <typename T>
+inline D2<T>
+compose_each(D2<T> const & a, D2<T> const & b) {
+ D2<T> r;
+ for(unsigned i = 0; i < 2; i++)
+ r[i] = compose(a[i],b[i]);
+ return r;
+}
+
+template <typename T>
+inline D2<T>
+compose_each(T const & a, D2<T> const & b) {
+ D2<T> r;
+ for(unsigned i = 0; i < 2; i++)
+ r[i] = compose(a,b[i]);
+ return r;
+}
+
+
+template<typename T>
+inline Point
+D2<T>::operator()(double t) const {
+ Point p;
+ for(unsigned i = 0; i < 2; i++)
+ p[i] = (*this)[i](t);
+ return p;
+}
+
+//TODO: we might want to have this take a Point as the parameter.
+template<typename T>
+inline Point
+D2<T>::operator()(double x, double y) const {
+ Point p;
+ for(unsigned i = 0; i < 2; i++)
+ p[i] = (*this)[i](x, y);
+ return p;
+}
+
+
+template<typename T>
+D2<T> derivative(D2<T> const & a) {
+ return D2<T>(derivative(a[X]), derivative(a[Y]));
+}
+template<typename T>
+D2<T> integral(D2<T> const & a) {
+ return D2<T>(integral(a[X]), integral(a[Y]));
+}
+
+/** A function to print out the Point. It just prints out the coords
+ on the given output stream */
+template <typename T>
+inline std::ostream &operator<< (std::ostream &out_file, const Geom::D2<T> &in_d2) {
+ out_file << "X: " << in_d2[X] << " Y: " << in_d2[Y];
+ return out_file;
+}
+
+//Some D2 Fragment implementation which requires rect:
+template <typename T>
+OptRect bounds_fast(const D2<T> &a) {
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+ return OptRect(bounds_fast(a[X]), bounds_fast(a[Y]));
+}
+template <typename T>
+OptRect bounds_exact(const D2<T> &a) {
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+ return OptRect(bounds_exact(a[X]), bounds_exact(a[Y]));
+}
+template <typename T>
+OptRect bounds_local(const D2<T> &a, const OptInterval &t) {
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+ return OptRect(bounds_local(a[X], t), bounds_local(a[Y], t));
+}
+
+
+
+// SBasis-specific declarations
+
+inline D2<SBasis> compose(D2<SBasis> const & a, SBasis const & b) {
+ return D2<SBasis>(compose(a[X], b), compose(a[Y], b));
+}
+
+SBasis L2(D2<SBasis> const & a, unsigned k);
+double L2(D2<double> const & a);
+
+D2<SBasis> multiply(Linear const & a, D2<SBasis> const & b);
+inline D2<SBasis> operator*(Linear const & a, D2<SBasis> const & b) { return multiply(a, b); }
+D2<SBasis> multiply(SBasis const & a, D2<SBasis> const & b);
+inline D2<SBasis> operator*(SBasis const & a, D2<SBasis> const & b) { return multiply(a, b); }
+D2<SBasis> truncate(D2<SBasis> const & a, unsigned terms);
+
+unsigned sbasis_size(D2<SBasis> const & a);
+double tail_error(D2<SBasis> const & a, unsigned tail);
+
+//Piecewise<D2<SBasis> > specific declarations
+
+Piecewise<D2<SBasis> > sectionize(D2<Piecewise<SBasis> > const &a);
+D2<Piecewise<SBasis> > make_cuts_independent(Piecewise<D2<SBasis> > const &a);
+Piecewise<D2<SBasis> > rot90(Piecewise<D2<SBasis> > const &a);
+Piecewise<SBasis> dot(Piecewise<D2<SBasis> > const &a, Piecewise<D2<SBasis> > const &b);
+Piecewise<SBasis> dot(Piecewise<D2<SBasis> > const &a, Point const &b);
+Piecewise<SBasis> cross(Piecewise<D2<SBasis> > const &a, Piecewise<D2<SBasis> > const &b);
+
+Piecewise<D2<SBasis> > operator*(Piecewise<D2<SBasis> > const &a, Affine const &m);
+
+Piecewise<D2<SBasis> > force_continuity(Piecewise<D2<SBasis> > const &f, double tol=0, bool closed=false);
+
+std::vector<Piecewise<D2<SBasis> > > fuse_nearby_ends(std::vector<Piecewise<D2<SBasis> > > const &f, double tol=0);
+
+std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > split_at_discontinuities (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwsbin, double tol = .0001);
+
+Point unitTangentAt(D2<SBasis> const & a, Coord t, unsigned n = 3);
+
+//bounds specializations with order
+inline OptRect bounds_fast(D2<SBasis> const & s, unsigned order=0) {
+ OptRect retval;
+ OptInterval xint = bounds_fast(s[X], order);
+ if (xint) {
+ OptInterval yint = bounds_fast(s[Y], order);
+ if (yint) {
+ retval = Rect(*xint, *yint);
+ }
+ }
+ return retval;
+}
+inline OptRect bounds_local(D2<SBasis> const & s, OptInterval i, unsigned order=0) {
+ OptRect retval;
+ OptInterval xint = bounds_local(s[X], i, order);
+ OptInterval yint = bounds_local(s[Y], i, order);
+ if (xint && yint) {
+ retval = Rect(*xint, *yint);
+ }
+ return retval;
+}
+
+std::vector<Interval> level_set( D2<SBasis> const &f, Rect region);
+std::vector<Interval> level_set( D2<SBasis> const &f, Point p, double tol);
+std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector<Rect> regions);
+std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector<Point> pts, double tol);
+
+
+} // end namespace Geom
+
+#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/include/2geom/ellipse.h b/include/2geom/ellipse.h
new file mode 100644
index 0000000..0d1567a
--- /dev/null
+++ b/include/2geom/ellipse.h
@@ -0,0 +1,260 @@
+/** @file
+ * @brief Ellipse shape
+ *//*
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright 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.
+ */
+
+
+#ifndef LIB2GEOM_SEEN_ELLIPSE_H
+#define LIB2GEOM_SEEN_ELLIPSE_H
+
+#include <vector>
+#include <2geom/angle.h>
+#include <2geom/bezier-curve.h>
+#include <2geom/exception.h>
+#include <2geom/forward.h>
+#include <2geom/line.h>
+#include <2geom/transforms.h>
+
+namespace Geom {
+
+class EllipticalArc;
+class Circle;
+
+/** @brief Set of points with a constant sum of distances from two foci.
+ *
+ * An ellipse can be specified in several ways. Internally, 2Geom uses
+ * the SVG style representation: center, rays and angle between the +X ray
+ * and the +X axis. Another popular way is to use an implicit equation,
+ * which is as follows:
+ * \f$Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0\f$
+ *
+ * @ingroup Shapes */
+class Ellipse
+ : boost::multipliable< Ellipse, Translate
+ , boost::multipliable< Ellipse, Scale
+ , boost::multipliable< Ellipse, Rotate
+ , boost::multipliable< Ellipse, Zoom
+ , boost::multipliable< Ellipse, Affine
+ , boost::equality_comparable< Ellipse
+ > > > > > >
+{
+ Point _center;
+ Point _rays;
+ Angle _angle;
+public:
+ Ellipse() {}
+ Ellipse(Point const &c, Point const &r, Coord angle)
+ : _center(c)
+ , _rays(r)
+ , _angle(angle)
+ {}
+ 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);
+ }
+ /// Construct ellipse from a circle.
+ Ellipse(Geom::Circle const &c);
+
+ /// Set center, rays and angle.
+ void set(Point const &c, Point const &r, Coord angle) {
+ _center = c;
+ _rays = r;
+ _angle = angle;
+ }
+ /// Set center, rays and angle as constituent values.
+ 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;
+ }
+ /// Set an ellipse by solving its implicit equation.
+ void setCoefficients(double A, double B, double C, double D, double E, double F);
+ /// Set the center.
+ void setCenter(Point const &p) { _center = p; }
+ /// Set the center by coordinates.
+ void setCenter(Coord cx, Coord cy) { _center[X] = cx; _center[Y] = cy; }
+ /// Set both rays of the ellipse.
+ void setRays(Point const &p) { _rays = p; }
+ /// Set both rays of the ellipse as coordinates.
+ void setRays(Coord x, Coord y) { _rays[X] = x; _rays[Y] = y; }
+ /// Set one of the rays of the ellipse.
+ void setRay(Coord r, Dim2 d) { _rays[d] = r; }
+ /// Set the angle the X ray makes with the +X axis.
+ void setRotationAngle(Angle a) { _angle = a; }
+
+ Point center() const { return _center; }
+ Coord center(Dim2 d) const { return _center[d]; }
+ /// Get both rays as a point.
+ Point rays() const { return _rays; }
+ /// Get one ray of the ellipse.
+ Coord ray(Dim2 d) const { return _rays[d]; }
+ /// Get the angle the X ray makes with the +X axis.
+ Angle rotationAngle() const { return _angle; }
+ /// Get the point corresponding to the +X ray of the ellipse.
+ Point initialPoint() const;
+ /// Get the point corresponding to the +X ray of the ellipse.
+ Point finalPoint() const { return initialPoint(); }
+
+ /** @brief Create an ellipse passing through the specified points
+ * At least five points have to be specified. */
+ void fit(std::vector<Point> const& points);
+
+ /** @brief Create an elliptical arc from a section of the ellipse.
+ * This is mainly useful to determine the flags of the new arc.
+ * The passed points should lie on the ellipse, otherwise the results
+ * will be undefined.
+ * @param ip Initial point of the arc
+ * @param inner Point in the middle of the arc, used to pick one of two possibilities
+ * @param fp Final point of the arc
+ * @return Newly allocated arc, delete when no longer used */
+ EllipticalArc *arc(Point const &ip, Point const &inner, Point const &fp);
+
+ /** @brief Return an ellipse with less degrees of freedom.
+ * The canonical form always has the angle less than \f$\frac{\pi}{2}\f$,
+ * and zero if the rays are equal (i.e. the ellipse is a circle). */
+ Ellipse canonicalForm() const;
+ void makeCanonical();
+
+ /** @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;
+ /** @brief Compute the transform that maps this ellipse to the unit circle.
+ * This may be a little more precise and/or faster than simply using
+ * unitCircleTransform().inverse(). An exception will be thrown for
+ * degenerate ellipses. */
+ Affine inverseUnitCircleTransform() const;
+
+ LineSegment majorAxis() const { return ray(X) >= ray(Y) ? axis(X) : axis(Y); }
+ LineSegment minorAxis() const { return ray(X) < ray(Y) ? axis(X) : axis(Y); }
+ LineSegment semimajorAxis(int sign = 1) const {
+ return ray(X) >= ray(Y) ? semiaxis(X, sign) : semiaxis(Y, sign);
+ }
+ LineSegment semiminorAxis(int sign = 1) const {
+ return ray(X) < ray(Y) ? semiaxis(X, sign) : semiaxis(Y, sign);
+ }
+ LineSegment axis(Dim2 d) const;
+ LineSegment semiaxis(Dim2 d, int sign = 1) const;
+
+ /// Get the tight-fitting bounding box of the ellipse.
+ Rect boundsExact() const;
+
+ /** @brief Get a fast to compute bounding box which contains the ellipse.
+ *
+ * The returned rectangle engulfs the ellipse but it may not be the smallest
+ * axis-aligned rectangle with this property.
+ */
+ Rect boundsFast() const;
+
+ /// Get the coefficients of the ellipse's implicit equation.
+ std::vector<double> coefficients() const;
+ void coefficients(Coord &A, Coord &B, Coord &C, Coord &D, Coord &E, Coord &F) const;
+
+ /** @brief Evaluate a point on the ellipse.
+ * The parameter range is \f$[0, 2\pi)\f$; larger and smaller values
+ * wrap around. */
+ Point pointAt(Coord t) const;
+ /// Evaluate a single coordinate of a point on the ellipse.
+ Coord valueAt(Coord t, Dim2 d) const;
+
+ /** @brief Find the time value of a point on an ellipse.
+ * If the point is not on the ellipse, the returned time value will correspond
+ * to an intersection with a ray from the origin passing through the point
+ * with the ellipse. Note that this is NOT the nearest point on the ellipse. */
+ Coord timeAt(Point const &p) const;
+
+ /// Get the value of the derivative at time t normalized to unit length.
+ Point unitTangentAt(Coord t) const;
+
+ /// Check whether the ellipse contains the given point.
+ bool contains(Point const &p) const;
+
+ /// Compute intersections with an infinite line.
+ std::vector<ShapeIntersection> intersect(Line const &line) const;
+ /// Compute intersections with a line segment.
+ std::vector<ShapeIntersection> intersect(LineSegment const &seg) const;
+ /// Compute intersections with another ellipse.
+ std::vector<ShapeIntersection> intersect(Ellipse const &other) const;
+ /// Compute intersections with a 2D Bezier polynomial.
+ std::vector<ShapeIntersection> intersect(D2<Bezier> const &other) const;
+
+ Ellipse &operator*=(Translate const &t) {
+ _center *= t;
+ return *this;
+ }
+ 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);
+
+ /// Compare ellipses for exact equality.
+ bool operator==(Ellipse const &other) const;
+};
+
+/** @brief Test whether two ellipses are approximately the same.
+ * This will check whether no point on ellipse a is further away from
+ * the corresponding point on ellipse b than precision.
+ * @relates Ellipse */
+bool are_near(Ellipse const &a, Ellipse const &b, Coord precision = EPSILON);
+
+/** @brief Outputs ellipse data, useful for debugging.
+ * @relates Ellipse */
+std::ostream &operator<<(std::ostream &out, Ellipse const &e);
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_ELLIPSE_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/include/2geom/elliptical-arc.h b/include/2geom/elliptical-arc.h
new file mode 100644
index 0000000..567e207
--- /dev/null
+++ b/include/2geom/elliptical-arc.h
@@ -0,0 +1,344 @@
+/**
+ * \file
+ * \brief Elliptical arc curve
+ *
+ *//*
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * 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 LIB2GEOM_SEEN_ELLIPTICAL_ARC_H
+#define LIB2GEOM_SEEN_ELLIPTICAL_ARC_H
+
+#include <algorithm>
+#include <2geom/affine.h>
+#include <2geom/angle.h>
+#include <2geom/bezier-curve.h>
+#include <2geom/curve.h>
+#include <2geom/ellipse.h>
+#include <2geom/sbasis-curve.h> // for non-native methods
+#include <2geom/utils.h>
+
+namespace Geom
+{
+
+class EllipticalArc : public Curve
+{
+public:
+ /** @brief Creates an arc with all variables set to zero. */
+ EllipticalArc()
+ : _initial_point(0,0)
+ , _final_point(0,0)
+ , _large_arc(false)
+ {}
+ /** @brief Create a new elliptical arc.
+ * @param ip Initial point of the arc
+ * @param r Rays of the ellipse as a point
+ * @param rot Angle of rotation of the X axis of the ellipse in radians
+ * @param large If true, the large arc is chosen (always >= 180 degrees), otherwise
+ * the smaller arc is chosen
+ * @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 const &ip, Point const &r,
+ Coord rot_angle, bool large_arc, bool sweep,
+ Point const &fp
+ )
+ : _initial_point(ip)
+ , _final_point(fp)
+ , _ellipse(0, 0, r[X], r[Y], rot_angle)
+ , _angles(0, 0, sweep)
+ , _large_arc(large_arc)
+ {
+ _updateCenterAndAngles();
+ }
+
+ /// Create a new elliptical arc, giving the ellipse's rays as separate coordinates.
+ EllipticalArc( Point const &ip, Coord rx, Coord ry,
+ Coord rot_angle, bool large_arc, bool sweep,
+ Point const &fp
+ )
+ : _initial_point(ip)
+ , _final_point(fp)
+ , _ellipse(0, 0, rx, ry, rot_angle)
+ , _angles(0, 0, sweep)
+ , _large_arc(large_arc)
+ {
+ _updateCenterAndAngles();
+ }
+
+ /// @name Retrieve basic information
+ /// @{
+
+ /** @brief Get a coordinate of the elliptical arc's center.
+ * @param d The dimension to retrieve
+ * @return The selected coordinate of the center */
+ Coord center(Dim2 d) const { return _ellipse.center(d); }
+
+ /** @brief Get the arc's center
+ * @return The arc's center, situated on the intersection of the ellipse's rays */
+ Point center() const { return _ellipse.center(); }
+
+ /** @brief Get one of the ellipse's rays
+ * @param d Dimension to retrieve
+ * @return The selected ray of the ellipse */
+ Coord ray(Dim2 d) const { return _ellipse.ray(d); }
+
+ /** @brief Get both rays as a point
+ * @return Point with X equal to the X ray and Y to Y ray */
+ Point rays() const { return _ellipse.rays(); }
+
+ /** @brief Get the defining ellipse's rotation
+ * @return Angle between the +X ray of the ellipse and the +X axis */
+ Angle rotationAngle() const {
+ return _ellipse.rotationAngle();
+ }
+
+ /** @brief Whether the arc is larger than half an ellipse.
+ * @return True if the arc is larger than \f$\pi\f$, false otherwise */
+ bool largeArc() const { return _large_arc; }
+
+ /** @brief Whether the arc turns clockwise
+ * @return True if the arc makes a clockwise turn when going from initial to final
+ * point, false otherwise */
+ bool sweep() const { return _angles.sweep(); }
+
+ Angle initialAngle() const { return _angles.initialAngle(); }
+ Angle finalAngle() const { return _angles.finalAngle(); }
+ /// @}
+
+ /// @name Modify parameters
+ /// @{
+
+ /// Change all of the arc's parameters.
+ void set( Point const &ip, double rx, double ry,
+ double rot_angle, bool large_arc, bool sweep,
+ Point const &fp
+ )
+ {
+ _initial_point = ip;
+ _final_point = fp;
+ _ellipse.setRays(rx, ry);
+ _ellipse.setRotationAngle(rot_angle);
+ _angles.setSweep(sweep);
+ _large_arc = large_arc;
+ _updateCenterAndAngles();
+ }
+
+ /// Change all of the arc's parameters.
+ void set( Point const &ip, Point const &r,
+ Angle rot_angle, bool large_arc, bool sweep,
+ Point const &fp
+ )
+ {
+ _initial_point = ip;
+ _final_point = fp;
+ _ellipse.setRays(r);
+ _ellipse.setRotationAngle(rot_angle);
+ _angles.setSweep(sweep);
+ _large_arc = large_arc;
+ _updateCenterAndAngles();
+ }
+
+ /** @brief Change the initial and final point in one operation.
+ * This method exists because modifying any of the endpoints causes rather costly
+ * recalculations of the center and extreme angles.
+ * @param ip New initial point
+ * @param fp New final point */
+ void setEndpoints(Point const &ip, Point const &fp) {
+ _initial_point = ip;
+ _final_point = fp;
+ _updateCenterAndAngles();
+ }
+ /// @}
+
+ /// @name Evaluate the arc as a function
+ /// @{
+ /** Check whether the arc contains the given angle
+ * @param t The angle to check
+ * @return True if the arc contains the angle, false otherwise */
+ bool containsAngle(Angle angle) const { return _angles.contains(angle); }
+
+ /** @brief Evaluate the arc at the specified angular coordinate
+ * @param t Angle
+ * @return Point corresponding to the given angle */
+ Point pointAtAngle(Coord t) const;
+
+ /** @brief Evaluate one of the arc's coordinates at the specified angle
+ * @param t Angle
+ * @param d The dimension to retrieve
+ * @return Selected coordinate of the arc at the specified angle */
+ Coord valueAtAngle(Coord t, Dim2 d) const;
+
+ /// Compute the curve time value corresponding to the given angular value.
+ Coord timeAtAngle(Angle a) const { return _angles.timeAtAngle(a); }
+
+ /// Compute the angular domain value corresponding to the given time value.
+ Angle angleAt(Coord t) const { return _angles.angleAt(t); }
+
+ /** @brief Compute the amount by which the angle parameter changes going from start to end.
+ * This has range \f$(-2\pi, 2\pi)\f$ and thus cannot be represented as instance
+ * of the class Angle. Add this to the initial angle to obtain the final angle. */
+ Coord sweepAngle() const { return _angles.sweepAngle(); }
+
+ /** @brief Get the elliptical angle spanned by the arc.
+ * This is basically the absolute value of sweepAngle(). */
+ Coord angularExtent() const { return _angles.extent(); }
+
+ /// Get the angular interval of the arc.
+ AngleInterval angularInterval() const { return _angles; }
+
+ /// Evaluate the arc in the curve domain, i.e. \f$[0, 1]\f$.
+ Point pointAt(Coord t) const override;
+
+ /// Evaluate a single coordinate on the arc in the curve domain.
+ Coord valueAt(Coord t, Dim2 d) const override;
+
+ /** @brief Compute a transform that maps the unit circle to the arc's 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 the arc's ellipse.
+ * @return Transform from unit circle to the arc's ellipse */
+ Affine unitCircleTransform() const {
+ Affine result = _ellipse.unitCircleTransform();
+ return result;
+ }
+
+ /** @brief Compute a transform that maps the arc's ellipse to the unit circle. */
+ Affine inverseUnitCircleTransform() const {
+ Affine result = _ellipse.inverseUnitCircleTransform();
+ return result;
+ }
+ /// @}
+
+ /// @name Deal with degenerate ellipses.
+ /// @{
+ /** @brief Check whether both rays are nonzero.
+ * If they are not, the arc is represented as a line segment instead. */
+ bool isChord() const {
+ return ray(X) == 0 || ray(Y) == 0;
+ }
+
+ /** @brief Get the line segment connecting the arc's endpoints.
+ * @return A linear segment with initial and final point corresponding to those of the arc. */
+ LineSegment chord() const { return LineSegment(_initial_point, _final_point); }
+ /// @}
+
+ // implementation of overloads goes here
+ Point initialPoint() const override { return _initial_point; }
+ Point finalPoint() const override { return _final_point; }
+ Curve* duplicate() const override { return new EllipticalArc(*this); }
+ void setInitial(Point const &p) override {
+ _initial_point = p;
+ _updateCenterAndAngles();
+ }
+ void setFinal(Point const &p) override {
+ _final_point = p;
+ _updateCenterAndAngles();
+ }
+ bool isDegenerate() const override {
+ return _initial_point == _final_point;
+ }
+ bool isLineSegment() const override { return isChord(); }
+ Rect boundsFast() const override {
+ return boundsExact();
+ }
+ Rect boundsExact() const override;
+ void expandToTransformed(Rect &bbox, Affine const &transform) const override;
+ // TODO: native implementation of the following methods
+ OptRect boundsLocal(OptInterval const &i, unsigned int deg) const override {
+ return SBasisCurve(toSBasis()).boundsLocal(i, deg);
+ }
+ std::vector<double> roots(double v, Dim2 d) const override;
+#ifdef HAVE_GSL
+ std::vector<double> allNearestTimes( Point const& p, double from = 0, double to = 1 ) const override;
+ double nearestTime( Point const& p, double from = 0, double to = 1 ) const override {
+ if ( are_near(ray(X), ray(Y)) && are_near(center(), p) ) {
+ return from;
+ }
+ return allNearestTimes(p, from, to).front();
+ }
+#endif
+ std::vector<CurveIntersection> intersect(Curve const &other, Coord eps=EPSILON) const override;
+ int degreesOfFreedom() const override { return 7; }
+ Curve *derivative() const override;
+
+ using Curve::operator*=;
+ void operator*=(Translate const &tr) override;
+ void operator*=(Scale const &s) override;
+ void operator*=(Rotate const &r) override;
+ void operator*=(Zoom const &z) override;
+ void operator*=(Affine const &m) override;
+
+ std::vector<Point> pointAndDerivatives(Coord t, unsigned int n) const override;
+ D2<SBasis> toSBasis() const override;
+ Curve *portion(double f, double t) const override;
+ Curve *reverse() const override;
+ bool operator==(Curve const &c) const override;
+ bool isNear(Curve const &other, Coord precision) const override;
+ void feed(PathSink &sink, bool moveto_initial) const override;
+ int winding(Point const &p) const override;
+
+private:
+ void _updateCenterAndAngles();
+ std::vector<ShapeIntersection> _filterIntersections(std::vector<ShapeIntersection> &&xs, bool is_first) const;
+ bool _validateIntersection(ShapeIntersection &xing, bool is_first) const;
+ std::vector<ShapeIntersection> _intersectSameEllipse(EllipticalArc const *other) const;
+
+ Point _initial_point, _final_point;
+ Ellipse _ellipse;
+ AngleInterval _angles;
+ bool _large_arc;
+}; // end class EllipticalArc
+
+
+// implemented in elliptical-arc-from-sbasis.cpp
+/** @brief Fit an elliptical arc to an SBasis fragment.
+ * @relates EllipticalArc */
+bool arc_from_sbasis(EllipticalArc &ea, D2<SBasis> const &in,
+ double tolerance = EPSILON, unsigned num_samples = 20);
+
+/** @brief Debug output for elliptical arcs.
+ * @relates EllipticalArc */
+std::ostream &operator<<(std::ostream &out, EllipticalArc const &ea);
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_ELLIPTICAL_ARC_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/include/2geom/exception.h b/include/2geom/exception.h
new file mode 100644
index 0000000..b472aae
--- /dev/null
+++ b/include/2geom/exception.h
@@ -0,0 +1,157 @@
+/**
+ * \file
+ * \brief Defines the different types of exceptions that 2geom can throw.
+ *
+ * There are two main exception classes: LogicalError and RangeError.
+ * Logical errors are 2geom faults/bugs; RangeErrors are 'user' faults,
+ * e.g. invalid arguments to lib2geom methods.
+ * This way, the 'user' can distinguish between groups of exceptions
+ * ('user' is the coder that uses lib2geom)
+ *
+ * Several macro's are defined for easily throwing exceptions
+ * (e.g. THROW_CONTINUITYERROR).
+ */
+/* Copyright 2007 Johan Engelen <goejendaagh@zonnet.nl>
+ *
+ * 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_EXCEPTION_H
+#define LIB2GEOM_SEEN_EXCEPTION_H
+
+#include <exception>
+#include <sstream>
+#include <string>
+
+namespace Geom {
+
+/**
+ * Base exception class, all 2geom exceptions should be derived from this one.
+ */
+class Exception : public std::exception {
+public:
+ Exception(const char * message, const char *file, const int line) {
+ std::ostringstream os;
+ os << "lib2geom exception: " << message << " (" << file << ":" << line << ")";
+ msgstr = os.str();
+ }
+
+ ~Exception() noexcept override {} // necessary to destroy the string object!!!
+
+ const char* what() const noexcept override {
+ return msgstr.c_str();
+ }
+protected:
+ std::string msgstr;
+};
+#define THROW_EXCEPTION(message) throw(Geom::Exception(message, __FILE__, __LINE__))
+
+//-----------------------------------------------------------------------
+
+class LogicalError : public Exception {
+public:
+ LogicalError(const char * message, const char *file, const int line)
+ : Exception(message, file, line) {}
+};
+#define THROW_LOGICALERROR(message) throw(LogicalError(message, __FILE__, __LINE__))
+
+class RangeError : public Exception {
+public:
+ RangeError(const char * message, const char *file, const int line)
+ : Exception(message, file, line) {}
+};
+#define THROW_RANGEERROR(message) throw(RangeError(message, __FILE__, __LINE__))
+
+//-----------------------------------------------------------------------
+// Special case exceptions. Best used with the defines :)
+
+class NotImplemented : public LogicalError {
+public:
+ NotImplemented(const char *file, const int line)
+ : LogicalError("Method not implemented", file, line) {}
+};
+#define THROW_NOTIMPLEMENTED(i) throw(NotImplemented(__FILE__, __LINE__))
+
+class InvariantsViolation : public LogicalError {
+public:
+ InvariantsViolation(const char *file, const int line)
+ : LogicalError("Invariants violation", file, line) {}
+};
+#define THROW_INVARIANTSVIOLATION(i) throw(InvariantsViolation(__FILE__, __LINE__))
+#define ASSERT_INVARIANTS(e) ((e) ? (void)0 : THROW_INVARIANTSVIOLATION())
+
+class NotInvertible : public RangeError {
+public:
+ NotInvertible(const char *file, const int line)
+ : RangeError("Function does not have a unique inverse", file, line) {}
+};
+#define THROW_NOTINVERTIBLE(i) throw(NotInvertible(__FILE__, __LINE__))
+
+class InfiniteSolutions : public RangeError {
+public:
+ InfiniteSolutions(const char *file, const int line)
+ : RangeError("There are infinite solutions", file, line) {}
+};
+#define THROW_INFINITESOLUTIONS(i) throw(InfiniteSolutions(__FILE__, __LINE__))
+
+class InfinitelyManySolutions : public RangeError {
+private:
+ char const *const _message;
+public:
+ InfinitelyManySolutions(const char *file, const int line, char const *message)
+ : RangeError("There are infinitely many solutions", file, line)
+ , _message{message}
+ {}
+ char const *what() const noexcept override { return _message; }
+};
+#define THROW_INFINITELY_MANY_SOLUTIONS(msg) throw(InfinitelyManySolutions(__FILE__, __LINE__, msg))
+
+class ContinuityError : public RangeError {
+public:
+ ContinuityError(const char *file, const int line)
+ : RangeError("Non-contiguous path", file, line) {}
+};
+#define THROW_CONTINUITYERROR(i) throw(ContinuityError(__FILE__, __LINE__))
+
+struct SVGPathParseError : public std::exception {
+ char const *what() const noexcept override { return "parse error"; }
+};
+
+
+} // namespace Geom
+
+#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/include/2geom/forward.h b/include/2geom/forward.h
new file mode 100644
index 0000000..2790924
--- /dev/null
+++ b/include/2geom/forward.h
@@ -0,0 +1,127 @@
+/**
+ * \file
+ * \brief Contains forward declarations of 2geom types
+ *//*
+ * Authors:
+ * Johan Engelen <goejendaagh@zonnet.nl>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2008-2010 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_FORWARD_H
+#define LIB2GEOM_SEEN_FORWARD_H
+
+namespace Geom {
+
+// primitives
+typedef double Coord;
+typedef int IntCoord;
+class Point;
+class IntPoint;
+class Line;
+class Ray;
+template <typename> class GenericInterval;
+template <typename> class GenericOptInterval;
+class Interval;
+class OptInterval;
+typedef GenericInterval<IntCoord> IntInterval;
+typedef GenericOptInterval<IntCoord> OptIntInterval;
+template <typename> class GenericRect;
+template <typename> class GenericOptRect;
+class Rect;
+class OptRect;
+typedef GenericRect<IntCoord> IntRect;
+typedef GenericOptRect<IntCoord> OptIntRect;
+
+// fragments
+class Linear;
+class Bezier;
+class SBasis;
+class Poly;
+
+// shapes
+class Circle;
+class Ellipse;
+class ConvexHull;
+
+// curves
+class Curve;
+class SBasisCurve;
+class BezierCurve;
+template <unsigned degree> class BezierCurveN;
+typedef BezierCurveN<1> LineSegment;
+typedef BezierCurveN<2> QuadraticBezier;
+typedef BezierCurveN<3> CubicBezier;
+class EllipticalArc;
+
+// paths and path sequences
+class Path;
+class PathVector;
+struct PathTime;
+class PathInterval;
+struct PathVectorTime;
+
+// errors
+class Exception;
+class LogicalError;
+class RangeError;
+class NotImplemented;
+class InvariantsViolation;
+class NotInvertible;
+class ContinuityError;
+
+// transforms
+class Affine;
+class Translate;
+class Rotate;
+class Scale;
+class HShear;
+class VShear;
+class Zoom;
+
+// templates
+template <typename> class D2;
+template <typename> class Piecewise;
+
+// misc
+class SVGPathSink;
+template <typename> class SVGPathGenerator;
+
+}
+
+#endif // SEEN_GEOM_FORWARD_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/include/2geom/generic-interval.h b/include/2geom/generic-interval.h
new file mode 100644
index 0000000..1d3cfdb
--- /dev/null
+++ b/include/2geom/generic-interval.h
@@ -0,0 +1,374 @@
+/**
+ * @file
+ * @brief Closed interval of generic values
+ *//*
+ * Copyright 2011 Krzysztof Kosiński <tweenk.pl@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_GENERIC_INTERVAL_H
+#define LIB2GEOM_SEEN_GENERIC_INTERVAL_H
+
+#include <cassert>
+#include <iostream>
+#include <optional>
+#include <2geom/coord.h>
+
+namespace Geom {
+
+template <typename C>
+class GenericOptInterval;
+
+/**
+ * @brief A range of numbers which is never empty.
+ * @ingroup Primitives
+ */
+template <typename C>
+class GenericInterval
+ : CoordTraits<C>::IntervalOps
+{
+ typedef typename CoordTraits<C>::IntervalType CInterval;
+ typedef GenericInterval<C> Self;
+protected:
+ C _b[2];
+public:
+ /// @name Create intervals.
+ /// @{
+ /** @brief Create an interval that contains only zero. */
+ GenericInterval() { _b[0] = 0; _b[1] = 0; }
+ /** @brief Create an interval that contains a single point. */
+ explicit GenericInterval(C u) { _b[0] = _b[1] = u; }
+ /** @brief Create an interval that contains all points between @c u and @c v. */
+ GenericInterval(C u, C v) {
+ if (u <= v) {
+ _b[0] = u; _b[1] = v;
+ } else {
+ _b[0] = v; _b[1] = u;
+ }
+ }
+
+ /** @brief Create an interval containing a range of values.
+ * The resulting interval will contain all values from the given range.
+ * The return type of iterators must be convertible to C. The given range
+ * must not be empty. For potentially empty ranges, see GenericOptInterval.
+ * @param start Beginning of the range
+ * @param end End of the range
+ * @return Interval that contains all values from [start, end). */
+ template <typename InputIterator>
+ static CInterval from_range(InputIterator start, InputIterator end) {
+ assert(start != end);
+ CInterval result(*start++);
+ for (; start != end; ++start) result.expandTo(*start);
+ return result;
+ }
+ /** @brief Create an interval from a C-style array of values it should contain. */
+ static CInterval from_array(C const *c, unsigned n) {
+ CInterval result = from_range(c, c+n);
+ return result;
+ }
+ /// @}
+
+ /// @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.
+ /// @{
+ /** @brief Check whether the interval includes this number. */
+ bool contains(C val) const {
+ return min() <= val && val <= max();
+ }
+ /** @brief Check whether the interval includes the given interval. */
+ bool contains(CInterval const &val) const {
+ return min() <= val.min() && val.max() <= max();
+ }
+ /** @brief Check whether the intervals have any common elements. */
+ bool intersects(CInterval const &val) const {
+ return contains(val.min()) || contains(val.max()) || val.contains(*this);
+ }
+ /// @}
+
+ /// @name Modify the interval.
+ /// @{
+ //TODO: NaN handleage for the next two?
+ /** @brief Set the lower boundary of the interval.
+ * When the given number is larger than the interval's largest element,
+ * it will be reduced to the single number @c val. */
+ void setMin(C val) {
+ if(val > _b[1]) {
+ _b[0] = _b[1] = val;
+ } else {
+ _b[0] = val;
+ }
+ }
+ /** @brief Set the upper boundary of the interval.
+ * When the given number is smaller than the interval's smallest element,
+ * it will be reduced to the single number @c val. */
+ void setMax(C val) {
+ if(val < _b[0]) {
+ _b[1] = _b[0] = val;
+ } else {
+ _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;
+ if(val > _b[1]) _b[1] = val; //no else, as we want to handle NaN
+ }
+ /** @brief Expand or shrink the interval in both directions by the given amount.
+ * After this method, the interval's length (extent) will be increased by
+ * <code>amount * 2</code>. Negative values can be given; they will shrink the interval.
+ * Shrinking by a value larger than half the interval's length will create a degenerate
+ * interval containing only the midpoint of the original. */
+ void expandBy(C amount) {
+ _b[0] -= amount;
+ _b[1] += amount;
+ if (_b[0] > _b[1]) {
+ C halfway = (_b[0]+_b[1])/2;
+ _b[0] = _b[1] = halfway;
+ }
+ }
+ /** @brief Union the interval with another one.
+ * The resulting interval will contain all points of both intervals.
+ * It might also contain some points which didn't belong to either - this happens
+ * when the intervals did not have any common elements. */
+ void unionWith(CInterval const &a) {
+ if(a._b[0] < _b[0]) _b[0] = a._b[0];
+ if(a._b[1] > _b[1]) _b[1] = a._b[1];
+ }
+ /// @}
+
+ /// @name Operators
+ /// @{
+ //IMPL: OffsetableConcept
+ //TODO: rename output_type to something else in the concept
+ typedef C output_type;
+ /** @brief Offset the interval by a specified amount */
+ Self &operator+=(C amnt) {
+ _b[0] += amnt; _b[1] += amnt;
+ return *this;
+ }
+ /** @brief Offset the interval by the negation of the specified amount */
+ Self &operator-=(C amnt) {
+ _b[0] -= amnt; _b[1] -= amnt;
+ return *this;
+ }
+
+ /** @brief Return an interval mirrored about 0 */
+ Self operator-() const { Self r(-_b[1], -_b[0]); return r; }
+ // IMPL: AddableConcept
+ /** @brief Add two intervals.
+ * Sum is defined as the set of points that can be obtained by adding any two values
+ * from both operands: \f$S = \{x \in A, y \in B: x + y\}\f$ */
+ Self &operator+=(CInterval const &o) {
+ _b[0] += o._b[0];
+ _b[1] += o._b[1];
+ return *this;
+ }
+ /** @brief Subtract two intervals.
+ * Difference is defined as the set of points that can be obtained by subtracting
+ * any value from the second operand from any value from the first operand:
+ * \f$S = \{x \in A, y \in B: x - y\}\f$ */
+ Self &operator-=(CInterval const &o) {
+ // equal to *this += -o
+ _b[0] -= o._b[1];
+ _b[1] -= o._b[0];
+ return *this;
+ }
+ /** @brief Union two intervals.
+ * Note that the intersection-and-assignment operator is not defined,
+ * because the result of an intersection can be empty, while Interval cannot. */
+ Self &operator|=(CInterval const &o) {
+ unionWith(o);
+ return *this;
+ }
+ /** @brief Test for interval equality. */
+ bool operator==(CInterval const &other) const {
+ return min() == other.min() && max() == other.max();
+ }
+ /// @}
+};
+
+/** @brief Union two intervals
+ * @relates GenericInterval */
+template <typename C>
+inline GenericInterval<C> unify(GenericInterval<C> const &a, GenericInterval<C> const &b) {
+ return a | b;
+}
+
+/**
+ * @brief A range of numbers that can be empty.
+ * @ingroup Primitives
+ */
+template <typename C>
+class GenericOptInterval
+ : public std::optional<typename CoordTraits<C>::IntervalType>
+ , boost::orable< GenericOptInterval<C>
+ , boost::andable< GenericOptInterval<C>
+ > >
+{
+ typedef typename CoordTraits<C>::IntervalType CInterval;
+ typedef typename CoordTraits<C>::OptIntervalType OptCInterval;
+ typedef std::optional<CInterval> Base;
+public:
+ /// @name Create optionally empty intervals.
+ /// @{
+ /** @brief Create an empty interval. */
+ GenericOptInterval() : Base() {}
+ /** @brief Wrap an existing interval. */
+ GenericOptInterval(GenericInterval<C> const &a) : Base(CInterval(a)) {}
+ /** @brief Create an interval containing a single point. */
+ GenericOptInterval(C u) : Base(CInterval(u)) {}
+ /** @brief Create an interval containing a range of numbers. */
+ GenericOptInterval(C u, C v) : Base(CInterval(u,v)) {}
+
+ /** @brief Create a possibly empty interval containing a range of values.
+ * The resulting interval will contain all values from the given range.
+ * The return type of iterators must be convertible to C. The given range
+ * may be empty.
+ * @param start Beginning of the range
+ * @param end End of the range
+ * @return Interval that contains all values from [start, end), or nothing if the range
+ * is empty. */
+ template <typename InputIterator>
+ static GenericOptInterval<C> from_range(InputIterator start, InputIterator end) {
+ if (start == end) {
+ GenericOptInterval<C> ret;
+ return ret;
+ }
+ GenericOptInterval<C> ret(CInterval::from_range(start, end));
+ return ret;
+ }
+ /// @}
+
+ /** @brief Check whether this interval is empty. */
+ bool empty() { return !*this; }
+
+ /** @brief Union with another interval, gracefully handling empty ones. */
+ void unionWith(GenericOptInterval<C> const &a) {
+ if (a) {
+ if (*this) { // check that we are not empty
+ (*this)->unionWith(*a);
+ } else {
+ *this = *a;
+ }
+ }
+ }
+ void intersectWith(GenericOptInterval<C> const &o) {
+ if (o && *this) {
+ if (!*this) return;
+ C u = std::max((*this)->min(), o->min());
+ C v = std::min((*this)->max(), o->max());
+ if (u <= v) {
+ *this = CInterval(u, v);
+ return;
+ }
+ }
+ (*static_cast<Base*>(this)) = std::nullopt;
+ }
+ GenericOptInterval<C> &operator|=(OptCInterval const &o) {
+ unionWith(o);
+ return *this;
+ }
+ GenericOptInterval<C> &operator&=(OptCInterval const &o) {
+ intersectWith(o);
+ return *this;
+ }
+
+ // The equality operators inherited from std::optional don't work with derived types, because
+ // the template overload ignores that the devived type is also an optional. It would result in
+ // `GenericInterval() != GenericInterval()` being true.
+ template <typename U, typename = std::enable_if_t<std::is_base_of_v<Base, U>>>
+ bool operator==(U const &other) const
+ {
+ return static_cast<Base const &>(*this) == static_cast<Base const &>(other);
+ }
+ template <typename U, typename = std::enable_if_t<std::is_base_of_v<Base, U>>>
+ bool operator!=(U const &other) const
+ {
+ return static_cast<Base const &>(*this) != static_cast<Base const &>(other);
+ }
+};
+
+/** @brief Intersect two intervals and return a possibly empty range of numbers
+ * @relates GenericOptInterval */
+template <typename C>
+inline GenericOptInterval<C> intersect(GenericInterval<C> const &a, GenericInterval<C> const &b) {
+ return GenericOptInterval<C>(a) & GenericOptInterval<C>(b);
+}
+/** @brief Intersect two intervals and return a possibly empty range of numbers
+ * @relates GenericOptInterval */
+template <typename C>
+inline GenericOptInterval<C> operator&(GenericInterval<C> const &a, GenericInterval<C> const &b) {
+ return GenericOptInterval<C>(a) & GenericOptInterval<C>(b);
+}
+
+template <typename C>
+inline std::ostream &operator<< (std::ostream &os,
+ Geom::GenericInterval<C> const &I) {
+ os << "Interval("<<I.min() << ", "<<I.max() << ")";
+ return os;
+}
+
+} // namespace Geom
+#endif // !LIB2GEOM_SEEN_GENERIC_INTERVAL_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/include/2geom/generic-rect.h b/include/2geom/generic-rect.h
new file mode 100644
index 0000000..4524d43
--- /dev/null
+++ b/include/2geom/generic-rect.h
@@ -0,0 +1,547 @@
+/**
+ * \file
+ * \brief Axis-aligned rectangle
+ *//*
+ * Authors:
+ * Michael Sloan <mgsloan@gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Copyright 2007-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, 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.
+ *
+ * Authors of original rect class:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * bulia byak <buliabyak@users.sf.net>
+ * MenTaLguY <mental@rydia.net>
+ */
+
+#ifndef LIB2GEOM_SEEN_GENERIC_RECT_H
+#define LIB2GEOM_SEEN_GENERIC_RECT_H
+
+#include <limits>
+#include <iostream>
+#include <optional>
+#include <2geom/coord.h>
+
+namespace Geom {
+
+template <typename C>
+class GenericOptRect;
+
+/**
+ * @brief Axis aligned, non-empty, generic rectangle.
+ * @ingroup Primitives
+ */
+template <typename C>
+class GenericRect
+ : CoordTraits<C>::RectOps
+{
+ typedef typename CoordTraits<C>::IntervalType CInterval;
+ typedef typename CoordTraits<C>::PointType CPoint;
+ typedef typename CoordTraits<C>::RectType CRect;
+ typedef typename CoordTraits<C>::OptRectType OptCRect;
+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). */
+ GenericRect() { f[X] = f[Y] = CInterval(); }
+ /** @brief Create a rectangle from X and Y intervals. */
+ GenericRect(CInterval const &a, CInterval const &b) {
+ f[X] = a;
+ f[Y] = b;
+ }
+ /** @brief Create a rectangle from two points. */
+ GenericRect(CPoint const &a, CPoint const &b) {
+ f[X] = CInterval(a[X], b[X]);
+ f[Y] = CInterval(a[Y], b[Y]);
+ }
+ /** @brief Create rectangle from coordinates of two points. */
+ GenericRect(C x0, C y0, C x1, C y1) {
+ f[X] = CInterval(x0, x1);
+ f[Y] = CInterval(y0, y1);
+ }
+ /** @brief Create a rectangle from a range of points.
+ * The resulting rectangle will contain all points from the range.
+ * The return type of iterators must be convertible to Point.
+ * The range must not be empty. For possibly empty ranges, see OptRect.
+ * @param start Beginning of the range
+ * @param end End of the range
+ * @return Rectangle that contains all points from [start, end). */
+ template <typename InputIterator>
+ static CRect from_range(InputIterator start, InputIterator end) {
+ assert(start != end);
+ CPoint p1 = *start++;
+ CRect result(p1, p1);
+ for (; start != end; ++start) {
+ result.expandTo(*start);
+ }
+ return result;
+ }
+ /** @brief Create a rectangle from a C-style array of points it should contain. */
+ static CRect from_array(CPoint const *c, unsigned n) {
+ CRect result = GenericRect<C>::from_range(c, c+n);
+ return result;
+ }
+ /** @brief Create rectangle from origin and dimensions. */
+ static CRect from_xywh(C x, C y, C w, C h) {
+ CPoint xy(x, y);
+ CPoint wh(w, h);
+ CRect result(xy, xy + wh);
+ return result;
+ }
+ /** @brief Create rectangle from origin and dimensions. */
+ static CRect from_xywh(CPoint const &xy, CPoint const &wh) {
+ CRect result(xy, xy + wh);
+ return result;
+ }
+ /// Create infinite rectangle.
+ static CRect infinite() {
+ CPoint p0(std::numeric_limits<C>::min(), std::numeric_limits<C>::min());
+ CPoint p1(std::numeric_limits<C>::max(), std::numeric_limits<C>::max());
+ CRect result(p0, p1);
+ return result;
+ }
+ /// @}
+
+ /// @name Inspect dimensions.
+ /// @{
+ CInterval &operator[](unsigned i) { return f[i]; }
+ CInterval const &operator[](unsigned i) const { return f[i]; }
+ CInterval &operator[](Dim2 d) { return f[d]; }
+ CInterval const &operator[](Dim2 d) const { return f[d]; }
+
+ /** @brief Get the corner of the rectangle with smallest coordinate values.
+ * In 2Geom standard coordinate system, this means upper left. */
+ CPoint min() const { CPoint p(f[X].min(), f[Y].min()); return p; }
+ /** @brief Get the corner of the rectangle with largest coordinate values.
+ * In 2Geom standard coordinate system, this means lower right. */
+ CPoint max() const { CPoint p(f[X].max(), f[Y].max()); return p; }
+ /** @brief Return the n-th corner of the rectangle.
+ * Returns corners in the direction of growing angles, starting from
+ * the one given by min(). For the standard coordinate system used
+ * in 2Geom (+Y downwards), this means clockwise starting from
+ * the upper left. */
+ CPoint corner(unsigned i) const {
+ switch(i % 4) {
+ case 0: return CPoint(f[X].min(), f[Y].min());
+ case 1: return CPoint(f[X].max(), f[Y].min());
+ case 2: return CPoint(f[X].max(), f[Y].max());
+ default: return CPoint(f[X].min(), f[Y].max());
+ }
+ }
+
+ //We should probably remove these - they're coord sys gnostic
+ /** @brief Return top coordinate of the rectangle (+Y is downwards). */
+ C top() const { return f[Y].min(); }
+ /** @brief Return bottom coordinate of the rectangle (+Y is downwards). */
+ C bottom() const { return f[Y].max(); }
+ /** @brief Return leftmost coordinate of the rectangle (+X is to the right). */
+ C left() const { return f[X].min(); }
+ /** @brief Return rightmost coordinate of the rectangle (+X is to the right). */
+ C right() const { return f[X].max(); }
+
+ /** @brief Get the horizontal extent of the rectangle. */
+ C width() const { return f[X].extent(); }
+ /** @brief Get the vertical extent of the rectangle. */
+ C height() const { return f[Y].extent(); }
+ /** @brief Get the ratio of width to height of the rectangle. */
+ Coord aspectRatio() const { return Coord(width()) / Coord(height()); }
+
+ /** @brief Get rectangle's width and height as a point.
+ * @return Point with X coordinate corresponding to the width and the Y coordinate
+ * corresponding to the height of the rectangle. */
+ CPoint dimensions() const { return CPoint(f[X].extent(), f[Y].extent()); }
+ /** @brief Get the point in the geometric center of the rectangle. */
+ CPoint midpoint() const { return CPoint(f[X].middle(), f[Y].middle()); }
+
+ /** @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 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 Get rectangle's distance SQUARED away from the given point **/
+ C distanceSq(const CPoint pt) const {
+ auto v = clamp(pt) - pt;
+ return v.x() * v.x() + v.y() * v.y();
+ }
+
+ /** @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.
+ /// @{
+ /** @brief Check whether the rectangles have any common points. */
+ bool intersects(GenericRect<C> const &r) const {
+ return f[X].intersects(r[X]) && f[Y].intersects(r[Y]);
+ }
+ /** @brief Check whether the rectangle includes all points in the given rectangle. */
+ bool contains(GenericRect<C> const &r) const {
+ return f[X].contains(r[X]) && f[Y].contains(r[Y]);
+ }
+
+ /** @brief Check whether the rectangles have any common points.
+ * Empty rectangles will not intersect with any other rectangle. */
+ inline bool intersects(OptCRect const &r) const;
+ /** @brief Check whether the rectangle includes all points in the given rectangle.
+ * Empty rectangles will be contained in any non-empty rectangle. */
+ inline bool contains(OptCRect const &r) const;
+
+ /** @brief Check whether the given point is within the rectangle. */
+ bool contains(CPoint const &p) const {
+ return f[X].contains(p[X]) && f[Y].contains(p[Y]);
+ }
+ /// @}
+
+ /// @name Modify the rectangle.
+ /// @{
+ /** @brief Set the minimum X coordinate of the rectangle. */
+ void setLeft(C val) {
+ f[X].setMin(val);
+ }
+ /** @brief Set the maximum X coordinate of the rectangle. */
+ void setRight(C val) {
+ f[X].setMax(val);
+ }
+ /** @brief Set the minimum Y coordinate of the rectangle. */
+ void setTop(C val) {
+ f[Y].setMin(val);
+ }
+ /** @brief Set the maximum Y coordinate of the rectangle. */
+ void setBottom(C val) {
+ f[Y].setMax(val);
+ }
+ /** @brief Set the upper left point of the rectangle. */
+ void setMin(CPoint const &p) {
+ f[X].setMin(p[X]);
+ f[Y].setMin(p[Y]);
+ }
+ /** @brief Set the lower right point of the rectangle. */
+ void setMax(CPoint const &p) {
+ f[X].setMax(p[X]);
+ f[Y].setMax(p[Y]);
+ }
+ /** @brief Enlarge the rectangle to contain the given point. */
+ void expandTo(CPoint const &p) {
+ f[X].expandTo(p[X]); f[Y].expandTo(p[Y]);
+ }
+ /** @brief Enlarge the rectangle to contain the argument. */
+ void unionWith(CRect const &b) {
+ f[X].unionWith(b[X]); f[Y].unionWith(b[Y]);
+ }
+ /** @brief Enlarge the rectangle to contain the argument.
+ * Unioning with an empty rectangle results in no changes. */
+ void unionWith(OptCRect const &b);
+
+ /** @brief Expand the rectangle in both directions by the specified amount.
+ * Note that this is different from scaling. Negative values will shrink the
+ * rectangle. If <code>-amount</code> is larger than
+ * half of the width, the X interval will contain only the X coordinate
+ * of the midpoint; same for height. */
+ void expandBy(C amount) {
+ expandBy(amount, amount);
+ }
+ /** @brief Expand the rectangle in both directions.
+ * Note that this is different from scaling. Negative values will shrink the
+ * rectangle. If <code>-x</code> is larger than
+ * half of the width, the X interval will contain only the X coordinate
+ * of the midpoint; same for height. */
+ void expandBy(C x, C y) {
+ f[X].expandBy(x); f[Y].expandBy(y);
+ }
+ /** @brief Expand the rectangle by the coordinates of the given point.
+ * This will expand the width by the X coordinate of the point in both directions
+ * and the height by Y coordinate of the point. Negative coordinate values will
+ * shrink the rectangle. If <code>-p[X]</code> is larger than half of the width,
+ * the X interval will contain only the X coordinate of the midpoint;
+ * same for height. */
+ void expandBy(CPoint const &p) {
+ expandBy(p[X], p[Y]);
+ }
+ /// @}
+
+ /// @name Operators
+ /// @{
+ /** @brief Offset the rectangle by a vector. */
+ GenericRect<C> &operator+=(CPoint const &p) {
+ f[X] += p[X];
+ f[Y] += p[Y];
+ return *this;
+ }
+ /** @brief Offset the rectangle by the negation of a vector. */
+ GenericRect<C> &operator-=(CPoint const &p) {
+ f[X] -= p[X];
+ f[Y] -= p[Y];
+ return *this;
+ }
+ /** @brief Union two rectangles. */
+ GenericRect<C> &operator|=(CRect const &o) {
+ unionWith(o);
+ return *this;
+ }
+ GenericRect<C> &operator|=(OptCRect const &o) {
+ unionWith(o);
+ return *this;
+ }
+ /** @brief Test for equality of rectangles. */
+ bool operator==(CRect const &o) const { return f[X] == o[X] && f[Y] == o[Y]; }
+ /// @}
+};
+
+/**
+ * @brief Axis-aligned generic rectangle that can be empty.
+ * @ingroup Primitives
+ */
+template <typename C>
+class GenericOptRect
+ : public std::optional<typename CoordTraits<C>::RectType>
+ , boost::equality_comparable< typename CoordTraits<C>::OptRectType
+ , boost::equality_comparable< typename CoordTraits<C>::OptRectType, typename CoordTraits<C>::RectType
+ , boost::orable< typename CoordTraits<C>::OptRectType
+ , boost::andable< typename CoordTraits<C>::OptRectType
+ , boost::andable< typename CoordTraits<C>::OptRectType, typename CoordTraits<C>::RectType
+ > > > > >
+{
+ typedef typename CoordTraits<C>::IntervalType CInterval;
+ typedef typename CoordTraits<C>::OptIntervalType OptCInterval;
+ typedef typename CoordTraits<C>::PointType CPoint;
+ typedef typename CoordTraits<C>::RectType CRect;
+ typedef typename CoordTraits<C>::OptRectType OptCRect;
+ typedef std::optional<CRect> Base;
+public:
+ typedef CInterval D1Value;
+ typedef CInterval &D1Reference;
+ typedef CInterval const &D1ConstReference;
+
+ /// @name Create potentially empty rectangles.
+ /// @{
+ GenericOptRect() : Base() {}
+ GenericOptRect(GenericRect<C> const &a) : Base(CRect(a)) {}
+ GenericOptRect(CPoint const &a, CPoint const &b) : Base(CRect(a, b)) {}
+ GenericOptRect(C x0, C y0, C x1, C y1) : Base(CRect(x0, y0, x1, y1)) {}
+ /// Creates an empty OptRect when one of the argument intervals is empty.
+ GenericOptRect(OptCInterval const &x_int, OptCInterval const &y_int) {
+ if (x_int && y_int) {
+ *this = CRect(*x_int, *y_int);
+ }
+ // else, stay empty.
+ }
+
+ /** @brief Create a rectangle from a range of points.
+ * The resulting rectangle will contain all points from the range.
+ * If the range contains no points, the result will be an empty rectangle.
+ * The return type of iterators must be convertible to the corresponding
+ * point type (Point or IntPoint).
+ * @param start Beginning of the range
+ * @param end End of the range
+ * @return Rectangle that contains all points from [start, end). */
+ template <typename InputIterator>
+ static OptCRect from_range(InputIterator start, InputIterator end) {
+ OptCRect result;
+ for (; start != end; ++start) {
+ result.expandTo(*start);
+ }
+ return result;
+ }
+ /// @}
+
+ /// @name Check other rectangles and points for inclusion.
+ /// @{
+ /** @brief Check for emptiness. */
+ inline bool empty() const { return !*this; };
+ /** @brief Check whether the rectangles have any common points.
+ * Empty rectangles will not intersect with any other rectangle. */
+ bool intersects(CRect const &r) const { return r.intersects(*this); }
+ /** @brief Check whether the rectangle includes all points in the given rectangle.
+ * Empty rectangles will be contained in any non-empty rectangle. */
+ bool contains(CRect const &r) const { return *this && (*this)->contains(r); }
+
+ /** @brief Check whether the rectangles have any common points.
+ * Empty rectangles will not intersect with any other rectangle.
+ * Two empty rectangles will not intersect each other. */
+ bool intersects(OptCRect const &r) const { return *this && (*this)->intersects(r); }
+ /** @brief Check whether the rectangle includes all points in the given rectangle.
+ * Empty rectangles will be contained in any non-empty rectangle.
+ * An empty rectangle will not contain other empty rectangles. */
+ bool contains(OptCRect const &r) const { return *this && (*this)->contains(r); }
+
+ /** @brief Check whether the given point is within the rectangle.
+ * An empty rectangle will not contain any points. */
+ bool contains(CPoint const &p) const { return *this && (*this)->contains(p); }
+ /// @}
+
+ /** @brief Returns an empty optional (testing false) if the rectangle has zero area. */
+ OptCRect regularized() const {
+ return *this && !(*this)->hasZeroArea() ? *this : OptCRect();
+ }
+
+ /// @name Modify the potentially empty rectangle.
+ /// @{
+ /** @brief Enlarge the rectangle to contain the argument.
+ * If this rectangle is empty, after callng this method it will
+ * be equal to the argument. */
+ void unionWith(CRect const &b) {
+ if (*this) {
+ (*this)->unionWith(b);
+ } else {
+ *this = b;
+ }
+ }
+ /** @brief Enlarge the rectangle to contain the argument.
+ * Unioning with an empty rectangle results in no changes.
+ * If this rectangle is empty, after calling this method it will
+ * be equal to the argument. */
+ void unionWith(OptCRect const &b) {
+ if (b) unionWith(*b);
+ }
+ /** @brief Leave only the area overlapping with the argument.
+ * If the rectangles do not have any points in common, after calling
+ * this method the rectangle will be empty. */
+ void intersectWith(CRect const &b) {
+ if (!*this) return;
+ OptCInterval x = (**this)[X] & b[X], y = (**this)[Y] & b[Y];
+ if (x && y) {
+ *this = CRect(*x, *y);
+ } else {
+ *(static_cast<Base*>(this)) = std::nullopt;
+ }
+ }
+ /** @brief Leave only the area overlapping with the argument.
+ * If the argument is empty or the rectangles do not have any points
+ * in common, after calling this method the rectangle will be empty. */
+ void intersectWith(OptCRect const &b) {
+ if (b) {
+ intersectWith(*b);
+ } else {
+ *(static_cast<Base*>(this)) = std::nullopt;
+ }
+ }
+ /** @brief Create or enlarge the rectangle to contain the given point.
+ * If the rectangle is empty, after calling this method it will be non-empty
+ * and it will contain only the given point. */
+ void expandTo(CPoint const &p) {
+ if (*this) {
+ (*this)->expandTo(p);
+ } else {
+ *this = CRect(p, p);
+ }
+ }
+ /// @}
+
+ /// @name Operators
+ /// @{
+ /** @brief Union with @a b */
+ GenericOptRect<C> &operator|=(OptCRect const &b) {
+ unionWith(b);
+ return *this;
+ }
+ /** @brief Intersect with @a b */
+ GenericOptRect<C> &operator&=(CRect const &b) {
+ intersectWith(b);
+ return *this;
+ }
+ /** @brief Intersect with @a b */
+ GenericOptRect<C> &operator&=(OptCRect const &b) {
+ intersectWith(b);
+ return *this;
+ }
+ /** @brief Test for equality.
+ * All empty rectangles are equal. */
+ bool operator==(OptCRect const &other) const {
+ if (!*this != !other) return false;
+ return *this ? (**this == *other) : true;
+ }
+ bool operator==(CRect const &other) const {
+ if (!*this) return false;
+ return **this == other;
+ }
+ /// @}
+};
+
+template <typename C>
+inline void GenericRect<C>::unionWith(OptCRect const &b) {
+ if (b) {
+ unionWith(*b);
+ }
+}
+template <typename C>
+inline bool GenericRect<C>::intersects(OptCRect const &r) const {
+ return r && intersects(*r);
+}
+template <typename C>
+inline bool GenericRect<C>::contains(OptCRect const &r) const {
+ return !r || contains(*r);
+}
+
+template <typename C>
+inline std::ostream &operator<<(std::ostream &out, GenericRect<C> const &r) {
+ out << "Rect " << r[X] << " x " << r[Y];
+ return out;
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_RECT_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/include/2geom/geom.h b/include/2geom/geom.h
new file mode 100644
index 0000000..7393ff4
--- /dev/null
+++ b/include/2geom/geom.h
@@ -0,0 +1,66 @@
+/**
+ * \file
+ * \brief Various geometrical calculations
+ *
+ * Authors:
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *
+ * Copyright (C) 1999-2002 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_GEOM_H
+#define LIB2GEOM_SEEN_GEOM_H
+
+//TODO: move somewhere else
+
+#include <vector>
+#include <2geom/forward.h>
+#include <optional>
+#include <2geom/bezier-curve.h>
+#include <2geom/line.h>
+
+namespace Geom {
+
+std::optional<Geom::LineSegment>
+rect_line_intersect(Geom::Rect &r,
+ Geom::LineSegment ls);
+
+int centroid(std::vector<Geom::Point> const &p, Geom::Point& centroid, double &area);
+
+}
+
+#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/include/2geom/int-interval.h b/include/2geom/int-interval.h
new file mode 100644
index 0000000..0faf48d
--- /dev/null
+++ b/include/2geom/int-interval.h
@@ -0,0 +1,63 @@
+/**
+ * \file
+ * \brief Closed interval of integer values
+ *//*
+ * Copyright 2011 Krzysztof Kosiński <tweenk.pl@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_INT_INTERVAL_H
+#define LIB2GEOM_SEEN_INT_INTERVAL_H
+
+#include <2geom/coord.h>
+#include <2geom/generic-interval.h>
+
+namespace Geom {
+
+/**
+ * @brief Range of integers that is never empty.
+ * @ingroup Primitives
+ */
+typedef GenericInterval<IntCoord> IntInterval;
+
+/**
+ * @brief Range of integers that can be empty.
+ * @ingroup Primitives
+ */
+typedef GenericOptInterval<IntCoord> OptIntInterval;
+
+} // namespace Geom
+#endif // !LIB2GEOM_SEEN_INT_INTERVAL_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/include/2geom/int-point.h b/include/2geom/int-point.h
new file mode 100644
index 0000000..6dbed11
--- /dev/null
+++ b/include/2geom/int-point.h
@@ -0,0 +1,202 @@
+/**
+ * \file
+ * \brief Cartesian point / 2D vector with integer coordinates
+ *//*
+ * Copyright 2011 Krzysztof Kosiński <tweenk.pl@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_INT_POINT_H
+#define LIB2GEOM_SEEN_INT_POINT_H
+
+#include <stdexcept>
+#include <boost/operators.hpp>
+#include <2geom/coord.h>
+
+namespace Geom {
+
+/**
+ * @brief Two-dimensional point with integer coordinates.
+ *
+ * This class is an exact equivalent of Point, except it stores integer coordinates.
+ * Integer points are useful in contexts related to rasterized graphics, for example
+ * for bounding boxes when rendering SVG.
+ *
+ * @see Point
+ * @ingroup Primitives */
+class IntPoint
+ : boost::additive< IntPoint
+ , boost::totally_ordered< IntPoint
+ , boost::multiplicative< IntPoint, IntCoord
+ , boost::multiplicative< IntPoint
+ > > > >
+{
+ IntCoord _pt[2] = { 0, 0 };
+public:
+ /// @name Create integer points
+ /// @{
+ /** Construct a point at the origin. */
+ IntPoint() = default;
+ /** Construct a point from its coordinates. */
+ IntPoint(IntCoord x, IntCoord y)
+ : _pt{ x, y }
+ {}
+ /// @}
+
+ /// @name Access the coordinates of a point
+ /// @{
+ IntCoord operator[](unsigned i) const {
+ if ( i > Y ) throw std::out_of_range("index out of range");
+ return _pt[i];
+ }
+ IntCoord &operator[](unsigned i) {
+ if ( i > Y ) throw std::out_of_range("index out of range");
+ return _pt[i];
+ }
+ IntCoord operator[](Dim2 d) const { return _pt[d]; }
+ IntCoord &operator[](Dim2 d) { return _pt[d]; }
+
+ IntCoord x() const noexcept { return _pt[X]; }
+ IntCoord &x() noexcept { return _pt[X]; }
+ IntCoord y() const noexcept { return _pt[Y]; }
+ IntCoord &y() noexcept { return _pt[Y]; }
+ /// @}
+
+ /// @name Vector-like arithmetic operations
+ /// @{
+ IntPoint operator-() const {
+ return IntPoint(-_pt[X], -_pt[Y]);
+ }
+ IntPoint &operator+=(IntPoint const &o) {
+ _pt[X] += o._pt[X];
+ _pt[Y] += o._pt[Y];
+ return *this;
+ }
+ IntPoint &operator-=(IntPoint const &o) {
+ _pt[X] -= o._pt[X];
+ _pt[Y] -= o._pt[Y];
+ return *this;
+ }
+ IntPoint &operator*=(IntPoint const &o) {
+ _pt[X] *= o._pt[X];
+ _pt[Y] *= o._pt[Y];
+ return *this;
+ }
+ IntPoint &operator*=(IntCoord o) {
+ _pt[X] *= o;
+ _pt[Y] *= o;
+ return *this;
+ }
+ IntPoint &operator/=(IntPoint const &o) {
+ _pt[X] /= o._pt[X];
+ _pt[Y] /= o._pt[Y];
+ return *this;
+ }
+ IntPoint &operator/=(IntCoord o) {
+ _pt[X] /= o;
+ _pt[Y] /= o;
+ return *this;
+ }
+ /// @}
+
+ /// @name Various utilities
+ /// @{
+ /** @brief Equality operator. */
+ bool operator==(IntPoint const &in_pnt) const {
+ return ((_pt[X] == in_pnt[X]) && (_pt[Y] == in_pnt[Y]));
+ }
+ /** @brief Lexicographical ordering for points.
+ * Y coordinate is regarded as more significant. When sorting according to this
+ * ordering, the points will be sorted according to the Y coordinate, and within
+ * points with the same Y coordinate according to the X coordinate. */
+ bool operator<(IntPoint const &p) const {
+ return ( ( _pt[Y] < p[Y] ) ||
+ (( _pt[Y] == p[Y] ) && ( _pt[X] < p[X] )));
+ }
+ /// @}
+
+ /** @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. */
+ 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::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::LexLess<Y> {
+ bool operator()(IntPoint const &a, IntPoint const &b) const {
+ return a[Y] < b[Y] || (a[Y] == b[Y] && a[X] < b[X]);
+ }
+};
+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
+
+#endif // !SEEN_GEOM_INT_POINT_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/include/2geom/int-rect.h b/include/2geom/int-rect.h
new file mode 100644
index 0000000..567d42d
--- /dev/null
+++ b/include/2geom/int-rect.h
@@ -0,0 +1,75 @@
+/**
+ * \file
+ * \brief Axis-aligned rectangle with integer coordinates
+ *//*
+ * Copyright 2011 Krzysztof Kosiński <tweenk.pl@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_INT_RECT_H
+#define LIB2GEOM_SEEN_INT_RECT_H
+
+#include <2geom/coord.h>
+#include <2geom/int-interval.h>
+#include <2geom/generic-rect.h>
+
+namespace Geom {
+
+typedef GenericRect<IntCoord> IntRect;
+typedef GenericOptRect<IntCoord> OptIntRect;
+
+// the functions below do not work when defined generically
+inline OptIntRect operator&(IntRect const &a, IntRect const &b) {
+ OptIntRect ret(a);
+ ret.intersectWith(b);
+ return ret;
+}
+inline OptIntRect intersect(IntRect const &a, IntRect const &b) {
+ return a & b;
+}
+inline OptIntRect intersect(OptIntRect const &a, OptIntRect const &b) {
+ return a & b;
+}
+inline IntRect unify(IntRect const &a, IntRect const &b) {
+ return a | b;
+}
+inline OptIntRect unify(OptIntRect const &a, OptIntRect const &b) {
+ return a | b;
+}
+
+} // end namespace Geom
+
+#endif // !LIB2GEOM_SEEN_INT_RECT_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/include/2geom/intersection-graph.h b/include/2geom/intersection-graph.h
new file mode 100644
index 0000000..940c43c
--- /dev/null
+++ b/include/2geom/intersection-graph.h
@@ -0,0 +1,259 @@
+/**
+ * \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 {
+
+/** @class PathIntersectionGraph
+ * @brief Intermediate data for computing Boolean operations on paths.
+ *
+ * This class implements the Greiner-Hormann clipping algorithm,
+ * with improvements inspired by Foster and Overfelt as well as some
+ * original contributions.
+ *
+ * For the purposes of boolean operations, a shape is defined as a PathVector
+ * using the "even-odd" rule, i.e., regions with odd winding are considered part
+ * of the shape, whereas regions with even winding are not.
+ *
+ * For this reason, the two path-vectors are sometimes called "shapes" or "operands" of
+ * the boolean operation. Each path-vector may contain several paths, which are called
+ * either "paths" or "components" in the documentation.
+ *
+ * @ingroup Paths
+ */
+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:
+ /** @brief Construct a path intersection graph for two shapes described via their boundaries.
+ * The boundaries are passed as path-vectors.
+ *
+ * @param a – the first operand, also referred to as operand A.
+ * @param b – the second operand, also referred to as operand B.
+ * @param precision – precision setting used for intersection calculations.
+ */
+ PathIntersectionGraph(PathVector const &a, PathVector const &b, Coord precision = EPSILON);
+
+ /**
+ * @brief Get the union of the shapes, A ∪ B.
+ *
+ * A point belongs to the union if and only if it belongs to at least one of the operands.
+ *
+ * @return A path-vector describing the union of the operands A and B.
+ */
+ PathVector getUnion();
+
+ /**
+ * @brief Get the intersection of the shapes, A ∩ B.
+ *
+ * A point belongs to the intersection if and only if it belongs to both shapes.
+ *
+ * @return A path-vector describing the intersection of the operands A and B.
+ */
+ PathVector getIntersection();
+
+ /**
+ * @brief Get the difference of the shapes, A ∖ B.
+ *
+ * A point belongs to the difference if and only if it belongs to A but not to B.
+ *
+ * @return A path-vector describing the difference of the operands A and B.
+ */
+ PathVector getAminusB();
+
+ /**
+ * @brief Get the opposite difference of the shapes, B ∖ A.
+ *
+ * A point belongs to the difference if and only if it belongs to B but not to A.
+ *
+ * @return A path-vector describing the difference of the operands B and A.
+ */
+ PathVector getBminusA();
+
+ /**
+ * @brief Get the symmetric difference of the shapes, A ∆ B.
+ *
+ * A point belongs to the symmetric difference if and only if it belongs to one of the two
+ * shapes A or B, but not both. This is equivalent to the logical XOR operation: the elements
+ * of A ∆ B are points which are in A XOR in B.
+ *
+ * @return A path-vector describing the symmetric difference of the operands A and B.
+ */
+ PathVector getXOR();
+
+ /// Returns the number of intersections used when computing Boolean operations.
+ std::size_t size() const;
+
+ /**
+ * @brief Get the geometric points where the two path-vectors intersect.
+ *
+ * Degenerate intersection points, where the shapes merely "kiss", are not retured.
+ *
+ * @param defective – whether to return only the defective crossings or only the true crossings.
+ * @return If defective is true, returns a vector containing all defective intersection points,
+ * i.e., points that are neither true transverse intersections nor degenerate intersections.
+ * If defective is false, returns all true transverse intersections.
+ */
+ std::vector<Point> intersectionPoints(bool defective = false) const;
+
+ /**
+ * @brief Get the geometric points located on path portions between consecutive intersections.
+ *
+ * These points were used for the winding number calculations which determined which path portions
+ * lie inside the other shape and which lie outside.
+ *
+ * @return A vector containing all sample points used for winding calculations.
+ */
+ std::vector<Point> windingPoints() const {
+ return _winding_points;
+ }
+
+ void fragments(PathVector &in, PathVector &out) const;
+
+
+ bool valid() const { return _graph_valid; }
+
+private:
+ enum InOutFlag {
+ INSIDE,
+ OUTSIDE,
+ BOTH
+ };
+
+ struct IntersectionVertex {
+ boost::intrusive::list_member_hook<> _hook;
+ boost::intrusive::list_member_hook<> _proc_hook;
+ PathVectorTime pos; ///< Intersection time.
+ Point p; ///< Geometric position of the intersection point; guarantees that endpoints are exact.
+ IntersectionVertex *neighbor; ///< A pointer to the corresponding vertex on the other shape.
+ /** Tells us whether the edge originating at this intersection lies inside or outside of
+ * the shape given by the other path-vector. The "edge originating" at this intersection is
+ * the portion of the path between this intersection and the next intersection, in the
+ * direction of increasing path time. */
+ InOutFlag next_edge;
+ unsigned which; ///< Index of the operand path-vector that this intersection vertex lies on.
+ /** Whether the intersection is defective, which means that for some reason the paths
+ * neither cross transversally through each other nor "kiss" at a common tangency point.
+ */
+ bool defective;
+ };
+
+ typedef boost::intrusive::list
+ < IntersectionVertex
+ , boost::intrusive::member_hook
+ < IntersectionVertex
+ , boost::intrusive::list_member_hook<>
+ , &IntersectionVertex::_hook
+ >
+ > IntersectionList;
+
+ typedef boost::intrusive::list
+ < IntersectionVertex
+ , boost::intrusive::member_hook
+ < IntersectionVertex
+ , boost::intrusive::list_member_hook<>
+ , &IntersectionVertex::_proc_hook
+ >
+ > UnprocessedList;
+
+ /// Stores processed intersection information for a single path in an operand path-vector.
+ struct PathData {
+ IntersectionList xlist; ///< List of crossings on this particular path.
+ std::size_t path_index; ///< Index of the path in its path-vector.
+ int which; ///< Index of the path-vector (in PathIntersectionGraph::_pv) that the path belongs to.
+ /** Whether this path as a whole is contained INSIDE or OUTSIDE relative to the other path-vector.
+ * The value BOTH means that some portions of the path are inside while others are outside.
+ */
+ InOutFlag status;
+
+ PathData(int w, std::size_t pi)
+ : path_index(pi)
+ , which(w)
+ , status(BOTH)
+ {}
+ };
+
+ struct IntersectionVertexLess;
+ typedef IntersectionList::iterator ILIter;
+ typedef IntersectionList::const_iterator CILIter;
+
+ PathVector _getResult(bool enter_a, bool enter_b);
+ void _handleNonintersectingPaths(PathVector &result, unsigned which, bool inside);
+ void _prepareArguments();
+ bool _prepareIntersectionLists(Coord precision);
+ void _assignEdgeWindingParities(Coord precision);
+ void _assignComponentStatusFromDegenerateIntersections();
+ void _removeDegenerateIntersections();
+ void _verify();
+
+ ILIter _getNeighbor(ILIter iter);
+ PathData &_getPathData(ILIter iter);
+
+ PathVector _pv[2]; ///< Stores the two operand path-vectors, A at _pv[0] and B at _pv[1].
+ boost::ptr_vector<IntersectionVertex> _xs; ///< Stores all crossings between the two shapes.
+ boost::ptr_vector<PathData> _components[2]; ///< Stores the crossing information for the operands.
+ UnprocessedList _ulist; ///< Temporarily holds all unprocessed during a boolean operation.
+ bool _graph_valid; ///< Whether all intersections are regular.
+ /** Stores sample points located on paths of the operand path-vectors,
+ * between consecutive intersections.
+ */
+ std::vector<Point> _winding_points;
+
+ friend std::ostream &operator<<(std::ostream &, PathIntersectionGraph const &);
+};
+
+std::ostream &operator<<(std::ostream &os, PathIntersectionGraph const &pig);
+
+} // 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:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/intersection.h b/include/2geom/intersection.h
new file mode 100644
index 0000000..8a23811
--- /dev/null
+++ b/include/2geom/intersection.h
@@ -0,0 +1,147 @@
+/**
+ * \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
+ : boost::totally_ordered< Intersection<TimeA, TimeB> >
+{
+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);
+ }
+
+ bool operator==(Intersection const &other) const {
+ if (first != other.first) return false;
+ if (second != other.second) return false;
+ return true;
+ }
+ bool operator<(Intersection const &other) const {
+ if (first < other.first) return true;
+ if (first == other.first && second < other.second) return true;
+ return false;
+ }
+
+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;
+};
+
+template <typename A, typename B> inline
+std::vector< Intersection<A, B> > transpose(std::vector< Intersection<B, A> > const &in) {
+ std::vector< Intersection<A, B> > result;
+ for (std::size_t i = 0; i < in.size(); ++i) {
+ result.push_back(Intersection<A, B>(in[i].second, in[i].first, in[i].point()));
+ }
+ return result;
+}
+
+template <typename T> inline
+void transpose_in_place(std::vector< Intersection<T, T> > &xs) {
+ for (std::size_t i = 0; i < xs.size(); ++i) {
+ std::swap(xs[i].first, xs[i].second);
+ }
+}
+
+typedef Intersection<> ShapeIntersection;
+
+
+} // 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:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/interval.h b/include/2geom/interval.h
new file mode 100644
index 0000000..11c8f28
--- /dev/null
+++ b/include/2geom/interval.h
@@ -0,0 +1,245 @@
+/**
+ * \file
+ * \brief Simple closed interval class
+ *//*
+ * Copyright 2007 Michael Sloan <mgsloan@gmail.com>
+ *
+ * Original Rect/Range code by:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * bulia byak <buliabyak@users.sf.net>
+ * MenTaLguY <mental@rydia.net>
+ *
+ * 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.
+ *
+ */
+#ifndef LIB2GEOM_SEEN_INTERVAL_H
+#define LIB2GEOM_SEEN_INTERVAL_H
+
+#include <boost/none.hpp>
+#include <boost/operators.hpp>
+#include <2geom/coord.h>
+#include <2geom/math-utils.h>
+#include <2geom/generic-interval.h>
+#include <2geom/int-interval.h>
+
+namespace Geom {
+
+/**
+ * @brief Range of real numbers that is never empty.
+ *
+ * Intervals are closed ranges \f$[a, b]\f$, which means they include their endpoints.
+ * To use them as open ranges, you can use the interiorContains() methods.
+ *
+ * @ingroup Primitives
+ */
+class Interval
+ : public GenericInterval<Coord>
+{
+ typedef GenericInterval<Coord> Base;
+public:
+ /// @name Create intervals.
+ /// @{
+ /** @brief Create an interval that contains only zero. */
+ Interval() {}
+ /** @brief Create an interval that contains a single point. */
+ explicit Interval(Coord u) : Base(u) {}
+ /** @brief Create an interval that contains all points between @c u and @c v. */
+ Interval(Coord u, Coord v) : Base(u,v) {}
+ /** @brief Convert from integer interval */
+ Interval(IntInterval const &i) : Base(i.min(), i.max()) {}
+ Interval(Base const &b) : Base(b) {}
+
+ /** @brief Create an interval containing a range of values.
+ * The resulting interval will contain all values from the given range.
+ * The return type of iterators must be convertible to Coord. The given range
+ * must not be empty. For potentially empty ranges, see OptInterval.
+ * @param start Beginning of the range
+ * @param end End of the range
+ * @return Interval that contains all values from [start, end). */
+ template <typename InputIterator>
+ static Interval from_range(InputIterator start, InputIterator end) {
+ Interval result = Base::from_range(start, end);
+ return result;
+ }
+ /** @brief Create an interval from a C-style array of values it should contain. */
+ static Interval from_array(Coord const *c, unsigned n) {
+ Interval result = from_range(c, c+n);
+ return result;
+ }
+ /// @}
+
+ /// @name Inspect contained values.
+ /// @{
+ /// Check whether both endpoints are finite.
+ bool isFinite() const {
+ return std::isfinite(min()) && std::isfinite(max());
+ }
+ /** @brief Map the interval [0,1] onto this one.
+ * This method simply performs 1D linear interpolation between endpoints. */
+ Coord valueAt(Coord t) const {
+ return lerp(t, min(), max());
+ }
+ /** @brief Compute a time value that maps to the given value.
+ * The supplied value does not need to be in the interval for this method to work. */
+ Coord timeAt(Coord v) const {
+ return (v - min()) / extent();
+ }
+ /// Find closest time in [0,1] that maps to the given value. */
+ Coord nearestTime(Coord v) const {
+ if (v <= min()) return 0;
+ if (v >= max()) return 1;
+ return timeAt(v);
+ }
+ /// @}
+
+ /// @name Test coordinates and other intervals for inclusion.
+ /// @{
+ /** @brief Check whether the interior of the interval includes this number.
+ * Interior means all numbers in the interval except its ends. */
+ bool interiorContains(Coord val) const { return min() < val && val < max(); }
+ /** @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(); }
+ /// Check whether the number is contained in the union of the interior and the lower boundary.
+ bool lowerContains(Coord val) const { 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());
+ }
+ /// @}
+
+ /// @name Operators
+ /// @{
+ // IMPL: ScalableConcept
+ /** @brief Scale an interval */
+ Interval &operator*=(Coord s) {
+ using std::swap;
+ _b[0] *= s;
+ _b[1] *= s;
+ 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) swap(_b[0], _b[1]);
+ return *this;
+ }
+ /** @brief Multiply two intervals.
+ * Product is defined as the set of points that can be obtained by multiplying
+ * any value from the second operand by any value from the first operand:
+ * \f$S = \{x \in A, y \in B: x * y\}\f$ */
+ Interval &operator*=(Interval const &o) {
+ // TODO implement properly
+ Coord mn = min(), mx = max();
+ expandTo(mn * o.min());
+ expandTo(mn * o.max());
+ expandTo(mx * o.min());
+ expandTo(mx * o.max());
+ return *this;
+ }
+ bool operator==(IntInterval const &ii) const {
+ return min() == Coord(ii.min()) && max() == Coord(ii.max());
+ }
+ bool operator==(Interval const &other) const {
+ return Base::operator==(other);
+ }
+ /// @}
+
+ /// @name Rounding to integer values
+ /// @{
+ /** @brief Return the smallest integer interval which contains this one. */
+ IntInterval roundOutwards() const {
+ IntInterval ret(floor(min()), ceil(max()));
+ return ret;
+ }
+ /** @brief Return the largest integer interval which is contained in this one. */
+ OptIntInterval roundInwards() const {
+ IntCoord u = ceil(min()), v = floor(max());
+ if (u > v) { OptIntInterval e; return e; }
+ IntInterval ret(u, v);
+ return ret;
+ }
+ /// @}
+};
+
+/**
+ * @brief Range of real numbers that can be empty.
+ * @ingroup Primitives
+ */
+class OptInterval
+ : public GenericOptInterval<Coord>
+{
+ typedef GenericOptInterval<Coord> Base;
+public:
+ using Base::Base;
+ using Base::operator==;
+ using Base::operator!=;
+
+ OptInterval(Base const &b) : Base(b) {}
+
+ /** @brief Promote from IntInterval. */
+ OptInterval(IntInterval const &i) : Base(Interval(i)) {}
+ /** @brief Promote from OptIntInterval. */
+ OptInterval(OptIntInterval const &i) : Base() {
+ if (i) *this = Interval(*i);
+ }
+};
+
+// functions required for Python bindings
+inline Interval unify(Interval const &a, Interval const &b)
+{
+ Interval r = a | b;
+ return r;
+}
+inline OptInterval intersect(Interval const &a, Interval const &b)
+{
+ OptInterval r = a & b;
+ return r;
+}
+
+} // end namespace Geom
+
+#endif //SEEN_INTERVAL_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/include/2geom/intervaltree/interval_tree.h b/include/2geom/intervaltree/interval_tree.h
new file mode 100644
index 0000000..85f91f9
--- /dev/null
+++ b/include/2geom/intervaltree/interval_tree.h
@@ -0,0 +1,126 @@
+#ifndef E_INTERVAL_TREE
+#define E_INTERVAL_TREE
+
+// From Emin Martinian, licenced LGPL and MPL with permission
+
+#include <vector>
+#include <math.h>
+#include <limits>
+#include <iostream>
+
+using std::vector;
+
+// The interval_tree.h and interval_tree.cc files contain code for
+// interval trees implemented using red-black-trees as described in
+// the book _Introduction_To_Algorithms_ by Cormen, Leisserson,
+// and Rivest.
+
+// CONVENTIONS:
+// Function names: Each word in a function name begins with
+// a capital letter. An example funcntion name is
+// CreateRedTree(a,b,c). Furthermore, each function name
+// should begin with a capital letter to easily distinguish
+// them from variables.
+//
+// Variable names: Each word in a variable name begins with
+// a capital letter EXCEPT the first letter of the variable
+// name. For example, int newLongInt. Global variables have
+// names beginning with "g". An example of a global
+// variable name is gNewtonsConstant.
+
+
+#ifndef MAX_INT
+#define MAX_INT INT_MAX // some architechturs define INT_MAX not MAX_INT
+#endif
+
+// The Interval class is an Abstract Base Class. This means that no
+// instance of the Interval class can exist. Only classes which
+// inherit from the Interval class can exist. Furthermore any class
+// which inherits from the Interval class must define the member
+// functions GetLowPoint and GetHighPoint.
+//
+// The GetLowPoint should return the lowest point of the interval and
+// the GetHighPoint should return the highest point of the interval.
+
+class Interval {
+public:
+ Interval();
+ virtual ~Interval();
+ virtual int GetLowPoint() const = 0;
+ virtual int GetHighPoint() const = 0;
+ virtual void Print() const;
+};
+
+class IntervalTreeNode {
+ friend class IntervalTree;
+public:
+ void Print(IntervalTreeNode*,
+ IntervalTreeNode*) const;
+ IntervalTreeNode();
+ IntervalTreeNode(Interval *);
+ ~IntervalTreeNode();
+protected:
+ Interval * storedInterval;
+ int key;
+ int high;
+ int maxHigh;
+ bool red; /* if red=0 then the node is black */
+ IntervalTreeNode * left;
+ IntervalTreeNode * right;
+ IntervalTreeNode * parent;
+};
+
+struct it_recursion_node {
+public:
+ /* this structure stores the information needed when we take the */
+ /* right branch in searching for intervals but possibly come back */
+ /* and check the left branch as well. */
+
+ IntervalTreeNode * start_node;
+ unsigned int parentIndex;
+ bool tryRightBranch;
+} ;
+
+
+class IntervalTree {
+public:
+ IntervalTree();
+ ~IntervalTree();
+ void Print() const;
+ Interval * DeleteNode(IntervalTreeNode *);
+ IntervalTreeNode * Insert(Interval *);
+ IntervalTreeNode * GetPredecessorOf(IntervalTreeNode *) const;
+ IntervalTreeNode * GetSuccessorOf(IntervalTreeNode *) const;
+ vector<void *> Enumerate(int low, int high) ;
+ void CheckAssumptions() const;
+protected:
+ /* A sentinel is used for root and for nil. These sentinels are */
+ /* created when ITTreeCreate is caled. root->left should always */
+ /* point to the node which is the root of the tree. nil points to a */
+ /* node which should always be black but has arbitrary children and */
+ /* parent and no key or info. The point of using these sentinels is so */
+ /* that the root and nil nodes do not require special cases in the code */
+ IntervalTreeNode * root;
+ IntervalTreeNode * nil;
+ void LeftRotate(IntervalTreeNode *);
+ void RightRotate(IntervalTreeNode *);
+ void TreeInsertHelp(IntervalTreeNode *);
+ void TreePrintHelper(IntervalTreeNode *) const;
+ void FixUpMaxHigh(IntervalTreeNode *);
+ void DeleteFixUp(IntervalTreeNode *);
+ void CheckMaxHighFields(IntervalTreeNode *) const;
+ int CheckMaxHighFieldsHelper(IntervalTreeNode * y,
+ const int currentHigh,
+ int match) const;
+private:
+ unsigned int recursionNodeStackSize;
+ it_recursion_node * recursionNodeStack;
+ unsigned int currentParent;
+ unsigned int recursionNodeStackTop;
+};
+
+
+#endif
+
+
+
diff --git a/include/2geom/line.h b/include/2geom/line.h
new file mode 100644
index 0000000..9a56602
--- /dev/null
+++ b/include/2geom/line.h
@@ -0,0 +1,605 @@
+/**
+ * \file
+ * \brief Infinite straight line
+ *//*
+ * 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_LINE_H
+#define LIB2GEOM_SEEN_LINE_H
+
+#include <cmath>
+#include <optional>
+#include <2geom/bezier-curve.h> // for LineSegment
+#include <2geom/rect.h>
+#include <2geom/crossing.h>
+#include <2geom/exception.h>
+#include <2geom/ray.h>
+#include <2geom/angle.h>
+#include <2geom/intersection.h>
+
+namespace Geom
+{
+
+// class docs in cpp file
+class Line
+ : boost::equality_comparable< Line >
+{
+private:
+ Point _initial;
+ Point _final;
+public:
+ /// @name Creating lines.
+ /// @{
+ /** @brief Create a default horizontal line.
+ * Creates a line with unit speed going in +X direction. */
+ Line()
+ : _initial(0,0), _final(1,0)
+ {}
+ /** @brief Create a line with the specified inclination.
+ * @param origin One of the points on the line
+ * @param angle Angle of the line in mathematical convention */
+ Line(Point const &origin, Coord angle)
+ : _initial(origin)
+ {
+ Point v;
+ sincos(angle, v[Y], v[X]);
+ _final = _initial + v;
+ }
+
+ /** @brief Create a line going through two points.
+ * 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() */
+ Line(double a, double b, double c) {
+ setCoefficients(a, b, c);
+ }
+
+ /// Create a line by extending a line segment.
+ explicit Line(LineSegment const &seg)
+ : _initial(seg.initialPoint())
+ , _final(seg.finalPoint())
+ {}
+
+ /// Create a line by extending a ray.
+ explicit Line(Ray const &r)
+ : _initial(r.origin())
+ , _final(r.origin() + r.vector())
+ {}
+
+ /// 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_vector(Point const &o, Point const &v) {
+ Line l(o, o + v);
+ return l;
+ }
+
+ Line* duplicate() const {
+ return new Line(*this);
+ }
+ /// @}
+
+ /// @name Retrieve and set the line's parameters.
+ /// @{
+
+ /// Get the line's origin point.
+ Point origin() const { return _initial; }
+ /** @brief Get the line's raw direction vector.
+ * The length of the retrieved vector is equal to the length of a segment parametrized by
+ * a time interval of length 1. */
+ Point vector() const { return _final - _initial; }
+ /** @brief Get the line's normalized direction vector.
+ * The retrieved vector is normalized to unit length. */
+ Point versor() const { return (_final - _initial).normalized(); }
+ /// Angle the line makes with the X axis, in mathematical convention.
+ Coord angle() const {
+ 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;
+ }
+
+ /** @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;
+ }
+ /** @brief Set the speed of the line.
+ * Origin remains unchanged. */
+ void setVector(Point const &v) {
+ _final = _initial + v;
+ }
+
+ /** @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;
+ }
+
+ /// Set a line based on two points it should pass through.
+ void setPoints(Point const &a, Point const &b) {
+ _initial = a;
+ _final = b;
+ }
+
+ /** @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;
+
+ /// 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 or exactly one point */
+ bool isDegenerate() const {
+ return _initial == _final;
+ }
+ /// Check if the line is horizontal (y is constant).
+ bool isHorizontal() const {
+ return _initial[Y] == _final[Y];
+ }
+ /// Check if the line is vertical (x is constant).
+ bool isVertical() const {
+ return _initial[X] == _final[X];
+ }
+
+ /** @brief Reparametrize the line so that it has unit speed.
+ * Note that the direction of the line may also change. */
+ void normalize() {
+ // this helps with the nasty case of a line that starts somewhere far
+ // and ends very close to the origin
+ if (L2sq(_final) < L2sq(_initial)) {
+ std::swap(_initial, _final);
+ }
+ 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 lerp(t, _initial, _final);;
+ }
+
+ Coord valueAt(Coord t, Dim2 d) const {
+ return lerp(t, _initial[d], _final[d]);
+ }
+
+ Coord timeAt(Point const &p) const;
+
+ /** @brief Get a time value corresponding to a projection of a point on the line.
+ * @param p Arbitrary point.
+ * @return Time value corresponding to a point closest to @c p. */
+ Coord timeAtProjection(Point const& p) const {
+ if ( isDegenerate() ) return 0;
+ Point v = vector();
+ 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 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.
+ /// @{
+ 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;
+ }
+
+ /** @brief Same as segment(), but allocate the line segment dynamically. */
+ // TODO remove this?
+ Curve* portion(Coord f, Coord t) const {
+ LineSegment* seg = new LineSegment(pointAt(f), pointAt(t));
+ return seg;
+ }
+
+ /** @brief Create a segment of this line.
+ * @param f Time value for the initial point of the segment
+ * @param t Time value for the final point of the segment
+ * @return Created line segment */
+ LineSegment segment(Coord f, Coord t) const {
+ return LineSegment(pointAt(f), pointAt(t));
+ }
+
+ /// Return the portion of the line that is inside the given rectangle
+ std::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 vector (in the direction
+ * of increasing time values).
+ * @param t Time value where the ray should start
+ * @return Ray starting at t and going in the direction of the vector */
+ Ray ray(Coord t) {
+ Ray result;
+ result.setOrigin(pointAt(t));
+ result.setVector(vector());
+ return result;
+ }
+
+ /** @brief Create a derivative of the line.
+ * The new line will always be degenerate. Its origin will be equal to this
+ * line's vector. */
+ Line derivative() const {
+ Point v = vector();
+ Line result(v, v);
+ return result;
+ }
+
+ /// Create a line transformed by an affine transformation.
+ Line transformed(Affine const& m) const {
+ Line l(_initial * m, _final * m);
+ return l;
+ }
+
+ /** @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(vector()).normalized();
+ }
+
+ // what does this do?
+ Point normalAndDist(double & dist) const {
+ Point n = normal();
+ dist = -dot(n, _initial);
+ return n;
+ }
+
+ /// Compute an affine matrix representing a reflection about the line.
+ Affine reflection() const {
+ Point v = versor();
+ 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 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
+ * additionally 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 = vector();
+ 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;
+ }
+
+ Affine transformTo(Line const &other) const;
+ /// @}
+
+ std::vector<ShapeIntersection> intersect(Line const &other) const;
+ std::vector<ShapeIntersection> intersect(Ray const &r) const;
+ std::vector<ShapeIntersection> intersect(LineSegment const &ls) const;
+
+ template <typename T>
+ Line &operator*=(T const &tr) {
+ BOOST_CONCEPT_ASSERT((TransformConcept<T>));
+ _initial *= tr;
+ _final *= tr;
+ return *this;
+ }
+
+ 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;
+ }
+
+ template <typename T>
+ friend Line operator*(Line const &l, T const &tr) {
+ BOOST_CONCEPT_ASSERT((TransformConcept<T>));
+ Line result(l);
+ result *= tr;
+ return result;
+ }
+}; // end class Line
+
+/** @brief Removes intersections outside of the unit interval.
+ * A helper used to implement line segment intersections.
+ * @param xs Line intersections
+ * @param a Whether the first time value has to be in the unit interval
+ * @param b Whether the second time value has to be in the unit interval
+ * @return Appropriately filtered intersections */
+void filter_line_segment_intersections(std::vector<ShapeIntersection> &xs, bool a=false, bool b=true);
+void filter_ray_intersections(std::vector<ShapeIntersection> &xs, bool a=false, bool b=true);
+
+/// @brief Compute distance from point to line.
+/// @relates Line
+inline
+double distance(Point const &p, Line const &line)
+{
+ 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_near(Point const &p, Line const &line, double eps = EPSILON)
+{
+ return are_near(distance(p, line), 0, eps);
+}
+
+inline
+bool are_parallel(Line const &l1, Line const &l2, double eps = EPSILON)
+{
+ return are_near(cross(l1.vector(), l2.vector()), 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 be 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_same(Line const &l1, Line const &l2, double eps = EPSILON)
+{
+ return are_parallel(l1, l2, eps) && are_near(l1.origin(), l2, eps);
+}
+
+/// Test whether two lines are perpendicular.
+/// @relates Line
+inline
+bool are_orthogonal(Line const &l1, Line const &l2, double eps = EPSILON)
+{
+ return are_near(dot(l1.vector(), l2.vector()), 0, eps);
+}
+
+// evaluate the angle between l1 and l2 rotating l1 in cw direction
+// until it overlaps l2
+// the returned value is an angle in the interval [0, PI[
+inline
+double angle_between(Line const& l1, Line const& l2)
+{
+ double angle = angle_between(l1.vector(), l2.vector());
+ if (angle < 0) angle += M_PI;
+ if (angle == M_PI) angle = 0;
+ return angle;
+}
+
+inline
+double distance(Point const &p, LineSegment const &seg)
+{
+ double t = seg.nearestTime(p);
+ return distance(p, seg.pointAt(t));
+}
+
+inline
+bool are_near(Point const &p, LineSegment const &seg, double eps = EPSILON)
+{
+ 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 &p, Line const &line)
+{
+ Point d = line.vector().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 &p, Line const &line)
+{
+ Line result(line);
+ result.setOrigin(p);
+ return result;
+}
+
+// build a line passing by the middle point of _segment and orthogonal to it.
+inline
+Line make_bisector_line(LineSegment const& _segment)
+{
+ return make_orthogonal_line( middle_point(_segment), Line(_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)
+{
+ 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 &p, Line const &line)
+{
+ return line.pointAt(line.nearestTime(p));
+}
+
+inline
+LineSegment projection(LineSegment const &seg, Line const &line)
+{
+ return line.segment(line.nearestTime(seg.initialPoint()),
+ line.nearestTime(seg.finalPoint()));
+}
+
+inline
+std::optional<LineSegment> clip(Line const &l, Rect const &r) {
+ return l.clip(r);
+}
+
+
+namespace detail
+{
+
+OptCrossing intersection_impl(Ray const& r1, Line const& l2, unsigned int i);
+OptCrossing intersection_impl( LineSegment const& ls1,
+ Line const& l2,
+ unsigned int i );
+OptCrossing intersection_impl( LineSegment const& ls1,
+ Ray const& r2,
+ unsigned int i );
+}
+
+
+inline
+OptCrossing intersection(Ray const& r1, Line const& l2)
+{
+ return detail::intersection_impl(r1, l2, 0);
+
+}
+
+inline
+OptCrossing intersection(Line const& l1, Ray const& r2)
+{
+ return detail::intersection_impl(r2, l1, 1);
+}
+
+inline
+OptCrossing intersection(LineSegment const& ls1, Line const& l2)
+{
+ return detail::intersection_impl(ls1, l2, 0);
+}
+
+inline
+OptCrossing intersection(Line const& l1, LineSegment const& ls2)
+{
+ return detail::intersection_impl(ls2, l1, 1);
+}
+
+inline
+OptCrossing intersection(LineSegment const& ls1, Ray const& r2)
+{
+ return detail::intersection_impl(ls1, r2, 0);
+
+}
+
+inline
+OptCrossing intersection(Ray const& r1, LineSegment const& ls2)
+{
+ return detail::intersection_impl(ls2, r1, 1);
+}
+
+
+OptCrossing intersection(Line const& l1, Line const& l2);
+
+OptCrossing intersection(Ray const& r1, Ray const& r2);
+
+OptCrossing intersection(LineSegment const& ls1, LineSegment const& ls2);
+
+
+} // end namespace Geom
+
+
+#endif // LIB2GEOM_SEEN_LINE_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/include/2geom/linear.h b/include/2geom/linear.h
new file mode 100644
index 0000000..75c6e01
--- /dev/null
+++ b/include/2geom/linear.h
@@ -0,0 +1,167 @@
+/**
+ * \file
+ * \brief Linear fragment function class
+ *//*
+ * Authors:
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Michael Sloan <mgsloan@gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 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
+ * 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_LINEAR_H
+#define LIB2GEOM_SEEN_LINEAR_H
+
+#include <2geom/interval.h>
+#include <2geom/math-utils.h>
+
+namespace Geom {
+
+class SBasis;
+
+/**
+ * @brief Function that interpolates linearly between two values.
+ * @ingroup Fragments
+ */
+class Linear
+ : boost::additive< Linear
+ , boost::arithmetic< Linear, Coord
+ , boost::equality_comparable< Linear
+ > > >
+{
+public:
+ Coord a[2];
+ Linear() {a[0]=0; a[1]=0;}
+ Linear(Coord aa, Coord b) {a[0] = aa; a[1] = b;}
+ Linear(Coord aa) {a[0] = aa; a[1] = aa;}
+
+ Coord operator[](unsigned i) const {
+ assert(i < 2);
+ return a[i];
+ }
+ Coord &operator[](unsigned i) {
+ assert(i < 2);
+ return a[i];
+ }
+
+ //IMPL: FragmentConcept
+ typedef Coord output_type;
+ bool isZero(Coord eps=EPSILON) const { return are_near(a[0], 0., eps) && are_near(a[1], 0., eps); }
+ bool isConstant(Coord eps=EPSILON) const { return are_near(a[0], a[1], eps); }
+ bool isFinite() const { return std::isfinite(a[0]) && std::isfinite(a[1]); }
+
+ Coord at0() const { return a[0]; }
+ Coord &at0() { return a[0]; }
+ Coord at1() const { return a[1]; }
+ Coord &at1() { return a[1]; }
+
+ Coord valueAt(Coord t) const { return lerp(t, a[0], a[1]); }
+ Coord operator()(Coord t) const { return valueAt(t); }
+
+ // not very useful, but required for FragmentConcept
+ 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;
+
+ 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];
+ }
+ double hat() const {
+ return (a[1] + a[0])/2;
+ }
+
+ // addition of other Linears
+ Linear &operator+=(Linear const &other) {
+ a[0] += other.a[0];
+ a[1] += other.a[1];
+ return *this;
+ }
+ Linear &operator-=(Linear const &other) {
+ a[0] -= other.a[0];
+ a[1] -= other.a[1];
+ return *this;
+ }
+
+ //
+ Linear &operator+=(Coord x) {
+ a[0] += x; a[1] += x;
+ return *this;
+ }
+ Linear &operator-=(Coord x) {
+ a[0] -= x; a[1] -= x;
+ return *this;
+ }
+ Linear &operator*=(Coord x) {
+ a[0] *= x; a[1] *= x;
+ return *this;
+ }
+ Linear &operator/=(Coord x) {
+ a[0] /= x; a[1] /= x;
+ return *this;
+ }
+ Linear operator-() const {
+ Linear ret(-a[0], -a[1]);
+ return ret;
+ }
+
+ bool operator==(Linear const &other) const {
+ return a[0] == other.a[0] && a[1] == other.a[1];
+ }
+};
+
+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;
+}
+
+} // end namespace Geom
+
+#endif //LIB2GEOM_SEEN_LINEAR_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/include/2geom/math-utils.h b/include/2geom/math-utils.h
new file mode 100644
index 0000000..4c35a80
--- /dev/null
+++ b/include/2geom/math-utils.h
@@ -0,0 +1,140 @@
+/**
+ * \file
+ * \brief Low level math functions and compatibility wrappers
+ *//*
+ * Authors:
+ * Johan Engelen <goejendaagh@zonnet.nl>
+ * Michael G. Sloan <mgsloan@gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Copyright 2006-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 LIB2GEOM_SEEN_MATH_UTILS_H
+#define LIB2GEOM_SEEN_MATH_UTILS_H
+
+#include <math.h> // sincos is usually only available in math.h
+#include <array>
+#include <cmath>
+#include <utility> // for std::pair
+#include <boost/math/special_functions/fpclassify.hpp>
+
+namespace Geom {
+
+/** @brief Sign function - indicates the sign of a numeric type.
+ * Mathsy people will know this is basically the derivative of abs, except for the fact
+ * that it is defined on 0.
+ * @return -1 when x is negative, 1 when positive, and 0 if equal to 0. */
+template <class T> inline int sgn(const T& x) {
+ return (x < 0 ? -1 : (x > 0 ? 1 : 0) );
+// can we 'optimize' this with:
+// return ( (T(0) < x) - (x < T(0)) );
+}
+
+template <class T> inline T sqr(const T& x) {return x * x;}
+template <class T> inline T cube(const T& x) {return x * x * x;}
+
+/** Between function - returns true if a number x is within a range: (min < x) && (max > x).
+ * The values delimiting the range and the number must have the same type.
+ */
+template <class T> inline const T& between (const T& min, const T& max, const T& x)
+ { return (min < x) && (max > x); }
+
+/** @brief Returns @a x rounded to the nearest multiple of \f$10^{p}\f$.
+
+ Implemented in terms of round, i.e. we make no guarantees as to what happens if x is
+ half way between two rounded numbers.
+
+ Note: places is the number of decimal places without using scientific (e) notation, not the
+ number of significant figures. This function may not be suitable for values of x whose
+ magnitude is so far from 1 that one would want to use scientific (e) notation.
+
+ places may be negative: e.g. places = -2 means rounding to a multiple of .01
+**/
+inline double decimal_round(double x, int p) {
+ //TODO: possibly implement with modulus instead?
+ double const multiplier = ::pow(10.0, p);
+ return ::round( x * multiplier ) / multiplier;
+}
+
+/** @brief Simultaneously compute a sine and a cosine of the same angle.
+ * This function can be up to 2 times faster than separate computation, depending
+ * on the platform. It uses the standard library function sincos() if available.
+ * @param angle Angle
+ * @param sin_ Variable that will store the sine
+ * @param cos_ Variable that will store the cosine */
+inline void sincos(double angle, double &sin_, double &cos_) {
+#ifdef HAVE_SINCOS
+ ::sincos(angle, &sin_, &cos_);
+#else
+ sin_ = ::sin(angle);
+ cos_ = ::cos(angle);
+#endif
+}
+
+/** @brief Scale the doubles in the passed array to make them "reasonably large".
+ *
+ * All doubles in the passed array will get scaled by the same power of 2 (which is
+ * a lossless operation) in such a way that their geometric average gets closer to 1.
+ *
+ * @tparam N The size of the passed array.
+ * @param[in,out] values The doubles to be rescaled in place.
+ * @return The exponent in the power of two by which the doubles got scaled.
+ */
+template <size_t N>
+inline int rescale_homogenous(std::array<double, N> &values)
+{
+ if constexpr (N == 0) {
+ return 0;
+ }
+ std::array<int, N> exponents;
+ std::array<double, N> mantissas;
+ int average = 0;
+ for (size_t i = 0; i < N; i++) {
+ mantissas[i] = std::frexp(values[i], &exponents[i]);
+ average += exponents[i];
+ }
+ average /= (int)N;
+ for (size_t i = 0; i < N; i++) {
+ values[i] = std::ldexp(mantissas[i], exponents[i] - average);
+ }
+ return -average;
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_MATH_UTILS_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/include/2geom/nearest-time.h b/include/2geom/nearest-time.h
new file mode 100644
index 0000000..007cd27
--- /dev/null
+++ b/include/2geom/nearest-time.h
@@ -0,0 +1,141 @@
+/** @file
+ * @brief Nearest time routines for D2<SBasis> and Piecewise<D2<SBasis>>
+ *//*
+ * Authors:
+ * 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.
+ */
+
+
+#ifndef LIB2GEOM_SEEN_NEAREST_TIME_H
+#define LIB2GEOM_SEEN_NEAREST_TIME_H
+
+
+#include <vector>
+
+#include <2geom/d2.h>
+#include <2geom/piecewise.h>
+#include <2geom/exception.h>
+#include <2geom/bezier.h>
+
+
+namespace Geom
+{
+
+/*
+ * Given a line L specified by a point A and direction vector v,
+ * 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_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 "deriv" is passed as parameter.
+ * The function return the first nearest point to "p" that is found.
+ */
+double nearest_time(Point const &p,
+ D2<SBasis> const &c, D2<SBasis> const &deriv,
+ double from = 0, double to = 1);
+
+inline
+double nearest_time(Point const &p,
+ D2<SBasis> const &c,
+ double from = 0, double to = 1 )
+{
+ return nearest_time(p, c, Geom::derivative(c), from, to);
+}
+
+/*
+ * 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_times(Point const& p,
+ D2<SBasis> const& c, D2<SBasis> const& dc,
+ double from = 0, double to = 1 );
+
+inline
+std::vector<double>
+all_nearest_times(Point const &p,
+ D2<SBasis> const &c,
+ double from = 0, double to = 1)
+{
+ return all_nearest_times(p, c, Geom::derivative(c), from, to);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Piecewise< D2<SBasis> > versions
+
+double nearest_time(Point const &p,
+ Piecewise< D2<SBasis> > const &c,
+ double from, double to);
+
+inline
+double nearest_time(Point const& p, Piecewise< D2<SBasis> > const &c)
+{
+ return nearest_time(p, c, c.cuts[0], c.cuts[c.size()]);
+}
+
+
+std::vector<double>
+all_nearest_times(Point const &p,
+ Piecewise< D2<SBasis> > const &c,
+ double from, double to);
+
+inline
+std::vector<double>
+all_nearest_times( Point const& p, Piecewise< D2<SBasis> > const& c )
+{
+ return all_nearest_times(p, c, c.cuts[0], c.cuts[c.size()]);
+}
+
+} // end namespace Geom
+
+#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/include/2geom/numeric/fitting-model.h b/include/2geom/numeric/fitting-model.h
new file mode 100644
index 0000000..0316f57
--- /dev/null
+++ b/include/2geom/numeric/fitting-model.h
@@ -0,0 +1,521 @@
+/*
+ * Fitting Models for Geom Types
+ *
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 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.
+ */
+
+
+#ifndef _NL_FITTING_MODEL_H_
+#define _NL_FITTING_MODEL_H_
+
+
+#include <2geom/d2.h>
+#include <2geom/sbasis.h>
+#include <2geom/bezier.h>
+#include <2geom/bezier-curve.h>
+#include <2geom/polynomial.h>
+#include <2geom/ellipse.h>
+#include <2geom/circle.h>
+#include <2geom/utils.h>
+#include <2geom/conicsec.h>
+
+
+namespace Geom { namespace NL {
+
+/*
+ * A model is an abstraction for an expression dependent from a parameter where
+ * the coefficients of this expression are the unknowns of the fitting problem.
+ * For a ceratain number of parameter values we know the related values
+ * the expression evaluates to: from each parameter value we get a row of
+ * the matrix of the fitting problem, from each expression value we get
+ * the related constant term.
+ * Example: given the model a*x^2 + b*x + c = 0; from x = 1 we get
+ * the equation a + b + c = 0, in this example the constant term is always
+ * the same for each parameter value.
+ *
+ * A model is required to implement 3 methods:
+ *
+ * - size : returns the number of unknown coefficients that appear in
+ * the expression of the fitting problem;
+ * - feed : its input is a parameter value and the related expression value,
+ * it generates a matrix row and a new entry of the constant vector
+ * of the fitting problem;
+ * - instance : it has an input parameter represented by the raw vector
+ * solution of the fitting problem and an output parameter
+ * of type InstanceType that return a specific object that is
+ * generated using the fitting problem solution, in the example
+ * above the object could be a Poly type.
+ */
+
+/*
+ * completely unknown models must inherit from this template class;
+ * example: the model a*x^2 + b*x + c = 0 to be solved wrt a, b, c;
+ * example: the model A(t) = known_sample_value_at(t) to be solved wrt
+ * the coefficients of the curve A(t) expressed in S-Basis form;
+ * parameter type: the type of x and t variable in the examples above;
+ * value type: the type of the known sample values (in the first example
+ * is constant )
+ * instance type: the type of the objects produced by using
+ * the fitting raw data solution
+ */
+
+
+
+
+template< typename ParameterType, typename ValueType, typename InstanceType >
+class LinearFittingModel
+{
+ public:
+ typedef ParameterType parameter_type;
+ typedef ValueType value_type;
+ typedef InstanceType instance_type;
+
+ static const bool WITH_FIXED_TERMS = false;
+
+ /*
+ * a LinearFittingModel must implement the following methods:
+ *
+ * void feed( VectorView & vector,
+ * parameter_type const& sample_parameter ) const;
+ *
+ * size_t size() const;
+ *
+ * void instance(instance_type &, raw_type const& raw_data) const;
+ *
+ */
+};
+
+
+/*
+ * partially known models must inherit from this template class
+ * example: the model a*x^2 + 2*x + c = 0 to be solved wrt a and c
+ */
+template< typename ParameterType, typename ValueType, typename InstanceType >
+class LinearFittingModelWithFixedTerms
+{
+ public:
+ typedef ParameterType parameter_type;
+ typedef ValueType value_type;
+ typedef InstanceType instance_type;
+
+ static const bool WITH_FIXED_TERMS = true;
+
+ /*
+ * a LinearFittingModelWithFixedTerms must implement the following methods:
+ *
+ * void feed( VectorView & vector,
+ * value_type & fixed_term,
+ * parameter_type const& sample_parameter ) const;
+ *
+ * size_t size() const;
+ *
+ * void instance(instance_type &, raw_type const& raw_data) const;
+ *
+ */
+
+
+};
+
+
+// incomplete model, it can be inherited to make up different kinds of
+// instance type; the raw data is a vector of coefficients of a polynomial
+// represented in standard power basis
+template< typename InstanceType >
+class LFMPowerBasis
+ : public LinearFittingModel<double, double, InstanceType>
+{
+ public:
+ LFMPowerBasis(size_t degree)
+ : m_size(degree + 1)
+ {
+ }
+
+ void feed( VectorView & coeff, double sample_parameter ) const
+ {
+ coeff[0] = 1;
+ double x_i = 1;
+ for (size_t i = 1; i < coeff.size(); ++i)
+ {
+ x_i *= sample_parameter;
+ coeff[i] = x_i;
+ }
+ }
+
+ size_t size() const
+ {
+ return m_size;
+ }
+
+ private:
+ size_t m_size;
+};
+
+
+// this model generates Geom::Poly objects
+class LFMPoly
+ : public LFMPowerBasis<Poly>
+{
+ public:
+ LFMPoly(size_t degree)
+ : LFMPowerBasis<Poly>(degree)
+ {
+ }
+
+ void instance(Poly & poly, ConstVectorView const& raw_data) const
+ {
+ poly.clear();
+ poly.resize(size());
+ for (size_t i = 0; i < raw_data.size(); ++i)
+ {
+ poly[i] = raw_data[i];
+ }
+ }
+};
+
+
+// incomplete model, it can be inherited to make up different kinds of
+// instance type; the raw data is a vector of coefficients of a polynomial
+// represented in standard power basis with leading term coefficient equal to 1
+template< typename InstanceType >
+class LFMNormalizedPowerBasis
+ : public LinearFittingModelWithFixedTerms<double, double, InstanceType>
+{
+ public:
+ LFMNormalizedPowerBasis(size_t _degree)
+ : m_model( _degree - 1)
+ {
+ assert(_degree > 0);
+ }
+
+
+ void feed( VectorView & coeff,
+ double & known_term,
+ double sample_parameter ) const
+ {
+ m_model.feed(coeff, sample_parameter);
+ known_term = coeff[m_model.size()-1] * sample_parameter;
+ }
+
+ size_t size() const
+ {
+ return m_model.size();
+ }
+
+ private:
+ LFMPowerBasis<InstanceType> m_model;
+};
+
+
+// incomplete model, it can be inherited to make up different kinds of
+// instance type; the raw data is a vector of coefficients of the equation
+// of an ellipse curve
+//template< typename InstanceType >
+//class LFMEllipseEquation
+// : public LinearFittingModelWithFixedTerms<Point, double, InstanceType>
+//{
+// public:
+// void feed( VectorView & coeff, double & fixed_term, Point const& p ) const
+// {
+// coeff[0] = p[X] * p[Y];
+// coeff[1] = p[Y] * p[Y];
+// coeff[2] = p[X];
+// coeff[3] = p[Y];
+// coeff[4] = 1;
+// fixed_term = p[X] * p[X];
+// }
+//
+// size_t size() const
+// {
+// return 5;
+// }
+//};
+
+// incomplete model, it can be inherited to make up different kinds of
+// instance type; the raw data is a vector of coefficients of the equation
+// of a conic section
+template< typename InstanceType >
+class LFMConicEquation
+ : public LinearFittingModelWithFixedTerms<Point, double, InstanceType>
+{
+ public:
+ void feed( VectorView & coeff, double & fixed_term, Point const& p ) const
+ {
+ coeff[0] = p[X] * p[Y];
+ coeff[1] = p[Y] * p[Y];
+ coeff[2] = p[X];
+ coeff[3] = p[Y];
+ coeff[4] = 1;
+ fixed_term = p[X] * p[X];
+ }
+
+ size_t size() const
+ {
+ return 5;
+ }
+};
+
+// this model generates Ellipse curves
+class LFMConicSection
+ : public LFMConicEquation<xAx>
+{
+ public:
+ void instance(xAx & c, ConstVectorView const& coeff) const
+ {
+ c.set(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]);
+ }
+};
+
+// this model generates Ellipse curves
+class LFMEllipse
+ : public LFMConicEquation<Ellipse>
+{
+ public:
+ void instance(Ellipse & e, ConstVectorView const& coeff) const
+ {
+ e.setCoefficients(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]);
+ }
+};
+
+
+// incomplete model, it can be inherited to make up different kinds of
+// instance type; the raw data is a vector of coefficients of the equation
+// of a circle curve
+template< typename InstanceType >
+class LFMCircleEquation
+ : public LinearFittingModelWithFixedTerms<Point, double, InstanceType>
+{
+ public:
+ void feed( VectorView & coeff, double & fixed_term, Point const& p ) const
+ {
+ coeff[0] = p[X];
+ coeff[1] = p[Y];
+ coeff[2] = 1;
+ fixed_term = p[X] * p[X] + p[Y] * p[Y];
+ }
+
+ size_t size() const
+ {
+ return 3;
+ }
+};
+
+
+// this model generates Ellipse curves
+class LFMCircle
+ : public LFMCircleEquation<Circle>
+{
+ public:
+ void instance(Circle & c, ConstVectorView const& coeff) const
+ {
+ c.setCoefficients(1, coeff[0], coeff[1], coeff[2]);
+ }
+};
+
+
+// this model generates SBasis objects
+class LFMSBasis
+ : public LinearFittingModel<double, double, SBasis>
+{
+ public:
+ LFMSBasis( size_t _order )
+ : m_size( 2*(_order+1) ),
+ m_order(_order)
+ {
+ }
+
+ void feed( VectorView & coeff, double t ) const
+ {
+ double u0 = 1-t;
+ double u1 = t;
+ double s = u0 * u1;
+ coeff[0] = u0;
+ coeff[1] = u1;
+ for (size_t i = 2; i < size(); i+=2)
+ {
+ u0 *= s;
+ u1 *= s;
+ coeff[i] = u0;
+ coeff[i+1] = u1;
+ }
+ }
+
+ size_t size() const
+ {
+ return m_size;
+ }
+
+ void instance(SBasis & sb, ConstVectorView const& raw_data) const
+ {
+ sb.resize(m_order+1);
+ for (unsigned int i = 0, k = 0; i < raw_data.size(); i+=2, ++k)
+ {
+ sb[k][0] = raw_data[i];
+ sb[k][1] = raw_data[i+1];
+ }
+ }
+
+ private:
+ size_t m_size;
+ size_t m_order;
+};
+
+
+// this model generates D2<SBasis> objects
+class LFMD2SBasis
+ : public LinearFittingModel< double, Point, D2<SBasis> >
+{
+ public:
+ LFMD2SBasis( size_t _order )
+ : mosb(_order)
+ {
+ }
+
+ void feed( VectorView & coeff, double t ) const
+ {
+ mosb.feed(coeff, t);
+ }
+
+ size_t size() const
+ {
+ return mosb.size();
+ }
+
+ void instance(D2<SBasis> & d2sb, ConstMatrixView const& raw_data) const
+ {
+ mosb.instance(d2sb[X], raw_data.column_const_view(X));
+ mosb.instance(d2sb[Y], raw_data.column_const_view(Y));
+ }
+
+ private:
+ LFMSBasis mosb;
+};
+
+
+// this model generates Bezier objects
+class LFMBezier
+ : public LinearFittingModel<double, double, Bezier>
+{
+ public:
+ LFMBezier( size_t _order )
+ : m_size(_order + 1),
+ m_order(_order)
+ {
+ binomial_coefficients(m_bc, m_order);
+ }
+
+ void feed( VectorView & coeff, double t ) const
+ {
+ double s = 1;
+ for (size_t i = 0; i < size(); ++i)
+ {
+ coeff[i] = s * m_bc[i];
+ s *= t;
+ }
+ double u = 1-t;
+ s = 1;
+ for (size_t i = size()-1; i > 0; --i)
+ {
+ coeff[i] *= s;
+ s *= u;
+ }
+ coeff[0] *= s;
+ }
+
+ size_t size() const
+ {
+ return m_size;
+ }
+
+ void instance(Bezier & b, ConstVectorView const& raw_data) const
+ {
+ assert(b.size() == raw_data.size());
+ for (unsigned int i = 0; i < raw_data.size(); ++i)
+ {
+ b[i] = raw_data[i];
+ }
+ }
+
+ private:
+ size_t m_size;
+ size_t m_order;
+ std::vector<size_t> m_bc;
+};
+
+
+// this model generates Bezier curves
+template <unsigned degree>
+class LFMBezierCurveN
+ : public LinearFittingModel< double, Point, BezierCurveN<degree> >
+{
+ public:
+ LFMBezierCurveN()
+ : mob(degree+1)
+ {
+ }
+
+ void feed( VectorView & coeff, double t ) const
+ {
+ mob.feed(coeff, t);
+ }
+
+ size_t size() const
+ {
+ return mob.size();
+ }
+
+ void instance(BezierCurveN<degree> & bc, ConstMatrixView const& raw_data) const
+ {
+ Bezier bx(degree);
+ Bezier by(degree);
+ mob.instance(bx, raw_data.column_const_view(X));
+ mob.instance(by, raw_data.column_const_view(Y));
+ bc = BezierCurveN<degree>(bx, by);
+ }
+
+ private:
+ LFMBezier mob;
+};
+
+} // end namespace NL
+} // end namespace Geom
+
+
+#endif // _NL_FITTING_MODEL_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/include/2geom/numeric/fitting-tool.h b/include/2geom/numeric/fitting-tool.h
new file mode 100644
index 0000000..78d66ca
--- /dev/null
+++ b/include/2geom/numeric/fitting-tool.h
@@ -0,0 +1,562 @@
+/*
+ * Fitting Tools
+ *
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 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.
+ */
+
+
+#ifndef _NL_FITTING_TOOL_H_
+#define _NL_FITTING_TOOL_H_
+
+
+#include <2geom/numeric/vector.h>
+#include <2geom/numeric/matrix.h>
+
+#include <2geom/point.h>
+
+#include <vector>
+
+
+/*
+ * The least_square_fitter class represents a tool for solving a fitting
+ * problem with respect to a given model that represents an expression
+ * dependent from a parameter where the coefficients of this expression
+ * are the unknowns of the fitting problem.
+ * The minimizing solution is found by computing the pseudo-inverse
+ * of the problem matrix
+ */
+
+
+namespace Geom { namespace NL {
+
+namespace detail {
+
+template< typename ModelT>
+class lsf_base
+{
+ public:
+ typedef ModelT model_type;
+ typedef typename model_type::parameter_type parameter_type;
+ typedef typename model_type::value_type value_type;
+
+ lsf_base( model_type const& _model, size_t forecasted_samples )
+ : m_model(_model),
+ m_total_samples(0),
+ m_matrix(forecasted_samples, m_model.size()),
+ m_psdinv_matrix(NULL)
+ {
+ }
+
+ // compute pseudo inverse
+ void update()
+ {
+ if (total_samples() == 0) return;
+ if (m_psdinv_matrix != NULL)
+ {
+ delete m_psdinv_matrix;
+ }
+ MatrixView mv(m_matrix, 0, 0, total_samples(), m_matrix.columns());
+ m_psdinv_matrix = new Matrix( pseudo_inverse(mv) );
+ assert(m_psdinv_matrix != NULL);
+ }
+
+ size_t total_samples() const
+ {
+ return m_total_samples;
+ }
+
+ bool is_full() const
+ {
+ return (total_samples() == m_matrix.rows());
+ }
+
+ void clear()
+ {
+ m_total_samples = 0;
+ }
+
+ virtual
+ ~lsf_base()
+ {
+ if (m_psdinv_matrix != NULL)
+ {
+ delete m_psdinv_matrix;
+ }
+ }
+
+ protected:
+ const model_type & m_model;
+ size_t m_total_samples;
+ Matrix m_matrix;
+ Matrix* m_psdinv_matrix;
+
+}; // end class lsf_base
+
+
+
+
+template< typename ModelT, typename ValueType = typename ModelT::value_type>
+class lsf_solution
+{
+};
+
+// a fitting process on samples with value of type double
+// produces a solution of type Vector
+template< typename ModelT>
+class lsf_solution<ModelT, double>
+ : public lsf_base<ModelT>
+{
+public:
+ typedef ModelT model_type;
+ typedef typename model_type::parameter_type parameter_type;
+ typedef typename model_type::value_type value_type;
+ typedef Vector solution_type;
+ typedef lsf_base<model_type> base_type;
+
+ using base_type::m_model;
+ using base_type::m_psdinv_matrix;
+ using base_type::total_samples;
+
+public:
+ lsf_solution<ModelT, double>( model_type const& _model,
+ size_t forecasted_samples )
+ : base_type(_model, forecasted_samples),
+ m_solution(_model.size())
+ {
+ }
+
+ template< typename VectorT >
+ solution_type& result(VectorT const& sample_values)
+ {
+ assert(sample_values.size() == total_samples());
+ ConstVectorView sv(sample_values);
+ m_solution = (*m_psdinv_matrix) * sv;
+ return m_solution;
+ }
+
+ // a comparison between old sample values and the new ones is performed
+ // in order to minimize computation
+ // prerequisite:
+ // old_sample_values.size() == new_sample_values.size()
+ // no update() call can be performed between two result invocations
+ template< typename VectorT >
+ solution_type& result( VectorT const& old_sample_values,
+ VectorT const& new_sample_values )
+ {
+ assert(old_sample_values.size() == total_samples());
+ assert(new_sample_values.size() == total_samples());
+ Vector diff(total_samples());
+ for (size_t i = 0; i < diff.size(); ++i)
+ {
+ diff[i] = new_sample_values[i] - old_sample_values[i];
+ }
+ Vector column(m_model.size());
+ Vector delta(m_model.size(), 0.0);
+ for (size_t i = 0; i < diff.size(); ++i)
+ {
+ if (diff[i] != 0)
+ {
+ column = m_psdinv_matrix->column_view(i);
+ column.scale(diff[i]);
+ delta += column;
+ }
+ }
+ m_solution += delta;
+ return m_solution;
+ }
+
+ solution_type& result()
+ {
+ return m_solution;
+ }
+
+private:
+ solution_type m_solution;
+
+}; // end class lsf_solution<ModelT, double>
+
+
+// a fitting process on samples with value of type Point
+// produces a solution of type Matrix (with 2 columns)
+template< typename ModelT>
+class lsf_solution<ModelT, Point>
+ : public lsf_base<ModelT>
+{
+public:
+ typedef ModelT model_type;
+ typedef typename model_type::parameter_type parameter_type;
+ typedef typename model_type::value_type value_type;
+ typedef Matrix solution_type;
+ typedef lsf_base<model_type> base_type;
+
+ using base_type::m_model;
+ using base_type::m_psdinv_matrix;
+ using base_type::total_samples;
+
+public:
+ lsf_solution<ModelT, Point>( model_type const& _model,
+ size_t forecasted_samples )
+ : base_type(_model, forecasted_samples),
+ m_solution(_model.size(), 2)
+ {
+ }
+
+ solution_type& result(std::vector<Point> const& sample_values)
+ {
+ assert(sample_values.size() == total_samples());
+ Matrix svm(total_samples(), 2);
+ for (size_t i = 0; i < total_samples(); ++i)
+ {
+ svm(i, X) = sample_values[i][X];
+ svm(i, Y) = sample_values[i][Y];
+ }
+ m_solution = (*m_psdinv_matrix) * svm;
+ return m_solution;
+ }
+
+ // a comparison between old sample values and the new ones is performed
+ // in order to minimize computation
+ // prerequisite:
+ // old_sample_values.size() == new_sample_values.size()
+ // no update() call can to be performed between two result invocations
+ solution_type& result( std::vector<Point> const& old_sample_values,
+ std::vector<Point> const& new_sample_values )
+ {
+ assert(old_sample_values.size() == total_samples());
+ assert(new_sample_values.size() == total_samples());
+ Matrix diff(total_samples(), 2);
+ for (size_t i = 0; i < total_samples(); ++i)
+ {
+ diff(i, X) = new_sample_values[i][X] - old_sample_values[i][X];
+ diff(i, Y) = new_sample_values[i][Y] - old_sample_values[i][Y];
+ }
+ Vector column(m_model.size());
+ Matrix delta(m_model.size(), 2, 0.0);
+ VectorView deltax = delta.column_view(X);
+ VectorView deltay = delta.column_view(Y);
+ for (size_t i = 0; i < total_samples(); ++i)
+ {
+ if (diff(i, X) != 0)
+ {
+ column = m_psdinv_matrix->column_view(i);
+ column.scale(diff(i, X));
+ deltax += column;
+ }
+ if (diff(i, Y) != 0)
+ {
+ column = m_psdinv_matrix->column_view(i);
+ column.scale(diff(i, Y));
+ deltay += column;
+ }
+ }
+ m_solution += delta;
+ return m_solution;
+ }
+
+ solution_type& result()
+ {
+ return m_solution;
+ }
+
+private:
+ solution_type m_solution;
+
+}; // end class lsf_solution<ModelT, Point>
+
+
+
+
+template< typename ModelT,
+ bool WITH_FIXED_TERMS = ModelT::WITH_FIXED_TERMS >
+class lsf_with_fixed_terms
+{
+};
+
+
+// fitting tool for completely unknown models
+template< typename ModelT>
+class lsf_with_fixed_terms<ModelT, false>
+ : public lsf_solution<ModelT>
+{
+ public:
+ typedef ModelT model_type;
+ typedef typename model_type::parameter_type parameter_type;
+ typedef typename model_type::value_type value_type;
+ typedef lsf_solution<model_type> base_type;
+ typedef typename base_type::solution_type solution_type;
+
+ using base_type::total_samples;
+ using base_type::is_full;
+ using base_type::m_matrix;
+ using base_type::m_total_samples;
+ using base_type::m_model;
+
+ public:
+ lsf_with_fixed_terms<ModelT, false>( model_type const& _model,
+ size_t forecasted_samples )
+ : base_type(_model, forecasted_samples)
+ {
+ }
+
+ void append(parameter_type const& sample_parameter)
+ {
+ assert(!is_full());
+ VectorView row = m_matrix.row_view(total_samples());
+ m_model.feed(row, sample_parameter);
+ ++m_total_samples;
+ }
+
+ void append_copy(size_t sample_index)
+ {
+ assert(!is_full());
+ assert(sample_index < total_samples());
+ VectorView dest_row = m_matrix.row_view(total_samples());
+ VectorView source_row = m_matrix.row_view(sample_index);
+ dest_row = source_row;
+ ++m_total_samples;
+ }
+
+}; // end class lsf_with_fixed_terms<ModelT, false>
+
+
+// fitting tool for partially known models
+template< typename ModelT>
+class lsf_with_fixed_terms<ModelT, true>
+ : public lsf_solution<ModelT>
+{
+ public:
+ typedef ModelT model_type;
+ typedef typename model_type::parameter_type parameter_type;
+ typedef typename model_type::value_type value_type;
+ typedef lsf_solution<model_type> base_type;
+ typedef typename base_type::solution_type solution_type;
+
+ using base_type::total_samples;
+ using base_type::is_full;
+ using base_type::m_matrix;
+ using base_type::m_total_samples;
+ using base_type::m_model;
+
+ public:
+ lsf_with_fixed_terms<ModelT, true>( model_type const& _model,
+ size_t forecasted_samples )
+ : base_type(_model, forecasted_samples),
+ m_vector(forecasted_samples),
+ m_vector_view(NULL)
+ {
+ }
+ void append(parameter_type const& sample_parameter)
+ {
+ assert(!is_full());
+ VectorView row = m_matrix.row_view(total_samples());
+ m_model.feed(row, m_vector[total_samples()], sample_parameter);
+ ++m_total_samples;
+ }
+
+ void append_copy(size_t sample_index)
+ {
+ assert(!is_full());
+ assert(sample_index < total_samples());
+ VectorView dest_row = m_matrix.row_view(total_samples());
+ VectorView source_row = m_matrix.row_view(sample_index);
+ dest_row = source_row;
+ m_vector[total_samples()] = m_vector[sample_index];
+ ++m_total_samples;
+ }
+
+ void update()
+ {
+ base_type::update();
+ if (total_samples() == 0) return;
+ if (m_vector_view != NULL)
+ {
+ delete m_vector_view;
+ }
+ m_vector_view = new VectorView(m_vector, base_type::total_samples());
+ assert(m_vector_view != NULL);
+ }
+
+
+ ~lsf_with_fixed_terms<model_type, true>() override
+ {
+ if (m_vector_view != NULL)
+ {
+ delete m_vector_view;
+ }
+ }
+
+ protected:
+ Vector m_vector;
+ VectorView* m_vector_view;
+
+}; // end class lsf_with_fixed_terms<ModelT, true>
+
+
+} // end namespace detail
+
+
+
+
+template< typename ModelT,
+ typename ValueType = typename ModelT::value_type,
+ bool WITH_FIXED_TERMS = ModelT::WITH_FIXED_TERMS >
+class least_squeares_fitter
+{
+};
+
+
+template< typename ModelT, typename ValueType >
+class least_squeares_fitter<ModelT, ValueType, false>
+ : public detail::lsf_with_fixed_terms<ModelT>
+{
+ public:
+ typedef ModelT model_type;
+ typedef detail::lsf_with_fixed_terms<model_type> base_type;
+ typedef typename base_type::parameter_type parameter_type;
+ typedef typename base_type::value_type value_type;
+ typedef typename base_type::solution_type solution_type;
+
+ public:
+ least_squeares_fitter<ModelT, ValueType, false>( model_type const& _model,
+ size_t forecasted_samples )
+ : base_type(_model, forecasted_samples)
+ {
+ }
+}; // end class least_squeares_fitter<ModelT, ValueType, true>
+
+
+template< typename ModelT>
+class least_squeares_fitter<ModelT, double, true>
+ : public detail::lsf_with_fixed_terms<ModelT>
+{
+ public:
+ typedef ModelT model_type;
+ typedef detail::lsf_with_fixed_terms<model_type> base_type;
+ typedef typename base_type::parameter_type parameter_type;
+ typedef typename base_type::value_type value_type;
+ typedef typename base_type::solution_type solution_type;
+
+ using base_type::m_vector_view;
+ //using base_type::result; // VSC legacy support
+ solution_type& result( std::vector<Point> const& old_sample_values,
+ std::vector<Point> const& new_sample_values )
+ {
+ return base_type::result(old_sample_values, new_sample_values);
+ }
+
+ solution_type& result()
+ {
+ return base_type::result();
+ }
+
+ public:
+ least_squeares_fitter<ModelT, double, true>( model_type const& _model,
+ size_t forecasted_samples )
+ : base_type(_model, forecasted_samples)
+ {
+ }
+
+ template< typename VectorT >
+ solution_type& result(VectorT const& sample_values)
+ {
+ assert(sample_values.size() == m_vector_view->size());
+ Vector sv(sample_values.size());
+ for (size_t i = 0; i < sv.size(); ++i)
+ sv[i] = sample_values[i] - (*m_vector_view)[i];
+ return base_type::result(sv);
+ }
+
+}; // end class least_squeares_fitter<ModelT, double, true>
+
+
+template< typename ModelT>
+class least_squeares_fitter<ModelT, Point, true>
+ : public detail::lsf_with_fixed_terms<ModelT>
+{
+ public:
+ typedef ModelT model_type;
+ typedef detail::lsf_with_fixed_terms<model_type> base_type;
+ typedef typename base_type::parameter_type parameter_type;
+ typedef typename base_type::value_type value_type;
+ typedef typename base_type::solution_type solution_type;
+
+ using base_type::m_vector_view;
+ //using base_type::result; // VCS legacy support
+ solution_type& result( std::vector<Point> const& old_sample_values,
+ std::vector<Point> const& new_sample_values )
+ {
+ return base_type::result(old_sample_values, new_sample_values);
+ }
+
+ solution_type& result()
+ {
+ return base_type::result();
+ }
+
+
+ public:
+ least_squeares_fitter<ModelT, Point, true>( model_type const& _model,
+ size_t forecasted_samples )
+ : base_type(_model, forecasted_samples)
+ {
+ }
+
+ solution_type& result(std::vector<Point> const& sample_values)
+ {
+ assert(sample_values.size() == m_vector_view->size());
+ NL::Matrix sv(sample_values.size(), 2);
+ for (size_t i = 0; i < sample_values.size(); ++i)
+ {
+ sv(i, X) = sample_values[i][X] - (*m_vector_view)[i];
+ sv(i, Y) = sample_values[i][Y] - (*m_vector_view)[i];
+ }
+ return base_type::result(sv);
+ }
+
+}; // end class least_squeares_fitter<ModelT, Point, true>
+
+
+} // end namespace NL
+} // end namespace Geom
+
+
+
+#endif // _NL_FITTING_TOOL_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/include/2geom/numeric/linear_system.h b/include/2geom/numeric/linear_system.h
new file mode 100644
index 0000000..f793e20
--- /dev/null
+++ b/include/2geom/numeric/linear_system.h
@@ -0,0 +1,138 @@
+/*
+ * LinearSystem class wraps some gsl routines for solving linear systems
+ *
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 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.
+ */
+
+
+#ifndef _NL_LINEAR_SYSTEM_H_
+#define _NL_LINEAR_SYSTEM_H_
+
+
+#include <cassert>
+
+#include <gsl/gsl_linalg.h>
+
+#include <2geom/numeric/matrix.h>
+#include <2geom/numeric/vector.h>
+
+
+namespace Geom { namespace NL {
+
+
+class LinearSystem
+{
+public:
+ LinearSystem(MatrixView & _matrix, VectorView & _vector)
+ : m_matrix(_matrix), m_vector(_vector), m_solution(_matrix.columns())
+ {
+ }
+
+ LinearSystem(Matrix & _matrix, Vector & _vector)
+ : m_matrix(_matrix), m_vector(_vector), m_solution(_matrix.columns())
+ {
+ }
+
+ const Vector & LU_solve()
+ {
+ assert( matrix().rows() == matrix().columns()
+ && matrix().rows() == vector().size() );
+ int s;
+ gsl_permutation * p = gsl_permutation_alloc(matrix().rows());
+ gsl_linalg_LU_decomp (matrix().get_gsl_matrix(), p, &s);
+ gsl_linalg_LU_solve( matrix().get_gsl_matrix(),
+ p,
+ vector().get_gsl_vector(),
+ m_solution.get_gsl_vector()
+ );
+ gsl_permutation_free(p);
+ return solution();
+ }
+
+ const Vector & SV_solve()
+ {
+ assert( matrix().rows() >= matrix().columns()
+ && matrix().rows() == vector().size() );
+
+ gsl_matrix* U = matrix().get_gsl_matrix();
+ gsl_matrix* V = gsl_matrix_alloc(matrix().columns(), matrix().columns());
+ gsl_vector* S = gsl_vector_alloc(matrix().columns());
+ gsl_vector* work = gsl_vector_alloc(matrix().columns());
+
+ gsl_linalg_SV_decomp( U, V, S, work );
+
+ gsl_vector* b = vector().get_gsl_vector();
+ gsl_vector* x = m_solution.get_gsl_vector();
+
+ gsl_linalg_SV_solve( U, V, S, b, x);
+
+ gsl_matrix_free(V);
+ gsl_vector_free(S);
+ gsl_vector_free(work);
+
+ return solution();
+ }
+
+ MatrixView & matrix()
+ {
+ return m_matrix;
+ }
+
+ VectorView & vector()
+ {
+ return m_vector;
+ }
+
+ const Vector & solution() const
+ {
+ return m_solution;
+ }
+
+private:
+ MatrixView m_matrix;
+ VectorView m_vector;
+ Vector m_solution;
+};
+
+
+} } // end namespaces
+
+
+#endif /*_NL_LINEAR_SYSTEM_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/include/2geom/numeric/matrix.h b/include/2geom/numeric/matrix.h
new file mode 100644
index 0000000..02851b4
--- /dev/null
+++ b/include/2geom/numeric/matrix.h
@@ -0,0 +1,603 @@
+/*
+ * Matrix, MatrixView, ConstMatrixView classes wrap the gsl matrix routines;
+ * "views" mimic the semantic of C++ references: any operation performed
+ * on a "view" is actually performed on the "viewed object"
+ *
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 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.
+ */
+
+
+
+
+#ifndef _NL_MATRIX_H_
+#define _NL_MATRIX_H_
+
+#include <2geom/exception.h>
+#include <2geom/numeric/vector.h>
+
+#include <cassert>
+#include <utility> // for std::pair
+#include <algorithm> // for std::swap
+#include <sstream>
+#include <string>
+#include <gsl/gsl_matrix.h>
+#include <gsl/gsl_linalg.h>
+
+
+namespace Geom { namespace NL {
+
+namespace detail
+{
+
+class BaseMatrixImpl
+{
+ public:
+ virtual ~BaseMatrixImpl()
+ {
+ }
+
+ ConstVectorView row_const_view(size_t i) const
+ {
+ return ConstVectorView(gsl_matrix_const_row(m_matrix, i));
+ }
+
+ ConstVectorView column_const_view(size_t i) const
+ {
+ return ConstVectorView(gsl_matrix_const_column(m_matrix, i));
+ }
+
+ const double & operator() (size_t i, size_t j) const
+ {
+ return *gsl_matrix_const_ptr(m_matrix, i, j);
+ }
+
+ const gsl_matrix* get_gsl_matrix() const
+ {
+ return m_matrix;
+ }
+
+ bool is_zero() const
+ {
+ return gsl_matrix_isnull(m_matrix);
+ }
+
+ bool is_positive() const
+ {
+ for ( unsigned int i = 0; i < rows(); ++i )
+ {
+ for ( unsigned int j = 0; j < columns(); ++j )
+ {
+ if ( (*this)(i,j) <= 0 ) return false;
+ }
+ }
+ return true;
+ }
+
+ bool is_negative() const
+ {
+ for ( unsigned int i = 0; i < rows(); ++i )
+ {
+ for ( unsigned int j = 0; j < columns(); ++j )
+ {
+ if ( (*this)(i,j) >= 0 ) return false;
+ }
+ }
+ return true;
+ }
+
+ bool is_non_negative() const
+ {
+ for ( unsigned int i = 0; i < rows(); ++i )
+ {
+ for ( unsigned int j = 0; j < columns(); ++j )
+ {
+ if ( (*this)(i,j) < 0 ) return false;
+ }
+ }
+ return true;
+ }
+
+ double max() const
+ {
+ return gsl_matrix_max(m_matrix);
+ }
+
+ double min() const
+ {
+ return gsl_matrix_min(m_matrix);
+ }
+
+ std::pair<size_t, size_t>
+ max_index() const
+ {
+ std::pair<size_t, size_t> indices;
+ gsl_matrix_max_index(m_matrix, &(indices.first), &(indices.second));
+ return indices;
+ }
+
+ std::pair<size_t, size_t>
+ min_index() const
+ {
+ std::pair<size_t, size_t> indices;
+ gsl_matrix_min_index(m_matrix, &(indices.first), &(indices.second));
+ return indices;
+ }
+
+ size_t rows() const
+ {
+ return m_rows;
+ }
+
+ size_t columns() const
+ {
+ return m_columns;
+ }
+
+ std::string str() const;
+
+ protected:
+ size_t m_rows, m_columns;
+ gsl_matrix* m_matrix;
+
+}; // end class BaseMatrixImpl
+
+
+inline
+bool operator== (BaseMatrixImpl const& m1, BaseMatrixImpl const& m2)
+{
+ if (m1.rows() != m2.rows() || m1.columns() != m2.columns()) return false;
+
+ for (size_t i = 0; i < m1.rows(); ++i)
+ for (size_t j = 0; j < m1.columns(); ++j)
+ if (m1(i,j) != m2(i,j)) return false;
+
+ return true;
+}
+
+template< class charT >
+inline
+std::basic_ostream<charT> &
+operator<< (std::basic_ostream<charT> & os, const BaseMatrixImpl & _matrix)
+{
+ if (_matrix.rows() == 0 || _matrix.columns() == 0) return os;
+
+ os << "[[" << _matrix(0,0);
+ for (size_t j = 1; j < _matrix.columns(); ++j)
+ {
+ os << ", " << _matrix(0,j);
+ }
+ os << "]";
+
+ for (size_t i = 1; i < _matrix.rows(); ++i)
+ {
+ os << ", [" << _matrix(i,0);
+ for (size_t j = 1; j < _matrix.columns(); ++j)
+ {
+ os << ", " << _matrix(i,j);
+ }
+ os << "]";
+ }
+ os << "]";
+ return os;
+}
+
+inline
+std::string BaseMatrixImpl::str() const
+{
+ std::ostringstream oss;
+ oss << (*this);
+ return oss.str();
+}
+
+
+class MatrixImpl : public BaseMatrixImpl
+{
+ public:
+
+ typedef BaseMatrixImpl base_type;
+
+ void set_all( double x )
+ {
+ gsl_matrix_set_all(m_matrix, x);
+ }
+
+ void set_identity()
+ {
+ gsl_matrix_set_identity(m_matrix);
+ }
+
+ using base_type::operator(); // VSC legacy support
+ const double & operator() (size_t i, size_t j) const
+ {
+ return base_type::operator ()(i, j);
+ }
+
+ double & operator() (size_t i, size_t j)
+ {
+ return *gsl_matrix_ptr(m_matrix, i, j);
+ }
+
+ using base_type::get_gsl_matrix;
+
+ gsl_matrix* get_gsl_matrix()
+ {
+ return m_matrix;
+ }
+
+ VectorView row_view(size_t i)
+ {
+ return VectorView(gsl_matrix_row(m_matrix, i));
+ }
+
+ VectorView column_view(size_t i)
+ {
+ return VectorView(gsl_matrix_column(m_matrix, i));
+ }
+
+ void swap_rows(size_t i, size_t j)
+ {
+ gsl_matrix_swap_rows(m_matrix, i, j);
+ }
+
+ void swap_columns(size_t i, size_t j)
+ {
+ gsl_matrix_swap_columns(m_matrix, i, j);
+ }
+
+ MatrixImpl & transpose()
+ {
+ assert(columns() == rows());
+ gsl_matrix_transpose(m_matrix);
+ return (*this);
+ }
+
+ MatrixImpl & scale(double x)
+ {
+ gsl_matrix_scale(m_matrix, x);
+ return (*this);
+ }
+
+ MatrixImpl & translate(double x)
+ {
+ gsl_matrix_add_constant(m_matrix, x);
+ return (*this);
+ }
+
+ MatrixImpl & operator+=(base_type const& _matrix)
+ {
+ gsl_matrix_add(m_matrix, _matrix.get_gsl_matrix());
+ return (*this);
+ }
+
+ MatrixImpl & operator-=(base_type const& _matrix)
+ {
+ gsl_matrix_sub(m_matrix, _matrix.get_gsl_matrix());
+ return (*this);
+ }
+
+}; // end class MatrixImpl
+
+} // end namespace detail
+
+
+using detail::operator==;
+using detail::operator<<;
+
+
+template <size_t N>
+class ConstBaseSymmetricMatrix;
+
+
+class Matrix: public detail::MatrixImpl
+{
+ public:
+ typedef detail::MatrixImpl base_type;
+
+ public:
+ // the matrix is not initialized
+ Matrix(size_t n1, size_t n2)
+ {
+ m_rows = n1;
+ m_columns = n2;
+ m_matrix = gsl_matrix_alloc(n1, n2);
+ }
+
+ Matrix(size_t n1, size_t n2, double x)
+ {
+ m_rows = n1;
+ m_columns = n2;
+ m_matrix = gsl_matrix_alloc(n1, n2);
+ gsl_matrix_set_all(m_matrix, x);
+ }
+
+ Matrix(Matrix const& _matrix)
+ : base_type()
+ {
+ m_rows = _matrix.rows();
+ m_columns = _matrix.columns();
+ m_matrix = gsl_matrix_alloc(rows(), columns());
+ gsl_matrix_memcpy(m_matrix, _matrix.get_gsl_matrix());
+ }
+
+ explicit
+ Matrix(base_type::base_type const& _matrix)
+ {
+ m_rows = _matrix.rows();
+ m_columns = _matrix.columns();
+ m_matrix = gsl_matrix_alloc(rows(), columns());
+ gsl_matrix_memcpy(m_matrix, _matrix.get_gsl_matrix());
+ }
+
+ template <size_t N>
+ explicit
+ Matrix(ConstBaseSymmetricMatrix<N> const& _smatrix)
+ {
+ m_rows = N;
+ m_columns = N;
+ m_matrix = gsl_matrix_alloc(N, N);
+ for (size_t i = 0; i < N; ++i)
+ for (size_t j = 0; j < N ; ++j)
+ (*gsl_matrix_ptr(m_matrix, i, j)) = _smatrix(i,j);
+ }
+
+ Matrix & operator=(Matrix const& _matrix)
+ {
+ assert( rows() == _matrix.rows() && columns() == _matrix.columns() );
+ gsl_matrix_memcpy(m_matrix, _matrix.get_gsl_matrix());
+ return *this;
+ }
+
+ Matrix & operator=(base_type::base_type const& _matrix)
+ {
+ assert( rows() == _matrix.rows() && columns() == _matrix.columns() );
+ gsl_matrix_memcpy(m_matrix, _matrix.get_gsl_matrix());
+ return *this;
+ }
+
+ template <size_t N>
+ Matrix & operator=(ConstBaseSymmetricMatrix<N> const& _smatrix)
+ {
+ assert (rows() == N && columns() == N);
+ for (size_t i = 0; i < N; ++i)
+ for (size_t j = 0; j < N ; ++j)
+ (*this)(i,j) = _smatrix(i,j);
+ return *this;
+ }
+
+ ~Matrix() override
+ {
+ gsl_matrix_free(m_matrix);
+ }
+
+ Matrix & transpose()
+ {
+ return static_cast<Matrix &>( base_type::transpose() );
+ }
+
+ Matrix & scale(double x)
+ {
+ return static_cast<Matrix &>( base_type::scale(x) );
+ }
+
+ Matrix & translate(double x)
+ {
+ return static_cast<Matrix &>( base_type::translate(x) );
+ }
+
+ Matrix & operator+=(base_type::base_type const& _matrix)
+ {
+ return static_cast<Matrix &>( base_type::operator+=(_matrix) );
+ }
+
+ Matrix & operator-=(base_type::base_type const& _matrix)
+ {
+ return static_cast<Matrix &>( base_type::operator-=(_matrix) );
+ }
+
+ friend
+ void swap(Matrix & m1, Matrix & m2);
+ friend
+ void swap_any(Matrix & m1, Matrix & m2);
+
+}; // end class Matrix
+
+
+// warning! this operation invalidates any view of the passed matrix objects
+inline
+void swap(Matrix & m1, Matrix & m2)
+{
+ 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)
+{
+ using std::swap;
+ swap(m1.m_matrix, m2.m_matrix);
+ swap(m1.m_rows, m2.m_rows);
+ swap(m1.m_columns, m2.m_columns);
+}
+
+
+
+class ConstMatrixView : public detail::BaseMatrixImpl
+{
+ public:
+ typedef detail::BaseMatrixImpl base_type;
+
+ public:
+ ConstMatrixView(const base_type & _matrix, size_t k1, size_t k2, size_t n1, size_t n2)
+ : m_matrix_view( gsl_matrix_const_submatrix(_matrix.get_gsl_matrix(), k1, k2, n1, n2) )
+ {
+ m_rows = n1;
+ m_columns = n2;
+ m_matrix = const_cast<gsl_matrix*>( &(m_matrix_view.matrix) );
+ }
+
+ ConstMatrixView(const ConstMatrixView & _matrix)
+ : base_type(),
+ m_matrix_view(_matrix.m_matrix_view)
+ {
+ m_rows = _matrix.rows();
+ m_columns = _matrix.columns();
+ m_matrix = const_cast<gsl_matrix*>( &(m_matrix_view.matrix) );
+ }
+
+ ConstMatrixView(const base_type & _matrix)
+ : m_matrix_view(gsl_matrix_const_submatrix(_matrix.get_gsl_matrix(), 0, 0, _matrix.rows(), _matrix.columns()))
+ {
+ m_rows = _matrix.rows();
+ m_columns = _matrix.columns();
+ m_matrix = const_cast<gsl_matrix*>( &(m_matrix_view.matrix) );
+ }
+
+ private:
+ gsl_matrix_const_view m_matrix_view;
+
+}; // end class ConstMatrixView
+
+
+
+
+class MatrixView : public detail::MatrixImpl
+{
+ public:
+ typedef detail::MatrixImpl base_type;
+
+ public:
+ MatrixView(base_type & _matrix, size_t k1, size_t k2, size_t n1, size_t n2)
+ {
+ m_rows = n1;
+ m_columns = n2;
+ m_matrix_view
+ = gsl_matrix_submatrix(_matrix.get_gsl_matrix(), k1, k2, n1, n2);
+ m_matrix = &(m_matrix_view.matrix);
+ }
+
+ MatrixView(const MatrixView & _matrix)
+ : base_type()
+ {
+ m_rows = _matrix.rows();
+ m_columns = _matrix.columns();
+ m_matrix_view = _matrix.m_matrix_view;
+ m_matrix = &(m_matrix_view.matrix);
+ }
+
+ MatrixView(Matrix & _matrix)
+ {
+ m_rows = _matrix.rows();
+ m_columns = _matrix.columns();
+ m_matrix_view
+ = gsl_matrix_submatrix(_matrix.get_gsl_matrix(), 0, 0, rows(), columns());
+ m_matrix = &(m_matrix_view.matrix);
+ }
+
+ MatrixView & operator=(MatrixView const& _matrix)
+ {
+ assert( rows() == _matrix.rows() && columns() == _matrix.columns() );
+ gsl_matrix_memcpy(m_matrix, _matrix.m_matrix);
+ return *this;
+ }
+
+ MatrixView & operator=(base_type::base_type const& _matrix)
+ {
+ assert( rows() == _matrix.rows() && columns() == _matrix.columns() );
+ gsl_matrix_memcpy(m_matrix, _matrix.get_gsl_matrix());
+ return *this;
+ }
+
+ MatrixView & transpose()
+ {
+ return static_cast<MatrixView &>( base_type::transpose() );
+ }
+
+ MatrixView & scale(double x)
+ {
+ return static_cast<MatrixView &>( base_type::scale(x) );
+ }
+
+ MatrixView & translate(double x)
+ {
+ return static_cast<MatrixView &>( base_type::translate(x) );
+ }
+
+ MatrixView & operator+=(base_type::base_type const& _matrix)
+ {
+ return static_cast<MatrixView &>( base_type::operator+=(_matrix) );
+ }
+
+ MatrixView & operator-=(base_type::base_type const& _matrix)
+ {
+ return static_cast<MatrixView &>( base_type::operator-=(_matrix) );
+ }
+
+ friend
+ void swap_view(MatrixView & m1, MatrixView & m2);
+
+ private:
+ gsl_matrix_view m_matrix_view;
+
+}; // end class MatrixView
+
+
+inline
+void swap_view(MatrixView & m1, MatrixView & m2)
+{
+ 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,
+ detail::BaseVectorImpl const& v );
+
+Matrix operator*( detail::BaseMatrixImpl const& A,
+ detail::BaseMatrixImpl const& B );
+
+Matrix pseudo_inverse(detail::BaseMatrixImpl const& A);
+
+double trace (detail::BaseMatrixImpl const& A);
+
+double det (detail::BaseMatrixImpl const& A);
+
+} } // end namespaces
+
+#endif /*_NL_MATRIX_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/include/2geom/numeric/symmetric-matrix-fs-operation.h b/include/2geom/numeric/symmetric-matrix-fs-operation.h
new file mode 100644
index 0000000..c5aaa72
--- /dev/null
+++ b/include/2geom/numeric/symmetric-matrix-fs-operation.h
@@ -0,0 +1,102 @@
+/*
+ * SymmetricMatrix basic operation
+ *
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 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 _NL_SYMMETRIC_MATRIX_FS_OPERATION_H_
+#define _NL_SYMMETRIC_MATRIX_FS_OPERATION_H_
+
+
+#include <2geom/numeric/symmetric-matrix-fs.h>
+#include <2geom/numeric/symmetric-matrix-fs-trace.h>
+
+
+
+
+namespace Geom { namespace NL {
+
+template <size_t N>
+SymmetricMatrix<N> adj(const ConstBaseSymmetricMatrix<N> & S);
+
+template <>
+inline
+SymmetricMatrix<2> adj(const ConstBaseSymmetricMatrix<2> & S)
+{
+ SymmetricMatrix<2> result;
+ result.get<0,0>() = S.get<1,1>();
+ result.get<1,0>() = -S.get<1,0>();
+ result.get<1,1>() = S.get<0,0>();
+ return result;
+}
+
+template <>
+inline
+SymmetricMatrix<3> adj(const ConstBaseSymmetricMatrix<3> & S)
+{
+ SymmetricMatrix<3> result;
+
+ result.get<0,0>() = S.get<1,1>() * S.get<2,2>() - S.get<1,2>() * S.get<2,1>();
+ result.get<1,0>() = S.get<0,2>() * S.get<2,1>() - S.get<0,1>() * S.get<2,2>();
+ result.get<1,1>() = S.get<0,0>() * S.get<2,2>() - S.get<0,2>() * S.get<2,0>();
+ result.get<2,0>() = S.get<0,1>() * S.get<1,2>() - S.get<0,2>() * S.get<1,1>();
+ result.get<2,1>() = S.get<0,2>() * S.get<1,0>() - S.get<0,0>() * S.get<1,2>();
+ result.get<2,2>() = S.get<0,0>() * S.get<1,1>() - S.get<0,1>() * S.get<1,0>();
+ return result;
+}
+
+template <size_t N>
+inline
+SymmetricMatrix<N> inverse(const ConstBaseSymmetricMatrix<N> & S)
+{
+ SymmetricMatrix<N> result = adj(S);
+ double d = det(S);
+ assert (d != 0);
+ result.scale (1/d);
+ return result;
+}
+
+} /* end namespace NL*/ } /* end namespace Geom*/
+
+
+#endif // _NL_SYMMETRIC_MATRIX_FS_OPERATION_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/include/2geom/numeric/symmetric-matrix-fs-trace.h b/include/2geom/numeric/symmetric-matrix-fs-trace.h
new file mode 100644
index 0000000..0e7a28c
--- /dev/null
+++ b/include/2geom/numeric/symmetric-matrix-fs-trace.h
@@ -0,0 +1,427 @@
+/*
+ * SymmetricMatrix trace
+ *
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 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 _NL_TRACE_H_
+#define _NL_TRACE_H_
+
+
+#include <2geom/numeric/matrix.h>
+#include <2geom/numeric/symmetric-matrix-fs.h>
+
+
+
+
+
+namespace Geom { namespace NL {
+
+
+namespace detail
+{
+
+/*
+ * helper routines
+ */
+
+inline
+int sgn_prod (int x, int y)
+{
+ if (x == 0 || y == 0) return 0;
+ if (x == y) return 1;
+ return -1;
+}
+
+inline
+bool abs_less (double x, double y)
+{
+ return (std::fabs(x) < std::fabs(y));
+}
+
+
+/*
+ * trace K-th of symmetric matrix S of order N
+ */
+template <size_t K, size_t N>
+struct trace
+{
+ static double evaluate(const ConstBaseSymmetricMatrix<N> &S);
+};
+
+template <size_t N>
+struct trace<1,N>
+{
+ static
+ double evaluate (const ConstBaseSymmetricMatrix<N> & S)
+ {
+ double t = 0;
+ for (size_t i = 0; i < N; ++i)
+ {
+ t += S(i,i);
+ }
+ return t;
+ }
+};
+
+template <size_t N>
+struct trace<N,N>
+{
+ static
+ double evaluate (const ConstBaseSymmetricMatrix<N> & S)
+ {
+ Matrix M(S);
+ return det(M);
+ }
+};
+
+/*
+ * trace for symmetric matrix of order 2
+ */
+template <>
+struct trace<1,2>
+{
+ static
+ double evaluate (const ConstBaseSymmetricMatrix<2> & S)
+ {
+ return (S.get<0,0>() + S.get<1,1>());
+ }
+};
+
+template <>
+struct trace<2,2>
+{
+ static
+ double evaluate (const ConstBaseSymmetricMatrix<2> & S)
+ {
+ return (S.get<0,0>() * S.get<1,1>() - S.get<0,1>() * S.get<1,0>());
+ }
+};
+
+
+/*
+ * trace for symmetric matrix of order 3
+ */
+template <>
+struct trace<1,3>
+{
+ static
+ double evaluate (const ConstBaseSymmetricMatrix<3> & S)
+ {
+ return (S.get<0,0>() + S.get<1,1>() + S.get<2,2>());
+ }
+};
+
+template <>
+struct trace<2,3>
+{
+ static
+ double evaluate (const ConstBaseSymmetricMatrix<3> & S)
+ {
+ double a00 = S.get<1,1>() * S.get<2,2>() - S.get<1,2>() * S.get<2,1>();
+ double a11 = S.get<0,0>() * S.get<2,2>() - S.get<0,2>() * S.get<2,0>();
+ double a22 = S.get<0,0>() * S.get<1,1>() - S.get<0,1>() * S.get<1,0>();
+ return (a00 + a11 + a22);
+ }
+};
+
+template <>
+struct trace<3,3>
+{
+ static
+ double evaluate (const ConstBaseSymmetricMatrix<3> & S)
+ {
+ double d = S.get<0,0>() * S.get<1,1>() * S.get<2,2>();
+ d += (2 * S.get<1,0>() * S.get<2,0>() * S.get<2,1>());
+ d -= (S.get<0,0>() * S.get<2,1>() * S.get<2,1>());
+ d -= (S.get<1,1>() * S.get<2,0>() * S.get<2,0>());
+ d -= (S.get<2,2>() * S.get<1,0>() * S.get<1,0>());
+ return d;
+ }
+};
+
+
+/*
+ * sign of trace K-th
+ */
+template <size_t K, size_t N>
+struct trace_sgn
+{
+ static
+ int evaluate (const ConstBaseSymmetricMatrix<N> & S)
+ {
+ double d = trace<K, N>::evaluate(S);
+ return sgn(d);
+ }
+};
+
+
+/*
+ * sign of trace for symmetric matrix of order 2
+ */
+template <>
+struct trace_sgn<2,2>
+{
+ static
+ int evaluate (const ConstBaseSymmetricMatrix<2> & S)
+ {
+ double m00 = S.get<0,0>();
+ double m10 = S.get<1,0>();
+ double m11 = S.get<1,1>();
+
+ int sm00 = sgn (m00);
+ int sm10 = sgn (m10);
+ int sm11 = sgn (m11);
+
+ if (sm10 == 0)
+ {
+ return sgn_prod (sm00, sm11);
+ }
+ else
+ {
+ int sm00m11 = sgn_prod (sm00, sm11);
+ if (sm00m11 == 1)
+ {
+ int e00, e10, e11;
+ double f00 = std::frexp (m00, &e00);
+ double f10 = std::frexp (m10, &e10);
+ double f11 = std::frexp (m11, &e11);
+
+ int e0011 = e00 + e11;
+ int e1010 = e10 << 1;
+ int ed = e0011 - e1010;
+
+ if (ed > 1)
+ {
+ return 1;
+ }
+ else if (ed < -1)
+ {
+ return -1;
+ }
+ else
+ {
+ double d = std::ldexp (f00 * f11, ed) - f10 * f10;
+ //std::cout << "trace_sgn<2,2>: det = " << d << std::endl;
+ double eps = std::ldexp (1, -50);
+ if (std::fabs(d) < eps) return 0;
+ return sgn (d);
+ }
+ }
+ return -1;
+ }
+ }
+};
+
+
+/*
+ * sign of trace for symmetric matrix of order 3
+ */
+template <>
+struct trace_sgn<2,3>
+{
+ static
+ int evaluate (const ConstBaseSymmetricMatrix<3> & S)
+ {
+ double eps = std::ldexp (1, -50);
+ double t[6];
+
+ t[0] = S.get<1,1>() * S.get<2,2>();
+ t[1] = - S.get<1,2>() * S.get<2,1>();
+ t[2] = S.get<0,0>() * S.get<2,2>();
+ t[3] = - S.get<0,2>() * S.get<2,0>();
+ t[4] = S.get<0,0>() * S.get<1,1>();
+ t[5] = - S.get<0,1>() * S.get<1,0>();
+
+
+ double* maxp = std::max_element (t, t+6, abs_less);
+ int em;
+ std::frexp(*maxp, &em);
+ double d = 0;
+ for (double i : t)
+ {
+ d += i;
+ }
+ double r = std::fabs (std::ldexp (d, -em)); // relative error
+ //std::cout << "trace_sgn<2,3>: d = " << d << std::endl;
+ //std::cout << "trace_sgn<2,3>: r = " << r << std::endl;
+ if (r < eps) return 0;
+ if (d > 0) return 1;
+ return -1;
+ }
+};
+
+template <>
+struct trace_sgn<3,3>
+{
+ static
+ int evaluate (const ConstBaseSymmetricMatrix<3> & S)
+ {
+
+ double eps = std::ldexp (1, -48);
+ double t[5];
+
+ t[0] = S.get<0,0>() * S.get<1,1>() * S.get<2,2>();
+ t[1] = 2 * S.get<1,0>() * S.get<2,0>() * S.get<2,1>();
+ t[2] = -(S.get<0,0>() * S.get<2,1>() * S.get<2,1>());
+ t[3] = -(S.get<1,1>() * S.get<2,0>() * S.get<2,0>());
+ t[4] = -(S.get<2,2>() * S.get<1,0>() * S.get<1,0>());
+
+ double* maxp = std::max_element (t, t+5, abs_less);
+ int em;
+ std::frexp(*maxp, &em);
+ double d = 0;
+ for (double i : t)
+ {
+ d += i;
+ }
+ //std::cout << "trace_sgn<3,3>: d = " << d << std::endl;
+ double r = std::fabs (std::ldexp (d, -em)); // relative error
+ //std::cout << "trace_sgn<3,3>: r = " << r << std::endl;
+
+ if (r < eps) return 0;
+ if (d > 0) return 1;
+ return -1;
+ }
+}; // end struct trace_sgn<3,3>
+
+} // end namespace detail
+
+
+template <size_t K, size_t N>
+inline
+double trace (const ConstBaseSymmetricMatrix<N> & _matrix)
+{
+ return detail::trace<K, N>::evaluate(_matrix);
+}
+
+template <size_t N>
+inline
+double trace (const ConstBaseSymmetricMatrix<N> & _matrix)
+{
+ return detail::trace<1, N>::evaluate(_matrix);
+}
+
+template <size_t N>
+inline
+double det (const ConstBaseSymmetricMatrix<N> & _matrix)
+{
+ return detail::trace<N, N>::evaluate(_matrix);
+}
+
+
+template <size_t K, size_t N>
+inline
+int trace_sgn (const ConstBaseSymmetricMatrix<N> & _matrix)
+{
+ return detail::trace_sgn<K, N>::evaluate(_matrix);
+}
+
+template <size_t N>
+inline
+int trace_sgn (const ConstBaseSymmetricMatrix<N> & _matrix)
+{
+ return detail::trace_sgn<1, N>::evaluate(_matrix);
+}
+
+template <size_t N>
+inline
+int det_sgn (const ConstBaseSymmetricMatrix<N> & _matrix)
+{
+ return detail::trace_sgn<N, N>::evaluate(_matrix);
+}
+
+/*
+template <size_t N>
+inline
+size_t rank (const ConstBaseSymmetricMatrix<N> & S)
+{
+ THROW_NOTIMPLEMENTED();
+ return 0;
+}
+
+template <>
+inline
+size_t rank<2> (const ConstBaseSymmetricMatrix<2> & S)
+{
+ if (S.is_zero()) return 0;
+ double d = S.get<0,0>() * S.get<1,1>() - S.get<0,1>() * S.get<1,0>();
+ if (d != 0) return 2;
+ return 1;
+}
+
+template <>
+inline
+size_t rank<3> (const ConstBaseSymmetricMatrix<3> & S)
+{
+ if (S.is_zero()) return 0;
+
+ double a20 = S.get<0,1>() * S.get<1,2>() - S.get<0,2>() * S.get<1,1>();
+ double a21 = S.get<0,2>() * S.get<1,0>() - S.get<0,0>() * S.get<1,2>();
+ double a22 = S.get<0,0>() * S.get<1,1>() - S.get<0,1>() * S.get<1,0>();
+ double d = a20 * S.get<2,0>() + a21 * S.get<2,1>() + a22 * S.get<2,2>();
+
+ if (d != 0) return 3;
+
+ if (a20 != 0 || a21 != 0 || a22 != 0) return 2;
+
+ double a00 = S.get<1,1>() * S.get<2,2>() - S.get<1,2>() * S.get<2,1>();
+ if (a00 != 0) return 2;
+
+ double a10 = S.get<0,2>() * S.get<2,1>() - S.get<0,1>() * S.get<2,2>();
+ if (a10 != 0) return 2;
+
+ double a11 = S.get<0,0>() * S.get<2,2>() - S.get<0,2>() * S.get<2,0>();
+ if (a11 != 0) return 2;
+
+ return 1;
+}
+*/
+
+} /* end namespace NL*/ } /* end namespace Geom*/
+
+
+
+
+#endif // _NL_TRACE_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/include/2geom/numeric/symmetric-matrix-fs.h b/include/2geom/numeric/symmetric-matrix-fs.h
new file mode 100644
index 0000000..2fadd69
--- /dev/null
+++ b/include/2geom/numeric/symmetric-matrix-fs.h
@@ -0,0 +1,733 @@
+/*
+ * SymmetricMatrix, ConstSymmetricMatrixView, SymmetricMatrixView template
+ * classes implement fixed size symmetric matrix; "views" mimic the semantic
+ * of C++ references: any operation performed on a "view" is actually performed
+ * on the "viewed object"
+ *
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 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 _NL_SYMMETRIC_MATRIX_FS_H_
+#define _NL_SYMMETRIC_MATRIX_FS_H_
+
+
+#include <2geom/numeric/vector.h>
+#include <2geom/numeric/matrix.h>
+#include <2geom/utils.h>
+#include <2geom/exception.h>
+
+#include <boost/static_assert.hpp>
+
+#include <cassert>
+#include <utility> // for std::pair
+#include <algorithm> // for std::swap, std::copy
+#include <sstream>
+#include <string>
+
+
+
+namespace Geom { namespace NL {
+
+
+namespace detail
+{
+
+template <size_t I, size_t J, bool B = (I < J)>
+struct index
+{
+ static const size_t K = index<J, I>::K;
+};
+
+template <size_t I, size_t J>
+struct index<I, J, false>
+{
+ static const size_t K = (((I+1) * I) >> 1) + J;
+};
+
+} // end namespace detail
+
+
+
+
+template <size_t N>
+class ConstBaseSymmetricMatrix;
+
+template <size_t N>
+class BaseSymmetricMatrix;
+
+template <size_t N>
+class SymmetricMatrix;
+
+template <size_t N>
+class ConstSymmetricMatrixView;
+
+template <size_t N>
+class SymmetricMatrixView;
+
+
+
+// declaration needed for friend clause
+template <size_t N>
+bool operator== (ConstBaseSymmetricMatrix<N> const& _smatrix1,
+ ConstBaseSymmetricMatrix<N> const& _smatrix2);
+
+
+
+
+template <size_t N>
+class ConstBaseSymmetricMatrix
+{
+ public:
+ const static size_t DIM = N;
+ const static size_t DATA_SIZE = ((DIM+1) * DIM) / 2;
+
+ public:
+
+ ConstBaseSymmetricMatrix (VectorView const& _data)
+ : m_data(_data)
+ {
+ }
+
+ double operator() (size_t i, size_t j) const
+ {
+ return m_data[get_index(i,j)];
+ }
+
+ template <size_t I, size_t J>
+ double get() const
+ {
+ BOOST_STATIC_ASSERT ((I < N && J < N));
+ return m_data[detail::index<I, J>::K];
+ }
+
+
+ size_t rows() const
+ {
+ return DIM;
+ }
+
+ size_t columns() const
+ {
+ return DIM;
+ }
+
+ bool is_zero() const
+ {
+ return m_data.is_zero();
+ }
+
+ bool is_positive() const
+ {
+ return m_data.is_positive();
+ }
+
+ bool is_negative() const
+ {
+ return m_data.is_negative();
+ }
+
+ bool is_non_negative() const
+ {
+ return m_data.is_non_negative();
+ }
+
+ double min() const
+ {
+ return m_data.min();
+ }
+
+ double max() const
+ {
+ return m_data.max();
+ }
+
+ std::pair<size_t, size_t>
+ min_index() const
+ {
+ std::pair<size_t, size_t> indices(0,0);
+ double min_value = m_data[0];
+ for (size_t i = 1; i < DIM; ++i)
+ {
+ for (size_t j = 0; j <= i; ++j)
+ {
+ if (min_value > (*this)(i,j))
+ {
+ min_value = (*this)(i,j);
+ indices.first = i;
+ indices.second = j;
+ }
+ }
+ }
+ return indices;
+ }
+
+ std::pair<size_t, size_t>
+ max_index() const
+ {
+ std::pair<size_t, size_t> indices(0,0);
+ double max_value = m_data[0];
+ for (size_t i = 1; i < DIM; ++i)
+ {
+ for (size_t j = 0; j <= i; ++j)
+ {
+ if (max_value < (*this)(i,j))
+ {
+ max_value = (*this)(i,j);
+ indices.first = i;
+ indices.second = j;
+ }
+ }
+ }
+ return indices;
+ }
+
+ size_t min_on_row_index (size_t i) const
+ {
+ size_t idx = 0;
+ double min_value = (*this)(i,0);
+ for (size_t j = 1; j < DIM; ++j)
+ {
+ if (min_value > (*this)(i,j))
+ {
+ min_value = (*this)(i,j);
+ idx = j;
+ }
+ }
+ return idx;
+ }
+
+ size_t max_on_row_index (size_t i) const
+ {
+ size_t idx = 0;
+ double max_value = (*this)(i,0);
+ for (size_t j = 1; j < DIM; ++j)
+ {
+ if (max_value < (*this)(i,j))
+ {
+ max_value = (*this)(i,j);
+ idx = j;
+ }
+ }
+ return idx;
+ }
+
+ size_t min_on_column_index (size_t j) const
+ {
+ return min_on_row_index(j);
+ }
+
+ size_t max_on_column_index (size_t j) const
+ {
+ return max_on_row_index(j);
+ }
+
+ size_t min_on_diag_index () const
+ {
+ size_t idx = 0;
+ double min_value = (*this)(0,0);
+ for (size_t i = 1; i < DIM; ++i)
+ {
+ if (min_value > (*this)(i,i))
+ {
+ min_value = (*this)(i,i);
+ idx = i;
+ }
+ }
+ return idx;
+ }
+
+ size_t max_on_diag_index () const
+ {
+ size_t idx = 0;
+ double max_value = (*this)(0,0);
+ for (size_t i = 1; i < DIM; ++i)
+ {
+ if (max_value < (*this)(i,i))
+ {
+ max_value = (*this)(i,i);
+ idx = i;
+ }
+ }
+ return idx;
+ }
+
+ std::string str() const;
+
+ ConstSymmetricMatrixView<N-1> main_minor_const_view() const;
+
+ SymmetricMatrix<N> operator- () const;
+
+ Vector operator* (ConstVectorView _vector) const
+ {
+ assert (_vector.size() == DIM);
+ Vector result(DIM, 0.0);
+
+ for (size_t i = 0; i < DIM; ++i)
+ {
+ for (size_t j = 0; j < DIM; ++j)
+ {
+ result[i] += (*this)(i,j) * _vector[j];
+ }
+ }
+ return result;
+ }
+
+ protected:
+ static size_t get_index (size_t i, size_t j)
+ {
+ if (i < j) return get_index (j, i);
+ size_t k = (i+1) * i;
+ k >>= 1;
+ k += j;
+ return k;
+ }
+
+ protected:
+ ConstVectorView get_data() const
+ {
+ return m_data;
+ }
+
+ friend
+ bool operator==<N> (ConstBaseSymmetricMatrix const& _smatrix1,
+ ConstBaseSymmetricMatrix const& _smatrix2);
+
+ protected:
+ VectorView m_data;
+
+}; //end ConstBaseSymmetricMatrix
+
+
+template <size_t N>
+class BaseSymmetricMatrix : public ConstBaseSymmetricMatrix<N>
+{
+ public:
+ typedef ConstBaseSymmetricMatrix<N> base_type;
+
+
+ public:
+
+ BaseSymmetricMatrix (VectorView const& _data)
+ : base_type(_data)
+ {
+ }
+
+ 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)
+ {
+ return m_data[base_type::get_index(i,j)];
+ }
+
+ template <size_t I, size_t J>
+ double& get()
+ {
+ BOOST_STATIC_ASSERT ((I < N && J < N));
+ return m_data[detail::index<I, J>::K];
+ }
+
+ void set_all (double x)
+ {
+ m_data.set_all(x);
+ }
+
+ SymmetricMatrixView<N-1> main_minor_view();
+
+ BaseSymmetricMatrix& transpose() const
+ {
+ return (*this);
+ }
+
+ BaseSymmetricMatrix& translate (double c)
+ {
+ m_data.translate(c);
+ return (*this);
+ }
+
+ BaseSymmetricMatrix& scale (double c)
+ {
+ m_data.scale(c);
+ return (*this);
+ }
+
+ BaseSymmetricMatrix& operator+= (base_type const& _smatrix)
+ {
+ m_data += (static_cast<const BaseSymmetricMatrix &>(_smatrix).m_data);
+ return (*this);
+ }
+
+ BaseSymmetricMatrix& operator-= (base_type const& _smatrix)
+ {
+ m_data -= (static_cast<const BaseSymmetricMatrix &>(_smatrix).m_data);
+ return (*this);
+ }
+
+ using base_type::DIM;
+ using base_type::DATA_SIZE;
+ using base_type::m_data;
+ using base_type::operator-;
+ using base_type::operator*;
+
+}; //end BaseSymmetricMatrix
+
+
+template <size_t N>
+class SymmetricMatrix : public BaseSymmetricMatrix<N>
+{
+ public:
+ typedef BaseSymmetricMatrix<N> base_type;
+ typedef typename base_type::base_type base_base_type;
+
+ using base_type::DIM;
+ using base_type::DATA_SIZE;
+ using base_type::m_data;
+
+ public:
+ SymmetricMatrix ()
+ : base_type (VectorView(m_adata, DATA_SIZE))
+ {
+ }
+
+ explicit
+ SymmetricMatrix (ConstVectorView _data)
+ : base_type (VectorView(m_adata, DATA_SIZE))
+ {
+ assert (_data.size() == DATA_SIZE);
+ m_data = _data;
+ }
+
+ explicit
+ SymmetricMatrix (const double _data[DATA_SIZE])
+ : base_type (VectorView(m_adata, DATA_SIZE))
+ {
+ std::copy (_data, _data + DATA_SIZE, m_adata);
+ }
+
+ SymmetricMatrix (SymmetricMatrix const& _smatrix)
+ : base_type (VectorView(m_adata, DATA_SIZE))
+ {
+ m_data = _smatrix.m_data;
+ }
+
+ explicit
+ SymmetricMatrix (base_base_type const& _smatrix)
+ : base_type (VectorView(m_adata, DATA_SIZE))
+ {
+ m_data = static_cast<const ConstSymmetricMatrixView<DIM> &>(_smatrix).m_data;
+ }
+
+ explicit
+ SymmetricMatrix (ConstMatrixView const& _matrix)
+ : base_type (VectorView(m_adata, DATA_SIZE))
+ {
+ assert (_matrix.rows() == N && _matrix.columns() == N);
+ for (size_t i = 0; i < N; ++i)
+ for (size_t j = 0; j <= i ; ++j)
+ (*this)(i,j) = _matrix(i,j);
+ }
+
+ SymmetricMatrix& operator= (SymmetricMatrix const& _smatrix)
+ {
+ m_data = _smatrix.m_data;
+ return (*this);
+ }
+
+ SymmetricMatrix& operator= (base_base_type const& _smatrix)
+ {
+
+ m_data = static_cast<const ConstSymmetricMatrixView<DIM> &>(_smatrix).m_data;
+ return (*this);
+ }
+
+ SymmetricMatrix& operator= (ConstMatrixView const& _matrix)
+ {
+ assert (_matrix.rows() == N && _matrix.columns() == N);
+ for (size_t i = 0; i < N; ++i)
+ for (size_t j = 0; j <= i ; ++j)
+ (*this)(i,j) = _matrix(i,j);
+
+ return (*this);
+ }
+
+ // needed for accessing m_adata
+ friend class ConstSymmetricMatrixView<DIM>;
+ friend class SymmetricMatrixView<DIM>;
+ private:
+ double m_adata[DATA_SIZE];
+}; //end SymmetricMatrix
+
+
+template <size_t N>
+class ConstSymmetricMatrixView : public ConstBaseSymmetricMatrix<N>
+{
+ public:
+ typedef ConstBaseSymmetricMatrix<N> base_type;
+
+ using base_type::DIM;
+ using base_type::DATA_SIZE;
+ using base_type::m_data;
+
+
+ public:
+
+ explicit
+ ConstSymmetricMatrixView (ConstVectorView _data)
+ : base_type (const_vector_view_cast(_data))
+ {
+ assert (_data.size() == DATA_SIZE);
+ }
+
+ explicit
+ ConstSymmetricMatrixView (const double _data[DATA_SIZE])
+ : base_type (const_vector_view_cast (ConstVectorView (_data, DATA_SIZE)))
+ {
+ }
+
+ ConstSymmetricMatrixView (const ConstSymmetricMatrixView & _smatrix)
+ : base_type (_smatrix.m_data)
+ {
+ }
+
+ ConstSymmetricMatrixView (const base_type & _smatrix)
+ : base_type (static_cast<const ConstSymmetricMatrixView &>(_smatrix).m_data)
+ {
+ }
+
+}; //end SymmetricMatrix
+
+
+// declaration needed for friend clause
+template <size_t N>
+void swap_view(SymmetricMatrixView<N> & m1, SymmetricMatrixView<N> & m2);
+
+
+template <size_t N>
+class SymmetricMatrixView : public BaseSymmetricMatrix<N>
+{
+ public:
+ typedef BaseSymmetricMatrix<N> base_type;
+ typedef typename base_type::base_type base_base_type;
+
+ using base_type::DIM;
+ using base_type::DATA_SIZE;
+ using base_type::m_data;
+
+ public:
+
+ explicit
+ SymmetricMatrixView (VectorView _data)
+ : base_type (_data)
+ {
+ assert (_data.size() == DATA_SIZE);
+ }
+
+ explicit
+ SymmetricMatrixView (double _data[DATA_SIZE])
+ : base_type (VectorView (_data, DATA_SIZE))
+ {
+ }
+
+ SymmetricMatrixView (const SymmetricMatrixView & _smatrix)
+ : base_type (_smatrix.m_data)
+ {
+ }
+
+ SymmetricMatrixView (SymmetricMatrix<DIM> & _smatrix)
+ : base_type (VectorView (_smatrix.m_adata, DATA_SIZE))
+ {
+ }
+
+ SymmetricMatrixView& operator= (const SymmetricMatrixView & _smatrix)
+ {
+ m_data = _smatrix.m_data;
+ return (*this);
+ }
+
+ SymmetricMatrixView& operator= (const base_base_type & _smatrix)
+ {
+ m_data = static_cast<const ConstSymmetricMatrixView<DIM> &>(_smatrix).m_data;
+ return (*this);
+ }
+
+ friend
+ void swap_view<N>(SymmetricMatrixView & m1, SymmetricMatrixView & m2);
+
+}; //end SymmetricMatrix
+
+
+
+
+/*
+ * class ConstBaseSymmetricMatrix methods
+ */
+
+template <size_t N>
+inline
+std::string ConstBaseSymmetricMatrix<N>::str() const
+{
+ std::ostringstream oss;
+ oss << (*this);
+ return oss.str();
+}
+
+template <size_t N>
+inline
+ConstSymmetricMatrixView<N-1>
+ConstBaseSymmetricMatrix<N>::main_minor_const_view() const
+{
+ ConstVectorView data(m_data.get_gsl_vector()->data, DATA_SIZE - DIM);
+ ConstSymmetricMatrixView<N-1> mm(data);
+ return mm;
+}
+
+template <size_t N>
+inline
+SymmetricMatrix<N> ConstBaseSymmetricMatrix<N>::operator- () const
+{
+ SymmetricMatrix<N> result;
+ for (size_t i = 0; i < DATA_SIZE; ++i)
+ {
+ result.m_data[i] = -m_data[i];
+ }
+ return result;
+}
+
+
+/*
+ * class ConstBaseSymmetricMatrix friend free functions
+ */
+
+template <size_t N>
+inline
+bool operator== (ConstBaseSymmetricMatrix<N> const& _smatrix1,
+ ConstBaseSymmetricMatrix<N> const& _smatrix2)
+{
+ return (_smatrix1.m_data == _smatrix2.m_data);
+}
+
+/*
+ * class ConstBaseSymmetricMatrix related free functions
+ */
+
+template< size_t N, class charT >
+inline
+std::basic_ostream<charT> &
+operator<< (std::basic_ostream<charT> & os,
+ const ConstBaseSymmetricMatrix<N> & _matrix)
+{
+ os << "[[" << _matrix(0,0);
+ for (size_t j = 1; j < N; ++j)
+ {
+ os << ", " << _matrix(0,j);
+ }
+ os << "]";
+ for (size_t i = 1; i < N; ++i)
+ {
+ os << "\n [" << _matrix(i,0);
+ for (size_t j = 1; j < N; ++j)
+ {
+ os << ", " << _matrix(i,j);
+ }
+ os << "]";
+ }
+ os << "]";
+ return os;
+}
+
+
+/*
+ * class ConstBaseSymmetricMatrix specialized methods
+ */
+
+template<>
+inline
+size_t ConstBaseSymmetricMatrix<2>::get_index (size_t i, size_t j)
+{
+ return (i+j);
+}
+
+template<>
+inline
+size_t ConstBaseSymmetricMatrix<3>::get_index (size_t i, size_t j)
+{
+ size_t k = i + j;
+ if (i == 2 || j == 2) ++k;
+ return k;
+}
+
+
+/*
+ * class BaseSymmetricMatrix methods
+ */
+
+template <size_t N>
+inline
+SymmetricMatrixView<N-1> BaseSymmetricMatrix<N>::main_minor_view()
+{
+ VectorView data(m_data.get_gsl_vector()->data, DATA_SIZE - DIM);
+ SymmetricMatrixView<N-1> mm(data);
+ return mm;
+}
+
+
+/*
+ * class SymmetricMatrixView friend free functions
+ */
+
+template <size_t N>
+inline
+void swap_view(SymmetricMatrixView<N> & m1, SymmetricMatrixView<N> & m2)
+{
+ swap_view(m1.m_data, m2.m_data);
+}
+
+} /* end namespace NL*/ } /* end namespace Geom*/
+
+
+
+
+#endif // _NL_SYMMETRIC_MATRIX_FS_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/include/2geom/numeric/vector.h b/include/2geom/numeric/vector.h
new file mode 100644
index 0000000..b66115b
--- /dev/null
+++ b/include/2geom/numeric/vector.h
@@ -0,0 +1,594 @@
+/*
+ * Vector, VectorView, ConstVectorView classes wrap the gsl vector routines;
+ * "views" mimic the semantic of C++ references: any operation performed
+ * on a "view" is actually performed on the "viewed object"
+ *
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 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.
+ */
+
+
+
+
+#ifndef _NL_VECTOR_H_
+#define _NL_VECTOR_H_
+
+#include <cassert>
+#include <algorithm> // for std::swap
+#include <vector>
+#include <sstream>
+#include <string>
+
+
+#include <gsl/gsl_vector.h>
+#include <gsl/gsl_blas.h>
+
+
+namespace Geom { namespace NL {
+
+namespace detail
+{
+
+class BaseVectorImpl
+{
+ public:
+ double const& operator[](size_t i) const
+ {
+ return *gsl_vector_const_ptr(m_vector, i);
+ }
+
+ const gsl_vector* get_gsl_vector() const
+ {
+ return m_vector;
+ }
+ bool is_zero() const
+ {
+ return gsl_vector_isnull(m_vector);
+ }
+
+ bool is_positive() const
+ {
+ for ( size_t i = 0; i < size(); ++i )
+ {
+ if ( (*this)[i] <= 0 ) return false;
+ }
+ return true;
+ }
+
+ bool is_negative() const
+ {
+ for ( size_t i = 0; i < size(); ++i )
+ {
+ if ( (*this)[i] >= 0 ) return false;
+ }
+ return true;
+ }
+
+ bool is_non_negative() const
+ {
+ for ( size_t i = 0; i < size(); ++i )
+ {
+ if ( (*this)[i] < 0 ) return false;
+ }
+ return true;
+ }
+
+ double max() const
+ {
+ return gsl_vector_max(m_vector);
+ }
+
+ double min() const
+ {
+ return gsl_vector_min(m_vector);
+ }
+
+ size_t max_index() const
+ {
+ return gsl_vector_max_index(m_vector);
+ }
+
+ size_t min_index() const
+ {
+ return gsl_vector_min_index(m_vector);
+ }
+
+ size_t size() const
+ {
+ return m_size;
+ }
+
+ std::string str() const;
+
+ virtual ~BaseVectorImpl()
+ {
+ }
+
+ protected:
+ size_t m_size;
+ gsl_vector* m_vector;
+
+}; // end class BaseVectorImpl
+
+
+inline
+bool operator== (BaseVectorImpl const& v1, BaseVectorImpl const& v2)
+{
+ if (v1.size() != v2.size()) return false;
+
+ for (size_t i = 0; i < v1.size(); ++i)
+ {
+ if (v1[i] != v2[i]) return false;
+ }
+ return true;
+}
+
+template< class charT >
+inline
+std::basic_ostream<charT> &
+operator<< (std::basic_ostream<charT> & os, const BaseVectorImpl & _vector)
+{
+ if (_vector.size() == 0 ) return os;
+ os << "[" << _vector[0];
+ for (unsigned int i = 1; i < _vector.size(); ++i)
+ {
+ os << ", " << _vector[i];
+ }
+ os << "]";
+ return os;
+}
+
+inline
+std::string BaseVectorImpl::str() const
+{
+ std::ostringstream oss;
+ oss << (*this);
+ return oss.str();
+}
+
+inline
+double dot(BaseVectorImpl const& v1, BaseVectorImpl const& v2)
+{
+ double result;
+ gsl_blas_ddot(v1.get_gsl_vector(), v2.get_gsl_vector(), &result);
+ return result;
+}
+
+
+class VectorImpl : public BaseVectorImpl
+{
+ public:
+ typedef BaseVectorImpl base_type;
+
+ public:
+ void set_all(double x)
+ {
+ gsl_vector_set_all(m_vector, x);
+ }
+
+ void set_basis(size_t i)
+ {
+ gsl_vector_set_basis(m_vector, i);
+ }
+
+ using base_type::operator[];
+
+ double & operator[](size_t i)
+ {
+ return *gsl_vector_ptr(m_vector, i);
+ }
+
+ using base_type::get_gsl_vector;
+
+ gsl_vector* get_gsl_vector()
+ {
+ return m_vector;
+ }
+
+ void swap_elements(size_t i, size_t j)
+ {
+ gsl_vector_swap_elements(m_vector, i, j);
+ }
+
+ void reverse()
+ {
+ gsl_vector_reverse(m_vector);
+ }
+
+ VectorImpl & scale(double x)
+ {
+ gsl_vector_scale(m_vector, x);
+ return (*this);
+ }
+
+ VectorImpl & translate(double x)
+ {
+ gsl_vector_add_constant(m_vector, x);
+ return (*this);
+ }
+
+ VectorImpl & operator+=(base_type const& _vector)
+ {
+ gsl_vector_add(m_vector, _vector.get_gsl_vector());
+ return (*this);
+ }
+
+ VectorImpl & operator-=(base_type const& _vector)
+ {
+ gsl_vector_sub(m_vector, _vector.get_gsl_vector());
+ return (*this);
+ }
+
+}; // end class VectorImpl
+
+} // end namespace detail
+
+
+using detail::operator==;
+using detail::operator<<;
+
+class Vector : public detail::VectorImpl
+{
+ public:
+ typedef detail::VectorImpl base_type;
+
+ public:
+ Vector(size_t n)
+ {
+ m_size = n;
+ m_vector = gsl_vector_alloc(n);
+ }
+
+ Vector(size_t n, double x)
+ {
+ m_size = n;
+ m_vector = gsl_vector_alloc(n);
+ gsl_vector_set_all(m_vector, x);
+ }
+
+ // create a vector with n elements all set to zero
+ // but the i-th that is set to 1
+ Vector(size_t n, size_t i)
+ {
+ m_size = n;
+ m_vector = gsl_vector_alloc(n);
+ gsl_vector_set_basis(m_vector, i);
+ }
+
+ Vector(Vector const& _vector)
+ : base_type()
+ {
+ m_size = _vector.size();
+ m_vector = gsl_vector_alloc(size());
+ gsl_vector_memcpy(m_vector, _vector.m_vector);
+ }
+
+ explicit
+ Vector(base_type::base_type const& _vector)
+ {
+ m_size = _vector.size();
+ m_vector = gsl_vector_alloc(size());
+ gsl_vector_memcpy(m_vector, _vector.get_gsl_vector());
+ }
+
+ ~Vector() override
+ {
+ gsl_vector_free(m_vector);
+ }
+
+
+ Vector & operator=(Vector const& _vector)
+ {
+ assert( size() == _vector.size() );
+ gsl_vector_memcpy(m_vector, _vector.m_vector);
+ return (*this);
+ }
+
+ Vector & operator=(base_type::base_type const& _vector)
+ {
+ assert( size() == _vector.size() );
+ gsl_vector_memcpy(m_vector, _vector.get_gsl_vector());
+ return (*this);
+ }
+
+ Vector & scale(double x)
+ {
+ return static_cast<Vector&>( base_type::scale(x) );
+ }
+
+ Vector & translate(double x)
+ {
+ return static_cast<Vector&>( base_type::translate(x) );
+ }
+
+ Vector & operator+=(base_type::base_type const& _vector)
+ {
+ return static_cast<Vector&>( base_type::operator+=(_vector) );
+ }
+
+ Vector & operator-=(base_type::base_type const& _vector)
+ {
+ return static_cast<Vector&>( base_type::operator-=(_vector) );
+ }
+
+ friend
+ void swap(Vector & v1, Vector & v2);
+ friend
+ void swap_any(Vector & v1, Vector & v2);
+
+}; // end class Vector
+
+
+// warning! these operations invalidate any view of the passed vector objects
+inline
+void swap(Vector & v1, Vector & v2)
+{
+ assert(v1.size() == v2.size());
+ using std::swap;
+ swap(v1.m_vector, v2.m_vector);
+}
+
+inline
+void swap_any(Vector & v1, Vector & v2)
+{
+ using std::swap;
+ swap(v1.m_vector, v2.m_vector);
+ swap(v1.m_size, v2.m_size);
+}
+
+
+class ConstVectorView : public detail::BaseVectorImpl
+{
+ public:
+ typedef detail::BaseVectorImpl base_type;
+
+ public:
+ ConstVectorView(const base_type & _vector, size_t n, size_t offset = 0)
+ : m_vector_view( gsl_vector_const_subvector(_vector.get_gsl_vector(), offset, n) )
+ {
+ m_size = n;
+ m_vector = const_cast<gsl_vector*>( &(m_vector_view.vector) );
+ }
+
+ ConstVectorView(const base_type & _vector, size_t n, size_t offset , size_t stride)
+ : m_vector_view( gsl_vector_const_subvector_with_stride(_vector.get_gsl_vector(), offset, stride, n) )
+ {
+ m_size = n;
+ m_vector = const_cast<gsl_vector*>( &(m_vector_view.vector) );
+ }
+
+ ConstVectorView(const double* _vector, size_t n, size_t offset = 0)
+ : m_vector_view( gsl_vector_const_view_array(_vector + offset, n) )
+ {
+ m_size = n;
+ m_vector = const_cast<gsl_vector*>( &(m_vector_view.vector) );
+ }
+
+ ConstVectorView(const double* _vector, size_t n, size_t offset, size_t stride)
+ : m_vector_view( gsl_vector_const_view_array_with_stride(_vector + offset, stride, n) )
+ {
+ m_size = n;
+ m_vector = const_cast<gsl_vector*>( &(m_vector_view.vector) );
+ }
+
+ explicit
+ ConstVectorView(gsl_vector_const_view _gsl_vector_view)
+ : m_vector_view(_gsl_vector_view)
+ {
+ m_vector = const_cast<gsl_vector*>( &(m_vector_view.vector) );
+ m_size = m_vector->size;
+ }
+
+ explicit
+ ConstVectorView(const std::vector<double>& _vector)
+ : m_vector_view( gsl_vector_const_view_array(&(_vector[0]), _vector.size()) )
+ {
+ m_vector = const_cast<gsl_vector*>( &(m_vector_view.vector) );
+ m_size = _vector.size();
+ }
+
+ ConstVectorView(const ConstVectorView & _vector)
+ : base_type(),
+ m_vector_view(_vector.m_vector_view)
+ {
+ m_size = _vector.size();
+ m_vector = const_cast<gsl_vector*>( &(m_vector_view.vector) );
+ }
+
+ ConstVectorView(const base_type & _vector)
+ : m_vector_view(gsl_vector_const_subvector(_vector.get_gsl_vector(), 0, _vector.size()))
+ {
+ m_size = _vector.size();
+ m_vector = const_cast<gsl_vector*>( &(m_vector_view.vector) );
+ }
+
+ private:
+ gsl_vector_const_view m_vector_view;
+
+}; // end class ConstVectorView
+
+
+
+
+class VectorView : public detail::VectorImpl
+{
+ public:
+ typedef detail::VectorImpl base_type;
+
+ public:
+ VectorView(base_type & _vector, size_t n, size_t offset = 0, size_t stride = 1)
+ {
+ m_size = n;
+ if (stride == 1)
+ {
+ m_vector_view
+ = gsl_vector_subvector(_vector.get_gsl_vector(), offset, n);
+ m_vector = &(m_vector_view.vector);
+ }
+ else
+ {
+ m_vector_view
+ = gsl_vector_subvector_with_stride(_vector.get_gsl_vector(), offset, stride, n);
+ m_vector = &(m_vector_view.vector);
+ }
+ }
+
+ VectorView(double* _vector, size_t n, size_t offset = 0, size_t stride = 1)
+ {
+ m_size = n;
+ if (stride == 1)
+ {
+ m_vector_view
+ = gsl_vector_view_array(_vector + offset, n);
+ m_vector = &(m_vector_view.vector);
+ }
+ else
+ {
+ m_vector_view
+ = gsl_vector_view_array_with_stride(_vector + offset, stride, n);
+ m_vector = &(m_vector_view.vector);
+ }
+
+ }
+
+ VectorView(const VectorView & _vector)
+ : base_type()
+ {
+ m_size = _vector.size();
+ m_vector_view = _vector.m_vector_view;
+ m_vector = &(m_vector_view.vector);
+ }
+
+ VectorView(Vector & _vector)
+ {
+ m_size = _vector.size();
+ m_vector_view = gsl_vector_subvector(_vector.get_gsl_vector(), 0, size());
+ m_vector = &(m_vector_view.vector);
+ }
+
+ explicit
+ VectorView(gsl_vector_view _gsl_vector_view)
+ : m_vector_view(_gsl_vector_view)
+ {
+ m_vector = &(m_vector_view.vector);
+ m_size = m_vector->size;
+ }
+
+ explicit
+ VectorView(std::vector<double> & _vector)
+ {
+ m_size = _vector.size();
+ m_vector_view = gsl_vector_view_array(&(_vector[0]), _vector.size());
+ m_vector = &(m_vector_view.vector);
+ }
+
+ VectorView & operator=(VectorView const& _vector)
+ {
+ assert( size() == _vector.size() );
+ gsl_vector_memcpy(m_vector, _vector.get_gsl_vector());
+ return (*this);
+ }
+
+ VectorView & operator=(base_type::base_type const& _vector)
+ {
+ assert( size() == _vector.size() );
+ gsl_vector_memcpy(m_vector, _vector.get_gsl_vector());
+ return (*this);
+ }
+
+ VectorView & scale(double x)
+ {
+ return static_cast<VectorView&>( base_type::scale(x) );
+ }
+
+ VectorView & translate(double x)
+ {
+ return static_cast<VectorView&>( base_type::translate(x) );
+ }
+
+ VectorView & operator+=(base_type::base_type const& _vector)
+ {
+ return static_cast<VectorView&>( base_type::operator+=(_vector) );
+ }
+
+ VectorView & operator-=(base_type::base_type const& _vector)
+ {
+ return static_cast<VectorView&>( base_type::operator-=(_vector) );
+ }
+
+ friend
+ void swap_view(VectorView & v1, VectorView & v2);
+
+ private:
+ gsl_vector_view m_vector_view;
+
+}; // end class VectorView
+
+
+inline
+void swap_view(VectorView & v1, VectorView & v2)
+{
+ assert( v1.size() == v2.size() );
+ using std::swap;
+ swap(v1.m_vector_view, v2.m_vector_view); // not swap m_vector too
+}
+
+inline
+const VectorView & const_vector_view_cast (const ConstVectorView & view)
+{
+ const detail::BaseVectorImpl & bvi
+ = static_cast<const detail::BaseVectorImpl &>(view);
+ const VectorView & vv = reinterpret_cast<const VectorView &>(bvi);
+ return vv;
+}
+
+inline
+VectorView & const_vector_view_cast (ConstVectorView & view)
+{
+ detail::BaseVectorImpl & bvi
+ = static_cast<detail::BaseVectorImpl &>(view);
+ VectorView & vv = reinterpret_cast<VectorView &>(bvi);
+ return vv;
+}
+
+
+} } // end namespaces
+
+
+#endif /*_NL_VECTOR_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/include/2geom/ord.h b/include/2geom/ord.h
new file mode 100644
index 0000000..e190a4a
--- /dev/null
+++ b/include/2geom/ord.h
@@ -0,0 +1,80 @@
+/** @file
+ * @brief Comparator template
+ *//*
+ * 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 LIB2GEOM_SEEN_ORD_H
+#define LIB2GEOM_SEEN_ORD_H
+
+namespace {
+
+enum Cmp {
+ LESS_THAN=-1,
+ GREATER_THAN=1,
+ EQUAL_TO=0
+};
+
+static inline Cmp operator-(Cmp x) {
+ switch(x) {
+ case LESS_THAN:
+ return GREATER_THAN;
+ case GREATER_THAN:
+ return LESS_THAN;
+ case EQUAL_TO:
+ return EQUAL_TO;
+ }
+}
+
+template <typename T1, typename T2>
+inline Cmp cmp(T1 const &a, T2 const &b) {
+ if ( a < b ) {
+ return LESS_THAN;
+ } else if ( b < a ) {
+ return GREATER_THAN;
+ } else {
+ return EQUAL_TO;
+ }
+}
+
+}
+
+#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/include/2geom/orphan-code/arc-length.h b/include/2geom/orphan-code/arc-length.h
new file mode 100644
index 0000000..8029f04
--- /dev/null
+++ b/include/2geom/orphan-code/arc-length.h
@@ -0,0 +1,58 @@
+/**
+ * \file arc-length.h
+ * \brief Arc length computations for paths
+ *//*
+ * 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.
+ *
+ */
+
+#ifndef __2GEOM_ARC_LENGTH_H
+#define __2GEOM_ARC_LENGTH_H
+
+#include <2geom/path.h>
+
+/* Routines in this group return a path that looks the same, but
+ * include extra knots for certain points of interest. */
+
+/* find_vector_extreme_points
+ * extreme points . dir.
+ */
+
+double arc_length_subdividing(Geom::Path const & p, double tol);
+double arc_length_integrating(Geom::Path const & p, double tol);
+
+#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/include/2geom/orphan-code/chebyshev.h b/include/2geom/orphan-code/chebyshev.h
new file mode 100644
index 0000000..f729e1f
--- /dev/null
+++ b/include/2geom/orphan-code/chebyshev.h
@@ -0,0 +1,30 @@
+#ifndef _CHEBYSHEV
+#define _CHEBYSHEV
+
+#include <2geom/sbasis.h>
+#include <2geom/interval.h>
+
+/*** Conversion between Chebyshev approximation and SBasis.
+ *
+ */
+
+namespace Geom{
+
+SBasis chebyshev_approximant (double (*f)(double,void*), int order, Interval in, void* p=0);
+SBasis chebyshev_approximant_interpolating (double (*f)(double,void*), int order, Interval in, void* p=0);
+SBasis chebyshev(unsigned n);
+
+};
+
+/*
+ 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 :
+
+#endif
diff --git a/include/2geom/orphan-code/intersection-by-smashing.h b/include/2geom/orphan-code/intersection-by-smashing.h
new file mode 100644
index 0000000..996ec99
--- /dev/null
+++ b/include/2geom/orphan-code/intersection-by-smashing.h
@@ -0,0 +1,78 @@
+/*
+ * Diffeomorphism-based intersector: given two curves
+ * M(t)=(x(t),y(t)) and N(u)=(X(u),Y(u))
+ * and supposing M is a graph over the x-axis, we compute y(x) and the
+ * transformation:
+ * X <- X
+ * Y <- Y - y(X)
+ * smashes M on the x axis. The intersections are then given by the roots of:
+ * Y(u) - y(X(u)) = 0
+ *//*
+ * Authors:
+ * J.-F. Barraud <jfbarraud at gmail.com>
+ * Copyright 2010 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_BY_SMASHING_H
+#define SEEN_LIB2GEOM_INTERSECTION_BY_SMASHING_H
+
+#include <2geom/d2.h>
+#include <2geom/interval.h>
+#include <2geom/sbasis.h>
+#include <2geom/sbasis-geometric.h>
+#include <cstdlib>
+#include <vector>
+#include <algorithm>
+
+
+namespace Geom{
+
+struct SmashIntersection {
+ Rect times;
+ Rect bbox;
+};
+
+std::vector<SmashIntersection> smash_intersect( D2<SBasis> const &a, D2<SBasis> const &b, double tol);
+std::vector<SmashIntersection> monotonic_smash_intersect( D2<SBasis> const &a, D2<SBasis> const &b, double tol);
+//std::vector<Intersection> monotonic_smash_intersect( Curve const &a, double a_from, double a_to,
+// Curve const &b, double b_from, double b_to, double tol);
+
+std::vector<Interval> monotonicSplit(D2<SBasis> const &p);
+
+} // end namespace Geom
+
+#endif // !SEEN_LIB2GEOM_INTERSECTION_BY_SMASHING_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/include/2geom/orphan-code/linear-of.h b/include/2geom/orphan-code/linear-of.h
new file mode 100644
index 0000000..9ba1fb2
--- /dev/null
+++ b/include/2geom/orphan-code/linear-of.h
@@ -0,0 +1,269 @@
+/**
+ * \file
+ * \brief Linear fragment function class
+ *
+ * Authors:
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Michael Sloan <mgsloan@gmail.com>
+ *
+ * Copyright (C) 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.
+ */
+
+#ifndef SEEN_LINEAR_OF_H
+#define SEEN_LINEAR_OF_H
+#include <2geom/interval.h>
+#include <2geom/math-utils.h>
+
+namespace Geom{
+
+template <typename T>
+inline T lerp(double t, T a, T b) { return a*(1-t) + b*t; }
+
+template <typename T>
+class SBasisOf;
+
+template <typename T>
+class HatOf{
+public:
+ HatOf () {}
+ HatOf(T d) :d(d) {}
+ operator T() const { return d; }
+ T d;
+};
+
+template <typename T>
+class TriOf{
+public:
+ TriOf () {}
+ TriOf(double d) :d(d) {}
+ operator T() const { return d; }
+ T d;
+};
+
+
+//--------------------------------------------------------------------------
+#ifdef USE_SBASIS_OF
+template <typename T>
+class LinearOf;
+typedef Geom::LinearOf<double> Linear;
+#endif
+//--------------------------------------------------------------------------
+
+template <typename T>
+class LinearOf{
+public:
+ T a[2];
+ LinearOf() {}
+ LinearOf(T aa, T b) {a[0] = aa; a[1] = b;}
+ //LinearOf(double aa, double b) {a[0] = T(aa); a[1] = T(b);}
+ LinearOf(HatOf<T> h, TriOf<T> t) {
+ a[0] = T(h) - T(t)/2;
+ a[1] = T(h) + T(t)/2;
+ }
+
+ LinearOf(HatOf<T> h) {
+ a[0] = T(h);
+ a[1] = T(h);
+ }
+
+ unsigned input_dim(){return T::input_dim() + 1;}
+
+ T operator[](const int i) const {
+ assert(i >= 0);
+ assert(i < 2);
+ return a[i];
+ }
+ T& operator[](const int i) {
+ assert(i >= 0);
+ assert(i < 2);
+ return a[i];
+ }
+
+ //IMPL: FragmentConcept
+ typedef T output_type;
+ inline bool isZero() const { return a[0].isZero() && a[1].isZero(); }
+ inline bool isConstant() const { return a[0] == a[1]; }
+ inline bool isFinite() const { return std::isfinite(a[0]) && std::isfinite(a[1]); }
+
+ inline T at0() const { return a[0]; }
+ inline T at1() const { return a[1]; }
+
+ inline T valueAt(double t) const { return lerp(t, a[0], a[1]); }
+ inline T operator()(double t) const { return valueAt(t); }
+
+ //defined in sbasis.h
+ inline SBasisOf<T> toSBasis() const;
+
+//This is specific for T=double!!
+ 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)); }
+
+ operator TriOf<T>() const {
+ return a[1] - a[0];
+ }
+ operator HatOf<T>() const {
+ return (a[1] + a[0])/2;
+ }
+};
+
+template <>
+unsigned LinearOf<double>::input_dim(){return 1;}
+template <>
+inline OptInterval LinearOf<double>::bounds_exact() const { return Interval(a[0], a[1]); }
+template <>
+inline OptInterval LinearOf<double>::bounds_fast() const { return bounds_exact(); }
+template <>
+inline OptInterval LinearOf<double>::bounds_local(double u, double v) const { return Interval(valueAt(u), valueAt(v)); }
+template <>
+inline bool LinearOf<double>::isZero() const { return a[0]==0 && a[1]==0; }
+
+template <typename T>
+inline LinearOf<T> reverse(LinearOf<T> const &a) { return LinearOf<T>(a[1], a[0]); }
+
+//IMPL: AddableConcept
+template <typename T>
+inline LinearOf<T> operator+(LinearOf<T> const & a, LinearOf<T> const & b) {
+ return LinearOf<T>(a[0] + b[0], a[1] + b[1]);
+}
+template <typename T>
+inline LinearOf<T> operator-(LinearOf<T> const & a, LinearOf<T> const & b) {
+ return LinearOf<T>(a[0] - b[0], a[1] - b[1]);
+}
+template <typename T>
+inline LinearOf<T>& operator+=(LinearOf<T> & a, LinearOf<T> const & b) {
+ a[0] += b[0]; a[1] += b[1];
+ return a;
+}
+template <typename T>
+inline LinearOf<T>& operator-=(LinearOf<T> & a, LinearOf<T> const & b) {
+ a[0] -= b[0]; a[1] -= b[1];
+ return a;
+}
+//IMPL: OffsetableConcept
+template <typename T>
+inline LinearOf<T> operator+(LinearOf<T> const & a, double b) {
+ return LinearOf<T>(a[0] + b, a[1] + b);
+}
+template <typename T>
+inline LinearOf<T> operator-(LinearOf<T> const & a, double b) {
+ return LinearOf<T>(a[0] - b, a[1] - b);
+}
+template <typename T>
+inline LinearOf<T>& operator+=(LinearOf<T> & a, double b) {
+ a[0] += b; a[1] += b;
+ return a;
+}
+template <typename T>
+inline LinearOf<T>& operator-=(LinearOf<T> & a, double b) {
+ a[0] -= b; a[1] -= b;
+ return a;
+}
+/*
+//We can in fact offset in coeff ring T...
+template <typename T>
+inline LinearOf<T> operator+(LinearOf<T> const & a, T b) {
+ return LinearOf<T>(a[0] + b, a[1] + b);
+}
+template <typename T>
+inline LinearOf<T> operator-(LinearOf<T> const & a, T b) {
+ return LinearOf<T>(a[0] - b, a[1] - b);
+}
+template <typename T>
+inline LinearOf<T>& operator+=(LinearOf<T> & a, T b) {
+ a[0] += b; a[1] += b;
+ return a;
+}
+template <typename T>
+inline LinearOf<T>& operator-=(LinearOf<T> & a, T b) {
+ a[0] -= b; a[1] -= b;
+ return a;
+}
+*/
+
+//IMPL: boost::EqualityComparableConcept
+template <typename T>
+inline bool operator==(LinearOf<T> const & a, LinearOf<T> const & b) {
+ return a[0] == b[0] && a[1] == b[1];
+}
+template <typename T>
+inline bool operator!=(LinearOf<T> const & a, LinearOf<T> const & b) {
+ return a[0] != b[0] || a[1] != b[1];
+}
+//IMPL: ScalableConcept
+template <typename T>
+inline LinearOf<T> operator-(LinearOf<T> const &a) {
+ return LinearOf<T>(-a[0], -a[1]);
+}
+template <typename T>
+inline LinearOf<T> operator*(LinearOf<T> const & a, double b) {
+ return LinearOf<T>(a[0]*b, a[1]*b);
+}
+template <typename T>
+inline LinearOf<T> operator/(LinearOf<T> const & a, double b) {
+ return LinearOf<T>(a[0]/b, a[1]/b);
+}
+template <typename T>
+inline LinearOf<T> operator*=(LinearOf<T> & a, double b) {
+ a[0] *= b; a[1] *= b;
+ return a;
+}
+template <typename T>
+inline LinearOf<T> operator/=(LinearOf<T> & a, double b) {
+ a[0] /= b; a[1] /= b;
+ return a;
+}
+/*
+//We can in fact rescale in coeff ring T... (but not divide!)
+template <typename T>
+inline LinearOf<T> operator*(LinearOf<T> const & a, T b) {
+ return LinearOf<T>(a[0]*b, a[1]*b);
+}
+template <typename T>
+inline LinearOf<T> operator/(LinearOf<T> const & a, T b) {
+ return LinearOf<T>(a[0]/b, a[1]/b);
+}
+template <typename T>
+inline LinearOf<T> operator*=(LinearOf<T> & a, T b) {
+ a[0] *= b; a[1] *= b;
+ return a;
+}
+*/
+
+};
+
+#endif //SEEN_LINEAR_OF_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/include/2geom/orphan-code/linearN.h b/include/2geom/orphan-code/linearN.h
new file mode 100644
index 0000000..bb27c30
--- /dev/null
+++ b/include/2geom/orphan-code/linearN.h
@@ -0,0 +1,363 @@
+/**
+ * @file
+ * @brief LinearN fragment function class
+ *//*
+ * Authors:
+ * JF Barraud <jf.barraud@gmail.com>
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Michael Sloan <mgsloan@gmail.com>
+ *
+ * Copyright (C) 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.
+ */
+
+#ifndef SEEN_LINEARN_H
+#define SEEN_LINEARN_H
+#include <2geom/interval.h>
+#include <2geom/math-utils.h>
+#include <2geom/linear.h> //for conversion purpose ( + lerp() )
+
+#include <iostream>
+
+namespace Geom{
+
+//TODO: define this only once!! (see linear.h)
+inline double lerpppp(double t, double a, double b) { return a*(1-t) + b*t; }
+
+template<unsigned n>
+class SBasisN;
+
+template<unsigned n>
+class LinearN{
+public:
+ double a[1<<n];// 1<<n is 2^n
+ LinearN() {
+ for (unsigned i=0; i < (1<<n); i++){
+ a[i] = 0.;
+ }
+ }
+ LinearN(double aa[]) {
+ for (unsigned i=0; i < (1<<n); i++){
+ a[i] = aa[i];
+ }
+ }
+ LinearN(double c) {
+ for (unsigned i=0; i<(1<<n); i++){
+ a[i] = c;
+ }
+ }
+ LinearN(LinearN<n-1> const &aa, LinearN<n-1> const &b, unsigned var=0) {
+// for (unsigned i=0; i<(1<<n-1); i++){
+// a[i] = aa[i];
+// a[i+(1<<(n-1))] = b[i];
+// }
+ unsigned mask = (1<<var)-1;
+ for (unsigned i=0; i < (1<<(n-1)); i++){
+ unsigned low_i = i & mask, high_i = i & ~mask;
+ unsigned idx0 = (high_i<<1)|low_i;
+ unsigned idx1 = (high_i<<1)|(1<<var)|low_i;
+ a[idx0] = aa[i];
+ a[idx1] = b[i];
+ }
+
+ }
+ double operator[](const int i) const {
+ assert( i >= 0 );
+ assert( i < (1<<n) );
+ return a[i];
+ }
+ double& operator[](const int i) {
+ assert(i >= 0);
+ assert(i < (1<<n) );
+ return a[i];
+ }
+
+ //IMPL: FragmentConcept
+ typedef double output_type;
+ unsigned input_dim() const {return n;}
+ inline bool isZero() const {
+ for (unsigned i=0; i < (1<<n); i++){
+ if (a[i] != 0) return false;
+ }
+ return true; }
+ inline bool isConstant() const {
+ for (unsigned i=1; i < (1<<n); i++){
+ if (a[i] != a[0]) return false;
+ }
+ return true; }
+ inline bool isConstant(unsigned var) const {
+ unsigned mask = (1<<var)-1;
+ for (unsigned i=0; i < (1<<(n-1)); i++){
+ unsigned low_i = i & mask, high_i = i & ~mask;
+ unsigned idx0 = (high_i<<1)|low_i;
+ unsigned idx1 = (high_i<<1)|(1<<var)|low_i;
+ if (a[idx0] != a[idx1]) return false;
+ }
+ return true;
+ }
+ inline bool isFinite() const {
+ for (unsigned i=0; i < (1<<n); i++){
+ if ( !std::isfinite(a[i]) ) return false;
+ }
+ return true; }
+ //value if k-th variable is set to 0.
+ inline LinearN<n-1> at0(unsigned k=0) const {
+ LinearN<n-1> res;
+ unsigned mask = (1<<k)-1;
+ for (unsigned i=0; i < (1<<(n-1)); i++){
+ unsigned low_i = i & mask, high_i = i & ~mask;
+ unsigned idx = (high_i<<1)|low_i;
+ res[i] = a[idx];
+ }
+ return res;
+ }
+ //value if k-th variable is set to 1.
+ inline LinearN<n-1> at1(unsigned k=0) const {
+ LinearN<n-1> res;
+ for (unsigned i=0; i < (1<<(n-1)); i++){
+ unsigned mask = (1<<k)-1;
+ unsigned low_i = i & mask, high_i = i & ~mask;
+ unsigned idx = (high_i<<1)|(1<<k)|low_i;
+ res[i] = a[idx];
+ }
+ return res;
+ }
+ inline double atCorner(unsigned k) const {
+ assert( k < (1<<n) );
+ return a[k];
+ }
+ inline double atCorner(double t[]) const {
+ unsigned k=0;
+ for(unsigned i=0; i<n; i++){
+ if (t[i] == 1.) k = k | (1<<i);
+ else assert( t[i] == 0. );
+ }
+ return atCorner(k);
+ }
+ inline LinearN<n-1> partialEval(double t, unsigned var=0 ) const {
+ LinearN<n-1> res;
+ res = at0(var)*(1-t) + at1(var)*t;
+ return res;
+ }
+
+ //fixed and flags are used for recursion.
+ inline double valueAt(double t[], unsigned fixed=0, unsigned flags=0 ) const {
+ if (fixed == n) {
+ return a[flags];
+ }else{
+ double a0 = valueAt(t, fixed+1, flags);
+ double a1 = valueAt(t, fixed+1, flags|(1<<fixed));
+ return lerpppp( t[fixed], a0, a1 );
+ }
+ }
+ inline double operator()(double t[]) const { return valueAt(t); }
+
+ //defined in sbasisN.h
+ inline SBasisN<n> toSBasisN() const;
+
+ inline OptInterval bounds_exact() const {
+ double min=a[0], max=a[0];
+ for (unsigned i=1; i < (1<<n); i++){
+ if (a[i] < min) min = a[i];
+ if (a[i] > max) max = a[i];
+ }
+ return Interval(min, max);
+ }
+ inline OptInterval bounds_fast() const { return bounds_exact(); }
+ //inline OptInterval bounds_local(double u, double v) const { return Interval(valueAt(u), valueAt(v)); }
+};
+
+//LinearN<0> are doubles. Specialize them out.
+template<>
+class LinearN<0>{
+public:
+ double d;
+ LinearN () {}
+ LinearN(double d) :d(d) {}
+ operator double() const { return d; }
+ double operator[](const int i) const {assert (i==0); return d;}
+ double& operator[](const int i) {assert (i==0); return d;}
+ typedef double output_type;
+ unsigned input_dim() const {return 0;}
+ inline bool isZero() const { return d==0; }
+ inline bool isConstant() const { return true; }
+ inline bool isFinite() const { return std::isfinite(d); }
+};
+
+//LinearN<1> are usual Linear. Allow conversion.
+Linear toLinear(LinearN<1> f){
+ return Linear(f[0],f[1]);
+}
+
+
+
+//inline Linear reverse(Linear const &a) { return Linear(a[1], a[0]); }
+
+//IMPL: AddableConcept
+template<unsigned n>
+inline LinearN<n> operator+(LinearN<n> const & a, LinearN<n> const & b) {
+ LinearN<n> res;
+ for (unsigned i=0; i < (1<<n); i++){
+ res[i] = a[i] + b[i];
+ }
+ return res;
+}
+template<unsigned n>
+inline LinearN<n> operator-(LinearN<n> const & a, LinearN<n> const & b) {
+ LinearN<n> res;
+ for (unsigned i=0; i < (1<<n); i++){
+ res[i] = a[i] - b[i];
+ }
+ return res;
+}
+template<unsigned n>
+inline LinearN<n>& operator+=(LinearN<n> & a, LinearN<n> const & b) {
+ for (unsigned i=0; i < (1<<n); i++){
+ a[i] += b[i];
+ }
+ return a;
+}
+template<unsigned n>
+inline LinearN<n>& operator-=(LinearN<n> & a, LinearN<n> const & b) {
+ for (unsigned i=0; i < (1<<n); i++){
+ a[i] -= b[i];
+ }
+ return a;
+}
+//IMPL: OffsetableConcept
+template<unsigned n>
+inline LinearN<n> operator+(LinearN<n> const & a, double b) {
+ LinearN<n> res;
+ for (unsigned i=0; i < (1<<n); i++){
+ res[i] = a[i] + b;
+ }
+ return res;
+}
+template<unsigned n>
+inline LinearN<n> operator-(LinearN<n> const & a, double b) {
+ LinearN<n> res;
+ for (unsigned i=0; i < (1<<n); i++){
+ res[i] = a[i] - b;
+ }
+ return res;
+}
+template<unsigned n>
+inline LinearN<n>& operator+=(LinearN<n> & a, double b) {
+ for (unsigned i=0; i < (1<<n); i++){
+ a[i] += b;
+ }
+ return a;
+}
+template<unsigned n>
+inline LinearN<n>& operator-=(LinearN<n> & a, double b) {
+ for (unsigned i=0; i < (1<<n); i++){
+ a[i] -= b;
+ }
+ return a;
+}
+//IMPL: boost::EqualityComparableConcept
+template<unsigned n>
+inline bool operator==(LinearN<n> const & a, LinearN<n> const & b) {
+ for (unsigned i=0; i < (1<<n); i++){
+ if (a[i] != b[i]) return false;
+ }
+ return true;
+}
+template<unsigned n>
+inline bool operator!=(LinearN<n> const & a, LinearN<n> const & b) {
+ return !(a==b);
+}
+//IMPL: ScalableConcept
+template<unsigned n>
+inline LinearN<n> operator-(LinearN<n> const &a) {
+ LinearN<n> res;
+ for (unsigned i=0; i < (1<<n); i++){
+ res[i] = -a[i];
+ }
+ return res;
+}
+template<unsigned n>
+inline LinearN<n> operator*(LinearN<n> const & a, double b) {
+ LinearN<n> res;
+ for (unsigned i=0; i < (1<<n); i++){
+ res[i] = a[i] * b;
+ }
+ return res;
+}
+template<unsigned n>
+inline LinearN<n> operator/(LinearN<n> const & a, double b) {
+ LinearN<n> res;
+ for (unsigned i=0; i < (1<<n); i++){
+ res[i] = a[i] / b;
+ }
+ return res;
+}
+template<unsigned n>
+inline LinearN<n> operator*=(LinearN<n> & a, double b) {
+ for (unsigned i=0; i < (1<<n); i++){
+ a[i] *= b;
+ }
+ return a;
+}
+template<unsigned n>
+inline LinearN<n> operator/=(LinearN<n> & a, double b) {
+ for (unsigned i=0; i < (1<<n); i++){
+ a[i] /= b;
+ }
+ return a;
+}
+
+template<unsigned n>
+void setToVariable(LinearN<n> &x, unsigned k){;
+ x = LinearN<n>(0.);
+ unsigned mask = 1<<k;
+ for (unsigned i=0; i < (1<<n); i++){
+ if ( i & mask ) x[i] = 1;
+ }
+}
+
+template<unsigned n>
+inline std::ostream &operator<< (std::ostream &out_file, const LinearN<n> &bo) {
+ out_file << "{";
+ for (unsigned i=0; i < (1<<n); i++){
+ out_file << bo[i]<<(i == (1<<n)-1 ? "}" : ",");
+ }
+ return out_file;
+}
+
+
+}
+#endif //SEEN_LINEAR_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/include/2geom/orphan-code/redblacktree.h b/include/2geom/orphan-code/redblacktree.h
new file mode 100644
index 0000000..9d79342
--- /dev/null
+++ b/include/2geom/orphan-code/redblacktree.h
@@ -0,0 +1,121 @@
+/**
+ * \file
+ * \brief
+ * Implementation of Red-Black Tree as described in
+ * Intorduction to Algorithms. Cormen et al. Mc Grow Hill. 1990. pp 263-280
+ *
+ * The intention is to implement interval trees mentioned in the same book, after the red-black.
+ * Interval are heavily based on red-black trees (most operations are the same). So, we begin first
+ * with implementing red-black!
+ *
+ * Authors:
+ * ? <?@?.?>
+ *
+ * Copyright 2009-2009 Evangelos Katsikaros
+ *
+ * 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_REDBLACKTREE_H
+#define SEEN_LIB2GEOM_REDBLACKTREE_H
+
+#include <vector>
+//#include <cassert>
+#include <limits>
+#include <cfloat>
+
+#include <2geom/d2.h>
+#include <2geom/interval.h>
+
+namespace Geom{
+
+class RedBlack{
+public:
+ Interval interval; // Key of the redblack tree will be the min of the interval
+ RedBlack *left, *right, *parent;
+ bool isRed;
+ Coord subtree_max; // subtree_max = max( x->left->subtree_max, x->right->subtree_max, x->high )
+ int data;
+
+ RedBlack(): left(0), right(0), parent(0), isRed(false), subtree_max(0.0), data(0) {
+ Interval interval(0.0, 0.0);
+ }
+/*
+ RedBlack(Coord min, Coord max): left(0), right(0), parent(0), isRed(false), subtree_max(0.0), data(0) {
+ Interval interval( min, max );
+ }
+*/
+ inline Coord key(){ return interval.min(); };
+ inline Coord high(){ return interval.max(); };
+};
+
+
+class RedBlackTree{
+public:
+ RedBlack* root;
+
+ RedBlackTree(): root(0) {}
+
+ void insert(Rect const &r, int shape, int dimension);
+ void insert(Coord dimension_min, Coord dimension_max, int shape);
+
+ void erase(Rect const &r);
+ void erase(int shape);
+
+ RedBlack* search(Rect const &r, int dimension);
+ RedBlack* search(Interval i);
+ RedBlack* search(Coord a, Coord b);
+
+ void print_tree();
+private:
+ void inorder_tree_walk(RedBlack* x);
+ RedBlack* tree_minimum(RedBlack* x);
+ RedBlack* tree_successor(RedBlack* x);
+
+ void left_rotate(RedBlack* x);
+ void right_rotate(RedBlack* x);
+ void tree_insert(RedBlack* x);
+
+ void update_max(RedBlack* x);
+
+ RedBlack* erase(RedBlack* x); // TODO why rerutn pointer? to collect garbage ???
+ void erase_fixup(RedBlack* x);
+
+};
+
+} // end namespace Geom
+
+#endif // !SEEN_LIB2GEOM_REDBLACKTREE_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/include/2geom/orphan-code/rtree.h b/include/2geom/orphan-code/rtree.h
new file mode 100644
index 0000000..3ffae8e
--- /dev/null
+++ b/include/2geom/orphan-code/rtree.h
@@ -0,0 +1,241 @@
+/**
+ * \file
+ * \brief
+ * Implementation of Red-Black Tree as described in
+ * Intorduction to Algorithms. Cormen et al. Mc Grow Hill. 1990. pp 263-280
+ *
+ * The intention is to implement interval trees mentioned in the same book, after the red-black.
+ * Interval are heavily based on red-black trees (most operations are the same). So, we begin first
+ * with implementing red-black!
+ *
+ * Authors:
+ * ? <?@?.?>
+ *
+ * Copyright 2009-2009 Evangelos Katsikaros
+ *
+ * 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_RTREE_H
+#define SEEN_LIB2GEOM_RTREE_H
+
+#include <vector>
+#include <utility>
+
+
+#include <2geom/d2.h>
+#include <2geom/interval.h>
+
+namespace Geom{
+
+// used only in pick_next( )
+enum enum_add_to_group {
+ ADD_TO_GROUP_A = 0,
+ ADD_TO_GROUP_B
+};
+
+
+enum enum_split_strategy {
+ QUADRATIC_SPIT = 0,
+ LINEAR_COST,
+ TOTAL_STRATEGIES // this one must be the last item
+};
+
+
+
+template <typename T>
+class pedantic_vector:public std::vector<T> {
+public:
+ pedantic_vector(size_t s=0) : std::vector<T>(s) {}
+ T& operator[](unsigned i) {
+ //assert(i >= 0);
+ assert(i < std::vector<T>::size());
+ return std::vector<T>::operator[](i);
+ }
+ T const& operator[](unsigned i) const {
+ //assert(i >= 0);
+ assert(i < std::vector<T>::size());
+ return std::vector<T>::operator[](i);
+ }
+/*
+ erase( std::vector<T>::iterator it ) {
+ //assert(i >= 0);
+ assert( it < std::vector<T>::size());
+ return std::vector<T>::erase(it);
+ }
+*/
+};
+
+class RTreeNode;
+
+class RTreeRecord_Leaf{
+public:
+ Rect bounding_box;
+ int data;
+
+ RTreeRecord_Leaf(): bounding_box(), data(0)
+ {}
+
+ RTreeRecord_Leaf(Rect bb, int d): bounding_box(bb), data(d)
+ {}
+};
+
+class RTreeRecord_NonLeaf{
+public:
+ Rect bounding_box;
+ RTreeNode* data;
+
+ RTreeRecord_NonLeaf(): bounding_box(), data(0)
+ {}
+
+ RTreeRecord_NonLeaf(Rect bb, RTreeNode* d): bounding_box(bb), data(d)
+ {}
+};
+
+/*
+R-Tree has 2 kinds of nodes
+* Leaves which store:
+ - the actual data
+ - the bounding box of the data
+
+* Non-Leaves which store:
+ - a child node (data)
+ - the bounding box of the child node
+
+This causes some code duplication in rtree.cpp. There are 2 cases:
+- we care whether we touch a leaf/non-leaf node, since we write data in the node, so we want to
+ write the correct thing (int or RTreeNode*)
+- we do NOT care whether we touch a leaf/non-leaf node, because we only read/write the bounding
+ boxes which is the same in both cases.
+
+TODO:
+A better design would eliminate the duplication in the 2nd case, but we can't avoid the 1st probably.
+*/
+class RTreeNode{
+public:
+ // first: bounding box
+ // second: "data" (leaf-node) or node (NON leaf-node)
+ //pedantic_vector< RTreeRecord_Leaf > children_leaves; // if this is empty, then node is leaf-node
+ //pedantic_vector< RTreeRecord_NonLeaf > children_nodes; // if this is empty, then node is NON-leaf node
+
+ std::vector< RTreeRecord_Leaf > children_leaves; // if this is empty, then node is leaf-node
+ std::vector< RTreeRecord_NonLeaf > children_nodes; // if this is empty, then node is NON-leaf node
+
+ RTreeNode(): children_leaves(0), children_nodes(0)
+ {}
+
+};
+
+
+class RTree{
+public:
+ RTreeNode* root;
+
+ // min/max records per node
+ unsigned min_records;
+ unsigned max_records; // allow +1 (used during insert)
+
+ enum_split_strategy split_strategy;
+
+
+ RTree( unsigned n, unsigned m, enum_split_strategy split_s ):
+ root(0), min_records( n ), max_records( m ), split_strategy( split_s ),
+ tree_height(0)
+ {}
+
+ void insert( Rect const &r, unsigned shape);
+ void search( const Rect &search_area, std::vector< int >* result, const RTreeNode* subtree ) const;
+ //int erase( const RTreeRecord_Leaf & search );
+ int erase( const Rect &search_area, const int shape_to_delete );
+
+// update
+
+ void print_tree(RTreeNode* subtree_root, int depth ) const;
+
+private:
+ unsigned tree_height; // 0 is the root level
+
+ void insert( //Rect const &r,
+ //int shape,
+ const RTreeRecord_Leaf &leaf_record,
+ const bool &insert_high = false,
+ const unsigned &stop_height = 0,
+ const RTreeRecord_NonLeaf &nonleaf_record = RTreeRecord_NonLeaf()
+ );
+ // I1
+ RTreeNode* choose_node( const Rect &r, const bool &insert_high = false, const unsigned &stop_height=0 ) const;
+ double find_waste_area( const Rect &a, const Rect &b ) const;
+ double find_enlargement( const Rect &a, const Rect &b ) const;
+
+ // I2
+ std::pair<RTreeNode*, RTreeNode*> split_node( RTreeNode *s );
+ // QUADRATIC_SPIT
+ std::pair<RTreeNode*, RTreeNode*> quadratic_split( RTreeNode* s );
+ std::pair<unsigned, unsigned> pick_seeds( RTreeNode* s ) const;
+ std::pair<unsigned, enum_add_to_group> pick_next( RTreeNode* group_a, RTreeNode* group_b, RTreeNode* s, std::vector<bool> &assigned_v );
+ // others...
+
+ // I3
+ bool adjust_tree( RTreeNode* position,
+ std::pair<RTreeNode*, RTreeNode*> &node_division,
+ bool split_performed
+ );
+ std::pair< RTreeNode*, bool > find_parent( RTreeNode* subtree_root, Rect search_area, RTreeNode* wanted ) const;
+
+ void recalculate_bounding_box( RTreeNode* parent, RTreeNode* child, unsigned &child_in_parent );
+
+ void copy_group_a_to_existing_node( RTreeNode *position, RTreeNode* group_a );
+ RTreeRecord_NonLeaf create_nonleaf_record_from_rtreenode( Rect &new_entry_bounding, RTreeNode *rtreenode );
+ RTreeRecord_Leaf create_leaf_record_from_rtreenode( Rect &new_entry_bounding, RTreeNode *rtreenode );
+
+ // erase
+// RTreeNode* find_leaf( RTreeNode* subtree, const RTreeRecord_Leaf &search ) const;
+ RTreeNode* find_leaf( RTreeNode* subtree, const Rect &search_area, const int shape_to_delete ) const;
+
+ bool condense_tree( RTreeNode* position
+ // std::pair<RTreeNode*, RTreeNode*> &node_division, // modified: it holds the last split group
+ // bool initial_split_performed,
+ // const unsigned min_nodes
+ // const unsigned max_nodes
+ );
+ int remove_record_from_parent( RTreeNode* parent, RTreeNode* child );
+ void sanity_check(RTreeNode* subtree_root, int depth, bool used_during_insert = false ) const;
+
+};
+
+} // end namespace Geom
+
+#endif // !SEEN_LIB2GEOM_RTREE_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/include/2geom/orphan-code/sbasis-of.h b/include/2geom/orphan-code/sbasis-of.h
new file mode 100644
index 0000000..e5b76d6
--- /dev/null
+++ b/include/2geom/orphan-code/sbasis-of.h
@@ -0,0 +1,638 @@
+/**
+ * \file
+ * \brief Defines S-power basis function class
+ * with coefficient in arbitrary ring
+ *
+ * Authors:
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Michael Sloan <mgsloan@gmail.com>
+ *
+ * Copyright (C) 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.
+ */
+
+#ifndef SEEN_SBASIS_OF_H
+#define SEEN_SBASIS_OF_H
+#include <vector>
+#include <cassert>
+#include <iostream>
+
+#include <2geom/interval.h>
+#include <2geom/utils.h>
+#include <2geom/exception.h>
+
+#include <2geom/orphan-code/linear-of.h>
+
+namespace Geom {
+
+template<typename T>
+class SBasisOf;
+
+#ifdef USE_SBASIS_OF
+typedef Geom::SBasisOf<double> SBasis;
+#endif
+
+/*** An empty SBasisOf<T> is identically 0. */
+template<typename T>
+class SBasisOf : public std::vector<LinearOf<T> >{
+public:
+ SBasisOf() {}
+ explicit SBasisOf(T a) {
+ this->push_back(LinearOf<T>(a,a));
+ }
+ SBasisOf(SBasisOf<T> const & a) :
+ std::vector<LinearOf<T> >(a)
+ {}
+ SBasisOf(LinearOf<T> const & bo) {
+ this->push_back(bo);
+ }
+ SBasisOf(LinearOf<T>* bo) {
+ this->push_back(*bo);
+ }
+ //static unsigned input_dim(){return T::input_dim()+1;}
+
+ //IMPL: FragmentConcept
+ typedef T output_type;
+ inline bool isZero() const {
+ if(this->empty()) return true;
+ for(unsigned i = 0; i < this->size(); i++) {
+ if(!(*this)[i].isZero()) return false;
+ }
+ return true;
+ }
+ inline bool isConstant() const {
+ if (this->empty()) return true;
+ for (unsigned i = 0; i < this->size(); i++) {
+ if(!(*this)[i].isConstant()) return false;
+ }
+ return true;
+ }
+
+ //TODO: code this...
+ bool isFinite() const;
+
+ inline T at0() const {
+ if(this->empty()) return T(0); else return (*this)[0][0];
+ }
+ inline T at1() const{
+ if(this->empty()) return T(0); else return (*this)[0][1];
+ }
+
+ T valueAt(double t) const {
+ double s = t*(1-t);
+ T p0 = T(0.), p1 = T(0.);
+ for(unsigned k = this->size(); k > 0; k--) {
+ const LinearOf<T> &lin = (*this)[k-1];
+ p0 = p0*s + lin[0];
+ p1 = p1*s + lin[1];
+ }
+ return p0*(1-t) + p1*t;
+ }
+
+ T operator()(double t) const {
+ return valueAt(t);
+ }
+
+ /**
+ * The size of the returned vector equals n+1.
+ */
+ std::vector<T> valueAndDerivatives(double t, unsigned n) const{
+ std::vector<T> ret(n+1);
+ ret[0] = valueAt(t);
+ SBasisOf<T> tmp = *this;
+ for(unsigned i = 0; i < n; i++) {
+ tmp.derive();
+ ret[i+1] = tmp.valueAt(t);
+ }
+ return ret;
+ }
+
+ //The following lines only makes sens if T=double!
+ SBasisOf<T> toSBasis() const { return SBasisOf<T>(*this); }
+ double tailError(unsigned tail) const{
+ Interval bs = *bounds_fast(*this, tail);
+ return std::max(fabs(bs.min()),fabs(bs.max()));
+ }
+
+// compute f(g)
+ SBasisOf<T> operator()(SBasisOf<T> const & g) const;
+
+ LinearOf<T> operator[](unsigned i) const {
+ assert(i < this->size());
+ return std::vector<LinearOf<T> >::operator[](i);
+ }
+
+//MUTATOR PRISON
+ LinearOf<T>& operator[](unsigned i) { return this->at(i); }
+
+ //remove extra zeros
+ void normalize() {
+ while(!this->empty() && this->back().isZero())
+ this->pop_back();
+ }
+
+ void truncate(unsigned k) { if(k < this->size()) this->resize(k); }
+private:
+ void derive(); // in place version
+ unsigned dim;
+};
+
+//template<>
+//inline unsigned SBasisOf<double>::input_dim() { return 1; }
+
+//--------------------------------------------------------------------------
+#ifdef USE_SBASIS_OF
+
+//implemented in sbasis-roots.cpp
+OptInterval bounds_exact(SBasis const &a);
+OptInterval bounds_fast(SBasis const &a, int order = 0);
+OptInterval bounds_local(SBasis const &a, const OptInterval &t, int order = 0);
+
+std::vector<double> roots(SBasis const & s);
+std::vector<std::vector<double> > multi_roots(SBasis const &f,
+ std::vector<double> const &levels,
+ double htol=1e-7,
+ double vtol=1e-7,
+ double a=0,
+ double b=1);
+#endif
+//--------------------------------------------------------------------------
+
+
+//TODO: figure out how to stick this in linear, while not adding an sbasis dep
+template<typename T>
+inline SBasisOf<T> LinearOf<T>::toSBasis() const { return SBasisOf<T>(*this); }
+
+template<typename T>
+inline SBasisOf<T> reverse(SBasisOf<T> const &a) {
+ SBasisOf<T> result;
+ result.reserve(a.size());
+ for(unsigned k = 0; k < a.size(); k++)
+ result.push_back(reverse(a[k]));
+ return result;
+}
+
+//IMPL: ScalableConcept
+template<typename T>
+inline SBasisOf<T> operator-(const SBasisOf<T>& p) {
+ if(p.isZero()) return SBasisOf<T>();
+ SBasisOf<T> result;
+ result.reserve(p.size());
+
+ for(unsigned i = 0; i < p.size(); i++) {
+ result.push_back(-p[i]);
+ }
+ return result;
+}
+
+template<typename T>
+SBasisOf<T> operator*(SBasisOf<T> const &a, double k){
+ SBasisOf<T> c;
+ //TODO: what does this mean for vectors of vectors??
+ //c.reserve(a.size());
+ for(unsigned i = 0; i < a.size(); i++)
+ c.push_back(a[i] * k);
+ return c;
+}
+
+template<typename T>
+inline SBasisOf<T> operator*(double k, SBasisOf<T> const &a) { return a*k; }
+template<typename T>
+inline SBasisOf<T> operator/(SBasisOf<T> const &a, double k) { return a*(1./k); }
+template<typename T>
+SBasisOf<T>& operator*=(SBasisOf<T>& a, double b){
+ if (a.isZero()) return a;
+ if (b == 0)
+ a.clear();
+ else
+ for(unsigned i = 0; i < a.size(); i++)
+ a[i] *= b;
+ return a;
+}
+
+template<typename T>
+inline SBasisOf<T>& operator/=(SBasisOf<T>& a, double b) { return (a*=(1./b)); }
+
+/*
+//We can also multiply by element of ring coeff T:
+template<typename T>
+SBasisOf<T> operator*(SBasisOf<T> const &a, T k){
+ SBasisOf<T> c;
+ //TODO: what does this mean for vectors of vectors??
+ //c.reserve(a.size());
+ for(unsigned i = 0; i < a.size(); i++)
+ c.push_back(a[i] * k);
+ return c;
+}
+
+template<typename T>
+inline SBasisOf<T> operator*(T k, SBasisOf<T> const &a) { return a*k; }
+template<typename T>
+SBasisOf<T>& operator*=(SBasisOf<T>& a, T b){
+ if (a.isZero()) return a;
+ if (b == 0)
+ a.clear();
+ else
+ for(unsigned i = 0; i < a.size(); i++)
+ a[i] *= b;
+ return a;
+}
+*/
+
+//IMPL: AddableConcept
+template<typename T>
+inline SBasisOf<T> operator+(const SBasisOf<T>& a, const SBasisOf<T>& b){
+ SBasisOf<T> result;
+ const unsigned out_size = std::max(a.size(), b.size());
+ const unsigned min_size = std::min(a.size(), b.size());
+ //TODO: what does this mean for vector<vector>;
+ //result.reserve(out_size);
+
+ for(unsigned i = 0; i < min_size; i++) {
+ result.push_back(a[i] + b[i]);
+ }
+ for(unsigned i = min_size; i < a.size(); i++)
+ result.push_back(a[i]);
+ for(unsigned i = min_size; i < b.size(); i++)
+ result.push_back(b[i]);
+
+ assert(result.size() == out_size);
+ return result;
+}
+
+template<typename T>
+SBasisOf<T> operator-(const SBasisOf<T>& a, const SBasisOf<T>& b){
+ SBasisOf<T> result;
+ const unsigned out_size = std::max(a.size(), b.size());
+ const unsigned min_size = std::min(a.size(), b.size());
+ //TODO: what does this mean for vector<vector>;
+ //result.reserve(out_size);
+
+ for(unsigned i = 0; i < min_size; i++) {
+ result.push_back(a[i] - b[i]);
+ }
+ for(unsigned i = min_size; i < a.size(); i++)
+ result.push_back(a[i]);
+ for(unsigned i = min_size; i < b.size(); i++)
+ result.push_back(-b[i]);
+
+ assert(result.size() == out_size);
+ return result;
+}
+
+template<typename T>
+SBasisOf<T>& operator+=(SBasisOf<T>& a, const SBasisOf<T>& b){
+ const unsigned out_size = std::max(a.size(), b.size());
+ const unsigned min_size = std::min(a.size(), b.size());
+ //TODO: what does this mean for vectors of vectors
+ //a.reserve(out_size);
+ for(unsigned i = 0; i < min_size; i++)
+ a[i] += b[i];
+ for(unsigned i = min_size; i < b.size(); i++)
+ a.push_back(b[i]);
+
+ assert(a.size() == out_size);
+ return a;
+}
+
+template<typename T>
+SBasisOf<T>& operator-=(SBasisOf<T>& a, const SBasisOf<T>& b){
+ const unsigned out_size = std::max(a.size(), b.size());
+ const unsigned min_size = std::min(a.size(), b.size());
+ //TODO: what does this mean for vectors of vectors
+ //a.reserve(out_size);
+ for(unsigned i = 0; i < min_size; i++)
+ a[i] -= b[i];
+ for(unsigned i = min_size; i < b.size(); i++)
+ a.push_back(-b[i]);
+
+ assert(a.size() == out_size);
+ return a;
+}
+
+//TODO: remove?
+template<typename T>
+inline SBasisOf<T> operator+(const SBasisOf<T> & a, LinearOf<T> const & b) {
+ if(b.isZero()) return a;
+ if(a.isZero()) return b;
+ SBasisOf<T> result(a);
+ result[0] += b;
+ return result;
+}
+template<typename T>
+inline SBasisOf<T> operator-(const SBasisOf<T> & a, LinearOf<T> const & b) {
+ if(b.isZero()) return a;
+ SBasisOf<T> result(a);
+ result[0] -= b;
+ return result;
+}
+template<typename T>
+inline SBasisOf<T>& operator+=(SBasisOf<T>& a, const LinearOf<T>& b) {
+ if(a.isZero())
+ a.push_back(b);
+ else
+ a[0] += b;
+ return a;
+}
+template<typename T>
+inline SBasisOf<T>& operator-=(SBasisOf<T>& a, const LinearOf<T>& b) {
+ if(a.isZero())
+ a.push_back(-b);
+ else
+ a[0] -= b;
+ return a;
+}
+
+//IMPL: OffsetableConcept
+/*
+template<typename T>
+inline SBasisOf<T> operator+(const SBasisOf<T> & a, double b) {
+ if(a.isZero()) return LinearOf<T>(b, b);
+ SBasisOf<T> result(a);
+ result[0] += b;
+ return result;
+}
+template<typename T>
+inline SBasisOf<T> operator-(const SBasisOf<T> & a, double b) {
+ if(a.isZero()) return LinearOf<T>(-b, -b);
+ SBasisOf<T> result(a);
+ result[0] -= b;
+ return result;
+}
+template<typename T>
+inline SBasisOf<T>& operator+=(SBasisOf<T>& a, double b) {
+ if(a.isZero())
+ a.push_back(LinearOf<T>(b,b));
+ else
+ a[0] += b;
+ return a;
+}
+template<typename T>
+inline SBasisOf<T>& operator-=(SBasisOf<T>& a, double b) {
+ if(a.isZero())
+ a.push_back(LinearOf<T>(-b,-b));
+ else
+ a[0] -= b;
+ return a;
+}
+*/
+//We can also offset by elements of coeff ring T
+template<typename T>
+inline SBasisOf<T> operator+(const SBasisOf<T> & a, T b) {
+ if(a.isZero()) return LinearOf<T>(b, b);
+ SBasisOf<T> result(a);
+ result[0] += b;
+ return result;
+}
+template<typename T>
+inline SBasisOf<T> operator-(const SBasisOf<T> & a, T b) {
+ if(a.isZero()) return LinearOf<T>(-b, -b);
+ SBasisOf<T> result(a);
+ result[0] -= b;
+ return result;
+}
+template<typename T>
+inline SBasisOf<T>& operator+=(SBasisOf<T>& a, T b) {
+ if(a.isZero())
+ a.push_back(LinearOf<T>(b,b));
+ else
+ a[0] += b;
+ return a;
+}
+template<typename T>
+inline SBasisOf<T>& operator-=(SBasisOf<T>& a, T b) {
+ if(a.isZero())
+ a.push_back(LinearOf<T>(-b,-b));
+ else
+ a[0] -= b;
+ return a;
+}
+
+
+template<typename T>
+SBasisOf<T> shift(SBasisOf<T> const &a, int sh){
+ SBasisOf<T> c = a;
+ if(sh > 0) {
+ c.insert(c.begin(), sh, LinearOf<T>(0,0));
+ } else {
+ //TODO: truncate
+ }
+ return c;
+}
+
+template<typename T>
+SBasisOf<T> shift(LinearOf<T> const &a, int sh) {
+ SBasisOf<T> c;
+ if(sh > 0) {
+ c.insert(c.begin(), sh, LinearOf<T>(0,0));
+ c.push_back(a);
+ }
+ return c;
+}
+
+template<typename T>
+inline SBasisOf<T> truncate(SBasisOf<T> const &a, unsigned terms) {
+ SBasisOf<T> c;
+ c.insert(c.begin(), a.begin(), a.begin() + std::min(terms, (unsigned)a.size()));
+ return c;
+}
+
+template<typename T>
+SBasisOf<T> multiply_add(SBasisOf<T> const &a, SBasisOf<T> const &b, SBasisOf<T> c) {
+ if(a.isZero() || b.isZero())
+ return c;
+ c.resize(a.size() + b.size(), LinearOf<T>(T(0.),T(0.)));
+ for(unsigned j = 0; j < b.size(); j++) {
+ for(unsigned i = j; i < a.size()+j; i++) {
+ T tri = (b[j][1]-b[j][0])*(a[i-j][1]-a[i-j][0]);
+ c[i+1/*shift*/] += LinearOf<T>(-tri);
+ }
+ }
+ for(unsigned j = 0; j < b.size(); j++) {
+ for(unsigned i = j; i < a.size()+j; i++) {
+ for(unsigned dim = 0; dim < 2; dim++)
+ c[i][dim] += b[j][dim]*a[i-j][dim];
+ }
+ }
+ c.normalize();
+ //assert(!(0 == c.back()[0] && 0 == c.back()[1]));
+ return c;
+}
+
+template<typename T>
+SBasisOf<T> multiply(SBasisOf<T> const &a, SBasisOf<T> const &b) {
+ SBasisOf<T> c;
+ if(a.isZero() || b.isZero())
+ return c;
+ return multiply_add(a, b, c);
+}
+
+template<typename T>
+SBasisOf<T> integral(SBasisOf<T> const &c){
+ SBasisOf<T> a;
+ T aTri = T(0.);
+ for(int k = c.size()-1; k >= 0; k--) {
+ aTri = (HatOf<T>(c[k]).d + (k+1)*aTri/2)/(2*k+1);
+ a[k][0] -= aTri/2;
+ a[k][1] += aTri/2;
+ }
+ a.normalize();
+ return a;
+}
+
+template<typename T>
+SBasisOf<T> derivative(SBasisOf<T> const &a){
+ SBasisOf<T> c;
+ c.resize(a.size(), LinearOf<T>());
+ if(a.isZero())
+ return c;
+
+ for(unsigned k = 0; k < a.size()-1; k++) {
+ T d = (2*k+1)*(a[k][1] - a[k][0]);
+
+ c[k][0] = d + (k+1)*a[k+1][0];
+ c[k][1] = d - (k+1)*a[k+1][1];
+ }
+ int k = a.size()-1;
+ T d = (2*k+1)*(a[k][1] - a[k][0]);
+ //TODO: do a real test to know if d==0!
+ if(d == T(0.0))
+ c.pop_back();
+ else {
+ c[k][0] = d;
+ c[k][1] = d;
+ }
+
+ return c;
+}
+
+template<typename T>
+void SBasisOf<T>::derive() { // in place version
+ if(isZero()) return;
+ for(unsigned k = 0; k < this->size()-1; k++) {
+ T d = (2*k+1)*((*this)[k][1] - (*this)[k][0]);
+
+ (*this)[k][0] = d + (k+1)*(*this)[k+1][0];
+ (*this)[k][1] = d - (k+1)*(*this)[k+1][1];
+ }
+ int k = this->size()-1;
+ T d = (2*k+1)*((*this)[k][1] - (*this)[k][0]);
+ if(d == 0)//TODO: give this a meaning for general coeff ring.
+ this->pop_back();
+ else {
+ (*this)[k][0] = d;
+ (*this)[k][1] = d;
+ }
+}
+
+
+template<typename T>
+inline SBasisOf<T> operator*(SBasisOf<T> const & a, SBasisOf<T> const & b) {
+ return multiply(a, b);
+}
+
+template<typename T>
+inline SBasisOf<T>& operator*=(SBasisOf<T>& a, SBasisOf<T> const & b) {
+ a = multiply(a, b);
+ return a;
+}
+
+// a(b(t))
+//TODO: compose all compatibles types!
+template<typename T>
+SBasisOf<T> compose(SBasisOf<T> const &a, SBasisOf<T> const &b){
+ SBasisOf<double> s = multiply((SBasisOf<T>(LinearOf<T>(1,1))-b), b);
+ SBasisOf<T> r;
+
+ for(int i = a.size()-1; i >= 0; i--) {
+ r = multiply_add(r, s, SBasisOf<T>(LinearOf<T>(HatOf<T>(a[i][0]))) - b*a[i][0] + b*a[i][1]);
+ }
+ return r;
+}
+
+template<typename T>
+SBasisOf<T> compose(SBasisOf<T> const &a, SBasisOf<T> const &b, unsigned k){
+ SBasisOf<T> s = multiply((SBasisOf<T>(LinearOf<T>(1,1))-b), b);
+ SBasisOf<T> r;
+
+ for(int i = a.size()-1; i >= 0; i--) {
+ r = multiply_add(r, s, SBasisOf<T>(LinearOf<T>(HatOf<T>(a[i][0]))) - b*a[i][0] + b*a[i][1]);
+ }
+ r.truncate(k);
+ return r;
+}
+template<typename T>
+SBasisOf<T> compose(LinearOf<T> const &a, SBasisOf<T> const &b){
+ return compose(SBasisOf<T>(a),b);
+}
+template<typename T>
+SBasisOf<T> compose(SBasisOf<T> const &a, LinearOf<T> const &b){
+ return compose(a,SBasisOf<T>(b));
+}
+template<typename T>//TODO: don't be so lazy!!
+SBasisOf<T> compose(LinearOf<T> const &a, LinearOf<T> const &b){
+ return compose(SBasisOf<T>(a),SBasisOf<T>(b));
+}
+
+
+
+template<typename T>
+inline SBasisOf<T> portion(const SBasisOf<T> &t, double from, double to) { return compose(t, LinearOf<T>(from, to)); }
+
+// compute f(g)
+template<typename T>
+inline SBasisOf<T>
+SBasisOf<T>::operator()(SBasisOf<T> const & g) const {
+ return compose(*this, g);
+}
+
+template<typename T>
+inline std::ostream &operator<< (std::ostream &out_file, const LinearOf<T> &bo) {
+ out_file << "{" << bo[0] << ", " << bo[1] << "}";
+ return out_file;
+}
+
+template<typename T>
+inline std::ostream &operator<< (std::ostream &out_file, const SBasisOf<T> & p) {
+ for(unsigned i = 0; i < p.size(); i++) {
+ out_file << p[i] << "s^" << i << " + ";
+ }
+ return out_file;
+}
+
+};
+#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/include/2geom/orphan-code/sbasisN.h b/include/2geom/orphan-code/sbasisN.h
new file mode 100644
index 0000000..0b5a48f
--- /dev/null
+++ b/include/2geom/orphan-code/sbasisN.h
@@ -0,0 +1,1123 @@
+/**
+ * \file
+ * \brief Multi-dimensional symmetric power basis function.
+ * A SBasisN<n> is a polynomial f of n variables (t0,...,tn-1),
+ * written in a particular form. Let si = ti(1-t_i). f is written as
+ *
+ * f = sum_p s^p a_{p}(t0,...,t_{n-1})
+ *
+ * where p=(p0,...,p_{n-1}) is a multi index (called MultiDegree<n> in the code)
+ * s^p = prod_i si^pi, and a_p is a LinearN<n>.
+ * Recall a LinearN<n> is sum over all choices xi = ti or (1-ti) of terms of form
+ * a * x0*...*x_{n-1}
+ *
+ * Caution: degrees are expressed as degrees of s=t*(1-t). The real degree
+ * (with respect to t) of the polynomial is twice that + 0 or 1 depending
+ * whether the relevant LinearN<n> coeff is constant or not.
+ *//*
+ *
+ * Authors:
+ * JF Barraud <jf.barraud@gmail.com>
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Michael Sloan <mgsloan@gmail.com>
+ *
+ * Copyright (C) 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.
+ */
+
+#ifndef SEEN_SBASISN_H
+#define SEEN_SBASISN_H
+#include <vector>
+#include <cassert>
+#include <iostream>
+
+#include <2geom/orphan-code/linearN.h>
+#include <2geom/linear.h>//for conversion purpose
+#include <2geom/sbasis.h>//for conversion purpose
+#include <2geom/interval.h>
+#include <2geom/utils.h>
+#include <2geom/exception.h>
+
+
+namespace Geom{
+
+/** MultiDegree:
+ * \brief multi-degree (p0,...,p^{n-1}) of a s0^p0...s_{n-1}p^{n-1} monomial.
+ *
+ * a "Multi_deg" is a sequence p={p0,...,p_n-1} representing the monomial
+ * s^p = s_0^{p_0}*...*s_{n-1}^{p_{n-1}}.
+ * Caution: the degrees are expressed with respect to si! ( in SBasis code
+ * below, si = ti*(1-ti) ).
+ */
+
+template<unsigned n>
+class MultiDegree{
+public:
+ unsigned p[n];
+ MultiDegree(){
+ for (unsigned i = 0; i <n; i++) {
+ p[i]=0;
+ }
+ }
+ MultiDegree( unsigned const other_p[] ){
+ p = other_p;
+ }
+ MultiDegree(unsigned const idx, unsigned const sizes[]){
+ unsigned q = idx;
+ for (unsigned i = n-1; i >0; i--) {
+ div_t d = std::div(int(q), int(sizes[i]));
+ p[i] = d.rem;
+ q = d.quot;
+ }
+ p[0] = q;
+ }
+ unsigned operator[](const unsigned i) const {
+ assert(i < n); return p[i];
+ }
+ unsigned& operator[](const unsigned i) {
+ assert(i < n); return p[i];
+ }
+
+ unsigned asIdx(unsigned const sizes[]) const{
+ unsigned ret = p[0];
+ bool in_range = (p[0]<sizes[0]);
+ for (unsigned i = 1; i < n; i++) {
+ in_range = in_range && (p[i]<sizes[i]);
+ ret = ret*sizes[i] + p[i];
+ }
+ if (in_range) return ret;
+ //TODO: find a better warning than returning out of range idx!
+ ret =1;
+ for (unsigned i = 0; i < n; i++) {
+ ret *= sizes[i];
+ }
+ return ret;
+ }
+ bool stepUp(unsigned const sizes[], unsigned frozen_mask = 0){
+ unsigned i = 0;
+ while ( i < n && ( (1<<i) & frozen_mask ) ) i++;
+ while ( i <n && p[i] == sizes[i]-1 ) {
+ i++;
+ while (i<n && ( (1<<i) & frozen_mask ) ) i++;
+ }
+ if (i<n){
+ p[i]+=1;
+ for (unsigned j = 0; j < i; j++) {
+ if ( !( (1<<j) & frozen_mask ) ) p[j] = 0;
+ }
+ return true;
+ }else{
+ return false;
+ }
+ }
+ bool stepDown(unsigned const sizes[], unsigned frozen_mask = 0){
+ int i = n-1;
+ while (i>=0 && ( (1<<i) & frozen_mask ) ) i--;
+ while ( i >= 0 && p[i] == 0 ) {
+ i--;
+ while (i>=0 && ( (1<<i) & frozen_mask ) ) i--;
+ }
+ if ( i >= 0 ){
+ p[i]-=1;
+ for (unsigned j = i+1; j < n; j++) {
+ if ( !( (1<<j) & frozen_mask ) ) p[j] = sizes[j]-1;
+ }
+ return true;
+ }else{
+ return false;
+ }
+ }
+};
+
+/**
+ * Returns the maximal degree appearing in the two arguments for each variables.
+ */
+template <unsigned n>
+MultiDegree<n> max(MultiDegree<n> const &p, MultiDegree<n> const &q){
+ MultiDegree<n> ret;
+ for (unsigned i = 0; i <n; i++) {
+ ret.p[i] = (p[i]>q[i] ? p[i] : q[i]);
+ }
+ return ret;
+}
+
+template <unsigned n>
+MultiDegree<n> operator + (MultiDegree<n> const &p, MultiDegree<n> const &q){
+ MultiDegree<n> ret;
+ for (unsigned i = 0; i <n; i++) {
+ ret.p[i] = p[i] + q[i];
+ }
+ return ret;
+}
+template <unsigned n>
+MultiDegree<n> operator += (MultiDegree<n> const &p, MultiDegree<n> const &q){
+ for (unsigned i = 0; i <n; i++) {
+ p[i] += q[i];
+ }
+ return p;
+}
+
+/**
+ * \brief MultiDegree comparison.
+ * A MultiDegree \param p is smaller than another \param q
+ * if all it's smaller for all variables.
+ *
+ * In particular, p<=q and q<=p can both be false!
+ */
+template<unsigned n>
+bool operator<=(MultiDegree<n> const &p, MultiDegree<n> const &q){
+ for (unsigned i = 0; i <n; i++) {
+ if (p[i]>q[i]) return false;
+ }
+ return true;
+}
+
+
+/**
+ * \brief Polynomials of n variables, written in SBasis form.
+ * An SBasisN<n> f is internaly represented as a vector of LinearN<n>.
+ * It should be thought of as an n-dimensional vector: the coef of s0^p0...s_{n-1}p^{n-1}
+ * is soterd in f[p0,...,p_{n-1}]. The sizes of each dimension is stored in "sizes".
+ * Note: an empty SBasis is identically 0.
+ */
+template<unsigned n>
+class SBasisN : public std::vector<LinearN<n> >{
+public:
+ unsigned sizes[n];
+ SBasisN() {
+ for (unsigned i = 0; i < n; i++) {
+ sizes[i] = 0;
+ }
+ }
+ explicit SBasisN(double a) {
+ for (unsigned i = 0; i < n; i++) {
+ sizes[i] = 1;
+ }
+ this->push_back(LinearN<n>(a));
+ }
+ SBasisN(SBasisN<n> const & a) : std::vector<LinearN<n> >(a){
+ //TODO: efficient array copy??
+ for (unsigned i = 0; i < n; i++) {
+ sizes[i] = a.sizes[i];
+ }
+ }
+ SBasisN(LinearN<n> const & bo) {
+ for (unsigned i = 0; i < n; i++) {
+ sizes[i] = 1;
+ }
+ this->push_back(bo);
+ }
+ SBasisN(LinearN<n>* bo) {
+ for (unsigned i = 0; i < n; i++) {
+ sizes[i] = 1;
+ }
+ this->push_back(*bo);
+ }
+
+//----------------------------------------------
+//-- Degree/Sizing facilities ------------------
+//----------------------------------------------
+/**
+ * Internal recursive function used to compute partial degrees.
+ */
+ bool find_non_empty_level(unsigned var, MultiDegree<n> &fixed_degrees)const{
+ if (this->size()==0){
+ for (unsigned i = 0; i < n; i++) {
+ fixed_degrees[i] = 0;//FIXME this should be -infty!!
+ }
+ return false;
+ }
+ if ( !((*this)[fixed_degrees.asIdx(sizes)].isZero()) ) return true;
+
+ unsigned frozen = (1<<var);
+ if ( fixed_degrees.stepDown(sizes, frozen) ){
+ if ( find_non_empty_level(var, fixed_degrees) ) return true;
+ }
+ if ( fixed_degrees[var] > 0 ){
+ fixed_degrees[var] -= 1;
+ for (unsigned i = 0; i < n; i++) {
+ if (i!=var) fixed_degrees[i] = sizes[i]-1;
+ }
+ if (find_non_empty_level(var, fixed_degrees)) return true;
+ }
+ return false;//FIXME: this should return -infty in all variables!
+ }
+
+/**
+ * Returns the degree of an SBasisN<n> with respect to a given variable form its sizes.
+ * All terms are taken into account, even eventual trailing zeros.
+ * Note: degree is expressed with respect to s = t*(1-t), not t itself.
+ */
+ unsigned quick_degree(unsigned var) const{
+ return ( sizes[var] > 0 ? sizes[var]-1 : 0 );//this should be -infty.
+ }
+/**
+ * Computes the multi degree of the SBasis from it's sizes.
+ * All terms are taken into account, even eventual trailing zeros.
+ * Note: degrees are expressed with respect to s = t*(1-t), not t itself.
+ */
+ MultiDegree<n> quick_multi_degree() const{
+ MultiDegree<n> ret;
+ if (this->size()==0) return ret;//should be -infty for all vars.
+ for (unsigned i = 0; i < n; i++) {
+ assert( sizes[i]>0 );
+ ret.p[i] = sizes[i]-1;
+ }
+ return ret;
+ }
+/**
+ * Returns the degree of an SBasisN<n> with respect to a given variable.
+ * Trailing zeros are not taken into account.
+ * Note: degree is expressed with respect to s = t*(1-t), not t itself.
+ */
+ unsigned degree(unsigned var)const{
+ MultiDegree<n> degrees;
+ for(unsigned i = 0; i < n; i++) {
+ degrees[i] = sizes[i]-1;
+ }
+ if ( find_non_empty_level(var, degrees) ) return degrees[var];
+ else return 0;//should be -infty.
+ }
+/**
+ * Returns the *real* degree of an SBasisN<n> with respect to a given variable.
+ * Trailing zeros are not taken into account.
+ * Note: degree is expressed with respect to t itself, not s = t*(1-t).
+ * In particular: real_t_degree() = 2*degree() + 0 or 1.
+ */
+ unsigned real_t_degree(unsigned var)const{
+ unsigned deg = 0;
+ bool even = true;
+ bool notfound = true;
+ unsigned frozen = (1<<var);
+ MultiDegree<n> degrees;
+ for(unsigned i = 0; i < n; i++) {
+ degrees[i] = sizes[i]-1;
+ }
+ while( notfound ){
+ if ( find_non_empty_level(var, degrees) && degrees[var]>= deg ){
+ deg = degrees[var];
+ even = (*this)[degrees.asIdx(sizes)].isConstant(var);
+ }
+ notfound = even && degrees.stepDown(sizes, frozen);
+ }
+ return 2*deg + ( even ? 0 : 1 );
+ }
+/**
+ * Returns the *real* degrees of an SBasisN<n>.
+ * Trailing zeros are not taken into account.
+ * Note: degree is expressed with respect to t itself, not s = t*(1-t).
+ * In particular: real_t_degree() = 2*degree() + 0 or 1.
+ */
+ MultiDegree<n> real_t_degrees()const{
+ MultiDegree<n>res;
+ for(unsigned i = 0; i < n; i++) {
+ res[i] = real_t_degree(i);
+ }
+ return res;
+ }
+/**
+ * Computes the multi degree of the SBasis.
+ * Trailing zeros are not taken into account.
+ * Note: degree is expressed with respect to s = t*(1-t), not t itself.
+ */
+ MultiDegree<n> multi_degree() const{
+ MultiDegree<n> ret;
+ if (this->size()==0) return ret;//should be -infty for all vars.
+ for (unsigned i = 0; i < n; i++) {
+ ret[i] = this->degree(i);
+ }
+ return ret;
+ }
+/**
+ * Returns the highest degree over all variables.
+ * Note: degree is expressed with respect to s = t*(1-t), not t itself.
+ */
+ unsigned max_degree() const {
+ if (this->size()==0) return 0;//should be -infty!
+ unsigned d=0;
+ for (unsigned i = 0; i < n; i++) {
+ assert( sizes[i]>0 );
+ if (d < sizes[i]-1) d = sizes[i]-1;
+ }
+ return d;
+ }
+
+/**
+ * Resize an SBasisN<n> to match new sizes.
+ *
+ * Caution: if a new size is smaller, the corresponding coefficients are discarded.
+ */
+ void multi_resize(unsigned new_sizes[], LinearN<n> def_value = LinearN<n>(0.)){
+ SBasisN<n> result;
+ bool nothing_todo = true;
+ unsigned tot_size = 1;
+ for(unsigned i = 0; i < n; i++) {
+ nothing_todo = nothing_todo && (sizes[i] == new_sizes[i]);
+ result.sizes[i] = new_sizes[i];
+ tot_size *= new_sizes[i];
+ }
+ if (nothing_todo) return;
+ result.resize(tot_size, def_value);
+ for(unsigned i = 0; i < tot_size; i++) {
+ MultiDegree<n> d( i, result.sizes );
+ unsigned j = d.asIdx(sizes);
+ if ( j < this->size() ){
+ result[i] = (*this)[j];
+ }
+ }
+ *this = result;
+ }
+
+ //remove extra zeros
+ void normalize() {
+ MultiDegree<n> max_p = multi_degree();
+ unsigned new_sizes[n];
+ for (unsigned i=0; i<n; i++){
+ new_sizes[i] = max_p[i]+1;
+ }
+ multi_resize(new_sizes);
+ }
+
+//-----------------------------
+//-- Misc. --------------------
+//-----------------------------
+
+/**
+ * Returns the number of variables this function takes as input: n.
+ */
+ unsigned input_dim(){return n;};
+
+ //IMPL: FragmentConcept
+ typedef double output_type;
+
+ inline bool isZero() const {
+ if(this->size()==0) return true;
+ for(unsigned i = 0; i < this->size(); i++) {
+ if(!(*this)[i].isZero()) return false;
+ }
+ return true;
+ }
+ inline bool isConstant() const {
+ if (this->size()==0) return true;
+ if(!(*this)[0].isConstant()) return false;
+ for (unsigned i = 1; i < this->size(); i++) {
+ if(!(*this)[i].isZero()) return false;
+ }
+ return true;
+ }
+
+ bool isFinite() const{
+ for (unsigned i = 0; i < this->size(); i++) {
+ if(!(*this)[i].isFinite()) return false;
+ }
+ return true;
+ }
+
+
+//------------------------------------------
+//-- Evaluation methods --------------------
+//------------------------------------------
+/**
+ * Returns the value of the SBasis at a given corner of [0,1]^n.
+ * \param k describes the corner: if i-th bit is 0, ti=0, otherwise ti=1.
+ */
+ inline double atCorner(unsigned k) const {
+ if(this->size()==0) return 0.;
+ return (*this)[0].atCorner(k);
+ }
+/**
+ * Returns the value of the SBasis at a given corner of [0,1]^n.
+ * \param t[n] describes the corner: the values should be 0's and 1's.
+ */
+ inline double atCorner(double t[]) const {
+ if(this->size()==0) return 0.;
+ return (*this)[0].atCorner(t);
+ }
+/**
+ * Returns a "slice" of the array.
+ * Returns an SBasis containing all the coeff of (s-)degree \param deg in variable \param var
+ */
+ //TODO: move by bigger blocks (?) but they are broken into pieces in general...
+ SBasisN<n> slice(unsigned const var, unsigned const deg) const{
+ if (deg >= sizes[var] ) return SBasisN<n>();
+ SBasisN<n> res;
+ unsigned tot_size = 1;
+ for (unsigned i = 0; i < n; i++) {
+ res.sizes[i] = (i==var ? 1 : sizes[i]);
+ tot_size *= res.sizes[i];
+ }
+ res.resize( tot_size, LinearN<n>(0.));
+ for (unsigned i = 0; i < tot_size; i++) {
+ MultiDegree<n> d(i,res.sizes);
+ d.p[var] = deg;
+ res[i] = (*this)[d.asIdx(sizes)];
+ }
+ return res;
+ }
+/**
+ * Returns a the SBasisN<n-1> obtained by setting variable \param var to 0.
+ */
+ inline SBasisN<n-1> at0(unsigned var=0, unsigned deg=0) const {
+ SBasisN<n> sl = slice(var,deg);
+ SBasisN<n-1> res;
+ res.reserve(sl.size());
+ for (unsigned i = 0; i < n-1; i++) {
+ res.sizes[i] = sizes[ ( i<var ? i : i+1 ) ];
+ }
+ for (unsigned i = 0; i < sl.size(); i++) {
+ res.push_back( sl[i].at0(var) );
+ }
+ return res;
+ }
+/**
+ * Returns a the SBasisN<n-1> obtained by setting variable \param var to 1.
+ */
+ inline SBasisN<n-1> at1(unsigned var=0, unsigned deg=0) const {
+ SBasisN<n> sl = slice(var,deg);
+ SBasisN<n-1> res;
+ res.reserve(sl.size());
+ for (unsigned i = 0; i < n-1; i++) {
+ res.sizes[i] = sizes[ ( i<var ? i : i+1 ) ];
+ }
+ for (unsigned i = 0; i < sl.size(); i++) {
+ res.push_back( sl[i].at1(var) );
+ }
+ return res;
+ }
+/**
+ * Returns a the SBasisN<n-1> obtained by setting variable \param var to \param t.
+ */
+ inline SBasisN<n-1> partialEval(double t, unsigned var=0 ) const {
+ SBasisN<n> sl;
+ double s = t*(1-t);
+ double si = 1;
+ for (unsigned i = 0; i <sizes[var]; i++) {
+ sl = sl + slice(var, i)*si;
+ si *= s;
+ }
+ SBasisN<n-1> res;
+ res.resize(sl.size(), LinearN<n-1>(0.));
+ for (unsigned i = 0; i < n-1; i++) {
+ res.sizes[i] = sizes[ ( i<var ? i : i+1 ) ];
+ }
+ for (unsigned i = 0; i < sl.size(); i++) {
+ res[i] = sl[i].partialEval(t,var);
+ }
+ return res;
+ }
+
+/**
+ * \brief Internal recursive function.
+ * Replace each variable by it's value in the 's=t*(1-t)' factor
+ * but not in the LinearN<n> coeffs. Then sum up all coefficients.
+ * \param t[n]: values of the variables.
+ */
+ LinearN<n> sumCoefs( double t[], unsigned const k, unsigned const idx) const{
+ LinearN<n> a;
+ if (k == n){
+ a = (*this)[idx];
+ return (*this)[idx];
+ }
+ double s = t[k]*(1-t[k]);
+ double si=1;
+ for (unsigned i=0; i<sizes[k]; i++){
+ a += sumCoefs(t,k+1,idx*sizes[k]+i)*si;;
+ si *= s;
+ }
+ return a;
+ }
+/**
+ * Evaluate at given n-dimensional point.
+ * \param t[n]: values of the variables.
+ */
+ double valueAt(double t[]) const {
+ LinearN<n> a = sumCoefs(t,0,0);
+ return a.valueAt(t);
+ }
+
+ double operator()(double t[]) const {
+ return valueAt(t);
+ }
+
+ //double valueAndDerivative(double t, double &der) const;
+ //std::vector<double> valueAndDerivatives(double t, unsigned n) const;
+ //SBasisN toSBasisN() const { return SBasisN(*this); }
+ //double tailError(unsigned tail) const;
+
+
+//--------------------------------------------------
+//-- Coeff. manipulation ---------------------------
+//--------------------------------------------------
+
+/**
+ * Accessing the SBasisN<n> coefficients.
+ */
+ LinearN<n> operator[](unsigned i) const {
+ assert(i < this->size());
+ return std::vector<LinearN<n> >::operator[](i);
+ }
+ LinearN<n> operator[](MultiDegree<n> const &p) const {
+ unsigned i = p.asIdx(sizes);
+ assert(i < this->size());
+ return std::vector<LinearN<n> >::operator[](i);
+ }
+
+//MUTATOR PRISON
+ LinearN<n>& operator[](unsigned i) { return this->at(i); }
+// LinearN<n>& operator[](MultiDegree const &p) {
+// unsigned i = p.asIdx(sizes);
+// return this->at(i);
+// }
+
+ void appendCoef(const SBasisN<n-1> &a, const SBasisN<n-1> &b, unsigned var=0){
+ unsigned new_sizes[n];
+ MultiDegree<n-1> deg_a = a.multi_degree(), deg_b = b.multi_degree();
+ MultiDegree<n-1> dcoef = max( deg_a, deg_b );
+ for (unsigned i=0; i<n; i++){
+ if ( i == var ){
+ new_sizes[var] = sizes[var] + 1;
+ }else{
+ unsigned coef_size = dcoef[(i<var?i:i-1)] + 1;
+ new_sizes[i] = ( sizes[i]>coef_size ? sizes[i] : coef_size );
+ }
+ }
+ multi_resize(new_sizes);
+
+ MultiDegree<n> d;
+ d[var] = sizes[var]-1;
+ unsigned frozen_mask = (1<<var);
+ do{
+ for (unsigned i=0; i<n-1; i++){
+ dcoef.p[i] = d.p[ ( i<var ? i : i+1) ];
+ }
+ LinearN<n-1> a_d,b_d;
+ unsigned ia = dcoef.asIdx(a.sizes);
+ if ( ia < a.size() ) a_d = a[ia];
+ unsigned ib = dcoef.asIdx(b.sizes);
+ if ( ib < b.size() ) b_d = b[ib];
+ (*this)[d.asIdx(sizes)] = LinearN<n>(a_d,b_d);
+ }while (d.stepUp(sizes,frozen_mask));
+ }
+
+//private:
+ //void derive(); // in place version
+};
+
+//SBasisN<0> is a double. Specialize it out.
+template<>
+class SBasisN<0>{
+public:
+ double d;
+ SBasisN () {}
+ SBasisN(double d) :d(d) {}
+ operator double() const { return d; }
+};
+
+
+//SBasisN<1> are usual SBasis. Allow conversion.
+SBasis toSBasis(SBasisN<1> f){
+ SBasis res(f.size(), Linear());
+ for (unsigned i = 0; i < f.size(); i++) {
+ res[i] = toLinear(f[i]);
+ }
+ return res;
+}
+
+//TODO: figure out how to stick this in linear, while not adding an sbasis dep
+template<unsigned n>
+inline SBasisN<n> LinearN<n>::toSBasisN() const { return SBasisN<n>(*this); }
+
+
+
+
+//implemented in sbasis-roots.cpp
+//OptInterval bounds_exact(SBasisN const &a);
+//OptInterval bounds_fast(SBasisN const &a, int order = 0);
+//OptInterval bounds_local(SBasisN const &a, const OptInterval &t, int order = 0);
+
+/** Returns a function which reverses the domain of a.
+ \param a sbasis function
+
+useful for reversing a parameteric curve.
+*/
+//template<unsigned n>
+//inline SBasisN<n> reverse(SBasisN<n> const &a);
+
+//IMPL: ScalableConcept
+template<unsigned n>
+inline SBasisN<n> operator-(const SBasisN<n>& p) {
+ if(p.isZero()) return SBasisN<n>();
+ SBasisN<n> result;
+ for(unsigned i = 0; i < n; i++) {
+ result.sizes[i] = p.sizes[i];
+ }
+ result.reserve(p.size());
+ for(unsigned i = 0; i < p.size(); i++) {
+ result.push_back(-p[i]);
+ }
+ return result;
+}
+template<unsigned n>
+SBasisN<n> operator*(SBasisN<n> const &a, double c){
+ if(a.isZero()) return SBasisN<n>();
+ SBasisN<n> result;
+ for(unsigned i = 0; i < n; i++) {
+ result.sizes[i] = a.sizes[i];
+ }
+ result.reserve(a.size());
+ for(unsigned i = 0; i < a.size(); i++) {
+ result.push_back(a[i] * c);
+ }
+ return result;
+}
+template<unsigned n>
+inline SBasisN<n> operator*(double k, SBasisN<n> const &a) { return a*k; }
+template<unsigned n>
+inline SBasisN<n> operator/(SBasisN<n> const &a, double k) { return a*(1./k); }
+template<unsigned n>
+SBasisN<n>& operator*=(SBasisN<n>& a, double c){
+ for(unsigned i = 0; i < a.size(); i++) a[i] *= c;
+ return a;
+}
+template<unsigned n>
+inline SBasisN<n>& operator/=(SBasisN<n>& a, double b) { return (a*=(1./b)); }
+
+//IMPL: AddableConcept
+template<unsigned n>
+SBasisN<n> operator + (const SBasisN<n>& a, const SBasisN<n>& b){
+ if( a.isZero() ) return b;
+ if( b.isZero() ) return a;
+ SBasisN<n> result;
+ MultiDegree<n> deg = max(a.quick_multi_degree(),b.quick_multi_degree());
+ unsigned max_size = 1;
+ for(unsigned i = 0; i < n; i++) {
+ result.sizes[i] = deg[i]+1;
+ max_size *= result.sizes[i];
+ }
+ result.resize( max_size, LinearN<n>(0.) );
+ for(unsigned i = 0; i < result.size(); i++) {
+ MultiDegree<n> p(i,result.sizes);
+ unsigned ia = p.asIdx(a.sizes);
+ unsigned ib = p.asIdx(b.sizes);
+ if (ia<a.size()) {
+ result[i] += a[ia];
+ }
+ if (ib<b.size()) {
+ result[i] += b[ib];
+ }
+ }
+ return result;
+}
+template<unsigned n>
+SBasisN<n> operator-(const SBasisN<n>& a, const SBasisN<n>& b){return a+(-b);}
+template<unsigned n>
+SBasisN<n>& operator+=(SBasisN<n>& a, const SBasisN<n>& b){
+ if(b.isZero()) return a;
+ a = a + b;
+ return a;
+}
+template<unsigned n>
+SBasisN<n>& operator-=(SBasisN<n>& a, const SBasisN<n>& b){
+ a += -b;
+ return a;
+}
+
+//TODO: remove?
+template<unsigned n>
+inline SBasisN<n> operator+(const SBasisN<n> & a, LinearN<n> const & b) {
+ if(b.isZero()) return a;
+ if(a.isZero()) return b;
+ SBasisN<n> result(a);
+ result[0] += b;
+ return result;
+}
+template<unsigned n>
+
+inline SBasisN<n> operator-(const SBasisN<n> & a, LinearN<n> const & b) {
+ if(b.isZero()) return a;
+ if(a.isZero()) return -b;
+ SBasisN<n> result(a);
+ result[0] -= b;
+ return result;
+}
+template<unsigned n>
+inline SBasisN<n>& operator+=(SBasisN<n>& a, const LinearN<n>& b) {
+ if(a.size()==0)
+ a.push_back(b);
+ else
+ a[0] += b;
+ return a;
+}
+template<unsigned n>
+inline SBasisN<n>& operator-=(SBasisN<n>& a, const LinearN<n>& b) {
+ if(a.size()==0)
+ a.push_back(-b);
+ else
+ a[0] -= b;
+ return a;
+}
+
+//IMPL: OffsetableConcept
+template<unsigned n>
+inline SBasisN<n> operator+(const SBasisN<n> & a, double b) {
+ if(a.isZero()) return LinearN<n>(b);
+ SBasisN<n> result(a);
+ result[0] += b;
+ return result;
+}
+template<unsigned n>
+inline SBasisN<n> operator-(const SBasisN<n> & a, double b) {
+ if(a.isZero()) return LinearN<n>(-b);
+ SBasisN<n> result(a);
+ result[0] -= b;
+ return result;
+}
+template<unsigned n>
+inline SBasisN<n>& operator+=(SBasisN<n>& a, double b) {
+ if(a.size()==0)
+ a.push_back(LinearN<n>(b));
+ else
+ a[0] += b;
+ return a;
+}
+template<unsigned n>
+inline SBasisN<n>& operator-=(SBasisN<n>& a, double b) {
+ if(a.size()==0)
+ a.push_back(LinearN<n>(-b));
+ else
+ a[0] -= b;
+ return a;
+}
+
+template<unsigned n>
+SBasisN<n> shift(SBasisN<n> const &a, MultiDegree<n> sh){
+ SBasisN<n> result;
+ MultiDegree<n> deg = a.quick_multi_degree() + sh;
+ for(unsigned i = 0; i < n; i++) {
+ result.sizes[i] = deg[i]+1;
+ }
+ unsigned max_size = deg.asIdx(result.sizes);
+ result.resize( max_size, LinearN<n>(0.) );
+ for(unsigned i = 0; i < a.size(); i++) {
+ MultiDegree<n> p(i,a.sizes);
+ p+=sh;
+ result[p.asIdx(result.sizes)]=a[i];
+ }
+ return result;
+}
+template<unsigned n>
+SBasisN<n> shift(LinearN<n> const &a, MultiDegree<n> sh){
+ SBasisN<n> result;
+ for(unsigned i = 0; i < n; i++) {
+ result.sizes[i] = sh[i]+1;
+ }
+ unsigned max_size = sh.asIdx(result.sizes);
+ result.resize( max_size, LinearN<n>(0.) );
+ result[max_size-1]=a;
+ return result;
+}
+//shift only in one variable
+template<unsigned n>
+SBasisN<n> shift(LinearN<n> const &a, unsigned sh, unsigned var){
+ assert( var < n );
+ SBasisN<n> result;
+ for(unsigned i = 0; i < n; i++) {
+ result.sizes[i] = 1;
+ }
+ result.sizes[var] = sh+1;
+ result.resize( sh+1, LinearN<n>(0.) );
+ result[sh]=a;
+ return result;
+}
+
+//truncate only in first variable
+template<unsigned n>
+inline SBasisN<n> truncate(SBasisN<n> const &a, unsigned first_size) {
+ if ( first_size <= a.sizes[0] ) return a;
+ SBasisN<n> c;
+ for (unsigned i = 0; i < n; i++) {
+ c.sizes[i] = a.sizes[i];
+ }
+ c.sizes[0] = first_size;
+ unsigned tot_size = 1;
+ for(unsigned i = 0; i < n; i++) {
+ tot_size*=c.sizes[i];
+ }
+ c.insert(c.begin(), a.begin(), a.begin() + tot_size);
+ return c;
+}
+
+template<unsigned n>
+SBasisN<n> multiply(SBasisN<n> const &a, SBasisN<n> const &b){
+ SBasisN<n> c;
+ MultiDegree<n> d;
+ MultiDegree<n> t_deg = a.real_t_degrees() + b.real_t_degrees();
+ for(unsigned i = 0; i < n; i++) {
+ d[i] = ( t_deg[i]%2 == 0 ? t_deg[i]/2 : (t_deg[i]-1)/2 ) ;
+ }
+ unsigned new_sizes[n], tot_size = 1;
+ for(unsigned i = 0; i < n; i++) {
+ //c.sizes[i] = d[i] + 1+1;//product of linears might give 1 more s in each dir!!
+ new_sizes[i] = d[i] + 1;
+ tot_size*=new_sizes[i];
+ }
+ c.resize( tot_size, LinearN<n>(0.) );
+ for(unsigned i = 0; i < n; i++) {
+ c.sizes[i] = new_sizes[i];
+ }
+
+ for(unsigned ai = 0; ai < a.size(); ai++) {
+ for(unsigned bj = 0; bj < b.size(); bj++) {
+ MultiDegree<n> di( ai, a.sizes );
+ MultiDegree<n> dj( bj, b.sizes );
+ //compute a[ai]*b[bj]:
+ for(unsigned p = 0; p < (1<<n); p++) {
+ for(unsigned q = 0; q < (1<<n); q++) {
+
+ //compute a[ai][p]*b[bj][q]:
+ unsigned m = p^q;//m has ones for factors s, 0 for (t-s) or ((1-t)-s).
+ for(unsigned r = 0; r < (1<<n); r++) {
+ if (!(r&m)) {// a 1 in r means take t (or (1-t)), otherwise take -s.
+ int sign = 1;
+ MultiDegree<n> dr;
+ unsigned t0 = 0, t1 = 0;
+ for (unsigned var = 0; var < n; var++) {
+ //if var is in mask m, no choice, take s
+ if ( m & (1<<var) ){
+ dr.p[var] = 1;
+ }//if var is in mask r, take t or (1-t)
+ else if ( r & (1<<var) ){
+ dr.p[var] = 0;
+ if ( p&(1<<var) ) {
+ t0 = t0 | (1<<var);
+ }else{
+ t1 = t1 | (1<<var);
+ }
+ }//ohterwise take -s
+ else{
+ dr.p[var] = 1;
+ sign *= -1;
+ }
+ }
+ unsigned idx = (di+dj+dr).asIdx(c.sizes);
+ if (idx < c.size()){
+ for(unsigned s = 0; s < (1<<n); s++) {
+ if ( (t0 & ~s) || (t1 & s) ){
+ c[idx][s] += 0;
+ }else{
+ c[idx][s] += sign * a[ai][p] * b[bj][q];
+ }
+ }
+ }
+ }
+ }//r loop: all choices have been expanded in the product a[ai][p]*b[bj][q]
+ }//q loop
+ }//p loop: all products a[ai][p]*b[bj][q] have been computed.
+ }//bj loop
+ }//ai loop: all a[ai]b[bj] have been computed.
+
+ //TODO: normalize c, or even better, compute with the right size from scratch
+ return c;
+}
+
+
+template<unsigned n>
+inline SBasisN<n> operator*(SBasisN<n> const & a, SBasisN<n> const & b) {
+ return multiply(a, b);
+}
+
+template<unsigned n>
+inline SBasisN<n>& operator*=(SBasisN<n>& a, SBasisN<n> const & b) {
+ a = multiply(a, b);
+ return a;
+}
+
+template<unsigned m,unsigned n>
+SBasisN<m> compose(LinearN<n> const &f, std::vector<SBasisN<m> > const &t, unsigned fixed=0, unsigned flags=0 ){
+ assert (t.size() == n );
+ if (fixed == n) {
+ return SBasisN<m>(1.) * f[flags];
+ }else{
+ SBasisN<m> a0 = compose(f, t, fixed+1, flags);
+ SBasisN<m> a1 = compose(f, t, fixed+1, flags|(1<<fixed));
+ return (-t[fixed]+1) * a0 + t[fixed] * a1;
+ }
+}
+
+template<unsigned m,unsigned n>
+SBasisN<m> compose(SBasisN<n> const &f, std::vector<SBasisN<m> > const &t, unsigned const k=0, unsigned const idx = 0){
+ assert (t.size() == n );
+ if (k == n){
+ return compose( f[idx], t);
+ }
+ SBasisN<m> a;
+ SBasisN<m> s = multiply( t[k], (-t[k]+1.) );
+ SBasisN<m> si= SBasisN<m>(1.);
+ for (unsigned i=0; i<f.sizes[k]; i++){
+ a += compose(f, t,k+1,idx*f.sizes[k]+i)*si;;
+ si *= s;
+ }
+ return a;
+}
+
+template <unsigned n>
+inline std::ostream &operator<< (std::ostream &out_file, const MultiDegree<n> & d) {
+ out_file << "s^{";
+ for(unsigned i = 0; i < n; i++) {
+ out_file << d[i] << (i == n-1 ? "}" : ",");
+ }
+ return out_file;
+}
+template <unsigned n>
+inline std::ostream &operator<< (std::ostream &out_file, const SBasisN<n> & p) {
+ for(unsigned i = 0; i < p.size(); i++) {
+ MultiDegree<n> d(i, p.sizes);
+ out_file << d << " " << p[i] << " + ";
+ }
+ return out_file;
+}
+
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+#if 0
+
+
+// This performs a multiply and accumulate operation in about the same time as multiply. return a*b + c
+template<unsigned n>
+SBasisN<n> multiply_add(SBasisN<n> const &a, SBasisN<n> const &b, SBasisN<n> c);
+
+template<unsigned n>
+SBasisN<n> integral(SBasisN<n> const &c);
+template<unsigned n>
+SBasisN<n> derivative(SBasisN<n> const &a);
+
+template<unsigned n>
+SBasisN<n> sqrt(SBasisN<n> const &a, int k);
+
+// return a kth order approx to 1/a)
+template<unsigned n>
+SBasisN<n> reciprocal(LinearN<n> const &a, int k);
+template<unsigned n>
+SBasisN<n> divide(SBasisN<n> const &a, SBasisN<n> const &b, int k);
+
+
+/** Returns the degree of the first non zero coefficient.
+ \param a sbasis function
+ \param tol largest abs val considered 0
+ \returns first non zero coefficient
+*/
+template<unsigned n>
+inline unsigned
+valuation(SBasisN<n> const &a, double tol=0){
+ unsigned val=0;
+ while( val<a.size() &&
+ fabs(a[val][0])<tol &&
+ fabs(a[val][1])<tol )
+ val++;
+ return val;
+}
+
+// a(b(t))
+template<unsigned n>
+SBasisN<n> compose(SBasisN<n> const &a, SBasisN<n> const &b);
+template<unsigned n>
+SBasisN<n> compose(SBasisN<n> const &a, SBasisN<n> const &b, unsigned k);
+template<unsigned n>
+SBasisN<n> inverse(SBasisN<n> a, int k);
+//compose_inverse(f,g)=compose(f,inverse(g)), but is numerically more stable in some good cases...
+//TODO: requires g(0)=0 & g(1)=1 atm. generalization should be obvious.
+template<unsigned n>
+SBasisN<n> compose_inverse(SBasisN<n> const &f, SBasisN<n> const &g, unsigned order=2, double tol=1e-3);
+
+/** Returns the sbasis on domain [0,1] that was t on [from, to]
+ \param a sbasis function
+ \param from,to interval
+ \returns sbasis
+
+*/
+template<unsigned n>
+inline SBasisN<n> portion(const SBasisN<n> &t, double from, double to) { return compose(t, LinearN<n>(from, to)); }
+
+// compute f(g)
+template<unsigned n>
+inline SBasisN<n>
+SBasisN<n>::operator()(SBasisN<n> const & g) const {
+ return compose(*this, g);
+}
+
+template<unsigned n>
+inline std::ostream &operator<< (std::ostream &out_file, const LinearN<n> &bo) {
+ out_file << "{" << bo[0] << ", " << bo[1] << "}";
+ return out_file;
+}
+
+template<unsigned n>
+inline std::ostream &operator<< (std::ostream &out_file, const SBasisN<n> & p) {
+ for(unsigned i = 0; i < p.size(); i++) {
+ out_file << p[i] << "s^" << i << " + ";
+ }
+ return out_file;
+}
+
+// These are deprecated, use sbasis-math.h versions if possible
+template<unsigned n>
+SBasisN<n> sin(LinearN<n> bo, int k);
+template<unsigned n>
+SBasisN<n> cos(LinearN<n> bo, int k);
+
+template<unsigned n>
+std::vector<double> roots(SBasisN<n> const & s);
+template<unsigned n>
+std::vector<std::vector<double> > multi_roots(SBasisN<n> const &f,
+ std::vector<double> const &levels,
+ double htol=1e-7,
+ double vtol=1e-7,
+ double a=0,
+ double b=1);
+
+#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 :
+#endif
diff --git a/include/2geom/parallelogram.h b/include/2geom/parallelogram.h
new file mode 100644
index 0000000..0c7134f
--- /dev/null
+++ b/include/2geom/parallelogram.h
@@ -0,0 +1,83 @@
+/*
+ * Authors:
+ * Thomas Holder
+ * Sergei Izmailov
+ *
+ * Copyright 2020 Authors
+ *
+ * SPDX-License-Identifier: LGPL-2.1 or MPL-1.1
+ */
+
+#ifndef LIB2GEOM_SEEN_PARALLELOGRAM_H
+#define LIB2GEOM_SEEN_PARALLELOGRAM_H
+
+#include <2geom/affine.h>
+#include <2geom/rect.h>
+
+namespace Geom {
+
+/**
+ * Paralellogram, representing a linear transformation of a rectangle.
+ *
+ * Implements efficient "contains" and "intersects" operations.
+ */
+class Parallelogram {
+ Affine m_affine;
+
+ /// Transformed unit rectangle
+ Parallelogram(Affine const &affine)
+ : m_affine(affine)
+ {
+ }
+
+ public:
+ explicit Parallelogram(Rect const &rect)
+ : m_affine(rect.width(), 0, 0, rect.height(), rect.left(), rect.top())
+ {
+ }
+
+ Point corner(unsigned i) const;
+
+ Point midpoint() const { return Point(0.5, 0.5) * m_affine; }
+
+ /// Area (non-negative)
+ Coord area() const { return m_affine.descrim2(); }
+
+ /// Axis-aligned bounding box
+ Rect bounds() const;
+
+ bool intersects(Parallelogram const &) const;
+ bool intersects(Rect const &rect) const { return intersects(Parallelogram(rect)); }
+
+ bool contains(Point const &) const;
+ bool contains(Parallelogram const &) const;
+ bool contains(Rect const &rect) const { return contains(Parallelogram(rect)); }
+
+ /// Returns shorter side length
+ Coord minExtent() const;
+
+ /// Returns longer side length
+ Coord maxExtent() const;
+
+ /// Return a new transformed parallelogram
+ Parallelogram operator*(Affine const &affine) const { return m_affine * affine; }
+ Parallelogram &operator*=(Affine const &affine) { m_affine *= affine; return *this; }
+
+ /// True if this parallelogram does not have right angles
+ bool isSheared(Coord eps = EPSILON) const;
+};
+
+} // namespace Geom
+
+#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/include/2geom/path-intersection.h b/include/2geom/path-intersection.h
new file mode 100644
index 0000000..7d7cec9
--- /dev/null
+++ b/include/2geom/path-intersection.h
@@ -0,0 +1,118 @@
+/**
+ * \file
+ * \brief Path intersection
+ *//*
+ * 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 LIB2GEOM_SEEN_PATH_INTERSECTION_H
+#define LIB2GEOM_SEEN_PATH_INTERSECTION_H
+
+#include <2geom/crossing.h>
+#include <2geom/path.h>
+#include <2geom/sweep-bounds.h>
+
+namespace Geom {
+
+int winding(Path const &path, Point const &p);
+bool path_direction(Path const &p);
+
+inline bool contains(Path const & p, Point const &i, bool evenodd = true) {
+ return (evenodd ? winding(p, i) % 2 : winding(p, i)) != 0;
+}
+
+template<typename T>
+Crossings curve_sweep(Path const &a, Path const &b) {
+ T t;
+ Crossings ret;
+ std::vector<Rect> bounds_a = bounds(a), bounds_b = bounds(b);
+ std::vector<std::vector<unsigned> > ixs = sweep_bounds(bounds_a, bounds_b);
+ for(unsigned i = 0; i < a.size(); i++) {
+ for(std::vector<unsigned>::iterator jp = ixs[i].begin(); jp != ixs[i].end(); ++jp) {
+ Crossings cc = t.crossings(a[i], b[*jp]);
+ offset_crossings(cc, i, *jp);
+ ret.insert(ret.end(), cc.begin(), cc.end());
+ }
+ }
+ return ret;
+}
+
+Crossings pair_intersect(Curve const & A, Interval const &Ad,
+ Curve const & B, Interval const &Bd);
+Crossings mono_intersect(Curve const & A, Interval const &Ad,
+ Curve const & B, Interval const &Bd);
+
+struct SimpleCrosser : public Crosser<Path> {
+ Crossings crossings(Curve const &a, Curve const &b);
+ Crossings crossings(Path const &a, Path const &b) override { return curve_sweep<SimpleCrosser>(a, b); }
+ CrossingSet crossings(PathVector const &a, PathVector const &b) override { return Crosser<Path>::crossings(a, b); }
+};
+
+struct MonoCrosser : public Crosser<Path> {
+ Crossings crossings(Path const &a, Path const &b) override { return crossings(PathVector(a), PathVector(b))[0]; }
+ CrossingSet crossings(PathVector const &a, PathVector const &b) override;
+};
+
+typedef SimpleCrosser DefaultCrosser;
+
+std::vector<double> path_mono_splits(Path const &p);
+
+CrossingSet crossings_among(PathVector const & p);
+Crossings self_crossings(Path const & a);
+
+inline Crossings crossings(Curve const & a, Curve const & b) {
+ DefaultCrosser c = DefaultCrosser();
+ return c.crossings(a, b);
+}
+
+inline Crossings crossings(Path const & a, Path const & b) {
+ DefaultCrosser c = DefaultCrosser();
+ return c.crossings(a, b);
+}
+
+inline CrossingSet crossings(PathVector const & a, PathVector const & b) {
+ DefaultCrosser c = DefaultCrosser();
+ return c.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/include/2geom/path-sink.h b/include/2geom/path-sink.h
new file mode 100644
index 0000000..35bd1cd
--- /dev/null
+++ b/include/2geom/path-sink.h
@@ -0,0 +1,253 @@
+/**
+ * \file
+ * \brief callback interface for SVG path data
+ *//*
+ * Copyright 2007 MenTaLguY <mental@rydia.net>
+ *
+ * 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_SINK_H
+#define LIB2GEOM_SEEN_PATH_SINK_H
+
+#include <2geom/forward.h>
+#include <2geom/pathvector.h>
+#include <2geom/curves.h>
+#include <iterator>
+
+namespace Geom {
+
+
+/** @brief Callback interface for processing path data.
+ *
+ * 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 feed().
+ *
+ * @ingroup Paths
+ */
+class PathSink {
+public:
+ /** @brief Move to a different point without creating a segment.
+ * Usually starts a new subpath. */
+ virtual void moveTo(Point const &p) = 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;
+ /** @brief Output an elliptical arc segment.
+ * See the EllipticalArc class for the documentation of parameters. */
+ 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;
+ /** @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 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;
+
+ /** @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
+ 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 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 &);
+ /// Output a circle as two elliptical arcs.
+ virtual void feed(Circle const &e);
+ /// Output an ellipse as two elliptical arcs.
+ virtual void feed(Ellipse const &e);
+
+ virtual ~PathSink() {}
+};
+
+/** @brief Store paths to an output iterator
+ * @ingroup Paths */
+template <typename OutputIterator>
+class PathIteratorSink : public PathSink {
+public:
+ explicit PathIteratorSink(OutputIterator out)
+ : _in_path(false), _out(out) {}
+
+ void moveTo(Point const &p) override {
+ flush();
+ _path.start(p);
+ _start_p = p;
+ _in_path = true;
+ }
+//TODO: what if _in_path = false?
+
+ /** @brief Detect if the builder is in a path and thus will NOT
+ create a new moveTo command when given the next line
+ @return true if the builder is inside a subpath.
+ */
+ bool inPath() const {
+ return _in_path;
+ }
+
+ void lineTo(Point const &p) override {
+ // 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<LineSegment>(p);
+ }
+
+ void quadTo(Point const &c, Point const &p) override {
+ // 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<QuadraticBezier>(c, p);
+ }
+
+ void curveTo(Point const &c0, Point const &c1, Point const &p) override {
+ // 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<CubicBezier>(c0, c1, p);
+ }
+
+ void arcTo(Coord rx, Coord ry, Coord angle,
+ bool large_arc, bool sweep, Point const &p) override
+ {
+ // 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<EllipticalArc>(rx, ry, angle,
+ large_arc, sweep, p);
+ }
+
+ bool backspace() override
+ {
+ if (_in_path && _path.size() > 0) {
+ _path.erase_last();
+ return true;
+ }
+ return false;
+ }
+
+ void append(Path const &other)
+ {
+ if (!_in_path) {
+ moveTo(other.initialPoint());
+ }
+ _path.append(other);
+ }
+
+ void closePath() override {
+ if (_in_path) {
+ _path.close();
+ flush();
+ }
+ }
+
+ void flush() override {
+ if (_in_path) {
+ _in_path = false;
+ *_out++ = _path;
+ _path.clear();
+ }
+ }
+
+ void setStitching(bool s) {
+ _path.setStitching(s);
+ }
+
+ using PathSink::feed;
+ void feed(Path const &other) override
+ {
+ flush();
+ *_out++ = other;
+ }
+
+protected:
+ bool _in_path;
+ OutputIterator _out;
+ Path _path;
+ Point _start_p;
+};
+
+typedef std::back_insert_iterator<PathVector> SubpathInserter;
+
+/** @brief Store paths to a PathVector
+ * @ingroup Paths */
+class PathBuilder : public PathIteratorSink<SubpathInserter> {
+private:
+ PathVector _pathset;
+public:
+ /// Create a builder that outputs to an internal pathvector.
+ PathBuilder() : PathIteratorSink<SubpathInserter>(SubpathInserter(_pathset)) {}
+ /// Create a builder that outputs to pathvector given by reference.
+ PathBuilder(PathVector &pv) : PathIteratorSink<SubpathInserter>(SubpathInserter(pv)) {}
+
+ /// Retrieve the path
+ PathVector const &peek() const {return _pathset;}
+ /// Clear the stored path vector
+ void clear() { _pathset.clear(); }
+};
+
+}
+
+#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/include/2geom/path.h b/include/2geom/path.h
new file mode 100644
index 0000000..f3042d7
--- /dev/null
+++ b/include/2geom/path.h
@@ -0,0 +1,917 @@
+/** @file
+ * @brief Path - a sequence of contiguous curves
+ *//*
+ * 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 <cstddef>
+#include <iterator>
+#include <algorithm>
+#include <iostream>
+#include <memory>
+#include <optional>
+#include <utility>
+#include <vector>
+
+#include <boost/operators.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+
+#include <2geom/intersection.h>
+#include <2geom/curve.h>
+#include <2geom/bezier-curve.h>
+#include <2geom/transforms.h>
+
+namespace Geom {
+
+class Path;
+class ConvexHull;
+
+namespace PathInternal {
+
+typedef boost::ptr_vector<Curve> Sequence;
+
+struct PathData {
+ Sequence curves;
+ OptRect fast_bounds;
+};
+
+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;
+
+ public:
+ BaseIterator() : path(NULL), index(0) {}
+
+ 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];
+ }
+
+ 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;
+ }
+ std::ptrdiff_t operator-(Self const &other) const {
+ assert(path == other.path);
+ return (std::ptrdiff_t)index - (std::ptrdiff_t)other.index;
+ }
+
+ private:
+ P *path;
+ unsigned index;
+
+ friend class ::Geom::Path;
+};
+
+}
+
+/** @brief 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 PathTime
+ : boost::totally_ordered<PathTime>
+{
+ typedef PathInternal::Sequence::size_type size_type;
+
+ Coord t; ///< Time value in the curve
+ size_type curve_index; ///< Index of the curve in the path
+
+ PathTime() : t(0), curve_index(0) {}
+ PathTime(size_type idx, Coord tval) : t(tval), curve_index(idx) {}
+
+ bool operator<(PathTime 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==(PathTime const &other) const {
+ return curve_index == other.curve_index && t == other.t;
+ }
+ /// Convert times 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 times 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;
+ }
+ }
+
+ Coord asFlatTime() const { return curve_index + t; }
+};
+
+inline std::ostream &operator<<(std::ostream &os, PathTime const &pos) {
+ os << pos.curve_index << ": " << format_coord_nice(pos.t);
+ return os;
+}
+
+
+/** @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 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 time
+ * @param to Final time
+ * @param cross_start If true, the interval will proceed from the initial to final
+ * time 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(PathTime const &from, PathTime const &to, bool cross_start, size_type path_size);
+
+ /// Get the time value of the initial point.
+ PathTime const &initialTime() const { return _from; }
+ /// Get the time value of the final point.
+ PathTime const &finalTime() const { return _to; }
+
+ PathTime const &from() const { return _from; }
+ PathTime 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 time for inclusion.
+ bool contains(PathTime const &pos) const;
+
+ /// Get a time at least @a min_dist away in parameter space from the ends.
+ /// If no such time exists, the middle point is returned.
+ PathTime inside(Coord min_dist = EPSILON) const;
+
+ /// Select one of two intervals with given endpoints by parameter direction.
+ static PathInterval from_direction(PathTime const &from, PathTime 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(PathTime const &from, PathTime 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; }
+ size_type curveCount() const;
+
+private:
+ PathTime _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(PathTime const &from, PathTime 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(PathTime const &from, PathTime const &to,
+ PathInterval::size_type path_size)
+{
+ PathInterval result = PathInterval::from_direction(from, to, true, path_size);
+ return result;
+}
+
+/// 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<PathTime> PathIntersection;
+
+template <>
+struct ShapeTraits<Path> {
+ typedef PathTime TimeType;
+ typedef PathInterval IntervalType;
+ typedef Path AffineClosureType;
+ typedef PathIntersection IntersectionType;
+};
+
+/** @brief Stores information about the extremum points on a path, with respect
+ * to one of the coordinate axes.
+ * @relates Path::extrema()
+ */
+struct PathExtrema {
+ /** Points with the minimum and maximum value of a coordinate. */
+ Point min_point, max_point;
+
+ /** Directions in which the OTHER coordinates change at the extremum points.
+ *
+ * - equals +1.0 if the other coordinate increases,
+ * - equals 0.0 if the other coordinate is constant (e.g., for an axis-aligned segment),
+ * - equals -1.0 if the other coordinate decreases.
+ */
+ float glance_direction_at_min, glance_direction_at_max;
+
+ /** Path times corresponding to minimum and maximum points. */
+ PathTime min_time, max_time;
+};
+
+/** @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 final linear closing segment.
+ *
+ * If the final point of the last "real" segment coincides exactly with the initial
+ * point of the first segment, the closing segment will be absent from both
+ * [begin(), end_open()) and [begin(), end_closed()).
+ *
+ * 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.
+ *
+ * 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_comparable< Path >
+{
+public:
+ typedef PathInternal::PathData PathData;
+ 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) {}
+ Curve *duplicate() const override { return new ClosingSegment(*this); }
+ Curve *reverse() const override { return new ClosingSegment((*this)[1], (*this)[0]); }
+ };
+
+ class StitchSegment : public LineSegment {
+ public:
+ StitchSegment() : LineSegment() {}
+ StitchSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
+ Curve *duplicate() const override { return new StitchSegment(*this); }
+ Curve *reverse() const override { 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())
+ : _data(new PathData())
+ , _closing_seg(new ClosingSegment(p, p))
+ , _closed(false)
+ , _exception_on_stitch(true)
+ {
+ _data->curves.push_back(_closing_seg);
+ }
+
+ /// Construct a path containing a range of curves.
+ template <typename Iter>
+ Path(Iter first, Iter last, bool closed = false, bool stitch = false)
+ : _data(new PathData())
+ , _closed(closed)
+ , _exception_on_stitch(!stitch)
+ {
+ for (Iter i = first; i != last; ++i) {
+ _data->curves.push_back(i->duplicate());
+ }
+ if (!_data->curves.empty()) {
+ _closing_seg = new ClosingSegment(_data->curves.back().finalPoint(),
+ _data->curves.front().initialPoint());
+ } else {
+ _closing_seg = new ClosingSegment();
+ }
+ _data->curves.push_back(_closing_seg);
+ }
+
+ /// Create a path from a rectangle.
+ explicit Path(Rect const &r);
+ /// Create a path from a convex hull.
+ explicit Path(ConvexHull const &);
+ /// Create a path from a circle, using two elliptical arcs.
+ explicit Path(Circle const &c);
+ /// Create a path from an ellipse, using two elliptical arcs.
+ explicit Path(Ellipse const &e);
+
+ 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) noexcept {
+ using std::swap;
+ swap(other._data, _data);
+ swap(other._closing_seg, _closing_seg);
+ swap(other._closed, _closed);
+ swap(other._exception_on_stitch, _exception_on_stitch);
+ }
+ /** @brief Swap contents of two paths.
+ * @relates Path */
+ friend inline void swap(Path &a, Path &b) noexcept { a.swap(b); }
+
+ /** @brief Access a curve by index */
+ Curve const &operator[](size_type i) const { return _data->curves[i]; }
+ /** @brief Access a curve by index */
+ Curve const &at(size_type i) const { return _data->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 _data->curves.front(); }
+ /// Alias for front().
+ Curve const &initialCurve() const { return _data->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 _data->curves.back();
+ return _data->curves[_data->curves.size() - 2];
+ }
+ Curve const &back_closed() const {
+ return _closing_seg->isDegenerate()
+ ? _data->curves[_data->curves.size() - 2]
+ : _data->curves[_data->curves.size() - 1];
+ }
+ Curve const &back_default() const {
+ return _includesClosingSegment()
+ ? back_closed()
+ : back_open();
+ }
+ Curve const &finalCurve() const { return back_default(); }
+
+ 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 _data->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() ? _data->curves.size() - 1 : _data->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 _data->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 (_data->curves.size() == 1); }
+
+ /// Check whether the path is closed.
+ bool closed() const { return _closed; }
+
+ /** @brief Set whether the path is closed.
+ * When closing a path where the last segment can be represented as a closing
+ * segment, the last segment will be removed. When opening a path, the closing
+ * segment will be erased. This means that closing and then opening a path
+ * will not always give back the original path. */
+ void close(bool closed = true);
+
+ /** @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 a transform to each curve.
+ template <typename T>
+ Path &operator*=(T const &tr) {
+ BOOST_CONCEPT_ASSERT((TransformConcept<T>));
+ _unshare();
+ for (std::size_t i = 0; i < _data->curves.size(); ++i) {
+ _data->curves[i] *= tr;
+ }
+ return *this;
+ }
+
+ template <typename T>
+ friend Path operator*(Path const &path, T const &tr) {
+ BOOST_CONCEPT_ASSERT((TransformConcept<T>));
+ Path result(path);
+ result *= tr;
+ return result;
+ }
+
+ /** @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 PathTime 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 path time.
+ Curve const &curveAt(PathTime const &pos) const;
+ /// Get the point at the specified path time.
+ Point pointAt(PathTime const &pos) const;
+ /// Get one coordinate at the specified path time.
+ Coord valueAt(PathTime const &pos, Dim2 d) const;
+
+ Point operator()(Coord t) const { return pointAt(t); }
+
+ /** @brief Find the extrema of the specified coordinate.
+ *
+ * Returns a PathExtrema struct describing "witness" points on the path
+ * where the specified coordinate attains its minimum and maximum values.
+ */
+ PathExtrema extrema(Dim2 d) const;
+
+ /// Compute intersections with axis-aligned line.
+ std::vector<PathTime> roots(Coord v, Dim2 d) const;
+
+ /// Compute intersections with another path.
+ std::vector<PathIntersection> intersect(Path const &other, Coord precision = EPSILON) const;
+
+ /// Compute intersections of the path with itself.
+ std::vector<PathIntersection> intersectSelf(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());
+ }
+
+ PathTime nearestTime(Point const &p, Coord *dist = NULL) const;
+ std::vector<Coord> nearestTimePerCurve(Point const &p) const;
+
+ std::vector<Point> nodes() 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, PathTime const &from, PathTime const &to, bool cross_start = false) const {
+ PathInterval ival(from, to, cross_start, size_closed());
+ appendPortionTo(p, ival, std::nullopt, std::nullopt);
+ }
+
+ /** @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, std::nullopt, std::nullopt);
+ }
+
+ /** @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,
+ std::optional<Point> const &p_from, std::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(PathTime const &from, PathTime 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]; }
+
+ /** @brief Get the unit tangent vector at the start of the path,
+ * or the zero vector if undefined. */
+ Point initialUnitTangent() const {
+ for (auto const &curve : *this) {
+ if (!curve.isDegenerate()) {
+ return curve.unitTangentAt(0.0);
+ }
+ }
+ return Point();
+ }
+
+ /** @brief Get the unit tangent vector at the end of the path,
+ * or the zero vector if undefined. */
+ Point finalUnitTangent() const {
+ for (auto it = end(); it != begin();) {
+ --it;
+ if (!it->isDegenerate()) {
+ return it->unitTangentAt(1.0);
+ }
+ }
+ return Point();
+ }
+
+ void setInitial(Point const &p) {
+ _unshare();
+ _closed = false;
+ _data->curves.front().setInitial(p);
+ _closing_seg->setFinal(p);
+ }
+ void setFinal(Point const &p) {
+ _unshare();
+ _closed = false;
+ _data->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 automatically 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... Args>
+ void appendNew(Args&&... args) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), std::forward<Args>(args)...));
+ }
+
+ /** @brief Reduce the closing segment to a point if it's shorter than precision.
+ * Do this by moving the final point. */
+ void snapEnds(Coord precision = EPSILON);
+
+ /// Append a stitching segment ending at the specified point.
+ void stitchTo(Point const &p);
+
+ /** @brief Return a copy of the path without degenerate curves, except possibly for a
+ * degenerate closing segment. */
+ Path withoutDegenerateCurves() const;
+
+ /** @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->_data->curves.begin() + iter.index;
+ }
+ static Sequence::const_iterator seq_iter(const_iterator const &iter) {
+ return iter.path->_data->curves.begin() + iter.index;
+ }
+
+ // whether the closing segment is part of the path
+ bool _includesClosingSegment() const {
+ return _closed && !_closing_seg->isDegenerate();
+ }
+ void _unshare() {
+ // Called before every mutation.
+ // Ensure we have our own copy of curve data and reset cached values
+ if (!_data.unique()) {
+ _data.reset(new PathData(*_data));
+ _closing_seg = static_cast<ClosingSegment*>(&_data->curves.back());
+ }
+ _data->fast_bounds = OptRect();
+ }
+ PathTime _factorTime(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);
+
+ std::shared_ptr<PathData> _data;
+ 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) {
+ PathTime pt = c.nearestTime(p);
+ return pt.curve_index + pt.t;
+}
+
+bool are_near(Path const &a, Path const &b, Coord precision = EPSILON);
+
+/**
+ * @brief Find the first point where two paths diverge away from one another.
+ *
+ * If the two paths have a common starting point, the algorithm follows them for as long as the
+ * images of the paths coincide and finds the first point where they stop coinciding. Note that
+ * only the images of paths in the plane are compared, and not their parametrizations, so this
+ * is not a functional (parametric) coincidence. If you want to test parametric coincidence, use
+ * bool are_near(Path const&, Path const&, Coord) instead.
+ *
+ * The function returns the point where the traces of the two paths finally diverge up to the
+ * specified precision. If the traces (images) of the paths are nearly identical until the end,
+ * the returned point is their (almost) common endpoint. If however the image of one of the paths
+ * is completely contained in the image of the other path, the returned point is the endpoint of
+ * the shorter path.
+ *
+ * If the paths have different starting points, then the returned intersection has the special
+ * time values of -1.0 on both paths and the returned intersection point is the midpoint of the
+ * line segment connecting the two starting points.
+ *
+ * @param first The first path to follow; corresponds to .first in the return value.
+ * @param second The second path to follow; corresponds to .second in the return value.
+ * @param precision How close the paths' images need to be in order to be considered as overlapping.
+ * @return A path intersection specifying the point and path times where the two paths part ways.
+ */
+PathIntersection parting_point(Path const &first, Path const &second, Coord precision = EPSILON);
+
+std::ostream &operator<<(std::ostream &out, Path const &path);
+
+} // end namespace Geom
+
+
+#endif // LIB2GEOM_SEEN_PATH_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/pathvector.h b/include/2geom/pathvector.h
new file mode 100644
index 0000000..3fd7d36
--- /dev/null
+++ b/include/2geom/pathvector.h
@@ -0,0 +1,304 @@
+/** @file
+ * @brief PathVector - a sequence of subpaths
+ *//*
+ * Authors:
+ * Johan Engelen <j.b.c.engelen@alumnus.utwente.nl>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * 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
+ * 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_PATHVECTOR_H
+#define LIB2GEOM_SEEN_PATHVECTOR_H
+
+#include <optional>
+#include <boost/concept/requires.hpp>
+#include <boost/range/algorithm/equal.hpp>
+#include <2geom/forward.h>
+#include <2geom/path.h>
+#include <2geom/transforms.h>
+
+namespace Geom {
+
+/** @brief 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 PathVectorTime
+ : public PathTime
+ , boost::totally_ordered<PathVectorTime>
+{
+ size_type path_index; ///< Index of the path in the vector
+
+ PathVectorTime() : PathTime(0, 0), path_index(0) {}
+ PathVectorTime(size_type _i, size_type _c, Coord _t)
+ : PathTime(_c, _t), path_index(_i) {}
+ PathVectorTime(size_type _i, PathTime const &pos)
+ : PathTime(pos), path_index(_i) {}
+
+ bool operator<(PathVectorTime const &other) const {
+ if (path_index < other.path_index) return true;
+ if (path_index == other.path_index) {
+ return static_cast<PathTime const &>(*this) < static_cast<PathTime const &>(other);
+ }
+ return false;
+ }
+ bool operator==(PathVectorTime const &other) const {
+ return path_index == other.path_index
+ && static_cast<PathTime const &>(*this) == static_cast<PathTime const &>(other);
+ }
+
+ PathTime const &asPathTime() const {
+ return *static_cast<PathTime const *>(this);
+ }
+};
+
+inline std::ostream &operator<<(std::ostream &os, PathVectorTime const &pvt) {
+ os << pvt.path_index << ": " << pvt.asPathTime();
+ return os;
+}
+
+typedef Intersection<PathVectorTime> PathVectorIntersection;
+typedef PathVectorIntersection PVIntersection; ///< Alias to save typing
+
+template <>
+struct ShapeTraits<PathVector> {
+ typedef PathVectorTime TimeType;
+ //typedef PathVectorInterval IntervalType;
+ typedef PathVector AffineClosureType;
+ typedef PathVectorIntersection IntersectionType;
+};
+
+/** @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::equality_comparable< PathVector
+ > > > > > > > >
+{
+ typedef std::vector<Path> Sequence;
+public:
+ typedef PathVectorTime 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();
+ }
+ /** @brief Get all intersections of the path-vector with itself. This includes both
+ * self-intersections of constituent paths and intersections between different paths. */
+ std::vector<PathVectorIntersection> intersectSelf(Coord precision = EPSILON) const;
+ 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(PathVectorTime const &pos) {
+ return const_cast<Path &>(static_cast<PathVector const*>(this)->pathAt(pos));
+ }
+ Path const &pathAt(PathVectorTime const &pos) const {
+ return at(pos.path_index);
+ }
+ Curve const &curveAt(PathVectorTime const &pos) const {
+ return at(pos.path_index).at(pos.curve_index);
+ }
+ Point pointAt(PathVectorTime const &pos) const {
+ return at(pos.path_index).at(pos.curve_index).pointAt(pos.t);
+ }
+ Coord valueAt(PathVectorTime 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 (auto & i : *this) {
+ i *= t;
+ }
+ return *this;
+ }
+
+ bool operator==(PathVector const &other) const {
+ return boost::range::equal(_data, other._data);
+ }
+
+ void snapEnds(Coord precision = EPSILON);
+
+ 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;
+
+ std::optional<PathVectorTime> nearestTime(Point const &p, Coord *dist = NULL) const;
+ std::vector<PathVectorTime> allNearestTimes(Point const &p, Coord *dist = NULL) const;
+
+ std::vector<Point> nodes() const;
+
+private:
+ PathVectorTime _factorTime(Coord t) const;
+
+ Sequence _data;
+};
+
+inline OptRect bounds_fast(PathVector const &pv) { return pv.boundsFast(); }
+inline OptRect bounds_exact(PathVector const &pv) { return pv.boundsExact(); }
+
+std::ostream &operator<<(std::ostream &out, PathVector const &pv);
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_PATHVECTOR_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/include/2geom/piecewise.h b/include/2geom/piecewise.h
new file mode 100644
index 0000000..e34df15
--- /dev/null
+++ b/include/2geom/piecewise.h
@@ -0,0 +1,945 @@
+/** @file
+ * @brief Piecewise function class
+ *//*
+ * Copyright 2007 Michael 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, 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.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_PIECEWISE_H
+#define LIB2GEOM_SEEN_PIECEWISE_H
+
+#include <vector>
+#include <map>
+#include <utility>
+#include <boost/concept_check.hpp>
+#include <2geom/concepts.h>
+#include <2geom/math-utils.h>
+#include <2geom/sbasis.h>
+
+
+namespace Geom {
+/**
+ * @brief Function defined as discrete pieces.
+ *
+ * The Piecewise class manages a sequence of elements of a type as segments and
+ * the ’cuts’ between them. These cuts are time values which separate the pieces.
+ * This function representation allows for more interesting functions, as it provides
+ * a viable output for operations such as inversion, which may require multiple
+ * SBasis to properly invert the original.
+ *
+ * As for technical details, while the actual SBasis segments begin on the first
+ * cut and end on the last, the function is defined throughout all inputs by ex-
+ * tending the first and last segments. The exact switching between segments is
+ * arbitrarily such that beginnings (t=0) have preference over endings (t=1). This
+ * only matters if it is discontinuous at the location.
+ * \f[
+ * f(t) \rightarrow \left\{
+ * \begin{array}{cc}
+ * s_1,& t <= c_2 \\
+ * s_2,& c_2 <= t <= c_3\\
+ * \ldots \\
+ * s_n,& c_n <= t
+ * \end{array}\right.
+ * \f]
+ *
+ * @ingroup Fragments
+ */
+template <typename T>
+class Piecewise {
+ BOOST_CLASS_REQUIRE(T, Geom, FragmentConcept);
+
+ public:
+ std::vector<double> cuts;
+ std::vector<T> segs;
+ //segs[i] stretches from cuts[i] to cuts[i+1].
+
+ Piecewise() {}
+
+ explicit Piecewise(const T &s) {
+ push_cut(0.);
+ push_seg(s);
+ push_cut(1.);
+ }
+
+ unsigned input_dim(){return 1;}
+
+ typedef typename T::output_type output_type;
+
+ explicit Piecewise(const output_type & v) {
+ push_cut(0.);
+ push_seg(T(v));
+ push_cut(1.);
+ }
+
+ inline void reserve(unsigned i) { segs.reserve(i); cuts.reserve(i + 1); }
+
+ inline T const& operator[](unsigned i) const { return segs[i]; }
+ inline T& operator[](unsigned i) { return segs[i]; }
+ inline output_type operator()(double t) const { return valueAt(t); }
+ inline output_type valueAt(double t) const {
+ unsigned n = segN(t);
+ return segs[n](segT(t, n));
+ }
+ inline output_type firstValue() const {
+ return valueAt(cuts.front());
+ }
+ inline output_type lastValue() const {
+ return valueAt(cuts.back());
+ }
+
+ /**
+ * The size of the returned vector equals n_derivs+1.
+ */
+ std::vector<output_type> valueAndDerivatives(double t, unsigned n_derivs) const {
+ unsigned n = segN(t);
+ std::vector<output_type> ret, val = segs[n].valueAndDerivatives(segT(t, n), n_derivs);
+ double mult = 1;
+ for(unsigned i = 0; i < val.size(); i++) {
+ ret.push_back(val[i] * mult);
+ mult /= cuts[n+1] - cuts[n];
+ }
+ return ret;
+ }
+
+ //TODO: maybe it is not a good idea to have this?
+ Piecewise<T> operator()(SBasis f);
+ Piecewise<T> operator()(Piecewise<SBasis>f);
+
+ inline unsigned size() const { return segs.size(); }
+ inline bool empty() const { return segs.empty(); }
+ inline void clear() {
+ segs.clear();
+ cuts.clear();
+ }
+
+ /**Convenience/implementation hiding function to add segment/cut pairs.
+ * Asserts that basic size and order invariants are correct
+ */
+ inline void push(const T &s, double to) {
+ assert(cuts.size() - segs.size() == 1);
+ push_seg(s);
+ push_cut(to);
+ }
+ inline void push(T &&s, double to) {
+ assert(cuts.size() - segs.size() == 1);
+ push_seg(s);
+ push_cut(to);
+ }
+ //Convenience/implementation hiding function to add cuts.
+ inline void push_cut(double c) {
+ ASSERT_INVARIANTS(cuts.empty() || c > cuts.back());
+ cuts.push_back(c);
+ }
+ //Convenience/implementation hiding function to add segments.
+ inline void push_seg(const T &s) { segs.push_back(s); }
+ inline void push_seg(T &&s) { segs.emplace_back(s); }
+
+ /**Returns the segment index which corresponds to a 'global' piecewise time.
+ * Also takes optional low/high parameters to expedite the search for the segment.
+ */
+ inline unsigned segN(double t, int low = 0, int high = -1) const {
+ high = (high == -1) ? size() : high;
+ if(t < cuts[0]) return 0;
+ if(t >= cuts[size()]) return size() - 1;
+ while(low < high) {
+ int mid = (high + low) / 2; //Lets not plan on having huge (> INT_MAX / 2) cut sequences
+ double mv = cuts[mid];
+ if(mv < t) {
+ if(t < cuts[mid + 1]) return mid; else low = mid + 1;
+ } else if(t < mv) {
+ if(cuts[mid - 1] < t) return mid - 1; else high = mid - 1;
+ } else {
+ return mid;
+ }
+ }
+ return low;
+ }
+
+ /**Returns the time within a segment, given the 'global' piecewise time.
+ * Also takes an optional index parameter which may be used for efficiency or to find the time on a
+ * segment outside its range. If it is left to its default, -1, it will call segN to find the index.
+ */
+ inline double segT(double t, int i = -1) const {
+ if(i == -1) i = segN(t);
+ assert(i >= 0);
+ return (t - cuts[i]) / (cuts[i+1] - cuts[i]);
+ }
+
+ inline double mapToDomain(double t, unsigned i) const {
+ return (1-t)*cuts[i] + t*cuts[i+1]; //same as: t * (cuts[i+1] - cuts[i]) + cuts[i]
+ }
+
+ //Offsets the piecewise domain
+ inline void offsetDomain(double o) {
+ assert(std::isfinite(o));
+ if(o != 0)
+ for(unsigned i = 0; i <= size(); i++)
+ cuts[i] += o;
+ }
+
+ //Scales the domain of the function by a value. 0 will result in an empty Piecewise.
+ inline void scaleDomain(double s) {
+ assert(s > 0);
+ if(s == 0) {
+ cuts.clear(); segs.clear();
+ return;
+ }
+ for(unsigned i = 0; i <= size(); i++)
+ cuts[i] *= s;
+ }
+
+ //Retrieves the domain in interval form
+ inline Interval domain() const { return Interval(cuts.front(), cuts.back()); }
+
+ //Transforms the domain into another interval
+ inline void setDomain(Interval dom) {
+ if(empty()) return;
+ /* dom can not be empty
+ if(dom.empty()) {
+ cuts.clear(); segs.clear();
+ return;
+ }*/
+ double cf = cuts.front();
+ double o = dom.min() - cf, s = dom.extent() / (cuts.back() - cf);
+ for(unsigned i = 0; i <= size(); i++)
+ cuts[i] = (cuts[i] - cf) * s + o;
+ //fix floating point precision errors.
+ cuts[0] = dom.min();
+ cuts[size()] = dom.max();
+ }
+
+ //Concatenates this Piecewise function with another, offsetting time of the other to match the end.
+ inline void concat(const Piecewise<T> &other) {
+ if(other.empty()) return;
+
+ if(empty()) {
+ cuts = other.cuts; segs = other.segs;
+ return;
+ }
+
+ segs.insert(segs.end(), other.segs.begin(), other.segs.end());
+ double t = cuts.back() - other.cuts.front();
+ cuts.reserve(cuts.size() + other.size());
+ for(unsigned i = 0; i < other.size(); i++)
+ push_cut(other.cuts[i + 1] + t);
+ }
+
+ //Like concat, but ensures continuity.
+ inline void continuousConcat(const Piecewise<T> &other) {
+ boost::function_requires<AddableConcept<typename T::output_type> >();
+ if(other.empty()) return;
+
+ if(empty()) { segs = other.segs; cuts = other.cuts; return; }
+
+ typename T::output_type y = segs.back().at1() - other.segs.front().at0();
+ double t = cuts.back() - other.cuts.front();
+ reserve(size() + other.size());
+ for(unsigned i = 0; i < other.size(); i++)
+ push(other[i] + y, other.cuts[i + 1] + t);
+ }
+
+ //returns true if the Piecewise<T> meets some basic invariants.
+ inline bool invariants() const {
+ // segs between cuts
+ if(!(segs.size() + 1 == cuts.size() || (segs.empty() && cuts.empty())))
+ return false;
+ // cuts in order
+ for(unsigned i = 0; i < segs.size(); i++)
+ if(cuts[i] >= cuts[i+1])
+ return false;
+ return true;
+ }
+
+};
+
+/**
+ * ...
+ * \return ...
+ * \relates Piecewise
+ */
+template<typename T>
+inline typename FragmentConcept<T>::BoundsType bounds_fast(const Piecewise<T> &f) {
+ boost::function_requires<FragmentConcept<T> >();
+
+ if(f.empty()) return typename FragmentConcept<T>::BoundsType();
+ typename FragmentConcept<T>::BoundsType ret(bounds_fast(f[0]));
+ for(unsigned i = 1; i < f.size(); i++)
+ ret.unionWith(bounds_fast(f[i]));
+ return ret;
+}
+
+/**
+ * ...
+ * \return ...
+ * \relates Piecewise
+ */
+template<typename T>
+inline typename FragmentConcept<T>::BoundsType bounds_exact(const Piecewise<T> &f) {
+ boost::function_requires<FragmentConcept<T> >();
+
+ if(f.empty()) return typename FragmentConcept<T>::BoundsType();
+ typename FragmentConcept<T>::BoundsType ret(bounds_exact(f[0]));
+ for(unsigned i = 1; i < f.size(); i++)
+ ret.unionWith(bounds_exact(f[i]));
+ return ret;
+}
+
+/**
+ * ...
+ * \return ...
+ * \relates Piecewise
+ */
+template<typename T>
+inline typename FragmentConcept<T>::BoundsType bounds_local(const Piecewise<T> &f, const OptInterval &_m) {
+ boost::function_requires<FragmentConcept<T> >();
+
+ if(f.empty() || !_m) return typename FragmentConcept<T>::BoundsType();
+ Interval const &m = *_m;
+ if(m.isSingular()) return typename FragmentConcept<T>::BoundsType(f(m.min()));
+
+ unsigned fi = f.segN(m.min()), ti = f.segN(m.max());
+ double ft = f.segT(m.min(), fi), tt = f.segT(m.max(), ti);
+
+ if(fi == ti) return bounds_local(f[fi], Interval(ft, tt));
+
+ typename FragmentConcept<T>::BoundsType ret(bounds_local(f[fi], Interval(ft, 1.)));
+ for(unsigned i = fi + 1; i < ti; i++)
+ ret.unionWith(bounds_exact(f[i]));
+ if(tt != 0.) ret.unionWith(bounds_local(f[ti], Interval(0., tt)));
+
+ return ret;
+}
+
+/**
+ * Returns a portion of a piece of a Piecewise<T>, given the piece's index and a to/from time.
+ * \relates Piecewise
+ */
+template<typename T>
+T elem_portion(const Piecewise<T> &a, unsigned i, double from, double to) {
+ assert(i < a.size());
+ double rwidth = 1 / (a.cuts[i+1] - a.cuts[i]);
+ return portion( a[i], (from - a.cuts[i]) * rwidth, (to - a.cuts[i]) * rwidth );
+}
+
+/**Piecewise<T> partition(const Piecewise<T> &pw, std::vector<double> const &c);
+ * Further subdivides the Piecewise<T> such that there is a cut at every value in c.
+ * Precondition: c sorted lower to higher.
+ *
+ * //Given Piecewise<T> a and b:
+ * Piecewise<T> ac = a.partition(b.cuts);
+ * Piecewise<T> bc = b.partition(a.cuts);
+ * //ac.cuts should be equivalent to bc.cuts
+ *
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> partition(const Piecewise<T> &pw, std::vector<double> const &c) {
+ assert(pw.invariants());
+ if(c.empty()) return Piecewise<T>(pw);
+
+ Piecewise<T> ret = Piecewise<T>();
+ ret.reserve(c.size() + pw.cuts.size() - 1);
+
+ if(pw.empty()) {
+ ret.cuts = c;
+ for(unsigned i = 0; i < c.size() - 1; i++)
+ ret.push_seg(T());
+ return ret;
+ }
+
+ unsigned si = 0, ci = 0; //Segment index, Cut index
+
+ //if the cuts have something earlier than the Piecewise<T>, add portions of the first segment
+ while(ci < c.size() && c[ci] < pw.cuts.front()) {
+ bool isLast = (ci == c.size()-1 || c[ci + 1] >= pw.cuts.front());
+ ret.push_cut(c[ci]);
+ ret.push_seg( elem_portion(pw, 0, c[ci], isLast ? pw.cuts.front() : c[ci + 1]) );
+ ci++;
+ }
+
+ ret.push_cut(pw.cuts[0]);
+ double prev = pw.cuts[0]; //previous cut
+ //Loop which handles cuts within the Piecewise<T> domain
+ //Should have the cuts = segs + 1 invariant
+ while(si < pw.size() && ci <= c.size()) {
+ if(ci == c.size() && prev <= pw.cuts[si]) { //cuts exhausted, straight copy the rest
+ ret.segs.insert(ret.segs.end(), pw.segs.begin() + si, pw.segs.end());
+ ret.cuts.insert(ret.cuts.end(), pw.cuts.begin() + si + 1, pw.cuts.end());
+ return ret;
+ }else if(ci == c.size() || c[ci] >= pw.cuts[si + 1]) { //no more cuts within this segment, finalize
+ if(prev > pw.cuts[si]) { //segment already has cuts, so portion is required
+ ret.push_seg(portion(pw[si], pw.segT(prev, si), 1.0));
+ } else { //plain copy is fine
+ ret.push_seg(pw[si]);
+ }
+ ret.push_cut(pw.cuts[si + 1]);
+ prev = pw.cuts[si + 1];
+ si++;
+ } else if(c[ci] == pw.cuts[si]){ //coincident
+ //Already finalized the seg with the code immediately above
+ ci++;
+ } else { //plain old subdivision
+ ret.push(elem_portion(pw, si, prev, c[ci]), c[ci]);
+ prev = c[ci];
+ ci++;
+ }
+ }
+
+ //input cuts extend further than this Piecewise<T>, extend the last segment.
+ while(ci < c.size()) {
+ if(c[ci] > prev) {
+ ret.push(elem_portion(pw, pw.size() - 1, prev, c[ci]), c[ci]);
+ prev = c[ci];
+ }
+ ci++;
+ }
+ return ret;
+}
+
+/**
+ * Returns a Piecewise<T> with a defined domain of [min(from, to), max(from, to)].
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> portion(const Piecewise<T> &pw, double from, double to) {
+ if(pw.empty() || from == to) return Piecewise<T>();
+
+ Piecewise<T> ret;
+
+ double temp = from;
+ from = std::min(from, to);
+ to = std::max(temp, to);
+
+ unsigned i = pw.segN(from);
+ ret.push_cut(from);
+ if(i == pw.size() - 1 || to <= pw.cuts[i + 1]) { //to/from inhabit the same segment
+ ret.push(elem_portion(pw, i, from, to), to);
+ return ret;
+ }
+ ret.push_seg(portion( pw[i], pw.segT(from, i), 1.0 ));
+ i++;
+ unsigned fi = pw.segN(to, i);
+ ret.reserve(fi - i + 1);
+ if (to == pw.cuts[fi]) fi-=1;
+
+ ret.segs.insert(ret.segs.end(), pw.segs.begin() + i, pw.segs.begin() + fi); //copy segs
+ ret.cuts.insert(ret.cuts.end(), pw.cuts.begin() + i, pw.cuts.begin() + fi + 1); //and their cuts
+
+ ret.push_seg( portion(pw[fi], 0.0, pw.segT(to, fi)));
+ if(to != ret.cuts.back()) ret.push_cut(to);
+ ret.invariants();
+ return ret;
+}
+
+//TODO: seems like these should be mutating
+/**
+ * ...
+ * \return ...
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> remove_short_cuts(Piecewise<T> const &f, double tol) {
+ if(f.empty()) return f;
+ Piecewise<T> ret;
+ ret.reserve(f.size());
+ ret.push_cut(f.cuts[0]);
+ for(unsigned i=0; i<f.size(); i++){
+ if (f.cuts[i+1]-f.cuts[i] >= tol || i==f.size()-1) {
+ ret.push(f[i], f.cuts[i+1]);
+ }
+ }
+ return ret;
+}
+
+//TODO: seems like these should be mutating
+/**
+ * ...
+ * \return ...
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> remove_short_cuts_extending(Piecewise<T> const &f, double tol) {
+ if(f.empty()) return f;
+ Piecewise<T> ret;
+ ret.reserve(f.size());
+ ret.push_cut(f.cuts[0]);
+ double last = f.cuts[0]; // last cut included
+ for(unsigned i=0; i<f.size(); i++){
+ if (f.cuts[i+1]-f.cuts[i] >= tol) {
+ ret.push(elem_portion(f, i, last, f.cuts[i+1]), f.cuts[i+1]);
+ last = f.cuts[i+1];
+ }
+ }
+ return ret;
+}
+
+/**
+ * ...
+ * \return ...
+ * \relates Piecewise
+ */
+template<typename T>
+std::vector<double> roots(const Piecewise<T> &pw) {
+ std::vector<double> ret;
+ for(unsigned i = 0; i < pw.size(); i++) {
+ std::vector<double> sr = roots(pw[i]);
+ for (double & j : sr) ret.push_back(j * (pw.cuts[i + 1] - pw.cuts[i]) + pw.cuts[i]);
+
+ }
+ return ret;
+}
+
+//IMPL: OffsetableConcept
+/**
+ * ...
+ * \return \f$ a + b = \f$
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> operator+(Piecewise<T> const &a, typename T::output_type b) {
+ boost::function_requires<OffsetableConcept<T> >();
+//TODO:empty
+ Piecewise<T> ret;
+ ret.segs.reserve(a.size());
+ ret.cuts = a.cuts;
+ for(unsigned i = 0; i < a.size();i++)
+ ret.push_seg(a[i] + b);
+ return ret;
+}
+template<typename T>
+Piecewise<T> operator-(Piecewise<T> const &a, typename T::output_type b) {
+ boost::function_requires<OffsetableConcept<T> >();
+//TODO: empty
+ Piecewise<T> ret;
+ ret.segs.reserve(a.size());
+ ret.cuts = a.cuts;
+ for(unsigned i = 0; i < a.size();i++)
+ ret.push_seg(a[i] - b);
+ return ret;
+}
+template<typename T>
+Piecewise<T>& operator+=(Piecewise<T>& a, typename T::output_type b) {
+ boost::function_requires<OffsetableConcept<T> >();
+
+ if(a.empty()) { a.push_cut(0.); a.push(T(b), 1.); return a; }
+
+ for(unsigned i = 0; i < a.size();i++)
+ a[i] += b;
+ return a;
+}
+template<typename T>
+Piecewise<T>& operator-=(Piecewise<T>& a, typename T::output_type b) {
+ boost::function_requires<OffsetableConcept<T> >();
+
+ if(a.empty()) { a.push_cut(0.); a.push(T(-b), 1.); return a; }
+
+ for(unsigned i = 0;i < a.size();i++)
+ a[i] -= b;
+ return a;
+}
+
+//IMPL: ScalableConcept
+/**
+ * ...
+ * \return \f$ -a = \f$
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> operator-(Piecewise<T> const &a) {
+ boost::function_requires<ScalableConcept<T> >();
+
+ Piecewise<T> ret;
+ ret.segs.reserve(a.size());
+ ret.cuts = a.cuts;
+ for(unsigned i = 0; i < a.size();i++)
+ ret.push_seg(- a[i]);
+ return ret;
+}
+/**
+ * ...
+ * \return \f$ a * b = \f$
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> operator*(Piecewise<T> const &a, double b) {
+ boost::function_requires<ScalableConcept<T> >();
+
+ if(a.empty()) return Piecewise<T>();
+
+ Piecewise<T> ret;
+ ret.segs.reserve(a.size());
+ ret.cuts = a.cuts;
+ for(unsigned i = 0; i < a.size();i++)
+ ret.push_seg(a[i] * b);
+ return ret;
+}
+/**
+ * ...
+ * \return \f$ a * b = \f$
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> operator*(Piecewise<T> const &a, T b) {
+ boost::function_requires<ScalableConcept<T> >();
+
+ if(a.empty()) return Piecewise<T>();
+
+ Piecewise<T> ret;
+ ret.segs.reserve(a.size());
+ ret.cuts = a.cuts;
+ for(unsigned i = 0; i < a.size();i++)
+ ret.push_seg(a[i] * b);
+ return ret;
+}
+/**
+ * ...
+ * \return \f$ a / b = \f$
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> operator/(Piecewise<T> const &a, double b) {
+ boost::function_requires<ScalableConcept<T> >();
+
+ //FIXME: b == 0?
+ if(a.empty()) return Piecewise<T>();
+
+ Piecewise<T> ret;
+ ret.segs.reserve(a.size());
+ ret.cuts = a.cuts;
+ for(unsigned i = 0; i < a.size();i++)
+ ret.push_seg(a[i] / b);
+ return ret;
+}
+template<typename T>
+Piecewise<T>& operator*=(Piecewise<T>& a, double b) {
+ boost::function_requires<ScalableConcept<T> >();
+
+ for(unsigned i = 0; i < a.size();i++)
+ a[i] *= b;
+ return a;
+}
+template<typename T>
+Piecewise<T>& operator/=(Piecewise<T>& a, double b) {
+ boost::function_requires<ScalableConcept<T> >();
+
+ //FIXME: b == 0?
+
+ for(unsigned i = 0; i < a.size();i++)
+ a[i] /= b;
+ return a;
+}
+
+//IMPL: AddableConcept
+/**
+ * ...
+ * \return \f$ a + b = \f$
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> operator+(Piecewise<T> const &a, Piecewise<T> const &b) {
+ boost::function_requires<AddableConcept<T> >();
+
+ Piecewise<T> pa = partition(a, b.cuts), pb = partition(b, a.cuts);
+ Piecewise<T> ret;
+ assert(pa.size() == pb.size());
+ ret.segs.reserve(pa.size());
+ ret.cuts = pa.cuts;
+ for (unsigned i = 0; i < pa.size(); i++)
+ ret.push_seg(pa[i] + pb[i]);
+ return ret;
+}
+/**
+ * ...
+ * \return \f$ a - b = \f$
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> operator-(Piecewise<T> const &a, Piecewise<T> const &b) {
+ boost::function_requires<AddableConcept<T> >();
+
+ Piecewise<T> pa = partition(a, b.cuts), pb = partition(b, a.cuts);
+ Piecewise<T> ret = Piecewise<T>();
+ assert(pa.size() == pb.size());
+ ret.segs.reserve(pa.size());
+ ret.cuts = pa.cuts;
+ for (unsigned i = 0; i < pa.size(); i++)
+ ret.push_seg(pa[i] - pb[i]);
+ return ret;
+}
+template<typename T>
+inline Piecewise<T>& operator+=(Piecewise<T> &a, Piecewise<T> const &b) {
+ a = a+b;
+ return a;
+}
+template<typename T>
+inline Piecewise<T>& operator-=(Piecewise<T> &a, Piecewise<T> const &b) {
+ a = a-b;
+ return a;
+}
+
+/**
+ * ...
+ * \return \f$ a \cdot b = \f$
+ * \relates Piecewise
+ */
+template<typename T1,typename T2>
+Piecewise<T2> operator*(Piecewise<T1> const &a, Piecewise<T2> const &b) {
+ //function_requires<MultiplicableConcept<T1> >();
+ //function_requires<MultiplicableConcept<T2> >();
+
+ Piecewise<T1> pa = partition(a, b.cuts);
+ Piecewise<T2> pb = partition(b, a.cuts);
+ Piecewise<T2> ret = Piecewise<T2>();
+ assert(pa.size() == pb.size());
+ ret.segs.reserve(pa.size());
+ ret.cuts = pa.cuts;
+ for (unsigned i = 0; i < pa.size(); i++)
+ ret.push_seg(pa[i] * pb[i]);
+ return ret;
+}
+
+/**
+ * ...
+ * \return \f$ a \cdot b \f$
+ * \relates Piecewise
+ */
+template<typename T>
+inline Piecewise<T>& operator*=(Piecewise<T> &a, Piecewise<T> const &b) {
+ a = a * b;
+ return a;
+}
+
+Piecewise<SBasis> divide(Piecewise<SBasis> const &a, Piecewise<SBasis> const &b, unsigned k);
+//TODO: replace divide(a,b,k) by divide(a,b,tol,k)?
+//TODO: atm, relative error is ~(tol/a)%. Find a way to make it independent of a.
+//Nota: the result is 'truncated' where b is smaller than 'zero': ~ a/max(b,zero).
+Piecewise<SBasis>
+divide(Piecewise<SBasis> const &a, Piecewise<SBasis> const &b, double tol, unsigned k, double zero=1.e-3);
+Piecewise<SBasis>
+divide(SBasis const &a, Piecewise<SBasis> const &b, double tol, unsigned k, double zero=1.e-3);
+Piecewise<SBasis>
+divide(Piecewise<SBasis> const &a, SBasis const &b, double tol, unsigned k, double zero=1.e-3);
+Piecewise<SBasis>
+divide(SBasis const &a, SBasis const &b, double tol, unsigned k, double zero=1.e-3);
+
+//Composition: functions called compose_* are pieces of compose that are factored out in pw.cpp.
+std::map<double,unsigned> compose_pullback(std::vector<double> const &cuts, SBasis const &g);
+int compose_findSegIdx(std::map<double,unsigned>::iterator const &cut,
+ std::map<double,unsigned>::iterator const &next,
+ std::vector<double> const &levels,
+ SBasis const &g);
+
+/**
+ * ...
+ * \return ...
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> compose(Piecewise<T> const &f, SBasis const &g){
+ /// \todo add concept check
+ Piecewise<T> result;
+ if (f.empty()) return result;
+ if (g.isZero()) return Piecewise<T>(f(0));
+ if (f.size()==1){
+ double t0 = f.cuts[0], width = f.cuts[1] - t0;
+ return (Piecewise<T>) compose(f.segs[0],compose(Linear(-t0 / width, (1-t0) / width), g));
+ }
+
+ //first check bounds...
+ Interval bs = *bounds_fast(g);
+ if (f.cuts.front() > bs.max() || bs.min() > f.cuts.back()){
+ int idx = (bs.max() < f.cuts[1]) ? 0 : f.cuts.size()-2;
+ double t0 = f.cuts[idx], width = f.cuts[idx+1] - t0;
+ return (Piecewise<T>) compose(f.segs[idx],compose(Linear(-t0 / width, (1-t0) / width), g));
+ }
+
+ std::vector<double> levels;//we can forget first and last cuts...
+ levels.insert(levels.begin(),f.cuts.begin()+1,f.cuts.end()-1);
+ //TODO: use a std::vector<pairs<double,unsigned> > instead of a map<double,unsigned>.
+ std::map<double,unsigned> cuts_pb = compose_pullback(levels,g);
+
+ //-- Compose each piece of g with the relevant seg of f.
+ result.cuts.push_back(0.);
+ std::map<double,unsigned>::iterator cut=cuts_pb.begin();
+ std::map<double,unsigned>::iterator next=cut; next++;
+ while(next!=cuts_pb.end()){
+ //assert(std::abs(int((*cut).second-(*next).second))<1);
+ //TODO: find a way to recover from this error? the root finder missed some root;
+ // the levels/variations of f might be too close/fast...
+ int idx = compose_findSegIdx(cut,next,levels,g);
+ double t0=(*cut).first;
+ double t1=(*next).first;
+
+ if (!are_near(t0,t1,EPSILON*EPSILON)) { // prevent adding cuts that are extremely close together and that may cause trouble with rounding e.g. when reversing the path
+ SBasis sub_g=compose(g, Linear(t0,t1));
+ sub_g=compose(Linear(-f.cuts[idx]/(f.cuts[idx+1]-f.cuts[idx]),
+ (1-f.cuts[idx])/(f.cuts[idx+1]-f.cuts[idx])),sub_g);
+ result.push(compose(f[idx],sub_g),t1);
+ }
+
+ cut++;
+ next++;
+ }
+ return(result);
+}
+
+/**
+ * ...
+ * \return ...
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> compose(Piecewise<T> const &f, Piecewise<SBasis> const &g){
+/// \todo add concept check
+ Piecewise<T> result;
+ for(unsigned i = 0; i < g.segs.size(); i++){
+ Piecewise<T> fgi=compose(f, g.segs[i]);
+ fgi.setDomain(Interval(g.cuts[i], g.cuts[i+1]));
+ result.concat(fgi);
+ }
+ return result;
+}
+
+/*
+Piecewise<D2<SBasis> > compose(D2<SBasis2d> const &sb2d, Piecewise<D2<SBasis> > const &pwd2sb){
+/// \todo add concept check
+ Piecewise<D2<SBasis> > result;
+ result.push_cut(0.);
+ for(unsigned i = 0; i < pwd2sb.size(); i++){
+ result.push(compose_each(sb2d,pwd2sb[i]),i+1);
+ }
+ return result;
+}*/
+
+/** Compose an SBasis with the inverse of another.
+ * WARNING: It's up to the user to check that the second SBasis is indeed
+ * invertible (i.e. strictly increasing or decreasing).
+ * \return \f$ f \cdot g^{-1} \f$
+ * \relates Piecewise
+ */
+Piecewise<SBasis> pw_compose_inverse(SBasis const &f, SBasis const &g, unsigned order, double zero);
+
+
+
+template <typename T>
+Piecewise<T> Piecewise<T>::operator()(SBasis f){return compose((*this),f);}
+template <typename T>
+Piecewise<T> Piecewise<T>::operator()(Piecewise<SBasis>f){return compose((*this),f);}
+
+/**
+ * ...
+ * \return ...
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> integral(Piecewise<T> const &a) {
+ Piecewise<T> result;
+ result.segs.resize(a.segs.size());
+ result.cuts = a.cuts;
+ typename T::output_type c = a.segs[0].at0();
+ for(unsigned i = 0; i < a.segs.size(); i++){
+ result.segs[i] = integral(a.segs[i])*(a.cuts[i+1]-a.cuts[i]);
+ result.segs[i]+= c-result.segs[i].at0();
+ c = result.segs[i].at1();
+ }
+ return result;
+}
+
+/**
+ * ...
+ * \return ...
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> derivative(Piecewise<T> const &a) {
+ Piecewise<T> result;
+ result.segs.resize(a.segs.size());
+ result.cuts = a.cuts;
+ for(unsigned i = 0; i < a.segs.size(); i++){
+ result.segs[i] = derivative(a.segs[i])/(a.cuts[i+1]-a.cuts[i]);
+ }
+ return result;
+}
+
+std::vector<double> roots(Piecewise<SBasis> const &f);
+
+std::vector<std::vector<double> >multi_roots(Piecewise<SBasis> const &f, std::vector<double> const &values);
+
+//TODO: implement level_sets directly for pwsb instead of sb (and derive it fo sb).
+//It should be faster than the reverse as the algorithm may jump over full cut intervals.
+std::vector<Interval> level_set(Piecewise<SBasis> const &f, Interval const &level, double tol=1e-5);
+std::vector<Interval> level_set(Piecewise<SBasis> const &f, double v, double vtol, double tol=1e-5);
+//std::vector<Interval> level_sets(Piecewise<SBasis> const &f, std::vector<Interval> const &levels, double tol=1e-5);
+//std::vector<Interval> level_sets(Piecewise<SBasis> const &f, std::vector<double> &v, double vtol, double tol=1e-5);
+
+
+/**
+ * ...
+ * \return ...
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> reverse(Piecewise<T> const &f) {
+ Piecewise<T> ret = Piecewise<T>();
+ ret.reserve(f.size());
+ double start = f.cuts[0];
+ double end = f.cuts.back();
+ for (unsigned i = 0; i < f.cuts.size(); i++) {
+ double x = f.cuts[f.cuts.size() - 1 - i];
+ ret.push_cut(end - (x - start));
+ }
+ for (unsigned i = 0; i < f.segs.size(); i++)
+ ret.push_seg(reverse(f[f.segs.size() - i - 1]));
+ return ret;
+}
+
+/**
+ * Interpolates between a and b.
+ * \return a if t = 0, b if t = 1, or an interpolation between a and b for t in [0,1]
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> lerp(double t, Piecewise<T> const &a, Piecewise<T> b) {
+ // Make sure both paths have the same number of segments and cuts at the same locations
+ b.setDomain(a.domain());
+ Piecewise<T> pA = partition(a, b.cuts);
+ Piecewise<T> pB = partition(b, a.cuts);
+
+ return (pA*(1-t) + pB*t);
+}
+
+}
+#endif //LIB2GEOM_SEEN_PIECEWISE_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/include/2geom/point.h b/include/2geom/point.h
new file mode 100644
index 0000000..3a29066
--- /dev/null
+++ b/include/2geom/point.h
@@ -0,0 +1,449 @@
+/** @file
+ * @brief Cartesian point / 2D vector and related operations
+ *//*
+ * Authors:
+ * Michael G. Sloan <mgsloan@gmail.com>
+ * Nathan Hurst <njh@njhurst.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2006-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 LIB2GEOM_SEEN_POINT_H
+#define LIB2GEOM_SEEN_POINT_H
+
+#include <iostream>
+#include <iterator>
+#include <boost/operators.hpp>
+#include <2geom/forward.h>
+#include <2geom/coord.h>
+#include <2geom/int-point.h>
+#include <2geom/math-utils.h>
+#include <2geom/utils.h>
+
+namespace Geom {
+
+class Point
+ : boost::additive< Point
+ , boost::totally_ordered< Point
+ , boost::multiplicative< Point, Coord
+ , boost::multiplicative< Point
+ , boost::multiplicative< Point, IntPoint
+ , MultipliableNoncommutative< Point, Affine
+ , MultipliableNoncommutative< Point, Translate
+ , MultipliableNoncommutative< Point, Rotate
+ , MultipliableNoncommutative< Point, Scale
+ , MultipliableNoncommutative< Point, HShear
+ , MultipliableNoncommutative< Point, VShear
+ , MultipliableNoncommutative< Point, Zoom
+ > > > > > > > > > > > > // base class chaining, see documentation for Boost.Operator
+{
+ Coord _pt[2] = { 0, 0 };
+public:
+ using D1Value = Coord;
+ using D1Reference = Coord &;
+ using D1ConstReference = Coord const &;
+
+ /// @name Create points
+ /// @{
+ /** Construct a point at the origin. */
+ Point() = default;
+ /** Construct a point from its coordinates. */
+ Point(Coord x, Coord y)
+ : _pt{ x, y }
+ {}
+ /** Construct from integer point. */
+ Point(IntPoint const &p)
+ : Point(p[X], p[Y])
+ {}
+ /** @brief Construct a point from its polar coordinates.
+ * The angle is specified in radians, in the mathematical convention (increasing
+ * counter-clockwise from +X). */
+ static Point polar(Coord angle, Coord radius) {
+ Point ret(polar(angle));
+ ret *= radius;
+ return ret;
+ }
+ /** @brief Construct an unit vector from its angle.
+ * The angle is specified in radians, in the mathematical convention (increasing
+ * counter-clockwise from +X). */
+ static Point polar(Coord angle);
+ /// @}
+
+ /// @name Access the coordinates of a point
+ /// @{
+ Coord operator[](unsigned i) const { return _pt[i]; }
+ Coord &operator[](unsigned i) { return _pt[i]; }
+
+ Coord operator[](Dim2 d) const noexcept { return _pt[d]; }
+ Coord &operator[](Dim2 d) noexcept { return _pt[d]; }
+
+ Coord x() const noexcept { return _pt[X]; }
+ Coord &x() noexcept { return _pt[X]; }
+ Coord y() const noexcept { return _pt[Y]; }
+ Coord &y() noexcept { return _pt[Y]; }
+ /// @}
+
+ /// @name Vector operations
+ /// @{
+ /** @brief Compute the distance from origin.
+ * @return Length of the vector from origin to this point */
+ Coord length() const { return std::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
+ * right, then this is 90 degrees counter-clockwise. */
+ Point ccw() const {
+ return Point(_pt[Y], -_pt[X]);
+ }
+
+ /** @brief Return a point like this point but rotated +90 degrees.
+ * If the y axis grows downwards and the x axis grows to the
+ * right, then this is 90 degrees clockwise. */
+ Point cw() const {
+ return Point(-_pt[Y], _pt[X]);
+ }
+ /// @}
+
+ /// @name Vector-like arithmetic operations
+ /// @{
+ Point operator-() const {
+ return Point(-_pt[X], -_pt[Y]);
+ }
+ Point &operator+=(Point const &o) {
+ _pt[X] += o._pt[X];
+ _pt[Y] += o._pt[Y];
+ return *this;
+ }
+ Point &operator-=(Point const &o) {
+ _pt[X] -= o._pt[X];
+ _pt[Y] -= o._pt[Y];
+ return *this;
+ }
+ Point &operator*=(Coord s) {
+ for (double & i : _pt) i *= s;
+ return *this;
+ }
+ Point &operator*=(Point const &o) {
+ _pt[X] *= o._pt[X];
+ _pt[Y] *= o._pt[Y];
+ return *this;
+ }
+ Point &operator*=(IntPoint const &o) {
+ _pt[X] *= o.x();
+ _pt[Y] *= o.y();
+ return *this;
+ }
+ Point &operator/=(Coord s) {
+ //TODO: s == 0?
+ for (double & i : _pt) i /= s;
+ return *this;
+ }
+ Point &operator/=(Point const &o) {
+ _pt[X] /= o._pt[X];
+ _pt[Y] /= o._pt[Y];
+ return *this;
+ }
+ Point &operator/=(IntPoint const &o) {
+ _pt[X] /= o.x();
+ _pt[Y] /= o.y();
+ return *this;
+ }
+ /// @}
+
+ /// @name Affine transformations
+ /// @{
+ Point &operator*=(Affine const &m);
+ // implemented in transforms.cpp
+ Point &operator*=(Translate const &t);
+ Point &operator*=(Scale const &s);
+ Point &operator*=(Rotate const &r);
+ Point &operator*=(HShear const &s);
+ Point &operator*=(VShear const &s);
+ Point &operator*=(Zoom const &z);
+ /// @}
+
+ /// @name Conversion to integer points
+ /// @{
+ /** @brief Round to nearest integer coordinates. */
+ IntPoint round() const {
+ IntPoint ret(::round(_pt[X]), ::round(_pt[Y]));
+ return ret;
+ }
+ /** @brief Round coordinates downwards. */
+ IntPoint floor() const {
+ IntPoint ret(::floor(_pt[X]), ::floor(_pt[Y]));
+ return ret;
+ }
+ /** @brief Round coordinates upwards. */
+ IntPoint ceil() const {
+ IntPoint ret(::ceil(_pt[X]), ::ceil(_pt[Y]));
+ return ret;
+ }
+ /// @}
+
+ /// @name Various utilities
+ /// @{
+ /** @brief Check whether both coordinates are finite. */
+ bool isFinite() const {
+ for (double i : _pt) {
+ if(!std::isfinite(i)) return false;
+ }
+ return true;
+ }
+ /** @brief Check whether both coordinates are zero. */
+ bool isZero() const {
+ return _pt[X] == 0 && _pt[Y] == 0;
+ }
+ /** @brief Check whether the length of the vector is close to 1. */
+ bool isNormalized(Coord eps=EPSILON) const {
+ return are_near(length(), 1.0, eps);
+ }
+ /** @brief Equality operator.
+ * This tests for exact identity (as opposed to are_near()). Note that due to numerical
+ * errors, this test might return false even if the points should be identical. */
+ bool operator==(const Point &in_pnt) const {
+ return (_pt[X] == in_pnt[X]) && (_pt[Y] == in_pnt[Y]);
+ }
+ /** @brief Lexicographical ordering for points.
+ * Y coordinate is regarded as more significant. When sorting according to this
+ * ordering, the points will be sorted according to the Y coordinate, and within
+ * points with the same Y coordinate according to the X coordinate. */
+ bool operator<(const Point &p) const {
+ return _pt[Y] < p[Y] || (_pt[Y] == p[Y] && _pt[X] < p[X]);
+ }
+ /// @}
+
+ /** @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. */
+ 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
+};
+
+/** @brief Output operator for points.
+ * Prints out the coordinates.
+ * @relates Point */
+std::ostream &operator<<(std::ostream &out, const Geom::Point &p);
+
+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::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]);
+ }
+};
+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.
+ * This corresponds to the length of @a p. The result will not overflow even if
+ * \f$p_X^2 + p_Y^2\f$ is larger that the maximum value that can be stored
+ * in a <code>double</code>.
+ * @return \f$\sqrt{p_X^2 + p_Y^2}\f$
+ * @relates Point */
+inline Coord L2(Point const &p) {
+ return p.length();
+}
+
+/** @brief Compute the square of the Euclidean norm of @a 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) {
+ return p[0]*p[0] + p[1]*p[1];
+}
+
+/** @brief Returns p * Geom::rotate_degrees(90), but more efficient.
+ *
+ * Angle direction in 2Geom: If you use the traditional mathematics convention that y
+ * increases upwards, then positive angles are anticlockwise as per the mathematics convention. If
+ * you take the common non-mathematical convention that y increases downwards, then positive angles
+ * are clockwise, as is common outside of mathematics.
+ *
+ * There is no function to rotate by -90 degrees: use -rot90(p) instead.
+ * @relates Point */
+inline Point rot90(Point const &p) {
+ return Point(-p[Y], p[X]);
+}
+
+/** @brief Linear interpolation between two points.
+ * @param t Time value
+ * @param a First point
+ * @param b Second point
+ * @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(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.
+ * Dot product can be interpreted as a measure of how parallel the vectors are.
+ * For perpendicular vectors, it is zero. For parallel ones, its absolute value is highest,
+ * 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[X] * b[X] + a[Y] * b[Y];
+}
+
+/** @brief Compute the 2D cross product.
+ * 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)
+{
+ // equivalent implementation:
+ // return dot(a, b.ccw());
+ return a[X] * b[Y] - a[Y] * b[X];
+}
+
+/// Compute the (Euclidean) distance between points.
+/// @relates Point
+inline Coord distance (Point const &a, Point const &b) {
+ return (a - b).length();
+}
+
+/// 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) {
+ // do not use an unqualified calls to distance before the empty
+ // specialization of iterator_traits is defined - see end of file
+ return are_near((a - b).length(), 0, eps);
+}
+
+/// Test whether the relative distance between two points is less than some threshold.
+inline bool are_near_rel(Point const &a, Point const &b, double eps = EPSILON) {
+ return (a - b).length() <= eps * (a.length() + b.length()) / 2;
+}
+
+/// 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, 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));
+
+} // 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
+// is an iterator and tries to use std::distance instead of Geom::distance.
+namespace std {
+template <> class iterator_traits<Geom::Point> {};
+}
+
+#endif // LIB2GEOM_SEEN_POINT_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/include/2geom/polynomial.h b/include/2geom/polynomial.h
new file mode 100644
index 0000000..640cab6
--- /dev/null
+++ b/include/2geom/polynomial.h
@@ -0,0 +1,264 @@
+/**
+ * \file
+ * \brief Polynomial in canonical (monomial) basis
+ *//*
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ * 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.
+ */
+
+#ifndef LIB2GEOM_SEEN_POLY_H
+#define LIB2GEOM_SEEN_POLY_H
+#include <assert.h>
+#include <vector>
+#include <iostream>
+#include <algorithm>
+#include <complex>
+#include <2geom/coord.h>
+#include <2geom/utils.h>
+
+namespace Geom {
+
+/** @brief Polynomial in canonical (monomial) basis.
+ * @ingroup Fragments */
+class Poly : public std::vector<double>{
+public:
+ // coeff; // sum x^i*coeff[i]
+
+ //unsigned size() const { return coeff.size();}
+ unsigned degree() const { return size()-1;}
+
+ //double operator[](const int i) const { return (*this)[i];}
+ //double& operator[](const int i) { return (*this)[i];}
+
+ Poly operator+(const Poly& p) const {
+ Poly result;
+ const unsigned out_size = std::max(size(), p.size());
+ const unsigned min_size = std::min(size(), p.size());
+ result.reserve(out_size);
+
+ for(unsigned i = 0; i < min_size; i++) {
+ result.push_back((*this)[i] + p[i]);
+ }
+ for(unsigned i = min_size; i < size(); i++)
+ result.push_back((*this)[i]);
+ for(unsigned i = min_size; i < p.size(); i++)
+ result.push_back(p[i]);
+ assert(result.size() == out_size);
+ return result;
+ }
+ Poly operator-(const Poly& p) const {
+ Poly result;
+ const unsigned out_size = std::max(size(), p.size());
+ const unsigned min_size = std::min(size(), p.size());
+ result.reserve(out_size);
+
+ for(unsigned i = 0; i < min_size; i++) {
+ result.push_back((*this)[i] - p[i]);
+ }
+ for(unsigned i = min_size; i < size(); i++)
+ result.push_back((*this)[i]);
+ for(unsigned i = min_size; i < p.size(); i++)
+ result.push_back(-p[i]);
+ assert(result.size() == out_size);
+ return result;
+ }
+ Poly operator-=(const Poly& p) {
+ const unsigned out_size = std::max(size(), p.size());
+ const unsigned min_size = std::min(size(), p.size());
+ resize(out_size);
+
+ for(unsigned i = 0; i < min_size; i++) {
+ (*this)[i] -= p[i];
+ }
+ for(unsigned i = min_size; i < out_size; i++)
+ (*this)[i] = -p[i];
+ return *this;
+ }
+ Poly operator-(const double k) const {
+ Poly result;
+ const unsigned out_size = size();
+ result.reserve(out_size);
+
+ for(unsigned i = 0; i < out_size; i++) {
+ result.push_back((*this)[i]);
+ }
+ result[0] -= k;
+ return result;
+ }
+ Poly operator-() const {
+ Poly result;
+ result.resize(size());
+
+ for(unsigned i = 0; i < size(); i++) {
+ result[i] = -(*this)[i];
+ }
+ return result;
+ }
+ Poly operator*(const double p) const {
+ Poly result;
+ const unsigned out_size = size();
+ result.reserve(out_size);
+
+ for(unsigned i = 0; i < out_size; i++) {
+ result.push_back((*this)[i]*p);
+ }
+ assert(result.size() == out_size);
+ return result;
+ }
+ // equivalent to multiply by x^terms, negative terms are disallowed
+ Poly shifted(unsigned const terms) const {
+ Poly result;
+ size_type const out_size = size() + terms;
+ result.reserve(out_size);
+
+ result.resize(terms, 0.0);
+ result.insert(result.end(), this->begin(), this->end());
+
+ assert(result.size() == out_size);
+ return result;
+ }
+ Poly operator*(const Poly& p) const;
+
+ template <typename T>
+ T eval(T x) const {
+ T r = 0;
+ for(int k = size()-1; k >= 0; k--) {
+ r = r*x + T((*this)[k]);
+ }
+ return r;
+ }
+
+ template <typename T>
+ T operator()(T t) const { return (T)eval(t);}
+
+ void normalize();
+
+ void monicify();
+ Poly() {}
+ Poly(const Poly& p) : std::vector<double>(p) {}
+ Poly(const double a) {push_back(a);}
+
+public:
+ template <class T, class U>
+ void val_and_deriv(T x, U &pd) const {
+ pd[0] = back();
+ int nc = size() - 1;
+ int nd = pd.size() - 1;
+ for(unsigned j = 1; j < pd.size(); j++)
+ pd[j] = 0.0;
+ for(int i = nc -1; i >= 0; i--) {
+ int nnd = std::min(nd, nc-i);
+ for(int j = nnd; j >= 1; j--)
+ pd[j] = pd[j]*x + operator[](i);
+ pd[0] = pd[0]*x + operator[](i);
+ }
+ double cnst = 1;
+ for(int i = 2; i <= nd; i++) {
+ cnst *= i;
+ pd[i] *= cnst;
+ }
+ }
+
+ static Poly linear(double ax, double b) {
+ Poly p;
+ p.push_back(b);
+ p.push_back(ax);
+ return p;
+ }
+};
+
+inline Poly operator*(double a, Poly const & b) { return b * a;}
+
+Poly integral(Poly const & p);
+Poly derivative(Poly const & p);
+Poly divide_out_root(Poly const & p, double x);
+Poly compose(Poly const & a, Poly const & b);
+Poly divide(Poly const &a, Poly const &b, Poly &r);
+Poly gcd(Poly const &a, Poly const &b, const double tol=1e-10);
+
+/*** solve(Poly p)
+ * find all p.degree() roots of p.
+ * This function can take a long time with suitably crafted polynomials, but in practice it should be fast. Should we provide special forms for degree() <= 4?
+ */
+std::vector<std::complex<double> > solve(const Poly & p);
+
+#ifdef HAVE_GSL
+/*** solve_reals(Poly p)
+ * find all real solutions to Poly p.
+ * currently we just use solve and pick out the suitably real looking values, there may be a better algorithm.
+ */
+std::vector<double> solve_reals(const Poly & p);
+#endif
+double polish_root(Poly const & p, double guess, double tol);
+
+
+/** @brief Analytically solve quadratic equation.
+ * The equation is given in the standard form: ax^2 + bx + c = 0.
+ * Only real roots are returned. */
+std::vector<Coord> solve_quadratic(Coord a, Coord b, Coord c);
+
+/** @brief Analytically solve cubic equation.
+ * The equation is given in the standard form: ax^3 + bx^2 + cx + d = 0.
+ * Only real roots are returned. */
+std::vector<Coord> solve_cubic(Coord a, Coord b, Coord c, Coord d);
+
+
+inline std::ostream &operator<< (std::ostream &out_file, const Poly &in_poly) {
+ if(in_poly.size() == 0)
+ out_file << "0";
+ else {
+ for(int i = (int)in_poly.size()-1; i >= 0; --i) {
+ if(i == 1) {
+ out_file << "" << in_poly[i] << "*x";
+ out_file << " + ";
+ } else if(i) {
+ out_file << "" << in_poly[i] << "*x^" << i;
+ out_file << " + ";
+ } else
+ out_file << in_poly[i];
+
+ }
+ }
+ return out_file;
+}
+
+} // namespace Geom
+
+#endif //LIB2GEOM_SEEN_POLY_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/include/2geom/ray.h b/include/2geom/ray.h
new file mode 100644
index 0000000..4e60fd8
--- /dev/null
+++ b/include/2geom/ray.h
@@ -0,0 +1,192 @@
+/**
+ * \file
+ * \brief Infinite straight ray
+ *//*
+ * Copyright 2008 Marco Cecchetti <mrcekets at 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_RAY_H
+#define LIB2GEOM_SEEN_RAY_H
+
+#include <vector>
+#include <2geom/point.h>
+#include <2geom/bezier-curve.h> // for LineSegment
+#include <2geom/exception.h>
+#include <2geom/math-utils.h>
+#include <2geom/transforms.h>
+#include <2geom/angle.h>
+
+namespace Geom
+{
+
+/**
+ * @brief Straight ray from a specific point to infinity.
+ *
+ * Rays are "half-lines" - they begin at some specific point and extend in a straight line
+ * to infinity.
+ *
+ * @ingroup Primitives
+ */
+class Ray {
+private:
+ Point _origin;
+ Point _vector;
+
+public:
+ Ray() : _origin(0,0), _vector(1,0) {}
+ Ray(Point const& origin, Coord angle)
+ : _origin(origin)
+ {
+ sincos(angle, _vector[Y], _vector[X]);
+ }
+ Ray(Point const& A, Point const& B) {
+ setPoints(A, B);
+ }
+ Point origin() const { return _origin; }
+ Point vector() const { return _vector; }
+ Point versor() const { return _vector.normalized(); }
+ void setOrigin(Point const &o) { _origin = o; }
+ void setVector(Point const& v) { _vector = v; }
+ Coord angle() const { return std::atan2(_vector[Y], _vector[X]); }
+ void setAngle(Coord a) { sincos(a, _vector[Y], _vector[X]); }
+ void setPoints(Point const &a, Point const &b) {
+ _origin = a;
+ _vector = b - a;
+ if (are_near(_vector, Point(0,0)) )
+ _vector = Point(0,0);
+ else
+ _vector.normalize();
+ }
+ bool isDegenerate() const {
+ return ( _vector[X] == 0 && _vector[Y] == 0 );
+ }
+ Point pointAt(Coord t) const {
+ return _origin + _vector * t;
+ }
+ Coord valueAt(Coord t, Dim2 d) const {
+ return _origin[d] + _vector[d] * t;
+ }
+ std::vector<Coord> roots(Coord v, Dim2 d) const {
+ std::vector<Coord> result;
+ if ( _vector[d] != 0 ) {
+ double t = (v - _origin[d]) / _vector[d];
+ if (t >= 0) result.push_back(t);
+ } else if (_vector[(d+1)%2] == v) {
+ THROW_INFINITESOLUTIONS();
+ }
+ return result;
+ }
+ Coord nearestTime(Point const& point) const {
+ if ( isDegenerate() ) return 0;
+ double t = dot(point - _origin, _vector);
+ if (t < 0) t = 0;
+ return t;
+ }
+ Ray reverse() const {
+ Ray result;
+ result.setOrigin(_origin);
+ result.setVector(-_vector);
+ return result;
+ }
+ Curve *portion(Coord f, Coord t) const {
+ return new LineSegment(pointAt(f), pointAt(t));
+ }
+ LineSegment segment(Coord f, Coord t) const {
+ return LineSegment(pointAt(f), pointAt(t));
+ }
+ Ray transformed(Affine const& m) const {
+ return Ray(_origin * m, (_origin + _vector) * m);
+ }
+}; // end class Ray
+
+inline
+double distance(Point const& _point, Ray const& _ray) {
+ double t = _ray.nearestTime(_point);
+ return ::Geom::distance(_point, _ray.pointAt(t));
+}
+
+inline
+bool are_near(Point const& _point, Ray const& _ray, double eps = EPSILON) {
+ return are_near(distance(_point, _ray), 0, eps);
+}
+
+inline
+bool are_same(Ray const& r1, Ray const& r2, double eps = EPSILON) {
+ return are_near(r1.vector(), r2.vector(), eps)
+ && are_near(r1.origin(), r2.origin(), eps);
+}
+
+// evaluate the angle between r1 and r2 rotating r1 in cw or ccw direction on r2
+// the returned value is an angle in the interval [0, 2PI[
+inline
+double angle_between(Ray const& r1, Ray const& r2, bool cw = true) {
+ double angle = angle_between(r1.vector(), r2.vector());
+ if (angle < 0) angle += 2*M_PI;
+ if (!cw) angle = 2*M_PI - angle;
+ return angle;
+}
+
+/**
+ * @brief Returns the angle bisector for the two given rays.
+ *
+ * @a r1 is rotated half the way to @a r2 in either clockwise or counter-clockwise direction.
+ *
+ * @pre Both passed rays must have the same origin.
+ *
+ * @remarks If the versors of both given rays point in the same direction, the direction of the
+ * angle bisector ray depends on the third parameter:
+ * - If @a cw is set to @c true, the returned ray will equal the passed rays @a r1 and @a r2.
+ * - If @a cw is set to @c false, the returned ray will go in the opposite direction.
+ *
+ * @throws RangeError if the given rays do not have the same origins
+ */
+inline
+Ray make_angle_bisector_ray(Ray const& r1, Ray const& r2, bool cw = true)
+{
+ if ( !are_near(r1.origin(), r2.origin()) )
+ {
+ THROW_RANGEERROR("passed rays do not have the same origin");
+ }
+
+ Ray bisector(r1.origin(), r1.origin() + r1.vector() * Rotate(angle_between(r1, r2) / 2.0));
+
+ return (cw ? bisector : bisector.reverse());
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_RAY_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/include/2geom/rect.h b/include/2geom/rect.h
new file mode 100644
index 0000000..5edfc95
--- /dev/null
+++ b/include/2geom/rect.h
@@ -0,0 +1,263 @@
+/**
+ * \file
+ * \brief Axis-aligned rectangle
+ *//*
+ * Authors:
+ * Michael Sloan <mgsloan@gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Copyright 2007-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, 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.
+ *
+ * Authors of original rect class:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * bulia byak <buliabyak@users.sf.net>
+ * MenTaLguY <mental@rydia.net>
+ */
+
+#ifndef LIB2GEOM_SEEN_RECT_H
+#define LIB2GEOM_SEEN_RECT_H
+
+#include <2geom/affine.h>
+#include <2geom/interval.h>
+#include <2geom/int-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
+};
+
+/// Convert an align specification to coordinate fractions.
+Point align_factors(Align align);
+
+/** @brief Structure that specifies placement of within a viewport.
+ * Use this to create transformations that preserve aspect. */
+struct Aspect {
+ Align align;
+ Expansion expansion;
+ bool deferred; ///< for SVG compatibility
+
+ Aspect(Align a = ALIGN_NONE, Expansion ex = EXPANSION_MEET)
+ : align(a), expansion(ex), deferred(false)
+ {}
+};
+
+/**
+ * @brief Axis aligned, non-empty rectangle.
+ * @ingroup Primitives
+ */
+class Rect
+ : public GenericRect<Coord>
+{
+ typedef GenericRect<Coord> Base;
+public:
+ /// @name Create rectangles.
+ /// @{
+ /** @brief Create a rectangle that contains only the point at (0,0). */
+ Rect() {}
+ /** @brief Create a rectangle from X and Y intervals. */
+ Rect(Interval const &a, Interval const &b) : Base(a,b) {}
+ /** @brief Create a rectangle from two points. */
+ Rect(Point const &a, Point const &b) : Base(a,b) {}
+ Rect(Coord x0, Coord y0, Coord x1, Coord y1) : Base(x0, y0, x1, y1) {}
+ Rect(Base const &b) : Base(b) {}
+ Rect(IntRect const &ir) : Base(ir.min(), ir.max()) {}
+ /// @}
+
+ /// @name Inspect dimensions.
+ /// @{
+ /** @brief Check whether the rectangle has zero area up to specified tolerance.
+ * @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.
+ /// @{
+ /** @brief Check whether the interiors of the rectangles have any common points. */
+ bool interiorIntersects(Rect const &r) const {
+ return f[X].interiorIntersects(r[X]) && f[Y].interiorIntersects(r[Y]);
+ }
+ /** @brief Check whether the interior includes the given point. */
+ bool interiorContains(Point const &p) const {
+ return f[X].interiorContains(p[X]) && f[Y].interiorContains(p[Y]);
+ }
+ /** @brief Check whether the interior includes all points in the given rectangle.
+ * Interior of the rectangle is the entire rectangle without its borders. */
+ bool interiorContains(Rect const &r) const {
+ return f[X].interiorContains(r[X]) && f[Y].interiorContains(r[Y]);
+ }
+ inline bool interiorContains(OptRect const &r) const;
+ /// @}
+
+ /// @name Rounding to integer coordinates
+ /// @{
+ /** @brief Return the smallest integer rectangle which contains this one. */
+ IntRect roundOutwards() const {
+ IntRect ir(f[X].roundOutwards(), f[Y].roundOutwards());
+ return ir;
+ }
+ /** @brief Return the largest integer rectangle which is contained in this one. */
+ OptIntRect roundInwards() const {
+ OptIntRect oir(f[X].roundInwards(), f[Y].roundInwards());
+ return oir;
+ }
+ /// @}
+
+ /// @name SVG viewbox functionality.
+ /// @{
+ /** @brief Transform contents to viewport.
+ * Computes an affine that transforms the contents of this rectangle
+ * to the specified viewport. The aspect parameter specifies how to
+ * to the transformation (whether the aspect ratio of content
+ * should be kept and where it should be placed in the viewport). */
+ Affine transformTo(Rect const &viewport, Aspect const &aspect = Aspect()) const;
+ /// @}
+
+ /// @name Operators
+ /// @{
+ Rect &operator*=(Affine const &m);
+ bool operator==(IntRect const &ir) const {
+ return f[X] == ir[X] && f[Y] == ir[Y];
+ }
+ bool operator==(Rect const &other) const {
+ return Base::operator==(other);
+ }
+ /// @}
+};
+
+/**
+ * @brief Axis-aligned rectangle that can be empty.
+ * @ingroup Primitives
+ */
+class OptRect
+ : public GenericOptRect<Coord>
+{
+ typedef GenericOptRect<Coord> Base;
+public:
+ OptRect() : Base() {}
+ OptRect(Rect const &a) : Base(a) {}
+ OptRect(Point const &a, Point const &b) : Base(a, b) {}
+ OptRect(Coord x0, Coord y0, Coord x1, Coord y1) : Base(x0, y0, x1, y1) {}
+ OptRect(OptInterval const &x_int, OptInterval const &y_int) : Base(x_int, y_int) {}
+ OptRect(Base const &b) : Base(b) {}
+
+ OptRect(IntRect const &r) : Base(Rect(r)) {}
+ OptRect(OptIntRect const &r) : Base() {
+ if (r) *this = Rect(*r);
+ }
+
+ Affine transformTo(Rect const &viewport, Aspect const &aspect = Aspect()) {
+ Affine ret = Affine::identity();
+ if (empty()) return ret;
+ ret = (*this)->transformTo(viewport, aspect);
+ return ret;
+ }
+
+ bool operator==(OptRect const &other) const {
+ return Base::operator==(other);
+ }
+ bool operator==(Rect const &other) const {
+ return Base::operator==(other);
+ }
+};
+
+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));
+}
+
+// the functions below do not work when defined generically
+inline OptRect operator&(Rect const &a, Rect const &b) {
+ OptRect ret(a);
+ ret.intersectWith(b);
+ return ret;
+}
+inline OptRect intersect(Rect const &a, Rect const &b) {
+ return a & b;
+}
+inline OptRect intersect(OptRect const &a, OptRect const &b) {
+ return a & b;
+}
+inline Rect unify(Rect const &a, Rect const &b) {
+ return a | b;
+}
+inline OptRect unify(OptRect const &a, OptRect const &b) {
+ return a | b;
+}
+
+/** @brief Union a list of rectangles
+ * @deprecated Use OptRect::from_range instead */
+inline Rect union_list(std::vector<Rect> const &r) {
+ if(r.empty()) return Rect(Interval(0,0), Interval(0,0));
+ Rect ret = r[0];
+ for(unsigned i = 1; i < r.size(); i++)
+ ret.unionWith(r[i]);
+ return ret;
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_RECT_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/include/2geom/sbasis-2d.h b/include/2geom/sbasis-2d.h
new file mode 100644
index 0000000..98dec67
--- /dev/null
+++ b/include/2geom/sbasis-2d.h
@@ -0,0 +1,371 @@
+/**
+ * \file
+ * \brief Obsolete 2D SBasis function class
+ *//*
+ * Authors:
+ * Nathan Hurst <?@?.?>
+ * JFBarraud <?@?.?>
+ *
+ * Copyright 2006-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.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_SBASIS_2D_H
+#define LIB2GEOM_SEEN_SBASIS_2D_H
+#include <vector>
+#include <cassert>
+#include <algorithm>
+#include <2geom/d2.h>
+#include <2geom/sbasis.h>
+#include <iostream>
+
+namespace Geom{
+
+class Linear2d{
+public:
+ /*
+ u 0,1
+ v 0,2
+ */
+ double a[4];
+ Linear2d() {
+ a[0] = 0;
+ a[1] = 0;
+ a[2] = 0;
+ a[3] = 0;
+ }
+ Linear2d(double aa) {
+ for(double & i : a)
+ i = aa;
+ }
+ Linear2d(double a00, double a01, double a10, double a11)
+ {
+ a[0] = a00;
+ a[1] = a01;
+ a[2] = a10;
+ a[3] = a11;
+ }
+
+ double operator[](const int i) const {
+ assert(i >= 0);
+ assert(i < 4);
+ return a[i];
+ }
+ double& operator[](const int i) {
+ assert(i >= 0);
+ assert(i < 4);
+ return a[i];
+ }
+ double apply(double u, double v) {
+ return (a[0]*(1-u)*(1-v) +
+ a[1]*u*(1-v) +
+ a[2]*(1-u)*v +
+ a[3]*u*v);
+ }
+};
+
+inline Linear extract_u(Linear2d const &a, double u) {
+ return Linear(a[0]*(1-u) +
+ a[1]*u,
+ a[2]*(1-u) +
+ a[3]*u);
+}
+inline Linear extract_v(Linear2d const &a, double v) {
+ return Linear(a[0]*(1-v) +
+ a[2]*v,
+ a[1]*(1-v) +
+ a[3]*v);
+}
+inline Linear2d operator-(Linear2d const &a) {
+ return Linear2d(-a.a[0], -a.a[1],
+ -a.a[2], -a.a[3]);
+}
+inline Linear2d operator+(Linear2d const & a, Linear2d const & b) {
+ return Linear2d(a[0] + b[0],
+ a[1] + b[1],
+ a[2] + b[2],
+ a[3] + b[3]);
+}
+inline Linear2d operator-(Linear2d const & a, Linear2d const & b) {
+ return Linear2d(a[0] - b[0],
+ a[1] - b[1],
+ a[2] - b[2],
+ a[3] - b[3]);
+}
+inline Linear2d& operator+=(Linear2d & a, Linear2d const & b) {
+ for(unsigned i = 0; i < 4; i++)
+ a[i] += b[i];
+ return a;
+}
+inline Linear2d& operator-=(Linear2d & a, Linear2d const & b) {
+ for(unsigned i = 0; i < 4; i++)
+ a[i] -= b[i];
+ return a;
+}
+inline Linear2d& operator*=(Linear2d & a, double b) {
+ for(unsigned i = 0; i < 4; i++)
+ a[i] *= b;
+ return a;
+}
+
+inline bool operator==(Linear2d const & a, Linear2d const & b) {
+ for(unsigned i = 0; i < 4; i++)
+ if(a[i] != b[i])
+ return false;
+ return true;
+}
+inline bool operator!=(Linear2d const & a, Linear2d const & b) {
+ for(unsigned i = 0; i < 4; i++)
+ if(a[i] == b[i])
+ return false;
+ return true;
+}
+inline Linear2d operator*(double const a, Linear2d const & b) {
+ return Linear2d(a*b[0], a*b[1],
+ a*b[2], a*b[3]);
+}
+
+class SBasis2d : public std::vector<Linear2d>{
+public:
+ // vector in u,v
+ unsigned us, vs; // number of u terms, v terms
+ SBasis2d() {}
+ SBasis2d(Linear2d const & bo)
+ : us(1), vs(1) {
+ push_back(bo);
+ }
+ SBasis2d(SBasis2d const & a)
+ : std::vector<Linear2d>(a), us(a.us), vs(a.vs) {}
+
+ Linear2d& index(unsigned ui, unsigned vi) {
+ assert(ui < us);
+ assert(vi < vs);
+ return (*this)[ui + vi*us];
+ }
+
+ Linear2d index(unsigned ui, unsigned vi) const {
+ if(ui >= us)
+ return Linear2d(0);
+ if(vi >= vs)
+ return Linear2d(0);
+ return (*this)[ui + vi*us];
+ }
+
+ double apply(double u, double v) const {
+ double s = u*(1-u);
+ double t = v*(1-v);
+ Linear2d p;
+ double tk = 1;
+// XXX rewrite as horner
+ for(unsigned vi = 0; vi < vs; vi++) {
+ double sk = 1;
+ for(unsigned ui = 0; ui < us; ui++) {
+ p += (sk*tk)*index(ui, vi);
+ sk *= s;
+ }
+ tk *= t;
+ }
+ return p.apply(u,v);
+ }
+
+ void clear() {
+ fill(begin(), end(), Linear2d(0));
+ }
+
+ void normalize(); // remove extra zeros
+
+ double tail_error(unsigned tail) const;
+
+ void truncate(unsigned k);
+};
+
+inline SBasis2d operator-(const SBasis2d& p) {
+ SBasis2d result;
+ result.reserve(p.size());
+
+ for(unsigned i = 0; i < p.size(); i++) {
+ result.push_back(-p[i]);
+ }
+ return result;
+}
+
+inline SBasis2d operator+(const SBasis2d& a, const SBasis2d& b) {
+ SBasis2d result;
+ result.us = std::max(a.us, b.us);
+ result.vs = std::max(a.vs, b.vs);
+ const unsigned out_size = result.us*result.vs;
+ result.resize(out_size);
+
+ for(unsigned vi = 0; vi < result.vs; vi++) {
+ for(unsigned ui = 0; ui < result.us; ui++) {
+ Linear2d bo;
+ if(ui < a.us && vi < a.vs)
+ bo += a.index(ui, vi);
+ if(ui < b.us && vi < b.vs)
+ bo += b.index(ui, vi);
+ result.index(ui, vi) = bo;
+ }
+ }
+ return result;
+}
+
+inline SBasis2d operator-(const SBasis2d& a, const SBasis2d& b) {
+ SBasis2d result;
+ result.us = std::max(a.us, b.us);
+ result.vs = std::max(a.vs, b.vs);
+ const unsigned out_size = result.us*result.vs;
+ result.resize(out_size);
+
+ for(unsigned vi = 0; vi < result.vs; vi++) {
+ for(unsigned ui = 0; ui < result.us; ui++) {
+ Linear2d bo;
+ if(ui < a.us && vi < a.vs)
+ bo += a.index(ui, vi);
+ if(ui < b.us && vi < b.vs)
+ bo -= b.index(ui, vi);
+ result.index(ui, vi) = bo;
+ }
+ }
+ return result;
+}
+
+
+inline SBasis2d& operator+=(SBasis2d& a, const Linear2d& b) {
+ if(a.size() < 1)
+ a.push_back(b);
+ else
+ a[0] += b;
+ return a;
+}
+
+inline SBasis2d& operator-=(SBasis2d& a, const Linear2d& b) {
+ if(a.size() < 1)
+ a.push_back(-b);
+ else
+ a[0] -= b;
+ return a;
+}
+
+inline SBasis2d& operator+=(SBasis2d& a, double b) {
+ if(a.size() < 1)
+ a.push_back(Linear2d(b));
+ else {
+ for(unsigned i = 0; i < 4; i++)
+ a[0] += double(b);
+ }
+ return a;
+}
+
+inline SBasis2d& operator-=(SBasis2d& a, double b) {
+ if(a.size() < 1)
+ a.push_back(Linear2d(-b));
+ else {
+ a[0] -= b;
+ }
+ return a;
+}
+
+inline SBasis2d& operator*=(SBasis2d& a, double b) {
+ for(unsigned i = 0; i < a.size(); i++)
+ a[i] *= b;
+ return a;
+}
+
+inline SBasis2d& operator/=(SBasis2d& a, double b) {
+ for(unsigned i = 0; i < a.size(); i++)
+ a[i] *= (1./b);
+ return a;
+}
+
+SBasis2d operator*(double k, SBasis2d const &a);
+SBasis2d operator*(SBasis2d const &a, SBasis2d const &b);
+
+SBasis2d shift(SBasis2d const &a, int sh);
+
+SBasis2d shift(Linear2d const &a, int sh);
+
+SBasis2d truncate(SBasis2d const &a, unsigned terms);
+
+SBasis2d multiply(SBasis2d const &a, SBasis2d const &b);
+
+SBasis2d integral(SBasis2d const &c);
+
+SBasis2d partial_derivative(SBasis2d const &a, int dim);
+
+SBasis2d sqrt(SBasis2d const &a, int k);
+
+// return a kth order approx to 1/a)
+SBasis2d reciprocal(Linear2d const &a, int k);
+
+SBasis2d divide(SBasis2d const &a, SBasis2d const &b, int k);
+
+// a(b(t))
+SBasis2d compose(SBasis2d const &a, SBasis2d const &b);
+SBasis2d compose(SBasis2d const &a, SBasis2d const &b, unsigned k);
+SBasis2d inverse(SBasis2d const &a, int k);
+
+// these two should probably be replaced with compose
+SBasis extract_u(SBasis2d const &a, double u);
+SBasis extract_v(SBasis2d const &a, double v);
+
+SBasis compose(Linear2d const &a, D2<SBasis> const &p);
+
+SBasis compose(SBasis2d const &fg, D2<SBasis> const &p);
+
+D2<SBasis> compose_each(D2<SBasis2d> const &fg, D2<SBasis> const &p);
+
+inline std::ostream &operator<< (std::ostream &out_file, const Linear2d &bo) {
+ out_file << "{" << bo[0] << ", " << bo[1] << "}, ";
+ out_file << "{" << bo[2] << ", " << bo[3] << "}";
+ return out_file;
+}
+
+inline std::ostream &operator<< (std::ostream &out_file, const SBasis2d & p) {
+ for(unsigned i = 0; i < p.size(); i++) {
+ out_file << p[i] << "s^" << i << " + ";
+ }
+ return out_file;
+}
+
+D2<SBasis>
+sb2dsolve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B, unsigned degmax=2);
+
+D2<SBasis>
+sb2d_cubic_solve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B);
+
+} // end namespace Geom
+
+#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/include/2geom/sbasis-curve.h b/include/2geom/sbasis-curve.h
new file mode 100644
index 0000000..93d6772
--- /dev/null
+++ b/include/2geom/sbasis-curve.h
@@ -0,0 +1,160 @@
+/**
+ * \file
+ * \brief Symmetric power basis curve
+ *//*
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * 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 LIB2GEOM_SEEN_SBASIS_CURVE_H
+#define LIB2GEOM_SEEN_SBASIS_CURVE_H
+
+#include <2geom/curve.h>
+#include <2geom/exception.h>
+#include <2geom/nearest-time.h>
+#include <2geom/sbasis-geometric.h>
+#include <2geom/transforms.h>
+
+namespace Geom
+{
+
+/** @brief Symmetric power basis curve.
+ *
+ * Symmetric power basis (S-basis for short) polynomials are a versatile numeric
+ * representation of arbitrary continuous curves. They are the main representation of curves
+ * in 2Geom.
+ *
+ * S-basis is defined for odd degrees and composed of the following polynomials:
+ * \f{align*}{
+ P_k^0(t) &= t^k (1-t)^{k+1} \\
+ P_k^1(t) &= t^{k+1} (1-t)^k \f}
+ * This can be understood more easily with the help of the chart below. Each square
+ * represents a product of a specific number of \f$t\f$ and \f$(1-t)\f$ terms. Red dots
+ * are the canonical (monomial) basis, the green dots are the Bezier basis, and the blue
+ * dots are the S-basis, all of them of degree 7.
+ *
+ * @image html sbasis.png "Illustration of the monomial, Bezier and symmetric power bases"
+ *
+ * The S-Basis has several important properties:
+ * - S-basis polynomials are closed under multiplication.
+ * - Evaluation is fast, using a modified Horner scheme.
+ * - Degree change is as trivial as in the monomial basis. To elevate, just add extra
+ * zero coefficients. To reduce the degree, truncate the terms in the highest powers.
+ * Compare this with Bezier curves, where degree change is complicated.
+ * - Conversion between S-basis and Bezier basis is numerically stable.
+ *
+ * More in-depth information can be found in the following paper:
+ * J Sanchez-Reyes, "The symmetric analogue of the polynomial power basis".
+ * ACM Transactions on Graphics, Vol. 16, No. 3, July 1997, pages 319--357.
+ * http://portal.acm.org/citation.cfm?id=256162
+ *
+ * @ingroup Curves
+ */
+class SBasisCurve : public Curve {
+private:
+ D2<SBasis> inner;
+
+public:
+ explicit SBasisCurve(D2<SBasis> const &sb) : inner(sb) {}
+ explicit SBasisCurve(Curve const &other) : inner(other.toSBasis()) {}
+
+ Curve *duplicate() const override { return new SBasisCurve(*this); }
+ Point initialPoint() const override { return inner.at0(); }
+ Point finalPoint() const override { return inner.at1(); }
+ bool isDegenerate() const override { return inner.isConstant(0); }
+ bool isLineSegment() const override { return inner[X].size() == 1; }
+ Point pointAt(Coord t) const override { return inner.valueAt(t); }
+ std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const override {
+ return inner.valueAndDerivatives(t, n);
+ }
+ Coord valueAt(Coord t, Dim2 d) const override { return inner[d].valueAt(t); }
+ void setInitial(Point const &v) override {
+ for (unsigned d = 0; d < 2; d++) { inner[d][0][0] = v[d]; }
+ }
+ void setFinal(Point const &v) override {
+ for (unsigned d = 0; d < 2; d++) { inner[d][0][1] = v[d]; }
+ }
+ Rect boundsFast() const override { return *bounds_fast(inner); }
+ Rect boundsExact() const override { return *bounds_exact(inner); }
+ void expandToTransformed(Rect &bbox, Affine const &transform) const override {
+ bbox |= bounds_exact(inner * transform);
+ }
+ OptRect boundsLocal(OptInterval const &i, unsigned deg) const override {
+ return bounds_local(inner, i, deg);
+ }
+ std::vector<Coord> roots(Coord v, Dim2 d) const override { return Geom::roots(inner[d] - v); }
+ Coord nearestTime( Point const& p, Coord from = 0, Coord to = 1 ) const override {
+ return nearest_time(p, inner, from, to);
+ }
+ std::vector<Coord> allNearestTimes( Point const& p, Coord from = 0,
+ Coord to = 1 ) const override
+ {
+ return all_nearest_times(p, inner, from, to);
+ }
+ Coord length(Coord tolerance) const override { return ::Geom::length(inner, tolerance); }
+ Curve *portion(Coord f, Coord t) const override {
+ return new SBasisCurve(Geom::portion(inner, f, t));
+ }
+
+ using Curve::operator*=;
+ void operator*=(Affine const &m) override { inner = inner * m; }
+
+ Curve *derivative() const override {
+ return new SBasisCurve(Geom::derivative(inner));
+ }
+ D2<SBasis> toSBasis() const override { return inner; }
+ bool operator==(Curve const &c) const override {
+ SBasisCurve const *other = dynamic_cast<SBasisCurve const *>(&c);
+ if (!other) return false;
+ return inner == other->inner;
+ }
+ bool isNear(Curve const &/*c*/, Coord /*eps*/) const override {
+ THROW_NOTIMPLEMENTED();
+ return false;
+ }
+ int degreesOfFreedom() const override {
+ return inner[0].degreesOfFreedom() + inner[1].degreesOfFreedom();
+ }
+};
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_SBASIS_CURVE_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/include/2geom/sbasis-geometric.h b/include/2geom/sbasis-geometric.h
new file mode 100644
index 0000000..7f1e8aa
--- /dev/null
+++ b/include/2geom/sbasis-geometric.h
@@ -0,0 +1,146 @@
+/**
+ * \file
+ * \brief two-dimensional geometric operators.
+ *
+ * These operators are built on a more 'polynomially robust'
+ * transformation to map a function that takes a [0,1] parameter to a
+ * 2d vector into a function that takes the same [0,1] parameter to a
+ * unit vector with the same direction.
+ *
+ * Rather that using (X/sqrt(X))(t) which involves two unstable
+ * operations, sqrt and divide, this approach forms a curve directly
+ * 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.
+ */
+
+#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);
+
+Piecewise<SBasis>
+atan2(D2<SBasis> const &vect,
+ double tol=.01, unsigned order=3);
+
+Piecewise<SBasis>
+atan2(Piecewise<D2<SBasis> >const &vect,
+ double tol=.01, unsigned order=3);
+
+D2<Piecewise<SBasis> >
+tan2(SBasis const &angle,
+ double tol=.01, unsigned order=3);
+
+D2<Piecewise<SBasis> >
+tan2(Piecewise<SBasis> const &angle,
+ double tol=.01, unsigned order=3);
+
+Piecewise<D2<SBasis> >
+unitVector(D2<SBasis> const &vect,
+ double tol=.01, unsigned order=3);
+Piecewise<D2<SBasis> >
+unitVector(Piecewise<D2<SBasis> > const &vect,
+ double tol=.01, unsigned order=3);
+
+// Piecewise<D2<SBasis> >
+// uniform_speed(D2<SBasis> const M,
+// double tol=.1);
+
+Piecewise<SBasis> curvature( D2<SBasis> const &M, double tol=.01);
+Piecewise<SBasis> curvature(Piecewise<D2<SBasis> > const &M, double tol=.01);
+
+Piecewise<SBasis> arcLengthSb( D2<SBasis> const &M, double tol=.01);
+Piecewise<SBasis> arcLengthSb(Piecewise<D2<SBasis> > const &M, double tol=.01);
+
+double length( D2<SBasis> const &M, double tol=.01);
+double length(Piecewise<D2<SBasis> > const &M, double tol=.01);
+
+void length_integrating(D2<SBasis> const &B, double &result, double &abs_error, double tol);
+
+Piecewise<D2<SBasis> >
+arc_length_parametrization(D2<SBasis> const &M,
+ unsigned order=3,
+ double tol=.01);
+Piecewise<D2<SBasis> >
+arc_length_parametrization(Piecewise<D2<SBasis> > const &M,
+ unsigned order=3,
+ double tol=.01);
+
+
+unsigned centroid(Piecewise<D2<SBasis> > const &p, Point& centroid, double &area);
+
+std::vector<D2<SBasis> >
+cubics_fitting_curvature(Point const &M0, Point const &M1,
+ Point const &dM0, Point const &dM1,
+ double d2M0xdM0, double d2M1xdM1,
+ int insist_on_speed_signs = 1,
+ double epsilon = 1e-5);
+
+std::vector<D2<SBasis> >
+cubics_fitting_curvature(Point const &M0, Point const &M1,
+ Point const &dM0, Point const &dM1,
+ Point const &d2M0, Point const &d2M1,
+ int insist_on_speed_signs = 1,
+ double epsilon = 1e-5);
+
+std::vector<D2<SBasis> >
+cubics_with_prescribed_curvature(Point const &M0, Point const &M1,
+ Point const &dM0, Point const &dM1,
+ double k0, double k1,
+ int insist_on_speed_signs = 1,
+ double error = 1e-5);
+
+
+std::vector<double> find_tangents(Point P, D2<SBasis> const &A);
+std::vector<double> find_tangents_by_vector(Point V, D2<SBasis> const &A);
+std::vector<double> find_normals(Point P, D2<SBasis> const &A);
+std::vector<double> find_normals_by_vector(Point V, D2<SBasis> const &A);
+
+};
+
+#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/include/2geom/sbasis-math.h b/include/2geom/sbasis-math.h
new file mode 100644
index 0000000..e191dae
--- /dev/null
+++ b/include/2geom/sbasis-math.h
@@ -0,0 +1,99 @@
+/** @file
+ * @brief some std functions to work with (pw)s-basis
+ *//*
+ * Authors:
+ * Jean-Francois Barraud
+ *
+ * Copyright (C) 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.
+ */
+
+//this a first try to define sqrt, cos, sin, etc...
+//TODO: define a truncated compose(sb,sb, order) and extend it to pw<sb>.
+//TODO: in all these functions, compute 'order' according to 'tol'.
+//TODO: use template to define the pw version automatically from the sb version?
+
+#ifndef LIB2GEOM_SEEN_SBASIS_MATH_H
+#define LIB2GEOM_SEEN_SBASIS_MATH_H
+
+
+#include <2geom/sbasis.h>
+#include <2geom/piecewise.h>
+#include <2geom/d2.h>
+
+namespace Geom{
+//-|x|---------------------------------------------------------------
+Piecewise<SBasis> abs( SBasis const &f);
+Piecewise<SBasis> abs(Piecewise<SBasis>const &f);
+
+//- max(f,g), min(f,g) ----------------------------------------------
+Piecewise<SBasis> max( SBasis const &f, SBasis const &g);
+Piecewise<SBasis> max(Piecewise<SBasis> const &f, SBasis const &g);
+Piecewise<SBasis> max( SBasis const &f, Piecewise<SBasis> const &g);
+Piecewise<SBasis> max(Piecewise<SBasis> const &f, Piecewise<SBasis> const &g);
+Piecewise<SBasis> min( SBasis const &f, SBasis const &g);
+Piecewise<SBasis> min(Piecewise<SBasis> const &f, SBasis const &g);
+Piecewise<SBasis> min( SBasis const &f, Piecewise<SBasis> const &g);
+Piecewise<SBasis> min(Piecewise<SBasis> const &f, Piecewise<SBasis> const &g);
+
+//-sign(x)---------------------------------------------------------------
+Piecewise<SBasis> signSb( SBasis const &f);
+Piecewise<SBasis> signSb(Piecewise<SBasis>const &f);
+
+//-Sqrt---------------------------------------------------------------
+Piecewise<SBasis> sqrt( SBasis const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> sqrt(Piecewise<SBasis>const &f, double tol=1e-3, int order=3);
+
+//-sin/cos--------------------------------------------------------------
+Piecewise<SBasis> cos( SBasis const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> cos(Piecewise<SBasis> const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> sin( SBasis const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> sin(Piecewise<SBasis> const &f, double tol=1e-3, int order=3);
+//-Log---------------------------------------------------------------
+Piecewise<SBasis> log( SBasis const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> log(Piecewise<SBasis>const &f, double tol=1e-3, int order=3);
+
+//--1/x------------------------------------------------------------
+//TODO: change this...
+Piecewise<SBasis> reciprocalOnDomain(Interval range, double tol=1e-3);
+Piecewise<SBasis> reciprocal( SBasis const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> reciprocal(Piecewise<SBasis>const &f, double tol=1e-3, int order=3);
+
+//--interpolate------------------------------------------------------------
+Piecewise<SBasis> interpolate( std::vector<double> times, std::vector<double> values, unsigned smoothness = 1);
+}
+
+#endif //SEEN_GEOM_PW_SB_CALCULUS_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/include/2geom/sbasis-poly.h b/include/2geom/sbasis-poly.h
new file mode 100644
index 0000000..d18bc36
--- /dev/null
+++ b/include/2geom/sbasis-poly.h
@@ -0,0 +1,56 @@
+/** @file
+ * @brief Conversion between SBasis and Poly. Not recommended for general use due to instability.
+ *//*
+ * 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 LIB2GEOM_SEEN_SBASIS_POLY_H
+#define LIB2GEOM_SEEN_SBASIS_POLY_H
+
+#include <2geom/polynomial.h>
+#include <2geom/sbasis.h>
+
+namespace Geom{
+
+SBasis poly_to_sbasis(Poly const & p);
+Poly sbasis_to_poly(SBasis const & s);
+
+};
+
+#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/include/2geom/sbasis-to-bezier.h b/include/2geom/sbasis-to-bezier.h
new file mode 100644
index 0000000..eadb47b
--- /dev/null
+++ b/include/2geom/sbasis-to-bezier.h
@@ -0,0 +1,87 @@
+/**
+ * \file
+ * \brief Conversion between SBasis and Bezier basis polynomials
+ *//*
+ * 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 LIB2GEOM_SEEN_SBASIS_TO_BEZIER_H
+#define LIB2GEOM_SEEN_SBASIS_TO_BEZIER_H
+
+#include <2geom/d2.h>
+#include <2geom/pathvector.h>
+
+#include <vector>
+
+namespace Geom {
+
+class PathBuilder;
+
+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);
+void bezier_to_sbasis (D2<SBasis> & sb, std::vector<Point> const& bz);
+void build_from_sbasis(PathBuilder &pb, D2<SBasis> const &B, double tol, bool only_cubicbeziers);
+
+#if 0
+// this produces a degree k bezier from a degree k sbasis
+Bezier
+sbasis_to_bezier(SBasis const &B, unsigned q = 0);
+
+// inverse
+SBasis bezier_to_sbasis(Bezier const &B);
+
+
+std::vector<Geom::Point>
+sbasis_to_bezier(D2<SBasis> const &B, unsigned q = 0);
+#endif
+
+
+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)
+ { return path_from_sbasis(B, tol, true); }
+
+} // end namespace Geom
+
+#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/include/2geom/sbasis.h b/include/2geom/sbasis.h
new file mode 100644
index 0000000..5cb0e93
--- /dev/null
+++ b/include/2geom/sbasis.h
@@ -0,0 +1,530 @@
+/** @file
+ * @brief Polynomial in symmetric power basis (S-basis)
+ *//*
+ * Authors:
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Michael Sloan <mgsloan@gmail.com>
+ *
+ * Copyright (C) 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.
+ */
+
+#ifndef LIB2GEOM_SEEN_SBASIS_H
+#define LIB2GEOM_SEEN_SBASIS_H
+#include <cassert>
+#include <iostream>
+#include <utility>
+#include <vector>
+
+#include <2geom/linear.h>
+#include <2geom/interval.h>
+#include <2geom/utils.h>
+#include <2geom/exception.h>
+
+//#define USE_SBASISN 1
+
+
+#if defined(USE_SBASIS_OF)
+
+#include "sbasis-of.h"
+
+#elif defined(USE_SBASISN)
+
+#include "sbasisN.h"
+namespace Geom{
+
+/*** An empty SBasis is identically 0. */
+class SBasis : public SBasisN<1>;
+
+};
+#else
+
+namespace Geom {
+
+/**
+ * @brief Polynomial in symmetric power basis
+ * @ingroup Fragments
+ */
+class SBasis {
+ std::vector<Linear> d;
+ void push_back(Linear const&l) { d.push_back(l); }
+
+public:
+ // As part of our migration away from SBasis isa vector we provide this minimal set of vector interface methods.
+ size_t size() const {return d.size();}
+ typedef std::vector<Linear>::iterator iterator;
+ typedef std::vector<Linear>::const_iterator const_iterator;
+ Linear operator[](unsigned i) const {
+ return d[i];
+ }
+ Linear& operator[](unsigned i) { return d.at(i); }
+ const_iterator begin() const { return d.begin();}
+ const_iterator end() const { return d.end();}
+ iterator begin() { return d.begin();}
+ iterator end() { return d.end();}
+ bool empty() const { return d.size() == 1 && d[0][0] == 0 && d[0][1] == 0; }
+ Linear &back() {return d.back();}
+ Linear const &back() const {return d.back();}
+ void pop_back() {
+ if (d.size() > 1) {
+ d.pop_back();
+ } else {
+ d[0][0] = 0;
+ d[0][1] = 0;
+ }
+ }
+ void resize(unsigned n) { d.resize(std::max<unsigned>(n, 1));}
+ void resize(unsigned n, Linear const& l) { d.resize(std::max<unsigned>(n, 1), l);}
+ void reserve(unsigned n) { d.reserve(n);}
+ void clear() {
+ d.resize(1);
+ d[0][0] = 0;
+ d[0][1] = 0;
+ }
+ void insert(iterator before, const_iterator src_begin, const_iterator src_end) { d.insert(before, src_begin, src_end);}
+ Linear& at(unsigned i) { return d.at(i);}
+ //void insert(Linear* before, int& n, Linear const &l) { d.insert(std::vector<Linear>::iterator(before), n, l);}
+ bool operator==(SBasis const&B) const { return d == B.d;}
+ bool operator!=(SBasis const&B) const { return d != B.d;}
+
+ SBasis()
+ : d(1, Linear(0, 0))
+ {}
+ explicit SBasis(double a)
+ : d(1, Linear(a, a))
+ {}
+ explicit SBasis(double a, double b)
+ : d(1, Linear(a, b))
+ {}
+ SBasis(SBasis const &a)
+ : d(a.d)
+ {}
+ SBasis(std::vector<Linear> ls)
+ : d(std::move(ls))
+ {}
+ SBasis(Linear const &bo)
+ : d(1, bo)
+ {}
+ SBasis(Linear* bo)
+ : d(1, bo ? *bo : Linear(0, 0))
+ {}
+ 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);
+ assert(std::distance(first, last) >= 2);
+ for (; first != last; ++first) {
+ --last;
+ push_back(Linear(*first, *last));
+ }
+ }
+
+ //IMPL: FragmentConcept
+ typedef double output_type;
+ inline bool isZero(double eps=EPSILON) const {
+ assert(size() > 0);
+ for(unsigned i = 0; i < size(); i++) {
+ if(!(*this)[i].isZero(eps)) return false;
+ }
+ return true;
+ }
+ inline bool isConstant(double eps=EPSILON) const {
+ assert(size() > 0);
+ if(!(*this)[0].isConstant(eps)) return false;
+ for (unsigned i = 1; i < size(); i++) {
+ if(!(*this)[i].isZero(eps)) return false;
+ }
+ return true;
+ }
+
+ bool isFinite() const;
+ 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;}
+
+ double valueAt(double t) const {
+ assert(size() > 0);
+ double s = t*(1-t);
+ double p0 = 0, p1 = 0;
+ for(unsigned k = size(); k > 0; k--) {
+ const Linear &lin = (*this)[k-1];
+ p0 = p0*s + lin[0];
+ p1 = p1*s + lin[1];
+ }
+ return (1-t)*p0 + t*p1;
+ }
+ //double valueAndDerivative(double t, double &der) const {
+ //}
+ double operator()(double t) const {
+ return valueAt(t);
+ }
+
+ std::vector<double> valueAndDerivatives(double t, unsigned n) const;
+
+ SBasis toSBasis() const { return SBasis(*this); }
+
+ double tailError(unsigned tail) const;
+
+// compute f(g)
+ SBasis operator()(SBasis const & g) const;
+
+//MUTATOR PRISON
+ //remove extra zeros
+ void normalize() {
+ while(size() > 1 && back().isZero(0))
+ pop_back();
+ }
+
+ void truncate(unsigned k) { if(k < size()) resize(std::max<size_t>(k, 1)); }
+private:
+ void derive(); // in place version
+};
+
+//TODO: figure out how to stick this in linear, while not adding an sbasis dep
+inline SBasis Linear::toSBasis() const { return SBasis(*this); }
+
+//implemented in sbasis-roots.cpp
+OptInterval bounds_exact(SBasis const &a);
+OptInterval bounds_fast(SBasis const &a, int order = 0);
+OptInterval bounds_local(SBasis const &a, const OptInterval &t, int order = 0);
+
+/** Returns a function which reverses the domain of a.
+ \param a sbasis function
+ \relates SBasis
+
+useful for reversing a parameteric curve.
+*/
+inline SBasis reverse(SBasis const &a) {
+ SBasis result(a.size(), Linear());
+
+ for(unsigned k = 0; k < a.size(); k++)
+ result[k] = reverse(a[k]);
+ return result;
+}
+
+//IMPL: ScalableConcept
+inline SBasis operator-(const SBasis& p) {
+ if(p.isZero()) return SBasis();
+ SBasis result(p.size(), Linear());
+
+ for(unsigned i = 0; i < p.size(); i++) {
+ result[i] = -p[i];
+ }
+ return result;
+}
+SBasis operator*(SBasis const &a, double k);
+inline SBasis operator*(double k, SBasis const &a) { return a*k; }
+inline SBasis operator/(SBasis const &a, double k) { return a*(1./k); }
+SBasis& operator*=(SBasis& a, double b);
+inline SBasis& operator/=(SBasis& a, double b) { return (a*=(1./b)); }
+
+//IMPL: AddableConcept
+SBasis operator+(const SBasis& a, const SBasis& b);
+SBasis operator-(const SBasis& a, const SBasis& b);
+SBasis& operator+=(SBasis& a, const SBasis& b);
+SBasis& operator-=(SBasis& a, const SBasis& b);
+
+//TODO: remove?
+/*inline SBasis operator+(const SBasis & a, Linear const & b) {
+ if(b.isZero()) return a;
+ if(a.isZero()) return b;
+ SBasis result(a);
+ result[0] += b;
+ return result;
+}
+inline SBasis operator-(const SBasis & a, Linear const & b) {
+ if(b.isZero()) return a;
+ SBasis result(a);
+ result[0] -= b;
+ return result;
+}
+inline SBasis& operator+=(SBasis& a, const Linear& b) {
+ if(a.isZero())
+ a.push_back(b);
+ else
+ a[0] += b;
+ return a;
+}
+inline SBasis& operator-=(SBasis& a, const Linear& b) {
+ if(a.isZero())
+ a.push_back(-b);
+ else
+ a[0] -= b;
+ return a;
+ }*/
+
+//IMPL: OffsetableConcept
+inline SBasis operator+(const SBasis & a, double b) {
+ if(a.isZero()) return Linear(b, b);
+ SBasis result(a);
+ result[0] += b;
+ return result;
+}
+inline SBasis operator-(const SBasis & a, double b) {
+ if(a.isZero()) return Linear(-b, -b);
+ SBasis result(a);
+ result[0] -= b;
+ return result;
+}
+inline SBasis& operator+=(SBasis& a, double b) {
+ if(a.isZero())
+ a = SBasis(Linear(b,b));
+ else
+ a[0] += b;
+ return a;
+}
+inline SBasis& operator-=(SBasis& a, double b) {
+ if(a.isZero())
+ a = SBasis(Linear(-b,-b));
+ else
+ a[0] -= b;
+ return a;
+}
+
+SBasis shift(SBasis const &a, int sh);
+SBasis shift(Linear const &a, int sh);
+
+inline SBasis truncate(SBasis const &a, unsigned terms) {
+ SBasis c;
+ c.insert(c.begin(), a.begin(), a.begin() + std::min(terms, (unsigned)a.size()));
+ return c;
+}
+
+SBasis multiply(SBasis const &a, SBasis const &b);
+// This performs a multiply and accumulate operation in about the same time as multiply. return a*b + c
+SBasis multiply_add(SBasis const &a, SBasis const &b, SBasis c);
+
+SBasis integral(SBasis const &c);
+SBasis derivative(SBasis const &a);
+
+SBasis sqrt(SBasis const &a, int k);
+
+// return a kth order approx to 1/a)
+SBasis reciprocal(Linear const &a, int k);
+SBasis divide(SBasis const &a, SBasis const &b, int k);
+
+inline SBasis operator*(SBasis const & a, SBasis const & b) {
+ return multiply(a, b);
+}
+
+inline SBasis& operator*=(SBasis& a, SBasis const & b) {
+ a = multiply(a, b);
+ return a;
+}
+
+/** Returns the degree of the first non zero coefficient.
+ \param a sbasis function
+ \param tol largest abs val considered 0
+ \return first non zero coefficient
+ \relates SBasis
+*/
+inline unsigned
+valuation(SBasis const &a, double tol=0){
+ unsigned val=0;
+ while( val<a.size() &&
+ fabs(a[val][0])<tol &&
+ fabs(a[val][1])<tol )
+ val++;
+ return val;
+}
+
+// a(b(t))
+SBasis compose(SBasis const &a, SBasis const &b);
+SBasis compose(SBasis const &a, SBasis const &b, unsigned k);
+SBasis inverse(SBasis a, int k);
+//compose_inverse(f,g)=compose(f,inverse(g)), but is numerically more stable in some good cases...
+//TODO: requires g(0)=0 & g(1)=1 atm. generalization should be obvious.
+SBasis compose_inverse(SBasis const &f, SBasis const &g, unsigned order=2, double tol=1e-3);
+
+/** Returns the sbasis on domain [0,1] that was t on [from, to]
+ \param t sbasis function
+ \param from,to interval
+ \return sbasis
+ \relates SBasis
+*/
+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
+SBasis::operator()(SBasis const & g) const {
+ return compose(*this, g);
+}
+
+inline std::ostream &operator<< (std::ostream &out_file, const Linear &bo) {
+ out_file << "{" << bo[0] << ", " << bo[1] << "}";
+ return out_file;
+}
+
+inline std::ostream &operator<< (std::ostream &out_file, const SBasis & p) {
+ for(unsigned i = 0; i < p.size(); i++) {
+ if (i != 0) {
+ out_file << " + ";
+ }
+ out_file << p[i] << "s^" << i;
+ }
+ return out_file;
+}
+
+// These are deprecated, use sbasis-math.h versions if possible
+SBasis sin(Linear bo, int k);
+SBasis cos(Linear bo, int k);
+
+std::vector<double> roots(SBasis const & s);
+std::vector<double> roots(SBasis const & s, Interval const inside);
+std::vector<std::vector<double> > multi_roots(SBasis const &f,
+ std::vector<double> const &levels,
+ double htol=1e-7,
+ double vtol=1e-7,
+ double a=0,
+ double b=1);
+
+//--------- Levelset like functions -----------------------------------------------------
+
+/** Solve f(t) = v +/- tolerance. The collection of intervals where
+ * v - vtol <= f(t) <= v+vtol
+ * is returned (with a precision tol on the boundaries).
+ \param f sbasis function
+ \param level the value of v.
+ \param vtol: error tolerance on v.
+ \param a, b limit search on domain [a,b]
+ \param tol: tolerance on the result bounds.
+ \returns a vector of intervals.
+*/
+std::vector<Interval> level_set (SBasis const &f,
+ double level,
+ double vtol = 1e-5,
+ double a=0.,
+ double b=1.,
+ double tol = 1e-5);
+
+/** Solve f(t)\in I=[u,v], which defines a collection of intervals (J_k). More precisely,
+ * a collection (J'_k) is returned with J'_k = J_k up to a given tolerance.
+ \param f sbasis function
+ \param level: the given interval of deisred values for f.
+ \param a, b limit search on domain [a,b]
+ \param tol: tolerance on the bounds of the result.
+ \returns a vector of intervals.
+*/
+std::vector<Interval> level_set (SBasis const &f,
+ Interval const &level,
+ double a=0.,
+ double b=1.,
+ double tol = 1e-5);
+
+/** 'Solve' f(t) = v +/- tolerance for several values of v at once.
+ \param f sbasis function
+ \param levels vector of values, that should be sorted.
+ \param vtol: error tolerance on v.
+ \param a, b limit search on domain [a,b]
+ \param tol: the bounds of the returned intervals are exact up to that tolerance.
+ \returns a vector of vectors of intervals.
+*/
+std::vector<std::vector<Interval> > level_sets (SBasis const &f,
+ std::vector<double> const &levels,
+ double a=0.,
+ double b=1.,
+ double vtol = 1e-5,
+ double tol = 1e-5);
+
+/** 'Solve' f(t)\in I=[u,v] for several intervals I at once.
+ \param f sbasis function
+ \param levels vector of 'y' intervals, that should be disjoints and sorted.
+ \param a, b limit search on domain [a,b]
+ \param tol: the bounds of the returned intervals are exact up to that tolerance.
+ \returns a vector of vectors of intervals.
+*/
+std::vector<std::vector<Interval> > level_sets (SBasis const &f,
+ std::vector<Interval> const &levels,
+ double a=0.,
+ double b=1.,
+ double tol = 1e-5);
+
+}
+#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 :
+#endif
diff --git a/include/2geom/solver.h b/include/2geom/solver.h
new file mode 100644
index 0000000..5b082cb
--- /dev/null
+++ b/include/2geom/solver.h
@@ -0,0 +1,88 @@
+/**
+ * \file
+ * \brief Finding roots of Bernstein-Bezier polynomials
+ *//*
+ * 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 LIB2GEOM_SEEN_SOLVER_H
+#define LIB2GEOM_SEEN_SOLVER_H
+
+#include <2geom/point.h>
+#include <2geom/sbasis.h>
+#include <vector>
+
+namespace Geom {
+
+ class Point;
+ class Bezier;
+
+unsigned
+crossing_count(Geom::Point const *V, /* Control pts of Bezier curve */
+ unsigned degree); /* Degree of Bezier curve */
+void
+find_parametric_bezier_roots(
+ Geom::Point const *w, /* The control points */
+ unsigned degree, /* The degree of the polynomial */
+ std::vector<double> & solutions, /* RETURN candidate t-values */
+ unsigned depth); /* The depth of the recursion */
+
+unsigned
+crossing_count(double const *V, /* Control pts of Bezier curve */
+ unsigned degree, /* Degree of Bezier curve */
+ double left_t, double right_t);
+
+
+void
+find_bernstein_roots(
+ double const *w, /* The control points */
+ unsigned degree, /* The degree of the polynomial */
+ std::vector<double> & solutions, /* RETURN candidate t-values */
+ unsigned depth, /* The depth of the recursion */
+ double left_t=0, double right_t=1, bool use_secant=true);
+
+};
+
+void
+find_bernstein_roots(std::vector<double> &solutions, /* RETURN candidate t-values */
+ Geom::Bezier const& bz,
+ double left_t, double right_t);
+
+#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/include/2geom/svg-path-parser.h b/include/2geom/svg-path-parser.h
new file mode 100644
index 0000000..e25316c
--- /dev/null
+++ b/include/2geom/svg-path-parser.h
@@ -0,0 +1,199 @@
+/**
+ * \file
+ * \brief parse SVG path specifications
+ *
+ * Copyright 2007 MenTaLguY <mental@rydia.net>
+ * Copyright 2007 Aaron Spike <aaron@ekips.org>
+ *
+ * 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_PARSER_H
+#define LIB2GEOM_SEEN_SVG_PATH_PARSER_H
+
+#include <iostream>
+#include <iterator>
+#include <stdexcept>
+#include <vector>
+#include <cstdio>
+#include <2geom/exception.h>
+#include <2geom/point.h>
+#include <2geom/path-sink.h>
+#include <2geom/forward.h>
+
+namespace Geom {
+
+/** @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);
+ ~SVGPathParser();
+
+ /** @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);
+
+ /** @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();
+
+ /** @brief Set the threshold for considering the closing segment degenerate.
+ * When the current point was reached by a relative command, is closer
+ * to the initial point of the path than the specified threshold
+ * and a 'z' is encountered, the last segment will be adjusted instead so that
+ * the closing segment has exactly zero length. This is useful when reading
+ * SVG 1.1 paths that have non-linear final segments written in relative
+ * coordinates, which always suffer from some loss of precision. SVG 2
+ * allows alternate placement of 'z' which does not have this problem. */
+ void setZSnapThreshold(Coord threshold) { _z_snap_threshold = threshold; }
+ Coord zSnapThreshold() const { return _z_snap_threshold; }
+
+private:
+ bool _absolute;
+ bool _moveto_was_absolute;
+ Point _current;
+ Point _initial;
+ Point _cubic_tangent;
+ Point _quad_tangent;
+ std::vector<Coord> _params;
+ PathSink &_sink;
+ Coord _z_snap_threshold;
+ Curve *_curve;
+
+ 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 _pushCurve(Curve *c);
+
+ 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 ret;
+}
+
+/** @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;
+}
+
+/** @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"));
+ PathVector out = read_svgd_f(fi);
+ fclose(fi);
+ return out;
+}
+
+} // end namespace Geom
+
+#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/include/2geom/svg-path-writer.h b/include/2geom/svg-path-writer.h
new file mode 100644
index 0000000..92a80ec
--- /dev/null
+++ b/include/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() override {}
+
+ void moveTo(Point const &p) override;
+ void lineTo(Point const &p) override;
+ void quadTo(Point const &c, Point const &p) override;
+ void curveTo(Point const &c0, Point const &c1, Point const &p) override;
+ void arcTo(double rx, double ry, double angle,
+ bool large_arc, bool sweep, Point const &p) override;
+ void closePath() override;
+ void flush() override;
+
+ /// 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:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/sweep-bounds.h b/include/2geom/sweep-bounds.h
new file mode 100644
index 0000000..e0ebf29
--- /dev/null
+++ b/include/2geom/sweep-bounds.h
@@ -0,0 +1,62 @@
+/**
+ * \file
+ * \brief Sweepline intersection of groups of rectangles
+ *//*
+ * 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 LIB2GEOM_SEEN_SWEEP_H
+#define LIB2GEOM_SEEN_SWEEP_H
+
+#include <vector>
+#include <2geom/d2.h>
+
+namespace Geom {
+
+std::vector<std::vector<unsigned> >
+sweep_bounds(std::vector<Rect>, Dim2 dim = X);
+
+std::vector<std::vector<unsigned> >
+sweep_bounds(std::vector<Rect>, std::vector<Rect>, Dim2 dim = X);
+
+}
+
+#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/include/2geom/sweeper.h b/include/2geom/sweeper.h
new file mode 100644
index 0000000..233d181
--- /dev/null
+++ b/include/2geom/sweeper.h
@@ -0,0 +1,189 @@
+/** @file
+ * @brief Class for implementing sweepline algorithms
+ *//*
+ * 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 LIB2GEOM_SEEN_SWEEPER_H
+#define LIB2GEOM_SEEN_SWEEPER_H
+
+#include <2geom/coord.h>
+#include <algorithm>
+#include <vector>
+#include <boost/intrusive/list.hpp>
+
+namespace Geom {
+
+// exposition only
+template <typename Item>
+class SweepVector {
+public:
+ typedef typename std::vector<Item>::const_iterator ItemIterator;
+
+ SweepVector(std::vector<Item> const &v)
+ : _items(v)
+ {}
+
+ std::vector<Item> const &items() { return _items; }
+ Interval itemBounds(ItemIterator /*ii*/) { return Interval(); }
+
+ void addActiveItem(ItemIterator /*ii*/) {}
+ void removeActiveItem(ItemIterator /*ii*/) {}
+
+private:
+ std::vector<Item> const &_items;
+};
+
+/** @brief Generic sweepline algorithm.
+ *
+ * This class encapsulates an algorithm that sorts the objects according
+ * to their bounds, then moves an imaginary line (sweepline) over those
+ * bounds from left to right. Objects are added to the active list when
+ * the line starts intersecting their bounds, and removed when it completely
+ * passes over them.
+ *
+ * To use this, create a class that exposes the following methods:
+ * - Range items() - returns a forward iterable range of items that will be swept.
+ * - Interval itemBounds(iterator i) - given an iterator from the above range,
+ * compute the bounding interval of the referenced item in the direction of sweep.
+ * - void addActiveItem(iterator i) - add an item to the active list.
+ * - void removeActiveItem(iterator i) - remove an item from the active list.
+ *
+ * Create the object, then instantiate this template with the above class
+ * as the template parameter, pass it the constructed object of the class,
+ * and call the process() method.
+ *
+ * A good choice for the active list is a Boost intrusive list, which allows
+ * you to get an iterator from a value in constant time.
+ *
+ * Look in path.cpp for example usage.
+ *
+ * @tparam Item The type of items to sweep
+ * @tparam SweepTraits Traits class that defines the items' bounds,
+ * how to interpret them and how to sort the events
+ * @ingroup Utilities
+ */
+template <typename SweepSet>
+class Sweeper {
+public:
+ typedef typename SweepSet::ItemIterator Iter;
+
+ explicit Sweeper(SweepSet &set)
+ : _set(set)
+ {
+ std::size_t sz = std::distance(set.items().begin(), set.items().end());
+ _entry_events.reserve(sz);
+ _exit_events.reserve(sz);
+ }
+
+ /** @brief Process entry and exit events.
+ * This will iterate over all inserted items, calling the methods
+ * addActiveItem and removeActiveItem on the SweepSet passed at construction
+ * according to the order of the boundaries of each item. */
+ void process() {
+ if (_set.items().empty()) return;
+
+ Iter last = _set.items().end();
+ for (Iter i = _set.items().begin(); i != last; ++i) {
+ Interval b = _set.itemBounds(i);
+ // guard against NANs
+ assert(b.min() == b.min() && b.max() == b.max());
+ _entry_events.push_back(Event(b.max(), i));
+ _exit_events.push_back(Event(b.min(), i));
+ }
+
+ std::make_heap(_entry_events.begin(), _entry_events.end());
+ std::make_heap(_exit_events.begin(), _exit_events.end());
+
+ Event next_entry = _get_next(_entry_events);
+ Event next_exit = _get_next(_exit_events);
+
+ while (next_entry || next_exit) {
+ assert(next_exit);
+
+ if (!next_entry || next_exit > next_entry) {
+ // exit event - remove record from active list
+ _set.removeActiveItem(next_exit.item);
+ next_exit = _get_next(_exit_events);
+ } else {
+ // entry event - add record to active list
+ _set.addActiveItem(next_entry.item);
+ next_entry = _get_next(_entry_events);
+ }
+ }
+ }
+
+private:
+ struct Event
+ : boost::totally_ordered<Event>
+ {
+ Coord coord;
+ Iter item;
+
+ Event(Coord c, Iter const &i)
+ : coord(c), item(i)
+ {}
+ Event()
+ : coord(nan("")), item()
+ {}
+ bool operator<(Event const &other) const { return coord < other.coord; }
+ bool operator==(Event const &other) const { return coord == other.coord; }
+ operator bool() const { return !std::isnan(coord); }
+ };
+
+ static Event _get_next(std::vector<Event> &heap) {
+ if (heap.empty()) {
+ Event e;
+ return e;
+ }
+ std::pop_heap(heap.begin(), heap.end());
+ Event ret = heap.back();
+ heap.pop_back();
+ return ret;
+ }
+
+ SweepSet &_set;
+ std::vector<Event> _entry_events;
+ std::vector<Event> _exit_events;
+};
+
+} // namespace Geom
+
+#endif // !LIB2GEOM_SEEN_SWEEPER_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/include/2geom/symbolic/determinant-minor.h b/include/2geom/symbolic/determinant-minor.h
new file mode 100644
index 0000000..d70c397
--- /dev/null
+++ b/include/2geom/symbolic/determinant-minor.h
@@ -0,0 +1,175 @@
+/*
+ * GiNaC Copyright (C) 1999-2008 Johannes Gutenberg University Mainz, Germany
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _GEOM_SL_DETERMINANT_MINOR_H_
+#define _GEOM_SL_DETERMINANT_MINOR_H_
+
+#include <map>
+
+
+namespace Geom { namespace SL {
+
+/*
+ * determinant_minor
+ * This routine has been taken from the ginac project
+ * and adapted as needed; comments are the original ones.
+ */
+
+/** Recursive determinant for small matrices having at least one symbolic
+ * entry. The basic algorithm, known as Laplace-expansion, is enhanced by
+ * some bookkeeping to avoid calculation of the same submatrices ("minors")
+ * more than once. According to W.M.Gentleman and S.C.Johnson this algorithm
+ * is better than elimination schemes for matrices of sparse multivariate
+ * polynomials and also for matrices of dense univariate polynomials if the
+ * matrix' dimesion is larger than 7.
+ *
+ * @return the determinant as a new expression (in expanded form)
+ * @see matrix::determinant() */
+
+template< typename Coeff >
+Coeff determinant_minor(Matrix<Coeff> const& M)
+{
+ assert(M.rows() == M.columns());
+ // for small matrices the algorithm does not make any sense:
+ const unsigned int n = M.columns();
+ if (n == 1)
+ return M(0,0);
+ if (n == 2)
+ return (M(0,0) * M(1,1) - M(0,1) * M(1,0));
+ if (n == 3)
+ return ( M(0,0)*M(1,1)*M(2,2) + M(0,2)*M(1,0)*M(2,1)
+ + M(0,1)*M(1,2)*M(2,0) - M(0,2)*M(1,1)*M(2,0)
+ - M(0,0)*M(1,2)*M(2,1) - M(0,1)*M(1,0)*M(2,2) );
+
+ // This algorithm can best be understood by looking at a naive
+ // implementation of Laplace-expansion, like this one:
+ // ex det;
+ // matrix minorM(this->rows()-1,this->cols()-1);
+ // for (unsigned r1=0; r1<this->rows(); ++r1) {
+ // // shortcut if element(r1,0) vanishes
+ // if (m[r1*col].is_zero())
+ // continue;
+ // // assemble the minor matrix
+ // for (unsigned r=0; r<minorM.rows(); ++r) {
+ // for (unsigned c=0; c<minorM.cols(); ++c) {
+ // if (r<r1)
+ // minorM(r,c) = m[r*col+c+1];
+ // else
+ // minorM(r,c) = m[(r+1)*col+c+1];
+ // }
+ // }
+ // // recurse down and care for sign:
+ // if (r1%2)
+ // det -= m[r1*col] * minorM.determinant_minor();
+ // else
+ // det += m[r1*col] * minorM.determinant_minor();
+ // }
+ // return det.expand();
+ // What happens is that while proceeding down many of the minors are
+ // computed more than once. In particular, there are binomial(n,k)
+ // kxk minors and each one is computed factorial(n-k) times. Therefore
+ // it is reasonable to store the results of the minors. We proceed from
+ // right to left. At each column c we only need to retrieve the minors
+ // calculated in step c-1. We therefore only have to store at most
+ // 2*binomial(n,n/2) minors.
+
+ // Unique flipper counter for partitioning into minors
+ std::vector<unsigned int> Pkey;
+ Pkey.reserve(n);
+ // key for minor determinant (a subpartition of Pkey)
+ std::vector<unsigned int> Mkey;
+ Mkey.reserve(n-1);
+ // we store our subminors in maps, keys being the rows they arise from
+ typedef typename std::map<std::vector<unsigned>, Coeff> Rmap;
+ typedef typename std::map<std::vector<unsigned>, Coeff>::value_type Rmap_value;
+ Rmap A;
+ Rmap B;
+ Coeff det;
+ // initialize A with last column:
+ for (unsigned int r = 0; r < n; ++r)
+ {
+ Pkey.erase(Pkey.begin(),Pkey.end());
+ Pkey.push_back(r);
+ A.insert(Rmap_value(Pkey,M(r,n-1)));
+ }
+ // proceed from right to left through matrix
+ for (int c = n-2; c >= 0; --c)
+ {
+ Pkey.erase(Pkey.begin(),Pkey.end()); // don't change capacity
+ Mkey.erase(Mkey.begin(),Mkey.end());
+ for (unsigned int i = 0; i < n-c; ++i)
+ Pkey.push_back(i);
+ unsigned int fc = 0; // controls logic for our strange flipper counter
+ do
+ {
+ det = Geom::SL::zero<Coeff>()();
+ for (unsigned int r = 0; r < n-c; ++r)
+ {
+ // maybe there is nothing to do?
+ if (M(Pkey[r], c).is_zero())
+ continue;
+ // create the sorted key for all possible minors
+ Mkey.erase(Mkey.begin(),Mkey.end());
+ for (unsigned int i = 0; i < n-c; ++i)
+ if (i != r)
+ Mkey.push_back(Pkey[i]);
+ // Fetch the minors and compute the new determinant
+ if (r % 2)
+ det -= M(Pkey[r],c)*A[Mkey];
+ else
+ det += M(Pkey[r],c)*A[Mkey];
+ }
+ // store the new determinant at its place in B:
+ if (!det.is_zero())
+ B.insert(Rmap_value(Pkey,det));
+ // increment our strange flipper counter
+ for (fc = n-c; fc > 0; --fc)
+ {
+ ++Pkey[fc-1];
+ if (Pkey[fc-1]<fc+c)
+ break;
+ }
+ if (fc < n-c && fc > 0)
+ for (unsigned int j = fc; j < n-c; ++j)
+ Pkey[j] = Pkey[j-1]+1;
+ } while(fc);
+ // next column, so change the role of A and B:
+ A.swap(B);
+ B.clear();
+ }
+
+ return det;
+}
+
+
+
+} /*end namespace Geom*/ } /*end namespace SL*/
+
+#endif // _GEOM_SL_DETERMINANT_MINOR_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/include/2geom/symbolic/implicit.h b/include/2geom/symbolic/implicit.h
new file mode 100644
index 0000000..82d77cd
--- /dev/null
+++ b/include/2geom/symbolic/implicit.h
@@ -0,0 +1,353 @@
+/*
+ * Routines to compute the implicit equation of a parametric polynomial curve
+ *
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 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.
+ */
+
+
+#ifndef _GEOM_SL_IMPLICIT_H_
+#define _GEOM_SL_IMPLICIT_H_
+
+
+
+#include <2geom/symbolic/multipoly.h>
+#include <2geom/symbolic/matrix.h>
+
+
+#include <2geom/exception.h>
+
+#include <array>
+
+
+namespace Geom { namespace SL {
+
+typedef MultiPoly<1, double> MVPoly1;
+typedef MultiPoly<2, double> MVPoly2;
+typedef MultiPoly<3, double> MVPoly3;
+typedef std::array<MVPoly1, 3> poly_vector_type;
+typedef std::array<poly_vector_type, 2> basis_type;
+typedef std::array<double, 3> coeff_vector_type;
+
+namespace detail {
+
+/*
+ * transform a univariate polynomial f(t) in a 3-variate polynomial
+ * p(t, x, y) = f(t) * x^i * y^j
+ */
+inline
+void poly1_to_poly3(MVPoly3 & p3, MVPoly1 const& p1, size_t i, size_t j)
+{
+ multi_index_type I = make_multi_index(0, i, j);
+ for (; I[0] < p1.get_poly().size(); ++I[0])
+ {
+ p3.coefficient(I, p1[I[0]]);
+ }
+}
+
+/*
+ * evaluates the degree of a poly_vector_type, such a degree is defined as:
+ * deg({p[0](t), p[1](t), p[2](t)}) := {max(deg(p[i](t)), i = 0, 1, 2), k}
+ * here k is the index where the max is achieved,
+ * if deg(p[i](t)) == deg(p[j](t)) and i < j then k = i
+ */
+inline
+std::pair<size_t, size_t> deg(poly_vector_type const& p)
+{
+ std::pair<size_t, size_t> d;
+ d.first = p[0].get_poly().real_degree();
+ d.second = 0;
+ size_t k = p[1].get_poly().real_degree();
+ if (d.first < k)
+ {
+ d.first = k;
+ d.second = 1;
+ }
+ k = p[2].get_poly().real_degree();
+ if (d.first < k)
+ {
+ d.first = k;
+ d.second = 2;
+ }
+ return d;
+}
+
+} // end namespace detail
+
+
+/*
+ * A polynomial parametrization could be seen as 1-variety V in R^3,
+ * intersection of two surfaces x = f(t), y = g(t), this variety V has
+ * attached an ideal I in the ring of polynomials in t, x, y with coefficients
+ * on reals; a basis of generators for I is given by p(t, x, y) = x - f(t),
+ * q(t, x, y) = y - g(t); such a basis has the nice property that could be
+ * written as a couple of vectors of dim 3 with entries in R[t]; the original
+ * polinomials p and q can be obtained by doing a dot product between each
+ * vector and the vector {x, y, 1}
+ * As reference you can read the text book:
+ * Ideals, Varieties and Algorithms by Cox, Little, O'Shea
+ */
+inline
+void make_initial_basis(basis_type& b, MVPoly1 const& p, MVPoly1 const& q)
+{
+ // first basis vector
+ b[0][0] = 1;
+ b[0][1] = 0;
+ b[0][2] = -p;
+
+ // second basis vector
+ b[1][0] = 0;
+ b[1][1] = 1;
+ b[1][2] = -q;
+}
+
+/*
+ * Starting from the initial basis for the ideal I is possible to make up
+ * a new basis, still showing off the nice property that each generator is
+ * a moving line that is a linear combination of x, y, 1 where the coefficients
+ * are polynomials in R[t], and moreover each generator is of minimal degree.
+ * Can be proved that given a polynomial parametrization f(t), g(t)
+ * we are able to make up a "micro" basis of generators p(t, x, y), q(t, x, y)
+ * for the ideal I such that the deg(p, t) = m <= n/2 and deg(q, t) = n - m,
+ * where n = max(deg(f(t)), deg(g(t))); this let us halve the order of
+ * the Bezout matrix.
+ * Reference:
+ * Zheng, Sederberg - A Direct Approach to Computing the micro-basis
+ * of a Planar Rational Curves
+ * Deng, Chen, Shen - Computing micro-Basis of Rational Curves and Surfaces
+ * Using Polynomial Matrix Factorization
+ */
+inline
+void microbasis(basis_type& b, MVPoly1 const& p, MVPoly1 const& q)
+{
+ typedef std::pair<size_t, size_t> degree_pair_t;
+
+ size_t n = std::max(p.get_poly().real_degree(), q.get_poly().real_degree());
+ make_initial_basis(b, p, q);
+ degree_pair_t n0 = detail::deg(b[0]);
+ degree_pair_t n1 = detail::deg(b[1]);
+ size_t d;
+ double r0, r1;
+ //size_t iter = 0;
+ while ((n0.first + n1.first) > n)// && iter < 30)
+ {
+// ++iter;
+// std::cout << "iter = " << iter << std::endl;
+// for (size_t i= 0; i < 2; ++i)
+// for (size_t j= 0; j < 3; ++j)
+// std::cout << b[i][j] << std::endl;
+// std::cout << n0.first << ", " << n0.second << std::endl;
+// std::cout << n1.first << ", " << n1.second << std::endl;
+// std::cout << "-----" << std::endl;
+// if (n0.first < n1.first)
+// {
+// d = n1.first - n0.first;
+// r = b[1][n1.second][n1.first] / b[0][n1.second][n0.first];
+// for (size_t i = 0; i < b[0].size(); ++i)
+// b[1][i] -= ((r * b[0][i]).get_poly() << d);
+// b[1][n1.second][n1.first] = 0;
+// n1 = detail::deg(b[1]);
+// }
+// else
+// {
+// d = n0.first - n1.first;
+// r = b[0][n0.second][n0.first] / b[1][n0.second][n1.first];
+// for (size_t i = 0; i < b[0].size(); ++i)
+// b[0][i] -= ((r * b[1][i]).get_poly() << d);
+// b[0][n0.second][n0.first] = 0;
+// n0 = detail::deg(b[0]);
+// }
+
+ // this version shouldn't suffer of ill-conditioning due to
+ // cancellation issue
+ if (n0.first < n1.first)
+ {
+ d = n1.first - n0.first;
+ r0 = b[0][n1.second][n0.first];
+ r1 = b[1][n1.second][n1.first];
+ for (size_t i = 0; i < b[0].size(); ++i)
+ {
+ b[1][i] *= r0;
+ b[1][i] -= ((r1 * b[0][i]).get_poly() << d);
+ // without the following division the modulus grows
+ // beyond the limit of the double type
+ b[1][i] /= r0;
+ }
+ n1 = detail::deg(b[1]);
+ }
+ else
+ {
+ d = n0.first - n1.first;
+ r0 = b[0][n1.second][n0.first];
+ r1 = b[1][n1.second][n1.first];
+
+ for (size_t i = 0; i < b[0].size(); ++i)
+ {
+ b[0][i] *= r1;
+ b[0][i] -= ((r0 * b[1][i]).get_poly() << d);
+ b[0][i] /= r1;
+ }
+ n0 = detail::deg(b[0]);
+ }
+
+ }
+}
+
+/*
+ * computes the dot product:
+ * p(t, x, y) = {p0(t), p1(t), p2(t)} . {x, y, 1}
+ */
+inline
+void basis_to_poly(MVPoly3 & p0, poly_vector_type const& v)
+{
+ MVPoly3 p1, p2;
+ detail::poly1_to_poly3(p0, v[0], 1,0);
+ detail::poly1_to_poly3(p1, v[1], 0,1);
+ detail::poly1_to_poly3(p2, v[2], 0,0);
+ p0 += p1;
+ p0 += p2;
+}
+
+
+/*
+ * Make up a Bezout matrix with two basis genarators as input.
+ *
+ * A Bezout matrix is the matrix related to the symmetric bilinear form
+ * (f,g) -> B[f,g] where B[f,g](s,t) = (f(t)*g(s) - f(s)*g(t))/(s-t)
+ * where f, g are polynomials, this function is called a bezoutian.
+ * Given a basis of generators {p(t, x, y), q(t, x, y)} for the ideal I
+ * related to our parametrization x = f(t), y = g(t), we are able to prove
+ * that the implicit equation of such polynomial parametrization can be
+ * evaluated computing the determinant of the Bezout matrix made up using
+ * the polinomial p and q as univariate polynomials in t with coefficients
+ * in R[x,y], so the resulting Bezout matrix will be a matrix with bivariate
+ * polynomials as entries. A Bezout matrix is always symmetric.
+ * Reference:
+ * Sederberg, Zheng - Algebraic Methods for Computer Aided Geometric Design
+ */
+Matrix<MVPoly2>
+make_bezout_matrix (MVPoly3 const& p, MVPoly3 const& q)
+{
+ size_t pdeg = p.get_poly().real_degree();
+ size_t qdeg = q.get_poly().real_degree();
+ size_t n = std::max(pdeg, qdeg);
+
+ Matrix<MVPoly2> BM(n, n);
+ //std::cerr << "rows, columns " << BM.rows() << " , " << BM.columns() << std::endl;
+ for (size_t i = n; i >= 1; --i)
+ {
+ for (size_t j = n; j >= i; --j)
+ {
+ size_t m = std::min(i, n + 1 - j);
+ //std::cerr << "m = " << m << std::endl;
+ for (size_t k = 1; k <= m; ++k)
+ {
+ //BM(i-1,j-1) += (p[j-1+k] * q[i-k] - p[i-k] * q[j-1+k]);
+ BM(n-i,n-j) += (p.coefficient(j-1+k) * q.coefficient(i-k)
+ - p.coefficient(i-k) * q.coefficient(j-1+k));
+ }
+ }
+ }
+
+ for (size_t i = 0; i < n; ++i)
+ {
+ for (size_t j = 0; j < i; ++j)
+ BM(j,i) = BM(i,j);
+ }
+ return BM;
+}
+
+/*
+ * Make a matrix that represents a main minor (i.e. with the diagonal
+ * on the diagonal of the matrix to which it owns) of the Bezout matrix
+ * with order n-1 where n is the order of the Bezout matrix.
+ * The minor is obtained by removing the "h"-th row and the "h"-th column,
+ * and as the Bezout matrix is symmetric.
+ */
+Matrix<MVPoly2>
+make_bezout_main_minor (MVPoly3 const& p, MVPoly3 const& q, size_t h)
+{
+ size_t pdeg = p.get_poly().real_degree();
+ size_t qdeg = q.get_poly().real_degree();
+ size_t n = std::max(pdeg, qdeg);
+
+ Matrix<MVPoly2> BM(n-1, n-1);
+ size_t u = 0, v;
+ for (size_t i = 1; i <= n; ++i)
+ {
+ v = 0;
+ if (i == h)
+ {
+ u = 1;
+ continue;
+ }
+ for (size_t j = 1; j <= i; ++j)
+ {
+ if (j == h)
+ {
+ v = 1;
+ continue;
+ }
+ size_t m = std::min(i, n + 1 - j);
+ for (size_t k = 1; k <= m; ++k)
+ {
+ //BM(i-u-1,j-v-1) += (p[j-1+k] * q[i-k] - p[i-k] * q[j-1+k]);
+ BM(i-u-1,j-v-1) += (p.coefficient(j-1+k) * q.coefficient(i-k)
+ - p.coefficient(i-k) * q.coefficient(j-1+k));
+ }
+ }
+ }
+
+ --n;
+ for (size_t i = 0; i < n; ++i)
+ {
+ for (size_t j = 0; j < i; ++j)
+ BM(j,i) = BM(i,j);
+ }
+ return BM;
+}
+
+
+} /*end namespace Geom*/ } /*end namespace SL*/
+
+
+
+
+#endif // _GEOM_SL_IMPLICIT_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/include/2geom/symbolic/matrix.h b/include/2geom/symbolic/matrix.h
new file mode 100644
index 0000000..d9dc690
--- /dev/null
+++ b/include/2geom/symbolic/matrix.h
@@ -0,0 +1,265 @@
+/*
+ * Matrix<CoeffT> class template
+ *
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 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.
+ *
+ */
+
+
+#ifndef _GEOM_SL_MATRIX_H_
+#define _GEOM_SL_MATRIX_H_
+
+
+#include <array>
+#include <vector>
+#include <map>
+
+#include <2geom/point.h>
+#include <2geom/numeric/matrix.h>
+#include <2geom/symbolic/multipoly.h>
+
+
+
+
+namespace Geom { namespace SL {
+
+/*
+ * generic Matrix class template
+ * needed for building up a matrix with polynomial entries
+ */
+template< typename Coeff>
+class Matrix
+{
+ public:
+ typedef Coeff coeff_type;
+ typedef std::vector<coeff_type> container_type;
+
+ Matrix()
+ {}
+
+ Matrix(size_t m, size_t n)
+ : m_data(m*n), m_rows(m), m_columns(n)
+ {
+ }
+
+ void resize(size_t m, size_t n)
+ {
+ m_data.resize(m,n);
+ m_rows = m;
+ m_columns = n;
+ }
+
+ size_t rows() const
+ {
+ return m_rows;
+ }
+
+ size_t columns() const
+ {
+ return m_columns;
+ }
+
+ coeff_type const& operator() (size_t i, size_t j) const
+ {
+ return m_data[i * columns() + j];
+ }
+
+ coeff_type & operator() (size_t i, size_t j)
+ {
+ return m_data[i * columns() + j];
+ }
+
+
+ private:
+ container_type m_data;
+ size_t m_rows;
+ size_t m_columns;
+};
+
+
+template< typename Coeff, typename charT >
+inline
+std::basic_ostream<charT> &
+operator<< ( std::basic_ostream<charT> & os,
+ const Matrix<Coeff> & _matrix )
+{
+ if (_matrix.rows() == 0 || _matrix.columns() == 0) return os;
+
+ os << "{{" << _matrix(0,0);
+ for (size_t j = 1; j < _matrix.columns(); ++j)
+ {
+ os << ", " << _matrix(0,j);
+ }
+ os << "}";
+
+ for (size_t i = 1; i < _matrix.rows(); ++i)
+ {
+ os << ", {" << _matrix(i,0);
+ for (size_t j = 1; j < _matrix.columns(); ++j)
+ {
+ os << ", " << _matrix(i,j);
+ }
+ os << "}";
+ }
+ os << "}";
+ return os;
+}
+
+template <size_t N, typename CoeffT, typename T>
+void polynomial_matrix_evaluate (Matrix<T> & A,
+ Matrix< MultiPoly<N, CoeffT> > const& M,
+ std::array<T, N> const& X)
+{
+ A.resize(M.rows(), M.columns());
+ for (size_t i = 0; i < M.rows(); ++i)
+ {
+ for (size_t j = 0; j < M.columns(); ++j)
+ {
+ A(i,j) = M(i,j)(X);
+ }
+ }
+}
+
+
+inline
+void polynomial_matrix_evaluate (NL::Matrix & A,
+ Matrix< MultiPoly<2, double> > const& M,
+ Point const& P)
+{
+ for (size_t i = 0; i < M.rows(); ++i)
+ {
+ for (size_t j = 0; j < M.columns(); ++j)
+ {
+ A(i,j) = M(i,j)(P[X], P[Y]);
+ }
+ }
+}
+
+
+/*
+template< typename Coeff>
+class SymmetricSquareMatrix
+{
+ public:
+ typedef Coeff coeff_type;
+ typedef std::vector<coeff_type> container_type;
+
+ SymmetricSquareMatrix(size_t n)
+ : m_data((n*n)/2 + n), m_size(n)
+ {
+
+ }
+
+ size_t rows() const
+ {
+ return m_size;
+ }
+
+ size_t columns() const
+ {
+ return m_size;
+ }
+
+ coeff_type const& operator() (size_t i, size_t j) const
+ {
+ return m_data[i * columns() + j];
+ }
+
+ coeff_type & operator() (size_t i, size_t j)
+ {
+ return m_data[i * columns() + j];
+ }
+
+ coeff_type det()
+ {
+
+ }
+
+ private:
+ container_type m_data;
+ size_t m_size;
+};
+*/
+
+/*
+ * This is an adaptation of the LU algorithm used in the numerical case.
+ * This algorithm is based on the article due to Bareiss:
+ * "Sylvester's identity and multistep integer-preserving Gaussian elimination"
+ */
+
+/*
+template< typename CoeffT >
+CoeffT det(Matrix<CoeffT> const& M)
+{
+ assert(M.rows() == M.columns());
+
+ Matrix<CoeffT> A(M);
+ CoeffT n;
+ CoeffT d = one<CoeffT>()();
+ for (size_t k = 1; k < A.rows(); ++k)
+ {
+ for (size_t i = k; i < A.rows(); ++i)
+ {
+ for (size_t j = k; j < A.columns(); ++j)
+ {
+ n = A(i,j) * A(k-1,k-1) - A(k-1,j) * A(i,k-1);
+// std::cout << "k, i, j: "
+// << k << ", " << i << ", " << j << std::endl;
+// std::cout << "n = " << n << std::endl;
+// std::cout << "d = " << d << std::endl;
+ A(i,j) = factor(n, d);
+ }
+ }
+ d = A(k-1,k-1);
+ }
+ return A(A.rows()-1, A.columns()-1);
+}
+*/
+
+
+
+} /*end namespace Geom*/ } /*end namespace SL*/
+
+
+#include <2geom/symbolic/determinant-minor.h>
+
+
+#endif // _GEOM_SL_MATRIX_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/include/2geom/symbolic/multi-index.h b/include/2geom/symbolic/multi-index.h
new file mode 100644
index 0000000..311fae8
--- /dev/null
+++ b/include/2geom/symbolic/multi-index.h
@@ -0,0 +1,169 @@
+/*
+ * A multi-index is an ordered sequence of unsigned int
+ *
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 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.
+ */
+
+#ifndef _GEOM_SL_MULTI_INDEX_H_
+#define _GEOM_SL_MULTI_INDEX_H_
+
+
+#include <2geom/exception.h>
+
+#include <valarray>
+
+#include <boost/preprocessor/cat.hpp>
+#include <boost/preprocessor/repetition/enum_params.hpp>
+#include <boost/preprocessor/repetition/repeat.hpp>
+#include <boost/preprocessor/repetition/repeat_from_to.hpp>
+
+
+
+
+/*
+ * an helper macro for generating function with declaration:
+ * multi_index_type make_multi_index (size_t i0, ..., size_t iN)
+ * that is a facility to make up a multi-index from a list of values
+ */
+
+#define GEOM_SL_MAX_RANK 10
+
+#define GEOM_SL_ASSIGN_INDEX(z, k, unused) I[k] = BOOST_PP_CAT(i, k);
+
+#define GEOM_SL_MAKE_MULTI_INDEX(z, N, unused) \
+inline \
+multi_index_type make_multi_index (BOOST_PP_ENUM_PARAMS(N, size_t i)) \
+{ \
+ multi_index_type I(N); \
+ BOOST_PP_REPEAT(N, GEOM_SL_ASSIGN_INDEX, unused) \
+ return I; \
+}
+// end macro GEOM_SL_MAKE_MULTI_INDEX
+
+
+
+
+namespace Geom { namespace SL {
+
+/*
+ * A multi-index is an ordered sequence of unsigned int;
+ * it's useful for representing exponent, degree and coefficient index
+ * of a multi-variate polynomial;
+ * example: given a monomial x_(0)^i_(0)*x_(1)^i_(1)*...*x_(N-1)^i_(N-1)
+ * we can write it in the simpler form X^I where X=(x_(0), .., x_(N-1))
+ * and I=(i_(0), .., i_(N-1)) is a multi-index
+ * A multi-index is represented as a valarray this let us make simple
+ * arithmetic operations on a multi-index
+ */
+
+typedef std::valarray<size_t> multi_index_type;
+
+
+// make up a multi-index of size N and fill it with zeroes
+inline
+multi_index_type multi_index_zero(size_t N)
+{
+ return multi_index_type(N);
+}
+
+// helper functions for generating a multi-index from a list of values
+// we create an amount of GEOM_SL_MAX_RANK of suzh functions
+BOOST_PP_REPEAT_FROM_TO(0, GEOM_SL_MAX_RANK, GEOM_SL_MAKE_MULTI_INDEX, unused)
+
+
+// helper function for generating a multi-index of size N
+// from a single index v that is placed at position i with i in [0,N[
+template <size_t N>
+inline
+multi_index_type make_multi_index(size_t i, size_t v)
+{
+ if (!(i < N))
+ THROW_RANGEERROR ("make_multi_index<N> from a single index: "
+ "out of range position");
+ multi_index_type I(N);
+ I[i] = v;
+ return I;
+}
+
+// transform a N size multi-index in (N-1)-size multi-index
+// by removing the first index: (i1, i2,...,iN) -> (i2,..,iN)
+inline
+multi_index_type shift(multi_index_type const& I, size_t i = 1)
+{
+ size_t N = I.size() - i;
+ multi_index_type J = I[std::slice(i, N, 1)];
+ return J;
+}
+
+// valarray operator== returns a valarray of bool
+inline
+bool is_equal(multi_index_type const& I, multi_index_type const& J)
+{
+ if (I.size() != J.size()) return false;
+ for (size_t i = 0; i < I.size(); ++i)
+ if (I[i] != J[i]) return false;
+ return true;
+}
+
+// extended operator<< for printing a multi-index
+template <typename charT>
+inline
+std::basic_ostream<charT> &
+operator<< (std::basic_ostream<charT> & os,
+ const Geom::SL::multi_index_type & I)
+{
+ if (I.size() == 0 ) return os;
+ os << "[" << I[0];
+ for (unsigned int i = 1; i < I.size(); ++i)
+ {
+ os << ", " << I[i];
+ }
+ os << "]";
+ return os;
+}
+
+} /*end namespace Geom*/ } /*end namespace SL*/
+
+// argument dependent name lookup doesn't work with typedef
+using Geom::SL::operator<<;
+
+
+#endif // _GEOM_SL_MULTI_INDEX_
+
+
+/*
+ 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/include/2geom/symbolic/multipoly.h b/include/2geom/symbolic/multipoly.h
new file mode 100644
index 0000000..ab3a5f4
--- /dev/null
+++ b/include/2geom/symbolic/multipoly.h
@@ -0,0 +1,684 @@
+/*
+ * MultiPoly<N, CoeffT> class template
+ *
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 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.
+ */
+
+#ifndef _GEOM_SL_MULTIPOLY_H_
+#define _GEOM_SL_MULTIPOLY_H_
+
+#include <utility>
+
+#include <array>
+#include <functional>
+#include <type_traits>
+
+#include <2geom/symbolic/unity-builder.h>
+#include <2geom/symbolic/mvpoly-tools.h>
+
+
+namespace Geom { namespace SL {
+
+/*
+ * MultiPoly<N, CoeffT> class template
+ *
+ * It represents a multi-variate polynomial with N indeterminates
+ * and coefficients of type CoeffT, but it doesn't support explicit
+ * symbol attaching; the indeterminates should be thought as implicitly
+ * defined in an automatic enumerative style: x_(0),...,x_(N-1) .
+ *
+ */
+
+template <size_t N, typename CoeffT>
+class MultiPoly
+{
+public:
+ typedef typename mvpoly<N, CoeffT>::type poly_type;
+ typedef CoeffT coeff_type;
+ static const size_t rank = N;
+
+public:
+ MultiPoly()
+ {
+ }
+
+ MultiPoly(poly_type p)
+ : m_poly(std::move(p))
+ {
+ }
+
+ // create a mv polynomial of type c*X^I
+ MultiPoly(coeff_type c, multi_index_type const& I = multi_index_zero(N))
+ : m_poly(monomial<N, coeff_type>::make(I, c))
+ {
+ }
+
+ // create a mv polynomial p(x_(N-M),...,x_(N-1))*X'^I
+ // where I.size() == N-M and X'=(x_(0),...,x_(N-M-1))
+ template <size_t M>
+ MultiPoly (MultiPoly<M, CoeffT> const& p,
+ multi_index_type const& I = multi_index_zero(N-M),
+ typename std::enable_if_t<(M > 0) && (M < N)>* = 0)
+ {
+ Geom::SL::coefficient<N-M-1, poly_type>::set_safe(m_poly, I, p.m_poly);
+ }
+
+ /*
+ * assignment operators
+ */
+ MultiPoly& operator=(poly_type const& p)
+ {
+ m_poly = p;
+ return (*this);
+ }
+
+ MultiPoly& operator=(coeff_type const& c)
+ {
+ multi_index_type I = multi_index_zero(N);
+ (*this) = MultiPoly(c);
+ return (*this);
+ }
+
+ // return the degree of the mv polynomial wrt the ordering OrderT
+ template <typename OrderT>
+ multi_index_type degree() const
+ {
+ return Geom::SL::mvdegree<N, CoeffT, OrderT>::value(m_poly);
+ }
+
+ // return the coefficient of the term with the highest degree
+ // wrt the ordering OrderT
+ template <typename OrderT>
+ coeff_type const& leading_coefficient() const
+ {
+ return (*this)(degree<OrderT>());
+ }
+
+ template <typename OrderT>
+ coeff_type & leading_coefficient()
+ {
+ return (*this)(degree<OrderT>());
+ }
+
+ // return the coefficient of the term of degree 0 (wrt all indeterminates)
+ coeff_type const& trailing_coefficient() const
+ {
+ return (*this)(multi_index_zero(N));
+ }
+
+ coeff_type & trailing_coefficient()
+ {
+ return (*this)(multi_index_zero(N));
+ }
+
+ // access coefficient methods with no out-of-range checking
+ coeff_type const& operator() (multi_index_type const& I) const
+ {
+ return Geom::SL::coefficient<N-1, poly_type>::get(m_poly, I);
+ }
+
+ coeff_type & operator() (multi_index_type const& I)
+ {
+ return Geom::SL::coefficient<N-1, poly_type>::get(m_poly, I);
+ }
+
+ // safe coefficient get method
+ coeff_type const& coefficient(multi_index_type const& I) const
+ {
+ return Geom::SL::coefficient<N-1, poly_type>::get_safe(m_poly, I);
+ }
+
+ // safe coefficient set method
+ void coefficient(multi_index_type const& I, coeff_type const& c)
+ {
+ Geom::SL::coefficient<N-1, poly_type>::set_safe(m_poly, I, c);
+ }
+
+ // access the mv poly of rank N-1 with no out-of-range checking
+ typename poly_type::coeff_type const&
+ operator[] (size_t const& i) const
+ {
+ return m_poly[i];
+ }
+
+ typename poly_type::coeff_type &
+ operator[] (size_t const& i)
+ {
+ return m_poly[i];
+ }
+
+ // safe access to the mv poly of rank N-1
+ typename poly_type::coeff_type const&
+ coefficient(size_t const& i) const
+ {
+ return m_poly.coefficient(i);
+ }
+
+ void coefficient (size_t const& i,
+ typename poly_type::coeff_type const& c)
+ {
+ m_poly.coefficient(i, c);
+ }
+
+ /*
+ * polynomail evaluation:
+ * T can be any type that is able to be + and * with the coefficient type
+ */
+ template <typename T>
+ T operator() (std::array<T, N> const& X) const
+ {
+ return Geom::SL::mvpoly<N, CoeffT>::evaluate(m_poly, X);
+ }
+
+ template <typename T>
+ typename std::enable_if_t<(N == 1), T>
+ operator() (T const& x0) const
+ {
+ std::array<T, N> X = {{x0}};
+ return Geom::SL::mvpoly<N, CoeffT>::evaluate(m_poly, X);
+ }
+
+ template <typename T>
+ typename std::enable_if_t<(N == 2), T>
+ operator() (T const& x0, T const& x1) const
+ {
+ std::array<T, N> X = {{x0, x1}};
+ return Geom::SL::mvpoly<N, CoeffT>::evaluate(m_poly, X);
+ }
+
+ template <typename T>
+ typename std::enable_if_t<(N == 3), T>
+ operator() (T const& x0, T const& x1, T const& x2) const
+ {
+ std::array<T, N> X = {{x0, x1, x2}};
+ return Geom::SL::mvpoly<N, CoeffT>::evaluate(m_poly, X);
+ }
+
+ /*
+ * trim leading zero coefficients
+ */
+ void normalize()
+ {
+ Geom::SL::mvpoly<N, CoeffT>::normalize(m_poly);
+ }
+
+ /*
+ * select the sub multi-variate polynomial with rank M
+ * which is unambiguously characterized by the multi-index I
+ * requirements:
+ * - M > 0 && M < N;
+ * - multi-index size == N-M
+ */
+ template <size_t M>
+ typename mvpoly<M, CoeffT>::type const&
+ select (multi_index_type const& I= multi_index_zero(N-M),
+ typename std::enable_if_t<(M > 0) && (M < N)>* = 0) const
+ {
+ return Geom::SL::coefficient<N-M-1, poly_type>::get_safe(m_poly, I);
+ }
+
+ poly_type const& get_poly() const
+ {
+ return m_poly;
+ }
+
+ bool is_zero() const
+ {
+ return ((*this) == zero);
+ }
+
+ // return the opposite mv poly
+ MultiPoly operator-() const
+ {
+ MultiPoly r(-m_poly);
+ return r;
+ }
+
+ /*
+ * multipoly-multipoly mutating operators
+ */
+ MultiPoly& operator+=(MultiPoly const& p)
+ {
+ m_poly += p.m_poly;
+ return (*this);
+ }
+
+ MultiPoly& operator-=(MultiPoly const& p)
+ {
+ m_poly -= p.m_poly;
+ return (*this);
+ }
+
+ MultiPoly& operator*=(MultiPoly const& p)
+ {
+ m_poly *= p.m_poly;
+ return (*this);
+ }
+
+ MultiPoly& operator<<=(multi_index_type const& I)
+ {
+ Geom::SL::mvpoly<N, CoeffT>::shift(m_poly, I);
+ return (*this);
+ }
+
+ bool operator==(MultiPoly const& q) const
+ {
+ return (m_poly == q.m_poly);
+ }
+
+ bool operator!=(MultiPoly const& q) const
+ {
+ return !((*this) == q);
+ }
+
+ /*
+ * multipoly-coefficient mutating operators
+ */
+ MultiPoly& operator+=(CoeffT const& c)
+ {
+ trailing_coefficient() += c;
+ return (*this);
+ }
+
+ MultiPoly& operator-=(CoeffT const& c)
+ {
+ trailing_coefficient() -= c;
+ return (*this);
+ }
+
+ MultiPoly& operator*=(CoeffT const& c)
+ {
+ mvpoly<N, CoeffT>::template
+ for_each<0>(m_poly, std::bind(mvpoly<0, CoeffT>::multiply_to, std::placeholders::_1, c));
+ return (*this);
+ }
+
+ MultiPoly& operator/=(CoeffT const& c)
+ {
+ mvpoly<N, CoeffT>::template
+ for_each<0>(m_poly, std::bind(mvpoly<0, CoeffT>::divide_to, std::placeholders::_1, c));
+ return (*this);
+ }
+
+ /*
+ * multipoly-polynomial mutating operators
+ */
+ MultiPoly& operator+=(poly_type const& p)
+ {
+ m_poly += p;
+ return (*this);
+ }
+
+ MultiPoly& operator-=(poly_type const& p)
+ {
+ m_poly -= p;
+ return (*this);
+ }
+
+ MultiPoly& operator*=(poly_type const& p)
+ {
+ m_poly *= p;
+ return (*this);
+ }
+
+ /*
+ * multipoly<N>-multipoly<M> mutating operators
+ * requirements:
+ * - M > 0 && M < N;
+ * - they must have the same coefficient type.
+ */
+
+ template <size_t M>
+ typename std::enable_if_t<(M > 0) && (M < N), MultiPoly> &
+ operator+= (MultiPoly<M, CoeffT> const& p)
+ {
+ multi_index_type I = multi_index_zero(N-M);
+ Geom::SL::coefficient<N-M-1, poly_type>::get(m_poly, I) += p.m_poly;
+ return (*this);
+ }
+
+ template <size_t M>
+ typename std::enable_if_t<(M > 0) && (M < N), MultiPoly> &
+ operator-= (MultiPoly<M, CoeffT> const& p)
+ {
+ multi_index_type I = multi_index_zero(N-M);
+ Geom::SL::coefficient<N-M-1, poly_type>::get(m_poly, I) -= p.m_poly;
+ return (*this);
+ }
+
+ template <size_t M>
+ typename std::enable_if_t<(M > 0) && (M < N), MultiPoly> &
+ operator*= (MultiPoly<M, CoeffT> const& p)
+ {
+ mvpoly<N, CoeffT>::template
+ for_each<M>(m_poly, std::bind(mvpoly<M, CoeffT>::multiply_to, std::placeholders::_1, p.m_poly));
+ return (*this);
+ }
+
+ /*
+ * we need MultiPoly instantiations to be each other friend
+ * in order to be able of implementing operations between
+ * MultiPoly instantiations with a different ranks
+ */
+ template<size_t M, typename C>
+ friend class MultiPoly;
+
+ template< typename charT, size_t M, typename C>
+ friend
+ std::basic_ostream<charT> &
+ operator<< (std::basic_ostream<charT> & os, const MultiPoly<M, C> & p);
+
+ static const MultiPoly zero;
+ static const MultiPoly one;
+ static const coeff_type zero_coeff;
+ static const coeff_type one_coeff;
+
+private:
+ poly_type m_poly;
+
+}; // end class MultiPoly
+
+
+/*
+ * zero and one element spezcialization for MultiPoly
+ */
+template <size_t N, typename CoeffT>
+struct zero<MultiPoly<N, CoeffT>, false>
+{
+ MultiPoly<N, CoeffT> operator() ()
+ {
+ CoeffT _0c = zero<CoeffT>()();
+ MultiPoly<N, CoeffT> _0(_0c);
+ return _0;
+ }
+};
+
+
+template <size_t N, typename CoeffT>
+struct one<MultiPoly<N, CoeffT>, false>
+{
+ MultiPoly<N, CoeffT> operator() ()
+ {
+ CoeffT _1c = one<CoeffT>()();
+ MultiPoly<N, CoeffT> _1(_1c);
+ return _1;
+ }
+};
+
+
+/*
+ * initialization of MultiPoly static data members
+ */
+template <size_t N, typename CoeffT>
+const MultiPoly<N, CoeffT> MultiPoly<N, CoeffT>::one
+ = Geom::SL::one< MultiPoly<N, CoeffT> >()();
+
+template <size_t N, typename CoeffT>
+const MultiPoly<N, CoeffT> MultiPoly<N, CoeffT>::zero
+ = Geom::SL::zero< MultiPoly<N, CoeffT> >()();
+
+template <size_t N, typename CoeffT>
+const typename MultiPoly<N, CoeffT>::coeff_type MultiPoly<N, CoeffT>::zero_coeff
+ = Geom::SL::zero<typename MultiPoly<N, CoeffT>::coeff_type>()();
+
+template <size_t N, typename CoeffT>
+const typename MultiPoly<N, CoeffT>::coeff_type MultiPoly<N, CoeffT>::one_coeff
+ = Geom::SL::one<typename MultiPoly<N, CoeffT>::coeff_type>()();
+
+
+/*
+ * operator<< extended to print out a mv poly type
+ */
+template <typename charT, size_t N, typename CoeffT>
+inline
+std::basic_ostream<charT> &
+operator<< (std::basic_ostream<charT> & os, const MultiPoly<N, CoeffT> & p)
+{
+ return operator<<(os, p.m_poly);
+}
+
+/*
+ * equivalent to multiply by X^I
+ */
+template <size_t N, typename CoeffT>
+inline
+MultiPoly<N, CoeffT>
+operator<< (MultiPoly<N, CoeffT> const& p, multi_index_type const& I)
+{
+ MultiPoly<N, CoeffT> r(p);
+ r <<= I;
+ return r;
+}
+
+/*
+ * MultiPoly<M, CoeffT> - MultiPoly<N, CoeffT> binary mathematical operators
+ */
+
+template <size_t M, size_t N, typename CoeffT>
+inline
+typename std::enable_if_t<(M > 0) && (M <= N), MultiPoly<N, CoeffT> >
+operator+ (MultiPoly<N, CoeffT> const& p,
+ MultiPoly<M, CoeffT> const& q )
+{
+ MultiPoly<N, CoeffT> r(p);
+ r += q;
+ return r;
+}
+
+template <size_t M, size_t N, typename CoeffT>
+inline
+typename std::enable_if_t<(N > 0) && (M > N), MultiPoly<M, CoeffT> >
+operator+ (MultiPoly<N, CoeffT> const& p,
+ MultiPoly<M, CoeffT> const& q )
+{
+ MultiPoly<M, CoeffT> r(q);
+ r += p;
+ return r;
+}
+
+template <size_t M, size_t N, typename CoeffT>
+inline
+typename std::enable_if_t<(M > 0) && (M <= N), MultiPoly<N, CoeffT> >
+operator- (MultiPoly<N, CoeffT> const& p,
+ MultiPoly<M, CoeffT> const& q )
+{
+ MultiPoly<N, CoeffT> r(p);
+ r -= q;
+ return r;
+}
+
+template <size_t M, size_t N, typename CoeffT>
+inline
+typename std::enable_if_t<(N > 0) && (M > N), MultiPoly<M, CoeffT> >
+operator- (MultiPoly<N, CoeffT> const& p,
+ MultiPoly<M, CoeffT> const& q )
+{
+ MultiPoly<M, CoeffT> r(-q);
+ r += p;
+ return r;
+}
+
+
+template <size_t M, size_t N, typename CoeffT>
+inline
+typename std::enable_if_t<(M > 0) && (M <= N), MultiPoly<N, CoeffT> >
+operator* (MultiPoly<N, CoeffT> const& p,
+ MultiPoly<M, CoeffT> const& q )
+{
+ MultiPoly<N, CoeffT> r(p);
+ r *= q;
+ return r;
+}
+
+template <size_t M, size_t N, typename CoeffT>
+inline
+typename std::enable_if_t<(N > 0) && (M > N), MultiPoly<M, CoeffT> >
+operator* (MultiPoly<N, CoeffT> const& p,
+ MultiPoly<M, CoeffT> const& q )
+{
+ MultiPoly<M, CoeffT> r(q);
+ r *= p;
+ return r;
+}
+
+/*
+ * MultiPoly-coefficient and coefficient-MultiPoly binary mathematical operators
+ */
+
+template <size_t N, typename CoeffT>
+inline
+MultiPoly<N, CoeffT> operator+(MultiPoly<N, CoeffT> const& p, CoeffT const& c)
+{
+ MultiPoly<N, CoeffT> r(p);
+ r += c;
+ return r;
+}
+
+template <size_t N, typename CoeffT>
+inline
+MultiPoly<N, CoeffT> operator+(CoeffT const& c, MultiPoly<N, CoeffT> const& p)
+{
+ MultiPoly<N, CoeffT> r(p);
+ r += c;
+ return r;
+}
+
+template <size_t N, typename CoeffT>
+inline
+MultiPoly<N, CoeffT> operator-(MultiPoly<N, CoeffT> const& p, CoeffT const& c)
+{
+ MultiPoly<N, CoeffT> r(p);
+ r -= c;
+ return r;
+}
+
+template <size_t N, typename CoeffT>
+inline
+MultiPoly<N, CoeffT> operator-(CoeffT const& c, MultiPoly<N, CoeffT> const& p)
+{
+ MultiPoly<N, CoeffT> r(-p);
+ r += c;
+ return r;
+}
+
+template <size_t N, typename CoeffT>
+inline
+MultiPoly<N, CoeffT> operator*(MultiPoly<N, CoeffT> const& p, CoeffT const& c)
+{
+ MultiPoly<N, CoeffT> r(p);
+ r *= c;
+ return r;
+}
+
+template <size_t N, typename CoeffT>
+inline
+MultiPoly<N, CoeffT> operator*(CoeffT const& c, MultiPoly<N, CoeffT> const& p)
+{
+ MultiPoly<N, CoeffT> r(p);
+ r *= c;
+ return r;
+}
+
+
+template <size_t N, typename CoeffT>
+inline
+MultiPoly<N, CoeffT> operator/(MultiPoly<N, CoeffT> const& p, CoeffT const& c)
+{
+ MultiPoly<N, CoeffT> r(p);
+ r /= c;
+ return r;
+}
+
+
+
+
+/*
+template< size_t N, typename CoeffT >
+MultiPoly<N, CoeffT>
+factor( MultiPoly<N, CoeffT> const& f,
+ MultiPoly<N, CoeffT> const& g )
+{
+ typedef MultiPoly<N, CoeffT> poly_type;
+
+ if (g == poly_type::one) return f;
+ poly_type h(g), q, r(f);
+ multi_index_type deg_r = r.template degree<ordering::lex>();
+ multi_index_type deg_g = g.template degree<ordering::lex>();
+ multi_index_type deg0 = multi_index_zero(deg_g.size());
+ CoeffT ltg = g(deg_g);
+ if (is_equal(deg_g, deg0)) return (f / ltg);
+ //h(deg_g) = 0;
+// std::cout << "deg_g = " << deg_g << std::endl;
+// std::cout << "ltg = " << ltg << std::endl;
+ CoeffT lt, ltr;
+ multi_index_type deg(1, deg_g.size());
+ size_t iter = 0;
+ while (!is_equal(deg, deg0) && iter < 10000)
+ {
+ ++iter;
+ deg = deg_r - deg_g;
+ ltr = r(deg_r);
+ lt = ltr / ltg;
+ q.coefficient(deg, lt);
+ //r(deg_r) = 0;
+ r -= ((lt * g) << deg);
+ deg_r = r.template degree<ordering::lex>();
+// std::cout << "deg_r = " << deg_r << std::endl;
+// std::cout << "ltr = " << ltr << std::endl;
+// std::cout << "deg = " << deg << std::endl;
+// std::cout << "lt = " << lt << std::endl;
+// std::cout << "q = " << q << std::endl;
+// std::cout << "r = " << r << std::endl;
+
+// break;
+ }
+ //std::cout << "iter = " << iter << std::endl;
+ return q;
+}
+*/
+
+
+} /*end namespace Geom*/ } /*end namespace SL*/
+
+
+
+
+#endif /* _MULTIPOLY_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/include/2geom/symbolic/mvpoly-tools.h b/include/2geom/symbolic/mvpoly-tools.h
new file mode 100644
index 0000000..34dece7
--- /dev/null
+++ b/include/2geom/symbolic/mvpoly-tools.h
@@ -0,0 +1,690 @@
+/*
+ * Routines that extend univariate polynomial functions
+ * to multi-variate polynomial exploiting recursion at compile time
+ *
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 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.
+ */
+
+#ifndef _GEOM_SL_MVPOLY_TOOLS_H_
+#define _GEOM_SL_MVPOLY_TOOLS_H_
+
+
+#include <2geom/exception.h>
+
+#include <2geom/symbolic/multi-index.h>
+#include <2geom/symbolic/unity-builder.h>
+#include <2geom/symbolic/polynomial.h>
+
+#include <array>
+#include <functional>
+#include <iostream>
+#include <type_traits>
+
+
+namespace Geom { namespace SL {
+
+/*
+ * rank<PolyT>::value == total amount of indeterminates
+ * x_(0),x_(1),...,x_(rank-1) that belong to type PolyT
+ */
+
+template <typename T>
+struct rank
+{
+ static const size_t value = 0;
+};
+
+template <typename CoeffT>
+struct rank< Polynomial<CoeffT> >
+{
+ static const size_t value = rank<CoeffT>::value + 1;
+};
+
+
+/*
+ * mvpoly<N, CoeffT> creates a multi-variate polynomial type
+ * by nesting N-1 Polynomial class template and setting
+ * the coefficient type of the most nested Polynomial to CoeffT
+ * example: mvpoly<3, double>::type is the same than
+ * Polynomial< Polynomial< Polynomial<double> > >
+ */
+
+template <size_t N, typename CoeffT>
+struct mvpoly
+{
+ typedef Polynomial<typename mvpoly<N-1, CoeffT>::type> type;
+ typedef CoeffT coeff_type;
+ static const size_t rank = N;
+
+ /*
+ * computes the lexicographic degree of the mv polynomial p
+ */
+ static
+ multi_index_type lex_degree (type const& p)
+ {
+ multi_index_type D(N);
+ lex_degree_impl<0>(p, D);
+ return D;
+ }
+
+ /*
+ * Returns in the out-parameter D an N-sequence where each entry value
+ * represents the max degree of the polynomial related to the passed
+ * index I, if one index value in I is greater than the related max degree
+ * the routine returns false otherwise it returns true.
+ * This routine can be used to test if a given multi-index I is related
+ * to an actual initialized coefficient.
+ */
+ static
+ bool max_degree (type const& p,
+ multi_index_type& D,
+ multi_index_type const& I)
+ {
+ if (I.size() != N)
+ THROW_RANGEERROR ("multi-index with wrong length");
+ D.resize(N);
+ return max_degree_impl<0>(p, D, I);
+ }
+
+ /*
+ * Returns in the out-parameter D an N-sequence where each entry value
+ * represents the real degree of the polynomial related to the passed
+ * index I, if one index value in I is greater than the related real degree
+ * the routine returns false otherwise it returns true.
+ * This routine can be used to test if a given multi-index I is related
+ * to an actual initialized and non-zero coefficient.
+ */
+
+ static
+ bool real_degree (type const& p,
+ multi_index_type& D,
+ multi_index_type const& I)
+ {
+ if (I.size() != N)
+ THROW_RANGEERROR ("multi-index with wrong length");
+ D.resize(N);
+ return real_degree_impl<0>(p, D, I);
+ }
+
+ /*
+ * Multiplies p by X^I
+ */
+ static
+ void shift(type & p, multi_index_type const& I)
+ {
+ if (I.size() != N)
+ THROW_RANGEERROR ("multi-index with wrong length");
+ shift_impl<0>(p, I);
+ }
+
+ /*
+ * mv poly evaluation:
+ * T can be any type that is able to be += with the coefficient type
+ * and that can be *= with the same type T moreover a specialization
+ * of zero struct for the type T is needed
+ */
+ template <typename T>
+ static
+ T evaluate(type const& p, std::array<T, N> const& X)
+ {
+ return evaluate_impl<T, 0>(p, X);
+ }
+
+ /*
+ * trim leading zero coefficients
+ */
+ static
+ void normalize(type & p)
+ {
+ p.normalize();
+ for (size_t k = 0; k < p.size(); ++k)
+ mvpoly<N-1, CoeffT>::normalize(p[k]);
+ }
+
+ /*
+ * Applies the unary operator "op" to each coefficient of p with rank M.
+ * For instance when M = 0 op is applied to each coefficient
+ * of the multi-variate polynomial p.
+ * When M < N the function call recursively the for_each routine
+ * for p.real_degree() times, when M == N the operator "op" is invoked on p;
+ */
+ template <size_t M>
+ static
+ void for_each
+ (type & p,
+ std::function<void (typename mvpoly<M, CoeffT>::type &)> const& op,
+ typename std::enable_if_t<(M < N)>* = 0)
+ {
+ for (size_t k = 0; k <= p.real_degree(); ++k)
+ {
+ mvpoly<N-1, CoeffT>::template for_each<M>(p[k], op);
+ }
+ }
+
+ template <size_t M>
+ static
+ void for_each
+ (type & p,
+ std::function<void (typename mvpoly<M, CoeffT>::type &)> const& op,
+ typename std::enable_if_t<(M == N)>* = 0)
+ {
+ op(p);
+ }
+
+ // this is only an helper function to be passed to the for_each routine
+ static
+ void multiply_to (type& p, type const& q)
+ {
+ p *= q;
+ }
+
+ private:
+ template <size_t i>
+ static
+ void lex_degree_impl (type const& p, multi_index_type& D)
+ {
+ D[i] = p.real_degree();
+ mvpoly<N-1, CoeffT>::template lex_degree_impl<i+1>(p[D[i]], D);
+ }
+
+ template <size_t i>
+ static
+ bool max_degree_impl (type const& p,
+ multi_index_type& D,
+ multi_index_type const& I)
+ {
+ D[i] = p.max_degree();
+ if (I[i] > D[i]) return false;
+ return
+ mvpoly<N-1, CoeffT>::template max_degree_impl<i+1>(p[I[i]], D, I);
+ }
+
+ template <size_t i>
+ static
+ bool real_degree_impl (type const& p,
+ multi_index_type& D,
+ multi_index_type const& I)
+ {
+ D[i] = p.real_degree();
+ if (I[i] > D[i]) return false;
+ return
+ mvpoly<N-1, CoeffT>::template real_degree_impl<i+1>(p[I[i]], D, I);
+ }
+
+ template <size_t i>
+ static
+ void shift_impl(type & p, multi_index_type const& I)
+ {
+ p <<= I[i];
+ for (size_t k = 0; k < p.size(); ++k)
+ {
+ mvpoly<N-1, CoeffT>::template shift_impl<i+1>(p[k], I);
+ }
+ }
+
+ template <typename T, size_t i>
+ static
+ T evaluate_impl(type const& p, std::array<T, N+i> const& X)
+ {
+// T r = zero<T>()();
+// for (size_t k = p.max_degree(); k > 0; --k)
+// {
+// r += mvpoly<N-1, CoeffT>::template evaluate_impl<T, i+1>(p[k], X);
+// r *= X[i];
+// }
+// r += mvpoly<N-1, CoeffT>::template evaluate_impl<T, i+1>(p[0], X);
+
+ int n = p.max_degree();
+ T r = mvpoly<N-1, CoeffT>::template evaluate_impl<T, i+1>(p[n], X);
+ for (int k = n - 1; k >= 0; --k)
+ {
+ r *= X[i];
+ r += mvpoly<N-1, CoeffT>::template evaluate_impl<T, i+1>(p[k], X);
+ }
+ return r;
+ }
+
+ template <size_t M, typename C>
+ friend struct mvpoly;
+};
+
+/*
+ * rank 0 mv poly, that is a scalar value (usually a numeric value),
+ * the routines implemented here are used only to stop recursion
+ * (but for_each)
+ */
+template< typename CoeffT >
+struct mvpoly<0, CoeffT>
+{
+ typedef CoeffT type;
+ typedef CoeffT coeff_type;
+ static const size_t rank = 0;
+
+ template <size_t M>
+ static
+ void for_each
+ (type & p,
+ std::function<void (typename mvpoly<M, CoeffT>::type &)> const& op,
+ typename std::enable_if_t<(M == 0)>* = 0)
+ {
+ op(p);
+ }
+
+ // multiply_to and divide_to are only helper functions
+ // to be passed to the for_each routine
+ static
+ void multiply_to (type& p, type const& q)
+ {
+ p *= q;
+ }
+
+ static
+ void divide_to (type& p, type const& c)
+ {
+ p /= c;
+ }
+
+ private:
+ template <size_t i>
+ static
+ void lex_degree_impl (type const &/*p*/, multi_index_type&/*D*/)
+ {
+ return;
+ }
+
+ template <size_t i>
+ static
+ bool max_degree_impl (type const &/*p*/,
+ multi_index_type &/*D*/,
+ multi_index_type const &/*I*/)
+ {
+ return true;
+ }
+
+ template <size_t i>
+ static
+ bool real_degree_impl (type const &/*p*/,
+ multi_index_type &/*D*/,
+ multi_index_type const &/*I*/)
+ {
+ return true;
+ }
+
+ template <size_t i>
+ static
+ void shift_impl(type &/*p*/, multi_index_type const &/*I*/)
+ {}
+
+ template <typename T, size_t i>
+ static
+ T evaluate_impl(type const &p, std::array<T, i> const &/*X*/)
+ {
+ return p;
+ }
+
+ static
+ void normalize(type &/*p*/)
+ {}
+
+
+ template <size_t M, typename C>
+ friend struct mvpoly;
+};
+
+
+/*
+ * monomial::make generate a mv-poly made up by a single term:
+ * monomial::make<N>(I,c) == c*X^I, where X=(x_(0), .., x_(N-1))
+ */
+
+template <size_t N, typename CoeffT>
+struct monomial
+{
+ typedef typename mvpoly<N, CoeffT>::type poly_type;
+
+ static inline
+ poly_type make(multi_index_type const& I, CoeffT c)
+ {
+ if (I.size() != N) // an exponent for each indeterminate
+ THROW_RANGEERROR ("multi-index with wrong length");
+
+ return make_impl<0>(I, c);
+ }
+
+ private:
+ // at i-th level of recursion I need to pick up the i-th exponent in "I"
+ // so I pass i as a template parameter, this trick is needed to avoid
+ // to create a new multi-index at each recursion level:
+ // (J = I[std::slice[1, I.size()-1, 1)]) that will be more expensive
+ template <size_t i>
+ static
+ poly_type make_impl(multi_index_type const& I, CoeffT c)
+ {
+ poly_type p(monomial<N-1,CoeffT>::template make_impl<i+1>(I, c), I[i]);
+ return p;
+ }
+
+ // make_impl private require that monomial classes to be each other friend
+ template <size_t M, typename C>
+ friend struct monomial;
+};
+
+
+// case N = 0 for stopping recursion
+template <typename CoeffT>
+struct monomial<0, CoeffT>
+{
+ private:
+ template <size_t i>
+ static
+ CoeffT make_impl(multi_index_type const &/*I*/, CoeffT c)
+ {
+ return c;
+ }
+
+ template<size_t N, typename C>
+ friend struct monomial;
+};
+
+
+/*
+ * coefficient<N, PolyT>
+ *
+ * N should be in the range [0, rank<PolyT>-1]
+ *
+ * "type" == the type of the coefficient of the polynomial with
+ * rank = rank<PolyT> - N - 1, that is it is the type of the object returned
+ * by applying the operator[] of a Polynomial object N+1 times;
+ *
+ * "zero" represents the zero element (in the group theory meaning)
+ * for the coefficient type "type"; having it as a static class member
+ * allows to return always a (const) reference by the "get_safe" method
+ *
+ * get(p, I) returns the coefficient of the monomial X^I
+ * this method doesn't check if such a coefficient really exists,
+ * so it's up to the user checking that the passed multi-index I is
+ * not out of range
+ *
+ * get_safe(p, I) returns the coefficient of the monomial X^I
+ * in case such a coefficient doesn't really exist "zero" is returned
+ *
+ * set_safe(p, I, c) set the coefficient of the monomial X^I to "c"
+ * in case such a coefficient doesn't really exist this method creates it
+ * and creates all monomials X^J with J < I that don't exist yet, setting
+ * their coefficients to "zero";
+ * (with J < I we mean "<" wrt the lexicographic order)
+ *
+ */
+
+template <size_t N, typename T>
+struct coefficient
+{
+};
+
+
+template <size_t N, typename CoeffT>
+struct coefficient< N, Polynomial<CoeffT> >
+{
+ typedef typename coefficient<N-1, CoeffT>::type type;
+ typedef Polynomial<CoeffT> poly_type;
+
+ static const type zero;
+
+ static
+ type const& get(poly_type const& p, multi_index_type const& I)
+ {
+ if (I.size() != N+1)
+ THROW_RANGEERROR ("multi-index with wrong length");
+
+ return get_impl<0>(p, I);
+ }
+
+ static
+ type & get(poly_type & p, multi_index_type const& I)
+ {
+ if (I.size() != N+1)
+ THROW_RANGEERROR ("multi-index with wrong length");
+
+ return get_impl<0>(p, I);
+ }
+
+ static
+ type const& get_safe(poly_type const& p, multi_index_type const& I)
+ {
+ if (I.size() != N+1)
+ THROW_RANGEERROR ("multi-index with wrong length");
+
+ return get_safe_impl<0>(p, I);
+ }
+
+ static
+ void set_safe(poly_type & p, multi_index_type const& I, type const& c)
+ {
+ if (I.size() != N+1)
+ THROW_RANGEERROR ("multi-index with wrong length");
+
+ return set_safe_impl<0>(p, I, c);
+ }
+
+ private:
+ template <size_t i>
+ static
+ type const& get_impl(poly_type const& p, multi_index_type const& I)
+ {
+ return coefficient<N-1, CoeffT>::template get_impl<i+1>(p[I[i]], I);
+ }
+
+ template <size_t i>
+ static
+ type & get_impl(poly_type & p, multi_index_type const& I)
+ {
+ return coefficient<N-1, CoeffT>::template get_impl<i+1>(p[I[i]], I);
+ }
+
+ template <size_t i>
+ static
+ type const& get_safe_impl(poly_type const& p, multi_index_type const& I)
+ {
+ if (I[i] > p.max_degree())
+ {
+ return zero;
+ }
+ else
+ {
+ return
+ coefficient<N-1, CoeffT>::template get_safe_impl<i+1>(p[I[i]], I);
+ }
+ }
+
+ template <size_t i>
+ static
+ void set_safe_impl(poly_type & p, multi_index_type const& I, type const& c)
+ {
+ if (I[i] > p.max_degree())
+ {
+ multi_index_type J = shift(I, i+1);
+ CoeffT m = monomial<N, type>::make(J, c);
+ p.coefficient(I[i], m);
+ }
+ else
+ {
+ coefficient<N-1, CoeffT>::template set_safe_impl<i+1>(p[I[i]], I, c);
+ }
+ }
+
+ template<size_t M, typename T>
+ friend struct coefficient;
+
+};
+
+// initialization of static member zero
+template <size_t N, typename CoeffT>
+const typename coefficient< N, Polynomial<CoeffT> >::type
+coefficient< N, Polynomial<CoeffT> >::zero
+ = Geom::SL::zero<typename coefficient< N, Polynomial<CoeffT> >::type >()();
+
+
+// case N = 0 for stopping recursion
+template <typename CoeffT>
+struct coefficient< 0, Polynomial<CoeffT> >
+{
+ typedef CoeffT type;
+ typedef Polynomial<CoeffT> poly_type;
+
+ static const type zero;
+
+ static
+ type const& get(poly_type const& p, multi_index_type const& I)
+ {
+ if (I.size() != 1)
+ THROW_RANGEERROR ("multi-index with wrong length");
+
+ return p[I[0]];
+ }
+
+ static
+ type & get(poly_type & p, multi_index_type const& I)
+ {
+ if (I.size() != 1)
+ THROW_RANGEERROR ("multi-index with wrong length");
+
+ return p[I[0]];
+ }
+
+ static
+ type const& get_safe(poly_type const& p, multi_index_type const& I)
+ {
+ if (I.size() != 1)
+ THROW_RANGEERROR ("multi-index with wrong length");
+
+ return p.coefficient(I[0]);
+ }
+
+ static
+ void set_safe(poly_type & p, multi_index_type const& I, type const& c)
+ {
+ if (I.size() != 1)
+ THROW_RANGEERROR ("multi-index with wrong length");
+
+ p.coefficient(I[0], c);
+ }
+
+ private:
+ template <size_t i>
+ static
+ type const& get_impl(poly_type const& p, multi_index_type const& I)
+ {
+ return p[I[i]];
+ }
+
+ template <size_t i>
+ static
+ type & get_impl(poly_type & p, multi_index_type const& I)
+ {
+ return p[I[i]];
+ }
+
+ template <size_t i>
+ static
+ type const& get_safe_impl(poly_type const& p, multi_index_type const& I)
+ {
+ return p.coefficient(I[i]);
+ }
+
+ template <size_t i>
+ static
+ void set_safe_impl(poly_type & p, multi_index_type const& I, type const& c)
+ {
+ p.coefficient(I[i], c);
+ }
+
+ template<size_t M, typename T>
+ friend struct coefficient;
+};
+
+// initialization of static member zero
+template <typename CoeffT>
+const typename coefficient< 0, Polynomial<CoeffT> >::type
+coefficient< 0, Polynomial<CoeffT> >::zero
+ = Geom::SL::zero<typename coefficient< 0, Polynomial<CoeffT> >::type >()();
+
+
+/*
+ * ordering types:
+ * lex : lexicographic ordering
+ * ilex : inverse lexicographic ordering
+ * max_lex : max degree + lexicographic ordering for disambiguation
+ *
+ */
+
+namespace ordering
+{
+ struct lex; // WARNING: at present only lex ordering is supported
+ struct ilex;
+ struct max_lex;
+}
+
+
+/*
+ * degree of a mv poly wrt a given ordering
+ */
+
+template <size_t N, typename CoeffT, typename OrderT = ordering::lex>
+struct mvdegree
+{};
+
+template <size_t N, typename CoeffT>
+struct mvdegree<N, CoeffT, ordering::lex>
+{
+ typedef typename mvpoly<N, CoeffT>::type poly_type;
+ typedef ordering::lex ordering;
+
+ static
+ multi_index_type value(poly_type const& p)
+ {
+ return Geom::SL::mvpoly<N, CoeffT>::lex_degree(p);
+ }
+};
+
+} /*end namespace Geom*/ } /*end namespace SL*/
+
+
+#endif // _GEOM_SL_MVPOLY_TOOLS_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/include/2geom/symbolic/polynomial.h b/include/2geom/symbolic/polynomial.h
new file mode 100644
index 0000000..fea7e6c
--- /dev/null
+++ b/include/2geom/symbolic/polynomial.h
@@ -0,0 +1,569 @@
+/*
+ * Polynomial<CoeffT> class template
+ *
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 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.
+ */
+
+#ifndef _GEOM_SL_POLYNOMIAL_H_
+#define _GEOM_SL_POLYNOMIAL_H_
+
+
+#include <2geom/symbolic/unity-builder.h>
+
+#include <vector>
+#include <string>
+
+#include <2geom/exception.h>
+
+
+
+
+namespace Geom { namespace SL {
+
+/*
+ * Polynomial<CoeffT> class template
+ *
+ * It represents a generic univariate polynomial with coefficients
+ * of type CoeffT. One way to get a multi-variate polynomial is
+ * to utilize a Polynomial instantiation as coefficient type
+ * in a recursive style.
+ *
+ */
+
+template< typename CoeffT >
+class Polynomial
+{
+ public:
+ typedef CoeffT coeff_type;
+ typedef std::vector<coeff_type> coeff_container_t;
+ typedef typename coeff_container_t::iterator iterator;
+ typedef typename coeff_container_t::const_iterator const_iterator;
+
+ /*
+ * a Polynomial should be never empty
+ */
+ Polynomial()
+ {
+ m_coeff.push_back(zero_coeff);
+ }
+
+ explicit
+ Polynomial(CoeffT const& c, size_t i = 0)
+ {
+ m_coeff.resize(i, zero_coeff);
+ m_coeff.push_back(c);
+ }
+
+ /*
+ * forwarding of some std::vector methods
+ */
+
+ size_t size() const
+ {
+ return m_coeff.size();
+ }
+
+ const_iterator begin() const
+ {
+ return m_coeff.begin();
+ }
+
+ const_iterator end() const
+ {
+ return m_coeff.end();
+ }
+
+ iterator begin()
+ {
+ return m_coeff.begin();
+ }
+
+ iterator end()
+ {
+ return m_coeff.end();
+ }
+
+ void reserve(size_t n)
+ {
+ m_coeff.reserve(n);
+ }
+
+ size_t capacity() const
+ {
+ return m_coeff.capacity();
+ }
+
+ /*
+ * degree of the term with the highest degree
+ * and an initialized coefficient (even if zero)
+ */
+ size_t max_degree() const
+ {
+ if (size() == 0)
+ THROW_INVARIANTSVIOLATION (0);
+
+ return (size() - 1);
+ }
+
+ void max_degree(size_t n)
+ {
+ m_coeff.resize(n+1, zero_coeff);
+ }
+
+ /*
+ * degree of the term with the highest degree
+ * and an initialized coefficient that is not null
+ */
+ size_t real_degree() const
+ {
+ if (size() == 0)
+ THROW_INVARIANTSVIOLATION (0);
+
+ const_iterator it = end() - 1;
+ for (; it != begin(); --it)
+ {
+ if (*it != zero_coeff) break;
+ }
+ size_t i = static_cast<size_t>(it - begin());
+ return i;
+ }
+
+ bool is_zero() const
+ {
+ if (size() == 0)
+ THROW_INVARIANTSVIOLATION (0);
+
+ if (real_degree() != 0) return false;
+ if (m_coeff[0] != zero_coeff) return false;
+ return true;
+ }
+
+ /*
+ * trim leading zero coefficients
+ * after calling normalize max_degree == real_degree
+ */
+ void normalize()
+ {
+ size_t rd = real_degree();
+ if (rd != max_degree())
+ {
+ m_coeff.erase(begin() + rd + 1, end());
+ }
+ }
+
+ coeff_type const& operator[] (size_t i) const
+ {
+ return m_coeff[i];
+ }
+
+ coeff_type & operator[] (size_t i)
+ {
+ return m_coeff[i];
+ }
+
+ // safe coefficient getter routine
+ coeff_type const& coefficient(size_t i) const
+ {
+ if (i > max_degree())
+ {
+ return zero_coeff;
+ }
+ else
+ {
+ return m_coeff[i];
+ }
+ }
+
+ // safe coefficient setter routine
+ void coefficient(size_t i, coeff_type const& c)
+ {
+ //std::cerr << "i: " << i << " c: " << c << std::endl;
+ if (i > max_degree())
+ {
+ if (c == zero_coeff) return;
+ reserve(i+1);
+ m_coeff.resize(i, zero_coeff);
+ m_coeff.push_back(c);
+ }
+ else
+ {
+ m_coeff[i] = c;
+ }
+ }
+
+ coeff_type const& leading_coefficient() const
+ {
+ return m_coeff[real_degree()];
+ }
+
+ coeff_type & leading_coefficient()
+ {
+ return m_coeff[real_degree()];
+ }
+
+ /*
+ * polynomail evaluation:
+ * T can be any type that is able to be + and * with the coefficient type
+ */
+ template <typename T>
+ T operator() (T const& x) const
+ {
+ T r = zero<T>()();
+ for(size_t i = max_degree(); i > 0; --i)
+ {
+ r += (*this)[i];
+ r *= x;
+ }
+ r += (*this)[0];
+ return r;
+ }
+
+ // opposite polynomial
+ Polynomial operator-() const
+ {
+ Polynomial r;
+ // we need r.m_coeff to be empty so we can utilize push_back
+ r.m_coeff.pop_back();
+ r.reserve(size());
+ for(size_t i = 0; i < size(); ++i)
+ {
+ r.m_coeff.push_back( -(*this)[i] );
+ }
+ return r;
+ }
+
+ /*
+ * polynomial-polynomial mutating operators
+ */
+
+ Polynomial& operator+=(Polynomial const& p)
+ {
+ size_t sz = std::min(size(), p.size());
+ for (size_t i = 0; i < sz; ++i)
+ {
+ (*this)[i] += p[i];
+ }
+ if (size() < p.size())
+ {
+ m_coeff.insert(end(), p.begin() + size(), p.end());
+ }
+ return (*this);
+ }
+
+ Polynomial& operator-=(Polynomial const& p)
+ {
+ size_t sz = std::min(size(), p.size());
+ for (size_t i = 0; i < sz; ++i)
+ {
+ (*this)[i] -= p[i];
+ }
+ reserve(p.size());
+ for(size_t i = sz; i < p.size(); ++i)
+ {
+ m_coeff.push_back( -p[i] );
+ }
+ return (*this);
+ }
+
+ Polynomial& operator*=(Polynomial const& p)
+ {
+ Polynomial r;
+ r.m_coeff.resize(size() + p.size() - 1, zero_coeff);
+
+ for (size_t i = 0; i < size(); ++i)
+ {
+ for (size_t j = 0; j < p.size(); ++j)
+ {
+ r[i+j] += (*this)[i] * p[j];
+ }
+ }
+ (*this) = r;
+ return (*this);
+ }
+
+ /*
+ * equivalent to multiply by x^n
+ */
+ Polynomial& operator<<=(size_t n)
+ {
+ m_coeff.insert(begin(), n, zero_coeff);
+ return (*this);
+ }
+
+ /*
+ * polynomial-coefficient mutating operators
+ */
+
+ Polynomial& operator=(coeff_type const& c)
+ {
+ m_coeff[0] = c;
+ return (*this);
+ }
+
+ Polynomial& operator+=(coeff_type const& c)
+ {
+ (*this)[0] += c;
+ return (*this);
+ }
+
+ Polynomial& operator-=(coeff_type const& c)
+ {
+ (*this)[0] -= c;
+ return (*this);
+ }
+
+ Polynomial& operator*=(coeff_type const& c)
+ {
+ for (size_t i = 0; i < size(); ++i)
+ {
+ (*this)[i] *= c;
+ }
+ return (*this);
+ }
+
+ // return the poly in a string form
+ std::string str() const;
+
+ private:
+ // with zero_coeff defined as a static data member
+ // coefficient(size_t i) safe get method can always
+ // return a (const) reference
+ static const coeff_type zero_coeff;
+ coeff_container_t m_coeff;
+
+}; // end class Polynomial
+
+
+/*
+ * zero and one element spezcialization for Polynomial
+ */
+
+template< typename CoeffT >
+struct zero<Polynomial<CoeffT>, false>
+{
+ Polynomial<CoeffT> operator() () const
+ {
+ CoeffT zc = zero<CoeffT>()();
+ Polynomial<CoeffT> z(zc);
+ return z;
+ }
+};
+
+template< typename CoeffT >
+struct one<Polynomial<CoeffT>, false>
+{
+ Polynomial<CoeffT> operator() ()
+ {
+ CoeffT _1c = one<CoeffT>()();
+ Polynomial<CoeffT> _1(_1c);
+ return _1;
+ }
+};
+
+
+/*
+ * initialization of Polynomial static data members
+ */
+
+template< typename CoeffT >
+const typename Polynomial<CoeffT>::coeff_type Polynomial<CoeffT>::zero_coeff
+ = zero<typename Polynomial<CoeffT>::coeff_type>()();
+
+/*
+ * Polynomial - Polynomial binary mathematical operators
+ */
+
+template< typename CoeffT >
+inline
+bool operator==(Polynomial<CoeffT> const& p, Polynomial<CoeffT> const& q)
+{
+ size_t d = p.real_degree();
+ if (d != q.real_degree()) return false;
+ for (size_t i = 0; i <= d; ++i)
+ {
+ if (p[i] != q[i]) return false;
+ }
+ return true;
+}
+
+template< typename CoeffT >
+inline
+bool operator!=(Polynomial<CoeffT> const& p, Polynomial<CoeffT> const& q)
+{
+ return !(p == q);
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator+( Polynomial<CoeffT> const& p, Polynomial<CoeffT> const& q )
+{
+ Polynomial<CoeffT> r(p);
+ r += q;
+ return r;
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator-( Polynomial<CoeffT> const& p, Polynomial<CoeffT> const& q )
+{
+ Polynomial<CoeffT> r(p);
+ r -= q;
+ return r;
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator*( Polynomial<CoeffT> const& p, Polynomial<CoeffT> const& q )
+{
+ Polynomial<CoeffT> r(p);
+ r *= q;
+ return r;
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT> operator<<(Polynomial<CoeffT> const& p, size_t n)
+{
+ Polynomial<CoeffT> r(p);
+ r <<= n;
+ return r;
+}
+
+
+/*
+ * polynomial-coefficient and coefficient-polynomial mathematical operators
+ */
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator+( Polynomial<CoeffT> const& p, CoeffT const& c )
+{
+ Polynomial<CoeffT> r(p);
+ r += c;
+ return r;
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator+( CoeffT const& c, Polynomial<CoeffT> const& p)
+{
+ return (p + c);
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator-( Polynomial<CoeffT> const& p, CoeffT const& c )
+{
+ Polynomial<CoeffT> r(p);
+ r -= c;
+ return r;
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator-( CoeffT const& c, Polynomial<CoeffT> const& p)
+{
+ return (p - c);
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator*( Polynomial<CoeffT> const& p, CoeffT const& c )
+{
+ Polynomial<CoeffT> r(p);
+ r *= c;
+ return r;
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator*( CoeffT const& c, Polynomial<CoeffT> const& p)
+{
+ return (p * c);
+}
+
+
+/*
+ * operator<< extension for printing Polynomial
+ * and str() method for transforming a Polynomial into a string
+ */
+
+template< typename charT, typename CoeffT >
+inline
+std::basic_ostream<charT> &
+operator<< (std::basic_ostream<charT> & os, const Polynomial<CoeffT> & p)
+{
+ if (p.size() == 0) return os;
+ os << "{" << p[0];
+ for (size_t i = 1; i < p.size(); ++i)
+ {
+ os << ", " << p[i];
+ }
+ os << "}";
+ return os;
+}
+
+
+template< typename CoeffT >
+inline
+std::string Polynomial<CoeffT>::str() const
+{
+ std::ostringstream oss;
+ oss << (*this);
+ return oss.str();
+}
+
+
+} /*end namespace Geom*/ } /*end namespace SL*/
+
+
+
+
+#endif // _GEOM_SL_POLYNOMIAL_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/include/2geom/symbolic/unity-builder.h b/include/2geom/symbolic/unity-builder.h
new file mode 100644
index 0000000..cb8046f
--- /dev/null
+++ b/include/2geom/symbolic/unity-builder.h
@@ -0,0 +1,102 @@
+/*
+ * Routines to make up "zero" and "one" elements of a ring
+ *
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 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.
+ */
+
+#ifndef _GEOM_SL_UNITY_BUILDER_H_
+#define _GEOM_SL_UNITY_BUILDER_H_
+
+
+#include <type_traits>
+
+
+
+namespace Geom { namespace SL {
+
+
+/*
+ * zero builder function class type
+ *
+ * made up a zero element, in the algebraic ring theory meaning,
+ * for the type T
+ */
+
+template< typename T, bool numeric = std::is_arithmetic<T>::value >
+struct zero
+{};
+
+// specialization for basic numeric type
+template< typename T >
+struct zero<T, true>
+{
+ T operator() () const
+ {
+ return 0;
+ }
+};
+
+
+/*
+ * one builder function class type
+ *
+ * made up a one element, in the algebraic ring theory meaning,
+ * for the type T
+ */
+
+template< typename T, bool numeric = std::is_arithmetic<T>::value >
+struct one
+{};
+
+// specialization for basic numeric type
+template< typename T >
+struct one<T, true>
+{
+ T operator() ()
+ {
+ return 1;
+ }
+};
+
+} /*end namespace Geom*/ } /*end namespace SL*/
+
+
+#endif // _GEOM_SL_UNITY_BUILDER_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/include/2geom/transforms.h b/include/2geom/transforms.h
new file mode 100644
index 0000000..cc55e29
--- /dev/null
+++ b/include/2geom/transforms.h
@@ -0,0 +1,370 @@
+/**
+ * @file
+ * @brief Affine transformation classes
+ *//*
+ * Authors:
+ * ? <?@?.?>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Johan Engelen
+ *
+ * Copyright ?-2012 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_TRANSFORMS_H
+#define LIB2GEOM_SEEN_TRANSFORMS_H
+
+#include <cmath>
+#include <2geom/forward.h>
+#include <2geom/affine.h>
+#include <2geom/angle.h>
+#include <boost/concept/assert.hpp>
+
+namespace Geom {
+
+/** @brief Type requirements for transforms.
+ * @ingroup Concepts */
+template <typename T>
+struct TransformConcept {
+ T t, t2;
+ Affine m;
+ Point p;
+ bool bool_;
+ Coord epsilon;
+ void constraints() {
+ m = t; //implicit conversion
+ m *= t;
+ m = m * t;
+ m = t * m;
+ p *= t;
+ p = p * t;
+ t *= t;
+ t = t * t;
+ t = pow(t, 3);
+ bool_ = (t == t);
+ bool_ = (t != t);
+ t = T::identity();
+ t = t.inverse();
+ bool_ = are_near(t, t2);
+ bool_ = are_near(t, t2, epsilon);
+ }
+};
+
+/** @brief Base template for transforms.
+ * This class is an implementation detail and should not be used directly. */
+template <typename T>
+class TransformOperations
+ : boost::equality_comparable< T
+ , boost::multipliable< T
+ > >
+{
+public:
+ template <typename T2>
+ Affine operator*(T2 const &t) const {
+ Affine ret(*static_cast<T const*>(this)); ret *= t; return ret;
+ }
+};
+
+/** @brief Integer exponentiation for transforms.
+ * Negative exponents will yield the corresponding power of the inverse. This function
+ * can also be applied to matrices.
+ * @param t Affine or transform to exponantiate
+ * @param n Exponent
+ * @return \f$A^n\f$ if @a n is positive, \f$(A^{-1})^n\f$ if negative, identity if zero.
+ * @ingroup Transforms */
+template <typename T>
+T pow(T const &t, int n) {
+ BOOST_CONCEPT_ASSERT((TransformConcept<T>));
+ if (n == 0) return T::identity();
+ T result(T::identity());
+ T x(n < 0 ? t.inverse() : t);
+ if (n < 0) n = -n;
+ while ( n ) { // binary exponentiation - fast
+ if ( n & 1 ) { result *= x; --n; }
+ x *= x; n /= 2;
+ }
+ return result;
+}
+
+/** @brief Translation by a vector.
+ * @ingroup Transforms */
+class Translate
+ : public TransformOperations< Translate >
+{
+ Point vec;
+public:
+ /// Create a translation that doesn't do anything.
+ Translate() : vec(0, 0) {}
+ /// Construct a translation from its vector.
+ Translate(Point const &p) : vec(p) {}
+ /// Construct a translation from its coordinates.
+ Translate(Coord x, Coord y) : vec(x, y) {}
+
+ operator Affine() const { Affine ret(1, 0, 0, 1, vec[X], vec[Y]); return ret; }
+ Coord operator[](Dim2 dim) const { return vec[dim]; }
+ Coord operator[](unsigned dim) const { return vec[dim]; }
+ Translate &operator*=(Translate const &o) { vec += o.vec; return *this; }
+ bool operator==(Translate const &o) const { return vec == o.vec; }
+
+ Point vector() const { return vec; }
+ /// Get the inverse translation.
+ Translate inverse() const { return Translate(-vec); }
+ /// Get a translation that doesn't do anything.
+ static Translate identity() { Translate ret; return ret; }
+
+ friend class Point;
+};
+
+inline bool are_near(Translate const &a, Translate const &b, Coord eps=EPSILON) {
+ return are_near(a[X], b[X], eps) && are_near(a[Y], b[Y], eps);
+}
+
+/** @brief Scaling from the origin.
+ * During scaling, the point (0,0) will not move. To obtain a scale with a different
+ * invariant point, combine with translation to the origin and back.
+ * @ingroup Transforms */
+class Scale
+ : public TransformOperations< Scale >
+{
+ Point vec;
+public:
+ /// Create a scaling that doesn't do anything.
+ Scale() : vec(1, 1) {}
+ /// Create a scaling from two scaling factors given as coordinates of a point.
+ explicit Scale(Point const &p) : vec(p) {}
+ /// Create a scaling from two scaling factors.
+ Scale(Coord x, Coord y) : vec(x, y) {}
+ /// Create an uniform scaling from a single scaling factor.
+ explicit Scale(Coord s) : vec(s, s) {}
+ inline operator Affine() const { Affine ret(vec[X], 0, 0, vec[Y], 0, 0); return ret; }
+
+ Coord operator[](Dim2 d) const { return vec[d]; }
+ Coord operator[](unsigned d) const { return vec[d]; }
+ //TODO: should we keep these mutators? add them to the other transforms?
+ Coord &operator[](Dim2 d) { return vec[d]; }
+ Coord &operator[](unsigned d) { return vec[d]; }
+ Scale &operator*=(Scale const &b) { vec[X] *= b[X]; vec[Y] *= b[Y]; return *this; }
+ bool operator==(Scale const &o) const { return vec == o.vec; }
+
+ Point vector() const { return vec; }
+ Scale inverse() const { return Scale(1./vec[0], 1./vec[1]); }
+ static Scale identity() { Scale ret; return ret; }
+
+ friend class Point;
+};
+
+inline bool are_near(Scale const &a, Scale const &b, Coord eps=EPSILON) {
+ return are_near(a[X], b[X], eps) && are_near(a[Y], b[Y], eps);
+}
+
+/** @brief Rotation around the origin.
+ * Combine with translations to the origin and back to get a rotation around a different point.
+ * @ingroup Transforms */
+class Rotate
+ : public TransformOperations< Rotate >
+{
+ Point vec; ///< @todo Convert to storing the angle, as it's more space-efficient.
+public:
+ /// Construct a zero-degree rotation.
+ Rotate() : vec(1, 0) {}
+ /** @brief Construct a rotation from its angle in radians.
+ * Positive arguments correspond to counter-clockwise rotations (if Y grows upwards). */
+ explicit Rotate(Coord theta) : vec(Point::polar(theta)) {}
+ /// Construct a rotation from its characteristic vector.
+ explicit Rotate(Point const &p) : vec(unit_vector(p)) {}
+ /// Construct a rotation from the coordinates of its characteristic vector.
+ explicit Rotate(Coord x, Coord y) { Rotate(Point(x, y)); }
+ operator Affine() const { Affine ret(vec[X], vec[Y], -vec[Y], vec[X], 0, 0); return ret; }
+
+ /** @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; }
+ bool operator==(Rotate const &o) const { return vec == o.vec; }
+ Rotate inverse() const {
+ Rotate r;
+ r.vec = Point(vec[X], -vec[Y]);
+ return r;
+ }
+ /// @brief Get a zero-degree rotation.
+ static Rotate identity() { Rotate ret; return ret; }
+ /** @brief Construct a rotation from its angle in degrees.
+ * Positive arguments correspond to clockwise rotations if Y grows downwards. */
+ static Rotate from_degrees(Coord deg) {
+ Coord rad = (deg / 180.0) * M_PI;
+ return Rotate(rad);
+ }
+ static Affine around(Point const &p, Coord angle);
+
+ friend class Point;
+};
+
+inline bool are_near(Rotate const &a, Rotate const &b, Coord eps=EPSILON) {
+ return are_near(a[X], b[X], eps) && are_near(a[Y], b[Y], eps);
+}
+
+/** @brief Common base for shearing transforms.
+ * This class is an implementation detail and should not be used directly.
+ * @ingroup Transforms */
+template <typename S>
+class ShearBase
+ : public TransformOperations< S >
+{
+protected:
+ Coord f;
+ ShearBase(Coord _f) : f(_f) {}
+public:
+ Coord factor() const { return f; }
+ void setFactor(Coord nf) { f = nf; }
+ S &operator*=(S const &s) { f += s.f; return static_cast<S &>(*this); }
+ bool operator==(S const &s) const { return f == s.f; }
+ S inverse() const { S ret(-f); return ret; }
+ static S identity() { S ret(0); return ret; }
+
+ friend class Point;
+ friend class Affine;
+};
+
+/** @brief Horizontal shearing.
+ * Points on the X axis will not move. Combine with translations to get a shear
+ * with a different invariant line.
+ * @ingroup Transforms */
+class HShear
+ : public ShearBase<HShear>
+{
+public:
+ explicit HShear(Coord h) : ShearBase<HShear>(h) {}
+ operator Affine() const { Affine ret(1, 0, f, 1, 0, 0); return ret; }
+};
+
+inline bool are_near(HShear const &a, HShear const &b, Coord eps=EPSILON) {
+ return are_near(a.factor(), b.factor(), eps);
+}
+
+/** @brief Vertical shearing.
+ * Points on the Y axis will not move. Combine with translations to get a shear
+ * with a different invariant line.
+ * @ingroup Transforms */
+class VShear
+ : public ShearBase<VShear>
+{
+public:
+ explicit VShear(Coord h) : ShearBase<VShear>(h) {}
+ operator Affine() const { Affine ret(1, f, 0, 1, 0, 0); return ret; }
+};
+
+inline bool are_near(VShear const &a, VShear const &b, Coord eps=EPSILON) {
+ return are_near(a.factor(), b.factor(), eps);
+}
+
+/** @brief Combination of a translation and uniform scale.
+ * The translation part is applied first, then the result is scaled from the new origin.
+ * This way when the class is used to accumulate a zoom transform, trans always points
+ * to the new origin in original coordinates.
+ * @ingroup Transforms */
+class Zoom
+ : public TransformOperations< Zoom >
+{
+ Coord _scale;
+ Point _trans;
+ Zoom() : _scale(1), _trans() {}
+public:
+ /// Construct a zoom from a scaling factor.
+ explicit Zoom(Coord s) : _scale(s), _trans() {}
+ /// Construct a zoom from a translation.
+ explicit Zoom(Translate const &t) : _scale(1), _trans(t.vector()) {}
+ /// Construct a zoom from a scaling factor and a translation.
+ Zoom(Coord s, Translate const &t) : _scale(s), _trans(t.vector()) {}
+
+ operator Affine() const {
+ Affine ret(_scale, 0, 0, _scale, _trans[X] * _scale, _trans[Y] * _scale);
+ return ret;
+ }
+ Zoom &operator*=(Zoom const &z) {
+ _trans += z._trans / _scale;
+ _scale *= z._scale;
+ return *this;
+ }
+ bool operator==(Zoom const &z) const { return _scale == z._scale && _trans == z._trans; }
+
+ Coord scale() const { return _scale; }
+ void setScale(Coord s) { _scale = s; }
+ Point translation() const { return _trans; }
+ void setTranslation(Point const &p) { _trans = p; }
+ Zoom inverse() const { Zoom ret(1/_scale, Translate(-_trans*_scale)); return ret; }
+ static Zoom identity() { Zoom ret(1.0); return ret; }
+ static Zoom map_rect(Rect const &old_r, Rect const &new_r);
+
+ friend class Point;
+ friend class Affine;
+};
+
+inline bool are_near(Zoom const &a, Zoom const &b, Coord eps=EPSILON) {
+ return are_near(a.scale(), b.scale(), eps) &&
+ are_near(a.translation(), b.translation(), eps);
+}
+
+/** @brief Specialization of exponentiation for Scale.
+ * @relates Scale */
+template<>
+inline Scale pow(Scale const &s, int n) {
+ Scale ret(::pow(s[X], n), ::pow(s[Y], n));
+ return ret;
+}
+/** @brief Specialization of exponentiation for Translate.
+ * @relates Translate */
+template<>
+inline Translate pow(Translate const &t, int n) {
+ Translate ret(t[X] * n, t[Y] * n);
+ return ret;
+}
+
+
+/** @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);
+
+//TODO: decomposition of Affine into some finite combination of the above classes
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_TRANSFORMS_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/include/2geom/utils.h b/include/2geom/utils.h
new file mode 100644
index 0000000..87f49bb
--- /dev/null
+++ b/include/2geom/utils.h
@@ -0,0 +1,114 @@
+/**
+ * \file
+ * \brief Various utility functions.
+ *//*
+ * Copyright 2007 Johan Engelen <goejendaagh@zonnet.nl>
+ * 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_UTILS_H
+#define LIB2GEOM_SEEN_UTILS_H
+
+#include <cstddef>
+#include <vector>
+#include <boost/operators.hpp>
+
+namespace Geom {
+
+// Throw these errors instead of aserting so code can handle them if needed.
+using ErrorCode = int;
+enum Errors : ErrorCode {
+ GEOM_ERR_INTERSECGRAPH,
+};
+
+void binomial_coefficients(std::vector<size_t>& bc, std::size_t n);
+
+struct EmptyClass {};
+
+/**
+ * @brief Noncommutative multiplication helper.
+ * Generates operator*(T, U) from operator*=(T, U). Does not generate operator*(U, T)
+ * like boost::multipliable does. This makes it suitable for noncommutative cases,
+ * such as transforms.
+ */
+template <class T, class U, class B = EmptyClass>
+struct MultipliableNoncommutative : B
+{
+ friend T operator*(T const &lhs, U const &rhs) {
+ T nrv(lhs); nrv *= rhs; return nrv;
+ }
+};
+
+/** @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 &) {}
+};
+
+/** @brief Get the next iterator in the container with wrap-around.
+ * If the iterator would become the end iterator after incrementing,
+ * return the begin iterator instead. */
+template <typename Iter, typename Container>
+Iter cyclic_next(Iter i, Container &c) {
+ ++i;
+ if (i == c.end()) {
+ i = c.begin();
+ }
+ return i;
+}
+
+/** @brief Get the previous iterator in the container with wrap-around.
+ * If the passed iterator is the begin iterator, return the iterator
+ * just before the end iterator instead. */
+template <typename Iter, typename Container>
+Iter cyclic_prior(Iter i, Container &c) {
+ if (i == c.begin()) {
+ i = c.end();
+ }
+ --i;
+ return i;
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_UTILS_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/include/toys/lpe-framework.h b/include/toys/lpe-framework.h
new file mode 100644
index 0000000..9de5aa0
--- /dev/null
+++ b/include/toys/lpe-framework.h
@@ -0,0 +1,77 @@
+/**
+ * \file lpe-framework.h
+ * \brief A framework for writing an Inkscape Live Path Effect toy.
+ *
+ * Instead of using the standard toy framework, one can use this LPE
+ * framework when creating an LPE for Inkscape. When new 2geom functions
+ * are required for the LPE, adding this functionality directly in Inkscape
+ * greatly increases compile times. Using this framework, one only has to
+ * rebuild 2geom, which speeds up development considerably. (Think about
+ * how much of Inkscape will have to be rebuild if you change/add something
+ * in point.h ...)
+ * An example of how to use this framework is lpe.test.cpp.
+ *//*
+ * Copyright 2009 Johan Engelen <goejendaagh@zonnet.nl>
+ *
+ * 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_LPE_TOY_FRAMEWORK_H_
+#define _2GEOM_LPE_TOY_FRAMEWORK_H_
+
+/**
+ * This should greatly simplify creating toy code for a Live Path Effect (LPE) for Inkscape
+ */
+
+
+#include <toys/toy-framework-2.h>
+#include <2geom/pathvector.h>
+
+class LPEToy: public Toy {
+public:
+ LPEToy();
+ void draw(cairo_t *cr, std::ostringstream *notify, int width, int height, bool save, std::ostringstream *timer_stream) override;
+ virtual Geom::PathVector
+ doEffect_path (Geom::PathVector const & path_in);
+ virtual Geom::Piecewise<Geom::D2<Geom::SBasis> >
+ doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in);
+
+ /** this boolean defaults to false, it concatenates the input path to one pwd2,
+ * instead of normally 'splitting' the path into continuous pwd2 paths. */
+ bool concatenate_before_pwd2;
+ PointSetHandle curve_handle;
+};
+
+#endif // _2GEOM_LPE_TOY_FRAMEWORK_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=4:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/toys/path-cairo.h b/include/toys/path-cairo.h
new file mode 100644
index 0000000..4c0403f
--- /dev/null
+++ b/include/toys/path-cairo.h
@@ -0,0 +1,57 @@
+#ifndef PATH_CAIRO
+#define PATH_CAIRO
+
+
+#include <cairo.h>
+#include <2geom/line.h>
+#include <2geom/sbasis.h>
+#include <2geom/sbasis-2d.h>
+#include <2geom/d2.h>
+#include <2geom/piecewise.h>
+#include <2geom/path.h>
+#include <2geom/convex-hull.h>
+#include <vector>
+#include <string>
+
+typedef struct _cairo cairo_t;
+
+void cairo_curve(cairo_t *cr, Geom::Curve const &c);
+void cairo_rectangle(cairo_t *cr, Geom::Rect const &r);
+void cairo_convex_hull(cairo_t *cr, Geom::ConvexHull const &r);
+void cairo_path(cairo_t *cr, Geom::Path const &p);
+void cairo_path(cairo_t *cr, Geom::PathVector const &p);
+void cairo_path_stitches(cairo_t *cr, Geom::Path const &p);
+void cairo_path_stitches(cairo_t *cr, Geom::PathVector const &p);
+
+void cairo_d2_sb(cairo_t *cr, Geom::D2<Geom::SBasis> const &p);
+void cairo_d2_sb_handles(cairo_t *cr, Geom::D2<Geom::SBasis> const &p);
+void cairo_d2_sb2d(cairo_t* cr, Geom::D2<Geom::SBasis2d> const &sb2, Geom::Point dir, double width);
+void cairo_sb2d(cairo_t* cr, Geom::SBasis2d const &sb2, Geom::Point dir, double width);
+
+void cairo_d2_pw_sb(cairo_t *cr, Geom::D2<Geom::Piecewise<Geom::SBasis> > const &p);
+void cairo_pw_d2_sb(cairo_t *cr, Geom::Piecewise<Geom::D2<Geom::SBasis> > const &p);
+
+
+void draw_line(cairo_t *cr, const Geom::Line& l, const Geom::Rect& r);
+void draw_line(cairo_t *cr, Geom::Point n, double d, Geom::Rect r);
+void draw_line(cairo_t *cr, Geom::Point a, Geom::Point b, Geom::Rect r);
+
+void draw_line_seg(cairo_t *cr, Geom::Point a, Geom::Point b);
+void draw_line_seg_with_arrow(cairo_t *cr, Geom::Point a, Geom::Point b, double dangle = 15*M_PI/180, double radius = 20);
+void draw_spot(cairo_t *cr, Geom::Point h);
+void draw_handle(cairo_t *cr, Geom::Point h);
+void draw_cross(cairo_t *cr, Geom::Point h);
+void draw_circ(cairo_t *cr, Geom::Point h);
+void draw_ray(cairo_t *cr, Geom::Point h, Geom::Point dir);
+void draw_ray(cairo_t *cr, const Geom::Ray& ray, const Geom::Rect& r);
+void draw_line_segment(cairo_t *cr, const Geom::LineSegment& ls, const Geom::Rect& r);
+
+void cairo_move_to(cairo_t *cr, Geom::Point p1);
+void cairo_line_to(cairo_t *cr, Geom::Point p1);
+void cairo_curve_to(cairo_t *cr, Geom::Point p1, Geom::Point p2, Geom::Point p3);
+
+// H in [0,360)
+// S, V, R, G, B in [0,1]
+void convertHSVtoRGB(const double H, const double S, const double V,
+ double& R, double& G, double& B);
+#endif
diff --git a/include/toys/toy-framework-2.h b/include/toys/toy-framework-2.h
new file mode 100644
index 0000000..5504dfd
--- /dev/null
+++ b/include/toys/toy-framework-2.h
@@ -0,0 +1,451 @@
+
+#ifndef _2GEOM_TOY_FRAMEWORK2_H_
+#define _2GEOM_TOY_FRAMEWORK2_H_
+
+
+
+#include <cairo.h>
+#include <gtk/gtk.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <assert.h>
+#include <2geom/exception.h>
+#include <2geom/point.h>
+#include <2geom/geom.h>
+#include <2geom/sbasis.h>
+#include <2geom/d2.h>
+#include <sched.h>
+#include <toys/path-cairo.h>
+
+using std::vector;
+
+//Utility functions
+double uniform();
+
+void draw_text(cairo_t *cr, Geom::Point pos, const char* txt, bool bottom = false, const char* fontdesc = "Sans");
+void draw_text(cairo_t *cr, Geom::Point pos, const std::string& txt, bool bottom = false, const std::string& fontdesc = "Sans");
+void draw_number(cairo_t *cr, Geom::Point pos, int num, std::string name=std::string(), bool bottom = true);
+void draw_number(cairo_t *cr, Geom::Point pos, unsigned num, std::string name=std::string(), bool bottom = true);
+void draw_number(cairo_t *cr, Geom::Point pos, double num, std::string name=std::string(), bool bottom = true);
+
+struct colour{
+ double r,g,b,a;
+ colour(double r, double g, double b, double a) : r(r), g(g), b(b), a(a) {}
+ static colour from_hsv( float H, // hue shift (radians)
+ float S, // saturation shift (scalar)
+ float V, // value multiplier (scalar)
+ float A
+ );
+ static colour from_hsl( float H, // hue shift (radians)
+ float S, // saturation shift (scalar)
+ float L, // value multiplier (scalar)
+ float A
+ );
+};
+void cairo_set_source_rgba(cairo_t* cr, colour c);
+
+class Handle{
+public:
+ std::string name;
+ float rgb[3];
+ Handle() {rgb[0] = rgb[1] = rgb[2] = 0;}
+ virtual ~Handle() {}
+ virtual void draw(cairo_t *cr, bool annotes=false) = 0;
+
+ virtual void* hit(Geom::Point pos) = 0;
+ virtual void move_to(void* hit, Geom::Point om, Geom::Point m) = 0;
+ virtual void load(FILE* f)=0;
+ virtual void save(FILE* f)=0;
+};
+
+class Toggle : public Handle{
+public:
+ Geom::Rect bounds;
+ const char* text;
+ bool on;
+ Toggle() : bounds(Geom::Point(0,0), Geom::Point(0,0)), text(""), on(false) {}
+ Toggle(const char* txt, bool v) : bounds(Geom::Point(0,0), Geom::Point(0,0)), text(txt), on(v) {}
+ Toggle(Geom::Rect bnds, const char* txt, bool v) : bounds(bnds), text(txt), on(v) {}
+ void draw(cairo_t *cr, bool annotes = false) override;
+ void toggle();
+ void set(bool state);
+ void handle_click(GdkEventButton* e);
+ void* hit(Geom::Point pos) override;
+ void move_to(void* /*hit*/, Geom::Point /*om*/, Geom::Point /*m*/) override { /* not implemented */ }
+ void load(FILE* /*f*/) override { /* not implemented */ }
+ void save(FILE* /*f*/) override { /* not implemented */ }
+};
+
+
+template< typename T>
+class VectorHandle : public Handle
+{
+ public:
+ VectorHandle()
+ : m_handles()
+ {
+ }
+ void draw(cairo_t *cr, bool annotes=false) override
+ {
+ for (iterator it = m_handles.begin(); it != m_handles.end(); ++it)
+ it->draw(cr, annotes);
+ }
+
+ void* hit(Geom::Point pos) override
+ {
+ void* result = NULL;
+ for (iterator it = m_handles.begin(); it != m_handles.end(); ++it)
+ {
+ result = it->hit(pos);
+ if (result != NULL) break;
+ }
+ return result;
+ }
+
+ void move_to(void* hit, Geom::Point om, Geom::Point m) override
+ {
+ if (hit != NULL)
+ {
+ static_cast<T*>(hit)->move_to(hit, om, m);
+ }
+ }
+
+ void load(FILE* f) override
+ {
+ for (iterator it = m_handles.begin(); it != m_handles.end(); ++it)
+ it->load(f);
+ }
+
+ void save(FILE* f) override
+ {
+ for (iterator it = m_handles.begin(); it != m_handles.end(); ++it)
+ it->save(f);
+ }
+
+ void clear()
+ {
+ m_handles.clear();
+ }
+
+ void reserve(size_t sz)
+ {
+ m_handles.reserve(sz);
+ }
+
+ size_t size() const
+ {
+ return m_handles.size();
+ }
+
+ void push_back (const T& _handle)
+ {
+ m_handles.push_back(_handle);
+ }
+
+ const T& operator[] (size_t i) const
+ {
+ return m_handles.at(i);
+ }
+
+ T& operator[] (size_t i)
+ {
+ return m_handles.at(i);
+ }
+
+ private:
+ typedef typename std::vector<T>::iterator iterator;
+ std::vector<T> m_handles;
+}; // end class VectorHandle
+
+
+class PointHandle : public Handle{
+public:
+ PointHandle(double x, double y) : pos(x,y) {}
+ PointHandle(Geom::Point pt) : pos(pt) {}
+ PointHandle() {}
+ Geom::Point pos;
+ void draw(cairo_t *cr, bool annotes = false) override;
+
+ void* hit(Geom::Point mouse) override;
+ void move_to(void* hit, Geom::Point om, Geom::Point m) override;
+ void load(FILE* f) override;
+ void save(FILE* f) override;
+};
+
+class PointSetHandle : public Handle{
+public:
+ PointSetHandle() {}
+ std::vector<Geom::Point> pts;
+ void draw(cairo_t *cr, bool annotes = false) override;
+
+ void* hit(Geom::Point mouse) override;
+ void move_to(void* hit, Geom::Point om, Geom::Point m) override;
+ void push_back(double x, double y) {pts.emplace_back(x,y);}
+ void push_back(Geom::Point pt) {pts.push_back(pt);}
+ unsigned size() {return pts.size();}
+ Geom::D2<Geom::SBasis> asBezier();
+ void load(FILE* f) override;
+ void save(FILE* f) override;
+};
+
+class RectHandle : public Handle{
+public:
+ RectHandle() {}
+ RectHandle(Geom::Rect pos, bool show_center_handle) : pos(pos), show_center_handle(show_center_handle) {}
+ Geom::Rect pos;
+ bool show_center_handle;
+ void draw(cairo_t *cr, bool annotes = false) override;
+
+ void* hit(Geom::Point mouse) override;
+ void move_to(void* hit, Geom::Point om, Geom::Point m) override;
+ void load(FILE* f) override;
+ void save(FILE* f) override;
+};
+
+
+// used by Slider
+inline std::string default_formatter(double x)
+{
+ std::ostringstream os;
+ os << x;
+ return os.str();
+}
+
+class Slider : public Handle
+{
+ public:
+
+ typedef std::string (*formatter_t) (double );
+ typedef double value_type;
+
+ Slider()
+ : m_handle(), m_pos(Geom::Point(0,0)), m_length(1),
+ m_min(0), m_max(1), m_step(0), m_dir(Geom::X),
+ m_label(""), m_formatter(&default_formatter)
+ {
+ value(0);
+ }
+
+ // pass step = 0 for having a continuos value variation
+ Slider( value_type _min, value_type _max, value_type _step,
+ value_type _value, std::string _label = "" )
+ : m_handle(),m_pos(Geom::Point(0,0)), m_length(1),
+ m_min(_min), m_max(_max), m_step(_step), m_dir(Geom::X),
+ m_label(std::move(_label)), m_formatter(&default_formatter)
+ {
+ value(_value);
+ }
+
+ void set( value_type _min, value_type _max, value_type _step,
+ value_type _value, const std::string& _label = "" )
+ {
+ m_min = _min;
+ m_max = _max;
+ m_step = _step;
+ m_label = _label;
+ value(_value);
+ }
+
+ value_type value() const;
+
+ void value(value_type _value);
+
+ value_type max_value() const
+ {
+ return m_max;
+ }
+
+ void max_value(value_type _value);
+
+ value_type min_value() const
+ {
+ return m_min;
+ }
+
+ void min_value(value_type _value);
+
+ // dir = X horizontal slider dir = Y vertical slider
+ void geometry(Geom::Point _pos, value_type _length, Geom::Dim2 _dir = Geom::X);
+
+ void draw(cairo_t* cr, bool annotate = false) override;
+
+ void formatter( formatter_t _formatter )
+ {
+ m_formatter = _formatter;
+ }
+
+ void* hit(Geom::Point pos) override
+ {
+ if (m_handle.hit(pos) != NULL)
+ return this;
+ return NULL;
+ }
+
+ void move_to(void* hit, Geom::Point om, Geom::Point m) override;
+
+ void load(FILE* f) override
+ {
+ m_handle.load(f);
+ }
+
+ void save(FILE* f) override
+ {
+ m_handle.save(f);
+ }
+
+ private:
+ PointHandle m_handle;
+ Geom::Point m_pos;
+ value_type m_length;
+ value_type m_min, m_max, m_step;
+ int m_dir;
+ std::string m_label;
+ formatter_t m_formatter;
+};
+
+
+
+
+
+class Toy {
+public:
+ vector<Handle*> handles;
+ bool mouse_down = false;
+ Geom::Point old_mouse_point;
+ Handle* selected = nullptr;
+ void* hit_data = nullptr;
+ int canvas_click_button = 0;
+ double notify_offset = 0.0;
+ std::string name;
+ bool show_timings = false;
+ FILE* spool_file = nullptr; // if non-NULL we record all interactions to this file
+
+ Toy() {}
+
+ virtual ~Toy() {}
+
+ virtual void draw(cairo_t *cr, std::ostringstream *notify, int w, int h, bool save, std::ostringstream *timing_stream);
+
+ virtual void mouse_moved(GdkEventMotion* e);
+ virtual void mouse_pressed(GdkEventButton* e);
+ virtual void mouse_released(GdkEventButton* e);
+ virtual void canvas_click(Geom::Point at, int button);
+ virtual void scroll(GdkEventScroll* e);
+
+ virtual void key_hit(GdkEventKey */*e*/) {}
+
+ //Cheapo way of informing the framework what the toy would like drawn for it.
+ virtual bool should_draw_numbers() { return true; }
+ virtual int should_draw_bounds() { return 0; }
+
+ virtual void first_time(int /*argc*/, char** /*argv*/) {}
+
+ virtual void resize_canvas(Geom::Rect const & /*s*/) {}
+ virtual void load(FILE* f);
+ virtual void save(FILE* f);
+};
+
+//Framework Accesors
+void redraw();
+void take_screenshot(const char* file);
+void init(int argc, char **argv, Toy *t, int width=600, int height=600);
+
+void toggle_events(std::vector<Toggle> &ts, GdkEventButton* e);
+void draw_toggles(cairo_t *cr, std::vector<Toggle> &ts);
+Geom::Point read_point(FILE* f);
+
+
+
+
+
+const long long NS_PER_SECOND = 1000000000LL;
+const long long NS_PER_NS = 1;
+
+
+class Timer{
+public:
+ Timer() {}
+ // note that CPU time is tracked per-thread, so the timer is only useful
+ // in the thread it was start()ed from.
+
+ class Time{
+ public:
+ double value;
+ Time(long long /*s*/, long long l) : value(l) {}
+ Time(double v) : value(v) {}
+ Time operator/(double iters) const {
+ return Time(value / iters);
+ }
+ };
+
+ void start() {
+ nsec(start_time);
+ }
+ void lap(long long &ns) {
+ nsec(ns);
+ ns -= start_time;
+ }
+ Time lap() {
+ long long ns;
+ nsec(ns);
+ return Time(start_time, ns - start_time);
+ }
+ void nsec(long long &ns) {
+#if ! (defined(_WIN32) || defined(__APPLE__))
+ clock_gettime(clock, &ts);
+ ns = ts.tv_sec * NS_PER_SECOND + ts.tv_nsec / NS_PER_NS;
+#else
+ ns = 0;
+#endif
+ }
+ /** Ask the OS nicely for a big time slice */
+ void ask_for_timeslice() {
+#ifndef _WIN32
+ sched_yield();
+#endif
+ }
+private:
+ long long start_time;
+#if ! (defined(_WIN32) || defined(__APPLE__))
+ struct timespec ts;
+# ifdef _POSIX_THREAD_CPUTIME
+ static const clockid_t clock = CLOCK_THREAD_CPUTIME_ID;
+# else
+# ifdef CLOCK_MONOTONIC
+ static const clockid_t clock = CLOCK_MONOTONIC;
+# else
+ static const clockid_t clock = CLOCK_REALTIME;
+# endif
+# endif
+#endif
+};
+
+inline std::ostream& operator<<(std::ostream& o, Timer::Time const &t) {
+ double tm = t.value;
+ unsigned prefix = 0;
+ char prefixes[] = "num kMGT";
+ while(prefix < sizeof(prefixes) and tm > 1000) {
+ tm /= 1000.0;
+ prefix += 1;
+ }
+ o << tm << prefixes[prefix] << "s";
+ return o;
+}
+
+
+
+#endif // _2GEOM_TOY_FRAMEWORK2_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=4:softtabstop=4:fileencoding=utf-8:textwidth=99 :