summaryrefslogtreecommitdiffstats
path: root/src/2geom
diff options
context:
space:
mode:
authorJohan B. C. Engelen <jbc.engelen@swissonline.ch>2011-02-13 20:06:36 +0000
committerJohan Engelen <goejendaagh@zonnet.nl>2011-02-13 20:06:36 +0000
commitc8ec8ac8c5a3665adf39b1a939404027395ace0e (patch)
tree9c53b646d227da2a44460fb13d6016843049f7f7 /src/2geom
parentcalligraphy tool does not need to know the type of the end curves to add the ... (diff)
downloadinkscape-c8ec8ac8c5a3665adf39b1a939404027395ace0e.tar.gz
inkscape-c8ec8ac8c5a3665adf39b1a939404027395ace0e.zip
update to latest 2geom. fixes bezier curve casts.
(bzr r10050)
Diffstat (limited to 'src/2geom')
-rw-r--r--src/2geom/bezier-curve.cpp123
-rw-r--r--src/2geom/bezier-curve.h257
-rw-r--r--src/2geom/forward.h5
-rw-r--r--src/2geom/numeric/fitting-model.h17
4 files changed, 169 insertions, 233 deletions
diff --git a/src/2geom/bezier-curve.cpp b/src/2geom/bezier-curve.cpp
index 1a2e079bf..bde0e3ef1 100644
--- a/src/2geom/bezier-curve.cpp
+++ b/src/2geom/bezier-curve.cpp
@@ -41,7 +41,7 @@ namespace Geom
/**
* @class BezierCurve
- * @brief Two-dimensional Bezier curve of arbitrary order.
+ * @brief Two-dimensional Bezier curve of arbitrary order. (this is an abstract class)
*
* Bezier curves are an expansion of the concept of linear interpolation to n points.
* Linear segments in 2Geom are in fact Bezier curves of order 1.
@@ -85,69 +85,25 @@ namespace Geom
* Every bezier curve is contained in its control polygon (the convex polygon composed
* of its control points). This fact is useful for sweepline algorithms and intersection.
*
- * Bezier curves of order 1, 2 and 3 are common enough to have their own more specific subclasses.
- * Note that if you create a generic BezierCurve, you cannot use a dynamic cast to those more
- * specific types, and you might face a slight preformance penalty relative to the more specific
- * classes. To obtain an instance of the correct optimized type, use the optimize()
- * or duplicate() methods. The difference is that optimize() will not create a new object
- * if it can't be optimized or is already an instance of one of the specific types, while
- * duplicate() will always create a new object.
+ * Bezier curves of order 1, 2 and 3 are common enough to have their own more specific subclasses:
+ * LineSegment, QuadraticBezier, and CubicBezier.
+ * Note that you cannot create a generic BezierCurve, you can only create a BezierCurve of a
+ * specific order, by creating a BezierCurveN.
*
+ * @relates BezierCurveN
* @ingroup Curves
*/
-/** @brief Create an optimized instance of the curve.
- * If the curve was created as a generic Bezier curve but has an order between 1 and 3,
- * this method will create a newly allocated curve that is a LineSegment, a QuadraticBezier,
- * or a CubicBezier. For other cases it returns the pointer to the current curve, to avoid
- * allocating extra memory. Be careful when you use this method, because whether you have
- * to delete the returned object or not depends on its return value! If easier deletion
- * semantics are required, you can use the duplicate() method instead, which returns
- * optimized objects by default.
- * @return Pointer to a curve that is an instance of LineSegment, QuadraticBezier
- * or CubicBezier based on the order. If the object is already an instance
- * of te more specific types or the order is above 3, a pointer to the original
- * object is returned instead, and no memory is allocated. */
-BezierCurve *BezierCurve::optimize() const
-{
- switch(order()) {
- case 1:
- if (!dynamic_cast<LineSegment const *>(this))
- return new LineSegment((*this)[0], (*this)[1]);
- break;
- case 2:
- if (!dynamic_cast<QuadraticBezier const *>(this))
- return new QuadraticBezier((*this)[0], (*this)[1], (*this)[2]);
- break;
- case 3:
- if (!dynamic_cast<CubicBezier const *>(this))
- return new CubicBezier((*this)[0], (*this)[1], (*this)[2], (*this)[3]);
- break;
- }
- return const_cast<BezierCurve*>(this);
-}
-Curve *BezierCurve::duplicate() const
-{
- switch(order()) {
- case 1:
- return new LineSegment((*this)[0], (*this)[1]);
- case 2:
- return new QuadraticBezier((*this)[0], (*this)[1], (*this)[2]);
- case 3:
- return new CubicBezier((*this)[0], (*this)[1], (*this)[2], (*this)[3]);
- }
- return new BezierCurve(*this);
-}
-
-Curve *BezierCurve::derivative() const
-{
- if (order() == 1) {
- double dx = inner[X][1] - inner[X][0], dy = inner[Y][1] - inner[Y][0];
- return new LineSegment(Point(dx,dy),Point(dx,dy));
- }
- return new BezierCurve(Geom::derivative(inner[X]), Geom::derivative(inner[Y]));
-}
+ /**
+ * @class BezierCurveN
+ * @tparam degree unsigned value indicating the order of the bezier curve
+ * @brief Two-dimensional Bezier curve of specific order.
+ *
+ * @relates BezierCurve
+ * @ingroup Curves
+ */
+
Coord BezierCurve::length(Coord tolerance) const
{
switch (order())
@@ -171,55 +127,6 @@ Coord BezierCurve::length(Coord tolerance) const
}
}
-/**
- * @class LineSegment
- * @brief Linear segment, a special case of a Bezier curve.
- *
- * This class uses some optimizations for a linear segment (a Bezier curve of order 1).
- * Note that if you created a BezierCurve, you will not be able to cast it to a LineSegment
- * using a dynamic cast regardless of its order - use the optimize() method.
- *
- * @ingroup Curves */
-Coord LineSegment::nearestPoint(Point const& p, Coord from, Coord to) const {
- if ( from > to ) std::swap(from, to);
- Point ip = pointAt(from);
- Point fp = pointAt(to);
- Point v = fp - ip;
- Coord l2v = L2sq(v);
- if (l2v == 0) return 0;
- Coord t = dot( p - ip, v ) / l2v;
- if ( t <= 0 ) return from;
- else if ( t >= 1 ) return to;
- else return from + t*(to-from);
-}
-
-
-/**
- * @class QuadraticBezier
- * @brief Quadratic Bezier curve.
- * Note that if you created a BezierCurve, you will not be able to cast it to a QuadraticBezier
- * using a dynamic cast regardless of its order - use the optimize() method.
- * @ingroup Curves */
-
-Coord QuadraticBezier::length(Coord tolerance) const
-{
- std::vector<Point> pts = points();
- return bezier_length(pts[0], pts[1], pts[2], tolerance);
-}
-
-/**
- * @class CubicBezier
- * @brief Cubic Bezier curve.
- * Note that if you created a BezierCurve, you will not be able to cast it to a CubicBezier
- * using a dynamic cast regardless of its order - use the optimize() method.
- * @ingroup Curves */
-
-Coord CubicBezier::length(Coord tolerance) const
-{
- std::vector<Point> pts = points();
- return bezier_length(pts[0], pts[1], pts[2], pts[3], tolerance);
-}
-
static Coord bezier_length_internal(std::vector<Point> &v1, Coord tolerance)
{
/* The Bezier length algorithm used in 2Geom utilizes a simple fact:
diff --git a/src/2geom/bezier-curve.h b/src/2geom/bezier-curve.h
index 1014c79f0..40da6f366 100644
--- a/src/2geom/bezier-curve.h
+++ b/src/2geom/bezier-curve.h
@@ -49,57 +49,7 @@ protected:
D2<Bezier> inner;
public:
- /// @name Construct the curve
- /// @{
- /** @brief Construct a Bezier curve of the specified order with all points zero. */
- explicit BezierCurve(unsigned order)
- : inner(Bezier::Order(order), Bezier::Order(order))
- {}
- /** @brief Construct from 2D Bezier polynomial. */
- explicit BezierCurve(D2<Bezier > const &x)
- : inner(x)
- {
- if (inner[X].order() != inner[Y].order())
- THROW_LOGICALERROR("Beziers used to construct a BezierCurve "
- "are not of the same order");
- }
- /** @brief Construct from two 1D Bezier polynomials of the same order. */
- BezierCurve(Bezier x, Bezier y)
- : inner(x, y)
- {
- // throw an error if the orders do not match
- if (x.order() != y.order())
- THROW_LOGICALERROR("Beziers used to construct a BezierCurve "
- "are not of the same order");
- }
- /** @brief Construct a linear segment from its endpoints. */
- BezierCurve(Point c0, Point c1) {
- for(unsigned d = 0; d < 2; d++)
- inner[d] = Bezier(c0[d], c1[d]);
- }
- /** @brief Construct a quadratic Bezier curve from its control points. */
- BezierCurve(Point c0, Point c1, Point c2) {
- for(unsigned d = 0; d < 2; d++)
- inner[d] = Bezier(c0[d], c1[d], c2[d]);
- }
- /** @brief Construct a cubic Bezier curve from its control points. */
- BezierCurve(Point c0, Point c1, Point c2, Point c3) {
- for(unsigned d = 0; d < 2; d++)
- inner[d] = Bezier(c0[d], c1[d], c2[d], c3[d]);
- }
- /** @brief Construct a Bezier curve from a vector of its control points. */
- BezierCurve(std::vector<Point> const &points) {
- unsigned ord = points.size() - 1;
- if (ord < 1) THROW_LOGICALERROR("Not enough points to construct a Bezier curve");
- for (unsigned d = 0; d < 2; ++d) {
- inner[d] = Bezier(Bezier::Order(ord));
- for(unsigned i = 0; i <= ord; i++)
- inner[d][i] = points[i][d];
- }
- }
- /// @}
-
- // methods new to BezierCurve go here
+ /// No constructors allowed!
/// @name Access and modify control points
/// @{
@@ -110,14 +60,14 @@ public:
* @return Vector with order() + 1 control points. */
std::vector<Point> points() const { return bezier_points(inner); }
/** @brief Modify a control point.
- * @param ix The zero-based index of the point to modify
+ * @param ix The zero-based index of the point to modify. Note that the caller is responsible for checking that this value is <= order().
* @param v The new value of the point */
void setPoint(unsigned ix, Point v) {
inner[X].setPoint(ix, v[X]);
inner[Y].setPoint(ix, v[Y]);
}
/** @brief Set new control points for this curve.
- * @param ps Vector which must contain order() + 1 points. */
+ * @param ps Vector which must contain order() + 1 points. Note that the caller is responsible for checking the size of this vector. */
virtual void setPoints(std::vector<Point> const &ps) {
// must be virtual, because HLineSegment will need to redefine it
if (ps.size() != order() + 1)
@@ -127,27 +77,13 @@ public:
}
}
/** Access control points of the curve.
- * @param ix Index of the control point
+ * @param ix The (zero-based) index of the control point. Note that the caller is responsible for checking that this value is <= order().
* @return The control point. No-reference return, use setPoint() to modify control points. */
Point const operator[](unsigned ix) const { return Point(inner[X][ix], inner[Y][ix]); }
/// @}
- /** @brief Divide a Bezier curve into two curves
- * @param t Time value
- * @return Pair of Bezier curves \f$(\mathbf{D}, \mathbf{E})\f$ such that
- * \f$\mathbf{D}[ [0,1] ] = \mathbf{C}[ [0,t] ]\f$ and
- * \f$\mathbf{E}[ [0,1] ] = \mathbf{C}[ [t,1] ]\f$ */
- std::pair<BezierCurve, BezierCurve> subdivide(Coord t) const {
- std::pair<Bezier, Bezier> sx = inner[X].subdivide(t), sy = inner[Y].subdivide(t);
- return std::make_pair(
- BezierCurve(sx.first, sy.first),
- BezierCurve(sx.second, sy.second));
- }
- BezierCurve *optimize() const;
-
// implementation of virtual methods goes here
#ifndef DOXYGEN_SHOULD_SKIP_THIS
- virtual Curve *duplicate() const;
virtual Point initialPoint() const { return inner.at0(); }
virtual Point finalPoint() const { return inner.at1(); }
virtual bool isDegenerate() const { return inner.isConstant(); }
@@ -170,20 +106,6 @@ public:
virtual std::vector<Coord> roots(Coord v, Dim2 d) const {
return (inner[d] - v).roots();
}
- Curve *portion(Coord f, Coord t) const {
- return new BezierCurve(Geom::portion(inner, f, t));
- }
- Curve *reverse() const {
- return new BezierCurve(Geom::reverse(inner));
- }
- virtual Curve *transformed(Affine const &m) const {
- BezierCurve *ret = new BezierCurve(order());
- std::vector<Point> ps = points();
- for(unsigned i = 0; i <= order(); i++) ps[i] = ps[i] * m;
- ret->setPoints(ps);
- return ret;
- }
- virtual Curve *derivative() const;
virtual Coord length(Coord tolerance) const;
virtual Point pointAt(Coord t) const { return inner.valueAt(t); }
virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const { return inner.valueAndDerivatives(t, n); }
@@ -192,51 +114,154 @@ public:
#endif
};
-class LineSegment : public BezierCurve {
+template <unsigned degree>
+class BezierCurveN : public BezierCurve {
+
public:
- LineSegment() : BezierCurve(1) {}
- LineSegment(Point c0, Point c1) : BezierCurve(c0, c1) {}
+ template <unsigned required_degree>
+ static void assert_degree(BezierCurveN<required_degree> const *) {}
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
- virtual Curve *duplicate() const { return new LineSegment(*this); }
- virtual Rect boundsFast() const { return Rect(initialPoint(), finalPoint()); }
- virtual Rect boundsExact() const { return boundsFast(); }
- virtual Coord nearestPoint(Point const& p, Coord from = 0, Coord to = 1) const;
+ /// @name Construct the curve
+ /// @{
+ /** @brief Construct a Bezier curve of the specified order with all points zero. */
+ BezierCurveN() {
+ inner = D2<Bezier> (Bezier::Order(degree), Bezier::Order(degree));
+ }
+
+ /** @brief Construct from 2D Bezier polynomial. */
+ explicit BezierCurveN(D2<Bezier > const &x) {
+ inner = x;
+ }
+
+ /** @brief Construct from two 1D Bezier polynomials of the same order. */
+ BezierCurveN(Bezier x, Bezier y) {
+ inner = D2<Bezier > (x,y);
+ }
+
+ /** @brief Construct a Bezier curve from a vector of its control points. */
+ BezierCurveN(std::vector<Point> const &points) {
+ unsigned ord = points.size() - 1;
+ if (ord != degree) THROW_LOGICALERROR("BezierCurve<degree> does not match number of points");
+ for (unsigned d = 0; d < 2; ++d) {
+ inner[d] = Bezier(Bezier::Order(ord));
+ for(unsigned i = 0; i <= ord; i++)
+ inner[d][i] = points[i][d];
+ }
+ }
+
+ /** @brief Construct a linear segment from its endpoints. */
+ BezierCurveN(Point c0, Point c1) {
+ assert_degree<1>(this);
+ for(unsigned d = 0; d < 2; d++)
+ inner[d] = Bezier(c0[d], c1[d]);
+ }
+
+ /** @brief Construct a quadratic Bezier curve from its control points. */
+ BezierCurveN(Point c0, Point c1, Point c2) {
+ assert_degree<2>(this);
+ for(unsigned d = 0; d < 2; d++)
+ inner[d] = Bezier(c0[d], c1[d], c2[d]);
+ }
+
+ /** @brief Construct a cubic Bezier curve from its control points. */
+ BezierCurveN(Point c0, Point c1, Point c2, Point c3) {
+ assert_degree<3>(this);
+ for(unsigned d = 0; d < 2; d++)
+ inner[d] = Bezier(c0[d], c1[d], c2[d], c3[d]);
+ }
+
+ // default copy
+ // default assign
+
+ /// @}
+
+
+ virtual Curve *duplicate() const {
+ return new BezierCurveN(*this);
+ }
virtual Curve *portion(Coord f, Coord t) const {
- return new LineSegment(pointAt(f), pointAt(t));
+ if (degree == 1) {
+ return new BezierCurveN<1>(pointAt(f), pointAt(t));
+ } else {
+ return new BezierCurveN(Geom::portion(inner, f, t));
+ }
+ }
+ virtual Curve *reverse() const {
+ if (degree == 1) {
+ return new BezierCurveN<1>(finalPoint(), initialPoint());
+ } else {
+ return new BezierCurveN(Geom::reverse(inner));
+ }
}
- virtual Curve *reverse() const { return new LineSegment(finalPoint(), initialPoint()); }
virtual Curve *transformed(Affine const &m) const {
- return new LineSegment(initialPoint() * m, finalPoint() * m);
+ if (degree == 1) {
+ return new BezierCurveN<1>(initialPoint() * m, finalPoint() * m);
+ } else {
+ BezierCurveN *ret = new BezierCurveN();
+ std::vector<Point> ps = points();
+ for (unsigned i = 0; i <= degree; i++) {
+ ps[i] = ps[i] * m;
+ }
+ ret->setPoints(ps);
+ return ret;
+ }
}
- virtual Curve *derivative() const {
- Coord dx = inner[X][1] - inner[X][0], dy = inner[Y][1] - inner[Y][0];
- return new LineSegment(Point(dx,dy),Point(dx,dy));
+ virtual Curve *derivative() const;
+
+ /** @brief Divide a Bezier curve into two curves
+ * @param t Time value
+ * @return Pair of Bezier curves \f$(\mathbf{D}, \mathbf{E})\f$ such that
+ * \f$\mathbf{D}[ [0,1] ] = \mathbf{C}[ [0,t] ]\f$ and
+ * \f$\mathbf{E}[ [0,1] ] = \mathbf{C}[ [t,1] ]\f$ */
+ std::pair<BezierCurveN, BezierCurveN> subdivide(Coord t) const {
+ std::pair<Bezier, Bezier> sx = inner[X].subdivide(t), sy = inner[Y].subdivide(t);
+ return std::make_pair(
+ BezierCurveN(sx.first, sy.first),
+ BezierCurveN(sx.second, sy.second));
}
- virtual Coord length(Coord) const { return distance(initialPoint(), finalPoint()); }
-#endif
-};
-class QuadraticBezier : public BezierCurve {
-public:
- QuadraticBezier(Point c0, Point c1, Point c2) : BezierCurve(c0, c1, c2) {}
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
- virtual Coord length(Coord tolerance) const;
-#endif
-};
+ double nearestPoint( Point const& p, double from = 0, double to = 1 ) const {
+ return Curve::nearestPoint(p, from, to);
+ }
-/** @brief Cubic Bezier curve.
- * Note that if you created a BezierCurve, you will not be able to cast it to a CubicBezier
- * using a dynamic cast regardless of its order - use the optimize() method.
- * @ingroup Curves */
-class CubicBezier : public BezierCurve {
-public:
- CubicBezier(Point c0, Point c1, Point c2, Point c3) : BezierCurve(c0, c1, c2, c3) {}
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
- virtual Coord length(Coord tolerance) const;
-#endif
};
+// BezierCurveN<0> is meaningless; specialize it out
+template<> class BezierCurveN<0> : public BezierCurveN<1> { public: BezierCurveN();};
+
+// provide convenient names for common degree bezier curves
+typedef BezierCurveN<1> LineSegment;
+typedef BezierCurveN<2> QuadraticBezier;
+typedef BezierCurveN<3> CubicBezier;
+
+template <unsigned degree>
+inline
+Curve *BezierCurveN<degree>::derivative() const {
+ return new BezierCurveN<degree-1>(Geom::derivative(inner[X]), Geom::derivative(inner[Y]));
+}
+template <>
+inline
+Curve *BezierCurveN<1>::derivative() const {
+ double dx = inner[X][1] - inner[X][0], dy = inner[Y][1] - inner[Y][0];
+ return new BezierCurveN<1>(Point(dx,dy),Point(dx,dy));
+}
+
+template<>
+inline
+double LineSegment::nearestPoint(Point const& p, double from, double to) const
+{
+ if ( from > to ) std::swap(from, to);
+ Point ip = pointAt(from);
+ Point fp = pointAt(to);
+ Point v = fp - ip;
+ Coord l2v = L2sq(v);
+ if (l2v == 0) return 0;
+ Coord t = dot( p - ip, v ) / l2v;
+ if ( t <= 0 ) return from;
+ else if ( t >= 1 ) return to;
+ else return from + t*(to-from);
+}
+
inline Point middle_point(LineSegment const& _segment) {
return ( _segment.initialPoint() + _segment.finalPoint() ) / 2;
}
diff --git a/src/2geom/forward.h b/src/2geom/forward.h
index 93a762022..399344dda 100644
--- a/src/2geom/forward.h
+++ b/src/2geom/forward.h
@@ -58,7 +58,10 @@ class SBasisCurve;
class BezierCurve;
class HLineSegment;
class VLineSegment;
-class LineSegment;
+template <unsigned degree> class BezierCurveN;
+typedef BezierCurveN<1> LineSegment;
+typedef BezierCurveN<2> QuadraticBezier;
+typedef BezierCurveN<3> CubicBezier;
class EllipticalArc;
class SVGEllipticalArc;
diff --git a/src/2geom/numeric/fitting-model.h b/src/2geom/numeric/fitting-model.h
index b1ecbe92c..a44c1ddac 100644
--- a/src/2geom/numeric/fitting-model.h
+++ b/src/2geom/numeric/fitting-model.h
@@ -470,12 +470,13 @@ class LFMBezier
// this model generates Bezier curves
-class LFMBezierCurve
- : public LinearFittingModel< double, Point, BezierCurve >
+template <unsigned degree>
+class LFMBezierCurveN
+ : public LinearFittingModel< double, Point, BezierCurveN<degree> >
{
public:
- LFMBezierCurve( size_t _order )
- : mob(_order)
+ LFMBezierCurveN()
+ : mob(degree+1)
{
}
@@ -489,13 +490,13 @@ class LFMBezierCurve
return mob.size();
}
- void instance(BezierCurve & bc, ConstMatrixView const& raw_data) const
+ void instance(BezierCurveN<degree> & bc, ConstMatrixView const& raw_data) const
{
- Bezier bx(size()-1);
- Bezier by(size()-1);
+ Bezier bx(degree);
+ Bezier by(degree);
mob.instance(bx, raw_data.column_const_view(X));
mob.instance(by, raw_data.column_const_view(Y));
- bc = BezierCurve(bx, by);
+ bc = BezierCurveN<degree>(bx, by);
}
private: