summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKrzysztof Kosi??ski <tweenk.pl@gmail.com>2016-02-08 07:32:51 +0000
committerKrzysztof Kosiński <tweenk.pl@gmail.com>2016-02-08 07:32:51 +0000
commit0a2477feea6e1df586b926b8482afbf79e2355e1 (patch)
tree109bce789702f504ab3bc90e2fe4541b421b0940
parentChanged one icon/action in meassure toolbar to one more explicit (diff)
downloadinkscape-0a2477feea6e1df586b926b8482afbf79e2355e1.tar.gz
inkscape-0a2477feea6e1df586b926b8482afbf79e2355e1.zip
Sync 2Geom to commit 5ee51c1c4f2066faa3e2c82021fc92671ad44ba4
(bzr r14639)
-rw-r--r--src/2geom/CMakeLists.txt1
-rw-r--r--src/2geom/Makefile_insert2
-rw-r--r--src/2geom/angle.h4
-rw-r--r--src/2geom/bezier-clipping.cpp4
-rw-r--r--src/2geom/bezier-curve.cpp98
-rw-r--r--src/2geom/bezier-curve.h14
-rw-r--r--src/2geom/crossing.h2
-rw-r--r--src/2geom/curve.cpp8
-rw-r--r--src/2geom/curve.h3
-rw-r--r--src/2geom/d2-sbasis.cpp40
-rw-r--r--src/2geom/d2-sbasis.h165
-rw-r--r--src/2geom/d2.h81
-rw-r--r--src/2geom/ellipse.cpp78
-rw-r--r--src/2geom/ellipse.h3
-rw-r--r--src/2geom/elliptical-arc.cpp303
-rw-r--r--src/2geom/elliptical-arc.h4
-rw-r--r--src/2geom/generic-interval.h2
-rw-r--r--src/2geom/generic-rect.h2
-rw-r--r--src/2geom/int-point.h13
-rw-r--r--src/2geom/intersection-graph.cpp373
-rw-r--r--src/2geom/intersection-graph.h67
-rw-r--r--src/2geom/line.cpp14
-rw-r--r--src/2geom/line.h2
-rw-r--r--src/2geom/linear.h137
-rw-r--r--src/2geom/path.cpp268
-rw-r--r--src/2geom/path.h76
-rw-r--r--src/2geom/pathvector.cpp111
-rw-r--r--src/2geom/pathvector.h2
-rw-r--r--src/2geom/piecewise.h2
-rw-r--r--src/2geom/rect.cpp78
-rw-r--r--src/2geom/rect.h57
-rw-r--r--src/2geom/sbasis-curve.h5
-rw-r--r--src/2geom/sbasis-math.cpp3
-rw-r--r--src/2geom/svg-path-parser.cpp4
-rw-r--r--src/2geom/svg-path-writer.cpp2
-rw-r--r--src/2geom/sweeper.h238
-rw-r--r--src/2geom/utils.h24
-rw-r--r--src/2geom/viewbox.cpp133
-rw-r--r--src/2geom/viewbox.h102
-rw-r--r--src/display/canvas-axonomgrid.cpp8
-rw-r--r--src/display/nr-filter.cpp2
-rw-r--r--src/gradient-chemistry.cpp2
-rw-r--r--src/live_effects/lpe-bendpath.cpp4
-rw-r--r--src/live_effects/lpe-copy_rotate.cpp15
-rw-r--r--src/live_effects/lpe-dynastroke.cpp1
-rw-r--r--src/live_effects/lpe-interpolate.cpp1
-rw-r--r--src/live_effects/lpe-knot.cpp1
-rw-r--r--src/live_effects/lpe-patternalongpath.cpp4
-rw-r--r--src/live_effects/lpe-simplify.cpp4
-rw-r--r--src/live_effects/lpe-transform_2pts.cpp8
-rw-r--r--src/live_effects/parameter/filletchamferpointarray.cpp2
-rw-r--r--src/selection.cpp2
-rw-r--r--src/seltrans.cpp4
-rw-r--r--src/sp-guide.cpp2
-rw-r--r--src/svg/svg-affine.cpp2
-rw-r--r--src/svg/svg-path.cpp2
-rw-r--r--src/ui/dialog/guides.cpp4
-rw-r--r--src/ui/tools/measure-tool.cpp18
-rw-r--r--src/ui/tools/node-tool.cpp4
59 files changed, 1406 insertions, 1209 deletions
diff --git a/src/2geom/CMakeLists.txt b/src/2geom/CMakeLists.txt
index eb7abb614..33f8baee6 100644
--- a/src/2geom/CMakeLists.txt
+++ b/src/2geom/CMakeLists.txt
@@ -48,7 +48,6 @@ set(2geom_SRC
toposweep.cpp
transforms.cpp
utils.cpp
- viewbox.cpp
# -------
diff --git a/src/2geom/Makefile_insert b/src/2geom/Makefile_insert
index e3c6836fd..b56942caa 100644
--- a/src/2geom/Makefile_insert
+++ b/src/2geom/Makefile_insert
@@ -120,8 +120,6 @@
2geom/transforms.h \
2geom/utils.cpp \
2geom/utils.h \
- 2geom/viewbox.cpp \
- 2geom/viewbox.h \
2geom/numeric/fitting-model.h \
2geom/numeric/fitting-tool.h \
2geom/numeric/linear_system.h \
diff --git a/src/2geom/angle.h b/src/2geom/angle.h
index af4442e88..f0caaba6b 100644
--- a/src/2geom/angle.h
+++ b/src/2geom/angle.h
@@ -383,10 +383,10 @@ private:
/** @brief Given an angle in degrees, return radians
* @relates Angle */
-inline Coord deg_to_rad(Coord deg) { return deg*M_PI/180.0;}
+inline Coord rad_from_deg(Coord deg) { return deg*M_PI/180.0;}
/** @brief Given an angle in radians, return degrees
* @relates Angle */
-inline Coord rad_to_deg(Coord rad) { return rad*180.0/M_PI;}
+inline Coord deg_from_rad(Coord rad) { return rad*180.0/M_PI;}
} // end namespace Geom
diff --git a/src/2geom/bezier-clipping.cpp b/src/2geom/bezier-clipping.cpp
index be8dd5a5f..03a6dfecd 100644
--- a/src/2geom/bezier-clipping.cpp
+++ b/src/2geom/bezier-clipping.cpp
@@ -788,7 +788,7 @@ void iterate<intersection_point_tag> (std::vector<Interval>& domsA,
#endif
dom = clip<intersection_point_tag>(*C1, *C2, precision);
- if (dom.isEmpty())
+ if (dom.empty())
{
#if VERBOSE
std::cerr << "dom: empty" << std::endl;
@@ -937,7 +937,7 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
#endif
dom = clip<collinear_normal_tag>(*C1, *C2, precision);
- if (dom.isEmpty()) {
+ if (dom.empty()) {
#if VERBOSE
std::cerr << "dom: empty" << std::endl;
#endif
diff --git a/src/2geom/bezier-curve.cpp b/src/2geom/bezier-curve.cpp
index 17221264b..866263047 100644
--- a/src/2geom/bezier-curve.cpp
+++ b/src/2geom/bezier-curve.cpp
@@ -115,6 +115,17 @@ BezierCurve::BezierCurve(std::vector<Point> const &pts)
}
}
+bool BezierCurve::isDegenerate() const
+{
+ for (unsigned d = 0; d < 2; ++d) {
+ Coord ic = inner[d][0];
+ for (unsigned i = 1; i < size(); ++i) {
+ if (inner[d][i] != ic) return false;
+ }
+ }
+ return true;
+}
+
Coord BezierCurve::length(Coord tolerance) const
{
switch (order())
@@ -169,6 +180,29 @@ BezierCurve::intersect(Curve const &other, Coord eps) const
return result;
}
+bool BezierCurve::isNear(Curve const &c, Coord precision) const
+{
+ if (this == &c) return true;
+
+ BezierCurve const *other = dynamic_cast<BezierCurve const *>(&c);
+ if (!other) return false;
+
+ if (!are_near(inner.at0(), other->inner.at0(), precision)) return false;
+ if (!are_near(inner.at1(), other->inner.at1(), precision)) return false;
+
+ if (size() == other->size()) {
+ for (unsigned i = 1; i < order(); ++i) {
+ if (!are_near(inner.point(i), other->inner.point(i), precision)) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ // TODO: comparison after degree elevation
+ return false;
+ }
+}
+
bool BezierCurve::operator==(Curve const &c) const
{
if (this == &c) return true;
@@ -281,6 +315,23 @@ std::vector<CurveIntersection> BezierCurveN<1>::intersect(Curve const &other, Co
}
template <>
+int BezierCurveN<1>::winding(Point const &p) const
+{
+ Point ip = inner.at0(), fp = inner.at1();
+ if (p[Y] == std::max(ip[Y], fp[Y])) return 0;
+
+ Point v = fp - ip;
+ assert(v[Y] != 0);
+ Coord t = (p[Y] - ip[Y]) / v[Y];
+ assert(t >= 0 && t <= 1);
+ Coord xcross = lerp(t, ip[X], fp[X]);
+ if (xcross > p[X]) {
+ return v[Y] > 0 ? 1 : -1;
+ }
+ return 0;
+}
+
+template <>
void BezierCurveN<1>::feed(PathSink &sink, bool moveto_initial) const
{
if (moveto_initial) {
@@ -308,7 +359,7 @@ void BezierCurveN<3>::feed(PathSink &sink, bool moveto_initial) const
}
-static Coord bezier_length_internal(std::vector<Point> &v1, Coord tolerance)
+static Coord bezier_length_internal(std::vector<Point> &v1, Coord tolerance, int level)
{
/* The Bezier length algorithm used in 2Geom utilizes a simple fact:
* the Bezier curve is longer than the distance between its endpoints
@@ -317,13 +368,15 @@ static Coord bezier_length_internal(std::vector<Point> &v1, Coord tolerance)
* error tolerance, we can be sure that the true value is no further than
* 0.5 * tolerance from their arithmetic mean. When it's larger, we recursively
* subdivide the Bezier curve into two parts and add their lengths.
+ *
+ * We cap the maximum number of subdivisions at 256, which corresponds to 8 levels.
*/
Coord lower = distance(v1.front(), v1.back());
Coord upper = 0.0;
for (size_t i = 0; i < v1.size() - 1; ++i) {
upper += distance(v1[i], v1[i+1]);
}
- if (upper - lower < 2*tolerance) {
+ if (upper - lower <= 2*tolerance || level >= 8) {
return (lower + upper) / 2;
}
@@ -381,7 +434,8 @@ static Coord bezier_length_internal(std::vector<Point> &v1, Coord tolerance)
v2[i] = v1[0];
}
- return bezier_length_internal(v1, 0.5*tolerance) + bezier_length_internal(v2, 0.5*tolerance);
+ return bezier_length_internal(v1, 0.5 * tolerance, level + 1) +
+ bezier_length_internal(v2, 0.5 * tolerance, level + 1);
}
/** @brief Compute the length of a bezier curve given by a vector of its control points
@@ -390,17 +444,17 @@ Coord bezier_length(std::vector<Point> const &points, Coord tolerance)
{
if (points.size() < 2) return 0.0;
std::vector<Point> v1 = points;
- return bezier_length_internal(v1, tolerance);
+ return bezier_length_internal(v1, tolerance, 0);
}
-/** @brief Compute the length of a quadratic bezier curve given by its control points
- * @relatesalso QuadraticBezier */
-Coord bezier_length(Point a0, Point a1, Point a2, Coord tolerance)
+static Coord bezier_length_internal(Point a0, Point a1, Point a2, Coord tolerance, int level)
{
Coord lower = distance(a0, a2);
Coord upper = distance(a0, a1) + distance(a1, a2);
- if (upper - lower < 2*tolerance) return (lower + upper)/2;
+ if (upper - lower <= 2*tolerance || level >= 8) {
+ return (lower + upper) / 2;
+ }
Point // Casteljau subdivision
// b0 = a0,
@@ -408,17 +462,25 @@ Coord bezier_length(Point a0, Point a1, Point a2, Coord tolerance)
b1 = 0.5*(a0 + a1),
c1 = 0.5*(a1 + a2),
b2 = 0.5*(b1 + c1); // == c2
- return bezier_length(a0, b1, b2, 0.5*tolerance) + bezier_length(b2, c1, a2, 0.5*tolerance);
+ return bezier_length_internal(a0, b1, b2, 0.5 * tolerance, level + 1) +
+ bezier_length_internal(b2, c1, a2, 0.5 * tolerance, level + 1);
}
-/** @brief Compute the length of a cubic bezier curve given by its control points
- * @relatesalso CubicBezier */
-Coord bezier_length(Point a0, Point a1, Point a2, Point a3, Coord tolerance)
+/** @brief Compute the length of a quadratic bezier curve given by its control points
+ * @relatesalso QuadraticBezier */
+Coord bezier_length(Point a0, Point a1, Point a2, Coord tolerance)
+{
+ return bezier_length_internal(a0, a1, a2, tolerance, 0);
+}
+
+static Coord bezier_length_internal(Point a0, Point a1, Point a2, Point a3, Coord tolerance, int level)
{
Coord lower = distance(a0, a3);
Coord upper = distance(a0, a1) + distance(a1, a2) + distance(a2, a3);
- if (upper - lower < 2*tolerance) return (lower + upper)/2;
+ if (upper - lower <= 2*tolerance || level >= 8) {
+ return (lower + upper) / 2;
+ }
Point // Casteljau subdivision
// b0 = a0,
@@ -429,7 +491,15 @@ Coord bezier_length(Point a0, Point a1, Point a2, Point a3, Coord tolerance)
b2 = 0.5*(b1 + t0),
c2 = 0.5*(t0 + c1),
b3 = 0.5*(b2 + c2); // == c3
- return bezier_length(a0, b1, b2, b3, 0.5*tolerance) + bezier_length(b3, c2, c1, a3, 0.5*tolerance);
+ return bezier_length_internal(a0, b1, b2, b3, 0.5 * tolerance, level + 1) +
+ bezier_length_internal(b3, c2, c1, a3, 0.5 * tolerance, level + 1);
+}
+
+/** @brief Compute the length of a cubic bezier curve given by its control points
+ * @relatesalso CubicBezier */
+Coord bezier_length(Point a0, Point a1, Point a2, Point a3, Coord tolerance)
+{
+ return bezier_length_internal(a0, a1, a2, a3, tolerance, 0);
}
} // end namespace Geom
diff --git a/src/2geom/bezier-curve.h b/src/2geom/bezier-curve.h
index 9ac4d7b4d..9416ba78c 100644
--- a/src/2geom/bezier-curve.h
+++ b/src/2geom/bezier-curve.h
@@ -104,7 +104,7 @@ public:
// implementation of virtual methods goes here
virtual Point initialPoint() const { return inner.at0(); }
virtual Point finalPoint() const { return inner.at1(); }
- virtual bool isDegenerate() const { return inner.isConstant(0); }
+ virtual bool isDegenerate() const;
virtual bool isLineSegment() const { return size() == 2; }
virtual void setInitial(Point const &v) { setPoint(0, v); }
virtual void setFinal(Point const &v) { setPoint(order(), v); }
@@ -166,6 +166,7 @@ public:
}
virtual Coord valueAt(Coord t, Dim2 d) const { return inner[d].valueAt(t); }
virtual D2<SBasis> toSBasis() const {return inner.toSBasis(); }
+ virtual bool isNear(Curve const &c, Coord precision) const;
virtual bool operator==(Curve const &c) const;
virtual void feed(PathSink &sink, bool) const;
};
@@ -244,6 +245,10 @@ public:
BezierCurveN(sx.second, sy.second));
}
+ virtual bool isDegenerate() const {
+ return BezierCurve::isDegenerate();
+ }
+
virtual bool isLineSegment() const {
return size() == 2;
}
@@ -274,6 +279,9 @@ public:
// call super. this is implemented only to allow specializations
return BezierCurve::intersect(other, eps);
}
+ virtual int winding(Point const &p) const {
+ return Curve::winding(p);
+ }
virtual void feed(PathSink &sink, bool moveto_initial) const {
// call super. this is implemented only to allow specializations
BezierCurve::feed(sink, moveto_initial);
@@ -304,10 +312,14 @@ Curve *BezierCurveN<degree>::derivative() const {
}
// optimized specializations
+template <> inline bool BezierCurveN<1>::isDegenerate() const {
+ return inner[X][0] == inner[X][1] && inner[Y][0] == inner[Y][1];
+}
template <> inline bool BezierCurveN<1>::isLineSegment() const { return true; }
template <> Curve *BezierCurveN<1>::derivative() const;
template <> Coord BezierCurveN<1>::nearestTime(Point const &, Coord, Coord) const;
template <> std::vector<CurveIntersection> BezierCurveN<1>::intersect(Curve const &, Coord) const;
+template <> int BezierCurveN<1>::winding(Point const &) const;
template <> void BezierCurveN<1>::feed(PathSink &sink, bool moveto_initial) const;
template <> void BezierCurveN<2>::feed(PathSink &sink, bool moveto_initial) const;
template <> void BezierCurveN<3>::feed(PathSink &sink, bool moveto_initial) const;
diff --git a/src/2geom/crossing.h b/src/2geom/crossing.h
index 425fa58f5..0f007b192 100644
--- a/src/2geom/crossing.h
+++ b/src/2geom/crossing.h
@@ -119,7 +119,7 @@ struct CrossingOrder {
(ix == b.a ? b.ta : b.tb);
else
return (ix == a.a ? a.ta : a.tb) >
- (ix == a.a ? a.ta : a.tb);
+ (ix == b.a ? b.ta : b.tb);
}
};
diff --git a/src/2geom/curve.cpp b/src/2geom/curve.cpp
index 24c8be9f8..387a9180b 100644
--- a/src/2geom/curve.cpp
+++ b/src/2geom/curve.cpp
@@ -69,14 +69,14 @@ int Curve::winding(Point const &p) const
// skip endpoint roots when they are local maxima on the Y axis
// this follows the convention used in other winding routines,
// i.e. that the bottommost coordinate is not part of the shape
- bool ingore_0 = unitTangentAt(0)[Y] <= 0;
+ bool ignore_0 = unitTangentAt(0)[Y] <= 0;
bool ignore_1 = unitTangentAt(1)[Y] >= 0;
int wind = 0;
for (std::size_t i = 0; i < ts.size(); ++i) {
Coord t = ts[i];
//std::cout << t << std::endl;
- if ((t == 0 && ingore_0) || (t == 1 && ignore_1)) continue;
+ if ((t == 0 && ignore_0) || (t == 1 && ignore_1)) continue;
if (valueAt(t, X) > p[X]) { // root is ray intersection
Point tangent = unitTangentAt(t);
if (tangent[Y] > 0) {
@@ -108,11 +108,7 @@ std::vector<CurveIntersection> Curve::intersectSelf(Coord eps) const
// Monotonic segments cannot have self-intersections.
// Thus, we can split the curve at roots and intersect the portions.
std::vector<Coord> splits;
-#if __cplusplus <= 199711L
std::auto_ptr<Curve> deriv(derivative());
-#else
- std::unique_ptr<Curve> deriv(derivative());
-#endif
splits = deriv->roots(0, X);
if (splits.empty()) {
return result;
diff --git a/src/2geom/curve.h b/src/2geom/curve.h
index abbdb1100..41db9ca8a 100644
--- a/src/2geom/curve.h
+++ b/src/2geom/curve.h
@@ -333,6 +333,9 @@ public:
* @return True if the curves are identical, false otherwise */
virtual bool operator==(Curve const &c) const = 0;
+ /** @brief Test whether two curves are approximately the same. */
+ virtual bool isNear(Curve const &c, Coord precision) const = 0;
+
/** @brief Feed the curve to a PathSink */
virtual void feed(PathSink &sink, bool moveto_initial) const;
/// @}
diff --git a/src/2geom/d2-sbasis.cpp b/src/2geom/d2-sbasis.cpp
index 4f00ff6a5..07eccce76 100644
--- a/src/2geom/d2-sbasis.cpp
+++ b/src/2geom/d2-sbasis.cpp
@@ -1,7 +1,41 @@
+/**
+ * \file
+ * \brief Some two-dimensional SBasis operations
+ *//*
+ * Authors:
+ * MenTaLguy <mental@rydia.net>
+ * Jean-François Barraud <jf.barraud@gmail.com>
+ * Johan Engelen <j.b.c.engelen@alumnus.utwente.nl>
+ *
+ * Copyright 2007-2012 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
#include <2geom/d2.h>
-/* One would think that we would include d2-sbasis.h, however,
- * you cannot actually include it in anything - only d2 may import it.
- * This is due to the trickinesses of template submatching. */
+#include <2geom/piecewise.h>
namespace Geom {
diff --git a/src/2geom/d2-sbasis.h b/src/2geom/d2-sbasis.h
deleted file mode 100644
index a0769b314..000000000
--- a/src/2geom/d2-sbasis.h
+++ /dev/null
@@ -1,165 +0,0 @@
-/**
- * \file
- * \brief Do not include this file
- *
- * We don't actually want anyone to
- * include this, other than D2.h.
- *//*
- * Authors:
- * ? <?@?.?>
- *
- * Copyright ?-? authors
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- *
- */
-
-#ifdef LIB2GEOM_SEEN_D2_H /*This is intentional: we don't actually want anyone to
- include this, other than D2.h. If somone else tries, D2
- won't be defined. If it is, this will already be included. */
-#ifndef LIB2GEOM_SEEN_D2_SBASIS_H
-#define LIB2GEOM_SEEN_D2_SBASIS_H
-
-#include <2geom/sbasis.h>
-#include <2geom/sbasis-2d.h>
-#include <2geom/piecewise.h>
-#include <2geom/affine.h>
-
-//TODO: implement intersect
-
-namespace Geom {
-
-inline D2<SBasis> compose(D2<SBasis> const & a, SBasis const & b) {
- return D2<SBasis>(compose(a[X], b), compose(a[Y], b));
-}
-
-SBasis L2(D2<SBasis> const & a, unsigned k);
-double L2(D2<double> const & a);
-
-D2<SBasis> multiply(Linear const & a, D2<SBasis> const & b);
-inline D2<SBasis> operator*(Linear const & a, D2<SBasis> const & b) { return multiply(a, b); }
-D2<SBasis> multiply(SBasis const & a, D2<SBasis> const & b);
-inline D2<SBasis> operator*(SBasis const & a, D2<SBasis> const & b) { return multiply(a, b); }
-D2<SBasis> truncate(D2<SBasis> const & a, unsigned terms);
-
-unsigned sbasis_size(D2<SBasis> const & a);
-double tail_error(D2<SBasis> const & a, unsigned tail);
-
-//Piecewise<D2<SBasis> > specific decls:
-
-Piecewise<D2<SBasis> > sectionize(D2<Piecewise<SBasis> > const &a);
-D2<Piecewise<SBasis> > make_cuts_independent(Piecewise<D2<SBasis> > const &a);
-Piecewise<D2<SBasis> > rot90(Piecewise<D2<SBasis> > const &a);
-Piecewise<SBasis> dot(Piecewise<D2<SBasis> > const &a, Piecewise<D2<SBasis> > const &b);
-Piecewise<SBasis> dot(Piecewise<D2<SBasis> > const &a, Point const &b);
-Piecewise<SBasis> cross(Piecewise<D2<SBasis> > const &a, Piecewise<D2<SBasis> > const &b);
-
-Piecewise<D2<SBasis> > operator*(Piecewise<D2<SBasis> > const &a, Affine const &m);
-
-Piecewise<D2<SBasis> > force_continuity(Piecewise<D2<SBasis> > const &f, double tol=0, bool closed=false);
-
-std::vector<Piecewise<D2<SBasis> > > fuse_nearby_ends(std::vector<Piecewise<D2<SBasis> > > const &f, double tol=0);
-
-std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > split_at_discontinuities (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwsbin, double tol = .0001);
-
-/**
-* note that -unitTangentAt(reverse(a),0.) == unitTangentAt(a,1.) but the former is more reliable (sometimes the sign is wrong for the latter)
-*/
-Point unitTangentAt(D2<SBasis> const & a, Coord t, unsigned n = 3);
-
-class CoordIterator
-: public std::iterator<std::input_iterator_tag, SBasis const>
-{
-public:
- CoordIterator(std::vector<D2<SBasis> >::const_iterator const &iter, unsigned d) : impl_(iter), ix_(d) {}
-
- inline bool operator==(CoordIterator const &other) { return other.impl_ == impl_; }
- inline bool operator!=(CoordIterator const &other) { return other.impl_ != impl_; }
-
- inline SBasis operator*() const {
- return (*impl_)[ix_];
- }
-
- inline CoordIterator &operator++() {
- ++impl_;
- return *this;
- }
- inline CoordIterator operator++(int) {
- CoordIterator old=*this;
- ++(*this);
- return old;
- }
-
-private:
- std::vector<D2<SBasis> >::const_iterator impl_;
- unsigned ix_;
-};
-
-inline CoordIterator iterateCoord(Piecewise<D2<SBasis> > const &a, unsigned d) {
- return CoordIterator(a.segs.begin(), d);
-}
-
-//bounds specializations with order
-inline OptRect bounds_fast(D2<SBasis> const & s, unsigned order=0) {
- OptRect retval;
- OptInterval xint = bounds_fast(s[X], order);
- if (xint) {
- OptInterval yint = bounds_fast(s[Y], order);
- if (yint) {
- retval = Rect(*xint, *yint);
- }
- }
- return retval;
-}
-inline OptRect bounds_local(D2<SBasis> const & s, OptInterval i, unsigned order=0) {
- OptRect retval;
- OptInterval xint = bounds_local(s[X], i, order);
- OptInterval yint = bounds_local(s[Y], i, order);
- if (xint && yint) {
- retval = Rect(*xint, *yint);
- }
- return retval;
-}
-
-std::vector<Interval> level_set( D2<SBasis> const &f, Rect region);
-std::vector<Interval> level_set( D2<SBasis> const &f, Point p, double tol);
-std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector<Rect> regions);
-std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector<Point> pts, double tol);
-
-}
-
-#endif // LIB2GEOM_SEEN_D2_SBASIS_H
-#endif // LIB2GEOM_SEEN_D2_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/d2.h b/src/2geom/d2.h
index dca6fa614..cd67ec65b 100644
--- a/src/2geom/d2.h
+++ b/src/2geom/d2.h
@@ -1,8 +1,12 @@
/**
* \file
- * \brief Lifts one dimensional objects into 2d
+ * \brief Lifts one dimensional objects into 2D
*//*
- * Copyright 2007 Michael Sloan <mgsloan@gmail.com>
+ * Authors:
+ * Michael Sloan <mgsloan@gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright 2007-2015 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -461,12 +465,6 @@ inline std::ostream &operator<< (std::ostream &out_file, const Geom::D2<T> &in_d
return out_file;
}
-} //end namespace Geom
-
-#include <2geom/d2-sbasis.h>
-
-namespace Geom {
-
//Some D2 Fragment implementation which requires rect:
template <typename T>
OptRect bounds_fast(const D2<T> &a) {
@@ -484,6 +482,73 @@ OptRect bounds_local(const D2<T> &a, const OptInterval &t) {
return OptRect(bounds_local(a[X], t), bounds_local(a[Y], t));
}
+
+
+// SBasis-specific declarations
+
+inline D2<SBasis> compose(D2<SBasis> const & a, SBasis const & b) {
+ return D2<SBasis>(compose(a[X], b), compose(a[Y], b));
+}
+
+SBasis L2(D2<SBasis> const & a, unsigned k);
+double L2(D2<double> const & a);
+
+D2<SBasis> multiply(Linear const & a, D2<SBasis> const & b);
+inline D2<SBasis> operator*(Linear const & a, D2<SBasis> const & b) { return multiply(a, b); }
+D2<SBasis> multiply(SBasis const & a, D2<SBasis> const & b);
+inline D2<SBasis> operator*(SBasis const & a, D2<SBasis> const & b) { return multiply(a, b); }
+D2<SBasis> truncate(D2<SBasis> const & a, unsigned terms);
+
+unsigned sbasis_size(D2<SBasis> const & a);
+double tail_error(D2<SBasis> const & a, unsigned tail);
+
+//Piecewise<D2<SBasis> > specific declarations
+
+Piecewise<D2<SBasis> > sectionize(D2<Piecewise<SBasis> > const &a);
+D2<Piecewise<SBasis> > make_cuts_independent(Piecewise<D2<SBasis> > const &a);
+Piecewise<D2<SBasis> > rot90(Piecewise<D2<SBasis> > const &a);
+Piecewise<SBasis> dot(Piecewise<D2<SBasis> > const &a, Piecewise<D2<SBasis> > const &b);
+Piecewise<SBasis> dot(Piecewise<D2<SBasis> > const &a, Point const &b);
+Piecewise<SBasis> cross(Piecewise<D2<SBasis> > const &a, Piecewise<D2<SBasis> > const &b);
+
+Piecewise<D2<SBasis> > operator*(Piecewise<D2<SBasis> > const &a, Affine const &m);
+
+Piecewise<D2<SBasis> > force_continuity(Piecewise<D2<SBasis> > const &f, double tol=0, bool closed=false);
+
+std::vector<Piecewise<D2<SBasis> > > fuse_nearby_ends(std::vector<Piecewise<D2<SBasis> > > const &f, double tol=0);
+
+std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > split_at_discontinuities (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwsbin, double tol = .0001);
+
+Point unitTangentAt(D2<SBasis> const & a, Coord t, unsigned n = 3);
+
+//bounds specializations with order
+inline OptRect bounds_fast(D2<SBasis> const & s, unsigned order=0) {
+ OptRect retval;
+ OptInterval xint = bounds_fast(s[X], order);
+ if (xint) {
+ OptInterval yint = bounds_fast(s[Y], order);
+ if (yint) {
+ retval = Rect(*xint, *yint);
+ }
+ }
+ return retval;
+}
+inline OptRect bounds_local(D2<SBasis> const & s, OptInterval i, unsigned order=0) {
+ OptRect retval;
+ OptInterval xint = bounds_local(s[X], i, order);
+ OptInterval yint = bounds_local(s[Y], i, order);
+ if (xint && yint) {
+ retval = Rect(*xint, *yint);
+ }
+ return retval;
+}
+
+std::vector<Interval> level_set( D2<SBasis> const &f, Rect region);
+std::vector<Interval> level_set( D2<SBasis> const &f, Point p, double tol);
+std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector<Rect> regions);
+std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector<Point> pts, double tol);
+
+
} // end namespace Geom
#endif
diff --git a/src/2geom/ellipse.cpp b/src/2geom/ellipse.cpp
index 4b5ad9762..afde428b1 100644
--- a/src/2geom/ellipse.cpp
+++ b/src/2geom/ellipse.cpp
@@ -142,6 +142,24 @@ LineSegment Ellipse::semiaxis(Dim2 d, int sign) const
return ls;
}
+Rect Ellipse::boundsExact() const
+{
+ Angle extremes[2][2];
+ double sinrot, cosrot;
+ sincos(_angle, sinrot, cosrot);
+
+ extremes[X][0] = std::atan2( -ray(Y) * sinrot, ray(X) * cosrot );
+ extremes[X][1] = extremes[X][0] + M_PI;
+ extremes[Y][0] = std::atan2( ray(Y) * cosrot, ray(X) * sinrot );
+ extremes[Y][1] = extremes[Y][0] + M_PI;
+
+ Rect result;
+ for (unsigned d = 0; d < 2; ++d) {
+ result[d] = Interval(valueAt(extremes[d][0], d ? Y : X),
+ valueAt(extremes[d][1], d ? Y : X));
+ }
+ return result;
+}
std::vector<double> Ellipse::coefficients() const
{
@@ -396,45 +414,39 @@ std::vector<ShapeIntersection> Ellipse::intersect(Line const &line) const
coefficients(A, B, C, D, E, F);
Affine iuct = inverseUnitCircleTransform();
- if (line.isHorizontal()) {
- // substitute y into the ellipse equation and solve
- Coord y = line.initialPoint()[Y];
- std::vector<Coord> xs = solve_quadratic(A, B*y + D, C*y*y + E*y + F);
- for (unsigned i = 0; i < xs.size(); ++i) {
- Point p(xs[i], y);
- result.push_back(ShapeIntersection(atan2(p * iuct), line.timeAt(p), p));
- }
- return result;
- }
- if (line.isVertical()) {
- // substitute y into the ellipse equation and solve
- Coord x = line.initialPoint()[X];
- std::vector<Coord> ys = solve_quadratic(C, B*x + E, A*x*x + D*x + F);
- for (unsigned i = 0; i < ys.size(); ++i) {
- Point p(x, ys[i]);
- result.push_back(ShapeIntersection(atan2(p * iuct), line.timeAt(p), p));
- }
- return result;
- }
-
// generic case
Coord a, b, c;
line.coefficients(a, b, c);
+ Point lv = line.versor();
- // y = -a/b x - C/B
- // TODO: when is it better to substitute X?
- Coord q = -a/b;
- Coord r = -c/b;
+ if (fabs(lv[X]) > fabs(lv[Y])) {
+ // y = -a/b x - c/b
+ Coord q = -a/b;
+ Coord r = -c/b;
- // substitute that into the ellipse equation, making it quadratic in x
- Coord I = A + B*q + C*q*q; // x^2 terms
- Coord J = B*r + C*2*q*r + D + E*q; // x^1 terms
- Coord K = C*r*r + E*r + F; // x^0 terms
- std::vector<Coord> xs = solve_quadratic(I, J, K);
+ // substitute that into the ellipse equation, making it quadratic in x
+ Coord I = A + B*q + C*q*q; // x^2 terms
+ Coord J = B*r + C*2*q*r + D + E*q; // x^1 terms
+ Coord K = C*r*r + E*r + F; // x^0 terms
+ std::vector<Coord> xs = solve_quadratic(I, J, K);
- for (unsigned i = 0; i < xs.size(); ++i) {
- Point p(xs[i], q*xs[i] + r);
- result.push_back(ShapeIntersection(atan2(p * iuct), line.timeAt(p), p));
+ for (unsigned i = 0; i < xs.size(); ++i) {
+ Point p(xs[i], q*xs[i] + r);
+ result.push_back(ShapeIntersection(atan2(p * iuct), line.timeAt(p), p));
+ }
+ } else {
+ Coord q = -b/a;
+ Coord r = -c/a;
+
+ Coord I = A*q*q + B*q + C;
+ Coord J = A*2*q*r + B*r + D*q + E;
+ Coord K = A*r*r + D*r + F;
+ std::vector<Coord> xs = solve_quadratic(I, J, K);
+
+ for (unsigned i = 0; i < xs.size(); ++i) {
+ Point p(q*xs[i] + r, xs[i]);
+ result.push_back(ShapeIntersection(atan2(p * iuct), line.timeAt(p), p));
+ }
}
return result;
}
diff --git a/src/2geom/ellipse.h b/src/2geom/ellipse.h
index 4b1701cce..f6089c944 100644
--- a/src/2geom/ellipse.h
+++ b/src/2geom/ellipse.h
@@ -171,6 +171,9 @@ public:
LineSegment axis(Dim2 d) const;
LineSegment semiaxis(Dim2 d, int sign = 1) const;
+ /// Get the tight-fitting bounding box of the ellipse.
+ Rect boundsExact() const;
+
/// Get the coefficients of the ellipse's implicit equation.
std::vector<double> coefficients() const;
void coefficients(Coord &A, Coord &B, Coord &C, Coord &D, Coord &E, Coord &F) const;
diff --git a/src/2geom/elliptical-arc.cpp b/src/2geom/elliptical-arc.cpp
index 77d3d788d..a0e379a21 100644
--- a/src/2geom/elliptical-arc.cpp
+++ b/src/2geom/elliptical-arc.cpp
@@ -92,47 +92,58 @@ namespace Geom
* @ingroup Curves
*/
+
+/** @brief Compute bounds of an elliptical arc.
+ * The bounds computation works as follows. The extreme X and Y points
+ * are either the endpoints or local minima / maxima of the ellipse.
+ * We already have endpoints, and we can find the local extremes
+ * by computing a partial derivative with respect to the angle
+ * and equating that to zero:
+ * \f{align*}{
+ x &= r_x \cos \varphi \cos \theta - r_y \sin \varphi \sin \theta + c_x \\
+ \frac{\partial x}{\partial \theta} &= -r_x \cos \varphi \sin \theta - r_y \sin \varphi \cos \theta = 0 \\
+ \frac{\sin \theta}{\cos \theta} &= \tan\theta = -\frac{r_y \sin \varphi}{r_x \cos \varphi} \\
+ \theta &= \tan^{-1} \frac{-r_y \sin \varphi}{r_x \cos \varphi}
+ \f}
+ * The local extremes correspond to two angles separated by \f$\pi\f$.
+ * Once we compute these angles, we check whether they belong to the arc,
+ * and if they do, we evaluate the ellipse at these angles.
+ * The bounding box of the arc is equal to the bounding box of the endpoints
+ * and the local extrema that belong to the arc.
+ */
Rect EllipticalArc::boundsExact() const
{
if (isChord()) {
return chord().boundsExact();
}
- using std::swap;
+ Coord coord[2][4] = {
+ { _initial_point[X], _final_point[X], 0, 0 },
+ { _initial_point[Y], _final_point[Y], 0, 0 }
+ };
+ int ncoord[2] = { 2, 2 };
- // TODO: simplify / document what is going on here.
- double extremes[4];
+ Angle extremes[2][2];
double sinrot, cosrot;
sincos(rotationAngle(), 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] )
- swap(arc_extremes[0], arc_extremes[1]);
- arc_extremes[2] = initialPoint()[Y];
- arc_extremes[3] = finalPoint()[Y];
- if ( arc_extremes[2] < arc_extremes[3] )
- swap(arc_extremes[2], arc_extremes[3]);
-
- if ( !are_near(initialPoint(), finalPoint()) ) {
- for (unsigned i = 0; i < 4; ++i) {
- if (containsAngle(extremes[i])) {
- arc_extremes[i] = valueAtAngle(extremes[i], (i >> 1) ? Y : X);
+ extremes[X][0] = std::atan2( -ray(Y) * sinrot, ray(X) * cosrot );
+ extremes[X][1] = extremes[X][0] + M_PI;
+ extremes[Y][0] = std::atan2( ray(Y) * cosrot, ray(X) * sinrot );
+ extremes[Y][1] = extremes[Y][0] + M_PI;
+
+ for (unsigned d = 0; d < 2; ++d) {
+ for (unsigned i = 0; i < 2; ++i) {
+ if (containsAngle(extremes[d][i])) {
+ coord[d][ncoord[d]++] = valueAtAngle(extremes[d][i], d ? Y : X);
}
}
}
- return Rect( Point(arc_extremes[1], arc_extremes[3]) ,
- Point(arc_extremes[0], arc_extremes[2]) );
+ Interval xival = Interval::from_range(coord[X], coord[X] + ncoord[X]);
+ Interval yival = Interval::from_range(coord[Y], coord[Y] + ncoord[Y]);
+ Rect result(xival, yival);
+ return result;
}
@@ -149,116 +160,14 @@ Coord EllipticalArc::valueAtAngle(Coord t, Dim2 d) const
std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const
{
- if (isChord()) {
- return chord().roots(v, d);
- }
-
std::vector<Coord> sol;
- Interval unit_interval(0, 1);
- if ( are_near(ray(X), 0) && are_near(ray(Y), 0) ) {
- if ( center(d) == v )
- sol.push_back(0);
+ if (isChord()) {
+ sol = chord().roots(v, d);
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 (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 = 0.0;
- switch(d)
- {
- case X:
- switch(dim)
- {
- case X: ray_prj = -ray(Y) * std::sin(rotationAngle());
- break;
- case Y: ray_prj = ray(X) * std::cos(rotationAngle());
- break;
- }
- break;
- case Y:
- switch(dim)
- {
- case X: ray_prj = ray(Y) * std::cos(rotationAngle());
- break;
- case Y: ray_prj = ray(X) * std::sin(rotationAngle());
- 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 = timeAtAngle(s);
- //std::cerr << " -> t: " << s << std::endl;
- if (unit_interval.contains(s)) {
- sol.push_back(s);
- }
- return sol;
- }
- }
+ Interval unit_interval(0, 1);
double rotx, roty;
if (d == X) {
@@ -278,10 +187,10 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const
//std::cerr << "b = " << b << std::endl;
//std::cerr << "c = " << c << std::endl;
- if ( are_near(a,0) )
+ if (a == 0)
{
sol.push_back(M_PI);
- if ( !are_near(b,0) )
+ if (b != 0)
{
double s = 2 * std::atan(-c/(2*b));
if ( s < 0 ) s += 2*M_PI;
@@ -292,8 +201,7 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const
{
double delta = b * b - a * c;
//std::cerr << "delta = " << delta << std::endl;
- if ( are_near(delta, 0) )
- {
+ if (delta == 0) {
double s = 2 * std::atan(-b/a);
if ( s < 0 ) s += 2*M_PI;
sol.push_back(s);
@@ -312,7 +220,7 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const
std::vector<double> arc_sol;
for (unsigned int i = 0; i < sol.size(); ++i ) {
- //std::cerr << "s = " << rad_to_deg(sol[i]);
+ //std::cerr << "s = " << deg_from_rad(sol[i]);
sol[i] = timeAtAngle(sol[i]);
//std::cerr << " -> t: " << sol[i] << std::endl;
if (unit_interval.contains(sol[i])) {
@@ -354,11 +262,7 @@ EllipticalArc::pointAndDerivatives(Coord t, unsigned int n) const
std::vector<Point> result;
result.reserve(nn);
double angle = angleAt(t);
-#if __cplusplus <= 199711L
std::auto_ptr<EllipticalArc> ea( static_cast<EllipticalArc*>(duplicate()) );
-#else
- std::unique_ptr<EllipticalArc> ea( static_cast<EllipticalArc*>(duplicate()) );
-#endif
ea->_ellipse.setCenter(0, 0);
unsigned int m = std::min(nn, 4u);
for ( unsigned int i = 0; i < m; ++i )
@@ -890,6 +794,25 @@ bool EllipticalArc::operator==(Curve const &c) const
return true;
}
+bool EllipticalArc::isNear(Curve const &c, Coord precision) const
+{
+ EllipticalArc const *other = dynamic_cast<EllipticalArc const *>(&c);
+ if (!other) {
+ if (isChord()) {
+ return c.isNear(chord(), precision);
+ }
+ return false;
+ }
+
+ if (!are_near(_initial_point, other->_initial_point, precision)) return false;
+ if (!are_near(_final_point, other->_final_point, precision)) return false;
+ if (isChord() && other->isChord()) return true;
+
+ if (sweep() != other->sweep()) return false;
+ if (!are_near(_ellipse, other->_ellipse, precision)) return false;
+ return true;
+}
+
void EllipticalArc::feed(PathSink &sink, bool moveto_initial) const
{
if (moveto_initial) {
@@ -898,6 +821,104 @@ void EllipticalArc::feed(PathSink &sink, bool moveto_initial) const
sink.arcTo(ray(X), ray(Y), rotationAngle(), _large_arc, sweep(), _final_point);
}
+int EllipticalArc::winding(Point const &p) const
+{
+ using std::swap;
+
+ double sinrot, cosrot;
+ sincos(rotationAngle(), sinrot, cosrot);
+
+ Angle ymin_a = std::atan2( ray(Y) * cosrot, ray(X) * sinrot );
+ Angle ymax_a = ymin_a + M_PI;
+
+ Point ymin = pointAtAngle(ymin_a);
+ Point ymax = pointAtAngle(ymax_a);
+ if (ymin[Y] > ymax[Y]) {
+ swap(ymin, ymax);
+ swap(ymin_a, ymax_a);
+ }
+
+ Interval yspan(ymin[Y], ymax[Y]);
+ if (!yspan.lowerContains(p[Y])) return 0;
+
+ bool left = cross(ymax - ymin, p - ymin) > 0;
+ bool inside = _ellipse.contains(p);
+ bool includes_ymin = _angles.contains(ymin_a);
+ bool includes_ymax = _angles.contains(ymax_a);
+
+ AngleInterval rarc(ymin_a, ymax_a, true),
+ larc(ymax_a, ymin_a, true);
+
+ // we'll compute the result for an arc in the direction of increasing angles
+ // and then negate if necessary
+ Angle ia = initialAngle(), fa = finalAngle();
+ Point ip = _initial_point, fp = _final_point;
+ if (!sweep()) {
+ swap(ia, fa);
+ swap(ip, fp);
+ }
+
+ bool initial_left = larc.contains(ia);
+ bool initial_right = !initial_left; //rarc.contains(ia);
+ bool final_right = rarc.contains(fa);
+ bool final_left = !final_right;//larc.contains(fa);
+
+ int result = 0;
+ if (inside || left) {
+ if (includes_ymin && final_right) {
+ Interval ival(ymin[Y], fp[Y]);
+ if (ival.lowerContains(p[Y])) {
+ ++result;
+ }
+ }
+ if (initial_right && final_right && !largeArc()) {
+ Interval ival(ip[Y], fp[Y]);
+ if (ival.lowerContains(p[Y])) {
+ ++result;
+ }
+ }
+ if (initial_right && includes_ymax) {
+ Interval ival(ip[Y], ymax[Y]);
+ if (ival.lowerContains(p[Y])) {
+ ++result;
+ }
+ }
+ if (!initial_right && !final_right && includes_ymin && includes_ymax) {
+ Interval ival(ymax[Y], ymin[Y]);
+ if (ival.lowerContains(p[Y])) {
+ ++result;
+ }
+ }
+ }
+ if (left && !inside) {
+ if (includes_ymin && initial_left) {
+ Interval ival(ymin[Y], ip[Y]);
+ if (ival.lowerContains(p[Y])) {
+ --result;
+ }
+ }
+ if (initial_left && final_left && !largeArc()) {
+ Interval ival(ip[Y], fp[Y]);
+ if (ival.lowerContains(p[Y])) {
+ --result;
+ }
+ }
+ if (final_left && includes_ymax) {
+ Interval ival(fp[Y], ymax[Y]);
+ if (ival.lowerContains(p[Y])) {
+ --result;
+ }
+ }
+ if (!initial_left && !final_left && includes_ymin && includes_ymax) {
+ Interval ival(ymax[Y], ymin[Y]);
+ if (ival.lowerContains(p[Y])) {
+ --result;
+ }
+ }
+ }
+ return sweep() ? result : -result;
+}
+
std::ostream &operator<<(std::ostream &out, EllipticalArc const &ea)
{
out << "EllipticalArc("
diff --git a/src/2geom/elliptical-arc.h b/src/2geom/elliptical-arc.h
index fbb290dca..96a92eb88 100644
--- a/src/2geom/elliptical-arc.h
+++ b/src/2geom/elliptical-arc.h
@@ -215,7 +215,7 @@ public:
/// Get the angular interval of the arc.
AngleInterval angularInterval() const { return _angles; }
- /// Evaluate the arc in the curve domain, i.e. \f$[0, 1]\$.
+ /// Evaluate the arc in the curve domain, i.e. \f$[0, 1]\f$.
virtual Point pointAt(Coord t) const;
/// Evaluate a single coordinate on the arc in the curve domain.
@@ -300,7 +300,9 @@ public:
virtual Curve *portion(double f, double t) const;
virtual Curve *reverse() const;
virtual bool operator==(Curve const &c) const;
+ virtual bool isNear(Curve const &other, Coord precision) const;
virtual void feed(PathSink &sink, bool moveto_initial) const;
+ virtual int winding(Point const &p) const;
private:
void _updateCenterAndAngles();
diff --git a/src/2geom/generic-interval.h b/src/2geom/generic-interval.h
index 716390e57..ce7945ab6 100644
--- a/src/2geom/generic-interval.h
+++ b/src/2geom/generic-interval.h
@@ -291,8 +291,6 @@ public:
/// @}
/** @brief Check whether this interval is empty. */
- bool isEmpty() { return !*this; };
- /// Alias of isEmpty() for STL similarity.
bool empty() { return !*this; }
/** @brief Union with another interval, gracefully handling empty ones. */
diff --git a/src/2geom/generic-rect.h b/src/2geom/generic-rect.h
index f7d722757..afd2cb8a7 100644
--- a/src/2geom/generic-rect.h
+++ b/src/2geom/generic-rect.h
@@ -393,7 +393,7 @@ public:
/// @name Check other rectangles and points for inclusion.
/// @{
/** @brief Check for emptiness. */
- inline bool isEmpty() const { return !*this; };
+ inline bool empty() const { return !*this; };
/** @brief Check whether the rectangles have any common points.
* Empty rectangles will not intersect with any other rectangle. */
bool intersects(CRect const &r) const { return r.intersects(*this); }
diff --git a/src/2geom/int-point.h b/src/2geom/int-point.h
index 7c737b1d7..50d3a6720 100644
--- a/src/2geom/int-point.h
+++ b/src/2geom/int-point.h
@@ -60,15 +60,6 @@ public:
_pt[X] = x;
_pt[Y] = y;
}
- IntPoint(IntPoint const &p) {
- _pt[X] = p._pt[X];
- _pt[Y] = p._pt[Y];
- }
- IntPoint &operator=(IntPoint const &p) {
- _pt[X] = p._pt[X];
- _pt[Y] = p._pt[Y];
- return *this;
- }
/// @}
/// @name Access the coordinates of a point
@@ -92,6 +83,10 @@ public:
/// @name Vector-like arithmetic operations
/// @{
+ IntPoint operator-() const {
+ IntPoint ret(-_pt[X], -_pt[Y]);
+ return ret;
+ }
IntPoint &operator+=(IntPoint const &o) {
_pt[X] += o._pt[X];
_pt[Y] += o._pt[Y];
diff --git a/src/2geom/intersection-graph.cpp b/src/2geom/intersection-graph.cpp
index e18561f67..d469d3ffc 100644
--- a/src/2geom/intersection-graph.cpp
+++ b/src/2geom/intersection-graph.cpp
@@ -34,6 +34,7 @@
#include <2geom/intersection-graph.h>
#include <2geom/path.h>
#include <2geom/pathvector.h>
+#include <2geom/utils.h>
#include <iostream>
#include <iterator>
@@ -56,101 +57,174 @@ struct PathIntersectionGraph::IntersectionVertexLess {
*/
PathIntersectionGraph::PathIntersectionGraph(PathVector const &a, PathVector const &b, Coord precision)
- : _a(a)
- , _b(b)
+ : _graph_valid(true)
{
if (a.empty() || b.empty()) return;
+ _pv[0] = a;
+ _pv[1] = b;
+
+ _prepareArguments();
+ bool has_intersections = _prepareIntersectionLists(precision);
+ if (!has_intersections) return;
+
+ _assignEdgeWindingParities(precision);
+ _assignComponentStatusFromDegenerateIntersections();
+ _removeDegenerateIntersections();
+ if (_graph_valid) {
+ _verify();
+ }
+}
+
+void PathIntersectionGraph::_prepareArguments()
+{
// all paths must be closed, otherwise we will miss some intersections
- for (std::size_t i = 0; i < a.size(); ++i) {
- _a[i].close();
+ for (int w = 0; w < 2; ++w) {
+ for (std::size_t i = 0; i < _pv[w].size(); ++i) {
+ _pv[w][i].close();
+ }
}
- for (std::size_t i = 0; i < b.size(); ++i) {
- _b[i].close();
+ // remove degenerate segments
+ for (int w = 0; w < 2; ++w) {
+ for (std::size_t i = _pv[w].size(); i > 0; --i) {
+ if (_pv[w][i-1].empty()) {
+ _pv[w].erase(_pv[w].begin() + (i-1));
+ }
+ for (std::size_t j = _pv[w][i-1].size(); j > 0; --j) {
+ if (_pv[w][i-1][j-1].isDegenerate()) {
+ _pv[w][i-1].erase(_pv[w][i-1].begin() + (j-1));
+ }
+ }
+ }
}
+}
- std::vector<PVIntersection> pxs = _a.intersect(_b, precision);
+bool PathIntersectionGraph::_prepareIntersectionLists(Coord precision)
+{
+ std::vector<PVIntersection> pxs = _pv[0].intersect(_pv[1], precision);
// NOTE: this early return means that the path data structures will not be created
// if there are no intersections at all!
- if (pxs.empty()) return;
+ if (pxs.empty()) return false;
// prepare intersection lists for each path component
- for (std::size_t i = 0; i < _a.size(); ++i) {
- _apaths.push_back(new PathData());
- }
- for (std::size_t i = 0; i < _b.size(); ++i) {
- _bpaths.push_back(new PathData());
+ for (unsigned w = 0; w < 2; ++w) {
+ for (std::size_t i = 0; i < _pv[w].size(); ++i) {
+ _components[w].push_back(new PathData(w, i));
+ }
}
+ // create intersection vertices
for (std::size_t i = 0; i < pxs.size(); ++i) {
IntersectionVertex *xa, *xb;
xa = new IntersectionVertex();
xb = new IntersectionVertex();
- xa->processed = xb->processed = false;
+ //xa->processed = xb->processed = false;
+ xa->which = 0; xb->which = 1;
xa->pos = pxs[i].first;
xb->pos = pxs[i].second;
xa->p = xb->p = pxs[i].point();
xa->neighbor = xb;
xb->neighbor = xa;
+ xa->next_edge = xb->next_edge = OUTSIDE;
+ xa->defective = xb->defective = false;
_xs.push_back(xa);
_xs.push_back(xb);
- _apaths[xa->pos.path_index].xlist.push_back(*xa);
- _bpaths[xb->pos.path_index].xlist.push_back(*xb);
+ _components[0][xa->pos.path_index].xlist.push_back(*xa);
+ _components[1][xb->pos.path_index].xlist.push_back(*xb);
}
- for (std::size_t i = 0; i < _apaths.size(); ++i) {
- _apaths[i].xlist.sort(IntersectionVertexLess());
- }
- for (std::size_t i = 0; i < _bpaths.size(); ++i) {
- _bpaths[i].xlist.sort(IntersectionVertexLess());
+ // sort components according to time value of intersections
+ for (unsigned w = 0; w < 2; ++w) {
+ for (std::size_t i = 0; i < _components[w].size(); ++i) {
+ _components[w][i].xlist.sort(IntersectionVertexLess());
+ }
}
- typedef IntersectionList::iterator Iter;
-
- // determine in/out/on flags using winding
- for (unsigned npv = 0; npv < 2; ++npv) {
- boost::ptr_vector<PathData> &ls = npv ? _bpaths : _apaths;
- boost::ptr_vector<PathData> &ols = npv ? _apaths : _bpaths;
- PathVector const &pv = npv ? b : a;
- PathVector const &other = npv ? a : b;
-
- for (unsigned li = 0; li < ls.size(); ++li) {
- IntersectionList &xl = ls[li].xlist;
- for (Iter i = xl.begin(); i != xl.end(); ++i) {
- Iter n = boost::next(i);
- if (n == xl.end()) {
- n = xl.begin();
- }
+ return true;
+}
+
+void PathIntersectionGraph::_assignEdgeWindingParities(Coord precision)
+{
+ // determine the winding numbers of path portions between intersections
+ for (unsigned w = 0; w < 2; ++w) {
+ unsigned ow = (w+1) % 2;
+
+ for (unsigned li = 0; li < _components[w].size(); ++li) {
+ IntersectionList &xl = _components[w][li].xlist;
+ for (ILIter i = xl.begin(); i != xl.end(); ++i) {
+ ILIter n = cyclic_next(i, xl);
std::size_t pi = i->pos.path_index;
- PathInterval ival = forward_interval(i->pos, n->pos, pv[pi].size());
+ PathInterval ival = forward_interval(i->pos, n->pos, _pv[w][pi].size());
PathTime mid = ival.inside(precision);
- // TODO check for degenerate cases
- int w = other.winding(pv[pi].pointAt(mid));
- if (w % 2) {
- i->next = POINT_INSIDE;
- n->previous = POINT_INSIDE;
+ Point wpoint = _pv[w][pi].pointAt(mid);
+ _winding_points.push_back(wpoint);
+ int wdg = _pv[ow].winding(wpoint);
+ if (wdg % 2) {
+ i->next_edge = INSIDE;
} else {
- i->next = POINT_OUTSIDE;
- n->previous = POINT_OUTSIDE;
+ i->next_edge = OUTSIDE;
}
}
+ }
+ }
+}
+
+void PathIntersectionGraph::_assignComponentStatusFromDegenerateIntersections()
+{
+ // If a path has only degenerate intersections, assign its status now.
+ // This protects against later accidentaly picking a point for winding
+ // determination that is exactly at a removed intersection.
+ for (unsigned w = 0; w < 2; ++w) {
+ for (unsigned li = 0; li < _components[w].size(); ++li) {
+ IntersectionList &xl = _components[w][li].xlist;
+ bool has_in = false;
+ bool has_out = false;
+ for (ILIter i = xl.begin(); i != xl.end(); ++i) {
+ has_in |= (i->next_edge == INSIDE);
+ has_out |= (i->next_edge == OUTSIDE);
+ }
+ if (has_in && !has_out) {
+ _components[w][li].status = INSIDE;
+ }
+ if (!has_in && has_out) {
+ _components[w][li].status = OUTSIDE;
+ }
+ }
+ }
+}
- // remove intersections that don't change between in/out
- // and assign exit / entry flags
- for (Iter i = xl.begin(); i != xl.end();) {
- if (i->previous == i->next) {
- IntersectionList &oxl = ols[i->neighbor->pos.path_index].xlist;
- oxl.erase(oxl.iterator_to(*i->neighbor));
- xl.erase(i++);
- if (i->next == POINT_INSIDE) {
- ++ls[li].removed_in;
- } else {
- ++ls[li].removed_out;
+void PathIntersectionGraph::_removeDegenerateIntersections()
+{
+ // remove intersections that don't change between in/out
+ for (unsigned w = 0; w < 2; ++w) {
+ for (unsigned li = 0; li < _components[w].size(); ++li) {
+ IntersectionList &xl = _components[w][li].xlist;
+ for (ILIter i = xl.begin(); i != xl.end();) {
+ ILIter n = cyclic_next(i, xl);
+ if (i->next_edge == n->next_edge) {
+ bool last_node = (i == n);
+ ILIter nn = _getNeighbor(n);
+ IntersectionList &oxl = _getPathData(nn).xlist;
+
+ // When exactly 3 out of 4 edges adjacent to an intersection
+ // have the same winding, we have a defective intersection,
+ // which is neither degenerate nor normal. Those can occur in paths
+ // that contain overlapping segments. We cannot handle that case
+ // for now, so throw an exception.
+ if (cyclic_prior(nn, oxl)->next_edge != nn->next_edge) {
+ _graph_valid = false;
+ n->defective = true;
+ nn->defective = true;
+ ++i;
+ continue;
}
+
+ oxl.erase(nn);
+ xl.erase(n);
+ if (last_node) break;
} else {
- i->entry = ((i->next == POINT_INSIDE) && (i->previous == POINT_OUTSIDE));
++i;
}
}
@@ -158,6 +232,20 @@ PathIntersectionGraph::PathIntersectionGraph(PathVector const &a, PathVector con
}
}
+void PathIntersectionGraph::_verify()
+{
+ for (unsigned w = 0; w < 2; ++w) {
+ for (unsigned li = 0; li < _components[w].size(); ++li) {
+ IntersectionList &xl = _components[w][li].xlist;
+ assert(xl.size() % 2 == 0);
+ for (ILIter i = xl.begin(); i != xl.end(); ++i) {
+ ILIter j = cyclic_next(i, xl);
+ assert(i->next_edge != j->next_edge);
+ }
+ }
+ }
+}
+
PathVector PathIntersectionGraph::getUnion()
{
PathVector result = _getResult(false, false);
@@ -192,8 +280,9 @@ PathVector PathIntersectionGraph::getBminusA()
PathVector PathIntersectionGraph::getXOR()
{
- PathVector r1 = getAminusB();
- PathVector r2 = getBminusA();
+ PathVector r1, r2;
+ r1 = getAminusB();
+ r2 = getBminusA();
std::copy(r2.begin(), r2.end(), std::back_inserter(r1));
return r1;
}
@@ -201,37 +290,61 @@ PathVector PathIntersectionGraph::getXOR()
std::size_t PathIntersectionGraph::size() const
{
std::size_t result = 0;
- for (std::size_t i = 0; i < _apaths.size(); ++i) {
- result += _apaths[i].xlist.size();
+ for (std::size_t i = 0; i < _components[0].size(); ++i) {
+ result += _components[0][i].xlist.size();
}
return result;
}
-std::vector<Point> PathIntersectionGraph::intersectionPoints() const
+std::vector<Point> PathIntersectionGraph::intersectionPoints(bool defective) const
{
std::vector<Point> result;
- typedef IntersectionList::const_iterator Iter;
- for (std::size_t i = 0; i < _apaths.size(); ++i) {
- for (Iter j = _apaths[i].xlist.begin(); j != _apaths[i].xlist.end(); ++j) {
- result.push_back(j->p);
+ typedef IntersectionList::const_iterator CILIter;
+ for (std::size_t i = 0; i < _components[0].size(); ++i) {
+ for (CILIter j = _components[0][i].xlist.begin(); j != _components[0][i].xlist.end(); ++j) {
+ if (j->defective == defective) {
+ result.push_back(j->p);
+ }
}
}
return result;
}
+void PathIntersectionGraph::fragments(PathVector &in, PathVector &out) const
+{
+ typedef boost::ptr_vector<PathData>::const_iterator PIter;
+ for (unsigned w = 0; w < 2; ++w) {
+ for (PIter li = _components[w].begin(); li != _components[w].end(); ++li) {
+ for (CILIter k = li->xlist.begin(); k != li->xlist.end(); ++k) {
+ CILIter n = cyclic_next(k, li->xlist);
+ // TODO: investigate why non-contiguous paths are sometimes generated here
+ Path frag(k->p);
+ frag.setStitching(true);
+ PathInterval ival = forward_interval(k->pos, n->pos, _pv[w][k->pos.path_index].size());
+ _pv[w][k->pos.path_index].appendPortionTo(frag, ival, k->p, n->p);
+ if (k->next_edge == INSIDE) {
+ in.push_back(frag);
+ } else {
+ out.push_back(frag);
+ }
+ }
+ }
+ }
+}
+
PathVector PathIntersectionGraph::_getResult(bool enter_a, bool enter_b)
{
- typedef IntersectionList::iterator Iter;
+ typedef boost::ptr_vector<PathData>::iterator PIter;
PathVector result;
if (_xs.empty()) return result;
// reset processed status
- for (unsigned npv = 0; npv < 2; ++npv) {
- boost::ptr_vector<PathData> &ls = npv ? _bpaths : _apaths;
- for (std::size_t li = 0; li < ls.size(); ++li) {
- for (Iter k = ls[li].xlist.begin(); k != ls[li].xlist.end(); ++k) {
- k->processed = false;
+ _ulist.clear();
+ for (unsigned w = 0; w < 2; ++w) {
+ for (PIter li = _components[w].begin(); li != _components[w].end(); ++li) {
+ for (ILIter k = li->xlist.begin(); k != li->xlist.end(); ++k) {
+ _ulist.push_back(*k);
}
}
}
@@ -239,18 +352,17 @@ PathVector PathIntersectionGraph::_getResult(bool enter_a, bool enter_b)
unsigned n_processed = 0;
while (true) {
- PathVector const *cur = &_a, *other = &_b;
- boost::ptr_vector<PathData> *lscur = &_apaths, *lsother = &_bpaths;
-
- // find unprocessed intersection
- Iter i;
- if (!_findUnprocessed(i)) break;
+ // get unprocessed intersection
+ if (_ulist.empty()) break;
+ IntersectionVertex &iv = _ulist.front();
+ unsigned w = iv.which;
+ ILIter i = _components[w][iv.pos.path_index].xlist.iterator_to(iv);
result.push_back(Path(i->p));
result.back().setStitching(true);
- while (!i->processed) {
- Iter prev = i;
+ while (i->_proc_hook.is_linked()) {
+ ILIter prev = i;
std::size_t pi = i->pos.path_index;
// determine which direction to go
// union: always go outside
@@ -258,53 +370,55 @@ PathVector PathIntersectionGraph::_getResult(bool enter_a, bool enter_b)
// a minus b: go inside in b, outside in a
// b minus a: go inside in a, outside in b
bool reverse = false;
- if (cur == &_a) {
- reverse = i->entry ^ enter_a;
+ if (w == 0) {
+ reverse = (i->next_edge == INSIDE) ^ enter_a;
} else {
- reverse = i->entry ^ enter_b;
+ reverse = (i->next_edge == INSIDE) ^ enter_b;
}
// get next intersection
if (reverse) {
- if (i == (*lscur)[pi].xlist.begin()) {
- i = (*lscur)[pi].xlist.end();
- }
- --i;
+ i = cyclic_prior(i, _components[w][pi].xlist);
} else {
- ++i;
- if (i == (*lscur)[pi].xlist.end()) {
- i = (*lscur)[pi].xlist.begin();
- }
+ i = cyclic_next(i, _components[w][pi].xlist);
}
// append portion of path
PathInterval ival = PathInterval::from_direction(
prev->pos.asPathTime(), i->pos.asPathTime(),
- reverse, (*cur)[pi].size());
+ reverse, _pv[i->which][pi].size());
- (*cur)[pi].appendPortionTo(result.back(), ival, prev->p, i->p);
+ _pv[i->which][pi].appendPortionTo(result.back(), ival, prev->p, i->p);
// mark both vertices as processed
- prev->processed = true;
- i->processed = true;
+ //prev->processed = true;
+ //i->processed = true;
n_processed += 2;
+ if (prev->_proc_hook.is_linked()) {
+ _ulist.erase(_ulist.iterator_to(*prev));
+ }
+ if (i->_proc_hook.is_linked()) {
+ _ulist.erase(_ulist.iterator_to(*i));
+ }
// switch to the other path
- i = (*lsother)[i->neighbor->pos.path_index].xlist.iterator_to(*i->neighbor);
- std::swap(lscur, lsother);
- std::swap(cur, other);
+ i = _getNeighbor(i);
+ w = i->which;
}
result.back().close(true);
assert(!result.back().empty());
}
+ /*if (n_processed != size() * 2) {
+ std::cerr << "Processed " << n_processed << " intersections, expected " << (size() * 2) << std::endl;
+ }*/
assert(n_processed == size() * 2);
return result;
}
-void PathIntersectionGraph::_handleNonintersectingPaths(PathVector &result, int which, bool inside)
+void PathIntersectionGraph::_handleNonintersectingPaths(PathVector &result, unsigned which, bool inside)
{
/* Every component that has any intersections will be processed by _getResult.
* Here we take care of paths that don't have any intersections. They are either
@@ -312,68 +426,53 @@ void PathIntersectionGraph::_handleNonintersectingPaths(PathVector &result, int
* evaluating the winding rule at the initial point. If inside is true and
* the path is inside, we add it to the result.
*/
- boost::ptr_vector<PathData> const &ls = which ? _bpaths : _apaths;
- PathVector const &cur = which ? _b : _a;
- PathVector const &other = which ? _a : _b;
+ unsigned w = which;
+ unsigned ow = (w+1) % 2;
- for (std::size_t i = 0; i < cur.size(); ++i) {
+ for (std::size_t i = 0; i < _pv[w].size(); ++i) {
// the path data vector might have been left empty if there were no intersections at all
- bool has_path_data = !ls.empty();
+ bool has_path_data = !_components[w].empty();
// Skip if the path has intersections
- if (has_path_data && !ls[i].xlist.empty()) continue;
+ if (has_path_data && !_components[w][i].xlist.empty()) continue;
bool path_inside = false;
- // If the path had any intersections removed, use the result of that,
- // since one of those might have been at the initial point.
- // Also, it saves time.
- if (has_path_data && ls[i].removed_in != 0) {
+ // Use the in/out determination from constructor, if available
+ if (has_path_data && _components[w][i].status == INSIDE) {
path_inside = true;
- } else if (has_path_data && ls[i].removed_out != 0) {
+ } else if (has_path_data && _components[w][i].status == OUTSIDE) {
path_inside = false;
} else {
- int w = other.winding(cur[i].initialPoint());
- path_inside = w % 2 != 0;
+ int wdg = _pv[ow].winding(_pv[w][i].initialPoint());
+ path_inside = wdg % 2 != 0;
}
if (path_inside == inside) {
- result.push_back(cur[i]);
+ result.push_back(_pv[w][i]);
}
}
}
-bool PathIntersectionGraph::_findUnprocessed(IntersectionList::iterator &result)
+PathIntersectionGraph::ILIter PathIntersectionGraph::_getNeighbor(ILIter iter)
{
- typedef IntersectionList::iterator Iter;
-
- Iter it, last;
-
- for (std::size_t k = 0; k < _apaths.size(); ++k) {
- it = _apaths[k].xlist.begin();
- last = _apaths[k].xlist.end();
- for (; it != last; ++it) {
- if (!it->_hook.is_linked()) {
- // this intersection was removed since it did not change inside/outside status
- continue;
- }
- if (!it->processed) {
- result = it;
- return true;
- }
- }
- }
-
- return false;
+ unsigned ow = (iter->which + 1) % 2;
+ return _components[ow][iter->neighbor->pos.path_index].xlist.iterator_to(*iter->neighbor);
}
+PathIntersectionGraph::PathData &
+PathIntersectionGraph::_getPathData(ILIter iter)
+{
+ return _components[iter->which][iter->pos.path_index];
+}
std::ostream &operator<<(std::ostream &os, PathIntersectionGraph const &pig)
{
+ typedef PathIntersectionGraph::IntersectionList::const_iterator CILIter;
os << "Intersection graph:\n"
<< pig._xs.size()/2 << " total intersections\n"
<< pig.size() << " considered intersections\n";
- for (std::size_t i = 0; i < pig._apaths.size(); ++i) {
- typedef PathIntersectionGraph::IntersectionList::const_iterator Iter;
- for (Iter j = pig._apaths[i].xlist.begin(); j != pig._apaths[i].xlist.end(); ++j) {
+ for (std::size_t i = 0; i < pig._components[0].size(); ++i) {
+ PathIntersectionGraph::IntersectionList const &xl = pig._components[0][i].xlist;
+ for (CILIter j = xl.begin(); j != xl.end(); ++j) {
os << j->pos << " - " << j->neighbor->pos << " @ " << j->p << "\n";
}
}
diff --git a/src/2geom/intersection-graph.h b/src/2geom/intersection-graph.h
index bd9aaee81..332c83f18 100644
--- a/src/2geom/intersection-graph.h
+++ b/src/2geom/intersection-graph.h
@@ -58,23 +58,29 @@ public:
/// Returns the number of intersections used when computing Boolean operations.
std::size_t size() const;
- std::vector<Point> intersectionPoints() const;
+ std::vector<Point> intersectionPoints(bool defective = false) const;
+ std::vector<Point> windingPoints() const {
+ return _winding_points;
+ }
+ void fragments(PathVector &in, PathVector &out) const;
+ bool valid() const { return _graph_valid; }
private:
enum InOutFlag {
- POINT_INSIDE,
- POINT_OUTSIDE
+ INSIDE,
+ OUTSIDE,
+ BOTH
};
struct IntersectionVertex {
boost::intrusive::list_member_hook<> _hook;
+ boost::intrusive::list_member_hook<> _proc_hook;
PathVectorTime pos;
Point p; // guarantees that endpoints are exact
IntersectionVertex *neighbor;
- bool entry; // going in +t direction enters the other path
- InOutFlag previous;
- InOutFlag next;
- bool processed; // TODO: use intrusive unprocessed list instead
+ InOutFlag next_edge;
+ unsigned which;
+ bool defective;
};
typedef boost::intrusive::list
@@ -86,27 +92,50 @@ private:
>
> IntersectionList;
+ typedef boost::intrusive::list
+ < IntersectionVertex
+ , boost::intrusive::member_hook
+ < IntersectionVertex
+ , boost::intrusive::list_member_hook<>
+ , &IntersectionVertex::_proc_hook
+ >
+ > UnprocessedList;
+
struct PathData {
IntersectionList xlist;
- unsigned removed_in;
- unsigned removed_out;
-
- PathData()
- : removed_in(0)
- , removed_out(0)
+ std::size_t path_index;
+ int which;
+ InOutFlag status;
+
+ PathData(int w, std::size_t pi)
+ : path_index(pi)
+ , which(w)
+ , status(BOTH)
{}
};
struct IntersectionVertexLess;
+ typedef IntersectionList::iterator ILIter;
+ typedef IntersectionList::const_iterator CILIter;
PathVector _getResult(bool enter_a, bool enter_b);
- void _handleNonintersectingPaths(PathVector &result, int which, bool inside);
- bool _findUnprocessed(IntersectionList::iterator &result);
-
- PathVector _a, _b;
+ void _handleNonintersectingPaths(PathVector &result, unsigned which, bool inside);
+ void _prepareArguments();
+ bool _prepareIntersectionLists(Coord precision);
+ void _assignEdgeWindingParities(Coord precision);
+ void _assignComponentStatusFromDegenerateIntersections();
+ void _removeDegenerateIntersections();
+ void _verify();
+
+ ILIter _getNeighbor(ILIter iter);
+ PathData &_getPathData(ILIter iter);
+
+ PathVector _pv[2];
boost::ptr_vector<IntersectionVertex> _xs;
- boost::ptr_vector<PathData> _apaths;
- boost::ptr_vector<PathData> _bpaths;
+ boost::ptr_vector<PathData> _components[2];
+ UnprocessedList _ulist;
+ bool _graph_valid;
+ std::vector<Point> _winding_points;
friend std::ostream &operator<<(std::ostream &, PathIntersectionGraph const &);
};
diff --git a/src/2geom/line.cpp b/src/2geom/line.cpp
index bada8ef38..ee2edeba7 100644
--- a/src/2geom/line.cpp
+++ b/src/2geom/line.cpp
@@ -235,6 +235,20 @@ Coord Line::timeAt(Point const &p) const
}
}
+/** @brief Create a transformation that maps one line to another.
+ * This will return a transformation \f$A\f$ such that
+ * \f$L_1(t) \cdot A = L_2(t)\f$, where \f$L_1\f$ is this line
+ * and \f$L_2\f$ is the line passed as the parameter. The returned
+ * transformation will preserve angles. */
+Affine Line::transformTo(Line const &other) const
+{
+ Affine result = Translate(-_initial);
+ result *= Rotate(angle_between(versor(), other.versor()));
+ result *= Scale(other.versor().length() / versor().length());
+ result *= Translate(other._initial);
+ return result;
+}
+
std::vector<ShapeIntersection> Line::intersect(Line const &other) const
{
std::vector<ShapeIntersection> result;
diff --git a/src/2geom/line.h b/src/2geom/line.h
index 7d4766e12..8a66d4472 100644
--- a/src/2geom/line.h
+++ b/src/2geom/line.h
@@ -359,6 +359,8 @@ public:
Affine m = rotationToZero(other_dimension(d));
return m;
}
+
+ Affine transformTo(Line const &other) const;
/// @}
std::vector<ShapeIntersection> intersect(Line const &other) const;
diff --git a/src/2geom/linear.h b/src/2geom/linear.h
index b0306fb9f..316ed8639 100644
--- a/src/2geom/linear.h
+++ b/src/2geom/linear.h
@@ -5,8 +5,9 @@
* Authors:
* Nathan Hurst <njh@mail.csse.monash.edu.au>
* Michael Sloan <mgsloan@gmail.com>
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
*
- * Copyright (C) 2006-2007 authors
+ * Copyright (C) 2006-2015 authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -38,14 +39,6 @@
#include <2geom/interval.h>
#include <2geom/math-utils.h>
-//#define USE_SBASIS_OF
-
-#ifdef USE_SBASIS_OF
-
-#include "linear-of.h"
-
-#else
-
namespace Geom {
class SBasis;
@@ -54,26 +47,31 @@ class SBasis;
* @brief Function that interpolates linearly between two values.
* @ingroup Fragments
*/
-class Linear {
+class Linear
+ : boost::additive< Linear
+ , boost::arithmetic< Linear, Coord
+ , boost::equality_comparable< Linear
+ > > >
+{
public:
- double a[2];
+ Coord a[2];
Linear() {a[0]=0; a[1]=0;}
- Linear(double aa, double b) {a[0] = aa; a[1] = b;}
- Linear(double aa) {a[0] = aa; a[1] = aa;}
+ Linear(Coord aa, Coord b) {a[0] = aa; a[1] = b;}
+ Linear(Coord aa) {a[0] = aa; a[1] = aa;}
- double operator[](unsigned i) const {
+ Coord operator[](unsigned i) const {
assert(i < 2);
return a[i];
}
- double &operator[](unsigned i) {
+ Coord &operator[](unsigned i) {
assert(i < 2);
return a[i];
}
//IMPL: FragmentConcept
- typedef double output_type;
- bool isZero(double eps=EPSILON) const { return are_near(a[0], 0., eps) && are_near(a[1], 0., eps); }
- bool isConstant(double eps=EPSILON) const { return are_near(a[0], a[1], eps); }
+ typedef Coord output_type;
+ bool isZero(Coord eps=EPSILON) const { return are_near(a[0], 0., eps) && are_near(a[1], 0., eps); }
+ bool isConstant(Coord eps=EPSILON) const { return are_near(a[0], a[1], eps); }
bool isFinite() const { return IS_FINITE(a[0]) && IS_FINITE(a[1]); }
Coord at0() const { return a[0]; }
@@ -81,10 +79,10 @@ public:
Coord at1() const { return a[1]; }
Coord &at1() { return a[1]; }
- double valueAt(double t) const { return lerp(t, a[0], a[1]); }
- double operator()(double t) const { return valueAt(t); }
+ Coord valueAt(Coord t) const { return lerp(t, a[0], a[1]); }
+ Coord operator()(Coord t) const { return valueAt(t); }
- // not very useful, but required for ShapeConcept
+ // not very useful, but required for FragmentConcept
std::vector<Coord> valueAndDerivatives(Coord t, unsigned n) {
std::vector<Coord> result(n+1, 0.0);
result[0] = valueAt(t);
@@ -107,6 +105,44 @@ public:
double hat() const {
return (a[1] + a[0])/2;
}
+
+ // addition of other Linears
+ Linear &operator+=(Linear const &other) {
+ a[0] += other.a[0];
+ a[1] += other.a[1];
+ return *this;
+ }
+ Linear &operator-=(Linear const &other) {
+ a[0] -= other.a[0];
+ a[1] -= other.a[1];
+ return *this;
+ }
+
+ //
+ Linear &operator+=(Coord x) {
+ a[0] += x; a[1] += x;
+ return *this;
+ }
+ Linear &operator-=(Coord x) {
+ a[0] -= x; a[1] -= x;
+ return *this;
+ }
+ Linear &operator*=(Coord x) {
+ a[0] *= x; a[1] *= x;
+ return *this;
+ }
+ Linear &operator/=(Coord x) {
+ a[0] /= x; a[1] /= x;
+ return *this;
+ }
+ Linear operator-() const {
+ Linear ret(-a[0], -a[1]);
+ return ret;
+ }
+
+ bool operator==(Linear const &other) const {
+ return a[0] == other.a[0] && a[1] == other.a[1];
+ }
};
inline Linear reverse(Linear const &a) { return Linear(a[1], a[0]); }
@@ -115,64 +151,7 @@ inline Linear portion(Linear const &a, Coord from, Coord to) {
return result;
}
-//IMPL: AddableConcept
-inline Linear operator+(Linear const & a, Linear const & b) {
- return Linear(a[0] + b[0], a[1] + b[1]);
-}
-inline Linear operator-(Linear const & a, Linear const & b) {
- return Linear(a[0] - b[0], a[1] - b[1]);
-}
-inline Linear& operator+=(Linear & a, Linear const & b) {
- a[0] += b[0]; a[1] += b[1];
- return a;
-}
-inline Linear& operator-=(Linear & a, Linear const & b) {
- a[0] -= b[0]; a[1] -= b[1];
- return a;
-}
-//IMPL: OffsetableConcept
-inline Linear operator+(Linear const & a, double b) {
- return Linear(a[0] + b, a[1] + b);
-}
-inline Linear operator-(Linear const & a, double b) {
- return Linear(a[0] - b, a[1] - b);
-}
-inline Linear& operator+=(Linear & a, double b) {
- a[0] += b; a[1] += b;
- return a;
-}
-inline Linear& operator-=(Linear & a, double b) {
- a[0] -= b; a[1] -= b;
- return a;
-}
-//IMPL: boost::EqualityComparableConcept
-inline bool operator==(Linear const & a, Linear const & b) {
- return a[0] == b[0] && a[1] == b[1];
-}
-inline bool operator!=(Linear const & a, Linear const & b) {
- return a[0] != b[0] || a[1] != b[1];
-}
-//IMPL: ScalableConcept
-inline Linear operator-(Linear const &a) {
- return Linear(-a[0], -a[1]);
-}
-inline Linear operator*(Linear const & a, double b) {
- return Linear(a[0]*b, a[1]*b);
-}
-inline Linear operator/(Linear const & a, double b) {
- return Linear(a[0]/b, a[1]/b);
-}
-inline Linear operator*=(Linear & a, double b) {
- a[0] *= b; a[1] *= b;
- return a;
-}
-inline Linear operator/=(Linear & a, double b) {
- a[0] /= b; a[1] /= b;
- return a;
-}
-
-}
-#endif
+} // end namespace Geom
#endif //LIB2GEOM_SEEN_LINEAR_H
diff --git a/src/2geom/path.cpp b/src/2geom/path.cpp
index 871441751..2e2bce9f1 100644
--- a/src/2geom/path.cpp
+++ b/src/2geom/path.cpp
@@ -98,6 +98,24 @@ bool PathInterval::contains(PathTime const &pos) const {
}
}
+PathInterval::size_type PathInterval::curveCount() const
+{
+ if (isDegenerate()) return 0;
+ if (_cross_start) {
+ if (_reverse) {
+ return _path_size - _to.curve_index + _from.curve_index + 1;
+ } else {
+ return _path_size - _from.curve_index + _to.curve_index + 1;
+ }
+ } else {
+ if (_reverse) {
+ return _from.curve_index - _to.curve_index + 1;
+ } else {
+ return _to.curve_index - _from.curve_index + 1;
+ }
+ }
+}
+
PathTime PathInterval::inside(Coord min_dist) const
{
// If there is some node further than min_dist (in time coord) from the ends,
@@ -220,25 +238,25 @@ PathInterval PathInterval::from_direction(PathTime const &from, PathTime const &
Path::Path(Rect const &r)
- : _curves(new Sequence())
+ : _data(new PathData())
, _closing_seg(new ClosingSegment(r.corner(3), r.corner(0)))
, _closed(true)
, _exception_on_stitch(true)
{
for (unsigned i = 0; i < 3; ++i) {
- _curves->push_back(new LineSegment(r.corner(i), r.corner(i+1)));
+ _data->curves.push_back(new LineSegment(r.corner(i), r.corner(i+1)));
}
- _curves->push_back(_closing_seg);
+ _data->curves.push_back(_closing_seg);
}
Path::Path(ConvexHull const &ch)
- : _curves(new Sequence())
+ : _data(new PathData())
, _closing_seg(new ClosingSegment(Point(), Point()))
, _closed(true)
, _exception_on_stitch(true)
{
if (ch.empty()) {
- _curves->push_back(_closing_seg);
+ _data->curves.push_back(_closing_seg);
return;
}
@@ -248,52 +266,52 @@ Path::Path(ConvexHull const &ch)
Point last = ch.front();
for (std::size_t i = 1; i < ch.size(); ++i) {
- _curves->push_back(new LineSegment(last, ch[i]));
+ _data->curves.push_back(new LineSegment(last, ch[i]));
last = ch[i];
}
- _curves->push_back(_closing_seg);
+ _data->curves.push_back(_closing_seg);
_closed = true;
}
Path::Path(Circle const &c)
- : _curves(new Sequence())
+ : _data(new PathData())
, _closing_seg(NULL)
, _closed(true)
, _exception_on_stitch(true)
{
Point p1 = c.pointAt(0);
Point p2 = c.pointAt(M_PI);
- _curves->push_back(new EllipticalArc(p1, c.radius(), c.radius(), 0, false, true, p2));
- _curves->push_back(new EllipticalArc(p2, c.radius(), c.radius(), 0, false, true, p1));
+ _data->curves.push_back(new EllipticalArc(p1, c.radius(), c.radius(), 0, false, true, p2));
+ _data->curves.push_back(new EllipticalArc(p2, c.radius(), c.radius(), 0, false, true, p1));
_closing_seg = new ClosingSegment(p1, p1);
- _curves->push_back(_closing_seg);
+ _data->curves.push_back(_closing_seg);
}
Path::Path(Ellipse const &e)
- : _curves(new Sequence())
+ : _data(new PathData())
, _closing_seg(NULL)
, _closed(true)
, _exception_on_stitch(true)
{
Point p1 = e.pointAt(0);
Point p2 = e.pointAt(M_PI);
- _curves->push_back(new EllipticalArc(p1, e.rays(), e.rotationAngle(), false, true, p2));
- _curves->push_back(new EllipticalArc(p2, e.rays(), e.rotationAngle(), false, true, p1));
+ _data->curves.push_back(new EllipticalArc(p1, e.rays(), e.rotationAngle(), false, true, p2));
+ _data->curves.push_back(new EllipticalArc(p2, e.rays(), e.rotationAngle(), false, true, p1));
_closing_seg = new ClosingSegment(p1, p1);
- _curves->push_back(_closing_seg);
+ _data->curves.push_back(_closing_seg);
}
void Path::close(bool c)
{
if (c == _closed) return;
- if (c && _curves->size() >= 2) {
+ if (c && _data->curves.size() >= 2) {
// when closing, if last segment is linear and ends at initial point,
// replace it with the closing segment
- Sequence::iterator last = _curves->end() - 2;
+ Sequence::iterator last = _data->curves.end() - 2;
if (last->isLineSegment() && last->finalPoint() == initialPoint()) {
_closing_seg->setInitial(last->initialPoint());
- _curves->erase(last);
+ _data->curves.erase(last);
}
}
_closed = c;
@@ -302,27 +320,35 @@ void Path::close(bool c)
void Path::clear()
{
_unshare();
- _curves->pop_back().release();
- _curves->clear();
+ _data->curves.pop_back().release();
+ _data->curves.clear();
_closing_seg->setInitial(Point(0, 0));
_closing_seg->setFinal(Point(0, 0));
- _curves->push_back(_closing_seg);
+ _data->curves.push_back(_closing_seg);
_closed = false;
}
OptRect Path::boundsFast() const
{
OptRect bounds;
- if (empty())
+ if (empty()) {
return bounds;
+ }
+ // if the path is not empty, we look for cached bounds
+ if (_data->fast_bounds) {
+ return _data->fast_bounds;
+ }
+
bounds = front().boundsFast();
const_iterator iter = begin();
- // the closing path segment can be ignored, because it will always lie within the bbox of the rest of the path
+ // 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());
}
}
+ _data->fast_bounds = bounds;
return bounds;
}
@@ -377,11 +403,11 @@ bool Path::operator==(Path const &other) const
return true;
if (_closed != other._closed)
return false;
- return *_curves == *other._curves;
+ return _data->curves == other._data->curves;
}
void Path::start(Point const &p) {
- if (_curves->size() > 1) {
+ if (_data->curves.size() > 1) {
clear();
}
_closing_seg->setInitial(p);
@@ -442,86 +468,106 @@ std::vector<PathTime> Path::roots(Coord v, Dim2 d) const
// Instead of O(N^2), this takes O(N + X), where X is the number of overlaps
// between the bounding boxes of curves.
-struct CurveSweepTraits {
- struct Bound {
- Rect r;
+struct CurveIntersectionSweepSet
+{
+public:
+ struct CurveRecord {
+ boost::intrusive::list_member_hook<> _hook;
+ Curve const *curve;
+ Rect bounds;
std::size_t index;
- int which;
+ unsigned which;
+
+ CurveRecord(Curve const *pc, std::size_t idx, unsigned w)
+ : curve(pc)
+ , bounds(curve->boundsFast())
+ , index(idx)
+ , which(w)
+ {}
};
- typedef std::less<Coord> Compare;
- inline static Coord entry_value(Bound const &b) { return b.r[X].min(); }
- inline static Coord exit_value(Bound const &b) { return b.r[X].max(); }
-};
-class CurveSweeper
- : public Sweeper<Curve const *, CurveSweepTraits>
-{
-public:
- CurveSweeper(Path const &a, Path const &b, std::vector<PathIntersection> &result, Coord prec)
+ typedef std::vector<CurveRecord>::const_iterator ItemIterator;
+
+ CurveIntersectionSweepSet(std::vector<PathIntersection> &result,
+ Path const &a, Path const &b, Coord precision)
: _result(result)
- , _precision(prec)
+ , _precision(precision)
+ , _sweep_dir(X)
{
- for (std::size_t i = 0; i < a.size(); ++i) {
- Bound bound;
- bound.r = a[i].boundsFast();
- bound.index = i;
- bound.which = 0;
- insert(bound, &a[i]);
+ std::size_t asz = a.size(), bsz = b.size();
+ _records.reserve(asz + bsz);
+
+ for (std::size_t i = 0; i < asz; ++i) {
+ _records.push_back(CurveRecord(&a[i], i, 0));
}
- for (std::size_t i = 0; i < b.size(); ++i) {
- Bound bound;
- bound.r = b[i].boundsFast();
- bound.index = i;
- bound.which = 1;
- insert(bound, &b[i]);
+ for (std::size_t i = 0; i < bsz; ++i) {
+ _records.push_back(CurveRecord(&b[i], i, 1));
}
- }
-protected:
- void _enter(Record const &record) {
- int which = record.bound.which;
-
- for (RecordList::iterator i = _active_items.begin(); i != _active_items.end(); ++i) {
- // do not intersect in the same path
- if (i->bound.which == which) continue;
- // do not intersect if boxes do not overlap in Y
- if (!record.bound.r[Y].intersects(i->bound.r[Y])) continue;
-
- std::vector<CurveIntersection> cx;
- int ia = record.bound.index;
- int ib = i->bound.index;
+ OptRect abb = a.boundsFast() | b.boundsFast();
+ if (abb && abb->height() > abb->width()) {
+ _sweep_dir = Y;
+ }
+ }
- if (which == 0) {
- cx = record.item->intersect(*i->item, _precision);
- } else {
- cx = i->item->intersect(*record.item, _precision);
- std::swap(ia, ib);
- }
+ std::vector<CurveRecord> const &items() { return _records; }
+ Interval itemBounds(ItemIterator ii) {
+ return ii->bounds[_sweep_dir];
+ }
- for (std::size_t ci = 0; ci < cx.size(); ++ci) {
- PathTime a(ia, cx[ci].first), b(ib, cx[ci].second);
- PathIntersection px(a, b, cx[ci].point());
- _result.push_back(px);
+ void addActiveItem(ItemIterator ii) {
+ unsigned w = ii->which;
+ unsigned ow = (w+1) % 2;
+
+ _active[w].push_back(const_cast<CurveRecord&>(*ii));
+
+ for (ActiveCurveList::iterator i = _active[ow].begin(); i != _active[ow].end(); ++i) {
+ if (!ii->bounds.intersects(i->bounds)) continue;
+ std::vector<CurveIntersection> cx = ii->curve->intersect(*i->curve, _precision);
+ for (std::size_t k = 0; k < cx.size(); ++k) {
+ PathTime tw(ii->index, cx[k].first), tow(i->index, cx[k].second);
+ _result.push_back(PathIntersection(
+ w == 0 ? tw : tow,
+ w == 0 ? tow : tw,
+ cx[k].point()));
}
}
}
+ void removeActiveItem(ItemIterator ii) {
+ ActiveCurveList &acl = _active[ii->which];
+ acl.erase(acl.iterator_to(*ii));
+ }
private:
+ typedef boost::intrusive::list
+ < CurveRecord
+ , boost::intrusive::member_hook
+ < CurveRecord
+ , boost::intrusive::list_member_hook<>
+ , &CurveRecord::_hook
+ >
+ > ActiveCurveList;
+
+ std::vector<CurveRecord> _records;
std::vector<PathIntersection> &_result;
+ ActiveCurveList _active[2];
Coord _precision;
+ Dim2 _sweep_dir;
};
std::vector<PathIntersection> Path::intersect(Path const &other, Coord precision) const
{
std::vector<PathIntersection> result;
- CurveSweeper sweeper(*this, other, result, precision);
+ CurveIntersectionSweepSet cisset(result, *this, other, precision);
+ Sweeper<CurveIntersectionSweepSet> sweeper(cisset);
sweeper.process();
// preprocessing to remove duplicate intersections at endpoints
+ std::size_t asz = size(), bsz = other.size();
for (std::size_t i = 0; i < result.size(); ++i) {
- result[i].first.normalizeForward(size());
- result[i].second.normalizeForward(other.size());
+ result[i].first.normalizeForward(asz);
+ result[i].second.normalizeForward(bsz);
}
std::sort(result.begin(), result.end());
result.erase(std::unique(result.begin(), result.end()), result.end());
@@ -672,7 +718,7 @@ PathTime Path::nearestTime(Point const &p, Coord *dist) const
Coord mindist = std::numeric_limits<Coord>::max();
PathTime ret;
- if (_curves->size() == 1) {
+ if (_data->curves.size() == 1) {
// naked moveto
ret.curve_index = 0;
ret.t = 0;
@@ -701,6 +747,16 @@ PathTime Path::nearestTime(Point const &p, Coord *dist) const
return ret;
}
+std::vector<Point> Path::nodes() const
+{
+ std::vector<Point> result;
+ size_type path_size = size_closed();
+ for (size_type i = 0; i < path_size; ++i) {
+ result.push_back(_data->curves[i].initialPoint());
+ }
+ return result;
+}
+
void Path::appendPortionTo(Path &ret, double from, double to) const
{
if (!(from >= 0 && to >= 0)) {
@@ -797,16 +853,16 @@ Path Path::reversed() const
Path ret(finalPoint());
if (empty()) return ret;
- ret._curves->pop_back(); // this also deletes the closing segment from ret
+ ret._data->curves.pop_back(); // this also deletes the closing segment from ret
- RIter iter(_includesClosingSegment() ? _curves->end() : _curves->end() - 1);
- RIter rend(_curves->begin());
+ RIter iter(_includesClosingSegment() ? _data->curves.end() : _data->curves.end() - 1);
+ RIter rend(_data->curves.begin());
if (_closed) {
// when the path is closed, there are two cases:
if (front().isLineSegment()) {
// 1. initial segment is linear: it becomes the new closing segment.
- rend = RIter(_curves->begin() + 1);
+ rend = RIter(_data->curves.begin() + 1);
ret._closing_seg = new ClosingSegment(front().finalPoint(), front().initialPoint());
} else {
// 2. initial segment is not linear: the closing segment becomes degenerate.
@@ -820,9 +876,9 @@ Path Path::reversed() const
}
for (; iter != rend; ++iter) {
- ret._curves->push_back(iter->reverse());
+ ret._data->curves.push_back(iter->reverse());
}
- ret._curves->push_back(ret._closing_seg);
+ ret._data->curves.push_back(ret._closing_seg);
ret._closed = _closed;
return ret;
}
@@ -896,10 +952,10 @@ void Path::replace(iterator first, iterator last, Path const &path)
void Path::snapEnds(Coord precision)
{
if (!_closed) return;
- if (_curves->size() > 1 && are_near(_closing_seg->length(precision), 0, precision)) {
+ if (_data->curves.size() > 1 && are_near(_closing_seg->length(precision), 0, precision)) {
_unshare();
_closing_seg->setInitial(_closing_seg->finalPoint());
- (_curves->end() - 1)->setFinal(_closing_seg->finalPoint());
+ (_data->curves.end() - 1)->setFinal(_closing_seg->finalPoint());
}
}
@@ -908,7 +964,7 @@ void Path::snapEnds(Coord precision)
void Path::do_update(Sequence::iterator first, Sequence::iterator last, Sequence &source)
{
// TODO: handle cases where first > last in closed paths?
- bool last_beyond_closing_segment = (last == _curves->end());
+ bool last_beyond_closing_segment = (last == _data->curves.end());
// special case:
// if do_update replaces the closing segment, we have to regenerate it
@@ -916,7 +972,7 @@ void Path::do_update(Sequence::iterator first, Sequence::iterator last, Sequence
if (first == last) return; // nothing to do
// only removing some segments
- if ((!_closed && first == _curves->begin()) || (!_closed && last == _curves->end() - 1) || last_beyond_closing_segment) {
+ if ((!_closed && first == _data->curves.begin()) || (!_closed && last == _data->curves.end() - 1) || last_beyond_closing_segment) {
// just adjust the closing segment
// do nothing
} else if (first->initialPoint() != (last - 1)->finalPoint()) {
@@ -927,17 +983,17 @@ void Path::do_update(Sequence::iterator first, Sequence::iterator last, Sequence
}
} else {
// replacing
- if (first == _curves->begin() && last == _curves->end()) {
+ if (first == _data->curves.begin() && last == _data->curves.end()) {
// special case: replacing everything should work the same in open and closed curves
- _curves->erase(_curves->begin(), _curves->end() - 1);
+ _data->curves.erase(_data->curves.begin(), _data->curves.end() - 1);
_closing_seg->setFinal(source.front().initialPoint());
_closing_seg->setInitial(source.back().finalPoint());
- _curves->transfer(_curves->begin(), source.begin(), source.end(), source);
+ _data->curves.transfer(_data->curves.begin(), source.begin(), source.end(), source);
return;
}
// stitch in front
- if (!_closed && first == _curves->begin()) {
+ if (!_closed && first == _data->curves.begin()) {
// not necessary to stitch in front
} else if (first->initialPoint() != source.front().initialPoint()) {
if (_exception_on_stitch) {
@@ -947,7 +1003,7 @@ void Path::do_update(Sequence::iterator first, Sequence::iterator last, Sequence
}
// stitch at the end
- if ((!_closed && last == _curves->end() - 1) || last_beyond_closing_segment) {
+ if ((!_closed && last == _data->curves.end() - 1) || last_beyond_closing_segment) {
// repurpose the closing segment as the stitch segment
// do nothing
} else if (source.back().finalPoint() != (last - 1)->finalPoint()) {
@@ -962,8 +1018,8 @@ void Path::do_update(Sequence::iterator first, Sequence::iterator last, Sequence
if (last_beyond_closing_segment) {
--last;
}
- _curves->erase(first, last);
- _curves->transfer(first, source.begin(), source.end(), source);
+ _data->curves.erase(first, last);
+ _data->curves.transfer(first, source.begin(), source.end(), source);
// adjust closing segment
if (size_open() == 0) {
@@ -978,7 +1034,7 @@ void Path::do_update(Sequence::iterator first, Sequence::iterator last, Sequence
void Path::do_append(Curve *c)
{
- if (&_curves->front() == _closing_seg) {
+ if (&_data->curves.front() == _closing_seg) {
_closing_seg->setFinal(c->initialPoint());
} else {
// if we can't freely move the closing segment, we check whether
@@ -994,20 +1050,20 @@ void Path::do_append(Curve *c)
return;
}
}
- _curves->insert(_curves->end() - 1, c);
+ _data->curves.insert(_data->curves.end() - 1, c);
_closing_seg->setInitial(c->finalPoint());
}
void Path::checkContinuity() const
{
- Sequence::const_iterator i = _curves->begin(), j = _curves->begin();
+ Sequence::const_iterator i = _data->curves.begin(), j = _data->curves.begin();
++j;
- for (; j != _curves->end(); ++i, ++j) {
+ for (; j != _data->curves.end(); ++i, ++j) {
if (i->finalPoint() != j->initialPoint()) {
THROW_CONTINUITYERROR();
}
}
- if (_curves->front().initialPoint() != _curves->back().finalPoint()) {
+ if (_data->curves.front().initialPoint() != _data->curves.back().finalPoint()) {
THROW_CONTINUITYERROR();
}
}
@@ -1040,6 +1096,16 @@ Piecewise<D2<SBasis> > paths_to_pw(PathVector const &paths)
return ret;
}
+bool are_near(Path const &a, Path const &b, Coord precision)
+{
+ if (a.size() != b.size()) return false;
+
+ for (unsigned i = 0; i < a.size(); ++i) {
+ if (!a[i].isNear(b[i], precision)) return false;
+ }
+ return true;
+}
+
std::ostream &operator<<(std::ostream &out, Path const &path)
{
SVGPathWriter pw;
diff --git a/src/2geom/path.h b/src/2geom/path.h
index 3ca43e0e5..3552ec43e 100644
--- a/src/2geom/path.h
+++ b/src/2geom/path.h
@@ -55,6 +55,11 @@ namespace PathInternal {
typedef boost::ptr_vector<Curve> Sequence;
+struct PathData {
+ Sequence curves;
+ OptRect fast_bounds;
+};
+
template <typename P>
class BaseIterator
: public boost::random_access_iterator_helper
@@ -221,6 +226,7 @@ public:
}
size_type pathSize() const { return _path_size; }
+ size_type curveCount() const;
private:
PathTime _from, _to;
@@ -316,6 +322,7 @@ class Path
: boost::equality_comparable< Path >
{
public:
+ typedef PathInternal::PathData PathData;
typedef PathInternal::Sequence Sequence;
typedef PathInternal::BaseIterator<Path> iterator;
typedef PathInternal::BaseIterator<Path const> const_iterator;
@@ -342,31 +349,31 @@ public:
/// Construct an empty path starting at the specified point.
explicit Path(Point const &p = Point())
- : _curves(new Sequence())
+ : _data(new PathData())
, _closing_seg(new ClosingSegment(p, p))
, _closed(false)
, _exception_on_stitch(true)
{
- _curves->push_back(_closing_seg);
+ _data->curves.push_back(_closing_seg);
}
/// Construct a path containing a range of curves.
template <typename Iter>
Path(Iter first, Iter last, bool closed = false, bool stitch = false)
- : _curves(new Sequence())
+ : _data(new PathData())
, _closed(closed)
, _exception_on_stitch(!stitch)
{
for (Iter i = first; i != last; ++i) {
- _curves->push_back(i->duplicate());
+ _data->curves.push_back(i->duplicate());
}
- if (!_curves->empty()) {
- _closing_seg = new ClosingSegment(_curves->back().finalPoint(),
- _curves->front().initialPoint());
+ if (!_data->curves.empty()) {
+ _closing_seg = new ClosingSegment(_data->curves.back().finalPoint(),
+ _data->curves.front().initialPoint());
} else {
_closing_seg = new ClosingSegment();
}
- _curves->push_back(_closing_seg);
+ _data->curves.push_back(_closing_seg);
}
/// Create a path from a rectangle.
@@ -386,7 +393,7 @@ public:
* @todo Add noexcept specifiers for C++11 */
void swap(Path &other) throw() {
using std::swap;
- swap(other._curves, _curves);
+ swap(other._data, _data);
swap(other._closing_seg, _closing_seg);
swap(other._closed, _closed);
swap(other._exception_on_stitch, _exception_on_stitch);
@@ -396,26 +403,26 @@ public:
friend inline void swap(Path &a, Path &b) throw() { a.swap(b); }
/** @brief Access a curve by index */
- Curve const &operator[](size_type i) const { return (*_curves)[i]; }
+ Curve const &operator[](size_type i) const { return _data->curves[i]; }
/** @brief Access a curve by index */
- Curve const &at(size_type i) const { return _curves->at(i); }
+ Curve const &at(size_type i) const { return _data->curves.at(i); }
/** @brief Access the first curve in the path.
* Since the curve always contains at least a degenerate closing segment,
* it is always safe to use this method. */
- Curve const &front() const { return _curves->front(); }
+ Curve const &front() const { return _data->curves.front(); }
/// Alias for front().
- Curve const &initialCurve() const { return _curves->front(); }
+ Curve const &initialCurve() const { return _data->curves.front(); }
/** @brief Access the last curve in the path. */
Curve const &back() const { return back_default(); }
Curve const &back_open() const {
- if (empty()) return _curves->back();
- return (*_curves)[_curves->size() - 2];
+ if (empty()) return _data->curves.back();
+ return _data->curves[_data->curves.size() - 2];
}
Curve const &back_closed() const {
return _closing_seg->isDegenerate()
- ? (*_curves)[_curves->size() - 2]
- : (*_curves)[_curves->size() - 1];
+ ? _data->curves[_data->curves.size() - 2]
+ : _data->curves[_data->curves.size() - 1];
}
Curve const &back_default() const {
return _includesClosingSegment()
@@ -436,13 +443,13 @@ public:
iterator end_closed() { return iterator(*this, size_closed()); }
/// Size without the closing segment, even if the path is closed.
- size_type size_open() const { return _curves->size() - 1; }
+ size_type size_open() const { return _data->curves.size() - 1; }
/** @brief Size with the closing segment, if it makes a difference.
* If the closing segment is degenerate, i.e. its initial and final points
* are exactly equal, then it is not included in this size. */
size_type size_closed() const {
- return _closing_seg->isDegenerate() ? _curves->size() - 1 : _curves->size();
+ return _closing_seg->isDegenerate() ? _data->curves.size() - 1 : _data->curves.size();
}
/// Natural size of the path.
@@ -452,7 +459,7 @@ public:
/// Natural size of the path.
size_type size() const { return size_default(); }
- size_type max_size() const { return _curves->max_size() - 1; }
+ size_type max_size() const { return _data->curves.max_size() - 1; }
/** @brief Check whether path is empty.
* The path is empty if it contains only the closing segment, which according
@@ -460,7 +467,7 @@ public:
* containers, two empty paths are not necessarily identical, because the
* degenerate closing segment may be at a different point, affecting the operation
* of methods such as appendNew(). */
- bool empty() const { return (_curves->size() == 1); }
+ bool empty() const { return (_data->curves.size() == 1); }
/// Check whether the path is closed.
bool closed() const { return _closed; }
@@ -497,8 +504,8 @@ public:
Path &operator*=(T const &tr) {
BOOST_CONCEPT_ASSERT((TransformConcept<T>));
_unshare();
- for (std::size_t i = 0; i < _curves->size(); ++i) {
- (*_curves)[i] *= tr;
+ for (std::size_t i = 0; i < _data->curves.size(); ++i) {
+ _data->curves[i] *= tr;
}
return *this;
}
@@ -570,6 +577,8 @@ public:
PathTime nearestTime(Point const &p, Coord *dist = NULL) const;
std::vector<Coord> nearestTimePerCurve(Point const &p) const;
+ std::vector<Point> nodes() const;
+
void appendPortionTo(Path &p, Coord f, Coord t) const;
/** @brief Append a subset of this path to another path.
@@ -662,13 +671,13 @@ public:
void setInitial(Point const &p) {
_unshare();
_closed = false;
- _curves->front().setInitial(p);
+ _data->curves.front().setInitial(p);
_closing_seg->setFinal(p);
}
void setFinal(Point const &p) {
_unshare();
_closed = false;
- (*_curves)[size_open() - 1].setFinal(p);
+ _data->curves[size_open() - 1].setFinal(p);
_closing_seg->setInitial(p);
}
@@ -804,10 +813,10 @@ public:
private:
static Sequence::iterator seq_iter(iterator const &iter) {
- return iter.path->_curves->begin() + iter.index;
+ return iter.path->_data->curves.begin() + iter.index;
}
static Sequence::const_iterator seq_iter(const_iterator const &iter) {
- return iter.path->_curves->begin() + iter.index;
+ return iter.path->_data->curves.begin() + iter.index;
}
// whether the closing segment is part of the path
@@ -815,10 +824,13 @@ private:
return _closed && !_closing_seg->isDegenerate();
}
void _unshare() {
- if (!_curves.unique()) {
- _curves.reset(new Sequence(*_curves));
- _closing_seg = static_cast<ClosingSegment*>(&_curves->back());
+ // Called before every mutation.
+ // Ensure we have our own copy of curve data and reset cached values
+ if (!_data.unique()) {
+ _data.reset(new PathData(*_data));
+ _closing_seg = static_cast<ClosingSegment*>(&_data->curves.back());
}
+ _data->fast_bounds = OptRect();
}
PathTime _factorTime(Coord t) const;
@@ -828,7 +840,7 @@ private:
// n.b. takes ownership of curve object
void do_append(Curve *curve);
- boost::shared_ptr<Sequence> _curves;
+ boost::shared_ptr<PathData> _data;
ClosingSegment *_closing_seg;
bool _closed;
bool _exception_on_stitch;
@@ -841,6 +853,8 @@ inline Coord nearest_time(Point const &p, Path const &c) {
return pt.curve_index + pt.t;
}
+bool are_near(Path const &a, Path const &b, Coord precision = EPSILON);
+
std::ostream &operator<<(std::ostream &out, Path const &path);
} // end namespace Geom
diff --git a/src/2geom/pathvector.cpp b/src/2geom/pathvector.cpp
index bff201c71..28ab26237 100644
--- a/src/2geom/pathvector.cpp
+++ b/src/2geom/pathvector.cpp
@@ -35,6 +35,7 @@
#include <2geom/path.h>
#include <2geom/pathvector.h>
#include <2geom/svg-path-writer.h>
+#include <2geom/sweeper.h>
namespace Geom {
@@ -133,19 +134,96 @@ void PathVector::snapEnds(Coord precision)
}
}
-std::vector<PVIntersection> PathVector::intersect(PathVector const &other, Coord precision) const
-{
- typedef PathVectorTime PVPos;
- std::vector<PVIntersection> result;
- for (std::size_t i = 0; i < size(); ++i) {
- for (std::size_t j = 0; j < other.size(); ++j) {
- std::vector<PathIntersection> xs = (*this)[i].intersect(other[j], precision);
- for (std::size_t k = 0; k < xs.size(); ++k) {
- PVIntersection pvx(PVPos(i, xs[k].first), PVPos(j, xs[k].second), xs[k].point());
- result.push_back(pvx);
+// sweepline optimization
+// this is very similar to CurveIntersectionSweepSet in path.cpp
+// should probably be merged
+class PathIntersectionSweepSet {
+public:
+ struct PathRecord {
+ boost::intrusive::list_member_hook<> _hook;
+ Path const *path;
+ std::size_t index;
+ unsigned which;
+
+ PathRecord(Path const &p, std::size_t i, unsigned w)
+ : path(&p)
+ , index(i)
+ , which(w)
+ {}
+ };
+
+ typedef std::vector<PathRecord>::iterator ItemIterator;
+
+ PathIntersectionSweepSet(std::vector<PVIntersection> &result,
+ PathVector const &a, PathVector const &b, Coord precision)
+ : _result(result)
+ , _precision(precision)
+ {
+ _records.reserve(a.size() + b.size());
+ for (std::size_t i = 0; i < a.size(); ++i) {
+ _records.push_back(PathRecord(a[i], i, 0));
+ }
+ for (std::size_t i = 0; i < b.size(); ++i) {
+ _records.push_back(PathRecord(b[i], i, 1));
+ }
+ }
+
+ std::vector<PathRecord> &items() { return _records; }
+
+ Interval itemBounds(ItemIterator ii) {
+ OptRect r = ii->path->boundsFast();
+ return (*r)[X];
+ }
+
+ void addActiveItem(ItemIterator ii) {
+ unsigned w = ii->which;
+ unsigned ow = (ii->which + 1) % 2;
+
+ for (ActivePathList::iterator i = _active[ow].begin(); i != _active[ow].end(); ++i) {
+ if (!ii->path->boundsFast().intersects(i->path->boundsFast())) continue;
+ std::vector<PathIntersection> px = ii->path->intersect(*i->path, _precision);
+ for (std::size_t k = 0; k < px.size(); ++k) {
+ PathVectorTime tw(ii->index, px[k].first), tow(i->index, px[k].second);
+ _result.push_back(PVIntersection(
+ w == 0 ? tw : tow,
+ w == 0 ? tow : tw,
+ px[k].point()));
}
}
+ _active[w].push_back(*ii);
+ }
+
+ void removeActiveItem(ItemIterator ii) {
+ ActivePathList &apl = _active[ii->which];
+ apl.erase(apl.iterator_to(*ii));
}
+
+private:
+ typedef boost::intrusive::list
+ < PathRecord
+ , boost::intrusive::member_hook
+ < PathRecord
+ , boost::intrusive::list_member_hook<>
+ , &PathRecord::_hook
+ >
+ > ActivePathList;
+
+ std::vector<PVIntersection> &_result;
+ std::vector<PathRecord> _records;
+ ActivePathList _active[2];
+ Coord _precision;
+};
+
+std::vector<PVIntersection> PathVector::intersect(PathVector const &other, Coord precision) const
+{
+ std::vector<PVIntersection> result;
+
+ PathIntersectionSweepSet pisset(result, *this, other, precision);
+ Sweeper<PathIntersectionSweepSet> sweeper(pisset);
+ sweeper.process();
+
+ std::sort(result.begin(), result.end());
+
return result;
}
@@ -153,6 +231,7 @@ int PathVector::winding(Point const &p) const
{
int wind = 0;
for (const_iterator i = begin(); i != end(); ++i) {
+ if (!i->boundsFast().contains(p)) continue;
wind += i->winding(p);
}
return wind;
@@ -201,6 +280,18 @@ std::vector<PathVectorTime> PathVector::allNearestTimes(Point const &p, Coord *d
return retval;
}
+std::vector<Point> PathVector::nodes() const
+{
+ std::vector<Point> result;
+ for (size_type i = 0; i < size(); ++i) {
+ size_type path_size = (*this)[i].size_closed();
+ for (size_type j = 0; j < path_size; ++j) {
+ result.push_back((*this)[i][j].initialPoint());
+ }
+ }
+ return result;
+}
+
PathVectorTime PathVector::_factorTime(Coord t) const
{
PathVectorTime ret;
diff --git a/src/2geom/pathvector.h b/src/2geom/pathvector.h
index 108f2aa05..0cbe5bf52 100644
--- a/src/2geom/pathvector.h
+++ b/src/2geom/pathvector.h
@@ -272,6 +272,8 @@ public:
boost::optional<PathVectorTime> nearestTime(Point const &p, Coord *dist = NULL) const;
std::vector<PathVectorTime> allNearestTimes(Point const &p, Coord *dist = NULL) const;
+ std::vector<Point> nodes() const;
+
private:
PathVectorTime _factorTime(Coord t) const;
diff --git a/src/2geom/piecewise.h b/src/2geom/piecewise.h
index a5a65a9fe..3a12671e0 100644
--- a/src/2geom/piecewise.h
+++ b/src/2geom/piecewise.h
@@ -222,7 +222,7 @@ class Piecewise {
inline void setDomain(Interval dom) {
if(empty()) return;
/* dom can not be empty
- if(dom.isEmpty()) {
+ if(dom.empty()) {
cuts.clear(); segs.clear();
return;
}*/
diff --git a/src/2geom/rect.cpp b/src/2geom/rect.cpp
index 383e72c8e..5a821e9f9 100644
--- a/src/2geom/rect.cpp
+++ b/src/2geom/rect.cpp
@@ -30,9 +30,56 @@
*/
#include <2geom/rect.h>
+#include <2geom/transforms.h>
namespace Geom {
+Point align_factors(Align g) {
+ Point p;
+ switch (g) {
+ case ALIGN_XMIN_YMIN:
+ p[X] = 0.0;
+ p[Y] = 0.0;
+ break;
+ case ALIGN_XMID_YMIN:
+ p[X] = 0.5;
+ p[Y] = 0.0;
+ break;
+ case ALIGN_XMAX_YMIN:
+ p[X] = 1.0;
+ p[Y] = 0.0;
+ break;
+ case ALIGN_XMIN_YMID:
+ p[X] = 0.0;
+ p[Y] = 0.5;
+ break;
+ case ALIGN_XMID_YMID:
+ p[X] = 0.5;
+ p[Y] = 0.5;
+ break;
+ case ALIGN_XMAX_YMID:
+ p[X] = 1.0;
+ p[Y] = 0.5;
+ break;
+ case ALIGN_XMIN_YMAX:
+ p[X] = 0.0;
+ p[Y] = 1.0;
+ break;
+ case ALIGN_XMID_YMAX:
+ p[X] = 0.5;
+ p[Y] = 1.0;
+ break;
+ case ALIGN_XMAX_YMAX:
+ p[X] = 1.0;
+ p[Y] = 1.0;
+ break;
+ default:
+ break;
+ }
+ return p;
+}
+
+
/** @brief Transform the rectangle by an affine.
* The result of the transformation might not be axis-aligned. The return value
* of this operation will be the smallest axis-aligned rectangle containing
@@ -49,6 +96,37 @@ Rect &Rect::operator*=(Affine const &m) {
return *this;
}
+Affine Rect::transformTo(Rect const &viewport, Aspect const &aspect) const
+{
+ // 1. translate viewbox to origin
+ Geom::Affine total = Translate(-min());
+
+ // 2. compute scale
+ Geom::Point vdims = viewport.dimensions();
+ Geom::Point dims = dimensions();
+ Geom::Scale scale(vdims[X] / dims[X], vdims[Y] / dims[Y]);
+
+ if (aspect.align == ALIGN_NONE) {
+ // apply non-uniform scale
+ total *= scale * Translate(viewport.min());
+ } else {
+ double uscale = 0;
+ if (aspect.expansion == EXPANSION_MEET) {
+ uscale = std::min(scale[X], scale[Y]);
+ } else {
+ uscale = std::max(scale[X], scale[Y]);
+ }
+ scale = Scale(uscale);
+
+ // compute offset for align
+ Geom::Point offset = vdims - dims * scale;
+ offset *= Scale(align_factors(aspect.align));
+ total *= scale * Translate(viewport.min() + offset);
+ }
+
+ return total;
+}
+
Coord distanceSq(Point const &p, Rect const &rect)
{
double dx = 0, dy = 0;
diff --git a/src/2geom/rect.h b/src/2geom/rect.h
index 51daf6b5a..d86741476 100644
--- a/src/2geom/rect.h
+++ b/src/2geom/rect.h
@@ -47,6 +47,43 @@
namespace Geom {
+/** Values for the <align> parameter of preserveAspectRatio.
+ * See: http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute */
+enum Align {
+ ALIGN_NONE,
+ ALIGN_XMIN_YMIN,
+ ALIGN_XMID_YMIN,
+ ALIGN_XMAX_YMIN,
+ ALIGN_XMIN_YMID,
+ ALIGN_XMID_YMID,
+ ALIGN_XMAX_YMID,
+ ALIGN_XMIN_YMAX,
+ ALIGN_XMID_YMAX,
+ ALIGN_XMAX_YMAX
+};
+
+/** Values for the <meetOrSlice> parameter of preserveAspectRatio.
+ * See: http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute */
+enum Expansion {
+ EXPANSION_MEET,
+ EXPANSION_SLICE
+};
+
+/// Convert an align specification to coordinate fractions.
+Point align_factors(Align align);
+
+/** @brief Structure that specifies placement of within a viewport.
+ * Use this to create transformations that preserve aspect. */
+struct Aspect {
+ Align align;
+ Expansion expansion;
+ bool deferred; ///< for SVG compatibility
+
+ Aspect(Align a = ALIGN_NONE, Expansion ex = EXPANSION_MEET)
+ : align(a), expansion(ex), deferred(false)
+ {}
+};
+
/**
* @brief Axis aligned, non-empty rectangle.
* @ingroup Primitives
@@ -113,6 +150,16 @@ public:
}
/// @}
+ /// @name SVG viewbox functionality.
+ /// @{
+ /** @brief Transform contents to viewport.
+ * Computes an affine that transforms the contents of this rectangle
+ * to the specified viewport. The aspect parameter specifies how to
+ * to the transformation (whether the aspect ratio of content
+ * should be kept and where it should be placed in the viewport). */
+ Affine transformTo(Rect const &viewport, Aspect const &aspect = Aspect()) const;
+ /// @}
+
/// @name Operators
/// @{
Rect &operator*=(Affine const &m);
@@ -145,8 +192,14 @@ public:
OptRect(OptIntRect const &r) : Base() {
if (r) *this = Rect(*r);
}
- // actually, the only reason we have this class, instead of typedefing
- // to GenericOptRect<Coord>, are the above constructors
+
+ Affine transformTo(Rect const &viewport, Aspect const &aspect = Aspect()) {
+ Affine ret = Affine::identity();
+ if (empty()) return ret;
+ ret = (*this)->transformTo(viewport, aspect);
+ return ret;
+ }
+
bool operator==(OptRect const &other) const {
return Base::operator==(other);
}
diff --git a/src/2geom/sbasis-curve.h b/src/2geom/sbasis-curve.h
index affe7edc0..cfc4ee9a8 100644
--- a/src/2geom/sbasis-curve.h
+++ b/src/2geom/sbasis-curve.h
@@ -37,6 +37,7 @@
#define LIB2GEOM_SEEN_SBASIS_CURVE_H
#include <2geom/curve.h>
+#include <2geom/exception.h>
#include <2geom/nearest-time.h>
#include <2geom/sbasis-geometric.h>
#include <2geom/transforms.h>
@@ -131,6 +132,10 @@ public:
if (!other) return false;
return inner == other->inner;
}
+ virtual bool isNear(Curve const &/*c*/, Coord /*eps*/) const {
+ THROW_NOTIMPLEMENTED();
+ return false;
+ }
virtual int degreesOfFreedom() const {
return inner[0].degreesOfFreedom() + inner[1].degreesOfFreedom();
}
diff --git a/src/2geom/sbasis-math.cpp b/src/2geom/sbasis-math.cpp
index 896eb18a7..e9ee917de 100644
--- a/src/2geom/sbasis-math.cpp
+++ b/src/2geom/sbasis-math.cpp
@@ -34,9 +34,8 @@
//TODO: define a truncated compose(sb,sb, order) and extend it to pw<sb>.
//TODO: in all these functions, compute 'order' according to 'tol'.
+#include <2geom/d2.h>
#include <2geom/sbasis-math.h>
-
-#include <2geom/d2-sbasis.h>
#include <stdio.h>
#include <math.h>
//#define ZERO 1e-3
diff --git a/src/2geom/svg-path-parser.cpp b/src/2geom/svg-path-parser.cpp
index b6e6da869..93694156d 100644
--- a/src/2geom/svg-path-parser.cpp
+++ b/src/2geom/svg-path-parser.cpp
@@ -1424,7 +1424,7 @@ _match:
Point point = _pop_point();
bool sweep = _pop_flag();
bool large_arc = _pop_flag();
- double angle = deg_to_rad(_pop());
+ double angle = rad_from_deg(_pop());
double ry = _pop();
double rx = _pop();
@@ -1530,7 +1530,7 @@ _again:
Point point = _pop_point();
bool sweep = _pop_flag();
bool large_arc = _pop_flag();
- double angle = deg_to_rad(_pop());
+ double angle = rad_from_deg(_pop());
double ry = _pop();
double rx = _pop();
diff --git a/src/2geom/svg-path-writer.cpp b/src/2geom/svg-path-writer.cpp
index 1c40ba64e..c484a172b 100644
--- a/src/2geom/svg-path-writer.cpp
+++ b/src/2geom/svg-path-writer.cpp
@@ -150,7 +150,7 @@ void SVGPathWriter::arcTo(double rx, double ry, double angle,
_setCommand('A');
_current_pars.push_back(rx);
_current_pars.push_back(ry);
- _current_pars.push_back(rad_to_deg(angle));
+ _current_pars.push_back(deg_from_rad(angle));
_current_pars.push_back(large_arc ? 1. : 0.);
_current_pars.push_back(sweep ? 1. : 0.);
_current_pars.push_back(p[X]);
diff --git a/src/2geom/sweeper.h b/src/2geom/sweeper.h
index 8c4e182a6..b3c96455f 100644
--- a/src/2geom/sweeper.h
+++ b/src/2geom/sweeper.h
@@ -41,32 +41,24 @@
namespace Geom {
-/** @brief Sweep traits class for interval bounds.
- * @relates Sweeper
- * @ingroup Utilities */
-struct IntervalSweepTraits {
- typedef Interval Bound;
- typedef std::less<Coord> Compare;
- inline static Coord entry_value(Bound const &b) { return b.min(); }
- inline static Coord exit_value(Bound const &b) { return b.max(); }
-};
+// exposition only
+template <typename Item>
+class SweepVector {
+public:
+ typedef typename std::vector<Item>::const_iterator ItemIterator;
-/** @brief Sweep traits class for rectangle bounds.
- * @tparam D Which axis to use for sweeping
- * @ingroup Utilities */
-template <Dim2 D>
-struct RectSweepTraits {
- typedef Rect Bound;
- typedef std::less<Coord> Compare;
- inline static Coord entry_value(Bound const &b) { return b[D].min(); }
- inline static Coord exit_value(Bound const &b) { return b[D].max(); }
-};
+ SweepVector(std::vector<Item> const &v)
+ : _items(v)
+ {}
-template <typename T>
-struct BoundsFast {
- Rect operator()(T const &item) const {
- return item.boundsFast();
- }
+ std::vector<Item> const &items() { return _items; }
+ Interval itemBounds(ItemIterator ii) { return Interval(); }
+
+ void addActiveItem(ItemIterator ii) {}
+ void removeActiveItem(ItemIterator ii) {}
+
+private:
+ std::vector<Item> const &_items;
};
/** @brief Generic sweepline algorithm.
@@ -77,13 +69,19 @@ struct BoundsFast {
* the line starts intersecting their bounds, and removed when it completely
* passes over them.
*
- * To use this, create a derived class and reimplement the _enter()
- * and/or _leave() virtual functions, insert all the objects,
- * and finally call process(). Inside _enter() and _leave(), the items that have
- * their bounds intersected by the sweepline are available in a list called
- * _active_items. This is an intrusive linked list, so you should access it using
- * iterators. Do not add or remove items from it. You can specify the bound type
- * and how it should be accessed by defining a custom SweepTraits class.
+ * To use this, create a class that exposes the following methods:
+ * - Range items() - returns a forward iterable range of items that will be swept.
+ * - Interval itemBounds(iterator i) - given an iterator from the above range,
+ * compute the bounding interval of the referenced item in the direction of sweep.
+ * - void addActiveItem(iterator i) - add an item to the active list.
+ * - void removeActiveItem(iterator i) - remove an item from the active list.
+ *
+ * Create the object, then instantiate this template with the above class
+ * as the template parameter, pass it the constructed object of the class,
+ * and call the process() method.
+ *
+ * A good choice for the active list is a Boost intrusive list, which allows
+ * you to get an iterator from a value in constant time.
*
* Look in path.cpp for example usage.
*
@@ -92,38 +90,17 @@ struct BoundsFast {
* how to interpret them and how to sort the events
* @ingroup Utilities
*/
-template <typename Item, typename SweepTraits = IntervalSweepTraits>
+template <typename SweepSet>
class Sweeper {
public:
- /// Type of the item's boundary - usually this will be an Interval or Rect.
- typedef typename SweepTraits::Bound Bound;
-
- Sweeper() {}
-
- /** @brief Insert a single item for sweeping.
- * @param bound Boundary of the item, as defined in sweep traits
- * @param item The item itself */
- void insert(Bound const &bound, Item const &item) {
- assert(!(typename SweepTraits::Compare()(
- SweepTraits::exit_value(bound),
- SweepTraits::entry_value(bound))));
- _items.push_back(Record(bound, item));
- }
-
- /** @brief Insert a range of items using the supplied bounds functor.
- * The bounds are computed from items using the supplied bounds functor.
- * @param first Start of range
- * @param last End of range (one-past-the-end iterator)
- * @param f Bounds functor */
- template <typename Iter, typename BoundFunc>
- void insert(Iter first, Iter last, BoundFunc f = BoundFunc()) {
- for (; first != last; ++first) {
- Bound b = f(*first);
- assert(!(typename SweepTraits::Compare()(
- SweepTraits::exit_value(b),
- SweepTraits::entry_value(b))));
- _items.push_back(Record(b, *first));
- }
+ typedef typename SweepSet::ItemIterator Iter;
+
+ explicit Sweeper(SweepSet &set)
+ : _set(set)
+ {
+ std::size_t sz = std::distance(set.items().begin(), set.items().end());
+ _entry_events.reserve(sz);
+ _exit_events.reserve(sz);
}
/** @brief Process entry and exit events.
@@ -131,109 +108,70 @@ public:
* functions _enter() and _leave() according to the order of the boundaries
* of each item. */
void process() {
- if (_items.empty()) return;
+ if (_set.items().empty()) return;
+
+ Iter last = _set.items().end();
+ for (Iter i = _set.items().begin(); i != last; ++i) {
+ Interval b = _set.itemBounds(i);
+ // guard against NANs
+ assert(b.min() == b.min() && b.max() == b.max());
+ _entry_events.push_back(Event(b.max(), i));
+ _exit_events.push_back(Event(b.min(), i));
+ }
- typename SweepTraits::Compare cmp;
+ boost::make_heap(_entry_events);
+ boost::make_heap(_exit_events);
- // we store the events in heaps, which is slightly more efficient
- // than sorting them, since a heap requires linear time to construct
- for (RecordIter i = _items.begin(); i != _items.end(); ++i) {
- _entry_events.push_back(i);
- _exit_events.push_back(i);
- }
- boost::make_heap(_entry_events, _entry_heap_compare);
- boost::make_heap(_exit_events, _exit_heap_compare);
- boost::pop_heap(_entry_events, _entry_heap_compare);
- boost::pop_heap(_exit_events, _exit_heap_compare);
-
- RecordIter next_entry = _entry_events.back();
- RecordIter next_exit = _exit_events.back();
- _entry_events.pop_back();
- _exit_events.pop_back();
-
- while (next_entry != _items.end() || next_exit != _items.end()) {
- assert(next_exit != _items.end());
-
- if (next_entry == _items.end() ||
- cmp(SweepTraits::exit_value(next_exit->bound),
- SweepTraits::entry_value(next_entry->bound)))
- {
+ Event next_entry = _get_next(_entry_events);
+ Event next_exit = _get_next(_exit_events);
+
+ while (next_entry || next_exit) {
+ assert(next_exit);
+
+ if (!next_entry || next_exit > next_entry) {
// exit event - remove record from active list
- _leave(*next_exit);
- _active_items.erase(_active_items.iterator_to(*next_exit));
- if (!_exit_events.empty()) {
- boost::pop_heap(_exit_events, _exit_heap_compare);
- next_exit = _exit_events.back();
- _exit_events.pop_back();
- } else {
- next_exit = _items.end();
- // we should end the loop after this happens
- }
+ _set.removeActiveItem(next_exit.item);
+ next_exit = _get_next(_exit_events);
} else {
// entry event - add record to active list
- _enter(*next_entry);
- _active_items.push_back(*next_entry);
- if (!_entry_events.empty()) {
- boost::pop_heap(_entry_events, _entry_heap_compare);
- next_entry = _entry_events.back();
- _entry_events.pop_back();
- } else {
- next_entry = _items.end();
- }
+ _set.addActiveItem(next_entry.item);
+ next_entry = _get_next(_entry_events);
}
}
-
- assert(_active_items.empty());
}
-protected:
- /// The item and its sweepline boundary.
- struct Record {
- boost::intrusive::list_member_hook<> _hook;
- Bound bound;
- Item item;
-
- Record(Bound const &b, Item const &i)
- : bound(b), item(i)
+private:
+ struct Event
+ : boost::totally_ordered<Event>
+ {
+ Coord coord;
+ Iter item;
+
+ Event(Coord c, Iter const &i)
+ : coord(c), item(i)
+ {}
+ Event()
+ : coord(nan("")), item()
{}
+ bool operator<(Event const &other) const { return coord < other.coord; }
+ bool operator==(Event const &other) const { return coord == other.coord; }
+ operator bool() const { return !IS_NAN(coord); }
};
- typedef typename std::vector<Record>::iterator RecordIter;
-
- typedef boost::intrusive::list
- < Record
- , boost::intrusive::member_hook
- < Record
- , boost::intrusive::list_member_hook<>
- , &Record::_hook
- >
- > RecordList;
-
- /** @brief Enter an item record.
- * Override this to process an item as it is about to enter the active list.
- * When called, the passed record will not be part of the active list. */
- virtual void _enter(Record const &) {}
- /** @brief Leave an item record.
- * Override this to process an item as it is about to leave the active list.
- * When called, the passed record will be part of the active list. */
- virtual void _leave(Record const &) {}
-
- /// The list of all item records undergoing sweeping.
- std::vector<Record> _items;
- /// The list of active item records.
- RecordList _active_items;
-private:
- inline static bool _entry_heap_compare(RecordIter a, RecordIter b) {
- typename SweepTraits::Compare cmp;
- return cmp(SweepTraits::entry_value(b->bound), SweepTraits::entry_value(a->bound));
- }
- inline static bool _exit_heap_compare(RecordIter a, RecordIter b) {
- typename SweepTraits::Compare cmp;
- return cmp(SweepTraits::exit_value(b->bound), SweepTraits::exit_value(a->bound));
+ static Event _get_next(std::vector<Event> &heap) {
+ if (heap.empty()) {
+ Event e;
+ return e;
+ }
+ boost::pop_heap(heap);
+ Event ret = heap.back();
+ heap.pop_back();
+ return ret;
}
- std::vector<RecordIter> _entry_events;
- std::vector<RecordIter> _exit_events;
+ SweepSet &_set;
+ std::vector<Event> _entry_events;
+ std::vector<Event> _exit_events;
};
} // namespace Geom
diff --git a/src/2geom/utils.h b/src/2geom/utils.h
index bc0ad74b8..3e0dd4717 100644
--- a/src/2geom/utils.h
+++ b/src/2geom/utils.h
@@ -71,6 +71,30 @@ struct NullIterator
void operator=(T const &v) {}
};
+/** @brief Get the next iterator in the container with wrap-around.
+ * If the iterator would become the end iterator after incrementing,
+ * return the begin iterator instead. */
+template <typename Iter, typename Container>
+Iter cyclic_next(Iter i, Container &c) {
+ ++i;
+ if (i == c.end()) {
+ i = c.begin();
+ }
+ return i;
+}
+
+/** @brief Get the previous iterator in the container with wrap-around.
+ * If the passed iterator is the begin iterator, return the iterator
+ * just before the end iterator instead. */
+template <typename Iter, typename Container>
+Iter cyclic_prior(Iter i, Container &c) {
+ if (i == c.begin()) {
+ i = c.end();
+ }
+ --i;
+ return i;
+}
+
} // end namespace Geom
#endif // LIB2GEOM_SEEN_UTILS_H
diff --git a/src/2geom/viewbox.cpp b/src/2geom/viewbox.cpp
deleted file mode 100644
index 69bd0c487..000000000
--- a/src/2geom/viewbox.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/**
- * \file
- * \brief Convenience class for SVG viewBox handling
- *//*
- * Authors:
- * Krzysztof Kosiński <tweenk.pl@gmail.com>
- *
- * Copyright (C) 2013 Authors
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- */
-
-#include <2geom/transforms.h>
-#include <2geom/viewbox.h>
-
-namespace Geom {
-
-/** Convert an align specification to coordinate fractions. */
-Point align_factors(Align g) {
- Point p;
- switch (g) {
- case ALIGN_XMIN_YMIN:
- p[X] = 0.0;
- p[Y] = 0.0;
- break;
- case ALIGN_XMID_YMIN:
- p[X] = 0.5;
- p[Y] = 0.0;
- break;
- case ALIGN_XMAX_YMIN:
- p[X] = 1.0;
- p[Y] = 0.0;
- break;
- case ALIGN_XMIN_YMID:
- p[X] = 0.0;
- p[Y] = 0.5;
- break;
- case ALIGN_XMID_YMID:
- p[X] = 0.5;
- p[Y] = 0.5;
- break;
- case ALIGN_XMAX_YMID:
- p[X] = 1.0;
- p[Y] = 0.5;
- break;
- case ALIGN_XMIN_YMAX:
- p[X] = 0.0;
- p[Y] = 1.0;
- break;
- case ALIGN_XMID_YMAX:
- p[X] = 0.5;
- p[Y] = 1.0;
- break;
- case ALIGN_XMAX_YMAX:
- p[X] = 1.0;
- p[Y] = 1.0;
- break;
- default:
- break;
- }
- return p;
-}
-
-/** Obtain transformation from the viewbox to the specified viewport. */
-Affine ViewBox::transformTo(Geom::Rect const &viewport) const
-{
- if (!_box) {
- return Geom::Affine::identity();
- }
-
- // 1. translate viewbox to origin
- Geom::Affine total = Translate(-_box->min());
-
- // 2. compute scale
- Geom::Point vdims = viewport.dimensions();
- Geom::Point bdims = _box->dimensions();
- Geom::Scale scale(vdims[X] / bdims[X], vdims[Y] / bdims[Y]);
-
- if (_align == ALIGN_NONE) {
- // apply non-uniform scale
- // = Scale(_box->dimensions()).inverse() * Scale(viewport.dimensions())
- total *= scale * Translate(viewport.min());
- } else {
- double uscale = 0;
- if (_expansion == EXPANSION_MEET) {
- uscale = std::min(scale[X], scale[Y]);
- } else {
- uscale = std::max(scale[X], scale[Y]);
- }
- scale = Scale(uscale);
-
- // compute offset for align
- Geom::Point offset = bdims * scale - vdims;
- offset *= Scale(align_factors(_align));
- total *= Translate(-offset);
- }
-
- return total;
-}
-
-} // namespace Geom
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/viewbox.h b/src/2geom/viewbox.h
deleted file mode 100644
index 81f59ee36..000000000
--- a/src/2geom/viewbox.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/**
- * \file
- * \brief Convenience class for SVG viewBox handling
- *//*
- * Authors:
- * Krzysztof Kosiński <tweenk.pl@gmail.com>
- *
- * Copyright (C) 2013 Authors
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- */
-
-#ifndef LIB2GEOM_SEEN_VIEWBOX_H
-#define LIB2GEOM_SEEN_VIEWBOX_H
-
-#include <2geom/affine.h>
-#include <2geom/rect.h>
-
-namespace Geom {
-
-/** Values for the <align> parameter of preserveAspectRatio.
- * See: http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute */
-enum Align {
- ALIGN_NONE,
- ALIGN_XMIN_YMIN,
- ALIGN_XMID_YMIN,
- ALIGN_XMAX_YMIN,
- ALIGN_XMIN_YMID,
- ALIGN_XMID_YMID,
- ALIGN_XMAX_YMID,
- ALIGN_XMIN_YMAX,
- ALIGN_XMID_YMAX,
- ALIGN_XMAX_YMAX
-};
-
-/** Values for the <meetOrSlice> parameter of preserveAspectRatio.
- * See: http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute */
-enum Expansion {
- EXPANSION_MEET,
- EXPANSION_SLICE
-};
-
-Point align_factors(Align align);
-
-class ViewBox {
- OptRect _box;
- Align _align;
- Expansion _expansion;
-
-public:
- explicit ViewBox(OptRect const &r = OptRect(), Align a = ALIGN_XMID_YMID, Expansion ex = EXPANSION_MEET)
- : _box(r)
- , _align(a)
- , _expansion(ex)
- {}
-
- void setBox(OptRect const &r) { _box = r; }
- void setAlign(Align a) { _align = a; }
- void setExpansion(Expansion ex) { _expansion = ex; }
- OptRect const &box() const { return _box; }
- Align align() const { return _align; }
- Expansion expansion() const { return _expansion; }
-
- /** Obtain transformation from the viewbox to the specified viewport. */
- Affine transformTo(Geom::Rect const &viewport) const;
-};
-
-} // namespace Geom
-
-#endif // !LIB2GEOM_SEEN_VIEWBOX_H
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/display/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp
index 1794ccbab..14f36376f 100644
--- a/src/display/canvas-axonomgrid.cpp
+++ b/src/display/canvas-axonomgrid.cpp
@@ -172,9 +172,9 @@ CanvasAxonomGrid::CanvasAxonomGrid (SPNamedView * nv, Inkscape::XML::Node * in_r
angle_deg[Z] = prefs->getDouble("/options/grids/axonom/angle_z", 30.0);
angle_deg[Y] = 0;
- angle_rad[X] = Geom::deg_to_rad(angle_deg[X]);
+ angle_rad[X] = Geom::rad_from_deg(angle_deg[X]);
tan_angle[X] = tan(angle_rad[X]);
- angle_rad[Z] = Geom::deg_to_rad(angle_deg[Z]);
+ angle_rad[Z] = Geom::rad_from_deg(angle_deg[Z]);
tan_angle[Z] = tan(angle_rad[Z]);
snapper = new CanvasAxonomGridSnapper(this, &namedview->snap_manager, 0);
@@ -272,7 +272,7 @@ CanvasAxonomGrid::readRepr()
angle_deg[X] = g_ascii_strtod(value, NULL);
if (angle_deg[X] < 0.) angle_deg[X] = 0.;
if (angle_deg[X] > 89.0) angle_deg[X] = 89.0;
- angle_rad[X] = Geom::deg_to_rad(angle_deg[X]);
+ angle_rad[X] = Geom::rad_from_deg(angle_deg[X]);
tan_angle[X] = tan(angle_rad[X]);
}
@@ -280,7 +280,7 @@ CanvasAxonomGrid::readRepr()
angle_deg[Z] = g_ascii_strtod(value, NULL);
if (angle_deg[Z] < 0.) angle_deg[Z] = 0.;
if (angle_deg[Z] > 89.0) angle_deg[Z] = 89.0;
- angle_rad[Z] = Geom::deg_to_rad(angle_deg[Z]);
+ angle_rad[Z] = Geom::rad_from_deg(angle_deg[Z]);
tan_angle[Z] = tan(angle_rad[Z]);
}
diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp
index dec5b1f57..8591be7eb 100644
--- a/src/display/nr-filter.cpp
+++ b/src/display/nr-filter.cpp
@@ -200,7 +200,7 @@ void Filter::area_enlarge(Geom::IntRect &bbox, Inkscape::DrawingItem const *item
Geom::Rect item_bbox;
Geom::OptRect maybe_bbox = item->itemBounds();
- if (maybe_bbox.isEmpty()) {
+ if (maybe_bbox.empty()) {
// Code below needs a bounding box
return;
}
diff --git a/src/gradient-chemistry.cpp b/src/gradient-chemistry.cpp
index 9f2d030d4..7b4c0ac20 100644
--- a/src/gradient-chemistry.cpp
+++ b/src/gradient-chemistry.cpp
@@ -379,7 +379,7 @@ SPGradient *sp_gradient_reset_to_userspace(SPGradient *gr, SPItem *item)
if (angle != 0.0) {
- Geom::Line grl(center, Geom::deg_to_rad(angle));
+ Geom::Line grl(center, Geom::rad_from_deg(angle));
Geom::LineSegment bbl1(bbox->corner(0), bbox->corner(1));
Geom::LineSegment bbl2(bbox->corner(1), bbox->corner(2));
Geom::LineSegment bbl3(bbox->corner(2), bbox->corner(3));
diff --git a/src/live_effects/lpe-bendpath.cpp b/src/live_effects/lpe-bendpath.cpp
index 874e23c4c..d7c0b69a4 100644
--- a/src/live_effects/lpe-bendpath.cpp
+++ b/src/live_effects/lpe-bendpath.cpp
@@ -199,7 +199,7 @@ KnotHolderEntityWidthBendPath::knot_set(Geom::Point const &p, Geom::Point const&
if (cubic) {
ray.setPoints(ptA, (*cubic)[1]);
}
- ray.setAngle(ray.angle() + Geom::deg_to_rad(90));
+ ray.setAngle(ray.angle() + Geom::rad_from_deg(90));
Geom::Point knot_pos = this->knot->pos * item->i2dt_affine().inverse();
Geom::Coord nearest_to_ray = ray.nearestTime(knot_pos);
if(nearest_to_ray == 0){
@@ -225,7 +225,7 @@ KnotHolderEntityWidthBendPath::knot_get() const
if (cubic) {
ray.setPoints(ptA,(*cubic)[1]);
}
- ray.setAngle(ray.angle() + Geom::deg_to_rad(90));
+ ray.setAngle(ray.angle() + Geom::rad_from_deg(90));
Geom::Point result_point = Geom::Point::polar(ray.angle(), (lpe->original_height/2.0) * lpe->prop_scale) + ptA;
bp_helper_path.clear();
diff --git a/src/live_effects/lpe-copy_rotate.cpp b/src/live_effects/lpe-copy_rotate.cpp
index fd9b853d5..87d8f05a9 100644
--- a/src/live_effects/lpe-copy_rotate.cpp
+++ b/src/live_effects/lpe-copy_rotate.cpp
@@ -17,7 +17,6 @@
#include "live_effects/lpe-copy_rotate.h"
#include <2geom/path.h>
#include <2geom/transforms.h>
-#include <2geom/d2-sbasis.h>
#include <2geom/angle.h>
#include "knot-holder-entity.h"
@@ -107,8 +106,8 @@ LPECopyRotate::doBeforeEffect (SPLPEItem const* lpeitem)
dir = unit_vector(B - A);
// I first suspected the minus sign to be a bug in 2geom but it is
// likely due to SVG's choice of coordinate system orientation (max)
- start_pos = origin + dir * Rotate(-deg_to_rad(starting_angle)) * dist_angle_handle;
- rot_pos = origin + dir * Rotate(-deg_to_rad(rotation_angle+starting_angle)) * dist_angle_handle;
+ start_pos = origin + dir * Rotate(-rad_from_deg(starting_angle)) * dist_angle_handle;
+ rot_pos = origin + dir * Rotate(-rad_from_deg(rotation_angle+starting_angle)) * dist_angle_handle;
if(copiesTo360 ){
rot_pos = origin;
}
@@ -129,9 +128,9 @@ LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & p
}
Piecewise<D2<SBasis> > output;
- Affine pre = Translate(-origin) * Rotate(-deg_to_rad(starting_angle));
+ Affine pre = Translate(-origin) * Rotate(-rad_from_deg(starting_angle));
for (int i = 0; i < num_copies; ++i) {
- Rotate rot(-deg_to_rad(rotation_angle * i));
+ Rotate rot(-rad_from_deg(rotation_angle * i));
Affine t = pre * rot * Translate(origin);
output.concat(pwd2_in * t);
}
@@ -146,7 +145,7 @@ LPECopyRotate::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geo
Geom::Path hp;
hp.start(start_pos);
hp.appendNew<Geom::LineSegment>((Geom::Point)origin);
- hp.appendNew<Geom::LineSegment>(origin + dir * Rotate(-deg_to_rad(rotation_angle+starting_angle)) * dist_angle_handle);
+ hp.appendNew<Geom::LineSegment>(origin + dir * Rotate(-rad_from_deg(rotation_angle+starting_angle)) * dist_angle_handle);
Geom::PathVector pathv;
pathv.push_back(hp);
hp_vec.push_back(pathv);
@@ -188,7 +187,7 @@ KnotHolderEntityStartingAngle::knot_set(Geom::Point const &p, Geom::Point const
// I first suspected the minus sign to be a bug in 2geom but it is
// likely due to SVG's choice of coordinate system orientation (max)
- lpe->starting_angle.param_set_value(rad_to_deg(-angle_between(lpe->dir, s - lpe->origin)));
+ lpe->starting_angle.param_set_value(deg_from_rad(-angle_between(lpe->dir, s - lpe->origin)));
if (state & GDK_SHIFT_MASK) {
lpe->dist_angle_handle = L2(lpe->B - lpe->A);
} else {
@@ -208,7 +207,7 @@ KnotHolderEntityRotationAngle::knot_set(Geom::Point const &p, Geom::Point const
// I first suspected the minus sign to be a bug in 2geom but it is
// likely due to SVG's choice of coordinate system orientation (max)
- lpe->rotation_angle.param_set_value(rad_to_deg(-angle_between(lpe->dir, s - lpe->origin)) - lpe->starting_angle);
+ lpe->rotation_angle.param_set_value(deg_from_rad(-angle_between(lpe->dir, s - lpe->origin)) - lpe->starting_angle);
if (state & GDK_SHIFT_MASK) {
lpe->dist_angle_handle = L2(lpe->B - lpe->A);
} else {
diff --git a/src/live_effects/lpe-dynastroke.cpp b/src/live_effects/lpe-dynastroke.cpp
index c60db4fc4..aeecd5d5c 100644
--- a/src/live_effects/lpe-dynastroke.cpp
+++ b/src/live_effects/lpe-dynastroke.cpp
@@ -20,7 +20,6 @@
#include <2geom/bezier-to-sbasis.h>
#include <2geom/sbasis-to-bezier.h>
#include <2geom/d2.h>
-#include <2geom/d2-sbasis.h>
#include <2geom/sbasis-math.h>
#include <2geom/piecewise.h>
diff --git a/src/live_effects/lpe-interpolate.cpp b/src/live_effects/lpe-interpolate.cpp
index b1ad07d23..74c7efd90 100644
--- a/src/live_effects/lpe-interpolate.cpp
+++ b/src/live_effects/lpe-interpolate.cpp
@@ -16,7 +16,6 @@
#include <2geom/path.h>
#include <2geom/sbasis-to-bezier.h>
-#include <2geom/d2-sbasis.h>
#include <2geom/piecewise.h>
#include <2geom/sbasis-geometric.h>
diff --git a/src/live_effects/lpe-knot.cpp b/src/live_effects/lpe-knot.cpp
index bf84e645d..a033a6c4a 100644
--- a/src/live_effects/lpe-knot.cpp
+++ b/src/live_effects/lpe-knot.cpp
@@ -27,7 +27,6 @@
#include <2geom/sbasis-to-bezier.h>
#include <2geom/sbasis.h>
#include <2geom/d2.h>
-#include <2geom/d2-sbasis.h>
#include <2geom/path.h>
#include <2geom/bezier-to-sbasis.h>
#include <2geom/basic-intersection.h>
diff --git a/src/live_effects/lpe-patternalongpath.cpp b/src/live_effects/lpe-patternalongpath.cpp
index 706091be9..9397d848f 100644
--- a/src/live_effects/lpe-patternalongpath.cpp
+++ b/src/live_effects/lpe-patternalongpath.cpp
@@ -298,7 +298,7 @@ KnotHolderEntityWidthPatternAlongPath::knot_set(Geom::Point const &p, Geom::Poin
if (cubic) {
ray.setPoints(ptA, (*cubic)[1]);
}
- ray.setAngle(ray.angle() + Geom::deg_to_rad(90));
+ ray.setAngle(ray.angle() + Geom::rad_from_deg(90));
Geom::Point knot_pos = this->knot->pos * item->i2dt_affine().inverse();
Geom::Coord nearest_to_ray = ray.nearestTime(knot_pos);
if(nearest_to_ray == 0){
@@ -327,7 +327,7 @@ KnotHolderEntityWidthPatternAlongPath::knot_get() const
if (cubic) {
ray.setPoints(ptA, (*cubic)[1]);
}
- ray.setAngle(ray.angle() + Geom::deg_to_rad(90));
+ ray.setAngle(ray.angle() + Geom::rad_from_deg(90));
Geom::Point result_point = Geom::Point::polar(ray.angle(), (lpe->original_height/2.0) * lpe->prop_scale) + ptA;
pap_helper_path.clear();
diff --git a/src/live_effects/lpe-simplify.cpp b/src/live_effects/lpe-simplify.cpp
index a919756df..4b62c6c65 100644
--- a/src/live_effects/lpe-simplify.cpp
+++ b/src/live_effects/lpe-simplify.cpp
@@ -220,8 +220,8 @@ LPESimplify::generateHelperPathAndSmooth(Geom::PathVector &result)
}
Geom::Ray ray1(point_at2, point_at3);
Geom::Ray ray2(point_at3, point_at4);
- double angle1 = Geom::rad_to_deg(ray1.angle());
- double angle2 = Geom::rad_to_deg(ray2.angle());
+ double angle1 = Geom::deg_from_rad(ray1.angle());
+ double angle2 = Geom::deg_from_rad(ray2.angle());
if((smooth_angles >= std::abs(angle2 - angle1)) && !are_near(point_at4,point_at3) && !are_near(point_at2,point_at3)) {
double dist = Geom::distance(point_at2,point_at3);
Geom::Angle angleFixed = ray2.angle();
diff --git a/src/live_effects/lpe-transform_2pts.cpp b/src/live_effects/lpe-transform_2pts.cpp
index f2b756567..dd1a29689 100644
--- a/src/live_effects/lpe-transform_2pts.cpp
+++ b/src/live_effects/lpe-transform_2pts.cpp
@@ -46,7 +46,7 @@ LPETransform2Pts::LPETransform2Pts(LivePathEffectObject *lpeobject) :
point_b(Geom::Point()),
pathvector(),
append_path(false),
- previous_angle(Geom::deg_to_rad(0)),
+ previous_angle(Geom::rad_from_deg(0)),
previous_start(Geom::Point()),
previous_lenght(-1)
{
@@ -151,10 +151,10 @@ LPETransform2Pts::doBeforeEffect (SPLPEItem const* lpeitem)
}
if(lock_lenght && !lock_angle && previous_lenght != -1) {
Geom::Ray transformed((Geom::Point)start,(Geom::Point)end);
- if(previous_start == start || previous_angle == Geom::deg_to_rad(0)) {
+ if(previous_start == start || previous_angle == Geom::rad_from_deg(0)) {
previous_angle = transformed.angle();
}
- } else if(lock_angle && !lock_lenght && previous_angle != Geom::deg_to_rad(0)) {
+ } else if(lock_angle && !lock_lenght && previous_angle != Geom::rad_from_deg(0)) {
if(previous_start == start){
previous_lenght = Geom::distance((Geom::Point)start, (Geom::Point)end);
}
@@ -406,7 +406,7 @@ LPETransform2Pts::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const
trans = (Geom::Point)end - helper.initialPoint();
}
if(offset != 0){
- trans = Geom::Point::polar(transformed.angle() + Geom::deg_to_rad(-90),offset) + trans;
+ trans = Geom::Point::polar(transformed.angle() + Geom::rad_from_deg(-90),offset) + trans;
}
m *= Geom::Translate(trans);
diff --git a/src/live_effects/parameter/filletchamferpointarray.cpp b/src/live_effects/parameter/filletchamferpointarray.cpp
index 399307502..117d57460 100644
--- a/src/live_effects/parameter/filletchamferpointarray.cpp
+++ b/src/live_effects/parameter/filletchamferpointarray.cpp
@@ -392,7 +392,7 @@ void FilletChamferPointArrayParam::updateCanvasIndicators()
Geom::PathVector pathv = sp_svg_read_pathv(svgd);
Geom::Affine aff = Geom::Affine();
aff *= Geom::Scale(helper_size);
- aff *= Geom::Rotate(ray1.angle() - deg_to_rad(270));
+ aff *= Geom::Rotate(ray1.angle() - rad_from_deg(270));
aff *= Geom::Translate(last_pwd2[i].valueAt(Xvalue));
pathv *= aff;
hp.push_back(pathv[0]);
diff --git a/src/selection.cpp b/src/selection.cpp
index 020912381..6fc426be7 100644
--- a/src/selection.cpp
+++ b/src/selection.cpp
@@ -374,7 +374,7 @@ SPItem *Selection::_sizeistItem(bool sml, Selection::CompareSize compare) {
for ( std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end(); ++i) {
Geom::OptRect obox = SP_ITEM(*i)->desktopPreferredBounds();
- if (!obox || obox.isEmpty()) continue;
+ if (!obox || obox.empty()) continue;
Geom::Rect bbox = *obox;
gdouble size = compare == 2 ? bbox.area() :
diff --git a/src/seltrans.cpp b/src/seltrans.cpp
index f010a687d..b54525610 100644
--- a/src/seltrans.cpp
+++ b/src/seltrans.cpp
@@ -1195,7 +1195,7 @@ gboolean Inkscape::SelTrans::skewRequest(SPSelTransHandle const &handle, Geom::P
}
// Update the status text
- double degrees = mod360symm(Geom::rad_to_deg(radians));
+ double degrees = mod360symm(Geom::deg_from_rad(radians));
_message_context.setF(Inkscape::IMMEDIATE_MESSAGE,
// TRANSLATORS: don't modify the first ";"
// (it will NOT be displayed as ";" - only the second one will be)
@@ -1271,7 +1271,7 @@ gboolean Inkscape::SelTrans::rotateRequest(Geom::Point &pt, guint state)
pt = _point * Geom::Translate(-_origin) * _relative_affine * Geom::Translate(_origin);
// Update the status text
- double degrees = mod360symm(Geom::rad_to_deg(radians));
+ double degrees = mod360symm(Geom::deg_from_rad(radians));
_message_context.setF(Inkscape::IMMEDIATE_MESSAGE,
// TRANSLATORS: don't modify the first ";"
// (it will NOT be displayed as ";" - only the second one will be)
diff --git a/src/sp-guide.cpp b/src/sp-guide.cpp
index d1e101fd5..17a1a9ff1 100644
--- a/src/sp-guide.cpp
+++ b/src/sp-guide.cpp
@@ -501,7 +501,7 @@ char* SPGuide::description(bool const verbose) const
descr = g_strdup_printf(_("horizontal, at %s"), position_string_y->str);
} else {
double const radians = this->angle();
- double const degrees = Geom::rad_to_deg(radians);
+ double const degrees = Geom::deg_from_rad(radians);
int const degrees_int = (int) round(degrees);
descr = g_strdup_printf(_("at %d degrees, through (%s,%s)"),
degrees_int, position_string_x->str, position_string_y->str);
diff --git a/src/svg/svg-affine.cpp b/src/svg/svg-affine.cpp
index c853f9930..d9d79bba5 100644
--- a/src/svg/svg-affine.cpp
+++ b/src/svg/svg-affine.cpp
@@ -119,7 +119,7 @@ sp_svg_transform_read(gchar const *str, Geom::Affine *transform)
if (n_args != 1 && n_args != 3) {
return false;
}
- Geom::Rotate const rot(Geom::deg_to_rad(args[0]));
+ Geom::Rotate const rot(Geom::rad_from_deg(args[0]));
if (n_args == 3) {
a = ( Geom::Translate(-args[1], -args[2])
* rot
diff --git a/src/svg/svg-path.cpp b/src/svg/svg-path.cpp
index ba9e11452..7bb58fc9c 100644
--- a/src/svg/svg-path.cpp
+++ b/src/svg/svg-path.cpp
@@ -84,7 +84,7 @@ static void sp_svg_write_curve(Inkscape::SVG::PathString & str, Geom::Curve cons
}
else if(Geom::EllipticalArc const *elliptical_arc = dynamic_cast<Geom::EllipticalArc const *>(c)) {
str.arcTo( elliptical_arc->ray(Geom::X), elliptical_arc->ray(Geom::Y),
- Geom::rad_to_deg(elliptical_arc->rotationAngle()),
+ Geom::deg_from_rad(elliptical_arc->rotationAngle()),
elliptical_arc->largeArc(), elliptical_arc->sweep(),
elliptical_arc->finalPoint() );
} else {
diff --git a/src/ui/dialog/guides.cpp b/src/ui/dialog/guides.cpp
index e0efcde36..556d77a28 100644
--- a/src/ui/dialog/guides.cpp
+++ b/src/ui/dialog/guides.cpp
@@ -98,7 +98,7 @@ void GuidelinePropertiesDialog::_onOK()
} else if ( deg_angle == 0. || deg_angle == 180. || deg_angle == -180.) {
normal = Geom::Point(0.,1.);
} else {
- double rad_angle = Geom::deg_to_rad( deg_angle );
+ double rad_angle = Geom::rad_from_deg( deg_angle );
normal = Geom::rot90(Geom::Point::polar(rad_angle, 1.0));
}
//To allow reposition from dialog
@@ -326,7 +326,7 @@ void GuidelinePropertiesDialog::_setup() {
} else if (_guide->isHorizontal()) {
_oldangle = 0;
} else {
- _oldangle = Geom::rad_to_deg( std::atan2( - _guide->getNormal()[Geom::X], _guide->getNormal()[Geom::Y] ) );
+ _oldangle = Geom::deg_from_rad( std::atan2( - _guide->getNormal()[Geom::X], _guide->getNormal()[Geom::Y] ) );
}
{
diff --git a/src/ui/tools/measure-tool.cpp b/src/ui/tools/measure-tool.cpp
index 813b064aa..3d194191a 100644
--- a/src/ui/tools/measure-tool.cpp
+++ b/src/ui/tools/measure-tool.cpp
@@ -747,9 +747,9 @@ void MeasureTool::toGuides()
}
}
setGuide(start,0,"");
- setGuide(start,Geom::deg_to_rad(90),_("Start"));
+ setGuide(start,Geom::rad_from_deg(90),_("Start"));
setGuide(end,0,_("End"));
- setGuide(end,Geom::deg_to_rad(90),"");
+ setGuide(end,Geom::rad_from_deg(90),"");
showCanvasItems(true);
doc->ensureUpToDate();
DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_MEASURE,_("Add guides from measure tool"));
@@ -808,9 +808,9 @@ void MeasureTool::toMarkDimension()
Geom::Point start = start_p + Geom::Point::polar(ray.angle(), 5);
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
dimension_offset = prefs->getDouble("/tools/measure/offset", 5.0);
- start = start + Geom::Point::polar(ray.angle() + Geom::deg_to_rad(90), -dimension_offset);
+ start = start + Geom::Point::polar(ray.angle() + Geom::rad_from_deg(90), -dimension_offset);
Geom::Point end = end_p + Geom::Point::polar(ray.angle(), -5);
- end = end+ Geom::Point::polar(ray.angle() + Geom::deg_to_rad(90), -dimension_offset);
+ end = end+ Geom::Point::polar(ray.angle() + Geom::rad_from_deg(90), -dimension_offset);
guint32 color = 0x000000ff;
setLine(start, end, true, color);
Glib::ustring unit_name = prefs->getString("/tools/measure/unit");
@@ -827,7 +827,7 @@ void MeasureTool::toMarkDimension()
totallengthval = Inkscape::Util::Quantity::convert(totallengthval, "px", unit_name);
double scale = prefs->getDouble("/tools/measure/scale", 100.0) / 100.0;
gchar *totallength_str = g_strdup_printf(precision_str.str().c_str(), totallengthval * scale, unit_name.c_str());
- setLabelText(totallength_str, middle, fontsize, Geom::deg_to_rad(180) - ray.angle(), color);
+ setLabelText(totallength_str, middle, fontsize, Geom::rad_from_deg(180) - ray.angle(), color);
g_free(totallength_str);
doc->ensureUpToDate();
DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_MEASURE,_("Add global measure line"));
@@ -967,7 +967,7 @@ void MeasureTool::setLabelText(const char *value, Geom::Point pos, double fontsi
if (!measure_repr && bbox) {
Geom::Point center = bbox->midpoint();
text_item->transform *= Geom::Translate(center).inverse();
- pos += Geom::Point::polar(angle+ Geom::deg_to_rad(90), -bbox->height());
+ pos += Geom::Point::polar(angle+ Geom::rad_from_deg(90), -bbox->height());
}
if(measure_repr) {
/* Create <group> */
@@ -1244,7 +1244,7 @@ void MeasureTool::showCanvasItems(bool to_guides, bool to_item, bool to_phantom,
start_p, end_p,
fontsize);
{
- setMeasureCanvasText(true, precision, Geom::rad_to_deg(angle), fontsize, unit_name, angleDisplayPt, 0x337f337f, TEXT_ANCHOR_CENTER, to_item, to_phantom, measure_repr);
+ setMeasureCanvasText(true, precision, Geom::deg_from_rad(angle), fontsize, unit_name, angleDisplayPt, 0x337f337f, TEXT_ANCHOR_CENTER, to_item, to_phantom, measure_repr);
}
{
@@ -1277,9 +1277,9 @@ void MeasureTool::showCanvasItems(bool to_guides, bool to_item, bool to_phantom,
cross_number= g_strdup_printf(_("Crossing %u"), idx + 1);
}
if (!prefs->getBool("/tools/measure/ignore_1st_and_last", true) && idx == 0) {
- setGuide(desktop->doc2dt(intersections[idx]), angle + Geom::deg_to_rad(90), "");
+ setGuide(desktop->doc2dt(intersections[idx]), angle + Geom::rad_from_deg(90), "");
} else {
- setGuide(desktop->doc2dt(intersections[idx]), angle + Geom::deg_to_rad(90), cross_number);
+ setGuide(desktop->doc2dt(intersections[idx]), angle + Geom::rad_from_deg(90), cross_number);
}
g_free(cross_number);
}
diff --git a/src/ui/tools/node-tool.cpp b/src/ui/tools/node-tool.cpp
index cceaca7cb..4149403ea 100644
--- a/src/ui/tools/node-tool.cpp
+++ b/src/ui/tools/node-tool.cpp
@@ -495,7 +495,7 @@ bool NodeTool::root_handler(GdkEvent* event) {
// We will show a pre-snap indication for when the user adds a node through double-clicking
// Adding a node will only work when a path has been selected; if that's not the case then snapping is useless
- if (not this->desktop->selection->isEmpty()) {
+ if (!this->desktop->selection->isEmpty()) {
if (!(event->motion.state & GDK_SHIFT_MASK)) {
m.setup(this->desktop);
Inkscape::SnapCandidatePoint scp(motion_dt, Inkscape::SNAPSOURCE_OTHER_HANDLE);
@@ -678,7 +678,7 @@ void NodeTool::update_tip(GdkEvent *event) {
}
}
g_assert(positions.size() == 2);
- const double angle = Geom::rad_to_deg(Geom::Line(positions[0], positions[1]).angle());
+ const double angle = Geom::deg_from_rad(Geom::Line(positions[0], positions[1]).angle());
nodestring = g_strdup_printf("<b>%u of %u</b> nodes selected, angle: %.2f°.", sz, total, angle);
}
else {