//***************************************************************************************** // Truevision - a 3d modeler for gnome and povray // // spline2d.cc // // Vincent LE PRINCE // Copyright (C) 2000-2005 Vincent LE PRINCE // This file is part of the TRUEVISION Package // 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. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ //******************************************************************************************* #include "include/spline2d.h" #include "include/tvio.h" // Lathe preview subdivisions const int LatheSubdivision = 12; const float SplineSubdivision = 10; /* Some files do not define M_PI... */ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif //************************************** // Spline points //************************************** Spline2DPoint::Spline2DPoint( float a, float b ) { x = a; y = b; control1[0] = -0.08; control1[1]=-0.08; control2[0] = 0.08; control2[1]=0.08; } Spline2DPoint::Spline2DPoint( float a, float b , float c1x, float c1y, float c2x, float c2y ) { x = a; y = b; control1[0] = c1x; control1[1]=c1y; control2[0] = c2x; control2[1]=c2y; } void Spline2DPoint::display_rotated( float angle, bool invert ) { float cosa = cos( angle ); float sina = sin( angle ); if ( ! invert ) glNormal3f( cosa, 0, sina ); else glNormal3f( -cosa, 0, -sina ); glVertex3f( cosa*x, y, sina*x ); } void Spline2DPoint::save( ofstream & file ) { file << "\n\tPOINT{"; file << " X=" << x << " Y=" << y ; file << " C1X=" << control1[0] << " C1Y=" << control1[1] ; file << " C2X=" << control2[0] << " C2Y=" << control2[1] ; file << " }"; } void Spline2DPoint::load( ifstream & file, char *tag ) { char * val = NULL; do { val = tvio_get_next_val( file ); if ( val == NULL ) return; if ( ! strcmp( val, "X" ) ) { x = tvio_get_value_as_float( file ); continue; } if ( ! strcmp( val, "Y" ) ) { y = tvio_get_value_as_float( file ); continue; } if ( ! strcmp( val, "C1X" ) ) { control1[0] = tvio_get_value_as_float( file ); continue; } if ( ! strcmp( val, "C1Y" ) ) { control1[1]= tvio_get_value_as_float( file ); continue; } if ( ! strcmp( val, "C2X" ) ) { control2[0] = tvio_get_value_as_float( file ); continue; } if ( ! strcmp( val, "C2Y" ) ) { control2[1] = tvio_get_value_as_float( file ); continue; } } while( val != NULL ); } void Spline2DPoint::output_to_povray( ofstream & file , int spline_type, bool first, bool for_lathe ) { if ( for_lathe ) { if ( spline_type != TV_SPLINE2D_BEZIER ) file << "<" << x << ", " << y << ">"; else { if ( ! first ) file << "<" << x+control1[0] << ", " << y+control1[1] << ">, "; file << "<" << x << ", " << y << ">"; if ( first ) file << ", <" << x+control2[0] << ", " << y+control2[1] << "> "; } } else { if ( spline_type != TV_SPLINE2D_BEZIER ) file << "<" << x << ", " << -y << ">"; else { if ( ! first ) file << "<" << x+control1[0] << ", " << -y-control1[1] << ">, "; file << "<" << x << ", " << -y << ">"; if ( first ) file << ", <" << x+control2[0] << ", " << -y-control2[1] << "> "; } } } //********************************************************* // Spline 2d //********************************************************* Spline2D::Spline2D( SplineType atype, int asubdivisions ) { type = atype; subdivisions = asubdivisions; } Spline2D::~Spline2D() { for( unsigned int i = 0 ; i < Points.size() ; i++ ) delete Points[i]; } Spline2D * Spline2D::auto_copy() { Spline2D *nspline = new Spline2D( type, subdivisions ); nspline->set_for_lathe( for_lathe ); for ( unsigned int i = 0 ; i < Points.size() ; i++ ) nspline->add_point( Points[i]->auto_copy() ); return nspline; } void Spline2D::add_point( float x, float y ) { Spline2DPoint *pt = new Spline2DPoint( x, y ); Points.push_back( pt ); } void Spline2D::add_point( float x, float y, float c1x, float c1y, float c2x, float c2y ) { Spline2DPoint *pt = new Spline2DPoint( x, y, c1x, c1y, c2x, c2y ); Points.push_back( pt ); } void Spline2D::set_point( int i, float x, float y, float c1x, float c1y, float c2x, float c2y ) { if ( for_lathe ) { if ( x < 0 ) x = 0; if ( x+c1x < 0 ) c1x = -x; if ( x+c2x < 0 ) c2x = -x; } Points[i]->set( x, y, c1x, c1y, c2x, c2y ); } void Spline2D::append( ) { float x = Points.back()->getx() + 0.1; float y = Points.back()->gety() + 0.1; add_point( x, y ); } void Spline2D::prepend() { float x = Points[0]->getx() - 0.1; float y = Points[0]->gety() - 0.1; if ( for_lathe && x < 0 ) x = 0; Spline2DPoint *pt = new Spline2DPoint( x, y ); Points.insert( Points.begin(), pt ); } void Spline2D::insert( int selected ) { float x, y; if ( selected > 0 ) { x = ( Points[selected]->getx() + Points[selected-1]->getx() ) / 2.0; y = ( Points[selected]->gety() + Points[selected-1]->gety() ) / 2.0; if ( for_lathe && x < 0 ) x = 0; } else { x = Points[selected]->getx() / 2.0; y = Points[selected]->gety() / 2.0; if ( for_lathe && x < 0 ) x = 0; } Spline2DPoint *pt = new Spline2DPoint( x, y ); Points.insert( Points.begin() + selected, pt ); } void Spline2D::delete_point( int selected ) { if ( Points.size() <= 4 ) return; Points.erase(Points.begin()+selected); } // Save & Load void Spline2D::save( ofstream & file ) { file << "\nSPLINE2D{ "; int psize = Points.size(); for ( int i =0 ; i < psize ; i++ ) Points[i]->save( file ); file << " }"; } bool Spline2D::load( ifstream & file, char *ltag ) { if ( strcmp( ltag, "SPLINE2D" ) ) return false; Points.clear(); char * tag = NULL; do { tag = tvio_get_next_tag( file ); if ( tag == NULL ) break; if ( ! strcmp( tag, "POINT" ) ) { Spline2DPoint *pt = new Spline2DPoint(); pt->load( file, tag ); Points.push_back( pt ); continue; } tvio_skip_section(file ); } while ( tag != NULL ); return true; } void Spline2D::output_to_povray( ofstream & file ) { if ( ! for_lathe ) { if ( type == TV_SPLINE2D_QUADRATIC || type == TV_SPLINE2D_CUBIC) add_point( Points[1]->getx(), Points[1]->gety() ); if ( type == TV_SPLINE2D_LINEAR || type == TV_SPLINE2D_CUBIC) add_point( Points[0]->getx(), Points[0]->gety() ); if ( type == TV_SPLINE2D_BEZIER ) { float ctrl1[2]; float ctrl2[2]; get_point_ctrl1( 0, ctrl1 ); get_point_ctrl2( 0, ctrl2 ); add_point( Points[0]->getx(), Points[0]->gety(), ctrl1[0], ctrl1[1], ctrl2[0], ctrl2[1] ); } } int psize = Points.size(); if ( type == TV_SPLINE2D_BEZIER ) { for ( int i = 0 ; i < psize-1 ; i++ ) { Points[i]->output_to_povray( file, type, true, for_lathe ); Points[i+1]->output_to_povray( file, type, false, for_lathe ); if ( i < psize - 2 ) file << ", "; } } else { for ( int i = 0 ; i < psize ; i++ ) { Points[i]->output_to_povray( file, type, false, for_lathe ); if ( i < psize - 1 ) file << ", "; } } if ( ! for_lathe ) { Points.erase( Points.end()-1 ); if ( type == TV_SPLINE2D_CUBIC ) Points.erase( Points.end()-1 ); } } void Spline2D::get_segments( int & ptnum, float * & xcoords, float * & ycoords ) { if ( ! for_lathe ) { if ( type == TV_SPLINE2D_QUADRATIC || type == TV_SPLINE2D_CUBIC) add_point( Points[1]->getx(), Points[1]->gety() ); if ( type == TV_SPLINE2D_LINEAR || type == TV_SPLINE2D_CUBIC) add_point( Points[0]->getx(), Points[0]->gety() ); if ( type == TV_SPLINE2D_BEZIER ) { float ctrl1[2]; float ctrl2[2]; get_point_ctrl1( 0, ctrl1 ); get_point_ctrl2( 0, ctrl2 ); add_point( Points[0]->getx(), Points[0]->gety(), ctrl1[0], ctrl1[1], ctrl2[0], ctrl2[1] ); } } int size = Points.size(); int segs = size; if ( type == TV_SPLINE2D_QUADRATIC ) segs -= 1; if ( type == TV_SPLINE2D_CUBIC ) segs -= 2; if ( type != TV_SPLINE2D_LINEAR ) ptnum = (segs-1) * (subdivisions+1) +1; else ptnum = segs; xcoords = new float[ ptnum ]; ycoords = new float[ ptnum ]; float div = 1 / (float)subdivisions; switch ( type ) { case TV_SPLINE2D_LINEAR: { for ( int i = 0 ; i < ptnum ; i++ ) { xcoords[i] = Points[i]->getx(); ycoords[i] = Points[i]->gety(); } } break; case TV_SPLINE2D_QUADRATIC: { int cseg = 0; for ( int i = 1 ; i < size-1 ; i++ ) { float a[2], b[2], c[2]; Points[i]->get( a ); Points[i+1]->get( b ); Points[i-1]->get( c ); xcoords[cseg] = a[0]; ycoords[cseg++] = a[1]; for ( float t = 0 ; t < 1 ; t += div ) { float t2 = t*t; xcoords[cseg] = t2*( 0.5*c[0] - 1.0*a[0] + 0.5*b[0] ) + t*( -0.5*c[0] + 0.5*b[0] ) + a[0]; ycoords[cseg++] = t2*( 0.5*c[1] - 1.0*a[1] + 0.5*b[1] ) + t*( -0.5*c[1] + 0.5*b[1] ) + a[1]; } } xcoords[cseg] = Points.back()->getx(); ycoords[cseg++] = Points.back()->gety(); } break; case TV_SPLINE2D_CUBIC: { int cseg = 0; for ( int i = 1 ; i < size-2 ; i++ ) { float a[2], b[2], c[2], d[2]; Points[i]->get(a); Points[i+1]->get(b); Points[i-1]->get(c); Points[i+2]->get(d); xcoords[cseg] = a[0]; ycoords[cseg++] = a[1]; for ( float t = 0 ; t < 1 ; t += div ) { float t2 = t*t; float t3 = t2*t; xcoords[cseg] = t3*( -0.5*c[0] + 1.5*a[0] - 1.5*b[0] + 0.5*d[0] ) + t2*( c[0] - 2.5*a[0] +2.0*b[0]- 0.5*d[0] ) + t*( -0.5*c[0] + 0.5*b[0] ) + a[0]; ycoords[cseg++] = t3*( -0.5*c[1] + 1.5*a[1] - 1.5*b[1] + 0.5*d[1] ) + t2*( c[1] - 2.5*a[1] +2.0*b[1]- 0.5*d[1] ) + t*( -0.5*c[1] + 0.5*b[1] ) + a[1]; } } xcoords[cseg] = Points[size-2]->getx(); ycoords[cseg++] = Points[size-2]->gety(); } break; case TV_SPLINE2D_BEZIER: { int cseg = 0; for ( int i = 0 ; i < size-1 ; i++ ) { float a[2], b[2], c[2], d[2]; Points[i]->get(a); Points[i+1]->get(b); Points[i]->get_control2(c); c[0] += a[0]; c[1] += a[1]; Points[i+1]->get_control1(d); d[0] += b[0];d[1] += b[1]; xcoords[cseg] = a[0]; ycoords[cseg++] = a[1]; for ( float t = 0 ; t < 1 ; t += div ) { float t2 = t*t; float t3 = t2*t; xcoords[cseg] = t3*( -a[0] + 3*c[0] - 3*d[0] + b[0] ) + t2*( 3*a[0] - 6*c[0] +3*d[0] ) + t*( -3*a[0] + 3*c[0] ) + a[0]; ycoords[cseg++] = t3*( -a[1] + 3*c[1] - 3*d[1] + b[1] ) + t2*( 3*a[1] - 6*c[1] +3*d[1] ) + t*( -3*a[1] + 3*c[1] ) + a[1]; } } xcoords[cseg] = Points[size-1]->getx(); ycoords[cseg++] = Points[size-1]->gety(); } break; } if ( ! for_lathe ) { Points.erase( Points.end()-1 ); if ( type == TV_SPLINE2D_CUBIC ) Points.erase( Points.end()-1 ); } } void Spline2D::gl_display_segments() { float *xcoords, *ycoords; int ptnum; get_segments( ptnum, xcoords, ycoords ); glBegin( GL_LINE_STRIP ); if ( for_lathe ) for ( int i = 0 ; i < ptnum ; i++ ) glVertex3f( xcoords[i], ycoords[i], 0 ); else for ( int i = 0 ; i < ptnum ; i++ ) glVertex3f( xcoords[i], 0, ycoords[i] ); glEnd(); delete xcoords; delete ycoords; }