//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// superellipsoid.cc
//
// Christian Spoer <spoer@gmx.de>
//
// Vincent LE PRINCE <vincentleprince@users.sourceforge.net>
// Christian Spoer <spoer@users.sourceforge.net>
// Copyright (C) 2000-2005 Vincent LE PRINCE
// This file is part of the TRUEVISION Package
//
// Some of the code comes from the GIRAM-Project.
//
//   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/superellipsoid.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

//**************************************
// Constructeur
//**************************************
Superellipsoid::Superellipsoid( app_objs *appref ) : Object3D_with_material( appref )
{
	type = TV_OBJ3D_SUPERELLIPSOID;
	category = TV_OBJ3D_OBJECTS;
	set_name( "Superellipsoid" );
	
	// Base
	location = new ObjParam_point( _("Location"), "LOC", NULL, app_ref, true );
	location->set( 0, 0, 0 );
	scale = new ObjParam_scale( _("Scale"), "SIZE", NULL, app_ref, true );
	scale->set( 0.2, 0.2, 0.2 );
	east = new ObjParam_float( _("\"East\"-Value"), "EAST", NULL, app_ref, true, 0.5 );
	east->set_range( 1.0, 0.001, 0.01, 3 );
	
	north = new ObjParam_float( _("\"North\"-Value"), "NORTH", NULL, app_ref, true, 0.5 );
	north->set_range( 1.0, 0.001, 0.01, 3 );
	rotation = new ObjParam_rotation( _("Rotation"), "ROT", NULL, app_ref, true );
	rotation->set( 0, 0, 0 );
}

Superellipsoid::Superellipsoid( Superellipsoid & ref ) : Object3D_with_material( ref )
{
	location = new ObjParam_point( *ref.location );
	scale = new ObjParam_scale( *ref.scale );
	rotation = new ObjParam_rotation( *ref.rotation );
	east = new ObjParam_float( *ref.east );
	north = new ObjParam_float( *ref.north );
}

Superellipsoid::~Superellipsoid()
{
	delete location;
	delete scale;
	delete rotation;
	delete east;
	delete north;
}

//**************************************
// Display
//**************************************
void Superellipsoid::display( glview *view, bool set_col )
{
	if ( hidden->value() ) return;
	if ( location->changed() || scale->changed() || rotation->changed() || east->changed() || north->changed() )
		list.invalidate();
		
	Object3D::display( view );	
	if ( set_col ) set_color();
	if ( ! list.exec() )
		{
		// mise à zero des parametres
		location->unchange();
		scale->unchange();
		rotation->unchange();
		east->unchange();
		north->unchange();
	
		// creation de la liste si necessaire
		list.begin();
		glPushMatrix();
	
	
		// Position et direction
		gfloat a, b, c;
		location->get( a, b, c );
		glTranslatef( a, b, c );
		rotation->gl_rotate();
		scale->get( a, b, c );
		glScalef( a, b, c );

	const int numt = 10;	
   	gdouble s1,s2,c1,c2,t1,t2;
	gint i,j;
	
	gdouble east_v = east->value();
	gdouble north_v = north->value();

	// The North-Pole
	glBegin( GL_TRIANGLE_FAN );
	glNormal3f( 0, 0, 1 );
	glVertex3f(0.0, 0.0, 1.0);
	
	for ( i = 0 ; i < numt ; i++)
	{
		t1 = M_PI / numt;
    	if (sin(t1) >= 0.0)
			s1 =  pow( sin(t1), north_v);
    	else
			s1 = -pow(-sin(t1), north_v);
    	
		if (cos(t1) >= 0.0)
			c1 =  pow( cos(t1), north_v);
    	else
			c1 = -pow(-cos(t1), north_v);
		
    	t2 = i * 2.0 * M_PI / numt;
    	if (sin(t2) >= 0.0)
			s2 =  pow( sin(t2), east_v);
    	else
			s2 = -pow(-sin(t2), east_v);
    	if (cos(t2) >= 0.0)
			c2 =  pow( cos(t2), east_v);
    	else
			c2 = -pow(-cos(t2), east_v);
    	
		glVertex3f(s1*c2, s1*s2, c1);
		
		t2 = (i+1) * 2.0 * M_PI / numt;
    	if (sin(t2) >= 0.0)
			s2 =  pow( sin(t2), east_v);
    	else
			s2 = -pow(-sin(t2), east_v);
    	if (cos(t2) >= 0.0)
			c2 =  pow( cos(t2), east_v);
    	else
			c2 = -pow(-cos(t2), east_v);
			
		glNormal3f( s1*c2, s1*s2, c1);
    	glVertex3f(s1*c2, s1*s2, c1);
	}
	glEnd();
	
	
	// Then the Mid Zone
	
	glBegin( GL_TRIANGLE_STRIP );
  	
	for (i = 1 ; i < numt-1 ; i++)
  	{
    	for (j = 0 ; j < numt ; j++)
    	{
      		t1 = i * M_PI / numt;
      		if (sin(t1) >= 0.0)
				s1 =  pow( sin(t1), north_v);
      		else
                s1 = -pow(-sin(t1), north_v);
      		if (cos(t1) >= 0.0)
				c1 =  pow( cos(t1), north_v);
      		else
                c1 = -pow(-cos(t1), north_v);
			
      		t2 = j * 2.0 * M_PI / numt;
			if (sin(t2) >= 0.0)
				s2 =  pow( sin(t2), east_v);
      		else
                s2 = -pow(-sin(t2), east_v);
      		if (cos(t2) >= 0.0)
				c2 =  pow( cos(t2), east_v);
      		else
                c2 = -pow(-cos(t2), east_v);
      		glNormal3f(s1*c2, s1*s2, c1);
     		glVertex3f(s1*c2, s1*s2, c1);

      		t1 = (i+1) * M_PI / numt;
      		if (sin(t1) >= 0.0)
				s1 =  pow( sin(t1), north_v);
      		else
                s1 = -pow(-sin(t1), north_v);
      		if (cos(t1) >= 0.0)
				c1 =  pow( cos(t1), north_v);
      		else
                c1 = -pow(-cos(t1), north_v);
			
      		t2 = j * 2.0 * M_PI / numt;
      		if (sin(t2) >= 0.0)
				s2 =  pow( sin(t2), east_v);
      		else
                s2 = -pow(-sin(t2), east_v);
      		if (cos(t2) >= 0.0)
				c2 =  pow( cos(t2), east_v);
      		else
                c2 = -pow(-cos(t2), east_v);
      		glNormal3f(s1*c2, s1*s2, c1);
      		glVertex3f(s1*c2, s1*s2, c1);

			t1 = i * M_PI / numt;
      		if (sin(t1) >= 0.0)
				s1 =  pow( sin(t1), north_v);
		    else
                s1 = -pow(-sin(t1), north_v);
      		if (cos(t1) >= 0.0)
				c1 =  pow( cos(t1), north_v);
      		else
                c1 = -pow(-cos(t1), north_v);
			
      		t2 = (j+1) * 2.0 * M_PI / numt;
      		if (sin(t2) >= 0.0)
				s2 =  pow( sin(t2), east_v);
      		else
                s2 = -pow(-sin(t2), east_v);
      		if (cos(t2) >= 0.0)
				c2 =  pow( cos(t2), east_v);
      		else
				c2 = -pow(-cos(t2), east_v);
      		glNormal3f(s1*c2, s1*s2, c1);
      		glVertex3f(s1*c2, s1*s2, c1);

      		t1 = (i+1) * M_PI / numt;
      		if (sin(t1) >= 0.0)
				s1 =  pow( sin(t1), north_v);
      		else
                s1 = -pow(-sin(t1), north_v);
      		if (cos(t1) >= 0.0)
				c1 =  pow( cos(t1), north_v);
      		else
				c1 = -pow(-cos(t1), north_v);
			
      		t2 = (j+1) * 2.0 * M_PI / numt;
      		if (sin(t2) >= 0.0)
				s2 =  pow( sin(t2), east_v);
      		else
                s2 = -pow(-sin(t2), east_v);
      		if (cos(t2) >= 0.0)
				c2 =  pow( cos(t2), east_v);
      		else
                c2 = -pow(-cos(t2), east_v);
      		glNormal3f( s1*c2, s1*s2, c1);      		
			glVertex3f( s1*c2, s1*s2, c1);
		}
	}
	glEnd();
	
	// And the South Pole
	glBegin( GL_TRIANGLE_FAN );
	glNormal3f(0.0, 0.0, -1.0);
	glVertex3f(0.0, 0.0, -1.0);
	
  	for (i=0 ; i<numt ; i++)
  	{
		t1 = M_PI - M_PI / numt;
    	if (sin(t1) >= 0.0)
			s1 =  pow( sin(t1), north_v);
    	else
			s1 = -pow(-sin(t1), north_v);
    	if (cos(t1) >= 0.0)
			c1 =  pow( cos(t1), north_v);
    	else
			c1 = -pow(-cos(t1), north_v);
		
    	t2 = i * 2.0 * M_PI / numt;
    	if (sin(t2) >= 0.0)
			s2 =  pow( sin(t2), east_v);
    	else
			s2 = -pow(-sin(t2), east_v);
    	if (cos(t2) >= 0.0)
			c2 =  pow( cos(t2), east_v);
    	else
			c2 = -pow(-cos(t2), east_v);
    	glVertex3f(s1*c2, s1*s2, c1);
    	
    	t2 = (i+1) * 2.0 * M_PI / numt;
    	if (sin(t2) >= 0.0)
			s2 =  pow( sin(t2), east_v);
    	else
			s2 = -pow(-sin(t2), east_v);
    	if (cos(t2) >= 0.0)
			c2 =  pow( cos(t2), east_v);
    	else
			c2 = -pow(-cos(t2), east_v);
    	glNormal3f(s1*c2, s1*s2, c1);
     	glVertex3f(s1*c2, s1*s2, c1);
   }

   	glEnd();

	glPopMatrix();
	list.end();
	}
}


//***********************************************
// Edit
//***********************************************
void Superellipsoid::edit_widget( GtkWidget *wid )
{
PREF_DEF
bool tt = pref->tooltips->value();
	// Options communes
	Object3D::edit_widget( wid );
	
	// Options de geometrie
	new_table( edit_cont, _("General settings"), 5 );
		location->get_widget( table, tt, 1 );
		north->get_widget( table, tt, 2 );
		east->get_widget( table, tt, 3 );
		scale->get_widget( table, tt, 4 );
		rotation->get_widget( table, tt, 5 );

	get_texture_widgets( edit_cont, tt );
	gtk_widget_show_all( wid );
}

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

	switch( vmanager->get_pointer_mode() )
	{
	case TV_PMODE_SELECT:
		case TV_PMODE_TRANSLATE:
			location->mouse_drag( drag );
			break;
	
		case TV_PMODE_SCALE:
			{ scale->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 Superellipsoid::pref_changed()
{
	Object3D::pref_changed();
	location->pref_changed();
	scale->pref_changed();
	rotation->pref_changed();
	east->pref_changed();
	north->pref_changed();
}

//***********************************************
// Destroy editor
//***********************************************
void Superellipsoid::destroy_editor()
{
	Object3D::destroy_editor();
	location->clear_widget();
	scale->clear_widget();
	texture->clear_widget();
	rotation->clear_widget();
	east->clear_widget();
	north->clear_widget();
}

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

float x, y, z;
	
	// Check if the north and the east-values are valid
	if ( north->value() > 1.0)
		north->set( 1.0 );
	if ( north->value() <= 0.0)
		north->set( 0.0001 );
	
	if ( east->value() > 1.0)
		east->set( 1.0 );
	if ( east->value() <= 0.0)
		east->set( 0.0001 );
	
	file << "\n\n//Superellipsoid " << name->value();
	file << "\nsuperellipsoid {\n\t";
	file << "<" << east->value() << ", " << north->value() << ">";
	Object3D_with_material::output_to_povray_pass1( file );
	
	scale->get( x, y, z );
	file << "\n\tscale <" << x << ", " << y << ", " << z << ">";
	rotation->output_to_povray( file );
	location->get( x, y, z );
	file << "\n\ttranslate <" << x << "," << y << "," << -z << "> \n\t";
	file << "\n}";
}
	
	
void Superellipsoid::save( ofstream & file )
{
	file << "\nSUPERELLIPSOID{\n";
	save_basics( file );
	location->save( file );
	scale->save( file );
	rotation->save( file );
	east->save( file );
	north->save( file );
	texture->save( file );
	file << "\n}";
}

bool Superellipsoid::load( ifstream & file, char *ltag )
{
	if ( strcmp( ltag, "SUPERELLIPSOID" ) ) 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 ( scale->load( file, tag ) ) continue;
 		if ( rotation->load( file, tag ) ) continue;
 		if ( east->load( file, tag ) ) continue;
 		if ( north->load( file, tag ) ) continue;
			
		tvio_skip_section(file );
	} while ( tag != NULL );

	return true;
}

void
Superellipsoid::set_east(float e)
{
        printf("Setting East Value to: %f\n", e);fflush(stdout);

        if (e < 0.001)
                east->set(0.0001);
        else if (e > 1.0)
                east->set(1.0);
        else
                east->set(e);
}

void
Superellipsoid::set_north(float n)
{
        printf("Setting North Value to: %f\n", n);fflush(stdout);

        if (n < 0.0)
                north->set(0.0001);
        else if (n > 1.0)
                north->set(1.0);
        else
                north->set(n);
}
