fixed: auto_ptr -> unique_ptr
[opensg.git] / Source / System / NodeCores / Drawables / Nurbs / Internal / OSGBezierCurve3D.cpp
blobcea9cc4cecc3f959c68e59300e030b6b2dbc1f9f
1 /*---------------------------------------------------------------------------*\
2 * OpenSG NURBS Library *
3 * *
4 * *
5 * Copyright (C) 2001-2006 by the University of Bonn, Computer Graphics Group*
6 * *
7 * http://cg.cs.uni-bonn.de/ *
8 * *
9 * contact: edhellon@cs.uni-bonn.de, guthe@cs.uni-bonn.de, rk@cs.uni-bonn.de *
10 * *
11 \*---------------------------------------------------------------------------*/
12 /*---------------------------------------------------------------------------*\
13 * License *
14 * *
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. *
18 * *
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. *
23 * *
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. *
27 * *
28 \*---------------------------------------------------------------------------*/
29 /*---------------------------------------------------------------------------*\
30 * Changes *
31 * *
32 * *
33 * *
34 * *
35 * *
36 * *
37 \*---------------------------------------------------------------------------*/
38 #ifdef WIN32
39 # pragma warning (disable : 985)
40 #endif
41 #include "OSGBezierCurve3D.h"
42 #include "OSGBaseTypes.h"
44 OSG_USING_NAMESPACE
47 #ifdef _DEBUG
48 #ifdef WIN32
49 #undef THIS_FILE
50 static char THIS_FILE[] = __FILE__;
51 #endif
52 #endif
55 //setup functions
56 int BezierCurve3D::setControlPointVector(const DCTPVec4dvector& cps)
58 if(cps.size() < 2)
59 return -1; //invalid dimension, at least two points are required
60 control_points = cps;
61 return 0;
65 //some REAL functionality
66 //! Compute the value of the Bezier curve at given the parameter value.
67 /*!
68 * This function computes the value of the Bezier curve
69 * at the given parameter value, using de Casteljau's method of
70 * repeated linear interpolations.
72 * \param t the parameter value at which to compute the approximation
73 * \param error flag to indicate if an error has happened
74 * \return the approximated value
77 Vec3d BezierCurve3D::computewdeCasteljau(double t, int &error)
79 Vec4d rat_res = computewdeCasteljau4D(t, error);
80 Vec3d res;
82 res[0] = rat_res[0] / rat_res[3];
83 res[1] = rat_res[1] / rat_res[3];
84 res[2] = rat_res[2] / rat_res[3];
86 return res;
90 Vec4d BezierCurve3D::computewdeCasteljau4D(double t, int &error)
92 //FIXME: verification before goin' into computation!!
93 // Vec3dvector Q = control_points; //local array not to destroy da other points
94 const unsigned int n = UInt32(control_points.size()) - 1;
95 Vec4d rat_res;
97 if(n < 1) //too few points, at least 2 needed
99 error = -1;
100 rat_res = control_points[0];
101 rat_res[0] = DCTP_EPS * floor(rat_res[0] / DCTP_EPS);
102 rat_res[1] = DCTP_EPS * floor(rat_res[1] / DCTP_EPS);
103 rat_res[2] = DCTP_EPS * floor(rat_res[2] / DCTP_EPS);
104 rat_res[3] = DCTP_EPS * floor(rat_res[3] / DCTP_EPS);
105 return rat_res;
108 unsigned int i, k;
109 Vec4d *Q = new Vec4d[n];
110 const double t2 = 1.0 - t;
111 const unsigned int n1 = n - 1;
113 for(i = 0; i < n; ++i)
115 const unsigned int i1 = i + 1;
116 Q[i] = control_points[i] * t2 + control_points[i1] * t;
119 for(k = 0; k < n1; ++k)
121 const unsigned int nk = n1 - k;
123 for(i = 0; i < nk; ++i)
125 const unsigned int i1 = i + 1;
126 Q[i] *= t2;
127 Q[i] += Q[i1] * t;
131 rat_res = Q[0];
133 delete[] Q;
134 return rat_res;
138 //! Compute the linear approximation of the Bezier curve at the given parameter value.
140 * This function computes the linear approximation of the Bezier curve
141 * at the given parameter value.
143 * \param t the parameter value at which to compute the approximation
144 * \param error flag to indicate if an error has happened
145 * \return the approximated value
148 Vec3d BezierCurve3D::computeLinearApproximation(double t, int &error)
150 //FIXME: verification before goin' into computation!!
151 DCTPVec4dvector::size_type n = control_points.size() - 1;
152 Vec3d result(0.0, 0.0, 0.0);
153 Vec4d rat_res;
155 if(n < 1)
156 { //too few points, at least 2 needed
157 error = -1;
158 return result;
160 rat_res = control_points[0] * t + control_points[n] * (1 - t);
161 result[0] = rat_res[0] / rat_res[3];
162 result[1] = rat_res[1] / rat_res[3];
163 result[2] = rat_res[2] / rat_res[3];
165 return (result);
168 //! Subdivide Bezier curve at midpoint into two new curves.
170 * This function subdivides a Bezier curve into two new Bezier curves
171 * of the same degree at the midpoint, using de Casteljau's
172 * algorithm.
174 * \param newbeziers the two new Bezier curves (returned in a vector of size 2)
175 * \return zero on success, and a negative value when some error occured.
179 int BezierCurve3D::midPointSubDivision(bezier3dvector &newbeziers)
181 // This function is time critical so optimize at the cost of readabiltity...
182 DCTPVec4dvector::size_type n = control_points.size();
184 if(n < 2) //too few points, at least 2 needed to split curve
186 return -1;
189 newbeziers.resize(2); // we return exactly two curves
190 DCTPVec4dvector::size_type i, k;
192 DCTPVec4dvector &cp1 = newbeziers[0].control_points;
193 DCTPVec4dvector &cp2 = newbeziers[1].control_points;
194 cp1.clear(); // very important for performance (no copying around of obsolte stuff!)
195 cp2.clear(); // very important for performance (no copying around of obsolte stuff!)
196 cp1.resize(n);
197 cp2.resize(n);
199 for(k = 0; k < n; ++k)
201 cp1[k] = control_points[k];
204 --n;
206 for(k = 0; k < n; ++k)
208 cp2[n - k] = cp1[n];
210 for(i = n; i > k; --i)
212 cp1[i] += cp1[i - 1];
213 cp1[i] *= 0.5;
217 cp2[0] = cp1[n];
218 // newbeziers[ 0 ].setControlPointVector( cp1 );
219 // newbeziers[ 1 ].setControlPointVector( cp2 );
220 return 0;
223 //! Subdivide Bezier curve at midpoint into two new curves.
225 * This function subdivides a Bezier curve into two new Bezier curves
226 * of the same degree at the midpoint, using de Casteljau's
227 * algorithm.
229 * \param newbeziers the two new Bezier curves (returned in a vector of size 2)
230 * \return zero on success, and a negative value when some error occured.
234 int BezierCurve3D::midPointSubDivision(BezierCurve3D &newcurve)
236 // This function is time critical so optimize at the cost of readabiltity...
237 DCTPVec4dvector::size_type n = control_points.size();
239 if(n < 2)
241 return -1; //too few points, at least 2 needed to split curve
244 DCTPVec4dvector::size_type i, k;
246 DCTPVec4dvector &cp1 = control_points;
247 DCTPVec4dvector &cp2 = newcurve.control_points;
249 cp2.clear(); // very important for performance (no copying around of obsolte stuff!)
250 cp2.resize(n);
252 --n;
254 for(k = 0; k < n; ++k)
256 cp2[n - k] = cp1[n];
258 for(i = n; i > k; --i)
260 cp1[i] += cp1[i - 1];
261 cp1[i] *= 0.5;
265 cp2[0] = cp1[n];
267 return 0;
270 //! Subdivide Bezier curve at t into two new curves.
272 * This function subdivides a Bezier curve into two new Bezier curves
273 * of the same degree at the parameter value `t', using de Casteljau's
274 * algorithm.
276 * \param t the parameter at which to subdivide the curve.
277 * \param newbeziers the two new Bezier curves (returned in a vector of size 2)
278 * \return zero on success, and a negative value when some error occured.
282 int BezierCurve3D::subDivision(double t, bezier3dvector &newbeziers)
284 if(t <= 0.0 || t >= 1.0)
285 return -1; // t must be between (0, 1) exclusive
287 newbeziers.resize(2); // we return exactly two curves
288 DCTPVec4dvector Q = control_points; //local array not to destroy da other points
289 DCTPVec4dvector::size_type n = control_points.size() - 1;
290 DCTPVec4dvector cp1(n + 1);
291 DCTPVec4dvector cp2(n + 1);
292 if(n < 1) //too few points, at least 2 needed to split curve
294 return -1;
297 for(DCTPVec4dvector::size_type k = 0; k < n; ++k)
299 cp1[k] = Q [0];
300 cp2[n - k] = Q [n - k];
302 for(DCTPVec4dvector::size_type i = 0; i < n - k; ++i)
304 Q[i] = Q[i] * (1.0 - t) + Q[i + 1] * t;
308 cp1[n] = Q[0];
309 cp2[0] = Q[0];
310 newbeziers[0].setControlPointVector(cp1);
311 newbeziers[1].setControlPointVector(cp2);
312 return 0;
315 //! Subdivide Bezier curve at t into two new curves.
317 * This function subdivides a Bezier curve into two new Bezier curves
318 * of the same degree at the parameter value `t', using de Casteljau's
319 * algorithm.
321 * \param t the parameter at which to subdivide the curve.
322 * \param newbeziers the two new Bezier curves (returned in a vector of size 2)
323 * \return zero on success, and a negative value when some error occured.
327 int BezierCurve3D::subDivision(double t, BezierCurve3D &newcurve)
329 if(t <= 0.0 || t >= 1.0)
331 return -1; // t must be between (0, 1) exclusive
334 // This function is time critical so optimize at the cost of readabiltity...
335 DCTPVec4dvector::size_type n = control_points.size();
337 if(n < 2)
339 return -1; //too few points, at least 2 needed to split curve
342 double t2 = 1.0 - t;
344 DCTPVec4dvector::size_type i, k;
346 DCTPVec4dvector &cp1 = control_points;
347 DCTPVec4dvector &cp2 = newcurve.control_points;
349 cp2.clear(); // very imporatant for performance (no copying around of obsolte stuff!)
350 cp2.resize(n);
352 --n;
354 for(k = 0; k < n; ++k)
356 cp2[n - k] = cp1[n];
358 for(i = n; i > k; --i)
360 cp1[i] *= t;
361 cp1[i] += cp1[i - 1] * t2;
365 cp2[0] = cp1[n];
367 return 0;
370 //! Approximate curve linearly with given maximum tolerance.
372 * This function approximates the Bezier curve with a polyline. The maximum
373 * error of the approximation will not be larger than the given tolerance.
376 * \param vertices the vertices of the approximating polyline.
377 * \param delta maximum error.
378 * \return zero on success, and a negative value when some error occured.
380 int BezierCurve3D::approximate(std::vector<double> &vertices, double delta, unsigned char strategy)
382 vertices.resize(0);
384 // check for first degree Bezier.
385 if(control_points.size() == 2)
387 vertices.push_back(0.0);
388 vertices.push_back(1.0);
389 return 0;
392 // call recursive subdividing function
393 return approximate_sub(vertices, delta, 0.0, 1.0, strategy);
396 int BezierCurve3D::approximate_sub(std::vector<double> &vertices, double delta, double min, double max, unsigned char strategy)
398 double max_error = 0;
399 Vec3d ae;
400 double aenorm;
401 double t1, t2;
402 int worst_i = 0;
403 Vec3d n0_norm;
404 DCTPVec3dvector eucl;
405 DCTPVec4dvector mycps;
406 int i;
407 std::vector<double> t;
409 int n = UInt32(control_points.size()) - 1;
411 for(i = 0; i <= n; i++)
413 t.push_back((double( n - i ) ) / n);
416 // first, make a copy of the cp vector, and do one
417 // or more de Casteljau steps to get rid of 0 weights
418 mycps = control_points;
420 for(i = 0; i <= n; ++i)
422 unsigned int s = UInt32(mycps.size());
423 bool ok = true;
425 mycps.push_back(mycps[s - 1]);
426 t.push_back(t[s - 1]);
428 for(int j = s; j > i; --j)
430 mycps[j] = (mycps[j] + mycps[j - 1]) * 0.5f;
431 t[j] = (t[j] + t[j - 1]) * 0.5f;
432 if(mycps[j][2] < DCTP_EPS)
433 ok = false;
436 if(ok)
437 break;
440 n = UInt32(mycps.size()) - 1;
442 eucl.resize(n + 1);
444 for(i = 0; i <= n; ++i)
446 eucl[i][0] = mycps[i][0] / mycps[i][3];
447 eucl[i][1] = mycps[i][1] / mycps[i][3];
448 eucl[i][2] = mycps[i][2] / mycps[i][3];
451 if( (strategy & DISTANCE) == LINE_DISTANCE)
453 n0_norm = eucl[n] - eucl[0];
454 aenorm = n0_norm.squareLength();
455 if(aenorm > 1e-300)
457 n0_norm *= 1.0 / aenorm;
459 else
461 n0_norm = Vec3d(0.0, 0.0, 0.0);
465 for(i = 0; i <= n; i++)
467 if( (strategy & DISTANCE) == POINT_DISTANCE)
469 t2 = 1.0 - t[i];
471 else
473 t2 = (eucl[i] - eucl[0]).dot(n0_norm);
474 if(t2 < 0.0)
476 t2 = 0.0;
478 else if(t2 > 1.0)
480 t2 = 1.0;
483 t1 = 1.0 - t2;
484 ae = eucl[i] - eucl[0] * t1 - eucl[n] * t2;
486 // std::cerr << "i: " << i << " ae.x: " << ae[0] << " ae.y: " << ae[1] << std::endl;
488 aenorm = sqrt(ae.squareLength() );
489 if(aenorm > max_error)
491 max_error = aenorm;
492 worst_i = i;
496 // std::cerr.precision( DCTP_PRECISION );
497 // std::cerr << " twopower: " << twopower << std::endl;
498 // std::cerr << " act_error: " << act_error << " max_error: " << max_error << std::endl;
499 // std::cerr << control_points[ 0 ][0] << " " << control_points[ 0 ][1] << std::endl;
500 // std::cerr << control_points[ control_points.size() - 1 ][0] << " " << control_points[ control_points.size() - 1 ][1] << std::endl;
501 if(max_error > delta)
503 // we have to subdivide further
504 BezierCurve3D newbez;
505 int error;
506 double mid;
507 if( (strategy & SUBDIVISION) == MIDPOINT_SUBDIVISION)
509 error = midPointSubDivision(newbez);
510 if(error)
511 return error;
512 mid = (min + max) * 0.5;
514 else
516 mid = (double(worst_i) ) / n;
517 error = subDivision(mid, newbez);
518 if(error)
519 return error;
520 mid = min + (max - min) * mid;
522 approximate_sub(vertices, delta, min, mid, strategy);
523 newbez.approximate_sub(vertices, delta, mid, max, strategy);
525 else
527 // this is a good enough approximation
528 if(!vertices.size() )
530 // this is the first approximation, we have to record both the
531 // startpoint and the endpoint
532 vertices.push_back(min);
533 vertices.push_back(max);
535 else
537 // we had some subdivisions before, we only need to record the endpoint
538 vertices.push_back(max);
541 return 0;
544 // generate a bezier curve through these points
545 int BezierCurve3D::createCurve(DCTPVec4dvector &points)
547 int n = UInt32(points.size()) - 1;
549 if(n < 1)
551 return -1; // too few points
554 control_points.resize(n + 1);
556 control_points[0] = points[0];
557 control_points[n] = points[n];
559 if(n == 1)
561 return 0; // linear curve, so we are done...
564 /* if( m_svvvdCreateMatrix.size( ) == 0 )
566 if( loadCreateMatrices( ) != 0 )
568 return -3; // matrix file not found
572 if( n - 1 > m_svvvdCreateMatrix.size( ) )
574 return -2; // degree too high (sorry...)
577 int i;
578 // int j;
579 // std::vector< std::vector< double > > &mat = m_svvvdCreateMatrix[ n - 2 ];
581 for(i = 1; i < n; ++i)
583 /* std::vector< double > &row = mat[ i ];
584 control_points[ i ][0] = control_points[ i ][1] = control_points[ i ][2] = 0.0;
585 for( j = 0; j <= n; ++j )
587 control_points[ i ] += ( points[ j ] - points[ n >> 1 ] ) * row[ j ];
588 // std::cerr << row[ j ] << " ";
590 control_points[ i ] += points[ n >> 1 ];
591 // std::cerr << std::endl;*/
592 control_points[i] = points[i];
595 // try to approximate it better
596 double maxerr = 1.0;
598 // std::cerr << "n = " << n;
600 unsigned int ui_exit = 0;
601 while(maxerr > 1e-8)
603 std::vector<Vec4d> vcl_err(n + 1);
605 maxerr = 0.0;
607 for(i = 0; i <= n; ++i)
609 int err = 0;
610 vcl_err[i] = points[i] - computewdeCasteljau4D( (double(i) ) / n, err);
612 double acterr = (vcl_err[i]).squareLength();
614 maxerr = osgMax(maxerr, acterr);
617 for(i = 1; i < n; ++i)
619 // std::vector< double > &row = mat[ i ];
620 control_points[i] += vcl_err[i];
623 if( (++ui_exit) > 10000)
625 // for( i = 0; i <= n; ++i )
626 // {
627 // FIXME: operator<< depracated
628 // std::cerr << points[ i ] << std::endl;
629 // }
630 // std::cerr << std::endl;
631 // std::cerr << "approxerr: " << sqrt( maxerr ) << std::endl;
633 return -2;
636 // std::cerr << ", approxerr: " << sqrt( maxerr ) << std::endl;
637 return 0; // everything ok.
641 //! Degree reduce the Bezier curve with t tolerance if possible.
643 * This function tries to degree reduce the Bezier curve ensuring
644 * that the new curve does not deviate more from the original curve
645 * than a specified error tolerance.
647 * \param t error tolerance which is still accepted for the new
648 * (lower degree) curve.
649 * \return true on success, false if degree reduction is not possible
650 * within the given tolerance.
652 bool BezierCurve3D::reduceDegree(double tol)
654 UInt32 n = UInt32(control_points.size()) - 1; // orig cps: 0, ..., n
655 if(n < 2)
657 // cannot degree reduce a first degree curve
658 return false;
660 DCTPVec4dvector b_left(n);
661 DCTPVec4dvector b_right(n);
662 UInt32 i;
664 // calculate b_right:
665 b_right[0] = control_points[0];
667 for(i = 1; i < n; ++i)
669 b_right[i] = (control_points[i] * n - b_right[i - 1] * i) *
670 (1.0 / Real64(n - i));
673 // calculate b_left:
674 b_left[n - 1] = control_points[n];
676 for(i = n - 1; i > 0; --i)
678 b_left[i - 1] = (control_points[i] * n - b_left[i] * (n - i)) *
679 (1.0 / Real64(i));
682 // check for introduced error:
683 Real64 quad_error = tol * tol;
684 double dist4d;
686 unsigned int n_half = n >> 1;
687 unsigned int n_half1 = (n + 1) >> 1;
689 // for (i = 0; i < n_half; ++i)
690 // {
691 // control_points[ i ] = b_right[ i ];
692 // }
693 for(i = n_half1; i < n; ++i)
695 b_right[i] = b_left[i];
698 if(n_half != n_half1)
700 dist4d = b_right[n_half].dist2(b_left[n_half]);
701 b_right[n_half] = b_left[n_half] * 0.5 + b_right[n_half] * 0.5;
703 else
705 dist4d = control_points[n_half].dist2(0.5 * (b_right[n_half - 1] + b_left[n_half]));
710 //Vec3d dist;
711 double minweight = b_right[0][3];
713 for(i = 1; i < n; ++i)
715 if(minweight > b_right[i][3])
716 minweight = b_right[i][3];
719 if(minweight < DCTP_EPS)
721 // can't degree reduce a curve with zero weight(s)...
722 return false;
725 if(dist4d / (minweight * minweight) > quad_error)
727 return false;
730 control_points = b_right;
731 // setControlPointVector( b_new );
733 return true;