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



Blob::Blob( app_objs *appref ) : ObjCSG( appref )
{
type = TV_OBJ3D_BLOB;
category = TV_OBJ3D_OBJECTS;
set_name( "Blob" );
threshold = new ObjParam_float( N_("Threshold"), "THRES", NULL,app_ref, true, 1 );
threshold->set_range( 100, 0, 0.1, 4 );
sturm = new ObjParam_bool( N_("Sturm"), "STURM", NULL, app_ref, true, false );
hierarchy = new ObjParam_bool( N_("Hieriarchy"), "HIERAR", NULL, app_ref, true, true );
evaluate = new ObjParam_bool( N_("Evaluate"), "EVAL", N_("OpenGL preview"), app_ref, true, false );
}


Blob::Blob( Blob & ref ) : ObjCSG( ref ) 
{ 
threshold = new ObjParam_float( *ref.threshold );
sturm = new ObjParam_bool( *ref.sturm ); 
hierarchy = new ObjParam_bool( *ref.hierarchy ); 
evaluate = new ObjParam_bool( *ref.evaluate );
}


void Blob::edit_widget( GtkWidget *wid )
{
PREF_DEF
bool tt = pref->tooltips->value();
// Options communes
Object3D_with_material::edit_widget( wid, true );

// add sphere & add cylinder buttons
GtkWidget *button = gtk_button_new_with_label( N_("Add Spherical component") );
	//gtk_container_set_border_width( GTK_CONTAINER(button), 1 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_blob_add_sphere), this );
	gtk_box_pack_start( GTK_BOX(edit_cont), button, FALSE, FALSE, 1 );
button = gtk_button_new_with_label( N_("Add Cylindrical component") );
	//gtk_container_set_border_width( GTK_CONTAINER(button), 1 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_blob_add_cylinder), this );
	gtk_box_pack_start( GTK_BOX(edit_cont), button, FALSE, TRUE, 1 );

// Options de geometrie
new_table( edit_cont, _("General settings"), 8 );
	evaluate->get_widget( table, tt, 1 );
	threshold->get_widget( table, tt, 2 );
	sturm->get_widget( table, tt, 3 );
	hierarchy->get_widget( table, tt, 4 );
	translation->get_widget( table, tt, 5 );
	size->get_widget( table, tt, 6 );
	rotation->get_widget( table, tt, 7 );

get_texture_widgets( edit_cont, tt );

gtk_widget_show_all( wid );
}

void Blob::destroy_editor()
{
Object3D_with_material::destroy_editor();
translation->clear_widget();
size->clear_widget();
rotation->clear_widget();
threshold->clear_widget();
sturm->clear_widget();
hierarchy->clear_widget();
evaluate->clear_widget();
}

void Blob::pref_changed()
{
Object3D_with_material::pref_changed();
translation->pref_changed();
size->pref_changed();
rotation->pref_changed();
threshold->pref_changed();
sturm->pref_changed();
hierarchy->pref_changed();
evaluate->pref_changed();
}

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

file << "\n\nblob {";
file << "\n\tthreshold " << threshold->value();
for ( unsigned int i = 0 ; i < children.size() ; i++ )
	children[i]->output_to_povray_pass1( file );

file << "\n\tsturm " << ( sturm->value() ? "on" : "off" );
file << "\n\thierarchy " << ( hierarchy->value() ? "on" : "off" );
Object3D_with_material::output_to_povray_pass1( file );
float x, y, z;
size->get( x, y, z );
file << "\n\tscale <" << x << ',' << y << ',' << z << ">\n\t";
rotation->output_to_povray( file );
translation->get( x, y, z );
file << "\n\ttranslate <" << x << "," << y << "," << -z << ">";
file << "\n}";
}


void Blob::save( ofstream & file )
{
file << "\nBLOB{\n";
save_basics( file );
translation->save( file );
size->save( file );
rotation->save( file );
texture->save( file );
node_expanded->save( file );
threshold->save( file );
sturm->save( file );
hierarchy->save( file );
evaluate->save( file );
	
for ( unsigned int i = 0 ; i < children.size() ; i++ )
	children[i]->save( file );
file << "\n}";
}

bool Blob::load( ifstream & file, char *ltag )
{
if ( strcmp( "BLOB", ltag ) ) return false;
OBJLIST_DEF
char * tag = NULL;
do {
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
	if ( load_basics( file, tag ) ) continue;
	if ( translation->load( file, tag ) ) continue;
	if ( rotation->load( file, tag ) ) continue;
	if ( size->load( file, tag ) ) continue;
	if ( node_expanded->load( file, tag ) ) continue;
	if ( threshold->load( file, tag ) ) continue;
	if ( sturm->load( file, tag ) ) continue;
	if ( hierarchy->load( file, tag ) ) continue;
	if ( evaluate->load( file, tag ) ) continue;
	
	Object3D *obj = objlist->create_object( tag );
	if ( obj != NULL )
		{
		if ( obj->load( file, tag ) )
			{
			add_object( obj );
			continue;
			}
		else delete obj;
		}
		
	tvio_skip_section(file );
	} while ( tag != NULL );
//cout << "\nblob loaded !"; cout.flush();
return true;
}

void Blob::add_sphere()
{
BlobSphere *sphere = new BlobSphere( app_ref );
add_object( sphere );
VMAN_DEF
vmanager->refresh();
((Object3D*)(sphere))->add_to_tree( tree_view, tree_store, tree_selection, &node_iter , NULL );
//((Object3D*)(sphere))->ctree_node_select();
}

void Blob::add_cylinder()
{
BlobCylinder *cylinder = new BlobCylinder( app_ref );
add_object( cylinder );
VMAN_DEF
vmanager->refresh();
((Object3D*)(cylinder))->add_to_tree( tree_view, tree_store, tree_selection, &node_iter , NULL );
//((Object3D*)(cylinder))->ctree_node_select();
}


//**************************************
// Display
//**************************************
void Blob::display( glview *view, bool set_col )
{
if ( hidden->value() ) return;
if ( evaluate->value() == FALSE ) { ObjCSG::display( view );  return; }

if ( translation->changed() || size->changed() || rotation->changed() || threshold->changed() )
	list.invalidate();
for ( unsigned int i = 0 ; i < children.size() ; i++ )
	if ( children[i]->changed() ) list.invalidate();

if ( set_col ) set_color();
Object3D::display( view );	
if ( ! list.exec() )
	{
	// mise à zero des parametres
	translation->unchange();
	size->unchange();
	rotation->unchange();
	threshold->unchange();

	// creation de la liste si necessaire
	list.begin();
	glPushMatrix();

	// Position et direction
	gfloat x, y, z;
	translation->get( x, y, z );
	glTranslatef( x, y, z );
	rotation->gl_rotate();

	size->get( x, y, z );
	glScalef( x, y, z );

	float max[3],  min[3];
	for ( int i = 0 ; i < 3 ; i++ ) { min[i] = 100; max[i] = -100; }
	//gfloat a,b,c;

	for ( unsigned int i = 0 ; i < children.size() ; i++ )
		{
		float *pos = children[i]->get_location();
		float radius = children[i]->get_strength_radius();
		children[i]->unchange();
		for ( int i = 0 ; i < 3 ; i++ ) 
			{
			float test = pos[i] + radius;
			if ( test > max[i] ) max[i] = test;
			test = pos[i] - radius;
			if ( test < min[i] ) min[i] = test;
			}
		for ( int i = 0 ; i < 3 ; i++ )	
			{
			float len = ( max[i] - min[i] ) / 50.0;
			max[i] += len;
			min[i] -= len;
			}
		}
	
	ImplicitSurface Preview( app_ref, BlobTestFunc, this, min, max );
		
	glPopMatrix();
	list.end();
	}
}


bool Blob::is_inside( float x, float y, float z )
{
	
float strength = 0;
for ( unsigned int i = 0 ; i < children.size() ; i++ )
		strength += children[i]->get_strength( x, y, z );
//cout << "\ndens = " << strength; cout.flush();
if ( threshold->value() < strength ) return true; 
else return false;
}
