//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// spheresweep.cc
//
// Vincent LE PRINCE <vincentleprince@users.sourceforge.net>
// 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/spheresweep.h"
#include "include/viewmanager.h"
#include "include/objectlist.h"
#include "include/tvio.h"
#include "include/preferences.h"


/* Some <math.h> files do not define M_PI... */
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif


// Spline type
const int spline3d_type_num = 3;
const gchar * spline3d_type_list[spline3d_type_num] = { N_("Linear"), N_("Cubic"), N_("B Spline") };
const char *spline3d_type_keys[spline3d_type_num] = { "linear_spline",  "cubic_spline", "b_spline" };
	

//**************************************
// SphereSweep - Constructor
//**************************************
SphereSweep::SphereSweep( app_objs *appref ) : Object3D_with_material( appref )
{
	// Init
	type = TV_OBJ3D_SPHERESWEEP;
	category = TV_OBJ3D_OBJECTS;
	set_name( "SphereSweep" );
	spline_edit_box = NULL;
	in_undo = false;
	in_update = false;

	// Spline	- default definition
	spline = new Spline3D( TV_SPLINE3D_LINEAR, 10 );
	spline->add_point( 0,0.1, 0, 0.1 );
	spline->add_point( 0.1, 0.1, 0.1, 0.1 );
	spline->add_point( 0.2, 0.1,0.2, 0.1 );
	spline->add_point( 0.3, 0.1, 0.3, 0.1 );
	
	// Base
	location = new ObjParam_point( N_("Location"), "LOC", NULL, app_ref, true );
	location->set( 0, 0, 0 );
	size = new ObjParam_scale( N_("Size"), "SIZE", NULL, app_ref, true );
	size->set( 1, 1, 1 );
	rotation = new ObjParam_rotation( N_("Rotation"), "ROT", NULL, app_ref, true );
	rotation->set( 0, 0, 0 );
	edit = new ObjParam_bool_activator( N_("Edit"), "EDIT", N_("Edit SphereSweep"), app_ref, true, true );
	tolerance = new ObjParam_float( N_("Tolerance"), "TOLER", NULL, app_ref, false, 0.00001 );
	tolerance->set_range( 1, 0.000001, 0.000001, 6 );
	spline_type = new ObjParam_option_combo( N_("Spline mode"), "SPLINET", NULL, app_ref, true );
	spline_type->set_list( spline3d_type_list, spline3d_type_num, 0 );
		
	current_point_num = new ObjParam_int( N_("Point #"), "PNUM", NULL, app_ref, true, 1 );
	current_point_num->set_range( spline->size(), 1, 1 );
	current_point_num->set_send_undo( false );
	current_point = new ObjParam_point( N_("Position"), "POS", NULL, app_ref, true );
	current_point->set_send_undo( false );
	radius = new ObjParam_float( N_("Sphere radius"), "RAD", NULL, app_ref, true, true );
	radius->set_range( 500, 0, 0.01, 5 );
	radius->set_send_undo( false );
}

//****************************************************
// Copy constructor
//****************************************************
SphereSweep::SphereSweep( SphereSweep & ref ) : Object3D_with_material( ref )
{
	in_undo = false;
	spline_edit_box = NULL;
	in_update = false;
	location = new ObjParam_point( *ref.location );
	size = new ObjParam_scale( *ref.size );
	rotation = new ObjParam_rotation( *ref.rotation );
	edit = new ObjParam_bool_activator( *ref.edit );
	tolerance = new ObjParam_float( *ref.tolerance );
	spline_type = new ObjParam_option_combo( *ref.spline_type );
	current_point_num = new ObjParam_int( *ref.current_point_num );
	current_point = new ObjParam_point( *ref.current_point );	
	radius = new ObjParam_float( *ref.radius );	

	spline = ref.spline->auto_copy();
	current_point_num->set_range( spline->size(), 1, 1 );
}


//****************************************************
// Destructor
//****************************************************
SphereSweep::~SphereSweep()
{
	delete location;
	delete size;
	delete rotation;
	delete edit;
	delete tolerance;
	delete spline_type;
	delete current_point_num;
	delete current_point;
	delete radius;
	delete spline;
}


//**************************************
// Display
// Dessin de la boite
//**************************************
void SphereSweep::display( glview *view, bool set_col )
{
	if ( hidden->value() ) 
		return;
	if ( location->changed() || size->changed() || rotation->changed()  || edit->changed() || spline_type->changed() 
		|| current_point_num->changed() || radius->changed() )
		list.invalidate();

	PREF_DEF
	int quality = 10 + pref->preview_quality->value()*4;

	// Edition mode display
	// no display list / a picking name for each point
	if (  edit->value() ) {
		OBJLIST_DEF
		int selected_point = current_point_num->value() - 1;
		set_color();
		glPushMatrix();
		int spline_size = spline->size();

		// Get render mode from OpenGL ( Normal or Select )
		int mode;
		glGetIntegerv( GL_RENDER_MODE, &mode );

		// Normal drawing mode
		// display all, including lines ( segments betwenn points )
		if ( mode != GL_SELECT ) {
			
			// Draw linear lines in stipple mode
			glEnable( GL_LINE_STIPPLE );
			glLineStipple( 1, 0xAAA );
			spline->set_type( TV_SPLINE3D_LINEAR );
			spline->set_subdivisions( quality );
			spline->gl_display_segments();
			glDisable( GL_LINE_STIPPLE );
		
			// Draw Spline in plain mode  ( as a curve this time )
			spline->set_type( (SplineType3D)spline_type->value() );
			spline->set_subdivisions( 20 );
			spline->gl_display_segments();
		
			// Draw circles at each pointn except the last one
			for ( int i = 0 ; i < spline_size-1 ; i++ ) {
				float xcoords[ quality ], ycoords[ quality ], zcoords[ quality ];
				float a[3] = { spline->getx(i), spline->gety(i), spline->getz(i) };
				float b[3] = { spline->getx(i+1), spline->gety(i+1), spline->getz(i+1) };
				float rad = spline->get_extra_param( i );
				get_circle_points( a, b, rad, xcoords, ycoords, zcoords, NULL, NULL, NULL, quality );
				glBegin( GL_LINE_LOOP );
					for ( int j = 0 ; j < quality ; j++ )
						glVertex3f( xcoords[j], ycoords[j], zcoords[j] );
				glEnd();
			}
		
			// Draw last point circle,orientation is calculated from previous point 
			// For other points it is calculated with next point
			float xcoords[ quality ], ycoords[ quality ], zcoords[ quality ];
			float a[3] = { spline->getx(spline_size-1), spline->gety(spline_size-1), spline->getz(spline_size-1) };
			float b[3] = { spline->getx(spline_size-2), spline->gety(spline_size-2), spline->getz(spline_size-2) };
			float rad = spline->get_extra_param( spline_size-1 );
			get_circle_points( a, b, rad, xcoords, ycoords, zcoords, NULL, NULL, NULL, quality, true );
			glBegin( GL_LINE_LOOP );
				for ( int j = 0 ; j < quality ; j++ )
					glVertex3f( xcoords[j], ycoords[j], zcoords[j] );
			glEnd();
		}
		
		// Draw Points ( much, much easier :)
		// We only draw this for selection mode ( with pick names )
		pick_name = -1;		
		for ( int i = 0 ; i < spline_size ; i++ ) {
			int pick = objlist->get_pick_name();
			if ( pick_name == -1 ) pick_name = pick;
			glLoadName( pick );
			if ( i != selected_point ) {
				glBegin( GL_POINTS );
				spline->display_point(i);
				glEnd();
			} else {
				glColor3f( 1.0, 0, 0 );
				glPointSize( 6 );
				glBegin( GL_POINTS );
				spline->display_point( selected_point );	
				glEnd();
				set_color();
				glPointSize( 4 );
				set_color();
			}
		}
		glPopMatrix();
		list.end();
	} else {
		
		// Preview mode :)
		// SphereSweep opengl drawing
		// No point selection, we have a display list
		Object3D::display( view );	
		if ( set_col ) 
			set_color();
		if ( ! list.exec() ) {
			// parameters unchange for list validation
			location->unchange();
			size->unchange();
			rotation->unchange();
			edit->unchange();
			spline_type->unchange();
			current_point_num->unchange();
			current_point->unchange();

			spline->set_type( (SplineType3D)spline_type->value() );
			int spline_size = spline->size();
		
			// create list if necessary
			list.begin();
			glPushMatrix();
	
			// Poistion and direction
			gfloat x, y, z;
			gfloat a, b, c;
			location->get( x, y, z );
			glTranslatef( x, y, z );
			rotation->gl_rotate();
			size->get( a, b, c );
			glScalef( a, b, c );


			// Linear spline preview
			// This one is easy : draw tubes from one point to another
			// and a sphere at each point
			if ( spline_type->value() == TV_SPLINE3D_LINEAR ) {
				for ( int i = 0 ; i < spline_size ; i++ ) {
					float center[3] = { spline->getx(i), spline->gety(i), spline->getz(i) };
					float radius1 = spline->get_extra_param( i );

					// Draw a Sphere at each point
					glPushMatrix();
					glTranslatef( center[0], center[1], center[2] );
					GLUquadricObj *quadobj = gluNewQuadric();
					if ( !quadobj ) { 
						app_warning( _("Out of memory" ) ); 
						return; 
					}	
					gluQuadricDrawStyle( quadobj, GLU_FILL );
			    	gluSphere(quadobj, radius1 , quality, quality );
					glPopMatrix();		

					// Draw a tube to next point
					if ( i == spline_size -1 ) 
						break;
					float direction[3] = { spline->getx(i+1), spline->gety(i+1), spline->getz(i+1) };
					float radius2 = spline->get_extra_param( i+1 );
					float points[2][3][quality];
					float normals[2][3][quality];
					get_circle_points( center, direction, radius1, points[0][0], points[0][1], points[0][2], normals[0][0], normals[0][1], normals[0][2],quality );
					get_circle_points( direction, center, radius2, points[1][0], points[1][1], points[1][2], normals[1][0], normals[1][1], normals[1][2],quality, true );								
					glBegin( GL_QUAD_STRIP );			
					for ( int j = 0 ; j < quality ; j++ ) {
						glNormal3f( normals[0][0][j],normals[0][1][j], normals[0][2][j] );
						glVertex3f( points[0][0][j], points[0][1][j], points[0][2][j] );
						glNormal3f( normals[1][0][j],normals[1][1][j], normals[1][2][j] );
						glVertex3f( points[1][0][j], points[1][1][j], points[1][2][j] );					
					}
					glNormal3f( normals[0][0][0],normals[0][1][0], normals[0][2][0] );
					glVertex3f( points[0][0][0], points[0][1][0], points[0][2][0] );
					glNormal3f( normals[1][0][0],normals[1][1][0], normals[1][2][0] );
					glVertex3f( points[1][0][0], points[1][1][0], points[1][2][0] );				
					glEnd();
				}
			} else {
				
				// Cubic splines and B-splines preview
				// Much more difficult :(
				
				// Calculate spline points and radius interpolation
				float *splinex, *spliney, *splinez;
				float *radices;
				int ptnum;
				spline->set_subdivisions( quality );
				spline->get_segments( ptnum, splinex, spliney, splinez );
				spline->interpolate_extra_param( ptnum, radices );
				//cout << "\nptnum = " << ptnum; cout.flush();
			
				// Initialize buffers
				int point_num = ptnum + quality*2;
				float spline_x[point_num], spline_y[point_num], spline_z[point_num];
				float radius[point_num];
				//cout << "\npoint num = " << point_num; cout.flush();
		
				// add some points for start/end half spheres
				float start_radius = radices[0];
				float end_radius = radices[ptnum-1];
				float start_dir[3] = { splinex[1] - splinex[0], spliney[1] - spliney[0], splinez[1] - splinez[0] };
				float end_dir[3] = { splinex[ptnum-2] - splinex[ptnum-1], spliney[ptnum-2] - spliney[ptnum-1], splinez[ptnum-2] - splinez[ptnum-1] };
				float start_dir_norm = 0;
				float end_dir_norm = 0;
				for ( int i = 0 ; i < 3 ; i++ ) { 
					start_dir_norm += start_dir[i]*start_dir[i]; 
					end_dir_norm += end_dir[i]*end_dir[i]; 
				}
				start_dir_norm = sqrt( start_dir_norm );
				end_dir_norm = sqrt( end_dir_norm );
				for ( int i = 0 ; i < 3 ; i++ ) { 
					start_dir[i] = start_dir[i] / start_dir_norm * start_radius / (float)quality; 
					end_dir[i] = end_dir[i] / end_dir_norm * end_radius / (float)quality; 
				}
				// Copy spline values
				for ( int i = 0 ; i < ptnum ; i ++ ) {
					int ind = quality + i;
					spline_x[ind] = splinex[i]; 
					spline_y[ind] = spliney[i]; 
					spline_z[ind] = splinez[i]; 
					radius[ind] = radices[i];
				}
				for ( int i = 1 ; i < quality+1 ; i++ ) {
					spline_x[ quality - i ] = spline_x[ quality - i + 1 ] - start_dir[0];
					spline_y[ quality - i ] = spline_y[ quality - i + 1 ] - start_dir[1];
					spline_z[ quality - i ] = spline_z[ quality - i + 1 ] - start_dir[2];					
					spline_x[ point_num - quality  + i -2 ] = spline_x[ point_num - quality +  i - 3 ] - end_dir[0];
					spline_y[ point_num - quality  + i  -2] = spline_y[ point_num - quality +  i - 3 ] - end_dir[1];
					spline_z[ point_num - quality  + i -2 ] = spline_z[ point_num - quality +  i - 3 ] - end_dir[2];
					float angle =  acos((float)i / (float)quality); 
					radius[ quality - i ] = sin(angle)*start_radius;
					radius[ point_num - quality + i -2 ] = sin(angle) * end_radius;
				}
				delete splinex;
				delete spliney;
				delete splinez;
		
				//	Get circles and normals
				float points[point_num][3][quality];
				float normals[point_num][3][quality];
				for ( int i = 0 ; i < point_num-1 ; i++ ) {
					float center[3] = { spline_x[i], spline_y[i], spline_z[i]  };
					int offset; 
					if ( i != point_num -3 ) 
						offset = i+2; 
					else 
						offset = i+1;
					float direction[3] = { spline_x[offset], spline_y[offset], spline_z[offset] };
					get_circle_points( center, direction, radius[i], points[i][0], points[i][1], points[i][2], normals[i][0], normals[i][1], normals[i][2],quality );
				}
		
				// Draw strips between circles
				for ( int i = 0 ; i < point_num-2 ; i++ ) {
					glBegin( GL_QUAD_STRIP );			
					for ( int j = 0 ; j < quality ; j++ ) {
						glNormal3f( normals[i][0][j],normals[i][1][j], normals[i][2][j] );
						glVertex3f( points[i][0][j], points[i][1][j], points[i][2][j] );
						glNormal3f( normals[i+1][0][j],normals[i+1][1][j], normals[i+1][2][j] );
						glVertex3f( points[i+1][0][j], points[i+1][1][j], points[i+1][2][j] );					
					}
					glNormal3f( normals[i][0][0],normals[i][1][0], normals[i][2][0] );
					glVertex3f( points[i][0][0], points[i][1][0], points[i][2][0] );
					glNormal3f( normals[i+1][0][0],normals[i+1][1][0], normals[i+1][2][0] );
					glVertex3f( points[i+1][0][0], points[i+1][1][0], points[i+1][2][0] );				
					glEnd();
				}
			}
			glPopMatrix();
			list.end();
		}
	}
}


void SphereSweep::get_circle_points( float *center, float *direction, float radius, float *xcoords, float *ycoords, float * zcoords, float *xnorm, float *ynorm, float * znorm, int divisions, bool inv_dir )
{
	// calculate direction vector
	double vector[3] = { direction[0] - center [0], direction[1] - center [1], direction[2] - center [2], };
	double denom = sqrt( vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2] );
	if ( inv_dir ) 
		for ( int i = 0 ; i < 3 ; i++ ) 
			vector[i] /= -denom;
	else 
		for ( int i = 0 ; i < 3 ; i++ ) 
			vector[i] /= denom;


	// convert it as rotation angles
	double a1 = -asin( vector[1] );
	double a2 = asin( vector[0] / cos(a1) );
	double a2b = asin( vector[0] / cos(M_PI-a1) );
	double vals[4][2] = {  { a1, a2 }, { a1, M_PI-a2 }, { M_PI-a1, a2b }, { M_PI-a1, M_PI-a2b } };
	int i = 0;
	for ( i= 0; i < 4 ; i++ ) {
		a1 = vals[i][0]; a2 = vals[i][1];
		double offset = fabs( vector[2 ] - cos(a1)*cos(a2) );
		offset += fabs( vector[1] + sin(a1) );
		offset += fabs( vector[0] - cos(a1)*sin(a2) );
		if ( offset < 0.01 ) 
			break;
	}

	double cosa1 = cos(a1); double sina1 = sin(a1);
	double cosa2 = cos(a2); double sina2 = sin(a2);
	
	// calculating points and normals
	float epsilon = 2*M_PI / (float)divisions;
	float angle = 0;
	for ( int i = 0 ; i < divisions ; i++ ) {
		float y = radius * sin(angle);
		float x = radius * cos(angle);
		if ( xnorm != NULL ) {
			xnorm[i] = x*cosa2 + y*sina1*sina2;
			ynorm[i] = y*cosa1;
			znorm[i] = y*sina1 *cosa2 - x*sina2;
		}
		xcoords[i] = x*cosa2 + y*sina1*sina2 + center[0];
		ycoords[i] = y*cosa1 + center[1];
		zcoords[i] =  y*sina1 *cosa2 - x*sina2 + center[2];
		angle += epsilon;
	}
}


//***********************************************
// Edit
//***********************************************
void SphereSweep::edit_widget( GtkWidget *wid )
{
	PREF_DEF
	bool tt = pref->tooltips->value();
	// Options communes
	Object3D::edit_widget( wid );

	// Spline edition
	new_frame( edit_cont, N_("SphereSweep edition") );
	new_table_no_frame( frame, 2 );
	spline_type->get_widget( table, tt, 1 );
	edit->get_widget( table, tt, 2 );

	spline_edit_box = dlg_simple_box_frame( N_("Spline edition"), frame );
	current_point_num->get_widget( spline_edit_box, tt );
	current_point->get_widget( spline_edit_box, tt,  sign_ssweep_current_point_changed, this );
	current_point->reduce_size();
	radius->get_widget( spline_edit_box, tt ,  sign_ssweep_current_point_changed, this);

	// Bouttons
	GtkWidget *hbox = gtk_hbox_new( TRUE, 3 );
	gtk_box_pack_start( GTK_BOX(spline_edit_box), hbox, TRUE, TRUE, 1 );
	GtkWidget *button = gtk_button_new_with_label( N_("Append") );
	gtk_box_pack_start( GTK_BOX(hbox), button, TRUE, TRUE, 1 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_ssweep_append_point), this );
	button = gtk_button_new_with_label( N_("Prepend") );
	gtk_box_pack_start( GTK_BOX(hbox), button, TRUE, TRUE, 1 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_ssweep_prepend_point), this );
	button = gtk_button_new_with_label( N_("Insert") );
	gtk_box_pack_start( GTK_BOX(hbox), button, TRUE, TRUE, 1 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_ssweep_insert_point), this );
	button = gtk_button_new_with_label( N_("Delete") );
	gtk_box_pack_start( GTK_BOX(hbox), button, TRUE, TRUE, 1 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_ssweep_delete_point), this );
	
	// Options de geometrie
	new_table( edit_cont, N_("General settings"), 4 );
	location->get_widget( table, tt, 1 );
	size->get_widget( table, tt, 2 );
	rotation->get_widget( table, tt, 3 );
	tolerance->get_widget( table, tt, 4 );
	
	get_texture_widgets( edit_cont, tt );
	gtk_widget_show_all( wid );
	edit->set_target( spline_edit_box );
	edit->toggle();
	current_point_num->connect_signal( GTK_SIGNAL_FUNC(sign_ssweep_current_point_num_changed), this );
	current_point_num_changed();
}


//***********************************************
// Mouse drag
//***********************************************
void SphereSweep::mouse_drag( struct drag_info *drag )
{
	VMAN_DEF
	OBJLIST_DEF

	switch( vmanager->get_pointer_mode() ) {
		case TV_PMODE_SELECT:
		case TV_PMODE_TRANSLATE:
			if ( edit->value() )
				current_point->mouse_drag( drag );
			else 
				location->mouse_drag( drag );
			break;

		case TV_PMODE_SCALE: 
			size->mouse_drag( drag );
			break;

		case TV_PMODE_ROTATE:
			rotation->mouse_drag( drag ); 
			break;

		case TV_PMODE_CUSTOM:
			((ObjParam_point*)(objlist->get_current_param()))->mouse_drag( drag );
			break;

		default: 
			break;
	}
}


//***********************************************
// Pref_changed
//***********************************************
void SphereSweep::pref_changed()
{
	Object3D::pref_changed();
	location->pref_changed();
	size->pref_changed();
	rotation->pref_changed();
	edit->pref_changed();
	tolerance->pref_changed();
	spline_type->pref_changed();
	current_point_num->pref_changed();
	current_point->pref_changed();
	radius->pref_changed();
}


//***********************************************
// Destroy editor
//***********************************************
void SphereSweep::destroy_editor()
{
	Object3D::destroy_editor();
	location->clear_widget();
	size->clear_widget();
	texture->clear_widget();
	rotation->clear_widget();
	edit->clear_widget();
	tolerance->clear_widget();
	spline_type->clear_widget();
	current_point_num->clear_widget();
	current_point->clear_widget();
	radius->clear_widget();
	if ( spline_edit_box != NULL ) 
		spline_edit_box = NULL;
}


//***********************************************
// Output to povray
//***********************************************
void SphereSweep::output_to_povray_pass1( ofstream & file )
{
	file << "\n\n// SphereSweep : " << name->value();
	file << "\n#declare "; get_underscore_name( file ); file << " ="; 

	int sptype = spline_type->value();
	file << "\nsphere_sweep {\n\t" << spline3d_type_keys[ sptype ];
	int psize = spline->size();
	file << "\n\t" << psize << ",\n\t";

	spline->set_type( (SplineType3D)spline_type->value() );
	spline->output_to_povray( file );

	file << "\n\ttolerance " << tolerance->value();
	float a, b, c, x, y, z;
	Object3D_with_material::output_to_povray_pass1( file );
	file << "\n\t";
	size->get( a, b, c );
	file << "scale <" << a << "," << b << "," << c << ">";
	rotation->output_to_povray( file );
	location->get( x, y, z );
	file << "\n\ttranslate <" << x << "," << y << "," << -z << "> \n\t";
	file << "\n}";
}



//***********************************************
// Save & load
//***********************************************
void SphereSweep::save( ofstream & file )
{
	file << "\nSPHERESWEEP{\n";
	save_basics( file );
	location->save( file );
	size->save( file );
	rotation->save( file );
	texture->save( file );
	edit->save( file );
	tolerance->save( file );
	spline_type->save( file );
	
	spline->save( file );
	file << "\n}";
}

bool SphereSweep::load( ifstream & file, char *ltag )
{
	if ( strcmp( ltag, "SPHERESWEEP" ) ) 
		return false;
	set_load_progress( file );

	char * tag = NULL;
	do {
		tag = tvio_get_next_tag( file );
		if ( tag == NULL ) 
			break;
	
		if ( load_basics( file, tag ) ) continue;
 		if ( location->load( file, tag ) ) continue;
 		if ( size->load( file, tag ) ) continue;
 		if ( rotation->load( file, tag ) ) continue;
		if ( edit->load( file, tag ) ) continue;
		if ( tolerance->load( file, tag ) ) continue;
		if ( spline_type->load( file, tag ) ) continue;
		if ( spline->load( file, tag ) ) continue;

		tvio_skip_section(file );
	} while ( tag != NULL );

	current_point_num->set_range( spline->size(), 1, 1 );
	return true;
}


//*******************************************************
//  Spline Edition
//*******************************************************
void SphereSweep::current_point_num_changed()
{
	if ( spline_edit_box == NULL ) return;
	if ( current_point_num->in_update ) return;
	if ( in_update ) return;
	in_update = true;
	int i = current_point_num->value() - 1;
	current_point->set( spline->getx(i), spline->gety(i), spline->getz(i) );
	current_point->update_widget();
	current_point->unchange();
	current_point_num->update_widget();
	radius->set( spline->get_extra_param(i) );
	radius->update_widget();
	radius->unchange();
	radius->update_widget();

	in_update = false;
}

void SphereSweep::current_point_changed()
{
	if ( in_update ) return;
	if ( spline_edit_box == NULL ) return;
	if ( current_point->in_update ) return;
	int i = current_point_num->value() - 1;
	float x, y, z, ext;
	if ( current_point->change_is_reversible() ) push_undo_item();
	if ( !current_point->is_in_drag() ) push_undo_item();

	current_point->flush();
	current_point->get( x, y, z );
	radius->flush();
	ext = radius->value();
	spline->set_point( i, x, y, z, ext );
	list.invalidate();
	VMAN_DEF
	vmanager->refresh();
}

void SphereSweep::delete_point()
{
	int size = spline->size();
	if ( size <= 4 ) return;
	push_undo_item();
	int selected_point = current_point_num->value() - 1;
	spline->delete_point( selected_point );
	current_point_num->reset_range( 1, size-1 );
	current_point_num_changed();
	list.invalidate();
	VMAN_DEF
	vmanager->refresh();
}

void SphereSweep::append_point()
{
	push_undo_item();
	spline->append();
	int size = spline->size();
	current_point_num->reset_range( 1, size );
	current_point_num->set( size );
	current_point_num->update_widget();
	current_point_num_changed();
	list.invalidate();
	VMAN_DEF
	vmanager->refresh();
}

void SphereSweep::prepend_point()
{
	push_undo_item();
	spline->prepend();
	int size = spline->size();
	current_point_num->reset_range( 1, size );
	current_point_num->set( 1 );
	current_point_num->update_widget();
	current_point_num_changed();
	list.invalidate();
	VMAN_DEF
	vmanager->refresh();
}

void SphereSweep::insert_point()
{
	int selected_point = current_point_num->value() - 1;
	if ( selected_point == 0 ) return;
	push_undo_item();
	spline->insert( selected_point );
	current_point_num->reset_range( 1, spline->size() );
	current_point_num_changed();
	list.invalidate();
	VMAN_DEF
	vmanager->refresh();
}


bool SphereSweep::pick_test( int pname )
{
	int selected_point = current_point_num->value() - 1;
	if ( edit->value() ) {
		if ( ( pname >= pick_name ) && ( pname <= pick_name + spline->size()  ) ) {
			int pointed = pname - pick_name;
			current_point_num->set( pointed + 1 );
			if ( selected_point != current_point_num->value() -1 )
				current_point_num_changed();
			list.invalidate();
			VMAN_DEF
			vmanager->refresh();			
			return true;
		} else 
		return false;
	} else {
		if ( pname == pick_name ) return true;
		else 
			return false;
	}
	return false;
}

void SphereSweep::undo( Object3D *copy )
{
	in_undo = true;
	SphereSweep *changed = (SphereSweep*)copy;
	Spline3D *sp = changed->spline;
	changed->spline = spline;
	spline = sp;
	int spline_size = spline->size();
	current_point_num->reset_range( 1, spline_size );
	if ( current_point_num->value() > spline_size  ) current_point_num->set( spline_size );
	current_point_num_changed();
	VMAN_DEF
	vmanager->refresh();
	in_undo = false;
}
