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)
41 #include "OSGBezierCurve3D.h"
42 #include "OSGBaseTypes.h"
50 static char THIS_FILE
[] = __FILE__
;
56 int BezierCurve3D::setControlPointVector(const DCTPVec4dvector
& cps
)
59 return -1; //invalid dimension, at least two points are required
65 //some REAL functionality
66 //! Compute the value of the Bezier curve at given the parameter value.
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
);
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];
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;
97 if(n
< 1) //too few points, at least 2 needed
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
);
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;
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);
156 { //too few points, at least 2 needed
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];
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
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
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!)
199 for(k
= 0; k
< n
; ++k
)
201 cp1
[k
] = control_points
[k
];
206 for(k
= 0; k
< n
; ++k
)
210 for(i
= n
; i
> k
; --i
)
212 cp1
[i
] += cp1
[i
- 1];
218 // newbeziers[ 0 ].setControlPointVector( cp1 );
219 // newbeziers[ 1 ].setControlPointVector( cp2 );
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
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();
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!)
254 for(k
= 0; k
< n
; ++k
)
258 for(i
= n
; i
> k
; --i
)
260 cp1
[i
] += cp1
[i
- 1];
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
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
297 for(DCTPVec4dvector::size_type k
= 0; k
< n
; ++k
)
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
;
310 newbeziers
[0].setControlPointVector(cp1
);
311 newbeziers
[1].setControlPointVector(cp2
);
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
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();
339 return -1; //too few points, at least 2 needed to split curve
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!)
354 for(k
= 0; k
< n
; ++k
)
358 for(i
= n
; i
> k
; --i
)
361 cp1
[i
] += cp1
[i
- 1] * t2
;
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
)
384 // check for first degree Bezier.
385 if(control_points
.size() == 2)
387 vertices
.push_back(0.0);
388 vertices
.push_back(1.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;
404 DCTPVec3dvector eucl
;
405 DCTPVec4dvector mycps
;
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());
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
)
440 n
= UInt32(mycps
.size()) - 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();
457 n0_norm
*= 1.0 / aenorm
;
461 n0_norm
= Vec3d(0.0, 0.0, 0.0);
465 for(i
= 0; i
<= n
; i
++)
467 if( (strategy
& DISTANCE
) == POINT_DISTANCE
)
473 t2
= (eucl
[i
] - eucl
[0]).dot(n0_norm
);
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
)
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
;
507 if( (strategy
& SUBDIVISION
) == MIDPOINT_SUBDIVISION
)
509 error
= midPointSubDivision(newbez
);
512 mid
= (min
+ max
) * 0.5;
516 mid
= (double(worst_i
) ) / n
;
517 error
= subDivision(mid
, newbez
);
520 mid
= min
+ (max
- min
) * mid
;
522 approximate_sub(vertices
, delta
, min
, mid
, strategy
);
523 newbez
.approximate_sub(vertices
, delta
, mid
, max
, strategy
);
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
);
537 // we had some subdivisions before, we only need to record the endpoint
538 vertices
.push_back(max
);
544 // generate a bezier curve through these points
545 int BezierCurve3D::createCurve(DCTPVec4dvector
&points
)
547 int n
= UInt32(points
.size()) - 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
];
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...)
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
598 // std::cerr << "n = " << n;
600 unsigned int ui_exit
= 0;
603 std::vector
<Vec4d
> vcl_err(n
+ 1);
607 for(i
= 0; i
<= n
; ++i
)
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 )
627 // FIXME: operator<< depracated
628 // std::cerr << points[ i ] << std::endl;
630 // std::cerr << std::endl;
631 // std::cerr << "approxerr: " << sqrt( maxerr ) << std::endl;
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
657 // cannot degree reduce a first degree curve
660 DCTPVec4dvector
b_left(n
);
661 DCTPVec4dvector
b_right(n
);
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
));
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
)) *
682 // check for introduced error:
683 Real64 quad_error
= tol
* tol
;
686 unsigned int n_half
= n
>> 1;
687 unsigned int n_half1
= (n
+ 1) >> 1;
689 // for (i = 0; i < n_half; ++i)
691 // control_points[ i ] = b_right[ i ];
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;
705 dist4d
= control_points
[n_half
].dist2(0.5 * (b_right
[n_half
- 1] + b_left
[n_half
]));
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)...
725 if(dist4d
/ (minweight
* minweight
) > quad_error
)
730 control_points
= b_right
;
731 // setControlPointVector( b_new );