//***************************************************************************************** // Truevision - a 3d modeler for gnome and povray // // spline3d.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/spline3d.h" #include "include/tvio.h" /* Some files do not define M_PI... */ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif //************************************** // Spline points //************************************** Spline3DPoint::Spline3DPoint( float a, float b, float c ) { x = a; y = b; z = c; extra_param = 0.0; } Spline3DPoint::Spline3DPoint( float a, float b , float c, float extra ) { x = a; y = b; z = c; extra_param = extra; } void Spline3DPoint::save( ofstream & file ) { file << "\n\tPOINT{"; file << " X=" << x << " Y=" << y << " Z=" << z; file << " EXT=" << extra_param; file << " }"; } void Spline3DPoint::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, "Z" ) ) { z = tvio_get_value_as_float( file ); continue; } if ( ! strcmp( val, "EXT" ) ) { extra_param = tvio_get_value_as_float( file ); continue; } } while( val != NULL ); } void Spline3DPoint::output_to_povray( ofstream & file , int spline_type, bool first, bool for_lathe ) { } //********************************************************* // Spline 3d //********************************************************* Spline3D::Spline3D( SplineType3D atype, int asubdivisions ) { type = atype; subdivisions = asubdivisions; } Spline3D::~Spline3D() { for( unsigned int i = 0 ; i < Points.size() ; i++ ) delete Points[i]; } Spline3D * Spline3D::auto_copy() { Spline3D *nspline = new Spline3D( type, subdivisions ); for ( unsigned int i = 0 ; i < Points.size() ; i++ ) nspline->add_point( Points[i]->auto_copy() ); return nspline; } void Spline3D::add_point( float x, float y, float z ) { Spline3DPoint *pt = new Spline3DPoint( x, y, z ); Points.push_back( pt ); } void Spline3D::add_point( float x, float y, float z, float extra_param ) { Spline3DPoint *pt = new Spline3DPoint( x, y, z, extra_param ); Points.push_back( pt ); } void Spline3D::append( ) { float x = Points.back()->getx() + 0.1; float y = Points.back()->gety() + 0.1; float z = Points.back()->getz() + 0.1; float ext = Points.back()->get_extra_param(); add_point( x, y, z, ext ); } void Spline3D::prepend() { float x = Points[0]->getx() - 0.1; float y = Points[0]->gety() - 0.1; float z = Points[0]->getz() - 0.1; float ext = Points.back()->get_extra_param(); Spline3DPoint *pt = new Spline3DPoint( x, y, z, ext ); Points.insert( Points.begin(), pt ); } void Spline3D::insert( int selected ) { float x, y, z, ext; if ( selected > 0 ) { x = ( Points[selected]->getx() + Points[selected-1]->getx() ) / 2.0; y = ( Points[selected]->gety() + Points[selected-1]->gety() ) / 2.0; z = ( Points[selected]->getz() + Points[selected-1]->getz() ) / 2.0; ext = ( Points[selected]->get_extra_param() + Points[selected-1]->get_extra_param() ) / 2.0; } else { x = Points[selected]->getx() / 2.0; y = Points[selected]->gety() / 2.0; z = Points[selected]->getz() / 2.0; ext = Points[selected]->get_extra_param() / 2.0; } Spline3DPoint *pt = new Spline3DPoint( x, y, z, ext ); Points.insert( Points.begin() + selected, pt ); } void Spline3D::delete_point( int selected ) { if ( Points.size() <= 2 ) return; Points.erase(Points.begin()+selected); } // Save & Load void Spline3D::save( ofstream & file ) { file << "\nSPLINE3D{ "; int psize = Points.size(); for ( int i =0 ; i < psize ; i++ ) Points[i]->save( file ); file << " }"; } bool Spline3D::load( ifstream & file, char *ltag ) { if ( strcmp( ltag, "SPLINE3D" ) ) return false; Points.clear(); char * tag = NULL; do { tag = tvio_get_next_tag( file ); if ( tag == NULL ) break; if ( ! strcmp( tag, "POINT" ) ) { Spline3DPoint *pt = new Spline3DPoint(); pt->load( file, tag ); Points.push_back( pt ); continue; } tvio_skip_section(file ); } while ( tag != NULL ); return true; } void Spline3D::output_to_povray( ofstream & file ) { int spline_size = Points.size(); for ( int i = 0 ; i < spline_size ; i++ ) { float p[3]; float radius = Points[i]->get_extra_param(); Points[i]->get( p ); file << " <" << p[0] << "," << p[1] << "," << -p[2] << ">, "<< radius; } } const float cubicspline_matrix[4][4] = { -0.5, 1.5, -1.5, 0.5, 1, -2.5, 2, -0.5, -0.5, 0, 0.5, 0, 0, 1, 0, 0 }; const float bspline_matrix[4][4] = { -0.1666666, .5, -.5, 0.1666666, 0.5, -1, 0.5, 0, -0.5, 0, 0.5, 0, 0.1666666, 0.6666666, 0.1666666, 0 }; /*const float bspline_matrix[4][4] = { -1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0 };*/ void Spline3D::get_segments( int & ptnum, float * & xcoords, float * & ycoords, float * & zcoords ) { int size = Points.size(); int segs = size; ptnum = segs; if ( type == TV_SPLINE3D_BSPLINE ) { segs -= 2; ptnum = (segs-1) * (subdivisions+1)+1; } if ( type == TV_SPLINE3D_CUBIC ) { segs -= 2; ptnum = (segs-1) * (subdivisions)+1; } xcoords = new float[ ptnum ]; ycoords = new float[ ptnum ]; zcoords = new float[ ptnum ]; float div = 1 / (float)subdivisions; //cout << "\nptnum = " << ptnum; cout.flush(); switch ( type ) { case TV_SPLINE3D_LINEAR: { for ( int i = 0 ; i < ptnum ; i++ ) { xcoords[i] = Points[i]->getx(); ycoords[i] = Points[i]->gety(); zcoords[i] = Points[i]->getz(); } } break; case TV_SPLINE3D_BSPLINE: { int cseg = 0; //cout << "\nsegs = "; cout.flush(); for ( int i = 0 ; i < size-3 ; i++ ) { float a[3], b[3], c[3], d[3]; Points[i]->get(a); Points[i+1]->get(b); Points[i+2]->get(c); Points[i+3]->get(d); float t = 0; bool reloop = true; for ( int k = 0 ; k < subdivisions+1 ; k++ ) { //float t = (float)k / (float)(subdivisions-1); float tt[4]; float ttt[4]; tt[0] = t*t*t; tt[1] = t*t; tt[2] = t; tt[3] = 1; for ( int j = 0 ; j < 4 ; j++ ) { ttt[j] = 0; for ( int w = 0 ; w < 4 ; w++ ) ttt[j] += tt[w] * bspline_matrix[w][j]; } //cout << "\n\t seg ->" << cseg; xcoords[cseg] = ttt[0] * a[0] + ttt[1] * b[0] + ttt[2] * c[0] + ttt[3] * d[0]; ycoords[cseg] = ttt[0] * a[1] + ttt[1] * b[1] + ttt[2] * c[1] + ttt[3] * d[1]; zcoords[cseg] = ttt[0] * a[2] + ttt[1] * b[2] + ttt[2] * c[2] + ttt[3] * d[2]; //cout << " : < " << xcoords[cseg] << " - " << ycoords[cseg] << " - " << zcoords[cseg] << " > "; cseg += 1; t += div; if ( i == size-4 && k == subdivisions && reloop == true ) { k--; reloop = false; } } } //cout << "\ncseg = " << cseg; cout.flush(); } break; case TV_SPLINE3D_CUBIC: { int cseg = 0; for ( int i = 0 ; i < size-3 ; i++ ) { float a[3], b[3], c[3], d[3]; Points[i]->get(a); Points[i+1]->get(b); Points[i+2]->get(c); Points[i+3]->get(d); for ( float t = 0 ; t < 1 ; t += div ) { float tt[4]; float ttt[4]; tt[0] = t*t*t; tt[1] = t*t; tt[2] = t; tt[3] = 1; for ( int j = 0 ; j < 4 ; j++ ) { ttt[j] = 0; for ( int k = 0 ; k < 4 ; k++ ) ttt[j] += tt[k] * cubicspline_matrix[k][j]; } xcoords[cseg] = ttt[0] * a[0] + ttt[1] * b[0] + ttt[2] * c[0] + ttt[3] * d[0]; ycoords[cseg] = ttt[0] * a[1] + ttt[1] * b[1] + ttt[2] * c[1] + ttt[3] * d[1]; zcoords[cseg++] = ttt[0] * a[2] + ttt[1] * b[2] + ttt[2] * c[2] + ttt[3] * d[2]; } } xcoords[cseg] = Points[size-2]->getx(); ycoords[cseg] = Points[size-2]->gety(); zcoords[cseg] = Points[size-2]->getz(); } break; } } void Spline3D::interpolate_extra_param( int & ptnum, float * & extra_params ) { // Calculate segments int size = Points.size(); int segs = size; ptnum = segs; if ( type == TV_SPLINE3D_BSPLINE ) { segs -= 2; ptnum = (segs-1) * (subdivisions+1)+1; } if ( type == TV_SPLINE3D_CUBIC ) { segs -= 2; ptnum = (segs-1) * (subdivisions)+1; } extra_params = new float[ ptnum ]; float div = 1 / (float)subdivisions; //cout << "\nptnum = " << ptnum; cout.flush(); switch ( type ) { // Linear spline, no interpolation case TV_SPLINE3D_LINEAR: { for ( int i = 0 ; i < ptnum ; i++ ) extra_params[i] = Points[i]->get_extra_param(); } break; // Cubic spline, cubic interpolation case TV_SPLINE3D_CUBIC: { int cseg = 0; for ( int i = 0 ; i < size-3 ; i++ ) { float a, b, c, d; a = Points[i]->get_extra_param(); b = Points[i+1]->get_extra_param(); c = Points[i+2]->get_extra_param(); d = Points[i+3]->get_extra_param(); for ( float t = 0 ; t < 1 ; t += div ) { float t2 = t*t; float t3 = t2*t; extra_params[cseg++] = t3*( -0.5*a + 1.5*b - 1.5*c + 0.5*d ) + t2*( a - 2.5*b +2.0*c- 0.5*d ) + t*( -0.5*a + 0.5*c ) + b; } } extra_params[cseg] = Points[size-2]->get_extra_param(); } break; // B Spline, bezier interpolation case TV_SPLINE3D_BSPLINE: { int cseg = 0; for ( int i = 0 ; i < size-3 ; i++ ) { float a, b, c, d; a = Points[i]->get_extra_param(); b = Points[i+1]->get_extra_param(); c = Points[i+2]->get_extra_param(); d = Points[i+3]->get_extra_param(); float t = 0; bool reloop = true; for ( int k = 0 ; k < subdivisions+1 ; k++ ) { float t2 = t*t; float t3 = t2*t; float u1 = 0.16666666666; float u2 = 0.6666666666; extra_params[cseg++] = t3*( -u1*a + 0.5*b - 0.5*c + u1*d ) + t2*( 0.5*a - b + 0.5*c ) + t*( -0.5*a + 0.5*c ) + u1*a + u2*b + u1*c; t += div; if ( i == size-4 && k == subdivisions && reloop == true ) { k--; reloop = false; } } } } break; } } void Spline3D::gl_display_segments() { float *xcoords, *ycoords, *zcoords; int ptnum; get_segments( ptnum, xcoords, ycoords, zcoords ); glBegin( GL_LINE_STRIP ); for ( int i = 0 ; i < ptnum ; i++ ) glVertex3f( xcoords[i], ycoords[i], zcoords[i] ); glEnd(); delete xcoords; delete ycoords; delete zcoords; }