diff options
| author | Johan B. C. Engelen <jbc.engelen@swissonline.ch> | 2011-02-13 20:06:36 +0000 |
|---|---|---|
| committer | Johan Engelen <goejendaagh@zonnet.nl> | 2011-02-13 20:06:36 +0000 |
| commit | c8ec8ac8c5a3665adf39b1a939404027395ace0e (patch) | |
| tree | 9c53b646d227da2a44460fb13d6016843049f7f7 /src/2geom | |
| parent | calligraphy tool does not need to know the type of the end curves to add the ... (diff) | |
| download | inkscape-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.cpp | 123 | ||||
| -rw-r--r-- | src/2geom/bezier-curve.h | 257 | ||||
| -rw-r--r-- | src/2geom/forward.h | 5 | ||||
| -rw-r--r-- | src/2geom/numeric/fitting-model.h | 17 |
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: |
