/* -*- mia-c++ -*-
 *
 * This file is part of viewitgui - a library and program for the
 * visualization of 3D data sets. 
 *
 * Copyright (c) Leipzig, Madrid 1999-2013 Mirco Hellmann, Gert Wollny
 *
 * viewitgui 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 3 of the License, or
 * (at your option) any later version.
 *
 * viewitgui 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 viewitgui; if not, see <http://www.gnu.org/licenses/>.
 */

#include <viewit/mesh.hh>
#include <cmath>
#include <viewit/gltext.hh>

#include <queue>
#include <iostream>
using namespace std; 

//#define CUT_SIZE 65500 




CTriangleMeshABase::CTriangleMeshABase(const string& name, size_t _n_triangles, triangle_ui_t *t,
				     size_t _n_vertices):
	TDrawable(name),
	n_triangles(_n_triangles),
	n_vertices(_n_vertices)
{
	tri_mirror = new triangle_ui_t[n_triangles];
	triangles = t;
	
	light_on();
	culling_on();	
	triangle_mid_points = new C3DFVector[n_triangles];
}

CTriangleMeshABase::CTriangleMeshABase(const string& name, const CTriangleMeshABase& org):
	TDrawable(name,org),
	n_triangles(org.n_triangles),
	n_vertices(org.n_vertices)
{
	triangles = new triangle_ui_t[n_triangles];
	tri_mirror = new triangle_ui_t[n_triangles];
	memcpy(triangles,org.triangles, sizeof(triangle_ui_t) * n_triangles);
	light_on();
	culling_on();
	triangle_mid_points = new C3DFVector[n_triangles];
}

CTriangleMeshABase::~CTriangleMeshABase()
{
	delete[] triangles;
	delete[] tri_mirror;
	delete[] triangle_mid_points;	
}

void CTriangleMeshABase::do_gl_draw(const TCamera& c) const
{
	if (draw_shiny()) {
		cvdebug() << "CTriangleMeshABase draw shiny\n"; 
		TColor specular_color(.5,.5,0.5,1.0);
		const float shine = 12.0f;
		glMaterialfv(GL_FRONT,GL_SPECULAR,&specular_color.x);
		glMaterialfv(GL_FRONT,GL_SHININESS,&shine);
	}else{
		cvdebug() << "CTriangleMeshABase draw normal\n"; 
		TColor specular_color(.0,.0,0.0,1.0);
		const float shine = 0.0f;
		glMaterialfv(GL_FRONT,GL_SPECULAR,&specular_color.x);
		glMaterialfv(GL_FRONT,GL_SHININESS,&shine);
	}
	if (is_transparent()) {
		sort_triangles(triangles, tri_mirror,c);
	}
		
#ifdef LOD_EMULATE		
	if (c.low_detail()) {
		for (int i = 0; i < n_triangles - 5000; i += 20000) {
			glDrawElements(GL_TRIANGLES,15000,GL_UNSIGNED_INT,&tri_mirror[i]);
		}
	}else
#endif			
		glDrawElements(GL_TRIANGLES,3 * n_triangles,GL_UNSIGNED_INT,tri_mirror);
}

bool CTriangleMeshABase::do_handle_key_event(int key)
{
	switch (key) {
	case 'g':toggle_draw_shiny(); break;
	case 'l':toggle_lightmodel();break; // light on both sides or not
	case 'k':toggle_culling_enable();break; 
	case 'f':
		if (do_cull_front())
			cull_back();
		else
			cull_front();
	default:
		return TDrawable::do_handle_key_event(key);
	}
	return true; 
}

template <class triangle>
void CTriangleMeshABase::sort_triangles(const triangle *in, triangle *out, const TCamera& c)const
{
	// this is too slow
	priority_queue<collect_t> c_queue; 
		
	for (size_t i = 0; i < n_triangles; i++) {
		collect_t collect; 
		collect.i = i; 
		collect.d = - c.get_z_dist(triangle_mid_points[i]);
		c_queue.push(collect);
	}
	
	while (!c_queue.empty()) {
		collect_t col= c_queue.top();
		c_queue.pop();
		*out = in[col.i];
		out++;

	}
}

const short endian_magic=1199; 

bool CTriangleMeshABase::do_export(FILE *f)const
{
	
	tri_mesh_header header = {{{'M','I','A'},"",endian_magic}, n_vertices, n_triangles};
	strncpy(header.base.type,get_type_name(),sizeof(header.base.type));
	
	if (fwrite(&header,sizeof(header),1,f) != 1)
		return false;
	
	if (fwrite(triangles,sizeof(triangle_ui_t),n_triangles,f) != n_triangles)
		return false;

	if (write_vertices(f)) {
		return true; 
	}
	return false; 
}

template <class vertex_type>
void CTriangleMeshABase::calc_triangle_mid_points(vertex_type *vertex)
{
	TCamera c;
	c.gl_use_camera();
	
	triangle_ui_t *t  = triangles; 
	for (size_t i = 0; i < n_triangles; i++,++t) {
		triangle_mid_points[i] = (vertex[t->a].vertex + 
					  vertex[t->b].vertex + 
					  vertex[t->c].vertex) * 0.33333333f;
	}
	sort_triangles(triangles, tri_mirror,c);
	calc_center(vertex);
}

template <class vertex_type>
void CTriangleMeshABase::calc_center(vertex_type *vertex)
{
	float vol = 0.0; 
	
	triangle_ui_t *t  = triangles; 
	for (size_t i = 0; i < n_triangles; i++,++t) {
		
		C3DFVector normal = (vertex[t->a].vertex - vertex[t->b].vertex) ^
			(vertex[t->c].vertex - vertex[t->b].vertex);

		float n = normal.norm();
		m_center += triangle_mid_points[i] * n;
		vol += n; 
	}
	m_center /= vol; 
}

const char* CTriangleMeshABase::get_classname() const 
{
	return TRIANGLEMESHABASE_CLASSNAME;
}

void CTriangleMeshABase::get_classname_list(list<string> *classlist) const
{
	if (classlist) {
		classlist->push_front(TRIANGLEMESHABASE_CLASSNAME);
		TDrawable::get_classname_list(classlist);
	}
}

const char *CTriangleMeshA::get_type_name()const
{
	return TRIMESH_TYPENAME;
} 	

const char* CTriangleMeshA::get_classname() const 
{
	return TRIANGLEMESH_CLASSNAME;
}

void CTriangleMeshA::get_classname_list(list<string> *classlist) const
{
	if (classlist) {
		classlist->push_front(TRIANGLEMESH_CLASSNAME);
		CTriangleMeshABase::get_classname_list(classlist);
	}
}

void CTriangleMeshABase::povray_output_description(FILE *f)const
{
	// a mesh and a smooth mesh use color1 for shading
	fprintf(f,"object {\n  %s\n",get_name().c_str());
	fprintf(f,"  pigment { color %s_color1 }\n",get_name().c_str());
	fprintf(f,"  finish { ambient 0.2 diffuse 0.8 phong 0.7  phong_size 25}\n}\n");
}


CTriangleMeshA::CTriangleMeshA(const string& name,int _n_triangles, triangle_ui_t *t,
			     int _n_vertices, vertex_t *v):
	CTriangleMeshABase(name,_n_triangles,t,_n_vertices),
	vertices(v)
{
}

bool CTriangleMeshA::write_vertices(FILE *f)const
{
	return fwrite(vertices, sizeof(vertex_t), n_vertices, f) == n_vertices;
}
/*
CTriangleMeshA::CTriangleMeshA(const string& name,const CTriangleMeshA& org) :
	CTriangleMeshABase(name,org)
{
	vertices = new vertex_t[n_vertices];
	memcpy(vertices,org.vertices,sizeof(vertex_t) * n_vertices);
}
*/

void CTriangleMeshA::gl_attach()
{
	calc_triangle_mid_points(vertices);
}


CTriangleMeshA::~CTriangleMeshA()
{
	delete[] vertices;
}

void CTriangleMeshA::do_gl_draw(const TCamera& c) const
{
	cvdebug() << "CTriangleMeshA draw\n"; 
	glColor4fv(&get_color1().x);
	glInterleavedArrays(GL_V3F,0, vertices);
	CTriangleMeshABase::do_gl_draw(c); 
}

void CTriangleMeshA::povray_output_object(FILE *f)const
{
	fprintf(f,"#declare %s=mesh {\n",get_name().c_str());
	for (size_t i = 0; i < n_triangles; i++) {
		const C3DFVector& v1 = vertices[triangles[i].a].vertex;
		const C3DFVector& v2 = vertices[triangles[i].b].vertex;
		const C3DFVector& v3 = vertices[triangles[i].c].vertex;
		fprintf(f,"triangle { < %f, %f, %f >, < %f, %f, %f >, < %f, %f, %f >}\n",
			v1.x,v1.y,v1.z, v2.x,v2.y,v2.z, v3.x,v3.y,v3.z);
	}
	fprintf(f,"}\n");
}


TVertexList *CTriangleMeshA::get_vertex_list(int stride)const
{
	TVertexList *result = new TVertexList; 
	vertex_t *v = vertices; 
	for (size_t i = 0; i < n_vertices; i += stride) {
		result->push_back(v[i].vertex);
	}
	return result; 
}

TSmoothTriangleMesh::TSmoothTriangleMesh(const string& name,int _n_triangles, triangle_ui_t *t,
					 int _n_vertices, normal_vertex_t *v):
	CTriangleMeshABase(name,_n_triangles,t,_n_vertices),
	vertices(v)
{
}
	
TSmoothTriangleMesh::TSmoothTriangleMesh(const string& name,int _n_triangles, triangle_ui_t *t,
					 int _n_vertices, vertex_t *v):
	CTriangleMeshABase(name,_n_triangles,t,_n_vertices)
{
	create_from_vertex_array(v);
}

const char *TSmoothTriangleMesh::get_type_name()const
{
	return SMOOTHTRIMESH_TYPENAME;
} 	


bool TSmoothTriangleMesh::write_vertices(FILE *f)const
{
	return fwrite(vertices, sizeof(normal_vertex_t), n_vertices, f) == n_vertices;
}

void TSmoothTriangleMesh::create_from_vertex_array(const vertex_t *in_v)
{
	vertices  = new normal_vertex_t[n_vertices];
	normal_vertex_t *v = vertices; 
	for (size_t i = 0; i < n_vertices; i++,++v,++in_v) {
		v->normal = C3DFVector();
		v->vertex = in_v->vertex;
	}
	recalc_normals();
}

TSmoothTriangleMesh::TSmoothTriangleMesh(const string& name,const CTriangleMeshA& mesh):
	CTriangleMeshABase(name,mesh)
{
	create_from_vertex_array(mesh.get_vertices());
	recalc_normals();	
	
}

TSmoothTriangleMesh::TSmoothTriangleMesh(const string& name, const TSmoothTriangleMesh& mesh,const C3DTransformation& _deform):
	CTriangleMeshABase(name,mesh)
{
	vertices = new normal_vertex_t[n_vertices];
	memcpy(vertices,mesh.vertices,n_vertices * sizeof(normal_vertex_t));
	deform(_deform);
	
}

TSmoothTriangleMesh::TSmoothTriangleMesh(const string& name, const CTriangleMeshA& mesh,const C3DTransformation& _deform):
	CTriangleMeshABase(name,mesh)
{

	create_from_vertex_array(mesh.get_vertices());
	deform(_deform);
	
}

TSmoothTriangleMesh::~TSmoothTriangleMesh()
{
	delete[] vertices; 
}

void TSmoothTriangleMesh::gl_attach()
{
	calc_triangle_mid_points(vertices);
}


TVertexList *TSmoothTriangleMesh::get_vertex_list(int stride)const
{
	TVertexList *result = new TVertexList; 
	normal_vertex_t *v = vertices; 
	for (size_t i = 0; i < n_vertices; i += stride) {
		result->push_back(v[i].vertex);
	}
	return result; 
}


template <class triangle_t>
void do_recalc_normals(triangle_t *t, normal_vertex_t *v,int n_triangles)
{
	// weight 
	for (int i = 0; i < n_triangles; i++, t++) {
		C3DFVector e1 = v[t->a].vertex - v[t->b].vertex; 
		C3DFVector e2 = v[t->c].vertex - v[t->b].vertex; 
		C3DFVector e3 = v[t->c].vertex - v[t->a].vertex; 
		C3DFVector help_normal = e2 ^ e1; 
		if (help_normal.norm2() == 0) {
			cerr << "!";
			continue;
		}
		float weight1 = acos(dot(e1,e2) / (e1.norm() * e2.norm())); 
		float weight2 = acos(dot(e3,e2) / (e3.norm() * e2.norm()));
		v[t->b].normal += weight1 * help_normal; 
		v[t->c].normal += weight2 * help_normal; 
			
		float weight3 = M_PI - weight1  - weight2; 
		
		v[t->a].normal += weight3 * help_normal;
	}
}

void TSmoothTriangleMesh::recalc_normals()
{
	// normals should be clean here
	cerr << get_name()<< ":rebuilding normals" << endl; 

	do_recalc_normals(triangles,vertices,n_triangles); 
	
	// normalize normals
	normal_vertex_t *v = vertices; 
	for (size_t i = 0; i < n_vertices; i++,v++) {
		float norm = v->normal.norm();
		if ( norm > 0)
			v->normal /= norm;
		else 
			cerr << "Warning: vertex " << i << " has zero normal" << endl;
	}
}

void TSmoothTriangleMesh::deform(const C3DTransformation& shift)
{
	normal_vertex_t *nv = vertices; 
	int i = n_vertices; 
	while (i--) {
		nv->vertex += shift.apply(nv->vertex);
		nv->normal = C3DFVector();
		nv++; 
	}
	recalc_normals();
}

void TSmoothTriangleMesh::do_gl_draw(const TCamera& c) const
{
	cvdebug() << "MESH: draw SmoothTriangleMesh with color"<< get_color1() <<"\n"; 
	glColor4fv(&get_color1().x);
	glInterleavedArrays(GL_N3F_V3F,0, vertices);
	CTriangleMeshABase::do_gl_draw(c); 
}

void TSmoothTriangleMesh::povray_output_object(FILE *f)const
{
	fprintf(f,"#declare %s=mesh {\n",get_name().c_str());
	for (size_t i = 0; i < n_triangles; i++) {
		const C3DFVector& v1 = vertices[triangles[i].a].vertex;
		const C3DFVector& n1 = vertices[triangles[i].a].normal;
		const C3DFVector& v2 = vertices[triangles[i].b].vertex;
		const C3DFVector& n2 = vertices[triangles[i].b].normal;
		const C3DFVector& v3 = vertices[triangles[i].c].vertex;
		const C3DFVector& n3 = vertices[triangles[i].c].normal;
		fprintf(f,"smooth_triangle { < %f, %f, %f >, < %f, %f, %f >, ",
			v1.x,v1.y,v1.z, n1.x,n1.y,n1.z);
		fprintf(f,"< %f, %f, %f >, < %f, %f, %f >, ",
			v2.x,v2.y,v2.z, n2.x,n2.y,n2.z);
		fprintf(f,"< %f, %f, %f >, < %f, %f, %f >  }\n",
			v3.x,v3.y,v3.z, n3.x,n3.y,n3.z);
	}
	fprintf(f,"}\n");
}

const char* TSmoothTriangleMesh::get_classname() const 
{
	return SMOOTHTRIANGLEMESH_CLASSNAME;
}

void TSmoothTriangleMesh::get_classname_list(list<string> *classlist) const
{
	if (classlist) {
		classlist->push_front(SMOOTHTRIANGLEMESH_CLASSNAME);
		CTriangleMeshABase::get_classname_list(classlist);
	}
}

const char* TColorSmoothMesh::get_classname() const 
{
	return COLORSMOOTHMESH_CLASSNAME;
}

void TColorSmoothMesh::get_classname_list(list<string> *classlist) const
{
	if (classlist) {
		classlist->push_front(COLORSMOOTHMESH_CLASSNAME);
		CTriangleMeshABase::get_classname_list(classlist);
	}
}



TColorSmoothMesh::TColorSmoothMesh(const string& name,int _n_triangles, triangle_ui_t *t,
				   int _n_vertices, color_normal_vertex_t *v):
	CTriangleMeshABase(name,_n_triangles,t,_n_vertices),
	vertices(v),
	scale_type(st_standart),
	do_draw_decoration(true)
{
	
}

TColorSmoothMesh::TColorSmoothMesh(const string& name, int _n_triangles, triangle_ui_t *t,
				   int _n_vertices, color_normal_vertex_t *v, float *scale):
	CTriangleMeshABase(name, _n_triangles, t,_n_vertices),
	vertices(v),
	vertex_deform_scale(scale),
	max_shrink(0), 
	max_grow(0),
 	max_grow_log(0),
	max_shrink_log(0),
	scale_type(st_standart),
	do_draw_decoration(true)
{
	assert(scale);

	
	for (size_t i = 0; i < n_vertices; i++) {
		float shift = vertex_deform_scale[i];
		if (shift  < 0) {
			shift = -shift;
			if (shift > max_shrink) max_shrink = shift;
			shift = log(shift + 1.0);
			if (shift > max_shrink_log) max_shrink_log = shift;
		}else{
			if (shift > max_grow) max_grow = shift;
			shift = log(shift + 1.0);
			if (shift > max_grow_log) max_grow_log = shift;
		}
	}
	
	if (max_shrink != 0 ) {
		inv_max_shrink = 1.0f / max_shrink;
		inv_max_shrink_log = 1.0f / max_shrink_log;
	}
	if (max_grow != 0 ) {
		inv_max_grow_log = 1.0f / max_grow_log;
		inv_max_grow = 1.0f / max_grow;
	}

}



bool TColorSmoothMesh::write_vertices(FILE *f)const
{
	if (fwrite(vertices, sizeof(color_normal_vertex_t), n_vertices, f) == n_vertices) {
		return (fwrite(vertex_deform_scale, sizeof(float),n_vertices, f) == n_vertices);
	}
	return false; 
}

const char *TColorSmoothMesh::get_type_name()const
{
	return COLORSMOOTHTRIMESH_TYPENAME;
}

void TColorSmoothMesh::gl_attach()
{
	calc_triangle_mid_points(vertices);
	apply_color_change();

}

void TColorSmoothMesh::apply_color_change()
{
	ms = 2.0; 
	ims = 0.5; 

	color_normal_vertex_t *cnv = vertices;
	switch (scale_type) {
	case st_log:
		for (size_t i = 0; i < n_vertices; i++,cnv++) {
			float scale = vertex_deform_scale[i];
			if (scale < 0) { // if max_shrink == 0 then never true; 
				scale = log( 1.0f - scale);
				cnv->color = ((max_shrink_log - scale ) * get_color1() + 
					      scale * get_color3()) * inv_max_shrink_log; 
			}else if (scale > 0){
				scale = log(scale + 1.0f);
				cnv->color = ((max_grow_log - scale) * get_color1() + 
					      scale * get_color2()) * inv_max_grow_log; 
			}else{
				cnv->color = get_color1();
			}
		}
		break; 
	case st_divide:
		for (size_t i = 0; i < n_vertices; i++,cnv++) {
			float scale = vertex_deform_scale[i]; 
			if (scale < 0) { // if max_shrink == 0 then never true; 
				cnv->color = get_color3(); 
			}else if (scale > 0){
				cnv->color = get_color2(); 
			}else{
				cnv->color = get_color1();
			}
		}
		break; 		
	case st_standart:
		for (size_t i = 0; i < n_vertices; i++,cnv++) {
			float scale = vertex_deform_scale[i]; 
			if (scale < 0) { // if max_shrink == 0 then never true; 
				cnv->color = ((scale + max_shrink) * get_color1() - 
					      scale * get_color3()) * inv_max_shrink; 
			}else if (scale > 0){
				cnv->color = ((max_grow - scale) * get_color1() + 
					      scale * get_color2()) * inv_max_grow; 
			}else{
				cnv->color = get_color1();
			}
		}
		break; 
	case st_scale_32mm:
		ms *= 2.0;
		ims *= 0.5; 
	case st_scale_16mm:		
		ms *= 2.0;
		ims *= 0.5; 
	case st_scale_8mm:		
		ms *= 2.0;
		ims *= 0.5; 
	case st_scale_4mm:		
		ms *= 2.0;
		ims *= 0.5; 
	case st_scale_2mm:
		for (size_t i = 0; i < n_vertices; i++,cnv++) {
			float scale = vertex_deform_scale[i]; 
			if (scale < 0) { // if max_shrink == 0 then never true; 
				if (scale < ms)
					cnv->color = ((scale + ms) * get_color1() - 
						      scale * get_color3()) * ims; 
				else
					cnv->color = get_color3(); 
			}else if (scale > 0){
				if (scale < ms)
					cnv->color = ((ms - scale) * get_color1() + 
						      scale * get_color2()) * ims; 
				else
					cnv->color = get_color2(); 
			}else{
				cnv->color = get_color1();
			}
		}
		break; 
	default:
		std::cerr << "Warning: unknown scale type" << std::endl; 
	}
}

TVertexList *TColorSmoothMesh::get_vertex_list(int stride)const
{
	TVertexList *result = new TVertexList; 
	color_normal_vertex_t *v = vertices; 
	for (size_t i = 0; i < n_vertices; i += stride) {
		result->push_back(v[i].vertex);
	}
	return result; 
}


unique_ptr<TNormalVertexList> TColorSmoothMesh::get_vertex_normal_list(int stride)const
{
	unique_ptr<TNormalVertexList> result(new TNormalVertexList);
	color_normal_vertex_t *v = vertices; 
	for (size_t i = 0; i < n_vertices; i += stride) {
		normal_vertex_t nv; 
		nv.vertex = v[i].vertex; 
		nv.normal = v[i].normal;
		result->push_back(nv);
	}
	return result; 
}

TColorSmoothMesh::~TColorSmoothMesh()
{
	delete[] vertex_deform_scale; 
	delete[] vertices; 
}

void TColorSmoothMesh::do_gl_draw(const TCamera& c) const
{
	glEnable(GL_COLOR_MATERIAL);
	glInterleavedArrays(GL_C4F_N3F_V3F,0, vertices);
	CTriangleMeshABase::do_gl_draw(c); 
	glDisable(GL_COLOR_MATERIAL);
}

void TColorSmoothMesh::draw_decoration(const C2DBounds& viewport,const TGLText& writer)
{
	if (! (do_draw_decoration && is_selected()) ) 
		return;  
		
	int y = viewport.y; 
	
	glColor4f(0.0f,.5f,0.0f,0.3f);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	glBegin(GL_QUADS);
	glVertex2f(5.0f,y - 10.0f);
	glVertex2f(90.0f,y - 10.0f);
	glVertex2f(90.0f,y - 110.0f);
	glVertex2f(5.0f,y - 110.0f);
	glEnd();
	glDisable(GL_BLEND);
	
	
	glBegin(GL_QUAD_STRIP);
	glColor3fv(&get_color2().x);
	glVertex2f(10.0f,y - 20.0f);
 	glVertex2f(20.0f,y - 20.0f);
	glColor3fv(&get_color1().x);
	glVertex2f(10.0f,y - 60.0f);
	glVertex2f(20.0f,y - 60.0f);
	glColor3fv(&get_color3().x);
	glVertex2f(10.0f,y - 100.0f);
	glVertex2f(20.0f,y - 100.0f);
	glEnd();
	
	char value[20]; 
	
	switch (scale_type) {
	case st_log:
		snprintf(value,20," e^%3.1f",max_grow_log);
		glColor3fv(&get_color2().x);
		writer.write(25, int(y - 34), value);
		
		glColor3fv(&get_color1().x);
		writer.write(25, int(y - 67), "  0.0");
		
		snprintf(value,20,"-e^%3.1f",max_shrink_log);
		glColor3fv(&get_color3().x);
		writer.write(25 , int(y - 100), value);
		break; 
	case st_standart:
		snprintf(value,20,"%5.1f",get_max_grow());
		glColor3fv(&get_color2().x);
		writer.write(25, int(y - 34), value);
		
		glColor3fv(&get_color1().x);
		writer.write(25, int(y - 67), "  0.0");
		
		snprintf(value,20,"%5.1f",-get_max_shrink());
		glColor3fv(&get_color3().x);
		writer.write(25 , int(y - 100), value);
		break; 
	default:
		if (ms > get_max_grow())
			snprintf(value,20,"%5.1f",ms);
		else
			snprintf(value,20,"%5.1f+",ms);
		
		glColor3fv(&get_color2().x);
		writer.write(25, int(y - 34), value);
		
		glColor3fv(&get_color1().x);
		writer.write(25, int(y - 67), "  0.0");
		
		if (ms > get_max_shrink())
			snprintf(value,20,"%5.1f",-ms);
		else
			snprintf(value,20,"%5.1f-",-ms);
		glColor3fv(&get_color3().x);
		writer.write(25 , int(y - 100), value);

		
	}

}

void TColorSmoothMesh::toggle_scale_type()
{
	scale_type++;
	if (scale_type == st_last) {
		scale_type = st_standart; 
	}
	apply_color_change();
}

void TColorSmoothMesh::set_transparency(float alpha)
{
	TDrawable::set_transparency(alpha);
	apply_color_change();
}

bool TColorSmoothMesh::do_handle_key_event(int key)
{
	switch (key) {
	case 's':toggle_scale_type(); 
		break;
	case 'd':do_draw_decoration = !do_draw_decoration; 
		break; 
	case 't':
	case 'T':CTriangleMeshABase::do_handle_key_event(key);
		apply_color_change();
		break; 
	default:
		return CTriangleMeshABase::do_handle_key_event(key);
	}
	return true; 	
}

void TColorSmoothMesh::povray_write_color(FILE *f,float scale)const
{
	if (scale < 0) { // if max_shrink == 0 then never true; 
		fprintf(f,"%f * %s_color1 + %f * %s_color3, ",(scale + max_shrink) * inv_max_shrink,
			get_name().c_str(), - scale * inv_max_shrink,get_name().c_str());
	}else if (scale > 0){
		fprintf(f,"%f * %s_color1 + %f * %s_color2, ",(max_grow - scale) * inv_max_grow,
			get_name().c_str(), scale * inv_max_grow, get_name().c_str());
	}else{
		fprintf(f,"%s_color1",get_name().c_str());
	}
}

void TColorSmoothMesh::povray_output_object(FILE *f)const
{
	fprintf(f,"#include \"trimap.mcr\"\n");
	fprintf(f,"#declare %s=mesh {\n",get_name().c_str() );

	for (size_t i = 0; i < n_triangles; i++) {
		const C3DFVector& v1 = vertices[triangles[i].a].vertex;
		const C3DFVector& n1 = vertices[triangles[i].a].normal;
		const C3DFVector& v2 = vertices[triangles[i].b].vertex;
		const C3DFVector& n2 = vertices[triangles[i].b].normal;
		const C3DFVector& v3 = vertices[triangles[i].c].vertex;
		const C3DFVector& n3 = vertices[triangles[i].c].normal;
		fprintf(f,"  cv_smooth_triangle { < %f, %f, %f >, < %f, %f, %f >, ",
			v1.x,v1.y,v1.z, n1.x,n1.y,n1.z);
		povray_write_color(f,vertex_deform_scale[triangles[i].a]);
		
		fprintf(f,"< %f, %f, %f >, < %f, %f, %f >, ",
			v2.x,v2.y,v2.z, n2.x,n2.y,n2.z);
		povray_write_color(f,vertex_deform_scale[triangles[i].b]);
		
		fprintf(f,"< %f, %f, %f >, < %f, %f, %f >  }\n",
			v3.x,v3.y,v3.z, n3.x,n3.y,n3.z);
		povray_write_color(f,vertex_deform_scale[triangles[i].c]);
	}
	fprintf(f,"}\n");
}

void TColorSmoothMesh::povray_output_description(FILE *f)const
{
        // The colored mesh only needs the lightening parameters
        fprintf(f,"object {\n  %s\n \n",get_name().c_str());
        fprintf(f,"  finish { ambient 0.2 diffuse 0.8 phong 0.7  phong_size 25}\n}\n");
}


