/****************************************************************************
* MeshLab                                                           o o     *
* A versatile mesh processing toolbox                             o     o   *
*                                                                _   O  _   *
* Copyright(C) 2004-2008                                           \/)\/    *
* 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 "meshcolorize.h"
#include <vcg/complex/algorithms/clean.h>
#include <vcg/complex/algorithms/stat.h>
#include <vcg/complex/algorithms/smooth.h>
#include <vcg/complex/algorithms/update/curvature.h>
#include <vcg/complex/algorithms/parametrization/distortion.h>
#include <vcg/space/fitting3.h>

// ERROR CHECKING UTILITY
#define CheckError(x,y); if ((x)) {this->errorMessage = (y); return false;}

using namespace std;
using namespace vcg;

ExtraMeshColorizePlugin::ExtraMeshColorizePlugin() {
    typeList <<
        CP_CLAMP_QUALITY <<
        CP_SATURATE_QUALITY <<
        CP_MAP_VQUALITY_INTO_COLOR <<
        CP_MAP_FQUALITY_INTO_COLOR <<
        CP_DISCRETE_CURVATURE <<
        CP_TRIANGLE_QUALITY <<
        CP_VERTEX_SMOOTH <<
        CP_FACE_SMOOTH <<
        CP_MESH_TO_FACE <<
        CP_VERTEX_TO_FACE <<
        CP_FACE_TO_VERTEX <<
        CP_TEXTURE_TO_VERTEX <<
        CP_RANDOM_FACE <<
        CP_RANDOM_CONNECTED_COMPONENT;

    FilterIDType tt;
    foreach(tt , types())
        actionList << new QAction(filterName(tt), this);
}

QString ExtraMeshColorizePlugin::filterName(FilterIDType c) const{
    switch(c){
    case CP_CLAMP_QUALITY:             return QString("Clamp Vertex Quality");
    case CP_SATURATE_QUALITY:          return QString("Saturate Vertex Quality");
    case CP_MAP_VQUALITY_INTO_COLOR:   return QString("Colorize by vertex Quality");
    case CP_MAP_FQUALITY_INTO_COLOR:   return QString("Colorize by face Quality");
    case CP_DISCRETE_CURVATURE:        return QString("Discrete Curvatures");
    case CP_TRIANGLE_QUALITY:          return QString("Per Face Quality according to Triangle shape and aspect ratio");
    case CP_VERTEX_SMOOTH:             return QString("Smooth: Laplacian Vertex Color");
    case CP_FACE_SMOOTH:               return QString("Smooth: Laplacian Face Color");
    case CP_VERTEX_TO_FACE:            return QString("Transfer Color: Vertex to Face");
    case CP_MESH_TO_FACE:            return QString("Transfer Color: Mesh to Face");
    case CP_FACE_TO_VERTEX:            return QString("Transfer Color: Face to Vertex");
    case CP_TEXTURE_TO_VERTEX:         return QString("Transfer Color: Texture to Vertex");
    case CP_RANDOM_FACE:               return QString("Random Face Color");
    case CP_RANDOM_CONNECTED_COMPONENT:return QString("Random Component Color ");
    default: assert(0);
    }
    return QString("error!");
}
QString ExtraMeshColorizePlugin::filterInfo(FilterIDType filterId) const {
    switch(filterId){
    case CP_CLAMP_QUALITY:             return QString("Clamp vertex quality values to a given range according to specific values or to percentiles");
    case CP_SATURATE_QUALITY:          return QString("Saturate vertex quality, so that for each vertex the gradient of the quality is lower than the given threshold value (in absolute value)\n"
                                           "The saturation is done in a conservative way (quality is always decreased and never increased)");
    case CP_MAP_VQUALITY_INTO_COLOR :  return QString("Color vertices depending on their quality field (manually equalized).");
    case CP_MAP_FQUALITY_INTO_COLOR :  return QString("Color faces depending on their quality field (manually equalized).");
    case CP_DISCRETE_CURVATURE :       return QString("Colorize according to various discrete curvature computed as described in:<br>"
                                           "'<i>Discrete Differential-Geometry Operators for Triangulated 2-Manifolds</i>' <br>"
                                           "M. Meyer, M. Desbrun, P. Schroder, A. H. Barr");
    case CP_TRIANGLE_QUALITY:         return QString("Compute a quality and colorize faces depending on triangle quality:<br>"
                                          "1: minimum ratio height/edge among the edges<br>"
                                          "2: ratio between radii of incenter and circumcenter<br>"
                                          "3: 2*sqrt(a, b)/(a+b), a, b the eigenvalues of M^tM, M transform triangle into equilateral");
    case CP_VERTEX_SMOOTH:            return QString("Laplacian Smooth Vertex Color");
    case CP_FACE_SMOOTH:              return QString("Laplacian Smooth Face Color");
    case CP_VERTEX_TO_FACE:           return QString("Vertex to Face color transfer");
    case CP_MESH_TO_FACE:           return QString("Mesh to Face color transfer");
    case CP_FACE_TO_VERTEX:           return QString("Face to Vertex color transfer");
    case CP_TEXTURE_TO_VERTEX:        return QString("Texture to Vertex color transfer");
    case CP_RANDOM_FACE:              return QString("Colorize Faces randomly. If internal edges are present they are used. Useful for quads.");
    case CP_RANDOM_CONNECTED_COMPONENT:  return QString("Colorize each connected component randomly.");
    default: assert(0); return QString("");
    }
}

void ExtraMeshColorizePlugin::initParameterSet(QAction *a, MeshModel &m, RichParameterSet & par){
    // variables cannot be defined within switch statement
    QStringList metrics;
    QStringList curvNameList;
    pair<float,float> minmax;

    switch(ID(a)){
    case CP_FACE_SMOOTH:
    case CP_VERTEX_SMOOTH:
        par.addParam(new RichInt("iteration",1,QString("Iteration"),QString("the number of iteration of the smoothing algorithm")));
        break;
    case CP_TRIANGLE_QUALITY:
        metrics.push_back("area/max side");
        metrics.push_back("inradius/circumradius");
        metrics.push_back("mean ratio");
        metrics.push_back("Area");
        metrics.push_back("Texture Angle Distortion");
        metrics.push_back("Texture Area Distortion");
        metrics.push_back("Planarity (abs plane dist)");
        metrics.push_back("Planarity (relative)");

        par.addParam(new RichEnum("Metric", 0, metrics, tr("Metric:"), tr("Choose a metric to compute triangle quality.")));
        break;
    case CP_DISCRETE_CURVATURE:
        curvNameList.push_back("Mean Curvature");
        curvNameList.push_back("Gaussian Curvature");
        curvNameList.push_back("RMS Curvature");
        curvNameList.push_back("ABS Curvature");
        par.addParam(new RichEnum("CurvatureType", 0, curvNameList, tr("Type:"),
            QString("Choose the curvature value that you want transferred onto the scalar Quality."
                    "Mean (H) and Gaussian (K) curvature are computed according the technique described in the Desbrun et al. paper.<br>"
                    "Absolute curvature is defined as |H|+|K| and RMS curvature as sqrt(4* H^2 - 2K) as explained in <br><i>Improved curvature estimation"
                    "for watershed segmentation of 3-dimensional meshes </i> by S. Pulla, A. Razdan, G. Farin. ")));
        break;
    case CP_SATURATE_QUALITY:
        par.addParam(new RichFloat("gradientThr",1,"Gradient Threshold","The maximum value admitted for the quality gradient (in absolute valu)"));
        par.addParam(new RichBool("updateColor",false,"Update ColorMap","if true the color ramp is computed again"));

        break;
    case CP_MESH_TO_FACE:
        par.addParam(new RichBool("allVisibleMesh",false,"Apply to all Meshes","If true the color mapping is applied to all the meshes."));
        break;
    case CP_CLAMP_QUALITY:
    case CP_MAP_VQUALITY_INTO_COLOR:
        minmax = tri::Stat<CMeshO>::ComputePerVertexQualityMinMax(m.cm);
        par.addParam(new RichFloat("minVal",minmax.first,"Min","The value that will be mapped with the lower end of the scale (blue)"));
        par.addParam(new RichFloat("maxVal",minmax.second,"Max","The value that will be mapped with the upper end of the scale (red)"));
        par.addParam(new RichDynamicFloat("perc",0,0,100,"Percentile Crop [0..100]","If not zero this value will be used for a percentile cropping of the quality values.<br> If this parameter is set a value P to <i>P</i> then the two values <i>V_min,V_max</i> for which <i>P</i>% of the vertices have a quality <b>lower or greater than <i>V_min,V_max</i> are used as min/max values for clamping.<br><br> The automated percentile cropping is very useful for automatically discarding outliers."));
        par.addParam(new RichBool("zeroSym",false,"Zero Simmetric","If true the min max range will be enlarged to be symmertic (so that green is always Zero)"));
        break;
    case CP_MAP_FQUALITY_INTO_COLOR:
        minmax = tri::Stat<CMeshO>::ComputePerFaceQualityMinMax(m.cm);
        par.addParam(new RichFloat("minVal",minmax.first,"Min","The value that will be mapped with the lower end of the scale (blue)"));
        par.addParam(new RichFloat("maxVal",minmax.second,"Max","The value that will be mapped with the upper end of the scale (red)"));
        par.addParam(new RichDynamicFloat("perc",0,0,100,"Percentile Crop [0..100]","If not zero this value will be used for a percentile cropping of the quality values.<br> If this parameter is set to a value<i>P</i> then the two values <i>V_min,V_max</i> for which <i>P</i>% of the vertices have a quality <b>lower or greater than <i>V_min,V_max</i> are used as min/max values for clamping.<br><br> The automated percentile cropping is very useful for automatically discarding outliers."));
        par.addParam(new RichBool("zeroSym",false,"Zero Simmetric","If true the min max range will be enlarged to be symmertic (so that green is always Zero)"));
        break;
    }
}

bool ExtraMeshColorizePlugin::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos *cb)
{
    MeshModel &m=*(md.mm());
    switch(ID(filter)) {
    case CP_SATURATE_QUALITY:{
        m.updateDataMask(MeshModel::MM_VERTFACETOPO);
        tri::UpdateQuality<CMeshO>::VertexSaturate(m.cm, par.getFloat("gradientThr"));
        if(par.getBool("updateColor"))
        {
            Histogramf H;
            tri::Stat<CMeshO>::ComputePerVertexQualityHistogram(m.cm,H);
            m.updateDataMask(MeshModel::MM_VERTCOLOR);
            tri::UpdateColor<CMeshO>::PerVertexQualityRamp(m.cm,H.Percentile(0.1f),H.Percentile(0.9f));
        }
        Log("Saturated ");
                             }
                             break;
    case CP_MAP_VQUALITY_INTO_COLOR:
        m.updateDataMask(MeshModel::MM_VERTCOLOR);
    case CP_CLAMP_QUALITY:
        {
            float RangeMin = par.getFloat("minVal");
            float RangeMax = par.getFloat("maxVal");
            bool usePerc = par.getDynamicFloat("perc")>0;

            Histogramf H;
            tri::Stat<CMeshO>::ComputePerVertexQualityHistogram(m.cm,H);

            float PercLo = H.Percentile(par.getDynamicFloat("perc")/100.f);
            float PercHi = H.Percentile(1.0-par.getDynamicFloat("perc")/100.f);

            if(par.getBool("zeroSym"))
            {
                RangeMin = min(RangeMin, -math::Abs(RangeMax));
                RangeMax = max(math::Abs(RangeMin), RangeMax);
                PercLo = min(PercLo, -math::Abs(PercHi));
                PercHi = max(math::Abs(PercLo), PercHi);
            }

            if(usePerc)
            {
                if(ID(filter)==CP_CLAMP_QUALITY) tri::UpdateQuality<CMeshO>::VertexClamp(m.cm,PercLo,PercHi);
                else tri::UpdateColor<CMeshO>::PerVertexQualityRamp(m.cm,PercLo,PercHi);
                Log("Quality Range: %f %f; Used (%f %f) percentile (%f %f) ",H.MinV(),H.MaxV(),PercLo,PercHi,par.getDynamicFloat("perc"),100-par.getDynamicFloat("perc"));
            } else {
                if(ID(filter)==CP_CLAMP_QUALITY) tri::UpdateQuality<CMeshO>::VertexClamp(m.cm,RangeMin,RangeMax);
                else tri::UpdateColor<CMeshO>::PerVertexQualityRamp(m.cm,RangeMin,RangeMax);
                Log("Quality Range: %f %f; Used (%f %f)",H.MinV(),H.MaxV(),RangeMin,RangeMax);
            }
            break;
        }
    case CP_MAP_FQUALITY_INTO_COLOR: {
        m.updateDataMask(MeshModel::MM_FACECOLOR);
        float RangeMin = par.getFloat("minVal");
        float RangeMax = par.getFloat("maxVal");
        float perc = par.getDynamicFloat("perc");
        bool usePerc = perc>0;

        Histogramf H;
        tri::Stat<CMeshO>::ComputePerFaceQualityHistogram(m.cm,H);
        float PercLo = H.Percentile(perc/100.f);
        float PercHi = H.Percentile(1.0-perc/100.f);

        // Make the range and percentile symmetric w.r.t. zero, so that
        // the value zero is always colored in yellow
        if(par.getBool("zeroSym")){
            RangeMin = min(RangeMin, -math::Abs(RangeMax));
            RangeMax = max(math::Abs(RangeMin), RangeMax);
            PercLo = min(PercLo, -math::Abs(PercHi));
            PercHi = max(math::Abs(PercLo), PercHi);
        }

        if(usePerc){
            tri::UpdateColor<CMeshO>::PerFaceQualityRamp(m.cm,PercLo,PercHi);
            Log("Quality Range: %f %f; Used (%f %f) percentile (%f %f) ",
                H.MinV(), H.MaxV(), PercLo, PercHi, perc, 100-perc);
        } else {
            tri::UpdateColor<CMeshO>::PerFaceQualityRamp(m.cm,RangeMin,RangeMax);
            Log("Quality Range: %f %f; Used (%f %f)",H.MinV(),H.MaxV(),RangeMin,RangeMax);
        }
        break;
                                     }

    case CP_DISCRETE_CURVATURE:
        {
            m.updateDataMask(MeshModel::MM_FACEFACETOPO | MeshModel::MM_VERTCURV);
            m.updateDataMask(MeshModel::MM_VERTCOLOR | MeshModel::MM_VERTQUALITY);
            tri::UpdateFlags<CMeshO>::FaceBorderFromFF(m.cm);

            if ( tri::Clean<CMeshO>::CountNonManifoldEdgeFF(m.cm) > 0) {
                errorMessage = "Mesh has some not 2-manifold faces, Curvature computation requires manifoldness"; // text
                return false; // can't continue, mesh can't be processed
            }

            int delvert=tri::Clean<CMeshO>::RemoveUnreferencedVertex(m.cm);
            if(delvert) Log("Pre-Curvature Cleaning: Removed %d unreferenced vertices",delvert);
            tri::Allocator<CMeshO>::CompactVertexVector(m.cm);
            tri::UpdateCurvature<CMeshO>::MeanAndGaussian(m.cm);
            int curvType = par.getEnum("CurvatureType");

            switch(curvType){
            case 0: tri::UpdateQuality<CMeshO>::VertexFromMeanCurvatureHG(m.cm);        Log( "Computed Mean Curvature");      break;
            case 1: tri::UpdateQuality<CMeshO>::VertexFromGaussianCurvatureHG(m.cm);    Log( "Computed Gaussian Curvature"); break;
            case 2: tri::UpdateQuality<CMeshO>::VertexFromRMSCurvature(m.cm);         Log( "Computed RMS Curvature"); break;
            case 3: tri::UpdateQuality<CMeshO>::VertexFromAbsoluteCurvature(m.cm);    Log( "Computed ABS Curvature"); break;
            default : assert(0);
            }

            Histogramf H;
            tri::Stat<CMeshO>::ComputePerVertexQualityHistogram(m.cm,H);
            tri::UpdateColor<CMeshO>::PerVertexQualityRamp(m.cm,H.Percentile(0.1f),H.Percentile(0.9f));
            Log( "Curvature Range: %f %f (Used 90 percentile %f %f) ",H.MinV(),H.MaxV(),H.Percentile(0.1f),H.Percentile(0.9f));
            break;
        }
    case CP_TRIANGLE_QUALITY:
        {
            m.updateDataMask(MeshModel::MM_FACECOLOR | MeshModel::MM_FACEQUALITY);
            CMeshO::FaceIterator fi;
            Distribution<float> distrib;
            float minV = 0;
            float maxV = 1.0;
            int metric = par.getEnum("Metric");
            if(metric ==4 || metric ==5 )
            {
                if(!m.hasDataMask(MeshModel::MM_VERTTEXCOORD) && !m.hasDataMask(MeshModel::MM_WEDGTEXCOORD))
                {
                    this->errorMessage = "This metric need Texture Coordinate";
                    return false;
                }
            }
            if((metric ==6 || metric ==7) && !m.hasDataMask(MeshModel::MM_POLYGONAL))
            {
                this->errorMessage = "This metric is meaningless for triangle only meshes (all faces are planar by definition)";
                return false;
            }
            switch(metric){

            case 0: { //area / max edge
                minV = 0;
                maxV = sqrt(3.0f)/2.0f;
                for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD())
                    (*fi).Q() = vcg::Quality((*fi).P(0), (*fi).P(1),(*fi).P(2));
                    } break;

            case 1: { //inradius / circumradius
                for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD())
                    (*fi).Q() = vcg::QualityRadii((*fi).P(0), (*fi).P(1), (*fi).P(2));
                    } break;

            case 2: { //mean ratio
                for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD())
                    (*fi).Q() = vcg::QualityMeanRatio((*fi).P(0), (*fi).P(1), (*fi).P(2));
                    } break;

            case 3: { // AREA
                for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD())
                    (*fi).Q() = vcg::DoubleArea((*fi))*0.5f;
                tri::Stat<CMeshO>::ComputePerFaceQualityMinMax(m.cm,minV,maxV);
                    } break;

            case 4: { //TEXTURE Angle Distortion
                if(m.hasDataMask(MeshModel::MM_WEDGTEXCOORD))
                {
                    for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD())
                        (*fi).Q() = Distortion<CMeshO,true>::AngleDistortion(&*fi);
                } else {
                    for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD())
                        (*fi).Q() = Distortion<CMeshO,false>::AngleDistortion(&*fi);
                }
                tri::Stat<CMeshO>::ComputePerFaceQualityDistribution(m.cm,distrib);
                minV = distrib.Percentile(CMeshO::ScalarType(0.05));
                maxV = distrib.Percentile(CMeshO::ScalarType(0.95));
                    } break;

            case 5: { //TEXTURE Area Distortion
                CMeshO::ScalarType areaScaleVal, edgeScaleVal;
                if(m.hasDataMask(MeshModel::MM_WEDGTEXCOORD))
                {
                    Distortion<CMeshO,true>::MeshScalingFactor(m.cm, areaScaleVal,edgeScaleVal);
                    for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD())
                        (*fi).Q() = Distortion<CMeshO,true>::AreaDistortion(&*fi,areaScaleVal);
                } else {
                    Distortion<CMeshO,false>::MeshScalingFactor(m.cm, areaScaleVal,edgeScaleVal);
                    for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD())
                        (*fi).Q() = Distortion<CMeshO,false>::AreaDistortion(&*fi,areaScaleVal);

                }
                tri::Stat<CMeshO>::ComputePerFaceQualityDistribution(m.cm,distrib);
                minV = distrib.Percentile(CMeshO::ScalarType(0.05));
                maxV = distrib.Percentile(CMeshO::ScalarType(0.95));
                    } break;
            case 6:
            case 7: { // polygonal planarity
                tri::UpdateFlags<CMeshO>::FaceClearV(m.cm);
                std::vector<CMeshO::VertexPointer> vertVec;
                std::vector<CMeshO::FacePointer> faceVec;
                for(size_t i=0;i<m.cm.face.size();++i)
                    if(!m.cm.face[i].IsV())
                    {
                        tri::PolygonSupport<CMeshO,CMeshO>::ExtractPolygon(&(m.cm.face[i]),vertVec,faceVec);
                        //assert(faceVec.size()==vertVec.size()-2);

                        std::vector<CMeshO::CoordType> pointVec;
                        for(size_t j=0;j<vertVec.size();++j)
                            pointVec.push_back(vertVec[j]->P());

                        Plane3m pl;
                        vcg::FitPlaneToPointSet(pointVec,pl);
                        float maxDist = 0, sumDist=0, halfPerim=0;
                        for(size_t j=0;j<vertVec.size();++j)
                        {
                            float d=fabs(SignedDistancePlanePoint(pl,pointVec[j]));
                            sumDist+=d;
                            maxDist=max(maxDist,d);
                            halfPerim += Distance(pointVec[j],pointVec[(j+1)%vertVec.size()]);
                        }

                        float avgDist = sumDist/float(vertVec.size());
                        for(size_t j=0;j<faceVec.size();++j)
                            if(metric==6)
                                faceVec[j]->Q()=maxDist;
                            else
                                faceVec[j]->Q()=avgDist/halfPerim;

                    }
                    tri::Stat<CMeshO>::ComputePerFaceQualityDistribution(m.cm,distrib);
                    minV = distrib.Percentile(CMeshO::ScalarType(0.05));
                    maxV = distrib.Percentile(CMeshO::ScalarType(0.95));
                    } break;

            default: assert(0);
            }
            tri::UpdateColor<CMeshO>::PerFaceQualityRamp(m.cm,minV,maxV,false);
            break;
        }


    case CP_RANDOM_CONNECTED_COMPONENT:
        m.updateDataMask(MeshModel::MM_FACEFACETOPO);
        m.updateDataMask(MeshModel::MM_FACEMARK | MeshModel::MM_FACECOLOR);
        vcg::tri::UpdateColor<CMeshO>::PerFaceRandomConnectedComponent(m.cm);
        break;

    case CP_RANDOM_FACE:
        m.updateDataMask(MeshModel::MM_FACEFACETOPO);
        m.updateDataMask(MeshModel::MM_FACEMARK | MeshModel::MM_FACECOLOR);
        vcg::tri::UpdateColor<CMeshO>::PerFaceRandom(m.cm);
        break;

    case CP_VERTEX_SMOOTH:
        {
            int iteration = par.getInt("iteration");
            tri::Smooth<CMeshO>::VertexColorLaplacian(m.cm,iteration,false,cb);
        }
        break;
    case CP_FACE_SMOOTH:
        {
            m.updateDataMask(MeshModel::MM_FACEFACETOPO);
            int iteration = par.getInt("iteration");
            tri::Smooth<CMeshO>::FaceColorLaplacian(m.cm,iteration,false,cb);
        }
        break;
    case CP_FACE_TO_VERTEX:
        m.updateDataMask(MeshModel::MM_VERTCOLOR);
        tri::UpdateColor<CMeshO>::PerVertexFromFace(m.cm);
        break;
    case CP_MESH_TO_FACE:
        {
            QList<MeshModel *> meshList;
            foreach(MeshModel *mmi,md.meshList)
            {
                if(mmi->visible)
                {
                    mmi->updateDataMask(MeshModel::MM_FACECOLOR);
                    tri::UpdateColor<CMeshO>::PerFaceConstant(mmi->cm,mmi->cm.C());
                }
            }
        }
        break;

    case CP_VERTEX_TO_FACE:
        m.updateDataMask(MeshModel::MM_FACECOLOR);
        tri::UpdateColor<CMeshO>::PerFaceFromVertex(m.cm);
        break;
    case CP_TEXTURE_TO_VERTEX:
        {
            m.updateDataMask(MeshModel::MM_VERTCOLOR);
            if(!HasPerWedgeTexCoord(m.cm)) break;
            CMeshO::FaceIterator fi;

			vector <QImage> srcImgs;
			srcImgs.resize(m.cm.textures.size());
			QString path;

			for (int textInd = 0; textInd < m.cm.textures.size(); textInd++)
			{
				path = m.fullName();
				path = path.left(std::max<int>(path.lastIndexOf('\\'), path.lastIndexOf('/')) + 1).append(m.cm.textures[textInd].c_str());
				CheckError(!QFile(path).exists(), QString("Source texture \"").append(path).append("\" doesn't exists"));
				CheckError(!srcImgs[textInd].load(path), QString("Source texture \"").append(path).append("\" cannot be opened"));
			}

            for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD())
            {
                for (int i=0; i<3; i++)
                {
                    // note the trick for getting only the fractional part of the uv with the correct wrapping (e.g. 1.5 -> 0.5 and -0.3 -> 0.7)
                    vcg::Point2f newcoord((*fi).WT(i).P().X()-floor((*fi).WT(i).P().X()),(*fi).WT(i).P().Y()-floor((*fi).WT(i).P().Y()));
					int textIndex = (*fi).WT(i).N();
					if ((textIndex >= 0) && (textIndex < m.cm.textures.size()))
					{
						QRgb val = srcImgs[textIndex].pixel(newcoord[0] * srcImgs[textIndex].width(), (1 - newcoord[1])*srcImgs[textIndex].height() - 1);
						(*fi).V(i)->C() = Color4b(qRed(val), qGreen(val), qBlue(val), 255);
					}
					else
					{
						(*fi).V(i)->C() = Color4b(255, 255, 255, 255);
					}
                }
            }
        }

        break;
    }
    return true;
}

MeshFilterInterface::FILTER_ARITY ExtraMeshColorizePlugin::filterArity( QAction* ) const
{
    return SINGLE_MESH;
}

MeshFilterInterface::FilterClass ExtraMeshColorizePlugin::getClass(QAction *a){
    switch(ID(a)){
    case   CP_SATURATE_QUALITY:
    case   CP_CLAMP_QUALITY:
        return MeshFilterInterface::Quality;
    case   CP_DISCRETE_CURVATURE:
        return FilterClass(Normal + VertexColoring);

    case   CP_MAP_VQUALITY_INTO_COLOR:
    case   CP_VERTEX_SMOOTH:
    case   CP_FACE_TO_VERTEX:
    case   CP_TEXTURE_TO_VERTEX:
        return MeshFilterInterface::VertexColoring;

    case   CP_TRIANGLE_QUALITY:
        return FilterClass(Quality + FaceColoring);

    case   CP_RANDOM_FACE:
    case   CP_RANDOM_CONNECTED_COMPONENT:
    case   CP_FACE_SMOOTH:
    case   CP_VERTEX_TO_FACE:
    case   CP_MESH_TO_FACE:
    case   CP_MAP_FQUALITY_INTO_COLOR:
        return MeshFilterInterface::FaceColoring;

    default:
        assert(0);
        return MeshFilterInterface::Generic;
    }
}

int ExtraMeshColorizePlugin::getPreConditions(QAction *a) const
{
    switch(ID(a)){
    case CP_TRIANGLE_QUALITY:
    case CP_RANDOM_FACE:
    case CP_RANDOM_CONNECTED_COMPONENT:
    case CP_DISCRETE_CURVATURE:
        return MeshModel::MM_FACENUMBER;
    case CP_SATURATE_QUALITY:
    case CP_CLAMP_QUALITY:
    case CP_MAP_VQUALITY_INTO_COLOR:
        return MeshModel::MM_VERTQUALITY;
    case CP_MAP_FQUALITY_INTO_COLOR:
        return MeshModel::MM_FACEQUALITY;
    case CP_FACE_TO_VERTEX:
    case CP_FACE_SMOOTH:
        return MeshModel::MM_FACECOLOR;
    case   CP_VERTEX_SMOOTH:
    case   CP_VERTEX_TO_FACE:
        return MeshModel::MM_VERTCOLOR;
    case   CP_TEXTURE_TO_VERTEX:
    case   CP_MESH_TO_FACE:
        return MeshModel::MM_NONE; // TODO: wrong? compare with original
    default: assert(0);
        return MeshModel::MM_NONE;
    }
}

int ExtraMeshColorizePlugin::postCondition( QAction* a ) const{
    switch(ID(a)){
    case CP_TRIANGLE_QUALITY:
        return MeshModel::MM_FACECOLOR | MeshModel::MM_FACEQUALITY;
    case CP_RANDOM_FACE:
    case CP_RANDOM_CONNECTED_COMPONENT:
    case CP_FACE_SMOOTH:
    case CP_VERTEX_TO_FACE:
    case CP_MESH_TO_FACE:
    case CP_MAP_FQUALITY_INTO_COLOR:
        return MeshModel::MM_FACECOLOR;
    case CP_MAP_VQUALITY_INTO_COLOR:
    case CP_FACE_TO_VERTEX:
    case CP_VERTEX_SMOOTH:
    case CP_TEXTURE_TO_VERTEX:
        return MeshModel::MM_VERTCOLOR;
    case CP_DISCRETE_CURVATURE:
        return MeshModel::MM_VERTCOLOR | MeshModel::MM_VERTQUALITY | MeshModel::MM_VERTNUMBER;
    case CP_CLAMP_QUALITY:
    case CP_SATURATE_QUALITY:
        return MeshModel::MM_VERTCOLOR | MeshModel::MM_VERTQUALITY;
    default: assert(0);
        return MeshModel::MM_NONE;
    }
}



MESHLAB_PLUGIN_NAME_EXPORTER(ExtraMeshColorizePlugin)

