/****************************************************************************
* MeshLab                                                           o o     *
* A versatile mesh processing toolbox                             o     o   *
*                                                                _   O  _   *
* Copyright(C) 2005                                                \/)\/    *
* Visual Computing Lab                                            /\/|      *
* ISTI - Italian National Research Council                           |      *
*                                                                    \      *
* All rights reserved.                                                      *
*                                                                           *
* 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 (http://www.gnu.org/licenses/gpl.txt)          *
* for more details.                                                         *
*                                                                           *
****************************************************************************/

#include "decorate_base.h"
#include <wrap/gl/addons.h>
#include <vcg/complex/algorithms/stat.h>
#include <vcg/complex/algorithms/bitquad_support.h>
#include <common/GLExtensionsManager.h>
#include <meshlab/glarea.h>
#include <wrap/qt/checkGLError.h>
#include <wrap/qt/gl_label.h>
#include <QGLShader>
#include <meshlab/glarea_setting.h>
#include <wrap/gl/gl_type_name.h>
using namespace vcg;
using namespace std;


QString DecorateBasePlugin::decorationInfo(ActionIDType filter) const
{
	switch(filter)
	{
	case DP_SHOW_AXIS:              return tr("Draw XYZ axes in world coordinates");
	case DP_SHOW_BOX_CORNERS:       return tr("Draw object's bounding box corners");
	case DP_SHOW_NORMALS:           return tr("Draw per vertex/face normals");
	case DP_SHOW_CURVATURE:         return tr("Draw per vertex/face principal curvature directions");
	case DP_SHOW_LABEL:             return tr("Draw on all the vertex/edge/face a label with their index<br> Useful for debugging<br>(WARNING: do not use it on large meshes)");
	case DP_SHOW_VERT_QUALITY_HISTOGRAM: return tr("Draw a (colored) Histogram of the per vertex quality");
	case DP_SHOW_FACE_QUALITY_HISTOGRAM: return tr("Draw a (colored) Histogram of the per face quality");
	case DP_SHOW_QUALITY_CONTOUR:   return tr("Draw quality contours, e.g. the isolines of the quality field defined over the surface ");
	case DP_SHOW_CAMERA:            return tr("Draw the position of the mesh camera and raster cameras");
	case DP_SHOW_TEXPARAM:          return tr("Draw an overlaid flattened version of the current mesh that show the current parametrization");
	case DP_SHOW_SELECTED_MESH:     return tr("Highlight the current mesh");
	}
	assert(0);
	return QString();
}

QString DecorateBasePlugin::pluginName() const
{
	return "DecorateBase";
}

QString DecorateBasePlugin::decorationName(ActionIDType filter) const
{
	switch(filter)
	{
	case DP_SHOW_NORMALS:                return QString("Show Normal");
	case DP_SHOW_CURVATURE:              return QString("Show Curvature");
	case DP_SHOW_BOX_CORNERS:            return QString("Show Box Corners");
	case DP_SHOW_AXIS:                   return QString("Show Axis");
	case DP_SHOW_LABEL:                  return QString("Show Labels");
	case DP_SHOW_CAMERA:                 return QString("Show Camera");
	case DP_SHOW_TEXPARAM:               return QString("Show UV Tex Param");
	case DP_SHOW_VERT_QUALITY_HISTOGRAM: return QString("Show Vertex Quality Histogram");
	case DP_SHOW_FACE_QUALITY_HISTOGRAM: return QString("Show Face Quality Histogram");
	case DP_SHOW_QUALITY_CONTOUR:        return QString("Show Quality Contour");
	case DP_SHOW_SELECTED_MESH:          return QString("Show Current Mesh");
	default: assert(0);
	}
	return QString("error!");
}

void DecorateBasePlugin::decorateDoc(const QAction* a, MeshDocument &md, const RichParameterList *rm, GLArea *gla, QPainter *painter, GLLogStream &/*_log*/)
{
	QFont qf;
	
	switch (ID(a))
	{
	
	case DP_SHOW_CAMERA:
	{
		bool showCameraDetails = rm->getBool(ShowCameraDetails());
		
		// draw all visible mesh cameras
		if(rm->getBool(ShowMeshCameras()))
		{
			for(const MeshModel& meshm : md.meshIterator())
			{
				if (&meshm != md.mm() || (!showCameraDetails))   // non-selected meshes, only draw
				{
					if (meshm.isVisible()) DrawCamera(&meshm, meshm.cm.shot, Color4b::DarkRed, md.mm()->cm.Tr, rm, painter, qf);
				}
				else                          // selected mesh, draw & display data
				{
					DrawCamera(&meshm, meshm.cm.shot, Color4b::Magenta, md.mm()->cm.Tr, rm, painter, qf);
					DisplayCamera(meshm.label(), meshm.cm.shot, 1);
				}
			}
			
			if (md.meshNumber() == 0)
				this->realTimeLog("Show Mesh Camera", md.mm()->label(), "There are no Mesh Layers");
		}
		
		// draw all visible raster cameras
		if(rm->getBool(ShowRasterCameras())) {
			for(RasterModel& raster: md.rasterIterator()) {
				if(&raster != md.rm() || !showCameraDetails ) { // non-selected raster, only draw
					if(raster.isVisible()) DrawCamera(nullptr, raster.shot, Color4b::DarkBlue, md.mm()->cm.Tr, rm, painter,qf);
				}
				else {                         // selected raster, draw & display data
					DrawCamera(nullptr, raster.shot, Color4b::Cyan, md.mm()->cm.Tr, rm, painter, qf);
					DisplayCamera(raster.label(), raster.shot, 2);
				}
			}
			
			if (md.rasterNumber() == 0)
				this->realTimeLog("Show Raster Camera", md.mm()->label(), "There are no Rasters");
		}
	} break;
		
	case DP_SHOW_SELECTED_MESH:
	{
		if ((gla == NULL) || (gla->mvc() == NULL))
			return;
		
		glPushAttrib(GL_ENABLE_BIT|GL_VIEWPORT_BIT|	  GL_CURRENT_BIT |  GL_DEPTH_BUFFER_BIT);
		glDisable(GL_LIGHTING);
		glEnable(GL_BLEND);
		vcg::Color4b q = rm->getColor4b(selectedMeshBlendingColor());
		glBlendColor(q.X() / 255.0f, q.Y() / 255.0f, q.Z() / 255.0f, q.W() / 255.0f);
		glBlendFunc(GL_CONSTANT_COLOR, GL_ONE);
		glDepthRange (0.0, 0.9999);
		glDepthFunc(GL_LEQUAL);
		glPointSize(3);
		
		MLSceneGLSharedDataContext* sharedcont = gla->mvc()->sharedDataContext();
		if ((sharedcont != NULL) && (md.mm() != NULL))
		{
			sharedcont->draw(md.mm()->id(),gla->context());
		}
		glPopAttrib();
	} break;
		
	case DP_SHOW_AXIS:
	{
		CoordinateFrame(md.bbox().Diag()/2.0).Render(gla,painter);
	} break;
		
	} // end switch
}

void DecorateBasePlugin::decorateMesh(const QAction* a, MeshModel &m, const RichParameterList *rm, GLArea *gla, QPainter *painter, GLLogStream &_log)
{
	this->setLog(&_log);
	QFont qf;
	
	textColor = rm->getColor4b( GLAreaSetting::textColorParam());
	
	glPushMatrix();
	glMultMatrix(m.cm.Tr);
	switch (ID(a))
	{
	
	case DP_SHOW_CURVATURE:
	{
		// Note the standard way for adding extra per-mesh data using the per-mesh attributes.
		CMeshO::PerMeshAttributeHandle< vector<PointPC> > bvH = vcg::tri::Allocator<CMeshO>::GetPerMeshAttribute<vector<PointPC> >(m.cm,"CurvatureVector");
		DrawLineVector(bvH());
	} break;
		
	case DP_SHOW_NORMALS:
	{
		glPushAttrib(GL_ENABLE_BIT );
		Scalarm NormalLen=rm->getFloat(NormalLength());
		Scalarm NormalWid = rm->getFloat(NormalWidth());
		vcg::Color4b VertNormalColor = rm->getColor4b(NormalVertColor());
		vcg::Color4b FaceNormalColor = rm->getColor4b(NormalFaceColor());
		bool showselection = rm->getBool(NormalSelection());
		
		Scalarm LineLen = m.cm.bbox.Diag()*NormalLen;
		
		//query line width range
		GLfloat widthRange[2];
		widthRange[0] = 1.0f; widthRange[1] = 1.0f;
		glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, widthRange);
		
		//set line width according to the width range
		NormalWid = (NormalWid < widthRange[0]) ?  widthRange[0] :  NormalWid;
		NormalWid = (NormalWid > widthRange[1]) ?  widthRange[1] :  NormalWid;
		
		//store current linewidth and set new line width
		GLfloat lineWidthtmp[1];
		glGetFloatv(GL_LINE_WIDTH, lineWidthtmp);
		glLineWidth(NormalWid);
		
		glDisable(GL_LIGHTING);
		glDisable(GL_TEXTURE_2D);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glBegin(GL_LINES);
		if(rm->getBool(NormalVertFlag())) // vert Normals
		{
			glColor(VertNormalColor);
			for(CMeshO::VertexIterator vi=m.cm.vert.begin();vi!=m.cm.vert.end();++vi) if(!(*vi).IsD())
			{
				if ((!showselection) || (showselection && vi->IsS()))
				{
					glVertex((*vi).P());
					glVertex((*vi).P() + (*vi).N()*LineLen);
				}
			}
		}
		if(rm->getBool(NormalFaceFlag())) // face Normals
		{
			glColor(FaceNormalColor);
			for(CMeshO::FaceIterator fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD())
			{
				if ((!showselection) || (showselection && fi->IsS()))
				{
					Point3m b = Barycenter(*fi);
					glVertex(b);
					glVertex(b + (*fi).N()*LineLen);
				}
			}
		}
		glEnd();
		//restore previous line width
		glLineWidth(lineWidthtmp[0]);
		glPopAttrib();
	} break;
		
	case DP_SHOW_BOX_CORNERS:
	{
		bool untransformed = rm->getBool(this->BBAbsParam());
		DrawBBoxCorner(m, untransformed);
		
		Point3m bmin, bmax;
		bmin = m.cm.bbox.min;
		bmax = m.cm.bbox.max;
		
		this->realTimeLog("Bounding Box", m.label(), "<table>"
                "<tr><td>Min: </td><td width=70 align=right>%7.4f</td><td width=70 align=right> %7.4f</td><td width=70 align=right> %7.4f</td></tr>"
                "<tr><td>Max: </td><td width=70 align=right>%7.4f</td><td width=70 align=right> %7.4f</td><td width=70 align=right> %7.4f</td></tr>"
				"<tr><td>Size: </td><td width=70 align=right>%7.4f</td><td width=70 align=right> %7.4f</td><td width=70 align=right> %7.4f</td></tr>"
				"<tr><td>Center: </td><td width=70 align=right>%7.4f</td><td width=70 align=right> %7.4f</td><td width=70 align=right> %7.4f</td></tr>"
				"</table>""Warning: values do not consider transformation", bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2], 
				math::Abs(bmax[0] - bmin[0]), math::Abs(bmax[1] - bmin[1]), math::Abs(bmax[2] - bmin[2]), 
				(bmax[0] + bmin[0]) / 2.0, (bmax[1] + bmin[1]) / 2.0, (bmax[2] + bmin[2]) / 2.0);
	} break;
		
	case DP_SHOW_LABEL:
	{
		if(rm->getBool(LabelVertFlag())) DrawVertLabel(m,painter);
		if(rm->getBool(LabelEdgeFlag())) DrawEdgeLabel(m,painter);
		if(rm->getBool(LabelFaceFlag())) DrawFaceLabel(m,painter);
	} break;
		
	case DP_SHOW_TEXPARAM : this->DrawTexParam(m,gla,painter,rm,qf); break;
		
	case DP_SHOW_VERT_QUALITY_HISTOGRAM :
	{
		CMeshO::PerMeshAttributeHandle<CHist > qH;
		qH = vcg::tri::Allocator<CMeshO>::GetPerMeshAttribute<CHist>(m.cm,"VertexQualityHist");
		CHist &ch=qH();
		this->DrawColorHistogram(ch,gla, painter,rm,qf, true);
	} break;
		
	case DP_SHOW_FACE_QUALITY_HISTOGRAM :
	{
		CMeshO::PerMeshAttributeHandle<CHist > qH;
		qH = vcg::tri::Allocator<CMeshO>::GetPerMeshAttribute<CHist>(m.cm,"FaceQualityHist");
		CHist &ch=qH();
		this->DrawColorHistogram(ch,gla, painter,rm,qf, false);
	} break;
		
	case DP_SHOW_QUALITY_CONTOUR :
	{
		glPushAttrib(GL_ENABLE_BIT|GL_VIEWPORT_BIT|	  GL_CURRENT_BIT |  GL_DEPTH_BUFFER_BIT);
		glDisable(GL_LIGHTING);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glDepthRange (0.0, 0.9999);
		glDepthFunc(GL_LEQUAL);
		//      glColor4f(1.0f, 1.0f, 1.0f, 0.3f);
		QGLShaderProgram *glp=this->contourShaderProgramMap[&m];
		
		std::pair<float,float> mmqH = vcg::tri::Stat<CMeshO>::ComputePerVertexQualityMinMax(m.cm);
		this->realTimeLog("Quality Contour", m.label(),
						  "min Q %f -- max Q %f",mmqH.first,mmqH.second);
		
		float stripe_num = rm->getFloat(this->ShowContourFreq());
		float stripe_width = rm->getFloat(this->ShowContourWidth());
		float stripe_alpha = rm->getFloat(this->ShowContourAlpha());
		bool stripe_ramp = rm->getBool(this->ShowContourRamp());
		float colormap = rm->getEnum(this->ShowContourColorMap());
		glp->bind();
		glp->setUniformValue("quality_min",mmqH.first);
		glp->setUniformValue("quality_max",mmqH.second);
		glp->setUniformValue("stripe_num",stripe_num);
		glp->setUniformValue("stripe_width",stripe_width);
		glp->setUniformValue("stripe_alpha",stripe_alpha);
		glp->setUniformValue("stripe_ramp",stripe_ramp);
		glp->setUniformValue("colormap",colormap);
		
		
		int vert_quality = glp->attributeLocation("vert_quality");
		glBegin(GL_TRIANGLES);
		for(CMeshO::FaceIterator fi=m.cm.face.begin();fi!=m.cm.face.end();++fi)
		{
			glp->setAttributeValue(vert_quality,fi->V(0)->Q());
			glVertex(fi->V(0)->P());
			glp->setAttributeValue(vert_quality,fi->V(1)->Q());
			glVertex(fi->V(1)->P());
			glp->setAttributeValue(vert_quality,fi->V(2)->Q());
			glVertex(fi->V(2)->P());
		}
		glEnd();
		glp->release();
		glPopAttrib();
		
	} break;
		
	} // end switch;
	glPopMatrix();
}

void DecorateBasePlugin::DrawLineVector(std::vector<PointPC> &EV)
{
	glPushAttrib(GL_ENABLE_BIT|GL_VIEWPORT_BIT| GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT);
	glDisable(GL_LIGHTING);
	glDepthFunc(GL_LEQUAL);
	glEnable(GL_LINE_SMOOTH);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glLineWidth(1.f);
	glDepthRange (0.0, 0.999);
	if (EV.size() > 0)
	{
		glEnableClientState (GL_VERTEX_ARRAY);
		glEnableClientState (GL_COLOR_ARRAY);
		
		glVertexPointer(3,vcg::GL_TYPE_NM<Scalarm>::SCALAR(),sizeof(PointPC),&(EV.begin()[0].first));
		glColorPointer(4,GL_UNSIGNED_BYTE,sizeof(PointPC),&(EV.begin()[0].second));
		glDrawArrays(GL_LINES,0,GLsizei(EV.size()));
		glDisableClientState (GL_COLOR_ARRAY);
		glDisableClientState (GL_VERTEX_ARRAY);
	}
	glPopAttrib();
}

/**
Draw a line with labeled ticks.
\param a,b the two endpoints of the line (in 3D)
\param aVal,bVal the two values at the line endpoints.
\param tickScalarDistance the distance between labeled ticks ( 1/10 small ticks are added between them).

for example if you want the a quoted line between 0.363 and 1.567 tickScalarDistance == 0.1 it means that
you will have a line with labeled ticks at 0.4 0.5 etc.
*/



void DecorateBasePlugin::drawQuotedLine(const Point3d &a,const Point3d &b, float aVal, float bVal, float tickScalarDistance, QPainter *painter, QFont qf,float angle,bool rightAlign)
{
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_CULL_FACE);
	glDisable(GL_LIGHTING);
	glDisable(GL_TEXTURE_2D);
	glDisable(GL_LIGHT0);
	glDisable(GL_NORMALIZE);
	float labelMargin =tickScalarDistance /4.0;
	float firstTick;
	// fmod returns the floating-point remainder of numerator/denominator (with the sign of the dividend)
	// fmod ( 104.5 , 10) returns 4.5     --> aVal - fmod(aval/tick) = 100
	// fmod ( -104.5 , 10) returns -4.5
	// So it holds that
	
	if (aVal > 0 )
		firstTick = aVal - fmod(aVal,tickScalarDistance) + tickScalarDistance;
	else if(aVal ==0 )
		firstTick = tickScalarDistance;
	else //aVal < 0
		firstTick = aVal + fmod(fabs(aVal),tickScalarDistance);
	
	// now we are sure that aVal < firstTick
	// let also be sure that there is enough space
	if ( (firstTick-aVal) < (labelMargin) )
		firstTick +=tickScalarDistance;
	
	
	float tickDistTen=tickScalarDistance /10.0f;
	float firstTickTen;
	if(aVal > 0) firstTickTen = aVal - fmod(aVal,tickDistTen) + tickDistTen;
	else firstTickTen = aVal - fmod(aVal,tickDistTen);
	
	int neededZeros=0;
	
	Point3d Zero = a-((b-a)/(bVal-aVal))*aVal; // 3D Position of Zero.
	Point3d v(b-a);
	//v.Normalize();
	v = v*(1.0/(bVal-aVal));
	glLabel::Mode md(qf,textColor,angle,rightAlign);
	if(tickScalarDistance > 0)   // Draw lines only if the two endpoint are not coincident
	{
		neededZeros = ceil(max(0.0,-log10(double(tickScalarDistance))));
		glPointSize(3);
		float i;
		glBegin(GL_POINTS);
		for(i=firstTick;i<bVal;i+=tickScalarDistance)
			glVertex(Zero+v*i);
		glEnd();
		for(i=firstTick; (i+labelMargin)<bVal;i+=tickScalarDistance)
			glLabel::render(painter,Point3m::Construct(Zero+v*i),tr("%1   ").arg(i,4+neededZeros,'f',neededZeros),md);
		glPointSize(1);
		glBegin(GL_POINTS);
		for(i=firstTickTen;i<bVal;i+=tickDistTen)
			glVertex(Zero+v*i);
		glEnd();
	}
	
	// Draws bigger ticks at 0 and at max size
	glPointSize(6);
	
	glBegin(GL_POINTS);
	glVertex(a);
	glVertex(b);
	if(bVal*aVal<0) glVertex(Zero);
	glEnd();
	
	
	// bold font at beginning and at the end
	md.qFont.setBold(true);
	glLabel::render(painter,Point3f::Construct(a), tr("%1   ").arg(aVal,4+neededZeros,'f',neededZeros) ,md);
	glLabel::render(painter,Point3f::Construct(b), tr("%1   ").arg(bVal,4+neededZeros,'f',neededZeros) ,md);
	
	glPopAttrib();
}


float DecorateBasePlugin::niceRound2(float Val,float base)	{return powf(base,ceil(log10(Val)/log10(base)));}
float DecorateBasePlugin::niceRound(float val)	{return powf(10.f,ceil(log10(val)));}

void DecorateBasePlugin::DrawBBoxCorner(MeshModel &m, bool absBBoxFlag)
{
	glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_POINT_BIT | GL_CURRENT_BIT | GL_LIGHTING_BIT | GL_COLOR_BUFFER_BIT );
	glDisable(GL_LIGHTING);
	glDisable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_LINE_SMOOTH);
	glLineWidth(1.0);
	glColor(vcg::Color4b(vcg::Color4b::Cyan));
	Box3f b;
	b.Import(m.cm.bbox);
	if(absBBoxFlag) {
		glColor(vcg::Color4b(vcg::Color4b::Cyan));
	} else {
		glColor(vcg::Color4b(vcg::Color4b::Green));
	}
	Point3f mi=b.min;
	Point3f ma=b.max;
	Point3f d3=(b.max-b.min)/4.0;
	Point3f zz(0,0,0);
	
	glPushMatrix();
	if (absBBoxFlag)
		glMultMatrix(Inverse(m.cm.Tr));
	
	glBegin(GL_LINES);
	glVertex3f(mi[0],mi[1],mi[2]); glVertex3f(mi[0]+d3[0],mi[1]+zz[1],mi[2]+zz[2]);
	glVertex3f(mi[0],mi[1],mi[2]); glVertex3f(mi[0]+zz[0],mi[1]+d3[1],mi[2]+zz[2]);
	glVertex3f(mi[0],mi[1],mi[2]); glVertex3f(mi[0]+zz[0],mi[1]+zz[1],mi[2]+d3[2]);
	
	glVertex3f(ma[0],mi[1],mi[2]); glVertex3f(ma[0]-d3[0],mi[1]+zz[1],mi[2]+zz[2]);
	glVertex3f(ma[0],mi[1],mi[2]); glVertex3f(ma[0]+zz[0],mi[1]+d3[1],mi[2]+zz[2]);
	glVertex3f(ma[0],mi[1],mi[2]); glVertex3f(ma[0]+zz[0],mi[1]+zz[1],mi[2]+d3[2]);
	
	glVertex3f(mi[0],ma[1],mi[2]); glVertex3f(mi[0]+d3[0],ma[1]+zz[1],mi[2]+zz[2]);
	glVertex3f(mi[0],ma[1],mi[2]); glVertex3f(mi[0]+zz[0],ma[1]-d3[1],mi[2]+zz[2]);
	glVertex3f(mi[0],ma[1],mi[2]); glVertex3f(mi[0]+zz[0],ma[1]+zz[1],mi[2]+d3[2]);
	
	glVertex3f(ma[0],ma[1],mi[2]); glVertex3f(ma[0]-d3[0],ma[1]+zz[1],mi[2]+zz[2]);
	glVertex3f(ma[0],ma[1],mi[2]); glVertex3f(ma[0]+zz[0],ma[1]-d3[1],mi[2]+zz[2]);
	glVertex3f(ma[0],ma[1],mi[2]); glVertex3f(ma[0]+zz[0],ma[1]+zz[1],mi[2]+d3[2]);
	
	glVertex3f(mi[0],mi[1],ma[2]); glVertex3f(mi[0]+d3[0],mi[1]+zz[1],ma[2]+zz[2]);
	glVertex3f(mi[0],mi[1],ma[2]); glVertex3f(mi[0]+zz[0],mi[1]+d3[1],ma[2]+zz[2]);
	glVertex3f(mi[0],mi[1],ma[2]); glVertex3f(mi[0]+zz[0],mi[1]+zz[1],ma[2]-d3[2]);
	
	glVertex3f(ma[0],mi[1],ma[2]); glVertex3f(ma[0]-d3[0],mi[1]+zz[1],ma[2]+zz[2]);
	glVertex3f(ma[0],mi[1],ma[2]); glVertex3f(ma[0]+zz[0],mi[1]+d3[1],ma[2]+zz[2]);
	glVertex3f(ma[0],mi[1],ma[2]); glVertex3f(ma[0]+zz[0],mi[1]+zz[1],ma[2]-d3[2]);
	
	glVertex3f(mi[0],ma[1],ma[2]); glVertex3f(mi[0]+d3[0],ma[1]+zz[1],ma[2]+zz[2]);
	glVertex3f(mi[0],ma[1],ma[2]); glVertex3f(mi[0]+zz[0],ma[1]-d3[1],ma[2]+zz[2]);
	glVertex3f(mi[0],ma[1],ma[2]); glVertex3f(mi[0]+zz[0],ma[1]+zz[1],ma[2]-d3[2]);
	
	glVertex3f(ma[0],ma[1],ma[2]); glVertex3f(ma[0]-d3[0],ma[1]+zz[1],ma[2]+zz[2]);
	glVertex3f(ma[0],ma[1],ma[2]); glVertex3f(ma[0]+zz[0],ma[1]-d3[1],ma[2]+zz[2]);
	glVertex3f(ma[0],ma[1],ma[2]); glVertex3f(ma[0]+zz[0],ma[1]+zz[1],ma[2]-d3[2]);
	glEnd();
	
	glPopMatrix();
	glPopAttrib();
}

int DecorateBasePlugin::getDecorationClass(const QAction *action) const
{
	switch(ID(action))
	{
	case DP_SHOW_NORMALS :
	case DP_SHOW_CURVATURE :
	case DP_SHOW_VERT_QUALITY_HISTOGRAM :
	case DP_SHOW_FACE_QUALITY_HISTOGRAM :
	case DP_SHOW_QUALITY_CONTOUR :
	case DP_SHOW_BOX_CORNERS :
	case DP_SHOW_LABEL :
	case DP_SHOW_TEXPARAM : return DecorateBasePlugin::PerMesh;
	case DP_SHOW_AXIS : return DecorateBasePlugin::PerDocument;
	case DP_SHOW_CAMERA : return DecorateBasePlugin::PerDocument;
	case DP_SHOW_SELECTED_MESH : return DecorateBasePlugin::PerDocument;
	}
	assert (0);
	return 0;
}

bool DecorateBasePlugin::isDecorationApplicable(const QAction* action, const MeshModel& m, QString &ErrorMessage) const
{
	if( ID(action) == DP_SHOW_LABEL )
	{
		if(m.cm.vn <1000 && m.cm.fn<2000) 
			return true;
		else 
		{
			ErrorMessage=QString("<br>CANNOT START DECORATOR: the layer contains too many faces and vertices.<br>Printing on the screen thousand of numbers would be useless and VERY SLOW");
			return false;
		}
	}
	if (ID(action) == DP_SHOW_QUALITY_CONTOUR)
	{
		if (m.hasDataMask(MeshModel::MM_FACEQUALITY) || m.hasDataMask(MeshModel::MM_VERTQUALITY))
			return true;
		else
		{
			ErrorMessage = QString("<br>CANNOT START DECORATOR: the layer contains neither vertex nor face quality");
			return false;
		}
	}
	if (ID(action) == DP_SHOW_VERT_QUALITY_HISTOGRAM)
	{
		if (m.hasDataMask(MeshModel::MM_VERTQUALITY))
			return true;
		else
		{
			ErrorMessage = QString("<br>CANNOT START DECORATOR: the layer does not contain vertex quality");
			return false;
		}
	}
	if (ID(action) == DP_SHOW_FACE_QUALITY_HISTOGRAM)
	{
		if (m.hasDataMask(MeshModel::MM_FACEQUALITY))
			return true;
		else
		{
			ErrorMessage = QString("<br>CANNOT START DECORATOR: the layer does not contain face quality");
			return false;
		}
	}
	
	if (ID(action) == DP_SHOW_CURVATURE)
	{
		if (m.hasDataMask(MeshModel::MM_VERTCURVDIR) || m.hasDataMask(MeshModel::MM_FACECURVDIR))
			return true;
		else
		{
			ErrorMessage = QString("<br>CANNOT START DECORATOR: the layer contains neither vertex nor face curvature attribute");
			return false;
		}
	}
	
	return true;
}

bool DecorateBasePlugin::startDecorate(const QAction * action, MeshDocument &, const RichParameterList *, GLArea *)
{
	switch(ID(action))
	{
	case DP_SHOW_AXIS :
	case DP_SHOW_CAMERA :
		return true;
	case DP_SHOW_SELECTED_MESH :
	{
		return GLExtensionsManager::initializeGLextensions_notThrowing();
	}
		
	}
	return true;
}


void DecorateBasePlugin::endDecorate(const QAction * action, MeshModel &m, const RichParameterList *, GLArea *)
{
	switch(ID(action))
	{
	case DP_SHOW_QUALITY_CONTOUR :
		if(this->contourShaderProgramMap[&m]!=0)
		{
			delete this->contourShaderProgramMap[&m];
			this->contourShaderProgramMap[&m]=0;
		}
		break;
	default: break;
	}
}

bool DecorateBasePlugin::startDecorate(const QAction * action, MeshModel &m, const RichParameterList *rm, GLArea *gla)
{
	switch(ID(action))
	{
	case DP_SHOW_CURVATURE :
	{
		CMeshO::PerMeshAttributeHandle< vector<PointPC> > cvH = vcg::tri::Allocator<CMeshO>::GetPerMeshAttribute< vector<PointPC> >(m.cm,"CurvatureVector");
		vector<PointPC> *CVp = &cvH();
		CVp->clear();
		float NormalLen=rm->getFloat(CurvatureLength());
		bool curvScale =rm->getBool(CurvatureScaling());
		float LineLen = m.cm.bbox.Diag()*NormalLen;
		float scale1,scale2;
		if (rm->getBool(this->ShowPerVertexCurvature()) && m.hasDataMask(MeshModel::MM_VERTCURVDIR))
		{
			if(curvScale) // get range of curvature values
			{
				auto minmaxPD = tri::Stat<CMeshO>::ComputePerVertexCurvatureDirMinMax(m.cm);
				scale1=minmaxPD.first.second;
				scale2=minmaxPD.second.second;
				for(CMeshO::VertexIterator vi=m.cm.vert.begin();vi!=m.cm.vert.end();++vi)
					if(!(*vi).IsD())
					{
						CVp->push_back(make_pair((*vi).P(), Color4b::Green));
						CVp->push_back(make_pair((*vi).P() +Point3m::Construct((*vi).PD1()*scale1*LineLen*0.25), Color4b::Green));
						CVp->push_back(make_pair((*vi).P(),  Color4b::Red));
						CVp->push_back(make_pair((*vi).P()+Point3m::Construct((*vi).PD2()*scale2*LineLen*0.25),  Color4b::Red));
					}
			}
			else
			{
			for(CMeshO::VertexIterator vi=m.cm.vert.begin();vi!=m.cm.vert.end();++vi)
				if(!(*vi).IsD())
				{
					CVp->push_back(make_pair((*vi).P(),
											 Color4b::Green));
					CVp->push_back(make_pair((*vi).P() +Point3m::Construct((*vi).PD1()/Norm((*vi).PD1())*LineLen*0.25),
											 Color4b::Green));
					CVp->push_back(make_pair((*vi).P(),
											 Color4b::Red));
					CVp->push_back(make_pair((*vi).P()+Point3m::Construct((*vi).PD2()/Norm((*vi).PD2())*LineLen*0.25),
											 Color4b::Red));
				}
			}
		}
		if (rm->getBool(this->ShowPerFaceCurvature()) && m.hasDataMask(MeshModel::MM_FACECURVDIR))
		{
			for(CMeshO::FaceIterator fi=m.cm.face.begin();fi!=m.cm.face.end();++fi)
				if(!(*fi).IsD())
				{
					Point3m bar =  Barycenter(*fi);
					CVp->push_back(make_pair(bar, Color4b::Green));
					CVp->push_back(make_pair(bar +(*fi).PD1()/Norm((*fi).PD1())*LineLen*0.25,
											 Color4b::Green));
					CVp->push_back(make_pair(bar, Color4b::Red));
					CVp->push_back(make_pair(bar +(*fi).PD2()/Norm((*fi).PD2())*LineLen*0.25,
											 Color4b::Red));
				}
		}
	} break;
		
	case DP_SHOW_VERT_QUALITY_HISTOGRAM :
	{
		if(!(tri::HasPerVertexColor(m.cm)) ) return false;
		CMeshO::PerMeshAttributeHandle<CHist > qH = vcg::tri::Allocator<CMeshO>::GetPerMeshAttribute<CHist>(m.cm,"VertexQualityHist");
		
		CHist *H = &qH();
		std::pair<float,float> minmax;
		minmax = tri::Stat<CMeshO>::ComputePerVertexQualityMinMax(m.cm);
		if(rm->getBool(perVertexHistFixedParam())) {
			minmax.first=rm->getFloat(perVertexHistFixedMinParam());
			minmax.second=rm->getFloat(perVertexHistFixedMaxParam());
		}
		
		H->SetRange( minmax.first, minmax.second, rm->getInt(perVertexHistBinNumParam()));
		if(rm->getBool(perVertexHistAreaParam()))
		{
			for(CMeshO::FaceIterator fi = m.cm.face.begin(); fi!= m.cm.face.end();++fi) if(!(*fi).IsD())
			{
				float area6=DoubleArea(*fi)/6.0f;
				for(int i=0;i<3;++i)
					H->Add((*fi).V(i)->Q(),(*fi).V(i)->C(),area6);
			}
		} else {
			for(CMeshO::VertexIterator vi = m.cm.vert.begin(); vi!= m.cm.vert.end();++vi) if(!(*vi).IsD())
			{
				H->Add((*vi).Q(),(*vi).C(),1.0f);
			}
		}
	}
	break;
	
	case DP_SHOW_FACE_QUALITY_HISTOGRAM :
	{
		if(!(tri::HasPerFaceColor(m.cm)) ) return false;
		CMeshO::PerMeshAttributeHandle<CHist > qH = vcg::tri::Allocator<CMeshO>::GetPerMeshAttribute<CHist>(m.cm,"FaceQualityHist");
		
		CHist *H = &qH();
		std::pair<float,float> minmax;
		minmax = tri::Stat<CMeshO>::ComputePerFaceQualityMinMax(m.cm);
		if(rm->getBool(perFaceHistFixedParam())) {
			minmax.first=rm->getFloat(perFaceHistFixedMinParam());
			minmax.second=rm->getFloat(perFaceHistFixedMaxParam());
		}
		
		H->SetRange( minmax.first, minmax.second, rm->getInt(perFaceHistBinNumParam()));
		
		if(rm->getBool(perFaceHistAreaParam())) {
			for(CMeshO::FaceIterator fi = m.cm.face.begin(); fi!= m.cm.face.end();++fi) if(!(*fi).IsD())
				H->Add((*fi).Q(),(*fi).C(),DoubleArea(*fi)*0.5f);
		} else {
			for(CMeshO::FaceIterator fi = m.cm.face.begin(); fi!= m.cm.face.end();++fi) if(!(*fi).IsD())
				H->Add((*fi).Q(),(*fi).C(),1.0f);
		}
	}
	break;
	
	case DP_SHOW_QUALITY_CONTOUR :
	{
		if(this->contourShaderProgramMap[&m] == 0)
		{
			bool ret=true;
			this->contourShaderProgramMap[&m] = new QGLShaderProgram(gla);
			QGLShaderProgram *gsp =  this->contourShaderProgramMap[&m];
			
			ret &= gsp->addShaderFromSourceFile(QGLShader::Vertex,":/decorate/contour.vert");
			//      qDebug("Compiled shader. Log is %s", qUtf8Printable(contourShaderProgram->log()));
			ret &= gsp->addShaderFromSourceFile(QGLShader::Fragment,":/decorate/contour.frag");
			//      qDebug("Compiled shader. Log is %s", qUtf8Printable(contourShaderProgram->log()));
			ret &= gsp->link();
			QString rs = gsp->log();
			//      qDebug("Linked shader. Log is %s", qUtf8Printable(contourShaderProgram->log()));
			if(!ret) return false;
		}
	} break;
		
	case DP_SHOW_CAMERA :
	{
		connect(gla,SIGNAL(transmitShot(QString,vcg::Shotf)),this,SLOT(setValue(QString,vcg::Shotf)));
		connect(this,SIGNAL(askViewerShot(QString)),gla,SLOT(sendViewerShot(QString)));
	} break;
	}
	return true;
}

void DecorateBasePlugin::DrawFaceLabel(MeshModel &m, QPainter *painter)
{
	glPushAttrib(GL_LIGHTING_BIT  | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT );
	glDepthFunc(GL_ALWAYS);
	glDisable(GL_LIGHTING);
	glColor3f(.4f,.4f,.4f);
	for(size_t i=0;i<m.cm.face.size();++i)
	{
		if(!m.cm.face[i].IsD())
		{
			Point3m bar=Barycenter(m.cm.face[i]);
			glLabel::render(painter, bar,tr("%1").arg(i),glLabel::Mode(textColor));
		}
	}
	glPopAttrib();
}

void DecorateBasePlugin::DrawEdgeLabel(MeshModel &m,QPainter *painter)
{
	glPushAttrib(GL_LIGHTING_BIT  | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT );
	glDepthFunc(GL_ALWAYS);
	glDisable(GL_LIGHTING);
	glColor3f(.4f,.4f,.4f);
	for(size_t i=0;i<m.cm.edge.size();++i)
	{
		if(!m.cm.edge[i].IsD())
		{
			Point3m bar=(m.cm.edge[i].V(0)->P()+m.cm.edge[i].V(0)->P())/2.0f;
			glLabel::render(painter, bar,tr("%1").arg(i),glLabel::Mode(textColor));
		}
	}
	glPopAttrib();
}


void DecorateBasePlugin::DrawVertLabel(MeshModel &m,QPainter *painter)
{
	glPushAttrib(GL_LIGHTING_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT );
	glDepthFunc(GL_ALWAYS);
	glDisable(GL_LIGHTING);
	glColor3f(.4f,.4f,.4f);
	for(size_t i=0;i<m.cm.vert.size();++i){
		if(!m.cm.vert[i].IsD())
			glLabel::render(painter, m.cm.vert[i].P(),tr("%1").arg(i),glLabel::Mode(textColor));
	}
	glPopAttrib();
}

void DecorateBasePlugin::setValue(QString /*name*/,Shotf newVal)
{
	curShot=newVal;
}


void DecorateBasePlugin::DisplayCamera(QString who, const Shotm &ls, int cameraSourceId)
{
	if(!ls.IsValid())
	{
		if(cameraSourceId == 1 )
			this->realTimeLog("Show Mesh Camera", who, "Current Mesh Has an invalid Camera");
		else if(cameraSourceId == 2 )
			this->realTimeLog("Show Raster Camera", who, "Current Raster Has an invalid Camera");
		else
			this->realTimeLog("Show Camera", who, "Current TrackBall Has an invalid Camera");
		return;
	}
	
	const char *typeBuf;
	if(ls.Intrinsics.cameraType == Camera<Scalarm>::PERSPECTIVE) typeBuf="Persp";
	if(ls.Intrinsics.cameraType == Camera<Scalarm>::ORTHO)       typeBuf="Ortho";
	
	Point3m vp = ls.GetViewPoint();
	Point3m ax0 = ls.Axis(0);
	Point3m ax1 = ls.Axis(1);
	Point3m ax2 = ls.Axis(2);
	float fov = ls.GetFovFromFocal();
	float focal = ls.Intrinsics.FocalMm;
	//  glLabel::render2D(painter,glLabel::TOP_LEFT,ln++, QString("ViewPoint %1 %2 %3").arg(vp[0]).arg(vp[1]).arg(vp[2]));
	//  glLabel::render2D(painter,glLabel::TOP_LEFT,ln++, QString("axis 0 - %1 %2 %3").arg(ax0[0]).arg(ax0[1]).arg(ax0[2]));
	//  glLabel::render2D(painter,glLabel::TOP_LEFT,ln++, QString("axis 1 - %1 %2 %3").arg(ax1[0]).arg(ax1[1]).arg(ax1[2]));
	//  glLabel::render2D(painter,glLabel::TOP_LEFT,ln++, QString("axis 2 - %1 %2 %3").arg(ax2[0]).arg(ax2[1]).arg(ax2[2]));
	//  glLabel::render2D(painter,glLabel::TOP_LEFT,ln++, QString("Fov %1 ( %2 x %3) ").arg(fov).arg(ls.Intrinsics.ViewportPx[0]).arg(ls.Intrinsics.ViewportPx[1]));
	//  glLabel::render2D(painter,glLabel::TOP_LEFT,ln++, QString("Focal Length %1 (pxsize %2 x %3) ").arg(focal).arg(ls.Intrinsics.PixelSizeMm[0]).arg(ls.Intrinsics.PixelSizeMm[1]));
	
	
	this->realTimeLog("Camera Info", who,
					  "<table>"
        "<tr><td>Viewpoint: </td><td width=70 align=right>%7.4f</td><td width=70 align=right> %7.4f</td><td width=70 align=right> %7.4f</td></tr>"
        "<tr><td>axis 0:    </td><td width=70 align=right>%7.4f</td><td width=70 align=right> %7.4f</td><td width=70 align=right> %7.4f</td></tr>"
        "<tr><td>axis 1:    </td><td width=70 align=right>%7.4f</td><td width=70 align=right> %7.4f</td><td width=70 align=right> %7.4f</td></tr>"
        "<tr><td>axis 2:    </td><td width=70 align=right>%7.4f</td><td width=70 align=right> %7.4f</td><td width=70 align=right> %7.4f</td></tr>"
        "</table><br>"
        "<table>"
        "<tr><td>FOV (%s): %7.4f     </td><td> Viewport (%i  x %i)</td></tr>"
        "<tr><td>Focal Length %7.4f  </td><td> PxSize (%.4f x %.4f)</td></tr>"
        "</table>",
					  vp[0],vp[1],vp[2],
			ax0[0],ax0[1],ax0[2],
			ax1[0],ax1[1],ax1[2],
			ax2[0],ax2[1],ax2[2],
			typeBuf, fov,  ls.Intrinsics.ViewportPx[0], ls.Intrinsics.ViewportPx[1],
			focal,ls.Intrinsics.PixelSizeMm[0],ls.Intrinsics.PixelSizeMm[1]);
}

void DecorateBasePlugin::DrawCamera(const MeshModel* m, const Shotm &ls, vcg::Color4b camcolor, Matrix44m &currtr, const RichParameterList *rm, QPainter * /*painter*/, QFont /*qf*/)
{
	if(!ls.IsValid())  // no drawing if camera not valid
		return;
	
	Point3m vp = ls.GetViewPoint();
	Point3m ax0 = ls.Axis(0);
	Point3m ax1 = ls.Axis(1);
	Point3m ax2 = ls.Axis(2);
	
	glPushAttrib(GL_LIGHTING_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT );
	glDepthFunc(GL_ALWAYS);
	glDisable(GL_LIGHTING);
	
	if (ls.Intrinsics.cameraType == Camera<Scalarm>::PERSPECTIVE)
	{
		// draw scale
		float drawscale = 1.0;
		if (rm->getEnum(CameraScaleParam()) == 1)  // fixed scale
		{
			drawscale = rm->getFloat(FixedScaleParam());
		}
		
		// arbitrary size to draw axis
		float len;
		len = ls.Intrinsics.FocalMm * drawscale;
		
		glPushMatrix();
		if (rm->getBool(ApplyMeshTr())) {
			if (m != NULL) {
				glMultMatrix(m->cm.Tr);
				glRotatef(180, 0.0, 1.0, 0.0);
			}
			else
				glMultMatrix(currtr);  //add current mesh transform for raster camera
		}
		
		// grey axis, aligned with scene axis
		glColor3f(.7f,.7f,.7f);
		glBegin(GL_LINES);
		glVertex3f(vp[0]-(len/2.0),vp[1],vp[2]); 	glVertex3f(vp[0]+(len/2.0),vp[1],vp[2]);
		glVertex3f(vp[0],vp[1]-(len/2.0),vp[2]); 	glVertex3f(vp[0],vp[1]+(len/2.0),vp[2]);
		glVertex3f(vp[0],vp[1],vp[2]-(len/2.0)); 	glVertex3f(vp[0],vp[1],vp[2]+(len/2.0));
		glEnd();
		
		//if(m!=NULL) //if mesh camera, apply mesh transform
		//    glMultMatrix(m->cm.Tr);
		
		// RGB axis, aligned with camera axis
		glBegin(GL_LINES);
		glColor3f(1.0,0,0); glVertex(vp); 	glVertex(vp+ax0*len);
		glColor3f(0,1.0,0); glVertex(vp); 	glVertex(vp+ax1*len);
		glColor3f(0,0,1.0); glVertex(vp); 	glVertex(vp+ax2*len);
		glEnd();
		
		// Now draw the frustum
		Point3m viewportCenter = vp - (ax2*ls.Intrinsics.FocalMm * drawscale);
		Point3m viewportHorizontal = ax0* float(ls.Intrinsics.ViewportPx[0]*ls.Intrinsics.PixelSizeMm[0]/2.0f * drawscale);
		Point3m viewportVertical   = ax1* float(ls.Intrinsics.ViewportPx[1]*ls.Intrinsics.PixelSizeMm[1]/2.0f * drawscale);
		
		glBegin(GL_LINES);
		glColor(camcolor);
		glVertex3f(vp[0],vp[1],vp[2]); glVertex(viewportCenter);
		glColor(camcolor);
		glVertex(vp); glVertex(viewportCenter+viewportHorizontal+viewportVertical);
		glVertex(vp); glVertex(viewportCenter+viewportHorizontal-viewportVertical);
		glVertex(vp); glVertex(viewportCenter-viewportHorizontal+viewportVertical);
		glVertex(vp); glVertex(viewportCenter-viewportHorizontal-viewportVertical);
		glEnd();
		glBegin(GL_LINE_LOOP);
		glVertex(viewportCenter+viewportHorizontal+viewportVertical);
		glVertex(viewportCenter+viewportHorizontal-viewportVertical);
		glVertex(viewportCenter-viewportHorizontal-viewportVertical);
		glVertex(viewportCenter-viewportHorizontal+viewportVertical);
		glEnd();
		
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glColor4f(.8f,.8f,.8f,.2f);
		glBegin(GL_TRIANGLE_FAN);
		glVertex(vp);
		glVertex(viewportCenter+viewportHorizontal+viewportVertical);
		glVertex(viewportCenter+viewportHorizontal-viewportVertical);
		glVertex(viewportCenter-viewportHorizontal-viewportVertical);
		glVertex(viewportCenter-viewportHorizontal+viewportVertical);
		glVertex(viewportCenter+viewportHorizontal+viewportVertical);
		glEnd();
		glDisable(GL_BLEND);
		
		// remove mesh transform
		glPopMatrix();
	}
	
	glPopAttrib();
}

void DecorateBasePlugin::DrawColorHistogram(CHist &ch, GLArea *gla, QPainter *painter, const RichParameterList *par, QFont qf, bool perVertex)
{
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	
	float ratio = float(gla->width())/gla->height();
	glOrtho(0,ratio,0,1,-1,1);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	
	glPushAttrib(GL_ENABLE_BIT);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glDisable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);
	
	float len = ch.MaxV() - ch.MinV();
	float maxWide = ch.MaxCount();
	float histWide=maxWide;
	QString histFixedParam = perVertex ? perVertexHistFixedParam() : perFaceHistFixedParam();
	if(par->getBool(histFixedParam))
	{
		QString histFixedWidthParam = perVertex ? perVertexHistFixedWidthParam() : perFaceHistFixedWidthParam();
		histWide = par->getFloat(histFixedWidthParam);
		if(histWide ==0 ) histWide = maxWide;
	}
	float bn = ch.BinNum();
	
	float border = 0.15f;
	float histH = 1.0f - 2.f*border;
	float histW = 0.3f;
	
	glBegin(GL_QUAD_STRIP);
	for(float i =0;i<bn;++i)
	{
		float val = ch.MinV() + (i/bn)*(ch.MaxV() - ch.MinV());
		float wide = histW *float(ch.BinCount(val))/histWide;
		wide= std::min(0.5f,wide);
		float ypos  = ( i   /bn)*histH;
		float ypos2 = ((i+1)/bn)*histH;
		
		glColor(ch.BinColorAvg(val));
		glVertex3f(border,    border+ypos,0);
		glVertex3f(border+wide, border+ypos,0);
		glVertex3f(border,   border+ypos2,0);
		glVertex3f(border+wide,border+ypos2,0);
	}
	
	glEnd();
	
	glColor(textColor);
	drawQuotedLine(Point3d(border*4/5.0,border,0),Point3d(border*4/5.0,1.0-border,0),ch.MinV(),ch.MaxV(),len/20.0,painter,qf,0,true);
	glLabel::Mode mode(textColor);
	QString parName = perVertex ? perVertexHistFontSize() : perFaceHistFontSize();
	int fSize = par->getInt(parName);
	mode.qFont.setPixelSize(fSize);
	glLabel::render(painter,Point3f(border,1-border*0.5,0),QString("MinV %1 MaxV %2 MaxC %3").arg(ch.MinElem()).arg(ch.MaxElem()).arg(maxWide), mode);
	// Closing 2D
	glPopAttrib();
	glPopMatrix(); // restore modelview
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
}

// This function performs the Scale/Translation transform
// that is needed to correctly draw a single texture.
// When more than a single texture is used they are stacked vertically
void DecorateBasePlugin::PlaceTexParam(int /*TexInd*/, int /*TexNum*/)
{
	
}


void DecorateBasePlugin::DrawTexParam(MeshModel &m, GLArea *gla, QPainter *painter,  const RichParameterList *rm, QFont qf)
{
	if ((gla == NULL) && (gla->getSceneGLSharedContext() == NULL))
		return;
	if (!m.hasDataMask(MeshModel::MM_WEDGTEXCOORD)) return;
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	float ratio = float(gla->width()) / gla->height();
	glOrtho(-ratio, ratio, -1, 1, -1, 1);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	glTranslatef(ratio - 1.0, 0.0f, 0.0f);
	glScalef(0.9f, 0.9f, 0.9f);
	
	int texInd = 0;
	QString textureName;
	
	if (m.cm.textures.empty())
	{
		textureName = "-- no texture --";
	}
	else
	{
		texInd = std::min(rm->getInt(this->TextureIndexParam()), int(m.cm.textures.size()) - 1);
		textureName = "TEX " + QString::number(texInd) + ": " + QString(m.cm.textures[texInd].c_str()) + " ";
	}
	
	glLabel::render(painter, Point3f(0.0f, -0.10f, 0.0f), textureName, glLabel::Mode(textColor));
	checkGLError::debugInfo("DrawTexParam");
	drawQuotedLine(Point3d(0, 0, 0), Point3d(0, 1, 0), 0.0f, 1.0f, 0.1f, painter, qf, 0.0f, true);
	drawQuotedLine(Point3d(0, 0, 0), Point3d(1, 0, 0), 0.0f, 1.0f, 0.1f, painter, qf, 90.0f);
	
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glDisable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	
	if (rm->getBool(this->TextureStyleParam()))
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	else
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	
	bool faceColor = rm->getBool(this->TextureFaceColorParam());
	if (faceColor && !m.hasDataMask(MeshModel::MM_FACECOLOR))
	{
		this->realTimeLog("Show UV Tex Param", "The model has no face color", "The model has no Face Color");
		faceColor = false;
	}
	
	if (!m.cm.textures.empty())
	{
		MLSceneGLSharedDataContext* ctx = gla->getSceneGLSharedContext();
		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, ctx->getTextureId(m.id(), texInd));
		
	}
	
	glBegin(GL_TRIANGLES);
	for (size_t i = 0; i<m.cm.face.size(); ++i)
		if (!m.cm.face[i].IsD() && (m.cm.face[i].WT(0).N() == texInd))
		{
			if (faceColor)
				glColor(m.cm.face[i].C());
			else
				m.cm.face[i].IsS() ? glColor(vcg::Color4b(vcg::Color4b::Red)) : glColor(vcg::Color4b(vcg::Color4b::White));
			glTexCoord(m.cm.face[i].WT(0).P());
			glVertex(m.cm.face[i].WT(0).P());
			glTexCoord(m.cm.face[i].WT(1).P());
			glVertex(m.cm.face[i].WT(1).P());
			glTexCoord(m.cm.face[i].WT(2).P());
			glVertex(m.cm.face[i].WT(2).P());
		}
	glEnd();
	glDisable(GL_TEXTURE_2D);
	
	// Closing 2D
	glPopAttrib();
	
	glPopMatrix(); // restore modelview
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
}

void DecorateBasePlugin::initGlobalParameterList(const QAction* action, RichParameterList &parset)
{
	
	switch(ID(action))
	{
	case DP_SHOW_BOX_CORNERS :
	{
		parset.addParam(RichBool(this->BBAbsParam(), false, "Draw Untransformed","If true the bbox is drawn in the original, untransformed position "
			"(instead of the position obtained by transforming it using the matrix associated to the current Layer)"));
	} break;
		
	case DP_SHOW_TEXPARAM : 
	{
		assert(!parset.hasParameter(TextureStyleParam()));
		parset.addParam(RichBool(TextureStyleParam(), true,"Texture Param Wire","if true the parametrization is drawn in a textured wireframe style"));
		parset.addParam(RichBool(TextureFaceColorParam(), false,"Face Color","if true the parametrization is drawn with a per face color (useful if you want display per face parametrization distortion)"));
		parset.addParam(RichInt(TextureIndexParam(), 0, "Texture Index", "Which texture is shown, for models with multiple textures (index start at 0)."));
	} break;
		
	case DP_SHOW_LABEL :
	{
		parset.addParam(RichBool(LabelVertFlag(),true,"Per Vertex",""));
		parset.addParam(RichBool(LabelEdgeFlag(),true,"Per Edge",""));
		parset.addParam(RichBool(LabelFaceFlag(),true,"Per Face",""));
	} break;
		
	case DP_SHOW_NORMALS : 
	{
		parset.addParam(RichFloat(NormalLength(),0.05f,"Vector Length","The length of the normal expressed as a percentage of the bbox of the mesh"));
		parset.addParam(RichFloat(NormalWidth(), 1.0f,"Normal Width","The width of the normal expressed in pixels"));
		parset.addParam(RichColor(NormalVertColor(),QColor(102, 102, 255, 153),QString("Curr Vert Normal Color"),QString("Current Vert Normal Color")));
		parset.addParam(RichColor(NormalFaceColor(),QColor(102, 102, 255, 153),QString("Curr Face Normal Color"),QString("Current Face Normal Color")));
		parset.addParam(RichBool(NormalVertFlag(),true,"Per Vertex",""));
		parset.addParam(RichBool(NormalFaceFlag(),true,"Per Face",""));
		parset.addParam(RichBool(NormalSelection(), false, "Show Selected", ""));
	} break;
		
	case DP_SHOW_CURVATURE : 
	{
		parset.addParam(RichFloat(CurvatureLength(),0.05f,"Vector Length","The max length of the curvature vectors expressed as a percentage of the bbox of the mesh"));
		parset.addParam(RichBool(CurvatureScaling(),false,"Vector Scaling","The length of the curvature vectors is proportional to the curvature along that direction."));
		parset.addParam(RichBool(ShowPerVertexCurvature(),true,"Per Vertex",""));
		parset.addParam(RichBool(ShowPerFaceCurvature(),true,"Per Face",""));
	} break;
		
	case DP_SHOW_VERT_QUALITY_HISTOGRAM :
	{
		parset.addParam(RichInt(perVertexHistBinNumParam(), 256,"Histogram Bins","If true the parametrization is drawn in a textured wireframe style"));
		parset.addParam(RichBool(perVertexHistAreaParam(), false,"Area Weighted","If true the histogram is computed according to the surface of the involved elements.<br>"
			"e.g. each face contribute to the histogram proportionally to its area and each vertex with 1/3 of sum of the areas of the incident triangles."));
		parset.addParam(RichBool(perVertexHistFixedParam(), false,"Fixed Width","if true the parametrization is drawn in a textured wireframe style"));
		parset.addParam(RichFloat(perVertexHistFixedMinParam(), 0,"Min Hist Value","Used only if the Fixed Histogram Width Parameter is checked"));
		parset.addParam(RichFloat(perVertexHistFixedMaxParam(), 0,"Max Hist Value","Used only if the Fixed Histogram Width Parameter is checked"));
		parset.addParam(RichFloat(perVertexHistFixedWidthParam(), 0,"Hist Width","If not zero, this value is used to scale histogram width  so that it is the indicated value.<br>"
			"Useful only if you have to compare multiple histograms.<br>"
			"Warning, with wrong values the histogram can become excessively flat or it can overflow"));
		parset.addParam(RichInt(perVertexHistFontSize(), 12, "Text Font Size", "Text Font size rendered"));
	} break;
		
	case DP_SHOW_FACE_QUALITY_HISTOGRAM :
	{
		parset.addParam(RichInt(perFaceHistBinNumParam(), 256,"Histogram Bins","If true the parametrization is drawn in a textured wireframe style"));
		parset.addParam(RichBool(perFaceHistAreaParam(), false,"Area Weighted","If true the histogram is computed according to the surface of the involved elements.<br>"
			"e.g. each face contribute to the histogram proportionally to its area and each vertex with 1/3 of sum of the areas of the incident triangles."));
		parset.addParam(RichBool(perFaceHistFixedParam(), false,"Fixed Width","if true the parametrization is drawn in a textured wireframe style"));
		parset.addParam(RichFloat(perFaceHistFixedMinParam(), 0,"Min Hist Value","Used only if the Fixed Histogram Width Parameter is checked"));
		parset.addParam(RichFloat(perFaceHistFixedMaxParam(), 0,"Max Hist Value","Used only if the Fixed Histogram Width Parameter is checked"));
		parset.addParam(RichFloat(perFaceHistFixedWidthParam(), 0,"Hist Width","If not zero, this value is used to scale histogram width  so that it is the indicated value.<br>"
			"Useful only if you have to compare multiple histograms.<br>"
			"Warning, with wrong values the histogram can become excessively flat or it can overflow"));
		parset.addParam(RichInt(perFaceHistFontSize(), 12, "Text Font Size", "Text Font size rendered"));
	} break;
		
	case DP_SHOW_CAMERA :
	{
		QStringList methods; methods << "Trackball" << "Mesh Camera" << "Raster Camera";
		QStringList scale; scale << "No Scale" << "Fixed Factor";
		parset.addParam(RichEnum(this->CameraScaleParam(), 0, scale,"Camera Scale Method","Change rendering scale for better visibility in the scene"));
		parset.addParam(RichFloat(this->FixedScaleParam(), 5.0,"Scale Factor","Draw scale. Used only if the Fixed Factor scaling is chosen"));
		parset.addParam(RichBool(this->ShowMeshCameras(), false, "Show Mesh Cameras","if true, valid cameras are shown for all visible mesh layers"));
		parset.addParam(RichBool(this->ShowRasterCameras(), true, "Show Raster Cameras","if true, valid cameras are shown for all visible raster layers"));
		parset.addParam(RichBool(this->ShowCameraDetails(), false, "Show Current Camera Details","if true, prints on screen all intrinsics and extrinsics parameters for current camera"));
		parset.addParam(RichBool(this->ApplyMeshTr(), false, "Apply Current Mesh Matrix", "if true, the positions of the cameras are mutiplied with the transformation matrix of the current mesh layer"));
	} break;
		
	case DP_SHOW_QUALITY_CONTOUR :
	{
		QStringList ColorMapList; ColorMapList << "None" << "ColorJet"<<"Parula";
		parset.addParam(RichFloat       (this->ShowContourFreq(), 20, "Number of Contours","The number of contours that are drawn between min and max of the quality values."));
		parset.addParam(RichDynamicFloat(this->ShowContourWidth(), 0.5f,0.0f,1.0f, "Width","Relative width of the contours; in the 0..1 range."));
		parset.addParam(RichDynamicFloat(this->ShowContourAlpha(), 0.5f,0.0f,1.0f, "Alpha of Contours","Transparency of che contours that are overdrawn over the mesh."));
		parset.addParam(RichBool(this->ShowContourRamp(), true, "Ramp Contour","If enabled show a ramp that gives you info about the gradient of the quality field (transparent to opaque means increasing values) "));
		parset.addParam(RichEnum(this->ShowContourColorMap(), 0, ColorMapList,"ColorMap","Choose a colormap for the contours"));
	} break;
		
	case DP_SHOW_SELECTED_MESH :
	{
		parset.addParam(RichColor(selectedMeshBlendingColor(),QColor(255, 178,0, 50),QString("Curr Mesh Blend Color"),QString("Current Mesh Blending Color")));
	} break;
	}
	
}


MESHLAB_PLUGIN_NAME_EXPORTER(DecorateBasePlugin)
