//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// prism.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/prism.h"
#include "include/viewmanager.h"
#include "include/objectlist.h"
#include "include/tvio.h"
#include "include/preferences.h"
#include "include/utils3d.h"


// Prism preview subdivisions
const int PrismSubdivision = 12;
const int PrismSplineSubdivision = 10;

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

// Spline type
const int prism_spline_type_num = 4;
const gchar * prism_spline_type_list[prism_spline_type_num] = { N_("Linear"), N_("Quadratic"), N_("Cubic"), N_("Bezier") };
const char *prism_spline_type_keys[prism_spline_type_num] = { "linear_spline", "quadratic_spline", "cubic_spline", "bezier_spline" };
	
// Sweep type
const int sweep_type_num = 2;
const gchar * sweep_type_list[ sweep_type_num ] = { N_("Linear"), N_("Conic") };
const char *prism_sweep_type_keys[ sweep_type_num ] = { "linear_sweep", "conic_sweep" };

//**************************************
// Prism - Constructeur
//**************************************
Prism::Prism( app_objs *appref ) : Object3D_with_material( appref )
{
type = TV_OBJ3D_PRISM;
category = TV_OBJ3D_OBJECTS;
set_name( "Prism" );
spline_edit_box = NULL;
in_undo = false;
in_update = false;

// Spline
Spline2D *spline = new Spline2D( TV_SPLINE2D_LINEAR, PrismSplineSubdivision );
spline->set_for_lathe( false );
spline->add_point( 0.2, 0.2, -0.1, 0.1, 0.1, -0.1 );
spline->add_point( 0.2, -0.2 , 0.1, 0.1, -0.1, -0.1 );
spline->add_point( -0.2, -0.2, 0.1, -0.1, -0.1, 0.1 );
spline->add_point( -0.2, 0.2, -0.1, -0.1, 0.1,0.1 );
splines.push_back( spline );
	
// 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 Prism"), app_ref, true, true );
sturm = new ObjParam_bool( N_("Sturm"), "STURM", N_("Use alternative root solver"), app_ref, true, false );
spline_type = new ObjParam_option_combo( N_("Spline mode"), "SPLINET", NULL, app_ref, true );
spline_type->set_list( prism_spline_type_list, prism_spline_type_num, 0 );
sweep_type = new ObjParam_option_combo( N_("Sweep mode"), "SWEEP", NULL, app_ref, true );
sweep_type->set_list( sweep_type_list, sweep_type_num, 0 );
open = new ObjParam_bool( N_("Open"), "OPEN", N_("Open caps"), app_ref, true, false );
height1 = new ObjParam_float( N_("Height 1"), "HEI1", N_("Height of base end"), app_ref, true, 0 );
height1->set_range( 50, -50, 0.1, 5 );
height2 = new ObjParam_float( N_("Height 2"), "HEI2", N_("Height of cap end"), app_ref, true, 0.2 );
height2->set_range( 50, -50, 0.1, 5 );

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_spline_num = new ObjParam_int( N_("Spline #"), "SNUM", NULL, app_ref, true, 1 );
current_spline_num->set_range( splines.size(), 1, 1 );
current_spline_num->set_send_undo( false );
current_point = new ObjParam_point_2d( N_("Position"), "POS", NULL, app_ref, false, true );
current_point->set_send_undo( false );
current_point_ctrl1 = new ObjParam_point_2d( N_("Control1"), "CTRL1", NULL, app_ref, false, true );
current_point_ctrl1->set_send_undo( false );
current_point_ctrl2 = new ObjParam_point_2d( N_("Control2"), "CTRL2", NULL, app_ref, false, true );
current_point_ctrl2->set_send_undo( false );
}

Prism::Prism( Prism & 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 );
sturm = new ObjParam_bool( *ref.sturm );
spline_type = new ObjParam_option_combo( *ref.spline_type );
sweep_type = new ObjParam_option_combo( *ref.sweep_type );
current_point_num = new ObjParam_int( *ref.current_point_num );
current_spline_num = new ObjParam_int( *ref.current_spline_num );
current_point = new ObjParam_point_2d( *ref.current_point );	
current_point_ctrl1 = new ObjParam_point_2d( *ref.current_point_ctrl1 );	
current_point_ctrl2 = new ObjParam_point_2d( *ref.current_point_ctrl2 );	
open = new ObjParam_bool( *ref.open );
height1 = new ObjParam_float( *ref.height1 );
height2 = new ObjParam_float( *ref.height2 );

for ( unsigned int i = 0 ; i < ref.splines.size() ; i++ )
	splines.push_back( ref.splines[i]->auto_copy() );
current_point_num->set_range( splines[ current_spline_num->value() - 1 ]->size(), 1, 1 );
}

Prism::~Prism()
{
delete location;
delete size;
delete rotation;
delete edit;
delete sturm;
delete spline_type;
delete sweep_type;
delete current_point_num;
delete current_spline_num;
delete current_point;
delete current_point_ctrl1;
delete current_point_ctrl2;
splines.clear();
delete open;
delete height1;
delete height2;
}

//**************************************
// Display
// Dessin de la boite
//**************************************
void Prism::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() || current_point->changed() || current_point_ctrl1->changed() || current_point_ctrl1->changed() \
	|| sweep_type->changed()  || open->changed()  || height1->changed() || height2->changed() || current_spline_num->changed())
	list.invalidate();

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

// Mode Edition
// Sans liste et avec picking pour les points, mais pas pour les lignes
if (  edit->value() )
	{
	OBJLIST_DEF
	int selected_point = current_point_num->value() - 1;
	set_color();
	glPushMatrix();

	int mode;
	pick_name = -1;		
	for ( unsigned int sp = 0 ; sp < splines.size() ; sp++ )
		{
		Spline2D *spline = splines[sp];
		glGetIntegerv( GL_RENDER_MODE, &mode );
		spline->set_type( (SplineType)spline_type->value() );
		spline->set_subdivisions( quality );

		// Lines ( linear, quadradtic, cubic or bezier... )
		if ( mode != GL_SELECT ) // Draw all
			{
			spline->gl_display_segments();
			if ( spline_type->value() !=  (int)TV_SPLINE2D_LINEAR )
				{
				glEnable( GL_LINE_STIPPLE );
				glLineStipple( 1, 0xAAA );
				glBegin( GL_LINES );
				if ( spline_type->value() ==  (int)TV_SPLINE2D_QUADRATIC ) 
					{			
					spline->display_point( 0 );
					spline->display_point( 1 );
					}
				if ( spline_type->value() ==  (int)TV_SPLINE2D_CUBIC ) 
					{
					int size = spline->size();						
					spline->display_point( 0 );
					spline->display_point( 1 );
					spline->display_point( size-2 ); 
					spline->display_point( size-1 );	
					}
				if ( spline_type->value() ==  (int)TV_SPLINE2D_BEZIER && sp == (unsigned)(current_spline_num->value() - 1) ) 
					{
					spline->display_point( selected_point );
					spline->display_point_ctrl1( selected_point );
					spline->display_point( selected_point );
					spline->display_point_ctrl2( selected_point );
					}
				glEnd();
				glDisable( GL_LINE_STIPPLE );
				}		
			} // end non select mode

	// Draw Points ( much, much easier :)
			int size = spline->size();
			for ( int i = 0 ; i < size ; i++ ) 
				{
				int pick = objlist->get_pick_name();
				if ( pick_name == -1 ) pick_name = pick;
				glLoadName( pick );
				if ( i != selected_point || sp != (unsigned)(current_spline_num->value() - 1)) 
					{
					glBegin( GL_POINTS );
					spline->display_point(i);
					glEnd();
					}
				else
					{
					if ( selected_ctrl == 0  ) glColor3f( 1.0, 0, 0 );
					glPointSize( 6 );
					glBegin( GL_POINTS );
					spline->display_point( selected_point );	
					glEnd();
					set_color();
					if ( spline_type->value() == TV_SPLINE2D_BEZIER )
						{
						pick = objlist->get_pick_name();
						glLoadName( pick );
						if ( selected_ctrl == 1  ) glColor3f( 1.0, 0, 0 );
						glBegin( GL_POINTS );
						spline->display_point_ctrl1( selected_point );	
						glEnd();
						set_color();
						pick = objlist->get_pick_name();
						glLoadName( pick );
						if ( selected_ctrl == 2  ) glColor3f( 1.0, 0, 0 );
						glBegin( GL_POINTS );
						spline->display_point_ctrl2( selected_point );	
						glEnd();
						}
					glPointSize( 4 );
					set_color();
					}
				last_pick_name  = pick;
				}
		glPopMatrix();
		list.end();
		}
	}
else
	// Prism opengl drawing
	// No point selection, we got a list
	{	
	Object3D::display( view );	
	if ( set_col ) set_color();
	if ( ! list.exec() )
		{
		// mise à zero des parametres
		location->unchange();
		size->unchange();
		rotation->unchange();
		edit->unchange();
		spline_type->unchange();
		sweep_type->unchange();
		current_point_num->unchange();
		current_spline_num->unchange();
		current_point->unchange();
		current_point_ctrl1->unchange();
		current_point_ctrl2->unchange();
		open->unchange();
		height1->unchange();
		height2->unchange();
			
		// creation de la liste si necessaire
		list.begin();
		glPushMatrix();
		
		// Position et 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 );
	
		float *xcoords, *ycoords, *prev_xcoords=NULL, *prev_ycoords = NULL;
		int ptnum = 0, prev_ptnum=0;
		bool pair = true;
		float hei1 = height1->value();
		float hei2 = height2->value();

		for ( unsigned int sp = 0 ; sp < splines.size() ; sp++ )
			{
			Spline2D *spline = splines[sp];
			spline->set_type( (SplineType)spline_type->value() );
			spline->set_subdivisions( quality );
			spline->get_segments( ptnum, xcoords, ycoords );
			for ( int i = 0 ; i < ptnum-1 ; i++ )
				{
				glBegin( GL_QUAD_STRIP );
				for ( int slice = 0 ; slice < ptnum ; slice++ )
					{
					if ( sweep_type->value() == 0 ) 
						{
						if ( slice == 0) get_normal( xcoords[slice], hei1, ycoords[slice], xcoords[slice+1], hei2, ycoords[ slice+1] , xcoords[slice], hei2, ycoords[ slice ], pair );
						else if ( slice < ptnum - 1 ) 	get_normal( xcoords[slice-1], hei1, ycoords[slice-1], xcoords[slice+1], hei2, ycoords[ slice+1] , xcoords[slice-1], hei2, ycoords[ slice-1 ], pair );
						glVertex3f( xcoords[slice], hei1, ycoords[slice] ); 
						glVertex3f( xcoords[slice], hei2, ycoords[slice] ); 
						}
					else
						{
						if ( slice == 0) get_normal( xcoords[slice], hei1, ycoords[slice], xcoords[slice+1], hei2, ycoords[ slice+1] , xcoords[slice], hei2, ycoords[ slice ], pair );
						else if ( slice < ptnum - 1 ) 	get_normal(0, hei1, 0, xcoords[slice+1], hei2, ycoords[ slice+1] , xcoords[slice-1], hei2, ycoords[ slice-1 ], pair );
						glVertex3f( hei1*xcoords[slice], hei1, hei1*ycoords[slice] ); 					
						glVertex3f( hei2*xcoords[slice], hei2, hei2*ycoords[slice] ); 
						}
					}
				glEnd();
				}
				
			if ( !open->value() )
				{
				if ( pair && sp == splines.size()-1 )
					{
					if ( sweep_type->value() == 0 )
						{
						glBegin( GL_TRIANGLE_FAN );
						if ( hei1 < hei2 ) glNormal3f( 0, -1, 0 );
						else glNormal3f( 0, 1, 0 );
						glVertex3f( 0, hei1, 0 );
						for ( int i = 0 ; i < ptnum ; i++ )
							glVertex3f( xcoords[i], hei1, ycoords[i] ); 
						glEnd();
						}
						
					glBegin( GL_TRIANGLE_FAN );
					if ( hei1 < hei2 ) glNormal3f( 0, 1, 0 );
					else glNormal3f( 0, -1, 0 );
					glVertex3f( 0, hei2, 0 );
					if ( sweep_type->value() == 0 )		
						for ( int i = 0 ; i < ptnum ; i++ )
							glVertex3f( xcoords[i], hei2, ycoords[i] ); 
					else
						for ( int i = 0 ; i < ptnum ; i++ )
							glVertex3f( hei2*xcoords[i], hei2, hei2*ycoords[i] ); 
					glEnd();
					}
					
				if ( !pair )
					{
					if ( sweep_type->value() == 0 )
						{
						glBegin( GL_QUAD_STRIP );
						if ( hei1 < hei2 ) glNormal3f( 0, -1, 0 );
						else glNormal3f( 0, 1, 0 );
						for ( int i = 0 ; i < ptnum && i < prev_ptnum ; i++ )
								{
								glVertex3f( xcoords[i], hei1, ycoords[i] );
								glVertex3f( prev_xcoords[i], hei1, prev_ycoords[i] );
								}
						glEnd();
							if ( ptnum < prev_ptnum ) 
							{
							glBegin( GL_TRIANGLE_FAN );
							glVertex3f( xcoords[ptnum-1], hei1, ycoords[ptnum-1] );
							for ( int i = ptnum-1 ; i < prev_ptnum ; i++ )
								glVertex3f( prev_xcoords[i], hei1, prev_ycoords[i] );							
							glEnd();
							}
	
						if ( ptnum > prev_ptnum ) 
							{
							glBegin( GL_TRIANGLE_FAN );
							glVertex3f( prev_xcoords[prev_ptnum-1], hei1, prev_ycoords[prev_ptnum-1] );
							for ( int i = prev_ptnum-1 ; i < ptnum ; i++ )
								glVertex3f( xcoords[i], hei1, ycoords[i] );							
							glEnd();
							}
						}
						
					glBegin( GL_QUAD_STRIP );
					if ( hei1 < hei2 ) glNormal3f( 0, 1, 0 );
					else glNormal3f( 0, -1, 0 );
					for ( int i = 0 ; i < ptnum && i < prev_ptnum ; i++ )
							{
							glVertex3f( xcoords[i], hei2, ycoords[i] );
							glVertex3f( prev_xcoords[i], hei2, prev_ycoords[i] );
							}
					glEnd();
					if ( ptnum < prev_ptnum ) 
						{
						glBegin( GL_TRIANGLE_FAN );
						glVertex3f( xcoords[ptnum-1], hei2, ycoords[ptnum-1] );
						for ( int i = ptnum-1 ; i < prev_ptnum ; i++ )
							glVertex3f( prev_xcoords[i], hei2, prev_ycoords[i] );							
						glEnd();
						}
	
					if ( ptnum > prev_ptnum ) 
						{
						glBegin( GL_TRIANGLE_FAN );
						glVertex3f( prev_xcoords[prev_ptnum-1], hei2, prev_ycoords[prev_ptnum-1] );
						for ( int i = prev_ptnum-1 ; i < ptnum ; i++ )
							glVertex3f( xcoords[i], hei2, ycoords[i] );							
						glEnd();
						}
					}
				}
				
			if (  pair && sp != splines.size() )  
				{
				prev_xcoords = xcoords; prev_ycoords = ycoords;
				prev_ptnum = ptnum;
				}
			else
				{
				delete xcoords;
				delete ycoords;
				if ( prev_xcoords != NULL ) delete prev_xcoords;
				if ( prev_ycoords != NULL ) delete prev_ycoords;
				}
			pair = !pair;
			}
		glPopMatrix();
		list.end();
		}			
	}
}


//***********************************************
// Edit
//***********************************************

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

// Spline edition
new_frame( edit_cont, N_("Prism edition") );
new_table_no_frame( frame, 6 );
	sweep_type->get_widget( table, tt, 1 );
	height1->get_widget( table, tt, 2 );
	height2->get_widget( table, tt, 3 );
	open->get_widget( table, tt, 4 );
	spline_type->get_widget( table, tt, 5 );
	edit->get_widget( table, tt, 6 );

spline_edit_box = dlg_simple_box_frame( N_("Splines edition"), frame );
		current_spline_num->get_widget( spline_edit_box, tt );

// Bouttons spline num
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( _("Add spline") );
	gtk_box_pack_start( GTK_BOX(hbox), button, TRUE, TRUE, 1 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_prism_append_spline), this );
	button = gtk_button_new_with_label( _("Delete spline") );
	gtk_box_pack_start( GTK_BOX(hbox), button, TRUE, TRUE, 1 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_prism_delete_spline), this );

	current_point_num->get_widget( spline_edit_box, tt );
	current_point->get_widget( spline_edit_box, tt,  sign_prism_current_point_changed, this );
	current_point_ctrl1->get_widget( spline_edit_box, tt,  sign_prism_current_point_changed, this );
	current_point_ctrl2->get_widget( spline_edit_box, tt,  sign_prism_current_point_changed, this );

// Bouttons spline edit
hbox = gtk_hbox_new( TRUE, 3 );
gtk_box_pack_start( GTK_BOX(spline_edit_box), hbox, TRUE, TRUE, 1 );
	button = gtk_button_new_with_label( _("Append") );
	gtk_box_pack_start( GTK_BOX(hbox), button, TRUE, TRUE, 1 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_prism_append_point), this );
	button = gtk_button_new_with_label( _("Prepend") );
	gtk_box_pack_start( GTK_BOX(hbox), button, TRUE, TRUE, 1 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_prism_prepend_point), this );
	button = gtk_button_new_with_label( _("Insert") );
	gtk_box_pack_start( GTK_BOX(hbox), button, TRUE, TRUE, 1 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_prism_insert_point), this );
	button = gtk_button_new_with_label( _("Delete") );
	gtk_box_pack_start( GTK_BOX(hbox), button, TRUE, TRUE, 1 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_prism_delete_point), this );
	
// Options de geometrie
new_table( edit_cont, _("General settings"), 4 );
	location->get_widget( table, tt, 1 );
	size->get_widget( table, tt, 2 );
	rotation->get_widget( table, tt, 3 );
	sturm->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_spline_num->connect_signal( GTK_SIGNAL_FUNC(sign_prism_current_spline_num_changed), this );
current_spline_num_changed();
current_point_num->connect_signal( GTK_SIGNAL_FUNC(sign_prism_current_point_num_changed), this );
//current_point_num_changed();
}

//***********************************************
// Mouse drag
//***********************************************
void Prism::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() ) 
			{
			if ( selected_ctrl == 0 ) current_point->mouse_drag( drag );
			if ( selected_ctrl == 1 ) current_point_ctrl1->mouse_drag( drag );
			if ( selected_ctrl == 2 ) current_point_ctrl2->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 Prism::pref_changed()
{
Object3D::pref_changed();
location->pref_changed();
size->pref_changed();
rotation->pref_changed();
edit->pref_changed();
sturm->pref_changed();
spline_type->pref_changed();
sweep_type->pref_changed();
current_point_num->pref_changed();
current_spline_num->pref_changed();
current_point->pref_changed();
current_point_ctrl1->pref_changed();
current_point_ctrl2->pref_changed();
open->pref_changed();
height1->pref_changed();
height2->pref_changed();
}

//***********************************************
// Destroy editor
//***********************************************
void Prism::destroy_editor()
{
Object3D::destroy_editor();
location->clear_widget();
size->clear_widget();
texture->clear_widget();
rotation->clear_widget();
edit->clear_widget();
sturm->clear_widget();
spline_type->clear_widget();
sweep_type->clear_widget();
current_point_num->clear_widget();
current_spline_num->clear_widget();
current_point->clear_widget();
current_point_ctrl1->clear_widget();
current_point_ctrl2->clear_widget();
open->clear_widget();
height1->clear_widget();
height2->clear_widget();
if ( spline_edit_box != NULL ) spline_edit_box = NULL;
}

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

int sptype = spline_type->value();
int swtype = sweep_type->value();
file << "\nprism {\n\t" << prism_spline_type_keys[ sptype ] << ' ' << prism_sweep_type_keys[ swtype ] ;
file << "\n\t" << height1->value() << ", " 	<< height2->value() << ", ";

int psize = 0;
for ( unsigned int i = 0 ; i < splines.size() ; i++ )
	psize += splines[i]->size() + 1;
if ( sptype == TV_SPLINE2D_CUBIC ) psize += splines.size() ;
if ( spline_type->value() == TV_SPLINE2D_BEZIER )
	psize = ( psize - splines.size() )*4;
file  << psize << ",\n\t";

for ( unsigned int i = 0 ; i < splines.size() ; i++ )
	{
	splines[i]->set_type( (SplineType)spline_type->value() );
	splines[i]->output_to_povray( file );
	if ( i < splines.size() -1 ) file << ", ";
	}

if ( open->value() ) file << "\n\topen";
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";
if ( sturm->value() ) file << "sturm\n\t";
file << "\n}";
}



//***********************************************
// Save & load
//***********************************************
void Prism::save( ofstream & file )
{
file << "\nPRISM{\n";
save_basics( file );
location->save( file );
size->save( file );
rotation->save( file );
texture->save( file );
edit->save( file );
sturm->save( file );
spline_type->save( file );
sweep_type->save( file );
open->save( file );
height1->save( file );
height2->save( file );
for ( unsigned int i = 0 ; i < splines.size() ; i++ )	
	splines[i]->save( file );
file << "\n}";
}

bool Prism::load( ifstream & file, char *ltag )
{
if ( strcmp( ltag, "PRISM" ) ) return false;
set_load_progress( file );
splines.clear();

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 ( sturm->load( file, tag ) ) continue;
	if ( spline_type->load( file, tag ) ) continue;
	if ( sweep_type->load( file, tag ) ) continue;
	if ( open->load( file, tag ) ) continue;
	if ( height1->load( file, tag ) ) continue;
	if ( height2->load( file, tag ) ) continue;

	if ( ! strcmp( tag, "SPLINE2D" ) ) 
		{
		Spline2D *spline = new Spline2D( TV_SPLINE2D_LINEAR, PrismSplineSubdivision );
		spline->load( file, tag );
		spline->set_for_lathe( false );
		splines.push_back( spline );
		continue;
		}
		
	tvio_skip_section(file );
	} while ( tag != NULL );

current_spline_num->set_range( splines.size(), 1, 1);
current_point_num->set_range( splines[0]->size(), 1, 1 );	
return true;
}


//*******************************************************
//  Spline Edition
//*******************************************************
void Prism::current_spline_num_changed()
{
if ( spline_edit_box == NULL ) return;
if ( current_spline_num->in_update ) return;
if ( in_update ) return;
in_update = true;
Spline2D *spline = splines[ current_spline_num->value() -  1 ];
if ( current_point_num->value() > spline->size() ) current_point_num->set(0);
current_spline_num->update_widget();
in_update = false;
current_point_num_changed();
}


void Prism::current_point_num_changed()
{
Spline2D *spline = splines[ current_spline_num->value() -1 ];
if ( spline_edit_box == NULL ) return;
if ( current_point_num->in_update ) return;
if ( in_update ) return;
in_update = true;
selected_ctrl = 0; 
int i = current_point_num->value() - 1;
current_point->set( spline->getx(i), spline->gety(i) );
current_point->update_widget();
current_point->unchange();
float a[2];
spline->get_point_ctrl1( i, a );
current_point_ctrl1->set( a[0], a[1] );
current_point_ctrl1->update_widget();
current_point_ctrl1->unchange();
spline->get_point_ctrl2( i, a );
current_point_ctrl2->set( a[0], a[1] );
current_point_ctrl2->update_widget();
current_point_ctrl2->unchange();
current_point_num->update_widget();
in_update = false;
}

void Prism::current_point_changed()
{
if ( in_update ) return;
if ( spline_edit_box == NULL ) return;
if ( current_point->in_update ) return;
if ( current_point_ctrl1->in_update ) return;
if ( current_point_ctrl2->in_update ) return;
Spline2D *spline = splines[ current_spline_num->value() -1  ];
int i = current_point_num->value() - 1;
float x, y, z, c1x, c1y, c2x, c2y;
if ( current_point->change_is_reversible() ||  current_point_ctrl1->change_is_reversible() ||  current_point_ctrl1->change_is_reversible() ) push_undo_item();
if ( !current_point->is_in_drag() &&  !current_point_ctrl1->is_in_drag() &&   !current_point_ctrl2->is_in_drag()) push_undo_item();

current_point->flush();
current_point_ctrl1->flush();
current_point_ctrl2->flush();
current_point->get( x, y, z );
current_point_ctrl1->get( c1x, c1y, z );
current_point_ctrl2->get( c2x, c2y, z );
spline->set_point( i, x, y, c1x, c1y, c2x, c2y );
list.invalidate();
VMAN_DEF
vmanager->refresh();
}

void Prism::delete_point()
{
Spline2D *spline = splines[ current_spline_num->value() -1 ];
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 Prism::append_point()
{
push_undo_item();
Spline2D *spline = splines[ current_spline_num->value() -1 ];
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 Prism::prepend_point()
{
push_undo_item();
Spline2D *spline = splines[ current_spline_num->value() -1 ];
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 Prism::insert_point()
{
int selected_point = current_point_num->value() - 1;
if ( selected_point == 0 ) return;
push_undo_item();
Spline2D *spline = splines[ current_spline_num->value() -1 ];
spline->insert( selected_point );
current_point_num->reset_range( 1, spline->size() );
current_point_num_changed();
list.invalidate();
VMAN_DEF
vmanager->refresh();
}


bool Prism::pick_test( int pname )
{
int selected_point = current_point_num->value() - 1;
unsigned int selected_spline = current_spline_num->value() - 1;
//cout << "\nPrism pick test :";
//cout << "\n\tpname = " << pname;
//cout << "\n\tpick_name = " << pick_name;
//cout << "\n\tlast pick name = " << last_pick_name;
//cout.flush();	
	
if ( edit->value() )
	{
	int offset = ( spline_type->value() == TV_SPLINE2D_BEZIER ) ? 2 : 0;
	if ( pname >= pick_name && pname <= last_pick_name )
		{
		int cur_pname = pick_name;
		for ( unsigned int i	= 0 ; i < splines.size() ; i++ )
			{
			offset = ( spline_type->value() == TV_SPLINE2D_BEZIER  && i == selected_spline ) ? 2 : 0;
			int max_pname = cur_pname + splines[i]->size() + offset - 1;
			if ( pname <= max_pname )
				{
				current_spline_num->set( i +1 );
				if ( offset == 0 ) { current_point_num->set( pname - cur_pname +1 );  }
				else
					{
					selected_ctrl = 0;
					int pointed = pname - cur_pname;
					if ( pointed < selected_point ) { current_point_num->set( pointed + 1 );  }
					if ( pointed > selected_point + 2 ) { current_point_num->set( pointed-1 ); }
					if ( pointed > selected_point && pointed <= selected_point + 2 ) selected_ctrl = pointed - selected_point;
					//cout << "\n\tselected = " << selected;
					//cout << "\n\tpointed = " << pointed; 
					//cout << "\n\tselected ctrl = " << selected_ctrl;
					//cout.flush();
					}
					break;
				}
			cur_pname += max_pname;
			}
			
		if ( selected_spline != (unsigned int )(current_spline_num->value() - 1) ) 
			current_spline_num_changed();
		else 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 Prism::undo( Object3D *copy )
{
in_undo = true;
Prism *changed = (Prism*)copy;
changed->splines.swap( splines );
current_spline_num->reset_range( 1, splines.size() );
VMAN_DEF
vmanager->refresh();
current_spline_num->update_widget();
in_undo = false;
}



void Prism::append_spline()
{
Spline2D *spline = new Spline2D( TV_SPLINE2D_LINEAR, PrismSplineSubdivision );
spline->set_for_lathe( false );
spline->add_point( 0.2, 0.2, -0.1, 0.1, 0.1, -0.1 );
spline->add_point( 0.2, -0.2 , 0.1, 0.1, -0.1, -0.1 );
spline->add_point( -0.2, -0.2, 0.1, -0.1, -0.1, 0.1 );
spline->add_point( -0.2, 0.2, -0.1, -0.1, 0.1,0.1 );
splines.push_back( spline );
	
current_spline_num->reset_range( 1, splines.size() );
current_spline_num->set( splines.size() );
VMAN_DEF
vmanager->refresh();
current_spline_num->update_widget();
}

void Prism::delete_spline()
{
if ( splines.size() == 1 )  return;
int spline_num = current_spline_num->value() -1;
Spline2D *spline = splines[ spline_num ];
splines.erase( splines.begin() + spline_num );
delete spline;
current_spline_num->reset_range( 1, splines.size() );
if ( (unsigned int )current_spline_num->value() > splines.size() ) current_spline_num->set( splines.size() );
VMAN_DEF
vmanager->refresh();
//current_spline_num_changed();
current_spline_num->update_widget();
}
