summaryrefslogtreecommitdiffstats
path: root/src/2geom
diff options
context:
space:
mode:
authorJohan B. C. Engelen <jbc.engelen@swissonline.ch>2011-02-02 21:24:36 +0000
committerJohan Engelen <goejendaagh@zonnet.nl>2011-02-02 21:24:36 +0000
commit53933f5fea9d07d1ba6304b88439fba257ee8c34 (patch)
tree21f94cd05346fc1236751bb1db3e0850e5aece54 /src/2geom
parentTranslations. French translation minor update. (diff)
downloadinkscape-53933f5fea9d07d1ba6304b88439fba257ee8c34.tar.gz
inkscape-53933f5fea9d07d1ba6304b88439fba257ee8c34.zip
update to latest 2geom !
(bzr r10025)
Diffstat (limited to 'src/2geom')
-rw-r--r--src/2geom/Makefile_insert2
-rw-r--r--src/2geom/angle.h208
-rw-r--r--src/2geom/basic-intersection.cpp2
-rw-r--r--src/2geom/bezier-curve.h390
-rw-r--r--src/2geom/bezier-utils.cpp20
-rw-r--r--src/2geom/bezier.h11
-rw-r--r--src/2geom/chebyshev.cpp2
-rw-r--r--src/2geom/choose.h56
-rw-r--r--src/2geom/circle.cpp3
-rw-r--r--src/2geom/circle.h8
-rw-r--r--src/2geom/convex-cover.cpp137
-rw-r--r--src/2geom/convex-cover.h3
-rw-r--r--src/2geom/coord.h11
-rw-r--r--src/2geom/curve.h328
-rw-r--r--src/2geom/curves.h13
-rw-r--r--src/2geom/d2-sbasis.cpp53
-rw-r--r--src/2geom/d2-sbasis.h9
-rw-r--r--src/2geom/d2.h4
-rw-r--r--src/2geom/ellipse.cpp26
-rw-r--r--src/2geom/ellipse.h13
-rw-r--r--src/2geom/elliptical-arc.cpp1601
-rw-r--r--src/2geom/elliptical-arc.h420
-rw-r--r--src/2geom/exception.h26
-rw-r--r--src/2geom/forward.h59
-rw-r--r--src/2geom/hvlinesegment.h536
-rw-r--r--src/2geom/interval.h414
-rw-r--r--src/2geom/line.cpp164
-rw-r--r--src/2geom/line.h471
-rw-r--r--src/2geom/matrix.cpp258
-rw-r--r--src/2geom/matrix.h165
-rw-r--r--src/2geom/numeric/fitting-model.h50
-rw-r--r--src/2geom/numeric/matrix.cpp43
-rw-r--r--src/2geom/numeric/matrix.h42
-rw-r--r--src/2geom/numeric/vector.h18
-rw-r--r--src/2geom/path-intersection.cpp130
-rw-r--r--src/2geom/path.cpp18
-rw-r--r--src/2geom/path.h4
-rw-r--r--src/2geom/pathvector.cpp2
-rw-r--r--src/2geom/pathvector.h7
-rw-r--r--src/2geom/piecewise.cpp74
-rw-r--r--src/2geom/piecewise.h135
-rw-r--r--src/2geom/point.cpp149
-rw-r--r--src/2geom/point.h410
-rw-r--r--src/2geom/poly.cpp1
-rw-r--r--src/2geom/poly.h3
-rw-r--r--src/2geom/ray.h32
-rw-r--r--src/2geom/rect.h252
-rw-r--r--src/2geom/recursive-bezier-intersection.cpp26
-rw-r--r--src/2geom/region.cpp4
-rw-r--r--src/2geom/region.h2
-rw-r--r--src/2geom/sbasis-curve.h127
-rw-r--r--src/2geom/sbasis-geometric.cpp41
-rw-r--r--src/2geom/sbasis-roots.cpp232
-rw-r--r--src/2geom/sbasis.h84
-rw-r--r--src/2geom/shape.cpp2
-rw-r--r--src/2geom/shape.h2
-rw-r--r--src/2geom/solve-bezier-one-d.cpp267
-rw-r--r--src/2geom/solver.h15
-rw-r--r--src/2geom/svg-elliptical-arc.cpp938
-rw-r--r--src/2geom/svg-elliptical-arc.h383
-rw-r--r--src/2geom/svg-path-parser.cpp47
-rw-r--r--src/2geom/svg-path.cpp4
-rw-r--r--src/2geom/sweep.h3
-rw-r--r--src/2geom/transforms.cpp199
-rw-r--r--src/2geom/transforms.h260
-rw-r--r--src/2geom/utils.h41
66 files changed, 4703 insertions, 4757 deletions
diff --git a/src/2geom/Makefile_insert b/src/2geom/Makefile_insert
index 7ded3a637..ead0e3517 100644
--- a/src/2geom/Makefile_insert
+++ b/src/2geom/Makefile_insert
@@ -22,7 +22,7 @@
2geom/elliptical-arc.cpp \
2geom/geom.cpp \
2geom/line.cpp \
- 2geom/matrix.cpp \
+ 2geom/affine.cpp \
2geom/nearest-point.cpp \
2geom/path-intersection.cpp \
2geom/path.cpp \
diff --git a/src/2geom/angle.h b/src/2geom/angle.h
index c950dd803..42e3531f3 100644
--- a/src/2geom/angle.h
+++ b/src/2geom/angle.h
@@ -1,11 +1,13 @@
/**
- * \file angle.h
+ * \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 authors
+ * 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
@@ -35,12 +37,177 @@
#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
+/** @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.
+ *
+ * @ingroup Primitives
+ */
+class Angle
+ : boost::additive< Angle
+ , boost::equality_comparable< Angle
+ > >
+{
+public:
+ Angle(Coord v) : _angle(v) { _normalize(); } // this can be called implicitly
+ explicit Angle(Point p) : _angle(atan2(p)) { _normalize(); }
+ Angle(Point a, Point b) : _angle(angle_between(a, b)) { _normalize(); }
+ operator Coord() const { return radians(); }
+ Angle &operator+=(Angle const &o) {
+ _angle += o._angle;
+ _normalize();
+ return *this;
+ }
+ Angle &operator-=(Angle const &o) {
+ _angle -= o._angle;
+ _normalize();
+ return *this;
+ }
+ bool operator==(Angle const &o) const {
+ return _angle == o._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() / M_PI * 180.0; }
+ /** @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 / M_PI * 180.0;
+ if (ret < 0) ret += 360;
+ return ret;
+ }
+
+ static Angle from_degrees(Coord d) {
+ Angle a(d * M_PI / 180);
+ return a;
+ }
+ 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:
+ Angle() {}
+ void _normalize() {
+ _angle -= floor(_angle / (2*M_PI)) * 2*M_PI;
+ }
+ Coord _angle; // this is always in [0, 2pi)
+ friend class AngleInterval;
+};
+
+/** @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 contains the zero angle.
+ *
+ * This class is immutable - you cannot change the values of start and end angles
+ * without creating a new instance of this class.
+ *
+ * @ingroup Primitives
+ */
+class AngleInterval {
+public:
+ AngleInterval(Angle const &s, Angle const &e, bool cw = false)
+ : _start_angle(s), _end_angle(e), _sweep(cw)
+ {}
+ AngleInterval(double s, double e, bool cw = false)
+ : _start_angle(s), _end_angle(e), _sweep(cw)
+ {}
+ /** @brief Get the angular coordinate of the interval's initial point
+ * @return Angle in range \f$[0,2\pi)\f$ corresponding to the start of arc */
+ Angle const &initialAngle() const { return _start_angle; }
+ /** @brief Get the angular coordinate of the interval's final point
+ * @return Angle in range \f$[0,2\pi)\f$ corresponding to the end of arc */
+ Angle const &finalAngle() const { return _end_angle; }
+ bool isDegenerate() const { return initialAngle() == finalAngle(); }
+ /** @brief 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); }
+#if 0
+ /** @brief Find an angle nearest to the specified time value.
+ * @param a Query angle
+ * @return If the interval contains the query angle, a number from the range [0, 1]
+ * such that a = angleAt(t); otherwise, 0 or 1, depending on which extreme
+ * angle is nearer. */
+ Coord nearestAngle(Angle const &a) const {
+ Coord dist = distance(_start_angle, a, _sweep).radians0();
+ Coord span = distance(_start_angle, _end_angle, _sweep).radians0();
+ if (dist <= span) return dist / span;
+ else return distance_abs(_start_angle, a).radians0() > distance_abs(_end_angle, a).radians0() ? 1.0 : 0.0;
+ }
+#endif
+ /** @brief Check whether the interval includes the given angle. */
+ bool contains(Angle const &a) const {
+ 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.
+ * @return Extent in range \f$[0, 2\pi)\f$ */
+ Coord extent() const {
+ Coord d = _end_angle - _start_angle;
+ if (!_sweep) d = -d;
+ if (d < 0) d += 2*M_PI;
+ return d;
+ }
+protected:
+ AngleInterval() {}
+ Angle _start_angle;
+ Angle _end_angle;
+ bool _sweep;
+};
+
inline double deg_to_rad(double deg) { return deg*M_PI/180.0;}
inline double rad_to_deg(double rad) { return rad*180.0/M_PI;}
@@ -89,9 +256,44 @@ Coord map_unit_interval_on_circular_arc(Coord t, double start_angle, double end_
return angle;
}
+/*
+ * Return true if the given angle is contained in the circular arc determined
+ * by the passed angles.
+ *
+ * a: the angle to be tested
+ * sa: the angle the arc start from
+ * ia: an angle strictly inner to the arc
+ * ea: the angle the arc end to
+ *
+ * prerequisite: the inner angle has to be not equal (mod 2PI) to the start
+ * or the end angle, except when they are equal each other, too.
+ * warning: when sa == ea (mod 2PI) they define a whole circle
+ * if ia != sa (mod 2PI), on the contrary if ia == sa == ea (mod 2PI)
+ * they define a single point.
+ */
+inline
+bool arc_contains (double a, double sa, double ia, double ea)
+{
+ a -= sa;
+ a = std::fmod(a, 2*M_PI);
+ if (a < 0) a += 2*M_PI;
+ ia -= sa;
+ ia = std::fmod(ia, 2*M_PI);
+ if (ia < 0) ia += 2*M_PI;
+ ea -= sa;
+ ea = std::fmod(ea, 2*M_PI);
+ if (ea < 0) ea += 2*M_PI;
+ if (ia == 0 && ea == 0) return (a == 0);
+ if (ia == 0 || ia == ea)
+ {
+ THROW_RANGEERROR ("arc_contains: passed angles do not define an arc");
+ }
+ return (ia < ea && a <= ea) || (ia > ea && (a >= ea || a == 0));
}
+} // end namespace Geom
+
#endif
diff --git a/src/2geom/basic-intersection.cpp b/src/2geom/basic-intersection.cpp
index 66f174da6..3be6792b9 100644
--- a/src/2geom/basic-intersection.cpp
+++ b/src/2geom/basic-intersection.cpp
@@ -202,7 +202,7 @@ static void intersect_polish_root (D2<SBasis> const &A, double &s,
// We're using the standard transformation matricies, which is numerically rather poor. Much better to solve the equation using elimination.
- Matrix jack(as[1][0], as[1][1],
+ Affine jack(as[1][0], as[1][1],
-bs[1][0], -bs[1][1],
0, 0);
Point soln = (F)*jack.inverse();
diff --git a/src/2geom/bezier-curve.h b/src/2geom/bezier-curve.h
index c943512c7..1014c79f0 100644
--- a/src/2geom/bezier-curve.h
+++ b/src/2geom/bezier-curve.h
@@ -1,12 +1,14 @@
/**
* \file
- * \brief Bezier-Curve
+ * \brief Bezier curve
*
+ *//*
* Authors:
- * MenTaLguY <mental@rydia.net>
- * Marco Cecchetti <mrcekets at gmail.com>
+ * MenTaLguY <mental@rydia.net>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
*
- * Copyright 2007-2008 authors
+ * Copyright 2007-2009 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -32,217 +34,225 @@
* the specific language governing rights and limitations.
*/
-
-
-
-#ifndef _2GEOM_BEZIER_CURVE_H_
-#define _2GEOM_BEZIER_CURVE_H_
-
+#ifndef SEEN_LIB2GEOM_BEZIER_CURVE_H
+#define SEEN_LIB2GEOM_BEZIER_CURVE_H
#include <2geom/curve.h>
#include <2geom/sbasis-curve.h> // for non-native winding method
#include <2geom/bezier.h>
-#include <algorithm>
-
-
namespace Geom
{
-
-
-template <unsigned order>
class BezierCurve : public Curve {
-
-private:
- D2<Bezier > inner;
-
-public:
- template <unsigned required_degree>
- static void assert_degree(BezierCurve<required_degree> const *) {}
-
- BezierCurve() : inner(Bezier::Order(order), Bezier::Order(order)) {
- }
-
- explicit BezierCurve(D2<Bezier > const &x) : inner(x) {}
-
- BezierCurve(Bezier x, Bezier y) : inner(x, y) {}
-
- // default copy
- // default assign
-
- BezierCurve(Point c0, Point c1) {
- assert_degree<1>(this);
- for(unsigned d = 0; d < 2; d++)
- inner[d] = Bezier(c0[d], c1[d]);
- }
-
- BezierCurve(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]);
- }
-
- BezierCurve(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]);
- }
-
- unsigned degree() const { return order; }
-
- Curve *duplicate() const { return new BezierCurve(*this); }
-
- Point initialPoint() const { return inner.at0(); }
- Point finalPoint() const { return inner.at1(); }
-
- bool isDegenerate() const { return inner.isConstant(); }
-
- void setInitial(Point v) { setPoint(0, v); }
- void setFinal(Point v) { setPoint(order, v); }
-
- void setPoint(unsigned ix, Point v) { inner[X].setPoint(ix, v[X]); inner[Y].setPoint(ix, v[Y]); }
- Point const operator[](unsigned ix) const { return Point(inner[X][ix], inner[Y][ix]); }
-
- virtual OptRect boundsFast() const { return bounds_fast(inner); }
- virtual OptRect boundsExact() const { return bounds_exact(inner); }
- virtual OptRect boundsLocal(OptInterval i, unsigned deg) const {
- 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();
- }
-//TODO: local
-
-//TODO: implement next 3 natively
- int winding(Point p) const {
- return SBasisCurve(toSBasis()).winding(p);
- }
-
- virtual int degreesOfFreedom() const {
- return 2*order;
- }
-
- std::vector<double>
- roots(double v, Dim2 d) const {
- return (inner[d] - v).roots();
- }
-
- double nearestPoint( Point const& p, double from = 0, double to = 1 ) const
- {
- return Curve::nearestPoint(p, from, to);
- }
-
- void setPoints(std::vector<Point> ps) {
- for(unsigned i = 0; i <= order; i++) {
- setPoint(i, ps[i]);
- }
- }
- std::vector<Point> points() const { return bezier_points(inner); }
-
- std::pair<BezierCurve<order>, BezierCurve<order> > subdivide(Coord t) const {
- std::pair<Bezier, Bezier > sx = inner[X].subdivide(t), sy = inner[Y].subdivide(t);
- return std::pair<BezierCurve<order>, BezierCurve<order> >(
- BezierCurve<order>(sx.first, sy.first),
- BezierCurve<order>(sx.second, sy.second));
- }
-
- Curve *portion(double f, double t) const {
- return new BezierCurve(Geom::portion(inner, f, t));
- }
-
- Curve *reverse() const {
- return new BezierCurve(Geom::reverse(inner));
- }
-
- Curve *transformed(Matrix const &m) const {
- BezierCurve *ret = new BezierCurve();
- std::vector<Point> ps = points();
- for(unsigned i = 0; i <= order; i++) ps[i] = ps[i] * m;
- ret->setPoints(ps);
- return ret;
- }
-
- Curve *derivative() const;
-
- Point pointAt(double t) const { return inner.valueAt(t); }
- std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const { return inner.valueAndDerivatives(t, n); }
-
- double valueAt(double t, Dim2 d) const { return inner[d].valueAt(t); }
-
- D2<SBasis> toSBasis() const {return inner.toSBasis(); }
-
protected:
- BezierCurve(Point c[]) {
- Coord x[order+1], y[order+1];
- for(unsigned i = 0; i <= order; i++) {
- x[i] = c[i][X]; y[i] = c[i][Y];
+ D2<Bezier> inner;
+
+public:
+ /// @name Construct the curve
+ /// @{
+ /** @brief Construct a Bezier curve of the specified order with all points zero. */
+ explicit BezierCurve(unsigned order)
+ : inner(Bezier::Order(order), Bezier::Order(order))
+ {}
+ /** @brief Construct from 2D Bezier polynomial. */
+ explicit BezierCurve(D2<Bezier > const &x)
+ : inner(x)
+ {
+ if (inner[X].order() != inner[Y].order())
+ THROW_LOGICALERROR("Beziers used to construct a BezierCurve "
+ "are not of the same order");
+ }
+ /** @brief Construct from two 1D Bezier polynomials of the same order. */
+ BezierCurve(Bezier x, Bezier y)
+ : inner(x, y)
+ {
+ // throw an error if the orders do not match
+ if (x.order() != y.order())
+ THROW_LOGICALERROR("Beziers used to construct a BezierCurve "
+ "are not of the same order");
+ }
+ /** @brief Construct a linear segment from its endpoints. */
+ BezierCurve(Point c0, Point c1) {
+ for(unsigned d = 0; d < 2; d++)
+ inner[d] = Bezier(c0[d], c1[d]);
+ }
+ /** @brief Construct a quadratic Bezier curve from its control points. */
+ BezierCurve(Point c0, Point c1, Point c2) {
+ 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. */
+ BezierCurve(Point c0, Point c1, Point c2, Point c3) {
+ for(unsigned d = 0; d < 2; d++)
+ inner[d] = Bezier(c0[d], c1[d], c2[d], c3[d]);
+ }
+ /** @brief Construct a Bezier curve from a vector of its control points. */
+ BezierCurve(std::vector<Point> const &points) {
+ unsigned ord = points.size() - 1;
+ if (ord < 1) THROW_LOGICALERROR("Not enough points to construct a Bezier curve");
+ 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];
+ }
+ }
+ /// @}
+
+ // methods new to BezierCurve go here
+
+ /// @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 control points.
+ * @return Vector with order() + 1 control points. */
+ std::vector<Point> points() const { return bezier_points(inner); }
+ /** @brief Modify a control point.
+ * @param ix The zero-based index of the point to modify
+ * @param v The new value of the point */
+ void setPoint(unsigned ix, Point v) {
+ inner[X].setPoint(ix, v[X]);
+ inner[Y].setPoint(ix, v[Y]);
+ }
+ /** @brief Set new control points for this curve.
+ * @param ps Vector which must contain order() + 1 points. */
+ 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]);
+ }
+ }
+ /** Access control points of the curve.
+ * @param ix Index of the control point
+ * @return The control point. No-reference return, use setPoint() to modify control points. */
+ Point const operator[](unsigned ix) const { return Point(inner[X][ix], inner[Y][ix]); }
+ /// @}
+
+ /** @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<BezierCurve, BezierCurve> subdivide(Coord t) const {
+ std::pair<Bezier, Bezier> sx = inner[X].subdivide(t), sy = inner[Y].subdivide(t);
+ return std::make_pair(
+ BezierCurve(sx.first, sy.first),
+ BezierCurve(sx.second, sy.second));
+ }
+ BezierCurve *optimize() const;
+
+ // implementation of virtual methods goes here
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ virtual Curve *duplicate() const;
+ virtual Point initialPoint() const { return inner.at0(); }
+ virtual Point finalPoint() const { return inner.at1(); }
+ virtual bool isDegenerate() const { return inner.isConstant(); }
+ virtual void setInitial(Point const &v) { setPoint(0, v); }
+ virtual void setFinal(Point const &v) { setPoint(order(), v); }
+ virtual Rect boundsFast() const { return *bounds_fast(inner); }
+ virtual Rect boundsExact() const { return *bounds_exact(inner); }
+ virtual OptRect boundsLocal(OptInterval const &i, unsigned deg) const {
+ 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();
+ }
+ virtual int degreesOfFreedom() const {
+ return 2 * (order() + 1);
+ }
+ virtual std::vector<Coord> roots(Coord v, Dim2 d) const {
+ return (inner[d] - v).roots();
+ }
+ Curve *portion(Coord f, Coord t) const {
+ return new BezierCurve(Geom::portion(inner, f, t));
}
- inner = Bezier(x, y);
- }
+ Curve *reverse() const {
+ return new BezierCurve(Geom::reverse(inner));
+ }
+ virtual Curve *transformed(Affine const &m) const {
+ BezierCurve *ret = new BezierCurve(order());
+ std::vector<Point> ps = points();
+ for(unsigned i = 0; i <= order(); i++) ps[i] = ps[i] * m;
+ ret->setPoints(ps);
+ return ret;
+ }
+ virtual Curve *derivative() const;
+ virtual Coord length(Coord tolerance) const;
+ virtual Point pointAt(Coord t) const { return inner.valueAt(t); }
+ virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const { return inner.valueAndDerivatives(t, n); }
+ virtual Coord valueAt(Coord t, Dim2 d) const { return inner[d].valueAt(t); }
+ virtual D2<SBasis> toSBasis() const {return inner.toSBasis(); }
+#endif
};
-// BezierCurve<0> is meaningless; specialize it out
-template<> class BezierCurve<0> : public BezierCurve<1> { public: BezierCurve();};
-
-typedef BezierCurve<1> LineSegment;
-typedef BezierCurve<2> QuadraticBezier;
-typedef BezierCurve<3> CubicBezier;
-
-
-template<>
-inline
-double LineSegment::nearestPoint(Point const& p, double from, double to) const
-{
- if ( from > to ) std::swap(from, to);
- Point ip = pointAt(from);
- Point fp = pointAt(to);
- Point v = fp - ip;
- double l2v = L2sq(v);
- if(l2v == 0) return 0;
- double t = dot( p - ip, v ) / l2v;
- if ( t <= 0 ) return from;
- else if ( t >= 1 ) return to;
- else return from + t*(to-from);
-}
+class LineSegment : public BezierCurve {
+public:
+ LineSegment() : BezierCurve(1) {}
+ LineSegment(Point c0, Point c1) : BezierCurve(c0, c1) {}
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ virtual Curve *duplicate() const { return new LineSegment(*this); }
+ virtual Rect boundsFast() const { return Rect(initialPoint(), finalPoint()); }
+ virtual Rect boundsExact() const { return boundsFast(); }
+ virtual Coord nearestPoint(Point const& p, Coord from = 0, Coord to = 1) const;
+ virtual Curve *portion(Coord f, Coord t) const {
+ return new LineSegment(pointAt(f), pointAt(t));
+ }
+ virtual Curve *reverse() const { return new LineSegment(finalPoint(), initialPoint()); }
+ virtual Curve *transformed(Affine const &m) const {
+ return new LineSegment(initialPoint() * m, finalPoint() * m);
+ }
+ virtual Curve *derivative() const {
+ Coord dx = inner[X][1] - inner[X][0], dy = inner[Y][1] - inner[Y][0];
+ return new LineSegment(Point(dx,dy),Point(dx,dy));
+ }
+ virtual Coord length(Coord) const { return distance(initialPoint(), finalPoint()); }
+#endif
+};
-inline
-Point middle_point(LineSegment const& _segment)
-{
- return ( _segment.initialPoint() + _segment.finalPoint() ) / 2;
-}
+class QuadraticBezier : public BezierCurve {
+public:
+ QuadraticBezier(Point c0, Point c1, Point c2) : BezierCurve(c0, c1, c2) {}
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ virtual Coord length(Coord tolerance) const;
+#endif
+};
-inline
-double length(LineSegment const& _segment)
-{
- return distance(_segment.initialPoint(), _segment.finalPoint());
-}
+/** @brief Cubic Bezier curve.
+ * Note that if you created a BezierCurve, you will not be able to cast it to a CubicBezier
+ * using a dynamic cast regardless of its order - use the optimize() method.
+ * @ingroup Curves */
+class CubicBezier : public BezierCurve {
+public:
+ CubicBezier(Point c0, Point c1, Point c2, Point c3) : BezierCurve(c0, c1, c2, c3) {}
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ virtual Coord length(Coord tolerance) const;
+#endif
+};
-template <unsigned order>
-inline
-Curve *BezierCurve<order>::derivative() const {
- return new BezierCurve<order-1>(Geom::derivative(inner[X]), Geom::derivative(inner[Y]));
+inline Point middle_point(LineSegment const& _segment) {
+ return ( _segment.initialPoint() + _segment.finalPoint() ) / 2;
}
-template <>
-inline
-Curve *BezierCurve<1>::derivative() const {
- double dx = inner[X][1] - inner[X][0], dy = inner[Y][1] - inner[Y][0];
- return new BezierCurve<1>(Point(dx,dy),Point(dx,dy));
+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 // _2GEOM_BEZIER_CURVE_H_
-
-
-
/*
Local Variables:
mode:c++
diff --git a/src/2geom/bezier-utils.cpp b/src/2geom/bezier-utils.cpp
index dc8025115..eb317940f 100644
--- a/src/2geom/bezier-utils.cpp
+++ b/src/2geom/bezier-utils.cpp
@@ -56,9 +56,7 @@
#include <2geom/isnan.h>
#include <assert.h>
-namespace Geom{
-
-typedef Point BezierCurve[];
+namespace Geom {
/* Forward declarations */
static void generate_bezier(Point b[], Point const d[], double const u[], unsigned len,
@@ -68,16 +66,16 @@ static void estimate_lengths(Point bezier[],
Point const &tHat1, Point const &tHat2);
static void estimate_bi(Point b[4], unsigned ei,
Point const data[], double const u[], unsigned len);
-static void reparameterize(Point const d[], unsigned len, double u[], BezierCurve const bezCurve);
-static double NewtonRaphsonRootFind(BezierCurve const Q, Point const &P, double u);
+static void reparameterize(Point const d[], unsigned len, double u[], Point const bezCurve[]);
+static double NewtonRaphsonRootFind(Point const Q[], Point const &P, double u);
static Point darray_center_tangent(Point const d[], unsigned center, unsigned length);
static Point darray_right_tangent(Point const d[], unsigned const len);
static unsigned copy_without_nans_or_adjacent_duplicates(Point const src[], unsigned src_len, Point dest[]);
static void chord_length_parameterize(Point const d[], double u[], unsigned len);
static double compute_max_error_ratio(Point const d[], double const u[], unsigned len,
- BezierCurve const bezCurve, double tolerance,
+ Point const bezCurve[], double tolerance,
unsigned *splitPoint);
-static double compute_hook(Point const &a, Point const &b, double const u, BezierCurve const bezCurve,
+static double compute_hook(Point const &a, Point const &b, double const u, Point const bezCurve[],
double const tolerance);
@@ -546,7 +544,7 @@ static void
reparameterize(Point const d[],
unsigned const len,
double u[],
- BezierCurve const bezCurve)
+ Point const bezCurve[])
{
assert( 2 <= len );
@@ -572,7 +570,7 @@ reparameterize(Point const d[],
* \return Improved u
*/
static double
-NewtonRaphsonRootFind(BezierCurve const Q, Point const &P, double const u)
+NewtonRaphsonRootFind(Point const Q[], Point const &P, double const u)
{
assert( 0.0 <= u );
assert( u <= 1.0 );
@@ -904,7 +902,7 @@ chord_length_parameterize(Point const d[], double u[], unsigned const len)
*/
static double
compute_max_error_ratio(Point const d[], double const u[], unsigned const len,
- BezierCurve const bezCurve, double const tolerance,
+ Point const bezCurve[], double const tolerance,
unsigned *const splitPoint)
{
assert( 2 <= len );
@@ -974,7 +972,7 @@ compute_max_error_ratio(Point const d[], double const u[], unsigned const len,
* distance.)
*/
static double
-compute_hook(Point const &a, Point const &b, double const u, BezierCurve const bezCurve,
+compute_hook(Point const &a, Point const &b, double const u, Point const bezCurve[],
double const tolerance)
{
Point const P = bezier_pt(3, bezCurve, u);
diff --git a/src/2geom/bezier.h b/src/2geom/bezier.h
index 2a06d44f5..a7d75da45 100644
--- a/src/2geom/bezier.h
+++ b/src/2geom/bezier.h
@@ -46,7 +46,7 @@ namespace Geom {
inline Coord subdivideArr(Coord t, Coord const *v, Coord *left, Coord *right, unsigned order) {
/*
* Bernstein :
- * Evaluate a Bernstein function at a particular parameter value
+ * Evaluate a Bernstein function at a particular parameter value
* Fill in control points for resulting sub-curves.
*
*/
@@ -248,7 +248,7 @@ public:
// initialize temp storage variables
std::valarray<Coord> d_(order()+1);
- for (unsigned i = 0; i < size(); i++) {
+ for(unsigned i = 0; i < size(); i++) {
d_[i] = c_[i];
}
@@ -256,10 +256,10 @@ public:
if(n_derivs > order()) {
nn = order()+1; // only calculate the non zero derivs
}
- for (unsigned di = 0; di < nn; di++) {
+ for(unsigned di = 0; di < nn; di++) {
//val_n_der[di] = (subdivideArr(t, &d_[0], NULL, NULL, order() - di));
val_n_der[di] = bernsteinValueAt(t, &d_[0], order() - di);
- for (unsigned i = 0; i < order() - di; i++) {
+ for(unsigned i = 0; i < order() - di; i++) {
d_[i] = (order()-di)*(d_[i+1] - d_[i]);
}
}
@@ -381,7 +381,8 @@ inline Bezier integral(const Bezier & a) {
}
inline OptInterval bounds_fast(Bezier const & b) {
- return Interval::fromArray(&const_cast<Bezier&>(b).c_[0], b.size());
+ OptInterval ret = Interval::from_array(&const_cast<Bezier&>(b).c_[0], b.size());
+ return ret;
}
//TODO: better bounds exact
diff --git a/src/2geom/chebyshev.cpp b/src/2geom/chebyshev.cpp
index d0d6edab4..c886daf88 100644
--- a/src/2geom/chebyshev.cpp
+++ b/src/2geom/chebyshev.cpp
@@ -93,7 +93,7 @@ SBasis chebyshev_approximant_interpolating (double (*f)(double,void*),
wr.fa = fa;
wr.fb = fb;
wr.in = in;
- //printf("%f %f\n", fa, fb);
+ printf("%f %f\n", fa, fb);
wr.f = f;
wr.pp = p;
return compose(Linear(in[0], in[1]), Linear(fa, fb)) + chebyshev_approximant(f_interp, order, in, &wr) + Linear(fa, fb);
diff --git a/src/2geom/choose.h b/src/2geom/choose.h
index 579c46718..3fecf1ba2 100644
--- a/src/2geom/choose.h
+++ b/src/2geom/choose.h
@@ -33,7 +33,7 @@
#define _CHOOSE_H
#include <vector>
-// XXX: Can we keep only the left terms easily?
+// XXX: Can we keep only the left terms easily?
// this would more than halve the array
// row index becomes n2 = n/2, row2 = n2*(n2+1)/2, row = row2*2+(n&1)?n2:0
// we could also leave off the ones
@@ -54,7 +54,7 @@ T choose(unsigned n, unsigned k) {
unsigned p = pascals_triangle.size() - rows_done;
pascals_triangle.push_back(1);
for(unsigned i = 0; i < rows_done-1; i++) {
- pascals_triangle.push_back(pascals_triangle[p]
+ pascals_triangle.push_back(pascals_triangle[p]
+ pascals_triangle[p+1]);
p++;
}
@@ -75,6 +75,58 @@ T choose(unsigned n, unsigned k) {
return r;
}*/
+
+
+template <typename ValueType>
+class BinomialCoefficient
+{
+ public:
+ typedef ValueType value_type;
+ typedef std::vector<value_type> container_type;
+
+ BinomialCoefficient(unsigned int _n)
+ : n(_n), m(n >> 1)
+ {
+ coefficients.reserve(m+1);
+ coefficients.push_back(1);
+ int h = m + 1;
+ value_type bct = 1;
+ for (int i = 1; i < h; ++i)
+ {
+ bct *= (n-i+1);
+ bct /= i;
+ coefficients.push_back(bct);
+ }
+ }
+
+ unsigned int size() const
+ {
+ return degree() +1;
+ }
+
+ unsigned int degree() const
+ {
+ return n;
+ }
+
+ value_type operator[] (unsigned int k) const
+ {
+ if (k > m) k = n - k;
+ return coefficients[k];
+ }
+
+ private:
+ const int n;
+ const unsigned int m;
+ container_type coefficients;
+};
+
+
+
+
+
+
+
#endif
/*
diff --git a/src/2geom/circle.cpp b/src/2geom/circle.cpp
index 8a0704735..06eb4c2a1 100644
--- a/src/2geom/circle.cpp
+++ b/src/2geom/circle.cpp
@@ -89,10 +89,11 @@ void Circle::set(std::vector<Point> const& points)
}
-SVGEllipticalArc
+EllipticalArc *
Circle::arc(Point const& initial, Point const& inner, Point const& final,
bool _svg_compliant)
{
+ // TODO native implementation!
Ellipse e(center(X), center(Y), ray(), ray(), 0);
return e.arc(initial, inner, final, _svg_compliant);
}
diff --git a/src/2geom/circle.h b/src/2geom/circle.h
index 987570b62..ec58e163a 100644
--- a/src/2geom/circle.h
+++ b/src/2geom/circle.h
@@ -39,11 +39,12 @@
#include <2geom/point.h>
#include <2geom/exception.h>
#include <2geom/path.h>
+#include <vector>
namespace Geom
{
-class SVGEllipticalArc;
+class EllipticalArc;
class Circle
{
@@ -87,12 +88,11 @@ class Circle
// prerequisite: at least 3 points must be passed
void set(std::vector<Point> const& points);
- SVGEllipticalArc
+ EllipticalArc *
arc(Point const& initial, Point const& inner, Point const& final,
bool _svg_compliant = true);
- void
- getPath(std::vector<Path> &path_out);
+ void getPath(std::vector<Path> &path_out);
Point center() const
{
diff --git a/src/2geom/convex-cover.cpp b/src/2geom/convex-cover.cpp
index db8094540..21a5c3107 100644
--- a/src/2geom/convex-cover.cpp
+++ b/src/2geom/convex-cover.cpp
@@ -33,6 +33,7 @@
#include <2geom/exception.h>
#include <algorithm>
#include <map>
+#include <assert.h>
/** Todo:
+ modify graham scan to work top to bottom, rather than around angles
@@ -48,6 +49,7 @@
using std::vector;
using std::map;
using std::pair;
+using std::make_pair;
namespace Geom{
@@ -338,40 +340,74 @@ ConvexHull::is_degenerate() const {
}
-/* Here we really need a rotating calipers implementation. This implementation is slow and incorrect.
- This incorrectness is a problem because it throws off the algorithms. Perhaps I will come up with
- something better tomorrow. The incorrectness is in the order of the bridges - they must be in the
- order of traversal around. Since the a->b and b->a bridges are seperated, they don't need to be merge
- order, just the order of the traversal of the host hull. Currently some situations make a n->0 bridge
- first.*/
-pair< map<int, int>, map<int, int> >
-bridges(ConvexHull a, ConvexHull b) {
- map<int, int> abridges;
- map<int, int> bbridges;
-
- for(unsigned ia = 0; ia < a.boundary.size(); ia++) {
- for(unsigned ib = 0; ib < b.boundary.size(); ib++) {
- Point d = b[ib] - a[ia];
- Geom::Coord e = cross(d, a[ia - 1] - a[ia]), f = cross(d, a[ia + 1] - a[ia]);
- Geom::Coord g = cross(d, b[ib - 1] - a[ia]), h = cross(d, b[ib + 1] - a[ia]);
- if (e > 0 && f > 0 && g > 0 && h > 0) abridges[ia] = ib;
- else if(e < 0 && f < 0 && g < 0 && h < 0) bbridges[ib] = ia;
- }
- }
-
- return make_pair(abridges, bbridges);
+int sgn(double x) {
+ if(x == 0) return 0;
+ return (x<0)?-1:1;
}
-std::vector<Point> bridge_points(ConvexHull a, ConvexHull b) {
- vector<Point> ret;
- pair< map<int, int>, map<int, int> > indices = bridges(a, b);
- for(map<int, int>::iterator it = indices.first.begin(); it != indices.first.end(); it++) {
- ret.push_back(a[it->first]);
- ret.push_back(b[it->second]);
+bool same_side(Point L[2], Point xs[4]) {
+ int side = 0;
+ for(int i = 0; i < 4; i++) {
+ int sn = sgn(SignedTriangleArea(L[0], L[1], xs[i]));
+ if(sn and not side)
+ side = sn;
+ else if(sn != side) return false;
}
- for(map<int, int>::iterator it = indices.second.begin(); it != indices.second.end(); it++) {
- ret.push_back(b[it->first]);
- ret.push_back(a[it->second]);
+ return true;
+}
+
+/** find bridging pairs between two convex hulls.
+ * this code is based on Hormoz Pirzadeh's masters thesis. There is room for optimisation:
+ * 1. reduce recomputation
+ * 2. use more efficient angle code
+ * 3. write as iterator
+ */
+std::vector<pair<int, int> > bridges(ConvexHull a, ConvexHull b) {
+ vector<pair<int, int> > ret;
+
+ // 1. find maximal points on a and b
+ int ai = 0, bi = 0;
+ // 2. find first copodal pair
+ double ap_angle = atan2(a[ai+1] - a[ai]);
+ double bp_angle = atan2(b[bi+1] - b[bi]);
+ Point L[2] = {a[ai], b[bi]};
+ while(ai < int(a.size()) or bi < int(b.size())) {
+ if(ap_angle == bp_angle) {
+ // In the case of parallel support lines, we must consider all four pairs of copodal points
+ {
+ assert(0); // untested
+ Point xs[4] = {a[ai-1], a[ai+1], b[bi-1], b[bi+1]};
+ if(same_side(L, xs)) ret.push_back(make_pair(ai, bi));
+ xs[2] = b[bi];
+ xs[3] = b[bi+2];
+ if(same_side(L, xs)) ret.push_back(make_pair(ai, bi));
+ xs[0] = a[ai];
+ xs[1] = a[ai+2];
+ if(same_side(L, xs)) ret.push_back(make_pair(ai, bi));
+ xs[2] = b[bi-1];
+ xs[3] = b[bi+1];
+ if(same_side(L, xs)) ret.push_back(make_pair(ai, bi));
+ }
+ ai++;
+ ap_angle += angle_between(a[ai] - a[ai-1], a[ai+1] - a[ai]);
+ L[0] = a[ai];
+ bi++;
+ bp_angle += angle_between(b[bi] - b[bi-1], b[bi+1] - b[bi]);
+ L[1] = b[bi];
+ std::cout << "parallel\n";
+ } else if(ap_angle < bp_angle) {
+ ai++;
+ ap_angle += angle_between(a[ai] - a[ai-1], a[ai+1] - a[ai]);
+ L[0] = a[ai];
+ Point xs[4] = {a[ai-1], a[ai+1], b[bi-1], b[bi+1]};
+ if(same_side(L, xs)) ret.push_back(make_pair(ai, bi));
+ } else {
+ bi++;
+ bp_angle += angle_between(b[bi] - b[bi-1], b[bi+1] - b[bi]);
+ L[1] = b[bi];
+ Point xs[4] = {a[ai-1], a[ai+1], b[bi-1], b[bi+1]};
+ if(same_side(L, xs)) ret.push_back(make_pair(ai, bi));
+ }
}
return ret;
}
@@ -428,16 +464,48 @@ ConvexHull intersection(ConvexHull /*a*/, ConvexHull /*b*/) {
return ret;
}
+template <typename T>
+T idx_to_pair(pair<T, T> p, int idx) {
+ return idx?p.second:p.first;
+}
+
/*** ConvexHull merge(ConvexHull a, ConvexHull b);
* find the smallest convex hull that surrounds a and b.
*/
ConvexHull merge(ConvexHull a, ConvexHull b) {
ConvexHull ret;
- pair< map<int, int>, map<int, int> > bpair = bridges(a, b);
- map<int, int> ab = bpair.first;
- map<int, int> bb = bpair.second;
+ std::cout << "---\n";
+ std::vector<pair<int, int> > bpair = bridges(a, b);
+
+ // Given our list of bridges {(pb1, qb1), ..., (pbk, qbk)}
+ // we start with the highest point in p0, q0, say it is p0.
+ // then the merged hull is p0, ..., pb1, qb1, ..., qb2, pb2, ...
+ // In other words, either of the two polygons vertices are added in order until the vertex coincides with a bridge point, at which point we swap.
+
+ unsigned state = (a[0][Y] < b[0][Y])?0:1;
+ ret.boundary.reserve(a.size() + b.size());
+ ConvexHull chs[2] = {a, b};
+ unsigned idx = 0;
+
+ for(unsigned k = 0; k < bpair.size(); k++) {
+ unsigned limit = idx_to_pair(bpair[k], state);
+ std::cout << bpair[k].first << " , " << bpair[k].second << "; "
+ << idx << ", " << limit << ", s: "
+ << state
+ << " \n";
+ while(idx <= limit) {
+ ret.boundary.push_back(chs[state][idx++]);
+ }
+ state = 1-state;
+ idx = idx_to_pair(bpair[k], state);
+ }
+ while(idx < chs[state].size()) {
+ ret.boundary.push_back(chs[state][idx++]);
+ }
+ return ret;
+ /*
ab[-1] = 0;
bb[-1] = 0;
@@ -460,6 +528,7 @@ ConvexHull merge(ConvexHull a, ConvexHull b) {
if(bb[i] == 0 && i != -1) break;
i = bb[i];
}
+ */
return ret;
}
diff --git a/src/2geom/convex-cover.h b/src/2geom/convex-cover.h
index 8a5124019..d5e2dee44 100644
--- a/src/2geom/convex-cover.h
+++ b/src/2geom/convex-cover.h
@@ -67,6 +67,7 @@ public:
bool contains_point(Point p);
bool strict_contains_point(Point p);
+ inline size_t size() const { return boundary.size();}
inline Point operator[](int i) const {
int l = boundary.size();
@@ -133,7 +134,7 @@ public:
// do two convex hulls intersect?
bool intersectp(ConvexHull a, ConvexHull b);
-std::vector<Point> bridge_points(ConvexHull a, ConvexHull b);
+std::vector<std::pair<int, int> > bridges(ConvexHull a, ConvexHull b);
// find the convex hull intersection
ConvexHull intersection(ConvexHull a, ConvexHull b);
diff --git a/src/2geom/coord.h b/src/2geom/coord.h
index 481723413..9c42f6bfc 100644
--- a/src/2geom/coord.h
+++ b/src/2geom/coord.h
@@ -37,8 +37,11 @@
namespace Geom {
+/** @brief Axis enum (X or Y). */
+enum Dim2 { X=0, Y=1 };
+
/**
- * A "real" type with sufficient precision for coordinates.
+ * @brief Floating point type used to store coordinates.
*
* You may safely assume that double (or even float) provides enough precision for storing
* on-canvas points, and hence that double provides enough precision for dot products of
@@ -51,7 +54,11 @@ const Coord EPSILON = 1e-5; //1e-18;
inline Coord infinity() { return std::numeric_limits<Coord>::infinity(); }
//IMPL: NearConcept
-inline bool are_near(Coord a, Coord b, double eps=EPSILON) { return fabs(a-b) <= eps; }
+inline bool are_near(Coord a, Coord b, double eps=EPSILON) { return a-b <= eps && a-b >= -eps; }
+inline bool rel_error_bound(Coord a, Coord b, double eps=EPSILON) { return a <= eps*b && a >= -eps*b; }
+
+
+typedef long IntCoord;
} /* namespace Geom */
diff --git a/src/2geom/curve.h b/src/2geom/curve.h
index 65bf86ef6..77f6808e1 100644
--- a/src/2geom/curve.h
+++ b/src/2geom/curve.h
@@ -1,12 +1,14 @@
/**
* \file
- * \brief Abstract Curve Type
+ * \brief Abstract curve type
*
+ *//*
* Authors:
- * MenTaLguY <mental@rydia.net>
- * Marco Cecchetti <mrcekets at gmail.com>
+ * MenTaLguY <mental@rydia.net>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
*
- * Copyright 2007-2008 authors
+ * Copyright 2007-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
@@ -33,23 +35,16 @@
*/
-
-
#ifndef _2GEOM_CURVE_H_
#define _2GEOM_CURVE_H_
-
+#include <vector>
#include <2geom/coord.h>
#include <2geom/point.h>
#include <2geom/interval.h>
-#include <2geom/nearest-point.h>
#include <2geom/sbasis.h>
#include <2geom/d2.h>
-#include <2geom/matrix.h>
-#include <2geom/exception.h>
-
-#include <vector>
-
+#include <2geom/affine.h>
namespace Geom
{
@@ -61,99 +56,230 @@ protected:
static int root_winding(Curve const &c, Point p);
};
+
+/**
+ * @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 : private CurveHelpers {
public:
- virtual ~Curve() {}
-
- virtual Point initialPoint() const = 0;
- virtual Point finalPoint() const = 0;
-
- /* isDegenerate returns true if the curve has "zero length".
- * For a bezier curve this means for example that all handles are at the same point */
- virtual bool isDegenerate() const = 0;
-
- virtual Curve *duplicate() const = 0;
-
- virtual OptRect boundsFast() const = 0;
- virtual OptRect boundsExact() const = 0;
- virtual OptRect boundsLocal(OptInterval i, unsigned deg) const = 0;
- OptRect boundsLocal(OptInterval i) const { return boundsLocal(i, 0); }
-
- virtual std::vector<double> roots(double v, Dim2 d) const = 0;
-
- virtual int winding(Point p) const { return root_winding(*this, p); }
-
- virtual int degreesOfFreedom() const { return 0;}
-
- //mental: review these
- virtual Curve *portion(double f, double t) const = 0;
- virtual Curve *reverse() const { return portion(1, 0); }
- virtual Curve *derivative() const = 0;
-
- virtual void setInitial(Point v) = 0;
- virtual void setFinal(Point v) = 0;
-
- virtual
- double nearestPoint( Point const& p, double from = 0, double to = 1 ) const
- {
- return nearest_point(p, toSBasis(), from, to);
- }
-
- virtual
- std::vector<double>
- allNearestPoints( Point const& p, double from = 0, double to = 1 ) const
- {
- return all_nearest_points(p, toSBasis(), from, to);
- }
-
- /*
- Path operator*=(Matrix)
- This is not possible, because:
- A Curve can be many things, for example a HLineSegment.
- Such a segment cannot be transformed and stay a HLineSegment in general (take for example rotations).
- This means that these curves become a different type of curve, hence one should use "transformed(Matrix).
- */
-
- virtual Curve *transformed(Matrix const &m) const = 0;
-
- virtual Point pointAt(Coord t) const { return pointAndDerivatives(t, 0).front(); }
- virtual Coord valueAt(Coord t, Dim2 d) const { return pointAt(t)[d]; }
- virtual Point operator() (double t) const { return pointAt(t); }
-
- /* pointAndDerivatives returns a vector that looks like the following:
- * [ point at t, 1st derivative at t, 2nd derivative at t, ... , n'th derivative at t] */
- virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const = 0;
-
- /* unitTangentAt returns the unit vector tangent to the curve at position t
- * (in the direction of increasing t). The method uses l'Hopital's rule when the derivative
- * is (0,0), parameter n determines the maximum nr of iterations (for when higher derivatives are also (0,0) ).
- * Point(0,0) is returned if no non-zero derivative could be found.
- * Note that unitTangentAt(1) will probably not give the desired result. Probably one should do:
- * Curve * c_reverse = c.reverse();
- * Point tangent = - c_reverse->unitTangentAt(0);
- * delete c_reverse;
- */
- virtual Point unitTangentAt(Coord t, unsigned n = 3) const
- {
- std::vector<Point> derivs = pointAndDerivatives(t, n);
- for (unsigned deriv_n = 1; deriv_n < derivs.size(); deriv_n++) {
- Coord length = derivs[deriv_n].length();
- if ( ! are_near(length, 0) ) {
- // length of derivative is non-zero, so return unit vector
- return derivs[deriv_n] / length;
- }
+ 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;
+ /** @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 boundsExact() depending on the curve type.
+ * @return The smallest possible rectangle containing all of the curve's points. */
+ virtual Rect boundsExact() const = 0;
+ // I have no idea what the 'deg' parameter is for, so this is undocumented for now.
+ virtual OptRect boundsLocal(OptInterval const &i, unsigned deg) const = 0;
+ /** @brief Compute the bounding box of a part of the curve.
+ * Since this method returns the smallest possible bounding rectangle of the specified portion,
+ * it can also be rather slow.
+ * @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 Create a curve transformed by an affine transformation.
+ * This method returns a new curve instead modifying the existing one, because some curve
+ * types are not closed under affine transformations. The returned curve may be of different
+ * underlying type (as is the case for horizontal and vertical line segments).
+ * @param m Affine describing the affine transformation
+ * @return Pointer to a new, transformed curve */
+ virtual Curve *transformed(Affine const &m) const = 0;
+ /** @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 nearestPoint( Point const& p, Coord a = 0, Coord b = 1 ) const;
+ /** @brief A version that takes an Interval. */
+ Coord nearestPoint(Point const &p, Interval const &i) const {
+ return nearestPoint(p, i.min(), i.max());
}
- return Point (0,0);
- };
-
- virtual D2<SBasis> toSBasis() const = 0;
- virtual bool operator==(Curve const &c) const { return this == &c;}
+ /** @brief Compute time values at which the curve comes closest to a specified point.
+ * @param p Query point
+ * @param a Minimum time value to consider
+ * @param b Maximum time value to consider; \f$a < b\f$
+ * @return Vector of points closest and equally far away from the query point */
+ virtual std::vector<Coord> allNearestPoints( Point const& p, Coord from = 0,
+ Coord to = 1 ) const;
+ /** @brief A version that takes an Interval. */
+ std::vector<Coord> allNearestPoints(Point const &p, Interval const &i) {
+ return allNearestPoints(p, i.min(), i.max());
+ }
+ /** @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 winding number of the curve at the specified point.
+ * @todo Does this makes sense for curves at all? */
+ virtual int winding(Point const &p) const { return root_winding(*this, p); }
+ /** @brief Compute 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 specfied 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$
+ * @bug This method might currently break for the case of t being exactly 1. A workaround
+ * is to reverse the curve and use the negated unit tangent at 0 like this:
+ * @code
+ Curve *c_reverse = c.reverse();
+ Point tangent = - c_reverse->unitTangentAt(0);
+ delete c_reverse; @endcode */
+ virtual Point unitTangentAt(Coord t, unsigned n = 3) const;
+ /** @brief Convert the curve to a symmetric power basis polynomial.
+ * Symmetric power basis polynomials (S-basis for short) are numerical representations
+ * of curves with excellent numerical properties. Most high level operations provided by 2Geom
+ * 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.
+ * @return True if the curves are identical, false otherwise */
+ virtual bool operator==(Curve const &c) const { return this == &c;}
+ /// @}
};
inline
-Coord nearest_point(Point const& p, Curve const& c)
-{
- return c.nearestPoint(p);
+Coord nearest_point(Point const& p, Curve const& c) {
+ return c.nearestPoint(p);
}
} // end namespace Geom
@@ -161,8 +287,6 @@ Coord nearest_point(Point const& p, Curve const& c)
#endif // _2GEOM_CURVE_H_
-
-
/*
Local Variables:
mode:c++
diff --git a/src/2geom/curves.h b/src/2geom/curves.h
index 6c8435387..64cf3d4fb 100644
--- a/src/2geom/curves.h
+++ b/src/2geom/curves.h
@@ -1,7 +1,7 @@
/**
* \file
- * \brief this file is only a helper header to include all curve types at once
- *
+ * \brief Include all curve types
+ *//*
* Authors:
* MenTaLguY <mental@rydia.net>
* Marco Cecchetti <mrcekets at gmail.com>
@@ -32,9 +32,6 @@
* the specific language governing rights and limitations.
*/
-
-
-
#ifndef _2GEOM_CURVES_H_
#define _2GEOM_CURVES_H_
@@ -43,15 +40,11 @@
#include <2geom/sbasis-curve.h>
#include <2geom/bezier-curve.h>
#include <2geom/hvlinesegment.h>
-//#include <2geom/elliptical-arc.h>
+#include <2geom/elliptical-arc.h>
#include <2geom/svg-elliptical-arc.h>
-
#endif // _2GEOM_CURVES_H_
-
-
-
/*
Local Variables:
mode:c++
diff --git a/src/2geom/d2-sbasis.cpp b/src/2geom/d2-sbasis.cpp
index 4f5a53cd2..697a9d84e 100644
--- a/src/2geom/d2-sbasis.cpp
+++ b/src/2geom/d2-sbasis.cpp
@@ -121,7 +121,7 @@ Piecewise<SBasis> cross(Piecewise<D2<SBasis> > const &a,
return result;
}
-Piecewise<D2<SBasis> > operator*(Piecewise<D2<SBasis> > const &a, Matrix const &m) {
+Piecewise<D2<SBasis> > operator*(Piecewise<D2<SBasis> > const &a, Affine const &m) {
Piecewise<D2<SBasis> > result;
if(a.empty()) return result;
result.push_cut(a.cuts[0]);
@@ -259,6 +259,57 @@ std::vector<Piecewise<D2<SBasis> > > fuse_nearby_ends(std::vector<Piecewise<D2<S
return f;
}
+/*
+ * Computes the intersection of two sets given as (ordered) union of intervals.
+ */
+static std::vector<Interval> intersect( std::vector<Interval> const &a, std::vector<Interval> const &b){
+ std::vector<Interval> result;
+ //TODO: use order!
+ for (unsigned i=0; i < a.size(); i++){
+ for (unsigned j=0; j < b.size(); j++){
+ OptInterval c( a[i] );
+ c &= b[j];
+ if ( c ) {
+ result.push_back( *c );
+ }
+ }
+ }
+ return result;
+}
+
+std::vector<Interval> level_set( D2<SBasis> const &f, Rect region){
+ std::vector<Rect> regions( 1, region );
+ return level_sets( f, regions ).front();
+}
+std::vector<Interval> level_set( D2<SBasis> const &f, Point p, double tol){
+ Rect region(p, p);
+ region.expandBy( tol );
+ return level_set( f, region );
+}
+std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector<Rect> regions){
+ std::vector<Interval> regsX (regions.size(), Interval() );
+ std::vector<Interval> regsY (regions.size(), Interval() );
+ for ( unsigned i=0; i < regions.size(); i++ ){
+ regsX[i] = regions[i][X];
+ regsY[i] = regions[i][Y];
+ }
+ std::vector<std::vector<Interval> > x_in_regs = level_sets( f[X], regsX );
+ std::vector<std::vector<Interval> > y_in_regs = level_sets( f[Y], regsY );
+ std::vector<std::vector<Interval> >result(regions.size(), std::vector<Interval>() );
+ for (unsigned i=0; i<regions.size(); i++){
+ result[i] = intersect ( x_in_regs[i], y_in_regs[i] );
+ }
+ return result;
+}
+std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector<Point> pts, double tol){
+ std::vector<Rect> regions( pts.size(), Rect() );
+ for (unsigned i=0; i<pts.size(); i++){
+ regions[i] = Rect( pts[i], pts[i] );
+ regions[i].expandBy( tol );
+ }
+ return level_sets( f, regions );
+}
+
} // namespace Geom
diff --git a/src/2geom/d2-sbasis.h b/src/2geom/d2-sbasis.h
index 2b087bda6..bd6c35805 100644
--- a/src/2geom/d2-sbasis.h
+++ b/src/2geom/d2-sbasis.h
@@ -45,7 +45,7 @@
#include <2geom/sbasis.h>
#include <2geom/sbasis-2d.h>
#include <2geom/piecewise.h>
-#include <2geom/matrix.h>
+#include <2geom/affine.h>
//TODO: implement intersect
@@ -76,7 +76,7 @@ Piecewise<SBasis> dot(Piecewise<D2<SBasis> > const &a, Piecewise<D2<SBasis> > co
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, Matrix const &m);
+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);
@@ -138,6 +138,11 @@ inline OptRect bounds_local(D2<SBasis> const & s, OptInterval i, unsigned order=
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);
+
}
#endif
diff --git a/src/2geom/d2.h b/src/2geom/d2.h
index bdf042806..3e4de430e 100644
--- a/src/2geom/d2.h
+++ b/src/2geom/d2.h
@@ -34,7 +34,7 @@
#include <2geom/point.h>
#include <2geom/interval.h>
-#include <2geom/matrix.h>
+#include <2geom/affine.h>
#include <boost/concept_check.hpp>
#include <2geom/concepts.h>
@@ -251,7 +251,7 @@ 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, Matrix const &m) {
+D2<T> operator*(D2<T> const &v, Affine const &m) {
boost::function_requires<AddableConcept<T> >();
boost::function_requires<ScalableConcept<T> >();
D2<T> ret;
diff --git a/src/2geom/ellipse.cpp b/src/2geom/ellipse.cpp
index 8030ea517..bea99e5dd 100644
--- a/src/2geom/ellipse.cpp
+++ b/src/2geom/ellipse.cpp
@@ -170,7 +170,7 @@ void Ellipse::set(std::vector<Point> const& points)
}
-SVGEllipticalArc
+EllipticalArc *
Ellipse::arc(Point const& initial, Point const& inner, Point const& final,
bool _svg_compliant)
{
@@ -212,21 +212,27 @@ Ellipse::arc(Point const& initial, Point const& inner, Point const& final,
}
}
- SVGEllipticalArc ea( initial, ray(X), ray(Y), rot_angle(),
- large_arc_flag, sweep_flag, final, _svg_compliant);
- return ea;
+ EllipticalArc *ret_arc;
+ if (_svg_compliant) {
+ ret_arc = new SVGEllipticalArc(initial, ray(X), ray(Y), rot_angle(),
+ large_arc_flag, sweep_flag, final);
+ } else {
+ ret_arc = new EllipticalArc(initial, ray(X), ray(Y), rot_angle(),
+ large_arc_flag, sweep_flag, final);
+ }
+ return ret_arc;
}
-Ellipse Ellipse::transformed(Matrix const& m) const
+Ellipse Ellipse::transformed(Affine const& m) const
{
double cosrot = std::cos(rot_angle());
double sinrot = std::sin(rot_angle());
- Matrix A( ray(X) * cosrot, ray(X) * sinrot,
+ Affine A( ray(X) * cosrot, ray(X) * sinrot,
-ray(Y) * sinrot, ray(Y) * cosrot,
0, 0 );
Point new_center = center() * m;
- Matrix M = m.without_translation();
- Matrix AM = A * M;
+ Affine M = m.withoutTranslation();
+ Affine AM = A * M;
if ( are_near(AM.det(), 0) )
{
double angle;
@@ -250,11 +256,11 @@ Ellipse Ellipse::transformed(Matrix const& m) const
}
std::vector<double> coeff = implicit_form_coefficients();
- Matrix Q( coeff[0], coeff[1]/2,
+ Affine Q( coeff[0], coeff[1]/2,
coeff[1]/2, coeff[2],
0, 0 );
- Matrix invm = M.inverse();
+ Affine invm = M.inverse();
Q = invm * Q ;
std::swap( invm[1], invm[2] );
Q *= invm;
diff --git a/src/2geom/ellipse.h b/src/2geom/ellipse.h
index 8e44f3395..297254366 100644
--- a/src/2geom/ellipse.h
+++ b/src/2geom/ellipse.h
@@ -35,15 +35,15 @@
#ifndef _2GEOM_ELLIPSE_H_
#define _2GEOM_ELLIPSE_H_
-
+#include <vector>
#include <2geom/point.h>
#include <2geom/exception.h>
-#include <2geom/matrix.h>
+#include <2geom/affine.h>
namespace Geom
{
-class SVGEllipticalArc;
+class EllipticalArc;
class Circle;
class Ellipse
@@ -88,9 +88,8 @@ class Ellipse
// prerequisite: at least 5 points must be passed
void set(std::vector<Point> const& points);
- SVGEllipticalArc
- arc(Point const& initial, Point const& inner, Point const& final,
- bool _svg_compliant = true);
+ EllipticalArc *
+ arc(Point const& initial, Point const& inner, Point const& final, bool svg_compliant = true);
Point center() const
{
@@ -114,7 +113,7 @@ class Ellipse
std::vector<double> implicit_form_coefficients() const;
- Ellipse transformed(Matrix const& m) const;
+ Ellipse transformed(Affine const& m) const;
private:
Point m_centre, m_ray;
diff --git a/src/2geom/elliptical-arc.cpp b/src/2geom/elliptical-arc.cpp
index fd0e7cf9b..acaad82cf 100644
--- a/src/2geom/elliptical-arc.cpp
+++ b/src/2geom/elliptical-arc.cpp
@@ -1,7 +1,10 @@
/*
* SVG Elliptical Arc Class
*
- * Copyright 2008 Marco Cecchetti <mrcekets at gmail.com>
+ * Authors:
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@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
@@ -27,896 +30,881 @@
* the specific language governing rights and limitations.
*/
+#include <cfloat>
+#include <limits>
+#include <memory>
#include <2geom/elliptical-arc.h>
+#include <2geom/ellipse.h>
+#include <2geom/sbasis-geometric.h>
#include <2geom/bezier-curve.h>
#include <2geom/poly.h>
-#include <2geom/sbasis-math.h>
-
-#include <cfloat>
-#include <limits>
-
+#include <2geom/transforms.h>
+#include <2geom/utils.h>
+#include <2geom/numeric/vector.h>
+#include <2geom/numeric/fitting-tool.h>
+#include <2geom/numeric/fitting-model.h>
namespace Geom
{
+/**
+ * @class EllipticalArc
+ * @brief Elliptical arc curve
+ *
+ * Elliptical arc is a curve taking the shape of a section of an ellipse.
+ *
+ * The arc function has two forms: the regular one, mapping the unit interval to points
+ * on 2D plane (the linear domain), and a second form that maps some interval
+ * \f$A \subseteq [0,2\pi)\f$ to the same points (the angular domain). The interval \f$A\f$
+ * determines which part of the ellipse forms the arc. The arc is said to contain an angle
+ * if its angular domain includes that angle (and therefore it is defined for that angle).
+ *
+ * The angular domain considers each ellipse to be
+ * a rotated, scaled and translated unit circle: 0 corresponds to \f$(1,0)\f$ on the unit circle,
+ * \f$\pi/2\f$ corresponds to \f$(0,1)\f$, \f$\pi\f$ to \f$(-1,0)\f$ and \f$3\pi/2\f$
+ * to \f$(0,-1)\f$. After the angle is mapped to a point from a unit circle, the point is
+ * transformed using a matrix of this form
+ * \f[ M = \left[ \begin{array}{ccc}
+ r_X \cos(\theta) & -r_Y \sin(\theta) & 0 \\
+ r_X \sin(\theta) & r_Y \cos(\theta) & 0 \\
+ c_X & c_Y & 1 \end{array} \right] \f]
+ * where \f$r_X, r_Y\f$ are the X and Y rays of the ellipse, \f$\theta\f$ is its angle of rotation,
+ * and \f$c_X, c_Y\f$ the coordinates of the ellipse's center - thus mapping the angle
+ * to some point on the ellipse. Note that for example the point at angluar coordinate 0,
+ * the center and the point at angular coordinate \f$\pi/4\f$ do not necessarily
+ * create an angle of \f$\pi/4\f$ radians; it is only the case if both axes of the ellipse
+ * are of the same length (i.e. it is a circle).
+ *
+ * @image html ellipse-angular-coordinates.png "An illustration of the angular domain"
+ *
+ * Each arc is defined by five variables: The initial and final point, the ellipse's rays,
+ * and the ellipse's rotation. Each set of those parameters corresponds to four different arcs,
+ * with two of them larger than half an ellipse and two of them turning clockwise while traveling
+ * from initial to final point. The two flags disambiguate between them: "large arc flag" selects
+ * the bigger arc, while the "sweep flag" selects the clockwise arc.
+ *
+ * @image html elliptical-arc-flags.png "The four possible arcs and the meaning of flags"
+ *
+ * @ingroup Curves
+ */
-OptRect EllipticalArc::boundsExact() const
+Rect EllipticalArc::boundsExact() const
{
- std::vector<double> extremes(4);
- double cosrot = std::cos(rotation_angle());
- double sinrot = std::sin(rotation_angle());
- extremes[0] = std::atan2( -ray(Y) * sinrot, ray(X) * cosrot );
- extremes[1] = extremes[0] + M_PI;
- if ( extremes[0] < 0 ) extremes[0] += 2*M_PI;
- extremes[2] = std::atan2( ray(Y) * cosrot, ray(X) * sinrot );
- extremes[3] = extremes[2] + M_PI;
- if ( extremes[2] < 0 ) extremes[2] += 2*M_PI;
-
-
- std::vector<double>arc_extremes(4);
- arc_extremes[0] = initialPoint()[X];
- arc_extremes[1] = finalPoint()[X];
- if ( arc_extremes[0] < arc_extremes[1] )
- std::swap(arc_extremes[0], arc_extremes[1]);
- arc_extremes[2] = initialPoint()[Y];
- arc_extremes[3] = finalPoint()[Y];
- if ( arc_extremes[2] < arc_extremes[3] )
- std::swap(arc_extremes[2], arc_extremes[3]);
-
-
- if ( start_angle() < end_angle() )
- {
- if ( sweep_flag() )
- {
- for ( unsigned int i = 0; i < extremes.size(); ++i )
- {
- if ( start_angle() < extremes[i] && extremes[i] < end_angle() )
- {
- arc_extremes[i] = pointAtAngle(extremes[i])[i >> 1];
- }
- }
- }
- else
- {
- for ( unsigned int i = 0; i < extremes.size(); ++i )
- {
- if ( start_angle() > extremes[i] || extremes[i] > end_angle() )
- {
- arc_extremes[i] = pointAtAngle(extremes[i])[i >> 1];
- }
- }
- }
- }
- else
- {
- if ( sweep_flag() )
- {
- for ( unsigned int i = 0; i < extremes.size(); ++i )
- {
- if ( start_angle() < extremes[i] || extremes[i] < end_angle() )
- {
- arc_extremes[i] = pointAtAngle(extremes[i])[i >> 1];
- }
- }
- }
- else
- {
- for ( unsigned int i = 0; i < extremes.size(); ++i )
- {
- if ( start_angle() > extremes[i] && extremes[i] > end_angle() )
- {
- arc_extremes[i] = pointAtAngle(extremes[i])[i >> 1];
- }
- }
- }
- }
-
- return Rect( Point(arc_extremes[1], arc_extremes[3]) ,
- Point(arc_extremes[0], arc_extremes[2]) );
+ double extremes[4];
+ double sinrot, cosrot;
+ sincos(_rot_angle, sinrot, cosrot);
+
+ extremes[0] = std::atan2( -ray(Y) * sinrot, ray(X) * cosrot );
+ extremes[1] = extremes[0] + M_PI;
+ if ( extremes[0] < 0 ) extremes[0] += 2*M_PI;
+ extremes[2] = std::atan2( ray(Y) * cosrot, ray(X) * sinrot );
+ extremes[3] = extremes[2] + M_PI;
+ if ( extremes[2] < 0 ) extremes[2] += 2*M_PI;
+
+
+ double arc_extremes[4];
+ arc_extremes[0] = initialPoint()[X];
+ arc_extremes[1] = finalPoint()[X];
+ if ( arc_extremes[0] < arc_extremes[1] )
+ std::swap(arc_extremes[0], arc_extremes[1]);
+ arc_extremes[2] = initialPoint()[Y];
+ arc_extremes[3] = finalPoint()[Y];
+ if ( arc_extremes[2] < arc_extremes[3] )
+ std::swap(arc_extremes[2], arc_extremes[3]);
+
+ for (unsigned i = 0; i < 4; ++i) {
+ if (containsAngle(extremes[i])) {
+ arc_extremes[i] = valueAtAngle(extremes[i], i >> 1 ? Y : X);
+ }
+ }
+ return Rect( Point(arc_extremes[1], arc_extremes[3]) ,
+ Point(arc_extremes[0], arc_extremes[2]) );
}
-std::vector<double>
-EllipticalArc::roots(double v, Dim2 d) const
+Point EllipticalArc::pointAtAngle(Coord t) const
{
- if ( d > Y )
- {
- THROW_RANGEERROR("dimention out of range");
- }
-
- std::vector<double> sol;
- if ( are_near(ray(X), 0) && are_near(ray(Y), 0) )
- {
- if ( center(d) == v )
- sol.push_back(0);
- return sol;
- }
-
- const char* msg[2][2] =
- {
- { "d == X; ray(X) == 0; "
- "s = (v - center(X)) / ( -ray(Y) * std::sin(rotation_angle()) ); "
- "s should be contained in [-1,1]",
- "d == X; ray(Y) == 0; "
- "s = (v - center(X)) / ( ray(X) * std::cos(rotation_angle()) ); "
- "s should be contained in [-1,1]"
- },
- { "d == Y; ray(X) == 0; "
- "s = (v - center(X)) / ( ray(Y) * std::cos(rotation_angle()) ); "
- "s should be contained in [-1,1]",
- "d == Y; ray(Y) == 0; "
- "s = (v - center(X)) / ( ray(X) * std::sin(rotation_angle()) ); "
- "s should be contained in [-1,1]"
- },
- };
-
- for ( unsigned int dim = 0; dim < 2; ++dim )
- {
- if ( are_near(ray(dim), 0) )
- {
-
- if ( initialPoint()[d] == v && finalPoint()[d] == v )
- {
- THROW_INFINITESOLUTIONS(0);
- }
- if ( (initialPoint()[d] < finalPoint()[d])
- && (initialPoint()[d] > v || finalPoint()[d] < v) )
- {
- return sol;
- }
- if ( (initialPoint()[d] > finalPoint()[d])
- && (finalPoint()[d] > v || initialPoint()[d] < v) )
- {
- return sol;
- }
- double ray_prj;
- switch(d)
- {
- case X:
- switch(dim)
- {
- case X: ray_prj = -ray(Y) * std::sin(rotation_angle());
- break;
- case Y: ray_prj = ray(X) * std::cos(rotation_angle());
- break;
- }
- break;
- case Y:
- switch(dim)
- {
- case X: ray_prj = ray(Y) * std::cos(rotation_angle());
- break;
- case Y: ray_prj = ray(X) * std::sin(rotation_angle());
- break;
- }
- break;
- }
-
- double s = (v - center(d)) / ray_prj;
- if ( s < -1 || s > 1 )
- {
- THROW_LOGICALERROR(msg[d][dim]);
- }
- switch(dim)
- {
- case X:
- s = std::asin(s); // return a value in [-PI/2,PI/2]
- if ( logical_xor( sweep_flag(), are_near(start_angle(), M_PI/2) ) )
- {
- if ( s < 0 ) s += 2*M_PI;
- }
- else
- {
- s = M_PI - s;
- if (!(s < 2*M_PI) ) s -= 2*M_PI;
- }
- break;
- case Y:
- s = std::acos(s); // return a value in [0,PI]
- if ( logical_xor( sweep_flag(), are_near(start_angle(), 0) ) )
- {
- s = 2*M_PI - s;
- if ( !(s < 2*M_PI) ) s -= 2*M_PI;
- }
- break;
- }
-
- //std::cerr << "s = " << rad_to_deg(s);
- s = map_to_01(s);
- //std::cerr << " -> t: " << s << std::endl;
- if ( !(s < 0 || s > 1) )
- sol.push_back(s);
- return sol;
- }
- }
-
- double rotx, roty;
- switch(d)
- {
- case X:
- rotx = std::cos(rotation_angle());
- roty = -std::sin(rotation_angle());
- break;
- case Y:
- rotx = std::sin(rotation_angle());
- roty = std::cos(rotation_angle());
- break;
- }
- double rxrotx = ray(X) * rotx;
- double c_v = center(d) - v;
-
- double a = -rxrotx + c_v;
- double b = ray(Y) * roty;
- double c = rxrotx + c_v;
- //std::cerr << "a = " << a << std::endl;
- //std::cerr << "b = " << b << std::endl;
- //std::cerr << "c = " << c << std::endl;
-
- if ( are_near(a,0) )
- {
- sol.push_back(M_PI);
- if ( !are_near(b,0) )
- {
- double s = 2 * std::atan(-c/(2*b));
- if ( s < 0 ) s += 2*M_PI;
- sol.push_back(s);
- }
- }
- else
- {
- double delta = b * b - a * c;
- //std::cerr << "delta = " << delta << std::endl;
- if ( are_near(delta, 0) )
- {
- double s = 2 * std::atan(-b/a);
- if ( s < 0 ) s += 2*M_PI;
- sol.push_back(s);
- }
- else if ( delta > 0 )
- {
- double sq = std::sqrt(delta);
- double s = 2 * std::atan( (-b - sq) / a );
- if ( s < 0 ) s += 2*M_PI;
- sol.push_back(s);
- s = 2 * std::atan( (-b + sq) / a );
- if ( s < 0 ) s += 2*M_PI;
- sol.push_back(s);
- }
- }
-
- std::vector<double> arc_sol;
- for (unsigned int i = 0; i < sol.size(); ++i )
- {
- //std::cerr << "s = " << rad_to_deg(sol[i]);
- sol[i] = map_to_01(sol[i]);
- //std::cerr << " -> t: " << sol[i] << std::endl;
- if ( !(sol[i] < 0 || sol[i] > 1) )
- arc_sol.push_back(sol[i]);
- }
- return arc_sol;
-
-
-// return SBasisCurve(toSBasis()).roots(v, d);
+ Point ret = Point::polar(t) * unitCircleTransform();
+ return ret;
}
-// D(E(t,C),t) = E(t+PI/2,O)
-Curve* EllipticalArc::derivative() const
+Coord EllipticalArc::valueAtAngle(Coord t, Dim2 d) const
{
- EllipticalArc* result = new EllipticalArc(*this);
- result->m_center[X] = result->m_center[Y] = 0;
- result->m_start_angle += M_PI/2;
- if( !( result->m_start_angle < 2*M_PI ) )
- {
- result->m_start_angle -= 2*M_PI;
- }
- result->m_end_angle += M_PI/2;
- if( !( result->m_end_angle < 2*M_PI ) )
- {
- result->m_end_angle -= 2*M_PI;
- }
- result->m_initial_point = result->pointAtAngle( result->start_angle() );
- result->m_final_point = result->pointAtAngle( result->end_angle() );
- return result;
+ Coord sinrot, cosrot, cost, sint;
+ sincos(_rot_angle, sinrot, cosrot);
+ sincos(t, sint, cost);
+ if ( d == X ) {
+ return ray(X) * cosrot * cost
+ - ray(Y) * sinrot * sint
+ + center(X);
+ } else {
+ return ray(X) * sinrot * cost
+ + ray(Y) * cosrot * sint
+ + center(Y);
+ }
}
-std::vector<Point>
-EllipticalArc::pointAndDerivatives(Coord t, unsigned int n) const
+Affine EllipticalArc::unitCircleTransform() const
{
- unsigned int nn = n+1; // nn represents the size of the result vector.
- std::vector<Point> result;
- result.reserve(nn);
- double angle = map_unit_interval_on_circular_arc(t, start_angle(),
- end_angle(), sweep_flag());
- EllipticalArc ea(*this);
- ea.m_center = Point(0,0);
- unsigned int m = std::min(nn, 4u);
- for ( unsigned int i = 0; i < m; ++i )
- {
- result.push_back( ea.pointAtAngle(angle) );
- angle += M_PI/2;
- if ( !(angle < 2*M_PI) ) angle -= 2*M_PI;
- }
- m = nn / 4;
- for ( unsigned int i = 1; i < m; ++i )
- {
- for ( unsigned int j = 0; j < 4; ++j )
- result.push_back( result[j] );
- }
- m = nn - 4 * m;
- for ( unsigned int i = 0; i < m; ++i )
- {
- result.push_back( result[i] );
- }
- if ( !result.empty() ) // nn != 0
- result[0] = pointAtAngle(angle);
- return result;
+ Affine ret = Scale(ray(X), ray(Y)) * Rotate(_rot_angle);
+ ret.setTranslation(center());
+ return ret;
}
-D2<SBasis> EllipticalArc::toSBasis() const
+std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const
{
- // the interval of parametrization has to be [0,1]
- Coord et = start_angle() + ( sweep_flag() ? sweep_angle() : -sweep_angle() );
- Linear param(start_angle(), et);
- Coord cos_rot_angle = std::cos(rotation_angle());
- Coord sin_rot_angle = std::sin(rotation_angle());
- // order = 4 seems to be enough to get a perfect looking elliptical arc
- // should it be choosen in function of the arc length anyway ?
- // or maybe a user settable parameter: toSBasis(unsigned int order) ?
- SBasis arc_x = ray(X) * cos(param,4);
- SBasis arc_y = ray(Y) * sin(param,4);
- D2<SBasis> arc;
- arc[0] = arc_x * cos_rot_angle - arc_y * sin_rot_angle + Linear(center(X),center(X));
- arc[1] = arc_x * sin_rot_angle + arc_y * cos_rot_angle + Linear(center(Y),center(Y));
- return arc;
+ std::vector<Coord> sol;
+
+ if ( are_near(ray(X), 0) && are_near(ray(Y), 0) ) {
+ if ( center(d) == v )
+ sol.push_back(0);
+ return sol;
+ }
+
+ static const char* msg[2][2] =
+ {
+ { "d == X; ray(X) == 0; "
+ "s = (v - center(X)) / ( -ray(Y) * std::sin(_rot_angle) ); "
+ "s should be contained in [-1,1]",
+ "d == X; ray(Y) == 0; "
+ "s = (v - center(X)) / ( ray(X) * std::cos(_rot_angle) ); "
+ "s should be contained in [-1,1]"
+ },
+ { "d == Y; ray(X) == 0; "
+ "s = (v - center(X)) / ( ray(Y) * std::cos(_rot_angle) ); "
+ "s should be contained in [-1,1]",
+ "d == Y; ray(Y) == 0; "
+ "s = (v - center(X)) / ( ray(X) * std::sin(_rot_angle) ); "
+ "s should be contained in [-1,1]"
+ },
+ };
+
+ for ( unsigned int dim = 0; dim < 2; ++dim )
+ {
+ if ( are_near(ray((Dim2) dim), 0) )
+ {
+ if ( initialPoint()[d] == v && finalPoint()[d] == v )
+ {
+ THROW_INFINITESOLUTIONS(0);
+ }
+ if ( (initialPoint()[d] < finalPoint()[d])
+ && (initialPoint()[d] > v || finalPoint()[d] < v) )
+ {
+ return sol;
+ }
+ if ( (initialPoint()[d] > finalPoint()[d])
+ && (finalPoint()[d] > v || initialPoint()[d] < v) )
+ {
+ return sol;
+ }
+ double ray_prj;
+ switch(d)
+ {
+ case X:
+ switch(dim)
+ {
+ case X: ray_prj = -ray(Y) * std::sin(_rot_angle);
+ break;
+ case Y: ray_prj = ray(X) * std::cos(_rot_angle);
+ break;
+ }
+ break;
+ case Y:
+ switch(dim)
+ {
+ case X: ray_prj = ray(Y) * std::cos(_rot_angle);
+ break;
+ case Y: ray_prj = ray(X) * std::sin(_rot_angle);
+ break;
+ }
+ break;
+ }
+
+ double s = (v - center(d)) / ray_prj;
+ if ( s < -1 || s > 1 )
+ {
+ THROW_LOGICALERROR(msg[d][dim]);
+ }
+ switch(dim)
+ {
+ case X:
+ s = std::asin(s); // return a value in [-PI/2,PI/2]
+ if ( logical_xor( _sweep, are_near(initialAngle(), M_PI/2) ) )
+ {
+ if ( s < 0 ) s += 2*M_PI;
+ }
+ else
+ {
+ s = M_PI - s;
+ if (!(s < 2*M_PI) ) s -= 2*M_PI;
+ }
+ break;
+ case Y:
+ s = std::acos(s); // return a value in [0,PI]
+ if ( logical_xor( _sweep, are_near(initialAngle(), 0) ) )
+ {
+ s = 2*M_PI - s;
+ if ( !(s < 2*M_PI) ) s -= 2*M_PI;
+ }
+ break;
+ }
+
+ //std::cerr << "s = " << rad_to_deg(s);
+ s = map_to_01(s);
+ //std::cerr << " -> t: " << s << std::endl;
+ if ( !(s < 0 || s > 1) )
+ sol.push_back(s);
+ return sol;
+ }
+ }
+
+ double rotx, roty;
+ sincos(_rot_angle, roty, rotx);
+ if (d == X) roty = -roty;
+
+ double rxrotx = ray(X) * rotx;
+ double c_v = center(d) - v;
+
+ double a = -rxrotx + c_v;
+ double b = ray(Y) * roty;
+ double c = rxrotx + c_v;
+ //std::cerr << "a = " << a << std::endl;
+ //std::cerr << "b = " << b << std::endl;
+ //std::cerr << "c = " << c << std::endl;
+
+ if ( are_near(a,0) )
+ {
+ sol.push_back(M_PI);
+ if ( !are_near(b,0) )
+ {
+ double s = 2 * std::atan(-c/(2*b));
+ if ( s < 0 ) s += 2*M_PI;
+ sol.push_back(s);
+ }
+ }
+ else
+ {
+ double delta = b * b - a * c;
+ //std::cerr << "delta = " << delta << std::endl;
+ if ( are_near(delta, 0) )
+ {
+ double s = 2 * std::atan(-b/a);
+ if ( s < 0 ) s += 2*M_PI;
+ sol.push_back(s);
+ }
+ else if ( delta > 0 )
+ {
+ double sq = std::sqrt(delta);
+ double s = 2 * std::atan( (-b - sq) / a );
+ if ( s < 0 ) s += 2*M_PI;
+ sol.push_back(s);
+ s = 2 * std::atan( (-b + sq) / a );
+ if ( s < 0 ) s += 2*M_PI;
+ sol.push_back(s);
+ }
+ }
+
+ std::vector<double> arc_sol;
+ for (unsigned int i = 0; i < sol.size(); ++i )
+ {
+ //std::cerr << "s = " << rad_to_deg(sol[i]);
+ sol[i] = map_to_01(sol[i]);
+ //std::cerr << " -> t: " << sol[i] << std::endl;
+ if ( !(sol[i] < 0 || sol[i] > 1) )
+ arc_sol.push_back(sol[i]);
+ }
+ return arc_sol;
}
-bool EllipticalArc::containsAngle(Coord angle) const
+// D(E(t,C),t) = E(t+PI/2,O), where C is the ellipse center
+// the derivative doesn't rotate the ellipse but there is a translation
+// of the parameter t by an angle of PI/2 so the ellipse points are shifted
+// of such an angle in the cw direction
+Curve *EllipticalArc::derivative() const
{
- if ( sweep_flag() )
- if ( start_angle() < end_angle() )
- return ( !( angle < start_angle() || angle > end_angle() ) );
- else
- return ( !( angle < start_angle() && angle > end_angle() ) );
- else
- if ( start_angle() > end_angle() )
- return ( !( angle > start_angle() || angle < end_angle() ) );
- else
- return ( !( angle > start_angle() && angle < end_angle() ) );
+ EllipticalArc *result = static_cast<EllipticalArc*>(duplicate());
+ result->_center[X] = result->_center[Y] = 0;
+ result->_start_angle += M_PI/2;
+ if( !( result->_start_angle < 2*M_PI ) )
+ {
+ result->_start_angle -= 2*M_PI;
+ }
+ result->_end_angle += M_PI/2;
+ if( !( result->_end_angle < 2*M_PI ) )
+ {
+ result->_end_angle -= 2*M_PI;
+ }
+ result->_initial_point = result->pointAtAngle( result->initialAngle() );
+ result->_final_point = result->pointAtAngle( result->finalAngle() );
+ return result;
}
-double EllipticalArc::valueAtAngle(Coord t, Dim2 d) const
+std::vector<Point>
+EllipticalArc::pointAndDerivatives(Coord t, unsigned int n) const
{
- double sin_rot_angle = std::sin(rotation_angle());
- double cos_rot_angle = std::cos(rotation_angle());
- if ( d == X )
+ unsigned int nn = n+1; // nn represents the size of the result vector.
+ std::vector<Point> result;
+ result.reserve(nn);
+ double angle = map_unit_interval_on_circular_arc(t, initialAngle(),
+ finalAngle(), _sweep);
+ std::auto_ptr<EllipticalArc> ea( static_cast<EllipticalArc*>(duplicate()) );
+ ea->_center = Point(0,0);
+ unsigned int m = std::min(nn, 4u);
+ for ( unsigned int i = 0; i < m; ++i )
{
- return ray(X) * cos_rot_angle * std::cos(t)
- - ray(Y) * sin_rot_angle * std::sin(t)
- + center(X);
+ result.push_back( ea->pointAtAngle(angle) );
+ angle += M_PI/2;
+ if ( !(angle < 2*M_PI) ) angle -= 2*M_PI;
}
- else if ( d == Y )
+ m = nn / 4;
+ for ( unsigned int i = 1; i < m; ++i )
{
- return ray(X) * sin_rot_angle * std::cos(t)
- + ray(Y) * cos_rot_angle * std::sin(t)
- + center(Y);
+ for ( unsigned int j = 0; j < 4; ++j )
+ result.push_back( result[j] );
+ }
+ m = nn - 4 * m;
+ for ( unsigned int i = 0; i < m; ++i )
+ {
+ result.push_back( result[i] );
}
- THROW_RANGEERROR("dimension parameter out of range");
+ if ( !result.empty() ) // nn != 0
+ result[0] = pointAtAngle(angle);
+ return result;
}
+bool EllipticalArc::containsAngle(Coord angle) const
+{
+ return AngleInterval::contains(angle);
+}
Curve* EllipticalArc::portion(double f, double t) const
{
- if (f < 0) f = 0;
- if (f > 1) f = 1;
- if (t < 0) t = 0;
- if (t > 1) t = 1;
- if ( are_near(f, t) )
- {
- EllipticalArc* arc = new EllipticalArc();
- arc->m_center = arc->m_initial_point = arc->m_final_point = pointAt(f);
- arc->m_start_angle = arc->m_end_angle = m_start_angle;
- arc->m_rot_angle = m_rot_angle;
- arc->m_sweep = m_sweep;
- arc->m_large_arc = m_large_arc;
- }
- EllipticalArc* arc = new EllipticalArc( *this );
- arc->m_initial_point = pointAt(f);
- arc->m_final_point = pointAt(t);
- double sa = sweep_flag() ? sweep_angle() : -sweep_angle();
- arc->m_start_angle = m_start_angle + sa * f;
- if ( !(arc->m_start_angle < 2*M_PI) )
- arc->m_start_angle -= 2*M_PI;
- if ( arc->m_start_angle < 0 )
- arc->m_start_angle += 2*M_PI;
- arc->m_end_angle = m_start_angle + sa * t;
- if ( !(arc->m_end_angle < 2*M_PI) )
- arc->m_end_angle -= 2*M_PI;
- if ( arc->m_end_angle < 0 )
- arc->m_end_angle += 2*M_PI;
- if ( f > t ) arc->m_sweep = !sweep_flag();
- if ( large_arc_flag() && (arc->sweep_angle() < M_PI) )
- arc->m_large_arc = false;
+ // fix input arguments
+ if (f < 0) f = 0;
+ if (f > 1) f = 1;
+ if (t < 0) t = 0;
+ if (t > 1) t = 1;
+
+ if ( are_near(f, t) )
+ {
+ EllipticalArc *arc = static_cast<EllipticalArc*>(duplicate());
+ arc->_center = arc->_initial_point = arc->_final_point = pointAt(f);
+ arc->_start_angle = arc->_end_angle = _start_angle;
+ arc->_rot_angle = _rot_angle;
+ arc->_sweep = _sweep;
+ arc->_large_arc = _large_arc;
+ return arc;
+ }
+
+ EllipticalArc *arc = static_cast<EllipticalArc*>(duplicate());
+ arc->_initial_point = pointAt(f);
+ arc->_final_point = pointAt(t);
+ if ( f > t ) arc->_sweep = !_sweep;
+ if ( _large_arc && fabs(sweepAngle() * (t-f)) < M_PI)
+ arc->_large_arc = false;
+ arc->_updateCenterAndAngles(arc->isSVGCompliant()); //TODO: be more clever
return arc;
}
-// NOTE: doesn't work with 360 deg arcs
-void EllipticalArc::calculate_center_and_extreme_angles()
+// the arc is the same but traversed in the opposite direction
+Curve *EllipticalArc::reverse() const {
+ EllipticalArc *rarc = static_cast<EllipticalArc*>(duplicate());
+ rarc->_sweep = !_sweep;
+ rarc->_initial_point = _final_point;
+ rarc->_final_point = _initial_point;
+ rarc->_start_angle = _end_angle;
+ rarc->_end_angle = _start_angle;
+ rarc->_updateCenterAndAngles(rarc->isSVGCompliant());
+ return rarc;
+}
+
+#ifdef HAVE_GSL // GSL is required for function "solve_reals"
+std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from, double to ) const
{
- // as stated in the svg standard the rotation angle parameter
- // value must be modulo 2*PI
- m_rot_angle = std::fmod(m_rot_angle, 2*M_PI);
- if (m_rot_angle < 0) m_rot_angle += 2*M_PI;
+ std::vector<double> result;
+
+ if ( from > to ) std::swap(from, to);
+ if ( from < 0 || to > 1 )
+ {
+ THROW_RANGEERROR("[from,to] interval out of range");
+ }
- if ( are_near(initialPoint(), finalPoint()) )
+ if ( ( are_near(ray(X), 0) && are_near(ray(Y), 0) ) || are_near(from, to) )
{
- if ( are_near(ray(X), 0) && are_near(ray(Y), 0) )
- {
- m_start_angle = m_end_angle = 0;
- m_center = initialPoint();
- return;
- }
- else
- {
- THROW_RANGEERROR("initial and final point are the same");
- }
- }
- if ( are_near(ray(X), 0) && are_near(ray(Y), 0) )
- { // but initialPoint != finalPoint
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints: "
- "ray(X) == 0 && ray(Y) == 0 but initialPoint != finalPoint"
- );
- }
- if ( are_near(ray(Y), 0) )
- {
- Point v = initialPoint() - finalPoint();
- if ( are_near(L2sq(v), 4*ray(X)*ray(X)) )
- {
- double angle = std::atan2(v[Y], v[X]);
- if (angle < 0) angle += 2*M_PI;
- if ( are_near( angle, rotation_angle() ) )
- {
- m_start_angle = 0;
- m_end_angle = M_PI;
- m_center = v/2 + finalPoint();
- return;
- }
- angle -= M_PI;
- if ( angle < 0 ) angle += 2*M_PI;
- if ( are_near( angle, rotation_angle() ) )
- {
- m_start_angle = M_PI;
- m_end_angle = 0;
- m_center = v/2 + finalPoint();
- return;
- }
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints: "
- "ray(Y) == 0 "
- "and slope(initialPoint - finalPoint) != rotation_angle "
- "and != rotation_angle + PI"
- );
- }
- if ( L2sq(v) > 4*ray(X)*ray(X) )
- {
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints: "
- "ray(Y) == 0 and distance(initialPoint, finalPoint) > 2*ray(X)"
- );
- }
- else
- {
- THROW_RANGEERROR(
- "there is infinite ellipses that satisfy the given constraints: "
- "ray(Y) == 0 and distance(initialPoint, finalPoint) < 2*ray(X)"
- );
- }
-
- }
-
- if ( are_near(ray(X), 0) )
- {
- Point v = initialPoint() - finalPoint();
- if ( are_near(L2sq(v), 4*ray(Y)*ray(Y)) )
- {
- double angle = std::atan2(v[Y], v[X]);
- if (angle < 0) angle += 2*M_PI;
- double rot_angle = rotation_angle() + M_PI/2;
- if ( !(rot_angle < 2*M_PI) ) rot_angle -= 2*M_PI;
- if ( are_near( angle, rot_angle ) )
- {
- m_start_angle = M_PI/2;
- m_end_angle = 3*M_PI/2;
- m_center = v/2 + finalPoint();
- return;
- }
- angle -= M_PI;
- if ( angle < 0 ) angle += 2*M_PI;
- if ( are_near( angle, rot_angle ) )
- {
- m_start_angle = 3*M_PI/2;
- m_end_angle = M_PI/2;
- m_center = v/2 + finalPoint();
- return;
- }
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints: "
- "ray(X) == 0 "
- "and slope(initialPoint - finalPoint) != rotation_angle + PI/2 "
- "and != rotation_angle + (3/2)*PI"
- );
- }
- if ( L2sq(v) > 4*ray(Y)*ray(Y) )
- {
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints: "
- "ray(X) == 0 and distance(initialPoint, finalPoint) > 2*ray(Y)"
- );
- }
- else
- {
- THROW_RANGEERROR(
- "there is infinite ellipses that satisfy the given constraints: "
- "ray(X) == 0 and distance(initialPoint, finalPoint) < 2*ray(Y)"
- );
- }
-
- }
-
- double sin_rot_angle = std::sin(rotation_angle());
- double cos_rot_angle = std::cos(rotation_angle());
-
- Point sp = sweep_flag() ? initialPoint() : finalPoint();
- Point ep = sweep_flag() ? finalPoint() : initialPoint();
-
- Matrix m( ray(X) * cos_rot_angle, ray(X) * sin_rot_angle,
- -ray(Y) * sin_rot_angle, ray(Y) * cos_rot_angle,
- 0, 0 );
- Matrix im = m.inverse();
- Point sol = (ep - sp) * im;
- double half_sum_angle = std::atan2(-sol[X], sol[Y]);
- double half_diff_angle;
- if ( are_near(std::fabs(half_sum_angle), M_PI/2) )
+ result.push_back(from);
+ return result;
+ }
+ else if ( are_near(ray(X), 0) || are_near(ray(Y), 0) )
{
- double anti_sgn_hsa = (half_sum_angle > 0) ? -1 : 1;
- double arg = anti_sgn_hsa * sol[X] / 2;
- // if |arg| is a little bit > 1 acos returns nan
- if ( are_near(arg, 1) )
- half_diff_angle = 0;
- else if ( are_near(arg, -1) )
- half_diff_angle = M_PI;
+ LineSegment seg(pointAt(from), pointAt(to));
+ Point np = seg.pointAt( seg.nearestPoint(p) );
+ if ( are_near(ray(Y), 0) )
+ {
+ if ( are_near(_rot_angle, M_PI/2)
+ || are_near(_rot_angle, 3*M_PI/2) )
+ {
+ result = roots(np[Y], Y);
+ }
+ else
+ {
+ result = roots(np[X], X);
+ }
+ }
else
{
- if ( !(-1 < arg && arg < 1) )
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints"
- );
- // assert( -1 < arg && arg < 1 );
- // if it fails
- // => there is no ellipse that satisfies the given constraints
- half_diff_angle = std::acos( arg );
+ if ( are_near(_rot_angle, M_PI/2)
+ || are_near(_rot_angle, 3*M_PI/2) )
+ {
+ result = roots(np[X], X);
+ }
+ else
+ {
+ result = roots(np[Y], Y);
+ }
}
+ return result;
+ }
+ else if ( are_near(ray(X), ray(Y)) )
+ {
+ Point r = p - center();
+ if ( are_near(r, Point(0,0)) )
+ {
+ THROW_INFINITESOLUTIONS(0);
+ }
+ // TODO: implement case r != 0
+// Point np = ray(X) * unit_vector(r);
+// std::vector<double> solX = roots(np[X],X);
+// std::vector<double> solY = roots(np[Y],Y);
+// double t;
+// if ( are_near(solX[0], solY[0]) || are_near(solX[0], solY[1]))
+// {
+// t = solX[0];
+// }
+// else
+// {
+// t = solX[1];
+// }
+// if ( !(t < from || t > to) )
+// {
+// result.push_back(t);
+// }
+// else
+// {
+//
+// }
+ }
- half_diff_angle = M_PI/2 - half_diff_angle;
+ // solve the equation <D(E(t),t)|E(t)-p> == 0
+ // that provides min and max distance points
+ // on the ellipse E wrt the point p
+ // after the substitutions:
+ // cos(t) = (1 - s^2) / (1 + s^2)
+ // sin(t) = 2t / (1 + s^2)
+ // where s = tan(t/2)
+ // we get a 4th degree equation in s
+ /*
+ * ry s^4 ((-cy + py) Cos[Phi] + (cx - px) Sin[Phi]) +
+ * ry ((cy - py) Cos[Phi] + (-cx + px) Sin[Phi]) +
+ * 2 s^3 (rx^2 - ry^2 + (-cx + px) rx Cos[Phi] + (-cy + py) rx Sin[Phi]) +
+ * 2 s (-rx^2 + ry^2 + (-cx + px) rx Cos[Phi] + (-cy + py) rx Sin[Phi])
+ */
+
+ Point p_c = p - center();
+ double rx2_ry2 = (ray(X) - ray(Y)) * (ray(X) + ray(Y));
+ double sinrot, cosrot;
+ sincos(_rot_angle, sinrot, cosrot);
+ double expr1 = ray(X) * (p_c[X] * cosrot + p_c[Y] * sinrot);
+ Poly coeff;
+ coeff.resize(5);
+ coeff[4] = ray(Y) * ( p_c[Y] * cosrot - p_c[X] * sinrot );
+ coeff[3] = 2 * ( rx2_ry2 + expr1 );
+ coeff[2] = 0;
+ coeff[1] = 2 * ( -rx2_ry2 + expr1 );
+ coeff[0] = -coeff[4];
+
+// for ( unsigned int i = 0; i < 5; ++i )
+// std::cerr << "c[" << i << "] = " << coeff[i] << std::endl;
+
+ std::vector<double> real_sol;
+ // gsl_poly_complex_solve raises an error
+ // if the leading coefficient is zero
+ if ( are_near(coeff[4], 0) )
+ {
+ real_sol.push_back(0);
+ if ( !are_near(coeff[3], 0) )
+ {
+ double sq = -coeff[1] / coeff[3];
+ if ( sq > 0 )
+ {
+ double s = std::sqrt(sq);
+ real_sol.push_back(s);
+ real_sol.push_back(-s);
+ }
+ }
}
else
{
- double arg = sol[Y] / ( 2 * std::cos(half_sum_angle) );
- // if |arg| is a little bit > 1 asin returns nan
- if ( are_near(arg, 1) )
- half_diff_angle = M_PI/2;
- else if ( are_near(arg, -1) )
- half_diff_angle = -M_PI/2;
- else
+ real_sol = solve_reals(coeff);
+ }
+
+ for ( unsigned int i = 0; i < real_sol.size(); ++i )
+ {
+ real_sol[i] = 2 * std::atan(real_sol[i]);
+ if ( real_sol[i] < 0 ) real_sol[i] += 2*M_PI;
+ }
+ // when s -> Infinity then <D(E)|E-p> -> 0 iff coeff[4] == 0
+ // so we add M_PI to the solutions being lim arctan(s) = PI when s->Infinity
+ if ( (real_sol.size() % 2) != 0 )
+ {
+ real_sol.push_back(M_PI);
+ }
+
+ double mindistsq1 = std::numeric_limits<double>::max();
+ double mindistsq2 = std::numeric_limits<double>::max();
+ double dsq;
+ unsigned int mi1, mi2;
+ for ( unsigned int i = 0; i < real_sol.size(); ++i )
+ {
+ dsq = distanceSq(p, pointAtAngle(real_sol[i]));
+ if ( mindistsq1 > dsq )
{
- if ( !(-1 < arg && arg < 1) )
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints"
- );
- // assert( -1 < arg && arg < 1 );
- // if it fails
- // => there is no ellipse that satisfies the given constraints
- half_diff_angle = std::asin( arg );
+ mindistsq2 = mindistsq1;
+ mi2 = mi1;
+ mindistsq1 = dsq;
+ mi1 = i;
+ }
+ else if ( mindistsq2 > dsq )
+ {
+ mindistsq2 = dsq;
+ mi2 = i;
}
}
- if ( ( m_large_arc && half_diff_angle > 0 )
- || (!m_large_arc && half_diff_angle < 0 ) )
+ double t = map_to_01( real_sol[mi1] );
+ if ( !(t < from || t > to) )
{
- half_diff_angle = -half_diff_angle;
- }
- if ( half_sum_angle < 0 ) half_sum_angle += 2*M_PI;
- if ( half_diff_angle < 0 ) half_diff_angle += M_PI;
-
- m_start_angle = half_sum_angle - half_diff_angle;
- m_end_angle = half_sum_angle + half_diff_angle;
- // 0 <= m_start_angle, m_end_angle < 2PI
- if ( m_start_angle < 0 ) m_start_angle += 2*M_PI;
- if( !(m_end_angle < 2*M_PI) ) m_end_angle -= 2*M_PI;
- sol[0] = std::cos(m_start_angle);
- sol[1] = std::sin(m_start_angle);
- m_center = sp - sol * m;
- if ( !sweep_flag() )
+ result.push_back(t);
+ }
+
+ bool second_sol = false;
+ t = map_to_01( real_sol[mi2] );
+ if ( real_sol.size() == 4 && !(t < from || t > to) )
{
- double angle = m_start_angle;
- m_start_angle = m_end_angle;
- m_end_angle = angle;
+ if ( result.empty() || are_near(mindistsq1, mindistsq2) )
+ {
+ result.push_back(t);
+ second_sol = true;
+ }
}
+
+ // we need to test extreme points too
+ double dsq1 = distanceSq(p, pointAt(from));
+ double dsq2 = distanceSq(p, pointAt(to));
+ if ( second_sol )
+ {
+ if ( mindistsq2 > dsq1 )
+ {
+ result.clear();
+ result.push_back(from);
+ mindistsq2 = dsq1;
+ }
+ else if ( are_near(mindistsq2, dsq) )
+ {
+ result.push_back(from);
+ }
+ if ( mindistsq2 > dsq2 )
+ {
+ result.clear();
+ result.push_back(to);
+ }
+ else if ( are_near(mindistsq2, dsq2) )
+ {
+ result.push_back(to);
+ }
+
+ }
+ else
+ {
+ if ( result.empty() )
+ {
+ if ( are_near(dsq1, dsq2) )
+ {
+ result.push_back(from);
+ result.push_back(to);
+ }
+ else if ( dsq2 > dsq1 )
+ {
+ result.push_back(from);
+ }
+ else
+ {
+ result.push_back(to);
+ }
+ }
+ }
+
+ return result;
}
+#endif
-Coord EllipticalArc::map_to_02PI(Coord t) const
+/*
+ * NOTE: this implementation follows Standard SVG 1.1 implementation guidelines
+ * for elliptical arc curves. See Appendix F.6.
+ */
+void EllipticalArc::_updateCenterAndAngles(bool svg)
{
- if ( sweep_flag() )
+ Point d = initialPoint() - finalPoint();
+
+ // TODO move this to SVGElipticalArc?
+ if (svg)
{
- Coord angle = start_angle() + sweep_angle() * t;
- if ( !(angle < 2*M_PI) )
- angle -= 2*M_PI;
- return angle;
+ if ( initialPoint() == finalPoint() )
+ {
+ _rot_angle = _start_angle = _end_angle = 0;
+ _center = initialPoint();
+ _rays = Geom::Point(0,0);
+ _large_arc = _sweep = false;
+ return;
+ }
+
+ _rays[X] = std::fabs(_rays[X]);
+ _rays[Y] = std::fabs(_rays[Y]);
+
+ if ( are_near(ray(X), 0) || are_near(ray(Y), 0) )
+ {
+ _rays[X] = L2(d) / 2;
+ _rays[Y] = 0;
+ _rot_angle = std::atan2(d[Y], d[X]);
+ _start_angle = 0;
+ _end_angle = M_PI;
+ _center = middle_point(initialPoint(), finalPoint());
+ _large_arc = false;
+ _sweep = false;
+ return;
+ }
}
else
{
- Coord angle = start_angle() - sweep_angle() * t;
- if ( angle < 0 ) angle += 2*M_PI;
- return angle;
+ if ( are_near(initialPoint(), finalPoint()) )
+ {
+ if ( are_near(ray(X), 0) && are_near(ray(Y), 0) )
+ {
+ _start_angle = _end_angle = 0;
+ _center = initialPoint();
+ return;
+ }
+ else
+ {
+ THROW_RANGEERROR("initial and final point are the same");
+ }
+ }
+ if ( are_near(ray(X), 0) && are_near(ray(Y), 0) )
+ { // but initialPoint != finalPoint
+ THROW_RANGEERROR(
+ "there is no ellipse that satisfies the given constraints: "
+ "ray(X) == 0 && ray(Y) == 0 but initialPoint != finalPoint"
+ );
+ }
+ if ( are_near(ray(Y), 0) )
+ {
+ Point v = initialPoint() - finalPoint();
+ if ( are_near(L2sq(v), 4*ray(X)*ray(X)) )
+ {
+ Angle angle(v);
+ if ( are_near( angle, _rot_angle ) )
+ {
+ _start_angle = 0;
+ _end_angle = M_PI;
+ _center = v/2 + finalPoint();
+ return;
+ }
+ angle -= M_PI;
+ if ( are_near( angle, _rot_angle ) )
+ {
+ _start_angle = M_PI;
+ _end_angle = 0;
+ _center = v/2 + finalPoint();
+ return;
+ }
+ THROW_RANGEERROR(
+ "there is no ellipse that satisfies the given constraints: "
+ "ray(Y) == 0 "
+ "and slope(initialPoint - finalPoint) != rotation_angle "
+ "and != rotation_angle + PI"
+ );
+ }
+ if ( L2sq(v) > 4*ray(X)*ray(X) )
+ {
+ THROW_RANGEERROR(
+ "there is no ellipse that satisfies the given constraints: "
+ "ray(Y) == 0 and distance(initialPoint, finalPoint) > 2*ray(X)"
+ );
+ }
+ else
+ {
+ THROW_RANGEERROR(
+ "there is infinite ellipses that satisfy the given constraints: "
+ "ray(Y) == 0 and distance(initialPoint, finalPoint) < 2*ray(X)"
+ );
+ }
+
+ }
+
+ if ( are_near(ray(X), 0) )
+ {
+ Point v = initialPoint() - finalPoint();
+ if ( are_near(L2sq(v), 4*ray(Y)*ray(Y)) )
+ {
+ double angle = std::atan2(v[Y], v[X]);
+ if (angle < 0) angle += 2*M_PI;
+ double rot_angle = _rot_angle.radians() + M_PI/2;
+ if ( !(rot_angle < 2*M_PI) ) rot_angle -= 2*M_PI;
+ if ( are_near( angle, rot_angle ) )
+ {
+ _start_angle = M_PI/2;
+ _end_angle = 3*M_PI/2;
+ _center = v/2 + finalPoint();
+ return;
+ }
+ angle -= M_PI;
+ if ( angle < 0 ) angle += 2*M_PI;
+ if ( are_near( angle, rot_angle ) )
+ {
+ _start_angle = 3*M_PI/2;
+ _end_angle = M_PI/2;
+ _center = v/2 + finalPoint();
+ return;
+ }
+ THROW_RANGEERROR(
+ "there is no ellipse that satisfies the given constraints: "
+ "ray(X) == 0 "
+ "and slope(initialPoint - finalPoint) != rotation_angle + PI/2 "
+ "and != rotation_angle + (3/2)*PI"
+ );
+ }
+ if ( L2sq(v) > 4*ray(Y)*ray(Y) )
+ {
+ THROW_RANGEERROR(
+ "there is no ellipse that satisfies the given constraints: "
+ "ray(X) == 0 and distance(initialPoint, finalPoint) > 2*ray(Y)"
+ );
+ }
+ else
+ {
+ THROW_RANGEERROR(
+ "there is infinite ellipses that satisfy the given constraints: "
+ "ray(X) == 0 and distance(initialPoint, finalPoint) < 2*ray(Y)"
+ );
+ }
+
+ }
+
}
+
+ Rotate rm(_rot_angle);
+ Affine m(rm);
+ m[1] = -m[1];
+ m[2] = -m[2];
+
+ Point p = (d / 2) * m;
+ double rx2 = _rays[X] * _rays[X];
+ double ry2 = _rays[Y] * _rays[Y];
+ double rxpy = _rays[X] * p[Y];
+ double rypx = _rays[Y] * p[X];
+ double rx2py2 = rxpy * rxpy;
+ double ry2px2 = rypx * rypx;
+ double num = rx2 * ry2;
+ double den = rx2py2 + ry2px2;
+ assert(den != 0);
+ double rad = num / den;
+ Point c(0,0);
+ if (rad > 1)
+ {
+ rad -= 1;
+ rad = std::sqrt(rad);
+
+ if (_large_arc == _sweep) rad = -rad;
+ c = rad * Point(rxpy / ray(Y), -rypx / ray(X));
+
+ _center = c * rm + middle_point(initialPoint(), finalPoint());
+ }
+ else if (rad == 1 || svg)
+ {
+ double lamda = std::sqrt(1 / rad);
+ _rays[X] *= lamda;
+ _rays[Y] *= lamda;
+ _center = middle_point(initialPoint(), finalPoint());
+ }
+ else
+ {
+ THROW_RANGEERROR(
+ "there is no ellipse that satisfies the given constraints"
+ );
+ }
+
+ Point sp((p[X] - c[X]) / ray(X), (p[Y] - c[Y]) / ray(Y));
+ Point ep((-p[X] - c[X]) / ray(X), (-p[Y] - c[Y]) / ray(Y));
+ Point v(1, 0);
+ _start_angle = angle_between(v, sp);
+ double sweep_angle = angle_between(sp, ep);
+ if (!_sweep && sweep_angle > 0) sweep_angle -= 2*M_PI;
+ if (_sweep && sweep_angle < 0) sweep_angle += 2*M_PI;
+
+ _end_angle = _start_angle;
+ _end_angle += sweep_angle;
}
-Coord EllipticalArc::map_to_01(Coord angle) const
+D2<SBasis> EllipticalArc::toSBasis() const
{
- return map_circular_arc_on_unit_interval(angle, start_angle(),
- end_angle(), sweep_flag());
+ D2<SBasis> arc;
+ // the interval of parametrization has to be [0,1]
+ Coord et = initialAngle().radians() + ( _sweep ? sweepAngle() : -sweepAngle() );
+ Linear param(initialAngle(), et);
+ Coord cos_rot_angle, sin_rot_angle;
+ sincos(_rot_angle, sin_rot_angle, cos_rot_angle);
+
+ // order = 4 seems to be enough to get a perfect looking elliptical arc
+ SBasis arc_x = ray(X) * cos(param,4);
+ SBasis arc_y = ray(Y) * sin(param,4);
+ arc[0] = arc_x * cos_rot_angle - arc_y * sin_rot_angle + Linear(center(X),center(X));
+ arc[1] = arc_x * sin_rot_angle + arc_y * cos_rot_angle + Linear(center(Y),center(Y));
+
+ // ensure that endpoints remain exact
+ for ( int d = 0 ; d < 2 ; d++ ) {
+ arc[d][0][0] = initialPoint()[d];
+ arc[d][0][1] = finalPoint()[d];
+ }
+
+ return arc;
}
-std::vector<double> EllipticalArc::
-allNearestPoints( Point const& p, double from, double to ) const
+Curve *EllipticalArc::transformed(Affine const& m) const
{
- if ( from > to ) std::swap(from, to);
- if ( from < 0 || to > 1 )
- {
- THROW_RANGEERROR("[from,to] interval out of range");
- }
- std::vector<double> result;
- if ( ( are_near(ray(X), 0) && are_near(ray(Y), 0) ) || are_near(from, to) )
- {
- result.push_back(from);
- return result;
- }
- else if ( are_near(ray(X), 0) || are_near(ray(Y), 0) )
- {
- LineSegment seg(pointAt(from), pointAt(to));
- Point np = seg.pointAt( seg.nearestPoint(p) );
- if ( are_near(ray(Y), 0) )
- {
- if ( are_near(rotation_angle(), M_PI/2)
- || are_near(rotation_angle(), 3*M_PI/2) )
- {
- result = roots(np[Y], Y);
- }
- else
- {
- result = roots(np[X], X);
- }
- }
- else
- {
- if ( are_near(rotation_angle(), M_PI/2)
- || are_near(rotation_angle(), 3*M_PI/2) )
- {
- result = roots(np[X], X);
- }
- else
- {
- result = roots(np[Y], Y);
- }
- }
- return result;
- }
- else if ( are_near(ray(X), ray(Y)) )
- {
- Point r = p - center();
- if ( are_near(r, Point(0,0)) )
- {
- THROW_INFINITESOLUTIONS(0);
- }
- // TODO: implement case r != 0
-// Point np = ray(X) * unit_vector(r);
-// std::vector<double> solX = roots(np[X],X);
-// std::vector<double> solY = roots(np[Y],Y);
-// double t;
-// if ( are_near(solX[0], solY[0]) || are_near(solX[0], solY[1]))
-// {
-// t = solX[0];
-// }
-// else
-// {
-// t = solX[1];
-// }
-// if ( !(t < from || t > to) )
-// {
-// result.push_back(t);
-// }
-// else
-// {
-//
-// }
- }
-
- // solve the equation <D(E(t),t)|E(t)-p> == 0
- // that provides min and max distance points
- // on the ellipse E wrt the point p
- // after the substitutions:
- // cos(t) = (1 - s^2) / (1 + s^2)
- // sin(t) = 2t / (1 + s^2)
- // where s = tan(t/2)
- // we get a 4th degree equation in s
- /*
- * ry s^4 ((-cy + py) Cos[Phi] + (cx - px) Sin[Phi]) +
- * ry ((cy - py) Cos[Phi] + (-cx + px) Sin[Phi]) +
- * 2 s^3 (rx^2 - ry^2 + (-cx + px) rx Cos[Phi] + (-cy + py) rx Sin[Phi]) +
- * 2 s (-rx^2 + ry^2 + (-cx + px) rx Cos[Phi] + (-cy + py) rx Sin[Phi])
- */
-
- Point p_c = p - center();
- double rx2_ry2 = (ray(X) - ray(Y)) * (ray(X) + ray(Y));
- double cosrot = std::cos( rotation_angle() );
- double sinrot = std::sin( rotation_angle() );
- double expr1 = ray(X) * (p_c[X] * cosrot + p_c[Y] * sinrot);
- Poly coeff;
- coeff.resize(5);
- coeff[4] = ray(Y) * ( p_c[Y] * cosrot - p_c[X] * sinrot );
- coeff[3] = 2 * ( rx2_ry2 + expr1 );
- coeff[2] = 0;
- coeff[1] = 2 * ( -rx2_ry2 + expr1 );
- coeff[0] = -coeff[4];
-
-// for ( unsigned int i = 0; i < 5; ++i )
-// std::cerr << "c[" << i << "] = " << coeff[i] << std::endl;
-
- std::vector<double> real_sol;
- // gsl_poly_complex_solve raises an error
- // if the leading coefficient is zero
- if ( are_near(coeff[4], 0) )
- {
- real_sol.push_back(0);
- if ( !are_near(coeff[3], 0) )
- {
- double sq = -coeff[1] / coeff[3];
- if ( sq > 0 )
- {
- double s = std::sqrt(sq);
- real_sol.push_back(s);
- real_sol.push_back(-s);
- }
- }
- }
- else
- {
- real_sol = solve_reals(coeff);
- }
-// else
-// {
-// double sol[8];
-// gsl_poly_complex_workspace * w = gsl_poly_complex_workspace_alloc(5);
-// gsl_poly_complex_solve(coeff, 5, w, sol );
-// gsl_poly_complex_workspace_free(w);
-//
-// for ( unsigned int i = 0; i < 4; ++i )
-// {
-// if ( sol[2*i+1] == 0 ) real_sol.push_back(sol[2*i]);
-// }
-// }
-
- for ( unsigned int i = 0; i < real_sol.size(); ++i )
- {
- real_sol[i] = 2 * std::atan(real_sol[i]);
- if ( real_sol[i] < 0 ) real_sol[i] += 2*M_PI;
- }
- // when s -> Infinity then <D(E)|E-p> -> 0 iff coeff[4] == 0
- // so we add M_PI to the solutions being lim arctan(s) = PI when s->Infinity
- if ( (real_sol.size() % 2) != 0 )
- {
- real_sol.push_back(M_PI);
- }
-
- double mindistsq1 = std::numeric_limits<double>::max();
- double mindistsq2 = std::numeric_limits<double>::max();
- double dsq;
- unsigned int mi1, mi2;
- for ( unsigned int i = 0; i < real_sol.size(); ++i )
- {
- dsq = distanceSq(p, pointAtAngle(real_sol[i]));
- if ( mindistsq1 > dsq )
- {
- mindistsq2 = mindistsq1;
- mi2 = mi1;
- mindistsq1 = dsq;
- mi1 = i;
- }
- else if ( mindistsq2 > dsq )
- {
- mindistsq2 = dsq;
- mi2 = i;
- }
- }
-
- double t = map_to_01( real_sol[mi1] );
- if ( !(t < from || t > to) )
- {
- result.push_back(t);
- }
-
- bool second_sol = false;
- t = map_to_01( real_sol[mi2] );
- if ( real_sol.size() == 4 && !(t < from || t > to) )
- {
- if ( result.empty() || are_near(mindistsq1, mindistsq2) )
- {
- result.push_back(t);
- second_sol = true;
- }
- }
-
- // we need to test extreme points too
- double dsq1 = distanceSq(p, pointAt(from));
- double dsq2 = distanceSq(p, pointAt(to));
- if ( second_sol )
- {
- if ( mindistsq2 > dsq1 )
- {
- result.clear();
- result.push_back(from);
- mindistsq2 = dsq1;
- }
- else if ( are_near(mindistsq2, dsq) )
- {
- result.push_back(from);
- }
- if ( mindistsq2 > dsq2 )
- {
- result.clear();
- result.push_back(to);
- }
- else if ( are_near(mindistsq2, dsq2) )
- {
- result.push_back(to);
- }
-
- }
- else
- {
- if ( result.empty() )
- {
- if ( are_near(dsq1, dsq2) )
- {
- result.push_back(from);
- result.push_back(to);
- }
- else if ( dsq2 > dsq1 )
- {
- result.push_back(from);
- }
- else
- {
- result.push_back(to);
- }
- }
- }
-
- return result;
+ Ellipse e(center(X), center(Y), ray(X), ray(Y), _rot_angle);
+ Ellipse et = e.transformed(m);
+ Point inner_point = pointAt(0.5);
+ return et.arc( initialPoint() * m,
+ inner_point * m,
+ finalPoint() * m,
+ isSVGCompliant() );
}
+Coord EllipticalArc::map_to_01(Coord angle) const
+{
+ return map_circular_arc_on_unit_interval(angle, initialAngle(),
+ finalAngle(), _sweep);
+}
} // end namespace Geom
-
/*
Local Variables:
mode:c++
@@ -928,4 +916,3 @@ allNearestPoints( Point const& p, double from, double to ) const
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
-
diff --git a/src/2geom/elliptical-arc.h b/src/2geom/elliptical-arc.h
index 002735944..e1e757207 100644
--- a/src/2geom/elliptical-arc.h
+++ b/src/2geom/elliptical-arc.h
@@ -1,12 +1,14 @@
/**
* \file
- * \brief Elliptical Arc - implementation of the svg elliptical arc path element
+ * \brief Elliptical arc curve
*
+ *//*
* Authors:
- * MenTaLguY <mental@rydia.net>
- * Marco Cecchetti <mrcekets at gmail.com>
+ * MenTaLguY <mental@rydia.net>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
*
- * Copyright 2007-2008 authors
+ * Copyright 2007-2009 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -32,272 +34,234 @@
* the specific language governing rights and limitations.
*/
-
-
-
#ifndef _2GEOM_ELLIPTICAL_ARC_H_
#define _2GEOM_ELLIPTICAL_ARC_H_
-
-#include <2geom/curve.h>
+#include <algorithm>
#include <2geom/angle.h>
-#include <2geom/utils.h>
+#include <2geom/bezier-curve.h>
+#include <2geom/curve.h>
+#include <2geom/affine.h>
#include <2geom/sbasis-curve.h> // for non-native methods
-
-#include <algorithm>
-
+#include <2geom/utils.h>
namespace Geom
{
-class EllipticalArc : public Curve
+class EllipticalArc : public Curve, public AngleInterval
{
- public:
- EllipticalArc()
- : m_initial_point(Point(0,0)), m_final_point(Point(0,0)),
- m_rx(0), m_ry(0), m_rot_angle(0),
- m_large_arc(true), m_sweep(true)
- {
- m_start_angle = m_end_angle = 0;
- m_center = Point(0,0);
- }
-
- EllipticalArc( Point _initial_point, double _rx, double _ry,
- double _rot_angle, bool _large_arc, bool _sweep,
- Point _final_point
- )
- : m_initial_point(_initial_point), m_final_point(_final_point),
- m_rx(_rx), m_ry(_ry), m_rot_angle(_rot_angle),
- m_large_arc(_large_arc), m_sweep(_sweep)
- {
- calculate_center_and_extreme_angles();
- }
-
- void set( Point _initial_point, double _rx, double _ry,
- double _rot_angle, bool _large_arc, bool _sweep,
- Point _final_point
- )
+public:
+ /** @brief Creates an arc with all variables set to zero, and both flags to true. */
+ EllipticalArc()
+ : AngleInterval(0, 0, true)
+ , _initial_point(0,0)
+ , _final_point(0,0)
+ , _rays(0,0)
+ , _center(0,0)
+ , _rot_angle(0)
+ , _large_arc(true)
+ {}
+ /** @brief Create a new elliptical arc.
+ * @param ip Initial point of the arc
+ * @param rx First ray of the ellipse
+ * @param ry Second ray of the ellipse
+ * @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 ip, Coord rx, Coord ry,
+ Coord rot_angle, bool large_arc, bool sweep,
+ Point fp
+ )
+ : AngleInterval(0,0,sweep)
+ , _initial_point(ip)
+ , _final_point(fp)
+ , _rays(rx, ry)
+ , _rot_angle(rot_angle)
+ , _large_arc(large_arc)
{
- m_initial_point = _initial_point;
- m_final_point = _final_point;
- m_rx = _rx;
- m_ry = _ry;
- m_rot_angle = _rot_angle;
- m_large_arc = _large_arc;
- m_sweep = _sweep;
- calculate_center_and_extreme_angles();
+ _updateCenterAndAngles(false);
}
- Curve* duplicate() const
- {
- return new EllipticalArc(*this);
- }
-
- double center(unsigned int i) const
- {
- return m_center[i];
+ // methods new to EllipticalArc go here
+
+ /// @name Retrieve and modify parameters
+ /// @{
+ /** @brief Get the interval of angles the arc contains
+ * @return The interval between the final and initial angles of the arc */
+ Interval angleInterval() const { return Interval(initialAngle(), finalAngle()); }
+ /** @brief Get a coordinate of the elliptical arc's center.
+ * @param d The dimension to retrieve
+ * @return The selected coordinate of the center */
+ /** @brief Get the defining ellipse's rotation
+ * @return Angle between the +X ray of the ellipse and the +X axis */
+ Angle rotationAngle() const {
+ return _rot_angle;
}
-
- Point center() const
+ /** @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 _rays[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 _rays; }
+ /** @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 _sweep; }
+ /** @brief Get the line segment connecting the arc's endpoints.
+ * @return A linear segment with initial and final point correspoding to those of the arc. */
+ LineSegment chord() const { return LineSegment(_initial_point, _final_point); }
+ /** @brief Change the arc's parameters. */
+ void set( Point const &ip, double rx, double ry,
+ double rot_angle, bool large_arc, bool sweep,
+ Point const &fp
+ )
{
- return m_center;
+ _initial_point = ip;
+ _final_point = fp;
+ _rays[X] = rx;
+ _rays[Y] = ry;
+ _rot_angle = Angle(rot_angle);
+ _large_arc = large_arc;
+ _sweep = sweep;
+ _updateCenterAndAngles(isSVGCompliant());
}
-
- Point initialPoint() const
- {
- return m_initial_point;
+ /** @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 setExtremes(Point const &ip, Point const &fp) {
+ _initial_point = ip;
+ _final_point = fp;
+ _updateCenterAndAngles(isSVGCompliant());
}
-
- Point finalPoint() const
- {
- return m_final_point;
+ /// @}
+
+ /// @name Access computed parameters of the arc
+ /// @{
+ Coord center(Dim2 d) const { return _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 _center; }
+ /** @brief Get the extent of the arc
+ * @return The angle between the initial and final point, in arc's angular coordinates */
+ Coord sweepAngle() const {
+ return extent();
}
-
- double start_angle() const
- {
- return m_start_angle;
- }
-
- double end_angle() const
- {
- return m_end_angle;
- }
-
- double ray(unsigned int i) const
- {
- return (i == 0) ? m_rx : m_ry;
- }
-
- bool large_arc_flag() const
- {
- return m_large_arc;
- }
-
- bool sweep_flag() const
- {
- return m_sweep;
- }
-
- double rotation_angle() const
- {
- return m_rot_angle;
- }
-
- void setInitial( const Point _point)
- {
- m_initial_point = _point;
- calculate_center_and_extreme_angles();
+ /// @}
+
+ /// @name Angular evaluation
+ /// @{
+ /** 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(Coord angle) const;
+ /** @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;
+ /** @brief Retrieve the unit circle transform.
+ * 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;
+ /// @}
+
+ /** @brief Check whether the arc adheres to SVG 1.1 implementation guidelines */
+ virtual bool isSVGCompliant() const { return false; }
+
+ std::pair<EllipticalArc, EllipticalArc> subdivide(Coord t) const {
+ EllipticalArc* arc1 = static_cast<EllipticalArc*>(portion(0, t));
+ EllipticalArc* arc2 = static_cast<EllipticalArc*>(portion(t, 1));
+ assert( arc1 != NULL && arc2 != NULL);
+ std::pair<EllipticalArc, EllipticalArc> arc_pair(*arc1, *arc2);
+ delete arc1;
+ delete arc2;
+ return arc_pair;
}
- void setFinal( const Point _point)
- {
- m_final_point = _point;
- calculate_center_and_extreme_angles();
+ // implementation of overloads goes here
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ virtual Point initialPoint() const { return _initial_point; }
+ virtual Point finalPoint() const { return _final_point; }
+ virtual Curve* duplicate() const { return new EllipticalArc(*this); }
+ virtual void setInitial(Point const &p) {
+ _initial_point = p;
+ _updateCenterAndAngles(isSVGCompliant());
}
-
- void setExtremes( const Point& _initial_point, const Point& _final_point )
- {
- m_initial_point = _initial_point;
- m_final_point = _final_point;
- calculate_center_and_extreme_angles();
+ virtual void setFinal(Point const &p) {
+ _final_point = p;
+ _updateCenterAndAngles(isSVGCompliant());
}
-
- bool isDegenerate() const
- {
+ virtual bool isDegenerate() const {
return ( are_near(ray(X), 0) || are_near(ray(Y), 0) );
}
-
-
- virtual OptRect boundsFast() const
- {
+ virtual Rect boundsFast() const {
return boundsExact();
}
-
- virtual OptRect boundsExact() const;
-
+ virtual Rect boundsExact() const;
// TODO: native implementation of the following methods
- virtual OptRect boundsLocal(OptInterval i, unsigned int deg) const
- {
+ virtual OptRect boundsLocal(OptInterval const &i, unsigned int deg) const {
return SBasisCurve(toSBasis()).boundsLocal(i, deg);
}
-
- std::vector<double> roots(double v, Dim2 d) const;
-
- std::vector<double>
- allNearestPoints( Point const& p, double from = 0, double to = 1 ) const;
-
- double nearestPoint( Point const& p, double from = 0, double to = 1 ) const
- {
- if ( are_near(ray(X), ray(Y)) && are_near(center(), p) )
- {
- return from;
- }
- return allNearestPoints(p, from, to).front();
- }
-
- // TODO: native implementation of the following methods
- int winding(Point p) const
- {
- return SBasisCurve(toSBasis()).winding(p);
- }
-
- int degreesOfFreedom() const { return 7;}
-
- Curve *derivative() const;
-
- // TODO: native implementation of the following methods
- Curve *transformed(Matrix const &m) const
- {
- return SBasisCurve(toSBasis()).transformed(m);
- }
-
- std::vector<Point> pointAndDerivatives(Coord t, unsigned int n) const;
-
- D2<SBasis> toSBasis() const;
-
- bool containsAngle(Coord angle) const;
-
- double valueAtAngle(Coord t, Dim2 d) const;
-
- Point pointAtAngle(Coord t) const
- {
- double sin_rot_angle = std::sin(rotation_angle());
- double cos_rot_angle = std::cos(rotation_angle());
- Matrix m( ray(X) * cos_rot_angle, ray(X) * sin_rot_angle,
- -ray(Y) * sin_rot_angle, ray(Y) * cos_rot_angle,
- center(X), center(Y) );
- Point p( std::cos(t), std::sin(t) );
- return p * m;
+ virtual std::vector<double> roots(double v, Dim2 d) const;
+#ifdef HAVE_GSL
+ virtual std::vector<double> allNearestPoints( Point const& p, double from = 0, double to = 1 ) const;
+#endif
+ virtual double nearestPoint( Point const& p, double from = 0, double to = 1 ) const {
+ if ( are_near(ray(X), ray(Y)) && are_near(center(), p) ) {
+ return from;
+ }
+ return allNearestPoints(p, from, to).front();
}
-
- double valueAt(Coord t, Dim2 d) const
- {
- Coord tt = map_to_02PI(t);
- return valueAtAngle(tt, d);
+ virtual int degreesOfFreedom() const { return 7; }
+ virtual Curve *derivative() const;
+ virtual Curve *transformed(Affine const &m) const;
+
+ /**
+ * The size of the returned vector equals n+1.
+ */
+ virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned int n) const;
+
+ virtual D2<SBasis> toSBasis() const;
+ virtual double valueAt(Coord t, Dim2 d) const {
+ return valueAtAngle(angleAt(t), d);
}
-
- Point pointAt(Coord t) const
- {
- Coord tt = map_to_02PI(t);
- return pointAtAngle(tt);
+ virtual Point pointAt(Coord t) const {
+ return pointAtAngle(angleAt(t));
}
+ virtual Curve* portion(double f, double t) const;
+ virtual Curve* reverse() const;
+#endif
- std::pair<EllipticalArc, EllipticalArc>
- subdivide(Coord t) const
- {
- EllipticalArc* arc1 = static_cast<EllipticalArc*>(portion(0, t));
- EllipticalArc* arc2 = static_cast<EllipticalArc*>(portion(t, 1));
- assert( arc1 != NULL && arc2 != NULL);
- std::pair<EllipticalArc, EllipticalArc> arc_pair(*arc1, *arc2);
- delete arc1;
- delete arc2;
- return arc_pair;
- }
+protected:
+ void _updateCenterAndAngles(bool svg);
- Curve* portion(double f, double t) const;
-
- // the arc is the same but traversed in the opposite direction
- Curve* reverse() const
- {
- EllipticalArc* rarc = new EllipticalArc( *this );
- rarc->m_sweep = !m_sweep;
- rarc->m_initial_point = m_final_point;
- rarc->m_final_point = m_initial_point;
- rarc->m_start_angle = m_end_angle;
- rarc->m_end_angle = m_start_angle;
- return rarc;
- }
+ Point _initial_point, _final_point;
+ Point _rays, _center;
+ Angle _rot_angle;
+ bool _large_arc;
- double sweep_angle() const
- {
- Coord d = end_angle() - start_angle();
- if ( !sweep_flag() ) d = -d;
- if ( d < 0 )
- d += 2*M_PI;
- return d;
- }
-
- private:
- Coord map_to_02PI(Coord t) const;
+private:
Coord map_to_01(Coord angle) const;
- void calculate_center_and_extreme_angles();
- private:
- Point m_initial_point, m_final_point;
- double m_rx, m_ry, m_rot_angle;
- bool m_large_arc, m_sweep;
- double m_start_angle, m_end_angle;
- Point m_center;
-
}; // end class EllipticalArc
-
} // end namespace Geom
#endif // _2GEOM_ELLIPTICAL_ARC_H_
-
-
-
/*
Local Variables:
mode:c++
diff --git a/src/2geom/exception.h b/src/2geom/exception.h
index fd92ca5e1..12736d639 100644
--- a/src/2geom/exception.h
+++ b/src/2geom/exception.h
@@ -1,11 +1,17 @@
-#ifndef LIB2GEOM_EXCEPTION_HEADER
-#define LIB2GEOM_EXCEPTION_HEADER
-
/**
* \file
* \brief Defines the different types of exceptions that 2geom can throw.
*
- * Copyright 2007 Johan Engelen <goejendaagh@zonnet.nl>
+ * 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
@@ -32,13 +38,18 @@
*
*/
+#ifndef LIB2GEOM_EXCEPTION_HEADER
+#define LIB2GEOM_EXCEPTION_HEADER
+
#include <exception>
#include <sstream>
#include <string>
namespace Geom {
-// Base exception class, all 2geom exceptions should be derived from this one.
+/**
+ * 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) {
@@ -58,10 +69,7 @@ protected:
#define THROW_EXCEPTION(message) throw(Geom::Exception(message, __FILE__, __LINE__))
//-----------------------------------------------------------------------
-// Two main exception classes: LogicalError and RangeError.
-// Logical errors are 2geom faults/bugs, RangeErrors are 'user' faults.
-// This way, the 'user' can distinguish between groups of exceptions
-// ('user' is the coder that uses lib2geom)
+
class LogicalError : public Exception {
public:
LogicalError(const char * message, const char *file, const int line)
diff --git a/src/2geom/forward.h b/src/2geom/forward.h
index adc099379..93a762022 100644
--- a/src/2geom/forward.h
+++ b/src/2geom/forward.h
@@ -1,11 +1,12 @@
/**
* \file
* \brief Contains forward declarations of 2geom types
- *
+ *//*
* Authors:
* Johan Engelen <goejendaagh@zonnet.nl>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
*
- * Copyright 2008 authors
+ * 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
@@ -38,21 +39,34 @@
namespace Geom {
+// basic types
+typedef double Coord;
+class Point;
+class Interval;
+class OptInterval;
+class Line;
+class Ray;
+
+// fragments
+class Linear;
class Bezier;
-template <unsigned> class BezierCurve;
-template<> class BezierCurve<0>;
-typedef BezierCurve<2> QuadraticBezier;
-typedef BezierCurve<1> LineSegment;
-typedef BezierCurve<3> CubicBezier;
-class EllipticalArc;
-class SVGEllipticalArc;
+class SBasis;
+// curves
+class Curve;
+class SBasisCurve;
+class BezierCurve;
class HLineSegment;
class VLineSegment;
+class LineSegment;
+class EllipticalArc;
+class SVGEllipticalArc;
-typedef double Coord;
-class Point;
+// paths and path sequences
+class Path;
+typedef std::vector<Path> PathVector;
+// errors
class Exception;
class LogicalError;
class RangeError;
@@ -61,32 +75,27 @@ class InvariantsViolation;
class NotInvertible;
class ContinuityError;
-class Interval;
-class OptInterval;
-class Linear;
-class Hat;
-class Tri;
-
-class Matrix;
+// transforms
+class Affine;
class Translate;
class Rotate;
class Scale;
+class HShear;
+class VShear;
-class Curve;
-class Path;
-typedef std::vector<Path> PathVector;
-
-template <class> class D2;
+// templates
+template <typename> class D2;
template <typename> class Piecewise;
-class SBasis;
-class SBasisCurve;
typedef D2<Interval> Rect;
class OptRect;
class Shape;
class Region;
+class Hat;
+class Tri;
+// misc
class SVGPathSink;
template <typename> class SVGPathGenerator;
diff --git a/src/2geom/hvlinesegment.h b/src/2geom/hvlinesegment.h
index 6a9edbcea..9419be8f6 100644
--- a/src/2geom/hvlinesegment.h
+++ b/src/2geom/hvlinesegment.h
@@ -39,127 +39,37 @@
namespace Geom
{
-class HLineSegment : public Curve
+template <Dim2 axis>
+class AxisLineSegment : public LineSegment
{
- public:
- HLineSegment()
- {}
-
- HLineSegment(Coord _x0, Coord _x1, Coord _y)
- : m_line_seg(Point(_x0, _y), Point(_x1, _y))
- {
- }
-
- HLineSegment(Point const& _p, double _length)
- : m_line_seg(_p, Point(_p[X] + _length, _p[Y]))
- {
- }
-
- HLineSegment(Point const& _p0, Point const& _p1)
- : m_line_seg(_p0, _p1)
- {
- if ( _p0[Y] != _p1[Y] )
- {
- THROW_RANGEERROR("HLineSegment::HLineSegment passed points should "
- "have the same Y value");
- }
- }
-
- Curve* duplicate() const
- {
- return new HLineSegment(*this);
- }
-
- bool isDegenerate() const
- {
- return m_line_seg.isDegenerate();
- }
-
- Point initialPoint() const
- {
- return m_line_seg.initialPoint();
- }
-
- Point finalPoint() const
- {
- return m_line_seg.finalPoint();
- }
-
- Coord getY()
- {
- return initialPoint()[Y];
- }
-
- void setInitial(Point _p)
- {
- m_line_seg.setInitial( Point(_p[X], initialPoint()[Y]) );
- }
-
- void setFinal(Point _p)
- {
- m_line_seg.setFinal( Point(_p[X], finalPoint()[Y]) );
- }
-
- void setX0(Coord _x)
- {
- m_line_seg.setInitial( Point(_x, initialPoint()[Y]) );
- }
-
- void setX1(Coord _x)
- {
- m_line_seg.setFinal( Point(_x, finalPoint()[Y]) );
- }
-
- void setY(Coord _y)
- {
- m_line_seg.setInitial( Point(initialPoint()[X], _y) );
- m_line_seg.setFinal( Point(finalPoint()[X], _y) );
- }
-
- virtual OptRect boundsFast() const
- {
- return boundsExact();
- }
-
- virtual OptRect boundsExact() const
- {
- return Rect( initialPoint(), finalPoint() );
- }
-
- virtual OptRect boundsLocal(OptInterval i, unsigned deg) const
- {
- return m_line_seg.boundsLocal(i, deg);
- }
-
- int winding(Point p) const
- {
- return m_line_seg.winding(p);
- }
-
- int degreesOfFreedom() const { return 3;}
-
- std::vector<double>
- roots(double v, Dim2 d) const
- {
- if (d < 0 || d > 1)
- {
- THROW_RANGEERROR("dimension argument out of range");
- }
- std::vector<double> result;
- if (d == X)
- {
- if ( v >= initialPoint()[X] && v <= finalPoint()[X] )
- {
- double t = 0;
+public:
+ static const Dim2 other_axis = static_cast<Dim2>((axis + 1) % 2);
+ virtual void setInitial(Point const &p) {
+ Point f = finalPoint();
+ f[axis] = p[axis];
+ LineSegment::setInitial(p);
+ LineSegment::setFinal(f);
+ }
+ virtual void setFinal(Point const &p) {
+ Point i = initialPoint();
+ i[axis] = p[axis];
+ LineSegment::setFinal(p);
+ LineSegment::setInitial(i);
+ }
+ virtual Rect boundsFast() const { return boundsExact(); }
+ virtual Rect boundsExact() const { Rect r(initialPoint(), finalPoint()); return r; }
+ virtual int degreesOfFreedom() const { return 3; }
+ virtual std::vector<Coord> roots(Coord v, Dim2 d) const {
+ std::vector<Coord> result;
+ if (d == axis) {
+ if ( v >= initialPoint()[axis] && v <= finalPoint()[axis] ) {
+ Coord t = 0;
if (!isDegenerate())
- t = (v - initialPoint()[X]) / (finalPoint()[X] - initialPoint()[X]);
+ t = (v - initialPoint()[axis]) / (finalPoint()[axis] - initialPoint()[axis]);
result.push_back(t);
}
- }
- else
- {
- if (v == initialPoint()[Y])
- {
+ } else {
+ if (v == initialPoint()[other_axis]) {
if (!isDegenerate())
THROW_INFINITESOLUTIONS(0);
result.push_back(0);
@@ -167,270 +77,161 @@ class HLineSegment : public Curve
}
return result;
}
-
- double nearestPoint( Point const& p, double from = 0, double to = 1 ) const
- {
+ virtual Coord nearestPoint( Point const &p, Coord from = 0, Coord to = 1 ) const {
if ( from > to ) std::swap(from, to);
- double xfrom = pointAt(from)[X];
- double xto = pointAt(to)[X];
- if ( xfrom > xto )
- {
+ Coord xfrom = valueAt(from, axis);
+ Coord xto = valueAt(to, axis);
+ if ( xfrom > xto ) {
std::swap(xfrom, xto);
std::swap(from, to);
}
- if ( p[X] > xfrom && p[X] < xto )
- {
- return (p[X] - initialPoint()[X]) / (finalPoint()[X] - initialPoint()[X]);
+ if ( p[axis] > xfrom && p[axis] < xto ) {
+ return (p[axis] - initialPoint()[axis]) / (finalPoint()[axis] - initialPoint()[axis]);
}
else if ( p[X] <= xfrom )
return from;
else
return to;
}
-
- std::pair<HLineSegment, HLineSegment> subdivide(Coord t) const
- {
- std::pair<HLineSegment, HLineSegment> result;
- Point p = pointAt(t);
- result.first.setInitial(initialPoint());
- result.first.setFinal(p);
- result.second.setInitial(p);
- result.second.setFinal(finalPoint());
- return result;
- }
-
- Curve* portion(double f, double t) const
- {
- Point ip = pointAt(f);
- Point ep = pointAt(t);
- return new HLineSegment(ip[X], ep[X], ip[Y]);
- }
-
- Curve* reverse() const
- {
- return
- new HLineSegment(finalPoint()[X], initialPoint()[X], initialPoint()[Y]);
- }
-
- Curve* transformed(Matrix const & m) const
- {
- Point ip = initialPoint() * m;
- Point ep = finalPoint() * m;
- if (ip[Y] == ep[Y]) {
- return new HLineSegment(ip[X], ep[X], ip[Y]);
- } else {
- return new LineSegment(ip, ep);
- }
- }
-
- Curve* derivative() const
- {
- double x = finalPoint()[X] - initialPoint()[X];
- return new HLineSegment(x, x, 0);
- }
-
- Point pointAt(double t) const
- {
+ virtual Point pointAt(Coord t) const {
if ( t < 0 || t > 1 )
- THROW_RANGEERROR("domain parameter out of range");
- double x = initialPoint()[X] + t * (finalPoint()[X] - initialPoint()[X]);
- return Point(x, initialPoint()[Y]);
+ THROW_RANGEERROR("HLineSegment: Time value out of range");
+ Coord x = initialPoint()[axis] + t * (finalPoint()[axis] - initialPoint()[axis]);
+ Point ret(x, initialPoint()[other_axis]);
+ return ret;
}
-
- double valueAt(double t, Dim2 d) const
- {
- if (d < 0 || d > 1)
- {
- THROW_RANGEERROR("dimension argument out of range");
- }
+ virtual Coord valueAt(Coord t, Dim2 d) const {
if ( t < 0 || t > 1 )
- THROW_RANGEERROR("domain parameter out of range");
-
- if (d == Y) return initialPoint()[Y];
-
- return initialPoint()[X] + t * (finalPoint()[X] - initialPoint()[X]);
+ THROW_RANGEERROR("HLineSegment: Time value out of range");
+ if (d != axis) return initialPoint()[other_axis];
+ return initialPoint()[axis] + t * (finalPoint()[axis] - initialPoint()[axis]);
}
- std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const
- {
+ /**
+ * The size of the returned vector equals n+1.
+ */
+ virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const {
std::vector<Point> result;
result.push_back(pointAt(t));
- if (n > 0)
- {
- double x = finalPoint()[X] - initialPoint()[X];
+ if (n > 0) {
+ Coord x = finalPoint()[axis] - initialPoint()[axis];
result.push_back( Point(x, 0) );
}
- if (n > 1)
- {
+ if (n > 1) {
/* higher order derivatives are zero,
* so the other n-1 vector elements are (0,0) */
result.insert( result.end(), n-1, Point(0, 0) );
}
return result;
}
+protected:
+ AxisLineSegment(Point const &p0, Point const &p1) : LineSegment(p0, p1) {}
+ AxisLineSegment() {}
+};
- D2<SBasis> toSBasis() const
- {
- return m_line_seg.toSBasis();
- }
-
- private:
- LineSegment m_line_seg;
-
-}; // end class HLineSegment
-
-
-class VLineSegment : public Curve
+class HLineSegment : public AxisLineSegment<X>
{
- public:
- VLineSegment()
+public:
+ HLineSegment() {}
+ HLineSegment(Coord x0, Coord x1, Coord y)
+ : AxisLineSegment<X>(Point(x0, y), Point(x1, y))
{}
- VLineSegment(Coord _x, Coord _y0, Coord _y1)
- : m_line_seg(Point(_x, _y0), Point(_x, _y1))
- {
- }
-
- VLineSegment(Point const& _p, double _length)
- : m_line_seg(_p, Point(_p[X], _p[Y] + _length))
- {
- }
+ HLineSegment(Point const& p, Coord len)
+ : AxisLineSegment<X>(p, Point(p[X] + len, p[Y]))
+ {}
- VLineSegment(Point const& _p0, Point const& _p1)
- : m_line_seg(_p0, _p1)
+ HLineSegment(Point const& p0, Point const& p1)
+ : AxisLineSegment<X>(p0, p1)
{
- if ( _p0[X] != _p1[X] )
- {
- THROW_RANGEERROR("VLineSegment::VLineSegment passed points should "
- "have the same X value");
+ if ( p0[Y] != p1[Y] ) {
+ THROW_RANGEERROR("HLineSegment::HLineSegment passed points should "
+ "have the same Y value");
}
}
- Curve* duplicate() const
- {
- return new VLineSegment(*this);
+ Coord getY() { return initialPoint()[Y]; }
+ void setInitialX(Coord x) {
+ LineSegment::setInitial(Point(x, initialPoint()[Y]));
}
-
- bool isDegenerate() const
- {
- return m_line_seg.isDegenerate();
+ void setFinalX(Coord x) {
+ LineSegment::setFinal(Point(x, initialPoint()[Y]));
}
-
- Point initialPoint() const
- {
- return m_line_seg.initialPoint();
- }
-
- Point finalPoint() const
- {
- return m_line_seg.finalPoint();
+ void setY(Coord y) {
+ LineSegment::setInitial( Point(initialPoint()[X], y) );
+ LineSegment::setFinal( Point(finalPoint()[X], y) );
}
-
- Coord getX()
- {
- return initialPoint()[X];
+ std::pair<HLineSegment, HLineSegment> subdivide(Coord t) const {
+ std::pair<HLineSegment, HLineSegment> result;
+ Point p = pointAt(t);
+ result.first.setInitial(initialPoint());
+ result.first.setFinal(p);
+ result.second.setInitial(p);
+ result.second.setFinal(finalPoint());
+ return result;
}
- void setInitial(Point _p)
- {
- m_line_seg.setInitial( Point(initialPoint()[X], _p[Y]) );
+ virtual Curve* duplicate() const { return new HLineSegment(*this); }
+ virtual Curve *portion(Coord f, Coord t) const {
+ Point ip = pointAt(f);
+ Point ep = pointAt(t);
+ return new HLineSegment(ip[X], ep[X], ip[Y]);
}
-
- void setFinal(Point _p)
- {
- m_line_seg.setFinal( Point(finalPoint()[X], _p[Y]) );
+ virtual Curve *reverse() const {
+ Point ip = initialPoint();
+ return new HLineSegment(finalPoint()[X], ip[X], ip[Y]);
}
-
- void setY0(Coord _y)
- {
- m_line_seg.setInitial( Point(initialPoint()[X], _y) );
+ virtual Curve *transformed(Affine const & m) const {
+ Point ip = initialPoint() * m;
+ Point ep = finalPoint() * m;
+ // cannot afford to lose precision here since it can lead to discontinuous paths
+ if (m.isZoom(0)) {
+ return new HLineSegment(ip[X], ep[X], ip[Y]);
+ } else {
+ return new LineSegment(ip, ep);
+ }
}
-
- void setY1(Coord _y)
- {
- m_line_seg.setFinal( Point(finalPoint()[Y], _y) );
+ virtual Curve *derivative() const {
+ Coord x = finalPoint()[X] - initialPoint()[X];
+ return new HLineSegment(x, x, 0);
}
+}; // end class HLineSegment
- void setX(Coord _x)
- {
- m_line_seg.setInitial( Point(_x, initialPoint()[Y]) );
- m_line_seg.setFinal( Point(_x, finalPoint()[Y]) );
- }
- virtual OptRect boundsFast() const
- {
- return boundsExact();
- }
+class VLineSegment : public AxisLineSegment<Y>
+{
+public:
+ VLineSegment() {}
- virtual OptRect boundsExact() const
- {
- return Rect( initialPoint(), finalPoint() );
- }
+ VLineSegment(Coord x, Coord y0, Coord y1)
+ : AxisLineSegment<Y>(Point(x, y0), Point(x, y1))
+ {}
- virtual OptRect boundsLocal(OptInterval i, unsigned deg) const
- {
- return m_line_seg.boundsLocal(i, deg);
- }
+ VLineSegment(Point const& _p, Coord _length)
+ : AxisLineSegment<Y>(_p, Point(_p[X], _p[Y] + _length))
+ {}
- int winding(Point p) const
+ VLineSegment(Point const& _p0, Point const& _p1)
+ : AxisLineSegment<Y>(_p0, _p1)
{
- return m_line_seg.winding(p);
+ if ( _p0[X] != _p1[X] ) {
+ THROW_RANGEERROR("VLineSegment::VLineSegment passed points should "
+ "have the same X value");
+ }
}
-
- int degreesOfFreedom() const { return 3;}
- std::vector<double>
- roots(double v, Dim2 d) const
- {
- if (d < 0 || d > 1)
- {
- THROW_RANGEERROR("dimension argument out of range");
- }
- std::vector<double> result;
- if (d == Y)
- {
- if ( v >= initialPoint()[Y] && v <= finalPoint()[Y] )
- {
- double t = 0;
- if (!isDegenerate())
- t = (v - initialPoint()[Y]) / (finalPoint()[Y] - initialPoint()[Y]);
- result.push_back(t);
- }
- }
- else
- {
- if (v == initialPoint()[X])
- {
- if (!isDegenerate())
- THROW_INFINITESOLUTIONS(0);
- result.push_back(0);
- }
- }
- return result;
+ Coord getX() { return initialPoint()[X]; }
+ void setInitialY(Coord _y) {
+ LineSegment::setInitial( Point(initialPoint()[X], _y) );
}
-
- double nearestPoint( Point const& p, double from = 0, double to = 1 ) const
- {
- if ( from > to ) std::swap(from, to);
- double yfrom = pointAt(from)[Y];
- double yto = pointAt(to)[Y];
- if (yfrom > yto)
- {
- std::swap(yfrom, yto);
- std::swap(from, to);
- }
- if ( p[Y] > yfrom && p[Y] < yto )
- {
- return (p[Y] - initialPoint()[Y]) / (finalPoint()[Y] - initialPoint()[Y]);
- }
- else if ( p[Y] <= yfrom )
- return from;
- else
- return to;
+ void setFinalY(Coord _y) {
+ LineSegment::setFinal( Point(finalPoint()[Y], _y) );
}
-
- std::pair<VLineSegment, VLineSegment> subdivide(Coord t) const
- {
+ void setX(Coord _x) {
+ LineSegment::setInitial( Point(_x, initialPoint()[Y]) );
+ LineSegment::setFinal( Point(_x, finalPoint()[Y]) );
+ }
+ std::pair<VLineSegment, VLineSegment> subdivide(Coord t) const {
std::pair<VLineSegment, VLineSegment> result;
Point p = pointAt(t);
result.first.setInitial(initialPoint());
@@ -440,88 +241,31 @@ class VLineSegment : public Curve
return result;
}
- Curve* portion(double f, double t) const
- {
+ virtual Curve *duplicate() const { return new VLineSegment(*this); }
+ virtual Curve *portion(Coord f, Coord t) const {
Point ip = pointAt(f);
- Point ep = pointAt(t);
- return new VLineSegment(ip[X], ip[Y], ep[Y]);
+ Coord epy = valueAt(t, Y);
+ return new VLineSegment(ip[X], ip[Y], epy);
}
-
- Curve* reverse() const
- {
- return
- new VLineSegment(initialPoint()[X], finalPoint()[Y], initialPoint()[Y]);
+ virtual Curve *reverse() const {
+ Point ip = initialPoint();
+ return new VLineSegment(ip[X], finalPoint()[Y], ip[Y]);
}
-
- Curve* transformed(Matrix const & m) const
- {
+ virtual Curve *transformed(Affine const & m) const {
Point ip = initialPoint() * m;
Point ep = finalPoint() * m;
- if (ip[X] == ep[X]) {
+ if (m.isZoom()) {
return new VLineSegment(ip[X], ip[Y], ep[Y]);
} else {
return new LineSegment(ip, ep);
}
}
-
- Curve* derivative() const
- {
- double y = finalPoint()[Y] - initialPoint()[Y];
+ virtual Curve* derivative() const {
+ Coord y = finalPoint()[Y] - initialPoint()[Y];
return new VLineSegment(0, y, y);
}
-
- Point pointAt(double t) const
- {
- if ( t < 0 || t > 1 )
- THROW_RANGEERROR("domain parameter out of range");
- double y = initialPoint()[Y] + t * (finalPoint()[Y] - initialPoint()[Y]);
- return Point(initialPoint()[X], y);
- }
-
- double valueAt(double t, Dim2 d) const
- {
- if (d < 0 || d > 1)
- {
- THROW_RANGEERROR("dimension argument out of range");
- }
- if ( t < 0 || t > 1 )
- THROW_RANGEERROR("domain parameter out of range");
-
- if (d == X) return initialPoint()[X];
-
- return initialPoint()[Y] + t * (finalPoint()[Y] - initialPoint()[Y]);
- }
-
- std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const
- {
- std::vector<Point> result;
- result.push_back(pointAt(t));
- if (n > 0)
- {
- double y = finalPoint()[Y] - initialPoint()[Y];
- result.push_back( Point(0, y) );
- }
- if (n > 1)
- {
- /* higher order derivatives are zero,
- * so the other n-1 vector elements are (0,0) */
- result.insert( result.end(), n-1, Point(0, 0) );
- }
- return result;
- }
-
- D2<SBasis> toSBasis() const
- {
- return m_line_seg.toSBasis();
- }
-
- private:
- LineSegment m_line_seg;
-
}; // end class VLineSegment
-
-
} // end namespace Geom
diff --git a/src/2geom/interval.h b/src/2geom/interval.h
index 68a406318..a790a6c3b 100644
--- a/src/2geom/interval.h
+++ b/src/2geom/interval.h
@@ -1,7 +1,7 @@
/**
* \file
* \brief Simple closed interval class
- *
+ *//*
* Copyright 2007 Michael Sloan <mgsloan@gmail.com>
*
* Original Rect/Range code by:
@@ -34,216 +34,295 @@
* the specific language governing rights and limitations.
*
*/
-#ifndef SEEN_INTERVAL_H
-#define SEEN_INTERVAL_H
+#ifndef LIB2GEOM_SEEN_INTERVAL_H
+#define LIB2GEOM_SEEN_INTERVAL_H
#include <assert.h>
+#include <boost/none.hpp>
+#include <boost/optional.hpp>
+#include <boost/operators.hpp>
#include <2geom/coord.h>
-
-#include <boost/optional/optional.hpp>
+#include <2geom/isnan.h>
namespace Geom {
-class Interval;
+class OptInterval;
/**
- * \brief This class represents a range of numbers that is never empty.
+ * @brief Range of 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.
*
- * The endpoints are included in the range.
+ * @ingroup Primitives
*/
-class Interval {
+class Interval
+ : boost::equality_comparable< Interval
+ , boost::additive< Interval
+ , boost::multipliable< Interval
+ , boost::arithmetic< Interval, Coord
+ , boost::orable< Interval
+ > > > > >
+{
private:
+ /// @invariant _b[0] <= _b[1]
Coord _b[2];
public:
- /// The default constructor creates an interval [0,0] DO NOT RELY ON THIS, BEST NOT TO USE THIS CONSTRUCTOR
+ /// @name Create intervals.
+ /// @{
+ /** @brief Create an interval that contains only zero. */
explicit Interval() { _b[0] = 0; _b[1] = 0; }
+ /** @brief Create an interval that contains a single point. */
explicit Interval(Coord u) { _b[0] = _b[1] = u; }
- /* When creating an Interval using the constructor specifying the exact range, the created interval
- * will be [u,v] when u<=v ; and will be [v,u] when v < u !!!
- */
+ /** @brief Create an interval that contains all points between @c u and @c v. */
Interval(Coord u, Coord v) {
- if(u < v) {
+ if (u <= v) {
_b[0] = u; _b[1] = v;
} else {
_b[0] = v; _b[1] = u;
}
}
-
- double operator[](unsigned i) const {
- assert(i < 2);
- return _b[i];
- }
- inline double& operator[](unsigned i) { return _b[i]; } //Trust the user...
-
- inline Coord min() const { return _b[0]; }
- inline Coord max() const { return _b[1]; }
- inline Coord extent() const { return _b[1] - _b[0]; }
- inline Coord middle() const { return (_b[1] + _b[0]) * 0.5; }
-
-// inline bool isEmpty() const { return _b[0] > _b[1]; }
- inline bool isSingular() const { return _b[0] == _b[1]; }
- inline bool contains(Coord val) const { return _b[0] <= val && val <= _b[1]; }
- bool contains(const Interval & val) const { return _b[0] <= val._b[0] && val._b[1] <= _b[1]; }
- bool intersects(const Interval & val) const {
- return contains(val._b[0]) || contains(val._b[1]) || val.contains(*this);
- }
-
- inline bool operator==(Interval other) const { return _b[0] == other._b[0] && _b[1] == other._b[1]; }
- inline bool operator!=(Interval other) const { return _b[0] != other._b[0] || _b[1] != other._b[1]; }
-
- //IMPL: OffsetableConcept
- //TODO: rename output_type to something else in the concept
- typedef Coord output_type;
- inline Interval operator+(Coord amnt) {
- return Interval(_b[0] + amnt, _b[1] + amnt);
- }
- inline Interval operator-(Coord amnt) {
- return Interval(_b[0] - amnt, _b[1] - amnt);
+
+ /** @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) {
+ assert(start != end);
+ Interval result(*start++);
+ for (; start != end; ++start) result.expandTo(*start);
+ return result;
}
- inline Interval operator+=(Coord amnt) {
- _b[0] += amnt; _b[1] += amnt;
- return *this;
+ /** @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;
}
- inline Interval operator-=(Coord amnt) {
- _b[0] -= amnt; _b[1] -= amnt;
- return *this;
+ /// @}
+
+ /// @name Inspect endpoints.
+ /// @{
+ Coord operator[](unsigned i) const { return _b[i]; }
+ Coord& operator[](unsigned i) { return _b[i]; }
+
+ Coord min() const { return _b[0]; }
+ Coord max() const { return _b[1]; }
+ Coord extent() const { return _b[1] - _b[0]; }
+ Coord middle() const { return (_b[1] + _b[0]) * 0.5; }
+ bool isSingular() const { return _b[0] == _b[1]; }
+ bool isFinite() const {
+ return IS_FINITE(_b[0]) && IS_FINITE(_b[1]);
}
-
- //IMPL: ScalableConcept
- inline Interval operator-() const { return Interval(*this); }
- inline Interval operator*(Coord s) const { return Interval(_b[0]*s, _b[1]*s); }
- inline Interval operator/(Coord s) const { return Interval(_b[0]/s, _b[1]/s); }
- Interval operator*=(Coord s) {
- if(s < 0) {
- Coord temp = _b[0];
- _b[0] = _b[1]*s;
- _b[1] = temp*s;
- } else {
- _b[0] *= s;
- _b[1] *= s;
- }
- return *this;
+ /// @}
+
+ /// @name Test coordinates and other intervals for inclusion.
+ /// @{
+ /** @brief Check whether the interval includes this number. */
+ bool contains(Coord val) const { return _b[0] <= val && val <= _b[1]; }
+ /** @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 _b[0] < val && val < _b[1]; }
+ /** @brief Check whether the interval includes the given interval. */
+ bool contains(Interval const &val) const { return _b[0] <= val._b[0] && val._b[1] <= _b[1]; }
+ /** @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 _b[0] < val._b[0] && val._b[1] < _b[1]; }
+ /** @brief Check whether the intervals have any common elements. */
+ bool intersects(Interval const &val) const {
+ return contains(val._b[0]) || contains(val._b[1]) || val.contains(*this);
}
- Interval operator/=(Coord s) {
- //TODO: what about s=0?
- if(s < 0) {
- Coord temp = _b[0];
- _b[0] = _b[1]/s;
- _b[1] = temp/s;
- } else {
- _b[0] /= s;
- _b[1] /= s;
- }
- return *this;
+ /** @brief Check whether the interiors of the intervals have any common elements. */
+ bool interiorIntersects(Interval const &val) const {
+ return interiorContains(val._b[0]) || interiorContains(val._b[1]) || val.interiorContains(*this);
}
-
+ /// @}
+
+ /// @name Modify the interval.
+ /// @{
//TODO: NaN handleage for the next two?
- //TODO: Evaluate if wrap behaviour is proper.
- //If val > max, then rather than becoming a min==max range, it 'wraps' over
+ /** @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(Coord val) {
if(val > _b[1]) {
- _b[0] = _b[1];
- _b[1] = val;
+ _b[0] = _b[1] = val;
} else {
_b[0] = val;
}
}
- //If val < min, then rather than becoming a min==max range, it 'wraps' over
+ /** @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(Coord val) {
if(val < _b[0]) {
- _b[1] = _b[0];
- _b[0] = val;
+ _b[1] = _b[0] = val;
} else {
_b[1] = val;
}
}
-
- inline void extendTo(Coord val) {
+ /** @brief Extend the interval to include the given number. */
+ void expandTo(Coord val) {
if(val < _b[0]) _b[0] = val;
if(val > _b[1]) _b[1] = val; //no else, as we want to handle NaN
}
-
- static Interval fromArray(const Coord* c, int n) {
- assert(n > 0);
- Interval result(c[0]);
- for(int i = 1; i < n; i++) result.extendTo(c[i]);
- return result;
- }
-
- /** When this would create an empty interval, the interval will be the centerpoint of the old range only.
- */
- inline void expandBy(double amnt) {
- _b[0] -= amnt;
- _b[1] += amnt;
+ /** @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(double amount) {
+ _b[0] -= amount;
+ _b[1] += amount;
if (_b[0] > _b[1]) {
Coord halfway = (_b[0]+_b[1])/2;
_b[0] = _b[1] = halfway;
}
}
-
- inline void unionWith(const Interval & a) {
+ /** @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(const Interval & a) {
if(a._b[0] < _b[0]) _b[0] = a._b[0];
if(a._b[1] > _b[1]) _b[1] = a._b[1];
}
-};
-
-//IMPL: AddableConcept
-inline Interval operator+(const Interval & a, const Interval & b) {
- return Interval(a.min() + b.min(), a.max() + b.max());
-}
-inline Interval operator-(const Interval & a, const Interval & b) {
- return Interval(a.min() - b.max(), a.max() - b.min());
-}
-inline Interval operator+=(Interval & a, const Interval & b) { a = a + b; return a; }
-inline Interval operator-=(Interval & a, const Interval & b) { a = a - b; return a; }
-
-//There might be impls of this based off sign checks
-inline Interval operator*(const Interval & a, const Interval & b) {
- Interval res(a.min() * b.min());
- res.extendTo(a.min() * b.max());
- res.extendTo(a.max() * b.min());
- res.extendTo(a.max() * b.max());
- return res;
-}
-inline Interval operator*=(Interval & a, const Interval & b) { a = a * b; return a; }
+ /// @}
-/* reinstate if useful (doesn't do the proper thing for 0 inclusion)
-inline Interval operator/(const Interval & a, const Interval & b) {
- Interval res(a.min() / b.min());
- res.extendTo(a.min() / b.max());
- res.extendTo(a.max() / b.min());
- res.extendTo(a.max() / b.max());
- return res;
-}
-inline Interval operator/=(Interval & a, const Interval & b) { a = a / b; return a; }
-*/
+ /// @name Operators
+ /// @{
+ inline operator OptInterval();
+ bool operator==(Interval const &other) const { return _b[0] == other._b[0] && _b[1] == other._b[1]; }
+
+ //IMPL: OffsetableConcept
+ //TODO: rename output_type to something else in the concept
+ typedef Coord output_type;
+ /** @brief Offset the interval by a specified amount */
+ Interval &operator+=(Coord amnt) {
+ _b[0] += amnt; _b[1] += amnt;
+ return *this;
+ }
+ /** @brief Offset the interval by the negation of the specified amount */
+ Interval &operator-=(Coord amnt) {
+ _b[0] -= amnt; _b[1] -= amnt;
+ return *this;
+ }
+
+ // IMPL: ScalableConcept
+ /** @brief Return an interval mirrored about 0 */
+ Interval operator-() const { return Interval(-_b[1], -_b[0]); }
+ /** @brief Scale an interval */
+ Interval &operator*=(Coord s) {
+ _b[0] *= s;
+ _b[1] *= s;
+ if(s < 0) std::swap(_b[0], _b[1]);
+ return *this;
+ }
+ /** @brief Scale an interval by the inverse of the specified value */
+ Interval &operator/=(Coord s) {
+ _b[0] /= s;
+ _b[1] /= s;
+ if(s < 0) std::swap(_b[0], _b[1]);
+ return *this;
+ }
+ // 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$ */
+ Interval &operator+=(Interval 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$ */
+ Interval &operator-=(Interval const &o) {
+ // equal to *this += -o
+ _b[0] -= o._b[1];
+ _b[1] -= o._b[0];
+ 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;
+ }
+ /** @brief Union two intervals.
+ * Note that intersection is only defined for OptIntervals, because the result
+ * of an intersection can be empty, while an Interval cannot. */
+ Interval &operator|=(Interval const &o) {
+ unionWith(o);
+ return *this;
+ }
+ /// @}
+};
-// 'union' conflicts with C keyword
-inline Interval unify(const Interval & a, const Interval & b) {
- return Interval(std::min(a.min(), b.min()),
- std::max(a.max(), b.max()));
+/** @brief Union two intervals
+ * @relates Interval */
+inline Interval unify(Interval const &a, Interval const &b) {
+ return a | b;
}
/**
- * \brief OptInterval is an Interval that can be empty.
+ * @brief A range of numbers that can be empty.
+ * @ingroup Primitives
*/
-class OptInterval : public boost::optional<Interval> {
+class OptInterval
+ : public boost::optional<Interval>
+ , boost::orable< OptInterval
+ , boost::andable< OptInterval
+ > >
+{
public:
+ /// @name Create optionally empty intervals.
+ /// @{
+ /** @brief Create an empty interval. */
OptInterval() : boost::optional<Interval>() {};
+ /** @brief Wrap an existing interval. */
OptInterval(Interval const &a) : boost::optional<Interval>(a) {};
+ /** @brief Create an interval containing a single point. */
OptInterval(Coord u) : boost::optional<Interval>(Interval(u)) {};
+ /** @brief Create an interval containing a range of numbers. */
OptInterval(Coord u, Coord v) : boost::optional<Interval>(Interval(u,v)) {};
- /**
- * Check whether this OptInterval is empty or not.
- */
- inline bool isEmpty() { return (*this == false); };
-
- /**
- * If \c this is empty, copy argument \c a. Otherwise, union with it (and do nothing when \c a is empty)
- */
- inline void unionWith(const OptInterval & a) {
+ /** @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 double. 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 OptInterval from_range(InputIterator start, InputIterator end) {
+ if (start == end) {
+ OptInterval ret;
+ return ret;
+ }
+ OptInterval ret(Interval::from_range(start, end));
+ return ret;
+ }
+ /// @}
+
+ /** @brief Check whether this OptInterval is empty. */
+ bool isEmpty() { return !*this; };
+
+ /** @brief Union with another interval, gracefully handling empty ones. */
+ inline void unionWith(OptInterval const &a) {
if (a) {
if (*this) { // check that we are not empty
(*this)->unionWith(*a);
@@ -252,14 +331,41 @@ public:
}
}
}
+ inline void intersectWith(OptInterval const &o) {
+ if (o && *this) {
+ Coord u, v;
+ u = std::max((*this)->min(), o->min());
+ v = std::min((*this)->max(), o->max());
+ if (u <= v) {
+ *this = Interval(u, v);
+ return;
+ }
+ }
+ (*static_cast<boost::optional<Interval>*>(this)) = boost::none;
+ }
+ OptInterval &operator|=(OptInterval const &o) {
+ unionWith(o);
+ return *this;
+ }
+ OptInterval &operator&=(OptInterval const &o) {
+ intersectWith(o);
+ return *this;
+ }
};
-inline OptInterval intersect(const Interval & a, const Interval & b) {
- Coord u = std::max(a.min(), b.min()),
- v = std::min(a.max(), b.max());
- //technically >= might be incorrect, but singulars suck
- return u > v ? OptInterval()
- : OptInterval(Interval(u, v));
+/** @brief Intersect two intervals and return a possibly empty range of numbers
+ * @relates OptInterval */
+inline OptInterval intersect(Interval const &a, Interval const &b) {
+ return OptInterval(a) & OptInterval(b);
+}
+/** @brief Intersect two intervals and return a possibly empty range of numbers
+ * @relates OptInterval */
+inline OptInterval operator&(Interval const &a, Interval const &b) {
+ return OptInterval(a) & OptInterval(b);
+}
+
+inline Interval::operator OptInterval() {
+ return OptInterval(*this);
}
#ifdef _GLIBCXX_IOSTREAM
diff --git a/src/2geom/line.cpp b/src/2geom/line.cpp
index a91ff03a3..5ef8dfddc 100644
--- a/src/2geom/line.cpp
+++ b/src/2geom/line.cpp
@@ -37,6 +37,90 @@
namespace Geom
{
+/**
+ * @class Line
+ * @brief Infinite line on a plane.
+ *
+ * Every line in 2Geom has a special point on it, called the origin. The direction of the line
+ * is stored as a unit vector (versor). This way a line can be interpreted as a function
+ * \f$ f: (-\infty, \infty) \to \mathbb{R}^2\f$. Zero corresponds to the origin point,
+ * positive values to the points in the direction of the unit vector, and negative values
+ * to points in the opposite direction.
+ *
+ * @ingroup Primitives
+ */
+
+/** @brief Set the line by solving the line equation.
+ * A line is a set of points that satisfies the line equation
+ * \f$Ax + By + C = 0\f$. This function changes the line so that its points
+ * satisfy the line equation with the given coefficients. */
+void Line::setCoefficients (double a, double b, double c) {
+ if (a == 0 && b == 0) {
+ if (c != 0) {
+ THROW_LOGICALERROR("the passed coefficients gives the empty set");
+ }
+ m_versor = Point(0,0);
+ m_origin = Point(0,0);
+ } else {
+ double l = hypot(a,b);
+ a /= l;
+ b /= l;
+ c /= l;
+ Point N(a, b);
+ m_versor = N.ccw();
+ m_origin = -c * N;
+ }
+}
+
+/** @brief Get the line equation coefficients of this line.
+ * @return Vector with three values corresponding to the A, B and C
+ * coefficients of the line equation for this line. */
+std::vector<double> Line::coefficients() const {
+ std::vector<double> coeff;
+ coeff.reserve(3);
+ Point N = versor().cw();
+ coeff.push_back (N[X]);
+ coeff.push_back (N[Y]);
+ double d = - dot (N, origin());
+ coeff.push_back (d);
+ return coeff;
+}
+
+/** @brief Find intersection with an axis-aligned line.
+ * @param v Coordinate of the axis-aligned line
+ * @param d Which axis the coordinate is on. X means a vertical line, Y means a horizontal line.
+ * @return Time values at which this line intersects the query line. */
+std::vector<Coord> Line::roots(Coord v, Dim2 d) const {
+ if (d < 0 || d > 1)
+ THROW_RANGEERROR("Line::roots, dimension argument out of range");
+ std::vector<Coord> result;
+ if ( m_versor[d] != 0 )
+ {
+ result.push_back( (v - m_origin[d]) / m_versor[d] );
+ }
+ // TODO: else ?
+ return result;
+}
+
+/** @brief Get a time value corresponding to a point.
+ * @param p Point on the line. If the point is not on the line,
+ * the returned value will be meaningless.
+ * @return Time value t such that \f$f(t) = p\f$.
+ * @see timeAtProjection */
+Coord Line::timeAt(Point const& _point) const {
+ Coord t;
+ if ( m_versor[X] != 0 ) {
+ t = (_point[X] - m_origin[X]) / m_versor[X];
+ }
+ else if ( m_versor[Y] != 0 ) {
+ t = (_point[Y] - m_origin[Y]) / m_versor[Y];
+ }
+ else { // degenerate case
+ t = 0;
+ }
+ return t;
+}
+
namespace detail
{
@@ -55,18 +139,18 @@ OptCrossing intersection_impl(Point const& V1, Point const O1,
Crossing c;
c.ta = detBV2 * inv_detV1V2;
c.tb = detV1B * inv_detV1V2;
-// std::cerr << "ta = " << solution.ta << std::endl;
-// std::cerr << "tb = " << solution.tb << std::endl;
+// std::cerr << "ta = " << c.ta << std::endl;
+// std::cerr << "tb = " << c.tb << std::endl;
return OptCrossing(c);
}
OptCrossing intersection_impl(Ray const& r1, Line const& l2, unsigned int i)
{
- OptCrossing crossing =
+ OptCrossing crossing =
intersection_impl(r1.versor(), r1.origin(),
l2.versor(), l2.origin() );
-
+
if (crossing)
{
if (crossing->ta < 0)
@@ -97,7 +181,7 @@ OptCrossing intersection_impl( LineSegment const& ls1,
Line const& l2,
unsigned int i )
{
- OptCrossing crossing =
+ OptCrossing crossing =
intersection_impl(ls1.finalPoint() - ls1.initialPoint(),
ls1.initialPoint(),
l2.versor(),
@@ -135,7 +219,7 @@ OptCrossing intersection_impl( LineSegment const& ls1,
unsigned int i )
{
Point direction = ls1.finalPoint() - ls1.initialPoint();
- OptCrossing crossing =
+ OptCrossing crossing =
intersection_impl( direction,
ls1.initialPoint(),
r2.versor(),
@@ -143,9 +227,9 @@ OptCrossing intersection_impl( LineSegment const& ls1,
if (crossing)
{
- if ( crossing->getTime(0) < 0
- || crossing->getTime(0) > 1
- || crossing->getTime(1) < 0 )
+ if ( (crossing->getTime(0) < 0)
+ || (crossing->getTime(0) > 1)
+ || (crossing->getTime(1) < 0) )
{
return OptCrossing();
}
@@ -203,7 +287,7 @@ OptCrossing intersection_impl( LineSegment const& ls1,
OptCrossing intersection(Line const& l1, Line const& l2)
{
- OptCrossing crossing =
+ OptCrossing crossing =
detail::intersection_impl( l1.versor(), l1.origin(),
l2.versor(), l2.origin() );
if (crossing)
@@ -223,7 +307,7 @@ OptCrossing intersection(Line const& l1, Line const& l2)
OptCrossing intersection(Ray const& r1, Ray const& r2)
{
- OptCrossing crossing =
+ OptCrossing crossing =
detail::intersection_impl( r1.versor(), r1.origin(),
r2.versor(), r2.origin() );
@@ -333,6 +417,62 @@ OptCrossing intersection( LineSegment const& ls1, LineSegment const& ls2 )
}
+boost::optional<LineSegment> clip (Line const& l, Rect const& r)
+{
+ typedef boost::optional<LineSegment> opt_linesegment;
+ LineSegment result;
+ //size_t index = 0;
+ std::vector<Point> points;
+ LineSegment ls (r.corner(0), r.corner(1));
+ try
+ {
+ OptCrossing oc = intersection (ls, l);
+ if (oc)
+ {
+ points.push_back (l.pointAt (oc->tb));
+ }
+ }
+ catch (InfiniteSolutions e)
+ {
+ return opt_linesegment(ls);
+ }
+
+ for (size_t i = 2; i < 5; ++i)
+ {
+ ls.setInitial (ls[1]);
+ ls.setFinal (r.corner(i));
+ try
+ {
+ OptCrossing oc = intersection (ls, l);
+ if (oc)
+ {
+ points.push_back (l.pointAt (oc->tb));
+ if (points.size() > 1)
+ {
+ size_t sz = points.size();
+ if (!are_near (points[sz - 2], points[sz - 1], 1e-10))
+ {
+ result.setInitial (points[sz - 2]);
+ result.setFinal (points[sz - 1]);
+ return opt_linesegment(result);
+ }
+ }
+ }
+ }
+ catch (InfiniteSolutions e)
+ {
+ return opt_linesegment(ls);
+ }
+ }
+ if (points.size() != 0)
+ {
+ result.setInitial (points[0]);
+ result.setFinal (points[0]);
+ return opt_linesegment(result);
+ }
+ return opt_linesegment();
+}
+
Line make_angle_bisector_line(Line const& l1, Line const& l2)
{
@@ -359,6 +499,8 @@ Line make_angle_bisector_line(Line const& l1, Line const& l2)
}
+
+
} // end namespace Geom
diff --git a/src/2geom/line.h b/src/2geom/line.h
index a7e6a54bb..ccb0ae6c5 100644
--- a/src/2geom/line.h
+++ b/src/2geom/line.h
@@ -35,265 +35,278 @@
#include <cmath>
#include <2geom/bezier-curve.h> // for LineSegment
+#include <2geom/rect.h>
#include <2geom/crossing.h>
#include <2geom/exception.h>
#include <2geom/ray.h>
+#include <boost/optional.hpp>
+
namespace Geom
{
-class Line
-{
- public:
- Line()
- : m_origin(0,0), m_versor(1,0)
- {
- }
-
- Line(Point const& _origin, Coord angle )
- : m_origin(_origin), m_versor(std::cos(angle), std::sin(angle))
- {
- }
-
- Line(Point const& A, Point const& B)
- {
- setBy2Points(A, B);
- }
-
- explicit
- Line(LineSegment const& _segment)
- {
- setBy2Points(_segment.initialPoint(), _segment.finalPoint());
- }
-
- explicit
- Line(Ray const& _ray)
- : m_origin(_ray.origin()), m_versor(_ray.versor())
- {
- }
-
- static Line fromNormalDistance(Point n, double c) {
- Point P = n*c/(dot(n,n));
-
- return Line(P, P+rot90(n));
+class Line {
+private:
+ Point m_origin;
+ Point m_versor;
+public:
+ /// @name Creating lines.
+ /// @{
+ /** @brief Create a default horizontal line. */
+ Line()
+ : m_origin(0,0), m_versor(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 )
+ : m_origin(_origin)
+ {
+ sincos(angle, m_versor[Y], m_versor[X]);
+ }
+
+ /** @brief Create a line going through two points.
+ * @param A First point
+ * @param B Second point */
+ Line(Point const& A, Point const& B) {
+ setPoints(A, B);
}
- static Line fromPointDirection(Point o, Point v) {
+
+ /** @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);
+ }
+
+ /** @brief Create a line by extending a line segment. */
+ explicit Line(LineSegment const& _segment) {
+ setPoints(_segment.initialPoint(), _segment.finalPoint());
+ }
+
+ /** @brief Create a line by extending a ray. */
+ explicit Line(Ray const& _ray)
+ : m_origin(_ray.origin()), m_versor(_ray.versor())
+ {}
+
+ // huh?
+ static Line from_normal_distance(Point n, double c) {
+ Point P = n * c / dot(n,n);
+ Line l(P, P+rot90(n));
+ return l;
+ }
+ /** @brief Create a line from origin and unit vector.
+ * Note that each line direction has two possible unit vectors.
+ * @param o Point through which the line will pass
+ * @param v Unit vector of the line's direction */
+ static Line from_origin_and_versor(Point o, Point v) {
Line l;
l.m_origin = o;
l.m_versor = v;
return l;
}
- Line* duplicate() const
- {
- return new Line(*this);
- }
-
- Point origin() const
- {
- return m_origin;
- }
-
- Point versor() const
- {
- return m_versor;
- }
-
- void origin(Point const& _point)
- {
- m_origin = _point;
- }
-
- void versor(Point const& _versor)
- {
- m_versor = _versor;
- }
-
- // return the angle described by rotating the X-axis in cw direction
- // until it overlaps the line
- // the returned value is in the interval [0, PI[
- Coord angle() const
- {
- double a = std::atan2(m_versor[Y], m_versor[X]);
- if (a < 0) a += M_PI;
- if (a == M_PI) a = 0;
- return a;
- }
-
- void angle(Coord _angle)
- {
- m_versor[X] = std::cos(_angle);
- m_versor[Y] = std::sin(_angle);
- }
-
- void setBy2Points(Point const& A, Point const& B)
- {
- m_origin = A;
- m_versor = B - A;
- if ( are_near(m_versor, Point(0,0)) )
- m_versor = Point(0,0);
- else
- m_versor.normalize();
- }
-
- bool isDegenerate() const
- {
- return ( m_versor[X] == 0 && m_versor[Y] == 0 );
- }
-
- Point pointAt(Coord t) const
- {
- return m_origin + m_versor * t;
- }
-
- Coord valueAt(Coord t, Dim2 d) const
- {
- if (d < 0 || d > 1)
- THROW_RANGEERROR("Ray::valueAt, dimension argument out of range");
- return m_origin[d] + m_versor[d] * t;
- }
-
- std::vector<Coord> roots(Coord v, Dim2 d) const
- {
- if (d < 0 || d > 1)
- THROW_RANGEERROR("Ray::roots, dimension argument out of range");
- std::vector<Coord> result;
- if ( m_versor[d] != 0 )
- {
- result.push_back( (v - m_origin[d]) / m_versor[d] );
- }
- // TODO: else ?
- return result;
- }
-
- // require are_near(_point, *this)
- // on the contrary the result value is meaningless
- Coord timeAt(Point const& _point) const
- {
- Coord t;
- if ( m_versor[X] != 0 )
- {
- t = (_point[X] - m_origin[X]) / m_versor[X];
- }
- else if ( m_versor[Y] != 0 )
- {
- t = (_point[Y] - m_origin[Y]) / m_versor[Y];
- }
- else // degenerate case
- {
- t = 0;
- }
- return t;
- }
-
- Coord timeAtProjection(Point const& _point) const
- {
- if ( isDegenerate() ) return 0;
- return dot( _point - m_origin, m_versor );
- }
-
- Coord nearestPoint(Point const& _point) const
- {
- return timeAtProjection(_point);
- }
-
- Line reverse() const
- {
- Line result;
- result.origin(m_origin);
- result.versor(-m_versor);
- return result;
- }
-
- Curve* portion(Coord f, Coord t) const
- {
- LineSegment* seg = new LineSegment(pointAt(f), pointAt(t));
- return seg;
- }
-
- LineSegment segment(Coord f, Coord t) const
- {
- return LineSegment(pointAt(f), pointAt(t));
- }
-
- Ray ray(Coord t)
- {
- Ray result;
- result.origin(pointAt(t));
- result.versor(m_versor);
- return result;
- }
-
- Line derivative() const
- {
- Line result;
- result.origin(m_versor);
- result.versor(Point(0,0));
- return result;
- }
-
- Line transformed(Matrix const& m) const
- {
- return Line(m_origin * m, (m_origin + m_versor) * m);
- }
-
- static Line from_normal_and_dist(Point const &n, double d) {
- return Line(n*d, n*d + rot90(n));
+ Line* duplicate() const {
+ return new Line(*this);
+ }
+ /// @}
+
+ /// @name Retrieve and set the line's parameters.
+ /// @{
+ /** @brief Get the line's origin point. */
+ Point origin() const { return m_origin; }
+ /** @brief Get the line's direction unit vector. */
+ Point versor() const { return m_versor; }
+ // return the angle described by rotating the X-axis in cw direction
+ // until it overlaps the line
+ // the returned value is in the interval [0, PI[
+ Coord angle() const {
+ double a = std::atan2(m_versor[Y], m_versor[X]);
+ if (a < 0) a += M_PI;
+ if (a == M_PI) a = 0;
+ return a;
}
- private:
- Point m_origin;
- Point m_versor;
+ void setOrigin(Point const& _point) {
+ m_origin = _point;
+ }
+ void setVersor(Point const& _versor) {
+ m_versor = _versor;
+ }
+
+ void setAngle(Coord _angle) {
+ sincos(_angle, m_versor[Y], m_versor[X]);
+ }
+
+ /** @brief Set a line based on two points it should pass through. */
+ void setPoints(Point const& A, Point const& B) {
+ m_origin = A;
+ if ( are_near(A, B) )
+ m_versor = Point(0,0);
+ else
+ m_versor = B - A;
+ m_versor.normalize();
+ }
+ void setCoefficients (double a, double b, double c);
+ std::vector<double> coefficients() const;
+
+ /** @brief Check if the line has any points.
+ * 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 */
+ bool isDegenerate() const {
+ return ( m_versor[X] == 0 && m_versor[Y] == 0 );
+ }
+ /// @}
+
+ /// @name Evaluate the line as a function.
+ ///@{
+ Point pointAt(Coord t) const {
+ return m_origin + m_versor * t;
+ }
+
+ Coord valueAt(Coord t, Dim2 d) const {
+ if (d < 0 || d > 1)
+ THROW_RANGEERROR("Line::valueAt, dimension argument out of range");
+ return m_origin[d] + m_versor[d] * t;
+ }
+
+ 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;
+ return dot( p - m_origin, m_versor );
+ }
+
+ /** @brief Find a point on the line closest to the query point.
+ * This is an alias for timeAtProjection(). */
+ Coord nearestPoint(Point const& _point) const {
+ return timeAtProjection(_point);
+ }
+
+ std::vector<Coord> roots(Coord v, Dim2 d) const;
+ /// @}
+
+ /// @name Create other objects based on this line.
+ /// @{
+ /** @brief Create a line containing the same points, but with negated time values.
+ * @return Line \f$g\f$ such that \f$g(t) = f(-t)\f$ */
+ Line reverse() const
+ {
+ Line result;
+ result.setOrigin(m_origin);
+ result.setVersor(-m_versor);
+ 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));
+ }
+
+ /** @brief Create a ray starting at the specified time value.
+ * The created ray will go in the direction of the line's versor (in the direction
+ * of increasing time values).
+ * @param t Time value where the ray should start
+ * @return Ray starting at t and going in the direction of the versor */
+ Ray ray(Coord t) {
+ Ray result;
+ result.origin(pointAt(t));
+ result.versor(m_versor);
+ 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 versor. */
+ Line derivative() const {
+ Line result;
+ result.setOrigin(m_versor);
+ result.setVersor(Point(0,0));
+ return result;
+ }
+
+ /** @brief Create a line transformed by an affine transformation. */
+ Line transformed(Affine const& m) const {
+ return Line(m_origin * m, (m_origin + m_versor) * m);
+ }
+
+ /** @brief Get a vector normal to the line.
+ * If Y grows upwards, then this is the left normal. If Y grows downwards,
+ * then this is the right normal. */
+ Point normal() const {
+ return rot90(m_versor);
+ }
+
+ // what does this do?
+ Point normalAndDist(double & dist) const {
+ Point n = normal();
+ dist = -dot(n, m_origin);
+ return n;
+ }
+ /// @}
}; // end class Line
inline
double distance(Point const& _point, Line const& _line)
{
- if ( _line.isDegenerate() )
- {
- return distance( _point, _line.origin() );
- }
- else
- {
- return fabs( dot(_point - _line.origin(), _line.versor().ccw()) );
- }
+ if ( _line.isDegenerate() )
+ {
+ return ::Geom::distance( _point, _line.origin() );
+ }
+ else
+ {
+ return fabs( dot(_point - _line.origin(), _line.versor().ccw()) );
+ }
}
inline
bool are_near(Point const& _point, Line const& _line, double eps = EPSILON)
{
- return are_near(distance(_point, _line), 0, eps);
+ return are_near(distance(_point, _line), 0, eps);
}
inline
bool are_parallel(Line const& l1, Line const& l2, double eps = EPSILON)
{
- return ( are_near(l1.versor(), l2.versor(), eps)
- || are_near(l1.versor(), -l2.versor(), eps) );
+ return ( are_near(l1.versor(), l2.versor(), eps)
+ || are_near(l1.versor(), -l2.versor(), eps) );
}
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);
+ return are_parallel(l1, l2, eps) && are_near(l1.origin(), l2, eps);
}
inline
bool are_orthogonal(Line const& l1, Line const& l2, double eps = EPSILON)
{
- return ( are_near(l1.versor(), l2.versor().cw(), eps)
- || are_near(l1.versor(), l2.versor().ccw(), eps) );
+ return ( are_near(l1.versor(), l2.versor().cw(), eps)
+ || are_near(l1.versor(), l2.versor().ccw(), eps) );
}
inline
bool are_collinear(Point const& p1, Point const& p2, Point const& p3,
- double eps = EPSILON)
+ double eps = EPSILON)
{
- return are_near( cross(p3, p2) - cross(p3, p1) + cross(p2, p1), 0, eps);
+ return are_near( cross(p3, p2) - cross(p3, p1) + cross(p2, p1), 0, eps);
}
// evaluate the angle between l1 and l2 rotating l1 in cw direction
@@ -302,10 +315,10 @@ bool are_collinear(Point const& p1, Point const& p2, Point const& p3,
inline
double angle_between(Line const& l1, Line const& l2)
{
- double angle = angle_between(l1.versor(), l2.versor());
- if (angle < 0) angle += M_PI;
- if (angle == M_PI) angle = 0;
- return angle;
+ double angle = angle_between(l1.versor(), l2.versor());
+ if (angle < 0) angle += M_PI;
+ if (angle == M_PI) angle = 0;
+ return angle;
}
inline
@@ -317,60 +330,60 @@ double distance(Point const& _point, LineSegment const& _segment)
inline
bool are_near(Point const& _point, LineSegment const& _segment,
- double eps = EPSILON)
+ double eps = EPSILON)
{
- return are_near(distance(_point, _segment), 0, eps);
+ return are_near(distance(_point, _segment), 0, eps);
}
// build a line passing by _point and orthogonal to _line
inline
Line make_orthogonal_line(Point const& _point, Line const& _line)
{
- Line l;
- l.origin(_point);
- l.versor(_line.versor().cw());
- return l;
+ Line l;
+ l.setOrigin(_point);
+ l.setVersor(_line.versor().cw());
+ return l;
}
// build a line passing by _point and parallel to _line
inline
Line make_parallel_line(Point const& _point, Line const& _line)
{
- Line l(_line);
- l.origin(_point);
- return l;
+ Line l(_line);
+ l.setOrigin(_point);
+ return l;
}
// 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) );
+ 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)
{
- Point M = middle_point(A,B);
- return Line(O,M);
+ Point M = middle_point(A,B);
+ return Line(O,M);
}
// prj(P) = rot(v, Point( rot(-v, P-O)[X], 0 )) + O
inline
Point projection(Point const& _point, Line const& _line)
{
- return _line.pointAt( _line.nearestPoint(_point) );
+ return _line.pointAt( _line.nearestPoint(_point) );
}
inline
LineSegment projection(LineSegment const& _segment, Line const& _line)
{
- return _line.segment( _line.nearestPoint(_segment.initialPoint()),
- _line.nearestPoint(_segment.finalPoint()) );
+ return _line.segment( _line.nearestPoint(_segment.initialPoint()),
+ _line.nearestPoint(_segment.finalPoint()) );
}
-
+boost::optional<LineSegment> clip (Line const& l, Rect const& r);
namespace detail
diff --git a/src/2geom/matrix.cpp b/src/2geom/matrix.cpp
deleted file mode 100644
index e130d2027..000000000
--- a/src/2geom/matrix.cpp
+++ /dev/null
@@ -1,258 +0,0 @@
-#define __Geom_MATRIX_C__
-
-/** \file
- * Various matrix routines. Currently includes some Geom::Rotate etc. routines too.
- */
-
-/*
- * Authors:
- * Lauris Kaplinski <lauris@kaplinski.com>
- * Michael G. Sloan <mgsloan@gmail.com>
- *
- * This code is in public domain
- */
-
-#include <2geom/utils.h>
-#include <2geom/matrix.h>
-#include <2geom/point.h>
-
-namespace Geom {
-
-/** Creates a Matrix given an axis and origin point.
- * The axis is represented as two vectors, which represent skew, rotation, and scaling in two dimensions.
- * from_basis(Point(1, 0), Point(0, 1), Point(0, 0)) would return the identity matrix.
-
- \param x_basis the vector for the x-axis.
- \param y_basis the vector for the y-axis.
- \param offset the translation applied by the matrix.
- \return The new Matrix.
- */
-//NOTE: Inkscape's version is broken, so when including this version, you'll have to search for code with this func
-//TODO: move to Matrix::from_basis
-Matrix from_basis(Point const x_basis, Point const y_basis, Point const offset) {
- return Matrix(x_basis[X], x_basis[Y],
- y_basis[X], y_basis[Y],
- offset [X], offset [Y]);
-}
-
-Point Matrix::xAxis() const {
- return Point(_c[0], _c[1]);
-}
-
-Point Matrix::yAxis() const {
- return Point(_c[2], _c[3]);
-}
-
-/** Gets the translation imparted by the Matrix.
- */
-Point Matrix::translation() const {
- return Point(_c[4], _c[5]);
-}
-
-void Matrix::setXAxis(Point const &vec) {
- for(int i = 0; i < 2; i++)
- _c[i] = vec[i];
-}
-
-void Matrix::setYAxis(Point const &vec) {
- for(int i = 0; i < 2; i++)
- _c[i + 2] = vec[i];
-}
-
-/** Sets the translation imparted by the Matrix.
- */
-void Matrix::setTranslation(Point const &loc) {
- for(int i = 0; i < 2; i++)
- _c[i + 4] = loc[i];
-}
-
-/** Calculates the amount of x-scaling imparted by the Matrix. This is the scaling applied to
- * the original x-axis region. It is \emph{not} the overall x-scaling of the transformation.
- * Equivalent to L2(m.xAxis())
- */
-double Matrix::expansionX() const {
- return sqrt(_c[0] * _c[0] + _c[1] * _c[1]);
-}
-
-/** Calculates the amount of y-scaling imparted by the Matrix. This is the scaling applied before
- * the other transformations. It is \emph{not} the overall y-scaling of the transformation.
- * Equivalent to L2(m.yAxis())
- */
-double Matrix::expansionY() const {
- return sqrt(_c[2] * _c[2] + _c[3] * _c[3]);
-}
-
-void Matrix::setExpansionX(double val) {
- double exp_x = expansionX();
- if(!are_near(exp_x, 0.0)) { //TODO: best way to deal with it is to skip op?
- double coef = val / expansionX();
- for(unsigned i=0;i<2;i++) _c[i] *= coef;
- }
-}
-
-void Matrix::setExpansionY(double val) {
- double exp_y = expansionY();
- if(!are_near(exp_y, 0.0)) { //TODO: best way to deal with it is to skip op?
- double coef = val / expansionY();
- for(unsigned i=2; i<4; i++) _c[i] *= coef;
- }
-}
-
-/** Sets this matrix to be the Identity Matrix. */
-void Matrix::setIdentity() {
- _c[0] = 1.0; _c[1] = 0.0;
- _c[2] = 0.0; _c[3] = 1.0;
- _c[4] = 0.0; _c[5] = 0.0;
-}
-
-//TODO: use eps
-
-bool Matrix::isIdentity(Coord const eps) const {
- return are_near(_c[0], 1.0, eps) && are_near(_c[1], 0.0, eps) &&
- are_near(_c[2], 0.0, eps) && are_near(_c[3], 1.0, eps) &&
- are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
-}
-
-/** Answers the question "Does this matrix perform a translation, and \em{only} a translation?"
- \param eps an epsilon value defaulting to EPSILON
- \return A bool representing yes/no.
- */
-bool Matrix::isTranslation(Coord const eps) const {
- return are_near(_c[0], 1.0, eps) && are_near(_c[1], 0.0, eps) &&
- are_near(_c[2], 0.0, eps) && are_near(_c[3], 1.0, eps) &&
- (!are_near(_c[4], 0.0, eps) || !are_near(_c[5], 0.0, eps));
-}
-
-/** Answers the question "Does this matrix perform a scale, and \em{only} a Scale?"
- \param eps an epsilon value defaulting to EPSILON
- \return A bool representing yes/no.
- */
-bool Matrix::isScale(Coord const eps) const {
- return (!are_near(_c[0], 1.0, eps) || !are_near(_c[3], 1.0, eps)) && //NOTE: these are the diags, and the next line opposite diags
- are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) &&
- are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
-}
-
-/** Answers the question "Does this matrix perform a uniform scale, and \em{only} a uniform scale?"
- \param eps an epsilon value defaulting to EPSILON
- \return A bool representing yes/no.
- */
-bool Matrix::isUniformScale(Coord const eps) const {
- return !are_near(_c[0], 1.0, eps) && are_near(_c[0], _c[3], eps) &&
- are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) &&
- are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
-}
-
-/** Answers the question "Does this matrix perform a rotation, and \em{only} a rotation?"
- \param eps an epsilon value defaulting to EPSILON
- \return A bool representing yes/no.
- */
-bool Matrix::isRotation(Coord const eps) const {
- return are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps) &&
- are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps) &&
- are_near(_c[0]*_c[0] + _c[1]*_c[1], 1.0, eps);
-}
-
-bool Matrix::onlyScaleAndTranslation(Coord const eps) const {
- return are_near(_c[0], _c[3], eps) && are_near(_c[1], 0, eps) && are_near(_c[2], 0, eps);
-}
-
-bool Matrix::isSingular(Coord const eps) const {
- return are_near(det(), 0.0, eps);
-}
-
-bool Matrix::flips() const {
- return cross(xAxis(), yAxis()) > 0;
-}
-
-/** Returns the Scale/Rotate/skew part of the matrix without the translation part. */
-Matrix Matrix::without_translation() const {
- return Matrix(_c[0], _c[1], _c[2], _c[3], 0, 0);
-}
-
-/** Attempts to calculate the inverse of a matrix.
- * This is a Matrix such that m * m.inverse() is very near (hopefully < epsilon difference) the identity Matrix.
- * \textbf{The Identity Matrix is returned if the matrix has no inverse.}
- \return The inverse of the Matrix if defined, otherwise the Identity Matrix.
- */
-Matrix Matrix::inverse() const {
- Matrix d;
-
- Geom::Coord const determ = det();
- // the numerical precision of the determinant must be significant
- if (fabs(determ) > 1e-18) {
- Geom::Coord const ideterm = 1.0 / determ;
-
- d._c[0] = _c[3] * ideterm;
- d._c[1] = -_c[1] * ideterm;
- d._c[2] = -_c[2] * ideterm;
- d._c[3] = _c[0] * ideterm;
- d._c[4] = -_c[4] * d._c[0] - _c[5] * d._c[2];
- d._c[5] = -_c[4] * d._c[1] - _c[5] * d._c[3];
- } else {
- d.setIdentity();
- }
-
- return d;
-}
-
-/** Calculates the determinant of a Matrix. */
-Geom::Coord Matrix::det() const {
- return _c[0] * _c[3] - _c[1] * _c[2];
-}
-
-/** Calculates the scalar of the descriminant of the Matrix.
- * This is simply the absolute value of the determinant.
- */
-Geom::Coord Matrix::descrim2() const {
- return fabs(det());
-}
-
-/** Calculates the descriminant of the Matrix. */
-Geom::Coord Matrix::descrim() const {
- return sqrt(descrim2());
-}
-
-Matrix operator*(Matrix const &m0, Matrix const &m1) {
- Matrix ret;
- for(int a = 0; a < 5; a += 2) {
- for(int b = 0; b < 2; b++) {
- ret[a + b] = m0[a] * m1[b] + m0[a + 1] * m1[b + 2];
- }
- }
- ret[4] += m1[4];
- ret[5] += m1[5];
- return ret;
-}
-
-//TODO: What's this!?!
-Matrix elliptic_quadratic_form(Matrix const &m) {
- double const od = m[0] * m[1] + m[2] * m[3];
- return Matrix(m[0]*m[0] + m[1]*m[1], od,
- od, m[2]*m[2] + m[3]*m[3],
- 0, 0);
-}
-
-Eigen::Eigen(Matrix const &m) {
- double const B = -m[0] - m[3];
- double const C = m[0]*m[3] - m[1]*m[2];
- double const center = -B/2.0;
- double const delta = sqrt(B*B-4*C)/2.0;
- values[0] = center + delta; values[1] = center - delta;
- for (int i = 0; i < 2; i++) {
- vectors[i] = unit_vector(rot90(Point(m[0]-values[i], m[1])));
- }
-}
-
-} //namespace Geom
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/matrix.h b/src/2geom/matrix.h
deleted file mode 100644
index 6a378dbf1..000000000
--- a/src/2geom/matrix.h
+++ /dev/null
@@ -1,165 +0,0 @@
-#ifndef __Geom_MATRIX_H__
-#define __Geom_MATRIX_H__
-
-/** \file
- * \brief Definition of Geom::Matrix types.
- *
- * Main authors:
- * Lauris Kaplinski <lauris@kaplinski.com>:
- * Original NRMatrix definition and related macros.
- *
- * Nathan Hurst <njh@mail.csse.monash.edu.au>:
- * Geom::Matrix class version of the above.
- *
- * Michael G. Sloan <mgsloan@gmail.com>:
- * Reorganization and additions.
- *
- * This code is in public domain.
- */
-
-//#include <glib/gmessages.h>
-
-#include <2geom/point.h>
-
-namespace Geom {
-
-/**
- * The Matrix class.
- *
- * For purposes of multiplication, points should be thought of as row vectors
- *
- * \f$(p_X p_Y 1)\f$
- *
- * to be right-multiplied by transformation matrices of the form
- * \f[
- \left[
- \begin{array}{ccc}
- c_0&c_1&0 \\
- c_2&c_3&0 \\
- c_4&c_5&1
- \end{array}
- \right]
- \f]
- * (so the columns of the matrix correspond to the columns (elements) of the result,
- * and the rows of the matrix correspond to columns (elements) of the "input").
- */
-class Matrix {
- private:
- Coord _c[6];
- public:
- Matrix() {}
-
- Matrix(Matrix const &m) {
- for(int i = 0; i < 6; i++) {
- _c[i] = m[i];
- }
- }
-
- Matrix(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;
- }
-
- Matrix &operator=(Matrix const &m) {
- for(int i = 0; i < 6; i++)
- _c[i] = m._c[i];
- return *this;
- }
-
- inline Coord operator[](unsigned const i) const { return _c[i]; }
- inline Coord &operator[](unsigned const i) { return _c[i]; }
-
-
- Point xAxis() const;
- Point yAxis() const;
- void setXAxis(Point const &vec);
- void setYAxis(Point const &vec);
-
- Point translation() const;
- void setTranslation(Point const &loc);
-
- double expansionX() const;
- double expansionY() const;
- inline Point expansion() const { return Point(expansionX(), expansionY()); }
- void setExpansionX(double val);
- void setExpansionY(double val);
-
- void setIdentity();
-
- bool isIdentity(Coord eps = EPSILON) const;
- bool isTranslation(Coord eps = EPSILON) const;
- bool isRotation(double eps = EPSILON) const;
- bool isScale(double eps = EPSILON) const;
- bool isUniformScale(double eps = EPSILON) const;
- bool onlyScaleAndTranslation(double eps = EPSILON) const;
- bool isSingular(double eps = EPSILON) const;
-
- bool flips() const;
-
- Matrix without_translation() const;
-
- Matrix inverse() const;
-
- Coord det() const;
- Coord descrim2() const;
- Coord descrim() const;
-};
-
-Matrix operator*(Matrix const &a, Matrix const &b);
-inline Matrix &operator*=(Matrix &a, Matrix const &b) { a = a * b; return a; }
-
-/** A function to print out the Matrix (for debugging) */
-inline std::ostream &operator<< (std::ostream &out_file, const Geom::Matrix &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;
-}
-
-/** Given a matrix m such that unit_circle = m*x, this returns the
- * quadratic form x*A*x = 1. */
-Matrix elliptic_quadratic_form(Matrix 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(Matrix const &m);
-};
-
-// Matrix factories
-Matrix from_basis(const Point x_basis, const Point y_basis, const Point offset=Point(0,0));
-
-/** Returns the Identity Matrix. */
-inline Matrix identity() {
- return Matrix(1.0, 0.0,
- 0.0, 1.0,
- 0.0, 0.0);
-}
-
-inline bool operator==(Matrix const &a, Matrix const &b) {
- for(unsigned i = 0; i < 6; ++i) {
- if ( a[i] != b[i] ) return false;
- }
- return true;
-}
-inline bool operator!=(Matrix const &a, Matrix const &b) { return !( a == b ); }
-
-
-
-} /* namespace Geom */
-
-#endif /* !__Geom_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/src/2geom/numeric/fitting-model.h b/src/2geom/numeric/fitting-model.h
index dcf0e8e1d..b1ecbe92c 100644
--- a/src/2geom/numeric/fitting-model.h
+++ b/src/2geom/numeric/fitting-model.h
@@ -43,6 +43,7 @@
#include <2geom/ellipse.h>
#include <2geom/circle.h>
#include <2geom/utils.h>
+#include <2geom/conicsec.h>
namespace Geom { namespace NL {
@@ -83,6 +84,10 @@ namespace Geom { namespace NL {
* instance type: the type of the objects produced by using
* the fitting raw data solution
*/
+
+
+
+
template< typename ParameterType, typename ValueType, typename InstanceType >
class LinearFittingModel
{
@@ -230,8 +235,32 @@ class LFMNormalizedPowerBasis
// 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 LFMEllipseEquation
+class LFMConicEquation
: public LinearFittingModelWithFixedTerms<Point, double, InstanceType>
{
public:
@@ -251,10 +280,20 @@ class LFMEllipseEquation
}
};
+// 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 LFMEllipseEquation<Ellipse>
+ : public LFMConicEquation<Ellipse>
{
public:
void instance(Ellipse & e, ConstVectorView const& coeff) const
@@ -431,9 +470,8 @@ class LFMBezier
// this model generates Bezier curves
-template< unsigned int N >
class LFMBezierCurve
- : public LinearFittingModel< double, Point, BezierCurve<N> >
+ : public LinearFittingModel< double, Point, BezierCurve >
{
public:
LFMBezierCurve( size_t _order )
@@ -451,13 +489,13 @@ class LFMBezierCurve
return mob.size();
}
- void instance(BezierCurve<N> & bc, ConstMatrixView const& raw_data) const
+ void instance(BezierCurve & bc, ConstMatrixView const& raw_data) const
{
Bezier bx(size()-1);
Bezier by(size()-1);
mob.instance(bx, raw_data.column_const_view(X));
mob.instance(by, raw_data.column_const_view(Y));
- bc = BezierCurve<N>(bx, by);
+ bc = BezierCurve(bx, by);
}
private:
diff --git a/src/2geom/numeric/matrix.cpp b/src/2geom/numeric/matrix.cpp
index 94a345fd5..98ff3b6ca 100644
--- a/src/2geom/numeric/matrix.cpp
+++ b/src/2geom/numeric/matrix.cpp
@@ -37,10 +37,8 @@
#include <2geom/numeric/vector.h>
-
namespace Geom { namespace NL {
-
Vector operator*( detail::BaseMatrixImpl const& A,
detail::BaseVectorImpl const& v )
{
@@ -101,6 +99,47 @@ Matrix pseudo_inverse(detail::BaseMatrixImpl const& A)
return P;
}
+
+double trace (detail::BaseMatrixImpl const& A)
+{
+ if (A.rows() != A.columns())
+ {
+ THROW_RANGEERROR ("NL::Matrix: computing trace: "
+ "rows() != columns()");
+ }
+ double t = 0;
+ for (size_t i = 0; i < A.rows(); ++i)
+ {
+ t += A(i,i);
+ }
+ return t;
+}
+
+
+double det (detail::BaseMatrixImpl const& A)
+{
+ if (A.rows() != A.columns())
+ {
+ THROW_RANGEERROR ("NL::Matrix: computing determinant: "
+ "rows() != columns()");
+ }
+
+ Matrix LU(A);
+ int s;
+ gsl_permutation * p = gsl_permutation_alloc(LU.rows());
+ gsl_linalg_LU_decomp (LU.get_gsl_matrix(), p, &s);
+
+ double t = 1;
+ for (size_t i = 0; i < LU.rows(); ++i)
+ {
+ t *= LU(i,i);
+ }
+
+ gsl_permutation_free(p);
+ return t;
+}
+
+
} } // end namespaces
/*
diff --git a/src/2geom/numeric/matrix.h b/src/2geom/numeric/matrix.h
index f2a934235..a130bd748 100644
--- a/src/2geom/numeric/matrix.h
+++ b/src/2geom/numeric/matrix.h
@@ -38,6 +38,7 @@
#ifndef _NL_MATRIX_H_
#define _NL_MATRIX_H_
+#include <2geom/exception.h>
#include <2geom/numeric/vector.h>
#include <cassert>
@@ -45,7 +46,6 @@
#include <algorithm> // for std::swap
#include <sstream>
#include <string>
-
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_linalg.h>
@@ -232,13 +232,13 @@ class MatrixImpl : public BaseMatrixImpl
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);
- }
+ 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)
+ double & operator() (size_t i, size_t j)
{
return *gsl_matrix_ptr(m_matrix, i, j);
}
@@ -310,6 +310,8 @@ using detail::operator==;
using detail::operator<<;
+template <size_t N>
+class ConstBaseSymmetricMatrix;
class Matrix: public detail::MatrixImpl
@@ -352,6 +354,18 @@ class Matrix: public detail::MatrixImpl
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() );
@@ -366,6 +380,16 @@ class Matrix: public detail::MatrixImpl
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;
+ }
+
virtual ~Matrix()
{
gsl_matrix_free(m_matrix);
@@ -557,6 +581,10 @@ Matrix operator*( detail::BaseMatrixImpl const& A,
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_*/
diff --git a/src/2geom/numeric/vector.h b/src/2geom/numeric/vector.h
index 46701645a..6ab898f29 100644
--- a/src/2geom/numeric/vector.h
+++ b/src/2geom/numeric/vector.h
@@ -555,6 +555,24 @@ void swap_view(VectorView & v1, VectorView & v2)
std::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
diff --git a/src/2geom/path-intersection.cpp b/src/2geom/path-intersection.cpp
index 5e58525c7..58ee6232b 100644
--- a/src/2geom/path-intersection.cpp
+++ b/src/2geom/path-intersection.cpp
@@ -28,7 +28,7 @@ int winding(Path const &path, Point p) {
if(iter == path.end_closed()) { return 0; }
if(iter->initialPoint()[Y]!=p[Y]) { start = iter; break; }
if(iter->finalPoint()[Y]!=p[Y]) { start = iter; break; }
- if(iter->boundsFast()->height()!=0.){ start = iter; break; }
+ if(iter->boundsFast().height()!=0.){ start = iter; break; }
}
int wind = 0;
unsigned cnt = 0;
@@ -39,18 +39,18 @@ int winding(Path const &path, Point p) {
cnt++;
if(cnt > path.size()) return wind; //some bug makes this required
starting = false;
- Rect bounds = *(iter->boundsFast());
+ Rect bounds = (iter->boundsFast());
Coord x = p[X], y = p[Y];
-
+
if(x > bounds.right() || !bounds[Y].contains(y)) continue; //ray doesn't intersect box
-
+
Point final = iter->finalPoint();
Point initial = iter->initialPoint();
Cmp final_to_ray = cmp(final[Y], y);
Cmp initial_to_ray = cmp(initial[Y], y);
-
+
// if y is included, these will have opposite values, giving order.
- Cmp c = cmp(final_to_ray, initial_to_ray);
+ Cmp c = cmp(final_to_ray, initial_to_ray);
if(x < bounds.left()) {
// ray goes through bbox
// winding delta determined by position of endpoints
@@ -73,7 +73,7 @@ int winding(Path const &path, Point p) {
next++;
for(; ; next++) {
if(next == path.end_closed()) next = path.begin();
- Rect bnds = *(next->boundsFast());
+ Rect bnds = (next->boundsFast());
//TODO: X considerations
if(bnds.height() > 0) {
//It has diverged
@@ -100,7 +100,7 @@ int winding(Path const &path, Point p) {
//Looks like it looped, which means everything's flat
return 0;
}
-
+
cont:(void)0;
}
return wind;
@@ -135,10 +135,10 @@ bool path_direction(Path const &p) {
} else if(final_to_ray == EQUAL_TO) goto doh;
}
return res < 0;
-
+
doh:
//Otherwise fallback on area
-
+
Piecewise<D2<SBasis> > pw = p.toPwSb();
double area;
Point centre;
@@ -214,39 +214,41 @@ intersect_polish_f (const gsl_vector * x, void *params,
{
const double x0 = gsl_vector_get (x, 0);
const double x1 = gsl_vector_get (x, 1);
-
- Geom::Point dx = ((struct rparams *) params)->A(x0) -
+
+ Geom::Point dx = ((struct rparams *) params)->A(x0) -
((struct rparams *) params)->B(x1);
-
+
gsl_vector_set (f, 0, dx[0]);
gsl_vector_set (f, 1, dx[1]);
-
+
return GSL_SUCCESS;
}
#endif
-static void
+static void
intersect_polish_root (Curve const &A, double &s,
Curve const &B, double &t) {
+ int status;
+ size_t iter = 0;
std::vector<Point> as, bs;
as = A.pointAndDerivatives(s, 2);
bs = B.pointAndDerivatives(t, 2);
Point F = as[0] - bs[0];
double best = dot(F, F);
-
+
for(int i = 0; i < 4; i++) {
-
+
/**
we want to solve
J*(x1 - x0) = f(x0)
-
+
|dA(s)[0] -dB(t)[0]| (X1 - X0) = A(s) - B(t)
- |dA(s)[1] -dB(t)[1]|
+ |dA(s)[1] -dB(t)[1]|
**/
// We're using the standard transformation matricies, which is numerically rather poor. Much better to solve the equation using elimination.
- Matrix jack(as[1][0], as[1][1],
+ Affine jack(as[1][0], as[1][1],
-bs[1][0], -bs[1][1],
0, 0);
Point soln = (F)*jack.inverse();
@@ -257,7 +259,7 @@ intersect_polish_root (Curve const &A, double &s,
else if (ns>1) ns=1;
if (nt<0) nt=0;
else if (nt>1) nt=1;
-
+
as = A.pointAndDerivatives(ns, 2);
bs = B.pointAndDerivatives(nt, 2);
F = as[0] - bs[0];
@@ -275,35 +277,33 @@ intersect_polish_root (Curve const &A, double &s,
const size_t n = 2;
struct rparams p = {A, B};
gsl_multiroot_function f = {&intersect_polish_f, n, &p};
-
+
double x_init[2] = {s, t};
gsl_vector *x = gsl_vector_alloc (n);
-
+
gsl_vector_set (x, 0, x_init[0]);
gsl_vector_set (x, 1, x_init[1]);
-
+
const gsl_multiroot_fsolver_type *T = gsl_multiroot_fsolver_hybrids;
gsl_multiroot_fsolver *sol = gsl_multiroot_fsolver_alloc (T, 2);
gsl_multiroot_fsolver_set (sol, &f, x);
-
- int status = 0;
- size_t iter = 0;
+
do
{
iter++;
status = gsl_multiroot_fsolver_iterate (sol);
-
+
if (status) /* check if solver is stuck */
break;
-
+
status =
gsl_multiroot_test_residual (sol->f, 1e-12);
}
while (status == GSL_CONTINUE && iter < 1000);
-
+
s = gsl_vector_get (sol->x, 0);
t = gsl_vector_get (sol->x, 1);
-
+
gsl_multiroot_fsolver_free (sol);
gsl_vector_free (x);
}
@@ -315,7 +315,7 @@ intersect_polish_root (Curve const &A, double &s,
* It passes in the curves, time intervals, and keeps track of depth, while
* returning the results through the Crossings parameter.
*/
-void pair_intersect(Curve const & A, double Al, double Ah,
+void pair_intersect(Curve const & A, double Al, double Ah,
Curve const & B, double Bl, double Bh,
Crossings &ret, unsigned depth = 0) {
// std::cout << depth << "(" << Al << ", " << Ah << ")\n";
@@ -324,15 +324,15 @@ void pair_intersect(Curve const & A, double Al, double Ah,
OptRect Br = B.boundsLocal(Interval(Bl, Bh));
if (!Br) return;
-
+
if(! Ar->intersects(*Br)) return;
-
+
//Checks the general linearity of the function
- if((depth > 12)) { // || (A.boundsLocal(Interval(Al, Ah), 1).maxExtent() < 0.1
+ if((depth > 12)) { // || (A.boundsLocal(Interval(Al, Ah), 1).maxExtent() < 0.1
//&& B.boundsLocal(Interval(Bl, Bh), 1).maxExtent() < 0.1)) {
double tA, tB, c;
- if(linear_intersect(A.pointAt(Al), A.pointAt(Ah),
- B.pointAt(Bl), B.pointAt(Bh),
+ if(linear_intersect(A.pointAt(Al), A.pointAt(Ah),
+ B.pointAt(Bl), B.pointAt(Bh),
tA, tB, c)) {
tA = tA * (Ah - Al) + Al;
tB = tB * (Bh - Bl) + Bl;
@@ -385,8 +385,8 @@ void mono_intersect(Curve const &A, double Al, double Ah,
if(depth > 12 || (Ar.maxExtent() < tol && Ar.maxExtent() < tol)) {
double tA, tB, c;
- if(linear_intersect(A.pointAt(Al), A.pointAt(Ah),
- B.pointAt(Bl), B.pointAt(Bh),
+ if(linear_intersect(A.pointAt(Al), A.pointAt(Ah),
+ B.pointAt(Bl), B.pointAt(Bh),
tA, tB, c)) {
tA = tA * (Ah - Al) + Al;
tB = tB * (Bh - Bl) + Bl;
@@ -483,7 +483,7 @@ std::vector<double> offset_doubles(std::vector<double> const &x, double offs) {
std::vector<double> path_mono_splits(Path const &p) {
std::vector<double> ret;
if(p.empty()) return ret;
-
+
bool pdx=2, pdy=2; //Previous derivative direction
for(unsigned i = 0; i < p.size(); i++) {
std::vector<double> spl = offset_doubles(curve_mono_splits(p[i]), i);
@@ -502,7 +502,7 @@ std::vector<double> path_mono_splits(Path const &p) {
}
/**
- * Applies path_mono_splits to multiple paths, and returns the results such that
+ * Applies path_mono_splits to multiple paths, and returns the results such that
* time-set i corresponds to Path i.
*/
std::vector<std::vector<double> > paths_mono_splits(std::vector<Path> const &ps) {
@@ -541,14 +541,14 @@ CrossingSet MonoCrosser::crossings(std::vector<Path> const &a, std::vector<Path>
if(b.empty()) return CrossingSet(a.size(), Crossings());
CrossingSet results(a.size() + b.size(), Crossings());
if(a.empty()) return results;
-
+
std::vector<std::vector<double> > splits_a = paths_mono_splits(a), splits_b = paths_mono_splits(b);
std::vector<std::vector<Rect> > bounds_a = split_bounds(a, splits_a), bounds_b = split_bounds(b, splits_b);
-
- std::vector<Rect> bounds_a_union, bounds_b_union;
+
+ std::vector<Rect> bounds_a_union, bounds_b_union;
for(unsigned i = 0; i < bounds_a.size(); i++) bounds_a_union.push_back(union_list(bounds_a[i]));
for(unsigned i = 0; i < bounds_b.size(); i++) bounds_b_union.push_back(union_list(bounds_b[i]));
-
+
std::vector<std::vector<unsigned> > cull = sweep_bounds(bounds_a_union, bounds_b_union);
Crossings n;
for(unsigned i = 0; i < cull.size(); i++) {
@@ -556,7 +556,7 @@ CrossingSet MonoCrosser::crossings(std::vector<Path> const &a, std::vector<Path>
unsigned j = cull[i][jx];
unsigned jc = j + a.size();
Crossings res;
-
+
//Sweep of the monotonic portions
std::vector<std::vector<unsigned> > cull2 = sweep_bounds(bounds_a[i], bounds_b[j]);
for(unsigned k = 0; k < cull2.size(); k++) {
@@ -567,9 +567,9 @@ CrossingSet MonoCrosser::crossings(std::vector<Path> const &a, std::vector<Path>
res, .1);
}
}
-
+
for(unsigned k = 0; k < res.size(); k++) { res[k].a = i; res[k].b = jc; }
-
+
merge_crossings(results[i], res, i);
merge_crossings(results[i], res, jc);
}
@@ -583,22 +583,22 @@ CrossingSet MonoCrosser::crossings(std::vector<Path> const &a, std::vector<Path>
CrossingSet crossings_among(std::vector<Path> const &p) {
CrossingSet results(p.size(), Crossings());
if(p.empty()) return results;
-
+
std::vector<std::vector<double> > splits = paths_mono_splits(p);
std::vector<std::vector<Rect> > prs = split_bounds(p, splits);
std::vector<Rect> rs;
for(unsigned i = 0; i < prs.size(); i++) rs.push_back(union_list(prs[i]));
-
+
std::vector<std::vector<unsigned> > cull = sweep_bounds(rs);
-
+
//we actually want to do the self-intersections, so add em in:
for(unsigned i = 0; i < cull.size(); i++) cull[i].push_back(i);
-
+
for(unsigned i = 0; i < cull.size(); i++) {
for(unsigned jx = 0; jx < cull[i].size(); jx++) {
unsigned j = cull[i][jx];
Crossings res;
-
+
//Sweep of the monotonic portions
std::vector<std::vector<unsigned> > cull2 = sweep_bounds(prs[i], prs[j]);
for(unsigned k = 0; k < cull2.size(); k++) {
@@ -609,14 +609,14 @@ CrossingSet crossings_among(std::vector<Path> const &p) {
res, .1);
}
}
-
+
for(unsigned k = 0; k < res.size(); k++) { res[k].a = i; res[k].b = j; }
-
+
merge_crossings(results[i], res, i);
merge_crossings(results[j], res, j);
}
}
-
+
return results;
}
*/
@@ -635,7 +635,7 @@ Crossings curve_self_crossings(Curve const &a) {
}
/*
-void mono_curve_intersect(Curve const & A, double Al, double Ah,
+void mono_curve_intersect(Curve const & A, double Al, double Ah,
Curve const & B, double Bl, double Bh,
Crossings &ret, unsigned depth=0) {
// std::cout << depth << "(" << Al << ", " << Ah << ")\n";
@@ -643,9 +643,9 @@ void mono_curve_intersect(Curve const & A, double Al, double Ah,
B0 = B.pointAt(Bl), B1 = B.pointAt(Bh);
//inline code that this implies? (without rect/interval construction)
if(!Rect(A0, A1).intersects(Rect(B0, B1)) || A0 == A1 || B0 == B1) return;
-
+
//Checks the general linearity of the function
- if((depth > 12) || (A.boundsLocal(Interval(Al, Ah), 1).maxExtent() < 0.1
+ if((depth > 12) || (A.boundsLocal(Interval(Al, Ah), 1).maxExtent() < 0.1
&& B.boundsLocal(Interval(Bl, Bh), 1).maxExtent() < 0.1)) {
double tA, tB, c;
if(linear_intersect(A0, A1, B0, B1, tA, tB, c)) {
@@ -705,7 +705,7 @@ Crossings path_self_crossings(Path const &p) {
for(unsigned jx = 0; jx < cull[i].size(); jx++) {
unsigned j = cull[i][jx];
res.clear();
-
+
std::vector<std::vector<unsigned> > cull2 = sweep_bounds(bnds[i], bnds[j]);
for(unsigned k = 0; k < cull2.size(); k++) {
for(unsigned lx = 0; lx < cull2[k].size(); lx++) {
@@ -713,7 +713,7 @@ Crossings path_self_crossings(Path const &p) {
mono_curve_intersect(p[i], spl[i][k-1], spl[i][k], p[j], spl[j][l-1], spl[j][l], res);
}
}
-
+
//if(fabs(int(i)-j) == 1 || fabs(int(i)-j) == p.size()-1) {
Crossings res2;
for(unsigned k = 0; k < res.size(); k++) {
@@ -742,7 +742,7 @@ Crossings self_crossings(Path const &p) {
unsigned j = cull[i][jx];
res.clear();
pair_intersect(p[i], 0, 1, p[j], 0, 1, res);
-
+
//if(fabs(int(i)-j) == 1 || fabs(int(i)-j) == p.size()-1) {
Crossings res2;
for(unsigned k = 0; k < res.size(); k++) {
@@ -767,9 +767,9 @@ void flip_crossings(Crossings &crs) {
CrossingSet crossings_among(std::vector<Path> const &p) {
CrossingSet results(p.size(), Crossings());
if(p.empty()) return results;
-
+
SimpleCrosser cc;
-
+
std::vector<std::vector<unsigned> > cull = sweep_bounds(bounds(p));
for(unsigned i = 0; i < cull.size(); i++) {
Crossings res = self_crossings(p[i]);
@@ -779,7 +779,7 @@ CrossingSet crossings_among(std::vector<Path> const &p) {
merge_crossings(results[i], res, i);
for(unsigned jx = 0; jx < cull[i].size(); jx++) {
unsigned j = cull[i][jx];
-
+
Crossings res = cc.crossings(p[i], p[j]);
for(unsigned k = 0; k < res.size(); k++) { res[k].a = i; res[k].b = j; }
merge_crossings(results[i], res, i);
diff --git a/src/2geom/path.cpp b/src/2geom/path.cpp
index c47902649..58d6b9b5e 100644
--- a/src/2geom/path.cpp
+++ b/src/2geom/path.cpp
@@ -46,8 +46,9 @@ namespace Geom
OptRect Path::boundsFast() const {
OptRect bounds;
if (empty()) return bounds;
- bounds=front().boundsFast();
+ bounds = front().boundsFast();
const_iterator iter = begin();
+ // the closing path segment can be ignored, because it will always lie within the bbox of the rest of the path
if ( iter != end() ) {
for ( ++iter; iter != end() ; ++iter ) {
bounds.unionWith(iter->boundsFast());
@@ -59,8 +60,9 @@ OptRect Path::boundsFast() const {
OptRect Path::boundsExact() const {
OptRect bounds;
if (empty()) return bounds;
- bounds=front().boundsExact();
+ bounds = front().boundsExact();
const_iterator iter = begin();
+ // the closing path segment can be ignored, because it will always lie within the bbox of the rest of the path
if ( iter != end() ) {
for ( ++iter; iter != end() ; ++iter ) {
bounds.unionWith(iter->boundsExact());
@@ -77,7 +79,7 @@ iter inc(iter const &x, unsigned n) {
return ret;
}
-Path &Path::operator*=(Matrix const &m) {
+Path &Path::operator*=(Affine const &m) {
unshare();
Sequence::iterator last = get_curves().end() - 1;
Sequence::iterator it;
@@ -146,7 +148,7 @@ Path::allNearestPoints(Point const& _point, double from, double to) const
Rect bb(Geom::Point(0,0),Geom::Point(0,0));
for ( unsigned int i = si + 1; i < ei; ++i )
{
- bb = *(_path[i].boundsFast());
+ bb = (_path[i].boundsFast());
dsq = distanceSq(_point, bb);
if ( mindistsq < dsq ) continue;
all_t = _path[i].allNearestPoints(_point);
@@ -165,7 +167,7 @@ Path::allNearestPoints(Point const& _point, double from, double to) const
ni.push_back(i);
}
}
- bb = *(_path[ei].boundsFast());
+ bb = (_path[ei].boundsFast());
dsq = distanceSq(_point, bb);
if ( mindistsq >= dsq )
{
@@ -203,7 +205,7 @@ Path::nearestPointPerCurve(Point const& _point) const
{
//return a single nearest point for each curve in this path
std::vector<double> np;
- for (const_iterator it = begin() ; it != end_default(); ++it)
+ for (const_iterator it = begin() ; it != end_default() ; ++it)
//for (std::vector<Path>::const_iterator it = _path.begin(); it != _path.end(), ++it){
{
np.push_back(it->nearestPoint(_point));
@@ -256,7 +258,7 @@ double Path::nearestPoint(Point const &_point, double from, double to, double *d
Rect bb(Geom::Point(0,0),Geom::Point(0,0));
for ( unsigned int i = si + 1; i < ei; ++i )
{
- bb = *(_path[i].boundsFast());
+ bb = (_path[i].boundsFast());
dsq = distanceSq(_point, bb);
if ( mindistsq <= dsq ) continue;
t = _path[i].nearestPoint(_point);
@@ -268,7 +270,7 @@ double Path::nearestPoint(Point const &_point, double from, double to, double *d
mindistsq = dsq;
}
}
- bb = *(_path[ei].boundsFast());
+ bb = (_path[ei].boundsFast());
dsq = distanceSq(_point, bb);
if ( mindistsq > dsq )
{
diff --git a/src/2geom/path.h b/src/2geom/path.h
index 3167bb09e..cbd449248 100644
--- a/src/2geom/path.h
+++ b/src/2geom/path.h
@@ -291,13 +291,13 @@ public:
return !( *this == other );
}
- Path operator*(Matrix const &m) const {
+ Path operator*(Affine const &m) const {
Path ret(*this);
ret *= m;
return ret;
}
- Path &operator*=(Matrix const &m);
+ Path &operator*=(Affine const &m);
Point pointAt(double t) const
{
diff --git a/src/2geom/pathvector.cpp b/src/2geom/pathvector.cpp
index 3d11dd48b..fc0ad75c4 100644
--- a/src/2geom/pathvector.cpp
+++ b/src/2geom/pathvector.cpp
@@ -38,7 +38,7 @@
#include <2geom/pathvector.h>
#include <2geom/path.h>
-#include <2geom/matrix.h>
+#include <2geom/affine.h>
namespace Geom {
diff --git a/src/2geom/pathvector.h b/src/2geom/pathvector.h
index a531cc955..2f45b9d86 100644
--- a/src/2geom/pathvector.h
+++ b/src/2geom/pathvector.h
@@ -3,7 +3,8 @@
* \brief PathVector - std::vector containing Geom::Path
* This file provides a set of operations that can be performed on PathVector,
* e.g. an affine transform.
- *
+ */
+/*
* Authors:
* Johan Engelen <goejendaagh@zonnet.nl>
*
@@ -46,13 +47,13 @@ typedef std::vector<Geom::Path> PathVector;
/* general path transformation: */
inline
-void operator*= (PathVector & path_in, Matrix const &m) {
+void operator*= (PathVector & path_in, Affine const &m) {
for(PathVector::iterator it = path_in.begin(); it != path_in.end(); ++it) {
(*it) *= m;
}
}
inline
-PathVector operator*(PathVector const & path_in, Matrix const &m) {
+PathVector operator*(PathVector const & path_in, Affine const &m) {
PathVector ret(path_in);
ret *= m;
return ret;
diff --git a/src/2geom/piecewise.cpp b/src/2geom/piecewise.cpp
index fcecc13c1..ba1e60ea3 100644
--- a/src/2geom/piecewise.cpp
+++ b/src/2geom/piecewise.cpp
@@ -154,6 +154,54 @@ int compose_findSegIdx(std::map<double,unsigned>::iterator const &cut,
return idx;
}
+
+Piecewise<SBasis> pw_compose_inverse(SBasis const &f, SBasis const &g, unsigned order, double zero){
+ Piecewise<SBasis> result;
+
+ assert( f.size()>0 && g.size()>0);
+ SBasis g01 = g;
+ bool flip = ( g01.at0() > g01.at1() );
+
+ //OptInterval g_range = bounds_exact(g);
+ OptInterval g_range( Interval( g.at0(), g.at1() ));
+
+ g01 -= g_range->min();
+ g01 /= g_range->extent();
+ if ( flip ){
+ g01 *= -1.;
+ g01 += 1.;
+ }
+#if 1
+ assert( std::abs( g01.at0() - 0. ) < zero );
+ assert( std::abs( g01.at1() - 1. ) < zero );
+ //g[0][0] = 0.;
+ //g[0][1] = 1.;
+#endif
+
+ SBasis foginv = compose_inverse( f, g01, order, zero );
+ SBasis err = compose( foginv, g01) - f;
+
+ if ( err.tailError(0) < zero ){
+ result = Piecewise<SBasis> (foginv);
+ }else{
+ SBasis g_portion = portion( g01, Interval(0.,.5) );
+ SBasis f_portion = portion( f, Interval(0.,.5) );
+ result = pw_compose_inverse(f_portion, g_portion, order, zero);
+
+ g_portion = portion( g01, Interval(.5, 1.) );
+ f_portion = portion( f, Interval(.5, 1.) );
+ Piecewise<SBasis> result_next;
+ result_next = pw_compose_inverse(f_portion, g_portion, order, zero);
+ result.concat( result_next );
+ }
+ if (flip) {
+ result = reverse(result);
+ }
+ result.setDomain(*g_range);
+ return result;
+}
+
+
std::vector<double> roots(Piecewise<SBasis> const &f){
std::vector<double> result;
for (unsigned i=0; i<f.size(); i++){
@@ -179,6 +227,32 @@ std::vector<std::vector<double> > multi_roots(Piecewise<SBasis> const &f, std::v
return result;
}
+
+std::vector<Interval> level_set(Piecewise<SBasis> const &f, Interval const &level, double tol){
+ std::vector<Interval> result;
+ for (unsigned i=0; i<f.size(); i++){
+ std::vector<Interval> resulti = level_set( f[i], level, 0., 1., tol);
+ for (unsigned j=0; j<resulti.size(); j++){
+ double a = f.cuts[i] + resulti[j].min() * ( f.cuts[i+1] - f.cuts[i] );
+ double b = f.cuts[i] + resulti[j].max() * ( f.cuts[i+1] - f.cuts[i] );
+ Interval domj( a, b );
+ //Interval domj( f.mapToDomain(resulti[j].min(), i ), f.mapToDomain(resulti[j].max(), i ) );
+
+ if ( j==0 && result.size() > 0 && result.back().intersects(domj) ){
+ result.back().unionWith(domj);
+ }else{
+ result.push_back(domj);
+ }
+ }
+ }
+ return result;
+}
+std::vector<Interval> level_set(Piecewise<SBasis> const &f, double v, double vtol, double tol){
+ Interval level ( v-vtol, v+vtol );
+ return level_set( f, level, tol);
+}
+
+
}
/*
Local Variables:
diff --git a/src/2geom/piecewise.h b/src/2geom/piecewise.h
index 62185b472..19c66d8f0 100644
--- a/src/2geom/piecewise.h
+++ b/src/2geom/piecewise.h
@@ -265,6 +265,11 @@ class Piecewise {
};
+/**
+ * ...
+ * \return ...
+ * \relates Piecewise
+ */
template<typename T>
inline typename FragmentConcept<T>::BoundsType bounds_fast(const Piecewise<T> &f) {
boost::function_requires<FragmentConcept<T> >();
@@ -276,6 +281,11 @@ inline typename FragmentConcept<T>::BoundsType bounds_fast(const Piecewise<T> &f
return ret;
}
+/**
+ * ...
+ * \return ...
+ * \relates Piecewise
+ */
template<typename T>
inline typename FragmentConcept<T>::BoundsType bounds_exact(const Piecewise<T> &f) {
boost::function_requires<FragmentConcept<T> >();
@@ -287,6 +297,11 @@ inline typename FragmentConcept<T>::BoundsType bounds_exact(const Piecewise<T> &
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> >();
@@ -308,7 +323,10 @@ inline typename FragmentConcept<T>::BoundsType bounds_local(const Piecewise<T> &
return ret;
}
-//returns a portion of a piece of a Piecewise<T>, given the piece's index and a to/from time.
+/**
+ * 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());
@@ -324,6 +342,8 @@ T elem_portion(const Piecewise<T> &a, unsigned i, double from, double to) {
* 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) {
@@ -389,8 +409,9 @@ Piecewise<T> partition(const Piecewise<T> &pw, std::vector<double> const &c) {
return ret;
}
-/**Piecewise<T> portion(const Piecewise<T> &pw, double from, double to);
- * Returns a Piecewise<T> with a defined domain of [min(from, to), max(from, to)].
+/**
+ * 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) {
@@ -424,6 +445,11 @@ Piecewise<T> portion(const Piecewise<T> &pw, double from, double to) {
}
//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;
@@ -439,6 +465,11 @@ Piecewise<T> remove_short_cuts(Piecewise<T> const &f, double tol) {
}
//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;
@@ -455,6 +486,11 @@ Piecewise<T> remove_short_cuts_extending(Piecewise<T> const &f, double tol) {
return ret;
}
+/**
+ * ...
+ * \return ...
+ * \relates Piecewise
+ */
template<typename T>
std::vector<double> roots(const Piecewise<T> &pw) {
std::vector<double> ret;
@@ -467,6 +503,11 @@ std::vector<double> roots(const Piecewise<T> &pw) {
}
//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> >();
@@ -511,6 +552,11 @@ Piecewise<T>& operator-=(Piecewise<T>& a, typename T::output_type b) {
}
//IMPL: ScalableConcept
+/**
+ * ...
+ * \return \f$ -a = \f$
+ * \relates Piecewise
+ */
template<typename T>
Piecewise<T> operator-(Piecewise<T> const &a) {
boost::function_requires<ScalableConcept<T> >();
@@ -522,6 +568,11 @@ Piecewise<T> operator-(Piecewise<T> const &a) {
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> >();
@@ -535,6 +586,11 @@ Piecewise<T> operator*(Piecewise<T> const &a, double b) {
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> >();
@@ -548,6 +604,11 @@ Piecewise<T> operator*(Piecewise<T> const &a, T b) {
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> >();
@@ -582,6 +643,11 @@ Piecewise<T>& operator/=(Piecewise<T>& a, double b) {
}
//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> >();
@@ -595,6 +661,11 @@ Piecewise<T> operator+(Piecewise<T> const &a, Piecewise<T> const &b) {
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> >();
@@ -619,6 +690,11 @@ inline Piecewise<T>& operator-=(Piecewise<T> &a, Piecewise<T> const &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> >();
@@ -635,6 +711,11 @@ Piecewise<T2> operator*(Piecewise<T1> const &a, Piecewise<T2> const &b) {
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;
@@ -661,9 +742,14 @@ int compose_findSegIdx(std::map<double,unsigned>::iterator const &cut,
std::vector<double> const &levels,
SBasis const &g);
-//TODO: add concept check
+/**
+ * ...
+ * \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));
@@ -707,9 +793,14 @@ Piecewise<T> compose(Piecewise<T> const &f, SBasis const &g){
return(result);
}
-//TODO: add concept check for following composition functions
+/**
+ * ...
+ * \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]);
@@ -721,6 +812,7 @@ Piecewise<T> compose(Piecewise<T> const &f, Piecewise<SBasis> const &g){
/*
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++){
@@ -729,11 +821,26 @@ Piecewise<D2<SBasis> > compose(D2<SBasis2d> const &sb2d, Piecewise<D2<SBasis> >
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;
@@ -748,6 +855,11 @@ Piecewise<T> integral(Piecewise<T> const &a) {
return result;
}
+/**
+ * ...
+ * \return ...
+ * \relates Piecewise
+ */
template<typename T>
Piecewise<T> derivative(Piecewise<T> const &a) {
Piecewise<T> result;
@@ -763,6 +875,19 @@ 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>();
diff --git a/src/2geom/point.cpp b/src/2geom/point.cpp
index 45e035d4a..9df88df92 100644
--- a/src/2geom/point.cpp
+++ b/src/2geom/point.cpp
@@ -1,25 +1,46 @@
-#include <2geom/point.h>
#include <assert.h>
-#include <2geom/coord.h>
-#include <2geom/isnan.h> //temporary fix for isnan()
+#include <math.h>
+#include <2geom/point.h>
#include <2geom/transforms.h>
namespace Geom {
-/** \brief Scales this vector to make it a unit vector (within rounding error).
+/**
+ * @class Point
+ * @brief Two-dimensional point that doubles as a vector.
+ *
+ * Points in 2Geom are represented in Cartesian coordinates, e.g. as a pair of numbers
+ * that store the X and Y coordinates. Each point is also a vector in \f$\mathbb{R}^2\f$
+ * from the origin (point at 0,0) to the stored coordinates,
+ * and has methods implementing several vector operations (like length()).
*
- * The current version tries to handle infinite coordinates gracefully,
- * but it's not clear that any callers need that.
+ * \par Operator note
+ * \par
+ * Most operators are provided by Boost operator helpers, so they are not visible in this class.
+ * If @a p, @a q, @a r denote points, @a s a floating-point scalar, and @a m a transformation matrix,
+ * then the following operations are available:
+ * @code
+ p += q; p -= q; r = p + q; r = p - q;
+ p *= s; p /= s; q = p * s; q = s * p; q = p / s;
+ p *= m; p /= m; q = p * m; q = m * p; q = p / m;
+ @endcode
+ * It is possible to left-multiply a point by a matrix, even though mathematically speaking
+ * this is undefined. The result is a point identical to that obtained by right-multiplying.
+ * Division of points by matrices is defined as multiplication by their inverses.
*
- * \pre \f$this \neq (0, 0)\f$
- * \pre Neither component is NaN.
- * \post \f$-\epsilon<\left|this\right|-1<\epsilon\f$
- */
+ * @ingroup Primitives */
+
+/** @brief Normalize the vector representing the point.
+ * After this method returns, the length of the vector will be 1 (unless both coordinates are
+ * zero - the zero point will be returned then). The function tries to handle infinite
+ * coordinates gracefully. If any of the coordinates are NaN, the function will do nothing.
+ * @post \f$-\epsilon < \left|this\right| - 1 < \epsilon\f$
+ * @see unit_vector(Geom::Point const &) */
void Point::normalize() {
double len = hypot(_pt[0], _pt[1]);
if(len == 0) return;
if(IS_NAN(len)) return;
- static double const inf = 1e400;
+ static double const inf = HUGE_VAL;
if(len != inf) {
*this /= len;
} else {
@@ -58,7 +79,10 @@ void Point::normalize() {
}
}
-/** Compute the L1 norm, or manhattan distance, of \a p. */
+/** @brief Compute the first norm (Manhattan distance) of @a p.
+ * This is equal to the sum of absolutes values of the coordinates.
+ * @return \f$|p_X| + |p_Y|\f$
+ * @relates Point */
Coord L1(Point const &p) {
Coord d = 0;
for ( int i = 0 ; i < 2 ; i++ ) {
@@ -67,7 +91,9 @@ Coord L1(Point const &p) {
return d;
}
-/** Compute the L infinity, or maximum, norm of \a p. */
+/** @brief Compute the infinity norm (maximum norm) of @a p.
+ * @return \f$\max(p_X, p_Y)\f$
+ * @relates Point */
Coord LInfty(Point const &p) {
Coord const a(fabs(p[0]));
Coord const b(fabs(p[1]));
@@ -76,80 +102,83 @@ Coord LInfty(Point const &p) {
: a );
}
-/** Returns true iff p is a zero vector, i.e.\ Point(0, 0).
- *
- * (NaN is considered non-zero.)
- */
-bool
-is_zero(Point const &p)
-{
+/** @brief True if the point has both coordinates zero.
+ * NaNs are treated as not equal to zero.
+ * @relates Point */
+bool is_zero(Point const &p) {
return ( p[0] == 0 &&
p[1] == 0 );
}
-bool
-is_unit_vector(Point const &p)
-{
- return fabs(1.0 - L2(p)) <= 1e-4;
- /* The tolerance of 1e-4 is somewhat arbitrary. Point::normalize is believed to return
- points well within this tolerance. I'm not aware of any callers that want a small
- tolerance; most callers would be ok with a tolerance of 0.25. */
+/** @brief True if the point has a length near 1. The are_near() function is used.
+ * @relates Point */
+bool is_unit_vector(Point const &p) {
+ return are_near(L2(p), 1.0);
}
-
-Coord atan2(Point const p) {
+/** @brief Return the angle between the point and the +X axis.
+ * @return Angle in \f$(-\pi, \pi]\f$.
+ * @relates Point */
+Coord atan2(Point const &p) {
return std::atan2(p[Y], p[X]);
}
-/** compute the angle turning from a to b. This should give \f$\pi/2\f$ for angle_between(a, rot90(a));
- * This works by projecting b onto the basis defined by a, rot90(a)
- */
-Coord angle_between(Point const a, Point const b) {
+/** @brief Compute the angle between a and b relative to the origin.
+ * The computation is done by projecting b onto the basis defined by a, rot90(a).
+ * @return Angle in \f$(-\pi, \pi]\f$.
+ * @relates Point */
+Coord angle_between(Point const &a, Point const &b) {
return std::atan2(cross(b,a), dot(b,a));
}
-
-
-/** Returns a version of \a a scaled to be a unit vector (within rounding error).
- *
- * The current version tries to handle infinite coordinates gracefully,
- * but it's not clear that any callers need that.
- *
- * \pre a != Point(0, 0).
- * \pre Neither coordinate is NaN.
- * \post L2(ret) very near 1.0.
- */
+/** @brief Create a normalized version of a point.
+ * This is equivalent to copying the point and calling its normalize() method.
+ * The returned point will be (0,0) if the argument has both coordinates equal to zero.
+ * If any coordinate is NaN, this function will do nothing.
+ * @param a Input point
+ * @return Point on the unit circle in the same direction from origin as a, or the origin
+ * if a has both coordinates equal to zero
+ * @relates Point */
Point unit_vector(Point const &a)
{
Point ret(a);
ret.normalize();
return ret;
}
-
+/** @brief Return the "absolute value" of the point's vector.
+ * This is defined in terms of the default lexicographical ordering. If the point is "larger"
+ * that the origin (0, 0), its negation is returned. This corresponds to making the Y coordinate
+ * positive. You can check whether the points' vectors have the same direction (e.g. lie
+ * on the same line passing through the origin) using
+ * @code abs(a).normalize() == abs(b).normalize() @endcode.
+ * To check with some margin of error, use
+ * @code are_near(abs(a).normalize(), abs(b).normalize()) @endcode.
+ * Although naively this should take the absolute value of each coordinate, such an operation
+ * is not very useful.
+ * @return \f$p' = (p_X, -p_Y)\f$
+ * @relates Point */
Point abs(Point const &b)
{
- Point ret;
- for ( int i = 0 ; i < 2 ; i++ ) {
- ret[i] = fabs(b[i]);
- }
+ Point ret = b;
+ ret[Y] = fabs(ret[Y]);
return ret;
}
-Point operator*(Point const &v, Matrix const &m) {
- Point ret;
+/** @brief Transform the point by the specified matrix. */
+Point &Point::operator*=(Affine const &m) {
+ double x = _pt[X], y = _pt[Y];
for(int i = 0; i < 2; i++) {
- ret[i] = v[X] * m[i] + v[Y] * m[i + 2] + m[i + 4];
+ _pt[i] = x * m[i] + y * m[i + 2] + m[i + 4];
}
- return ret;
-}
-
-Point operator/(Point const &p, Matrix const &m) { return p * m.inverse(); }
-
-Point &Point::operator*=(Matrix const &m)
-{
- *this = *this * m;
return *this;
}
+/** @brief Snap the angle B - A - dir to miltiples of \f$2\pi/n\f$.
+ * The 'dir' argument must be normalized (have an unit length), otherwise the result
+ * is undefined.
+ * @return Point with the same distance from A as B, with a snapped angle.
+ * @post distance(A, B) == distance(A, result)
+ * @post angle_between(result - A, dir) == \f$2k\pi/n, k \in \mathbb{N}\f$
+ * @relates Point */
Point constrain_angle(Point const &A, Point const &B, unsigned int n, Point const &dir)
{
// for special cases we could perhaps use explicit testing (which might be faster)
diff --git a/src/2geom/point.h b/src/2geom/point.h
index 562950525..97f5142e3 100644
--- a/src/2geom/point.h
+++ b/src/2geom/point.h
@@ -1,241 +1,367 @@
-#ifndef SEEN_Geom_POINT_H
-#define SEEN_Geom_POINT_H
-
/**
* \file
- * \brief Defines a Cartesian 2D Point class.
+ * \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.
*/
-#include <iostream>
+#ifndef SEEN_Geom_POINT_H
+#define SEEN_Geom_POINT_H
+#include "config.h"
+#include <iostream>
+#include <iterator>
+#include <boost/operators.hpp>
+#include <2geom/forward.h>
#include <2geom/coord.h>
-#include <2geom/isnan.h>
-#include <2geom/utils.h>
+#include <2geom/isnan.h> //temporary fix for isnan()
+#include <2geom/math-utils.h>
namespace Geom {
-enum Dim2 { X=0, Y=1 };
-
-class Matrix;
-
-/// Cartesian 2D point.
-class Point {
+class Point
+ : boost::additive< Point
+ , boost::totally_ordered< Point
+ , boost::multiplicative< Point, Coord
+ , boost::multiplicative< Point, Affine
+ , boost::multiplicative< Point, Translate
+ , boost::multiplicative< Point, Rotate
+ , boost::multiplicative< Point, Scale
+ , boost::multiplicative< Point, HShear
+ , boost::multiplicative< Point, VShear
+ > > > > > > > > > // this uses chaining so it looks weird, but works
+{
Coord _pt[2];
- public:
- /// The default constructor creates an Point(0,0) DO NOT RELY ON THIS, BEST NOT TO USE THIS CONSTRUCTOR
- inline Point()
+public:
+ /** Construct a point on the origin. */
+ Point()
{ _pt[X] = _pt[Y] = 0; }
- inline Point(Coord x, Coord y) {
+ /** Construct a point from its coordinates. */
+ Point(Coord x, Coord y) {
_pt[X] = x; _pt[Y] = y;
}
-
- inline Point(Point const &p) {
+ Point(Point const &p) {
for (unsigned i = 0; i < 2; ++i)
_pt[i] = p._pt[i];
}
-
- inline Point &operator=(Point const &p) {
+ Point &operator=(Point const &p) {
for (unsigned i = 0; i < 2; ++i)
_pt[i] = p._pt[i];
return *this;
}
- inline Coord operator[](unsigned i) const { return _pt[i]; }
- inline Coord &operator[](unsigned i) { return _pt[i]; }
+ /// @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 throw() { return _pt[d]; }
Coord &operator[](Dim2 d) throw() { return _pt[d]; }
+ /// @}
- static inline Point polar(Coord angle, Coord radius) {
- return Point(radius * std::cos(angle), radius * std::sin(angle));
- }
-
- inline Coord length() const { return hypot(_pt[0], _pt[1]); }
+ /// @name Vector operations
+ /// @{
+ /** @brief Compute the distance from origin.
+ * @return Length of the vector from origin to this point */
+ Coord length() const { return hypot(_pt[0], _pt[1]); }
+ void normalize();
- /** 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.)
- **/
+ /** @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]);
}
- /** 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.)
- **/
+ /** @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]);
}
+ /// @}
- /**
- \brief A function to lower the precision of the point
- \param places The number of decimal places that should be in
- the final number.
- */
- inline void round (int places = 0) {
- _pt[X] = (Coord)(decimal_round((double)_pt[X], places));
- _pt[Y] = (Coord)(decimal_round((double)_pt[Y], places));
- return;
- }
-
- void normalize();
-
- inline bool isFinite() const {
- for ( unsigned i = 0 ; i < 2 ; ++i ) {
- if(!IS_FINITE(_pt[i])) return false;
- }
- return true;
- }
-
- inline Point operator+(Point const &o) const {
- return Point(_pt[X] + o._pt[X], _pt[Y] + o._pt[Y]);
- }
- inline Point operator-(Point const &o) const {
- return Point(_pt[X] - o._pt[X], _pt[Y] - o._pt[Y]);
+ /// @name Vector-like arithmetic operations
+ /// @{
+ Point operator-() const {
+ return Point(-_pt[X], -_pt[Y]);
}
- inline Point &operator+=(Point const &o) {
+ Point &operator+=(Point const &o) {
for ( unsigned i = 0 ; i < 2 ; ++i ) {
_pt[i] += o._pt[i];
}
return *this;
}
- inline Point &operator-=(Point const &o) {
+ Point &operator-=(Point const &o) {
for ( unsigned i = 0 ; i < 2 ; ++i ) {
_pt[i] -= o._pt[i];
}
return *this;
}
-
- inline Point operator-() const {
- return Point(-_pt[X], -_pt[Y]);
- }
- inline Point operator*(double const s) const {
- return Point(_pt[X] * s, _pt[Y] * s);
- }
- inline Point operator/(double const s) const {
- //TODO: s == 0?
- return Point(_pt[X] / s, _pt[Y] / s);
- }
- inline Point &operator*=(double const s) {
+ Point &operator*=(Coord s) {
for ( unsigned i = 0 ; i < 2 ; ++i ) _pt[i] *= s;
return *this;
}
- inline Point &operator/=(double const s) {
+ Point &operator/=(Coord s) {
//TODO: s == 0?
for ( unsigned i = 0 ; i < 2 ; ++i ) _pt[i] /= s;
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);
+ /** @brief Transform the point by the inverse of the specified matrix. */
+ template <typename T>
+ Point &operator/=(T const &m) {
+ *this *= m.inverse();
+ return *this;
+ }
+ /// @}
+
+ /// @name Various utilities
+ /// @{
+ /** @brief Lower the precision of the point.
+ * This will round both coordinates to multiples of \f$10^p\f$. */
+ void round (int p = 0) {
+ _pt[X] = (Coord)(decimal_round((double)_pt[X], p));
+ _pt[Y] = (Coord)(decimal_round((double)_pt[Y], p));
+ return;
+ }
- Point &operator*=(Matrix const &m);
-
- inline int operator == (const Point &in_pnt) {
+ /** @brief Check whether both coordinates are finite.
+ * @return True if neither coordinate is infinite. */
+ bool isFinite() const {
+ for ( unsigned i = 0 ; i < 2 ; ++i ) {
+ if(!IS_FINITE(_pt[i])) return false;
+ }
+ return true;
+ }
+ /** @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] )));
+ }
+ /// @}
+
+ /// @name Point factories
+ /// @{
+ /** @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) {
+ Point ret;
+ sincos(angle, ret[Y], ret[X]);
+ return ret;
+ }
+ /// @}
+
+ /** @brief Lexicographical ordering functor. */
+ template <Dim2 d> struct LexOrder;
+ /** @brief Lexicographical ordering functor with runtime dimension. */
+ class LexOrderRt {
+ public:
+ LexOrderRt(Dim2 d) : dim(d) {}
+ inline bool operator()(Point const &a, Point const &b);
+ private:
+ Dim2 dim;
+ };
friend inline std::ostream &operator<< (std::ostream &out_file, const Geom::Point &in_pnt);
};
-inline Point operator*(double const s, Point const &p) { return p * s; }
-
-/** A function to print out the Point. It just prints out the coords
- on the given output stream */
+/** @brief Output operator for points.
+ * Prints out the coordinates.
+ * @relates Point */
inline std::ostream &operator<< (std::ostream &out_file, const Geom::Point &in_pnt) {
out_file << "X: " << in_pnt[X] << " Y: " << in_pnt[Y];
return out_file;
}
-/** This is a rotation (sort of). */
-inline Point operator^(Point const &a, Point const &b) {
- Point const ret(a[0] * b[0] - a[1] * b[1],
- a[1] * b[0] + a[0] * b[1]);
- return ret;
+template<> struct Point::LexOrder<X> {
+ bool operator()(Point const &a, Point const &b) {
+ return a[X] < b[X] || (a[X] == b[X] && a[Y] < b[Y]);
+ }
+};
+template<> struct Point::LexOrder<Y> {
+ bool operator()(Point const &a, Point const &b) {
+ return a[Y] < b[Y] || (a[Y] == b[Y] && a[X] < b[X]);
+ }
+};
+inline bool Point::LexOrderRt::operator()(Point const &a, Point const &b) {
+ return dim ? Point::LexOrder<Y>()(a, b) : Point::LexOrder<X>()(a, b);
}
-//IMPL: boost::EqualityComparableConcept
-inline bool operator==(Point const &a, Point const &b) {
- return (a[X] == b[X]) && (a[Y] == b[Y]);
-}
-inline bool operator!=(Point const &a, Point const &b) {
- return (a[X] != b[X]) || (a[Y] != b[Y]);
+/** @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();
}
-/** This is a lexicographical ordering for points. It is remarkably useful for sweepline algorithms*/
-inline bool operator<=(Point const &a, Point const &b) {
- return ( ( a[Y] < b[Y] ) ||
- (( a[Y] == b[Y] ) && ( a[X] < b[X] )));
+/** @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];
}
-Coord L1(Point const &p);
-
-/** Compute the L2, or euclidean, norm of \a p. */
-inline Coord L2(Point const &p) { return p.length(); }
-
-/** Compute the square of L2 norm of \a p. Warning: this can overflow where L2 won't.*/
-inline Coord L2sq(Point const &p) { return p[0]*p[0] + p[1]*p[1]; }
-
-double LInfty(Point const &p);
-bool is_zero(Point const &p);
-bool is_unit_vector(Point const &p);
-
-extern double atan2(Point const p);
-/** compute the angle turning from a to b (signed). */
-extern double angle_between(Point const a, Point const b);
-
//IMPL: NearConcept
-inline bool are_near(Point const &a, Point const &b, double const eps=EPSILON) {
+/** @brief Nearness predicate for points.
+ * True if neither coordinate of @a a is further than @a eps from the corresponding
+ * coordinate of @a b.
+ * @relates Point */
+inline bool are_near(Point const &a, Point const &b, double const eps=EPSILON)
+{
return ( are_near(a[X],b[X],eps) && are_near(a[Y],b[Y],eps) );
}
-inline
-Point middle_point(Point const& P1, Point const& P2)
+/** @brief Return a point halfway between the specified ones.
+ * @relates Point */
+inline Point middle_point(Point const& P1, Point const& P2)
{
return (P1 + P2) / 2;
}
-/** Returns p * Geom::rotate_degrees(90), but more efficient.
+/** @brief Returns p * Geom::rotate_degrees(90), but more efficient.
*
- * Angle direction in Inkscape code: If you use the traditional mathematics convention that y
+ * 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 rot_neg90 function: use -rot90(p) instead.
- */
-inline Point rot90(Point const &p) { return Point(-p[Y], p[X]); }
+ * 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]);
+}
-/** Given two points and a parameter t \in [0, 1], return a point
- * proportionally from a to b by t. Akin to 1 degree bezier.*/
-inline Point lerp(double const t, Point const a, Point const b) { return (a * (1 - t) + b * t); }
+/** @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(double const t, Point const &a, Point const &b)
+{
+ return (a * (1 - t) + b * t);
+}
-Point unit_vector(Point const &a);
+/** @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[0] * b[0] + a[1] * b[1];
+}
-/** compute the dot product (inner product) between the vectors a and b. */
-inline Coord dot(Point const &a, Point const &b) { return a[0] * b[0] + a[1] * b[1]; }
-/** Defined as dot(a, b.cw()). */
-inline Coord cross(Point const &a, Point const &b) { return dot(a, b.cw()); }
+/** @brief Compute the 2D cross product.
+ * Defined as dot(a, b.cw()). This means it will be zero for parallel vectors,
+ * and its absolute value highest for perpendicular vectors.
+ * @relates Point*/
+inline Coord cross(Point const &a, Point const &b)
+{
+ return dot(a, b.cw());
+}
-/** compute the euclidean distance between points a and b. TODO: hypot safer/faster? */
-inline Coord distance (Point const &a, Point const &b) { return L2(a - b); }
+/** @brief Compute the (Euclidean) distance between points.
+ * @relates Point */
+inline Coord distance (Point const &a, Point const &b)
+{
+ return L2(a - b);
+}
-/** compute the square of the distance between points a and b. */
-inline Coord distanceSq (Point const &a, Point const &b) { return L2sq(a - b); }
+/** @brief Compute the square of the distance between points.
+ * @relates Point */
+inline Coord distanceSq (Point const &a, Point const &b)
+{
+ return L2sq(a - b);
+}
+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);
+extern double atan2(Point const &p);
+extern double angle_between(Point const &a, Point const &b);
Point abs(Point const &b);
-
-Point operator*(Point const &v, Matrix const &m);
-
-Point operator/(Point const &p, Matrix const &m);
-
-/** Constrains the angle (with respect to dir) of the line
- * joining A and B to a multiple of pi/n.
- */
Point constrain_angle(Point const &A, Point const &B, unsigned int n = 4, Geom::Point const &dir = Geom::Point(1,0));
} /* 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 /* !SEEN_Geom_POINT_H */
/*
diff --git a/src/2geom/poly.cpp b/src/2geom/poly.cpp
index 9fa8e47db..de0229172 100644
--- a/src/2geom/poly.cpp
+++ b/src/2geom/poly.cpp
@@ -1,6 +1,5 @@
#include <2geom/poly.h>
-#define HAVE_GSL
#ifdef HAVE_GSL
#include <gsl/gsl_poly.h>
#endif
diff --git a/src/2geom/poly.h b/src/2geom/poly.h
index e0ba0580f..3567bda6d 100644
--- a/src/2geom/poly.h
+++ b/src/2geom/poly.h
@@ -34,6 +34,7 @@
#ifndef LIB2GEOM_SEEN_POLY_H
#define LIB2GEOM_SEEN_POLY_H
+
#include <assert.h>
#include <vector>
#include <iostream>
@@ -205,11 +206,13 @@ Poly gcd(Poly const &a, Poly const &b, const double tol=1e-10);
*/
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);
inline std::ostream &operator<< (std::ostream &out_file, const Poly &in_poly) {
diff --git a/src/2geom/ray.h b/src/2geom/ray.h
index 3156a944d..638b86195 100644
--- a/src/2geom/ray.h
+++ b/src/2geom/ray.h
@@ -28,21 +28,30 @@
* the specific language governing rights and limitations.
*/
-#ifndef _2GEOM_RAY_H_
-#define _2GEOM_RAY_H_
+#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 <vector>
-
-
namespace Geom
{
-class Ray
-{
+/**
+ * @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 m_origin;
+ Point m_versor;
+
public:
Ray()
: m_origin(0,0), m_versor(1,0)
@@ -183,22 +192,17 @@ public:
return LineSegment(pointAt(f), pointAt(t));
}
- Ray transformed(Matrix const& m) const
+ Ray transformed(Affine const& m) const
{
return Ray(m_origin * m, (m_origin + m_versor) * m);
}
-
-private:
- Point m_origin;
- Point m_versor;
-
}; // end class ray
inline
double distance(Point const& _point, Ray const& _ray)
{
double t = _ray.nearestPoint(_point);
- return distance(_point, _ray.pointAt(t));
+ return ::Geom::distance(_point, _ray.pointAt(t));
}
inline
diff --git a/src/2geom/rect.h b/src/2geom/rect.h
index cce1d64f0..65bb1bb76 100644
--- a/src/2geom/rect.h
+++ b/src/2geom/rect.h
@@ -1,8 +1,7 @@
/**
* \file
- * \brief D2<Interval> specialization to Rect
- */
-/*
+ * \brief Axis-aligned rectangle
+ *//*
* Copyright 2007 Michael Sloan <mgsloan@gmail.com>
*
* This library is free software; you can redistribute it and/or
@@ -28,9 +27,7 @@
* 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:
+ * Authors of original rect class:
* Lauris Kaplinski <lauris@kaplinski.com>
* Nathan Hurst <njh@mail.csse.monash.edu.au>
* bulia byak <buliabyak@users.sf.net>
@@ -39,52 +36,81 @@
#include <2geom/d2.h>
-#ifndef _2GEOM_RECT
-#define _2GEOM_RECT
+#ifndef LIB2GEOM_RECT_H
+#define LIB2GEOM_RECT_H
-#include <2geom/matrix.h>
+#include <2geom/affine.h>
#include <boost/optional/optional.hpp>
namespace Geom {
-/** D2<Interval> specialization to Rect */
+
+/**
+ * @brief Axis-aligned, non-empty rectangle - convenience typedef
+ * @ingroup Primitives
+ */
typedef D2<Interval> Rect;
class OptRect;
-Rect unify(const Rect &, const Rect &);
+inline Rect unify(Rect const &, Rect const &);
+
/**
- * %Rect class.
- * The Rect class is actually a specialisation of D2<Interval>.
- *
+ * @brief Axis aligned, non-empty rectangle.
+ * @ingroup Primitives
*/
template<>
class D2<Interval> {
- private:
+private:
Interval f[2];
- public:
- /** Best not to use this constructor, do not rely on what it initializes the object to.
- *The default constructor creates a rect of default intervals.
- */
+public:
+ /// @name Create rectangles.
+ /// @{
+ /** @brief Create a rectangle that contains only the point at (0,0). */
D2<Interval>() { f[X] = f[Y] = Interval(); }
-
- public:
+ /** @brief Create a rectangle from X and Y intervals. */
D2<Interval>(Interval const &a, Interval const &b) {
f[X] = a;
f[Y] = b;
}
-
+ /** @brief Create a rectangle from two points. */
D2<Interval>(Point const & a, Point const & b) {
f[X] = Interval(a[X], b[X]);
f[Y] = Interval(a[Y], b[Y]);
}
+ /** @brief Create a rectangle from a range of points.
+ * The resulting rectangle will contain all ponts 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 Rect from_range(InputIterator start, InputIterator end) {
+ assert(start != end);
+ Point p1 = *start++;
+ Rect 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 Rect from_array(Point const *c, unsigned n) {
+ Rect result = Rect::from_range(c, c+n);
+ return result;
+ }
+ /// @}
- inline Interval& operator[](unsigned i) { return f[i]; }
- inline Interval const & operator[](unsigned i) const { return f[i]; }
-
- inline Point min() const { return Point(f[X].min(), f[Y].min()); }
- inline Point max() const { return Point(f[X].max(), f[Y].max()); }
+ /// @name Inspect dimensions.
+ /// @{
+ Interval& operator[](unsigned i) { return f[i]; }
+ Interval const & operator[](unsigned i) const { return f[i]; }
- /** Returns the four corners of the rectangle in positive order
- * (clockwise if +Y is up, anticlockwise if +Y is down) */
+ Point min() const { return Point(f[X].min(), f[Y].min()); }
+ Point max() const { return Point(f[X].max(), f[Y].max()); }
+ /** @brief Return the n-th corner of the rectangle.
+ * If the Y axis grows upwards, this returns corners in clockwise order
+ * starting from the lower left. If Y grows downwards, it returns the corners
+ * in counter-clockwise order starting from the upper left. */
Point corner(unsigned i) const {
switch(i % 4) {
case 0: return Point(f[X].min(), f[Y].min());
@@ -95,17 +121,23 @@ class D2<Interval> {
}
//We should probably remove these - they're coord sys gnostic
- inline double top() const { return f[Y].min(); }
- inline double bottom() const { return f[Y].max(); }
- inline double left() const { return f[X].min(); }
- inline double right() const { return f[X].max(); }
-
- inline double width() const { return f[X].extent(); }
- inline double height() const { return f[Y].extent(); }
+ /** @brief Return top coordinate of the rectangle (+Y is downwards). */
+ Coord top() const { return f[Y].min(); }
+ /** @brief Return bottom coordinate of the rectangle (+Y is downwards). */
+ Coord bottom() const { return f[Y].max(); }
+ /** @brief Return leftmost coordinate of the rectangle (+X is to the right). */
+ Coord left() const { return f[X].min(); }
+ /** @brief Return rightmost coordinate of the rectangle (+X is to the right). */
+ Coord right() const { return f[X].max(); }
+
+ Coord width() const { return f[X].extent(); }
+ Coord height() const { return f[Y].extent(); }
- /** Returns a vector from min to max. */
- inline Point dimensions() const { return Point(f[X].extent(), f[Y].extent()); }
- inline Point midpoint() const { return Point(f[X].middle(), f[Y].middle()); }
+ /** @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. */
+ Point dimensions() const { return Point(f[X].extent(), f[Y].extent()); }
+ Point midpoint() const { return Point(f[X].middle(), f[Y].middle()); }
/**
* \brief Compute the area of this rectangle.
@@ -113,39 +145,92 @@ class D2<Interval> {
* Note that a zero area rectangle is not empty - just as the interval [0,0] contains one point, the rectangle [0,0] x [0,0] contains 1 point and no area.
* \retval For a valid return value, the rect must be tested for emptyness first.
*/
- inline double area() const { return f[X].extent() * f[Y].extent(); }
- inline bool hasZeroArea(double eps = EPSILON) const { return (area() <= eps); }
+ /** @brief Compute rectangle's area. */
+ Coord area() const { return f[X].extent() * f[Y].extent(); }
+ /** @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(double eps = EPSILON) const { return (area() <= eps); }
- inline double maxExtent() const { return std::max(f[X].extent(), f[Y].extent()); }
- inline double minExtent() const { return std::min(f[X].extent(), f[Y].extent()); }
+ /** @brief Get the larger extent (width or height) of the rectangle. */
+ Coord maxExtent() const { return std::max(f[X].extent(), f[Y].extent()); }
+ /** @brief Get the smaller extent (width or height) of the rectangle. */
+ Coord minExtent() const { return std::min(f[X].extent(), f[Y].extent()); }
+ /// @}
-// inline bool isEmpty() const {
-// return f[X].isEmpty() || f[Y].isEmpty();
-// }
- inline bool intersects(Rect const &r) const {
- return f[X].intersects(r[X]) && f[Y].intersects(r[Y]);
+ /// @name Test other rectangles and points for inclusion.
+ /// @{
+ /** @brief Check whether the rectangles have any common points. */
+ bool intersects(Rect const &r) const {
+ return f[X].intersects(r[X]) && f[Y].intersects(r[Y]);
}
- inline bool contains(Rect const &r) const {
- return f[X].contains(r[X]) && f[Y].contains(r[Y]);
+ /** @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]);
}
- inline bool contains(Point const &p) const {
+ /** @brief Check whether the rectangle includes all points in the given rectangle. */
+ bool contains(Rect const &r) const {
+ return f[X].contains(r[X]) && f[Y].contains(r[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]);
+ }
+
+ /** @brief Check whether the rectangles have any common points.
+ * A non-empty rectangle will not intersect empty rectangles. */
+ inline bool intersects(OptRect const &r) const;
+ /** @brief Check whether the rectangle includes all points in the given rectangle.
+ * A non-empty rectangle will contain any empty rectangle. */
+ inline bool contains(OptRect const &r) const;
+ /** @brief Check whether the interior includes all points in the given rectangle.
+ * The interior of a non-empty rectangle will contain any empty rectangle. */
+ inline bool interiorContains(OptRect const &r) const;
+
+ /** @brief Check whether the given point is within the rectangle. */
+ bool contains(Point const &p) const {
return f[X].contains(p[X]) && f[Y].contains(p[Y]);
}
+ /** @brief Check whether the given point is in the rectangle's interior.
+ * This means the point must lie within the rectangle but not on its border. */
+ bool interiorContains(Point const &p) const {
+ return f[X].interiorContains(p[X]) && f[Y].interiorContains(p[Y]);
+ }
+ /// @}
- inline void expandTo(Point p) {
- f[X].extendTo(p[X]); f[Y].extendTo(p[Y]);
+ /// @name Modify the rectangle.
+ /// @{
+ /** @brief Enlarge the rectangle to contain the given point. */
+ void expandTo(Point p) {
+ f[X].expandTo(p[X]); f[Y].expandTo(p[Y]);
}
- inline void unionWith(Rect const &b) {
- f[X].unionWith(b[X]); f[Y].unionWith(b[Y]);
+ /** @brief Enlarge the rectangle to contain the given rectangle. */
+ void unionWith(Rect const &b) {
+ f[X].unionWith(b[X]); f[Y].unionWith(b[Y]);
}
+ /** @brief Enlarge the rectangle to contain the given rectangle.
+ * Unioning with an empty rectangle results in no changes. */
void unionWith(OptRect const &b);
-
- inline void expandBy(double amnt) {
- f[X].expandBy(amnt); f[Y].expandBy(amnt);
+
+ //TODO: figure out how these work with negative values and OptRect
+ /** @brief Expand the rectangle in both directions by the specified amount.
+ * Note that this is different from scaling. Negative values wil 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(Coord amount) {
+ f[X].expandBy(amount); f[Y].expandBy(amount);
}
- inline void expandBy(Point const p) {
- f[X].expandBy(p[X]); f[Y].expandBy(p[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(Point const p) {
+ f[X].expandBy(p[X]); f[Y].expandBy(p[Y]);
}
+ /// @}
};
inline Rect unify(Rect const & a, Rect const & b) {
@@ -161,7 +246,7 @@ inline Rect union_list(std::vector<Rect> const &r) {
}
inline
-double distanceSq( Point const& p, Rect const& rect )
+Coord distanceSq( Point const& p, Rect const& rect )
{
double dx = 0, dy = 0;
if ( p[X] < rect.left() )
@@ -187,15 +272,14 @@ double distanceSq( Point const& p, Rect const& rect )
* Returns the smallest distance between p and rect.
*/
inline
-double distance( Point const& p, Rect const& rect )
+Coord distance( Point const& p, Rect const& rect )
{
return std::sqrt(distanceSq(p, rect));
}
/**
- * The OptRect class can represent and empty Rect and non-empty Rects.
- * If OptRect is not empty, it means that both X and Y intervals are not empty.
- *
+ * @brief Axis-aligned rectangle that can be empty.
+ * @ingroup Primitives
*/
class OptRect : public boost::optional<Rect> {
public:
@@ -212,22 +296,25 @@ public:
// else, stay empty.
}
- /**
- * Check whether this OptRect is empty or not.
- */
+ /** @brief Check for emptiness. */
inline bool isEmpty() const { return (*this == false); };
- /**
- * If \c this is empty, copy argument \c b. Otherwise, union with it (and do nothing when \c b is empty)
- */
+ bool intersects(Rect const &r) const { return r.intersects(*this); }
+ bool contains(Rect const &r) const { return *this && (*this)->contains(r); }
+ bool interiorContains(Rect const &r) const { return *this && (*this)->interiorContains(r); }
+
+ bool intersects(OptRect const &r) const { return *this && (*this)->intersects(r); }
+ bool contains(OptRect const &r) const { return *this && (*this)->contains(r); }
+ bool interiorContains(OptRect const &r) const { return *this && (*this)->interiorContains(r); }
+
+ bool contains(Point const &p) const { return *this && (*this)->contains(p); }
+ bool interiorContains(Point const &p) const { return *this && (*this)->contains(p); }
+
inline void unionWith(OptRect const &b) {
- if (b) {
- if (*this) { // check that we are not empty
- (**this)[X].unionWith((*b)[X]);
- (**this)[Y].unionWith((*b)[Y]);
- } else {
- *this = b;
- }
+ if (*this) { // check that we are not empty
+ (*this)->unionWith(b);
+ } else {
+ *this = b;
}
}
};
@@ -256,6 +343,15 @@ inline void Rect::unionWith(OptRect const &b) {
unionWith(*b);
}
}
+inline bool Rect::intersects(OptRect const &r) const {
+ return r && intersects(*r);
+}
+inline bool Rect::contains(OptRect const &r) const {
+ return !r || contains(*r);
+}
+inline bool Rect::interiorContains(OptRect const &r) const {
+ return !r || interiorContains(*r);
+}
} // end namespace Geom
diff --git a/src/2geom/recursive-bezier-intersection.cpp b/src/2geom/recursive-bezier-intersection.cpp
index 421f61308..b4c81e08e 100644
--- a/src/2geom/recursive-bezier-intersection.cpp
+++ b/src/2geom/recursive-bezier-intersection.cpp
@@ -63,7 +63,7 @@ void
find_intersections_bezier_recursive( std::vector<std::pair<double, double> > &xs,
vector<Geom::Point> const & A,
vector<Geom::Point> const & B,
- double precision) {
+ double /*precision*/) {
OldBezier a, b;
a.p = A;
b.p = B;
@@ -317,9 +317,9 @@ double Lmax(Point p) {
return std::max(fabs(p[X]), fabs(p[Y]));
}
-unsigned wangs_theorem(OldBezier a) {
+unsigned wangs_theorem(OldBezier /*a*/) {
return 6; // seems a good approximation!
- double la1 = Lmax( ( a.p[2] - a.p[1] ) - (a.p[1] - a.p[0]) );
+ /*double la1 = Lmax( ( a.p[2] - a.p[1] ) - (a.p[1] - a.p[0]) );
double la2 = Lmax( ( a.p[3] - a.p[2] ) - (a.p[2] - a.p[1]) );
double l0 = std::max(la1, la2);
unsigned ra;
@@ -328,7 +328,7 @@ unsigned wangs_theorem(OldBezier a) {
else
ra = (unsigned)ceil( log4( M_SQRT2 * 6.0 / 8.0 * INV_EPS * l0 ) );
//std::cout << ra << std::endl;
- return ra;
+ return ra;*/
}
struct rparams
@@ -337,7 +337,7 @@ struct rparams
OldBezier &B;
};
-static int
+/*static int
intersect_polish_f (const gsl_vector * x, void *params,
gsl_vector * f)
{
@@ -351,22 +351,22 @@ intersect_polish_f (const gsl_vector * x, void *params,
gsl_vector_set (f, 1, dx[1]);
return GSL_SUCCESS;
-}
+}*/
-union dbl_64{
+/*union dbl_64{
long long i64;
double d64;
-};
+};*/
-static double EpsilonBy(double value, int eps)
+/*static double EpsilonBy(double value, int eps)
{
dbl_64 s;
s.d64 = value;
s.i64 += eps;
return s.d64;
-}
-
+}*/
+/*
static void intersect_polish_root (OldBezier &A, double &s,
OldBezier &B, double &t) {
const gsl_multiroot_fsolver_type *T;
@@ -394,7 +394,7 @@ static void intersect_polish_root (OldBezier &A, double &s,
iter++;
status = gsl_multiroot_fsolver_iterate (sol);
- if (status) /* check if solver is stuck */
+ if (status) // check if solver is stuck
break;
status =
@@ -439,7 +439,7 @@ static void intersect_polish_root (OldBezier &A, double &s,
best_v = trial_v;
}
}
-}
+}*/
void find_intersections_bezier_recursive( std::vector<std::pair<double, double> > &xs,
diff --git a/src/2geom/region.cpp b/src/2geom/region.cpp
index 3c8c7fd1c..8cfb1c68c 100644
--- a/src/2geom/region.cpp
+++ b/src/2geom/region.cpp
@@ -5,9 +5,9 @@
namespace Geom {
-Region Region::operator*(Matrix const &m) const {
+Region Region::operator*(Affine const &m) const {
Region r((m.flips() ? boundary.reverse() : boundary) * m, fill);
- if(box && m.onlyScaleAndTranslation()) r.box = (*box) * m;
+ if(box && m.isZoom()) r.box = (*box) * m;
return r;
}
diff --git a/src/2geom/region.h b/src/2geom/region.h
index 937817595..e23d6a158 100644
--- a/src/2geom/region.h
+++ b/src/2geom/region.h
@@ -81,7 +81,7 @@ class Region {
Region inverse() const { return Region(boundary.reverse(), box, !fill); }
- Region operator*(Matrix const &m) const;
+ Region operator*(Affine const &m) const;
bool invariants() const;
};
diff --git a/src/2geom/sbasis-curve.h b/src/2geom/sbasis-curve.h
index 6641c0fe1..22fe4fc1f 100644
--- a/src/2geom/sbasis-curve.h
+++ b/src/2geom/sbasis-curve.h
@@ -1,12 +1,13 @@
/**
* \file
- * \brief Symmetric Power Basis Curve
- *
+ * \brief Symmetric power basis curve
+ *//*
* Authors:
- * MenTaLguY <mental@rydia.net>
- * Marco Cecchetti <mrcekets at gmail.com>
+ * MenTaLguY <mental@rydia.net>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
*
- * Copyright 2007-2008 authors
+ * Copyright 2007-2009 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -32,75 +33,77 @@
* the specific language governing rights and limitations.
*/
-
-
-
#ifndef _2GEOM_SBASIS_CURVE_H_
#define _2GEOM_SBASIS_CURVE_H_
-
#include <2geom/curve.h>
-
+#include <2geom/nearest-point.h>
+#include <2geom/sbasis-geometric.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 combine the properties of Bezier curves
+ * (geometric interpretation of parameters, numerical stability near ends of the curve)
+ * and the monomial basis (fast evaluation). They are the main representation of curves
+ * in 2Geom.
+ *
+ * @ingroup Curves
+ */
class SBasisCurve : public Curve {
-
+
private:
- SBasisCurve();
- D2<SBasis> inner;
+ SBasisCurve();
+ D2<SBasis> inner;
public:
- explicit SBasisCurve(D2<SBasis> const &sb) : inner(sb) {}
- explicit SBasisCurve(Curve const &other) : inner(other.toSBasis()) {}
- Curve *duplicate() const { return new SBasisCurve(*this); }
-
- Point initialPoint() const { return inner.at0(); }
- Point finalPoint() const { return inner.at1(); }
- bool isDegenerate() const { return inner.isConstant(); }
- Point pointAt(Coord t) const { return inner.valueAt(t); }
- std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const {
- return inner.valueAndDerivatives(t, n);
- }
- double valueAt(Coord t, Dim2 d) const { return inner[d].valueAt(t); }
-
- void setInitial(Point v) { for(unsigned d = 0; d < 2; d++) { inner[d][0][0] = v[d]; } }
- void setFinal(Point v) { for(unsigned d = 0; d < 2; d++) { inner[d][0][1] = v[d]; } }
-
- virtual OptRect boundsFast() const { return bounds_fast(inner); }
- virtual OptRect boundsExact() const { return bounds_exact(inner); }
- virtual OptRect boundsLocal(OptInterval i, unsigned deg) const { return bounds_local(inner, i, deg); }
-
- std::vector<double> roots(double v, Dim2 d) const { return Geom::roots(inner[d] - v); }
-
- double nearestPoint( Point const& p, double from = 0, double to = 1 ) const
- {
- return nearest_point(p, inner, from, to);
- }
-
- std::vector<double>
- allNearestPoints( Point const& p, double from = 0, double to = 1 ) const
- {
- return all_nearest_points(p, inner, from, to);
- }
-
- Curve *portion(double f, double t) const {
- return new SBasisCurve(Geom::portion(inner, f, t));
- }
-
- Curve *transformed(Matrix const &m) const {
- return new SBasisCurve(inner * m);
- }
-
- Curve *derivative() const {
- return new SBasisCurve(Geom::derivative(inner));
- }
-
- D2<SBasis> toSBasis() const { return inner; }
-
- virtual int degreesOfFreedom() const { return inner[0].degreesOfFreedom() + inner[1].degreesOfFreedom();
- }
+ explicit SBasisCurve(D2<SBasis> const &sb) : inner(sb) {}
+ explicit SBasisCurve(Curve const &other) : inner(other.toSBasis()) {}
+
+ virtual Curve *duplicate() const { return new SBasisCurve(*this); }
+ virtual Point initialPoint() const { return inner.at0(); }
+ virtual Point finalPoint() const { return inner.at1(); }
+ virtual bool isDegenerate() const { return inner.isConstant(); }
+ virtual Point pointAt(Coord t) const { return inner.valueAt(t); }
+ virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const {
+ return inner.valueAndDerivatives(t, n);
+ }
+ virtual Coord valueAt(Coord t, Dim2 d) const { return inner[d].valueAt(t); }
+ virtual void setInitial(Point const &v) {
+ for (unsigned d = 0; d < 2; d++) { inner[d][0][0] = v[d]; }
+ }
+ virtual void setFinal(Point const &v) {
+ for (unsigned d = 0; d < 2; d++) { inner[d][0][1] = v[d]; }
+ }
+ virtual Rect boundsFast() const { return *bounds_fast(inner); }
+ virtual Rect boundsExact() const { return *bounds_exact(inner); }
+ virtual OptRect boundsLocal(OptInterval const &i, unsigned deg) const {
+ return bounds_local(inner, i, deg);
+ }
+ virtual std::vector<Coord> roots(Coord v, Dim2 d) const { return Geom::roots(inner[d] - v); }
+ virtual Coord nearestPoint( Point const& p, Coord from = 0, Coord to = 1 ) const {
+ return nearest_point(p, inner, from, to);
+ }
+ virtual std::vector<Coord> allNearestPoints( Point const& p, Coord from = 0,
+ Coord to = 1 ) const
+ {
+ return all_nearest_points(p, inner, from, to);
+ }
+ virtual Coord length(Coord tolerance) const { return ::Geom::length(inner, tolerance); }
+ virtual Curve *portion(Coord f, Coord t) const {
+ return new SBasisCurve(Geom::portion(inner, f, t));
+ }
+ virtual Curve *transformed(Affine const &m) const { return new SBasisCurve(inner * m); }
+ virtual Curve *derivative() const {
+ return new SBasisCurve(Geom::derivative(inner));
+ }
+ virtual D2<SBasis> toSBasis() const { return inner; }
+ virtual int degreesOfFreedom() const {
+ return inner[0].degreesOfFreedom() + inner[1].degreesOfFreedom();
+ }
};
diff --git a/src/2geom/sbasis-geometric.cpp b/src/2geom/sbasis-geometric.cpp
index 3fd667224..f4b445faa 100644
--- a/src/2geom/sbasis-geometric.cpp
+++ b/src/2geom/sbasis-geometric.cpp
@@ -151,7 +151,7 @@ Geom::cutAtRoots(Piecewise<D2<SBasis> > const &M, double ZERO){
\param vect a piecewise parameteric curve.
\param tol the maximum error allowed.
\param order the maximum degree to use for approximation
-
+ \relates Piecewise
*/
Piecewise<SBasis>
Geom::atan2(Piecewise<D2<SBasis> > const &vect, double tol, unsigned order){
@@ -182,7 +182,7 @@ Geom::atan2(Piecewise<D2<SBasis> > const &vect, double tol, unsigned order){
\param vect a piecewise parameteric curve.
\param tol the maximum error allowed.
\param order the maximum degree to use for approximation
-
+ \relates Piecewise, D2
*/
Piecewise<SBasis>
Geom::atan2(D2<SBasis> const &vect, double tol, unsigned order){
@@ -193,7 +193,7 @@ Geom::atan2(D2<SBasis> const &vect, double tol, unsigned order){
\param angle a piecewise function of angle wrt t.
\param tol the maximum error allowed.
\param order the maximum degree to use for approximation
-
+ \relates D2, SBasis
*/
D2<Piecewise<SBasis> >
Geom::tan2(SBasis const &angle, double tol, unsigned order){
@@ -204,7 +204,7 @@ Geom::tan2(SBasis const &angle, double tol, unsigned order){
\param angle a piecewise function of angle wrt t.
\param tol the maximum error allowed.
\param order the maximum degree to use for approximation
-
+ \relates Piecewise, D2
*/
D2<Piecewise<SBasis> >
Geom::tan2(Piecewise<SBasis> const &angle, double tol, unsigned order){
@@ -218,6 +218,8 @@ Geom::tan2(Piecewise<SBasis> const &angle, double tol, unsigned order){
unitVector(x,y) is computed as (b,-a) where a and b are solutions of:
ax+by=0 (eqn1) and a^2+b^2=1 (eqn2)
+
+ \relates Piecewise, D2
*/
Piecewise<D2<SBasis> >
Geom::unitVector(D2<SBasis> const &V_in, double tol, unsigned order){
@@ -296,6 +298,8 @@ Geom::unitVector(D2<SBasis> const &V_in, double tol, unsigned order){
unitVector(x,y) is computed as (b,-a) where a and b are solutions of:
ax+by=0 (eqn1) and a^2+b^2=1 (eqn2)
+
+ \relates Piecewise
*/
Piecewise<D2<SBasis> >
Geom::unitVector(Piecewise<D2<SBasis> > const &V, double tol, unsigned order){
@@ -314,7 +318,7 @@ Geom::unitVector(Piecewise<D2<SBasis> > const &V, double tol, unsigned order){
/** returns a function giving the arclength at each point in M.
\param M the Element.
\param tol the maximum error allowed.
-
+ \relates Piecewise
*/
Piecewise<SBasis>
Geom::arcLengthSb(Piecewise<D2<SBasis> > const &M, double tol){
@@ -328,7 +332,7 @@ Geom::arcLengthSb(Piecewise<D2<SBasis> > const &M, double tol){
/** returns a function giving the arclength at each point in M.
\param M the Element.
\param tol the maximum error allowed.
-
+ \relates Piecewise, D2
*/
Piecewise<SBasis>
Geom::arcLengthSb(D2<SBasis> const &M, double tol){
@@ -353,9 +357,8 @@ Geom::length(Piecewise<D2<SBasis> > const &M,
/** returns a function giving the curvature at each point in M.
\param M the Element.
\param tol the maximum error allowed.
-
- Todo:
- * claimed incomplete. Check.
+ \relates Piecewise, D2
+ \todo claimed incomplete. Check.
*/
Piecewise<SBasis>
Geom::curvature(D2<SBasis> const &M, double tol) {
@@ -371,9 +374,8 @@ Geom::curvature(D2<SBasis> const &M, double tol) {
/** returns a function giving the curvature at each point in M.
\param M the Element.
\param tol the maximum error allowed.
-
- Todo:
- * claimed incomplete. Check.
+ \relates Piecewise
+ \todo claimed incomplete. Check.
*/
Piecewise<SBasis>
Geom::curvature(Piecewise<D2<SBasis> > const &V, double tol){
@@ -395,7 +397,7 @@ Geom::curvature(Piecewise<D2<SBasis> > const &V, double tol){
\param M the Element.
\param tol the maximum error allowed.
\param order the maximum degree to use for approximation
-
+ \relates Piecewise, D2
*/
Piecewise<D2<SBasis> >
Geom::arc_length_parametrization(D2<SBasis> const &M,
@@ -424,7 +426,7 @@ Geom::arc_length_parametrization(D2<SBasis> const &M,
\param M the Element.
\param tol the maximum error allowed.
\param order the maximum degree to use for approximation
-
+ \relates Piecewise
*/
Piecewise<D2<SBasis> >
Geom::arc_length_parametrization(Piecewise<D2<SBasis> > const &M,
@@ -449,7 +451,7 @@ static double sb_length_integrating(double t, void* param) {
\param tol the maximum error allowed.
\param result variable to be incremented with the length of the path
\param abs_error variable to be incremented with the estimated error
-
+ \relates D2
If you only want the length, this routine may be faster/more accurate.
*/
void Geom::length_integrating(D2<SBasis> const &B, double &result, double &abs_error, double tol) {
@@ -474,7 +476,7 @@ void Geom::length_integrating(D2<SBasis> const &B, double &result, double &abs_e
/** Calculates the length of a D2<SBasis> through gsl integration.
\param s the Element.
\param tol the maximum error allowed.
-
+ \relates D2
If you only want the total length, this routine faster and more accurate than constructing an arcLengthSb.
*/
double
@@ -488,7 +490,7 @@ Geom::length(D2<SBasis> const &s,
/** Calculates the length of a Piecewise<D2<SBasis> > through gsl integration.
\param s the Element.
\param tol the maximum error allowed.
-
+ \relates Piecewise
If you only want the total length, this routine faster and more accurate than constructing an arcLengthSb.
*/
double
@@ -507,7 +509,7 @@ Geom::length(Piecewise<D2<SBasis> > const &s,
\param p the Element.
\param centroid on return contains the centroid of the shape
\param area on return contains the signed area of the shape.
-
+ \relates Piecewise
This approach uses green's theorem to compute the area and centroid using integrals. For curved shapes this is much faster than converting to polyline. Note that without an uncross operation the output is not the absolute area.
* Returned values:
@@ -620,6 +622,8 @@ solve_lambda0(double a0,double a1,double c0,double c1,
* proportional to the given ones.
* If insist_on_speed_signs == 0, allow speeds to point in the opposite direction (both at the same time)
* If insist_on_speed_signs == -1, allow speeds to point in both direction independantly.
+*
+* \relates D2
*/
std::vector<D2<SBasis> >
Geom::cubics_fitting_curvature(Point const &M0, Point const &M1,
@@ -743,6 +747,7 @@ Geom::cubics_with_prescribed_curvature(Point const &M0, Point const &M1,
/**
* \brief returns all the parameter values of A whose tangent passes through P.
+* \relates D2
*/
std::vector<double> find_tangents(Point P, D2<SBasis> const &A) {
SBasis crs (cross(A - P, derivative(A)));
diff --git a/src/2geom/sbasis-roots.cpp b/src/2geom/sbasis-roots.cpp
index 95fd0cf3b..1b870d88f 100644
--- a/src/2geom/sbasis-roots.cpp
+++ b/src/2geom/sbasis-roots.cpp
@@ -74,7 +74,7 @@ OptInterval bounds_exact(SBasis const &a) {
SBasis df = derivative(a);
vector<double>extrema = roots(df);
for (unsigned i=0; i<extrema.size(); i++){
- result.extendTo(a(extrema[i]));
+ result.expandTo(a(extrema[i]));
}
return result;
}
@@ -326,6 +326,211 @@ std::vector<std::vector<double> > multi_roots(SBasis const &f,
return(roots);
}
+
+
+static bool compareIntervalMin( Interval I, Interval J ){
+ return I.min()<J.min();
+}
+static bool compareIntervalMax( Interval I, Interval J ){
+ return I.max()<J.max();
+}
+
+//find the first interval whose max is >= x
+static unsigned upper_level(vector<Interval> const &levels, double x ){
+ return( lower_bound( levels.begin(), levels.end(), Interval(x,x), compareIntervalMax) - levels.begin() );
+}
+
+static std::vector<Interval> fuseContiguous(std::vector<Interval> const &sets, double tol=0.){
+ std::vector<Interval> result;
+ if (sets.size() == 0 ) return result;
+ result.push_back( sets.front() );
+ for (unsigned i=1; i < sets.size(); i++ ){
+ if ( result.back().max() + tol >= sets[i].min() ){
+ result.back().unionWith( sets[i] );
+ }else{
+ result.push_back( sets[i] );
+ }
+ }
+ return result;
+}
+
+/** level_sets internal method.
+* algorithm: (~adaptation of Newton method versus 'accroissements finis')
+ -compute f at both ends of the given segment [a,b].
+ -compute bounds m<df(t)<M for df on the segment.
+ Suppose f(a) is between two 'levels' c and C. Then
+ f wont enter c before a + (f(a)-c.max())/m
+ f wont enter C before a + (C.min()-f(a))/M
+ From this we conclude nothing happens before a'=a+min((f(a)-c.max())/m,(C.min()-f(a))/M).
+ We do the same for b: compute some b' such that nothing happens in (b',b].
+ -if [a',b'] is not empty, repeat the process with [a',(a'+b')/2] and [(a'+b')/2,b'].
+
+ If f(a) or f(b) belongs to some 'level' C, then use the same argument to find a' or b' such
+ that f remains in C on [a,a'] or [b',b]. In case f is monotonic, we also know f won't enter another
+ level before or after some time, allowing us to restrict the search a little more.
+
+ unfortunately, extra care is needed about rounding errors, and also to avoid the repetition of roots,
+ making things tricky and unpleasant...
+*/
+
+static void level_sets_internal(SBasis const &f,
+ SBasis const &df,
+ std::vector<Interval> const &levels,
+ std::vector<std::vector<Interval> > &solsets,
+ double a,
+ double fa,
+ double b,
+ double fb,
+ double tol=1e-5){
+
+ if (f.size()==0){
+ unsigned idx;
+ idx=upper_level( levels, 0. );
+ if (idx<levels.size() && levels[idx].contains(0.)){
+ solsets[idx].push_back( Interval(a,b) ) ;
+ }
+ return;
+ }
+
+ unsigned idxa=upper_level(levels,fa);
+ unsigned idxb=upper_level(levels,fb);
+
+ Interval bs = *bounds_local(df,Interval(a,b));
+
+ //first times when a level (higher or lower) can be reached from a or b.
+ double ta_hi; // f remains below next level for t<ta_hi
+ double ta_lo; // f remains above prev level for t<ta_lo
+ double tb_hi; // f remains below next level for t>tb_hi
+ double tb_lo; // f remains above next level for t>tb_lo
+
+ ta_hi=ta_lo=b+1;//default values => no root there.
+ tb_hi=tb_lo=a-1;//default values => no root there.
+
+ //--- if f(a) belongs to a level.-------
+ if ( idxa < levels.size() && levels[idxa].contains( fa ) ){
+ //find the first time when we may exit this level.
+ ta_lo = a + ( levels[idxa].min() - fa)/bs.min();
+ ta_hi = a + ( levels[idxa].max() - fa)/bs.max();
+ if ( ta_lo < a || ta_lo > b ) ta_lo = b;
+ if ( ta_hi < a || ta_hi > b ) ta_hi = b;
+ //move to that time for the next iteration.
+ solsets[idxa].push_back( Interval( a, std::min( ta_lo, ta_hi ) ) );
+ }else{
+ //--- if f(b) does not belong to a level.-------
+ if ( idxa == 0 ){
+ ta_lo = b;
+ }else{
+ ta_lo = a + ( levels[idxa-1].max() - fa)/bs.min();
+ if ( ta_lo < a ) ta_lo = b;
+ }
+ if ( idxa == levels.size() ){
+ ta_hi = b;
+ }else{
+ ta_hi = a + ( levels[idxa].min() - fa)/bs.max();
+ if ( ta_hi < a ) ta_hi = b;
+ }
+ }
+
+ //--- if f(b) belongs to a level.-------
+ if (idxb<levels.size() && levels.at(idxb).contains(fb)){
+ //find the first time from b when we may exit this level.
+ tb_lo = b + ( levels[idxb].min() - fb ) / bs.max();
+ tb_hi = b + ( levels[idxb].max() - fb ) / bs.min();
+ if ( tb_lo > b || tb_lo < a ) tb_lo = a;
+ if ( tb_hi > b || tb_hi < a ) tb_hi = a;
+ //move to that time for the next iteration.
+ solsets[idxb].push_back( Interval( std::max( tb_lo, tb_hi ), b) );
+ }else{
+ //--- if f(b) does not belong to a level.-------
+ if ( idxb == 0 ){
+ tb_lo = a;
+ }else{
+ tb_lo = b + ( levels[idxb-1].max() - fb)/bs.max();
+ if ( tb_lo > b ) tb_lo = a;
+ }
+ if ( idxb == levels.size() ){
+ tb_hi = a;
+ }else{
+ tb_hi = b + ( levels[idxb].min() - fb)/bs.min();
+ if ( tb_hi > b ) tb_hi = a;
+ }
+
+
+ if ( bs.min() < 0 && idxb < levels.size() )
+ tb_hi = b + ( levels[idxb ].min() - fb ) / bs.min();
+ if ( bs.max() > 0 && idxb > 0 )
+ tb_lo = b + ( levels[idxb-1].max() - fb ) / bs.max();
+ }
+
+ //let [t0,t1] be the next interval where to search.
+ double t0=std::min(ta_hi,ta_lo);
+ double t1=std::max(tb_hi,tb_lo);
+
+ if (t0>=t1) return;//no root here.
+
+ //if the interval is smaller than our resolution:
+ //pretend f simultaneously meets all the levels between f(t0) and f(t1)...
+ if ( t1 - t0 <= tol ){
+ Interval f_t0t1 ( f(t0), f(t1) );
+ unsigned idxmin = std::min(idxa, idxb);
+ unsigned idxmax = std::max(idxa, idxb);
+ //push [t0,t1] into all crossed level. Cheat to avoid overlapping intervals on different levels?
+ if ( idxmax > idxmin ){
+ for (unsigned idx = idxmin; idx < idxmax; idx++){
+ solsets[idx].push_back( Interval( t0, t1 ) );
+ }
+ }
+ if ( idxmax < levels.size() && f_t0t1.intersects( levels[idxmax] ) ){
+ solsets[idxmax].push_back( Interval( t0, t1 ) );
+ }
+ return;
+ }
+
+ //To make sure we finally exit the level jump at least by tol:
+ t0 = std::min( std::max( t0, a + tol ), b );
+ t1 = std::max( std::min( t1, b - tol ), a );
+
+ double t =(t0+t1)/2;
+ double ft=f(t);
+ level_sets_internal( f, df, levels, solsets, t0, f(t0), t, ft );
+ level_sets_internal( f, df, levels, solsets, t, ft, t1, f(t1) );
+}
+
+std::vector<std::vector<Interval> > level_sets(SBasis const &f,
+ std::vector<Interval> const &levels,
+ double a, double b, double tol){
+
+ std::vector<std::vector<Interval> > solsets(levels.size(), std::vector<Interval>());
+
+ SBasis df=derivative(f);
+ level_sets_internal(f,df,levels,solsets,a,f(a),b,f(b),tol);
+ // Fuse overlapping intervals...
+ for (unsigned i=0; i<solsets.size(); i++){
+ if ( solsets[i].size() == 0 ) continue;
+ std::sort( solsets[i].begin(), solsets[i].end(), compareIntervalMin );
+ solsets[i] = fuseContiguous( solsets[i], tol );
+ }
+ return solsets;
+}
+
+std::vector<Interval> level_set (SBasis const &f, double level, double vtol, double a, double b, double tol){
+ Interval fat_level( level - vtol, level + vtol );
+ return level_set(f, fat_level, a, b, tol);
+}
+std::vector<Interval> level_set (SBasis const &f, Interval const &level, double a, double b, double tol){
+ std::vector<Interval> levels(1,level);
+ return level_sets(f,levels, a, b, tol).front() ;
+}
+std::vector<std::vector<Interval> > level_sets (SBasis const &f, std::vector<double> const &levels, double vtol, double a, double b, double tol){
+ std::vector<Interval> fat_levels( levels.size(), Interval());
+ for (unsigned i = 0; i < levels.size(); i++){
+ fat_levels[i] = Interval( levels[i]-vtol, levels[i]+vtol);
+ }
+ return level_sets(f, fat_levels, a, b, tol);
+}
+
+
+//-------------------------------------
//-------------------------------------
@@ -358,6 +563,17 @@ std::vector<double> roots1(SBasis const & s) {
return res;
}
+std::vector<double> roots1(SBasis const & s, Interval const ivl) {
+ std::vector<double> res;
+ double d = s[0][0] - s[0][1];
+ if(d != 0) {
+ double r = s[0][0] / d;
+ if(ivl.contains(r))
+ res.push_back(r);
+ }
+ return res;
+}
+
/** Find all t s.t s(t) = 0
\param a sbasis function
\returns vector of zeros (roots)
@@ -377,6 +593,20 @@ std::vector<double> roots(SBasis const & s) {
}
}
}
+std::vector<double> roots(SBasis const & s, Interval const ivl) {
+ switch(s.size()) {
+ case 0:
+ return std::vector<double>();
+ case 1:
+ return roots1(s, ivl);
+ default:
+ {
+ Bezier bz;
+ sbasis_to_bezier(bz, s);
+ return bz.roots(ivl);
+ }
+ }
+}
};
diff --git a/src/2geom/sbasis.h b/src/2geom/sbasis.h
index d7390c64d..b1b0b6c2a 100644
--- a/src/2geom/sbasis.h
+++ b/src/2geom/sbasis.h
@@ -63,7 +63,10 @@ class SBasis : public SBasisN<1>;
namespace Geom{
-/*** An empty SBasis is identically 0. */
+/**
+* \brief S-power basis function class
+*
+* An empty SBasis is identically 0. */
class SBasis{
std::vector<Linear> d;
void push_back(Linear const&l) { d.push_back(l); }
@@ -125,8 +128,9 @@ public:
}
inline bool isConstant() const {
if (empty()) return true;
- for (unsigned i = 0; i < size(); i++) {
- if(!(*this)[i].isConstant()) return false;
+ if(!(*this)[0].isConstant()) return false;
+ for (unsigned i = 1; i < size(); i++) {
+ if(!(*this)[i].isZero()) return false;
}
return true;
}
@@ -188,6 +192,7 @@ 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.
*/
@@ -312,7 +317,8 @@ inline SBasis& operator*=(SBasis& a, SBasis const & b) {
/** 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
+ \return first non zero coefficient
+ \relates SBasis
*/
inline unsigned
valuation(SBasis const &a, double tol=0){
@@ -333,10 +339,10 @@ SBasis inverse(SBasis a, int k);
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 a sbasis function
+ \param t sbasis function
\param from,to interval
- \returns sbasis
-
+ \return sbasis
+ \relates SBasis
*/
inline SBasis portion(const SBasis &t, double from, double to) { return compose(t, Linear(from, to)); }
inline SBasis portion(const SBasis &t, Interval ivl) { return compose(t, Linear(ivl[0], ivl[1])); }
@@ -364,13 +370,75 @@ 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
diff --git a/src/2geom/shape.cpp b/src/2geom/shape.cpp
index 92af814cb..e9f5e55dc 100644
--- a/src/2geom/shape.cpp
+++ b/src/2geom/shape.cpp
@@ -622,7 +622,7 @@ Shape sanitize(std::vector<Path> const & ps) {
/* This transforms a shape by a matrix. In the case that the matrix flips
* the shape, it reverses the paths in order to preserve the fill.
*/
-Shape Shape::operator*(Matrix const &m) const {
+Shape Shape::operator*(Affine const &m) const {
Shape ret;
for(unsigned i = 0; i < size(); i++)
ret.content.push_back(content[i] * m);
diff --git a/src/2geom/shape.h b/src/2geom/shape.h
index 960f9668b..0a7ee9709 100644
--- a/src/2geom/shape.h
+++ b/src/2geom/shape.h
@@ -89,7 +89,7 @@ class Shape {
const Region &operator[](unsigned ix) const { return content[ix]; }
Shape inverse() const;
- Shape operator*(Matrix const &m) const;
+ Shape operator*(Affine const &m) const;
bool contains(Point const &p) const;
diff --git a/src/2geom/solve-bezier-one-d.cpp b/src/2geom/solve-bezier-one-d.cpp
index 876c483fe..c74e3243c 100644
--- a/src/2geom/solve-bezier-one-d.cpp
+++ b/src/2geom/solve-bezier-one-d.cpp
@@ -1,7 +1,12 @@
+
#include <2geom/solver.h>
+#include <2geom/choose.h>
+#include <2geom/bezier.h>
#include <2geom/point.h>
+
+#include <cmath>
#include <algorithm>
-#include <valarray>
+//#include <valarray>
/*** Find the zeros of the bernstein function. The code subdivides until it is happy with the
* linearity of the function. This requires an O(degree^2) subdivision for each step, even when
@@ -11,176 +16,208 @@
namespace Geom{
template<class t>
-static int SGN(t x) { return (x > 0 ? 1 : (x < 0 ? -1 : 0)); }
+static int SGN(t x) { return (x > 0 ? 1 : (x < 0 ? -1 : 0)); }
-const unsigned MAXDEPTH = 23; // Maximum depth for recursion. Using floats means 23 bits precision max
+//const unsigned MAXDEPTH = 23; // Maximum depth for recursion. Using floats means 23 bits precision max
-const double BEPSILON = ldexp(1.0,(-MAXDEPTH-1)); /*Flatness control value */
-const double SECANT_EPSILON = 1e-13; // secant method converges much faster, get a bit more precision
+//const double BEPSILON = ldexp(1.0,(-MAXDEPTH-1)); /*Flatness control value */
+//const double SECANT_EPSILON = 1e-13; // secant method converges much faster, get a bit more precision
/**
* This function is called _a lot_. We have included various manual memory management stuff to reduce the amount of mallocing that goes on. In the future it is possible that this will hurt performance.
**/
class Bernsteins{
public:
- double *Vtemp;
- unsigned N,degree;
+ static const size_t MAX_DEPTH = 22;
+ size_t degree, N;
std::vector<double> &solutions;
- bool use_secant;
- Bernsteins(int degr, std::vector<double> &so) : N(degr+1), degree(degr),solutions(so), use_secant(false) {
- Vtemp = new double[N*2];
- }
- ~Bernsteins() {
- delete[] Vtemp;
+ //std::vector<double> bc;
+ BinomialCoefficient<double> bc;
+
+ Bernsteins(size_t _degree, std::vector<double> & sol)
+ : degree(_degree), N(degree+1), solutions(sol), bc(degree)
+ {
}
+
void subdivide(double const *V,
double t,
double *Left,
double *Right);
- double horner(const double *b, double t);
-
- unsigned
+ unsigned
control_poly_flat_enough(double const *V);
+ double horner(const double *b, double t);
+
+
void
find_bernstein_roots(double const *w, /* The control points */
unsigned depth, /* The depth of the recursion */
double left_t, double right_t);
};
/*
- * find_bernstein_roots : Given an equation in Bernstein-Bernstein form, find all
+ * find_bernstein_roots : Given an equation in Bernstein-Bernstein form, find all
* of the roots in the open interval (0, 1). Return the number of roots found.
*/
void
find_bernstein_roots(double const *w, /* The control points */
- unsigned degree, /* The degree of the polynomial */
+ 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, double right_t, bool use_secant)
-{
+ unsigned depth, /* The depth of the recursion */
+ double left_t, double right_t, bool /*use_secant*/)
+{
Bernsteins B(degree, solutions);
- B.use_secant = use_secant;
B.find_bernstein_roots(w, depth, left_t, right_t);
}
void
-Bernsteins::find_bernstein_roots(double const *w, /* The control points */
- unsigned depth, /* The depth of the recursion */
- double left_t, double right_t)
+find_bernstein_roots(std::vector<double> &solutions, /* RETURN candidate t-values */
+ Geom::Bezier const& bz, /* The control points */
+ double left_t, double right_t)
+{
+ Bernsteins B(bz.degree(), solutions);
+ Geom::Bezier& bzl = const_cast<Geom::Bezier&>(bz);
+ double* w = &(bzl[0]);
+ B.find_bernstein_roots(w, 0, left_t, right_t);
+}
+
+
+
+void Bernsteins::find_bernstein_roots(double const *w, /* The control points */
+ unsigned depth, /* The depth of the recursion */
+ double left_t,
+ double right_t)
{
- unsigned n_crossings = 0; /* Number of zero-crossings */
-
+ size_t n_crossings = 0;
+
int old_sign = SGN(w[0]);
- for (unsigned i = 1; i < N; i++) {
- int sign = SGN(w[i]);
- if (sign) {
- if (sign != old_sign && old_sign) {
- n_crossings++;
+ //std::cout << "w[0] = " << w[0] << std::endl;
+ int sign;
+ for (size_t i = 1; i < N; i++)
+ {
+ //std::cout << "w[" << i << "] = " << w[i] << std::endl;
+ sign = SGN(w[i]);
+ if (sign != 0)
+ {
+ if (sign != old_sign && old_sign != 0)
+ {
+ ++n_crossings;
}
old_sign = sign;
}
}
-
- if (n_crossings == 0) // no solutions here
- return;
-
- if (n_crossings == 1) {
- /* Unique solution */
- /* Stop recursion when the tree is deep enough */
+ //std::cout << "n_crossings = " << n_crossings << std::endl;
+ if (n_crossings == 0) return; // no solutions here
+
+ if (n_crossings == 1) /* Unique solution */
+ {
+ //std::cout << "depth = " << depth << std::endl;
+ /* Stop recursion when the tree is deep enough */
/* if deep enough, return 1 solution at midpoint */
- if (depth >= MAXDEPTH) {
+ if (depth > MAX_DEPTH)
+ {
//printf("bottom out %d\n", depth);
const double Ax = right_t - left_t;
const double Ay = w[degree] - w[0];
-
+
solutions.push_back(left_t - Ax*w[0] / Ay);
return;
- solutions.push_back((left_t + right_t) / 2.0);
- return;
}
-
- // I thought secant method would be faster here, but it'aint. -- njh
- // Actually, it was, I just was using the wrong method for bezier evaluation. Horner's rule results in a very efficient algorithm - 10* faster (20080816)
- // Future work: try using brent's method
- if(use_secant) { // false position
- double s = 0;double t = 1;
- double e = 1e-10;
- int n,side=0;
- double r,fr,fs = w[0],ft = w[degree];
-
- for (n = 1; n <= 100; n++)
+
+
+ double s = 0, t = 1;
+ double e = 1e-10;
+ int side = 0;
+ double r, fr, fs = w[0], ft = w[degree];
+
+ for (size_t n = 0; n < 100; ++n)
+ {
+ r = (fs*t - ft*s) / (fs - ft);
+ if (fabs(t-s) < e * fabs(t+s)) break;
+
+ fr = horner(w, r);
+
+ if (fr * ft > 0)
{
- r = (fs*t - ft*s) / (fs - ft);
- if (fabs(t-s) < e*fabs(t+s)) break;
- fr = horner(w, r);
-
- if (fr * ft > 0)
- {
- t = r; ft = fr;
- if (side==-1) fs /= 2;
- side = -1;
- }
- else if (fs * fr > 0)
- {
- s = r; fs = fr;
- if (side==+1) ft /= 2;
- side = +1;
- }
- else break;
+ t = r; ft = fr;
+ if (side == -1) fs /= 2;
+ side = -1;
}
- solutions.push_back(r*right_t + (1-r)*left_t);
- return;
+ else if (fs * fr > 0)
+ {
+ s = r; fs = fr;
+ if (side == +1) ft /= 2;
+ side = +1;
+ }
+ else break;
}
+ solutions.push_back(r*right_t + (1-r)*left_t);
+ return;
+
}
/* Otherwise, solve recursively after subdividing control polygon */
- std::valarray<double> new_controls(2*N); // New left and right control polygons
- const double t = 0.5;
+// double Left[N], /* New left and right */
+// Right[N]; /* control polygons */
+ //const double t = 0.5;
+ double* LR = new double[2*N];
+ double* Left = LR;
+ double* Right = LR + N;
+ std::copy(w, w + N, Right);
-/*
- * Bernstein :
- * Evaluate a Bernstein function at a particular parameter value
- * Fill in control points for resulting sub-curves.
- *
- */
- for (unsigned i = 0; i < N; i++)
- Vtemp[i] = w[i];
-
- /* Triangle computation */
- const double omt = (1-t);
- new_controls[0] = Vtemp[0];
- new_controls[N+degree] = Vtemp[degree];
- double *prev_row = Vtemp;
- double *row = Vtemp + N;
- for (unsigned i = 1; i < N; i++) {
- for (unsigned j = 0; j < N - i; j++) {
- row[j] = omt*prev_row[j] + t*prev_row[j+1];
+ Left[0] = Right[0];
+ for (size_t i = 1; i < N; ++i)
+ {
+ for (size_t j = 0; j < N-i; ++j)
+ {
+ Right[j] = (Right[j] + Right[j+1]) * 0.5;
}
- new_controls[i] = row[0];
- new_controls[N+degree-i] = row[degree-i];
- std::swap(prev_row, row);
+ Left[i] = Right[0];
}
-
- double mid_t = left_t*(1-t) + right_t*t;
-
- find_bernstein_roots(&new_controls[0], depth+1, left_t, mid_t);
-
+
+ double mid_t = (left_t + right_t) * 0.5;
+
+
+ find_bernstein_roots(Left, depth+1, left_t, mid_t);
+
+
/* Solution is exactly on the subdivision point. */
- if (new_controls[N] == 0)
+ if (Right[0] == 0)
+ {
solutions.push_back(mid_t);
-
- find_bernstein_roots(&new_controls[N], depth+1, mid_t, right_t);
+ }
+
+ find_bernstein_roots(Right, depth+1, mid_t, right_t);
+ delete[] LR;
}
+
+// suggested by Sederberg.
+double Bernsteins::horner(const double *b, double t)
+{
+ double u, tn, tmp;
+ u = 1.0 - t;
+ tn = 1.0;
+ tmp = b[0] * u;
+ for(size_t i = 1; i < degree; ++i)
+ {
+ tn *= t;
+ tmp = (tmp + tn*bc[i]*b[i]) * u;
+ }
+ return (tmp + tn*t*b[degree]);
+}
+
+
+
+#if 0
/*
* control_poly_flat_enough :
* Check if the control polygon of a Bernstein curve is flat enough
* for recursive subdivision to bottom out.
*
*/
-unsigned
+unsigned
Bernsteins::control_poly_flat_enough(double const *V)
{
/* Find the perpendicular distance from each interior control point to line connecting V[0] and
@@ -204,7 +241,7 @@ Bernsteins::control_poly_flat_enough(double const *V)
else
max_distance_above = std::max(max_distance_above, dist);
}
-
+
const double abSquared = 1./((a * a) + 1);
const double intercept_1 = (a - max_distance_above * abSquared);
@@ -218,23 +255,7 @@ Bernsteins::control_poly_flat_enough(double const *V)
//printf("error %g %g %g\n", error, a, BEPSILON * a);
return error < BEPSILON * a;
}
-
-// suggested by Sederberg.
-double Bernsteins::horner(const double *b, double t) {
- int n = degree;
- double u, bc, tn, tmp;
- int i;
- u = 1.0 - t;
- bc = 1;
- tn = 1;
- tmp = b[0]*u;
- for(i=1; i<n; i++){
- tn = tn*t;
- bc = bc*(n-i+1)/i;
- tmp = (tmp + tn*bc*b[i])*u;
- }
- return (tmp + tn*t*b[n]);
-}
+#endif
};
diff --git a/src/2geom/solver.h b/src/2geom/solver.h
index 2aadaa476..5e77f13dc 100644
--- a/src/2geom/solver.h
+++ b/src/2geom/solver.h
@@ -4,7 +4,7 @@
*
* Authors:
* ? <?@?.?>
- *
+ *
* Copyright ?-? authors
*
* This library is free software; you can redistribute it and/or
@@ -37,9 +37,14 @@
#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 */
@@ -55,6 +60,8 @@ 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 */
@@ -64,6 +71,12 @@ find_bernstein_roots(
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
/*
diff --git a/src/2geom/svg-elliptical-arc.cpp b/src/2geom/svg-elliptical-arc.cpp
index 877667b43..ae8a3192c 100644
--- a/src/2geom/svg-elliptical-arc.cpp
+++ b/src/2geom/svg-elliptical-arc.cpp
@@ -36,6 +36,7 @@
#include <cfloat>
#include <limits>
+#include <memory>
#include <2geom/numeric/vector.h>
#include <2geom/numeric/fitting-tool.h>
@@ -46,929 +47,15 @@
namespace Geom
{
-
-OptRect SVGEllipticalArc::boundsExact() const
-{
- if (isDegenerate() && is_svg_compliant())
- return chord().boundsExact();
-
- std::vector<double> extremes(4);
- double cosrot = std::cos(rotation_angle());
- double sinrot = std::sin(rotation_angle());
- extremes[0] = std::atan2( -ray(Y) * sinrot, ray(X) * cosrot );
- extremes[1] = extremes[0] + M_PI;
- if ( extremes[0] < 0 ) extremes[0] += 2*M_PI;
- extremes[2] = std::atan2( ray(Y) * cosrot, ray(X) * sinrot );
- extremes[3] = extremes[2] + M_PI;
- if ( extremes[2] < 0 ) extremes[2] += 2*M_PI;
-
-
- std::vector<double>arc_extremes(4);
- arc_extremes[0] = initialPoint()[X];
- arc_extremes[1] = finalPoint()[X];
- if ( arc_extremes[0] < arc_extremes[1] )
- std::swap(arc_extremes[0], arc_extremes[1]);
- arc_extremes[2] = initialPoint()[Y];
- arc_extremes[3] = finalPoint()[Y];
- if ( arc_extremes[2] < arc_extremes[3] )
- std::swap(arc_extremes[2], arc_extremes[3]);
-
-
- if ( start_angle() < end_angle() )
- {
- if ( sweep_flag() )
- {
- for ( unsigned int i = 0; i < extremes.size(); ++i )
- {
- if ( start_angle() < extremes[i] && extremes[i] < end_angle() )
- {
- arc_extremes[i] = pointAtAngle(extremes[i])[i >> 1];
- }
- }
- }
- else
- {
- for ( unsigned int i = 0; i < extremes.size(); ++i )
- {
- if ( start_angle() > extremes[i] || extremes[i] > end_angle() )
- {
- arc_extremes[i] = pointAtAngle(extremes[i])[i >> 1];
- }
- }
- }
- }
- else
- {
- if ( sweep_flag() )
- {
- for ( unsigned int i = 0; i < extremes.size(); ++i )
- {
- if ( start_angle() < extremes[i] || extremes[i] < end_angle() )
- {
- arc_extremes[i] = pointAtAngle(extremes[i])[i >> 1];
- }
- }
- }
- else
- {
- for ( unsigned int i = 0; i < extremes.size(); ++i )
- {
- if ( start_angle() > extremes[i] && extremes[i] > end_angle() )
- {
- arc_extremes[i] = pointAtAngle(extremes[i])[i >> 1];
- }
- }
- }
- }
-
- return Rect( Point(arc_extremes[1], arc_extremes[3]) ,
- Point(arc_extremes[0], arc_extremes[2]) );
-}
-
-
-double SVGEllipticalArc::valueAtAngle(Coord t, Dim2 d) const
-{
- double sin_rot_angle = std::sin(rotation_angle());
- double cos_rot_angle = std::cos(rotation_angle());
- if ( d == X )
- {
- return ray(X) * cos_rot_angle * std::cos(t)
- - ray(Y) * sin_rot_angle * std::sin(t)
- + center(X);
- }
- else if ( d == Y )
- {
- return ray(X) * sin_rot_angle * std::cos(t)
- + ray(Y) * cos_rot_angle * std::sin(t)
- + center(Y);
- }
- THROW_RANGEERROR("dimension parameter out of range");
-}
-
-
-std::vector<double>
-SVGEllipticalArc::roots(double v, Dim2 d) const
-{
- if ( d > Y )
- {
- THROW_RANGEERROR("dimention out of range");
- }
-
- std::vector<double> sol;
-
- if (isDegenerate() && is_svg_compliant())
- {
- return chord().roots(v, d);
- }
- else
- {
- if ( are_near(ray(X), 0) && are_near(ray(Y), 0) )
- {
- if ( center(d) == v )
- sol.push_back(0);
- return sol;
- }
-
- const char* msg[2][2] =
- {
- { "d == X; ray(X) == 0; "
- "s = (v - center(X)) / ( -ray(Y) * std::sin(rotation_angle()) ); "
- "s should be contained in [-1,1]",
- "d == X; ray(Y) == 0; "
- "s = (v - center(X)) / ( ray(X) * std::cos(rotation_angle()) ); "
- "s should be contained in [-1,1]"
- },
- { "d == Y; ray(X) == 0; "
- "s = (v - center(X)) / ( ray(Y) * std::cos(rotation_angle()) ); "
- "s should be contained in [-1,1]",
- "d == Y; ray(Y) == 0; "
- "s = (v - center(X)) / ( ray(X) * std::sin(rotation_angle()) ); "
- "s should be contained in [-1,1]"
- },
- };
-
- for ( unsigned int dim = 0; dim < 2; ++dim )
- {
- if ( are_near(ray(dim), 0) )
- {
- if ( initialPoint()[d] == v && finalPoint()[d] == v )
- {
- THROW_INFINITESOLUTIONS(0);
- }
- if ( (initialPoint()[d] < finalPoint()[d])
- && (initialPoint()[d] > v || finalPoint()[d] < v) )
- {
- return sol;
- }
- if ( (initialPoint()[d] > finalPoint()[d])
- && (finalPoint()[d] > v || initialPoint()[d] < v) )
- {
- return sol;
- }
- double ray_prj;
- switch(d)
- {
- case X:
- switch(dim)
- {
- case X: ray_prj = -ray(Y) * std::sin(rotation_angle());
- break;
- case Y: ray_prj = ray(X) * std::cos(rotation_angle());
- break;
- }
- break;
- case Y:
- switch(dim)
- {
- case X: ray_prj = ray(Y) * std::cos(rotation_angle());
- break;
- case Y: ray_prj = ray(X) * std::sin(rotation_angle());
- break;
- }
- break;
- }
-
- double s = (v - center(d)) / ray_prj;
- if ( s < -1 || s > 1 )
- {
- THROW_LOGICALERROR(msg[d][dim]);
- }
- switch(dim)
- {
- case X:
- s = std::asin(s); // return a value in [-PI/2,PI/2]
- if ( logical_xor( sweep_flag(), are_near(start_angle(), M_PI/2) ) )
- {
- if ( s < 0 ) s += 2*M_PI;
- }
- else
- {
- s = M_PI - s;
- if (!(s < 2*M_PI) ) s -= 2*M_PI;
- }
- break;
- case Y:
- s = std::acos(s); // return a value in [0,PI]
- if ( logical_xor( sweep_flag(), are_near(start_angle(), 0) ) )
- {
- s = 2*M_PI - s;
- if ( !(s < 2*M_PI) ) s -= 2*M_PI;
- }
- break;
- }
-
- //std::cerr << "s = " << rad_to_deg(s);
- s = map_to_01(s);
- //std::cerr << " -> t: " << s << std::endl;
- if ( !(s < 0 || s > 1) )
- sol.push_back(s);
- return sol;
- }
- }
-
- }
-
- double rotx, roty;
- switch(d)
- {
- case X:
- rotx = std::cos(rotation_angle());
- roty = -std::sin(rotation_angle());
- break;
- case Y:
- rotx = std::sin(rotation_angle());
- roty = std::cos(rotation_angle());
- break;
- }
- double rxrotx = ray(X) * rotx;
- double c_v = center(d) - v;
-
- double a = -rxrotx + c_v;
- double b = ray(Y) * roty;
- double c = rxrotx + c_v;
- //std::cerr << "a = " << a << std::endl;
- //std::cerr << "b = " << b << std::endl;
- //std::cerr << "c = " << c << std::endl;
-
- if ( are_near(a,0) )
- {
- sol.push_back(M_PI);
- if ( !are_near(b,0) )
- {
- double s = 2 * std::atan(-c/(2*b));
- if ( s < 0 ) s += 2*M_PI;
- sol.push_back(s);
- }
- }
- else
- {
- double delta = b * b - a * c;
- //std::cerr << "delta = " << delta << std::endl;
- if ( are_near(delta, 0) )
- {
- double s = 2 * std::atan(-b/a);
- if ( s < 0 ) s += 2*M_PI;
- sol.push_back(s);
- }
- else if ( delta > 0 )
- {
- double sq = std::sqrt(delta);
- double s = 2 * std::atan( (-b - sq) / a );
- if ( s < 0 ) s += 2*M_PI;
- sol.push_back(s);
- s = 2 * std::atan( (-b + sq) / a );
- if ( s < 0 ) s += 2*M_PI;
- sol.push_back(s);
- }
- }
-
- std::vector<double> arc_sol;
- for (unsigned int i = 0; i < sol.size(); ++i )
- {
- //std::cerr << "s = " << rad_to_deg(sol[i]);
- sol[i] = map_to_01(sol[i]);
- //std::cerr << " -> t: " << sol[i] << std::endl;
- if ( !(sol[i] < 0 || sol[i] > 1) )
- arc_sol.push_back(sol[i]);
- }
- return arc_sol;
-}
-
-
-// D(E(t,C),t) = E(t+PI/2,O), where C is the ellipse center
-// the derivative doesn't rotate the ellipse but there is a translation
-// of the parameter t by an angle of PI/2 so the ellipse points are shifted
-// of such an angle in the cw direction
-Curve* SVGEllipticalArc::derivative() const
-{
- if (isDegenerate() && is_svg_compliant())
- return chord().derivative();
-
- SVGEllipticalArc* result = new SVGEllipticalArc(*this);
- result->m_center[X] = result->m_center[Y] = 0;
- result->m_start_angle += M_PI/2;
- if( !( result->m_start_angle < 2*M_PI ) )
- {
- result->m_start_angle -= 2*M_PI;
- }
- result->m_end_angle += M_PI/2;
- if( !( result->m_end_angle < 2*M_PI ) )
- {
- result->m_end_angle -= 2*M_PI;
- }
- result->m_initial_point = result->pointAtAngle( result->start_angle() );
- result->m_final_point = result->pointAtAngle( result->end_angle() );
- return result;
-}
-
-
-std::vector<Point>
-SVGEllipticalArc::pointAndDerivatives(Coord t, unsigned int n) const
-{
- if (isDegenerate() && is_svg_compliant())
- return chord().pointAndDerivatives(t, n);
-
- unsigned int nn = n+1; // nn represents the size of the result vector.
- std::vector<Point> result;
- result.reserve(nn);
- double angle = map_unit_interval_on_circular_arc(t, start_angle(),
- end_angle(), sweep_flag());
- SVGEllipticalArc ea(*this);
- ea.m_center = Point(0,0);
- unsigned int m = std::min(nn, 4u);
- for ( unsigned int i = 0; i < m; ++i )
- {
- result.push_back( ea.pointAtAngle(angle) );
- angle += M_PI/2;
- if ( !(angle < 2*M_PI) ) angle -= 2*M_PI;
- }
- m = nn / 4;
- for ( unsigned int i = 1; i < m; ++i )
- {
- for ( unsigned int j = 0; j < 4; ++j )
- result.push_back( result[j] );
- }
- m = nn - 4 * m;
- for ( unsigned int i = 0; i < m; ++i )
- {
- result.push_back( result[i] );
- }
- if ( !result.empty() ) // nn != 0
- result[0] = pointAtAngle(angle);
- return result;
-}
-
-bool SVGEllipticalArc::containsAngle(Coord angle) const
-{
- if ( sweep_flag() )
- if ( start_angle() < end_angle() )
- return ( !( angle < start_angle() || angle > end_angle() ) );
- else
- return ( !( angle < start_angle() && angle > end_angle() ) );
- else
- if ( start_angle() > end_angle() )
- return ( !( angle > start_angle() || angle < end_angle() ) );
- else
- return ( !( angle > start_angle() && angle < end_angle() ) );
-}
-
-Curve* SVGEllipticalArc::portion(double f, double t) const
-{
- // fix input arguments
- if (f < 0) f = 0;
- if (f > 1) f = 1;
- if (t < 0) t = 0;
- if (t > 1) t = 1;
-
- if ( are_near(f, t) )
- {
- SVGEllipticalArc* arc = new SVGEllipticalArc();
- arc->m_center = arc->m_initial_point = arc->m_final_point = pointAt(f);
- arc->m_start_angle = arc->m_end_angle = m_start_angle;
- arc->m_rot_angle = m_rot_angle;
- arc->m_sweep = m_sweep;
- arc->m_large_arc = m_large_arc;
- }
-
- SVGEllipticalArc* arc = new SVGEllipticalArc( *this );
- arc->m_initial_point = pointAt(f);
- arc->m_final_point = pointAt(t);
- double sa = sweep_flag() ? sweep_angle() : -sweep_angle();
- arc->m_start_angle = m_start_angle + sa * f;
- if ( !(arc->m_start_angle < 2*M_PI) )
- arc->m_start_angle -= 2*M_PI;
- if ( arc->m_start_angle < 0 )
- arc->m_start_angle += 2*M_PI;
- arc->m_end_angle = m_start_angle + sa * t;
- if ( !(arc->m_end_angle < 2*M_PI) )
- arc->m_end_angle -= 2*M_PI;
- if ( arc->m_end_angle < 0 )
- arc->m_end_angle += 2*M_PI;
- if ( f > t ) arc->m_sweep = !sweep_flag();
- if ( large_arc_flag() && (arc->sweep_angle() < M_PI) )
- arc->m_large_arc = false;
- return arc;
-}
-
-
-std::vector<double> SVGEllipticalArc::
-allNearestPoints( Point const& p, double from, double to ) const
-{
- std::vector<double> result;
- if (isDegenerate() && is_svg_compliant())
- {
- result.push_back( chord().nearestPoint(p, from, to) );
- return result;
- }
-
- if ( from > to ) std::swap(from, to);
- if ( from < 0 || to > 1 )
- {
- THROW_RANGEERROR("[from,to] interval out of range");
- }
-
- if ( ( are_near(ray(X), 0) && are_near(ray(Y), 0) ) || are_near(from, to) )
- {
- result.push_back(from);
- return result;
- }
- else if ( are_near(ray(X), 0) || are_near(ray(Y), 0) )
- {
- LineSegment seg(pointAt(from), pointAt(to));
- Point np = seg.pointAt( seg.nearestPoint(p) );
- if ( are_near(ray(Y), 0) )
- {
- if ( are_near(rotation_angle(), M_PI/2)
- || are_near(rotation_angle(), 3*M_PI/2) )
- {
- result = roots(np[Y], Y);
- }
- else
- {
- result = roots(np[X], X);
- }
- }
- else
- {
- if ( are_near(rotation_angle(), M_PI/2)
- || are_near(rotation_angle(), 3*M_PI/2) )
- {
- result = roots(np[X], X);
- }
- else
- {
- result = roots(np[Y], Y);
- }
- }
- return result;
- }
- else if ( are_near(ray(X), ray(Y)) )
- {
- Point r = p - center();
- if ( are_near(r, Point(0,0)) )
- {
- THROW_INFINITESOLUTIONS(0);
- }
- // TODO: implement case r != 0
-// Point np = ray(X) * unit_vector(r);
-// std::vector<double> solX = roots(np[X],X);
-// std::vector<double> solY = roots(np[Y],Y);
-// double t;
-// if ( are_near(solX[0], solY[0]) || are_near(solX[0], solY[1]))
-// {
-// t = solX[0];
-// }
-// else
-// {
-// t = solX[1];
-// }
-// if ( !(t < from || t > to) )
-// {
-// result.push_back(t);
-// }
-// else
-// {
-//
-// }
- }
-
- // solve the equation <D(E(t),t)|E(t)-p> == 0
- // that provides min and max distance points
- // on the ellipse E wrt the point p
- // after the substitutions:
- // cos(t) = (1 - s^2) / (1 + s^2)
- // sin(t) = 2t / (1 + s^2)
- // where s = tan(t/2)
- // we get a 4th degree equation in s
- /*
- * ry s^4 ((-cy + py) Cos[Phi] + (cx - px) Sin[Phi]) +
- * ry ((cy - py) Cos[Phi] + (-cx + px) Sin[Phi]) +
- * 2 s^3 (rx^2 - ry^2 + (-cx + px) rx Cos[Phi] + (-cy + py) rx Sin[Phi]) +
- * 2 s (-rx^2 + ry^2 + (-cx + px) rx Cos[Phi] + (-cy + py) rx Sin[Phi])
- */
-
- Point p_c = p - center();
- double rx2_ry2 = (ray(X) - ray(Y)) * (ray(X) + ray(Y));
- double cosrot = std::cos( rotation_angle() );
- double sinrot = std::sin( rotation_angle() );
- double expr1 = ray(X) * (p_c[X] * cosrot + p_c[Y] * sinrot);
- Poly coeff;
- coeff.resize(5);
- coeff[4] = ray(Y) * ( p_c[Y] * cosrot - p_c[X] * sinrot );
- coeff[3] = 2 * ( rx2_ry2 + expr1 );
- coeff[2] = 0;
- coeff[1] = 2 * ( -rx2_ry2 + expr1 );
- coeff[0] = -coeff[4];
-
-// for ( unsigned int i = 0; i < 5; ++i )
-// std::cerr << "c[" << i << "] = " << coeff[i] << std::endl;
-
- std::vector<double> real_sol;
- // gsl_poly_complex_solve raises an error
- // if the leading coefficient is zero
- if ( are_near(coeff[4], 0) )
- {
- real_sol.push_back(0);
- if ( !are_near(coeff[3], 0) )
- {
- double sq = -coeff[1] / coeff[3];
- if ( sq > 0 )
- {
- double s = std::sqrt(sq);
- real_sol.push_back(s);
- real_sol.push_back(-s);
- }
- }
- }
- else
- {
- real_sol = solve_reals(coeff);
- }
-
- for ( unsigned int i = 0; i < real_sol.size(); ++i )
- {
- real_sol[i] = 2 * std::atan(real_sol[i]);
- if ( real_sol[i] < 0 ) real_sol[i] += 2*M_PI;
- }
- // when s -> Infinity then <D(E)|E-p> -> 0 iff coeff[4] == 0
- // so we add M_PI to the solutions being lim arctan(s) = PI when s->Infinity
- if ( (real_sol.size() % 2) != 0 )
- {
- real_sol.push_back(M_PI);
- }
-
- double mindistsq1 = std::numeric_limits<double>::max();
- double mindistsq2 = std::numeric_limits<double>::max();
- double dsq;
- unsigned int mi1, mi2;
- for ( unsigned int i = 0; i < real_sol.size(); ++i )
- {
- dsq = distanceSq(p, pointAtAngle(real_sol[i]));
- if ( mindistsq1 > dsq )
- {
- mindistsq2 = mindistsq1;
- mi2 = mi1;
- mindistsq1 = dsq;
- mi1 = i;
- }
- else if ( mindistsq2 > dsq )
- {
- mindistsq2 = dsq;
- mi2 = i;
- }
- }
-
- double t = map_to_01( real_sol[mi1] );
- if ( !(t < from || t > to) )
- {
- result.push_back(t);
- }
-
- bool second_sol = false;
- t = map_to_01( real_sol[mi2] );
- if ( real_sol.size() == 4 && !(t < from || t > to) )
- {
- if ( result.empty() || are_near(mindistsq1, mindistsq2) )
- {
- result.push_back(t);
- second_sol = true;
- }
- }
-
- // we need to test extreme points too
- double dsq1 = distanceSq(p, pointAt(from));
- double dsq2 = distanceSq(p, pointAt(to));
- if ( second_sol )
- {
- if ( mindistsq2 > dsq1 )
- {
- result.clear();
- result.push_back(from);
- mindistsq2 = dsq1;
- }
- else if ( are_near(mindistsq2, dsq) )
- {
- result.push_back(from);
- }
- if ( mindistsq2 > dsq2 )
- {
- result.clear();
- result.push_back(to);
- }
- else if ( are_near(mindistsq2, dsq2) )
- {
- result.push_back(to);
- }
-
- }
- else
- {
- if ( result.empty() )
- {
- if ( are_near(dsq1, dsq2) )
- {
- result.push_back(from);
- result.push_back(to);
- }
- else if ( dsq2 > dsq1 )
- {
- result.push_back(from);
- }
- else
- {
- result.push_back(to);
- }
- }
- }
-
- return result;
-}
-
-
-/*
- * NOTE: this implementation follows Standard SVG 1.1 implementation guidelines
- * for elliptical arc curves. See Appendix F.6.
- */
-void SVGEllipticalArc::calculate_center_and_extreme_angles()
-{
- Point d = initialPoint() - finalPoint();
-
- if (is_svg_compliant())
- {
- if ( initialPoint() == finalPoint() )
- {
- m_rx = m_ry = m_rot_angle = m_start_angle = m_end_angle = 0;
- m_center = initialPoint();
- m_large_arc = m_sweep = false;
- return;
- }
-
- m_rx = std::fabs(m_rx);
- m_ry = std::fabs(m_ry);
-
- if ( are_near(ray(X), 0) || are_near(ray(Y), 0) )
- {
- m_rx = L2(d) / 2;
- m_ry = 0;
- m_rot_angle = std::atan2(d[Y], d[X]);
- if (m_rot_angle < 0) m_rot_angle += 2*M_PI;
- m_start_angle = 0;
- m_end_angle = M_PI;
- m_center = middle_point(initialPoint(), finalPoint());
- m_large_arc = false;
- m_sweep = false;
- return;
- }
- }
- else
- {
- if ( are_near(initialPoint(), finalPoint()) )
- {
- if ( are_near(ray(X), 0) && are_near(ray(Y), 0) )
- {
- m_start_angle = m_end_angle = 0;
- m_center = initialPoint();
- return;
- }
- else
- {
- THROW_RANGEERROR("initial and final point are the same");
- }
- }
- if ( are_near(ray(X), 0) && are_near(ray(Y), 0) )
- { // but initialPoint != finalPoint
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints: "
- "ray(X) == 0 && ray(Y) == 0 but initialPoint != finalPoint"
- );
- }
- if ( are_near(ray(Y), 0) )
- {
- Point v = initialPoint() - finalPoint();
- if ( are_near(L2sq(v), 4*ray(X)*ray(X)) )
- {
- double angle = std::atan2(v[Y], v[X]);
- if (angle < 0) angle += 2*M_PI;
- if ( are_near( angle, rotation_angle() ) )
- {
- m_start_angle = 0;
- m_end_angle = M_PI;
- m_center = v/2 + finalPoint();
- return;
- }
- angle -= M_PI;
- if ( angle < 0 ) angle += 2*M_PI;
- if ( are_near( angle, rotation_angle() ) )
- {
- m_start_angle = M_PI;
- m_end_angle = 0;
- m_center = v/2 + finalPoint();
- return;
- }
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints: "
- "ray(Y) == 0 "
- "and slope(initialPoint - finalPoint) != rotation_angle "
- "and != rotation_angle + PI"
- );
- }
- if ( L2sq(v) > 4*ray(X)*ray(X) )
- {
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints: "
- "ray(Y) == 0 and distance(initialPoint, finalPoint) > 2*ray(X)"
- );
- }
- else
- {
- THROW_RANGEERROR(
- "there is infinite ellipses that satisfy the given constraints: "
- "ray(Y) == 0 and distance(initialPoint, finalPoint) < 2*ray(X)"
- );
- }
-
- }
-
- if ( are_near(ray(X), 0) )
- {
- Point v = initialPoint() - finalPoint();
- if ( are_near(L2sq(v), 4*ray(Y)*ray(Y)) )
- {
- double angle = std::atan2(v[Y], v[X]);
- if (angle < 0) angle += 2*M_PI;
- double rot_angle = rotation_angle() + M_PI/2;
- if ( !(rot_angle < 2*M_PI) ) rot_angle -= 2*M_PI;
- if ( are_near( angle, rot_angle ) )
- {
- m_start_angle = M_PI/2;
- m_end_angle = 3*M_PI/2;
- m_center = v/2 + finalPoint();
- return;
- }
- angle -= M_PI;
- if ( angle < 0 ) angle += 2*M_PI;
- if ( are_near( angle, rot_angle ) )
- {
- m_start_angle = 3*M_PI/2;
- m_end_angle = M_PI/2;
- m_center = v/2 + finalPoint();
- return;
- }
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints: "
- "ray(X) == 0 "
- "and slope(initialPoint - finalPoint) != rotation_angle + PI/2 "
- "and != rotation_angle + (3/2)*PI"
- );
- }
- if ( L2sq(v) > 4*ray(Y)*ray(Y) )
- {
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints: "
- "ray(X) == 0 and distance(initialPoint, finalPoint) > 2*ray(Y)"
- );
- }
- else
- {
- THROW_RANGEERROR(
- "there is infinite ellipses that satisfy the given constraints: "
- "ray(X) == 0 and distance(initialPoint, finalPoint) < 2*ray(Y)"
- );
- }
-
- }
-
- }
-
- double sin_rot_angle = std::sin(rotation_angle());
- double cos_rot_angle = std::cos(rotation_angle());
-
-
- Matrix m( cos_rot_angle, -sin_rot_angle,
- sin_rot_angle, cos_rot_angle,
- 0, 0 );
-
- Point p = (d / 2) * m;
- double rx2 = m_rx * m_rx;
- double ry2 = m_ry * m_ry;
- double rxpy = m_rx * p[Y];
- double rypx = m_ry * p[X];
- double rx2py2 = rxpy * rxpy;
- double ry2px2 = rypx * rypx;
- double num = rx2 * ry2;
- double den = rx2py2 + ry2px2;
- assert(den != 0);
- double rad = num / den;
- Point c(0,0);
- if (rad > 1)
- {
- rad -= 1;
- rad = std::sqrt(rad);
-
- if (m_large_arc == m_sweep) rad = -rad;
- c = rad * Point(rxpy / m_ry, -rypx / m_rx);
-
- m[1] = -m[1];
- m[2] = -m[2];
-
- m_center = c * m + middle_point(initialPoint(), finalPoint());
- }
- else if (rad == 1 || is_svg_compliant())
- {
- double lamda = std::sqrt(1 / rad);
- m_rx *= lamda;
- m_ry *= lamda;
- m_center = middle_point(initialPoint(), finalPoint());
- }
- else
- {
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints"
- );
- }
-
- Point sp((p[X] - c[X]) / m_rx, (p[Y] - c[Y]) / m_ry);
- Point ep((-p[X] - c[X]) / m_rx, (-p[Y] - c[Y]) / m_ry);
- Point v(1, 0);
- m_start_angle = angle_between(v, sp);
- double sweep_angle = angle_between(sp, ep);
- if (!m_sweep && sweep_angle > 0) sweep_angle -= 2*M_PI;
- if (m_sweep && sweep_angle < 0) sweep_angle += 2*M_PI;
-
- if (m_start_angle < 0) m_start_angle += 2*M_PI;
- m_end_angle = m_start_angle + sweep_angle;
- if (m_end_angle < 0) m_end_angle += 2*M_PI;
- if (m_end_angle >= 2*M_PI) m_end_angle -= 2*M_PI;
-}
-
-
-D2<SBasis> SVGEllipticalArc::toSBasis() const
-{
-
- if (isDegenerate() && is_svg_compliant())
- return chord().toSBasis();
-
- D2<SBasis> arc;
- // the interval of parametrization has to be [0,1]
- Coord et = start_angle() + ( sweep_flag() ? sweep_angle() : -sweep_angle() );
- Linear param(start_angle(), et);
- Coord cos_rot_angle = std::cos(rotation_angle());
- Coord sin_rot_angle = std::sin(rotation_angle());
- // order = 4 seems to be enough to get a perfect looking elliptical arc
- SBasis arc_x = ray(X) * cos(param,4);
- SBasis arc_y = ray(Y) * sin(param,4);
- arc[0] = arc_x * cos_rot_angle - arc_y * sin_rot_angle + Linear(center(X),center(X));
- arc[1] = arc_x * sin_rot_angle + arc_y * cos_rot_angle + Linear(center(Y),center(Y));
-
- // ensure that endpoints remain exact
- for ( int d = 0 ; d < 2 ; d++ ) {
- arc[d][0][0] = initialPoint()[d];
- arc[d][0][1] = finalPoint()[d];
- }
-
- return arc;
-}
-
-
-Curve* SVGEllipticalArc::transformed(Matrix const& m) const
-{
- Ellipse e(center(X), center(Y), ray(X), ray(Y), rotation_angle());
- Ellipse et = e.transformed(m);
- Point inner_point = pointAt(0.5);
- SVGEllipticalArc ea = et.arc( initialPoint() * m,
- inner_point * m,
- finalPoint() * m,
- is_svg_compliant() );
- return ea.duplicate();
-}
-
-/*
- * helper routine to convert the parameter t value
- * btw [0,1] and [0,2PI] domain and back
+/**
+ * @class SVGEllipticalArc
+ * @brief SVG 1.1-compliant elliptical arc.
+ *
+ * This class is almost identical to the normal elliptical arc, but it differs slightly
+ * in the handling of degenerate arcs to be compliant with SVG 1.1 implementation guidelines.
*
+ * @ingroup Curves
*/
-Coord SVGEllipticalArc::map_to_02PI(Coord t) const
-{
- Coord angle = start_angle();
- if ( sweep_flag() )
- {
- angle += sweep_angle() * t;
- }
- else
- {
- angle -= sweep_angle() * t;
- }
- angle = std::fmod(angle, 2*M_PI);
- if ( angle < 0 ) angle += 2*M_PI;
- return angle;
-}
-
-Coord SVGEllipticalArc::map_to_01(Coord angle) const
-{
- return map_circular_arc_on_unit_interval(angle, start_angle(),
- end_angle(), sweep_flag());
-}
-
namespace detail
{
@@ -1015,7 +102,7 @@ struct ellipse_equation
}
make_elliptical_arc::
-make_elliptical_arc( SVGEllipticalArc& _ea,
+make_elliptical_arc( EllipticalArc& _ea,
curve_type const& _curve,
unsigned int _total_samples,
double _tolerance )
@@ -1133,13 +220,16 @@ bool make_elliptical_arc::make_elliptiarc()
if (svg_compliant_flag())
{
- ea = e.arc(initial_point, inner_point, final_point);
+ std::auto_ptr<EllipticalArc> arc( e.arc(initial_point, inner_point, final_point, true) );
+ ea = *arc;
}
else
{
try
{
- ea = e.arc(initial_point, inner_point, final_point, false);
+ std::auto_ptr<EllipticalArc> eap(
+ e.arc(initial_point, inner_point, final_point, false) );
+ ea = *eap;
}
catch(RangeError exc)
{
diff --git a/src/2geom/svg-elliptical-arc.h b/src/2geom/svg-elliptical-arc.h
index 34c51508b..79497cdb3 100644
--- a/src/2geom/svg-elliptical-arc.h
+++ b/src/2geom/svg-elliptical-arc.h
@@ -1,12 +1,14 @@
/**
* \file
- * \brief Elliptical Arc - implementation of the SVGEllipticalArc path element
+ * \brief SVG 1.1-compliant elliptical arc curve
*
+ *//*
* Authors:
- * MenTaLguY <mental@rydia.net>
- * Marco Cecchetti <mrcekets at gmail.com>
- *
- * Copyright 2007-2008 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
@@ -36,339 +38,95 @@
#ifndef _2GEOM_SVG_ELLIPTICAL_ARC_H_
#define _2GEOM_SVG_ELLIPTICAL_ARC_H_
-
#include <2geom/curve.h>
#include <2geom/angle.h>
#include <2geom/utils.h>
#include <2geom/bezier-curve.h>
+#include <2geom/elliptical-arc.h>
#include <2geom/sbasis-curve.h> // for non-native methods
#include <2geom/numeric/vector.h>
#include <2geom/numeric/fitting-tool.h>
#include <2geom/numeric/fitting-model.h>
-
-
#include <algorithm>
-
-
namespace Geom
{
-class SVGEllipticalArc : public Curve
-{
- public:
- SVGEllipticalArc(bool _svg_compliant = true)
- : m_initial_point(Point(0,0)), m_final_point(Point(0,0)),
- m_rx(0), m_ry(0), m_rot_angle(0),
- m_large_arc(true), m_sweep(true),
- m_svg_compliant(_svg_compliant),
- m_start_angle(0), m_end_angle(0),
- m_center(Point(0,0))
- {
- }
-
- /**
- * \brief constructor
- *
- * \param _initial_point: initial arc end point;
- * \param _rx: ellipse x-axis ray length
- * \param _ry: ellipse y-axis ray length
- * \param _rot_angle: ellipse x-axis rotation angle in radians;
- * \param _large_arc: if true the largest arc is chosen,
- * if false the smallest arc is chosen;
- * \param _sweep : if true the clockwise arc is chosen,
- * if false the counter-clockwise arc is chosen;
- * \param _final_point: final arc end point;
- * \param _svg_compliant: if true the class behaviour follows the Standard
- * SVG 1.1 implementation guidelines (see Appendix F.6)
- * if false the class behavoiur is more strict
- * on input parameter
- *
- * in case the initial and the final arc end-points overlaps
- * a degenerate arc of zero length is generated
- *
- */
- SVGEllipticalArc( Point _initial_point, double _rx, double _ry,
- double _rot_angle, bool _large_arc, bool _sweep,
- Point _final_point,
- bool _svg_compliant = true
+class SVGEllipticalArc : public EllipticalArc {
+public:
+ SVGEllipticalArc()
+ : EllipticalArc()
+ {}
+ SVGEllipticalArc( Point ip, double rx, double ry,
+ double rot_angle, bool large_arc, bool sweep,
+ Point fp
)
- : m_initial_point(_initial_point), m_final_point(_final_point),
- m_rx(_rx), m_ry(_ry), m_rot_angle(_rot_angle),
- m_large_arc(_large_arc), m_sweep(_sweep),
- m_svg_compliant(_svg_compliant)
+ : EllipticalArc()
{
- calculate_center_and_extreme_angles();
+ _initial_point = ip;
+ _final_point = fp;
+ _rays[X] = rx; _rays[Y] = ry;
+ _rot_angle = rot_angle;
+ _large_arc = large_arc;
+ _sweep = sweep;
+ _updateCenterAndAngles(true);
}
- void set( Point _initial_point, double _rx, double _ry,
- double _rot_angle, bool _large_arc, bool _sweep,
- Point _final_point
- )
- {
- m_initial_point = _initial_point;
- m_final_point = _final_point;
- m_rx = _rx;
- m_ry = _ry;
- m_rot_angle = _rot_angle;
- m_large_arc = _large_arc;
- m_sweep = _sweep;
- calculate_center_and_extreme_angles();
- }
-
- Curve* duplicate() const
- {
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ virtual Curve *duplicate() const {
return new SVGEllipticalArc(*this);
}
-
- double center(unsigned int i) const
- {
- return m_center[i];
+ virtual Coord valueAt(Coord t, Dim2 d) const {
+ if (isDegenerate()) return chord().valueAt(t, d);
+ return EllipticalArc::valueAt(t, d);
}
-
- Point center() const
- {
- return m_center;
+ virtual Point pointAt(Coord t) const {
+ if (isDegenerate()) return chord().pointAt(t);
+ return EllipticalArc::pointAt(t);
}
-
- Point initialPoint() const
- {
- return m_initial_point;
+ virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned int n) const {
+ if (isDegenerate()) return chord().pointAndDerivatives(t, n);
+ return EllipticalArc::pointAndDerivatives(t, n);
}
-
- Point finalPoint() const
- {
- return m_final_point;
+ virtual Rect boundsExact() const {
+ if (isDegenerate()) return chord().boundsExact();
+ return EllipticalArc::boundsExact();
}
-
- double start_angle() const
- {
- return m_start_angle;
+ virtual OptRect boundsLocal(OptInterval const &i, unsigned int deg) const {
+ if (isDegenerate()) return chord().boundsLocal(i, deg);
+ return EllipticalArc::boundsLocal(i, deg);
}
- double end_angle() const
- {
- return m_end_angle;
+ virtual Curve *derivative() const {
+ if (isDegenerate()) return chord().derivative();
+ return EllipticalArc::derivative();
}
- double ray(unsigned int i) const
- {
- return (i == 0) ? m_rx : m_ry;
+ virtual std::vector<Coord> roots(Coord v, Dim2 d) const {
+ if (isDegenerate()) return chord().roots(v, d);
+ return EllipticalArc::roots(v, d);
}
-
- bool large_arc_flag() const
- {
- return m_large_arc;
- }
-
- bool sweep_flag() const
- {
- return m_sweep;
- }
-
- double rotation_angle() const
- {
- return m_rot_angle;
- }
-
- void setInitial( const Point _point)
- {
- m_initial_point = _point;
- calculate_center_and_extreme_angles();
- }
-
- void setFinal( const Point _point)
- {
- m_final_point = _point;
- calculate_center_and_extreme_angles();
- }
-
- void setExtremes( const Point& _initial_point, const Point& _final_point )
- {
- m_initial_point = _initial_point;
- m_final_point = _final_point;
- calculate_center_and_extreme_angles();
- }
-
- bool isDegenerate() const
- {
- return ( are_near(ray(X), 0) || are_near(ray(Y), 0) );
- }
-
- bool is_svg_compliant() const
- {
- return m_svg_compliant;
- }
-
- virtual OptRect boundsFast() const
- {
- return boundsExact();
- }
-
- virtual OptRect boundsExact() const;
-
- // TODO: native implementation of the following methods
- virtual OptRect boundsLocal(OptInterval i, unsigned int deg) const
- {
- if (isDegenerate() && is_svg_compliant())
- return chord().boundsLocal(i, deg);
- else
- return SBasisCurve(toSBasis()).boundsLocal(i, deg);
- }
-
- std::vector<double> roots(double v, Dim2 d) const;
-
- /*
- * find all the points on the curve portion between "from" and "to"
- * at the same smallest distance from the point "p" the points are returned
- * as their parameter t value;
- */
- std::vector<double>
- allNearestPoints( Point const& p, double from = 0, double to = 1 ) const;
-
- /*
- * find a point on the curve portion between "from" and "to"
- * at the same smallest distance from the point "p";
- * the point is returned as its parameter t value;
- */
- double nearestPoint( Point const& p, double from = 0, double to = 1 ) const
- {
- if ( are_near(ray(X), ray(Y)) && are_near(center(), p) )
- {
- return from;
+#ifdef HAVE_GSL
+ virtual std::vector<Coord> allNearestPoints( Point const& p, double from = 0, double to = 1 ) const {
+ if (isDegenerate()) {
+ std::vector<Coord> result;
+ result.push_back(chord().nearestPoint(p, from, to));
+ return result;
}
- return allNearestPoints(p, from, to).front();
- }
-
- // TODO: native implementation of the following methods
- int winding(Point p) const
- {
- if (isDegenerate() && is_svg_compliant())
- return chord().winding(p);
- else
- return SBasisCurve(toSBasis()).winding(p);
- }
-
- int degreesOfFreedom() const { return 5;}
-
- Curve *derivative() const;
-
- Curve *transformed(Matrix const &m) const;
-
- std::vector<Point> pointAndDerivatives(Coord t, unsigned int n) const;
-
- D2<SBasis> toSBasis() const;
-
- /*
- * return true if the angle argument (in radiants) is contained
- * in the range [start_angle(), end_angle() ]
- */
- bool containsAngle(Coord angle) const;
-
- /*
- * return the value of the d-dimensional coordinate related to "t"
- * here t belongs to the [0,2PI] domain
- */
- double valueAtAngle(Coord t, Dim2 d) const;
-
- /*
- * return the point related to the parameter value "t"
- * here t belongs to the [0,2PI] domain
- */
- Point pointAtAngle(Coord t) const
- {
- double sin_rot_angle = std::sin(rotation_angle());
- double cos_rot_angle = std::cos(rotation_angle());
- Matrix m( ray(X) * cos_rot_angle, ray(X) * sin_rot_angle,
- -ray(Y) * sin_rot_angle, ray(Y) * cos_rot_angle,
- center(X), center(Y) );
- Point p( std::cos(t), std::sin(t) );
- return p * m;
- }
-
- /*
- * return the value of the d-dimensional coordinate related to "t"
- * here t belongs to the [0,1] domain
- */
- double valueAt(Coord t, Dim2 d) const
- {
- if (isDegenerate() && is_svg_compliant())
- return chord().valueAt(t, d);
-
- Coord tt = map_to_02PI(t);
- return valueAtAngle(tt, d);
- }
-
- /*
- * return the point related to the parameter value "t"
- * here t belongs to the [0,1] domain
- */
- Point pointAt(Coord t) const
- {
- if (isDegenerate() && is_svg_compliant())
- return chord().pointAt(t);
-
- Coord tt = map_to_02PI(t);
- return pointAtAngle(tt);
- }
-
- std::pair<SVGEllipticalArc, SVGEllipticalArc>
- subdivide(Coord t) const
- {
- SVGEllipticalArc* arc1 = static_cast<SVGEllipticalArc*>(portion(0, t));
- SVGEllipticalArc* arc2 = static_cast<SVGEllipticalArc*>(portion(t, 1));
- assert( arc1 != NULL && arc2 != NULL);
- std::pair<SVGEllipticalArc, SVGEllipticalArc> arc_pair(*arc1, *arc2);
- delete arc1;
- delete arc2;
- return arc_pair;
- }
-
- Curve* portion(double f, double t) const;
-
- // the arc is the same but traversed in the opposite direction
- Curve* reverse() const
- {
- SVGEllipticalArc* rarc = new SVGEllipticalArc( *this );
- rarc->m_sweep = !m_sweep;
- rarc->m_initial_point = m_final_point;
- rarc->m_final_point = m_initial_point;
- rarc->m_start_angle = m_end_angle;
- rarc->m_end_angle = m_start_angle;
- return rarc;
- }
-
-
- double sweep_angle() const
- {
- Coord d = end_angle() - start_angle();
- if ( !sweep_flag() ) d = -d;
- if ( d < 0 )
- d += 2*M_PI;
- return d;
- }
-
- LineSegment chord() const
- {
- return LineSegment(initialPoint(), finalPoint());
- }
-
- private:
- Coord map_to_02PI(Coord t) const;
- Coord map_to_01(Coord angle) const;
- void calculate_center_and_extreme_angles();
-
- private:
- Point m_initial_point, m_final_point;
- double m_rx, m_ry, m_rot_angle;
- bool m_large_arc, m_sweep;
- bool m_svg_compliant;
- double m_start_angle, m_end_angle;
- Point m_center;
-
+ return EllipticalArc::allNearestPoints(p, from, to);
+ }
+#endif
+ virtual D2<SBasis> toSBasis() const {
+ if (isDegenerate()) return chord().toSBasis();
+ return EllipticalArc::toSBasis();
+ }
+ virtual bool isSVGCompliant() const { return true; }
+ // TODO move SVG-specific behavior here.
+//protected:
+ //virtual void _updateCenterAndAngles();
+#endif
}; // end class SVGEllipticalArc
-
/*
* useful for testing and debugging
*/
@@ -379,9 +137,9 @@ operator<< (std::basic_ostream<charT> & os, const SVGEllipticalArc & ea)
{
os << "{ cx: " << ea.center(X) << ", cy: " << ea.center(Y)
<< ", rx: " << ea.ray(X) << ", ry: " << ea.ray(Y)
- << ", rot angle: " << decimal_round(rad_to_deg(ea.rotation_angle()),2)
- << ", start angle: " << decimal_round(rad_to_deg(ea.start_angle()),2)
- << ", end angle: " << decimal_round(rad_to_deg(ea.end_angle()),2)
+ << ", rot angle: " << decimal_round(rad_to_deg(ea.rotationAngle()),2)
+ << ", start angle: " << decimal_round(rad_to_deg(ea.initialAngle()),2)
+ << ", end angle: " << decimal_round(rad_to_deg(ea.finalAngle()),2)
<< " }";
return os;
@@ -396,6 +154,7 @@ namespace detail
struct ellipse_equation;
}
+// TODO this needs to be rewritten and moved to EllipticalArc header
/*
* make_elliptical_arc
*
@@ -428,7 +187,7 @@ class make_elliptical_arc
* and the generated elliptical arc; the smaller it is the
* the tolerance the higher it is the likelihood.
*/
- make_elliptical_arc( SVGEllipticalArc& _ea,
+ make_elliptical_arc( EllipticalArc& _ea,
curve_type const& _curve,
unsigned int _total_samples,
double _tolerance );
@@ -486,7 +245,7 @@ class make_elliptical_arc
}
private:
- SVGEllipticalArc& ea; // output elliptical arc
+ EllipticalArc& ea; // output elliptical arc
const curve_type & curve; // input curve
Piecewise<D2<SBasis> > dcurve; // derivative of the input curve
NL::LFMEllipse model; // model used for fitting
diff --git a/src/2geom/svg-path-parser.cpp b/src/2geom/svg-path-parser.cpp
index 804284077..4d35ccb35 100644
--- a/src/2geom/svg-path-parser.cpp
+++ b/src/2geom/svg-path-parser.cpp
@@ -1,3 +1,4 @@
+#line 1 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
/**
* \file
* \brief parse SVG path specifications
@@ -139,7 +140,7 @@ private:
};
-#line 144 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.cpp"
+#line 144 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.cpp"
static const char _svg_path_actions[] = {
0, 1, 0, 1, 1, 1, 2, 1,
3, 1, 4, 1, 5, 1, 15, 1,
@@ -1144,7 +1145,7 @@ static const int svg_path_first_final = 270;
static const int svg_path_en_main = 1;
-#line 144 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 144 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
void Parser::parse(char const *str)
@@ -1157,12 +1158,12 @@ throw(SVGPathParseError)
_reset();
-#line 1162 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.cpp"
+#line 1162 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.cpp"
{
cs = svg_path_start;
}
-#line 1167 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.cpp"
+#line 1167 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.cpp"
{
int _klen;
unsigned int _trans;
@@ -1235,13 +1236,13 @@ _match:
switch ( *_acts++ )
{
case 0:
-#line 156 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 156 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
start = p;
}
break;
case 1:
-#line 160 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 160 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
char const *end=p;
std::string buf(start, end);
@@ -1250,55 +1251,55 @@ _match:
}
break;
case 2:
-#line 167 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 167 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_push(1.0);
}
break;
case 3:
-#line 171 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 171 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_push(0.0);
}
break;
case 4:
-#line 175 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 175 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_absolute = true;
}
break;
case 5:
-#line 179 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 179 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_absolute = false;
}
break;
case 6:
-#line 183 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 183 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_moveTo(_pop_point());
}
break;
case 7:
-#line 187 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 187 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_lineTo(_pop_point());
}
break;
case 8:
-#line 191 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 191 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_hlineTo(Point(_pop_coord(X), _current[Y]));
}
break;
case 9:
-#line 195 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 195 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_vlineTo(Point(_current[X], _pop_coord(Y)));
}
break;
case 10:
-#line 199 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 199 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c1 = _pop_point();
@@ -1307,7 +1308,7 @@ _match:
}
break;
case 11:
-#line 206 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 206 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c1 = _pop_point();
@@ -1315,7 +1316,7 @@ _match:
}
break;
case 12:
-#line 212 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 212 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c = _pop_point();
@@ -1323,14 +1324,14 @@ _match:
}
break;
case 13:
-#line 218 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 218 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
_quadTo(_quad_tangent, p);
}
break;
case 14:
-#line 223 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 223 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
Point point = _pop_point();
bool sweep = _pop_flag();
@@ -1343,16 +1344,16 @@ _match:
}
break;
case 15:
-#line 234 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 234 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{
_closePath();
}
break;
case 16:
-#line 370 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 370 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
{goto _out;}
break;
-#line 1357 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.cpp"
+#line 1357 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.cpp"
}
}
@@ -1363,7 +1364,7 @@ _again:
goto _resume;
_out: {}
}
-#line 380 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl"
+#line 380 "/opt/shared/work/programming/eclipse/eclipse_3.4/lib2geom/src/2geom/svg-path-parser.rl"
if ( cs < svg_path_first_final ) {
diff --git a/src/2geom/svg-path.cpp b/src/2geom/svg-path.cpp
index 3e4bf7bec..d459b3e1b 100644
--- a/src/2geom/svg-path.cpp
+++ b/src/2geom/svg-path.cpp
@@ -61,8 +61,8 @@ void output(QuadraticBezier const &curve, SVGPathSink &sink) {
}
void output(SVGEllipticalArc const &curve, SVGPathSink &sink) {
- sink.arcTo( curve.ray(X), curve.ray(Y), curve.rotation_angle(),
- curve.large_arc_flag(), curve.sweep_flag(),
+ sink.arcTo( curve.ray(X), curve.ray(Y), curve.rotationAngle(),
+ curve.largeArc(), curve.sweep(),
curve.finalPoint() );
}
diff --git a/src/2geom/sweep.h b/src/2geom/sweep.h
index 299813244..1c73efee0 100644
--- a/src/2geom/sweep.h
+++ b/src/2geom/sweep.h
@@ -51,6 +51,9 @@ struct Event {
if(x > other.x) return false;
return closing < other.closing;
}
+ bool operator==(Event const &other) const {
+ return other.x == x && other.ix == ix && other.closing == closing;
+ }
};
std::vector<std::vector<unsigned> >
sweep_bounds(std::vector<Rect>, Dim2 dim = X);
diff --git a/src/2geom/transforms.cpp b/src/2geom/transforms.cpp
index 8182ce16d..2658719c4 100644
--- a/src/2geom/transforms.cpp
+++ b/src/2geom/transforms.cpp
@@ -1,106 +1,143 @@
+/**
+ * @file
+ * @brief Affine transformation classes
+ *//*
+ * Authors:
+ * ? <?@?.?>
+ * Krzysztof Kosiński <tweenk.pl@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.
+ */
+
+#include <boost/concept_check.hpp>
+#include <2geom/point.h>
#include <2geom/transforms.h>
namespace Geom {
-Matrix operator*(Translate const &t, Scale const &s) {
- Matrix ret(s);
- ret[4] = t[X] * s[X];
- ret[5] = t[Y] * s[Y];
- return ret;
+// Point transformation methods.
+Point &Point::operator*=(Translate const &t)
+{
+ _pt[X] += t.vec[X];
+ _pt[Y] += t.vec[Y];
+ return *this;
}
-
-Matrix operator*(Translate const &t, Rotate const &r) {
- Matrix ret(r);
- ret.setTranslation(t.vec * ret);
- return ret;
+Point &Point::operator*=(Scale const &s)
+{
+ _pt[X] *= s.vec[X];
+ _pt[Y] *= s.vec[Y];
+ return *this;
}
-
-Matrix operator*(Scale const &s, Translate const &t) {
- return Matrix(s[0], 0,
- 0 , s[1],
- t[0], t[1]);
+Point &Point::operator*=(Rotate const &r)
+{
+ double x = _pt[X], y = _pt[Y];
+ _pt[X] = x * r.vec[X] - y * r.vec[Y];
+ _pt[Y] = y * r.vec[X] + x * r.vec[Y];
+ return *this;
}
-
-Matrix operator*(Scale const &s, Matrix const &m) {
- Matrix ret(m);
- ret[0] *= s[X];
- ret[1] *= s[X];
- ret[2] *= s[Y];
- ret[3] *= s[Y];
- return ret;
+Point &Point::operator*=(HShear const &h)
+{
+ _pt[X] += h.f * _pt[X];
+ return *this;
}
-
-Matrix operator*(Matrix const &m, Translate const &t) {
- Matrix ret(m);
- ret[4] += t[X];
- ret[5] += t[Y];
- return ret;
+Point &Point::operator*=(VShear const &v)
+{
+ _pt[Y] += v.f * _pt[Y];
+ return *this;
}
-Matrix operator*(Matrix const &m, Scale const &s) {
- Matrix ret(m);
- ret[0] *= s[X]; ret[1] *= s[Y];
- ret[2] *= s[X]; ret[3] *= s[Y];
- ret[4] *= s[X]; ret[5] *= s[Y];
- return ret;
-}
+// Affine multiplication methods.
-Matrix operator*(Matrix const &m, Rotate const &r) {
- // TODO: we just convert the Rotate to a matrix and use the existing operator*(); is there a better way?
- Matrix ret(m);
- ret *= (Matrix) r;
- return ret;
+/** @brief Combine this transformation with a translation. */
+Affine &Affine::operator*=(Translate const &t) {
+ _c[4] += t[X];
+ _c[5] += t[Y];
+ return *this;
}
-Translate pow(Translate const &t, int n) {
- return Translate(t[0]*n, t[1]*n);
+/** @brief Combine this transformation with scaling. */
+Affine &Affine::operator*=(Scale const &s) {
+ _c[0] *= s[X]; _c[1] *= s[Y];
+ _c[2] *= s[X]; _c[3] *= s[Y];
+ _c[4] *= s[X]; _c[5] *= s[Y];
+ return *this;
}
-Coord pow(Coord x, long n) // shamelessly lifted from WP
-{
- Coord result = 1;
- while ( n ) {
- if ( n & 1 ) {
- result = result * x;
- n = n-1;
- }
- x = x*x;
- n = n/2;
- }
- return result;
+/** @brief Combine this transformation a rotation. */
+Affine &Affine::operator*=(Rotate const &r) {
+ // TODO: we just convert the Rotate to an Affine and use the existing operator*=()
+ // is there a better way?
+ *this *= (Affine) r;
+ return *this;
}
-Scale pow(Scale const &s, int n) {
- return Scale(pow(s[0],n), pow(s[1],n));
+/** @brief Combine this transformation with horizontal shearing (skew). */
+Affine &Affine::operator*=(HShear const &h) {
+ _c[0] += h.f * _c[1];
+ _c[2] += h.f * _c[3];
+ _c[4] += h.f * _c[5];
+ return *this;
}
-Rotate pow(Rotate x, long n)
-{
- Rotate result(0,1); // identity
- while ( n ) {
- if ( n & 1 ) {
- result = result * x;
- n = n-1;
- }
- x = x*x;
- n = n/2;
- }
- return result;
+/** @brief Combine this transformation with vertical shearing (skew). */
+Affine &Affine::operator*=(VShear const &v) {
+ _c[1] += v.f * _c[0];
+ _c[3] += v.f * _c[2];
+ _c[5] += v.f * _c[4];
+ return *this;
}
-Matrix pow(Matrix x, long n)
+// this checks whether the requirements of TransformConcept are satisfied for all transforms.
+// if you add a new transform type, include it here!
+void check_transforms()
{
- Matrix result;
- result.setIdentity();
- while ( n ) {
- if ( n & 1 ) {
- result = result * x;
- n = n-1;
- }
- x = x*x;
- n = n/2;
- }
- return result;
+#ifdef BOOST_CONCEPT_ASSERT
+ BOOST_CONCEPT_ASSERT((TransformConcept<Translate>));
+ BOOST_CONCEPT_ASSERT((TransformConcept<Scale>));
+ BOOST_CONCEPT_ASSERT((TransformConcept<Rotate>));
+ BOOST_CONCEPT_ASSERT((TransformConcept<HShear>));
+ BOOST_CONCEPT_ASSERT((TransformConcept<VShear>));
+ BOOST_CONCEPT_ASSERT((TransformConcept<Affine>)); // Affine is also a transform
+#endif
+
+ // check inter-transform multiplication
+ Affine m;
+ Translate t(Translate::identity());
+ Scale s(Scale::identity());
+ Rotate r(Rotate::identity());
+ HShear h(HShear::identity());
+ VShear v(VShear::identity());
+
+ // notice that the first column is always the same and enumerates all transform types,
+ // while the second one changes to each transform type in turn.
+ m = t * t; m = t * s; m = t * r; m = t * h; m = t * v;
+ m = s * t; m = s * s; m = s * r; m = s * h; m = s * v;
+ m = r * t; m = r * s; m = r * r; m = r * h; m = r * v;
+ m = h * t; m = h * s; m = h * r; m = h * h; m = h * v;
+ m = v * t; m = v * s; m = v * r; m = v * h; m = v * v;
}
}
diff --git a/src/2geom/transforms.h b/src/2geom/transforms.h
index 1d8d87da3..b8db82f14 100644
--- a/src/2geom/transforms.h
+++ b/src/2geom/transforms.h
@@ -1,11 +1,12 @@
/**
- * \file
- * \brief Transforms should be applied left to right. scale * translate means: first scale, then translate.
- *
+ * @file
+ * @brief Affine transformation classes
+ *//*
* Authors:
- * ? <?@?.?>
+ * ? <?@?.?>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
*
- * Copyright ?-? authors
+ * 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
@@ -29,132 +30,235 @@
* 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_Geom_TRANSFORMS_H
#define SEEN_Geom_TRANSFORMS_H
-#include <2geom/matrix.h>
+#include <2geom/forward.h>
+#include <2geom/affine.h>
#include <cmath>
namespace Geom {
+/** @brief Type requirements for transforms.
+ * @ingroup Concepts */
template <typename T>
struct TransformConcept {
T t;
- Matrix m;
+ Affine m;
Point p;
+ bool bool_;
void constraints() {
m = t; //implicit conversion
- t = t.inverse();
+ m *= t;
+ m = m * t;
+ m = t * m;
+ p *= t;
p = p * t;
+ p = t * p;
+ t *= t;
t = t * t;
+ t = pow(t, 3);
+ p /= t; // multiplication by inverse
+ p = p / t;
+ bool_ = (t == t);
+ bool_ = (t != t);
+ t = T::identity();
+ t = t.inverse();
}
};
+/** @brief Base template for transforms. */
+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;
+ }
+};
-class Rotate;
-class Translate {
- private:
- Translate();
+/** @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) {
+ 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 >
+{
+ Translate() : vec(0, 0) {}
Point vec;
- public:
+public:
+ /** @brief Construct a translation from its vector. */
explicit Translate(Point const &p) : vec(p) {}
- explicit Translate(Coord const x, Coord const y) : vec(x, y) {}
- inline operator Matrix() const { return Matrix(1, 0, 0, 1, vec[X], vec[Y]); }
+ /** @brief Construct a translation from its coordinates. */
+ explicit Translate(Coord x, Coord y) : vec(x, y) {}
- inline Coord operator[](Dim2 const dim) const { return vec[dim]; }
- inline Coord operator[](unsigned const dim) const { return vec[dim]; }
- inline bool operator==(Translate const &o) const { return vec == o.vec; }
- inline bool operator!=(Translate const &o) const { return vec != o.vec; }
+ 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; }
- inline Translate inverse() const { return Translate(-vec); }
+ /** @brief Get the inverse translation. */
+ Translate inverse() const { return Translate(-vec); }
+ /** @brief Get a translation that doesn't do anything. */
+ static Translate identity() { Translate ret; return ret; }
- friend Point operator*(Point const &v, Translate const &t);
- inline Translate operator*(Translate const &b) const { return Translate(vec + b.vec); }
-
- friend Matrix operator*(Translate const &t, Rotate const &r);
+ friend class Point;
};
-inline Point operator*(Point const &v, Translate const &t) { return v + t.vec; }
-
-class Scale {
- private:
+/** @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;
- Scale();
- public:
+ Scale() : vec(1, 1) {}
+public:
explicit Scale(Point const &p) : vec(p) {}
- Scale(Coord const x, Coord const y) : vec(x, y) {}
- explicit Scale(Coord const s) : vec(s, s) {}
- inline operator Matrix() const { return Matrix(vec[X], 0, 0, vec[Y], 0, 0); }
+ Scale(Coord x, Coord y) : vec(x, y) {}
+ explicit Scale(Coord s) : vec(s, s) {}
+ inline operator Affine() const { Affine ret(vec[X], 0, 0, vec[Y], 0, 0); return ret; }
- inline Coord operator[](Dim2 const d) const { return vec[d]; }
- inline Coord operator[](unsigned const d) const { return vec[d]; }
+ 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?
- inline Coord &operator[](Dim2 const d) { return vec[d]; }
- inline Coord &operator[](unsigned const d) { return vec[d]; }
- inline bool operator==(Scale const &o) const { return vec == o.vec; }
- inline bool operator!=(Scale const &o) const { return vec != o.vec; }
+ 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; }
+ Scale inverse() const { return Scale(1./vec[0], 1./vec[1]); }
+ static Scale identity() { Scale ret; return ret; }
- inline Scale inverse() const { return Scale(1./vec[0], 1./vec[1]); }
-
- friend Point operator*(Point const &v, Translate const &t);
- inline Scale operator*(Scale const &b) const { return Scale(vec[X]*b[X], vec[Y]*b[Y]); }
+ friend class Point;
};
-inline Point operator*(Point const &p, Scale const &s) { return Point(p[X] * s[X], p[Y] * s[Y]); }
-
-/** Notionally an Geom::Matrix corresponding to rotation about the origin.
- Behaves like Geom::Matrix for multiplication.
-**/
-class Rotate {
- private:
- Rotate() {}
+/** @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 >
+{
+ Rotate() : vec(1, 0) {}
Point vec;
- public:
- explicit Rotate(Coord theta) : vec(std::cos(theta), std::sin(theta)) {}
- Rotate(Point const &p) {Point v = p; v.normalize(); vec = v;} //TODO: UGLY!
+public:
+ /** @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)) {}
+ /** @brief Construct a rotation from its characteristic vector. */
+ explicit Rotate(Point const &p) : vec(unit_vector(p)) {}
+ /** @brief Construct a rotation from the coordinates of its characteristic vector. */
explicit Rotate(Coord x, Coord y) { Rotate(Point(x, y)); }
- inline operator Matrix() const { return Matrix(vec[X], vec[Y], -vec[Y], vec[X], 0, 0); }
+ operator Affine() const { Affine ret(vec[X], vec[Y], -vec[Y], vec[X], 0, 0); return ret; }
- inline Point vector() const { return vec; }
- inline Coord operator[](Dim2 const dim) const { return vec[dim]; }
- inline Coord operator[](unsigned const dim) const { return vec[dim]; }
- inline bool operator==(Rotate const &o) const { return vec == o.vec; }
- inline bool operator!=(Rotate const &o) const { return vec != o.vec; }
-
- inline Rotate inverse() const {
+ /** @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 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 0-degree rotation. */
+ static Rotate identity() { Rotate ret; return ret; }
+ /** @brief Construct a rotation from its angle in degrees.
+ * Positive arguments correspond to counter-clockwise rotations (if Y grows upwards). */
static Rotate from_degrees(Coord deg) {
Coord rad = (deg / 180.0) * M_PI;
return Rotate(rad);
}
- friend Point operator*(Point const &v, Rotate const &r);
- inline Rotate operator*(Rotate const &b) const { return Rotate(vec * b); }
+ friend class Point;
};
-inline Point operator*(Point const &v, Rotate const &r) { return v ^ r.vec; }
+/** @brief Common base for shearing transforms.
+ * @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 const*>(this); }
+ bool operator==(S const &s) const { return f == s.f; }
+ S inverse() const { return S(-f); }
+ static S identity() { return S(0); }
-Matrix operator*(Translate const &t, Scale const &s);
-Matrix operator*(Translate const &t, Rotate const &r);
+ friend class Point;
+ friend class Affine;
+};
-Matrix operator*(Scale const &s, Translate const &t);
-Matrix operator*(Scale const &s, Matrix const &m);
+/** @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; }
+};
-Matrix operator*(Matrix const &m, Translate const &t);
-Matrix operator*(Matrix const &m, Scale const &s);
-Matrix operator*(Matrix const &m, Rotate const &r);
-Matrix operator*(Matrix const &m1, Matrix const &m2);
+/** @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; }
+};
-Translate pow(Translate const &t, int n);
-Scale pow(Scale const &t, int n);
-Rotate pow(Rotate t, int n);
-Matrix pow(Matrix t, int n);
+/** @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;
+}
//TODO: matrix to trans/scale/rotate
diff --git a/src/2geom/utils.h b/src/2geom/utils.h
index dcadc8431..31468a204 100644
--- a/src/2geom/utils.h
+++ b/src/2geom/utils.h
@@ -33,7 +33,7 @@
*
*/
-#include <cmath>
+#include <2geom/math-utils.h>
#include <vector>
namespace Geom {
@@ -41,45 +41,6 @@ namespace Geom {
// proper logical xor
inline bool logical_xor (bool a, bool b) { return (a || b) && !(a && b); }
-/** Sign function - indicates the sign of a numeric type. -1 indicates negative, 1 indicates
- * positive, and 0 indicates, well, 0. Mathsy people will know this is basically the derivative
- * of abs, except for the fact that it is defined on 0.
- */
-template <class T> inline int sgn(const T& x) {return (x < 0 ? -1 : (x > 0 ? 1 : 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. 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; }
-
-/** Returns x rounded to the nearest integer. It is unspecified what happens
- * if x is half way between two integers: we may in future use rint/round
- * on platforms that have them.
- */
-inline double round(double const x) { return std::floor(x + .5); }
-
-/** Returns x rounded to the nearest \a places decimal places.
-
- 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 const x, int const places) {
- //TODO: possibly implement with modulus instead?
- double const multiplier = std::pow(10.0, places);
- return round( x * multiplier ) / multiplier;
-}
-
-
void binomial_coefficients(std::vector<size_t>& bc, size_t n);
}