1 /*---------------------------------------------------------------------------*\
2 * OpenSG NURBS Library *
5 * Copyright (C) 2001-2006 by the University of Bonn, Computer Graphics Group*
7 * http://cg.cs.uni-bonn.de/ *
9 * contact: edhellon@cs.uni-bonn.de, guthe@cs.uni-bonn.de, rk@cs.uni-bonn.de *
11 \*---------------------------------------------------------------------------*/
12 /*---------------------------------------------------------------------------*\
15 * This library is free software; you can redistribute it and/or modify it *
16 * under the terms of the GNU Library General Public License as published *
17 * by the Free Software Foundation, version 2. *
19 * This library is distributed in the hope that it will be useful, but *
20 * WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
22 * Library General Public License for more details. *
24 * You should have received a copy of the GNU Library General Public *
25 * License along with this library; if not, write to the Free Software *
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
28 \*---------------------------------------------------------------------------*/
29 /*---------------------------------------------------------------------------*\
37 \*---------------------------------------------------------------------------*/
39 # pragma warning (disable : 985)
42 #include "OSGBezierCurve2D.h"
50 static char THIS_FILE
[] = __FILE__
;
55 int BezierCurve2D::setControlPointVector(const DCTPVec3dvector
& cps
)
58 return -1; //invalid dimension, at least rwo points are required
64 //some REAL functionality
65 //! Compute the value of the Bezier curve at given the parameter value.
67 * This function computes the value of the Bezier curve
68 * at the given parameter value, using de Casteljau's method of
69 * repeated linear interpolations.
71 * \param t the parameter value at which to compute the approximation
72 * \param error flag to indicate if an error has happened
73 * \return the computed value
76 Vec2d
BezierCurve2D::computewdeCasteljau(double t
, int &error
)
78 //FIXME: verification before goin' into computation!!
79 DCTPVec3dvector Q
= control_points
; //local array not to destroy da other points
80 DCTPVec3dvector::size_type n
= Q
.size() - 1;
84 if(n
< 1) //too few points, at least 2 needed
87 Q
[0][0] = DCTP_EPS
* floor(Q
[0][0] / DCTP_EPS
);
88 Q
[0][1] = DCTP_EPS
* floor(Q
[0][1] / DCTP_EPS
);
89 result
[0] = Q
[0][0] / Q
[0][2];
90 result
[1] = Q
[0][1] / Q
[0][2];
93 // std::cerr.precision( DCTP_PRECISION );
94 // for ( unsigned int kkk = 0; kkk < Q.size(); kkk++ )
95 // std::cerr << Q[ kkk ] << " " ;
96 // std::cerr << std::endl;
99 for(DCTPVec3dvector::size_type k
= 0; k
< n
; ++k
)
100 for(DCTPVec3dvector::size_type i
= 0; i
< n
- k
; ++i
)
101 Q
[i
] = Q
[i
] * (1.0 - t
) + Q
[i
+ 1] * t
;
103 result
[0] = Q
[0][0] / Q
[0][2];
104 result
[1] = Q
[0][1] / Q
[0][2];
109 //! Compute the linear approximation of the Bezier curve at the given parameter value.
111 * This function computes the linear approximation of the Bezier curve
112 * at the given parameter value.
114 * \param t the parameter value at which to compute the approximation
115 * \param error flag to indicate if an error has happened
116 * \return the approximated value
119 Vec2d
BezierCurve2D::computeLinearApproximation(double t
, int &error
)
121 //FIXME: verification before goin' into computation!!
122 DCTPVec3dvector::size_type n
= control_points
.size() - 1;
123 Vec2d
result(0.0, 0.0);
127 if(n
< 1) //too few points, at least 2 needed
132 tempres
= control_points
[0] * (1 - t
) + control_points
[n
] * t
;
133 result
[0] = tempres
[0] / tempres
[2];
134 result
[1] = tempres
[1] / tempres
[2];
138 //! Subdivide Bezier curve at midpoint into two new curves.
140 * This function subdivides a Bezier curve into two new Bezier curves
141 * of the same degree at the midpoint, using de Casteljau's
144 * \param newbeziers the two new Bezier curves (returned in a vector of size 2)
145 * \return zero on success, and a negative value when some error occured.
149 int BezierCurve2D::midPointSubDivision(bezier2dvector
&newbeziers
)
151 // This function is time critical so optimize at the cost of readabiltity...
152 DCTPVec3dvector::size_type n
= control_points
.size();
154 if(n
< 2) //too few points, at least 2 needed to split curve
159 newbeziers
.resize(2); // we return exactly two curves
160 DCTPVec3dvector::size_type i
, k
;
162 DCTPVec3dvector
&cp1
= newbeziers
[0].control_points
;
163 DCTPVec3dvector
&cp2
= newbeziers
[1].control_points
;
165 cp1
.clear(); // very imporatant for performance (no copying around of obsolte stuff!)
166 cp2
.clear(); // very imporatant for performance (no copying around of obsolte stuff!)
170 for(k
= 0; k
< n
; ++k
)
172 cp1
[k
] = control_points
[k
];
177 for(k
= 0; k
< n
; ++k
)
181 for(i
= n
; i
> k
; --i
)
183 cp1
[i
] += cp1
[i
- 1];
192 //! Subdivide Bezier curve at midpoint into two new curves.
194 * This function subdivides a Bezier curve into two new Bezier curves
195 * of the same degree at the midpoint, using de Casteljau's
198 * \param newbeziers the two new Bezier curves (returned in a vector of size 2)
199 * \return zero on success, and a negative value when some error occured.
203 int BezierCurve2D::midPointSubDivision(BezierCurve2D
&newcurve
)
205 // This function is time critical so optimize at the cost of readabiltity...
206 DCTPVec3dvector::size_type n
= control_points
.size();
210 return -1; //too few points, at least 2 needed to split curve
213 DCTPVec3dvector::size_type i
, k
;
215 DCTPVec3dvector
&cp1
= control_points
;
216 DCTPVec3dvector
&cp2
= newcurve
.control_points
;
218 cp2
.clear(); // very imporatant for performance (no copying around of obsolte stuff!)
223 for(k
= 0; k
< n
; ++k
)
227 // cp2[ n - k ][0] = cp1[ n ][0];
228 // cp2[ n - k ][1] = cp1[ n ][1];
229 for(i
= n
; i
> k
; --i
)
231 cp1
[i
] += cp1
[i
- 1];
232 // cp1[ i ][0] += cp1[ i - 1 ][0];
233 // cp1[ i ][1] += cp1[ i - 1 ][1];
235 // cp1[ i ][0] *= 0.5;
236 // cp1[ i ][1] *= 0.5;
241 // cp2[ 0 ][0] = cp1[ n ][0];
242 // cp2[ 0 ][1] = cp1[ n ][1];
247 //! Subdivide Bezier curve at t into two new curves.
249 * This function subdivides a Bezier curve into two new Bezier curves
250 * of the same degree at the parameter value `t', using de Casteljau's
253 * \param t the parameter at which to subdivide the curve.
254 * \param newbeziers the two new Bezier curves (returned in a vector of size 2)
255 * \return zero on success, and a negative value when some error occured.
259 int BezierCurve2D::subDivision(double t
, bezier2dvector
&newbeziers
)
261 if(t
<= 0.0 || t
>= 1.0)
262 return -1; // t must be between (0, 1) exclusive
264 newbeziers
.resize(2); // we return exactly two curves
265 DCTPVec3dvector Q
= control_points
; //local array not to destroy da other points
266 DCTPVec3dvector::size_type n
= control_points
.size() - 1;
267 DCTPVec3dvector
cp1(n
+ 1);
268 DCTPVec3dvector
cp2(n
+ 1);
269 if(n
< 1) //too few points, at least 2 needed to split curve
274 for(DCTPVec3dvector::size_type k
= 0; k
< n
; ++k
)
277 cp2
[n
- k
] = Q
[n
- k
];
279 for(DCTPVec3dvector::size_type i
= 0; i
< n
- k
; ++i
)
281 Q
[i
] = Q
[i
] * (1.0 - t
) + Q
[i
+ 1] * t
;
287 newbeziers
[0].setControlPointVector(cp1
);
288 newbeziers
[1].setControlPointVector(cp2
);
292 //! Subdivide Bezier curve at t into two new curves.
294 * This function subdivides a Bezier curve into two new Bezier curves
295 * of the same degree at the parameter value `t', using de Casteljau's
298 * \param t the parameter at which to subdivide the curve.
299 * \param newbeziers the two new Bezier curves (returned in a vector of size 2)
300 * \return zero on success, and a negative value when some error occured.
304 int BezierCurve2D::subDivision(double t
, BezierCurve2D
&newcurve
)
306 if(t
<= 0.0 || t
>= 1.0)
308 return -1; // t must be between (0, 1) exclusive
311 // This function is time critical so optimize at the cost of readabiltity...
312 DCTPVec3dvector::size_type n
= control_points
.size();
316 return -1; //too few points, at least 2 needed to split curve
321 DCTPVec3dvector::size_type i
, k
;
323 DCTPVec3dvector
&cp1
= control_points
;
324 DCTPVec3dvector
&cp2
= newcurve
.control_points
;
326 cp2
.clear(); // very important for performance (no copying around of obsolte stuff!)
331 for(k
= 0; k
< n
; ++k
)
335 for(i
= n
; i
> k
; --i
)
338 cp1
[i
] += cp1
[i
- 1] * t2
;
347 //! Calculate intersection of the Bezier curve with the polyline (a, b).
349 * This function calculates the number of intersections the curve has
350 * with the polyline (a, b), using Bezier clipping and the minmax algorithm.
352 * \param res the double vector which will contain the results
353 * (in (0, 1) parameter space)
354 * \param a the first point of the polyline
355 * \param b the second point of the polyline
356 * \return zero on success, a negative value if some error occured, and <BR>
357 * 1 if the curve lies entirely on the polyline. <BR>
358 * 2 if the curve lies partly on the polyline. <BR>
361 int BezierCurve2D::intersection(DCTPdvector
&res
, Vec2d a
, Vec2d b
)
366 if(osgAbs(a
[0] - b
[0]) < DCTP_EPS
&& osgAbs(a
[1] - b
[1]) < DCTP_EPS
)
367 return -1; // the two points are (almost) the same
369 // DCTPdvector dists( control_points.size() ); // the signed distance of the control points and the polyline
370 Vec2d norm
; // norm of ( a - b )
371 //due to the check above, lab must be positive
372 double lab
= sqrt( (a
[0] - b
[0]) * (a
[0] - b
[0]) + (a
[1] - b
[1]) * (a
[1] - b
[1]) );
373 norm
[0] = -(b
[1] - a
[1]) / lab
; // This is a normal -> rotated 90 degrees
374 norm
[1] = (b
[0] - a
[0]) / lab
;
377 DCTPVec3dvector
newcp(control_points
.size() ); // the control points of the explicit Bezier curve
379 unsigned int cpsize
= UInt32(control_points
.size()) - 1;
381 for(i
= 0; i
<= cpsize
; i
++)
383 // std::cerr.precision( DCTP_PRECISION );
384 // std::cerr << "cp[i].x: " << control_points[ i ][0] << " cp[i].y: " << control_points[ i ][1] << std::endl;
385 newcp
[i
][0] = (i
/ cpsize
) * control_points
[i
][2];
386 newcp
[i
][1] = pointLineDistancewNormal(control_points
[i
], a
, norm
);
387 newcp
[i
][2] = control_points
[i
][2];
388 //std::cerr << " dists[i]: " << newcp[ i ][1] << std::endl;
389 if(newcp
[i
][1] < -DCTP_EPS
|| newcp
[i
][1] > DCTP_EPS
)
395 res
.resize(0); // zero result vector because
396 // check start and endpoint of curve, is it really on the polyline, or before/after ?
399 // we assume that the first and last control points can't have zero weights !!!
400 // (if they were zero, the curve goes through infinity...
401 first
[0] = control_points
[0][0] / control_points
[0][2];
402 first
[1] = control_points
[0][1] / control_points
[0][2];
403 last
[0] = control_points
[cpsize
][0] / control_points
[cpsize
][2];
404 last
[1] = control_points
[cpsize
][1] / control_points
[cpsize
][2];
406 if(osgAbs(first
[0] - last
[0]) > DCTP_EPS
)
408 ta
= (a
[0] - first
[0]) / (last
[0] - first
[0]);
409 tb
= (b
[0] - first
[0]) / (last
[0] - first
[0]);
413 ta
= (a
[1] - first
[1]) / (last
[1] - first
[1]);
414 tb
= (b
[1] - first
[1]) / (last
[1] - first
[1]);
433 return 0; // fully before or after
434 if(ta
== 0.0 && tb
== 1.0)
435 return 1; // fully on the polyline
437 res
.resize(2); // the curve lies partially on the polyline
438 // std::cerr << " ta: " << ta << " tb: " << tb << std::endl;
441 return 2; //the curve partially lies on the polyline
444 BezierCurve2D bstart
;
445 result
= bstart
.setControlPointVector(newcp
);
447 return result
; // some error happened
448 result
= bstart
.minMaxIntersection(res
, 0.0, 1.0);
449 bool zeroroot
= (osgAbs(newcp
[0][1]) < DCTP_EPS
);
450 bool oneroot
= (osgAbs(newcp
[cpsize
][1]) < DCTP_EPS
);
452 // special care taken for start/end points
453 // FIXME: hack #1 :-|
456 if(res
[res
.size() - 1] >= 1.0 - DCTP_EPS
)
457 res
[res
.size() - 1] = 1.0;
458 if(res
[0] <= DCTP_EPS
)
463 // we have some solutions, but the first solution is not 0.0, we
464 // have to insert 0.0 as first solution
465 if(res
.size() && res
[0] >= DCTP_EPS
)
466 res
.insert(res
.begin(), 0.0);
467 // we don't have any solutions yet, but this is a solution, so we have to
469 else if(!res
.size() )
474 // if we have oneroot, and the last item is not 1.0, we have to insert it
475 // as last result, regardless of what is actually in the vector (it can even be empty)
476 // check for the last item ( & its existence )
477 if(res
.size() && res
[res
.size() - 1] < 1.0 - DCTP_EPS
)
479 // insert it if the resultvector is empty
480 else if(!res
.size() )
485 // std::cerr << "still here. solutions: " << res.size() << std::endl;
486 for(i
= 0; i
< res
.size(); i
++)
491 // std::cerr << " i: " << i << " solution[i]: " << res[ i ];
492 p
= computewdeCasteljau(res
[i
], er
);
493 // std::cerr << " value: " << p[0] << " " << p[1] << std::endl;
494 if(osgAbs(b
[0] - a
[0]) > DCTP_EPS
)
495 t
= (p
[0] - a
[0]) / (b
[0] - a
[0]);
497 t
= (p
[1] - a
[1]) / (b
[1] - a
[1]);
498 // std::cerr << " lineparam: " << t << std::endl;
499 if(DCTPVecIsNotEqual(p
, a
) && DCTPVecIsNotEqual(p
, b
) )
500 if(t
< -DCTP_EPS
|| t
> (1 + DCTP_EPS
) )
502 res
.erase(res
.begin() + i
);
507 // std::cerr << "still here. solutions after: " << res.size() << std::endl;
508 // if ( res.size() ) std::cerr << res[ 0 ] << std::endl;
511 // we have to make this check _after_ the results have been filtered
512 if(res
.size() > control_points
.size() )
515 * We have more results than possible, the curve partly lies on the polyline
516 * we only return the first and last results, and hope for the best.
517 * note this is not a 100% general solution, but in the vast majority
519 * FIXME: a 100% general solution may be desirable some day...
521 double tt
= res
[res
.size() - 1];
524 return -2; // to indicate some problem happened
531 // calculate intersection of curve with line (a)
532 int BezierCurve2D::intersection(DCTPdvector
&res
, double a
, bool horiz
)
534 DCTPVec3dvector
newcp(control_points
.size() ); // the control points of the explicit Bezier curve
535 BezierCurve2D tempcurve
;
536 const unsigned int cpsize
= UInt32(control_points
.size()) - 1;
538 for(unsigned int i
= 0; i
< newcp
.size(); ++i
)
542 newcp
[i
] = control_points
[i
];
546 newcp
[i
][0] = control_points
[i
][1];
547 newcp
[i
][1] = control_points
[i
][0];
548 newcp
[i
][2] = control_points
[i
][2];
550 newcp
[i
][1] -= a
* newcp
[i
][2];
553 int result
= tempcurve
.setControlPointVector(newcp
);
555 return result
; // some error happened
556 result
= tempcurve
.minMaxIntersection(res
, 0.0, 1.0);
557 bool zeroroot
= (osgAbs(newcp
[0][1]) < DCTP_EPS
);
558 bool oneroot
= (osgAbs(newcp
[cpsize
][1]) < DCTP_EPS
);
559 // special care taken for start/end points
560 // FIXME: hack #1 :-|
563 if(res
[res
.size() - 1] >= 1.0 - DCTP_EPS
)
564 res
[res
.size() - 1] = 1.0;
565 if(res
[0] <= DCTP_EPS
)
570 // we have some solutions, but the first solution is not 0.0, we
571 // have to insert 0.0 as first solution
572 if(res
.size() && res
[0] >= DCTP_EPS
)
573 res
.insert(res
.begin(), 0.0);
574 // we don't have any solutions yet, but this is a solution, so we have to
576 else if(!res
.size() )
581 // if we have oneroot, and the last item is not 1.0, we have to insert it
582 // as last result, regardless of what is actually in the vector (it can even be empty)
583 // check for the last item ( & its existence )
584 if(res
.size() && res
[res
.size() - 1] < 1.0 - DCTP_EPS
)
586 // insert it if the resultvector is empty
587 else if(!res
.size() )
594 // calculate intersection of x axis and Bezier curve, calls itself recursively
595 int BezierCurve2D::minMaxIntersection(DCTPdvector
&res
, double s
, double e
)
597 double miny
= control_points
[0][1];
598 double maxy
= control_points
[0][1];
603 // std::cerr.precision( DCTP_PRECISION );
604 // std::cerr << "minmax for: " << s << " " << e << std::endl;
605 // find the minmax box of the control polygon - note we only need the y values
606 // meanwhile also check that we do not lie on the line :)
607 DCTPVec3dvector::size_type cpsize
= control_points
.size();
608 DCTPVec3dvector::size_type i
;
610 for(i
= 1; i
< cpsize
; i
++)
612 if(control_points
[i
][1] > maxy
)
613 maxy
= control_points
[i
][1];
614 else if(control_points
[i
][1] < miny
)
615 miny
= control_points
[i
][1];
618 // std::cerr << " miny: " << miny << " maxy: " << maxy << std::endl;
621 if(miny
> -DCTP_EPS
&& miny
< DCTP_EPS
&& maxy
> -DCTP_EPS
&& maxy
< DCTP_EPS
)
623 std::cerr
<< " found interval: [ " << control_points
[0][0] << " - " << control_points
[cpsize
- 1][0] << " ]" << std::endl
;
626 if(osgAbs(control_points
[0][1]) < DCTP_EPS
)
629 // if ( res.size() && res[ res.size() - 1 ] == s ) return 0;
630 if(!res
.size() || res
[res
.size() - 1] != s
)
632 std::cerr
<< " special recording: " << s
<< std::endl
;
634 // we have to check for the last point too, for really degenerate cases
635 // if ( !( osgAbs( control_points[ cpsize - 1 ][1] ) < DCTP_EPS ) || control_points[ 0 ] == control_points[ cpsize - 1 ] )
639 std::cerr
<< "still here 1 " << std::endl
;
640 if(osgAbs(control_points
[cpsize
- 1][1]) < DCTP_EPS
)
643 // if ( res.size() && res[ res.size() - 1 ] == e ) return 0;
644 if(!res
.size() || res
[res
.size() - 1] != e
)
646 std::cerr
<< " special recording #2: " << e
<< std::endl
;
651 std::cerr
<< "still here 2 " << std::endl
;
654 if( (miny
> 0 && maxy
> 0) || (miny
< 0 && maxy
< 0) )
658 if ( ( miny <= 0.0 && maxy <= 0.0 ) ||
659 ( miny > 0.0 && maxy > 0.0 ) ) return 0;
661 // if ( ! ( (miny < 0.0 && maxy > 0.0) ||
662 // (miny > 0.0 && maxy < 0.0) ) ) return 0;
663 // std::cerr <<"still here 3 " << std::endl;
664 if(maxy
- miny
< DCTP_EPS
)
668 // completely on the line, so return start and end...
677 res
.push_back( (s
+ e
) / 2.0); // we have a new (unique) solution, record it
681 /* if( maxy - miny < DCTP_EPS ) // this should be sufficient...
682 // if( control_points[ 0 ] == control_points[ cpsize - 1 ] &&
683 // (e - s ) < DCTP_EPS )
685 if ( res.size() > 0 ) {
686 if ( osgAbs( res[ res.size() - 1 ] - (s + e) / 2.0 ) > DCTP_EPS )
687 // osgAbs( res[ res.size() - 1 ][0] - e ) > DCTP_MINMAX_DIFFTOL) {
688 // std::cerr << " recording : " << s << std::endl;
689 // std::cerr << " res.size(): " << res.size() << " res[res.size()-1]: " << res[res.size()-1] << std::endl;
690 res.push_back( (s + e) / 2.0 ); // we have a new (unique) solution, record it
695 // std::cerr << " recording first: " << s << std::endl;
696 res.push_back( (s + e) / 2.0 ); // this is the first solution, record it anyway
700 // we have a solution in our minmax box, subdivide and continue recursively
701 BezierCurve2D newbez
;
703 // find first intersection of contol polygon with axis
704 for(i
= 1; i
< cpsize
; ++i
)
706 const double &p1
= control_points
[i
- 1][1];
707 const double &p2
= control_points
[i
][1];
713 if( (p2
== 0.0) && (i
== cpsize
- 1) )
720 mid
= (i
- p2
/ (p2
- p1
) ) / cpsize
;
729 if( (p2
== 0.0) && (i
== cpsize
- 1) )
736 mid
= (i
+ p2
/ (p1
- p2
) ) / cpsize
;
745 if(osgAbs(res
[res
.size() - 1] - s
) > DCTP_EPS
)
747 res
.push_back(s
); // we have a new (unique) solution, record it
752 res
.push_back(s
); // this is the first solution, record it anyway
761 // for( i = 0; i < cpsize; ++i )
763 // std::cerr << control_points[ i ][1] << " ";
765 // std::cerr << std::endl;
767 mid
= osgMin(0.999, osgMax(0.001, mid
) );
768 // std::cerr << mid << std::endl;
769 result
= subDivision(mid
, newbez
);
770 // result = midPointSubDivision( newbez );
772 return result
; // some error happened
774 result
= minMaxIntersection(res
, s
, s
+ (e
- s
) * mid
);
775 // result = minMaxIntersection( res, s, ( (s + e) / 2 ) );
777 return result
; // some error happened
779 result
= newbez
.minMaxIntersection(res
, s
+ (e
- s
) * mid
, e
);
780 // result = newbez.minMaxIntersection( res, ( (s + e) / 2 ), e );
785 // calculate the signed distance between a homogenious point and a line (given with a point and a normalvector)
786 // returns the signed distance
787 double BezierCurve2D::pointLineDistancewNormal(Vec3d p
, Vec2d a
, Vec2d n
)
793 // t[0] = p[0] - a[0];
794 // t[1] = p[1] - a[1];
795 // std::cerr << " a.x: " << a[0] << " a.y: " << a[1] << " p.x: " << p[0] << " p.y: " << p[1] << std::endl;
796 return t
[0] * n
[0] + t
[1] * n
[1];
799 //! Approximate curve linearly with given maximum tolerance.
801 * This function approximates the Bezier curve with a polyline. The maximum
802 * error of the approximation will not be larger than the given tolerance.
805 * \param vertices the vertices of the approximating polyline.
806 * \param delta maximum error.
807 * \return zero on success, and a negative value when some error occured.
809 int BezierCurve2D::approximate(DCTPVec2dvector
&vertices
, double delta
)
813 // check for first degree Bezier.
814 if(control_points
.size() == 2)
817 tmp
[0] = control_points
[0][0] / control_points
[0][2];
818 tmp
[1] = control_points
[0][1] / control_points
[0][2];
819 vertices
.push_back(tmp
);
820 tmp
[0] = control_points
[1][0] / control_points
[1][2];
821 tmp
[1] = control_points
[1][1] / control_points
[1][2];
822 vertices
.push_back(tmp
);
826 // call recursive subdividing function
827 return approximate_sub(vertices
, delta
);
831 int BezierCurve2D::approximateLength(DCTPVec2dvector
&vertices
, double delta
)
835 // check for first degree Bezier.
836 std::cerr
<< control_points
.size() << std::endl
;
837 if(control_points
.size() == 2)
839 double d_dist
= sqrt( (control_points
[1] - control_points
[0]).squareLength() );
840 int i_steps
= ( int ) ceil(d_dist
/ (1.5 * delta
) );
844 std::cerr
<< d_dist
<< " - " << delta
<< " -> " << i_steps
<< " " << d_dist
/ (3.0 * delta
) << std::endl
;
846 vertices
.push_back(control_points
[0]);
848 for(i_step
= 1; i_step
< i_steps
; ++i_step
)
850 d_param
= ( double ) i_step
/ ( double ) i_steps
;
851 vertices
.push_back(control_points
[0] + (control_points
[1] - control_points
[0]) * d_param
);
854 vertices
.push_back(control_points
[1]);
858 // call recursive subdividing function
859 // std::cerr << "approx..." << std::endl;
860 return approximateLength_sub(vertices
, delta
);
865 int BezierCurve2D::approximate_sub(DCTPVec2dvector
&vertices
, double delta
)
867 double max_error
= 0;
872 int n
= UInt32(control_points
.size()) - 1;
873 std::vector
<double> t
;
876 for(i
= 0; i
<= n
; i
++)
878 t
.push_back((double( n
- i
) ) / n
);
881 // first, make a copy of the cp vector, and do one
882 // or more de Casteljau steps to get rid of 0 weights
883 DCTPVec3dvector mycps
;
884 mycps
= control_points
;
886 for(i
= 0; i
<= n
; ++i
)
888 unsigned int s
= UInt32(mycps
.size());
891 mycps
.push_back(mycps
[s
- 1]);
892 t
.push_back(t
[s
- 1]);
894 for(int j
= s
; j
> i
; --j
)
896 mycps
[j
] = (mycps
[j
] + mycps
[j
- 1]) * 0.5f
;
897 t
[j
] = (t
[j
] + t
[j
- 1]) * 0.5f
;
898 if(mycps
[j
][2] < DCTP_EPS
)
906 n
= UInt32(mycps
.size()) - 1;
907 e0
[0] = mycps
[0][0] / mycps
[0][2];
908 e0
[1] = mycps
[0][1] / mycps
[0][2];
909 en
[0] = mycps
[n
][0] / mycps
[n
][2];
910 en
[1] = mycps
[n
][1] / mycps
[n
][2];
912 for(i
= 0; i
<= n
; i
++)
916 ei
[0] = mycps
[i
][0] / mycps
[i
][2];
917 ei
[1] = mycps
[i
][1] / mycps
[i
][2];
918 ae
= ei
- e0
* t1
- en
* t2
;
919 // ae[0] = control_points[ i ][0] - t1 * control_points[ 0 ][0] - t2 * control_points[ n ][0];
920 // ae[1] = control_points[ i ][1] - t1 * control_points[ 0 ][1] - t2 * control_points[ n ][1];
922 // std::cerr << "i: " << i << " ae.x: " << ae[0] << " ae.y: " << ae[1] << std::endl;
924 aenorm
= sqrt(ae
[0] * ae
[0] + ae
[1] * ae
[1]);
925 if(aenorm
> max_error
)
929 // std::cerr.precision( DCTP_PRECISION );
930 // std::cerr << " twopower: " << twopower << std::endl;
931 // std::cerr << " act_error: " << act_error << " max_error: " << max_error << std::endl;
932 // std::cerr << control_points[ 0 ][0] << " " << control_points[ 0 ][1] << std::endl;
933 // std::cerr << control_points[ control_points.size() - 1 ][0] << " " << control_points[ control_points.size() - 1 ][1] << std::endl;
934 if(max_error
> delta
)
936 // we have to subdivide further
937 BezierCurve2D newbez
;
939 error
= midPointSubDivision(newbez
);
942 approximate_sub(vertices
, delta
);
943 newbez
.approximate_sub(vertices
, delta
);
947 // this is a good enough approximation
949 if(!vertices
.size() )
951 // this is the first approximation, we have to record both the
952 // startpoint and the endpoint
953 tmp
[0] = control_points
[0][0] / control_points
[0][2];
954 tmp
[1] = control_points
[0][1] / control_points
[0][2];
955 vertices
.push_back(tmp
);
956 tmp
[0] = control_points
[control_points
.size() - 1][0] / control_points
[control_points
.size() - 1][2];
957 tmp
[1] = control_points
[control_points
.size() - 1][1] / control_points
[control_points
.size() - 1][2];
958 vertices
.push_back(tmp
);
962 // we had some subdivisions before, we only need to record the endpoint
963 tmp
[0] = control_points
[control_points
.size() - 1][0] / control_points
[control_points
.size() - 1][2];
964 tmp
[1] = control_points
[control_points
.size() - 1][1] / control_points
[control_points
.size() - 1][2];
965 vertices
.push_back(tmp
);
972 int BezierCurve2D::approximateLength_sub(DCTPVec2dvector
&vertices
, double delta
)
975 double max_error
= 0;
981 int n
= control_points
.size() - 1;
982 double d_dist
= sqrt( (control_points
[n
] - control_points
[0]).squareLength() );
983 // std::cerr << d_dist << " - " << delta << std::endl;
984 if(d_dist
> delta
* 1.5)
986 // we have to subdivide further
987 bezier2dvector newbez
;
989 error
= midPointSubDivision(newbez
);
992 newbez
[0].approximateLength_sub(vertices
, delta
);
993 newbez
[1].approximateLength_sub(vertices
, delta
);
997 for(int i
= 0; i
<= n
; i
++)
999 t1
= ( (double) (n
- i
) ) / n
;
1000 t2
= ( (double) i
) / n
;
1001 ae
= control_points
[i
] - control_points
[0] * t1
- control_points
[n
] * t2
;
1002 // ae[0] = control_points[ i ][0] - t1 * control_points[ 0 ][0] - t2 * control_points[ n ][0];
1003 // ae[1] = control_points[ i ][1] - t1 * control_points[ 0 ][1] - t2 * control_points[ n ][1];
1005 // std::cerr << "i: " << i << " ae.x: " << ae[0] << " ae.y: " << ae[1] << std::endl;
1007 aenorm
= sqrt(ae
[0] * ae
[0] + ae
[1] * ae
[1]);
1008 if(aenorm
> max_error
)
1011 twopower
*= 2; // this is a double so it can be arbitrarily high, an int wouldn't suffice
1014 act_error
= ( (twopower
- 1) / twopower
) * max_error
;
1015 // std::cerr.precision( DCTP_PRECISION );
1016 // std::cerr << " twopower: " << twopower << std::endl;
1017 // std::cerr << " act_error: " << act_error << " max_error: " << max_error << std::endl;
1018 // std::cerr << control_points[ 0 ][0] << " " << control_points[ 0 ][1] << std::endl;
1019 // std::cerr << control_points[ control_points.size() - 1 ][0] << " " << control_points[ control_points.size() - 1 ][1] << std::endl;
1020 if(act_error
> delta
)
1022 // we have to subdivide further
1023 bezier2dvector newbez
;
1025 error
= midPointSubDivision(newbez
);
1028 newbez
[0].approximateLength_sub(vertices
, delta
);
1029 newbez
[1].approximateLength_sub(vertices
, delta
);
1033 // this is a good enough approximation
1034 if(!vertices
.size() )
1036 // this is the first approximation, we have to record both the
1037 // startpoint and the endpoint
1038 vertices
.push_back(control_points
[0]);
1039 vertices
.push_back(control_points
[control_points
.size() - 1]);
1043 // we had some subdivisions before, we only need to record the endpoint
1044 vertices
.push_back(control_points
[control_points
.size() - 1]);
1052 //! Return distance between two homogenious control points
1054 * This functions returns the norm of the difference of the two control points
1055 * which serves as the distance betwen the two homogenious control points.
1057 double BezierCurve2D::homogeniousDistanceSquared(Vec3d v1
, Vec3d v2
)
1060 return v1
.dist2(v2
);
1063 //! Degree reduce the Bezier curve with t tolerance if possible.
1065 * This function tries to degree reduce the Bezier curve ensuring
1066 * that the new curve does not deviate more from the original curve
1067 * than a specified error tolerance.
1069 * \param t error tolerance which is still accepted for the new
1070 * (lower degree) curve.
1071 * \return true on success, false if degree reduction is not possible
1072 * within the given tolerance.
1074 bool BezierCurve2D::reduceDegree(double tol
)
1076 unsigned int n
= UInt32(control_points
.size()) - 1; // orig cps: 0, ..., n
1079 // cannot degree reduce a first degree curve
1082 DCTPVec3dvector
b_left(n
);
1083 DCTPVec3dvector
b_right(n
);
1086 // calculate b_right:
1087 b_right
[0] = control_points
[0];
1089 for(i
= 1; i
< n
; ++i
)
1091 b_right
[i
] = (control_points
[i
] * n
- b_right
[i
- 1] * i
) *
1092 (1.0 / double(n
- i
));
1095 // calculate b_left:
1096 b_left
[n
- 1] = control_points
[n
];
1098 for(i
= n
- 1; i
> 0; --i
)
1100 b_left
[i
- 1] = (control_points
[i
] * n
- b_left
[i
] * (n
- i
)) *
1104 // check for introduced error:
1105 double quad_error
= tol
* tol
;
1108 unsigned int n_half
= n
>> 1;
1109 unsigned int n_half1
= (n
+ 1) >> 1;
1111 // for (i = 0; i < n_half; ++i)
1113 // control_points[ i ] = b_right[ i ];
1115 for(i
= n_half1
; i
< n
; ++i
)
1117 b_right
[i
] = b_left
[i
];
1120 if(n_half
!= n_half1
)
1122 dist4d
= homogeniousDistanceSquared(b_right
[n_half
], b_left
[n_half
]);
1123 b_right
[n_half
] = b_left
[n_half
] * 0.5 + b_right
[n_half
] * 0.5;
1127 dist4d
= homogeniousDistanceSquared(control_points
[n_half
],
1128 0.5 * (b_right
[n_half
- 1] + b_left
[n_half
]));
1134 double minweight
= b_right
[0][2];
1136 for(i
= 1; i
< n
; ++i
)
1138 if(minweight
> b_right
[i
][2])
1139 minweight
= b_right
[i
][2];
1142 if(minweight
< DCTP_EPS
)
1144 // can't degree reduce a curve with zero weight(s)...
1148 if(dist4d
/ (minweight
* minweight
) > quad_error
)
1153 control_points
= b_right
;
1154 // setControlPointVector( b_new );
1160 unsigned int BezierCurve2D::computeNonratApproximationDegree(double eps
) const
1162 BezierCurve2D nonrat_curve
;
1163 bool rational
= false;
1164 unsigned int n
= UInt32(control_points
.size());
1168 BezierCurve2D deriv_curve
;
1169 BezierCurve2D diff_curve
;
1172 d0
[2] = d1
[2] = 0.0;
1174 for(i
= 0; i
< n
; ++i
)
1176 if(osgAbs(control_points
[i
][2] - 1.0) > DCTP_EPS
)
1188 // end control points
1189 nonrat_cp
[0] = control_points
[0][0] / control_points
[0][2];
1190 nonrat_cp
[1] = control_points
[0][1] / control_points
[0][2];
1191 nonrat_curve
.control_points
.push_back(nonrat_cp
);
1192 nonrat_cp
[0] = control_points
[n
- 1][0] / control_points
[n
- 1][2];
1193 nonrat_cp
[1] = control_points
[n
- 1][1] / control_points
[n
- 1][2];
1194 nonrat_curve
.control_points
.push_back(nonrat_cp
);
1197 deriv_curve
.control_points
= control_points
;
1200 deriv_curve
.CalculateDerivativeCurve();
1201 k
= UInt32(deriv_curve
.control_points
.size()) - 1;
1202 d0
[0] = deriv_curve
.control_points
[0][0] / deriv_curve
.control_points
[0][2];
1203 d0
[1] = deriv_curve
.control_points
[0][1] / deriv_curve
.control_points
[0][2];
1204 d1
[0] = deriv_curve
.control_points
[k
][0] / deriv_curve
.control_points
[k
][2];
1205 d1
[1] = deriv_curve
.control_points
[k
][1] / deriv_curve
.control_points
[k
][2];
1208 // add points to hermite curve
1209 nonrat_curve
.AddNthHermitePoints(d0
, d1
);
1212 CalculateDifferenceCurve(nonrat_curve
, diff_curve
);
1213 if(diff_curve
.CalculateSupinumSquared() < eps
* eps
)
1219 // degree might be too high by one
1220 nonrat_curve
.reduceDegree(eps
);
1222 return UInt32(nonrat_curve
.control_points
.size()) - 1;
1226 void BezierCurve2D::CalculateDerivativeCurve()
1230 BezierCurve2D deriv_curve
;
1231 BezierCurve2D temp_curve
;
1232 std::vector
<double> squared_weight
;
1233 bool rational
= false;
1235 // check for single point curves
1236 n
= UInt32(control_points
.size());
1239 control_points
[0] = Vec3d(0.0, 0.0, 1.0);
1242 // deriv: P'(t)/w'(t)
1243 CalculatePolyDerivCurve(deriv_curve
);
1245 // check for non-rational curves
1246 for(i
= 0; i
< n
; ++i
)
1248 if(osgAbs(control_points
[i
][2] - 1.0) > DCTP_EPS
)
1258 control_points
= deriv_curve
.control_points
;
1260 for(i
= 0; i
< n
- 1; ++i
)
1262 control_points
[i
][2] = 1.0;
1268 // deriv: P'(t)w(t) / w(t)w'(t), temp: P(t)w'(t) / w(t)w'(t)
1269 temp_curve
.control_points
= control_points
;
1270 temp_curve
.CrossMultiply(deriv_curve
);
1272 // deriv: ( P'(t)w(t) - P(t)w'(t) ) / 1
1273 for(i
= 0; i
< deriv_curve
.control_points
.size(); ++i
)
1275 deriv_curve
.control_points
[i
] -= temp_curve
.control_points
[i
];
1276 deriv_curve
.control_points
[i
][2] = 1.0;
1279 // elevate degree from 2n-1 to 2n
1280 deriv_curve
.DegreeElevate();
1282 // deriv: ( P'(t)w(t) - P(t)w'(t) )~ / w(t)^2
1283 SquareWeight(squared_weight
);
1285 for(i
= 0; i
< deriv_curve
.control_points
.size(); ++i
)
1287 deriv_curve
.control_points
[i
][2] = squared_weight
[i
];
1290 control_points
= deriv_curve
.control_points
;
1294 void BezierCurve2D::CalculateNOverIVector(std::vector
<double> &NOverI
, const unsigned int n
) const
1298 NOverI
.resize(n
+ 1);
1299 NOverI
[0] = NOverI
[n
] = 1.0;
1301 for(i
= 1; i
<= n
/ 2; ++i
)
1303 NOverI
[i
] = NOverI
[i
- 1] * (n
+ 1 - i
) / i
;
1304 NOverI
[n
- i
] = NOverI
[i
];
1309 void BezierCurve2D::CalculatePolyDerivCurve(BezierCurve2D
&DerivativeCurve
) const
1311 const unsigned int n
= UInt32(control_points
.size()) - 1;
1314 DerivativeCurve
.control_points
.resize(n
);
1316 for(i
= 0; i
< n
; ++i
)
1318 DerivativeCurve
.control_points
[i
] = (control_points
[i
+ 1] - control_points
[i
]) * n
;
1323 void BezierCurve2D::CrossMultiply(BezierCurve2D
&OtherCurve
)
1325 std::vector
<Vec3d
> p_wo
;
1326 std::vector
<Vec3d
> po_w
;
1327 const unsigned int n
= UInt32(control_points
.size()) - 1;
1328 const unsigned int m
= UInt32(OtherCurve
.control_points
.size()) - 1;
1329 const unsigned int nm
= n
+ m
;
1333 std::vector
<double> i_over_n
;
1334 std::vector
<double> j_over_m
;
1335 std::vector
<double> k_over_nm
;
1337 // calculate a over b arrays
1338 CalculateNOverIVector(i_over_n
, n
);
1339 CalculateNOverIVector(j_over_m
, m
);
1340 CalculateNOverIVector(k_over_nm
, nm
);
1343 p_wo
.resize(nm
+ 1);
1344 po_w
.resize(nm
+ 1);
1346 for(k
= 0; k
<= nm
; ++k
)
1348 p_wo
[k
] = po_w
[k
] = Vec3d(0.0, 0.0, 0.0);
1351 // cross multiply (leave out Bernstein factor)
1352 for(i
= 0; i
<= n
; ++i
)
1356 for(j
= 0; j
<= m
; ++j
)
1358 p_wo
[k
] += control_points
[i
] * OtherCurve
.control_points
[j
][2] * i_over_n
[i
] * j_over_m
[j
];
1359 po_w
[k
] += OtherCurve
.control_points
[j
] * control_points
[i
][2] * i_over_n
[i
] * j_over_m
[j
];
1364 // divide by new Bernstein factor
1365 for(k
= 0; k
<= nm
; ++k
)
1367 p_wo
[k
] /= k_over_nm
[k
];
1368 po_w
[k
] /= k_over_nm
[k
];
1371 control_points
= p_wo
;
1372 OtherCurve
.control_points
= po_w
;
1376 void BezierCurve2D::SquareWeight(std::vector
<double> &Squared
) const
1378 const unsigned int n
= UInt32(control_points
.size()) - 1;
1379 const unsigned int nn
= n
* 2;
1383 std::vector
<double> i_over_n
;
1384 std::vector
<double> k_over_nn
;
1386 // calculate a over b arrays
1387 CalculateNOverIVector(i_over_n
, n
);
1388 CalculateNOverIVector(k_over_nn
, nn
);
1391 Squared
.resize(nn
+ 1);
1393 for(k
= 0; k
<= nn
; ++k
)
1398 // cross multiply (leave out Bernstein factor)
1399 for(i
= 0; i
<= n
; ++i
)
1401 for(j
= 0; j
<= n
; ++j
)
1404 Squared
[k
] += control_points
[i
][2] * control_points
[j
][2] * i_over_n
[i
] * i_over_n
[j
];
1408 // divide by new Bernstein factor
1409 for(k
= 0; k
<= nn
; ++k
)
1411 Squared
[k
] /= k_over_nn
[k
];
1416 double BezierCurve2D::CalculateSupinumSquared() const
1418 const unsigned int n
= UInt32(control_points
.size());
1424 // generalized convex hull property
1425 sup
= (control_points
[0][0] * control_points
[0][0] + control_points
[0][1] * control_points
[0][1])
1426 / (control_points
[0][2] * control_points
[0][2]);
1428 for(i
= 1; i
< n
; ++i
)
1430 curr
= (control_points
[i
] + control_points
[i
- 1]) * 0.5;
1431 diff
= (curr
[0] * curr
[0] + curr
[1] * curr
[1]) / (curr
[2] * curr
[2]);
1438 diff
= (control_points
[n
- 1][0] * control_points
[n
- 1][0] + control_points
[n
- 1][1] * control_points
[n
- 1][1])
1439 / (control_points
[n
- 1][2] * control_points
[n
- 1][2]);
1445 // std::cerr << " CalculateSupinumSquared::sup: " << sup << std::endl;
1450 void BezierCurve2D::CalculateDifferenceCurve(const BezierCurve2D
&Other
, BezierCurve2D
&Diff
) const
1452 BezierCurve2D temp_curve
;
1454 bool rational
= false;
1456 Diff
.control_points
= control_points
;
1457 temp_curve
.control_points
= Other
.control_points
;
1459 // check for non-rational curves
1460 for(i
= 0; i
< control_points
.size(); ++i
)
1462 if(osgAbs(control_points
[i
][2] - 1.0) > DCTP_EPS
)
1471 for(i
= 0; i
< temp_curve
.control_points
.size(); ++i
)
1473 if(osgAbs(temp_curve
.control_points
[i
][2] - 1.0) > DCTP_EPS
)
1483 // rational curvel -> cross multiply with denominator
1484 Diff
.CrossMultiply(temp_curve
);
1488 // polynomial curves -> elevate to same degree
1489 while(Diff
.control_points
.size() < temp_curve
.control_points
.size() )
1491 Diff
.DegreeElevate();
1493 while(temp_curve
.control_points
.size() < control_points
.size() )
1495 temp_curve
.DegreeElevate();
1499 for(i
= 0; i
< Diff
.control_points
.size(); ++i
)
1501 Diff
.control_points
[i
][0] -= temp_curve
.control_points
[i
][0];
1502 Diff
.control_points
[i
][1] -= temp_curve
.control_points
[i
][1];
1507 void BezierCurve2D::DegreeElevate()
1509 const unsigned int n
= UInt32(control_points
.size());
1512 control_points
.push_back(control_points
[n
- 1]);
1514 for(i
= n
- 1; i
!= 0; --i
)
1516 double alpha
= i
/ double(n
);
1517 double malpha
= 1.0 - alpha
;
1519 control_points
[i
] = control_points
[i
- 1] * alpha
+ control_points
[i
] * malpha
;
1524 void BezierCurve2D::AddNthHermitePoints(Vec3d d0
, Vec3d d1
)
1526 const unsigned int n
= UInt32(control_points
.size()) / 2;
1528 std::vector
<double> n_over_i
;
1537 for(i
= UInt32(control_points
.size()) - 1; i
>= UInt32(control_points
.size()) - n
; --i
)
1546 CalculateNOverIVector(n_over_i
, n
);
1548 for(i
= 0; i
<= n
; ++i
)
1551 d0
-= control_points
[n
- i
] * (n_over_i
[i
] * sign
);
1553 d1
-= control_points
[2 * n
+ 1 - i
] * (n_over_i
[i
] * sign
);
1555 // std::cerr<<"i: " << i << " d1: " << d1 << std::endl;
1561 // std::cerr<<"bah0: "<< d0 << std::endl;
1562 control_points
[n
] = d0
;
1563 // std::cerr<<"bah1: "<< d1 << std::endl;
1564 control_points
[n
+ 1] = d1
;
1565 // std::cerr<<"bah3"<<std::endl;
1566 // for ( i = 0; i < control_points.size(); ++i )
1568 // std::cerr<<"AddNthHermitePoints:: cps[i]: " << control_points[i] << std::endl;