Curve evalBezier( const vector< Vec3f >& P, unsigned steps ) { // Check if( P.size() < 4 || P.size() % 3 != 1 ) { cerr << "evalBezier must be called with 3n+1 control points." << endl; exit( 0 ); } cerr << "\t>>> evalBezier has been called with the following input:" << endl; cerr << "\t>>> Control points (type vector< Vec3f >): "<< endl; for( unsigned i = 0; i < P.size(); ++i ) { cerr << "\t>>> "; printTranspose(P[i]); cerr << endl; } cerr << "\t>>> Steps (type steps): " << steps << endl; // Return value: initialize with starting values. Note the // initial normal of [0,1,0] will result in the // "counterclockwise" specification for 2D curves on the xy-plane. // I.e., the normal of the curve is facing left from the // direction of travel. For 3D curves, the choice of direction is // kind of arbitrary. so this initial normal works too. vector< Vec3f >::const_iterator segmentStart = P.begin(); vector< Vec3f >::const_iterator segmentEnd = P.size() > 4 ? P.begin() + 4 : P.end(); Curve R ( coreBezier ( P[0], P[1], P[2], P[3], Vec3f( 0, 0, 1 ), steps ) ); // Build the rest of the curve for( unsigned i = 0; i < ( P.size() - 4 ) / 3; ++i ) { Vec3f p0 = P[ 3 * i + 3 ]; Vec3f p1 = P[ 3 * i + 4 ]; Vec3f p2 = P[ 3 * i + 5 ]; Vec3f p3 = P[ 3 * i + 6 ]; vector< CurvePoint > Rnew ( coreBezier ( p0, p1, p2, p3, R.back().B, // init with previous computed binormal steps ) ); R.pop_back(); R.insert( R.end(), Rnew.begin(), Rnew.end() ); } // Finally, check for loop and patch ends if( approx( R.front().V, R.back().V ) && approx( R.front().T, R.back().T ) && !approx( R.front().N, R.back().N ) ) { Vec3f axis = R.front().N.cross(R.back().N); float angle = FW::acos( dot( R.front().N, R.back().N ) ); float sign = ( dot( axis, R.front().T ) > 0 ) ? 1.f : -1.f; for( unsigned i = 0; i < R.size(); ++i ) { float d = float( i ) / ( R.size() - 1 ); Mat3f M = Mat3f::rotation( R[i].T, -sign * d * angle ); R[i].N = M * R[i].N; R[i].B = M * R[i].B; } } return R; }