// Description: // Bezier curve template consisting of Bezier segments. // // Copyright (C) 2001 Frank Becker // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 2 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details // #include #include #include #include #include #include //Contruct Bezier curve with a given number of points per Bezier segment template< class POINT > BezierCurve::BezierCurve( const int pps): _controlPoints(0), _numSegments(-1), _pointsPerSegment( pps) { XTRACE(); // LOG_INFO << _pointsPerSegment << " points per bezier segment" << endl; } //Cleanup template< class POINT> BezierCurve::~BezierCurve() { XTRACE(); if( _controlPoints) delete[] _controlPoints; } //Change control point at given index template< class POINT> void BezierCurve::SetControlPoint( int idx, const POINT &np) { XTRACE(); if( (idx>=0) && (idx<(_numSegments*3+1))) { _controlPoints[ idx] = np; } } //Get control point info template< class POINT> POINT BezierCurve::GetControlPoint( int idx) { XTRACE(); if( (idx>=0) && (idx<(_numSegments*3+1))) { return( _controlPoints[ idx]); } POINT p; return( p); } //Add a segment to Bezier curve template< class POINT> void BezierCurve::AddSegment( const POINT &p) { XTRACE(); if( _numSegments == -1) { _controlPoints = new POINT[1]; _controlPoints[ 0] = p; _numSegments = 0; } else { _numSegments++; POINT *newControlPoints = new POINT[ 3*_numSegments+1]; memcpy( newControlPoints, _controlPoints, sizeof( POINT) * (3*(_numSegments-1)+1)); delete[] _controlPoints; _controlPoints = newControlPoints; //first control point of this segment is the last of the bezier curve POINT p1 = _controlPoints[ 3*_numSegments-3]; POINT dp = (p-p1)/3.0; //place the middle 2 control points on line segment //(i.e. new bezier segment is/straight line). _controlPoints[ 3*_numSegments-2] = p1+dp; _controlPoints[ 3*_numSegments-1] = p-dp; //last control point is the point that was passed in _controlPoints[ 3*_numSegments ] = p; } } //Delete Bezier segments from-to. template< class POINT> void BezierCurve::DeleteSegments( int fidx, int tidx) { XTRACE(); if( fidx==-1) fidx=0; fidx = fidx - (fidx%3); tidx = tidx + (3-(tidx%3)); if( (tidx>3) && (tidx>(_numSegments*3))) tidx = _numSegments*3; if( (fidx<0) || (fidx>(_numSegments*3)) || (tidx<0) || (tidx>(_numSegments*3))) { return; } int nSegments = (tidx-fidx)/3; _numSegments -= nSegments; POINT *newControlPoints = new POINT[ 3*_numSegments+1]; int i; for( i=0; i<=fidx; i++) { newControlPoints[ i] = _controlPoints[ i]; } int j; for( j=1; i<(_numSegments*3+1); j++, i++) { newControlPoints[ i] = _controlPoints[ tidx+j]; } delete[] _controlPoints; _controlPoints = newControlPoints; } //Move curve by vector p template< class POINT> void BezierCurve::MoveCurve( const POINT &p) { XTRACE(); for( int i=0; i<(3*_numSegments+1); i++) { _controlPoints[ i] = _controlPoints[i] + p; } } //Scale curve by s template< class POINT> void BezierCurve::ScaleCurve( const float s) { XTRACE(); for( int i=0; i<(3*_numSegments+1); i++) { _controlPoints[ i] = _controlPoints[ i] * s; } } //Get point on curve at time 0<=t<=_numSegments template< class POINT> void BezierCurve::GetPos( float t, POINT &pos) { XTRACE(); if( (t<0.0) || (t>(float)_numSegments)) return; float seg = (int)floor(t); float dt = t-seg; Bezier bez( &_controlPoints[ (int)(seg*3)]); bez.Pos( dt, pos); } //Get tangent for point on curve at time 0<=t<=_numSegments template< class POINT> void BezierCurve::GetTangent( float t, POINT &tangent) { XTRACE(); if( (t<0.0) || (t>(float)_numSegments)) return; float seg = (int)floor(t); float dt = t-seg; Bezier bez( &_controlPoints[ (int)(seg*3)]); bez.TVec( dt, tangent); } //Convert Bezier curve to an outline. Outline has to be deleted[] by client template< class POINT> void BezierCurve::ConvertToOutline( POINT *&outline, int &numPoints) { XTRACE(); if( _numSegments <= 0) { outline = 0; numPoints = 0; return; } numPoints = _pointsPerSegment * _numSegments + 1; outline = new POINT[ numPoints]; float dt = 1.0/_pointsPerSegment; int outlineIndex = 0; for( int i=0; i<_numSegments; i++) { Bezier bez( &_controlPoints[ i*3]); float t=0; for( int tt=0; tt<_pointsPerSegment; tt++) { bez.Pos( t, outline[ outlineIndex]); outlineIndex++; t+=dt; } } outline[ outlineIndex] = _controlPoints[ _numSegments*3]; } //Convert partial Bezier curve to an outline. //Outline has to be deleted[] by client. template< class POINT> void BezierCurve::ConvertToOutline( int fidx, int tidx, POINT *&outline, int &numPoints) { XTRACE(); if( fidx==-1) fidx=0; fidx = fidx - (fidx%3); tidx = tidx + (3-(tidx%3)); if( (tidx>3) && (tidx>(_numSegments*3))) tidx = _numSegments*3; if( (fidx<0) || (fidx>(_numSegments*3)) || (tidx<0) || (tidx>(_numSegments*3))) { numPoints = 0; outline = 0; return; } int nSegments = (tidx-fidx)/3; numPoints = _pointsPerSegment * nSegments + 1; outline = new POINT[ numPoints]; float dt = 1.0/_pointsPerSegment; int outlineIndex = 0; for( int i=0; i bez( &_controlPoints[ fidx + i*3]); float t=0; for( int tt=0; tt<_pointsPerSegment; tt++) { bez.Pos( t, outline[ outlineIndex]); outlineIndex++; t+=dt; } } outline[ outlineIndex] = _controlPoints[ fidx + nSegments*3]; } //Convert Bezier curve to an outline with perpendicular vectors. //Outline and pvecs have to be deleted[] by client. template< class POINT> void BezierCurve::ConvertToOutlineWithPVec( POINT *&outline, POINT *&pvecs, int &numPoints) { XTRACE(); if( _numSegments <= 0) { outline = 0; numPoints = 0; return; } numPoints = _pointsPerSegment * _numSegments + 1; outline = new POINT[ numPoints]; pvecs = new POINT[ numPoints]; float dt = 1.0/_pointsPerSegment; int outlineIndex = 0; for( int i=0; i<_numSegments; i++) { Bezier bez( &_controlPoints[ i*3]); float t=0; for( int tt=0; tt<_pointsPerSegment; tt++) { bez.Pos( t, outline[ outlineIndex]); bez.PVec( t, pvecs[ outlineIndex]); outlineIndex++; t+=dt; } if( i==(_numSegments-1)) { bez.Pos( t, outline[ outlineIndex]); bez.PVec( t, pvecs[ outlineIndex]); } } // outline[ outlineIndex] = _controlPoints[ _numSegments*3]; } //Save Bezier curve to file. template< class POINT> bool BezierCurve::Save( const char *filename) { XTRACE(); FILE *fp; if( (fp=fopen(filename,"w")) != NULL) { int dim = POINT::dimension(); fprintf( fp, "%d\n", _pointsPerSegment); fprintf( fp, "%d\n", _numSegments); for( int idx=0; idx<(_numSegments*3+1); idx++) { for( int d=0; d bool BezierCurve::Load( const char *filename) { XTRACE(); if( !ResourceManagerS::instance()->selectResource( string(filename))) { LOG_ERROR << "Curve [" << filename << "] not found." << endl; return false; } ziStream &infile = ResourceManagerS::instance()->getInputStream(); string line; int oldPointsPerSegment = _pointsPerSegment; int oldNumSegments = _numSegments; getline( infile, line); if( sscanf( line.c_str(), "%d\n", &_pointsPerSegment) != 1) { LOG_ERROR << "Expected points per segment" << endl; return false; } getline( infile, line); if( sscanf( line.c_str(), "%d\n", &_numSegments) != 1) { _pointsPerSegment = oldPointsPerSegment; LOG_ERROR << "Expected number of segments" << endl; return false; } if( oldNumSegments != _numSegments) { delete[] _controlPoints; _controlPoints = new POINT[ 3*_numSegments+1]; } for( int idx=0; idx<(_numSegments*3+1); idx++) { // LOG_INFO << "Loading control point #" << idx << endl; //FIXME: only support 1d, 2d and 3d bezier curves... float val[3]; getline( infile, line); int dim = sscanf( line.c_str(), "%f %f %f\n", &val[0], &val[1], &val[2]); if( dim != POINT::dimension()) { for( int i=dim; i