diff --git a/src/plugins_experimental/filter_multiscale_align/filter_multiscale_align.cpp b/src/plugins_experimental/filter_multiscale_align/filter_multiscale_align.cpp new file mode 100644 index 000000000..37eb0b75d --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/filter_multiscale_align.cpp @@ -0,0 +1,376 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ +#define _SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS + +#include "filter_multiscale_align.h" +#include +#include +#include +#include + +using namespace vcg; +using namespace std; + +class BaseSampler +{ +public: + BaseSampler(CMeshO* _m){m=_m; uvSpaceFlag = false; qualitySampling=false; tex=0;} + CMeshO *m; + QImage* tex; + int texSamplingWidth; + int texSamplingHeight; + bool uvSpaceFlag; + bool qualitySampling; + + void AddVert(CMeshO::VertexType &p) + { + tri::Allocator::AddVertices(*m,1); + m->vert.back().ImportData(p); + //p.C()=Color4b(0,0,0,1); + p.SetS(); + } + +}; // end class BaseSampler + + +// Constructor usually performs only two simple tasks of filling the two lists +// - typeList: with all the possible id of the filtering actions +// - actionList with the corresponding actions. If you want to add icons to your filtering actions you can do here by construction the QActions accordingly + +FilterMultiscaleAlign::FilterMultiscaleAlign() +{ + typeList <cm); + tri::RequirePerVertexNormal(toAlignMesh->cm); + + float scaleFact=1.0; + + ////// Poisson sampling to create the models and define the seams + float radiusRef, radiusToAlign, refCoarse, alignCoarse; + MeshModel* refSampled; + MeshModel* refAlign; + + if (refMesh->cm.vn>SUBSAMPLES) + { + radiusRef = 2*poissonDiskSampling(md, refMesh, "refSampled", SUBSAMPLES, false); + refSampled=md.getMesh(md.size()-1); + } + else + { + refSampled=refMesh; + radiusRef= tri::SurfaceSampling::ComputePoissonDiskRadius(refMesh->cm, refMesh->cm.vn); + } + poissonDiskSampling(md, refSampled, "refSampledSampled", opt.seedNumber, opt.checkBorders); + + if (toAlignMesh->cm.vn>SUBSAMPLES) + { + radiusToAlign = 2*poissonDiskSampling(md, toAlignMesh, "refToAlign", SUBSAMPLES, false); + refAlign=md.getMesh(md.size()-1); + } + else + { + refAlign=toAlignMesh; + radiusToAlign= tri::SurfaceSampling::ComputePoissonDiskRadius(toAlignMesh->cm, toAlignMesh->cm.vn); + } + poissonDiskSampling(md, refAlign, "refToAlignSampled", opt.seedNumber, opt.checkBorders); + + + Log("Min radius reference: %f , Min radius toAlign: %f",radiusRef,radiusToAlign); + + ///////////////// + ////PRE-SCALING + ///////////////// +// Here there could be a pre-scaling operation, that brings the two models in similar space. The ration could be given by radiusRef/radiusToAlign, since these two values define the "detail" of the models + ///////////////// + ///END PRE-SCALING + /////////////// + + refCoarse = MAX_SCALE_MULTIPLIER*refMesh->cm.bbox.Diag()/2.0f; + alignCoarse = MAX_SCALE_MULTIPLIER*toAlignMesh->cm.bbox.Diag()/2.0f; + + Log("Max radius reference: %f , Max radius toAlign: %f",refCoarse,alignCoarse); + + double logBase=std::log((double)MULTIPLIER); + + ///// This creates the scales vector + int numScales= (int)(std::log(max(refCoarse,alignCoarse)/min(radiusRef,radiusToAlign)) / logBase) + 2; + + Log("Nb scales: %i",numScales); + + _msa._seedNumber = opt.seedNumber; + scaleFact=_msa.process_scale(refSampled,radiusRef,refCoarse,refAlign,radiusToAlign,alignCoarse, MULTIPLIER, numScales, opt); + + if (scaleFact==-1) + Log("Sorry, alignment not found",scaleFact); + else + Log("Alignment found, final scale factor: %f ",scaleFact); + + refMesh->updateDataMask(MeshModel::MM_VERTQUALITY); + toAlignMesh->updateDataMask(MeshModel::MM_VERTQUALITY); + refSampled->updateDataMask(MeshModel::MM_VERTQUALITY); + refAlign->updateDataMask(MeshModel::MM_VERTQUALITY); + + break; + } + } + + + + return true; +} + +float FilterMultiscaleAlign::selectAllPoints(MeshDocument &md, MeshModel *mesh, QString label) +{ + + CMeshO::VertexIterator vj; + for(vj = mesh->cm.vert.begin(); vj != mesh->cm.vert.end(); ++vj) + { + (*vj).SetS(); + (*vj).Q()=MAX_SCALE_MULTIPLIER*mesh->cm.bbox.Diag(); + } + MeshModel *mm= md.addNewMesh("", label); + mm->updateDataMask(mesh); + mm->updateDataMask(MeshModel::MM_VERTQUALITY); + mm->updateDataMask(MeshModel::MM_VERTCOLOR); + + // workaround to estimate radius + return tri::SurfaceSampling:: + ComputePoissonDiskRadius(mesh->cm, + mesh->cm.vert.size()); +} + +///// This calculates the poisson sampling +float FilterMultiscaleAlign::poissonDiskSampling(MeshDocument &md, MeshModel *mesh, QString label, float numSamples, bool checkBord) +{ + + CMeshO::VertexIterator vj; + for(vj = mesh->cm.vert.begin(); vj != mesh->cm.vert.end(); ++vj) + { + (*vj).ClearS(); + + } + + MeshModel *mm= md.addNewMesh("",label); // After Adding a mesh to a MeshDocument the new mesh is the current one + mm->updateDataMask(mesh); + mm->updateDataMask(MeshModel::MM_VERTQUALITY); + mm->updateDataMask(MeshModel::MM_VERTCOLOR); + + int sampleNum = numSamples; + float radius = tri::SurfaceSampling::ComputePoissonDiskRadius(mesh->cm,sampleNum); + + Log("Computing %i Poisson Samples for an expected radius of %f",sampleNum,radius); + + // first of all generate montecarlo samples for fast lookup + CMeshO *presampledMesh=&(mesh->cm); + + //CMeshO MontecarloMesh; // this mesh is used only if we need real poisson sampling (and therefore we need to choose points different from the starting mesh vertices) + + + //QTime tt;tt.start(); + //BaseSampler sampler(&MontecarloMesh); + //sampler.qualitySampling =true; + //tri::SurfaceSampling::Montecarlo(mesh->cm, sampler, sampleNum*1); + //MontecarloMesh.bbox = mesh->cm.bbox; // we want the same bounding box + //presampledMesh=&MontecarloMesh; + //Log("Generated %i Montecarlo Samples (%i msec)",MontecarloMesh.vn,tt.elapsed()); + + + BaseSampler mps(&(mm->cm)); + + tri::SurfaceSampling::PoissonDiskParam pp; + //tri::SurfaceSampling::PoissonDiskParam::Stat pds; pp.pds=&pds; + pp.bestSampleChoiceFlag=true; + pp.bestSamplePoolSize =10; + + tri::SurfaceSampling::PoissonDiskPruning(mps, *presampledMesh, radius,pp); + //tri::SurfaceSampling::PoissonDisk(curMM->cm, mps, *presampledMesh, radius,pp); + vcg::tri::UpdateBounding::Box(mm->cm); + //Point3i &g=pp.pds->gridSize; + //Log("Grid size was %i %i %i (%i allocated on %i)",g[0],g[1],g[2], pp.pds->gridCellNum, g[0]*g[1]*g[2]); + Log("Sampling created a new mesh of %i points",md.mm()->cm.vn); + + // if (checkBord) + //{ + // _msa.checkBorders(md.mm(), radius, MULTIPLIER); + + // /*Log("Borders checked"); + // Log("Orig size %i Sample size %i",mesh->cm.vn, md.mm()->cm.vn);*/ + + // for(CMeshO::VertexIterator vi = mesh->cm.vert.begin(); vi != mesh->cm.vert.end(); ++vi) + // { + // if((*vi).IsS()) + // { + // for(CMeshO::VertexIterator vj = md.mm()->cm.vert.begin(); vj != md.mm()->cm.vert.end(); ++vj) + // { + // if ((*vj).P()==(*vi).P()) + // { + // if((*vj).Q()==0.0) + // { + // (*vi).ClearS(); + // } + // else + // { + // (*vi).Q()=(*vj).Q(); + // (*vi).C()=(*vj).C(); + // } + // //Log("One done"); + // break; + // } + // } + // } + // + + // } + //} + //else + //{ + for(CMeshO::VertexIterator vi = mesh->cm.vert.begin(); vi != mesh->cm.vert.end(); ++vi) + { + if((*vi).IsS()) + { + (*vi).Q()=MAX_SCALE_MULTIPLIER*mesh->cm.bbox.Diag(); + } + } + + //} + + //we have to use the indices of the vertices, and they MUST be continuous + tri::Allocator::CompactVertexVector(mesh->cm); + + return radius; + +} + + MeshFilterInterface::FilterClass FilterMultiscaleAlign::getClass(QAction *a) +{ + switch(ID(a)) + { + + case MS_ALIGN: + return FilterClass(MeshFilterInterface::Layer+MeshFilterInterface::RangeMap); + break; + default: assert(0); + return MeshFilterInterface::Generic; + } +} + + + MeshFilterInterface::FILTER_ARITY FilterMultiscaleAlign::filterArity(QAction* filter) const + { + switch (ID(filter)) + { + case MS_ALIGN: + return MeshFilterInterface::SINGLE_MESH; + } + return MeshFilterInterface::NONE; + } + + + +MESHLAB_PLUGIN_NAME_EXPORTER(FilterMultiscaleAlign) diff --git a/src/plugins_experimental/filter_multiscale_align/filter_multiscale_align.h b/src/plugins_experimental/filter_multiscale_align/filter_multiscale_align.h new file mode 100644 index 000000000..339c6bdc7 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/filter_multiscale_align.h @@ -0,0 +1,63 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ + +/**************************************************************************** + History + +****************************************************************************/ + +#ifndef FILTER_MULTISCALE_ALIGN_H +#define FILTER_MULTISCALE_ALIGN_H +#include +#include "multiscale_align.h" + +class FilterMultiscaleAlign : public QObject, public MeshFilterInterface +{ + Q_OBJECT + MESHLAB_PLUGIN_IID_EXPORTER(MESH_FILTER_INTERFACE_IID) + Q_INTERFACES(MeshFilterInterface) + +private: + MultiscaleAlign _msa; + +public: + enum { MS_ALIGN } ; + + FilterMultiscaleAlign(); + + QString filterName(FilterIDType filter) const; + QString filterInfo(FilterIDType filter) const; + FilterClass getClass(QAction *); + void initParameterSet(QAction *,MeshDocument & md, RichParameterSet & /*parent*/); + bool applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & /*parent*/, vcg::CallBackPos * cb) ; + + float poissonDiskSampling(MeshDocument &md, MeshModel *refMesh, QString label, float numSamples, bool checkBord=false); + float selectAllPoints(MeshDocument &md, MeshModel *mesh, QString label); + + QString pluginName(void) const { return "FilterMultiscaleAlign"; } + FILTER_ARITY filterArity(QAction*) const; + + +}; + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/filter_multiscale_align.pro b/src/plugins_experimental/filter_multiscale_align/filter_multiscale_align.pro new file mode 100644 index 000000000..3c2d5a3f6 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/filter_multiscale_align.pro @@ -0,0 +1,29 @@ +include (../../shared.pri) + +HEADERS += filter_multiscale_align.h \ + multiscale_align.h \ + localKdTree.h \ + struct.h \ + utils.h \ + generic_align.h + + +SOURCES += filter_multiscale_align.cpp \ + multiscale_align.cpp \ + generic_align.cpp + +TARGET = filter_multiscale_align + +## Dependencies +INCLUDEPATH += lib \ + $$VCGDIR/eigenlib #needed by Grenaille + + +unix: QMAKE_CXXFLAGS += -DQ_OS_LINUX + +# OpenMP +LIBS += -fopenmp +QMAKE_CXXFLAGS += -std=c++11 +QMAKE_CXXFLAGS += -fopenmp +#QMAKE_CXXFLAGS_RELEASE += -g +#QMAKE_CXXFLAGS_RELEASE -= -O2 diff --git a/src/plugins_experimental/filter_multiscale_align/generic_align.cpp b/src/plugins_experimental/filter_multiscale_align/generic_align.cpp new file mode 100644 index 000000000..c7d34e84d --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/generic_align.cpp @@ -0,0 +1,433 @@ +#include "generic_align.h" + +#include + +#include "utils.h" + +using namespace vcg; + +GenericAlign::GenericAlign() +{ +} + + +bool +GenericAlign::isCoupleOk(const CVertexO& firstToAl, + const CVertexO& firstRef, + const CVertexO& secondToAl, + const CVertexO& secondRef, + float scale) +{ + + const float distToAl=Distance(firstToAl.P(), secondToAl.P()); + const float distRef=Distance(firstRef.P(), secondRef.P()); + //std::cout << "DistAl " << distToAl << " DistRef " << distRef << std::endl; + if(distRef<(distToAl*scale*0.8) || distRef>(distToAl*scale*1.2)) + { + return false; + } + const float angleToAl=Utils::angleNorm(firstToAl.N(), secondToAl.N()); + const float angleRef=Utils::angleNorm(firstRef.N(), secondRef.N()); + //std::cout << "DistAl " << distToAl << " DistRef " << distRef << "angleAl " << angleToAl << " angleRef " << angleRef << std::endl; + if(abs(angleToAl-angleRef)>20 || angleToAl<20) + { + if(angleToAl<20) + std::cout << "angleAl " << angleToAl << std::endl; + return false; + } + //std::cout << "DistAl " << distToAl << " DistRef " << distRef << "angleAl " << angleToAl << " angleRef " << angleRef << std::endl; + + return true; +} + +bool +GenericAlign::isToAlignOk(int ind, + MeshModel* toAlign, + const std::vector >& corrs) +{ + bool ok=true; + for (unsigned int i=0; icm.vert[ind].P(), toAlign->cm.vert[corrs[i].first].P()); + ///// This condition is quite arbitrary... + float qual=(toAlign->cm.vert[corrs[i].first].Q()/3.0); + //std::cout << "Dist " << dist << " Qual " << qual << std::endl; + if(dist > &corrs, + float scale) +{ + + bool ok=true; + for (unsigned int i=0; icm.vert[al].P(), toAlign->cm.vert[corrs[i].first].P()); + float angleToAl=Utils::angleNorm(toAlign->cm.vert[al].N(), toAlign->cm.vert[corrs[i].first].N()); + float distRef=Distance(reference->cm.vert[ref].P(), reference->cm.vert[corrs[i].second].P()); + float angleRef=Utils::angleNorm(reference->cm.vert[ref].N(), reference->cm.vert[corrs[i].second].N()); + //std::cout << "DistAl " << distToAl << " DistRef " << distRef << "angleAl " << angleToAl << " angleRef " << angleRef << std::endl; + if(distRef<(distToAl*scale*0.8) || distRef>(distToAl*scale*1.2) || abs(angleToAl-angleRef)>20) + { + ok=false; + break; + } + } + + return ok; +} + + + +/////////// This chooses a set of points for which matches could be found. Very naive for now... +std::vector< int > +GenericAlign::selectSeedsDescr(DescriptorBase toAlign, + const std::vector& descrList, + bool shuffle) +{ + // reduction factor applied to the seed distance selection threshold + const float bboxDistReducFactor = 0.8; + + std::vector< int > seeds; + double qMax=0.0; + for(int i = 0; i < toAlign.selection.size(); i++) + { + const unsigned int& ind = toAlign.selection[i]; + + if(toAlign.model->cm.vert[ind].Q()>qMax) + qMax=toAlign.model->cm.vert[ind].Q(); + } + int init; int cand; double bestD=0.0; + float bboxDist=toAlign.model->cm.bbox.Diag()*bboxDistReducFactor/*/4.0*/; + if (shuffle) + { + float avrgDesc=0.0; + for(int i = 0; i < descrList.size(); i++) + { + avrgDesc+=descrList[i].descrip; + } + avrgDesc/=(float)descrList.size(); + std::cout << "AvrgDesc " << avrgDesc << std::endl; + + bool first=false; + while(!first) + { + init=rand() % toAlign.selection.size(); + std::cout << "AvrgDesc " << avrgDesc << " chosen " << descrList[init].descrip << std::endl; + if(descrList[init].descrip>=avrgDesc) + first=true; + } + init=toAlign.selection[init]; + } + else + { + float bestDescr=0.0; + for(int i = 0; i < descrList.size(); i++) + { + if(descrList[i].descrip>bestDescr) + { + bestDescr=descrList[i].descrip; + init=descrList[i].ind; + } + } + } + + //std::cout << "bestD " << bestD << std::endl; + seeds.push_back(init); + //seeds.reserve(nbSeeds); + bestD=0.0; + + // Avoid infinite loop by checking the distance threshold + while(seeds.size()<_seedNumber && bboxDist > toAlign.minScale) + { + cand=-1; bestD=0.0; + for(int i=0; icm.vert[descrList[i].ind]; + if(descrList[i].descrip*point.Q()>bestD) + { + bool ok=true; + for(int j=0; jcm.vert[seeds[j]].P()) +GenericAlign::selectSeeds(MeshModel* toAlign, + MeshModel* /*reference*/ , + int start) +{ + int ind; + std::vector< int > seeds; + double maxQ=0.0; double newMaxQ=0.0; + + //maxQ=toAlign->cm.vert[start].Q(); + for (int i = 0; i != toAlign->cm.vn; i++) { + if (toAlign->cm.vert[i].IsS() && toAlign->cm.vert[i].Q()>maxQ) + { + maxQ=toAlign->cm.vert[i].Q(); + ind=i; + } + } + if (start!=-1) + { + ind=start; + } + + std::cout << "MaxQ: " << maxQ << "Ind: " << ind << std::endl; + seeds.push_back(ind); + int cand=0; + + while (seeds.size()<15 && cand!=-1) + { + //std::cout << "Cycling... " << std::endl; + cand=-1; + double halfMax=maxQ/3.0; + //std::cout << " Half MaxQ: " << halfMax << std::endl; + for (int i = 0; i != toAlign->cm.vn; i++) { + //newMaxQ=0.0; + + if (toAlign->cm.vert[i].IsS() && toAlign->cm.vert[i].Q()>=halfMax && toAlign->cm.vert[i].Q()>=newMaxQ) + { + + //std::cout << " Ok, count: " << count << std::endl; + //std::cout << "Checking... " << std::endl; + + bool ok=true; + for (unsigned int j = 0; j < seeds.size(); j++) { + float halfDist=toAlign->cm.vert[seeds[j]].Q()/6.0; + //std::cout << " MinDist: " << halfDist << std::endl; + float dist=Distance(toAlign->cm.vert[i].P(),toAlign->cm.vert[seeds[j]].P()); + //std::cout << " Dist: " << dist << std::endl; + if(distcm.vert[i].Q() << std::endl; + newMaxQ=toAlign->cm.vert[i].Q(); + } + } + } + //std::cout << "Final cand: " << cand << std::endl; + if (cand!=-1) + seeds.push_back(cand); + maxQ=newMaxQ; + //std::cout << " new MaxQ: " << toAlign->cm.vert[cand].Q() << std::endl; + //std::cout << " new MaxQ: " << maxQ << std::endl; + } + + + return seeds; +} + + +/////// Given a set of three couples of points, it checks if the transformation is ok, both for final distances and difference in normals +bool GenericAlign::checkTriplets(const DescriptorBase &toAlign, + const DescriptorBase &reference, + std::vector >corrs, + const std::vector &thirdPoints, + float scale, + float error) +{ + float bestError=10000000; + Matrix44f res; + +#pragma omp parallel for + for(int r=0; r ref; std::vector refN; + std::vector toAl; std::vector toAlN; + corrs[2].second=thirdPoints[r].ind; + for (int i=0; i<3; i++) + { + vcg::Point3f r,rN; + r=reference.model->cm.vert[corrs[i].second].P(); + rN=reference.model->cm.vert[corrs[i].second].N(); + ref.push_back(r); + refN.push_back(rN); + + vcg::Point3f a, aN; + a=toAlign.model->cm.vert[corrs[i].first].P(); + aN=toAlign.model->cm.vert[corrs[i].first].N(); + toAl.push_back(a); + toAlN.push_back(aN); + + } + ////// This computes the roto-translation + scaling + ComputeSimilarityMatchMatrix(ref, toAl, res); + bool okNorm=true; + /// This is not so nice to see, but it works... + rot[0][0]=res[0][0];rot[0][1]=res[0][1];rot[0][2]=res[0][2];rot[1][0]=res[1][0];rot[1][1]=res[1][1];rot[1][2]=res[1][2];rot[2][0]=res[2][0];rot[2][1]=res[2][1];rot[2][2]=res[2][2]; + for (int i=0; i<3; i++) + { + vcg::Point3f normToAlign=rot*toAlN[i]; + float angle=Utils::angleNorm(refN[i],normToAlign); + if (angle>20) + { + okNorm=false; + break; + } + + } + //std::cout << res[0][0] << " " << res[0][1] << " " << res[0][2] << " " << res[0][3] << std::endl; + if (okNorm) + { + float totError=0; float error; + for (int i=0; i<3; i++) + { + vcg::Point4f rotToAlign=res*vcg::Point4f(toAl[i].X(),toAl[i].Y(),toAl[i].Z(),1); + error=Distance(ref[i],vcg::Point3f(rotToAlign[0],rotToAlign[1],rotToAlign[2])); + totError+=error; + } +#pragma omp critical + { + //std::cout << "New scale " << getScaleFromTransf(res) << std::endl; + if (totErrorcm.Tr=res; + return true; + } + + return false; +} + +/////// Given a set of four couples of points, it checks if the transformation is ok, both for final distances and difference in normals +bool +GenericAlign::checkQuadriplets(const DescriptorBase &toAlign, + const DescriptorBase &reference, + std::vector >corrs, + const std::vector &fourthPoints, + float scale, + float error) +{ + float bestError=10000000; + Matrix44f res; + +#pragma omp parallel for + for(int r=0; r ref; std::vector refN; + std::vector toAl; std::vector toAlN; + corrs[3].second=fourthPoints[r].ind; + for (int i=0; i<4; i++) + { + vcg::Point3f r,rN; + r=reference.model->cm.vert[corrs[i].second].P(); + rN=reference.model->cm.vert[corrs[i].second].N(); + ref.push_back(r); + refN.push_back(rN); + + vcg::Point3f a, aN; + a=toAlign.model->cm.vert[corrs[i].first].P(); + aN=toAlign.model->cm.vert[corrs[i].first].N(); + toAl.push_back(a); + toAlN.push_back(aN); + + } + ////// This computes the roto-translation + scaling + ComputeSimilarityMatchMatrix(ref, toAl, res); + bool okNorm=true; + /// This is not so nice to see, but it works... + rot[0][0]=res[0][0];rot[0][1]=res[0][1];rot[0][2]=res[0][2];rot[1][0]=res[1][0];rot[1][1]=res[1][1];rot[1][2]=res[1][2];rot[2][0]=res[2][0];rot[2][1]=res[2][1];rot[2][2]=res[2][2]; + for (int i=0; i<4; i++) + { + vcg::Point3f normToAlign=rot*toAlN[i]; + float angle=Utils::angleNorm(refN[i],normToAlign); + if (angle>20) + { + okNorm=false; + break; + } + + } + //std::cout << res[0][0] << " " << res[0][1] << " " << res[0][2] << " " << res[0][3] << std::endl; + if (okNorm) + { + float totError=0; float error; + for (int i=0; i<4; i++) + { + vcg::Point4f rotToAlign=res*vcg::Point4f(toAl[i].X(),toAl[i].Y(),toAl[i].Z(),1); + error=Distance(ref[i],vcg::Point3f(rotToAlign[0],rotToAlign[1],rotToAlign[2])); + totError+=error; + } +#pragma omp critical + { + if (totErrorcm.Tr=res; + return true; + } + + return false; +} + + diff --git a/src/plugins_experimental/filter_multiscale_align/generic_align.h b/src/plugins_experimental/filter_multiscale_align/generic_align.h new file mode 100644 index 000000000..74f61698f --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/generic_align.h @@ -0,0 +1,274 @@ +#ifndef GENERIC_ALIGN_H +#define GENERIC_ALIGN_H + + +////// Maximum number of points used to compute the GLS descriptor +#define GLS_SUBSAMPLE +#define GLS_MAX_NO_POINTS 20000 + + +#include + +#include "struct.h" + +class GenericAlign +{ +public: + GenericAlign(); + + int _seedNumber; + +protected: + // Given a couple of points, it checks if they are compatible with a + // list of previous couples, both for distance and difference in normals + inline bool isScaleOk(float scale1, float scale2, float multiplier) + { + if(scale1>=scale2) return (scale1/scale2<=multiplier); + else return (scale2/scale1<=multiplier); + } + + inline float getScaleFromTransf(const vcg::Matrix44f& res) + { + vcg::Point3f diag(res[0][0], res[1][1], res[2][2]); + return sqrt(diag.X()*diag.X()+diag.Y()*diag.Y()+diag.Z()*diag.Z()); + + } + + // Given a couple of points, it checks if they are compatible with a list of + // previous couples, both for distance and difference in normals + bool isCoupleOk(const CVertexO& firstToAl, + const CVertexO& firstRef, + const CVertexO& secondToAl, + const CVertexO& secondRef, + float scale); + + // Check if two points on the toAlign model are far enough to help in + // the registration + bool isToAlignOk(int ind, + MeshModel* toAlign, + const std::vector >& corrs); + + + bool isReferenceOk(int ref, + MeshModel* reference, + int al, + MeshModel* toAlign, + const std::vector > &corrs, + float scale); + + std::vector< int > + selectSeeds(MeshModel* toAlign, + MeshModel* /*reference*/ , + int start=0); + + std::vector< int > + selectSeedsDescr(DescriptorBase toAlign, + const std::vector &descrList, + bool shuffle); + + bool checkTriplets(const DescriptorBase& toAlign, + const DescriptorBase& reference, + std::vector > corrs, + const std::vector& thirdPoints, + float scale, + float error); + bool checkQuadriplets(const DescriptorBase& toAlign, + const DescriptorBase& reference, + std::vector > corrs, + const std::vector& thirdPoints, + float scale, + float error); + + // Create space to store GLS descriptors and computes it + inline + void preComputeDescriptor(DescriptorBase& mesh, + const std::vector &scales, + bool ompParallel) + { _preAllocateDescriptor(mesh, scales, ompParallel); } + + // Create space to store GLS descriptors without computing it + inline + void preAllocateDescriptor(DescriptorBase& mesh, + const std::vector &scales, + bool ompParallel) + { _preAllocateDescriptor(mesh, scales, ompParallel); } + + + template + inline void computeDescriptor( const MyPoint &query, + const std::vector& scales, + const DescriptorBase& base, + std::vector* result, bool overwrite = true); + + float ComputeLCP( const vcg::Matrix44f& transformation, + float epsilon, + const DescriptorBase& P, + const DescriptorBase& Q, + float prevLCP); + +private: + template + inline void + _preAllocateDescriptor(DescriptorBase& mesh, + const std::vector& scales, + bool ompParallel); +}; + + + +/// Compute the GLS descriptor for at the position query and for a given set of scales +template +void +GenericAlign::computeDescriptor( const MyPoint &query, + const std::vector& scales, + const DescriptorBase& base, + std::vector* result, bool overwrite) +{ + + unsigned int nbScale = scales.size(); + float scaleMax = base.maxScale; + + // compile time check + if (! overwrite) { + if (result->size() == nbScale) + return; + } + + if (result->size() != nbScale) + result->resize(nbScale); + + //QTime time; + + /// 3.1 Collect neighborhood at larger scale + std::vector n; + std::vector squaredDist; + + //time.start(); +#pragma omp critical + vcg::Point3f seed(query.pos().x(), query.pos().y(), query.pos().z()); + base.kdTree->doQueryDist(seed, scaleMax, n, squaredDist); + std::random_shuffle ( n.begin(), n.end() ); + //cout << "Nei. collection done in " << time.elapsed() << " msec" << endl; + + /// 3.2 Compute and store the fit at multiple scales + int nId; + + //time.start(); + /// last id of the partially sorted array + int lastnId = n.size()-1; + for (int t = scales.size()-1; t >= 0; t--){ + Fit mfit; + const float& scaleSize = scales[t]; + const float sqScale = scaleSize*scaleSize; + + if (scaleSize>base.minScale && scaleSize<=base.maxScale) + { +#ifdef GLS_SUBSAMPLE + // initialize descriptor + mfit.setWeightFunc(WeightFunc(scaleSize)); + mfit.init(query.pos()); + + /// last id we take into account wrt to GLS_MAX_NO_POINTS + int lastReducedId = lastnId < GLS_MAX_NO_POINTS ? lastnId : GLS_MAX_NO_POINTS; + for (nId = 0; nId <= lastReducedId; ){ + const unsigned int& id = n[nId]; // it is the original id, as requested when doing the kdist query + + MyPoint p (base.model->cm.vert[id]); + p.normal().normalize(); + + if ((query.pos() - p.pos()).squaredNorm() <= sqScale){ + mfit.addNeighbor(p); + nId++; //must don't go to next point when need to swap + }else { + if (lastnId == 0) + break; + + unsigned int tmp = n[nId]; + n[nId] = n[lastnId]; + n[lastnId] = tmp; + + lastnId--; + + lastReducedId = lastnId < GLS_MAX_NO_POINTS ? lastnId : GLS_MAX_NO_POINTS; + } + } + mfit.finalize(); + /* std::cout << scaleSize + << " " << mfit.tau() + << " " << mfit.kappa() + << " " << mfit.tau_normalized() + << " " << mfit.kappa_normalized() + << endl;*/ + +#else //GLS_SUBSAMPLE is not defined: Use all points + // initialize descriptor + mfit.setWeightFunc(WeightFunc(scaleSize)); + mfit.init(query.pos()); + + // compute descriptor + // we use here a partial sorting of the neighborhood: + // - we start by the coarser scale + // - each time we get point too far to be considered + // - we put it at the end of the neighborhood array + // + // Each time a neighbor is swapped, we record it and use the + // number of swap as right-end array limit. + for (nId = 0; nId <= lastnId; ){ + const unsigned int& id = n[nId]; // it is the original id, as requested when doing the kdist query + + MyPoint p (base.model->cm.vert[id]); + p.normal().normalize(); + + if ((query.pos() - p.pos()).squaredNorm() <= sqScale){ + mfit.addNeighbor(p); + nId++; //must don't go to next point when need to swap + }else { + if (lastnId == 0) + break; + + unsigned int tmp = n[nId]; + n[nId] = n[lastnId]; + n[lastnId] = tmp; + + lastnId--; + } + } + mfit.finalize(); +#endif //#ifdef GLS_SUBSAMPLE + } + + (*result)[t] = mfit; + } +} + + +// Precompute the GLS descriptor, only for the selected points +template +void +GenericAlign::_preAllocateDescriptor(DescriptorBase& mesh, + const std::vector& scales, + bool ompParallel){ + typedef typename std::vector Container; + using vcg::tri::Allocator; + + CMeshO::PerVertexAttributeHandle descriptorsHandler; + descriptorsHandler = + Allocator::GetPerVertexAttribute + (mesh.model->cm, std::string("GLSDescr")); + +#pragma omp parallel for if (ompParallel) + for(int i = 0; i < mesh.selection.size(); i++){ + + unsigned int ind = mesh.selection[i]; + + descriptorsHandler[ind] = new std::vector(); + + if(compute) + computeDescriptor(mesh.model->cm.vert[ind], + scales, + mesh, + descriptorsHandler[ind]); + } +} + +#endif // GENERIC_ALIGN_H diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Dynamic/basics.h b/src/plugins_experimental/filter_multiscale_align/lib/Dynamic/basics.h new file mode 100644 index 000000000..de94963ae --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Dynamic/basics.h @@ -0,0 +1,81 @@ +#ifndef _DYNAMIC_BASICS_ +#define _DYNAMIC_BASICS_ + +namespace DynamicProg{ + + namespace DynamicRelation{ + enum { + Left = 0x01, + Top = 0x02, + TopLeft = 0x04, + Nothing = 0x08, + }; + } + + + template + class DynamicStep { + public: + Scalar value; + int relationFlags; + + inline DynamicStep(Scalar v = 0, + int flags = DynamicRelation::Nothing) + :value(v), relationFlags(flags) {} + }; //struct DynamicStep + + template + inline bool operator< (const DynamicStep& s1, + const DynamicStep& s2){ + return s1.value < s2.value; + } + + template + inline bool operator> (const DynamicStep& s1, + const DynamicStep& s2){ + return s1.value > s2.value; + } + + template + inline bool operator<= (const DynamicStep& s1, + const DynamicStep& s2){ + return s1.value <= s2.value; + } + + + template + static inline + std::ostream & operator<< (std::ostream &o, + const DynamicStep &step){ + o << "(" << step.value; + if(step.relationFlags & DynamicProg::DynamicRelation::TopLeft) + o << ",topleft"; + else if(step.relationFlags & DynamicProg::DynamicRelation::Top) + o << ",top"; + else if(step.relationFlags & DynamicProg::DynamicRelation::Left) + o << ",left"; + o << ")"; + return o; + } + + + template + struct DynamicStepCoordinates { + unsigned int x, y; + Scalar value; + }; //struct DynamicStep + template + static inline + std::ostream & operator<< (std::ostream &o, + const DynamicStepCoordinates &dstep){ + o << "step( " << dstep.x; + o << " , " << dstep.y; + o << " ) = " << dstep.value; + o << ""; + return o; + } + +} // namespace DynamicProg + +#endif // _DYNAMIC_SOLVER_ + diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Dynamic/dynamicSolver.h b/src/plugins_experimental/filter_multiscale_align/lib/Dynamic/dynamicSolver.h new file mode 100644 index 000000000..7cfd0caba --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Dynamic/dynamicSolver.h @@ -0,0 +1,309 @@ +#ifndef _DYNAMIC_SOLVER_ +#define _DYNAMIC_SOLVER_ + +#include "Eigen/Core" +#include + +#include "basics.h" + +#include + + +#ifdef _MSC_VER +#include // for _isnan() on VC++ +#define isnan(x) _isnan(x) // VC++ uses _isnan() instead of isnan() +//#else +//#include // for isnan() everywhere else +#endif + +namespace DynamicProg{ + + /*! + Generic class that delegates the computation to DynProcess. You can use for + instance NeedlemanWunsch. + */ + template + class DynamicSolver{ + public: + typedef Eigen::Matrix, Eigen::Dynamic, Eigen::Dynamic> StepMatrix; + + int xMin,xMax,yMin,yMax; + //std::vector> estScales; + //int xBest,yBest; + std::vector> estScales; + //Scalar best; + //int size; + Scalar confidence; + //Scalar bestMax; + + private: + //! \brief Relation matrix + StepMatrix _matrix; + //! \brief Delegate that perform the dynamic programming processing + DynProcess _delegate; + //! \brief Output path computed by the solver + std::vector< DynamicStepCoordinates > _path; + + public: + template + inline void solve(const DataArray &d1, unsigned int d1Size, + const DataArray &d2, unsigned int d2Size, + double multiplier, int _xMin, int _xMax, int _yMin, int _yMax, + bool normalize = true) { + const unsigned int h = d1Size; + const unsigned int w = d2Size; + + xMin=_xMin; + xMax=_xMax; + yMin=_yMin; + yMax=_yMax; + _matrix = StepMatrix::Zero(w, h); + + // compensate for invalid descriptor values + while(xMax-1 > xMin && isnan(d1[xMax-1].kappa())) xMax--; + while(yMax-1 > yMin && isnan(d2[yMax-1].kappa())) yMax--; + while(xMin < xMax && isnan(d1[xMin].kappa())) xMin++; + while(yMin < yMax && isnan(d2[yMin].kappa())) yMin++; + + // deal with degenerate cases + /* if(xMin >= xMax || yMin >= yMax){ + size = 0; + confidence = 0; + + return; + }*/ + + // Build cost matrix + for (unsigned int j = yMin; j != yMax; j++) + for (unsigned int i = xMin; i != xMax; i++) + _matrix(i,j) = _delegate.eval(d1[i], d2[j], i, j, _matrix, multiplier); + + int maxDiag = std::min((yMax - yMin + 1), (xMax - xMin + 1)); + //std::cout << "MaxDiag: " << maxDiag << endl; + + for (int j = yMin; j != yMax; j++) + { + int size = j - xMax +1; + float scaleEst = 1.0f / std::pow((float)multiplier, (float)size); + //std::cout << "Size: " << size << " j: " << j << " xMax: " << xMax << " Scaleest: " << scaleEst << endl; + _matrix(xMax - 1, j).value /= std::min((float)(j - yMin + 1), (float)maxDiag); + std::pair cand (scaleEst, _matrix(xMax - 1, j).value); + estScales.push_back(cand); + } + + for (int j = xMin; j != xMax -1; j++) + { + float scaleEst = 1.0f / std::pow((float)multiplier, (float)(yMax - j - 1)); + //std::cout << "j: " << j << " yMax: " << yMax << "Scaleest: " << scaleEst << endl; + _matrix(j, yMax - 1).value /= std::min((float)(j - xMin + 1), (float)maxDiag); + std::pair cand(scaleEst, _matrix(j, yMax - 1).value); + estScales.push_back(cand); + } + + std::sort(estScales.begin(), estScales.end(), [](auto &left, auto &right) { + return left.second > right.second; + }); + + //std::cout << "xMin " << xMin << " " << " xMax " << xMax << " yMin " << yMin << " yMax " << yMax << endl; + /*for (int j = 0; j != estScales.size(); j++) + { + std::cout << "Scale: " << estScales[j].first << " Confidence: " << estScales[j].second << endl; + }*/ + + + + // // normalize by the number of scales and get the best value + // best=0.0; + // for (int j=yMin; jbest) + // { + // best=_matrix(i,j).value; + // xBest=i; + // yBest=j; + // } + // } + // } + + //vector maxVal; + //vector minVal; + //for (unsigned int i = 0; i < h; i++) + //{ + //maxVal.push_back(0.0f); + //minVal.push_back(1000.0f); + //} + //for (unsigned int j = yMin; j <= yMax; j++) + //{ + // float maxx = 0.0; float minn = 1000.0; + // for (unsigned int i = xMin; i <= xMax; i++) + // { + // if (_matrix(i, j).value > maxx) + // maxx = _matrix(i, j).value; + // if (_matrix(i, j).value < minn) + // minn = _matrix(i, j).value; + // } + // maxVal[j] = maxx; + // minVal[j] = minn; + //} + //for (unsigned int j = 0; j != h - 1; j++) + //{ + // for (unsigned int i = 0; i != w - 1; i++) + // { + // if (maxVal[j] == 0.0f) + // _matrix(i, j).value = 0; + // else + // _matrix(i, j).value= (_matrix(i, j).value - minVal[j]) / (maxVal[j] - minVal[j]); + // } + // + //} + +#define SAVE_MATRIX_CSV +#ifdef SAVE_MATRIX_CSV + + QFile file2("matrix.xls"); + file2.open(QFile::WriteOnly); + QTextStream stream2(&file2); + for (unsigned int j = 0; j != h - 1; j++) + { + for (unsigned int i = 0; i != w - 1; i++) + { + stream2 << _matrix(i, j).value << "\t"; + } + stream2 << "\n"; + } + file2.close(); +#endif + +// for (int j=yMin; jbest) +// { +// best=_matrix(i,j).value; +// xBest=i; +// yBest=j; +// } + +// } + //size=std::min(Scalar(yBest-yMin)+1,Scalar(xBest-xMin)+1); + ////// + // This is a final normalization of the value, I'm not sure it will be useful... ask me to explain, in the case. I commented + ///// + //std::cout << "size " << size << " " << " best " << best << endl; + /*bestMax=0.0; double dMax=0.0; + for(int i=0; idMax) + dMax=abs(d2[i].kappa()); + } + for(int i=yMin; i<=yMax; i++) + { + if(abs(d2[i].kappa())>bestMax) + bestMax=abs(d2[i].kappa()); + } + confidence=best*bestMax/(size*dMax);*/ + + /*confidence=best/(double)size; + std::cout << "confidence " << confidence << endl;*/ + + + } + + + //! \brief Read-access to the raw relation matrix used by the solver + inline const StepMatrix& stepMatrix() const + {return _matrix; } + + + inline void + setConfidence(Scalar conf){ + _delegate.setConfidence(conf); + } + + ////// This is the solve when only a single scale is needed + template + inline void solve_givenScale(const DataArray &d1, const DataArray &d2, double multiplier, + double scale, int _xMin, int _xMax, int _yMin, int _yMax) { + + xMin=_xMin; + xMax=_xMax; + yMin=_yMin; + yMax=_yMax; + + double logBase=std::log((double)multiplier); + int shift = (int)(std::log(scale) / logBase + 0.5); + //std::cout << "Shift " << shift << std::endl; + //std::cout << "xMax " << xMax << " yMax " << yMax << std::endl; + + std::vector compare; + + //std::cout << "Calc diff" << std::endl; + + for (unsigned int j = yMin; j != yMax; j++) + { + if ((j+shift)>xMin && (j+shift)best) + { + best=compare[j]; + size=j; + } + + } + confidence=best/size;*/ + //std::cout << "End calc best" << std::endl; + /*bestMax=0.0; double dMax=0.0; + for(int i=0; idMax) + dMax=abs(d2[i].kappa()); + } + for(int i=yMin; i<=yMax; i++) + { + if(abs(d2[i].kappa())>bestMax) + bestMax=abs(d2[i].kappa()); + } + confidence=best*bestMax/(size*dMax);*/ + //std::cout << "End calc conf" << std::endl; + + //std::cout << "shift " << shift << " Best " << best << " dMax " << dMax << " BestMax " << dMax << " tMax " << tMax << " Size " << size << " Confidence " << confidence << endl; + } + + ////// This is the solve when only a single scale is needed + + }; //class DynamicSolver + + + +} // namespace DynamicProg + +#endif // _DYNAMIC_SOLVER_ + diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Dynamic/needlemanWunsch.h b/src/plugins_experimental/filter_multiscale_align/lib/Dynamic/needlemanWunsch.h new file mode 100644 index 000000000..f15229e3a --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Dynamic/needlemanWunsch.h @@ -0,0 +1,111 @@ +#ifndef _DYNAMIC_NEEDLEMANWUNSCH_ +#define _DYNAMIC_NEEDLEMANWUNSCH_ + +#include "Eigen/Core" +#include +#include + +#include "basics.h" + +#ifdef _MSC_VER +#include // for _isnan() on VC++ +#define isnan(x) _isnan(x) // VC++ uses _isnan() instead of isnan() +//#else +//#include // for isnan() everywhere else +#endif + +namespace DynamicProg{ + /*! + Global alignment + + cmp: must define an eval function + */ + template class Cmp > + class NeedlemanWunsch{ + private: + Cmp _cmp; + Scalar _gapPenalty; + Scalar _confidence; + + public: + typedef typename Eigen::Matrix, Eigen::Dynamic, Eigen::Dynamic> StepMatrix; + + public: + inline NeedlemanWunsch(): _gapPenalty(-1), _confidence(0) {} + + inline DynamicStep eval (const DataType& v1, + const DataType& v2, + unsigned int x, + unsigned int y, + const StepMatrix& matrix, double multiplier) const; + + inline Scalar eval_couples (const DataType& v1, + const DataType& v2, + unsigned int x, + unsigned int y, + const StepMatrix& matrix, double multiplier) const; + + + inline void setConfidence(double conf); + + inline void setGapPenalty(Scalar gapPenalty) {_gapPenalty = gapPenalty;} + inline Scalar confidence() const { return _confidence;} + + + + }; //class NeedlemanWunsch + + + template class Cmp > + DynamicStep + NeedlemanWunsch::eval( const DataType& v1, + const DataType& v2, + unsigned int x, + unsigned int y, + const StepMatrix& matrix, double multiplier) const{ + + //std::cout << "here" << std::endl; + DynamicStepnei[3]; // will contain top, left and topleft + + + nei[2] = DynamicStep (matrix(x-1, y-1).value + _cmp.eval(v1, v2), + DynamicRelation::TopLeft); + + + return nei[2]; + + + } + + template class Cmp > + Scalar + NeedlemanWunsch::eval_couples( const DataType& v1, + const DataType& v2, + unsigned int x, + unsigned int y, + const StepMatrix& /*matrix*/, double multiplier) const{ + + + + return _cmp.eval(v1, v2); + + + + } + + template class Cmp > + void + NeedlemanWunsch::setConfidence(double conf) + { + + _confidence = conf; + + } + + + +} // namespace DynamicProg + + +#endif // _DYNAMIC_PATH + diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Dynamic/scaleEstimation.h b/src/plugins_experimental/filter_multiscale_align/lib/Dynamic/scaleEstimation.h new file mode 100644 index 000000000..8d59d75aa --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Dynamic/scaleEstimation.h @@ -0,0 +1,117 @@ +#ifndef _SCALE_ESTIMATION_ +#define _SCALE_ESTIMATION_ + +#include "Eigen/Core" +#include +#include + +#include "basics.h" + +#include + +namespace DynamicProg{ + + /*! + Estimate the relative stretch involved in a similarity matrix. + The reference scale is associated to data on the y-axis, and stretch is + given for the data on the x-axis. + */ + template + class VotingStretchEstimation{ + public: + typedef typename Eigen::Matrix, Eigen::Dynamic, Eigen::Dynamic> StepMatrix; + typedef typename std::vector< DynamicStepCoordinates > Path; + + public: + /*! + */ + inline Scalar estimate(int xBest, int yBest, double multiplier)const { + + //if (xBest + class ConvolutionStretchEstimation{ + public: + typedef typename Eigen::Matrix, Eigen::Dynamic, Eigen::Dynamic> StepMatrix; + typedef typename std::vector< DynamicStepCoordinates > Path; + + protected: + int m_offset; // offset + Scalar m_score; // score + Scalar m_multiplier; + + public: + + inline ConvolutionStretchEstimation() + :m_offset(0), m_score(0.), m_multiplier(1.) {} + + /* + * Iterate on the bottom and right border, and record the position with the + * highest score + */ + inline Scalar estimate(const StepMatrix& matrix, Scalar multiplier) { + + const int w = matrix.cols(); + const int h = matrix.rows(); + + m_multiplier = multiplier; + m_offset = 0; + m_score = 0.f; + + for(int s = 0; s != w; s++){ + const Scalar& v = matrix.template bottomRows<1>()(s).value; + if (v > m_score) { + m_offset = s-w+1; + m_score = v; + } + } + + for(int s = 1; s < h; s++){ + const Scalar& v = matrix.template rightCols<1>()(h-s-1).value; + if (v > m_score) { + m_offset = s; + m_score = v; + } + } + + return prevEstimation(); + } + + template + inline + void print (const StepMatrix& matrix, stream& str) const { + + const int w = matrix.cols(); + const int h = matrix.rows(); + + for(int s = 0; s != w; s++) + str << s-w+1 << " " + << matrix.template bottomRows<1>()(s).value << std::endl; + + for(int s = 1; s < h; s++) + str << s << " " + << matrix.template rightCols<1>()(h-s-1).value << std::endl; + } + + //! \brief Read access to the previous estimated scale + inline Scalar prevEstimation() const { return Scalar(1.0)/std::pow(m_multiplier, m_offset); } + //! \brief Read access to the score associated to the previous estimated scale + inline Scalar prevConfidence() const { return m_score; } + + }; + +} // namespace DynamicProg + +#endif // _DYNAMIC_SOLVER_ + diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/CMakeLists.txt b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/CMakeLists.txt new file mode 100644 index 000000000..c7e085c76 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/CMakeLists.txt @@ -0,0 +1,13 @@ +project(Grenaille) +cmake_minimum_required(VERSION 2.8) + + +FILE ( GLOB GRENAILLE_HEADERS + ${CMAKE_CURRENT_SOURCE_DIR}/Core/*.h +) + +FILE ( GLOB GRENAILLE_IMPL + ${CMAKE_CURRENT_SOURCE_DIR}/Core/*.hpp +) + +add_library (Grenaille OBJECT ${GRENAILLE_HEADERS} ${GRENAILLE_IMPL}) diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/algebraicSphere.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/algebraicSphere.h new file mode 100644 index 000000000..346f1f558 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/algebraicSphere.h @@ -0,0 +1,207 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +#ifndef _GRENAILLE_ALGEBRAIC_SPHERE_ +#define _GRENAILLE_ALGEBRAIC_SPHERE_ + +#include "primitive.h" // PrimitiveBase + +namespace Grenaille +{ + +/*! + \brief Algebraic Sphere primitive + + Method published in \cite Guennebaud:2007:APSS + + An algebraic hyper-sphere is defined as the \f$0\f$-isosurface of the scalar field + + \f$ s_\mathbf{u}(\mathbf{x}) = \left[ 1 \; \mathbf{x}^T \; \mathbf{x}^T\mathbf{x}\right]^T \cdot \mathbf{u} \f$ + + with \f$ \mathbf{u} \left[ u_c \; \mathbf{u_l} \; u_q\right]^T \f$ is the + vector of the constant, linear and quadratic parameters. + + \note If internally the scalar fields are stored in a local frame defined + by the evaluation position, the public methods involving a query (such as + project, potential, gradient) have to be defined in global + coordinates (e.g. you don't need to convert your query in the current locale + frame). + + This primitive provides: + \verbatim PROVIDES_ALGEBRAIC_SPHERE \endverbatim + + \todo Deal with planar case (_uq == 0) and what about _ul == 0 ? +*/ +template < class DataPoint, class _WFunctor, typename T = void > +class AlgebraicSphere : public PrimitiveBase +{ +private: + + typedef PrimitiveBase Base; + +protected: + + enum + { + PROVIDES_ALGEBRAIC_SPHERE /*!< \brief Provides Algebraic Sphere */ + }; + +public: + + /*! \brief Scalar type inherited from DataPoint */ + typedef typename DataPoint::Scalar Scalar; + /*! \brief Vector type inherited from DataPoint */ + typedef typename DataPoint::VectorType VectorType; + /*! \brief Weight Function */ + typedef _WFunctor WFunctor; + +private: + + //! \brief Evaluation position (needed for centered basis) + VectorType m_p; + +protected: + + //! \brief Is the implicit scalar field normalized using Pratt + bool m_isNormalized; + +// results +public: + + Scalar m_uc, /*!< \brief Constant parameter of the Algebraic hyper-sphere */ + m_uq; /*!< \brief Quadratic parameter of the Algebraic hyper-sphere */ + VectorType m_ul; /*!< \brief Linear parameter of the Algebraic hyper-sphere */ + +public: + + /*! \brief Default constructor */ + MULTIARCH inline AlgebraicSphere() + : Base() + { + m_p = VectorType::Zero(); + resetPrimitive(); + } + + /*! \brief Set the scalar field values to 0 and reset the isNormalized() status */ + MULTIARCH inline void resetPrimitive() + { + Base::resetPrimitive(); + + m_uc = Scalar(0.0); + m_ul = VectorType::Zero(); + m_uq = Scalar(0.0); + + m_isNormalized = false; + } + + /*! \brief Reading access to the basis center (evaluation position) */ + MULTIARCH inline const VectorType& basisCenter () const { return m_p; } + /*! \brief Writing access to the (evaluation position) */ + MULTIARCH inline VectorType& basisCenter () { return m_p; } + + /*! \brief compute the Pratt norm of the implicit scalar field. */ + MULTIARCH inline Scalar prattNorm() const + { + MULTIARCH_STD_MATH(sqrt); + return sqrt(prattNorm2()); + } + + /*! \brief compute the squared Pratt norm of the implicit scalar field. */ + MULTIARCH inline Scalar prattNorm2() const + { + return m_ul.squaredNorm() - Scalar(4.) * m_uc * m_uq; + } + + /*! + \brief Normalize the scalar field by the Pratt norm + \return false when the normalization fails (sphere is already normalized) + */ + MULTIARCH inline bool applyPrattNorm() + { + if (! m_isNormalized) + { + Scalar pn = prattNorm(); + m_uc /= pn; + m_ul *= Scalar(1.)/pn; + m_uq /= pn; + + m_isNormalized = true; + } + return true; + } + + /*! + \brief return the estimated radius of the sphere + \warning return inf if the fitted surface is planar + */ + MULTIARCH inline Scalar radius() + { + if(isPlane()) + { + //return infinity (non-sense value) +#ifdef __CUDACC__ + Scalar inf = 0.; + return Scalar(1.)/inf; +#else + return std::numeric_limits::infinity(); +#endif + } + + MULTIARCH_STD_MATH(sqrt); + Scalar b = Scalar(1.)/m_uq; + return Scalar(sqrt( ((Scalar(-0.5)*b)*m_ul).squaredNorm() - m_uc*b )); + } + + /*! + \brief return the estimated center of the sphere + \warning return Vector inf if the fitted surface is planar + */ + MULTIARCH inline VectorType center() + { + if(isPlane()) + { + //return infinity (non-sense value) + Scalar inf = 0.; + return VectorType::Constant(Scalar(1.)/inf); + } + + Scalar b = Scalar(1.)/m_uq; + return (Scalar(-0.5)*b)*m_ul + basisCenter(); + } + + //! \brief State indicating when the sphere has been normalized + MULTIARCH inline bool isNormalized() const { return m_isNormalized; } + + //! \brief Value of the scalar field at the location \f$ \mathbf{q} \f$ + MULTIARCH inline Scalar potential (const VectorType& _q) const; + + //! \brief Project a point on the sphere + MULTIARCH inline VectorType project (const VectorType& _q) const; + + //! \brief Approximation of the scalar field gradient at \f$ \mathbf{q} (not normalized) \f$ + MULTIARCH inline VectorType primitiveGradient (const VectorType& _q) const; + + /*! + \brief Used to know if the fitting result to a plane + \return true if finalize() have been called and the fitting result to a plane + */ + MULTIARCH inline bool isPlane() const + { + MULTIARCH_STD_MATH(abs); + Scalar epsilon = Eigen::NumTraits::dummy_precision(); + bool bPlanar = Eigen::internal::isMuchSmallerThan(abs(m_uq), 1., epsilon); + bool bReady = Base::isReady(); + + return bReady && bPlanar; + } + +}; //class AlgebraicSphere + +#include "algebraicSphere.hpp" + +} +#endif // _GRENAILLE_ALGEBRAIC_SPHERE_ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/algebraicSphere.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/algebraicSphere.hpp new file mode 100644 index 000000000..931e7a2c2 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/algebraicSphere.hpp @@ -0,0 +1,62 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +/*! +Use gradient descent +*/ +template < class DataPoint, class _WFunctor, typename T> +typename DataPoint::VectorType +AlgebraicSphere::project( const VectorType& _q ) const +{ + MULTIARCH_STD_MATH(min) + + // turn to centered basis + const VectorType lq = _q-m_p; + + //if(_isPlane) + //{ + VectorType grad; + VectorType dir = m_ul+Scalar(2.)*m_uq*lq; + Scalar ilg = Scalar(1.)/dir.norm(); + dir = dir*ilg; + Scalar ad = m_uc + m_ul.dot(lq) + m_uq * lq.squaredNorm(); + Scalar delta = -ad*min(ilg,Scalar(1.)); + VectorType proj = lq + dir*delta; + + for (int i=0; i<16; ++i) + { + grad = m_ul+Scalar(2.)*m_uq*proj; + ilg = Scalar(1.)/grad.norm(); + delta = -(m_uc + proj.dot(m_ul) + m_uq * proj.squaredNorm())*min(ilg,Scalar(1.)); + proj += dir*delta; + } + return proj + m_p; + //} + //return other - _ul * dot(other,_ul) + _uc; + //return normalize(other-_center) * _r + _center; +} + +template < class DataPoint, class _WFunctor, typename T> +typename DataPoint::Scalar +AlgebraicSphere::potential( const VectorType &_q ) const +{ + // turn to centered basis + const VectorType lq = _q-m_p; + + return m_uc + lq.dot(m_ul) + m_uq * lq.squaredNorm(); +} + + +template < class DataPoint, class _WFunctor, typename T> +typename DataPoint::VectorType +AlgebraicSphere::primitiveGradient( const VectorType &_q ) const +{ + // turn to centered basis + const VectorType lq = _q-m_p; + return (m_ul + Scalar(2.f) * m_uq * lq); +} + diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/basket.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/basket.h new file mode 100644 index 000000000..08e41676d --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/basket.h @@ -0,0 +1,76 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +#ifndef _GRENAILLE_BASKET_ +#define _GRENAILLE_BASKET_ + + +namespace Grenaille +{ + +/*! \brief Namespace used for structure or classes used internally by the lib */ +namespace internal +{ + /*! \brief Internal class used to build the Basket structure */ + template class Forward: public T {}; +} + + +#define BASKET_TP(I) template class Ext##I = internal::Forward +/*! + + \brief Agregator class used to declare specialized structures using CRTP (Curiously Recurring Template Pattern) + \todo Comment + + The various implementations of Grenaille::Concept are mixed through + specializations of the Basket class: + \code + typedef + Basket myFit; // Final structure to fit and extend a primitive over weighted samples + \endcode + +*/ + template < class P, class W, template class Fit, + BASKET_TP(0), BASKET_TP(1), BASKET_TP(2), BASKET_TP(3), BASKET_TP(4), BASKET_TP(5), BASKET_TP(6), BASKET_TP(7), BASKET_TP(8), BASKET_TP(9), BASKET_TP(10), BASKET_TP(11) > + class Basket + : public Ext11 > > > > > > > > > > > > + { + public: + /*! + * \brief Convenience function for STL-like iterators + * + * Add neighbors stored in a container using STL-like iterators, and + * call finalize at the end. + * \note Multi-pass fitting is supported by this function as an experimental feature. It thus should be used carefully. + */ + template + MULTIARCH inline + FIT_RESULT compute(const Iterator& begin, const Iterator& end){ + FIT_RESULT res = UNDEFINED; + do { + for (Iterator it = begin; it != end; ++it){ + this->addNeighbor(*it); + } + res = this->finalize(); + } while ( res == NEED_OTHER_PASS ); + return res; + } + }; // class Basket + +#undef BASKET_TP + +}// namespace Grenaille + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/covariancePlaneFit.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/covariancePlaneFit.h new file mode 100644 index 000000000..e64b16e7e --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/covariancePlaneFit.h @@ -0,0 +1,285 @@ +/* + Copyright (C) 2014 Nicolas Mellado + Copyright (C) 2015 Gael Guennebaud + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +#ifndef _GRENAILLE_COVARIANCE_PLANE_FIT_ +#define _GRENAILLE_COVARIANCE_PLANE_FIT_ + +#include +#include "enums.h" + +namespace Grenaille +{ + +/*! + \brief Plane fitting procedure using only points position + + \note This procedure requires two passes to fit a plane + + This class can also computes the surface variation measure introduced in + \cite Pauly:2002:PSSimplification. The solver used to analyse the covariance + matrix is stored for further use. + + \inherit Concept::FittingProcedureConcept + + \warning This class is currently untested and should not be used ! + + \see CompactPlane +*/ +template < class DataPoint, class _WFunctor, typename T > +class CovariancePlaneFit : public T +{ +private: + typedef T Base; + +protected: + enum + { + Check = Base::PROVIDES_PLANE + }; + +public: + + /*! \brief Scalar type inherited from DataPoint*/ + typedef typename Base::Scalar Scalar; + /*! \brief Vector type inherited from DataPoint*/ + typedef typename Base::VectorType VectorType; + /*! \brief Vector type inherited from DataPoint*/ + typedef typename Base::MatrixType MatrixType; + /*! \brief Weight Function*/ + typedef _WFunctor WFunctor; + /*! \brief Solver used to analyse the covariance matrix*/ + typedef Eigen::SelfAdjointEigenSolver Solver; + + protected: + + // computation data + Scalar m_sumW; /*!< \brief Sum of queries weight.*/ + VectorType m_cog, /*!< \brief Gravity center of the neighborhood */ + m_evalPos; /*!< \brief Center of the evaluation basis */ + MatrixType m_cov; /*!< \brief Covariance matrix */ + + Solver m_solver; /*!<\brief Solver used to analyse the covariance matrix */ + + WFunctor m_w; /*!< \brief Weight function (must inherits BaseWeightFunc) */ + +public: + + /*! \brief Default constructor */ + MULTIARCH inline CovariancePlaneFit() : Base() {} + + /**************************************************************************/ + /* Initialization */ + /**************************************************************************/ + /*! \copydoc Concept::FittingProcedureConcept::setWeightFunc() */ + MULTIARCH inline void setWeightFunc (const WFunctor& _w) { m_w = _w; } + + /*! \copydoc Concept::FittingProcedureConcept::init() */ + MULTIARCH inline void init (const VectorType& _evalPos); + + /**************************************************************************/ + /* Processing */ + /**************************************************************************/ + /*! \copydoc Concept::FittingProcedureConcept::addNeighbor() */ + MULTIARCH inline bool addNeighbor(const DataPoint &_nei); + + /*! \copydoc Concept::FittingProcedureConcept::finalize() */ + MULTIARCH inline FIT_RESULT finalize(); + + /**************************************************************************/ + /* Results */ + /**************************************************************************/ + + using Base::potential; + + /*! \brief Value of the scalar field at the evaluation point */ + MULTIARCH inline Scalar potential() const { return Base::potential(m_evalPos); } + + /*! \brief Value of the normal of the primitive at the evaluation point */ + MULTIARCH inline VectorType normal() const { return Base::m_p.template head(); } + + /*! \brief Reading access to the Solver used to analyse the covariance + matrix */ + MULTIARCH inline const Solver& solver() const { return m_solver; } + + /*! \brief Implements \cite Pauly:2002:PSSimplification surface variation. + + It computes the ratio \f$ d \frac{\lambda_0}{\sum_i \lambda_i} \f$ with \c d the dimension of the ambient space. + + \return 0 for invalid fits + */ + MULTIARCH inline Scalar surfaceVariation() const; +}; //class CovariancePlaneFit + +namespace internal { + +using ::Grenaille::internal::FitSpaceDer; +using ::Grenaille::internal::FitScaleDer; + +/*! + \brief Internal generic class computing the derivatives of covariance plane fits + \inherit Concept::FittingExtensionConcept + + The differentiation can be done automatically in scale and/or space, by + combining the enum values FitScaleDer and FitSpaceDer in the template + parameter Type. + + The differenciated values are stored in static arrays. The size of the + arrays is computed with respect to the derivation type (scale and/or space) + and the number of the dimension of the ambiant space. + By convention, the scale derivatives are stored at index 0 when Type + contains at least FitScaleDer. The size of these arrays can be known using + derDimension(), and the differentiation type by isScaleDer() and + isSpaceDer(). +*/ +template < class DataPoint, class _WFunctor, typename T, int Type> +class CovariancePlaneDer : public T +{ +private: + typedef T Base; /*!< \brief Generic base type */ + + +protected: + enum + { + Check = Base::PROVIDES_PLANE, /*!< \brief Needs plane */ + PROVIDES_COVARIANCE_PLANE_DERIVATIVE, /*!< \brief Provides derivatives for hyper-planes */ + PROVIDES_NORMAL_DERIVATIVE + }; + + static const int NbDerivatives = ((Type & FitScaleDer) ? 1 : 0 ) + ((Type & FitSpaceDer) ? DataPoint::Dim : 0); + static const int DerStorageOrder = (Type & FitSpaceDer) ? Eigen::RowMajor : Eigen::ColMajor; + +public: + typedef typename Base::Scalar Scalar; /*!< \brief Inherited scalar type*/ + typedef typename Base::VectorType VectorType; /*!< \brief Inherited vector type*/ + typedef typename Base::MatrixType MatrixType; /*!< \brief Inherited matrix type*/ + typedef typename Base::WFunctor WFunctor; /*!< \brief Weight Function*/ + + /*! \brief Static array of scalars with a size adapted to the differentiation type */ + typedef Eigen::Matrix VectorArray; + + /*! \brief Static array of scalars with a size adapted to the differentiation type */ + typedef Eigen::Matrix ScalarArray; +private: + // computation data + ScalarArray m_dSumW; /*!< \brief Sum of weight derivatives */ + MatrixType m_dCov[NbDerivatives]; + + VectorArray m_dCog; /*!< \brief Derivatives of the centroid */ + VectorArray m_dNormal; /*!< \brief Derivatives of the hyper-plane normal */ + ScalarArray m_dDist; /*!< \brief Derivatives of the MLS scalar field */ + +public: + + /************************************************************************/ + /* Initialization */ + /************************************************************************/ + /*! \see Concept::FittingProcedureConcept::init() */ + MULTIARCH void init(const VectorType &evalPos); + + /************************************************************************/ + /* Processing */ + /************************************************************************/ + /*! \see Concept::FittingProcedureConcept::addNeighbor() */ + MULTIARCH bool addNeighbor(const DataPoint &nei); + /*! \see Concept::FittingProcedureConcept::finalize() */ + MULTIARCH FIT_RESULT finalize(); + + + /**************************************************************************/ + /* Use results */ + /**************************************************************************/ + + /*! \brief Returns the derivatives of the scalar field at the evaluation point */ + MULTIARCH inline ScalarArray dPotential() const { return m_dDist; } + + /*! \brief Returns the derivatives of the primitive normal */ + MULTIARCH inline VectorArray dNormal() const { return m_dNormal; } + + /*! \brief State specified at compilation time to differenciate the fit in scale */ + MULTIARCH inline bool isScaleDer() const {return bool(Type & FitScaleDer);} + /*! \brief State specified at compilation time to differenciate the fit in space */ + MULTIARCH inline bool isSpaceDer() const {return bool(Type & FitSpaceDer);} + /*! \brief Number of dimensions used for the differentiation */ + MULTIARCH inline unsigned int derDimension() const { return NbDerivatives;} + +}; //class CovariancePlaneDer + +}// namespace internal + +/*! + \brief Differentiation in scale of the CovariancePlaneFit + \inherit Concept::FittingExtensionConcept + + Requierement: + \verbatim PROVIDES_COVARIANCE_PLANE \endverbatim + Provide: + \verbatim PROVIDES_COVARIANCE_PLANE_SCALE_DERIVATIVE \endverbatim +*/ +template < class DataPoint, class _WFunctor, typename T> +class CovariancePlaneScaleDer:public internal::CovariancePlaneDer +{ +protected: + /*! \brief Inherited class */ + typedef internal::CovariancePlaneDer Base; + enum { PROVIDES_COVARIANCE_PLANE_SCALE_DERIVATIVE, PROVIDES_NORMAL_SCALE_DERIVATIVE }; +}; + + +/*! + \brief Spatial differentiation of the CovariancePlaneFit + \inherit Concept::FittingExtensionConcept + + Requierement: + \verbatim PROVIDES_COVARIANCE_PLANE \endverbatim + Provide: + \verbatim PROVIDES_COVARIANCE_PLANE_SPACE_DERIVATIVE \endverbatim +*/ +template < class DataPoint, class _WFunctor, typename T> +class CovariancePlaneSpaceDer:public internal::CovariancePlaneDer +{ +protected: + /*! \brief Inherited class */ + typedef internal::CovariancePlaneDer Base; + enum { PROVIDES_COVARIANCE_PLANE_SPACE_DERIVATIVE, PROVIDES_NORMAL_SPACE_DERIVATIVE }; +}; + + +/*! + \brief Differentiation both in scale and space of the CovariancePlaneFit + \inherit Concept::FittingExtensionConcept + + Requierement: + \verbatim PROVIDES_COVARIANCE_PLANE \endverbatim + Provide: + \verbatim PROVIDES_COVARIANCE_PLANE_SCALE_DERIVATIVE + PROVIDES_COVARIANCE_PLANE_SPACE_DERIVATIVE + \endverbatim +*/ +template < class DataPoint, class _WFunctor, typename T> +class CovariancePlaneScaleSpaceDer:public internal::CovariancePlaneDer +{ +protected: + /*! \brief Inherited class */ + typedef internal::CovariancePlaneDer Base; + enum + { + PROVIDES_COVARIANCE_PLANE_SCALE_DERIVATIVE, + PROVIDES_COVARIANCE_PLANE_SPACE_DERIVATIVE, + PROVIDES_NORMAL_SCALE_DERIVATIVE, + PROVIDES_NORMAL_SPACE_DERIVATIVE + }; +}; + +#include "covariancePlaneFit.hpp" + +} //namespace Grenaille + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/covariancePlaneFit.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/covariancePlaneFit.hpp new file mode 100644 index 000000000..be991982a --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/covariancePlaneFit.hpp @@ -0,0 +1,201 @@ +/* + Copyright (C) 2014 Nicolas Mellado + Copyright (C) 2015 Gael Guennebaud + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +template < class DataPoint, class _WFunctor, typename T> +void +CovariancePlaneFit::init(const VectorType& _evalPos) +{ + // Setup primitive + Base::resetPrimitive(); + // Base::basisCenter() = _evalPos; + + // Setup fitting internal values + m_sumW = Scalar(0.0); + m_evalPos = _evalPos; + m_cog = VectorType::Zero(); + m_cov = MatrixType::Zero(); +} + +template < class DataPoint, class _WFunctor, typename T> +bool +CovariancePlaneFit::addNeighbor(const DataPoint& _nei) +{ + VectorType q = _nei.pos() - m_evalPos; + // compute weight + Scalar w = m_w.w(q, _nei); + + if (w > Scalar(0.)) + { + m_cog += w * q; + m_sumW += w; + m_cov += w * q * q.transpose(); + + ++(Base::m_nbNeighbors); + return true; + } + return false; +} + + +template < class DataPoint, class _WFunctor, typename T> +FIT_RESULT +CovariancePlaneFit::finalize () +{ + Scalar epsilon = Eigen::NumTraits::dummy_precision(); + + // handle specific configurations + // With less than 3 neighbors the fitting is undefined + if(m_sumW == Scalar(0.) || Base::m_nbNeighbors < 3) + { + Base::resetPrimitive(); + Base::m_eCurrentState = UNDEFINED; + return Base::m_eCurrentState; + } + + // Center the covariance on the centroid: + m_cov -= m_cog * m_cog.transpose() / m_sumW; + // Finalize the centroid + m_cog = m_cog/m_sumW + m_evalPos; + +#ifdef __CUDACC__ + m_solver.computeDirect(m_cov); +#else + m_solver.compute(m_cov); +#endif + + Base::setPlane(m_solver.eigenvectors().col(0), m_cog); + + // \todo Use the output of the solver to check stability + Base::m_eCurrentState = STABLE; + return Base::m_eCurrentState; +} + + +template < class DataPoint, class _WFunctor, typename T> +typename CovariancePlaneFit::Scalar +CovariancePlaneFit::surfaceVariation () const +{ + if( Base::m_eCurrentState == UNDEFINED ) + return 0; + + return m_solver.eigenvalues()(0) / m_solver.eigenvalues().mean(); +} + + +namespace internal +{ + +template < class DataPoint, class _WFunctor, typename T, int Type> +void +CovariancePlaneDer::init(const VectorType& _evalPos) +{ + Base::init(_evalPos); + + m_dCog = VectorArray::Zero(); + m_dSumW = ScalarArray::Zero(); + for(int k=0; k +bool +CovariancePlaneDer::addNeighbor(const DataPoint &_nei) +{ + bool bResult = Base::addNeighbor(_nei); + + if(bResult) + { + int spaceId = (Type & FitScaleDer) ? 1 : 0; + + ScalarArray dw; + + // centered basis + VectorType q = _nei.pos()-Base::m_evalPos; + + // compute weight + if (Type & FitScaleDer) + dw[0] = Base::m_w.scaledw(q, _nei); + + if (Type & FitSpaceDer) + dw.template segment(spaceId) = -Base::m_w.spacedw(q, _nei).transpose(); + + m_dSumW += dw; + m_dCog += q * dw; + for(int k=0; k +FIT_RESULT +CovariancePlaneDer::finalize() +{ + MULTIARCH_STD_MATH(sqrt); + + Base::finalize(); + // Test if base finalize end on a viable case (stable / unstable) + if (this->isReady()) + { + // pre-compute shifted eigenvalues to apply the pseudo inverse of C - lambda_0 I + Scalar epsilon = Scalar(2) * Eigen::NumTraits::epsilon(); + Scalar consider_as_zero = Scalar(2) * std::numeric_limits::denorm_min(); + Eigen::Matrix shifted_eivals = Base::m_solver.eigenvalues().template tail<2>().array() - Base::m_solver.eigenvalues()(0); + if(shifted_eivals(0) < consider_as_zero || shifted_eivals(0) < epsilon * shifted_eivals(1)) shifted_eivals(0) = 0; + if(shifted_eivals(1) < consider_as_zero) shifted_eivals(1) = 0; + + for(int k=0; k z = - Base::m_solver.eigenvectors().template rightCols<2>().transpose() * (m_dCov[k] * normal); + if(shifted_eivals(0)>0) z(0) /= shifted_eivals(0); + if(shifted_eivals(1)>0) z(1) /= shifted_eivals(1); + m_dNormal.col(k) = Base::m_solver.eigenvectors().template rightCols<2>() * z; + + VectorType dDiff = -m_dCog.col(k); + if(k>0 || !isScaleDer()) + dDiff(isScaleDer() ? k-1 : k) += 1; + m_dDist(k) = m_dNormal.col(k).dot(Base::m_evalPos-Base::m_cog) + normal.dot(dDiff); + } + } + + return Base::m_eCurrentState; +} + +}// namespace internal diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/curvature.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/curvature.h new file mode 100644 index 000000000..c6347e7ed --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/curvature.h @@ -0,0 +1,88 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include + +#ifndef _GRENAILLE_CURVATURE_ +#define _GRENAILLE_CURVATURE_ + +namespace Grenaille +{ + + +/*! + \brief Extension to compute curvature values from the Weingarten map \f$ \frac{d N}{d \mathbf{x}} \f$ + \inherit Concept::FittingExtensionConcept + + This class extracts curvature informations from the spatial derivatives of the normal field \f$ N \f$. + It first assemble a 2x2 matrix representation of the shape operator, and then performs an eigenvalue decomposition + using Eigen::SelfAdjointEigenSolver::computeDirect. + + The previous basket elements must provide a \c dNormal() method returning a 3x3 matrix. + If more than one basket element provide a \c dNormal() member, then the last one will be used. + + \warning This class is valid only in 3D. + \todo Add a compile time check for the working dimension +*/ +template < class DataPoint, class _WFunctor, typename T> +class CurvatureEstimator : public T +{ +private: + typedef T Base; + +protected: + enum + { + Check = Base::PROVIDES_NORMAL_SPACE_DERIVATIVE, + PROVIDES_PRINCIPALE_CURVATURES + }; + +public: + typedef typename Base::Scalar Scalar; /*!< \brief Inherited scalar type*/ + typedef typename Base::VectorType VectorType; /*!< \brief Inherited vector type*/ + typedef typename DataPoint::MatrixType MatrixType; /*!< \brief Matrix type inherited from DataPoint*/ + +private: + Scalar m_k1, m_k2; + VectorType m_v1, m_v2; + +public: + /*! \brief Default constructor */ + MULTIARCH inline CurvatureEstimator() : m_k1(0), m_k2(0) {} + + /**************************************************************************/ + /* Processing */ + /**************************************************************************/ + /*! \copydoc Concept::FittingProcedureConcept::finalize() */ + MULTIARCH inline FIT_RESULT finalize(); + + /**************************************************************************/ + /* Use results */ + /**************************************************************************/ + //! \brief Returns an estimate of the first principal curvature value + MULTIARCH inline Scalar k1() const { return m_k1; } + + //! \brief Returns an estimate of the second principal curvature value + MULTIARCH inline Scalar k2() const { return m_k2; } + + //! \brief Returns an estimate of the first principal curvature direction + MULTIARCH inline VectorType k1Direction() const { return m_v1; } + + //! \brief Returns an estimate of the second principal curvature direction + MULTIARCH inline VectorType k2Direction() const { return m_v2; } + + //! \brief Returns an estimate of the mean curvature + MULTIARCH inline Scalar kMean() const { return (m_k1 + m_k2)/2.;} + + //! \brief Returns an estimate of the Gaussian curvature + MULTIARCH inline Scalar GaussianCurvature() const { return m_k1 * m_k2;} +}; + +#include "curvature.hpp" + +} //namespace Grenaille + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/curvature.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/curvature.hpp new file mode 100644 index 000000000..862fac1b8 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/curvature.hpp @@ -0,0 +1,87 @@ +/* + Copyright (C) 2015 Gael Guennebaud + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + + +template < class DataPoint, class _WFunctor, typename T> +FIT_RESULT +CurvatureEstimator::finalize() +{ + typedef typename VectorType::Index Index; + typedef Eigen::Matrix Mat32; + typedef Eigen::Matrix Mat22; + + MULTIARCH_STD_MATH(sqrt); + MULTIARCH_STD_MATH(abs); + + FIT_RESULT bResult = Base::finalize(); + + if(bResult != UNDEFINED) + { + // Get the object space Weingarten map dN + MatrixType dN = Base::dNormal().template middleCols(Base::isScaleDer() ? 1: 0); + + // Make sure dN is orthogonal to the normal: (optional, does not seem to improve accuracy) +// VectorType n = Base::normal().normalized(); +// dN = dN - n * n.transpose() * dN; + + // Make sure that dN is symmetric: + // FIXME check why dN is not already symmetric (i.e., round-off errors or error in derivative formulas?) + dN = 0.5*(dN + dN.transpose().eval()); + + // Compute tangent-space basis from dN + // 1 - pick the column with maximal norm as the first tangent vector, + Index i0, i1, i2; + Scalar sqNorm = dN.colwise().squaredNorm().maxCoeff(&i0); + Mat32 B; + B.col(0) = dN.col(i0) / sqrt(sqNorm); + // 2 - orthogonalize the other column vectors, and pick the most reliable one + i1 = (i0+1)%3; + i2 = (i0+2)%3; + VectorType v1 = dN.col(i1) - B.col(0).dot(dN.col(i1)) * B.col(0); + VectorType v2 = dN.col(i2) - B.col(0).dot(dN.col(i2)) * B.col(0); + Scalar v1norm2 = v1.squaredNorm(); + Scalar v2norm2 = v2.squaredNorm(); + if(v1norm2 > v2norm2) B.col(1) = v1 / sqrt(v1norm2); + else B.col(1) = v2 / sqrt(v2norm2); + + // Compute the 2x2 matrix representing the shape operator by transforming dN to the basis B. + // Recall that dN is a bilinear form, it thus transforms as follows: + Mat22 S = B.transpose() * dN * B; + + Eigen::SelfAdjointEigenSolver eig2; + eig2.computeDirect(S); + + if (eig2.info() != Eigen::Success){ + return UNDEFINED; + } + + m_k1 = eig2.eigenvalues()(0); + m_k2 = eig2.eigenvalues()(1); + + m_v1 = B * eig2.eigenvectors().col(0); + m_v2 = B * eig2.eigenvectors().col(1); + + if(abs(m_k1) + +#ifndef _GRENAILLE_GLS_ +#define _GRENAILLE_GLS_ + +namespace Grenaille +{ + +/*! + \brief Growing Least Squares reparemetrization of the OrientedSphereFit + \inherit Concept::FittingExtensionConcept + + Method published in \cite Mellado:2012:GLS + + This class assumes that the WeightFunc defines the accessor + \code + w.evalScale(); + \endcode + in order to access to the evaluation scale, needed to compute the + scale invariant GLS reparametrization (all *_normalized methods). + + Computed values: + - tau(), eta() and kappa(): the GLS descriptor + \f$ \left[ \tau \; \eta \; \kappa \right]\f$ + - tau_normalized(), eta_normalized() and kappa_normalized(): + the scale invariant GLS descriptor + \f$ \left[ \frac{\tau}{t} \; \eta \; t\kappa \right]\f$ + + Requierement: + \verbatim PROVIDES_ALGEBRAIC_SPHERE \endverbatim + Provide: + \verbatim PROVIDES_GLS_PARAMETRIZATION \endverbatim +*/ +template < class DataPoint, class _WFunctor, typename T> +class GLSParam : public T +{ +private: + typedef T Base; + +protected: + enum + { + Check = Base::PROVIDES_ALGEBRAIC_SPHERE, + PROVIDES_GLS_PARAMETRIZATION + }; + +public: + typedef typename Base::Scalar Scalar; /*!< \brief Inherited scalar type*/ + typedef typename Base::VectorType VectorType; /*!< \brief Inherited vector type*/ + typedef typename Base::WFunctor WFunctor; /*!< \brief Weight Function*/ + + +protected: + Scalar m_t; /*!< \brief Evaluation scale. Needed to computed the normalized values*/ + Scalar m_fitness; /*!< \brief Save the fitness value to avoid side effect with Pratt normalization*/ + +public: + /*! \brief Default constructor */ + MULTIARCH inline GLSParam() : m_t(0) {} + + /**************************************************************************/ + /* Initialization */ + /**************************************************************************/ + /*! \copydoc Concept::FittingProcedureConcept::setWeightFunc() */ + MULTIARCH inline void setWeightFunc(const WFunctor& _w) + { + Base::setWeightFunc(_w); + m_t = _w.evalScale(); + } + + /**************************************************************************/ + /* Processing */ + /**************************************************************************/ + /*! \copydoc Concept::FittingProcedureConcept::finalize() */ + MULTIARCH inline FIT_RESULT finalize() + { + FIT_RESULT bResult = Base::finalize(); + + if(bResult != UNDEFINED) + { + m_fitness = Scalar(1.) - Base::prattNorm2(); + } + + return bResult; + } + + /**************************************************************************/ + /* Use results */ + /**************************************************************************/ + /*! \brief Compute and return \f$ \tau \f$ */ + MULTIARCH inline Scalar tau() const + { + return Base::isNormalized() ? Base::m_uc : Base::m_uc / Base::prattNorm(); + } + + /*! \brief Compute and return \f$ \eta \f$ */ + MULTIARCH inline VectorType eta() const { return Base::normal(); } + + /*! \brief Compute and return \f$ \kappa \f$ */ + MULTIARCH inline Scalar kappa() const + { + return Scalar(2.) * (Base::isNormalized() ? Base::m_uq : Base::m_uq / Base::prattNorm()); + } + + /*! \brief Compute and return \f$ \frac{\tau}{t} \f$ */ + MULTIARCH inline Scalar tau_normalized() const { return tau() / m_t; } + + /*! \brief Compute and return \f$ \eta \f$ */ + MULTIARCH inline VectorType eta_normalized() const { return eta(); } + + /*! \brief Compute and return \f$ t \kappa \f$ */ + MULTIARCH inline Scalar kappa_normalized() const { return kappa() * m_t; } + + /*! \brief Return the fitness, e.g. the pratt norm of the initial scalar field */ + MULTIARCH inline Scalar fitness() const { return m_fitness; } + + /*! + \brief Compare current instance with other. + \return a distance between two fits (0 correspond to two similar fits) + \warning Use the same scale to have a useful comparison (normalized value are used) + */ + MULTIARCH inline Scalar compareTo (const GLSParam& _other, + bool _useFitness = true) const + { + Scalar nTau = this->tau_normalized() - _other.tau_normalized(); + Scalar nKappa = this->kappa_normalized() - _other.kappa_normalized(); + Scalar nFitness = _useFitness ? this->fitness() - _other.fitness() : Scalar(0.); + + return nTau * nTau + nKappa * nKappa + nFitness * nFitness; + } + +}; //class GLSParam + + +/*! + \brief Differentiation of GLSParam + \inherit Concept::FittingExtensionConcept + + Method published in \cite Mellado:2012:GLS +*/ +template < class DataPoint, class _WFunctor, typename T> +class GLSDer : public T +{ +private: + typedef T Base; + +protected: + enum + { + Check = Base::PROVIDES_GLS_PARAMETRIZATION, + PROVIDES_GLS_DERIVATIVE + }; + +public: + typedef typename Base::Scalar Scalar; /*!< \brief Inherited scalar type */ + typedef typename Base::VectorType VectorType; /*!< \brief Inherited vector type */ + typedef typename Base::WFunctor WFunctor; /*!< \brief Weight Function */ + + typedef typename Base::VectorArray VectorArray; /*!< \brief Inherited vector array type */ + typedef typename Base::ScalarArray ScalarArray; /*!< \brief Inherited scalar array type */ + + MULTIARCH inline ScalarArray dtau() const; /*!< \brief Compute and return \f$ \tau \f$ derivatives */ + MULTIARCH inline VectorArray deta() const; /*!< \brief Compute and return \f$ \eta \f$ derivatives */ + MULTIARCH inline ScalarArray dkappa() const; /*!< \brief Compute and return \f$ \kappa \f$ derivatives */ + + MULTIARCH inline ScalarArray dtau_normalized() const; /*!< \brief Compute and return \f$ \tau \f$ derivatives */ + MULTIARCH inline VectorArray deta_normalized() const; /*!< \brief Compute and return \f$ t * d\eta \f$ */ + MULTIARCH inline ScalarArray dkappa_normalized() const; /*!< \brief Compute and return \f$ d\kappa * t^{2} \f$ */ +}; //class GLSScaleDer + + +/*! + \brief Extension to compute the Geometric Variation of GLSParam + \inherit Concept::FittingExtensionConcept + + The Geometric Variation is computed as the weighted sum of the + GLS scale-invariant partial derivatives + \f[ + \nu(\mathbf{p},t) = + w_\tau \left(\frac{\delta\tau}{\delta t}\right)^2 + + w_\eta \left( t \frac{\delta\eta}{\delta t}\right)^2 + + w_\kappa \left( t^2 \frac{\delta\kappa}{\delta t}\right)^2 + \f] + + Method published in \cite Mellado:2012:GLS + \todo Add more details +*/ +template < class DataPoint, class _WFunctor, typename T> +class GLSGeomVar : public T +{ +private: + typedef T Base; + +protected: + enum + { + Check = Base::PROVIDES_ALGEBRAIC_SPHERE_SCALE_DERIVATIVE & Base::PROVIDES_GLS_DERIVATIVE, + PROVIDES_GLS_GEOM_VAR + }; + +public: + typedef typename Base::Scalar Scalar; /*!< \brief Inherited scalar type*/ + + /*! + \brief Compute and return the Geometric Variation + */ + MULTIARCH inline Scalar geomVar(Scalar wtau = Scalar(1), + Scalar weta = Scalar(1), + Scalar wkappa = Scalar(1)) const; +}; + + +/*! \deprecated See class CurvatureEstimator */ +template < class DataPoint, class _WFunctor, typename T> +class GLSCurvatureHelper : public CurvatureEstimator +{ + typedef CurvatureEstimator Base; +public: + + typedef typename Base::Scalar Scalar; + typedef typename Base::VectorType VectorType; + + /*! \deprecated */ + MULTIARCH inline Scalar GLSk1() const { return Base::k1(); } + + /*! \deprecated */ + MULTIARCH inline Scalar GLSk2() const { return Base::k2(); } + + /*! \deprecated */ + MULTIARCH inline VectorType GLSk1Direction() const { return Base::k1Direction(); } + + /*! \deprecated */ + MULTIARCH inline VectorType GLSk2Direction() const { return Base::k2Direction(); } + + /*! \deprecated */ + MULTIARCH inline Scalar GLSGaussianCurvature() const { return Base::GaussianCurvature(); } + +}; + +#include "gls.hpp" + +} //namespace Grenaille + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/gls.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/gls.hpp new file mode 100644 index 000000000..c871b6149 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/gls.hpp @@ -0,0 +1,86 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +template < class DataPoint, class _WFunctor, typename T> +typename GLSDer ::ScalarArray + GLSDer ::dtau() const +{ + MULTIARCH_STD_MATH(sqrt); + + Scalar prattNorm2 = Base::prattNorm2(); + Scalar prattNorm = sqrt(prattNorm2); + Scalar cfactor = Scalar(.5) / prattNorm; + ScalarArray dfield = Base::m_dUc; + // Recall that tau is the field function at the evaluation point, we thus must take care about + // its variation when differentiating in space: + if(this->isScaleDer()) + dfield.template tail() += Base::m_ul; + + return (dfield * prattNorm - Base::m_uc * cfactor * Base::dprattNorm2()) / prattNorm2; +} + + +template < class DataPoint, class _WFunctor, typename T> +typename GLSDer ::VectorArray +GLSDer ::deta() const +{ + return Base::dNormal(); +} + + +template < class DataPoint, class _WFunctor, typename T> +typename GLSDer ::ScalarArray +GLSDer ::dkappa() const +{ + MULTIARCH_STD_MATH(sqrt); + + Scalar prattNorm2 = Base::prattNorm2(); + Scalar prattNorm = sqrt(prattNorm2); + Scalar cfactor = Scalar(.5) / prattNorm; + + return Scalar(2.) * (Base::m_dUq * prattNorm - Base::m_uq * cfactor * Base::dprattNorm2()) / prattNorm2; +} + + +template < class DataPoint, class _WFunctor, typename T> +typename GLSDer ::ScalarArray +GLSDer ::dtau_normalized() const +{ + return dtau(); +} + + +template < class DataPoint, class _WFunctor, typename T> +typename GLSDer ::VectorArray +GLSDer ::deta_normalized() const +{ + return Base::m_t * deta(); +} + + +template < class DataPoint, class _WFunctor, typename T> +typename GLSDer ::ScalarArray +GLSDer ::dkappa_normalized() const +{ + return dkappa() * Base::m_t * Base::m_t; +} + + + + +template < class DataPoint, class _WFunctor, typename T> +typename GLSGeomVar ::Scalar +GLSGeomVar ::geomVar( Scalar wtau, + Scalar weta, + Scalar wkappa ) const +{ + Scalar dtau = Base::dtau_normalized().col(0)(0); + Scalar deta = Base::deta_normalized().col(0).norm(); + Scalar dkappa = Base::dkappa_normalized().col(0)(0); + + return wtau*dtau*dtau + weta*deta*deta + wkappa*dkappa*dkappa; +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/orientedSphereFit.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/orientedSphereFit.h new file mode 100644 index 000000000..1a5fdbf7e --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/orientedSphereFit.h @@ -0,0 +1,311 @@ +/* +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +#ifndef _GRENAILLE_ORIENTED_SPHERE_FIT_ +#define _GRENAILLE_ORIENTED_SPHERE_FIT_ + +#include "algebraicSphere.h" + +namespace Grenaille +{ + +/*! + \brief Algebraic Sphere fitting procedure on oriented point sets + + Method published in \cite Guennebaud:2007:APSS. + + \inherit Concept::FittingProcedureConcept + + \see AlgebraicSphere +*/ +template < class DataPoint, class _WFunctor, typename T = void > +class OrientedSphereFit : public AlgebraicSphere +{ +private: + + typedef AlgebraicSphere Base; + +public: + + /*! \brief Scalar type inherited from DataPoint*/ + typedef typename Base::Scalar Scalar; + /*! \brief Vector type inherited from DataPoint*/ + typedef typename Base::VectorType VectorType; + /*! \brief Weight Function*/ + typedef _WFunctor WFunctor; + + protected: + + // computation data + VectorType m_sumN, /*!< \brief Sum of the normal vectors */ + m_sumP; /*!< \brief Sum of the relative positions */ + Scalar m_sumDotPN, /*!< \brief Sum of the dot product betwen relative positions and normals */ + m_sumDotPP, /*!< \brief Sum of the squared relative positions */ + m_sumW; /*!< \brief Sum of queries weight */ + + WFunctor m_w; /*!< \brief Weight function (must inherits BaseWeightFunc) */ + +public: + + /*! \brief Default constructor */ + MULTIARCH inline OrientedSphereFit() + : Base(){} + + /**************************************************************************/ + /* Initialization */ + /**************************************************************************/ + /*! \copydoc Concept::FittingProcedureConcept::setWeightFunc() */ + MULTIARCH inline void setWeightFunc (const WFunctor& _w) { m_w = _w; } + + /*! \copydoc Concept::FittingProcedureConcept::init() */ + MULTIARCH inline void init (const VectorType& _evalPos); + + + /**************************************************************************/ + /* Processing */ + /**************************************************************************/ + /*! \copydoc Concept::FittingProcedureConcept::addNeighbor() */ + MULTIARCH inline bool addNeighbor(const DataPoint &_nei); + + /*! \copydoc Concept::FittingProcedureConcept::finalize() */ + MULTIARCH inline FIT_RESULT finalize(); + + + /**************************************************************************/ + /* Results */ + /**************************************************************************/ + + using Base::potential; + + /*! \brief Value of the scalar field at the evaluation point */ + MULTIARCH inline Scalar potential() const { return Base::m_uc; } + + /*! \brief Value of the normal of the primitive at the evaluation point */ + MULTIARCH inline VectorType normal() const { return Base::m_ul.normalized(); } + +}; //class OrientedSphereFit + + +namespace internal +{ + +/*! + \brief Internal generic class performing the Fit derivation + \inherit Concept::FittingExtensionConcept + + The differentiation can be done automatically in scale and/or space, by + combining the enum values FitScaleDer and FitSpaceDer in the template + parameter Type. + + The differenciated values are stored in static arrays. The size of the + arrays is computed with respect to the derivation type (scale and/or space) + and the number of the dimension of the ambiant space. + By convention, the scale derivatives are stored at index 0 when Type + contains at least FitScaleDer. The size of these arrays can be known using + derDimension(), and the differentiation type by isScaleDer() and + isSpaceDer(). +*/ +template < class DataPoint, class _WFunctor, typename T, int Type> +class OrientedSphereDer : public T +{ +private: + typedef T Base; /*!< \brief Generic base type */ + +protected: + enum + { + Check = Base::PROVIDES_ALGEBRAIC_SPHERE, /*!< \brief Needs Algebraic Sphere */ + PROVIDES_ALGEBRAIC_SPHERE_DERIVATIVE, /*!< \brief Provides Algebraic Sphere derivative*/ + PROVIDES_NORMAL_DERIVATIVE + }; + +public: + typedef typename Base::Scalar Scalar; /*!< \brief Inherited scalar type*/ + typedef typename Base::VectorType VectorType; /*!< \brief Inherited vector type*/ + typedef typename Base::WFunctor WFunctor; /*!< \brief Weight Function*/ + +#define GLS_DER_NB_DERIVATIVES(TYPE,DIM) ((TYPE & FitScaleDer) ? 1 : 0 ) + ((TYPE & FitSpaceDer) ? DIM : 0) +#define GLS_DER_STORAGE_ORDER(TYPE) ((TYPE & FitSpaceDer) ? Eigen::RowMajor : Eigen::ColMajor ) + + /*! \brief Static array of scalars with a size adapted to the differentiation type */ + typedef Eigen::Matrix < Scalar, + DataPoint::Dim, + GLS_DER_NB_DERIVATIVES(Type,DataPoint::Dim), + GLS_DER_STORAGE_ORDER(Type) > VectorArray; + + /*! \brief Static array of scalars with a size adapted to the differentiation type */ + typedef Eigen::Matrix < Scalar, + 1, + GLS_DER_NB_DERIVATIVES(Type,DataPoint::Dim)/*, + GLS_DER_STORAGE_ORDER(Type)*/ > ScalarArray; +private: + // computation data + VectorArray m_dSumN, /*!< \brief Sum of the normal vectors with differenciated weights */ + m_dSumP; /*!< \brief Sum of the relative positions with differenciated weights*/ + ScalarArray m_dSumDotPN, /*!< \brief Sum of the dot product betwen relative positions and normals with differenciated weights */ + m_dSumDotPP, /*!< \brief Sum of the squared relative positions with differenciated weights */ + m_dSumW; /*!< \brief Sum of queries weight with differenciated weights */ + +public: + // results + ScalarArray m_dUc, /*!< \brief Derivative of the hyper-sphere constant term */ + m_dUq; /*!< \brief Derivative of the hyper-sphere quadratic term */ + VectorArray m_dUl; /*!< \brief Derivative of the hyper-sphere linear term */ + + /************************************************************************/ + /* Initialization */ + /************************************************************************/ + /*! \see Concept::FittingProcedureConcept::init() */ + MULTIARCH void init (const VectorType &evalPos); + + /************************************************************************/ + /* Processing */ + /************************************************************************/ + /*! \see Concept::FittingProcedureConcept::addNeighbor() */ + MULTIARCH bool addNeighbor(const DataPoint &nei); + /*! \see Concept::FittingProcedureConcept::finalize() */ + MULTIARCH FIT_RESULT finalize (); + + + /**************************************************************************/ + /* Use results */ + /**************************************************************************/ + + /*! \brief Returns the derivatives of the scalar field at the evaluation point */ + MULTIARCH inline ScalarArray dPotential() const; + + /*! \brief Returns the derivatives of the primitive normal */ + MULTIARCH inline VectorArray dNormal() const; + + /*! \brief compute the square of the Pratt norm derivative */ + MULTIARCH inline ScalarArray dprattNorm2() const + { + return Scalar(2.) * Base::m_ul.transpose() * m_dUl + - Scalar(4.) * Base::m_uq * m_dUc + - Scalar(4.) * Base::m_uc * m_dUq; + } + + /*! \brief compute the square of the Pratt norm derivative for dimension _d */ + MULTIARCH inline Scalar dprattNorm2(unsigned int _d) const + { + return Scalar(2.) * m_dUl.col(_d).dot(Base::m_ul) + - Scalar(4.) * m_dUc.col(_d)[0]*Base::m_uq + - Scalar(4.) * m_dUq.col(_d)[0]*Base::m_uc; + } + + /*! \brief compute the Pratt norm derivative for the dimension _d */ + MULTIARCH inline Scalar dprattNorm(unsigned int _d) const + { + MULTIARCH_STD_MATH(sqrt); + return sqrt(dprattNorm2(_d)); + } + + /*! \brief compute the Pratt norm derivative */ + MULTIARCH inline Scalar dprattNorm() const + { + MULTIARCH_STD_MATH(sqrt); + return dprattNorm2().array().sqrt(); + } + + /*! \brief State specified at compilation time to differenciate the fit in scale */ + MULTIARCH inline bool isScaleDer() const {return bool(Type & FitScaleDer);} + /*! \brief State specified at compilation time to differenciate the fit in space */ + MULTIARCH inline bool isSpaceDer() const {return bool(Type & FitSpaceDer);} + /*! \brief Number of dimensions used for the differentiation */ + MULTIARCH inline unsigned int derDimension() const { return GLS_DER_NB_DERIVATIVES(Type,DataPoint::Dim);} + + + //! Normalize the scalar field by the Pratt norm + /*! + \warning Requieres that isNormalized() return false + \return false when the original sphere has already been normalized. + */ + MULTIARCH inline bool applyPrattNorm(); + +}; //class OrientedSphereFitDer + +}// namespace internal + +/*! + \brief Differentiation in scale of the OrientedSphereFit + \inherit Concept::FittingExtensionConcept + + Requierement: + \verbatim PROVIDES_ALGEBRAIC_SPHERE \endverbatim + Provide: + \verbatim PROVIDES_ALGEBRAIC_SPHERE_SCALE_DERIVATIVE \endverbatim +*/ +template < class DataPoint, class _WFunctor, typename T> +class OrientedSphereScaleDer:public internal::OrientedSphereDer +{ +protected: + /*! \brief Inherited class */ + typedef internal::OrientedSphereDer Base; + enum + { + PROVIDES_ALGEBRAIC_SPHERE_SCALE_DERIVATIVE, + PROVIDES_NORMAL_SCALE_DERIVATIVE + }; +}; + + +/*! + \brief Spatial differentiation of the OrientedSphereFit + \inherit Concept::FittingExtensionConcept + + Requierement: + \verbatim PROVIDES_ALGEBRAIC_SPHERE \endverbatim + Provide: + \verbatim PROVIDES_ALGEBRAIC_SPHERE_SPACE_DERIVATIVE \endverbatim +*/ +template < class DataPoint, class _WFunctor, typename T> +class OrientedSphereSpaceDer:public internal::OrientedSphereDer +{ +protected: + /*! \brief Inherited class */ + typedef internal::OrientedSphereDer Base; + enum + { + PROVIDES_ALGEBRAIC_SPHERE_SPACE_DERIVATIVE, + PROVIDES_NORMAL_SPACE_DERIVATIVE + }; +}; + + +/*! + \brief Differentiation both in scale and space of the OrientedSphereFit + \inherit Concept::FittingExtensionConcept + + Requierement: + \verbatim PROVIDES_ALGEBRAIC_SPHERE \endverbatim + Provide: + \verbatim PROVIDES_ALGEBRAIC_SPHERE_SCALE_DERIVATIVE + PROVIDES_ALGEBRAIC_SPHERE_SPACE_DERIVATIVE + \endverbatim +*/ +template < class DataPoint, class _WFunctor, typename T> +class OrientedSphereScaleSpaceDer:public internal::OrientedSphereDer +{ +protected: + /*! \brief Inherited class */ + typedef internal::OrientedSphereDer Base; + enum + { + PROVIDES_ALGEBRAIC_SPHERE_SCALE_DERIVATIVE, + PROVIDES_ALGEBRAIC_SPHERE_SPACE_DERIVATIVE, + PROVIDES_NORMAL_SCALE_DERIVATIVE, + PROVIDES_NORMAL_SPACE_DERIVATIVE + }; +}; + + +#include "orientedSphereFit.hpp" + +} //namespace Grenaille + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/orientedSphereFit.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/orientedSphereFit.hpp new file mode 100644 index 000000000..8deaccb64 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/orientedSphereFit.hpp @@ -0,0 +1,253 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +template < class DataPoint, class _WFunctor, typename T> +void +OrientedSphereFit::init(const VectorType& _evalPos) +{ + // Setup primitive + Base::resetPrimitive(); + Base::basisCenter() = _evalPos; + + // Setup fitting internal values + m_sumP = VectorType::Zero(); + m_sumN = VectorType::Zero(); + m_sumDotPN = Scalar(0.0); + m_sumDotPP = Scalar(0.0); + m_sumW = Scalar(0.0); +} + +template < class DataPoint, class _WFunctor, typename T> +bool +OrientedSphereFit::addNeighbor(const DataPoint& _nei) +{ + // centered basis + VectorType q = _nei.pos() - Base::basisCenter(); + + // compute weight + Scalar w = m_w.w(q, _nei); + + if (w > Scalar(0.)) + { + // increment matrix + m_sumP += q * w; + m_sumN += _nei.normal() * w; + m_sumDotPN += w * _nei.normal().dot(q); + m_sumDotPP += w * q.squaredNorm(); + m_sumW += w; + + /*! \todo Handle add of multiple similar neighbors (maybe user side)*/ + ++(Base::m_nbNeighbors); + return true; + } + + return false; +} + + +template < class DataPoint, class _WFunctor, typename T> +FIT_RESULT +OrientedSphereFit::finalize () +{ + MULTIARCH_STD_MATH(sqrt); + MULTIARCH_STD_MATH(max); + MULTIARCH_STD_MATH(abs); + + // 1. finalize sphere fitting + Scalar epsilon = Eigen::NumTraits::dummy_precision(); + + // handle specific configurations + // With less than 3 neighbors the fitting is undefined + if(m_sumW == Scalar(0.) || Base::m_nbNeighbors < 3) + { + Base::m_ul.setZero(); + Base::m_uc = Scalar(0.); + Base::m_uq = Scalar(0.); + Base::m_isNormalized = false; + Base::m_eCurrentState = UNDEFINED; + return Base::m_eCurrentState; + } + + Scalar invSumW = Scalar(1.)/m_sumW; + + Scalar num = (m_sumDotPN - invSumW * m_sumP.dot(m_sumN)); + Scalar den1 = invSumW * m_sumP.dot(m_sumP); + Scalar den = m_sumDotPP - den1; + + // Deal with degenerate cases + if(abs(den) < epsilon * max(m_sumDotPP, den1)) + { + //plane + Scalar s = Scalar(1.) / Base::m_ul.norm(); + Base::m_ul = s*Base::m_ul; + Base::m_uc = s*Base::m_uc; + Base::m_uq = Scalar(0.); + } + else + { + //Generic case + Base::m_uq = Scalar(.5) * num / den; + Base::m_ul = (m_sumN - m_sumP * (Scalar(2.) * Base::m_uq)) * invSumW; + Base::m_uc = -invSumW * (Base::m_ul.dot(m_sumP) + m_sumDotPP * Base::m_uq); + } + + Base::m_isNormalized = false; + + if(Base::m_nbNeighbors < 6) + { + Base::m_eCurrentState = UNSTABLE; + } + else + { + Base::m_eCurrentState = STABLE; + } + + return Base::m_eCurrentState; +} + + +namespace internal +{ + +template < class DataPoint, class _WFunctor, typename T, int Type> +void +OrientedSphereDer::init(const VectorType& _evalPos) +{ + Base::init(_evalPos); + + m_dSumN = VectorArray::Zero(); + m_dSumP = VectorArray::Zero(); + + m_dSumDotPN = ScalarArray::Zero(); + m_dSumDotPP = ScalarArray::Zero(); + m_dSumW = ScalarArray::Zero(); + + m_dUc = ScalarArray::Zero(); + m_dUq = ScalarArray::Zero(); + m_dUl = VectorArray::Zero(); +} + + +template < class DataPoint, class _WFunctor, typename T, int Type> +bool +OrientedSphereDer::addNeighbor(const DataPoint &_nei) +{ + bool bResult = Base::addNeighbor(_nei); + + if(bResult) + { + ScalarArray dw; + + // centered basis + VectorType q = _nei.pos() - Base::basisCenter(); + + // compute weight + if (Type & FitScaleDer) + dw[0] = Base::m_w.scaledw(q, _nei); + + if (Type & FitSpaceDer) + dw.template tail() = -Base::m_w.spacedw(q, _nei).transpose(); + + // increment + m_dSumW += dw; + m_dSumP += q * dw; + m_dSumN += _nei.normal() * dw; + m_dSumDotPN += dw * _nei.normal().dot(q); + m_dSumDotPP += dw * q.squaredNorm(); + + return true; + } + + return false; +} + + +template < class DataPoint, class _WFunctor, typename T, int Type> +FIT_RESULT +OrientedSphereDer::finalize() +{ + MULTIARCH_STD_MATH(sqrt); + + Base::finalize(); + // Test if base finalize end on a viable case (stable / unstable) + if (this->isReady()) + { + Scalar invSumW = Scalar(1.)/Base::m_sumW; + + Scalar nume = Base::m_sumDotPN - invSumW*Base::m_sumP.dot(Base::m_sumN); + Scalar deno = Base::m_sumDotPP - invSumW*Base::m_sumP.dot(Base::m_sumP); + + ScalarArray dNume = m_dSumDotPN + - invSumW*invSumW * ( Base::m_sumW * ( Base::m_sumN.transpose() * m_dSumP + Base::m_sumP.transpose() * m_dSumN ) + - m_dSumW*Base::m_sumP.dot(Base::m_sumN) ); + + ScalarArray dDeno = m_dSumDotPP + - invSumW*invSumW*( Scalar(2.) * Base::m_sumW * Base::m_sumP.transpose() * m_dSumP + - m_dSumW*Base::m_sumP.dot(Base::m_sumP) ); + + m_dUq = Scalar(.5) * (deno * dNume - dDeno * nume)/(deno*deno); + + m_dUl = invSumW * ( m_dSumN - Scalar(2.)*(m_dSumP*Base::m_uq + Base::m_sumP*m_dUq) - Base::m_ul*m_dSumW); + + m_dUc = -invSumW*( Base::m_sumP.transpose() * m_dUl + + Base::m_sumDotPP * m_dUq + + Base::m_ul.transpose() * m_dSumP + + Base::m_uq * m_dSumDotPP + + m_dSumW * Base::m_uc); + } + + return Base::m_eCurrentState; +} + +template < class DataPoint, class _WFunctor, typename T, int Type> +typename OrientedSphereDer ::VectorArray +OrientedSphereDer::dNormal() const +{ + // Computes the derivatives of the normal of the sphere at the evaluation point. + // Therefore, we must take into account the variation of the evaluation point when differentiating wrt space + // i.e., normal(x) = grad / |grad|, with grad(x) = ul + 2 uq * x, and diff_x(grad) = dul + 2 uq I + VectorArray dgrad = m_dUl; + if(this->isSpaceDer()) + dgrad.template rightCols().diagonal().array() += Scalar(2)*Base::m_uq; + Scalar norm = Base::m_ul.norm(); + Scalar norm3 = norm*norm*norm; + return dgrad / norm - Base::m_ul * (Base::m_ul.transpose() * dgrad) / norm3; +} + +template < class DataPoint, class _WFunctor, typename T, int Type> +typename OrientedSphereDer ::ScalarArray +OrientedSphereDer::dPotential() const +{ + ScalarArray dfield = m_dUc; + if(this->isScaleDer()) + dfield.template tail() += Base::m_ul; + return dfield; +} + +template < class DataPoint, class _WFunctor, typename T, int Type> +bool +OrientedSphereDer::applyPrattNorm() +{ + if(Base::isNormalized()) + return false; //need original parameters without Pratt Normalization + + MULTIARCH_STD_MATH(sqrt); + Scalar pn2 = Base::prattNorm2(); + Scalar pn = sqrt(pn2); + + ScalarArray dpn2 = dprattNorm2(); + ScalarArray factor = Scalar(0.5) * dpn2 / pn; + + m_dUc = ( m_dUc * pn - Base::m_uc * factor ) / pn2; + m_dUl = ( m_dUl * pn - Base::m_ul * factor ) / pn2; + m_dUq = ( m_dUq * pn - Base::m_uq * factor ) / pn2; + + Base::applyPrattNorm(); + return true; +} + +}// namespace internal diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/plane.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/plane.h new file mode 100644 index 000000000..ca259ede2 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/plane.h @@ -0,0 +1,129 @@ +/* + Copyright (C) 2014 Nicolas Mellado + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +#ifndef _GRENAILLE_PLANE_ +#define _GRENAILLE_PLANE_ + +#include "primitive.h" // PrimitiveBase + +namespace Grenaille +{ + +/*! + \brief Implicit hyperplane defined by an homogeneous vector \f$\mathbf{p}\f$. + + In n-dimensionnal space, the plane is defined as + the \f$0\f$-isosurface of the scalar field + + \f$ s_\mathbf{u}(\mathbf{x}) = + \left[ \mathbf{x}^T \; 1 \;\right]^T \cdot \mathbf{p} \f$. + + This class uses a compact storage of n+1 scalars in n-dimensionnal space. It + can be sensitive to the data scale, leading to potential instabilities + due to round errors at large scales. + \todo Add standard plane storing 2n scalars (direction and center). + + + This primitive requires the definition of n-dimensionnal vectors + (VectorType) and homogeneous n-dimensionnal vectors (HVectorType) in + Concept::PointConcept. + + This primitive provides: + \verbatim PROVIDES_PLANE \endverbatim + + \note The first n-components of the plane must define a normalized vector + +*/ +template < class DataPoint, class _WFunctor, typename T = void > +class CompactPlane : public PrimitiveBase +{ +private: + + typedef PrimitiveBase Base; + +protected: + + enum + { + PROVIDES_PLANE /*!< \brief Provides a Plane primitive */ + }; + +public: + + /*! \brief Scalar type inherited from DataPoint */ + typedef typename DataPoint::Scalar Scalar; + /*! \brief Vector type inherited from DataPoint */ + typedef typename DataPoint::VectorType VectorType; + /*! \brief Matrix type inherited from DataPoint */ + typedef typename DataPoint::MatrixType MatrixType; + /*! \brief Homogeneous vector type inherited from DataPoint */ + typedef typename DataPoint::HVectorType HVectorType; + /*! \brief Weight Function */ + typedef _WFunctor WFunctor; + +// results +public: + + HVectorType m_p; /*!< \brief Homogeneous plane representation */ + +public: + + /*! \brief Default constructor */ + MULTIARCH inline CompactPlane() + : Base() + { + resetPrimitive(); + } + + /*! \brief Set the scalar field values to 0 and reset the isNormalized() + status */ + MULTIARCH inline void resetPrimitive() + { + Base::resetPrimitive(); + m_p = HVectorType::Zero(); + } + + /* \brief Init the plane from a direction and a position + \param _dir Orientation of the plane + \param _pos Position of the plane + */ + MULTIARCH inline void setPlane (const VectorType& _dir, + const VectorType& _pos) + { + m_p.template head() = _dir.normalized(); + m_p.template tail<1>()<< -_pos.dot(m_p.template head()); + } + + //! \brief Value of the scalar field at the location \f$ \mathbf{q} \f$ + MULTIARCH inline Scalar potential (const VectorType& _q) const + { + // Project on the normal vector and add the offset value + return m_p.template head().dot(_q) + + *( m_p.template tail<1>().data() ); + } + + //! \brief Project a point on the plane + MULTIARCH inline VectorType project (const VectorType& _q) const + { + // The potential is the distance from the point to the plane + return _q - potential(_q) * m_p.template head(); + } + + //! \brief Scalar field gradient direction at \f$ \mathbf{q}\f$ + MULTIARCH inline VectorType primitiveGradient (const VectorType&) const + { + // Uniform gradient defined only by the orientation of the plane + return m_p.template head(); + } + + +}; //class Plane + +} +#endif // _GRENAILLE_PLANE_ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/primitive.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/primitive.h new file mode 100644 index 000000000..e904f80ed --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/primitive.h @@ -0,0 +1,72 @@ +/* + Copyright (C) 2014 Nicolas Mellado + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +#ifndef _GRENAILLE_PRIMITIVE_ +#define _GRENAILLE_PRIMITIVE_ + +namespace Grenaille +{ + +/*! + \brief Primitive base class. + + This class stores and provides public access to the fitting state, and must + be inherited by classes implementing new primitives. + + Protected fields #m_eCurrentState and #m_nbNeighbors should be updated + during the fitting process by the inheriting class. +*/ +template < class DataPoint, class _WFunctor, typename T = void > +class PrimitiveBase +{ + +protected: + + //! \brief Represent the current state of the fit (finalize function + //! update the state) + FIT_RESULT m_eCurrentState; + + //! \brief Give the number of neighbors + int m_nbNeighbors; + +public: + + /*! \brief Default constructor */ + MULTIARCH inline PrimitiveBase() { } + + /*! \brief Reset fitting state + status */ + MULTIARCH inline void resetPrimitive() + { + m_eCurrentState = UNDEFINED; + m_nbNeighbors = 0; + } + + /*! \brief Is the primitive well fitted an ready to use (finalize has been + called) + \warning The fit can be unstable (having neighbors between 3 and 6) */ + MULTIARCH inline bool isReady() const + { + return (m_eCurrentState == STABLE) || (m_eCurrentState == UNSTABLE); + } + + /*! \brief Is the plane fitted an ready to use (finalize has been called + and the result is stable, eq. having more than 6 neighbors) */ + MULTIARCH inline bool isStable() const { return m_eCurrentState == STABLE; } + + /*! \return the current test of the fit */ + MULTIARCH inline FIT_RESULT getCurrentState() const + { + return m_eCurrentState; + } + +}; //class Plane + +} +#endif // _GRENAILLE_PLANE_ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/unorientedSphereFit.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/unorientedSphereFit.h new file mode 100644 index 000000000..5912bfd8a --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/unorientedSphereFit.h @@ -0,0 +1,288 @@ +/* + Copyright (C) 2013 Gael Guennebaud + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +#ifndef _GRENAILLE_UNORIENTED_SPHERE_FIT_ +#define _GRENAILLE_UNORIENTED_SPHERE_FIT_ + +#include +#include "algebraicSphere.h" + +namespace Grenaille +{ +/*! + \brief Algebraic Sphere fitting procedure on point sets with non-oriented normals + + Method published in \cite Chen:2013:NOMG. + + \inherit Concept::FittingProcedureConcept + + \see class AlgebraicSphere, class OrientedSphereFit +*/ +template < class DataPoint, class _WFunctor, typename T = void > +class UnorientedSphereFit : public AlgebraicSphere +{ +private: + typedef AlgebraicSphere Base; + +public: + /*! \brief Scalar type inherited from DataPoint*/ + typedef typename Base::Scalar Scalar; + /*! \brief Vector type inherited from DataPoint*/ + typedef typename Base::VectorType VectorType; + /*! \brief Weight Function*/ + typedef _WFunctor WFunctor; + +protected: + + enum + { + Dim = VectorType::SizeAtCompileTime //!< Dimension of the ambient space + }; + typedef Eigen::Matrix VectorB; + typedef Eigen::Matrix MatrixBB; + + MatrixBB m_matA; /*!< \brief The accumulated covariance matrix */ + VectorType m_sumP; /*!< \brief Sum of the relative positions */ + Scalar m_sumDotPP, /*!< \brief Sum of the squared relative positions */ + m_sumW; /*!< \brief Sum of queries weight */ + + WFunctor m_w; /*!< \brief Weight function (must inherits BaseWeightFunc) */ + + +public: + /*! \brief Default constructor */ + MULTIARCH inline UnorientedSphereFit() + : Base(){} + + + /**************************************************************************/ + /* Initialization */ + /**************************************************************************/ + /*! \copydoc Concept::FittingProcedureConcept::setWeightFunc() */ + MULTIARCH inline void setWeightFunc(const WFunctor& _w) { m_w = _w; } + + /*! \copydoc Concept::FittingProcedureConcept::init() */ + MULTIARCH inline void init(const VectorType& _evalPos); + + + /**************************************************************************/ + /* Processing */ + /**************************************************************************/ + /*! \copydoc Concept::FittingProcedureConcept::addNeighbor() */ + MULTIARCH inline bool addNeighbor(const DataPoint& _nei); + + /*! \copydoc Concept::FittingProcedureConcept::finalize() */ + MULTIARCH inline FIT_RESULT finalize(); + + using Base::potential; + + /*! \brief Value of the scalar field at the evaluation point */ + MULTIARCH inline Scalar potential() const { return Base::m_uc; } + + /*! \brief Value of the normal of the primitive at the evaluation point */ + MULTIARCH inline VectorType normal() const { return Base::m_ul.normalized(); } + +}; // class UnorientedSphereFit + + +#ifdef TOBEIMPLEMENTED + +namespace internal +{ + +/*! + \brief Internal generic class performing the Fit derivation + \inherit Concept::FittingExtensionConcept + + The differentiation can be done automatically in scale and/or space, by + combining the enum values FitScaleDer and FitSpaceDer in the template + parameter Type. + + The differenciated values are stored in static arrays. The size of the + arrays is computed with respect to the derivation type (scale and/or space) + and the number of the dimension of the ambiant space. + By convention, the scale derivatives are stored at index 0 when Type + contains at least FitScaleDer. The size of these arrays can be known using + derDimension(), and the differentiation type by isScaleDer() and + isSpaceDer(). +*/ +template < class DataPoint, class _WFunctor, typename T, int Type> +class UnorientedSphereDer : public T +{ +private: + typedef T Base; /*!< \brief Generic base type */ + +protected: + enum + { + Check = Base::PROVIDES_ALGEBRAIC_SPHERE, /*!< \brief Needs Algebraic Sphere */ + PROVIDES_ALGEBRAIC_SPHERE_DERIVATIVE /*!< \brief Provides Algebraic Sphere derivative*/ + }; + +public: + typedef typename Base::Scalar Scalar; /*!< \brief Inherited scalar type*/ + typedef typename Base::VectorType VectorType; /*!< \brief Inherited vector type*/ + typedef typename Base::WFunctor WFunctor; /*!< \brief Weight Function*/ + +#define GLS_DER_NB_DERIVATIVES(TYPE,DIM) ((TYPE & FitScaleDer) ? 1 : 0 ) + ((TYPE & FitSpaceDer) ? DIM : 0) + + /*! \brief Static array of scalars with a size adapted to the differentiation type */ + typedef Eigen::Matrix VectorArray; + /*! \brief Static array of scalars with a size adapted to the differentiation type */ + typedef Eigen::Matrix ScalarArray; + +private: + // computation data + VectorArray m_dSumN, /*!< \brief Sum of the normal vectors with differenciated weights */ + m_dSumP; /*!< \brief Sum of the relative positions with differenciated weights*/ + ScalarArray m_dSumDotPN, /*!< \brief Sum of the dot product betwen relative positions and normals with differenciated weights */ + m_dSumDotPP, /*!< \brief Sum of the squared relative positions with differenciated weights */ + m_dSumW; /*!< \brief Sum of queries weight with differenciated weights */ + +public: + // results + ScalarArray m_dUc, /*!< \brief Derivative of the hyper-sphere constant term */ + m_dUq; /*!< \brief Derivative of the hyper-sphere quadratic term */ + VectorArray m_dUl; /*!< \brief Derivative of the hyper-sphere linear term */ + + /************************************************************************/ + /* Initialization */ + /************************************************************************/ + /*! \see Concept::FittingProcedureConcept::init() */ + MULTIARCH void init(const VectorType& _evalPos); + + /************************************************************************/ + /* Processing */ + /************************************************************************/ + /*! \see Concept::FittingProcedureConcept::addNeighbor() */ + MULTIARCH bool addNeighbor(const DataPoint& _nei); + /*! \see Concept::FittingProcedureConcept::finalize() */ + MULTIARCH FIT_RESULT finalize(); + + + /**************************************************************************/ + /* Use results */ + /**************************************************************************/ + MULTIARCH inline ScalarArray dprattNorm2() const + { + return Scalar(2.) * Base::m_ul.transpose() * m_dUl + - Scalar(4.) * Base::m_uq * m_dUc + - Scalar(4.) * Base::m_uc * m_dUq; + } + + /*! \brief compute the square of the Pratt norm derivative for dimension d */ + MULTIARCH inline Scalar dprattNorm2(unsigned int _d) const + { + return Scalar(2.) * m_dUl.col(_d).dot(Base::m_ul) + - Scalar(4.) * m_dUc.col(_d)[0]*Base::m_uq + - Scalar(4.) * m_dUq.col(_d)[0]*Base::m_uc; + } + + /*! \brief compute the Pratt norm derivative for the dimension d */ + MULTIARCH inline Scalar dprattNorm(unsigned int _d) const + { + MULTIARCH_STD_MATH(sqrt); + return sqrt(dprattNorm2(_d)); + } + + /*! \brief compute the Pratt norm derivative for the dimension d */ + MULTIARCH inline Scalar dprattNorm() const + { + MULTIARCH_STD_MATH(sqrt); + return dprattNorm2().array().sqrt(); + } + + /*! \brief State specified at compilation time to differenciate the fit in scale */ + MULTIARCH inline bool isScaleDer() const {return Type & FitScaleDer;} + /*! \brief State specified at compilation time to differenciate the fit in space */ + MULTIARCH inline bool isSpaceDer() const {return Type & FitSpaceDer;} + /*! \brief Number of dimensions used for the differentiation */ + MULTIARCH inline unsigned int derDimension() const { return GLS_DER_NB_DERIVATIVES(Type,DataPoint::Dim);} + + /*! + \brief Normalize the scalar field by the Pratt norm + \warning Requieres that isNormalized() return false + \return false when the original sphere has already been normalized. + */ + MULTIARCH inline bool applyPrattNorm(); + +}; // class UnorientedSphereFitDer + +}// namespace internal + +/*! + \brief Differentiation in scale of the UnorientedSphereFit + \inherit Concept::FittingExtensionConcept + + Requierement: + \verbatim PROVIDES_ALGEBRAIC_SPHERE \endverbatim + Provide: + \verbatim PROVIDES_ALGEBRAIC_SPHERE_SCALE_DERIVATIVE \endverbatim +*/ +template < class DataPoint, class _WFunctor, typename T> +class UnorientedSphereScaleDer:public internal::UnorientedSphereDer +{ +protected: + /*! \brief Inherited class */ + typedef internal::UnorientedSphereDer Base; + enum { PROVIDES_ALGEBRAIC_SPHERE_SCALE_DERIVATIVE }; +}; + + +/*! + \brief Spatial differentiation of the UnorientedSphereFit + \inherit Concept::FittingExtensionConcept + + Requierement: + \verbatim PROVIDES_ALGEBRAIC_SPHERE \endverbatim + Provide: + \verbatim PROVIDES_ALGEBRAIC_SPHERE_SPACE_DERIVATIVE \endverbatim +*/ +template < class DataPoint, class _WFunctor, typename T> +class UnorientedSphereSpaceDer:public internal::UnorientedSphereDer +{ +protected: + /*! \brief Inherited class */ + typedef internal::UnorientedSphereDer Base; + enum { PROVIDES_ALGEBRAIC_SPHERE_SPACE_DERIVATIVE }; +}; + + +/*! + \brief Differentiation both in scale and space of the UnorientedSphereFit + \inherit Concept::FittingExtensionConcept + + Requierement: + \verbatim PROVIDES_ALGEBRAIC_SPHERE \endverbatim + Provide: + \verbatim PROVIDES_ALGEBRAIC_SPHERE_SCALE_DERIVATIVE + PROVIDES_ALGEBRAIC_SPHERE_SPACE_DERIVATIVE + \endverbatim +*/ +template < class DataPoint, class _WFunctor, typename T> +class UnorientedSphereScaleSpaceDer:public internal::UnorientedSphereDer +{ +protected: + /*! \brief Inherited class */ + typedef internal::UnorientedSphereDer Base; + enum + { + PROVIDES_ALGEBRAIC_SPHERE_SCALE_DERIVATIVE, + PROVIDES_ALGEBRAIC_SPHERE_SPACE_DERIVATIVE + }; +}; + +#endif // end TOBEIMPLEMENTED + +#include "unorientedSphereFit.hpp" + +} //namespace Grenaille + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/unorientedSphereFit.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/unorientedSphereFit.hpp new file mode 100644 index 000000000..4367107ec --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/unorientedSphereFit.hpp @@ -0,0 +1,283 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + + +#if NEVERDEFINED + +MatrixBB cov = MatrixBB::Zero(); +VectorDd sumP = VectorDd::Zero(); +double sumDotPP = 0.; +double sumOfWeights = 0.; + +// the normalization matrix +MatrixBB Q = MatrixBB::Zero(); + +for(uint i=0 ; igetNeighbor(i).position().cast(); + VectorDd n = pNeighborhood->getNeighbor(i).normal().cast(); + double w = pNeighborhood->getNeighborWeight(i); + + VectorB basis; + basis << n, n.dot(p); + + cov += w * basis * basis.transpose(); + sumOfWeights += w; + + MatrixBB q; + q << MatrixDDd::Identity(), p, + p.transpose(), p.squaredNorm(); + + Q += w * q; + + sumP += w * p; + sumDotPP += w * p.squaredNorm(); +} +cov /= sumOfWeights; +Q /= sumOfWeights; + +MatrixBB M = Q.inverse() * cov; +Eigen::EigenSolver eig(M); +VectorB eivals = eig.eigenvalues().real(); +int maxId = 0; +double l = eivals.maxCoeff(&maxId); +VectorB eivec = eig.eigenvectors().col(maxId).real(); + +// integrate +uLinear() = eivec.start().cast(); +uQuad() = 0.5*eivec(Dim); +uConstant() = -(1./sumOfWeights)*(eivec.start().dot(sumP) + 0.5*eivec(Dim) * sumDotPP); + +#endif + +template < class DataPoint, class _WFunctor, typename T> +void +UnorientedSphereFit::init(const VectorType& _evalPos) +{ + // Setup primitive + Base::resetPrimitive(); + Base::basisCenter() = _evalPos; + + // Setup fitting internal values + m_matA.setZero(); + // _matQ.setZero(); + m_sumP.setZero(); + m_sumDotPP = Scalar(0.0); + m_sumW = Scalar(0.0); +} + +template < class DataPoint, class _WFunctor, typename T> +bool +UnorientedSphereFit::addNeighbor(const DataPoint& _nei) +{ + // centered basis + VectorType q = _nei.pos() - Base::basisCenter(); + + // compute weight + Scalar w = m_w.w(q, _nei); + + if (w > Scalar(0.)) + { + VectorB basis; + basis << _nei.normal(), _nei.normal().dot(q); + + m_matA += w * basis * basis.transpose(); + m_sumP += w * q; + m_sumDotPP += w * q.squaredNorm(); + m_sumW += w; + + /*! \todo Handle add of multiple similar neighbors (maybe user side)*/ + ++(Base::m_nbNeighbors); + return true; + } + + return false; +} + +template < class DataPoint, class _WFunctor, typename T> +FIT_RESULT +UnorientedSphereFit::finalize () +{ + MULTIARCH_STD_MATH(sqrt); + + // 1. finalize sphere fitting + Scalar invSumW; + Scalar epsilon = Eigen::NumTraits::dummy_precision(); + + // handle specific configurations + // With less than 3 neighbors the fitting is undefined + if(m_sumW == Scalar(0.) || Base::m_nbNeighbors < 3) + { + Base::m_ul.setZero(); + Base::m_uc = 0; + Base::m_uq = 0; + Base::m_isNormalized = false; + Base::m_eCurrentState = UNDEFINED; + return Base::m_eCurrentState; + } + else + { + invSumW = Scalar(1.) / m_sumW; + } + + MatrixBB Q; + Q.template topLeftCorner().setIdentity(); + Q.col(Dim).template head() = m_sumP * invSumW; + Q.row(Dim).template head() = m_sumP * invSumW; + Q(Dim,Dim) = m_sumDotPP * invSumW; + m_matA *= invSumW; + + MatrixBB M = Q.inverse() * m_matA; + Eigen::EigenSolver eig(M); + VectorB eivals = eig.eigenvalues().real(); + int maxId = 0; + Scalar l = eivals.maxCoeff(&maxId); + VectorB eivec = eig.eigenvectors().col(maxId).real(); + + // integrate + Base::m_ul = eivec.template head(); + Base::m_uq = Scalar(0.5) * eivec(Dim); + Base::m_uc = -invSumW * (Base::m_ul.dot(m_sumP) + m_sumDotPP * Base::m_uq); + + Base::m_isNormalized = false; + + if(Base::m_nbNeighbors < 6) + { + Base::m_eCurrentState = UNSTABLE; + } + else + { + Base::m_eCurrentState = STABLE; + } + + return Base::m_eCurrentState; +} + +#ifdef TOBEIMPLEMENTED + +namespace internal +{ + +template < class DataPoint, class _WFunctor, typename T, int Type> +void +OrientedSphereDer::init(const VectorType& _evalPos) +{ + Base::init(_evalPos); + + m_dSumN = VectorArray::Zero(); + m_dSumP = VectorArray::Zero(); + + m_dSumDotPN = ScalarArray::Zero(); + m_dSumDotPP = ScalarArray::Zero(); + m_dSumW = ScalarArray::Zero(); + + m_dUc = ScalarArray::Zero(); + m_dUq = ScalarArray::Zero(); + m_dUl = VectorArray::Zero(); +} + + +template < class DataPoint, class _WFunctor, typename T, int Type> +bool +OrientedSphereDer::addNeighbor(const DataPoint &_nei) +{ + bool bResult = Base::addNeighbor(_nei); + if(bResult) + { + int spaceId = (Type & FitScaleDer) ? 1 : 0; + + ScalarArray w; + + // centered basis + VectorType q = _nei.pos() - Base::basisCenter(); + + // compute weight + if (Type & FitScaleDer) + w[0] = Base::m_w.scaledw(q, _nei); + + if (Type & FitSpaceDer){ + VectorType vw = Base::m_w.spacedw(q, _nei); + for(unsigned int i = 0; i < DataPoint::Dim; i++) + w[spaceId+i] = vw[i]; + } + + // increment + m_dSumW += w; + m_dSumP += q * w; + m_dSumN += _nei.normal() * w; + m_dSumDotPN += w * _nei.normal().dot(q); + m_dSumDotPP += w * q.squaredNorm(); + + return true; + } + + return false; +} + + +template < class DataPoint, class _WFunctor, typename T, int Type> +FIT_RESULT +OrientedSphereDer::finalize() +{ + MULTIARCH_STD_MATH(sqrt); + + Base::finalize(); + + // Test if base finalize end on a viable case (stable / unstable) + if (this->isReady()) + { + + Scalar invSumW = Scalar(1.)/Base::m_sumW; + + Scalar nume = Base::m_sumDotPN - invSumW*Base::m_sumP.dot(Base::m_sumN); + Scalar deno = Base::m_sumDotPP - invSumW*Base::m_sumP.dot(Base::m_sumP); + + ScalarArray dNume = m_dSumDotPN - invSumW*invSumW * ( Base::m_sumW * ( + Base::m_sumN.transpose() * m_dSumP + + Base::m_sumP.transpose() * m_dSumN ) + - m_dSumW*Base::m_sumP.dot(Base::m_sumN) ); + ScalarArray dDeno = m_dSumDotPP - invSumW*invSumW*( Scalar(2.)*Base::m_sumW * Base::m_sumP.transpose()*m_dSumP + - m_dSumW*Base::m_sumP.dot(Base::m_sumP) ); + + m_dUq = Scalar(.5) * (deno * dNume - dDeno * nume)/(deno*deno); + m_dUl = invSumW*((m_dSumN - Scalar(2.)*(m_dSumP*Base::m_uq+Base::m_sumP*m_dUq)) - Base::m_ul*m_dSumW); + m_dUc = -invSumW*( Base::m_sumP.transpose() * m_dUl + + Base::m_sumDotPP * m_dUq + Base::m_ul.transpose() * m_dSumP + + Base::m_uq*m_dSumDotPP + m_dSumW*Base::m_uc); + } + + return Base::m_eCurrentState; + +} + + +template < class DataPoint, class _WFunctor, typename T, int Type> +bool +OrientedSphereDer::applyPrattNorm() +{ + if(Base::isNormalized()) + return false; //need original parameters without Pratt Normalization + + + MULTIARCH_STD_MATH(sqrt); + Scalar pn2 = Base::prattNorm2(); + Scalar pn = sqrt(pn2); + + ScalarArray dpn2 = dprattNorm2(); + ScalarArray factor = Scalar(0.5) * dpn2 / pn; + + m_dUc = ( m_dUc * pn - Base::m_uc * factor ) / pn2; + m_dUl = ( m_dUl * pn - Base::m_ul * factor ) / pn2; + m_dUq = ( m_dUq * pn - Base::m_uq * factor ) / pn2; + + Base::applyPrattNorm(); + return true; +} + +}// namespace internal + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/weightFunc.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/weightFunc.h new file mode 100644 index 000000000..2e0670484 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/weightFunc.h @@ -0,0 +1,101 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +#ifndef _GRENAILLE_WEIGHT_FUNC_ +#define _GRENAILLE_WEIGHT_FUNC_ + +namespace Grenaille +{ +/*! + \brief Weighting function based on the euclidean distance between a query and a reference position + + The query is assumed to be expressed in centered coordinates (ie. relatively + to the evaluation position). + + This class inherits BaseWeightFunc. It can be specialized for any DataPoint, + and uses a generic 1D BaseWeightKernel. + + \inherit Concept::WeightFuncConcept + + \warning it assumes that the evaluation scale t is strictly positive +*/ +template +class DistWeightFunc +{ +public: + /*! \brief Scalar type from DataPoint */ + typedef typename DataPoint::Scalar Scalar; + /*! \brief Vector type from DataPoint */ + typedef typename DataPoint::VectorType VectorType; + + /*! + \brief Constructor that defines the current evaluation scale + \warning t > 0 + */ + MULTIARCH inline DistWeightFunc(const Scalar& _t = Scalar(1.)) + { + //\todo manage that assrt on __host__ and __device__ + //assert(_t > Scalar(0)); + m_t = _t; + } + + /*! + \brief Compute the weight of the given query with respect to its coordinates. + + As the query \f$\mathbf{q}\f$ is expressed in a centered basis, the + WeightKernel is directly applied to the norm of its coordinates with + respect to the current scale \f$ t \f$ : + + \f$ w(\frac{\left|\mathbf{q}_\mathsf{x}\right|}{t}) \f$ + */ + MULTIARCH inline Scalar w(const VectorType& _q, + const DataPoint& /*attributes*/) const; + + + /*! + \brief First order derivative in space (for each spatial dimension \f$\mathsf{x})\f$ + + \f$ \frac{\delta \frac{\left|\mathbf{q}_\mathsf{x}\right|}{t}}{\delta \mathsf{x}} + \nabla w(\frac{\left|\mathbf{q}_\mathsf{x}\right|}{t}) + = \frac{ \nabla{w(\frac{\left|\mathbf{q}_\mathsf{x}\right|}{t})}}{t} \f$ + + where \f$ \left|\mathbf{q}_\mathsf{x}\right| \f$ represents the norm of the + query coordinates expressed in centered basis, + for each spatial dimensions \f$ \mathsf{x}\f$. + */ + MULTIARCH inline VectorType spacedw(const VectorType& _q, + const DataPoint& /*attributes*/) const; + + + /*! + \brief First order derivative in scale \f$t\f$ + + \f$ \frac{\delta \frac{\left|\mathbf{q}\right|}{t}}{\delta t} + \nabla w(\frac{\left|\mathbf{q}\right|}{t}) + = - \frac{\left|\mathbf{q}\right|}{t^2} \nabla{w(\frac{\left|\mathbf{q}\right|}{t})} \f$ + + where \f$ \left|\mathbf{q}\right| \f$ represents the norm of the + query coordinates expressed in centered basis. + */ + MULTIARCH inline Scalar scaledw(const VectorType& _q, + const DataPoint& /*attributes*/) const; + + /*! \brief Access to the evaluation scale set during the initialization */ + MULTIARCH inline Scalar evalScale() const { return m_t; } + +protected: + Scalar m_t; /*!< \brief Evaluation scale */ + WeightKernel m_wk; /*!< \brief 1D function applied to weight queries */ + +};// class DistWeightFunc + +#include "weightFunc.hpp" + +}// namespace Grenaille + + +#endif // _GRENAILLE_WEIGHT_FUNC_ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/weightFunc.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/weightFunc.hpp new file mode 100644 index 000000000..4f8c6bd9c --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/weightFunc.hpp @@ -0,0 +1,36 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + + +template +typename DistWeightFunc::Scalar +DistWeightFunc::w( const VectorType& _q, + const DataPoint&) const +{ + Scalar d = _q.norm(); + return (d <= m_t) ? m_wk.f(d/m_t) : Scalar(0.); +} + +template +typename DistWeightFunc::VectorType +DistWeightFunc::spacedw( const VectorType& _q, + const DataPoint&) const +{ + VectorType result = VectorType::Zero(); + Scalar d = _q.norm(); + if (d <= m_t && d != Scalar(0.)) result = (_q / (d * m_t)) * m_wk.df(d/m_t); + return result; +} + +template +typename DistWeightFunc::Scalar +DistWeightFunc::scaledw( const VectorType& _q, + const DataPoint&) const +{ + Scalar d = _q.norm(); + return (d <= m_t) ? ( - d*m_wk.df(d/m_t)/(m_t*m_t) ) : Scalar(0.); +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/weightKernel.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/weightKernel.h new file mode 100644 index 000000000..774b54d66 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/Core/weightKernel.h @@ -0,0 +1,74 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +#ifndef _GRENAILLE_WEIGHT_KERNEL_ +#define _GRENAILLE_WEIGHT_KERNEL_ + +/*! + \file weightKernel.h Define 1D weight kernel functors +*/ + + +namespace Grenaille +{ +/*! + \brief Concept::WeightKernelConcept returning a constant value + + \inherit Concept::WeightKernelConcept +*/ +template +class ConstantWeightKernel +{ +public: + /*! \brief Scalar type defined outside the class */ + typedef _Scalar Scalar; + + // Init + //! \brief Default constructor that could be used to set the returned value + MULTIARCH inline ConstantWeightKernel(const Scalar& _value = Scalar(1.)) : m_y(_value){} + //! \brief Set the returned value + MULTIARCH inline void setValue(const Scalar& _value){ m_y = _value; } + + // Functor + //! \brief Return the constant value + MULTIARCH inline Scalar f (const Scalar&) const { return m_y; } + //! \brief Return \f$ 0 \f$ + MULTIARCH inline Scalar df (const Scalar&) const { return Scalar(0.); } + //! \brief Return \f$ 0 \f$ + MULTIARCH inline Scalar ddf(const Scalar&) const { return Scalar(0.); } + +private: + Scalar m_y; /*!< \brief Constant value returned by the kernel */ +};// class ConstantWeightKernel + + +/*! + \brief Smooth WeightKernel defined in \f$\left[0 : 1\right]\f$ + \todo Add a degree value as template parameter (in this class or another one), with specialized functions for 2 + + \inherit Concept::WeightKernelConcept +*/ +template +class SmoothWeightKernel +{ +public: + /*! \brief Scalar type defined outside the class*/ + typedef _Scalar Scalar; + + // Functor + /*! \brief Defines the smooth weighting function \f$ w(x) = (x^2-1)^2 \f$ */ + MULTIARCH inline Scalar f (const Scalar& _x) const { Scalar v = _x*_x - Scalar(1.); return v*v; } + /*! \brief Defines the smooth first order weighting function \f$ \nabla w(x) = 4x(x^2-1) \f$ */ + MULTIARCH inline Scalar df (const Scalar& _x) const { return Scalar(4.)*_x*(_x*_x-Scalar(1.)); } + /*! \brief Defines the smooth second order weighting function \f$ \nabla^2 w(x) = 12x^2-4 \f$ */ + MULTIARCH inline Scalar ddf(const Scalar& _x) const { return Scalar(12.)*_x*_x - Scalar(4.); } +};//class SmoothWeightKernel + +}// namespace Grenaille + + +#endif //_GRENAILLE_WEIGHT_KERNEL_ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille.bib b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille.bib new file mode 100644 index 000000000..83203c5c8 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille.bib @@ -0,0 +1,80 @@ +@inproceedings{Pauly:2002:PSSimplification, + author = {Pauly, Mark and Gross, Markus and Kobbelt, Leif P.}, + title = {Efficient Simplification of Point-sampled Surfaces}, + booktitle = {Proceedings of the Conference on Visualization '02}, + series = {VIS '02}, + year = {2002}, + isbn = {0-7803-7498-3}, + location = {Boston, Massachusetts}, + pages = {163--170}, + numpages = {8}, + url = {http://dl.acm.org/citation.cfm?id=602099.602123}, + acmid = {602123}, + publisher = {IEEE Computer Society}, + address = {Washington, DC, USA}, +} + +@article{Guennebaud:2007:APSS, + author = {Guennebaud, Ga\"{e}l and Gross, Markus}, + title = {Algebraic point set surfaces}, + journal = {ACM Trans. Graph.}, + issue_date = {July 2007}, + volume = {26}, + number = {3}, + month = jul, + year = {2007}, + issn = {0730-0301}, + articleno = {23}, + url = {http://doi.acm.org/10.1145/1276377.1276406}, + doi = {10.1145/1276377.1276406}, + acmid = {1276406}, + publisher = {ACM}, + address = {New York, NY, USA}, + keywords = {moving least square surfaces, point based graphics, sharp features, surface representation} +} + +@article{Mellado:2012:GLS, + author = {Mellado, Nicolas and Guennebaud, Ga\"{e}l and Barla, Pascal and Reuter, Patrick and Schlick, Christophe}, + title = {Growing Least Squares for the Analysis of Manifolds in Scale-Space}, + journal = {Comp. Graph. Forum}, + issue_date = {August 2012}, + volume = {31}, + number = {5}, + month = aug, + year = {2012}, + issn = {0167-7055}, + pages = {1691--1701}, + numpages = {11}, + url = {http://dx.doi.org/10.1111/j.1467-8659.2012.03174.x}, + doi = {10.1111/j.1467-8659.2012.03174.x}, + acmid = {2346805}, + publisher = {John Wiley and Sons, Inc.}, + address = {New York, NY, USA} +} + + +@inproceedings{Mellado:2013:SSC, + author = {Mellado, Nicolas and Barla, Pascal and Guennebaud, Ga\"{e}l and Reuter, Patrick and Duquesne, Gregory}, + title = {Screen-Space Curvature for Production-Quality Rendering and Compositing}, + keywords = {curvature, ray-tracing, shading, screen-space}, + booktitle = {SIGGRAPH Talk Program}, + publisher = {ACM New York}, + address = {Anaheim, United States}, + year = {2013}, + url = {http://hal.inria.fr/hal-00813560}, + month = Aug +} + + +@article{Chen:2013:NOMG, +author = {Chen, Jiazhou and Guennebaud, Ga\"{e}l and Barla, Pascal and Granier, Xavier}, +title = {Non-oriented MLS Gradient Fields}, +volume = {32}, +number = {8}, +issn = {1467-8659}, +url = {http://dx.doi.org/10.1111/cgf.12164}, +doi = {10.1111/cgf.12164}, +pages = {98--109}, +year = {2013}, +} + diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille.mdoc new file mode 100644 index 000000000..fdd4e1cfc --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille.mdoc @@ -0,0 +1,244 @@ +/*! + + \brief This module provides efficient methods for the fitting and analysis of point-clouds in arbitrary dimensions. + + */ +namespace Grenaille +{ + /*! + \brief Concepts used in Grenaille + + The Grenaille module is structured around five concepts: + - one defining the structure a sample (PointConcept); + - two defining how neighbor samples should be weighted (WeightKernelConcept and WeightFuncConcept); + - two defining how to perform fitting and what to extract from it (FittingProcedureConcept and FittingExtensionConcept). + + In practice, one must first determine which concept implementations they need, and then gather them in what we call a Basket. + \code + typedef + Basket myFit; // Final structure to fit and extend a primitive over weighted samples + \endcode + + \see The \ref grenaille_user_manual_page demonstrates step by step how to use a Basket for a simple application. + + \see For more details about each concept and examples of how to use them, browse through the pages below. + */ + namespace Concept{ + /*! + \brief Describes the procedure to fit a primitive to point samples. + + A typical example use would go like this: + \code + typedef Basket Fit; + MyWeightingFunction w ( some_parameters ); + + // Create a fit object + Fit fit; + + // init the internal state with respect to the reference position + fit.init( referencePosition ); + + // set the weighting function. Has no influence of the other internals + fit.setWeightFunc( w ); + + foreach neighbors of referencePosition + fit.addNeighbor(neighbor); + + fit.finalize(); + + if(fit.isStable()) + { + // use the fit + // ... + } + \endcode + + */ + template < class DataPoint, class _WFunctor, typename T = void > + class FittingProcedureConcept { + public: + /**************************************************************************/ + /* Initialization */ + /**************************************************************************/ + /*! + \brief Init the WeightFunc, without changing the other internal states + \warning Must be called be for any computation + */ + void setWeightFunc (const WFunctor& w){}; + + /*! + \brief Set the evaluation position and reset the internal states. + \warning Must be called be for any computation + */ + void init (const VectorType& evalPos){}; + + + /**************************************************************************/ + /* Processing */ + /**************************************************************************/ + /*! + \brief Add a neighbor to perform the fit + \return false if param nei is not a valid neighbour (weight = 0) + */ + bool addNeighbor(const DataPoint &nei){}; + + /*! + \brief Finalize the fitting procedure + \return the corresponding state of the fitting + \warning Must be called be for any use of the fitting output + */ + FIT_RESULT finalize (){}; + }; + + /*! + \brief Describes how to add features to an existing fitting procedure. + + A typical example use would go like this: + \code + typedef Basket ExtendedFit; + MyWeightingFunction w ( some_parameters ); + + // Create a fit object + ExtendedFit extFit; + + // init the internal state with respect to the reference position + extFit.init( referencePosition ); + + // set the weighting function. Has no influence of the other internals + extFit.setWeightFunc( w ); + + foreach neighbors of referencePosition + extFit.addNeighbor(neighbor); + + extFit.finalize(); + + if(extFit.isStable()) + { + // use the result of the fit and its extensions + // ... + } + \endcode + + */ + template < class DataPoint, class _WFunctor, typename T = void > + class FittingExtensionConcept { + public: + /**************************************************************************/ + /* Initialization */ + /**************************************************************************/ + /*! \see FittingProcedureConcept::setWeightFunc */ + void setWeightFunc (const WFunctor& w){}; + + /*! \see FittingProcedureConcept::init */ + void init (const VectorType& evalPos){}; + + + /**************************************************************************/ + /* Processing */ + /**************************************************************************/ + /*! \see FittingProcedureConcept::addNeighbor */ + bool addNeighbor(const DataPoint &nei){}; + + /*! \see FittingProcedureConcept::finalize */ + FIRESULT finalize (){}; + }; + + + /*! + \brief A 1D weighting function and its derivatives. + */ + template + class WeightKernelConcept{ + public: + typedef _Scalar Scalar; + + //! \brief Apply the weighting kernel to the scalar value \f$f(x)\f$ + MULTIARCH inline Scalar f (const Scalar& x) const {} + //! \brief Apply the first derivative of the weighting kernel to the scalar value \f$f'(x)\f$ + MULTIARCH inline Scalar df (const Scalar& x) const {} + //! \brief Apply the second derivative of the weighting kernel to the scalar value \f$f''(x)\f$ + MULTIARCH inline Scalar ddf(const Scalar& x) const {} + };// class WeightKernelConcept + + + /*! + \brief Applies a Concept::WeightKernelConcept to a Concept::PointConcept query. + */ + template + class WeightFuncConcept { + + public: + typedef typename DataPoint::Scalar Scalar; + typedef typename DataPoint::VectorType VectorType; + + /*! \brief Apply the weight function to a query. */ + MULTIARCH inline Scalar w(const VectorType& relativeQuery, + const DataPoint& attributes) const {} + + /*! \brief Apply the weight function differenciated in space to a query. */ + MULTIARCH inline VectorType spacedw(const VectorType& relativeQuery, + const DataPoint& attributes) const {} + + /*! \brief Apply the weight function differenciated in scale to a query. */ + MULTIARCH inline Scalar scaledw(const VectorType& relativeQuery, + const DataPoint& attributes) const {} + + /*! \brief Read access to the evaluation scale */ + MULTIARCH inline Scalar evalScale() const {} + };// class WeightFuncConcept + + /*! + \brief Definition of a point sample. + + All fitting methods in Grenaille rely on the definition of a Point type. Specific features might be required depending on the content of the Basket: for instance, an OrientedSphereFit will require points equipped with normals as illustrated in the \ref grenaille_user_manual_page. + + Grenaille does not provide an implementation for this concept so that users may adapt it to their own data structure. However, the following implementation can be used as a starting point for most 3D applications: + \code + class MyPoint{ + public: + enum {Dim = 3}; + typedef float Scalar; + typedef Eigen::Matrix VectorType; + + MULTIARCH inline MyPoint(const VectorType &pos = VectorType::Zero()) + : _pos(pos) {} + + MULTIARCH inline const VectorType& pos() const { return _pos; } + MULTIARCH inline VectorType& pos() { return _pos; } + + private: + VectorType _pos; + }; + \endcode + */ + class PointConcept{ + public: + /*! \brief Defines the ambient space dimension, 3 in this example */ + enum {Dim = 3}; + /*! \brief Defines the type used ton encode scalar values */ + typedef float Scalar; + /*! \brief Defines type used ton encode vector values */ + typedef Eigen::Matrix VectorType; + + /*! \brief Default constructor */ + MULTIARCH inline PointConcept(){} + + /*! \brief Read access to the position property */ + MULTIARCH inline const VectorType& pos() const {} + /*! \brief Write access to the position property */ + MULTIARCH inline VectorType& pos() {} + }; //class PointConcept + + } // End namespace Concept +} // End namespace Grenaille diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example.mdoc new file mode 100644 index 000000000..b2ac84a0a --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example.mdoc @@ -0,0 +1,20 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +/*! + \page grenaille_example_page Grenaille examples + + Here is a list of all Grenaille examples : + - \subpage grenaille_example_cxx_basic_page : Basic use of Grenaille. + - \subpage grenaille_example_cxx_fit_plane_page : Basic use of plane fitting. + - \subpage grenaille_example_cxx_binding_page : How to bind Grenaille with your own data structures. + - \subpage grenaille_example_opengl_circle_page : How to create new classes for baskets using OpenGL and VBO. + - \subpage grenaille_example_cxx_ssc_page : Calculate Screen Space Curvature using CUDA/C++. + - \subpage grenaille_example_python_ssc_page : Calculate Screen Space Curvature using Python and CUDA. + - \subpage grenaille_example_pcl_page : How to use Grenaille with Point cloud library (PCL). A basic example to compute and visualize surface curvature. + + */ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_cxx_basic.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_cxx_basic.mdoc new file mode 100644 index 000000000..be90ef30a --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_cxx_basic.mdoc @@ -0,0 +1,15 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +/*! + \page grenaille_example_cxx_basic_page Grenaille basic CPU + + This is an example of how to use Grenaille to compute + the GLS Geometric variation on random data. + + \include Grenaille/cpp/grenaille_basic_cpu.cpp +*/ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_cxx_binding.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_cxx_binding.mdoc new file mode 100644 index 000000000..d543fdea4 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_cxx_binding.mdoc @@ -0,0 +1,18 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +/*! + \page grenaille_example_cxx_binding_page Grenaille data-structure binding + + This is an example of how to instanciate the Grenaille::Concept::PointConcept + in order to use Grenaille on existing data structures without any memory + duplication. We apply exactly the same processing than in + \ref grenaille_example_cxx_basic_page, but this time by taking an interlaced + raw buffer as input. + + \include Grenaille/cpp/grenaille_binding.cpp +*/ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_cxx_fit_plane.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_cxx_fit_plane.mdoc new file mode 100644 index 000000000..c3dc403f2 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_cxx_fit_plane.mdoc @@ -0,0 +1,14 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +/*! + \page grenaille_example_cxx_fit_plane_page Grenaille basic plane fit + + This is an example of how to use Grenaille to compute surface variation on random data with plane fitting. + + \include Grenaille/cpp/grenaille_fit_plane.cpp +*/ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_cxx_ssc.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_cxx_ssc.mdoc new file mode 100644 index 000000000..fe7e617ca --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_cxx_ssc.mdoc @@ -0,0 +1,64 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +/*! + \page grenaille_example_cxx_ssc_page Screen Space Curvature using Cuda/C++ + + \section cxx_ssgl_intro_sec Introduction + + This is an example that use Grenaille to compute Screen Space Curvature in + C++ using Cuda. + + \subsection cxx_ssgl_sec_dep_subsec Installation and usage + This example requires the following third-party library : + - freeimageplus + + as well as a working cuda setup (tested with 5.0 and 7.0), and the lastest version of eigen dev branch which manage nvcc compiler. + + To compile and run the example, call + \code + cd build && make ssgls + cd examples/Grenaille/cpp && ./ssgls + \endcode + + This will takes the two input pictures packed in this example (in "data" directory) and compute the curvature for + a screenspace neighborhood 10x10 pixels. + \image html examples/ssgls_input.png "Screen-Space Curvature typical input. Left: world coordinates. Right: remapped normal vectors" + + It will generate this picture + \image html examples/ssgls_result1.png "Screen-Space Curvature estimation" + + \section cxx_ssgl_cuda_sec Cuda programming + + Here are the technical details related to the cuda and C++ biding for + screen-space curvature estimation. + + \subsection cxx_ssgl_cuda_mypoint_sec Define fitting data structure + \snippet Grenaille/cpp/ssgls.cu mypoint + + + \subsection cxx_ssgl_cuda_weight_sec Define weighting functions + \snippet Grenaille/cpp/ssgls.cu w_def + + \subsection cxx_ssgl_cuda_fit_sec Define fitting primitive + \snippet Grenaille/cpp/ssgls.cu fit_def + + + \subsection cxx_ssgl_cuda_kernel_sec Kernel + \snippet Grenaille/cpp/ssgls.cu kernel + + \subsection cxx_ssgl_cuda_access_sec Memory access + We format the input data, filled by dimension (in object space) + and then by the screen-space coordinates: + \snippet Grenaille/cpp/ssgls.cu data_acces + + + \section cxx_ssgl_sec The whole code + We use freeimageplus to format input data. + + \include Grenaille/cpp/ssgls.cu + */ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_opengl_circle.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_opengl_circle.mdoc new file mode 100644 index 000000000..8088baef8 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_opengl_circle.mdoc @@ -0,0 +1,17 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +/*! + \page grenaille_example_opengl_circle_page Grenaille Baskets OpenGL + + This is an example of how to use Grenaille to create new classes for baskets. This one allow you to plot a fit (circle, 2D) using OpenGL's VBOs. + + It can generate this picture : + \image html examples/circles.png "Circles using OpenGL and VBOs" + + \include Grenaille/opengl/circleGL.h +*/ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_pcl.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_pcl.mdoc new file mode 100644 index 000000000..def0939d5 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_pcl.mdoc @@ -0,0 +1,51 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +/*! + \page grenaille_example_pcl_page Using Grenaille to compute surface curvature in PCL + + \section pcl_intro_sec Introduction + + This is an example that use Grenaille to compute surface curvature with Point Cloud Library (PCL). + Of course, you have to install the last version of PCL (tested with v1.6) to make the example works. It provides a class implementation to be incorporate in the PCL tree, section "features", + and a method to use that feature and visualize data using a colormap. + + \subsection pcl_usage_sec Usage + + To compile this example, we provide a cmake file. All you have to do is to add the following instruction in your cmake process for patate examples : + + \code + -DCompilePCLExample=ON + \endcode + + You can also turn it on by checking the option with the cmake GUI. + + \warning Be sure to have the "bun_zipper.ply" file provided in the right build directory for a good program execution. + + \note Tip if you are using visual studio : if you have a problem with boost or pcl exceptions, go to Project -> Properties -> C/C++ -> Code Generation -> Enable C++ exceptions and turn it to "Yes (/EHsc)" + + Then execute the program and you will visualize this colored point cloud : + \image html examples/pcl_wrapper.png "Surface Curvature on a point cloud using GLS method" + + \section pcl_wrapper_sec PCL wrapper + + Here is the PCL implementation of our method. + + pcl_wrapper.h + \include Grenaille/pcl/pcl_wrapper.h + + pcl_wrapper.hpp + \include Grenaille/pcl/pcl_wrapper.hpp + + pcl_wrapper.cpp + \include Grenaille/pcl/pcl_wrapper.cpp + + \section pcl_main_sec Compute and visualization + + Now, a basic method to use the previous class and to visualize the result. + \include Grenaille/pcl/main.cpp + */ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_python_ssc.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_python_ssc.mdoc new file mode 100644 index 000000000..98f0a5113 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_example_python_ssc.mdoc @@ -0,0 +1,91 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +/*! + \page grenaille_example_python_ssc_page Screen Space Curvature using Cuda and Python + + \section pyssgl_intro_sec Introduction + + This is an example that use Grenaille to compute Screen Space Curvature in + python using Cuda. + + \subsection pyssgl_intro_sec_dep_subsec Installation and usage + This example requires the following python packages: + - pycuda + - matplotlib + + as well as a working cuda setup (tested with 5.0 and 7.0), and the lastest version of eigen dev branch which manage nvcc compiler. + + To compile and run the example, call + \code + cd build && make ssgls_py + cd examples/Grenaille/python && python ssgls.py -s 10 -p ./data/ssgls_sample_wc.png -n ./data/ssgls_sample_normal.png + \endcode + + The script will load the input pictures and compute the curvature for + a screenspace neighborhood 10x10 pixels. + \image html examples/ssgls_input.png "Screen-Space Curvature typical input. Left: world coordinates. Right: remapped normal vectors" + + It will generate this picture + \image html examples/ssgls_result1.png "Screen-Space Curvature estimation" + + as well as the following trace on the standard output + \code + Load CUDA kernel ........... DONE in 0.024 seconds. + Init Input data ............ DONE in 13.390 seconds. + Init queries ............... DONE in 0.720 seconds. + Set memory configuration ... DONE in 0.004 seconds. + Launch kernel .............. DONE in 1.505 seconds. + + ###########Configuration############# + Image size: 1902 x 911 + NbQueries: 1732722 + Scale: 10 + ################Results################ + Max value: 25.3449363708 + Min value: -31.8370857239 + ####################################### + \endcode + + \note Don't worry about the important execution time, this example uses a naive + and inefficient approach to represent queries with numpy, making the + total process really slow. However, the real computation time in cuda is + indicated by the last line of the timing trace. Note that we do not use any + re-sampling technique, that is why this step is directly related to the scale + parameter. + + + \section pyssgl_cuda_sec Cuda programming + + Here are the technical details related to the cuda and python biding for + screen-space curvature estimation. + + \subsection pyssgl_cuda_mypoint_sec Define fitting data structure + \snippet Grenaille/python/ssgls.cu mypoint + + + \subsection pyssgl_cuda_weight_sec Define weighting functions + \snippet Grenaille/python/ssgls.cu w_def + + \subsection pyssgl_cuda_fit_sec Define fitting primitive + \snippet Grenaille/python/ssgls.cu fit_def + + + \subsection pyssgl_cuda_kernel_sec Kernel + \snippet Grenaille/python/ssgls.cu kernel + + \subsection pyssgl_cuda_access_sec Memory access + We use numpy to format the input data, filled by dimension (in object space) + and then by the screen-space coordinates: + \snippet Grenaille/python/ssgls.cu data_acces + + + \section pyssgl_python_sec Python script + We use numpy to format input data. + + \include Grenaille/python/ssgls.py + */ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_user_manual.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_user_manual.mdoc new file mode 100644 index 000000000..c2130a805 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Grenaille/grenaille_user_manual.mdoc @@ -0,0 +1,311 @@ +namespace Grenaille +{ +/*! + \page grenaille_user_manual_page User Manual + + \authors Nicolas Mellado, Gautier Ciaudo, Gael Guennebaud, Pascal Barla + + \section grenaille_user_manual_intro Introduction + + This module is dedicated to the smooth fitting of point clouds and extraction of useful geometric properties. Figure 1(a) shows a typical example in 2D: + we reconstruct a potential function (shown in fake colors) from a few 2D points equipped with normals; then the 0-isoline of this potential gives us an + implicit curve (in orange) from which we can readily extract further properties like curvature. A great benefit of this implicit technique \cite Guennebaud:2007:APSS is that it works + in arbitrary dimensions: Figures 1(b-c) show how we reconstruct an implicit 3D surface with the same approach, starting from a 3D point cloud. Working with + meshes then simply consists of only considering their vertices. + + This is just the tip of the iceberg though, as we also provide methods for dealing with points equipped with non-oriented normals \cite Chen:2013:NOMG, techniques to analyze + points clouds in scale-space to discover salient structures \cite Mellado:2012:GLS, methods to compute multi-scale principal curvatures \cite Mellado:2013:SSC and methods to compute surface variation using a plane instead of a sphere for the fitting \cite Pauly:2002:PSSimplification. See the table below: + + Primitive | Supported Input | Fitting techniques | Analysis/Tools | Other usages | + ----------------- | ------------------- | --------------------------------------------- | -------------------------------------------------- | ------------ | + Plane | Points | CovariancePlaneFit | Surface Variation\cite Pauly:2002:PSSimplification | | + Algebraic Sphere | Oriented points | OrientedSphereFit \cite Guennebaud:2007:APSS | GLS\cite Mellado:2012:GLS | Ray Traced Curvature\cite Mellado:2013:SSC | + Algebraic Sphere | Non-oriented points | UnorientedSphereFit \cite Chen:2013:NOMG | | | + + In the following, we focus on a basic use of the module, using 3D points equipped with oriented normals fitted by a Grenaille::AlgebraicSphere. First, one must set up data samples that interface + with an external code; then run the fitting process in itself, and finally collect outputs. We also show how to compute curvatures with the very same approach, + as this is a common requirement of many geometric processing algorithms. + + \image html interpolation.png "Figure 1: (a) An implicit 2D curve fit to a 2D point cloud. (b) A 3D point cloud shown with splats. (c) An implicit 3D surface reconstructed from (b)." + + + \subsection grenaille_user_manual_codeStructure Code structure + We structured Grenaille as follow: a "core" folder defining operators that rely on no data structure and work both with CUDA and C++; and an "algorithms" folder with methods that may interface with user-provided data structures, with optimized solutions in C++, CUDA or both. + The rationale behind this separation is to provide two levels of complexity: core operators implement atomic scientific contributions that are agnostic of the host application; algorithms implement step-by-step procedures that will likely rely on core operators and/or structure traversal. + + In its current state, we do not have algorithms for Grenaille implemented: it's work in progress (we are open to contributions). If you want to use Grenaille, just include its main header: + \code +#include + \endcode + + \subsection grenaille_user_manual_cuda Cuda + Grenaille can be used directly on GPU, thanks to several mechanisms: + - Eigen Cuda capabilities, see Eigen documentation for more details. You *need* to use a consistent Eigen::Index on both CPU and GPU if you plan to transfer memory between the computing units. That's why we recommend to set the following preprocessor variable when compiling your project: \verbatim -DEIGEN_DEFAULT_DENSE_INDEX_TYPE=int\endverbatim + - C++03 Compliant: right now we *do not* use C++11 in Patate, to maximize compatibility across platforms and Cuda versions. + - Automatic CPU/GPU compilation qualifiers. We use the macro + \code + MULTIARCH void function(); + \endcode + to use the same code for C++ and CUDA. It has no effect when the code is compiled with GCC or Clang, but it will force the compilation for both host and device architectures when compiling with nvcc. A similar macro system is provided for mathematical functions, to switch between STL and CUDA versions. + + Check the \ref grenaille_example_cxx_ssc_page "C++/Cuda" and \ref grenaille_example_python_ssc_page "Python/Cuda" (using PyCuda) examples for more details and how-to. + + You are now ready to read the next sections, and discover how to interface Grenaille with your data-structures, how to fit various primitives and compute geometrical and differential properties. + + \section grenaille_user_manual_datas Data Samples + + The first step needed to use Grenaille is to define how samples are represented inside the module. + A strength of Grenaille is to define all these structures at compile time to generate optimized code for fast evaluation at runtime. + + The class Grenaille::Concept::PointConcept defines the interface that has to be implemented to represent a sample. Observe that there is no need for data conversion: all you need to do is to indicate how to access existing data. + + \warning You should avoid data of low magnitude (i.e., 1 should be a significant value) to get good results; thus rescaling might be necessary. + + As an example, let's fit a Grenaille::AlgebraicSphere onto points equipped with normals using the Grenaille::OrientedSphereFit. To this end, we must define a structure MyPoint containing a normal vector and its associated accessors. Depending on the fitting procedure we will use, we may need to define a Matrix Type. This is for instance required to compute \ref grenaille_user_manual_subsection_principalcurvatures. + This leads to the following class: + \code + using namespace Grenaille; + + // This class defines the input data format + class MyPoint + { + public: + enum {Dim = 3}; + typedef double Scalar; + typedef Eigen::Matrix VectorType; + typedef Eigen::Matrix MatrixType; // Type needed by Grenaille::GLSCurvatureHelper + + MULTIARCH inline MyPoint(const VectorType &pos = VectorType::Zero(), + const VectorType& normal = VectorType::Zero()) + : _pos(pos), _normal(normal) {} + + MULTIARCH inline const VectorType& pos() const { return _pos; } + MULTIARCH inline const VectorType& normal() const { return _normal; } + + MULTIARCH inline VectorType& pos() { return _pos; } + MULTIARCH inline VectorType& normal() { return _normal; } + + private: + VectorType _pos, _normal; + }; + \endcode + + To make the code more readable, we also define typedef helpers for Scalar and + Vector types outside of the MyPoint class: + \code + typedef MyPoint::Scalar Scalar; + typedef MyPoint::VectorType VectorType; + \endcode + + \section grenaille_user_manual_Fitting Fitting Process + + Two template classes must be specialized to indicate how a fit will be applied. + + The first step consists in identifying a weighting function: it defines how neighbor samples will contribute to the fit, as illustrated in Figure 2(a) in 2D. In this example, we choose a weight based on the Euclidean distance using Grenaille::DistWeightFunc, remapped through a bisquare kernel defined in Grenaille::SmoothWeightKernel: + \code + typedef DistWeightFunc > WeightFunc; + \endcode + + The second step identifies a complete fitting procedure through the specialization of a Grenaille::Basket. In our example, we want to apply an Grenaille::OrientedSphereFit to input data points, which outputs an Grenaille::AlgebraicSphere by default. We further require such a sphere to be reparametrized using a Grenaille::GLSParam extension, which provides more intuitive parameters. This leads to the following specialization: + + \code + typedef Basket Fit1; + \endcode + + At this point, most of the hard job has already been performed. + All we have to do now is to provide an instance of the weight function, where t refers to the neighborhood size, and iniate the fit at an arbitrary position p. + \code + // Create the previously defined fitting procedure + Fit1 fit; + + // Set a weighting function instance + fit.setWeightFunc(WeightFunc(t)); + + // Set the evaluation position + fit.init(p); + \endcode + + Then neighbors are added sequentially: in this example, we traverse a simple array, and samples outside of the neighborhood are automatically ignored by the weighting function. + Once all neighbors have been incorporated, the fit is performed and results stored in the specialized Basket object. STL-like iterators can be used directly for the fit by calling + \code + fit.compute(vecs.begin(), vecs.end()); + \endcode + + Internally, the container is traversed and the method finalize is called at the end: + \code + // Iterate over samples and fit the primitive + for(vector::iterator it = vecs.begin(); it != vecs.end(); it++) + fit.addNeighbor(*it); + + //finalize fitting + fit.finalize(); + \endcode + + After calling finalize or compute, it is better to test the return state of the fitting before using it. There are diffent states but the most important one is STABLE. + + \code + FIT_RESULT eResult = fit.compute(vecs.begin(), vecs.end()); // or = fit.finalize(); + if(eResult == STABLE) + { + //do things... + } + \endcode + + You may also use accessors to perform the same check: + + \code + fit.compute(vecs.begin(), vecs.end()); + + if(fit.isStable()) //You can also check the function isReady() + { + //do things; + } + \endcode + + \image html gls.png "Figure 2. (a) Fitting a 2D point cloud of positions and normals at a point p (in red) requires to define a weight function of size t (in green). (b) This results in an implicit scalar field (in fake colors), from which parameters of a local spherical surface can be extracted: an offset tau, a normal eta and a curvature kappa." + + \section grenaille_user_manual_outputs Basic Outputs + + Now that you have performed fitting, you may use its outputs in a number of ways (see Figure 2(b) for an illustration in 2D). + + \subsection grenaille_user_manual_subsection_access_field Scalar field + + You may directly access properties of the fitted scalar-field, as defined in Grenaille::AlgebraicSphere : + \code + cout << "Value of the scalar field at the initial point: " + << p.transpose() + << " is equal to " << fit.potential(p) + << endl; + + cout << "Its gradient is equal to: " + << fit.primitiveGradient(p).transpose() + << endl; + \endcode + This generates the following output: + \code + Value of the scalar field at the initial point: 0 0 0 is equal to -0.501162 + Its gradient is equal to: 0.00016028 0.000178782 -0.000384989 + \endcode + + \subsection grenaille_user_manual_subsection_access_sphere Sphere + + You may rather access properties of the fitted sphere (the 0-isosurface of the fitted scalar field), as defined in Grenaille::AlgebraicSphere : + \code + cout << "Center: [" << fit.center().transpose() << "] ; radius: " << fit.radius() << endl; + \endcode + You will obtain: + \code + Center: [-0.000160652 -0.000179197 0.000385884] ; radius: 1.00232 + \endcode + + Alternatively, you may prefer accessing parameters provided by the Grenaille::GLSParam extension: + \code + cout << "Fitted Sphere: " << endl + << "\t Tau : " << fit.tau() << endl + << "\t Eta : " << fit.eta().transpose() << endl + << "\t Kappa: " << fit.kappa() << endl; + \endcode + You will obtain: + \code + Fitted Sphere: + Tau : -0.501162 + Eta : 0.35325 0.394028 -0.848502 + Kappa: 0.997682 + \endcode + + \subsection grenaille_user_manual_subsection_access_other Other methods + + Thanks to the Grenaille::AlgebraicSphere, you may also project an arbitrary point onto the fitted sphere via: + \code + cout << "The initial point " << p.transpose() << endl + << "Is projected at " << fit.project(p).transpose() << endl; + \endcode + You will then obtain: + \code + The initial point 0 0 0 + Is projected at 0.353911 0.394765 -0.850088 + \endcode + + + \section grenaille_user_manual_cuvature Computing Curvatures + + + This part presents how to compute curvature values from the various fitting + procedures and extensions available in Grenaille. + + \subsection grenaille_user_manual_subsection_meancurvature Mean Curvature + + A fast approximation of the mean curvature is provided by GLSParam::kappa() and illustrated at two scales on a 3D surface in Figure 3: + \code + typedef Basket Fit; // GLS reparametrization + + Fit fit; + // Fit primitive + // ... + + // Get mean curvature + Scalar curvature = fit.kappa(); + \endcode + + It can also be extracted by analysing the spatial derivatives of fitted normals, + provided by GLSDer::deta(). Note that the computational cost is more important + in this case. + \code + typedef Basket Fit; // GLS differentiation + + Fit fit; + // Fit primitive + // ... + + // Mean curvature values is given by half of the trace of the jacobian matrix + MatrixType jacobian = fit.deta(); + cout << " Mean curvature: " << 0.5 * jacobian.trace() << endl; + \endcode + \note In this example, we build the Jacobian from fit.deta(), using an implicit cast from VectorArray to MatrixType. It will require a more careful conversion when the basket contains different extensions. More details \link Grenaille::internal::OrientedSphereDer here.\endlink + + \image html buste.png "Figure 3. Mean curvature computed at a fine (left) and a coarse (right) scale, and rendered with a simple color map (orange for concavities, blue for convexities)." + + \subsection grenaille_user_manual_subsection_principalcurvatures Principal Curvatures + + Principal curvatures and their associated directions can be computed by + an eigen decomposition of the spatial derivatives of eta, provided by + the extension GLSCurvatureHelper (based on the analysis of GLSDer::deta()): + \code + typedef Basket Fit; + + Fit fit; + // Fit primitive + // ... + // The eigen decomposition is called during the fit finalize + fit.finalize(); + + if(fit.isStable()) + { + // Get principal curvatures + Scalar k1 = fit.GLSk1(); + Scalar k2 = fit.GLSk2(); + + // Get associated directions + VectorType d1 = fit.GLSk1Direction(); + VectorType d2 = fit.GLSk2Direction(); + + // Get gaussian curvature + Scalar K = fit.GLSGaussianCurvature(); + } + \endcode + + \section grenaille_user_manual_going_further Going Further + + This page was intended to show you a standard use of the Grenaille module. However, there is more to it than Grenaille::OrientedSphereFit, as shown in its reference. For example, + you can create a totally different Basket with a Grenaille::CompactPlane and its extension Grenaille::CovariancePlaneFit using plane instead of algebraic sphere to procede the fit in order to compute Grenaille::CovariancePlaneFit::surfaceVariation. + We encourage you to make your own tests and have a look at our \ref grenaille_example_page "examples". It will give you ideas about how to use this module inside your own applications. + */ + } diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/CMakeLists.txt b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/CMakeLists.txt new file mode 100644 index 000000000..77dbd1658 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/CMakeLists.txt @@ -0,0 +1,15 @@ +project(Vitelotte) +cmake_minimum_required(VERSION 2.8) + + +FILE ( GLOB VITELOTTE_HEADERS + ${CMAKE_CURRENT_SOURCE_DIR}/Core/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/Utils/*.h +) + +FILE ( GLOB VITELOTTE_IMPL + ${CMAKE_CURRENT_SOURCE_DIR}/Core/*.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/Utils/*.hpp +) + +add_library (Vitelotte OBJECT ${VITELOTTE_HEADERS} ${VITELOTTE_IMPL}) diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/bezierPath.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/bezierPath.h new file mode 100644 index 000000000..5eb1ad820 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/bezierPath.h @@ -0,0 +1,198 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_BEZIER_PATH_ +#define _VITELOTTE_BEZIER_PATH_ + + +#include +#include + +#include + + +namespace Vitelotte +{ + + +enum BezierSegmentType +{ + BEZIER_EMPTY = 0, + BEZIER_LINEAR = 2, + BEZIER_QUADRATIC = 3, + BEZIER_CUBIC = 4 +}; + + +template < typename _Vector > +class BezierSegment { +public: + typedef typename _Vector::Scalar Scalar; + typedef _Vector Vector; + + typedef BezierSegment Self; + +public: + inline BezierSegment() + : m_type(BEZIER_EMPTY) {} + inline BezierSegment(BezierSegmentType type, const Vector* points) + : m_type(type) { + std::copy(points, points + type, m_points); + } + + inline BezierSegmentType type() const { return m_type; } + inline void setType(BezierSegmentType type) { m_type = type; } + + inline const Vector& point(unsigned i) const { + assert(i < m_type); + return m_points[i]; + } + inline Vector& point(unsigned i) { + assert(i < m_type); + return m_points[i]; + } + + inline Self getBackward() const { + Self seg; + seg.setType(type()); + for(unsigned i = 0; i < type(); ++i) { + seg.point(type() - 1 - i) = point(i); + } + return seg; + } + + void split(Scalar pos, Self& head, Self& tail) { + assert(m_type != BEZIER_EMPTY); + + // All the points of de Casteljau Algorithm + Vector pts[10]; + Vector* levels[] = { + &pts[9], + &pts[7], + &pts[4], + pts + }; + + // Initialize the current level. + std::copy(m_points, m_points + type(), levels[type() - 1]); + + // Compute all the points + for(int level = type()-1; level >= 0; --level) { + for(int i = 0; i < level; ++i) { + levels[level-1][i] = (Scalar(1) - pos) * levels[level][i] + + pos * levels[level][i+1]; + } + } + + // Set the segments + head.setType(type()); + tail.setType(type()); + + const unsigned last = type() - 1; + for(unsigned i = 0; i < type(); ++i) { + head.point(i) = levels[last - i][0]; + tail.point(i) = levels[i][i]; + } + } + + template < typename InIt > + void refineUniform(InIt out, unsigned nSplit) { + Self head; + Self tail = *this; + *(out++) = std::make_pair(Scalar(0), point(0)); + for(unsigned i = 0; i < nSplit; ++i) { + Scalar x = Scalar(i+1) / Scalar(nSplit + 1); + tail.split(x, head, tail); + *(out++) = std::make_pair(x, tail.point(0)); + } + *(out++) = std::make_pair(Scalar(1), point(type()-1)); + } + + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + +private: + BezierSegmentType m_type; + Vector m_points[4]; +}; + + +template < typename _Vector > +class BezierPath +{ +public: + typedef _Vector Vector; + +public: + static unsigned size(BezierSegmentType type) { return unsigned(type); } + +public: + inline BezierPath() {} + + inline unsigned nPoints() const { return m_points.size(); } + inline unsigned nSegments() const { return m_segments.size(); } + + inline BezierSegmentType type(unsigned si) const { return m_segments.at(si).type; } + inline unsigned nPoints(unsigned si) const { return size(type(si)); } + + inline const Vector& point(unsigned pi) const { return m_points.at(pi); } + inline Vector& point(unsigned pi) { return m_points.at(pi); } + inline const Vector& point(unsigned si, unsigned pi) const + { + assert(si < nSegments() && pi < nPoints(si)); + return m_points.at(m_segments[si].firstPoint + pi); + } + inline Vector& point(unsigned si, unsigned pi) + { + assert(si < nSegments() && pi < nPoints(si)); + return m_points.at(m_segments[si].firstPoint + pi); + } + + inline void setFirstPoint(const Vector& point) + { + assert(nPoints() == 0); + m_points.push_back(point); + } + + unsigned addSegment(BezierSegmentType type, const Vector* points) + { + assert(nPoints() != 0); + unsigned si = nSegments(); + Segment s; + s.type = type; + s.firstPoint = nPoints() - 1; + m_segments.push_back(s); + + for(unsigned i = 0; i < nPoints(si) - 1; ++i) + { + m_points.push_back(points[i]); + } + + return si; + } + + BezierSegment getSegment(unsigned si) const { + assert(si < nSegments()); + return BezierSegment(type(si), &m_points[m_segments[si].firstPoint]); + } + +private: + struct Segment { + BezierSegmentType type; + unsigned firstPoint; + }; + + typedef std::vector PointList; + typedef std::vector SegmentList; + +private: + PointList m_points; + SegmentList m_segments; +}; + + +} + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/elementBuilderBase.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/elementBuilderBase.h new file mode 100644 index 000000000..a8a4e9789 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/elementBuilderBase.h @@ -0,0 +1,54 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_ELEMENT_BUILDER_BASE_ +#define _VITELOTTE_ELEMENT_BUILDER_BASE_ + + +#include + +#include +#include + + +namespace Vitelotte +{ + + +/** + * \brief The base of every element builder. Provides default implementations + * of some methods. + */ +template < class _Mesh, typename _Scalar > +class ElementBuilderBase +{ +public: + typedef _Scalar Scalar; + typedef _Mesh Mesh; + + typedef Eigen::Matrix Vector; + typedef Eigen::Matrix Matrix; + typedef Eigen::Triplet Triplet; + + typedef typename Mesh::Face Face; + +public: + inline ElementBuilderBase() {} + + inline unsigned nExtraConstraints(const Mesh& /*mesh*/, Face /*element*/) const + { return 0; } + + template < typename Inserter > + inline void addExtraConstraints(Inserter& /*inserter*/, const Mesh& /*mesh*/, + Face /*element*/, SolverError* /*error*/) + {} +}; + + +} + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/femSolver.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/femSolver.h new file mode 100644 index 000000000..113140f32 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/femSolver.h @@ -0,0 +1,141 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_FEM_SOLVER_ +#define _VITELOTTE_FEM_SOLVER_ + +#include + +#include +#include + +#include "solverError.h" +#include "femUtils.h" +#include "vgMesh.h" + + +namespace Vitelotte +{ + + +/** + * \brief A diffusion solver based on the Finite Element Method. + * + * \tparam _Mesh The VGMesh type on which the solver operates. + * \tparam _ElementBuilder An ElementBuilder class that describe the type of + * elements used by the solver, and by extension the kind of diffusion to do. + */ +template < class _Mesh, class _ElementBuilder > +class FemSolver +{ +public: + typedef _Mesh Mesh; + typedef _ElementBuilder ElementBuilder; + typedef FemSolver Self; + + typedef typename ElementBuilder::Scalar Scalar; + +protected: + typedef typename Mesh::Node Node; + typedef typename Mesh::Vertex Vertex; + typedef typename Mesh::Face Face; + typedef typename Mesh::FaceIterator FaceIterator; + +public: + inline FemSolver(const ElementBuilder& elementBuilder = ElementBuilder()); + ~FemSolver(); + + /// \brief Build the internal stiffness matrix from `mesh`. + void build(const Mesh& mesh); + + /// \brief Solve the diffusion problem created by `build()` on `mesh`. + /// + /// Parameters of `build()` and `solve()` do not need to be the same, but + /// they must have the same connectivity. (In other words, only constrained + /// nodes values should differ.) + void solve(Mesh& mesh); + + /// \brief Returns a SolverError object describing the state of the solver. + inline const SolverError error() { return m_error; } + +public: + typedef Eigen::Matrix Matrix; + typedef Eigen::Triplet Triplet; + typedef Eigen::SparseMatrix StiffnessMatrix; + + typedef std::vector TripletVector; + typedef typename TripletVector::iterator TripletVectorIterator; + + typedef std::vector BoolVector; + typedef std::vector IndexMap; + + typedef Eigen::SimplicialLDLT LDLT; + + struct Block + { + TripletVector triplets; + StiffnessMatrix matrix; + LDLT* decomposition; + Matrix rhs; // Contains the rhs "extra" constraints + unsigned offset; // Offset to apply to block indices to get global indices + unsigned size; + unsigned nCoeffs; + + inline Block() : decomposition(0) {} + inline ~Block() { delete decomposition; } + }; + typedef std::vector BlockVector; + typedef typename BlockVector::iterator BlockIterator; + + struct BlockIndex + { + int block; + int index; + + inline BlockIndex(int block, int index) : block(block), index(index) {} + }; + typedef std::vector NodeMap; + +protected: + // Map each node to a row/column an split the problem in a set of + // independant sub-problems. + void preSort(const Mesh& mesh); + + // Compute the stiffness matrix coefficients. + void buildMatrix(const Mesh& mesh); + + // Factorize the stiffness matrix. + void factorize(); + +protected: + ElementBuilder m_elementBuilder; + + SolverError m_error; + + unsigned m_nUnknowns; + unsigned m_nConstraints; + + BlockVector m_blocks; + Eigen::VectorXi m_faceBlockMap; // The face -> block index map + TripletVector m_constraintTriplets; + StiffnessMatrix m_constraintBlock; + NodeMap m_nodeMap; // Map node->idx() -> (block id, index) + BoolVector m_fMask; // Mark faces processed by preSort + BoolVector m_nMask; // Mark nodes processed by preSort + Eigen::VectorXi m_fExtraIndices; // Each face with extra constraint is + // given a unique index in the range [0..n] + NodeMap m_fExtraMap; // Map m_fExtraIndex to a column index + // (no perm required) + Matrix m_x; +}; + + +} // namespace Vitelotte + +#include "femSolver.hpp" + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/femSolver.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/femSolver.hpp new file mode 100644 index 000000000..a6b54ea0b --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/femSolver.hpp @@ -0,0 +1,550 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include "femSolver.h" + + +namespace Vitelotte +{ + +namespace internal +{ + +template +struct CheckEigenSolverError +{ + static bool check(const Solver& solver, SolverError& error) + { + switch(solver.info()) { + case Eigen::Success: return false; + case Eigen::NumericalIssue: error.error("Numerical issue"); return true; + case Eigen::NoConvergence: error.error("No convergence"); return true; + case Eigen::InvalidInput: error.error("Invalid matrix"); return true; + } + error.error("Unknown Eigen error"); return true; + return true; + } +}; + + +template +bool checkEigenSolverError(const Solver& solver, SolverError& error) +{ + return CheckEigenSolverError::check(solver, error); +} + + +template < typename Solver > +class SolverInserter { +public: + typedef typename Solver::Scalar Scalar; + +protected: + typedef typename Solver::Mesh Mesh; + typedef typename Mesh::Face Face; + + typedef typename Solver::Matrix Matrix; + + typedef typename Solver::Triplet Triplet; + typedef typename Solver::TripletVector TripletVector; + + typedef typename Solver::BlockIndex BlockIndex; + typedef typename Solver::NodeMap NodeMap; + +public: + inline SolverInserter(TripletVector& blockCoeffs, TripletVector& consCoeffs, + Matrix& rhs, const NodeMap& nodeMap, + unsigned offset, unsigned extraOffset) + : m_blockCoeffs (blockCoeffs), + m_consCoeffs (consCoeffs), + m_rhs (rhs), + m_nodeMap (nodeMap), + m_offset (offset), + m_extraOffset (extraOffset) { + } + +#ifndef NDEBUG + void debug(unsigned nUnk, unsigned nCons, unsigned bi, unsigned size) { + m_nUnk = nUnk; + m_nCons = nCons; + m_bi = bi; + m_size = size; + } +#endif + +//#define DEBUG_INLINE __attribute__((always_inline)) +//#define DEBUG_INLINE __attribute__((noinline)) +#define DEBUG_INLINE + + DEBUG_INLINE void addCoeff(unsigned ni0, unsigned ni1, Scalar value) + { + BlockIndex bi0 = m_nodeMap[ni0]; + BlockIndex bi1 = m_nodeMap[ni1]; + + unsigned id = (bi0.block < 0) | ((bi1.block < 0) << 1); + switch(id) { + // Both are unknowns + case 0x00: { + assert(unsigned(bi0.block) == m_bi && unsigned(bi1.block) == m_bi); + assert(unsigned(bi0.index) < m_size); + assert(unsigned(bi1.index) < m_size); + m_blockCoeffs.push_back(Triplet( + std::max(bi0.index, bi1.index), + std::min(bi0.index, bi1.index), + value)); + break; + } + + // 0 is a constraint, 1 is an unknown + case 0x01: std::swap(bi0, bi1); // Fall-through + // 0 is an unknown, 1 is a constraint + case 0x02: { + assert(unsigned(bi0.block) == m_bi); + unsigned row = bi0.index + m_offset; + unsigned col = bi1.index; + assert(row < m_nUnk); + assert(col < m_nCons); + m_consCoeffs.push_back(Triplet(row, col, value)); + break; + } + + // Both are constraints + case 0x03: break; + + // Should not happen + default: assert(false); + } + } + + DEBUG_INLINE void addExtraCoeff(Face /*elem*/, unsigned ei, unsigned ni, Scalar value) + { + BlockIndex nbi = m_nodeMap[ni]; + unsigned eio = m_extraOffset + ei; + assert(eio < m_size); + + if(nbi.block >= 0) + { + assert(unsigned(nbi.block) == m_bi); + // extra should have greater indices + assert(nbi.index >= 0 && unsigned(nbi.index) < m_extraOffset); + m_blockCoeffs.push_back(Triplet(eio, nbi.index, value)); + } + else + { + unsigned row = eio + m_offset; + unsigned col = nbi.index; + assert(row < m_nUnk); + assert(col < m_nCons); + m_consCoeffs.push_back(Triplet(row, col, value)); + } + } + + template < typename Derived > + DEBUG_INLINE void setExtraRhs(Face /*elem*/, unsigned ei, const Eigen::DenseBase& value) + { + unsigned eio = m_extraOffset + ei; + assert(eio < m_size); + m_rhs.row(eio) = value; + } + + +private: + TripletVector& m_blockCoeffs; + TripletVector& m_consCoeffs; + Matrix& m_rhs; + const NodeMap& m_nodeMap; + unsigned m_offset; + unsigned m_extraOffset; +#ifndef DNDEBUG + unsigned m_nUnk; + unsigned m_nCons; + unsigned m_bi; + unsigned m_size; +#endif +}; + +#undef DEBUG_INLINE + +} + + +template < class _Mesh, class _ElementBuilder > +FemSolver<_Mesh, _ElementBuilder>::FemSolver(const ElementBuilder& elementBuilder) + : m_elementBuilder(elementBuilder) +{ +} + + +template < class _Mesh, class _ElementBuilder > +FemSolver<_Mesh, _ElementBuilder>::~FemSolver() +{ +} + + +template < class _Mesh, class _ElementBuilder > +void +FemSolver<_Mesh, _ElementBuilder>::build(const Mesh& mesh) +{ + // Cleanup error status + m_error.resetStatus(); + + // Compute node id to row/col mapping and initialize blocks + preSort(mesh); + if(m_error.status() == SolverError::STATUS_ERROR) return; + + // Fill the blocks + buildMatrix(mesh); + if(m_error.status() == SolverError::STATUS_ERROR) return; + + // Factorize the lhs + factorize(); +} + + +template < class _Mesh, class _ElementBuilder > +void +FemSolver<_Mesh, _ElementBuilder>::solve(Mesh& mesh) +{ + if(m_error.status() == SolverError::STATUS_ERROR) + { + return; + } + + // compute RHS + Matrix constraints = Matrix::Zero(m_nConstraints, mesh.nCoeffs()); + + for(typename Mesh::NodeIterator nit = mesh.nodesBegin(); + nit != mesh.nodesEnd(); ++nit) + { + BlockIndex bi = m_nodeMap[(*nit).idx()]; + if(mesh.isConstraint(*nit) && bi.block == -1 && bi.index >= 0) + { + constraints.row(bi.index) = mesh.value(*nit).template cast(); + } + } + + m_x.resize(m_nUnknowns, mesh.nCoeffs()); + Matrix b = m_constraintBlock * constraints; + + // Clear extra constraints + for(BlockIterator block = m_blocks.begin(); + block != m_blocks.end(); ++block) + { + block->rhs.setZero(); + } + + // Fill a Triplet vector with coefficients + fill m_b + m_constraintTriplets.clear(); + for(FaceIterator elem = mesh.facesBegin(); + elem != mesh.facesEnd(); ++elem) + { + typedef internal::SolverInserter Inserter; + + int bi = m_faceBlockMap((*elem).idx()); + if(bi < 0) continue; + Block& block = m_blocks[bi]; + int extraIndex = m_fExtraIndices((*elem).idx()); + unsigned extraOffset = (extraIndex < 0)? 0: m_fExtraMap[extraIndex].index; + + Inserter inserter(block.triplets, + m_constraintTriplets, + block.rhs, + m_nodeMap, + block.offset, + extraOffset); +#ifndef NDEBUG + inserter.debug(m_nUnknowns, m_nConstraints, bi, block.size); +#endif + m_elementBuilder.addExtraConstraints(inserter, mesh, *elem, &m_error); + + if(m_error.status() == SolverError::STATUS_ERROR) + { + return; + } + } + + // Solve block by block + for(BlockIterator block = m_blocks.begin(); + block != m_blocks.end(); ++block) + { + unsigned start = block->offset; + unsigned size = block->size; + + m_x.middleRows(start, size) = + block->decomposition->solve(block->rhs - b.middleRows(start, size)); + if(internal::checkEigenSolverError(*block->decomposition, m_error)) return; + } + + // Update mesh nodes + for(typename Mesh::NodeIterator nit = mesh.nodesBegin(); + nit != mesh.nodesEnd(); ++nit) + { + BlockIndex bi = m_nodeMap[(*nit).idx()]; + if(!mesh.isConstraint(*nit) && bi.block >= 0 && bi.index >= 0) + { + unsigned ri = m_blocks[bi.block].offset + bi.index; + mesh.value(*nit) = + m_x.row(ri).template cast(); + } + } +} + + +template < class _Mesh, class _ElementBuilder > +void +FemSolver<_Mesh, _ElementBuilder>::preSort(const Mesh& mesh) +{ + // FIXME: This algorithm assume there is only local constraints. + + typedef typename Mesh::HalfedgeAroundFaceCirculator HalfedgeCirculator; + typedef typename Mesh::HalfedgeAttribute HalfedgeAttribute; + typedef typename Mesh::Node Node; + + // 1- Count the number of faces with extra constraints and the total number + // of constraints. + unsigned nExtraFaces = 0; + unsigned nExtraCons = 0; + for(FaceIterator elem = mesh.facesBegin(); + elem != mesh.facesEnd(); ++elem) + { + unsigned nCons = m_elementBuilder.nExtraConstraints(mesh, *elem); + if(nCons) + { + ++nExtraFaces; + nExtraCons += nCons; + } + } + + // Note: these are not defivitive values. There may be less nodes than + // planed if not all of them are referenced by faces or some are deleted. + unsigned nNodes = mesh.nodesSize(); // Number of nodes (worst case) + unsigned uPos = 0; // r/c index of the last processed unknown node + unsigned uCount = 0; // total number of contributing unknown nodes + unsigned cPos = 0; // r/c index of the last processed constraint node + unsigned count = 0; // nb items in stack + unsigned extraFacesCount = 0; // nb faces with extra constrains + unsigned extraRangeStart = 0; // index of the first extra of the current range + unsigned elemExtraCount = 0; // nb extra node for the current block + + // Initialize some members + m_blocks.clear(); + m_blocks.reserve(32); + m_nodeMap.assign(nNodes, BlockIndex(-1, -1)); + m_fExtraIndices.resize(mesh.facesSize()); m_fExtraIndices.fill(-1); + m_fExtraMap.assign(nExtraFaces, BlockIndex(-1, -1)); + m_faceBlockMap.resize(mesh.facesSize()); m_faceBlockMap.fill(-1); + + FaceIterator faceIt = mesh.facesBegin(); + Eigen::VectorXi fStack(mesh.nFaces()); + m_fMask.assign(mesh.nFaces(), true); + m_nMask.assign(nNodes, true); + + // 2- Compute a mapping from node indices to column/row indices with a + // breadth first search. May split the problem in independant blocks. + + // Push the first face + m_blocks.push_back(Block()); + m_blocks.back().offset = uPos; + m_fMask[(*faceIt).idx()] = false; + fStack(count++) = (*faceIt).idx(); + + while(count) + { + // Pop a face + unsigned fi = fStack(--count); + unsigned bi = m_blocks.size() - 1; + Face face(fi); + + m_faceBlockMap(fi) = bi; + + // Process each node of each halfedges + HalfedgeCirculator hc = mesh.halfedges(face); + HalfedgeCirculator hcEnd = hc; + do { + assert(!mesh.isBoundary(*hc)); + + for(unsigned ai = 0; ai < Mesh::HALFEDGE_ATTRIB_COUNT; ++ai) { + HalfedgeAttribute attr = HalfedgeAttribute(ai); + if(!mesh.hasAttribute(attr)) continue; + + Node n = mesh.halfedgeNode(*hc, attr); + unsigned ni = n.idx(); + Node on = mesh.halfedgeOppositeNode(*hc, attr); + Face of = mesh.face(mesh.oppositeHalfedge(*hc)); + unsigned ofi = of.idx(); + bool isCons = mesh.isConstraint(n); + + if(!n.isValid()) { + m_error.error("Input mesh contains invalid nodes."); + return; + } + + // If the node is not yet pushed, do it + if(m_nMask[ni]) + { + m_nMask[ni] = false; + if(isCons) m_nodeMap[ni] = BlockIndex(-1, cPos++); + else m_nodeMap[ni] = BlockIndex(bi, uPos++); + } + + // If the node is used on the opposite face, this face must be + // in the same range, so push it. + if(!isCons && n == on && m_fMask[ofi]) + { + m_fMask[ofi] = false; + fStack(count++) = ofi; + } + } + + ++hc; + } while(hc != hcEnd); + + unsigned nElemExtra = m_elementBuilder.nExtraConstraints(mesh, face); + if(nElemExtra) + { + m_fExtraMap[extraFacesCount] = BlockIndex(bi, elemExtraCount); + m_fExtraIndices(fi) = extraFacesCount++; + elemExtraCount += nElemExtra; + } + + + // Finalize range when the stack is empty + if(!count) + { + // Search for unprocessed face + while(faceIt != mesh.facesEnd() && !m_fMask[(*faceIt).idx()]) ++faceIt; + + // NOTE: This bypass the bloc splitting mechanism, thus creating + // a single big bloc. For testing purpose and/or to allow + // non-local constraints. +// if(faceIt != mesh.facesEnd()) { +// fi = (*faceIt).idx(); +// m_fMask[fi] = false; +// fStack(count++) = fi; +// continue; +// } + + // Offset extra constraints indices so that they are > to node + // indices. It allow extra constraints (who have 0 on the diagonal) + // to be on the bottom-right of the matrix. + for(unsigned ei = extraRangeStart; ei < extraFacesCount; ++ei) + { + m_fExtraMap[ei].index += uPos; + } + uPos += elemExtraCount; + + // Finalize the block + m_blocks.back().size = uPos; + m_blocks.back().matrix.resize(uPos, uPos); + m_blocks.back().rhs.resize(uPos, mesh.nCoeffs()); + m_blocks.back().rhs.fill(0); + m_blocks.back().nCoeffs = 0; + + uCount += uPos; + uPos = 0; + extraRangeStart = extraFacesCount; + elemExtraCount = 0; + + // Push the first face of the next range, if any. + if(faceIt != mesh.facesEnd()) + { + fi = (*faceIt).idx(); + m_blocks.push_back(Block()); + m_blocks.back().offset = uCount; + m_fMask[fi] = false; + fStack(count++) = fi; + } + } + } + + // Compute the real number of unknowns and constraints. + m_nUnknowns = uCount; + m_nConstraints = cPos; + + m_constraintBlock.resize(m_nUnknowns, m_nConstraints); +} + + +template < class _Mesh, class _ElementBuilder > +void FemSolver<_Mesh, _ElementBuilder>::buildMatrix(const Mesh& mesh) +{ + // Pre-compute number of coefficients + for(FaceIterator elem = mesh.facesBegin(); + elem != mesh.facesEnd(); ++elem) + { + int bii = m_faceBlockMap((*elem).idx()); + if(bii < 0) continue; + Block& block = m_blocks[bii]; + block.nCoeffs += m_elementBuilder.nCoefficients(mesh, *elem, &m_error); + } + + // Reserve memory for triplets + for(BlockIterator block = m_blocks.begin(); + block != m_blocks.end(); ++block) + { + block->triplets.reserve(block->nCoeffs); + } + + // Compute triplets for all blocks + m_constraintTriplets.clear(); + for(FaceIterator elem = mesh.facesBegin(); + elem != mesh.facesEnd(); ++elem) + { + typedef internal::SolverInserter Inserter; + + int bi = m_faceBlockMap((*elem).idx()); + if(bi < 0) continue; + Block& block = m_blocks[bi]; + int extraIndex = m_fExtraIndices((*elem).idx()); + unsigned extraOffset = (extraIndex < 0)? 0: m_fExtraMap[extraIndex].index; + + Inserter inserter(block.triplets, + m_constraintTriplets, + block.rhs, + m_nodeMap, + block.offset, + extraOffset); +#ifndef NDEBUG + inserter.debug(m_nUnknowns, m_nConstraints, bi, block.size); +#endif + m_elementBuilder.addCoefficients(inserter, mesh, *elem, &m_error); + + if(m_error.status() == SolverError::STATUS_ERROR) + { + return; + } + } + + m_constraintBlock.setFromTriplets( + m_constraintTriplets.begin(), m_constraintTriplets.end()); + + // Fill the matrix + for(BlockIterator block = m_blocks.begin(); + block != m_blocks.end(); ++block) + { + block->matrix.setFromTriplets( + block->triplets.begin(), block->triplets.end()); + } +} + + +template < class _Mesh, class _ElementBuilder > +void FemSolver<_Mesh, _ElementBuilder>::factorize() +{ +//#ifdef _OPENMP +//#pragma omp parallel for schedule(static,1) +//#endif + for(BlockIterator block = m_blocks.begin(); + block != m_blocks.end(); ++block) + { + if(!block->decomposition) block->decomposition = new LDLT; + block->decomposition->compute(block->matrix); + if(internal::checkEigenSolverError(*block->decomposition, m_error)) return; + } +} + + +} // namespace Vitelotte diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/femUtils.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/femUtils.h new file mode 100644 index 000000000..57a956e79 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/femUtils.h @@ -0,0 +1,27 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_FEM_UTILS_ +#define _VITELOTTE_FEM_UTILS_ + +#include + + +namespace Vitelotte +{ + + +template +inline typename Eigen::MatrixBase::Scalar det2( + const Eigen::MatrixBase& _v0, const Eigen::MatrixBase& _v1); + + +} // namespace Vitelotte + +#include "femUtils.hpp" + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/femUtils.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/femUtils.hpp new file mode 100644 index 000000000..74c02563b --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/femUtils.hpp @@ -0,0 +1,21 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include "femUtils.h" + + +namespace Vitelotte +{ + + +template +inline typename Eigen::MatrixBase::Scalar det2(const Eigen::MatrixBase& _v0, const Eigen::MatrixBase& _v1) +{ + return _v0.x() * _v1.y() - _v0.y() * _v1.x(); +} + + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/fvElement.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/fvElement.h new file mode 100644 index 000000000..7d7e1880b --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/fvElement.h @@ -0,0 +1,426 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_FV_ELEMENT_ +#define _VITELOTTE_FV_ELEMENT_ + + +#include + +#include "../../common/defines.h" + +#include "linearElement.h" + + +namespace Vitelotte +{ + + +/** + * \brief Provides method to evaluate FV elements and their derivatives. + */ +template < typename _Scalar > +class FVElement : protected LinearElement<_Scalar> +{ +public: + typedef _Scalar Scalar; + typedef LinearElement Base; + + typedef Eigen::Matrix Vector; + typedef Eigen::Matrix Values; + typedef Eigen::Matrix Jacobian; + typedef Eigen::Matrix Hessian; + + typedef Eigen::Matrix BarycentricCoord; + +protected: + typedef Eigen::Matrix Vector3; + typedef Eigen::Matrix Matrix3; + typedef Eigen::Matrix Matrix2x3; + +public: + template < typename It > + MULTIARCH inline FVElement(It pointIt) + : Base(pointIt) + { + computeFromPoints(); + } + + template < typename Derived0, typename Derived1, typename Derived2 > + MULTIARCH inline FVElement( + const Eigen::MatrixBase& p0, + const Eigen::MatrixBase& p1, + const Eigen::MatrixBase& p2) + : Base(p0, p1, p2) + { + computeFromPoints(); + } + + using Base::projPoint; + using Base::edgeLength; + using Base::doubleArea; + + MULTIARCH inline const Matrix3& dldn() const + { + return m_dldn; + } + + MULTIARCH inline Scalar dldn(unsigned li, unsigned ni) const + { + assert(li < 3 && ni < 3); + return m_dldn(li, ni); + } + + using Base::bcProj; + + MULTIARCH inline Values eval(const BarycentricCoord& bc) const + { + Values eb; + Scalar bubble = _bubble(bc); + + for(int i = 0; i < 3; ++i) + { + Scalar gse = _gradientSubExpr(i, bc); + eb(i + 3) = 4 * _edgeSubExpr(i, bc) + + 4 * gse + - 12 * bubble; + eb(i + 6) = _gradientFactor(i) * gse; + } + + for(int i = 0; i < 3; ++i) + { + unsigned i1 = (i+1) % 3; + unsigned i2 = (i+2) % 3; + eb(i) = _vertexSubExpr(i, bc) + + 3 * bubble + + _vertexGradientFactor(i, i1) * eb(i1 + 6) + + _vertexGradientFactor(i, i2) * eb(i2 + 6); + } + + return eb; + } + + MULTIARCH inline Scalar eval(unsigned bi, const BarycentricCoord& bc) const + { + assert(bi < 9); + + Scalar v; + unsigned i = bi % 3; + switch(bi/3) + { + case 0: + { + unsigned i1 = (i+1) % 3; + unsigned i2 = (i+2) % 3; + v = _vertexSubExpr(i, bc) + + 3 * _bubble(bc) + + _vertexGradientFactor(i, i1) * _gradientFactor(i1) * _gradientSubExpr(i1, bc) + + _vertexGradientFactor(i, i2) * _gradientFactor(i2) * _gradientSubExpr(i2, bc); + break; + } + case 1: + { + v = 4 * _edgeSubExpr(i, bc) + + 4 * _gradientSubExpr(i, bc) + - 12 * _bubble(bc); + break; + } + case 2: + { + v = _gradientFactor(i) * _gradientSubExpr(i, bc); + break; + } + } + + return v; + } + + MULTIARCH inline Jacobian jacobian(const BarycentricCoord& bc) const + { + Jacobian grad; + Vector bubbleGradient = _bubbleGradient(bc); + + for(int i = 0; i < 3; ++i) + { + Vector gseGradient = _gradientSubExprGradient(i, bc); + grad.row(i + 3) = 4 * _edgeSubExprGradient(i, bc) + + 4 * gseGradient + - 12 * bubbleGradient; + grad.row(i + 6) = _gradientFactor(i) * gseGradient; + } + + for(int i = 0; i < 3; ++i) + { + unsigned i1 = (i+1) % 3; + unsigned i2 = (i+2) % 3; + Vector v1 = grad.row(i1 + 6); + Vector v2 = grad.row(i2 + 6); + grad.row(i) = _vertexSubExprGradient(i, bc) + + 3 * bubbleGradient + + _vertexGradientFactor(i, i1) * v1 + + _vertexGradientFactor(i, i2) * v2; + } + + return grad; + } + + MULTIARCH inline Vector gradient(unsigned bi, const BarycentricCoord& bc) const + { + assert(bi < 9); + + Vector grad; + unsigned i = bi % 3; + switch(bi/3) + { + case 0: + { + unsigned i1 = (i+1) % 3; + unsigned i2 = (i+2) % 3; + grad = _vertexSubExprGradient(i, bc) + + 3 * _bubbleGradient(bc) + + _vertexGradientFactor(i, i1) * _gradientFactor(i1) * _gradientSubExprGradient(i1, bc) + + _vertexGradientFactor(i, i2) * _gradientFactor(i2) * _gradientSubExprGradient(i2, bc); + break; + } + case 1: + { + grad = 4 * _edgeSubExprGradient(i, bc) + + 4 * _gradientSubExprGradient(i, bc) + - 12 * _bubbleGradient(bc); + break; + } + case 2: + { + grad = _gradientFactor(i) * _gradientSubExprGradient(i, bc); + break; + } + } + + return grad; + } + + MULTIARCH inline void hessian(const BarycentricCoord& bc, Hessian* h) const + { + Hessian bubbleHessian = _bubbleHessian(bc); + + for(int i = 0; i < 3; ++i) + { + Hessian gseHessian = _gradientSubExprHessian(i, bc); + h[i + 3] = 4 * _edgeSubExprHessian(i, bc) + + 4 * gseHessian + - 12 * bubbleHessian; + h[i + 6] = _gradientFactor(i) * gseHessian; + } + + for(int i = 0; i < 3; ++i) + { + unsigned i1 = (i+1) % 3; + unsigned i2 = (i+2) % 3; + Hessian& v1 = h[i1 + 6]; + Hessian& v2 = h[i2 + 6]; + h[i] = _vertexSubExprHessian(i, bc) + + 3 * bubbleHessian + + _vertexGradientFactor(i, i1) * v1 + + _vertexGradientFactor(i, i2) * v2; + } + } + + MULTIARCH inline Hessian hessian(unsigned bi, + const BarycentricCoord& bc) const + { + assert(bi < 9); + + Hessian h; + unsigned i = bi % 3; + switch(bi/3) + { + case 0: + { + unsigned i1 = (i+1) % 3; + unsigned i2 = (i+2) % 3; + h = _vertexSubExprHessian(i, bc) + + 3 * _bubbleHessian(bc) + + _vertexGradientFactor(i, i1) * _gradientFactor(i1) * _gradientSubExprHessian(i1, bc) + + _vertexGradientFactor(i, i2) * _gradientFactor(i2) * _gradientSubExprHessian(i2, bc); + break; + } + case 1: + { + h = 4 * _edgeSubExprHessian(i, bc) + + 4 * _gradientSubExprHessian(i, bc) + - 12 * _bubbleHessian(bc); + break; + } + case 2: + { + h = _gradientFactor(i) * _gradientSubExprHessian(i, bc); + break; + } + } + + return h; + } + + +public: + MULTIARCH inline Scalar _gradientFactor(unsigned bi) const + { + return - m_2delta / edgeLength(bi); + } + + MULTIARCH inline Scalar _vertexGradientFactor(unsigned bi, + unsigned ei) const + { + return dldn(bi, ei) + dldn(ei, ei) / 2; + } + + MULTIARCH inline Scalar _bubble(const BarycentricCoord& bc) const + { + return bc.prod(); + } + + MULTIARCH inline Vector _bubbleGradient(const BarycentricCoord& bc) const + { + return Base::jacobian().row(0) * bc(1) * bc(2) + + Base::jacobian().row(1) * bc(2) * bc(0) + + Base::jacobian().row(2) * bc(0) * bc(1); + } + + MULTIARCH inline Hessian _bubbleHessian(const BarycentricCoord& bc) const + { + Hessian h; + for(int d0 = 0; d0 < 2; ++d0) + { + for(int d1 = d0; d1 < 2; ++d1) + { + h(d0, d1) = Base::jacobian()(0, d0) * Base::jacobian()(1, d1) * bc(2) + + Base::jacobian()(0, d0) * Base::jacobian()(2, d1) * bc(1) + + Base::jacobian()(1, d0) * Base::jacobian()(0, d1) * bc(2) + + Base::jacobian()(1, d0) * Base::jacobian()(2, d1) * bc(0) + + Base::jacobian()(2, d0) * Base::jacobian()(0, d1) * bc(1) + + Base::jacobian()(2, d0) * Base::jacobian()(1, d1) * bc(0); + } + } + h(1, 0) = h(0, 1); + return h; + } + + MULTIARCH inline Scalar _vertexSubExpr(unsigned i, + const BarycentricCoord& bc) const + { + return bc(i) * (bc(i) - .5) * (bc(i) + 1); + } + + MULTIARCH inline Vector _vertexSubExprGradient( + unsigned i,const BarycentricCoord& bc) const + { + return Base::jacobian().row(i) * (3 * bc[i] * bc[i] + bc[i] - .5); + } + + MULTIARCH inline Hessian _vertexSubExprHessian( + unsigned i, const BarycentricCoord& bc) const + { + Hessian h; + for(int d0 = 0; d0 < 2; ++d0) + { + for(int d1 = d0; d1 < 2; ++d1) + { + h(d0, d1) = Base::jacobian()(i, d0) + * Base::jacobian()(i, d1) + * (6 * bc(i) + 1); + } + } + h(1, 0) = h(0, 1); + return h; + } + + MULTIARCH inline Scalar _edgeSubExpr(unsigned i, + const BarycentricCoord& bc) const + { + return bc((i+1)%3) * bc((i+2)%3); + } + + MULTIARCH inline Vector _edgeSubExprGradient( + unsigned i, const BarycentricCoord& bc) const + { + unsigned i1 = (i+1) % 3; + unsigned i2 = (i+2) % 3; + return Base::jacobian().row(i1) * bc(i2) + + Base::jacobian().row(i2) * bc(i1); + } + + MULTIARCH inline Hessian _edgeSubExprHessian( + unsigned i, const BarycentricCoord& /*bc*/) const + { + unsigned i1 = (i+1) % 3; + unsigned i2 = (i+2) % 3; + Hessian h; + for(int d0 = 0; d0 < 2; ++d0) + { + for(int d1 = d0; d1 < 2; ++d1) + { + h(d0, d1) = Base::jacobian()(i1, d0) * Base::jacobian()(i2, d1) + + Base::jacobian()(i2, d0) * Base::jacobian()(i1, d1); + } + } + h(1, 0) = h(0, 1); + return h; + } + + MULTIARCH inline Scalar _gradientSubExpr(unsigned i, + const BarycentricCoord& bc) const + { + return bc(i) * (2.*bc(i) - 1.) * (bc(i) - 1.); + } + + MULTIARCH inline Vector _gradientSubExprGradient( + unsigned i, const BarycentricCoord& bc) const + { + return Base::jacobian().row(i) * (6 * bc(i) * bc(i) - 6 * bc(i) + 1); + } + + MULTIARCH inline Hessian _gradientSubExprHessian( + unsigned i, const BarycentricCoord& bc) const + { + Hessian h; + for(int d0 = 0; d0 < 2; ++d0) + { + for(int d1 = d0; d1 < 2; ++d1) + { + h(d0, d1) = Base::jacobian()(i, d0) + * Base::jacobian()(i, d1) + * (12 * bc(i) - 6); + } + } + h(1, 0) = h(0, 1); + return h; + } + +protected: + MULTIARCH inline void computeFromPoints() + { + Matrix2x3 vs; + for(int i = 0; i < 3; ++i) + vs.col(i) = projPoint(i, 2) - projPoint(i, 1); + + for(int ni = 0; ni < 3; ++ni) + for(int li = 0; li < 3; ++li) + m_dldn(li, ni) = + vs.col(li).dot(vs.col(ni)) / (m_2delta * edgeLength(ni)); + } + + +protected: + using Base::m_2delta; + + Matrix3 m_dldn; +}; + + +} // namespace Vitelotte + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/fvElementBuilder.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/fvElementBuilder.h new file mode 100644 index 000000000..9ec823c20 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/fvElementBuilder.h @@ -0,0 +1,97 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_FV_ELEMENT_BUILDER_ +#define _VITELOTTE_FV_ELEMENT_BUILDER_ + + +#include +#include + +#include "solverError.h" +#include "elementBuilderBase.h" + +namespace Vitelotte +{ + + +/** + * \brief Fraeijs de Veubeke's (FV) element builder, for harmonic interpolation + * with quadratic triangles output. + * + * Use this element builder to solve biharmonic diffusion and get an image + * with quadratic interpolation. + * + * FV elements have 6 value nodes, 3 at each vertex (v) and 3 at edge midpoints + * (e), plus 3 gradient nodes corresponding to the mean value of the first + * (outward) normal derivative along each edge (g). There are the basis + * functions: + * + * \f{eqnarray*}{ + v_0 &=& \lambda_{0} \left(\lambda_{0} - \frac{1}{2}\right) \left(\lambda_{0} + 1\right) + + 3 \lambda_{0} \lambda_{1} \lambda_{2} + + \left( \frac{d \lambda_{0}}{d \boldsymbol{n}_{1}} + \frac{1}{2} \frac{d \lambda_{1}}{d \boldsymbol{n}_{1}} \right) g_1 + + \left( \frac{d \lambda_{0}}{d \boldsymbol{n}_{2}} + \frac{1}{2} \frac{d \lambda_{2}}{d \boldsymbol{n}_{2}} \right) g_2 \\ + + e_0 &=& 4 \lambda_{0} \left(- 2 \lambda_{0} + 1\right) \left(- \lambda_{0} + 1\right) + 4 \lambda_{1} \lambda_{2} - 12 \lambda_{0} \lambda_{1} \lambda_{2}\\ + g_0 &=& - \frac{2 \Delta}{l_{0}} \lambda_{0} \left(\lambda_{0} - 1\right) \left(2 \lambda_{0} - 1\right)\\ + \f} + * + * where \f$\lambda_i\f$ is the barycentric coordinate for the vertex \f$i\f$, + * \f$\Delta\f$ is the area of the triangle, \f$l_i\f$ is the length of the + * edge \f$i\f$ opposed to the vertex \f$i\f$ and \f$\boldsymbol{n}_i\f$ is the + * (clockwise, normalized) normal of the edge \f$i\f$. The others basis + * functions can be obtained by cyclic permutation of the indices. + */ +template < class _Mesh, typename _Scalar = typename _Mesh::Scalar > +class FVElementBuilder : public ElementBuilderBase<_Mesh, _Scalar> +{ +public: + typedef _Scalar Scalar; + typedef _Mesh Mesh; + + typedef ElementBuilderBase<_Mesh, _Scalar> Base; + + typedef typename Base::Vector Vector; + typedef typename Base::Matrix Matrix; + typedef typename Base::Triplet Triplet; + + typedef typename Base::Face Face; + +protected: + typedef Eigen::Matrix Vector3; + typedef Eigen::Matrix Vector6; + + typedef typename Mesh::Halfedge Halfedge; + typedef std::map PGCMap; + +public: + inline FVElementBuilder(Scalar sigma = Scalar(.5)); + + unsigned nCoefficients(const Mesh& mesh, Face element, + SolverError* error=0) const; + unsigned nExtraConstraints(const Mesh& mesh, Face element) const; + + template < typename Inserter > + void addCoefficients(Inserter& inserter, const Mesh& mesh, + Face element, SolverError* error=0); + + template < typename Inserter > + inline void addExtraConstraints(Inserter& inserter, const Mesh& mesh, + Face element, SolverError* error); + +private: + Scalar m_sigma; +// PGCMap m_pgcMap; +}; + + +} // namespace Vitelotte + +#include "fvElementBuilder.hpp" + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/fvElementBuilder.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/fvElementBuilder.hpp new file mode 100644 index 000000000..5182e1a74 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/fvElementBuilder.hpp @@ -0,0 +1,233 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +#include + +#include "fvElement.h" + +#include "fvElementBuilder.h" + + +namespace Vitelotte +{ + + +template < class _Mesh, typename _Scalar > +FVElementBuilder<_Mesh, _Scalar>::FVElementBuilder(Scalar sigma) + : m_sigma(sigma) +{ +} + + +template < class _Mesh, typename _Scalar > +unsigned +FVElementBuilder<_Mesh, _Scalar>:: + nCoefficients(const Mesh& mesh, Face element, + SolverError* /*error*/) const +{ + return mesh.nVertexGradientConstraints(element)? 61: 45; +} + + +template < class _Mesh, typename _Scalar > +unsigned +FVElementBuilder<_Mesh, _Scalar>:: + nExtraConstraints(const Mesh& mesh, Face element) const +{ + return mesh.nVertexGradientConstraints(element)? 2: 0; +} + + +template < class _Mesh, typename _Scalar > +template < typename Inserter > +void +FVElementBuilder<_Mesh, _Scalar>:: + addCoefficients(Inserter& inserter, const Mesh& mesh, + Face element, SolverError* error) +{ + if(mesh.valence(element) != 3) + { + if(error) error->error("Non-triangular face"); + return; + } + + typedef Eigen::Matrix Matrix9; + Matrix9 sm; + + int nodes[9]; + + typename Mesh::HalfedgeAroundFaceCirculator hit = mesh.halfedges(element); + typename Mesh::HalfedgeAroundFaceCirculator hend = hit; + do ++hit; + while(!mesh.isGradientConstraint(mesh.toVertex(*hit)) && hit != hend); + bool isPgc = mesh.isGradientConstraint(mesh.toVertex(*hit)); + + bool orient[3]; + // TODO: remove dynamic allocation with dynamic dims. + Vector p[3]; + --hit; + for(int i = 0; i < 3; ++i) + { + orient[i] = mesh.halfedgeOrientation(*hit); + nodes[3+i] = mesh.edgeValueNode(*hit).idx(); + nodes[6+i] = mesh.edgeGradientNode(*hit).idx(); + ++hit; + nodes[i] = mesh.toVertexValueNode(*hit).idx(); + p[i] = mesh.position(mesh.toVertex(*hit)).template cast(); + } + + for(int i = 0; i < 9; ++i) + { + if(nodes[i] < 0) + { + if(error) error->error("Invalid node"); + return; + } + } + + typedef FVElement Elem; + Elem elem(p); + + if(elem.doubleArea() <= 0) + { + if(error) error->warning("Degenerated or reversed triangle"); + } + + typedef Eigen::Array Array3; + Array3 dx2[9]; + Array3 dy2[9]; + Array3 dxy[9]; + for(int pi = 0; pi < 3; ++pi) + { + Vector3 bc((pi == 0)? 0: .5, + (pi == 1)? 0: .5, + (pi == 2)? 0: .5); + typename Elem::Hessian hessians[9]; + elem.hessian(bc, hessians); + + for(int bi = 0; bi < 9; ++bi) + { + dx2[bi](pi) = hessians[bi](0, 0); + dy2[bi](pi) = hessians[bi](1, 1); + dxy[bi](pi) = hessians[bi](0, 1); + } + } + + for(size_t i = 0; i < 9; ++i) + { + for(size_t j = i; j < 9; ++j) + { + EIGEN_ASM_COMMENT("MYBEGIN"); + + Array3 quadPointValue = + (dx2[i]+dy2[i]) * (dx2[j]+dy2[j]) + + (1.-m_sigma) * ( + 2. * dxy[i] * dxy[j] + - dx2[i] * dy2[j] + - dx2[j] * dy2[i]); + + Scalar value = quadPointValue.sum() * (elem.doubleArea() / 6); + + EIGEN_ASM_COMMENT("MYEND"); + + if((i < 6 || orient[i%3]) != (j < 6 || orient[j%3])) + { + value *= -1; + } + + sm(i, j) = value; + sm(j, i) = value; + } + } + + for(size_t i = 0; i < 9; ++i) + { + for(size_t j = 0; j < 9; ++j) + { + if(nodes[i] < nodes[j]) continue; + inserter.addCoeff(nodes[i], nodes[j], sm(i, j)); + } + } + + if(isPgc) + { + typedef Eigen::Matrix Vector9; + Vector9 fde1, fde2; + fde1 << + -1.0L/2.0L*(elem.doubleArea()*(2*elem.dldn(0, 1) + elem.dldn(1, 1)) + 7*elem.edgeLength(1))/(elem.edgeLength(1)*elem.edgeLength(2)), + (1.0L/2.0L)*(elem.doubleArea()*(elem.dldn(0, 0) + 2*elem.dldn(1, 0)) - elem.edgeLength(0))/(elem.edgeLength(0)*elem.edgeLength(2)), + -1.0L/2.0L*elem.doubleArea()*(elem.edgeLength(0)*(elem.dldn(1, 1) + 2*elem.dldn(2, 1)) - elem.edgeLength(1)*(elem.dldn(0, 0) + 2*elem.dldn(2, 0)))/(elem.edgeLength(0)*elem.edgeLength(1)*elem.edgeLength(2)), + -4/elem.edgeLength(2), + 4/elem.edgeLength(2), + 4/elem.edgeLength(2), + elem.doubleArea()/(elem.edgeLength(0)*elem.edgeLength(2)), + -elem.doubleArea()/(elem.edgeLength(1)*elem.edgeLength(2)), + 0; + fde2 << + -1.0L/2.0L*(elem.doubleArea()*(2*elem.dldn(0, 2) + elem.dldn(2, 2)) + 7*elem.edgeLength(2))/(elem.edgeLength(1)*elem.edgeLength(2)), + -1.0L/2.0L*elem.doubleArea()*(elem.edgeLength(0)*(2*elem.dldn(1, 2) + elem.dldn(2, 2)) - elem.edgeLength(2)*(elem.dldn(0, 0) + 2*elem.dldn(1, 0)))/(elem.edgeLength(0)*elem.edgeLength(1)*elem.edgeLength(2)), + (1.0L/2.0L)*(elem.doubleArea()*(elem.dldn(0, 0) + 2*elem.dldn(2, 0)) - elem.edgeLength(0))/(elem.edgeLength(0)*elem.edgeLength(1)), + -4/elem.edgeLength(1), + 4/elem.edgeLength(1), + 4/elem.edgeLength(1), + elem.doubleArea()/(elem.edgeLength(0)*elem.edgeLength(1)), + 0, + -elem.doubleArea()/(elem.edgeLength(1)*elem.edgeLength(2)); + + for(size_t i = 0; i < 9; ++i) + { + Scalar f = (i < 6 || orient[i%3])? 1: -1; + if(i != 8 /*fde1(i) != Scalar(0.)*/) + { + inserter.addExtraCoeff(element, 1, nodes[i], fde1(i) * f); + } + if(i != 7 /*fde2(i) != Scalar(0.)*/) + { + inserter.addExtraCoeff(element, 0, nodes[i], fde2(i) * f); + } + } + } +} + + +template < class _Mesh, typename _Scalar > +template < typename Inserter > +void +FVElementBuilder<_Mesh, _Scalar>:: + addExtraConstraints(Inserter& inserter, const Mesh& mesh, + Face element, SolverError* /*error*/) +{ + typename Mesh::HalfedgeAroundFaceCirculator hit = mesh.halfedges(element); + typename Mesh::HalfedgeAroundFaceCirculator hend = hit; + do ++hit; + while(!mesh.isGradientConstraint(mesh.toVertex(*hit)) && hit != hend); + if(!mesh.isGradientConstraint(mesh.toVertex(*hit))) { + return; + } + + + for(unsigned hi = 0; hi < 2; ++hi) + { + typename Mesh::Halfedge h = *hit; + + typename Mesh::Vertex from = mesh.fromVertex(h); + typename Mesh::Vertex to = mesh. toVertex(h); + + bool v0c = mesh.isGradientConstraint(from); + const typename Mesh::Gradient& grad = mesh.gradientConstraint(v0c? from: to); + typename Mesh::Vector v = mesh.position(to) - mesh.position(from); + if(!v0c) v = -v; + typename Mesh::Value cons = grad * v; + inserter.setExtraRhs(element, hi, cons.template cast()); + + ++hit; + } + +} + + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/linearElement.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/linearElement.h new file mode 100644 index 000000000..701339c77 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/linearElement.h @@ -0,0 +1,153 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_LINEAR_ELEMENT_ +#define _VITELOTTE_LINEAR_ELEMENT_ + + +#include + +#include "../../common/defines.h" + + +namespace Vitelotte +{ + + +/** + * \brief Provides method to evaluate linear elements and their derivatives. + */ +template < typename _Scalar > +class LinearElement +{ +public: + typedef _Scalar Scalar; + + typedef Eigen::Matrix Vector; + typedef Eigen::Matrix Values; + typedef Eigen::Matrix Jacobian; + typedef Eigen::Matrix Hessian; + + typedef Eigen::Matrix BarycentricCoord; + +protected: + typedef Eigen::Matrix Vector3; + + typedef Eigen::Matrix Matrix2; + typedef Eigen::Matrix Matrix3; + + typedef Eigen::Matrix Matrix3x2; + typedef Eigen::Matrix Matrix2x3; + +public: + template < typename It > + MULTIARCH inline LinearElement(It pointIt) + { + It it0 = pointIt; + It it1 = it0; ++it1; + It it2 = it1; ++it2; + computeFromPoints(*it0, *it1, *it2); + } + + template < typename Derived0, typename Derived1, typename Derived2 > + MULTIARCH inline LinearElement( + const Eigen::MatrixBase& p0, + const Eigen::MatrixBase& p1, + const Eigen::MatrixBase& p2) + { + computeFromPoints(p0, p1, p2); + } + + MULTIARCH inline Vector projPoint(unsigned pi, unsigned offset=0) const + { + assert(pi < 3 && offset < 3); + switch((pi + offset) % 3) + { + case 0: return Vector::Zero(); + case 1: return Vector(edgeLength(2), 0); + case 2: return m_p2; + } + return Vector(); + } + + MULTIARCH inline Scalar edgeLength(unsigned ei, unsigned offset=0) const + { + assert(ei < 3 && offset < 3); + return m_eLen((ei + offset) % 3); + } + + MULTIARCH inline Scalar doubleArea() const { return m_2delta; } + + MULTIARCH inline BarycentricCoord bcProj(const Vector& p) const + { + return m_lbf * (BarycentricCoord() << p, 1).finished(); + } + + MULTIARCH inline Values eval(const BarycentricCoord& bc) const + { + return bc; + } + + MULTIARCH inline Scalar eval(unsigned bi, const BarycentricCoord& bc) const + { + assert(bi < 3); + return bc(bi); + } + + MULTIARCH inline const Eigen::Block jacobian( + const BarycentricCoord& /*bc*/ = BarycentricCoord()) const + { + return m_lbf.template block<3, 2>(0, 0); + } + + MULTIARCH inline const Vector gradient( + unsigned bi, + const BarycentricCoord& /*bc*/ = BarycentricCoord()) const + { + assert(bi < 3); + return m_lbf.template block<1, 2>(bi, 0); + } + +protected: + template < typename Derived0, typename Derived1, typename Derived2 > + MULTIARCH inline void computeFromPoints( + const Eigen::MatrixBase& p0, + const Eigen::MatrixBase& p1, + const Eigen::MatrixBase& p2) + { + m_eLen(0) = (p2-p1).norm(); + m_eLen(1) = (p0-p2).norm(); + m_eLen(2) = (p1-p0).norm(); + + m_p2(0) = (p2-p0).dot(p1-p0) / edgeLength(2); + m_p2(1) = std::sqrt(edgeLength(1)*edgeLength(1) - m_p2(0)*m_p2(0)); + + m_2delta = edgeLength(2) * m_p2(1); + + for(int li = 0; li < 3; ++li) + { + m_lbf(li, 0) = projPoint(li, 1).y() - projPoint(li, 2).y(); + m_lbf(li, 1) = projPoint(li, 2).x() - projPoint(li, 1).x(); + m_lbf(li, 2) = projPoint(li, 1).x() * projPoint(li, 2).y() + - projPoint(li, 2).x() * projPoint(li, 1).y(); + } + m_lbf /= m_2delta; + } + +protected: +// Matrix2x3 m_points; + Vector m_p2; + Vector3 m_eLen; + + Scalar m_2delta; + Matrix3 m_lbf; +}; + + +} // namespace Vitelotte + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/linearElementBuilder.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/linearElementBuilder.h new file mode 100644 index 000000000..37d317660 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/linearElementBuilder.h @@ -0,0 +1,59 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_LINEAR_ELEMENT_BUILDER_ +#define _VITELOTTE_LINEAR_ELEMENT_BUILDER_ + + +#include +#include + + +#include "solverError.h" +#include "elementBuilderBase.h" + + +namespace Vitelotte +{ + + +/** + * \brief Linear element builder, for harmonic interpolation with linear + * triangles output. + */ +template < class _Mesh, typename _Scalar = typename _Mesh::Scalar > +class LinearElementBuilder : public ElementBuilderBase<_Mesh, _Scalar> +{ +public: + typedef _Scalar Scalar; + typedef _Mesh Mesh; + + typedef ElementBuilderBase<_Mesh, _Scalar> Base; + + typedef typename Base::Vector Vector; + typedef typename Base::Matrix Matrix; + typedef typename Base::Triplet Triplet; + + typedef typename Base::Face Face; + +public: + inline LinearElementBuilder(); + + unsigned nCoefficients(const Mesh& mesh, Face element, + SolverError* error=0) const; + + template < typename Inserter > + void addCoefficients(Inserter& inserter, const Mesh& mesh, + Face element, SolverError* error=0); +}; + + +} // namespace Vitelotte + +#include "linearElementBuilder.hpp" + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/linearElementBuilder.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/linearElementBuilder.hpp new file mode 100644 index 000000000..5728b5b85 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/linearElementBuilder.hpp @@ -0,0 +1,79 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include "linearElementBuilder.h" + + +namespace Vitelotte +{ + + +template < class _Mesh, typename _Scalar > +LinearElementBuilder<_Mesh, _Scalar>::LinearElementBuilder() +{ +} + +template < class _Mesh, typename _Scalar > +unsigned +LinearElementBuilder<_Mesh, _Scalar>:: + nCoefficients(const Mesh& /*mesh*/, Face /*element*/, + SolverError* /*error*/) const +{ + return 6; +} + + +template < class _Mesh, typename _Scalar > +template < typename Inserter > +void +LinearElementBuilder<_Mesh, _Scalar>:: + addCoefficients(Inserter& inserter, const Mesh& mesh, + Face element, SolverError* error) +{ + if(mesh.valence(element) != 3) + { + if(error) error->error("Non-triangular face"); + return; + } + + // TODO: remove dynamic allocation with dynamic dims. + Vector v[3]; + int nodes[3]; + + typename Mesh::HalfedgeAroundFaceCirculator hit = mesh.halfedges(element); + --hit; + for(int i = 0; i < 3; ++i) + { + v[i] = (mesh.position(mesh.toVertex(*hit)) - + mesh.position(mesh.fromVertex(*hit))).template cast(); + ++hit; + nodes[i] = mesh.toVertexValueNode(*hit).idx(); + if(nodes[i] < 0) + { + if(error) error->error("Invalid node"); + return; + } + } + + Scalar _2area = det2(v[0], v[1]); + Scalar inv4A = 1. / (2. * _2area); + + if(_2area <= 0 && error) + { + if(error) error->error("Degenerated or reversed triangle"); + } + + for(int i = 0; i < 3; ++i) + { + for(int j = i; j < 3; ++j) + { + inserter.addCoeff(nodes[i], nodes[j], v[i].dot(v[j]) * inv4A); + } + } +} + + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/morleyElement.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/morleyElement.h new file mode 100644 index 000000000..6ab8a5975 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/morleyElement.h @@ -0,0 +1,174 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_MORLEY_ELEMENT_ +#define _VITELOTTE_MORLEY_ELEMENT_ + + +#include + +#include "../../common/defines.h" + +#include "linearElement.h" + + +namespace Vitelotte +{ + + +/** + * \brief Provides method to evaluate Morley elements and their derivatives. + */ +template < typename _Scalar > +class MorleyElement : protected LinearElement<_Scalar> +{ +public: + typedef _Scalar Scalar; + typedef LinearElement Base; + + typedef Eigen::Matrix Vector; + typedef Eigen::Matrix Values; + typedef Eigen::Matrix Jacobian; + typedef Eigen::Matrix Hessian; + + typedef Eigen::Matrix BarycentricCoord; + +protected: + typedef Eigen::Matrix Vector3; + + typedef Eigen::Matrix Matrix2x3; + typedef Eigen::Matrix Matrix3; + +public: + template < typename It > + MULTIARCH inline MorleyElement(It pointIt) + : Base(pointIt) + { + computeFromPoints(); + } + + template < typename Derived0, typename Derived1, typename Derived2 > + MULTIARCH inline MorleyElement( + const Eigen::MatrixBase& p0, + const Eigen::MatrixBase& p1, + const Eigen::MatrixBase& p2) + : Base(p0, p1, p2) + { + computeFromPoints(); + } + + using Base::projPoint; + using Base::edgeLength; + using Base::doubleArea; + + using Base::bcProj; + + MULTIARCH inline Values eval(const BarycentricCoord& bc) const + { + Values basis; + for(unsigned i = 0; i < 6; ++i) + basis(i) = eval(i, bc); + return basis; + } + + MULTIARCH inline Scalar eval(unsigned bi, const BarycentricCoord& bc) const + { + assert(bi < 6); + if(bi < 3) + { + unsigned bi1 = (bi + 1) % 3; + unsigned bi2 = (bi + 2) % 3; + return bc(bi)*bc(bi) + + m_dldn(bi, bi1) * eval(bi1 + 3, bc) + + m_dldn(bi, bi2) * eval(bi2 + 3, bc); + } + bi -= 3; + return bc(bi) * (bc(bi) - 1) / m_dldn(bi, bi); + } + + MULTIARCH inline const Jacobian jacobian(const BarycentricCoord& bc) const + { + Jacobian j; + for(unsigned i = 0; i < 6; ++i) + j.row(i) = gradient(i, bc); + return j; + } + + MULTIARCH inline const Vector gradient(unsigned bi, + const BarycentricCoord& bc) const + { + assert(bi < 6); + if(bi < 3) + { + unsigned bi1 = (bi + 1) % 3; + unsigned bi2 = (bi + 2) % 3; + return Base::gradient(bi, bc) * (2 * bc(bi)) + + m_dldn(bi, bi1) * gradient(bi1 + 3, bc) + + m_dldn(bi, bi2) * gradient(bi2 + 3, bc); + } + bi -= 3; + return Base::gradient(bi, bc) * ((2 * bc(bi) - 1) / m_dldn(bi, bi)); + } + + MULTIARCH inline void hessian(const BarycentricCoord& bc, Hessian* h) const + { + for(unsigned i = 0; i < 6; ++i) + h[i] = hessian(i, bc); + } + + MULTIARCH inline Hessian hessian(unsigned bi, + const BarycentricCoord& bc) const + { + Hessian h; + for(int d0 = 0; d0 < 2; ++d0) + { + for(int d1 = d0; d1 < 2; ++d1) + { + h(d0, d1) = 2. + * Base::jacobian()(bi%3, d0) + * Base::jacobian()(bi%3, d1); + } + } + h(1, 0) = h(0, 1); + + if(bi < 3) + { + unsigned bi1 = (bi + 1) % 3; + unsigned bi2 = (bi + 2) % 3; + return h + + m_dldn(bi, bi1) * hessian(bi1 + 3, bc) + + m_dldn(bi, bi2) * hessian(bi2 + 3, bc); + } + + bi -= 3; + return h / m_dldn(bi, bi); + } + +protected: + MULTIARCH inline void computeFromPoints() + { + Matrix2x3 vs; + for(int i = 0; i < 3; ++i) + vs.col(i) = projPoint(i, 2) - projPoint(i, 1); + + for(int ni = 0; ni < 3; ++ni) + for(int li = 0; li < 3; ++li) + m_dldn(li, ni) = + vs.col(li).dot(vs.col(ni)) / (m_2delta * edgeLength(ni)); + } + + +protected: + using Base::m_2delta; + + Matrix3 m_dldn; +}; + + +} // namespace Vitelotte + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/morleyElementBuilder.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/morleyElementBuilder.h new file mode 100644 index 000000000..43324cc46 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/morleyElementBuilder.h @@ -0,0 +1,64 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_MORLEY_ELEMENT_BUILDER_ +#define _VITELOTTE_MORLEY_ELEMENT_BUILDER_ + + +#include +#include + + +#include "solverError.h" +#include "elementBuilderBase.h" + + +namespace Vitelotte +{ + + +/** + * \brief Morley element builder, for harmonic interpolation with linear + * triangles output. + */ +template < class _Mesh, typename _Scalar = typename _Mesh::Scalar > +class MorleyElementBuilder : public ElementBuilderBase<_Mesh, _Scalar> +{ +public: + typedef _Scalar Scalar; + typedef _Mesh Mesh; + + typedef ElementBuilderBase<_Mesh, _Scalar> Base; + + typedef typename Base::Vector Vector; + typedef typename Base::Matrix Matrix; + typedef typename Base::Triplet Triplet; + + typedef typename Base::Face Face; + + typedef Eigen::Matrix Vector6; + +public: + inline MorleyElementBuilder(Scalar sigma = Scalar(.5)); + + unsigned nCoefficients(const Mesh& mesh, Face element, + SolverError* error=0) const; + + template < typename Inserter > + void addCoefficients(Inserter& inserter, const Mesh& mesh, + Face element, SolverError* error=0); + +private: + Scalar m_sigma; +}; + + +} // namespace Vitelotte + +#include "morleyElementBuilder.hpp" + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/morleyElementBuilder.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/morleyElementBuilder.hpp new file mode 100644 index 000000000..29677b4d9 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/morleyElementBuilder.hpp @@ -0,0 +1,116 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include + +#include "morleyElement.h" + +#include "morleyElementBuilder.h" + + +namespace Vitelotte +{ + + +template < class _Mesh, typename _Scalar > +MorleyElementBuilder<_Mesh, _Scalar>::MorleyElementBuilder(Scalar sigma) + : m_sigma(sigma) +{ +} + +template < class _Mesh, typename _Scalar > +unsigned +MorleyElementBuilder<_Mesh, _Scalar>:: + nCoefficients(const Mesh& /*mesh*/, Face /*element*/, + SolverError* /*error*/) const +{ + return 36; +} + + +template < class _Mesh, typename _Scalar > +template < typename Inserter > +void +MorleyElementBuilder<_Mesh, _Scalar>:: + addCoefficients(Inserter& inserter, const Mesh& mesh, + Face element, SolverError* error) +{ + if(mesh.valence(element) != 3) + { + if(error) error->error("Non-triangular face"); + return; + } + + // TODO: remove dynamic allocation with dynamic dims. + Vector p[3]; + Vector v[3]; + bool orient[3]; + int nodes[6]; + + typename Mesh::HalfedgeAroundFaceCirculator hit = mesh.halfedges(element); + --hit; + for(int i = 0; i < 3; ++i) + { + v[i] = (mesh.position(mesh.toVertex(*hit)) - + mesh.position(mesh.fromVertex(*hit))).template cast(); + orient[i] = mesh.halfedgeOrientation(*hit); + nodes[i+3] = mesh.edgeGradientNode(*hit).idx(); + ++hit; + nodes[i] = mesh.toVertexValueNode(*hit).idx(); + p[i] = mesh.position(mesh.toVertex(*hit)).template cast(); + } + + for(int i = 0; i < 6; ++i) + { + if(nodes[i] < 0) + { + if(error) error->error("Invalid node"); + return; + } + } + + typedef MorleyElement Elem; + Elem elem(p); + + if(elem.doubleArea() <= 0 && error) + { + error->warning("Degenerated or reversed triangle"); + } + + typedef Eigen::Matrix Vector3; + Vector6 dx2; + Vector6 dxy; + Vector6 dy2; + Vector3 bc = Vector3(1, 1, 1) / 3; + typename Elem::Hessian hessians[6]; + elem.hessian(bc, hessians); + + for(int bi = 0; bi < 6; ++bi) + { + dx2(bi) = hessians[bi](0, 0); + dy2(bi) = hessians[bi](1, 1); + dxy(bi) = hessians[bi](0, 1); + } + + for(int i = 0; i < 6; ++i) + { + for(int j = i; j < 6; ++j) + { + Scalar value = + ((dx2(i) + dy2(i)) * (dx2(j) + dy2(j)) + + (1-m_sigma) * ( 2*dxy(i)*dxy(j) - dx2(i)*dy2(j) - dy2(i)*dx2(j))); + value *= elem.doubleArea() / 2; + if((i < 3 || orient[i%3]) != (j < 3 || orient[j%3])) + { + value *= -1; + } + inserter.addCoeff(nodes[i], nodes[j], value); + } + } +} + + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/quadraticElement.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/quadraticElement.h new file mode 100644 index 000000000..4917e69a6 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/quadraticElement.h @@ -0,0 +1,104 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_QUADRATIC_ELEMENT_ +#define _VITELOTTE_QUADRATIC_ELEMENT_ + + +#include + +#include "../../common/defines.h" + +#include "linearElement.h" + + +namespace Vitelotte +{ + + +/** + * \brief Provides method to evaluate quadratic elements and their derivatives. + */ +template < typename _Scalar > +class QuadraticElement : protected LinearElement<_Scalar> +{ +public: + typedef _Scalar Scalar; + typedef LinearElement Base; + + typedef Eigen::Matrix Vector; + typedef Eigen::Matrix Values; + typedef Eigen::Matrix Jacobian; + typedef Eigen::Matrix Hessian; + + typedef Eigen::Matrix BarycentricCoord; + +public: + template < typename It > + MULTIARCH inline QuadraticElement(It pointIt) + : Base(pointIt) + {} + + template < typename Derived0, typename Derived1, typename Derived2 > + MULTIARCH inline QuadraticElement( + const Eigen::MatrixBase& p0, + const Eigen::MatrixBase& p1, + const Eigen::MatrixBase& p2) + : Base(p0, p1, p2) + {} + + using Base::projPoint; + using Base::edgeLength; + using Base::doubleArea; + + using Base::bcProj; + + MULTIARCH inline Values eval(const BarycentricCoord& bc) const + { + Values basis; + for(unsigned i = 0; i < 6; ++i) + basis(i) = eval(i, bc); + return basis; + } + + MULTIARCH inline Scalar eval(unsigned bi, const BarycentricCoord& bc) const + { + assert(bi < 6); + if(bi < 3) + return bc(bi) * (2 * bc(bi) - 1); + return 4 * bc((bi + 1) % 3) * bc((bi + 2) % 3); + } + + MULTIARCH inline const Jacobian jacobian(const BarycentricCoord& bc) const + { + Jacobian j; + for(unsigned i = 0; i < 6; ++i) + j.row(i) = gradient(i, bc); + return j; + } + + MULTIARCH inline const Vector gradient(unsigned bi, + const BarycentricCoord& bc) const + { + assert(bi < 6); + if(bi < 3) + return Base::gradient(bi, bc) * (4 * bc(bi) - 1); + + unsigned i1 = (bi + 1) % 3; + unsigned i2 = (bi + 2) % 3; + return 4 * (Base::gradient(i1, bc) * bc(i2) + Base::gradient(i2, bc) * bc(i1)); + } + + +protected: + using Base::m_2delta; +}; + + +} // namespace Vitelotte + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/quadraticElementBuilder.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/quadraticElementBuilder.h new file mode 100644 index 000000000..7f9872218 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/quadraticElementBuilder.h @@ -0,0 +1,70 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_QUADRATIC_ELEMENT_BUILDER_ +#define _VITELOTTE_QUADRATIC_ELEMENT_BUILDER_ + + +#include +#include + + +#include "solverError.h" +#include "elementBuilderBase.h" + + +namespace Vitelotte +{ + +/** + * \brief Quadratic element builder, for harmonic interpolation with quadratic + * triangles output. + */ +template < class _Mesh, typename _Scalar = typename _Mesh::Scalar > +class QuadraticElementBuilder : public ElementBuilderBase<_Mesh, _Scalar> +{ +public: + typedef _Scalar Scalar; + typedef _Mesh Mesh; + + typedef ElementBuilderBase<_Mesh, _Scalar> Base; + + typedef typename Base::Vector Vector; + typedef typename Base::Matrix Matrix; + typedef typename Base::Triplet Triplet; + + typedef typename Base::Face Face; + +protected: + typedef Eigen::Matrix ElementStiffnessMatrix; + +protected: + static void initializeMatrices(); + +public: + inline QuadraticElementBuilder(); + + unsigned nCoefficients(const Mesh& mesh, Face element, + SolverError* error=0) const; + + template < typename Inserter > + void addCoefficients(Inserter& inserter, const Mesh& mesh, + Face element, SolverError* error=0); + +private: + static bool m_matricesInitialized; + static ElementStiffnessMatrix m_quadM1; + static ElementStiffnessMatrix m_quadM2; + static ElementStiffnessMatrix m_quadM3; +}; + + +} // namespace Vitelotte + +#include "quadraticElementBuilder.hpp" + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/quadraticElementBuilder.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/quadraticElementBuilder.hpp new file mode 100644 index 000000000..4a3b9695e --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/quadraticElementBuilder.hpp @@ -0,0 +1,148 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include "quadraticElementBuilder.h" + + +namespace Vitelotte +{ + + +template < class _Mesh, typename _Scalar > +void +QuadraticElementBuilder<_Mesh, _Scalar>::initializeMatrices() +{ + if(m_matricesInitialized) + return; + m_matricesInitialized = true; + + // 'Magic' initialization of base matrices for quadratic elements. + // See Paul Tsipouras, Compact representation of triangular finite + // elements for Poisson's equation, International Journal for + // Numerical Methods in Engineering, Volume 11, Issue 3, pages 419-430, + // 1977 + + const Scalar _1_6 = Scalar(1.) / Scalar(6.); + const Scalar _2_3 = Scalar(2.) / Scalar(3.); + const Scalar _4_3 = Scalar(4.) / Scalar(3.); + + m_quadM1 << + 1, _1_6, _1_6, 0, -_2_3, -_2_3, + _1_6, 0, -_1_6, _2_3, 0, -_2_3, + _1_6, -_1_6, 0, _2_3, -_2_3, 0, + 0, _2_3, _2_3, _4_3, -_4_3, -_4_3, + -_2_3, 0, -_2_3, -_4_3, _4_3, _4_3, + -_2_3, -_2_3, 0, -_4_3, _4_3, _4_3; + + m_quadM2 << + 0, _1_6, -_1_6, 0, _2_3, -_2_3, + _1_6, 1, _1_6, -_2_3, 0, -_2_3, + -_1_6, _1_6, 0, -_2_3, _2_3, 0, + 0, -_2_3, -_2_3, _4_3, -_4_3, _4_3, + _2_3, 0, _2_3, -_4_3, _4_3, -_4_3, + -_2_3, -_2_3, 0, _4_3, -_4_3, _4_3; + + m_quadM3 << + 0, -_1_6, _1_6, 0, -_2_3, _2_3, + -_1_6, 0, _1_6, -_2_3, 0, _2_3, + _1_6, _1_6, 1, -_2_3, -_2_3, 0, + 0, -_2_3, -_2_3, _4_3, _4_3, -_4_3, + -_2_3, 0, -_2_3, _4_3, _4_3, -_4_3, + _2_3, _2_3, 0, -_4_3, -_4_3, _4_3; +} + +template < class _Mesh, typename _Scalar > +QuadraticElementBuilder<_Mesh, _Scalar>::QuadraticElementBuilder() +{ + initializeMatrices(); +} + +template < class _Mesh, typename _Scalar > +unsigned +QuadraticElementBuilder<_Mesh, _Scalar>:: + nCoefficients(const Mesh& /*mesh*/, Face /*element*/, + SolverError* /*error*/) const +{ + return 36; +} + + +template < class _Mesh, typename _Scalar > +template < typename Inserter > +void +QuadraticElementBuilder<_Mesh, _Scalar>:: + addCoefficients(Inserter& inserter, const Mesh& mesh, + Face element, SolverError* error) +{ + if(mesh.valence(element) != 3) + { + if(error) error->error("Non-triangular face"); + return; + } + + // TODO: remove dynamic allocation with dynamic dims. + Vector v[3]; + int nodes[6]; + + typename Mesh::HalfedgeAroundFaceCirculator hit = mesh.halfedges(element); + --hit; + for(int i = 0; i < 3; ++i) + { + v[i] = (mesh.position(mesh.toVertex(*hit)) - + mesh.position(mesh.fromVertex(*hit))).template cast(); + nodes[3+i] = mesh.edgeValueNode(*hit).idx(); + ++hit; + nodes[i] = mesh.toVertexValueNode(*hit).idx(); + } + + for(int i = 0; i < 6; ++i) + { + if(nodes[i] < 0) + { + if(error) error->error("Invalid node"); + return; + } + } + + Scalar _2area = det2(v[0], v[1]); + + if(_2area <= 0 && error) + { + error->warning("Degenerated or reversed triangle"); + } + + Scalar inv4A = 1. / (2. * _2area); + + ElementStiffnessMatrix matrix = (m_quadM1 * v[0].squaredNorm() + + m_quadM2 * v[1].squaredNorm() + + m_quadM3 * v[2].squaredNorm()) * inv4A; + + for(int i = 0; i < 6; ++i) + { + for(int j = i; j < 6; ++j) + { + inserter.addCoeff(nodes[i], nodes[j], matrix(i, j)); + } + } +} + +template < class _Mesh, typename _Scalar > +bool QuadraticElementBuilder<_Mesh, _Scalar>::m_matricesInitialized = false; + +template < class _Mesh, typename _Scalar > +typename QuadraticElementBuilder<_Mesh, _Scalar>::ElementStiffnessMatrix + QuadraticElementBuilder<_Mesh, _Scalar>::m_quadM1; + +template < class _Mesh, typename _Scalar > +typename QuadraticElementBuilder<_Mesh, _Scalar>::ElementStiffnessMatrix + QuadraticElementBuilder<_Mesh, _Scalar>::m_quadM2; + +template < class _Mesh, typename _Scalar > +typename QuadraticElementBuilder<_Mesh, _Scalar>::ElementStiffnessMatrix + QuadraticElementBuilder<_Mesh, _Scalar>::m_quadM3; + + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/singularElementDecorator.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/singularElementDecorator.h new file mode 100644 index 000000000..c36408759 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/singularElementDecorator.h @@ -0,0 +1,62 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_SINGULAR_ELEMENT_DECORATOR_ +#define _VITELOTTE_SINGULAR_ELEMENT_DECORATOR_ + + +#include + +#include +#include + +#include "solverError.h" + + +namespace Vitelotte +{ + + +/** + * \brief An element builder decorator that add support for elements with at + * most one singularity. + */ +template < class _Element > +class SingularElementDecorator : public _Element +{ +public: + typedef _Element Base; + + typedef typename Base::Scalar Scalar; + typedef typename Base::Mesh Mesh; + + typedef typename Base::Vector Vector; + typedef typename Base::Matrix Matrix; + typedef typename Base::Triplet Triplet; + + typedef typename Base::Face Face; + + +public: + inline explicit SingularElementDecorator(const Base& element=Base()) + : Base(element) {} + + unsigned nCoefficients(const Mesh& mesh, Face element, + SolverError* error=0) const; + unsigned nExtraConstraints(const Mesh& mesh, Face element) const; + + template < typename Inserter > + void addCoefficients(Inserter& inserter, const Mesh& mesh, + Face element, SolverError* error=0); +}; + + +} // namespace Vitelotte + +#include "singularElementDecorator.hpp" + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/singularElementDecorator.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/singularElementDecorator.hpp new file mode 100644 index 000000000..d3ee12d44 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/singularElementDecorator.hpp @@ -0,0 +1,112 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include "singularElementDecorator.h" + + +namespace Vitelotte +{ + + +namespace internal { + +template < typename Mesh, typename Inserter > +class SingularInserter +{ +public: + typedef typename Inserter::Scalar Scalar; + typedef typename Mesh::Face Face; + typedef typename Mesh::HalfedgeAroundFaceCirculator HCirc; + +public: + SingularInserter(Inserter& inserter, const Mesh& mesh, Face face) + : m_inserter(inserter), m_from(-1), m_to(-1) { + + HCirc hit = mesh.halfedges(face); + HCirc hend = hit; + do + { + if(mesh.isSingular(*hit)) break; + ++hit; + } while(hit != hend); + assert(mesh.isSingular(*hit)); + + m_from = mesh.toVertexValueNode(*hit).idx(); + m_to = mesh.fromVertexValueNode(mesh.nextHalfedge(*hit)).idx(); + } + + inline unsigned map(unsigned ni) { + return (ni == unsigned(m_from))? m_to: ni; + } + + inline void addCoeff(unsigned ni0, unsigned ni1, Scalar value) { + m_inserter.addCoeff( ni0 , ni1 , value); + m_inserter.addCoeff(map(ni0), map(ni1), value); + } + + inline void addExtraCoeff(Face elem, unsigned ei, unsigned ni, Scalar value) { + m_inserter.addExtraCoeff(elem, ei*2 + 0, ni , value); + m_inserter.addExtraCoeff(elem, ei*2 + 1, map(ni), value); + } + + template < typename Derived > + inline void setExtraRhs(Face elem, unsigned ei, const Eigen::DenseBase& value) { + m_inserter.setExtraRhs(elem, ei*2 + 0, value); + m_inserter.setExtraRhs(elem, ei*2 + 1, value); + } + +private: + Inserter& m_inserter; + int m_from; + int m_to; +}; + +} + + +template < typename _Element > +unsigned +SingularElementDecorator<_Element>::nCoefficients( + const Mesh& mesh, Face element, SolverError* error) const +{ + return Base::nCoefficients(mesh, element, error) * + (mesh.nSingulars(element)? 2: 1); +} + + +template < typename _Element > +unsigned +SingularElementDecorator<_Element>::nExtraConstraints( + const Mesh& mesh, Face element) const +{ + return Base::nExtraConstraints(mesh, element) * + (mesh.nSingulars(element)? 2: 1); +} + + +template < typename _Element > +template < typename Inserter > +void +SingularElementDecorator<_Element>::addCoefficients( + Inserter& inserter, const Mesh& mesh, + Face element, SolverError* error) +{ + unsigned nSingular = mesh.nSingulars(element); + + if(nSingular > 1 && error) + error->warning("Element with more than one singular vertex"); + + if(!nSingular) { + Base::addCoefficients(inserter, mesh, element, error); + } else { + typedef internal::SingularInserter SInserter; + SInserter sInserter(inserter, mesh, element); + Base::addCoefficients(sInserter, mesh, element, error); + } +} + + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/solverError.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/solverError.h new file mode 100644 index 000000000..82cb47a5e --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/solverError.h @@ -0,0 +1,64 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_SOLVER_ERROR_ +#define _VITELOTTE_SOLVER_ERROR_ + + +#include + + +namespace Vitelotte +{ + + +/** + * \brief A class that encodes the status of a FemSolver object. + */ +class SolverError +{ +public: + enum Status + { + STATUS_OK, + STATUS_WARNING, + STATUS_ERROR + }; + +public: + inline SolverError() : m_status(STATUS_OK), m_message() {} + inline Status status() const { return m_status; } + inline const std::string& message() const { return m_message; } + inline void resetStatus() { m_status = STATUS_OK; m_message.clear(); } + + inline void set(Status status, const std::string& message) + { + if(m_status > status) + return; + m_status = status; + m_message = message; + } + + inline void error(const std::string& message) + { + set(STATUS_ERROR, message); + } + + inline void warning(const std::string& message) + { + set(STATUS_WARNING, message); + } + +private: + Status m_status; + std::string m_message; +}; + + +} + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/vgMesh.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/vgMesh.h new file mode 100644 index 000000000..a3026db64 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/vgMesh.h @@ -0,0 +1,675 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_VG_MESH_ +#define _VITELOTTE_VG_MESH_ + + +#include +#include +#include +#include +#include + +#include + +#include "../../common/surface_mesh/surfaceMesh.h" +#include "../../common/gl_utils/color.h" + +#include "bezierPath.h" + + +namespace Vitelotte +{ + + +using PatateCommon::ColorSpace; + + +enum +{ + Dynamic = Eigen::Dynamic +}; + + +#define PATATE_VG_MESH_HALFEDGE_ATTR_FLAG(_index) (1 << _index) +#define PATATE_VG_MESH_VERTEX_ATTR_FLAG(_index) (1 << (16 + _index)) + + +/** + * \brief A mesh with data suitable for representing complex color gradients, + * among other. + * + * For more details about VGMesh, see the [user manual](vitelotte_user_manual_vg_mesh_page). + */ +template < typename _Scalar, int _Dims=2, int _Coeffs=4 > +class VGMesh: public PatateCommon::SurfaceMesh +{ +public: + typedef _Scalar Scalar; + + enum { + DimsAtCompileTime = _Dims, + CoeffsAtCompileTime = _Coeffs + }; + + typedef VGMesh Self; + + typedef Eigen::Matrix Vector; + typedef Eigen::Matrix Value; + typedef Eigen::Matrix Gradient; + + typedef BezierSegment CurvedEdge; + +protected: + typedef Eigen::Matrix VectorMatrix; + typedef Eigen::Matrix NodeMatrix; + +public: + typedef typename VectorMatrix::ColXpr VectorXpr; + typedef typename VectorMatrix::ConstColXpr ConstVectorXpr; + typedef typename NodeMatrix::ColXpr ValueXpr; + typedef typename NodeMatrix::ConstColXpr ConstValueXpr; + typedef typename Value::ConstantReturnType UnconstrainedNodeType; + + struct Node : public BaseHandle + { + explicit Node(int _idx = -1) : BaseHandle(_idx) {} + std::ostream& operator<<(std::ostream& os) const { return os << 'n' << idx(); } + }; + + enum HalfedgeAttribute + { + TO_VERTEX_VALUE, + FROM_VERTEX_VALUE, + EDGE_VALUE, + EDGE_GRADIENT, + + HALFEDGE_ATTRIB_COUNT + }; + + // TODO: Unused now. Use it or remove it ? + enum VertexAttribute + { + VERTEX_POSITION, + VERTEX_GRADIENT_CONSTRAINT, + + VERTEX_ATTRIB_COUNT + }; + + enum + { + // Nodes + TO_VERTEX_VALUE_FLAG = PATATE_VG_MESH_HALFEDGE_ATTR_FLAG(TO_VERTEX_VALUE), + FROM_VERTEX_VALUE_FLAG = PATATE_VG_MESH_HALFEDGE_ATTR_FLAG(FROM_VERTEX_VALUE), + EDGE_VALUE_FLAG = PATATE_VG_MESH_HALFEDGE_ATTR_FLAG(EDGE_VALUE), + EDGE_GRADIENT_FLAG = PATATE_VG_MESH_HALFEDGE_ATTR_FLAG(EDGE_GRADIENT), + + // Other + VERTEX_GRADIENT_CONSTRAINT_FLAG = + PATATE_VG_MESH_VERTEX_ATTR_FLAG(VERTEX_GRADIENT_CONSTRAINT), + + // Aggregates + LINEAR_FLAGS = TO_VERTEX_VALUE_FLAG | FROM_VERTEX_VALUE_FLAG, + QUADRATIC_FLAGS = LINEAR_FLAGS | EDGE_VALUE_FLAG, + + MORLEY_FLAGS = LINEAR_FLAGS | EDGE_GRADIENT_FLAG, + FV_FLAGS = QUADRATIC_FLAGS | EDGE_GRADIENT_FLAG | VERTEX_GRADIENT_CONSTRAINT_FLAG + }; + +public: + /// this class iterates linearly over all nodes + /// \sa nodesBegin(), nodesEnd() + class NodeIterator + { + public: + + /// Default constructor + NodeIterator(Node n=Node(), const Self* m=NULL) : hnd_(n), mesh_(m) + { + if(mesh_ && mesh_->garbage()) + { + while(mesh_->isValid(hnd_) && mesh_->isDeleted(hnd_)) + { + hnd_ = Node(hnd_.idx() + 1); + } + } + } + + /// get the node the iterator refers to + Node operator*() const { return hnd_; } + + /// are two iterators equal? + bool operator==(const NodeIterator& rhs) const + { + return (hnd_==rhs.hnd_); + } + + /// are two iterators different? + bool operator!=(const NodeIterator& rhs) const + { + return !operator==(rhs); + } + + /// pre-increment iterator + NodeIterator& operator++() + { + hnd_ = Node(hnd_.idx() + 1); + assert(mesh_); + while(mesh_->garbage() && mesh_->isValid(hnd_) && mesh_->isDeleted(hnd_)) + { + hnd_ = Node(hnd_.idx() + 1); + } + return *this; + } + + /// pre-decrement iterator + NodeIterator& operator--() + { + --hnd_.idx_; + assert(mesh_); + while (mesh_->garbage() && mesh_->isValid(hnd_) && mesh_->isDeleted(hnd_)) --hnd_.idx_; + return *this; + } + + private: + Node hnd_; + const Self* mesh_; + }; + + /// Node property of type T + /// \sa VertexProperty, HalfedgeProperty, EdgeProperty, FaceProperty + template class NodeProperty : public PatateCommon::Property + { + public: + + /// default constructor + explicit NodeProperty() {} + explicit NodeProperty(PatateCommon::Property p) + : PatateCommon::Property(p) {} + + /// access the data stored for vertex \c v + typename PatateCommon::Property::Reference operator[](Node n) + { + return PatateCommon::Property::operator[](n.idx()); + } + + /// access the data stored for vertex \c v + typename PatateCommon::Property::ConstReference operator[](Node n) const + { + return PatateCommon::Property::operator[](n.idx()); + } + }; + +public: + + /// \name Constructor, derstructor, assignement + /// \{ + + /// \brief Create a VGMesh with `attirbutes` flags activated. + explicit VGMesh(unsigned attributes = 0); + explicit VGMesh(unsigned nDims, unsigned nCoeffs, unsigned attributes=0); + virtual ~VGMesh() {} + + VGMesh(const Self& other); + + VGMesh& operator=(const Self& rhs); + VGMesh& assign(const Self& rhs); + + /// \} + + + /// \name Mesh and general + /// \{ + + inline unsigned nDims() const { return m_positions.rows(); } + inline unsigned nCoeffs() const { return m_nodes.rows(); } + + void setNDims(unsigned nDims); + +protected: + inline unsigned positionsCapacity() const { return m_positions.cols(); } + +public: + + /** + * \brief Set the number of coefficients to nCoeffs. + * + * If this mesh has a fixed number of coefficients, nCoeffs must match it. + * + * If this function is used to increase the number of coefficients of a + * non-empty mesh, be aware that the new coefficients will be + * uninitialized. In other words, value() will return partially initialized + * vectors. + */ + void setNCoeffs(unsigned nCoeffs); + + inline void reserve(unsigned nvertices, unsigned nedges, unsigned nfaces, + unsigned nnodes); + inline void clear(); + void garbageCollection(unsigned flags = 0); + void releaseGCIndexMaps(); + + using PatateCommon::SurfaceMesh::gcMap; + inline Node gcMap(Node n) const + { + assert(n.isValid() && n.idx() < int(m_gcNodeMap.size())); + return m_gcNodeMap[n.idx()]; + } + + template + inline Vertex addVertex(const Eigen::DenseBase& pos); + + inline ConstVectorXpr position(Vertex v) const + { assert(unsigned(v.idx()) < verticesSize()); return m_positions.col(v.idx()); } + + inline VectorXpr position(Vertex v) + { assert(unsigned(v.idx()) < verticesSize()); return m_positions.col(v.idx()); } + + using PatateCommon::SurfaceMesh::isValid; + inline bool isValid(Node n) const; + + inline ColorSpace colorSpace() const { + return m_colorSpace; + } + + inline void setColorSpace(ColorSpace colorSpace) { + m_colorSpace = colorSpace; + } + + inline bool isCurved(Edge e) const { + return m_curvedEdges.find(e) != m_curvedEdges.end(); + } + + inline bool isCurved(Halfedge h) const { + return isCurved(edge(h)); + } + + inline const CurvedEdge& edgeCurve(Edge e) const { + return m_curvedEdges.at(e); + } + + inline CurvedEdge edgeCurve(Halfedge h) const { + return halfedgeOrientation(h)? + edgeCurve(edge(h)).getBackward(): + edgeCurve(edge(h)); + } + + inline void setEdgeCurve(Edge e, const CurvedEdge& curve) { + m_curvedEdges[e] = curve; + } + + inline void setEdgeCurve(Halfedge h, const CurvedEdge& curve) { + if(halfedgeOrientation(h)) setEdgeCurve(edge(h), curve.getBackward()); + else setEdgeCurve(edge(h), curve); + } + + /** + * \brief Return a boolean such that opposite halfedges give a different + * result. + * + * In practice, return `fromVertex(h).idx() > toVertex(h).idx()`. This + * make the method easily predictable. + */ + inline bool halfedgeOrientation(Halfedge h) const { + // It is possible to use h.idx() % 2 for a similar result, but it makes + // the halfedge orientation hard to predict. + return fromVertex(h).idx() > toVertex(h).idx(); + } + + template NodeProperty addNodeProperty(const std::string& name, const T t=T()) + { + return NodeProperty(m_nprops.add(name, t)); + } + + template NodeProperty getNodeProperty(const std::string& name) const + { + return NodeProperty(m_nprops.get(name)); + } + + template NodeProperty nodeProperty(const std::string& name, const T t=T()) + { + return NodeProperty(m_nprops.getOrAdd(name, t)); + } + + template void removeNodeProperty(NodeProperty& p) + { + m_nprops.remove(p); + } + + const std::type_info& getNodePropertyType(const std::string& name) + { + return m_nprops.getType(name); + } + + std::vector nodeProperties() const + { + return m_nprops.properties(); + } + + /// \} + + + /// \name Attributes + /// \{ + + /// \brief Return active attribute flags ored together. + unsigned getAttributes() const { return m_attributes; } + + /** + * \brief Set the active attributes to `attributes`. + * + * Attributes that are removed will release corresponding memory and + * new attributes will be set to their default value. Attibute that stay + * active won't be modified. + * + * \param attributes Attributes flags ORed together. + */ + void setAttributes(unsigned attributes); + + inline bool hasAttribute(HalfedgeAttribute attr) const + { assert(attr < HALFEDGE_ATTRIB_COUNT); return m_attributes & (1 << attr); } + inline bool hasToVertexValue() const { return m_attributes & TO_VERTEX_VALUE_FLAG; } + inline bool hasFromVertexValue() const { return m_attributes & FROM_VERTEX_VALUE_FLAG; } + inline bool hasEdgeValue() const { return m_attributes & EDGE_VALUE_FLAG; } + inline bool hasEdgeGradient() const { return m_attributes & EDGE_GRADIENT_FLAG; } + inline bool hasVertexGradientConstraint() const + { return m_attributes & VERTEX_GRADIENT_CONSTRAINT_FLAG; } + + inline Node toVertexValueNode(Halfedge h) const + { return halfedgeNode(h, TO_VERTEX_VALUE); } + inline Node& toVertexValueNode(Halfedge h) + { return halfedgeNode(h, TO_VERTEX_VALUE); } + + inline Node fromVertexValueNode(Halfedge h) const + { return halfedgeNode(h, FROM_VERTEX_VALUE); } + inline Node& fromVertexValueNode(Halfedge h) + { return halfedgeNode(h, FROM_VERTEX_VALUE); } + + inline Node edgeValueNode(Halfedge h) const + { return halfedgeNode(h, EDGE_VALUE); } + inline Node& edgeValueNode(Halfedge h) + { return halfedgeNode(h, EDGE_VALUE); } + + inline Node edgeGradientNode(Halfedge h) const + { return halfedgeNode(h, EDGE_GRADIENT); } + inline Node& edgeGradientNode(Halfedge h) + { return halfedgeNode(h, EDGE_GRADIENT); } + + HalfedgeAttribute oppositeAttribute(HalfedgeAttribute attr) const; + + inline Node halfedgeNode(Halfedge h, HalfedgeAttribute attr) const; + inline Node& halfedgeNode(Halfedge h, HalfedgeAttribute attr); + + inline Node halfedgeOppositeNode(Halfedge h, HalfedgeAttribute attr) const; + inline Node& halfedgeOppositeNode(Halfedge h, HalfedgeAttribute attr); + + /// \} + + + /// \name Point gradient constraints + /// \{ + + inline bool isGradientConstraint(Vertex v) const + { assert(hasVertexGradientConstraint()); return m_vertexGradientConstraints.count(v); } + inline Gradient& gradientConstraint(Vertex v) + { assert(hasVertexGradientConstraint()); return m_vertexGradientConstraints.at(v); } + inline const Gradient& gradientConstraint(Vertex v) const + { assert(hasVertexGradientConstraint()); return m_vertexGradientConstraints.at(v); } + inline void setGradientConstraint(Vertex v, const Gradient& grad); + inline void removeGradientConstraint(Vertex v); + + inline int nVertexGradientConstraints() const + { assert(hasVertexGradientConstraint()); return m_vertexGradientConstraints.size(); } + inline unsigned nVertexGradientConstraints(Halfedge h) const; + inline unsigned nVertexGradientConstraints(Face f) const; + + /// \} + + + /// \name Low-level nodes manipulation + /// \{ + + inline unsigned nodesSize() const { return m_nprops.size(); } + /// \brief Return the number of nodes. + inline unsigned nNodes() const { return nodesSize() - m_deletedNodes; } + +protected: + inline unsigned nodesCapacity() const { return m_nodes.cols(); } + +public: + using PatateCommon::SurfaceMesh::isDeleted; + inline bool isDeleted(Node n) const { return m_ndeleted[n]; } + + /// \brief Mark unused node as deleted, but does not free memory. + /// \sa garbageCollection() + void deleteUnusedNodes(); + + NodeIterator nodesBegin() const { return NodeIterator(Node(0), this); } + NodeIterator nodesEnd() const { return NodeIterator(Node(nodesSize()), this); } + + /** + * \brief Add and return a node with value `value` or an unknown node + * if no parameter is provided. + */ + inline Node addNode() { return addNode(unconstrainedValue()); } + template + inline Node addNode(const Eigen::DenseBase& value); + + /// \brief Read only access to the value of `node`. + inline ConstValueXpr value(Node node) const + { assert(unsigned(node.idx()) < nodesSize()); return m_nodes.col(node.idx()); } + + /// \brief Read-write access to the value of `node`. + inline ValueXpr value(Node node) + { assert(unsigned(node.idx()) < nodesSize()); return m_nodes.col(node.idx()); } + + inline UnconstrainedNodeType unconstrainedValue() const + { return Value::Constant(nCoeffs(), std::numeric_limits::quiet_NaN()); } + + /// \brief Return true iff `node` is a constraint. + inline bool isConstraint(Node node) const + { return isValid(node) && !std::isnan(value(node)[0]); } + + /** + * \brief Return true if the mesh has unknown nodes. + * \par Complexity + * This function operates in \f$O(n)\f$ with \f$n\f$ the number of nodes. + */ + inline bool hasUnknowns() const; + inline unsigned nUnknowns() const; + + /// \} + + + /// \name High-level nodes manipulation + /// \{ + + /** + * \brief Set nodes of all the halfedges inbetween halfedges `from` and + * `to` (in counter-clockwise direction) to `node`. + * + * \pre `from` and `to` mush have the same source vertex. + */ + void setVertexNodes(Node node, Halfedge from, Halfedge to); + + /** + * \brief Similar to VGMesh::setVertexNode, but for singularities. + * + * set nodes for a singularity starting with value `fromNode` in the + * direction of halfedge `from` to value `toNode` in the direction of + * halfedge `to`. + * + * \pre The mesh must have the VertexFromValue attribute active. + * \pre `from` and `to` mush have the same source vertex. + * \pre `fromNode` and `toNode` mush be constraints. + */ + void setSingularity(Node fromNode, Node toNode, Halfedge from, Halfedge to); + + /** + * \brief Simplify constraints when possible. + * + * This method operate in two steps: + * 1. Simplify each pair of opposite nodes (see + * VGMesh::simplifyOppositeNodes()). + * 2. Go around each vertex and try to simplfy them. + * + * After processing, the resulting mesh will use less nodes than the + * original if simplifications where possible, but still produce the same + * final result after finalization and solving. It may be usefull to make + * the solver slightly faster or to diminish file size. + * + * One typically wish to call VGMesh::deleteUnusedNodes() and + * VGMesh::garbageCollection after this function to effectively remove + * unused nodes. + * + * \note Currently, this method only work locally. This mean for instance + * that it won't merge all constraint nodes with the same value if they are + * not on the same primitive. + * + * \warning This method does not support non-local unknown node, ie. + * unknown node used on different primitive of the mesh to achieve special + * effects like color "teleportation". Using it in such case may lead to + * unexpected results. + */ + void simplifyConstraints(); + + void simplifyVertexArcConstraints(Halfedge from, Halfedge to); + void simplifyEdgeConstraints(Edge e); + + /** + * \brief Replace `n0` and `n1` by a single node on an invalid node if + * possible. + * + * This is mainly an helper funciton for VGMesh::simplifyConstraints(). + * + * \warning This method does not support non-local unknown node, ie. + * unknown node used on different primitive of the mesh to achieve special + * effects like color "teleportation". Using it in such case may lead to + * unexpected results. + */ + void simplifyOppositeNodes(Node& n0, Node& n1, + bool b0 = false, bool b1 = false) const; + + /** + * /brief Finalize a mesh with invalid node so that it can be send to a + * Solver. + * + * \todo create a tutorial page about this an link it from here. + * + * \warning This method does not support non-local unknown node, ie. + * unknown node used on different primitive of the mesh to achieve special + * effects like color "teleportation". Using it in such case may lead to + * unexpected results. + */ + void finalize(); + + void finalizeVertexArc(Halfedge from, Halfedge to); + void finalizeEdge(Edge e); + + /// \} + /// + + + /// \name Quadratic patches + /// \{ + + /// \brief Return true if the target vertex of `h` is singular. + inline bool isSingular(Halfedge h) const; + + /// \brief Return the number of singular vertex around `f`. + inline unsigned nSingulars(Face f) const; + + /** + * \brief Return the number of singular faces in the mesh. + * + * \par Complexity + * This method opperates in \f$O(n)\f$ with \f$n\f$ the number of + * halfedges. + */ + inline unsigned nSingularFaces() const; + + /// \} + + +protected: + // FIXME: Would likely be better to use a hash map, but this is only + // available in C++11... + typedef std::pair VertexGradientPair; + typedef std::map, + Eigen::aligned_allocator > VertexGradientMap; + + typedef std::pair EdgeCurvePair; + typedef std::map, + Eigen::aligned_allocator > CurvedEdgesMap; + +protected: + /// \name Removed topological operations + /// \{ + + inline void triangulate() { assert(false); } + inline void triangulate(Face /*f*/) { assert(false); } + + inline bool isCollapseOk(Halfedge /*h*/) { assert(false); return false; } + inline void collapse(Halfedge /*h*/) { assert(false); } + + inline void split(Face /*f*/, Vertex /*v*/) { assert(false); } + inline void split(Edge /*e*/, Vertex /*v*/) { assert(false); } + + inline Halfedge insertVertex(Edge /*e*/, Vertex /*v*/) + { assert(false); return Halfedge(); } + inline Halfedge insertVertex(Halfedge /*h*/, Vertex /*v*/) + { assert(false); return Halfedge(); } + + inline Halfedge insertEdge(Halfedge /*h0*/, Halfedge /*h1*/) + { assert(false); return Halfedge(); } + + inline bool isFlipOk(Edge /*e*/) const { assert(false); return false; } + inline void flip(Edge /*e*/) { assert(false); } + + /// \} + + /// \name Helper methods + /// \{ + + void copyVGMeshMembers(const Self& rhs); + void findConstrainedEdgesSimplify(Vertex vx, + std::vector& consEdges); + void resizePositionsMatrix(unsigned rows, unsigned cols); + void resizeNodesMatrix(unsigned rows, unsigned cols); + + /// \} + +protected: + static const char* _halfedgeAttrName[HALFEDGE_ATTRIB_COUNT]; + +protected: + unsigned m_attributes; + ColorSpace m_colorSpace; + + PatateCommon::PropertyContainer m_nprops; + + VectorMatrix m_positions; + VertexGradientMap m_vertexGradientConstraints; + CurvedEdgesMap m_curvedEdges; + + HalfedgeProperty m_halfedgeAttributes[HALFEDGE_ATTRIB_COUNT]; + + unsigned m_deletedNodes; + NodeMatrix m_nodes; + NodeProperty m_ndeleted; + std::vector m_gcNodeMap; + +}; + +} // namespace Vitelotte + + +#include "vgMesh.hpp" + + +#endif // VGMESH_H + diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/vgMesh.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/vgMesh.hpp new file mode 100644 index 000000000..1837dc47d --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Core/vgMesh.hpp @@ -0,0 +1,904 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include "vgMesh.h" + + +namespace Vitelotte +{ + + +template < typename _Scalar, int _Dim, int _Chan > +VGMesh<_Scalar, _Dim, _Chan>::VGMesh(unsigned attributes) + : m_attributes(0), + m_deletedNodes(0) +{ + m_ndeleted = addNodeProperty("n:deleted", false); + setAttributes(attributes); +} + + +template < typename _Scalar, int _Dim, int _Chan > +VGMesh<_Scalar, _Dim, _Chan>::VGMesh( + unsigned nDims, unsigned nCoeffs, unsigned attributes) + : m_attributes(0), + m_deletedNodes(0) +{ + setNDims(nDims); + setNCoeffs(nCoeffs); + m_ndeleted = addNodeProperty("n:deleted", false); + setAttributes(attributes); +} + + +template < typename _Scalar, int _Dim, int _Chan > +VGMesh<_Scalar, _Dim, _Chan>::VGMesh(const Self& other) + : PatateCommon::SurfaceMesh(other) +{ + operator=(other); + copyVGMeshMembers(other); +} + + +template < typename _Scalar, int _Dim, int _Chan > +VGMesh<_Scalar, _Dim, _Chan>& +VGMesh<_Scalar, _Dim, _Chan>::operator=(const Self& rhs) +{ + if(&rhs != this) + { + // FIXME: SurfaceMesh's operator= wont work with properties of different types. + PatateCommon::SurfaceMesh::operator=(rhs); + copyVGMeshMembers(rhs); + } + return *this; +} + + +template < typename _Scalar, int _Dim, int _Chan > +VGMesh<_Scalar, _Dim, _Chan>& +VGMesh<_Scalar, _Dim, _Chan>::assign(const Self& rhs) +{ + // FIXME: Implement this properly, ie. copy VGMesh properties without other + // custom properties. Disable it in the meantime. + assert(false); + if(&rhs != this) + { + // FIXME: SurfaceMesh's operator= wont work with properties of different types. + PatateCommon::SurfaceMesh::assign(rhs); + // Note: this function is unsuitable for assing() + copyVGMeshMembers(rhs); + } + return *this; +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::setNDims(unsigned nDims) +{ + assert(int(DimsAtCompileTime) == int(Dynamic) || + int(nDims) == int(DimsAtCompileTime)); + resizePositionsMatrix(nDims, positionsCapacity()); +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::setNCoeffs(unsigned nCoeffs) +{ + assert(int(CoeffsAtCompileTime) == int(Dynamic) || + int(nCoeffs) == int(CoeffsAtCompileTime)); + resizeNodesMatrix(nCoeffs, nodesCapacity()); +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::reserve( + unsigned nvertices, unsigned nedges, unsigned nfaces, unsigned nnodes) +{ + PatateCommon::SurfaceMesh::reserve(nvertices, nedges, nfaces); + if(nnodes > nodesCapacity()) + { + if(nnodes > nodesCapacity()) resizeNodesMatrix(nCoeffs(), nnodes); + m_nprops.reserve(nnodes); + } +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::clear() +{ + PatateCommon::SurfaceMesh::clear(); + m_nprops.resize(0); + m_nprops.freeMemory(); + m_deletedNodes = 0; + resizePositionsMatrix(nDims(), 0); + resizeNodesMatrix(nCoeffs(), 0); +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::garbageCollection(unsigned flags) +{ + // FIXME: this operation changes vertex order, so it can break edge + // orientation. A solution is to invert gradient nodes of affected edges + PatateCommon::SurfaceMesh::garbageCollection(GC_DONT_RELEASE_INDEX_MAPS); + + unsigned nN = nodesSize(); + + m_gcNodeMap.resize(nN); + for(unsigned i = 0; i < nN; ++i) m_gcNodeMap[i] = Node(i); + + + // remove deleted nodes + if(nN > 0) + { + int i0 = 0; + int i1 = nN - 1; + + while (1) + { + // find first deleted and last un-deleted + while(!m_ndeleted[Node(i0)] && i0 < i1) ++i0; + while( m_ndeleted[Node(i1)] && i0 < i1) --i1; + if(i0 >= i1) break; + + // swap + m_nprops.swap(i0, i1); + std::swap(m_gcNodeMap[i0], m_gcNodeMap[i1]); + }; + + // remember new size + nN = m_ndeleted[Node(i0)] ? i0 : i0+1; + } + + + + // remap vertices + for(unsigned vi = 0; vi < nVertices(); ++vi) { + Vertex v(vi); + if(v != gcMap(v)) + { + m_positions.col(vi) = m_positions.col(gcMap(v).idx()); + } + } + + VertexGradientMap vxGradConstraints; + for(typename VertexGradientMap::const_iterator vxGrad = m_vertexGradientConstraints.begin(); + vxGrad != m_vertexGradientConstraints.end(); ++vxGrad) + { + vxGradConstraints.insert(std::make_pair(gcMap(vxGrad->first), vxGrad->second)); + } + m_vertexGradientConstraints.swap(vxGradConstraints); + + // remap halfedges + for(HalfedgeIterator hit = halfedgesBegin(); hit != halfedgesEnd(); ++hit) + { + for(unsigned ai = 0; ai < HALFEDGE_ATTRIB_COUNT; ++ai) + { + HalfedgeAttribute attr = HalfedgeAttribute(ai); + if(hasAttribute(attr)) + { + Node& n = halfedgeNode(*hit, attr); + if(n.isValid()) n = gcMap(n); + } + } + } + + // remap nodes + for(unsigned ni = 0; ni < nN; ++ni) + { + Node n(ni); + if(n != gcMap(n)) + { + m_nodes.col(ni) = m_nodes.col(gcMap(n).idx()); + } + } + + if(!(flags & GC_DONT_RELEASE_INDEX_MAPS)) + { + releaseGCIndexMaps(); + } + + m_nprops.resize(nN); m_nprops.freeMemory(); + + m_deletedNodes = 0; + m_garbage = false; +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::releaseGCIndexMaps() +{ + PatateCommon::SurfaceMesh::releaseGCIndexMaps(); + std::vector nMap; + m_gcNodeMap.swap(nMap); +} + + +template < typename _Scalar, int _Dim, int _Chan > +template < typename Derived > +PatateCommon::SurfaceMesh::Vertex +VGMesh<_Scalar, _Dim, _Chan>::addVertex(const Eigen::DenseBase& pos) +{ + if(positionsCapacity() == verticesSize()) + { + unsigned size = std::max(16u, verticesSize() * 2); + resizePositionsMatrix(nDims(), size); + } + m_positions.col(verticesSize()) = pos; + Vertex v = PatateCommon::SurfaceMesh::addVertex(); + assert(nodesSize() <= positionsCapacity()); + return v; +} + + +template < typename _Scalar, int _Dim, int _Chan > +bool +VGMesh<_Scalar, _Dim, _Chan>::isValid(Node n) const +{ + return 0 <= n.idx() && unsigned(n.idx()) < nodesSize(); +} + + +template < typename _Scalar, int _Dim, int _Chan > +void VGMesh<_Scalar, _Dim, _Chan>::setAttributes(unsigned attributes) +{ + for(int ai = 0; ai < HALFEDGE_ATTRIB_COUNT; ++ai) { + unsigned flag = PATATE_VG_MESH_HALFEDGE_ATTR_FLAG(ai); + if(!(m_attributes & flag) && (attributes & flag)) { + m_halfedgeAttributes[ai] = + addHalfedgeProperty(_halfedgeAttrName[ai], Node()); + } else if((m_attributes & flag) && !(attributes & flag)) { + removeHalfedgeProperty(m_halfedgeAttributes[ai]); + } + } + + + m_attributes = attributes; +} + + +template < typename _Scalar, int _Dim, int _Chan > +typename VGMesh<_Scalar, _Dim, _Chan>::HalfedgeAttribute +VGMesh<_Scalar, _Dim, _Chan>:: + oppositeAttribute(HalfedgeAttribute attr) const +{ + switch(attr) + { + case TO_VERTEX_VALUE: + return FROM_VERTEX_VALUE; + case FROM_VERTEX_VALUE: + return TO_VERTEX_VALUE; + case EDGE_VALUE: + case EDGE_GRADIENT: + return attr; + default: + break; + } + abort(); +} + + +template < typename _Scalar, int _Dim, int _Chan > +typename VGMesh<_Scalar, _Dim, _Chan>::Node +VGMesh<_Scalar, _Dim, _Chan>:: + halfedgeNode(Halfedge h, HalfedgeAttribute attr) const +{ + return const_cast(this)->halfedgeNode(h, attr); +} + + +template < typename _Scalar, int _Dim, int _Chan > +typename VGMesh<_Scalar, _Dim, _Chan>::Node& +VGMesh<_Scalar, _Dim, _Chan>:: + halfedgeNode(Halfedge h, HalfedgeAttribute attr) +{ + if(attr == FROM_VERTEX_VALUE && !hasFromVertexValue()) + attr = TO_VERTEX_VALUE; + + assert(hasAttribute(attr)); + return m_halfedgeAttributes[attr][h]; +} + + +template < typename _Scalar, int _Dim, int _Chan > +typename VGMesh<_Scalar, _Dim, _Chan>::Node +VGMesh<_Scalar, _Dim, _Chan>:: + halfedgeOppositeNode(Halfedge h, HalfedgeAttribute attr) const +{ + return const_cast(this)->halfedgeOppositeNode(h, attr); +} + + +template < typename _Scalar, int _Dim, int _Chan > +typename VGMesh<_Scalar, _Dim, _Chan>::Node& +VGMesh<_Scalar, _Dim, _Chan>:: + halfedgeOppositeNode(Halfedge h, HalfedgeAttribute attr) +{ + return halfedgeNode(oppositeHalfedge(h), oppositeAttribute(attr)); +} + + +template < typename _Scalar, int _Dim, int _Chan > +void VGMesh<_Scalar, _Dim, _Chan>:: + setGradientConstraint(Vertex v, const Gradient& grad) +{ + assert(hasVertexGradientConstraint()); + + m_vertexGradientConstraints[v] = grad; +} + + +template < typename _Scalar, int _Dim, int _Chan > +void VGMesh<_Scalar, _Dim, _Chan>::removeGradientConstraint(Vertex v) +{ + assert(hasVertexGradientConstraint()); + + m_vertexGradientConstraints.erase(v); +} + + +template < typename _Scalar, int _Dim, int _Chan > +unsigned VGMesh<_Scalar, _Dim, _Chan>::nVertexGradientConstraints(Halfedge h) const +{ + assert(hasVertexGradientConstraint()); + + return isGradientConstraint(fromVertex(h)) + isGradientConstraint(toVertex(h)); +} + + +template < typename _Scalar, int _Dim, int _Chan > +unsigned VGMesh<_Scalar, _Dim, _Chan>::nVertexGradientConstraints(Face f) const +{ + assert(hasVertexGradientConstraint()); + + VertexAroundFaceCirculator vit = vertices(f); + VertexAroundFaceCirculator vend = vit; + unsigned count = 0; + + do + { + if(isGradientConstraint(*vit)) ++count; + ++vit; + } while(vit != vend); + + return count; +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::deleteUnusedNodes() +{ + for(unsigned i = 0; i < nodesSize(); ++i) + { + m_ndeleted[Node(i)] = true; + } + + HalfedgeIterator hBegin = halfedgesBegin(), + hEnd = halfedgesEnd(); + for(HalfedgeIterator hit = hBegin; hit != hEnd; ++hit) + { + if(!isBoundary(*hit)) + { + if(hasToVertexValue() && toVertexValueNode(*hit).isValid()) + m_ndeleted[toVertexValueNode(*hit)] = false; + if(hasFromVertexValue() && fromVertexValueNode(*hit).isValid()) + m_ndeleted[fromVertexValueNode(*hit)] = false; + if(hasEdgeValue() && edgeValueNode(*hit).isValid()) + m_ndeleted[edgeValueNode(*hit)] = false; + if(hasEdgeGradient() && edgeGradientNode(*hit).isValid()) + m_ndeleted[edgeGradientNode(*hit)] = false; + } + } + m_deletedNodes = 0; + for(unsigned ni = 0; ni < nodesSize(); ++ni) + { + if(m_ndeleted[Node(ni)]) ++m_deletedNodes; + } + if(m_deletedNodes) m_garbage = true; +} + + +template < typename _Scalar, int _Dim, int _Chan > +template +typename VGMesh<_Scalar, _Dim, _Chan>::Node +VGMesh<_Scalar, _Dim, _Chan>::addNode(const Eigen::DenseBase& value) +{ + if(nodesCapacity() == nodesSize()) + { + unsigned size = std::max(16u, nodesSize() * 2); + resizeNodesMatrix(nCoeffs(), size); + } + m_nodes.col(nodesSize()) = value; + m_nprops.pushBack(); + assert(nodesSize() <= nodesCapacity()); + return Node(nodesSize() - 1); +} + + +template < typename _Scalar, int _Dim, int _Chan > +bool +VGMesh<_Scalar, _Dim, _Chan>::hasUnknowns() const +{ + for(NodeIterator nit = nodesBegin(); + nit != nodesEnd(); ++nit) + if(!isConstraint(*nit)) + return true; + return false; +} + + +template < typename _Scalar, int _Dim, int _Chan > +unsigned +VGMesh<_Scalar, _Dim, _Chan>::nUnknowns() const +{ + unsigned nUnk = 0; + for(NodeIterator nit = nodesBegin(); + nit != nodesEnd(); ++nit) + if(!isConstraint(*nit)) + ++nUnk; + return nUnk; +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>:: + setVertexNodes(Node node, Halfedge from, Halfedge to) +{ + assert(fromVertex(from) == fromVertex(to)); + assert(hasToVertexValue()); + + Halfedge h = from; + do + { + if(hasFromVertexValue() && !isBoundary(h)) + { + fromVertexValueNode(h) = node; + } + + h = prevHalfedge(h); + + if(!isBoundary(h)) + { + toVertexValueNode(h) = node; + } + + h = oppositeHalfedge(h); + } + while(h != to && h != from); +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>:: + setSingularity(Node fromNode, Node toNode, Halfedge from, Halfedge to) +{ + assert(fromVertex(from) == fromVertex(to)); + assert(hasToVertexValue() && hasFromVertexValue()); + assert(isConstraint(fromNode) && isConstraint(toNode)); + + // Must be copies, not refs because m_nodes can be reallocated. + Value fromValue = value(fromNode); + Value toValue = value( toNode); + + const Vector& v = position(fromVertex(from)); + Vector fromVec = position(toVertex(from)) - v; + Vector toVec = position(toVertex( to)) - v; + + Scalar fromAngle = std::atan2(fromVec.y(), fromVec.x()); + Scalar toAngle = std::atan2( toVec.y(), toVec.x()); + Scalar totalAngle = toAngle - fromAngle; + if(totalAngle < Scalar(1.e-8)) totalAngle += Scalar(2.*M_PI); + + Node n = fromNode; + Halfedge h = from; + Halfedge last = oppositeHalfedge(to); + do + { + if(!isBoundary(h)) + fromVertexValueNode(h) = n; + + h = prevHalfedge(h); + + if(h != last) + { + // Current Halfedge is reversed. + Vector vec = position(fromVertex(h)) - v; + Scalar angle = std::atan2(vec.y(), vec.x()) - fromAngle; + if(angle < Scalar(1.e-8)) angle += Scalar(2.*M_PI); + Scalar a = angle / totalAngle; + n = addNode((Scalar(1)-a) * fromValue + a * toValue); + } + else + n = toNode; + + if(!isBoundary(h)) + toVertexValueNode(h) = n; + + h = oppositeHalfedge(h); + } + while(h != to && h != from); +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::simplifyConstraints() +{ + assert(hasToVertexValue()); + + std::vector consEdges; + consEdges.reserve(12); + + for(VertexIterator vit = verticesBegin(); + vit != verticesEnd(); ++vit) + { + consEdges.clear(); + findConstrainedEdgesSimplify(*vit, consEdges); + + Halfedge prev = consEdges.back(); + std::vector::iterator cit = consEdges.begin(); + for(; cit != consEdges.end(); ++cit) + { + simplifyVertexArcConstraints(prev, *cit); + prev = *cit; + } + } + + if(hasEdgeValue() || hasEdgeGradient()) + { + for(EdgeIterator eit = edgesBegin(); + eit != edgesEnd(); ++eit) + { + simplifyEdgeConstraints(*eit); + } + } +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::simplifyVertexArcConstraints( + Halfedge from, Halfedge to) +{ + assert(hasToVertexValue()); + assert(fromVertex(from) == fromVertex(to)); + + Node& n0 = halfedgeNode(from, FROM_VERTEX_VALUE); + Node& n1 = halfedgeOppositeNode(to, FROM_VERTEX_VALUE); + + bool n0c = n0.isValid() && isConstraint(n0); + bool n1c = n1.isValid() && isConstraint(n1); + + if((!n0c && !n1c) || + (n0c && !n1c) || + (n0c && n1c && value(n0) == value(n1))) + { + Node& n1o = halfedgeNode(to, FROM_VERTEX_VALUE); + if(n1o == n1) + { + n1o = n0; + } + n1 = n0; + } + else if(!n0c && n1c) + { + Node toReplace = n0; + Halfedge h = from; + while(true) { + Node& n = halfedgeNode(h, FROM_VERTEX_VALUE); + if(n == toReplace) + n = n1; + else + break; + + Node& no = halfedgeOppositeNode(h, FROM_VERTEX_VALUE); + if(no == toReplace) + n = n1; + else + break; + + h = nextHalfedge(h); + } + } +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::simplifyEdgeConstraints(Edge e) +{ + Halfedge h0 = halfedge(e, 0); + Halfedge h1 = halfedge(e, 1); + if(hasEdgeValue()) + { + Node& n0 = edgeValueNode(h0); + Node& n1 = edgeValueNode(h1); + simplifyOppositeNodes(n0, n1, isBoundary(h0), isBoundary(h1)); + } + if(hasEdgeGradient()) + { + Node& n0 = edgeGradientNode(h0); + Node& n1 = edgeGradientNode(h1); + simplifyOppositeNodes(n0, n1, isBoundary(h0), isBoundary(h1)); + } +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::simplifyOppositeNodes(Node& n0, Node& n1, + bool b0, bool b1) const +{ + bool n0c = !b0 && n0.isValid() && isConstraint(n0); + bool n1c = !b1 && n1.isValid() && isConstraint(n1); + + // if not a discontinuity, merge nodes. + if(n0c && n1c && value(n0) == value(n1)) + { + if(!b0) n0 = Node(std::min(n1.idx(), n0.idx())); + if(!b1) n1 = Node(std::min(n1.idx(), n0.idx())); + } + else if(!n0c && !n1c && n0 == n1) + { + // It is useless to use an unknown node here + // FIXME: Assume that these unknown node are only used + // around this vertex. + if(!b0) n0 = Node(); + if(!b1) n1 = Node(); + } + else if(n0c && !n1.isValid()) + { + if(!b1) n1 = n0; + } + else if(n1c && !n0.isValid()) + { + if(!b0) n0 = n1; + } +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::finalize() +{ + assert(hasToVertexValue()); + + std::vector consEdges; + consEdges.reserve(12); + + for(VertexIterator vit = verticesBegin(); + vit != verticesEnd(); ++vit) + { + consEdges.clear(); + findConstrainedEdgesSimplify(*vit, consEdges); + + if(consEdges.empty()) + consEdges.push_back(halfedge(*vit)); + + Halfedge prev = consEdges.back(); + std::vector::iterator cit = consEdges.begin(); + for(; cit != consEdges.end(); ++cit) + { + if(!isBoundary(prev)) + finalizeVertexArc(prev, *cit); + + prev = *cit; + } + } + + if(hasEdgeValue() || hasEdgeGradient()) + { + for(EdgeIterator eit = edgesBegin(); + eit != edgesEnd(); ++eit) + { + finalizeEdge(*eit); + } + } +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::finalizeVertexArc(Halfedge from, Halfedge to) +{ + Node n0 = halfedgeNode(from, FROM_VERTEX_VALUE); + Node n1 = halfedgeOppositeNode(to, FROM_VERTEX_VALUE); + + bool n0c = n0.isValid() && isConstraint(n0); + bool n1c = n1.isValid() && isConstraint(n1); + + Node n = Node(); + if(!n0c && !n1c) + { + // Free nodes, choose one valid or create a new one. + if (n0.isValid()) n = n0; + else if(n1.isValid()) n = n1; + else n = addNode(); + } + else if(n0c != n1c) + n = n0c? n0: n1; // One constraint, choose it + else if(n0 == n1 || value(n0) == value(n1)) + n = n0; // Same constraints, choose one arbitrarily + + // The remaining option is a singularity, that require special + // processing. + if(isValid(n)) + setVertexNodes(n, from, to); + else + setSingularity(n0, n1, from, to); +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::finalizeEdge(Edge e) +{ + Halfedge h0 = halfedge(e, 0); + Halfedge h1 = halfedge(e, 1); + if(hasEdgeValue()) + { + Node& n0 = edgeValueNode(h0); + Node& n1 = edgeValueNode(h1); + bool n0v = n0.isValid(); + bool n1v = n1.isValid(); + + if(!n0v && !isBoundary(h0)) n0 = n1v? n1: addNode(); + if(!n1v && !isBoundary(h1)) n1 = n0.isValid()? n0: addNode(); + } + if(hasEdgeGradient()) + { + Node& n0 = edgeGradientNode(h0); + Node& n1 = edgeGradientNode(h1); + bool n0v = n0.isValid(); + bool n1v = n1.isValid(); + + if(!n0v && !isBoundary(h0)) n0 = n1v? n1: addNode(); + if(!n1v && !isBoundary(h1)) n1 = n0.isValid()? n0: addNode(); + } +} + + +template < typename _Scalar, int _Dim, int _Chan > +bool +VGMesh<_Scalar, _Dim, _Chan>::isSingular(Halfedge h) const +{ + return hasFromVertexValue() && + toVertexValueNode(h) != fromVertexValueNode(nextHalfedge(h)); +} + + +template < typename _Scalar, int _Dim, int _Chan > +unsigned +VGMesh<_Scalar, _Dim, _Chan>::nSingulars(Face f) const +{ + unsigned nSingulars = 0; + HalfedgeAroundFaceCirculator + hit = halfedges(f), + hend = hit; + do + { + nSingulars += isSingular(*hit); + } + while(++hit != hend); + + return nSingulars; +} + + +template < typename _Scalar, int _Dim, int _Chan > +unsigned +VGMesh<_Scalar, _Dim, _Chan>::nSingularFaces() const +{ + unsigned n = 0; + for(FaceIterator fit = facesBegin(); + fit != facesEnd(); ++fit) + { + if(nSingulars(*fit)) + ++n; + } + return n; +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::copyVGMeshMembers(const Self& rhs) +{ + m_attributes = rhs.m_attributes; + + m_nprops = rhs.m_nprops; + + m_positions = rhs.m_positions; + m_vertexGradientConstraints = rhs.m_vertexGradientConstraints; + m_curvedEdges = rhs.m_curvedEdges; + + for(int ai = 0; ai < HALFEDGE_ATTRIB_COUNT; ++ai) { + m_halfedgeAttributes[ai] = getHalfedgeProperty(_halfedgeAttrName[ai]); + } + + m_deletedNodes = rhs.m_deletedNodes; + m_nodes = rhs.m_nodes/*.template cast()*/; + m_ndeleted = nodeProperty("n:deleted"); + + assert(verticesSize() <= positionsCapacity()); + assert(nodesSize() <= nodesCapacity()); +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>:: + findConstrainedEdgesSimplify(Vertex vx, + std::vector& consEdges) +{ + HalfedgeAroundVertexCirculator hit = halfedges(vx), + hEnd = hit; + do + { + Node& np = halfedgeOppositeNode(*hit, FROM_VERTEX_VALUE); + Node& n0 = halfedgeNode(*hit, FROM_VERTEX_VALUE); + bool boundary = isBoundary(edge(*hit)); + if(np.isValid() || n0.isValid() || boundary) + { + simplifyOppositeNodes(np, n0, isBoundary(oppositeHalfedge(*hit)), + isBoundary(*hit)); + + if(np.isValid() || n0.isValid() || boundary) + { + consEdges.push_back(*hit); + } + } + ++hit; + } + while(hit != hEnd); +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::resizePositionsMatrix(unsigned rows, unsigned cols) +{ + VectorMatrix positions(rows, cols); + unsigned minRows = std::min(rows, unsigned(m_positions.rows())); + unsigned minCols = std::min(cols, unsigned(m_positions.cols())); + positions.block(0, 0, minRows, minCols) = m_positions.block(0, 0, minRows, minCols); + m_positions.swap(positions); +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +VGMesh<_Scalar, _Dim, _Chan>::resizeNodesMatrix(unsigned rows, unsigned cols) +{ + NodeMatrix nodes(rows, cols); + unsigned minRows = std::min(rows, unsigned(m_nodes.rows())); + unsigned minCols = std::min(cols, unsigned(m_nodes.cols())); + nodes.block(0, 0, minRows, minCols) = m_nodes; + m_nodes.swap(nodes); +} + + +template < typename _Scalar, int _Dim, int _Chan > +const char* +VGMesh<_Scalar, _Dim, _Chan>::_halfedgeAttrName[] = { + "h:toVertexValueNode", + "h:fromVertexValueNode", + "h:edgeValueNode", + "h:edgeGradientNode" +}; + + + +} // namespace Vitelotte diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/dcMesh.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/dcMesh.h new file mode 100644 index 000000000..b4d37c950 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/dcMesh.h @@ -0,0 +1,296 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_DC_MESH_ +#define _VITELOTTE_DC_MESH_ + + +#include + +#include + +#include "../Core/vgMesh.h" + + +namespace Vitelotte +{ + + +/** + * \brief A 1D piecewise linear function. + */ +template +class PiecewiseLinearFunction +{ +public: + typedef _Value Value; + +private: + typedef std::pair FloatValuePair; + typedef std::map, Eigen::aligned_allocator > Samples; + +public: + typedef typename Samples::iterator Iterator; + typedef typename Samples::const_iterator ConstIterator; + +public: + PiecewiseLinearFunction() {} + + bool empty() const { return m_samples.empty(); } + unsigned size() const { return m_samples.size(); } + + bool has (float x) const { return m_samples.count(x); } + Value& sample(float x) { return m_samples.at(x); } + const Value& sample(float x) const { return m_samples.at(x); } + + void add(float x, const Value& v) { m_samples.insert(std::make_pair(x, v)); } + void remove(float x) { m_samples.erase(x); } + void clear() { m_samples.clear(); } + + Iterator begin() { return m_samples.begin(); } + ConstIterator begin() const { return m_samples.begin(); } + Iterator end() { return m_samples.end(); } + ConstIterator end() const { return m_samples.end(); } + + Value operator()(float x) const; + +private: + Samples m_samples; +}; + + +/** + * \brief A VGMesh with diffusion curves. + */ +template < typename _Scalar, int _Dims=2, int _Coeffs=4 > +class DCMesh : public Vitelotte::VGMesh<_Scalar, _Dims, _Coeffs> +{ +public: + typedef Vitelotte::VGMesh<_Scalar, _Dims, _Coeffs> Base; + + typedef typename Base::Scalar Scalar; + typedef typename Base::Vector Vector; + typedef typename Base::Value Value; + typedef typename Base::Gradient Gradient; + + typedef typename Base::CurvedEdge CurvedEdge; + + typedef typename Base::BaseHandle BaseHandle; + typedef typename Base::Vertex Vertex; + typedef typename Base::Halfedge Halfedge; + typedef typename Base::Edge Edge; + typedef typename Base::Face Face; + typedef typename Base::Node Node; + + typedef typename Base::VertexIterator VertexIterator; + typedef typename Base::HalfedgeIterator HalfedgeIterator; + typedef typename Base::HalfedgeAroundVertexCirculator + HalfedgeAroundVertexCirculator; + + using Base::verticesBegin; + using Base::verticesEnd; + using Base::halfedges; + using Base::halfedgesBegin; + using Base::halfedgesEnd; + using Base::setEdgeCurve; + using Base::oppositeHalfedge; + using Base::isBoundary; + + using Base::nCoeffs; + using Base::nDims; + using Base::unconstrainedValue; + using Base::hasFromVertexValue; + using Base::hasToVertexValue; + using Base::hasEdgeValue; + using Base::hasEdgeGradient; + using Base::fromVertexValueNode; + using Base::toVertexValueNode; + using Base::edgeValueNode; + using Base::edgeGradientNode; + using Base::halfedgeNode; + using Base::halfedgeOppositeNode; + using Base::halfedgeOrientation; + using Base::addNode; + using Base::hasVertexGradientConstraint; + using Base::setGradientConstraint; + using Base::removeGradientConstraint; + + typedef PiecewiseLinearFunction ValueFunction; + + typedef Vitelotte::BezierPath BezierPath; + + typedef typename Gradient::ConstantReturnType UnconstrainedGradientType; + + struct Curve : public BaseHandle + { + using BaseHandle::idx; + explicit Curve(int _idx = -1) : BaseHandle(_idx) {} + std::ostream& operator<<(std::ostream& os) const { return os << 'c' << idx(); } + }; + + struct PointConstraint : public BaseHandle + { + using BaseHandle::idx; + explicit PointConstraint(int _idx = -1) : BaseHandle(_idx) {} + std::ostream& operator<<(std::ostream& os) const { return os << "pc" << idx(); } + }; + + struct HalfedgeCurveConnectivity + { + Curve curve; + Halfedge next; + float pos; + }; + + struct CurveInfo + { + Halfedge firstHalfedge; + Halfedge lastHalfedge; + unsigned flags; + ValueFunction gradient[4]; + BezierPath bezierPath; + }; + + struct PointConstraintInfo + { + Vertex vertex; + Value value; + Gradient gradient; + }; + + enum + { + VALUE_TEAR = 0x01, + GRADIENT_TEAR = 0x02 + }; + + enum + { + LEFT = 0x00, + RIGHT = 0x01, + VALUE = 0x00, + GRADIENT = 0x02, + + VALUE_LEFT = VALUE | LEFT, + VALUE_RIGHT = VALUE | RIGHT, + GRADIENT_LEFT = GRADIENT | LEFT, + GRADIENT_RIGHT = GRADIENT | RIGHT + }; + +public: + DCMesh(); + DCMesh(unsigned nDims, unsigned nCoeffs); + DCMesh(const DCMesh& other); + + DCMesh& operator=(const DCMesh& other); + DCMesh& assign(const DCMesh& other); + + inline Curve curve(Halfedge h) const { return m_halfedgeCurveConn[h].curve; } + inline Curve& curve(Halfedge h) { return m_halfedgeCurveConn[h].curve; } + + inline float fromCurvePos(Halfedge h) const + { return m_halfedgeCurveConn[oppositeHalfedge(h)].pos; } + inline float& fromCurvePos(Halfedge h) + { return m_halfedgeCurveConn[oppositeHalfedge(h)].pos; } + inline float toCurvePos(Halfedge h) const { return m_halfedgeCurveConn[h].pos; } + inline float& toCurvePos(Halfedge h) { return m_halfedgeCurveConn[h].pos; } + + inline Halfedge nextCurveHalfedge(Halfedge h) const { return m_halfedgeCurveConn[h].next; } + inline Halfedge& nextCurveHalfedge(Halfedge h) { return m_halfedgeCurveConn[h].next; } + inline Halfedge prevCurveHalfedge(Halfedge h) const + { return m_halfedgeCurveConn[oppositeHalfedge(h)].next; } + inline Halfedge& prevCurveHalfedge(Halfedge h) + { return m_halfedgeCurveConn[oppositeHalfedge(h)].next; } + + inline unsigned nCurves() const { return m_curves.size(); } + Curve addCurve(unsigned flags); + + using Base::isValid; + inline bool isValid(PointConstraint pc) const + { return pc.isValid() && unsigned(pc.idx()) < nPointConstraints(); } + inline bool isValid(Curve c) const { return c.isValid() && unsigned(c.idx()) < nCurves(); } + + void addHalfedgeToCurve(Curve c, Halfedge h, float from, float to); + + inline Halfedge firstHalfedge(Curve c) const { return m_curves.at(c.idx()).firstHalfedge; } + inline Halfedge& firstHalfedge(Curve c) { return m_curves.at(c.idx()).firstHalfedge; } + inline Halfedge lastHalfedge(Curve c) const { return m_curves.at(c.idx()). lastHalfedge; } + inline Halfedge& lastHalfedge(Curve c) { return m_curves.at(c.idx()). lastHalfedge; } + + inline bool valueTear(Curve c) const { return m_curves.at(c.idx()).flags & VALUE_TEAR; } + inline bool gradientTear(Curve c) const { return m_curves.at(c.idx()).flags & GRADIENT_TEAR; } + + inline unsigned flags(Curve c) const { return m_curves.at(c.idx()).flags; } + void setFlags(Curve c, unsigned flags); + void setFlagsRaw(Curve c, unsigned flags); + + const ValueFunction& valueFunction(Curve c, unsigned which) const; + ValueFunction& valueFunction(Curve c, unsigned which); + const ValueFunction& valueFunctionRaw(Curve c, unsigned which) const; + ValueFunction& valueFunctionRaw(Curve c, unsigned which); + + const BezierPath& bezierPath(Curve c) const { return m_curves.at(c.idx()).bezierPath; } + BezierPath& bezierPath(Curve c) { return m_curves.at(c.idx()).bezierPath; } + + using Base::vertex; + inline Vertex vertex(PointConstraint pc) const { return m_pointConstraints[pc.idx()].vertex; } + inline void setVertex(PointConstraint pc, Vertex vx); + + inline PointConstraint pointConstraint(Vertex vx) { return m_pointConstraintConn[vx]; } + + inline bool isValueConstraint(PointConstraint pc) const + { return !isnan(m_pointConstraints[pc.idx()].value(0)); } + using Base::isGradientConstraint; + inline bool isGradientConstraint(PointConstraint pc) const + { return !isnan(m_pointConstraints[pc.idx()].gradient(0, 0)); } + + using Base::value; + inline const Value& value(PointConstraint pc) const + { return m_pointConstraints[pc.idx()].value; } + inline Value& value(PointConstraint pc) + { return m_pointConstraints[pc.idx()].value; } + + inline const Gradient& gradient(PointConstraint pc) const + { return m_pointConstraints[pc.idx()].gradient; } + inline Gradient& gradient(PointConstraint pc) + { return m_pointConstraints[pc.idx()].gradient; } + + inline unsigned nPointConstraints() const { return m_pointConstraints.size(); } + PointConstraint addPointConstraint(Vertex vx); + + + void clear(); + void setNodesFromCurves(); + + inline UnconstrainedGradientType unconstrainedGradientValue() const + { return Gradient::Constant(nCoeffs(), nDims(), std::numeric_limits::quiet_NaN()); } + + Value evalValueFunction(Curve c, unsigned which, float pos) const; + + +protected: + using Base::m_nprops; + +protected: + void copyVGMeshWithCurvesMembers(const DCMesh& other); + + typedef Node NodePair[2]; + void addGradientNodes(Node nodes[2], Curve c, unsigned gType, float pos); + +protected: + typename Base::template VertexProperty m_pointConstraintConn; + typename Base::template HalfedgeProperty m_halfedgeCurveConn; + + std::vector > m_pointConstraints; + std::vector m_curves; +}; + +} + +#include "dcMesh.hpp" + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/dcMesh.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/dcMesh.hpp new file mode 100644 index 000000000..ec29494b3 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/dcMesh.hpp @@ -0,0 +1,431 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include "dcMesh.h" + + +namespace Vitelotte +{ + + +template +typename PiecewiseLinearFunction<_Value>::Value + PiecewiseLinearFunction<_Value>::operator()(float x) const +{ + assert(!empty()); + + if(x <= m_samples.begin()->first) + return m_samples.begin()->second; + if(x >= m_samples.rbegin()->first) + return m_samples.rbegin()->second; + + ConstIterator next = m_samples.upper_bound(x); + ConstIterator prev = next; + --next; + + float alpha = (x - prev->first) / (next->first - prev->first); + return (1 - alpha) * prev->second + alpha * next->second; +} + + +template < typename _Scalar, int _Dim, int _Chan > +DCMesh<_Scalar, _Dim, _Chan>::DCMesh() +{ + m_pointConstraintConn = Base::template addVertexProperty( + "v:pointConstraintConnectivity"); + m_halfedgeCurveConn = Base::template addHalfedgeProperty( + "h:curveConnectivity"); +} + + +template < typename _Scalar, int _Dim, int _Chan > +DCMesh<_Scalar, _Dim, _Chan>::DCMesh(unsigned nDims, unsigned nCoeffs) + : Base(nDims, nCoeffs, 0) +{ + m_pointConstraintConn = Base::template addVertexProperty( + "v:pointConstraintConnectivity"); + m_halfedgeCurveConn = Base::template addHalfedgeProperty( + "h:curveConnectivity"); +} + + +template < typename _Scalar, int _Dim, int _Chan > +DCMesh<_Scalar, _Dim, _Chan>::DCMesh(const DCMesh& other) + : Base(other) +{ + copyVGMeshWithCurvesMembers(other); +} + + +template < typename _Scalar, int _Dim, int _Chan > +DCMesh<_Scalar, _Dim, _Chan>& +DCMesh<_Scalar, _Dim, _Chan>::operator=(const DCMesh& other) +{ + if(&other == this) return *this; + + Base::operator=(other); + copyVGMeshWithCurvesMembers(other); + + return *this; +} + + +template < typename _Scalar, int _Dim, int _Chan > +DCMesh<_Scalar, _Dim, _Chan>& +DCMesh<_Scalar, _Dim, _Chan>::assign(const DCMesh& other) +{ + if(&other == this) return *this; + + Base::assign(other); + copyVGMeshWithCurvesMembers(other); + + return *this; +} + + +template < typename _Scalar, int _Dim, int _Chan > +typename DCMesh<_Scalar, _Dim, _Chan>::Curve +DCMesh<_Scalar, _Dim, _Chan>::addCurve(unsigned flags) +{ + Curve c(nCurves()); + CurveInfo ci; + ci.flags = flags; + m_curves.push_back(ci); + return c; +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +DCMesh<_Scalar, _Dim, _Chan>::addHalfedgeToCurve(Curve c, Halfedge h, float from, float to) +{ + assert(isValid(c)); + assert(isValid(h)); + + CurveInfo& ci = m_curves[c.idx()]; + HalfedgeCurveConnectivity& hcc = m_halfedgeCurveConn[h]; + HalfedgeCurveConnectivity& ohcc = m_halfedgeCurveConn[oppositeHalfedge(h)]; + + assert(!hcc.curve.isValid()); + assert(!ohcc.curve.isValid()); + + hcc.curve = c; + hcc.pos = to; + ohcc.curve = c; + ohcc.pos = from; + if(ci.firstHalfedge.isValid()) + { + assert(ci.lastHalfedge.isValid()); + HalfedgeCurveConnectivity& phcc = m_halfedgeCurveConn[ci.lastHalfedge]; + + phcc.next = h; + ohcc.next = ci.lastHalfedge; + ci.lastHalfedge = h; + } + else + { + ci.firstHalfedge = h; + ci.lastHalfedge = h; + } +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +DCMesh<_Scalar, _Dim, _Chan>::setFlags(Curve c, unsigned flags) +{ + assert(isValid(c)); + + CurveInfo& ci = m_curves[c.idx()]; + if(flags & VALUE_TEAR && !(ci.flags & VALUE_TEAR)) + ci.gradient[VALUE_RIGHT] = ci.gradient[VALUE_LEFT]; + if(flags & GRADIENT_TEAR && !(ci.flags & GRADIENT_TEAR)) + ci.gradient[GRADIENT_RIGHT] = ci.gradient[GRADIENT_LEFT]; + ci.flags = flags; +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +DCMesh<_Scalar, _Dim, _Chan>::setFlagsRaw(Curve c, unsigned flags) +{ + assert(isValid(c)); + + CurveInfo& ci = m_curves[c.idx()]; + ci.flags = flags; +} + + +template < typename _Scalar, int _Dim, int _Chan > +const typename DCMesh<_Scalar, _Dim, _Chan>::ValueFunction& +DCMesh<_Scalar, _Dim, _Chan>::valueFunction(Curve c, unsigned which) const +{ + return const_cast(this)->valueFunction(c, which); +} + + +template < typename _Scalar, int _Dim, int _Chan > +typename DCMesh<_Scalar, _Dim, _Chan>::ValueFunction& +DCMesh<_Scalar, _Dim, _Chan>::valueFunction(Curve c, unsigned which) +{ + assert(isValid(c)); + assert(which < 4); + + switch(which) + { + case VALUE_RIGHT: + if(!valueTear(c)) + which = VALUE_LEFT; + break; + case GRADIENT_RIGHT: + if(!gradientTear(c)) + which = GRADIENT_LEFT; + break; + } + + return m_curves[c.idx()].gradient[which]; +} + + +template < typename _Scalar, int _Dim, int _Chan > +const typename DCMesh<_Scalar, _Dim, _Chan>::ValueFunction& +DCMesh<_Scalar, _Dim, _Chan>::valueFunctionRaw(Curve c, unsigned which) const +{ + return const_cast(this)->valueFunctionRaw(c, which); +} + + +template < typename _Scalar, int _Dim, int _Chan > +typename DCMesh<_Scalar, _Dim, _Chan>::ValueFunction& +DCMesh<_Scalar, _Dim, _Chan>::valueFunctionRaw(Curve c, unsigned which) +{ + assert(isValid(c)); + assert(which < 4); + + return m_curves[c.idx()].gradient[which]; +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +DCMesh<_Scalar, _Dim, _Chan>::setVertex(PointConstraint pc, Vertex vx) { + assert(!m_pointConstraintConn[vx].isValid()); + if(vertex(pc).isValid()) { + m_pointConstraintConn[vertex(pc)] = PointConstraint(); + } + m_pointConstraints[pc.idx()].vertex = vx; + m_pointConstraintConn[vx] = pc; +} + + +template < typename _Scalar, int _Dim, int _Chan > +typename DCMesh<_Scalar, _Dim, _Chan>::PointConstraint +DCMesh<_Scalar, _Dim, _Chan>::addPointConstraint(Vertex vx) +{ + PointConstraint pc(nPointConstraints()); + PointConstraintInfo pci; + pci.value = unconstrainedValue(); + pci.gradient = unconstrainedGradientValue(); + m_pointConstraints.push_back(pci); + setVertex(pc, vx); + return pc; +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +DCMesh<_Scalar, _Dim, _Chan>::clear() +{ + Base::clear(); + m_pointConstraints.clear(); + m_curves.clear(); +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +DCMesh<_Scalar, _Dim, _Chan>::setNodesFromCurves() +{ + m_nprops.resize(0); + for(HalfedgeIterator hit = halfedgesBegin(); + hit != halfedgesEnd(); ++hit) + { + if(hasFromVertexValue()) fromVertexValueNode(*hit) = Node(); + if(hasToVertexValue()) toVertexValueNode(*hit) = Node(); + if(hasEdgeValue()) edgeValueNode(*hit) = Node(); + if(hasEdgeGradient()) edgeGradientNode(*hit) = Node(); + } + if(hasVertexGradientConstraint()) { + for(VertexIterator vit = verticesBegin(); + vit != verticesEnd(); ++vit) + { + if(isGradientConstraint(*vit)) + { + removeGradientConstraint(*vit); + } + } + } + + for(unsigned pci = 0; pci < nPointConstraints(); ++pci) + { + PointConstraint pc(pci); + Vertex vx = vertex(pc); + assert(isValid(vx)); + + Node vn; + if(isValueConstraint(pc)) + vn = addNode(value(pc)); + + HalfedgeAroundVertexCirculator hit = halfedges(vx); + HalfedgeAroundVertexCirculator hend = hit; + do { + Halfedge opp = oppositeHalfedge(*hit); + if(isValueConstraint(pc)) + { + if(!isBoundary(*hit)) + halfedgeNode(*hit, Base::FROM_VERTEX_VALUE) = vn; + if(!isBoundary(opp)) + halfedgeOppositeNode(*hit, Base::FROM_VERTEX_VALUE) = vn; + } + ++hit; + } while(hit != hend); + + if(hasVertexGradientConstraint() && isGradientConstraint(pc)) + { + setGradientConstraint(vertex(pc), gradient(pc)); + } + } + + for(unsigned ci = 0; ci < nCurves(); ++ci) + { + Curve c(ci); + + Halfedge lh = firstHalfedge(c); + if(!lh.isValid()) + continue; + + const BezierPath& path = bezierPath(c); + unsigned si = 0; + Scalar splitPos = 0; + CurvedEdge head; + CurvedEdge tail = path.nSegments()? path.getSegment(si): CurvedEdge(); + + Node fromNode[2]; + addGradientNodes(fromNode, c, VALUE, fromCurvePos(lh)); + do { + Node toNode[2]; + addGradientNodes(toNode, c, VALUE, toCurvePos(lh)); + + Halfedge rh = oppositeHalfedge(lh); + float midPos = (fromCurvePos(lh) + toCurvePos(lh)) / 2.f; + + bool lhnb = !isBoundary(lh); + bool rhnb = !isBoundary(rh); + + if(hasFromVertexValue()) + { + if(lhnb) fromVertexValueNode(lh) = fromNode[LEFT]; + if(rhnb) fromVertexValueNode(rh) = toNode[RIGHT]; + } + if(hasToVertexValue()) + { + if(lhnb) toVertexValueNode(lh) = toNode[LEFT]; + if(rhnb) toVertexValueNode(rh) = fromNode[RIGHT]; + } + + if(hasEdgeValue()) + { + Node midNode[2]; + addGradientNodes(midNode, c, VALUE, midPos); + if(lhnb) edgeValueNode(lh) = midNode[LEFT]; + if(rhnb) edgeValueNode(rh) = midNode[RIGHT]; + } + + if(hasEdgeGradient()) + { + Node gNode[2]; + addGradientNodes(gNode, c, GRADIENT, midPos); + if(lhnb) edgeGradientNode(lh) = gNode[LEFT]; + if(rhnb) edgeGradientNode(rh) = gNode[RIGHT]; + + if(halfedgeOrientation(lh) && lhnb) + value(gNode[LEFT]) *= -1; + if(gNode[LEFT] != gNode[RIGHT] && halfedgeOrientation(lh) && rhnb) + value(gNode[RIGHT]) *= -1; + + } + + if(tail.type() != BEZIER_EMPTY) { + Scalar csi; + Scalar curvePos = std::modf(toCurvePos(lh) * path.nSegments() + 1.e-5, &csi); + + if(unsigned(csi) == si) { + Scalar pos = (curvePos - splitPos) / (1 - splitPos); + tail.split(pos, head, tail); + setEdgeCurve(lh, head); + splitPos = curvePos; + } else { + setEdgeCurve(lh, tail); + ++si; + tail = (si < path.nSegments())? + path.getSegment(si): + CurvedEdge(); + splitPos = 0; + } + } + + + fromNode[0] = toNode[0]; + fromNode[1] = toNode[1]; + lh = nextCurveHalfedge(lh); + } while(lh.isValid()); + } +} + + +template < typename _Scalar, int _Dim, int _Chan > +typename DCMesh<_Scalar, _Dim, _Chan>::Value +DCMesh<_Scalar, _Dim, _Chan>::evalValueFunction( + Curve c, unsigned which, float pos) const +{ + return valueFunction(c, which)(pos); +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +DCMesh<_Scalar, _Dim, _Chan>::copyVGMeshWithCurvesMembers(const DCMesh& other) +{ + m_pointConstraintConn = Base::template vertexProperty( + "v:pointConstraintConnectivity"); + m_halfedgeCurveConn = Base::template halfedgeProperty( + "h:curveConnectivity"); + + m_pointConstraints = other.m_pointConstraints; + m_curves = other.m_curves; +} + + +template < typename _Scalar, int _Dim, int _Chan > +void +DCMesh<_Scalar, _Dim, _Chan>::addGradientNodes( + Node nodes[2], Curve c, unsigned gType, float pos) +{ + bool tear = (gType == VALUE)? valueTear(c): gradientTear(c); + + nodes[LEFT] = valueFunction(c, gType | LEFT).empty()? + addNode(): + addNode(evalValueFunction(c, gType | LEFT, pos)); + nodes[RIGHT] = + (!tear)? + nodes[LEFT]: + valueFunction(c, gType | RIGHT).empty()? + addNode(): + addNode(evalValueFunction(c, gType | RIGHT, pos)); +} + + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgReader.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgReader.h new file mode 100644 index 000000000..758a92025 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgReader.h @@ -0,0 +1,104 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_MVG_READER_ +#define _VITELOTTE_MVG_READER_ + + +#include +#include +#include +#include +#include + +#include "../../common/surface_mesh/objReader.h" + + +namespace Vitelotte +{ + + +//class QVGReadError : public std::runtime_error +//{ +//public: +// inline QVGReadError(const std::string& _what) +// : std::runtime_error(_what) +// {} +//}; + + +/** + * \brief Reads a VGMesh stored in the MVG file format. + * + * \see VGMesh MVGWriter OBJBaseReader + */ +template < typename _Mesh > +class MVGReader: public PatateCommon::OBJBaseReader<_Mesh> +{ +public: + typedef _Mesh Mesh; + typedef PatateCommon::OBJBaseReader Base; + + typedef typename Mesh::Vector Vector; + typedef typename Mesh::Vertex Vertex; + typedef typename Mesh::Value Value; + typedef typename Mesh::Gradient Gradient; + + enum { + NO_WARN_UNKNOWN = 0x01 + }; + +public: + + /** + * \brief Default constructor + */ + inline MVGReader(unsigned flags = 0); + +protected: + using Base::readLine; + using Base::parseVector; + using Base::parseIndicesList; + using Base::error; + using Base::warning; + + virtual void parseHeader(std::istream& in, Mesh& mesh); + virtual bool parseDefinition(const std::string& spec, + std::istream& def, Mesh& mesh); + + void parseValue (std::istream& in); + void parseValueWithVoid(std::istream& in, Mesh& mesh); + void parseGradient (std::istream& in); + +protected: + using Base::m_error; + using Base::m_lineStream; + using Base::m_vector; + + unsigned m_flags; + std::vector m_fVertices; + std::string m_tmp; + Value m_value; + Gradient m_gradient; + std::vector m_faceIndices; + std::vector m_nodesIndices; +}; + + +template < typename Mesh > +bool readMvg(std::istream& in, Mesh& mesh, unsigned flags=0); + +template < typename Mesh > +bool readMvgFromFile(const std::string& filename, Mesh& mesh, unsigned flags=0); + + +} // namespace Vitelotte + +#include "mvgReader.hpp" + + +#endif // _MVGREADER_H_ + diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgReader.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgReader.hpp new file mode 100644 index 000000000..cbf0e3d3d --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgReader.hpp @@ -0,0 +1,329 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include "mvgReader.h" + + +namespace Vitelotte +{ + + +#define PTT_VOID +#define PTT_ERROR_IF(_cond, _msg, _ret) do { if(_cond) { error(_msg); return _ret; } } while(false) +#define PTT_RETURN_IF_ERROR(_ret) do { if(m_error) { return _ret; } } while(false) + + +template < typename _Mesh > +MVGReader<_Mesh>::MVGReader(unsigned flags) + : m_flags(flags) +{ + m_faceIndices.reserve(3); +} + + +template < typename _Mesh > +void +MVGReader<_Mesh>::parseHeader(std::istream& in, Mesh& mesh) +{ + m_lineStream >> m_tmp; + if(!in) error("Error while reading header"); + if(m_tmp != "mvg") error("Missing mvg header"); + + m_lineStream >> m_tmp; + if(!in) error("Error while reading header"); + if(m_tmp != "1.0") error("Unsuported version"); + + std::string cmd; + unsigned nDims = 2; + unsigned nCoeffs = 4; + unsigned attributes = 0; + ColorSpace colorSpace = PatateCommon::COLOR_NONE; + unsigned nVert = 1024; + unsigned nNode = 1024; + unsigned nFace = 1024; + while(true) + { + readLine(in); + m_lineStream >> cmd; + + if(cmd == "dim" || cmd == "dimensions") + m_lineStream >> nDims; + else if(cmd == "parameters" || cmd == "coefficients") + m_lineStream >> nCoeffs; + else if(cmd == "linear") + attributes = Mesh::LINEAR_FLAGS; + else if(cmd == "quadratic") + attributes = Mesh::QUADRATIC_FLAGS; + else if(cmd == "morley") + attributes = Mesh::MORLEY_FLAGS; + else if(cmd == "fv") + attributes = Mesh::FV_FLAGS; + else if(cmd == "attributes") + { + m_lineStream >> cmd; + if(cmd == "none") + attributes = 0; + else if(cmd == "linear") + attributes = Mesh::LINEAR_FLAGS; + else if(cmd == "quadratic") + attributes = Mesh::QUADRATIC_FLAGS; + else if(cmd == "morley") + attributes = Mesh::MORLEY_FLAGS; + else if(cmd == "fv") + attributes = Mesh::FV_FLAGS; + else + error("unknown attribute type"); + } + else if(cmd == "mesh") + m_lineStream >> attributes; + else if(cmd == "colorSpace") { + m_lineStream >> cmd; + bool ok = false; + colorSpace = PatateCommon::colorSpaceFromName(cmd, &ok); + if(!ok) error("unknown color space"); + } + else if(cmd == "vertices") + m_lineStream >> nVert; + else if(cmd == "nodes") + m_lineStream >> nNode; + else if(cmd == "faces") + m_lineStream >> nFace; + else + break; + + if(!in || !m_lineStream) error("Failed to read header"); + } + m_lineStream.seekg(0); + + if(int(Mesh::DimsAtCompileTime) != int(Dynamic) && nDims != mesh.nDims()) + { + error("Invalid number of dimensions."); + return; + } + if(int(Mesh::CoeffsAtCompileTime) != int(Dynamic) && nCoeffs != mesh.nCoeffs()) + { + error("Invalid number of coefficients."); + return; + } + + mesh.clear(); + mesh.setAttributes(attributes); + mesh.setColorSpace(colorSpace); + mesh.setNDims(nDims); + mesh.setNCoeffs(nCoeffs); + mesh.reserve(nVert, nVert+nFace, nFace, nNode); + + m_vector.resize(nDims); + m_value.resize(nCoeffs); + m_gradient.resize(nCoeffs, nDims); +} + + +template < typename _Mesh > +bool +MVGReader<_Mesh>::parseDefinition(const std::string& spec, + std::istream& def, Mesh& mesh) +{ + typedef typename Mesh::Node Node; + + int iOffset = 0; + + // vertex + if(spec == "v") + { + parseVector(def); PTT_RETURN_IF_ERROR(true); + if(!def.eof()) warning("Too much components."); + mesh.addVertex(m_vector); + } + + // nodes + else if(spec == "n") + { + parseValueWithVoid(def, mesh); PTT_RETURN_IF_ERROR(true); + if(!def.eof()) warning("Too much components."); + mesh.addNode(m_value); + } + + // face + else if(spec == "f") + { + m_fVertices.clear(); + m_nodesIndices.clear(); + + def >> std::ws; + + unsigned nVert = 0; + while(def.good()) + { + def >> m_tmp; + if(m_tmp == "-") break; + parseIndicesList(m_tmp, m_faceIndices); + if(m_faceIndices.size() < 1 || m_faceIndices.size() > 3) + error("Invalid number of indices"); + + m_fVertices.push_back( + typename Mesh::Vertex(m_faceIndices[0] - iOffset)); + + if(m_faceIndices.size() > 1) + { + m_nodesIndices.push_back(m_faceIndices[1]); + m_nodesIndices.push_back(m_faceIndices.back()); + } + ++nVert; + } + + // mid nodes + m_nodesIndices.resize(4*nVert); + unsigned nEAttrs = mesh.hasEdgeValue() + mesh.hasEdgeGradient(); + if(nEAttrs) + { + if(m_tmp != "-") + error("Only triangles meshes are supported"); + + for(unsigned i = 0; i < nVert; ++i) + { + def >> m_tmp; + parseIndicesList(m_tmp, m_faceIndices); + if(m_faceIndices.size() != nEAttrs) + error("Invalid number of indices"); + + if(mesh.hasEdgeValue()) + m_nodesIndices[2*nVert+i] = m_faceIndices.front(); + if(mesh.hasEdgeGradient()) + m_nodesIndices[3*nVert+i] = m_faceIndices.back(); + } + } + + typename Mesh::Face f = mesh.addFace(m_fVertices); + + typename Mesh::HalfedgeAroundFaceCirculator hit = mesh.halfedges(f); + for(unsigned i = 0; i < nVert; ++i) + { + if(mesh.hasToVertexValue()) + mesh.toVertexValueNode(*hit) = Node(m_nodesIndices[2*i] - iOffset); + ++hit; + if(mesh.hasFromVertexValue()) + mesh.fromVertexValueNode(*hit) = Node(m_nodesIndices[2*i + 1] - iOffset); + if(mesh.hasEdgeValue()) + mesh.edgeValueNode(*hit) = Node(m_nodesIndices[2*nVert + i] - iOffset); + if(mesh.hasEdgeGradient()) + mesh.edgeGradientNode(*hit) = Node(m_nodesIndices[3*nVert + i] - iOffset); + } + } + + else if(spec == "ce") + { + unsigned v0; + unsigned v1; + def >> v0 >> v1; + PTT_ERROR_IF(!def || v0 >= mesh.nVertices(), "Invalid vertex index", true); + PTT_ERROR_IF(!def || v1 >= mesh.nVertices(), "Invalid vertex index", true); + + typename Mesh::Halfedge h = mesh.findHalfedge(Vertex(v0), Vertex(v1)); + PTT_ERROR_IF(!h.isValid(), "Invalid edge", true); + + typename Mesh::CurvedEdge ce; + ce.setType(BEZIER_QUADRATIC); + parseVector(def); PTT_RETURN_IF_ERROR(true); + ce.point(1) = m_vector; + if(!def.eof()) { + ce.setType(BEZIER_CUBIC); + parseVector(def); PTT_RETURN_IF_ERROR(true); + ce.point(2) = m_vector; + } + ce.point(0) = mesh.position(Vertex(v0)); + ce.point(ce.type() - 1) = mesh.position(Vertex(v1)); + + mesh.setEdgeCurve(h, ce); + } + + else if(spec == "vgc") + { + unsigned vxIdx; + def >> vxIdx; + PTT_ERROR_IF(!def || vxIdx >= mesh.nVertices(), "Invalid vertex index", true); + + parseGradient(def); PTT_RETURN_IF_ERROR(true); + if(!def.eof()) warning("Too much components."); + + mesh.setGradientConstraint(Vertex(vxIdx), m_gradient); + } + + // Unknown element type. + else + { + if(!(m_flags & NO_WARN_UNKNOWN)) + { + warning("Unknown spec: " + spec); + } + return false; + } + return true; +} + + +template < typename _Mesh > +void +MVGReader<_Mesh>::parseValue(std::istream& in) { + for(unsigned i = 0; i < m_value.size(); ++i) { + in >> m_value(i); + } + PTT_ERROR_IF(!in, "Invalid value specification", PTT_VOID); + in >> std::ws; +} + + +template < typename _Mesh > +void +MVGReader<_Mesh>::parseValueWithVoid(std::istream& in, Mesh& mesh) { + in >> std::ws; + PTT_ERROR_IF(!in.good(), "Invalid value specification", PTT_VOID); + if(std::isalpha(in.peek())) + { + in >> m_tmp; + PTT_ERROR_IF(m_tmp != "void", "Invalid value specification", PTT_VOID); + m_value = mesh.unconstrainedValue(); + } + else + { + parseValue(in); PTT_RETURN_IF_ERROR(PTT_VOID); + } + in >> std::ws; +} + + +template < typename _Mesh > +void +MVGReader<_Mesh>::parseGradient(std::istream& in) { + for(unsigned i = 0; i < m_gradient.size(); ++i) { + in >> m_gradient(i); + } + PTT_ERROR_IF(!in, "Invalid gradient specification", PTT_VOID); + in >> std::ws; +} + + +template < typename Mesh > +bool readMvg(std::istream& in, Mesh& mesh, unsigned flags) +{ + MVGReader reader(flags); + return reader.read(in, mesh); +} + + +template < typename Mesh > +bool readMvgFromFile(const std::string& filename, Mesh& mesh, unsigned flags) +{ + std::ifstream in(filename.c_str()); + return readMvg(in, mesh, flags); +} + + +#undef PTT_VOID +#undef PTT_ERROR_IF +#undef PTT_RETURN_IF_ERROR + +} // namespace Vitelotte diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWithCurvesReader.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWithCurvesReader.h new file mode 100644 index 000000000..e2a5caceb --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWithCurvesReader.h @@ -0,0 +1,77 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_MVG_WITH_CURVES_READER_ +#define _VITELOTTE_MVG_WITH_CURVES_READER_ + + +#include "mvgReader.h" + + +namespace Vitelotte +{ + + +template < typename _Mesh > +class MVGWithCurvesReader: public Vitelotte::MVGReader<_Mesh> +{ +public: + typedef Vitelotte::MVGReader<_Mesh> Base; + + typedef _Mesh Mesh; + + typedef typename Mesh::Vector Vector; + typedef typename Mesh::Value Value; + typedef typename Mesh::ValueFunction ValueFunction; + + typedef typename Mesh::Vertex Vertex; + typedef typename Mesh::Halfedge Halfedge; + typedef typename Mesh::PointConstraint PointConstraint; + typedef typename Mesh::Curve Curve; + + using Base::parseDefinition; + +public: + MVGWithCurvesReader(); + + +protected: + enum + { + CONS_LEFT = 1, + CONS_RIGHT = 2, + TEAR = 4 + }; + +protected: + using Base::parseVector; + using Base::parseValue; + using Base::parseValueWithVoid; + using Base::parseGradient; + using Base::error; + using Base::warning; + + using Base::m_vector; + using Base::m_value; + using Base::m_gradient; + using Base::m_error; + + virtual bool parseDefinition(const std::string& spec, + std::istream& def, Mesh& mesh); + +protected: + std::string m_part; + std::string m_token; + std::istringstream m_in; + +}; + +} + +#include "mvgWithCurvesReader.hpp" + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWithCurvesReader.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWithCurvesReader.hpp new file mode 100644 index 000000000..0a8632883 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWithCurvesReader.hpp @@ -0,0 +1,183 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include +#include + +#include "mvgWithCurvesReader.h" + + +#define PTT_ERROR_IF(_cond, _msg) do { if(_cond) { error(_msg); return true; } } while(false) +#define PTT_RETURN_IF_ERROR() do { if(m_error) { return true; } } while(false) + + +namespace Vitelotte +{ + + +template < typename _Mesh > +MVGWithCurvesReader<_Mesh>::MVGWithCurvesReader() +{ + +} + + +template < typename _Mesh > +bool +MVGWithCurvesReader<_Mesh>::parseDefinition(const std::string& spec, + std::istream& def, Mesh& mesh) +{ + // Ponctual Value Constraint + if(spec == "pvc") + { + unsigned vi; + def >> vi; + PTT_ERROR_IF(!def || vi >= mesh.nVertices(), "Invalid vertex index"); + parseValue(def); PTT_RETURN_IF_ERROR(); + if(!def.eof()) warning("Too much component in point value constraint declaration"); + + PointConstraint pc = mesh.pointConstraint(Vertex(vi)); + if(!pc.isValid()) pc = mesh.addPointConstraint(Vertex(vi)); + mesh.value(pc) = m_value; + } + // Ponctual Gradient Constraint + else if(spec == "pgc") + { + unsigned vi; + def >> vi; + PTT_ERROR_IF(!def || vi >= mesh.nVertices(), "Invalid vertex index"); + parseGradient(def); PTT_RETURN_IF_ERROR(); + if(!def.eof()) warning("Too much component in point gradient constraint declaration"); + + PointConstraint pc = mesh.pointConstraint(Vertex(vi)); + if(!pc.isValid()) pc = mesh.addPointConstraint(Vertex(vi)); + mesh.gradient(pc) = m_gradient; + + return true; + } + // Curve + else if(spec == "c") + { + unsigned vi, prevVi; + float pos, prevPos; + Curve c = mesh.addCurve(0); + def >> prevVi; PTT_ERROR_IF(!def || prevVi >= mesh.nVertices(), "Invalid vertex index"); + def >> prevPos; PTT_ERROR_IF(!def, "Invalid vertex position"); + + while(def && !def.eof()) + { + def >> vi; PTT_ERROR_IF(!def || vi >= mesh.nVertices(), "Invalid vertex index"); + def >> pos; PTT_ERROR_IF(!def, "Invalid vertex position"); + PTT_ERROR_IF(!def, "Invalid curve declaration"); + Halfedge h = mesh.findHalfedge(Vertex(prevVi), Vertex(vi)); + PTT_ERROR_IF(!h.isValid(), "Invalid curve: prescribed edge does not exist"); + mesh.addHalfedgeToCurve(c, h, prevPos, pos); + prevVi = vi; + prevPos = pos; + } + return true; + } + // Diffusion Curve Value Tear / Diffusion Curve Gradient Tear + else if(spec == "dcvTear" || spec == "dcgTear") + { + unsigned ci; + def >> ci; PTT_ERROR_IF(!def || ci >= mesh.nCurves(), "Invalid curve index"); + unsigned flag = (spec == "dcvTear")? Mesh::VALUE_TEAR: Mesh::GRADIENT_TEAR; + mesh.setFlags(Curve(ci), mesh.flags(Curve(ci)) | flag); + } + // Diffusion Curve Value (Left/Right) / Diffusion Curve Gradient (Left/Right) + else if(spec == "dcv" || spec == "dcvLeft" || spec == "dcvRight" + || spec == "dcg" || spec == "dcgLeft" || spec == "dcgRight") + { + unsigned ci; + def >> ci; PTT_ERROR_IF(!def || ci >= mesh.nCurves(), "Invalid curve index"); + + // Update flags if required -- must be done before functionValue call + unsigned flags = mesh.flags(Curve(ci)); + if(spec == "dcvLeft" || spec == "dcvRight") flags |= Mesh::VALUE_TEAR; + if(spec == "dcgLeft" || spec == "dcgRight") flags |= Mesh::GRADIENT_TEAR; + mesh.setFlags(Curve(ci), flags); + + unsigned which = + (spec == "dcv" || spec == "dcvLeft" )? Mesh::VALUE_LEFT: + ( spec == "dcvRight")? Mesh::VALUE_RIGHT: + (spec == "dcg" || spec == "dcgLeft" )? Mesh::GRADIENT_LEFT: + Mesh::GRADIENT_RIGHT; + ValueFunction& vf = mesh.valueFunction(Curve(ci), which); + while(def && !def.eof()) + { + float pos; + def >> pos; PTT_ERROR_IF(!def, "Invalid parameter"); + parseValue(def); PTT_RETURN_IF_ERROR(); + vf.add(pos, m_value); + } + } + // Bezier Path + else if(spec == "bp") + { + typedef typename Mesh::BezierPath BezierPath; + + unsigned ci; + def >> ci; PTT_ERROR_IF(!def || ci >= mesh.nCurves(), "Invalid curve index"); + + BezierPath& path = mesh.bezierPath(Curve(ci)); + if(path.nPoints() != 0) + { + warning("Bezier curve already defined"); + return true; + } + + def >> m_part; PTT_ERROR_IF(!def || m_part != "M", "Expected M path command"); + parseVector(def); PTT_RETURN_IF_ERROR(); + path.setFirstPoint(m_vector); + + BezierSegmentType type = BEZIER_CUBIC; + unsigned count = 0; + Vector points[3]; + for(unsigned i = 0; i < 3; ++i) points[i].resize(mesh.nDims()); + def >> std::ws; + while(def && !def.eof()) + { + if(std::isalpha(def.peek())) + { + PTT_ERROR_IF(count != 0, "unexpected path command"); + def >> m_part; PTT_ERROR_IF(!def || m_part.size() != 1, "Invalid string in path definition"); + switch(m_part[0]) + { + case 'L': type = BEZIER_LINEAR; break; + case 'Q': type = BEZIER_QUADRATIC; break; + case 'C': type = BEZIER_CUBIC; break; + default: error("Unsupported path command"); return true; + } + } + else + { + parseVector(def); PTT_RETURN_IF_ERROR(); + points[count] = m_vector; + ++count; + + if(count == BezierPath::size(type) - 1) + { + path.addSegment(type, points); + count = 0; + } + } + def >> std::ws; + } + } + else + { + return Base::parseDefinition(spec, def, mesh); + } + return true; +} + + +} + + +#undef PTT_ERROR_IF +#undef PTT_RETURN_IF_ERROR diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWithCurvesWriter.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWithCurvesWriter.h new file mode 100644 index 000000000..601e005b7 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWithCurvesWriter.h @@ -0,0 +1,56 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_MVG_WITH_CURVES_WRITER_ +#define _VITELOTTE_MVG_WITH_CURVES_WRITER_ + + +#include "mvgWriter.h" + + +namespace Vitelotte +{ + + +template < typename _Mesh > +class MVGWithCurvesWriter: public Vitelotte::MVGWriter<_Mesh> +{ +public: + typedef Vitelotte::MVGWriter<_Mesh> Base; + + typedef _Mesh Mesh; + + typedef typename Mesh::Vector Vector; + typedef typename Mesh::Value Value; + typedef typename Mesh::ValueFunction ValueFunction; + + typedef typename Mesh::Vertex Vertex; + typedef typename Mesh::Halfedge Halfedge; + typedef typename Mesh::PointConstraint PointConstraint; + typedef typename Mesh::Curve Curve; + + typedef typename Base::Version Version; + +public: + MVGWithCurvesWriter(Version version=Base::LATEST_VERSION); + + void write(std::ostream& _out, const Mesh& mesh); + +protected: + void writeValueFunction(std::ostream& out, const ValueFunction& vg) const; + +protected: + using Base::vertexIndex; + + using Base::m_format; +}; + +} + +#include "mvgWithCurvesWriter.hpp" + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWithCurvesWriter.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWithCurvesWriter.hpp new file mode 100644 index 000000000..3d45a8275 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWithCurvesWriter.hpp @@ -0,0 +1,131 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include "mvgWithCurvesWriter.h" + + +namespace Vitelotte +{ + + +template < typename _Mesh > +MVGWithCurvesWriter<_Mesh>::MVGWithCurvesWriter(Version version) + : Base(version) { +} + + +template < typename _Mesh > +void +MVGWithCurvesWriter<_Mesh>::write(std::ostream& _out, const Mesh& mesh) { + Base::write(_out, mesh); + + for(unsigned pci = 0; pci < mesh.nPointConstraints(); ++pci) + { + PointConstraint pc(pci); + + if(mesh.isValueConstraint(pc)) + { + _out << "pvc " << vertexIndex(mesh.vertex(pc)) + << " " << mesh.value(pc).transpose().format(m_format) << "\n"; + } + if(mesh.isGradientConstraint(pc)) + { + _out << "pgc " << vertexIndex(mesh.vertex(pc)) + << " " << mesh.gradient(pc).transpose().format(m_format) << "\n"; + } +// _out << "pc " << (mesh.isValueConstraint(pc)? "o": "x") << " " +// << (mesh.isGradientConstraint(pc)? "o": "x") << ";"; +// if(mesh.isValueConstraint(pc)) +// { +// _out << " " << mesh.value(pc).transpose().format(m_format) << ";"; +// } +// if(mesh.isGradientConstraint(pc)) +// { +// _out << " " << mesh.gradient(pc).transpose().format(m_format) << ";"; +// } +// _out << " " << vertexIndex(mesh.vertex(pc)) << "\n"; + } + + for(unsigned ci = 0; ci < mesh.nCurves(); ++ci) + { + Curve curve(ci); + + Halfedge h = mesh.firstHalfedge(curve); + _out << "c " << vertexIndex(mesh.fromVertex(h)) << " " << mesh.fromCurvePos(h); + while(h.isValid()) + { + _out << " " << vertexIndex(mesh.toVertex(h)) << " " << mesh.toCurvePos(h); + h = mesh.nextCurveHalfedge(h); + } + _out << "\n"; + + if(mesh.bezierPath(curve).nPoints()) + { + typedef typename Mesh::BezierPath BezierPath; + const BezierPath& path = mesh.bezierPath(curve); + _out << "bp " << ci << " M " << path.point(0).transpose().format(m_format); + for(unsigned si = 0; si < path.nSegments(); ++si) + { + switch(path.type(si)) + { + case BEZIER_LINEAR: _out << " L"; break; + case BEZIER_QUADRATIC: _out << " Q"; break; + case BEZIER_CUBIC: _out << " C"; break; + case BEZIER_EMPTY: continue; + } + for(unsigned pi = 1; pi < path.nPoints(si); ++pi) + { + _out << " " << path.point(si, pi).transpose().format(m_format); + } + } + _out << "\n"; + } + + if(mesh.valueTear(curve)) _out << "dcvTear " << ci << "\n"; + if(mesh.gradientTear(curve)) _out << "dcgTear " << ci << "\n"; + + if(!mesh.valueFunction(curve, Mesh::VALUE_LEFT).empty()) + { + _out << (mesh.valueTear(curve)? "dcvLeft ": "dcv ") << ci; + writeValueFunction(_out, mesh.valueFunction(curve, Mesh::VALUE_LEFT)); + _out << "\n"; + } + if(mesh.valueTear(curve) && !mesh.valueFunction(curve, Mesh::VALUE_RIGHT).empty()) + { + _out << "dcvRight " << ci; + writeValueFunction(_out, mesh.valueFunction(curve, Mesh::VALUE_RIGHT)); + _out << "\n"; + } + + if(!mesh.valueFunction(curve, Mesh::GRADIENT_LEFT).empty()) + { + _out << (mesh.gradientTear(curve)? "dcgLeft ": "dcg ") << ci; + writeValueFunction(_out, mesh.valueFunction(curve, Mesh::GRADIENT_LEFT)); + _out << "\n"; + } + if(mesh.gradientTear(curve) && !mesh.valueFunction(curve, Mesh::GRADIENT_RIGHT).empty()) + { + _out << "dcgRight " << ci; + writeValueFunction(_out, mesh.valueFunction(curve, Mesh::GRADIENT_RIGHT)); + _out << "\n"; + } + } +} + + +template < typename _Mesh > +void +MVGWithCurvesWriter<_Mesh>::writeValueFunction(std::ostream& out, const ValueFunction& vg) const +{ + for(typename ValueFunction::ConstIterator stop = vg.begin(); + stop != vg.end(); ++stop) + { + out << " " << stop->first << " " << stop->second.transpose().format(m_format); + } +} + + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWriter.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWriter.h new file mode 100644 index 000000000..24c878599 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWriter.h @@ -0,0 +1,89 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_MVG_WRITER_ +#define _VITELOTTE_MVG_WRITER_ + + +#include +#include +#include +#include + +#include + + +namespace Vitelotte +{ + + +/** + * \brief The MVGWriter class writes VGMesh objects in `.mvg` files. + * + * \see VGMesh MVGReader + */ +template < typename _Mesh > +class MVGWriter +{ +public: + typedef _Mesh Mesh; + + typedef typename Mesh::Vertex Vertex; + typedef typename Mesh::Node Node; + + typedef std::vector IndexMap; + + enum Version + { + VERSION_1_0 = 0x100, + LATEST_VERSION = VERSION_1_0 + }; + + +public: + + /** + * \brief Constructor + */ + inline MVGWriter(Version version=LATEST_VERSION) + : m_version(version), + m_format(Eigen::StreamPrecision, Eigen::DontAlignCols, " ", " ") + {} + + /** + * \brief Write `mesh` in the stream `out` as a `.mvg` file. + */ + void write(std::ostream& out, const Mesh& mesh); + +protected: + int vertexIndex(Vertex vx) const; + int nodeIndex(Node node) const; + + void printNode(std::ostream& _out, Node n) const; + +protected: + Version m_version; + Eigen::IOFormat m_format; + IndexMap m_vxMap; + IndexMap m_nodeMap; +}; + + +template < typename Mesh > +void writeMvg(std::ostream& out, const Mesh& mesh, + typename MVGWriter::Version version=MVGWriter::LATEST_VERSION); + +template < typename Mesh > +void writeMvgToFile(const std::string& filename, const Mesh& mesh, + typename MVGWriter::Version version=MVGWriter::LATEST_VERSION); + + +} // namespace Vitelotte + +#include "mvgWriter.hpp" + + +#endif // _QVGWRITER_H_ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWriter.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWriter.hpp new file mode 100644 index 000000000..6d0225669 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/mvgWriter.hpp @@ -0,0 +1,242 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +#include + +#include "mvgWriter.h" + + +namespace Vitelotte { + +template < typename _Mesh > +void +MVGWriter<_Mesh>::write(std::ostream& _out, const Mesh& mesh) +{ + typedef typename Mesh::HalfedgeAttribute HalfedgeAttribute; + typedef typename Mesh::VertexIterator VertexIterator; + typedef typename Mesh::FaceIterator FaceIterator; + typedef typename Mesh::HalfedgeAroundFaceCirculator + HalfedgeAroundFaceCirculator; + typedef std::vector BoolMap; + + assert(m_version == VERSION_1_0); + + // Ensure that the stream we read encode numbers using the C locale + _out.imbue(std::locale::classic()); + + int iOffset = 0; + + _out << "mvg 1.0\n"; + _out << "dimensions " << mesh.nDims() << "\n"; + _out << "coefficients " << mesh.nCoeffs() << "\n"; + + if(mesh.getAttributes() == 0) + _out << "attributes none\n"; + else if(mesh.getAttributes() == Mesh::LINEAR_FLAGS) + _out << "attributes linear\n"; + else if(mesh.getAttributes() == Mesh::QUADRATIC_FLAGS) + _out << "attributes quadratic\n"; + else if(mesh.getAttributes() == Mesh::MORLEY_FLAGS) + _out << "attributes morley\n"; + else if(mesh.getAttributes() == Mesh::FV_FLAGS) + _out << "attributes fv\n"; + else + _out << "attributes " << mesh.getAttributes() << "\n"; + + _out << "colorSpace " << PatateCommon::getColorSpaceName(mesh.colorSpace()) << "\n"; + + _out << "vertices " << mesh.nVertices() << "\n"; + _out << "nodes " << mesh.nNodes() << "\n"; + _out << "faces " << mesh.nFaces() << "\n"; + + unsigned count = 0; + m_vxMap.resize(mesh.nVertices(), -1); + for(VertexIterator vit = mesh.verticesBegin(); + vit != mesh.verticesEnd(); ++vit) + { + m_vxMap[(*vit).idx()] = (count++); + _out << "v " << mesh.position(*vit).transpose().format(m_format) << "\n"; + } + + // Find used nodes and only output them. + BoolMap nodeUsed(mesh.nNodes(), false); + for(FaceIterator fit = mesh.facesBegin(); + fit != mesh.facesEnd(); ++fit) + { + HalfedgeAroundFaceCirculator + hit = mesh.halfedges(*fit), + hend = hit; + do + { + for(unsigned aid = 0; aid < Mesh::HALFEDGE_ATTRIB_COUNT; ++aid) { + HalfedgeAttribute attr = HalfedgeAttribute(aid); + if(mesh.hasAttribute(attr) && mesh.halfedgeNode(*hit, attr).isValid()) + { + nodeUsed[mesh.halfedgeNode(*hit, attr).idx()] = true; + } + } + } + while(++hit != hend); + } + + // Node printing conserve ordering + count = 0; + m_nodeMap.resize(mesh.nNodes(), -1); + for(unsigned i = 0; i < mesh.nNodes(); ++i) + { + if(!nodeUsed[i]) continue; + m_nodeMap[i] = (count++); + if(mesh.isConstraint(Node(i))) + { + _out << "n " << mesh.value(Node(i)).transpose().format(m_format) << "\n"; + } + else + { + _out << "n void\n"; + } + } + + for(FaceIterator fit = mesh.facesBegin(); + fit != mesh.facesEnd(); ++fit) + { + _out << "f"; + + HalfedgeAroundFaceCirculator + hit = mesh.halfedges(*fit), + hend = hit; + do + { + assert(vertexIndex(mesh.toVertex(*hit)) != -1); + _out << " " << vertexIndex(mesh.toVertex(*hit)) + iOffset; + + if(mesh.hasToVertexValue()) + { + Node vn = mesh.toVertexValueNode(*hit); + _out << "/"; + printNode(_out, vn); + + // VertexFromValue only makes sense if vertexValue in enable. + if(mesh.hasFromVertexValue()) + { + Node fn = mesh.fromVertexValueNode(mesh.nextHalfedge(*hit)); + if(vn != fn) + { + _out << "/"; + printNode(_out, fn); + } + } + } + } + while(++hit != hend); + + if(mesh.hasEdgeValue() || mesh.hasEdgeGradient()) + { + _out << " -"; + + ++hit; hend = hit; + do + { + char sep = ' '; + if(mesh.hasEdgeValue()) + { + _out << sep; + sep = '/'; + printNode(_out, mesh.edgeValueNode(*hit)); + } + if(mesh.hasEdgeGradient()) + { + _out << sep; + sep = '/'; + printNode(_out, mesh.edgeGradientNode(*hit)); + } + } + while(++hit != hend); + } + + _out << "\n"; + } + + for(typename Mesh::HalfedgeIterator hit = mesh.halfedgesBegin(); + hit != mesh.halfedgesEnd(); ++hit) + { + if(!mesh.isCurved(*hit) || mesh.halfedgeOrientation(*hit)) continue; + const typename Mesh::CurvedEdge& ce = mesh.edgeCurve(*hit); + if(ce.type() == BEZIER_EMPTY || ce.type() == BEZIER_LINEAR) continue; + _out << "ce " << vertexIndex(mesh.fromVertex(*hit)) + << " " << vertexIndex(mesh.toVertex(*hit)) + << " " << ce.point(1).transpose().format(m_format); + if(ce.type() == BEZIER_CUBIC) { + _out << " " << ce.point(2).transpose().format(m_format); + } + _out << "\n"; + } + + if(mesh.hasVertexGradientConstraint()) + { + for(VertexIterator vit = mesh.verticesBegin(); + vit != mesh.verticesEnd(); ++vit) + { + if(mesh.isGradientConstraint(*vit)) + { + _out << "vgc " << vertexIndex(*vit) << " " + << mesh.gradientConstraint(*vit).transpose().format(m_format) << "\n"; + } + } + } +} + + +template < typename _Mesh > +int +MVGWriter<_Mesh>::vertexIndex(Vertex vx) const +{ + return m_vxMap[vx.idx()]; +} + + +template < typename _Mesh > +int +MVGWriter<_Mesh>::nodeIndex(Node node) const +{ + return m_nodeMap[node.idx()]; +} + + +template < typename _Mesh > +void +MVGWriter<_Mesh>::printNode(std::ostream& _out, Node n) const +{ + if(n.isValid()) + { + assert(nodeIndex(n) != -1); + _out << nodeIndex(n); + } + else + { + _out << "x"; + } +} + + +template < typename Mesh > +void writeMvg(std::ostream& out, const Mesh& mesh, + typename MVGWriter::Version version) +{ + MVGWriter writer(version); + writer.write(out, mesh); +} + +template < typename Mesh > +void writeMvgToFile(const std::string& filename, const Mesh& mesh, + typename MVGWriter::Version version) +{ + std::ofstream out(filename.c_str()); + writeMvg(out, mesh, version); +} + + +} // namespace Vitelotte diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRenderer.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRenderer.h new file mode 100644 index 000000000..d0d83a39f --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRenderer.h @@ -0,0 +1,226 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _VITELOTTE_VG_MESH_RENDERER_ +#define _VITELOTTE_VG_MESH_RENDERER_ + + +#include +#include + +#include "../../common/gl_utils/shader.h" +#include "../../common/gl_utils/color.h" + +#include "../Core/femUtils.h" +#include "vgMeshRendererShaders.hpp" + + +namespace Vitelotte { + + +using PatateCommon::ColorSpace; + +/** + * \brief The VGMeshRendererResources encapsulate resources required by + * VGMeshRenderer for sharing. + */ +class VGMeshRendererResources { +public: + struct SolidUniforms + { + GLint viewMatrixLoc; + GLint normalMatrixLoc; + GLint nodesLoc; + GLint baseNodeIndexLoc; + GLint singularTrianglesLoc; + GLfloat smoothnessLoc; + GLint enableShadingLoc; + GLint meshColorSpaceLoc; + GLint screenColorSpaceLoc; + }; + + struct WireframeUniforms + { + GLint viewMatrixLoc; + GLint viewportSizeLoc; + GLfloat smoothnessLoc; + GLint lineWidthLoc; + GLint wireframeColorLoc; + }; + + enum + { + VG_MESH_POSITION_ATTR_LOC, + VG_MESH_NORMAL_ATTR_LOC + }; + +public: + inline VGMeshRendererResources(); + inline ~VGMeshRendererResources(); + + inline bool initialize(); + inline void releaseGLResources(); + + // private + inline PatateCommon::Shader& solidLinearShader() { return m_solidLinearShader; } + inline PatateCommon::Shader& solidQuadraticShader() { return m_solidQuadraticShader; } + inline PatateCommon::Shader& wireframeShader() { return m_wireframeShader; } + + inline const SolidUniforms& solidLinearUniforms() const { return m_solidLinearUniforms; } + inline const SolidUniforms& solidQuadraticUniforms() const { return m_solidQuadraticUniforms; } + inline const WireframeUniforms& wireframeUniforms() const { return m_wireframeUniforms; } + +protected: + inline bool initSolidShader(PatateCommon::Shader& shader, SolidUniforms& unif, + const char *fragCode); + inline bool initWireframeShader(); + +protected: + bool m_initialized; + PatateCommon::Shader m_solidLinearShader; + PatateCommon::Shader m_solidQuadraticShader; + PatateCommon::Shader m_wireframeShader; + SolidUniforms m_solidLinearUniforms; + SolidUniforms m_solidQuadraticUniforms; + WireframeUniforms m_wireframeUniforms; +}; + + +template < typename Vector > +struct DefaultPosProj { + inline Eigen::Vector4f operator()(const Vector& position) const; +}; + + +template < typename Value > +struct DefaultValueProj { + inline Eigen::Vector4f operator()(const Value& value) const; +}; + + +/** + * \brief An OpenGL 3 renderer to display VGMesh objects. + */ +template < class _Mesh, + typename _PosProj = DefaultPosProj , + typename _ValueProj = DefaultValueProj > +class VGMeshRenderer +{ +public: + typedef _Mesh Mesh; + typedef _PosProj PosProj; + typedef _ValueProj ValueProj; + + typedef typename Mesh::Node Node; + typedef typename Mesh::Vector Vector; + typedef typename Mesh::Value Value; + + typedef typename Mesh::Vertex Vertex; + typedef typename Mesh::Face Face; + + typedef VGMeshRendererResources Resources; + + typedef Eigen::Vector3f Vector3; + typedef Eigen::Vector4f Vector4; + + enum + { + NORMAL_TRIANGLES = 0x01, + SINGULAR_TRIANGLES = 0x02, + + ALL_TRIANGLES = NORMAL_TRIANGLES | SINGULAR_TRIANGLES + }; + + enum + { + NODES_TEXTURE_UNIT + }; + +public: + VGMeshRenderer(Resources* resources = 0, + const PosProj& posProj = PosProj(), + const ValueProj& valueProj = ValueProj()); + ~VGMeshRenderer(); + + const PosProj& positionProjection() const; + PosProj& positionProjection(); + const ValueProj& valueProjection() const; + ValueProj& valueProjection(); + + ColorSpace screenColorSpace() const; + void setScreenColorSpace(ColorSpace cs) const; + + void setResources(Resources* resources); + void releaseGLResources(); + void updateBuffers(const Mesh& mesh); + + void drawGeometry(unsigned geomFlags); + void render(const Eigen::Matrix4f& viewMatrix, float smoothness = 8); + void renderWireframe(const Eigen::Matrix4f& viewMatrix, + const Eigen::Vector2f& viewportSize, float lineWidth = 1, + const Eigen::Vector4f& color = Eigen::Vector4f(0, 0, 0, 1), + float smoothness = 8); + + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + +private: + typedef std::vector IndicesVector; + typedef std::vector > Vector4Vector; + + struct GlVertex { + Vector4 position; + Vector3 normal; + }; + typedef std::vector > VertexVector; + +protected: + Vector4 position(const Mesh& mesh, Vertex vx) const; + Vector4 color(const Mesh& mesh, Node node) const; + + void initResources(); + + template < typename Vec > + void createAndUploadBuffer(GLuint& glId, GLenum type, + const Vec& data, + GLenum usage = GL_DYNAMIC_DRAW); + +private: + bool m_useVao; + bool m_convertSrgbToLinear; + bool m_ownResources; + + PosProj m_positionProjection; + ValueProj m_valueProjection; + Vector4 m_invalidNodeColor; + ColorSpace m_meshColorSpace; + ColorSpace m_screenColorSpace; + + Resources* m_resources; + + GLuint m_verticesBuffer; + GLuint m_indicesBuffer; + GLuint m_nodesBuffer; + GLuint m_nodesTexture; + + GLuint m_vao; + + VertexVector m_vertices; + IndicesVector m_indices; + Vector4Vector m_nodes; + + bool m_quadratic; + bool m_3d; + unsigned m_nTriangles; + unsigned m_nSingulars; +}; + + +} + +#include "vgMeshRenderer.hpp" + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRenderer.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRenderer.hpp new file mode 100644 index 000000000..c76e8fd22 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRenderer.hpp @@ -0,0 +1,625 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include "vgMeshRenderer.h" + + +namespace Vitelotte +{ + + +VGMeshRendererResources::VGMeshRendererResources() + : m_initialized(false) +{ +} + + +VGMeshRendererResources::~VGMeshRendererResources() +{ + if(m_initialized) releaseGLResources(); +} + + +bool VGMeshRendererResources::initialize() +{ + bool ok = true; + ok &= initSolidShader(m_solidLinearShader, m_solidLinearUniforms, + VGMeshRendererShaders::frag_linear_glsl); + ok &= initSolidShader(m_solidQuadraticShader, m_solidQuadraticUniforms, + VGMeshRendererShaders::frag_quadratic_glsl); + ok &= initWireframeShader(); + m_initialized = ok; + return ok; +} + + +void VGMeshRendererResources::releaseGLResources() +{ + m_initialized = false; + + m_solidLinearShader.destroy(); + m_solidQuadraticShader.destroy(); + m_wireframeShader.destroy(); +} + + +bool VGMeshRendererResources::initSolidShader( + PatateCommon::Shader& shader, SolidUniforms& unif, const char* fragCode) +{ + shader.create(); + + bool ok = true; + ok &= shader.addShader(GL_VERTEX_SHADER, + VGMeshRendererShaders::vert_common_glsl); + ok &= shader.addShader(GL_TESS_CONTROL_SHADER, + VGMeshRendererShaders::ctrl_common_glsl); + ok &= shader.addShader(GL_TESS_EVALUATION_SHADER, + VGMeshRendererShaders::eval_common_glsl); + ok &= shader.addShader(GL_FRAGMENT_SHADER, + VGMeshRendererShaders::frag_common_glsl); + ok &= shader.addShader(GL_FRAGMENT_SHADER, fragCode); + + shader.bindAttributeLocation("vx_position", VG_MESH_POSITION_ATTR_LOC); + shader.bindAttributeLocation("vx_normal", VG_MESH_NORMAL_ATTR_LOC); + ok &= shader.finalize(); + + if(!ok) + return false; + + unif.viewMatrixLoc = shader.getUniformLocation("viewMatrix"); + unif.normalMatrixLoc = shader.getUniformLocation("normalMatrix"); + unif.nodesLoc = shader.getUniformLocation("nodes"); + unif.baseNodeIndexLoc = shader.getUniformLocation("baseNodeIndex"); + unif.singularTrianglesLoc = shader.getUniformLocation("singularTriangles"); + unif.smoothnessLoc = shader.getUniformLocation("smoothness"); + unif.enableShadingLoc = shader.getUniformLocation("enableShading"); + unif.meshColorSpaceLoc = shader.getUniformLocation("meshColorSpace"); + unif.screenColorSpaceLoc = shader.getUniformLocation("screenColorSpace"); + + return true; +} + + +bool VGMeshRendererResources::initWireframeShader() +{ + PatateCommon::Shader& shader = m_wireframeShader; + WireframeUniforms& unif = m_wireframeUniforms; + shader.create(); + + bool ok = true; + ok &= shader.addShader(GL_VERTEX_SHADER, + VGMeshRendererShaders::vert_common_glsl); + ok &= shader.addShader(GL_TESS_CONTROL_SHADER, + VGMeshRendererShaders::ctrl_common_glsl); + ok &= shader.addShader(GL_TESS_EVALUATION_SHADER, + VGMeshRendererShaders::eval_common_glsl); + // Uncomment this to display tesselated wireframe. +// ok &= shader.addShader(GL_GEOMETRY_SHADER, +// VGMeshRendererShaders::geom_common_glsl); + ok &= shader.addShader(GL_FRAGMENT_SHADER, + VGMeshRendererShaders::frag_common_glsl); + ok &= shader.addShader(GL_FRAGMENT_SHADER, + VGMeshRendererShaders::frag_wireframe_glsl); + + shader.bindAttributeLocation("vx_position", VG_MESH_POSITION_ATTR_LOC); + ok &= shader.finalize(); + + if(!ok) + return false; + + unif.viewMatrixLoc = shader.getUniformLocation("viewMatrix"); + unif.viewportSizeLoc = shader.getUniformLocation("viewportSize"); + unif.smoothnessLoc = shader.getUniformLocation("smoothness"); + unif.lineWidthLoc = shader.getUniformLocation("lineWidth"); + unif.wireframeColorLoc = shader.getUniformLocation("wireframeColor"); + + return true; +} + + +template < typename Vector > +Eigen::Vector4f DefaultPosProj::operator()(const Vector& position) const { + Eigen::Vector4f p = Eigen::Vector4f::Unit(3); + unsigned size = std::min(unsigned(position.size()), 3u); + p.head(size) = position.head(size); + return p; +} + + +template < typename Value > +Eigen::Vector4f DefaultValueProj::operator()(const Value& value) const { + typedef Eigen::Vector3f Vector3; + Eigen::Vector4f c = Eigen::Vector4f::Unit(3); + switch(value.size()) + { + case 2: c(3) = value(1); // fall-through + case 1: c.head<3>() = Vector3::Constant(value(0)); break; + case 3: c.head<3>() = value.template cast(); break; + default: c = value.template head<4>() + .template cast(); break; + } + return c; +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::VGMeshRenderer( + Resources* resources, const PosProj& posProj, const ValueProj& valueProj) : + m_useVao(true), + m_ownResources(false), + + m_positionProjection(posProj), + m_valueProjection(valueProj), + m_invalidNodeColor(Vector4::Unit(3)), + m_meshColorSpace(PatateCommon::COLOR_NONE), + m_screenColorSpace(PatateCommon::COLOR_SRGB), + + m_resources(resources), + + m_verticesBuffer(0), + m_indicesBuffer(0), + m_nodesBuffer(0), + m_nodesTexture(0), + + m_vao(0), + + m_vertices(), + m_indices(), + m_nodes(), + + m_quadratic(false), + m_nTriangles(0), + m_nSingulars(0) +{} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::~VGMeshRenderer() +{ + releaseGLResources(); +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +const typename VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::PosProj& +VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::positionProjection() const +{ + return m_positionProjection; +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +typename VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::PosProj& +VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::positionProjection() +{ + return m_positionProjection; +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +const typename VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::ValueProj& +VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::valueProjection() const +{ + return m_valueProjection; +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +typename VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::ValueProj& +VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::valueProjection() +{ + return m_valueProjection; +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +ColorSpace VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::screenColorSpace() const +{ + return m_screenColorSpace; +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +void VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::setScreenColorSpace(ColorSpace cs) const +{ + m_screenColorSpace = cs; +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +void VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::setResources( + Resources* resources) +{ + assert(!m_resources); + m_resources = resources; + m_ownResources = false; +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +void VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::releaseGLResources() +{ + if(m_ownResources) + { + delete m_resources; + m_resources = 0; + } + + if(m_verticesBuffer) + { + glDeleteBuffers(1, &m_verticesBuffer); + m_verticesBuffer = 0; + } + if(m_indicesBuffer) + { + glDeleteBuffers(1, &m_indicesBuffer); + m_indicesBuffer = 0; + } + if(m_nodesBuffer) + { + glDeleteBuffers(1, &m_nodesBuffer); + m_nodesBuffer = 0; + } + + if(m_nodesTexture) + { + glDeleteTextures(1, &m_nodesTexture); + m_nodesTexture = 0; + } + + if(m_vao) + { + glDeleteVertexArrays(1, &m_vao); + m_vao = 0; + } +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +void VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::updateBuffers(const Mesh& mesh) +{ + PATATE_ASSERT_NO_GL_ERROR(); + + m_quadratic = mesh.hasEdgeValue(); + m_3d = mesh.nDims() == 3; + m_meshColorSpace = mesh.colorSpace(); + + int nodePerTriangle = m_quadratic? 6: 3; + + // Compute number of singular and normal triangles + m_nSingulars = mesh.nSingularFaces(); + m_nTriangles = mesh.nFaces() - m_nSingulars; + + if(m_nSingulars + m_nTriangles == 0) { + return; + } + + // Reserve buffers + m_vertices.resize(mesh.verticesSize() + 2 * mesh.edgesSize()); + m_indices.resize((m_nTriangles * 3 + m_nSingulars * 3) * 3); + m_nodes.resize(m_nTriangles * nodePerTriangle + + m_nSingulars * (nodePerTriangle + 1)); + + // Push vertices positions + for(typename Mesh::VertexIterator vit = mesh.verticesBegin(); + vit != mesh.verticesEnd(); ++vit) + { + m_vertices[(*vit).idx()].position = position(mesh, *vit); + m_vertices[(*vit).idx()].normal.setZero(); + } + + // Subdivide curved edges + unsigned nVertices = mesh.verticesSize(); + for(typename Mesh::EdgeIterator eit = mesh.edgesBegin(); + eit != mesh.edgesEnd(); ++eit) + { + unsigned i1 = nVertices + 2 * (*eit).idx(); + unsigned i2 = i1 + 1; + + typename Mesh::Halfedge h = mesh.halfedge(*eit, 0); + bool orient = mesh.halfedgeOrientation(h); + const Vector& v0 = mesh.position(orient? mesh.toVertex(h): mesh.fromVertex(h)); + const Vector& v3 = mesh.position(orient? mesh.fromVertex(h): mesh.toVertex(h)); + if(!mesh.isCurved(*eit) || mesh.edgeCurve(*eit).type() == BEZIER_LINEAR) { + m_vertices[i1].position = m_positionProjection( + (2 * v0 + v3) / 3); + m_vertices[i2].position = m_positionProjection( + (v0 + 2 * v3) / 3); + } else if(mesh.edgeCurve(*eit).type() == BEZIER_QUADRATIC) { + m_vertices[i1].position = m_positionProjection( + (v0 + 2 * mesh.edgeCurve(*eit).point(1)) / 3); + m_vertices[i2].position = m_positionProjection( + (2 * mesh.edgeCurve(*eit).point(1) + v3) / 3); + } else { + m_vertices[i1].position = m_positionProjection( + mesh.edgeCurve(*eit).point(1)); + m_vertices[i2].position = m_positionProjection( + mesh.edgeCurve(*eit).point(2)); + } + // Normals are interpolated later. + } + + // Push faces indices and nodes + unsigned triIndex = 0; + unsigned singIndex = m_nTriangles * 3; + unsigned triNodeIndex = 0; + unsigned singNodeIndex = m_nTriangles * nodePerTriangle; + for(typename Mesh::FaceIterator fit = mesh.facesBegin(); + fit != mesh.facesEnd(); ++fit) + { + // Ensure we work with triangles + assert(mesh.valence(*fit) == 3); + typename Mesh::Halfedge h = mesh.halfedge(*fit); + + bool isSingular = mesh.nSingulars(*fit); + if(isSingular) + { + // The first vertex must be the singular one + while(!mesh.isSingular(h)) { h = mesh.nextHalfedge(h); } + } + + unsigned& index = isSingular? singIndex: triIndex; + unsigned& nodeIndex = isSingular? singNodeIndex: triNodeIndex; + Vector3 pts[3]; + // Push vertices nodes + for(int ei = 0; ei < 3; ++ei) + { + if(m_3d) pts[ei] = mesh.position(mesh.toVertex(h)); + m_indices[3 * (index + ei)] = mesh.toVertex(h).idx(); + h = mesh.nextHalfedge(h); + m_indices[3 * (index + ei) + 1] = nVertices + 2 * mesh.edge(h).idx() + + mesh.halfedgeOrientation(h); + m_indices[3 * (index + ei) + 2] = nVertices + 2 * mesh.edge(h).idx() + + 1 - mesh.halfedgeOrientation(h); + m_nodes[nodeIndex + ei] = mesh.hasToVertexValue()? + color(mesh, mesh.fromVertexValueNode(h)): + Eigen::Vector4f(.8, .8, .8, 1.); + } + // Singular node is the last one + if(isSingular) + m_nodes[nodeIndex + nodePerTriangle] = color(mesh, mesh.toVertexValueNode(h)); + + if(m_quadratic) + { + // Push edge nodes + h = mesh.prevHalfedge(h); + for(int ei = 0; ei < 3; ++ei) + { + m_nodes[nodeIndex + 3 + ei] = color(mesh, mesh.edgeValueNode(h)); + h = mesh.nextHalfedge(h); + } + } + + if(m_3d) { + // Compute normals + Vector3 fn = (pts[1] - pts[0]).cross(pts[2] - pts[0]); + for(int ei = 0; ei < 3; ++ei) { + m_vertices[mesh.toVertex(h).idx()].normal += fn; + h = mesh.nextHalfedge(h); + } + } + + index += 3; + nodeIndex += nodePerTriangle + isSingular; + } +// assert(triIndex == m_nTriangles * 3 && singIndex == m_indices.size()); +// assert(triNodeIndex == m_nTriangles * nodePerTriangle && singNodeIndex == m_nodes.size()); + + if(m_3d) { + // Normalize normals + for(typename Mesh::VertexIterator vit = mesh.verticesBegin(); + vit != mesh.verticesEnd(); ++vit) { + m_vertices[(*vit).idx()].normal.normalize(); + } + } + + // Create and upload buffers + createAndUploadBuffer(m_verticesBuffer, GL_ARRAY_BUFFER, + m_vertices); + + createAndUploadBuffer(m_indicesBuffer, GL_ELEMENT_ARRAY_BUFFER, + m_indices); + + createAndUploadBuffer(m_nodesBuffer, GL_ARRAY_BUFFER, + m_nodes); + + // Create and setup texture buffer + if(!m_nodesTexture) + { + glGenTextures(1, &m_nodesTexture); + } + glBindTexture(GL_TEXTURE_BUFFER, m_nodesTexture); + glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_nodesBuffer); + + PATATE_ASSERT_NO_GL_ERROR(); +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +void VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::drawGeometry(unsigned geomFlags) +{ + assert((geomFlags & ~ALL_TRIANGLES) == 0); + PATATE_ASSERT_NO_GL_ERROR(); + + unsigned nPrimitives = 0; + unsigned firstPrimitive = 0; + if(geomFlags & SINGULAR_TRIANGLES) + { + nPrimitives += m_nSingulars; + firstPrimitive = m_nTriangles; + } + if(geomFlags & NORMAL_TRIANGLES) + { + nPrimitives += m_nTriangles; + firstPrimitive = 0; + } + + if(nPrimitives == 0) + { + return; + } + + bool setupBuffers = !m_useVao || !m_vao; + if(m_useVao) + { + if(!m_vao) + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); + } + + if(setupBuffers) + { + glBindBuffer(GL_ARRAY_BUFFER, m_verticesBuffer); + + glEnableVertexAttribArray(Resources::VG_MESH_POSITION_ATTR_LOC); + glVertexAttribPointer(Resources::VG_MESH_POSITION_ATTR_LOC, + 4, GL_FLOAT, + false, sizeof(GlVertex), + PATATE_FIELD_OFFSET(GlVertex, position)); + + glEnableVertexAttribArray(Resources::VG_MESH_NORMAL_ATTR_LOC); + + glVertexAttribPointer(Resources::VG_MESH_NORMAL_ATTR_LOC, + 3, GL_FLOAT, + false, sizeof(GlVertex), + PATATE_FIELD_OFFSET(GlVertex, normal)); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indicesBuffer); + } + + glPatchParameteri(GL_PATCH_VERTICES, 9); + glDrawElements(GL_PATCHES, nPrimitives * 9, GL_UNSIGNED_INT, + (const void*)(firstPrimitive * 9 * sizeof(unsigned))); + + if(m_useVao) { + glBindVertexArray(0); + } + else { + glDisableVertexAttribArray(Resources::VG_MESH_POSITION_ATTR_LOC); + glDisableVertexAttribArray(Resources::VG_MESH_NORMAL_ATTR_LOC); + } + + PATATE_ASSERT_NO_GL_ERROR(); +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +void VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::render( + const Eigen::Matrix4f& viewMatrix, float smoothness) +{ + PATATE_ASSERT_NO_GL_ERROR(); + + if(!m_resources) initResources(); + + PatateCommon::Shader& shader = m_quadratic? + m_resources->solidQuadraticShader(): + m_resources->solidLinearShader(); + + const Resources::SolidUniforms& unif = m_quadratic? + m_resources->solidQuadraticUniforms(): + m_resources->solidLinearUniforms(); + + shader.use(); + + Eigen::Matrix3f normalMatrix = viewMatrix.topLeftCorner<3, 3>(); + glUniformMatrix4fv(unif.viewMatrixLoc, 1, false, viewMatrix.data()); + glUniformMatrix3fv(unif.normalMatrixLoc, 1, false, normalMatrix.data()); + glUniform1i(unif.nodesLoc, NODES_TEXTURE_UNIT); + glUniform1f(unif.smoothnessLoc, smoothness); + glUniform1i(unif.enableShadingLoc, m_3d); + glUniform1i(unif.meshColorSpaceLoc, m_meshColorSpace); + glUniform1i(unif.screenColorSpaceLoc, m_screenColorSpace); + + glActiveTexture(GL_TEXTURE0 + NODES_TEXTURE_UNIT); + glBindTexture(GL_TEXTURE_BUFFER, m_nodesTexture); + + // Normal triangles + glUniform1i(unif.baseNodeIndexLoc, 0); + glUniform1i(unif.singularTrianglesLoc, false); + + drawGeometry(NORMAL_TRIANGLES); + + // Singular triangles + int nodePerTriangle = m_quadratic? 6: 3; + glUniform1i(unif.baseNodeIndexLoc, m_nTriangles * nodePerTriangle); + glUniform1i(unif.singularTrianglesLoc, true); + + drawGeometry(SINGULAR_TRIANGLES); + + PATATE_ASSERT_NO_GL_ERROR(); +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +void VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::renderWireframe( + const Eigen::Matrix4f& viewMatrix, const Eigen::Vector2f& viewportSize, + float lineWidth, const Eigen::Vector4f& color, float smoothness) +{ + PATATE_ASSERT_NO_GL_ERROR(); + + if(!m_resources) initResources(); + + m_resources->wireframeShader().use(); + const Resources::WireframeUniforms& unif = m_resources->wireframeUniforms(); + + glUniformMatrix4fv(unif.viewMatrixLoc, 1, false, viewMatrix.data()); + glUniform2fv(unif.viewportSizeLoc, 1, viewportSize.data()); + glUniform1f(unif.smoothnessLoc, smoothness); + glUniform1f(unif.lineWidthLoc, lineWidth); + glUniform4fv(unif.wireframeColorLoc, 1, color.data()); + + drawGeometry(ALL_TRIANGLES); + + PATATE_ASSERT_NO_GL_ERROR(); +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +inline typename VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::Vector4 +VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::position(const Mesh& mesh, Vertex vx) const +{ + return m_positionProjection(mesh.position(vx)); +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +inline typename VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::Vector4 +VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::color(const Mesh& mesh, Node node) const +{ + Vector4 c = (mesh.isValid(node) && mesh.isConstraint(node))? + m_valueProjection(mesh.value(node)): + m_invalidNodeColor; + return c; +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +void VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::initResources() +{ + assert(!m_resources); + + m_resources = new Resources(); + bool ok = m_resources->initialize(); + if(!ok) std::abort(); + m_ownResources = true; +} + + +template < class _Mesh, typename _PosProj, typename _ValueProj > +template < typename Vec > +void VGMeshRenderer<_Mesh, _PosProj, _ValueProj>::createAndUploadBuffer( + GLuint& glId, GLenum type, const Vec& data, GLenum usage) +{ + if(!glId) + { + glGenBuffers(1, &glId); + } + glBindBuffer(type, glId); + glBufferData(type, data.size() * sizeof(typename Vec::value_type), + &(data[0]), usage); +} + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders.hpp new file mode 100644 index 000000000..0bec3ac45 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders.hpp @@ -0,0 +1,690 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ +// Generated by shader2cpp. + + +#ifndef _VITELOTTE_UTILS_VG_MESH_RENDERER_SHADERS_ +#define _VITELOTTE_UTILS_VG_MESH_RENDERER_SHADERS_ + + +namespace Vitelotte +{ +namespace VGMeshRendererShaders +{ + +static const char* vert_common_glsl = + "/*\n" + " This Source Code Form is subject to the terms of the Mozilla Public\n" + " License, v. 2.0. If a copy of the MPL was not distributed with this\n" + " file, You can obtain one at http://mozilla.org/MPL/2.0/.\n" + "*/\n" + "\n" + "#version 410 core\n" + "\n" + "uniform mat4 viewMatrix;\n" + "uniform mat3 normalMatrix;\n" + "\n" + "in vec4 vx_position;\n" + "in vec3 vx_normal;\n" + "\n" + "out vec4 ctrl_position_obj;\n" + "out vec3 ctrl_normal_obj;\n" + "out vec3 ctrl_normal_view;\n" + "\n" + "void main(void)\n" + "{\n" + " gl_Position = viewMatrix * vx_position;\n" + " ctrl_position_obj = vx_position;\n" + " ctrl_normal_obj = vx_normal;\n" + " ctrl_normal_view = normalMatrix * vx_normal;\n" + "}\n" + ""; + +static const char* ctrl_common_glsl = + "/*\n" + " This Source Code Form is subject to the terms of the Mozilla Public\n" + " License, v. 2.0. If a copy of the MPL was not distributed with this\n" + " file, You can obtain one at http://mozilla.org/MPL/2.0/.\n" + "*/\n" + "\n" + "#version 410 core\n" + "\n" + "layout(vertices = 9) out;\n" + "\n" + "uniform vec2 viewportSize;\n" + "uniform float smoothness;\n" + "\n" + "in vec4 ctrl_position_obj[];\n" + "in vec3 ctrl_normal_obj[];\n" + "in vec3 ctrl_normal_view[];\n" + "\n" + "out vec4 eval_position_obj[];\n" + "out vec3 eval_normal_obj[];\n" + "out vec3 eval_normal_view[];\n" + "out ScreenSpaceBlock {\n" + " flat vec3 heights_scr;\n" + " flat vec3 vertices_obj[3];\n" + " flat vec3 normEdges_obj[3];\n" + "} eval_ss[];\n" + "\n" + "bool isLinear(int i) {\n" + " vec4 p0 = eval_position_obj[3*i + 0];\n" + " vec4 p1 = eval_position_obj[3*i + 1];\n" + " vec4 p2 = eval_position_obj[3*i + 2];\n" + " vec4 p3 = eval_position_obj[(3*i + 3)%9];\n" + "\n" + " vec4 p1off = p1 - (2*p0 + p3) / 3;\n" + " vec4 p2off = p2 - (p0 + 2*p3) / 3;\n" + "\n" + " return dot(p1off, p1off) < 0.0001\n" + " && dot(p2off, p2off) < 0.0001;\n" + "}\n" + "\n" + "float curviness(int i) {\n" + " vec4 p0 = eval_position_obj[3*i + 0];\n" + " vec4 p1 = eval_position_obj[3*i + 1];\n" + " vec4 p2 = eval_position_obj[3*i + 2];\n" + " vec4 p3 = eval_position_obj[(3*i + 3)%9];\n" + "\n" + " vec4 p1off = p1 - (2*p0 + p3) / 3;\n" + " vec4 p2off = p2 - (p0 + 2*p3) / 3;\n" + "\n" + " return max(length(p1off), length(p2off)) / distance(p0, p3);\n" + "}\n" + "\n" + "void main(void)\n" + "{\n" + " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" + " eval_position_obj[gl_InvocationID] = ctrl_position_obj[gl_InvocationID];\n" + " eval_normal_obj[gl_InvocationID] = ctrl_normal_obj[gl_InvocationID];\n" + " eval_normal_view[gl_InvocationID] = ctrl_normal_view[gl_InvocationID];\n" + "\n" + " vec2 position_scr[3];\n" + " for(int i=0; i<3; ++i)\n" + " {\n" + " position_scr[i] = (viewportSize * gl_in[i*3].gl_Position.xy)\n" + " / (2.0 * gl_in[i*3].gl_Position.z);\n" + " }\n" + " float area = abs(cross(vec3(position_scr[1] - position_scr[0], 0.0),\n" + " vec3(position_scr[2] - position_scr[0], 0.0)).z);\n" + "\n" + " for(int i=0; i<3; ++i)\n" + " {\n" + " eval_ss[gl_InvocationID].heights_scr[i]\n" + " = area / length(position_scr[(i+2)%3] - position_scr[(i+1)%3]);\n" + " eval_ss[gl_InvocationID].vertices_obj[i]\n" + " = ctrl_position_obj[i*3].xyz;\n" + " eval_ss[gl_InvocationID].normEdges_obj[i]\n" + " = normalize(ctrl_position_obj[((i+2)%3)*3].xyz - ctrl_position_obj[((i+1)%3)*3].xyz);\n" + " }\n" + "\n" + " bvec3 isEdgeLinear = bvec3(isLinear(0), isLinear(1), isLinear(2));\n" + " gl_TessLevelOuter = float[4](\n" + " isEdgeLinear[1]? 1: smoothness,\n" + " isEdgeLinear[2]? 1: smoothness,\n" + " isEdgeLinear[0]? 1: smoothness,\n" + " 1);\n" + " gl_TessLevelInner = float[2](all(isEdgeLinear)? 1: smoothness, 1);\n" + " // Uncomment this for adaptive smoothing (using a very rough heristic).\n" + "// gl_TessLevelOuter = float[4](\n" + "// max(curviness(1) * smoothness, 1),\n" + "// max(curviness(2) * smoothness, 1),\n" + "// max(curviness(0) * smoothness, 1),\n" + "// 1);\n" + "}\n" + ""; + +static const char* eval_common_glsl = + "/*\n" + " This Source Code Form is subject to the terms of the Mozilla Public\n" + " License, v. 2.0. If a copy of the MPL was not distributed with this\n" + " file, You can obtain one at http://mozilla.org/MPL/2.0/.\n" + "*/\n" + "\n" + "#version 410 core\n" + "\n" + "layout(triangles) in;\n" + "\n" + "uniform mat4 viewMatrix;\n" + "\n" + "uniform samplerBuffer nodes;\n" + "uniform int baseNodeIndex;\n" + "uniform bool singularTriangles;\n" + "\n" + "in vec4 eval_position_obj[];\n" + "in vec3 eval_normal_obj[];\n" + "in vec3 eval_normal_view[];\n" + "in ScreenSpaceBlock {\n" + " flat vec3 heights_scr;\n" + " flat vec3 vertices_obj[3];\n" + " flat vec3 normEdges_obj[3];\n" + "} eval_ss[];\n" + "\n" + "flat out int frag_index;\n" + "out vec3 frag_linearBasis;\n" + "out vec4 frag_position_obj;\n" + "out vec3 frag_normal_obj;\n" + "out vec3 frag_normal_view;\n" + "out vec3 frag_edgeDist_scr;\n" + "flat out vec3 frag_vertices_obj[3];\n" + "flat out vec3 frag_normEdges_obj[3];\n" + "\n" + "void main(void)\n" + "{\n" + " vec3 c1 = gl_TessCoord;\n" + " vec3 c2 = c1 * c1;\n" + " vec3 c3 = c2 * c1;\n" + " vec4 mid = vec4(0);\n" + " for(int i = 0; i < 9; ++i) mid += eval_position_obj[i];\n" + " mid /= 9;\n" + " frag_position_obj = eval_position_obj[0] * c3[0]\n" + " + eval_position_obj[1] * c2[0] * c1[1] * 3\n" + " + eval_position_obj[2] * c1[0] * c2[1] * 3\n" + " + eval_position_obj[3] * c3[1]\n" + " + eval_position_obj[4] * c2[1] * c1[2] * 3\n" + " + eval_position_obj[5] * c1[1] * c2[2] * 3\n" + " + eval_position_obj[6] * c3[2]\n" + " + eval_position_obj[7] * c2[2] * c1[0] * 3\n" + " + eval_position_obj[8] * c1[2] * c2[0] * 3\n" + " + mid * c1[0] * c1[1] * c1[2] * 6;\n" + "\n" + " gl_Position = viewMatrix * frag_position_obj;\n" + " frag_index = gl_PrimitiveID;\n" + " frag_linearBasis = gl_TessCoord;\n" + " frag_normal_obj = mat3(eval_normal_obj[0],\n" + " eval_normal_obj[3],\n" + " eval_normal_obj[6]) * gl_TessCoord;\n" + " frag_normal_view = mat3(eval_normal_view[0],\n" + " eval_normal_view[3],\n" + " eval_normal_view[6]) * gl_TessCoord;\n" + " frag_edgeDist_scr = eval_ss[0].heights_scr * gl_TessCoord;\n" + " frag_vertices_obj = eval_ss[0].vertices_obj;\n" + " frag_normEdges_obj = eval_ss[0].normEdges_obj;\n" + "}\n" + ""; + +static const char* geom_common_glsl = + "/*\n" + " This Source Code Form is subject to the terms of the Mozilla Public\n" + " License, v. 2.0. If a copy of the MPL was not distributed with this\n" + " file, You can obtain one at http://mozilla.org/MPL/2.0/.\n" + "*/\n" + "\n" + "#version 410 core\n" + "\n" + "layout(triangles) in;\n" + "layout(triangle_strip, max_vertices = 3) out;\n" + "\n" + "uniform vec2 viewportSize;\n" + "\n" + "in vec4 geom_position_obj[];\n" + "in vec3 geom_normal_obj[];\n" + "in vec3 geom_normal_view[];\n" + "\n" + "flat out int frag_index;\n" + "out vec3 frag_linearBasis;\n" + "out vec3 frag_position_obj;\n" + "out vec3 frag_normal_obj;\n" + "out vec3 frag_normal_view;\n" + "out vec3 frag_edgeDist_scr;\n" + "flat out vec3 frag_vertices_obj[3];\n" + "flat out vec3 frag_normEdges_obj[3];\n" + "\n" + "const vec3 basis[3] = vec3[3](\n" + " vec3(1, 0, 0),\n" + " vec3(0, 1, 0),\n" + " vec3(0, 0, 1)\n" + ");\n" + "\n" + "void main()\n" + "{\n" + " vec2 position_scr[3];\n" + " for(int i=0; i<3; ++i)\n" + " {\n" + " position_scr[i] = (viewportSize * gl_in[i].gl_Position.xy)\n" + " / (2.0 * gl_in[i].gl_Position.z);\n" + " }\n" + " float area = abs(cross(vec3(position_scr[1] - position_scr[0], 0.0),\n" + " vec3(position_scr[2] - position_scr[0], 0.0)).z);\n" + " for(int i=0; i<3; ++i)\n" + " {\n" + " gl_Position = gl_in[i].gl_Position;\n" + " frag_index = gl_PrimitiveIDIn;\n" + " frag_linearBasis = basis[i];\n" + " frag_position_obj = geom_position_obj[i].xyz;\n" + " frag_normal_obj = geom_normal_obj[i];\n" + " frag_normal_view = geom_normal_view[i];\n" + " frag_edgeDist_scr = vec3(0.0);\n" + " frag_edgeDist_scr[i] = area / length(position_scr[(i+2)%3] - position_scr[(i+1)%3]);\n" + " for(int j=0; j<3; ++j)\n" + " {\n" + " frag_vertices_obj[j] = geom_position_obj[j].xyz;//gl_in[j].gl_Position.xy;\n" + " frag_normEdges_obj[j] = normalize(geom_position_obj[(j+2)%3].xyz\n" + " - geom_position_obj[(j+1)%3].xyz);\n" + " }\n" + " EmitVertex();\n" + " }\n" + "}\n" + ""; + +static const char* frag_common_glsl = + "/*\n" + " This Source Code Form is subject to the terms of the Mozilla Public\n" + " License, v. 2.0. If a copy of the MPL was not distributed with this\n" + " file, You can obtain one at http://mozilla.org/MPL/2.0/.\n" + "*/\n" + "\n" + "#version 410 core\n" + "\n" + "uniform float zoom;\n" + "uniform float pointRadius;\n" + "uniform float halfLineWidth;\n" + "uniform bool showWireframe;\n" + "uniform vec4 wireframeColor;\n" + "uniform vec4 pointColor;\n" + "\n" + "in vec3 frag_linearBasis;\n" + "in vec2 position;\n" + "flat in vec2 vertices[3];\n" + "flat in vec2 normEdges[3];\n" + "\n" + "\n" + "float irlerp(in vec3 vx, in vec3 v1, in vec3 v2)\n" + "{\n" + " float alpha = acos(clamp(dot(v1, vx), -1., 1.));\n" + " float beta = acos(clamp(dot(v1, v2), -1., 1.));\n" + " return alpha / beta;\n" + "}\n" + "\n" + "\n" + "vec4 quadraticInterp(in vec4 colors[6])\n" + "{\n" + " return\n" + " colors[0] * frag_linearBasis.x * (2. * frag_linearBasis.x - 1.) +\n" + " colors[1] * frag_linearBasis.y * (2. * frag_linearBasis.y - 1.) +\n" + " colors[2] * frag_linearBasis.z * (2. * frag_linearBasis.z - 1.) +\n" + " colors[3] * 4. * frag_linearBasis.y * frag_linearBasis.z +\n" + " colors[4] * 4. * frag_linearBasis.z * frag_linearBasis.x +\n" + " colors[5] * 4. * frag_linearBasis.x * frag_linearBasis.y;\n" + "}\n" + "\n" + "float diffuse(in vec3 n, in vec3 l)\n" + "{\n" + " return clamp(dot(n, l), 0., 1.);\n" + "}\n" + "\n" + "\n" + "///////////////////////////////////////////////////////////////////////////////\n" + "// Color space conversion\n" + "\n" + "const int COLOR_NONE = 0;\n" + "const int COLOR_SRGB = 1;\n" + "const int COLOR_LINEAR_RGB = 2;\n" + "const int COLOR_CIE_XYZ = 3;\n" + "const int COLOR_CIE_LAB = 4;\n" + "const int COLOR_CIE_LUV = 5;\n" + "\n" + "\n" + "// SRGB <-> linear rgbToxyz\n" + "\n" + "vec3 srgbFromLinearRGB(const in vec3 linear)\n" + "{\n" + " vec3 srgb = linear;\n" + " srgb[0] = (linear[0] > 0.0031308)?\n" + " 1.055 * pow(linear[0], 1./2.4) - .055f:\n" + " 12.92 * linear[0];\n" + " srgb[1] = (linear[1] > 0.0031308)?\n" + " 1.055 * pow(linear[1], 1./2.4) - .055f:\n" + " 12.92 * linear[1];\n" + " srgb[2] = (linear[2] > 0.0031308)?\n" + " 1.055 * pow(linear[2], 1./2.4) - .055f:\n" + " 12.92 * linear[2];\n" + " return srgb;\n" + "}\n" + "\n" + "vec3 linearRGBFromSrgb(const in vec3 srgb)\n" + "{\n" + " vec3 linear = srgb;\n" + " linear[0] = (linear[0] > 0.04045)?\n" + " pow((linear[0]+0.055) / 1.055, 2.4):\n" + " linear[0] / 12.92;\n" + " linear[1] = (linear[1] > 0.04045)?\n" + " pow((linear[1]+0.055) / 1.055, 2.4):\n" + " linear[1] / 12.92;\n" + " linear[2] = (linear[2] > 0.04045)?\n" + " pow((linear[2]+0.055) / 1.055, 2.4):\n" + " linear[2] / 12.92;\n" + " return linear;\n" + "}\n" + "\n" + "\n" + "// Linear RGB <-> Cie XYZ\n" + "\n" + "const mat3 xyzToRgb = mat3(\n" + " 3.2406, -1.5372, -0.4986,\n" + " -0.9689, 1.8758, 0.0415,\n" + " 0.0557, -0.2040, 1.0570\n" + ");\n" + "\n" + "vec3 linearRGBFromCieXYZ(const in vec3 cieXYZ) {\n" + " return transpose(xyzToRgb) * cieXYZ;\n" + "}\n" + "\n" + "mat3 rgbToxyz = mat3(\n" + " 0.4124, 0.3576, 0.1805,\n" + " 0.2126, 0.7152, 0.0722,\n" + " 0.0193, 0.1192, 0.9505\n" + ");\n" + "\n" + "vec3 cieXYZFromLinearRGB(const in vec3 lrgb) {\n" + " return transpose(rgbToxyz) * lrgb;\n" + "}\n" + "\n" + "\n" + "// Cie XYZ <-> Cie Lab\n" + "\n" + "float cieLabF(const in float v) {\n" + " return (v > 0.008856452)?\n" + " pow(v, 1./3.):\n" + " 1. / (3. * 0.042806183) * v + (4. / 29.);\n" + "}\n" + "\n" + "float cieLabFInv(const in float v) {\n" + " return (v > 0.206896552)?\n" + " pow(v, 3.):\n" + " 3. * 0.042806183 * (v - (4. / 29.));\n" + "}\n" + "\n" + "const vec3 cieLabWhite = vec3(0.95047, 1, 1.08883);\n" + "\n" + "vec3 cieLabFromCieXYZ(const in vec3 cieXYZ)\n" + "{\n" + " float fy = cieLabF(cieXYZ[1] / cieLabWhite[1]);\n" + " return vec3(\n" + " 1.16 * fy - 0.16,\n" + " 5.00 * (cieLabF(cieXYZ[0] / cieLabWhite[0]) - fy),\n" + " 2.00 * (fy - cieLabF(cieXYZ[2] / cieLabWhite[2])));\n" + "}\n" + "\n" + "\n" + "vec3 cieXYZFromCieLab(const in vec3 cielab)\n" + "{\n" + " float lf = (cielab[0] + 0.16) / 1.16;\n" + " return vec3(\n" + " cieLabWhite[0] * cieLabFInv(lf + cielab[1] / 5.00),\n" + " cieLabWhite[1] * cieLabFInv(lf),\n" + " cieLabWhite[2] * cieLabFInv(lf - cielab[2] / 2.00));\n" + "}\n" + "\n" + "\n" + "// Cie XYZ <-> Cie Luv\n" + "\n" + "vec3 cieLuvFromCieXYZ(const in vec3 cieXYZ)\n" + "{\n" + " const float wu = 0.197839825f;\n" + " const float wv = 0.468336303f;\n" + "\n" + " float l = (cieXYZ[1] <= 0.008856452f)?\n" + " 9.03296296296f * cieXYZ[1]:\n" + " 1.16f * pow(cieXYZ[1], 1.f/3.f) - .16f;\n" + " float d = cieXYZ[0] + 15.f * cieXYZ[1] + 3.f * cieXYZ[2];\n" + "\n" + " return vec3(\n" + " l,\n" + " (d > .001f)? 13.f * l * (4.f*cieXYZ[0] / d - wu): 0.f,\n" + " (d > .001f)? 13.f * l * (9.f*cieXYZ[1] / d - wv): 0.f);\n" + "}\n" + "\n" + "vec3 cieXYZFromCieLuv(const in vec3 cieluv)\n" + "{\n" + " const float wu = 0.197839825f;\n" + " const float wv = 0.468336303f;\n" + "\n" + " float up_13l = cieluv[1] + wu * (13.f * cieluv[0]);\n" + " float vp_13l = cieluv[2] + wv * (13.f * cieluv[0]);\n" + "\n" + " float y = (cieluv[0] <= .08f)?\n" + " cieluv[0] * 0.1107056f:\n" + " pow((cieluv[0]+.16f) / 1.16f, 3.f);\n" + " return vec3(\n" + " (vp_13l > .001f)? 2.25f * y * up_13l / vp_13l: 0.f,\n" + " y,\n" + " (vp_13l > .001f)? y * (156.f*cieluv[0] - 3.f*up_13l - 20.f*vp_13l) / (4.f * vp_13l): 0.f);\n" + "}\n" + "\n" + "\n" + "// General color conversion\n" + "\n" + "vec3 convertColor(in vec3 fromColor, in int from, in int to)\n" + "{\n" + " if(from == COLOR_NONE || to == COLOR_NONE || from == to)\n" + " return fromColor;\n" + "\n" + " vec3 color = fromColor;\n" + "\n" + " // To XYZ\n" + " if(from == COLOR_SRGB) {\n" + " color = linearRGBFromSrgb(color);\n" + " from = COLOR_LINEAR_RGB;\n" + " if(to == COLOR_LINEAR_RGB) return color;\n" + " }\n" + " if(from == COLOR_LINEAR_RGB) {\n" + " color = cieXYZFromLinearRGB(color);\n" + " }\n" + " if(from == COLOR_CIE_LAB) {\n" + " color = cieXYZFromCieLab(color);\n" + " }\n" + " if(from == COLOR_CIE_LUV) {\n" + " color = cieXYZFromCieLuv(color);\n" + " }\n" + "\n" + " // From XYZ\n" + " if(to < COLOR_CIE_XYZ) {\n" + " color = linearRGBFromCieXYZ(color);\n" + " if(to == COLOR_SRGB) {\n" + " color = srgbFromLinearRGB(color);\n" + " }\n" + " } else if(to == COLOR_CIE_LAB) {\n" + " color = cieLabFromCieXYZ(color);\n" + " } else if(to == COLOR_CIE_LUV) {\n" + " color = cieLuvFromCieXYZ(color);\n" + " }\n" + "\n" + " return color;\n" + "}\n" + "\n" + "\n" + ""; + +static const char* frag_linear_glsl = + "/*\n" + " This Source Code Form is subject to the terms of the Mozilla Public\n" + " License, v. 2.0. If a copy of the MPL was not distributed with this\n" + " file, You can obtain one at http://mozilla.org/MPL/2.0/.\n" + "*/\n" + "\n" + "#version 410 core\n" + "\n" + "uniform samplerBuffer nodes;\n" + "uniform int baseNodeIndex;\n" + "uniform bool singularTriangles;\n" + "uniform bool enableShading;\n" + "uniform int meshColorSpace;\n" + "uniform int screenColorSpace;\n" + "\n" + "flat in int frag_index;\n" + "in vec3 frag_linearBasis;\n" + "in vec4 frag_position_obj;\n" + "in vec3 frag_normal_obj;\n" + "in vec3 frag_normal_view;\n" + "flat in vec3 frag_vertices_obj[3];\n" + "flat in vec3 frag_normEdges_obj[3];\n" + "\n" + "out vec4 out_color;\n" + "\n" + "float irlerp(in vec3 vx, in vec3 v1, in vec3 v2);\n" + "vec4 quadraticInterp(in vec4 colors[6]);\n" + "float diffuse(in vec3 n, in vec3 l);\n" + "vec3 convertColor(in vec3 fromColor, in int from, in int to);\n" + "\n" + "int baseVxIndex = baseNodeIndex + frag_index * (3 + int(singularTriangles));\n" + "\n" + "vec4 linearInterp(in vec4 colors[3])\n" + "{\n" + " return\n" + " colors[0] * frag_linearBasis.x +\n" + " colors[1] * frag_linearBasis.y +\n" + " colors[2] * frag_linearBasis.z;\n" + "}\n" + "\n" + "void main(void)\n" + "{\n" + " vec4 colorNodes[] = vec4[3](\n" + " texelFetch(nodes, baseVxIndex + 0),\n" + " texelFetch(nodes, baseVxIndex + 1),\n" + " texelFetch(nodes, baseVxIndex + 2)\n" + " );\n" + "\n" + " if(singularTriangles)\n" + " {\n" + " colorNodes[0] = mix(colorNodes[0],\n" + " texelFetch(nodes, baseVxIndex + 3),\n" + " irlerp(normalize(frag_position_obj.xyz - frag_vertices_obj[0]),\n" + " frag_normEdges_obj[2], -frag_normEdges_obj[1]));\n" + " }\n" + "\n" + " out_color = linearInterp(colorNodes);\n" + "\n" + " if(enableShading) {\n" + " // Shading is done in linear RGB\n" + " out_color.rgb = convertColor(out_color.rgb, meshColorSpace, 2);\n" + "\n" + " vec3 n = normalize(frag_normal_view);\n" + " vec3 light = vec3(0.);\n" + " light = diffuse(n, normalize(vec3(-.2, 0, -1.))) * vec3(1., .9, .8) * .8\n" + " + diffuse(n, normalize(vec3( 1, .2, .2))) * vec3(.8, .9, 1.) * .6;\n" + "\n" + " out_color.rgb = convertColor(light * out_color.rgb,\n" + " 1, screenColorSpace);\n" + " } else {\n" + " out_color.rgb = convertColor(out_color.rgb, meshColorSpace, screenColorSpace);\n" + " }\n" + "}\n" + ""; + +static const char* frag_quadratic_glsl = + "/*\n" + " This Source Code Form is subject to the terms of the Mozilla Public\n" + " License, v. 2.0. If a copy of the MPL was not distributed with this\n" + " file, You can obtain one at http://mozilla.org/MPL/2.0/.\n" + "*/\n" + "\n" + "#version 410 core\n" + "\n" + "uniform samplerBuffer nodes;\n" + "uniform int baseNodeIndex;\n" + "uniform bool singularTriangles;\n" + "uniform bool enableShading;\n" + "uniform int meshColorSpace;\n" + "uniform int screenColorSpace;\n" + "\n" + "flat in int frag_index;\n" + "in vec3 frag_linearBasis;\n" + "in vec4 frag_position_obj;\n" + "in vec3 frag_normal_obj;\n" + "in vec3 frag_normal_view;\n" + "flat in vec3 frag_vertices_obj[3];\n" + "flat in vec3 frag_normEdges_obj[3];\n" + "\n" + "out vec4 out_color;\n" + "\n" + "float irlerp(in vec3 vx, in vec3 v1, in vec3 v2);\n" + "vec4 quadraticInterp(in vec4 colors[6]);\n" + "float diffuse(in vec3 n, in vec3 l);\n" + "vec3 convertColor(in vec3 fromColor, in int from, in int to);\n" + "\n" + "int baseVxIndex = baseNodeIndex + frag_index * (6 + int(singularTriangles));\n" + "int baseEdgeIndex = baseVxIndex + 3;\n" + "\n" + "void main(void)\n" + "{\n" + " vec4 colorNodes[] = vec4[6](\n" + " texelFetch(nodes, baseVxIndex + 0),\n" + " texelFetch(nodes, baseVxIndex + 1),\n" + " texelFetch(nodes, baseVxIndex + 2),\n" + " texelFetch(nodes, baseEdgeIndex + 0),\n" + " texelFetch(nodes, baseEdgeIndex + 1),\n" + " texelFetch(nodes, baseEdgeIndex + 2)\n" + " );\n" + "\n" + " if(singularTriangles)\n" + " {\n" + " colorNodes[0] = mix(\n" + " colorNodes[0],\n" + " texelFetch(nodes, baseVxIndex + 6),\n" + " irlerp(normalize(frag_position_obj.xyz - frag_vertices_obj[0]),\n" + " frag_normEdges_obj[2], -frag_normEdges_obj[1]));\n" + " }\n" + "\n" + " // Interpolation is done in srgb\n" + " out_color = quadraticInterp(colorNodes);\n" + "\n" + " if(enableShading) {\n" + " // Shading is done in linear RGB\n" + " out_color.rgb = convertColor(out_color.rgb, meshColorSpace, 2);\n" + "\n" + " vec3 n = normalize(frag_normal_view);\n" + " vec3 light = vec3(0.);\n" + " light = diffuse(n, normalize(vec3(-.2, 0, -1.))) * vec3(1., .9, .8) * .8\n" + " + diffuse(n, normalize(vec3( 1, .2, .2))) * vec3(.8, .9, 1.) * .6;\n" + "\n" + " out_color.rgb = convertColor(light * out_color.rgb,\n" + " 1, screenColorSpace);\n" + " } else {\n" + " out_color.rgb = convertColor(out_color.rgb, meshColorSpace, screenColorSpace);\n" + " }\n" + "}\n" + ""; + +static const char* frag_wireframe_glsl = + "/*\n" + " This Source Code Form is subject to the terms of the Mozilla Public\n" + " License, v. 2.0. If a copy of the MPL was not distributed with this\n" + " file, You can obtain one at http://mozilla.org/MPL/2.0/.\n" + "*/\n" + "\n" + "#version 410 core\n" + "\n" + "uniform samplerBuffer nodes;\n" + "uniform float lineWidth;\n" + "uniform vec4 wireframeColor;\n" + "\n" + "in vec3 frag_edgeDist_scr;\n" + "\n" + "out vec4 out_color;\n" + "\n" + "vec3 computeEdgeDist();\n" + "int minIndex(in vec3 dist);\n" + "float interpFactor(float dist, float radius);\n" + "\n" + "void main(void)\n" + "{\n" + " float alpha = smoothstep(\n" + " -0.5, 0.5,\n" + " lineWidth / 2.0 - min(frag_edgeDist_scr.x,\n" + " min(frag_edgeDist_scr.y,\n" + " frag_edgeDist_scr.z)));\n" + " if(alpha < 0.001)\n" + " discard;\n" + "\n" + " out_color = vec4(wireframeColor.rgb, wireframeColor.a * alpha);\n" + "}\n" + ""; + +} +} + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/ctrl_common.glsl b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/ctrl_common.glsl new file mode 100644 index 000000000..1f9c802da --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/ctrl_common.glsl @@ -0,0 +1,91 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#version 410 core + +layout(vertices = 9) out; + +uniform vec2 viewportSize; +uniform float smoothness; + +in vec4 ctrl_position_obj[]; +in vec3 ctrl_normal_obj[]; +in vec3 ctrl_normal_view[]; + +out vec4 eval_position_obj[]; +out vec3 eval_normal_obj[]; +out vec3 eval_normal_view[]; +out ScreenSpaceBlock { + flat vec3 heights_scr; + flat vec3 vertices_obj[3]; + flat vec3 normEdges_obj[3]; +} eval_ss[]; + +bool isLinear(int i) { + vec4 p0 = eval_position_obj[3*i + 0]; + vec4 p1 = eval_position_obj[3*i + 1]; + vec4 p2 = eval_position_obj[3*i + 2]; + vec4 p3 = eval_position_obj[(3*i + 3)%9]; + + vec4 p1off = p1 - (2*p0 + p3) / 3; + vec4 p2off = p2 - (p0 + 2*p3) / 3; + + return dot(p1off, p1off) < 0.0001 + && dot(p2off, p2off) < 0.0001; +} + +float curviness(int i) { + vec4 p0 = eval_position_obj[3*i + 0]; + vec4 p1 = eval_position_obj[3*i + 1]; + vec4 p2 = eval_position_obj[3*i + 2]; + vec4 p3 = eval_position_obj[(3*i + 3)%9]; + + vec4 p1off = p1 - (2*p0 + p3) / 3; + vec4 p2off = p2 - (p0 + 2*p3) / 3; + + return max(length(p1off), length(p2off)) / distance(p0, p3); +} + +void main(void) +{ + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + eval_position_obj[gl_InvocationID] = ctrl_position_obj[gl_InvocationID]; + eval_normal_obj[gl_InvocationID] = ctrl_normal_obj[gl_InvocationID]; + eval_normal_view[gl_InvocationID] = ctrl_normal_view[gl_InvocationID]; + + vec2 position_scr[3]; + for(int i=0; i<3; ++i) + { + position_scr[i] = (viewportSize * gl_in[i*3].gl_Position.xy) + / (2.0 * gl_in[i*3].gl_Position.z); + } + float area = abs(cross(vec3(position_scr[1] - position_scr[0], 0.0), + vec3(position_scr[2] - position_scr[0], 0.0)).z); + + for(int i=0; i<3; ++i) + { + eval_ss[gl_InvocationID].heights_scr[i] + = area / length(position_scr[(i+2)%3] - position_scr[(i+1)%3]); + eval_ss[gl_InvocationID].vertices_obj[i] + = ctrl_position_obj[i*3].xyz; + eval_ss[gl_InvocationID].normEdges_obj[i] + = normalize(ctrl_position_obj[((i+2)%3)*3].xyz - ctrl_position_obj[((i+1)%3)*3].xyz); + } + + bvec3 isEdgeLinear = bvec3(isLinear(0), isLinear(1), isLinear(2)); + gl_TessLevelOuter = float[4]( + isEdgeLinear[1]? 1: smoothness, + isEdgeLinear[2]? 1: smoothness, + isEdgeLinear[0]? 1: smoothness, + 1); + gl_TessLevelInner = float[2](all(isEdgeLinear)? 1: smoothness, 1); + // Uncomment this for adaptive smoothing (using a very rough heristic). +// gl_TessLevelOuter = float[4]( +// max(curviness(1) * smoothness, 1), +// max(curviness(2) * smoothness, 1), +// max(curviness(0) * smoothness, 1), +// 1); +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/eval_common.glsl b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/eval_common.glsl new file mode 100644 index 000000000..f864ac9d8 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/eval_common.glsl @@ -0,0 +1,66 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#version 410 core + +layout(triangles) in; + +uniform mat4 viewMatrix; + +uniform samplerBuffer nodes; +uniform int baseNodeIndex; +uniform bool singularTriangles; + +in vec4 eval_position_obj[]; +in vec3 eval_normal_obj[]; +in vec3 eval_normal_view[]; +in ScreenSpaceBlock { + flat vec3 heights_scr; + flat vec3 vertices_obj[3]; + flat vec3 normEdges_obj[3]; +} eval_ss[]; + +flat out int frag_index; +out vec3 frag_linearBasis; +out vec4 frag_position_obj; +out vec3 frag_normal_obj; +out vec3 frag_normal_view; +out vec3 frag_edgeDist_scr; +flat out vec3 frag_vertices_obj[3]; +flat out vec3 frag_normEdges_obj[3]; + +void main(void) +{ + vec3 c1 = gl_TessCoord; + vec3 c2 = c1 * c1; + vec3 c3 = c2 * c1; + vec4 mid = vec4(0); + for(int i = 0; i < 9; ++i) mid += eval_position_obj[i]; + mid /= 9; + frag_position_obj = eval_position_obj[0] * c3[0] + + eval_position_obj[1] * c2[0] * c1[1] * 3 + + eval_position_obj[2] * c1[0] * c2[1] * 3 + + eval_position_obj[3] * c3[1] + + eval_position_obj[4] * c2[1] * c1[2] * 3 + + eval_position_obj[5] * c1[1] * c2[2] * 3 + + eval_position_obj[6] * c3[2] + + eval_position_obj[7] * c2[2] * c1[0] * 3 + + eval_position_obj[8] * c1[2] * c2[0] * 3 + + mid * c1[0] * c1[1] * c1[2] * 6; + + gl_Position = viewMatrix * frag_position_obj; + frag_index = gl_PrimitiveID; + frag_linearBasis = gl_TessCoord; + frag_normal_obj = mat3(eval_normal_obj[0], + eval_normal_obj[3], + eval_normal_obj[6]) * gl_TessCoord; + frag_normal_view = mat3(eval_normal_view[0], + eval_normal_view[3], + eval_normal_view[6]) * gl_TessCoord; + frag_edgeDist_scr = eval_ss[0].heights_scr * gl_TessCoord; + frag_vertices_obj = eval_ss[0].vertices_obj; + frag_normEdges_obj = eval_ss[0].normEdges_obj; +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/frag_common.glsl b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/frag_common.glsl new file mode 100644 index 000000000..562cfc8f3 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/frag_common.glsl @@ -0,0 +1,226 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#version 410 core + +uniform float zoom; +uniform float pointRadius; +uniform float halfLineWidth; +uniform bool showWireframe; +uniform vec4 wireframeColor; +uniform vec4 pointColor; + +in vec3 frag_linearBasis; +in vec2 position; +flat in vec2 vertices[3]; +flat in vec2 normEdges[3]; + + +float irlerp(in vec3 vx, in vec3 v1, in vec3 v2) +{ + float alpha = acos(clamp(dot(v1, vx), -1., 1.)); + float beta = acos(clamp(dot(v1, v2), -1., 1.)); + return alpha / beta; +} + + +vec4 quadraticInterp(in vec4 colors[6]) +{ + return + colors[0] * frag_linearBasis.x * (2. * frag_linearBasis.x - 1.) + + colors[1] * frag_linearBasis.y * (2. * frag_linearBasis.y - 1.) + + colors[2] * frag_linearBasis.z * (2. * frag_linearBasis.z - 1.) + + colors[3] * 4. * frag_linearBasis.y * frag_linearBasis.z + + colors[4] * 4. * frag_linearBasis.z * frag_linearBasis.x + + colors[5] * 4. * frag_linearBasis.x * frag_linearBasis.y; +} + +float diffuse(in vec3 n, in vec3 l) +{ + return clamp(dot(n, l), 0., 1.); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Color space conversion + +const int COLOR_NONE = 0; +const int COLOR_SRGB = 1; +const int COLOR_LINEAR_RGB = 2; +const int COLOR_CIE_XYZ = 3; +const int COLOR_CIE_LAB = 4; +const int COLOR_CIE_LUV = 5; + + +// SRGB <-> linear rgbToxyz + +vec3 srgbFromLinearRGB(const in vec3 linear) +{ + vec3 srgb = linear; + srgb[0] = (linear[0] > 0.0031308)? + 1.055 * pow(linear[0], 1./2.4) - .055f: + 12.92 * linear[0]; + srgb[1] = (linear[1] > 0.0031308)? + 1.055 * pow(linear[1], 1./2.4) - .055f: + 12.92 * linear[1]; + srgb[2] = (linear[2] > 0.0031308)? + 1.055 * pow(linear[2], 1./2.4) - .055f: + 12.92 * linear[2]; + return srgb; +} + +vec3 linearRGBFromSrgb(const in vec3 srgb) +{ + vec3 linear = srgb; + linear[0] = (linear[0] > 0.04045)? + pow((linear[0]+0.055) / 1.055, 2.4): + linear[0] / 12.92; + linear[1] = (linear[1] > 0.04045)? + pow((linear[1]+0.055) / 1.055, 2.4): + linear[1] / 12.92; + linear[2] = (linear[2] > 0.04045)? + pow((linear[2]+0.055) / 1.055, 2.4): + linear[2] / 12.92; + return linear; +} + + +// Linear RGB <-> Cie XYZ + +const mat3 xyzToRgb = mat3( + 3.2406, -1.5372, -0.4986, + -0.9689, 1.8758, 0.0415, + 0.0557, -0.2040, 1.0570 +); + +vec3 linearRGBFromCieXYZ(const in vec3 cieXYZ) { + return transpose(xyzToRgb) * cieXYZ; +} + +mat3 rgbToxyz = mat3( + 0.4124, 0.3576, 0.1805, + 0.2126, 0.7152, 0.0722, + 0.0193, 0.1192, 0.9505 +); + +vec3 cieXYZFromLinearRGB(const in vec3 lrgb) { + return transpose(rgbToxyz) * lrgb; +} + + +// Cie XYZ <-> Cie Lab + +float cieLabF(const in float v) { + return (v > 0.008856452)? + pow(v, 1./3.): + 1. / (3. * 0.042806183) * v + (4. / 29.); +} + +float cieLabFInv(const in float v) { + return (v > 0.206896552)? + pow(v, 3.): + 3. * 0.042806183 * (v - (4. / 29.)); +} + +const vec3 cieLabWhite = vec3(0.95047, 1, 1.08883); + +vec3 cieLabFromCieXYZ(const in vec3 cieXYZ) +{ + float fy = cieLabF(cieXYZ[1] / cieLabWhite[1]); + return vec3( + 1.16 * fy - 0.16, + 5.00 * (cieLabF(cieXYZ[0] / cieLabWhite[0]) - fy), + 2.00 * (fy - cieLabF(cieXYZ[2] / cieLabWhite[2]))); +} + + +vec3 cieXYZFromCieLab(const in vec3 cielab) +{ + float lf = (cielab[0] + 0.16) / 1.16; + return vec3( + cieLabWhite[0] * cieLabFInv(lf + cielab[1] / 5.00), + cieLabWhite[1] * cieLabFInv(lf), + cieLabWhite[2] * cieLabFInv(lf - cielab[2] / 2.00)); +} + + +// Cie XYZ <-> Cie Luv + +vec3 cieLuvFromCieXYZ(const in vec3 cieXYZ) +{ + const float wu = 0.197839825f; + const float wv = 0.468336303f; + + float l = (cieXYZ[1] <= 0.008856452f)? + 9.03296296296f * cieXYZ[1]: + 1.16f * pow(cieXYZ[1], 1.f/3.f) - .16f; + float d = cieXYZ[0] + 15.f * cieXYZ[1] + 3.f * cieXYZ[2]; + + return vec3( + l, + (d > .001f)? 13.f * l * (4.f*cieXYZ[0] / d - wu): 0.f, + (d > .001f)? 13.f * l * (9.f*cieXYZ[1] / d - wv): 0.f); +} + +vec3 cieXYZFromCieLuv(const in vec3 cieluv) +{ + const float wu = 0.197839825f; + const float wv = 0.468336303f; + + float up_13l = cieluv[1] + wu * (13.f * cieluv[0]); + float vp_13l = cieluv[2] + wv * (13.f * cieluv[0]); + + float y = (cieluv[0] <= .08f)? + cieluv[0] * 0.1107056f: + pow((cieluv[0]+.16f) / 1.16f, 3.f); + return vec3( + (vp_13l > .001f)? 2.25f * y * up_13l / vp_13l: 0.f, + y, + (vp_13l > .001f)? y * (156.f*cieluv[0] - 3.f*up_13l - 20.f*vp_13l) / (4.f * vp_13l): 0.f); +} + + +// General color conversion + +vec3 convertColor(in vec3 fromColor, in int from, in int to) +{ + if(from == COLOR_NONE || to == COLOR_NONE || from == to) + return fromColor; + + vec3 color = fromColor; + + // To XYZ + if(from == COLOR_SRGB) { + color = linearRGBFromSrgb(color); + from = COLOR_LINEAR_RGB; + if(to == COLOR_LINEAR_RGB) return color; + } + if(from == COLOR_LINEAR_RGB) { + color = cieXYZFromLinearRGB(color); + } + if(from == COLOR_CIE_LAB) { + color = cieXYZFromCieLab(color); + } + if(from == COLOR_CIE_LUV) { + color = cieXYZFromCieLuv(color); + } + + // From XYZ + if(to < COLOR_CIE_XYZ) { + color = linearRGBFromCieXYZ(color); + if(to == COLOR_SRGB) { + color = srgbFromLinearRGB(color); + } + } else if(to == COLOR_CIE_LAB) { + color = cieLabFromCieXYZ(color); + } else if(to == COLOR_CIE_LUV) { + color = cieLuvFromCieXYZ(color); + } + + return color; +} + + diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/frag_linear.glsl b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/frag_linear.glsl new file mode 100644 index 000000000..7408594cb --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/frag_linear.glsl @@ -0,0 +1,73 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#version 410 core + +uniform samplerBuffer nodes; +uniform int baseNodeIndex; +uniform bool singularTriangles; +uniform bool enableShading; +uniform int meshColorSpace; +uniform int screenColorSpace; + +flat in int frag_index; +in vec3 frag_linearBasis; +in vec4 frag_position_obj; +in vec3 frag_normal_obj; +in vec3 frag_normal_view; +flat in vec3 frag_vertices_obj[3]; +flat in vec3 frag_normEdges_obj[3]; + +out vec4 out_color; + +float irlerp(in vec3 vx, in vec3 v1, in vec3 v2); +vec4 quadraticInterp(in vec4 colors[6]); +float diffuse(in vec3 n, in vec3 l); +vec3 convertColor(in vec3 fromColor, in int from, in int to); + +int baseVxIndex = baseNodeIndex + frag_index * (3 + int(singularTriangles)); + +vec4 linearInterp(in vec4 colors[3]) +{ + return + colors[0] * frag_linearBasis.x + + colors[1] * frag_linearBasis.y + + colors[2] * frag_linearBasis.z; +} + +void main(void) +{ + vec4 colorNodes[] = vec4[3]( + texelFetch(nodes, baseVxIndex + 0), + texelFetch(nodes, baseVxIndex + 1), + texelFetch(nodes, baseVxIndex + 2) + ); + + if(singularTriangles) + { + colorNodes[0] = mix(colorNodes[0], + texelFetch(nodes, baseVxIndex + 3), + irlerp(normalize(frag_position_obj.xyz - frag_vertices_obj[0]), + frag_normEdges_obj[2], -frag_normEdges_obj[1])); + } + + out_color = linearInterp(colorNodes); + + if(enableShading) { + // Shading is done in linear RGB + out_color.rgb = convertColor(out_color.rgb, meshColorSpace, 2); + + vec3 n = normalize(frag_normal_view); + vec3 light = vec3(0.); + light = diffuse(n, normalize(vec3(-.2, 0, -1.))) * vec3(1., .9, .8) * .8 + + diffuse(n, normalize(vec3( 1, .2, .2))) * vec3(.8, .9, 1.) * .6; + + out_color.rgb = convertColor(light * out_color.rgb, + 1, screenColorSpace); + } else { + out_color.rgb = convertColor(out_color.rgb, meshColorSpace, screenColorSpace); + } +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/frag_quadratic.glsl b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/frag_quadratic.glsl new file mode 100644 index 000000000..fb3bdfd6c --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/frag_quadratic.glsl @@ -0,0 +1,71 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#version 410 core + +uniform samplerBuffer nodes; +uniform int baseNodeIndex; +uniform bool singularTriangles; +uniform bool enableShading; +uniform int meshColorSpace; +uniform int screenColorSpace; + +flat in int frag_index; +in vec3 frag_linearBasis; +in vec4 frag_position_obj; +in vec3 frag_normal_obj; +in vec3 frag_normal_view; +flat in vec3 frag_vertices_obj[3]; +flat in vec3 frag_normEdges_obj[3]; + +out vec4 out_color; + +float irlerp(in vec3 vx, in vec3 v1, in vec3 v2); +vec4 quadraticInterp(in vec4 colors[6]); +float diffuse(in vec3 n, in vec3 l); +vec3 convertColor(in vec3 fromColor, in int from, in int to); + +int baseVxIndex = baseNodeIndex + frag_index * (6 + int(singularTriangles)); +int baseEdgeIndex = baseVxIndex + 3; + +void main(void) +{ + vec4 colorNodes[] = vec4[6]( + texelFetch(nodes, baseVxIndex + 0), + texelFetch(nodes, baseVxIndex + 1), + texelFetch(nodes, baseVxIndex + 2), + texelFetch(nodes, baseEdgeIndex + 0), + texelFetch(nodes, baseEdgeIndex + 1), + texelFetch(nodes, baseEdgeIndex + 2) + ); + + if(singularTriangles) + { + colorNodes[0] = mix( + colorNodes[0], + texelFetch(nodes, baseVxIndex + 6), + irlerp(normalize(frag_position_obj.xyz - frag_vertices_obj[0]), + frag_normEdges_obj[2], -frag_normEdges_obj[1])); + } + + // Interpolation is done in srgb + out_color = quadraticInterp(colorNodes); + + if(enableShading) { + // Shading is done in linear RGB + out_color.rgb = convertColor(out_color.rgb, meshColorSpace, 2); + + vec3 n = normalize(frag_normal_view); + vec3 light = vec3(0.); + light = diffuse(n, normalize(vec3(-.2, 0, -1.))) * vec3(1., .9, .8) * .8 + + diffuse(n, normalize(vec3( 1, .2, .2))) * vec3(.8, .9, 1.) * .6; + + out_color.rgb = convertColor(light * out_color.rgb, + 1, screenColorSpace); + } else { + out_color.rgb = convertColor(out_color.rgb, meshColorSpace, screenColorSpace); + } +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/frag_wireframe.glsl b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/frag_wireframe.glsl new file mode 100644 index 000000000..3b9a174f4 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/frag_wireframe.glsl @@ -0,0 +1,32 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#version 410 core + +uniform samplerBuffer nodes; +uniform float lineWidth; +uniform vec4 wireframeColor; + +in vec3 frag_edgeDist_scr; + +out vec4 out_color; + +vec3 computeEdgeDist(); +int minIndex(in vec3 dist); +float interpFactor(float dist, float radius); + +void main(void) +{ + float alpha = smoothstep( + -0.5, 0.5, + lineWidth / 2.0 - min(frag_edgeDist_scr.x, + min(frag_edgeDist_scr.y, + frag_edgeDist_scr.z))); + if(alpha < 0.001) + discard; + + out_color = vec4(wireframeColor.rgb, wireframeColor.a * alpha); +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/geom_common.glsl b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/geom_common.glsl new file mode 100644 index 000000000..3fdec6607 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/geom_common.glsl @@ -0,0 +1,61 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#version 410 core + +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +uniform vec2 viewportSize; + +in vec4 geom_position_obj[]; +in vec3 geom_normal_obj[]; +in vec3 geom_normal_view[]; + +flat out int frag_index; +out vec3 frag_linearBasis; +out vec3 frag_position_obj; +out vec3 frag_normal_obj; +out vec3 frag_normal_view; +out vec3 frag_edgeDist_scr; +flat out vec3 frag_vertices_obj[3]; +flat out vec3 frag_normEdges_obj[3]; + +const vec3 basis[3] = vec3[3]( + vec3(1, 0, 0), + vec3(0, 1, 0), + vec3(0, 0, 1) +); + +void main() +{ + vec2 position_scr[3]; + for(int i=0; i<3; ++i) + { + position_scr[i] = (viewportSize * gl_in[i].gl_Position.xy) + / (2.0 * gl_in[i].gl_Position.z); + } + float area = abs(cross(vec3(position_scr[1] - position_scr[0], 0.0), + vec3(position_scr[2] - position_scr[0], 0.0)).z); + for(int i=0; i<3; ++i) + { + gl_Position = gl_in[i].gl_Position; + frag_index = gl_PrimitiveIDIn; + frag_linearBasis = basis[i]; + frag_position_obj = geom_position_obj[i].xyz; + frag_normal_obj = geom_normal_obj[i]; + frag_normal_view = geom_normal_view[i]; + frag_edgeDist_scr = vec3(0.0); + frag_edgeDist_scr[i] = area / length(position_scr[(i+2)%3] - position_scr[(i+1)%3]); + for(int j=0; j<3; ++j) + { + frag_vertices_obj[j] = geom_position_obj[j].xyz;//gl_in[j].gl_Position.xy; + frag_normEdges_obj[j] = normalize(geom_position_obj[(j+2)%3].xyz + - geom_position_obj[(j+1)%3].xyz); + } + EmitVertex(); + } +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/vert_common.glsl b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/vert_common.glsl new file mode 100644 index 000000000..5ef38b7e9 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/Utils/vgMeshRendererShaders/vert_common.glsl @@ -0,0 +1,25 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#version 410 core + +uniform mat4 viewMatrix; +uniform mat3 normalMatrix; + +in vec4 vx_position; +in vec3 vx_normal; + +out vec4 ctrl_position_obj; +out vec3 ctrl_normal_obj; +out vec3 ctrl_normal_view; + +void main(void) +{ + gl_Position = viewMatrix * vx_position; + ctrl_position_obj = vx_position; + ctrl_normal_obj = vx_normal; + ctrl_normal_view = normalMatrix * vx_normal; +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte.bib b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte.bib new file mode 100644 index 000000000..3ad1552db --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte.bib @@ -0,0 +1,23 @@ +@InProceedings\{Orzan:2008:DiffusionCurves, + author = "Orzan, Alexandrina and Bousseau, Adrien and Winnem{\"o}ller, Holger and Barla, Pascal and Thollot, Jo{\"e}lle and Salesin, David", + title = "Diffusion Curves: A Vector Representation for Smooth-Shaded Images", + booktitle = "ACM Transactions on Graphics (Proceedings of SIGGRAPH 2008)", + volume = "27", + year = "2008", + url = "http://maverick.inria.fr/Publications/2008/OBWBTS08" +} + +@article{Boye:2012:VectorialSolver, +author = {Boy\'{e}, Simon and Barla, Pascal and Guennebaud, Ga\"{e}l}, +doi = {10.1145/2366145.2366192}, +issn = {07300301}, +journal = {ACM Transactions on Graphics}, +keywords = {diffusion curves,finite elements,vector graphics}, +month = nov, +number = {6}, +pages = {1}, +title = {{A vectorial solver for free-form vector gradients}}, +url = {http://dl.acm.org/citation.cfm?doid=2366145.2366192}, +volume = {31}, +year = {2012} +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte.mdoc new file mode 100644 index 000000000..949f6ddf9 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte.mdoc @@ -0,0 +1,77 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +/** + * \brief This module provides tools to generate and manipulate mesh-based + * vector gradients. + */ +namespace Vitelotte +{ + +///** +// * \brief Concepts used in Vitelotte. +// */ +//namespace Concept +//{ + +// TODO: Update this to match real code + +///** +// * \brief ElementConcept represents an interpolation function over a +// * face. +// */ +//template < class _Mesh, typename _Scalar > +//class ElementConcept +//{ +//} + + +///** +// * \brief Describe the procedure to compute the stiffness matrix of an element. +// */ +//template < class _Mesh, typename _Scalar > +//class ElementBuilderConcept +//{ +//public: +// /** +// * \brief The scalar type used by the FEM solver. +// * +// * May be different from the scalar type used by the Mesh. +// */ +// typedef _Scalar Scalar; + +// /** +// * \brief The type of the mesh containing the elements. +// */ +// typedef _Mesh Mesh; + +//public: +// /** +// * \brief Return the number of coefficients in the element stiffness +// * matrix. +// * +// * The returned value *must* be the number of coefficients pushed by +// * addCoefficients(). +// */ +// unsigned nCoefficients(const Mesh& mesh, Face element) const; + +// /** +// * \brief Compute the coefficients of the element stiffness matrix and +// * add them to `it`. +// * +// * Note that the number of coefficients pushed *must* be the value returned +// * by nCoefficients(). +// */ +// template < typename InIt > +// void addCoefficients(InIt& it, const Mesh& mesh, Face element) const; + +//}; + + + +//} + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example.mdoc new file mode 100644 index 000000000..2586aa48a --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example.mdoc @@ -0,0 +1,21 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +namespace Vitelotte { + +/*! + \page vitelotte_example_page Vitelotte examples + + Here is the structure of Vitelotte examples: + - \ref vitelotte_example_common_page : contains common bits of code used by other examples. + - \ref vitelotte_example_mvgtk_page : A command line tool who gives access to the high-level functionalities of the Vitelotte module. + - \ref vitelotte_example_mvg_viewer_page : A simple MVG viewer based on OpenGL and SFML. + - \ref vitelotte_example_mvg_editor_page : An MVG editor based on Qt. Allows to edit a diffusion image using high-level constraints representation provided by DCMesh or to edit nodes directly. + + */ + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_common.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_common.mdoc new file mode 100644 index 000000000..70192b476 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_common.mdoc @@ -0,0 +1,20 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +/*! + \page vitelotte_example_common_page Vitelotte common utilities + + The common directory under the example/vitelotte directory contains pieces of code used by several examples. + + This directory contains: + - GLLineRenderer and GLPointRenderer respectively renders lines and points in OpenGL. They use custom shader to produce high quality result with antialiasing. + - VGNodeRenderer renders a wireframe version of the mesh where constrained edges are bold and doubled if they represent a value discontinuity, and also display nodes per edges. It can be used to have a better understanding of the connectivity of a mesh, making it a useful debugging tool. This class rely on GLLineRenderer and GLPointRenderer and is used by examples doing rendering. + - plotObj.h contains a function that convert a 2D mvg file in a 3D plot. Used by the plot command of mvgtk and useful for debugging. + - textFormatter.h contains a simple function to break paragraph into multiple lines. Used by mvgtk for help display. + - shaders.cpp contains the shaders required by the various OpenGL classes in this directory. It is an automatically generated file that regroup the content of the shaders directory in a single .cpp. + +*/ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_mvg_editor.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_mvg_editor.mdoc new file mode 100644 index 000000000..d3e409bef --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_mvg_editor.mdoc @@ -0,0 +1,20 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +/*! + \page vitelotte_example_mvg_editor_page MVG Editor + + `mvg_editor` is a graphical application designed to edit mvg files. It depends on Qt. + + There are two modes of interaction : + + - **Curve mode :** If the mesh contains curves information, this mode permits to edit constraints at a high level. Left click to select curve and point constraints. When a curve is selected, you can edit color gradients defined over them. Left click and drag on a color stop to move it, left click on it to change its color, right click to remove it. Left click near the selected curve away from a color stop add a new one. + - **Node mode :** This mode lets you edit the mesh at its lowest level. + + Code of this example is too involved to be described here. However some parts are still interesting. The best place to look at is the Document class (`document.h` and `document.cpp`). Particularly, the commands classes implement all the operations done by the GUI (this is part of the undo mechanism). + + */ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_mvg_viewer.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_mvg_viewer.mdoc new file mode 100644 index 000000000..e349282b2 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_mvg_viewer.mdoc @@ -0,0 +1,29 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +/*! + \page vitelotte_example_mvg_viewer_page MVG Viewer + + `mvg_viewer` is a simple graphical application that displays a mvg file. It requires the GLFW3 library. To use it, just run + + \code + mvg_viewer + \endcode + + If the mesh contains invalid or unknown nodes, they will appear as black. + + You can zoom with the mouse wheel, pan the view with the left click and rotate around 3D meshes with the right click. + + There are 5 view modes that can be changed with the number keys: + + - 1: Default view, just display the image. + - 2: Display wireframe on top of the image. Width of the lines can be changed with the numpad '+' and '-' keys. + - 3: Display nodes (with VGNodeRenderer) on top of the image (may be slow). + - 4: Wireframe only. + - 5: Nodes only. + + */ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_mvgtk.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_mvgtk.mdoc new file mode 100644 index 000000000..f80c9a909 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_mvgtk.mdoc @@ -0,0 +1,121 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +namespace Vitelotte { + +/*! + \page vitelotte_example_mvgtk_page MVG Toolkit + + \section vitelotte_example_mvgtk_introduction_sec Introduction + + The MVG Toolkit, aka. mvgtk is more than a simple example, it is a command-line interface to the Vitelotte library. It makes high level operations on mvg files possible without writing C++ code. + + Here is a typical use of mvgtk: + \code + $ mvgtk -v my_file.mvg conv fv finalize solve out out.mvg + Load mvg "my_file.mvg"... + Set attributes to 271... + Finalize... + Solve... + Biharmonic quadratic diffusion. + Output "out.mvg"... + \endcode + + Mvgtk first take a list of global options, in this case -v that enables the verbose mode (mvgtk does not print anything without it, unless there is an error). Then you must specify the input file. Note that beside mvg files, mvgtk can load .obj files that will be treated as 3D mvg with no attributes. The rest of the command line is a sequence of command (with options) that are executed in order on the mesh. Most commands modify the mesh for the subsequent operations. Here is what the above line is doing: + 1. The conv fv command (convert) sets the mesh attributes to represent FV elements. In practice, just call \c VGMesh::setAttributes(VGMesh::FV_FLAGS). + 2. The finalize command finalizes the mesh (see \ref vitelotte_user_manual_vg_mesh_finalize_subsec). Just call VGMesh::finalize() as you may guess. + 3. The solve command solves the diffusion. The code of this command is a little bit more involved as it has to create the right solver class depending on the mesh attributes, call the right sequence of methods on it and do some error handling. + 4. Finally the out out.mvg command outputs the resulting mesh in the file out.mvg. This is the only command of the line that does not modify the mesh. Note that it is possible to use this command several times, for instance to output intermediate results. + + The complete list of commands and options can always be found by calling mvgtk --help. + + + \section vitelotte_example_mvgtk_options_sec Global options + + - -h, --help: print help and exit. + - -v, --verbose: verbose mode. + + + \section vitelotte_example_mvgtk_commands_sec Commands + + \subsection vitelotte_example_mvgtk_check_command_sub Check command + + The check command does some sanity check on the mesh. Potential problems are printed on the standard output. Subsequent commands are only executed if no error was found. + + This command takes no parameter, but outputs a more detailed diagnostic in verbose mode. + + Note that this command does not check exhaustively for all possible inconsistencies in the mesh; hence its success is no guarantee that subsequent commands will work. + + + \subsection vitelotte_example_mvgtk_compact_command_sub Compact command + + The compact command removes all unused nodes in the mesh (nodes not referenced by an element), making it more compact. This command takes no options. + + The implementation of this command is straightforward: + \include Vitelotte/mvgtk/compactCommand.cpp + + + \subsection vitelotte_example_mvgtk_convert_command_sub Convert command + + The convert ATTR (or conv) command permits to change the attributes of the mesh, thus effectively converting the mesh to a new kind of element. The mandatory parameter ATTR describes the new set of attributes. It must be one of the following: + - none: removes all attributes. + - linear: set attributes to represent linear elements. See VGMesh::LINEAR_FLAGS. + - quadratic: set attributes to represent quadratic elements. See VGMesh::QUADRATIC_FLAGS. + - morley: set attributes to represent morley elements. See VGMesh::MORLEY_FLAGS. + - fv: set attributes to represent FV elements. See VGMesh::FV_FLAGS. + + If you use convert to remove attributes from the mesh, it may be worth calling compact afterward to remove no longer used nodes as this is not done automatically. + + \include Vitelotte/mvgtk/convertCommand.cpp + + + \subsection vitelotte_example_mvgtk_c2n_command_sub Curves-to-nodes command + + The curves-to-nodes (or c2n) command sets nodes constraints according to curves and point constraints (those are high level constraints defined in the DCMesh class). This command first removes all nodes. Note that the resulting mesh will only have constrained nodes set, so you probably want to call finalize afterward. + + \include Vitelotte/mvgtk/curvesToNodesCommand.cpp + + + \subsection vitelotte_example_mvgtk_finalize_command_sub Finalize command + + The finalize command finalizes the mesh. See \ref vitelotte_user_manual_vg_mesh_finalize_subsec for a detailed explanation of the finalization mechanism. + + The code is as simple as it can be: + \include Vitelotte/mvgtk/finalizeCommand.cpp + + + \subsection vitelotte_example_mvgtk_output_command_sub Output command + + The output FILENAME (or out) command output the mesh in FILENAME using the MVG file format. + + + \subsection vitelotte_example_mvgtk_plot_command_sub Plot command + + The plot COEFF SUBDIV FILENAME command outputs the mesh in the file FILENAME as a .obj. The \c x and \c y vertex coordinates are not modified and the \c z coordinate is the value of the coefficient COEFF. For instance, to visualize the red channel of the diffusion curve image as a height field, you should use a value of 0 for COEFF. The SUBDIV parameter permits to control the level of subdivision of each element in the plot. The mesh must be 2D. + + Note that elements subdivision depends on the current mesh attributes. If the mesh represents FV element, the plot will show FV elements (which produce discontinuities at their boundaries). You can use convert before calling plot to change the elements used. + + \include Vitelotte/mvgtk/plotCommand.cpp + + + \subsection vitelotte_example_mvgtk_simplify_command_sub Simplify command + + The simplify (or simp) command simplifies a mesh by removing the nodes that can be reconstructed with finalize. It can be used to reduce file size before output. + + \include Vitelotte/mvgtk/simplifyCommand.cpp + + + \subsection vitelotte_example_mvgtk_solve_command_sub Solve command + + The solve command applies the FEM solver on the input mesh to compute values of the unknown node. For more information about how the solver works, including what kind of input it expects, see \ref vitelotte_user_manual_fem_solver_page. + + \include Vitelotte/mvgtk/solveCommand.cpp + + + */ + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_plot_obj.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_plot_obj.mdoc new file mode 100644 index 000000000..6355b4a33 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_plot_obj.mdoc @@ -0,0 +1,13 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +/*! + \page vitelotte_example_plot_obj_page OBJ Plotter example + + \todo vitelotte_example_plot_obj_page + + */ \ No newline at end of file diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_vg_node_renderer.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_vg_node_renderer.mdoc new file mode 100644 index 000000000..61a734c15 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_example_vg_node_renderer.mdoc @@ -0,0 +1,13 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +/*! + \page vitelotte_example_vg_node_renderer_page NodeRenderer example + + \todo vitelotte_example_vg_node_renderer_page + + */ \ No newline at end of file diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual.mdoc new file mode 100644 index 000000000..c5b7e9794 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual.mdoc @@ -0,0 +1,76 @@ +namespace Vitelotte +{ +/*! + \page vitelotte_user_manual_page User Manual + + \authors Simon Boyé, Gautier Ciaudo, Gael Guennebaud, Pascal Barla + + \section vitelotte_user_manual_intro Introduction + + The Vitelotte module provides tools to smoothly interpolate a set of constraints on a mesh. Its primary intent is to provide an efficient solver to create [diffusion curves images](@ref vitelotte_user_manual_diffusion_images_page) \cite Orzan:2008:DiffusionCurves. It includes a FEM-based diffusion solver that is able to do harmonic and biharmonic interpolation on triangular meshes with either linear or quadratic color interpolation over each face \cite Boye:2012:VectorialSolver. + + \image html vitelotte/vitelotte_overview.svg "Vitelotte takes a mesh with constraints along a curve as input (left) and produce a mesh with colors interpolated over each face (right)." + + The diffusion approach offers several advantages compared to methods currently used to produce smooth arbitrary color gradients, like Gradient Meshes. Diffusion curves do not enforce a regular topology, which permits to represent similar gradients with less control points. This leads to easier to manipulate and more lightweight images. However, diffusion curves require to solve a costly equation to produce the final image. + + Vitelotte represents images as arbitrary meshes with color interpolation over each face. This representation is easy to rasterize and scale independent, which makes it perfect to store the result of the diffusion. In facts, as our solver uses the finite element method, it also takes a mesh as input that encode the constraints and the structure of the final image. + + This approach has several advantages: + + - **Scale independence**: important features, like discontinuities, are represented exactly with curved edges while color functions are smooth at any scale. + - **Efficient rendering**: modern GPU excel at rendering polygons, which enable very fast rendering. Vitelotte contains an OpenGL 4 renderer that supports all the features of our meshes (curved edges, singularities, ...). + - **Arbitrary dimensions**: our solver can work on surfaces of any dimension. So, you can just use a planar 2D mesh but also diffuse color on 3D surface meshes. + - **Memory tradeoff**: the result of the diffusion can be a bit heavy with dense meshes. If data size matters, you may wish to store the mesh and the constraint and recompute the diffusion when required, which permits to trade CPU time for memory. + + Note that you are not constrained to diffuse colors with Vitelotte. You can also interpolate normals, displacement vectors or anything else that can be represented as a vectors. + + + \section vitelotte_user_manual_about About this manual + + This user manual is best read in order. The tutorial introduces some key concepts and the [VGMesh](@ref vitelotte_user_manual_vg_mesh_page) page details the VGMesh class, which is central to Vitelotte. The rest of the manual does not need to be read in order, but requires a good understanding of VGMesh. + + + \section vitelotte_user_manual_overview_library Library overview + + Here is a quick overview of the main elements of Vitelotte. For a more in-depth introduction, see [the tutorial](@ref vitelotte_user_manual_tutorial_page). + + At the heart of Vitelotte is the VGMesh class. It is a mesh representation that contains per-face attributes used for color interpolation. It has been designed to be easily extensible and can support various color interpolation schemes. It can also represent a mesh with some color constraints to use as solver input. The [DCMesh](@ref vitelotte_user_manual_dc_mesh_page) class is an extension that incorporates higher-level diffusion curve constraints. + + The [FemSolver](@ref vitelotte_user_manual_fem_solver_page) class implements a diffusion solver. It can do harmonic or biharmonic color diffusion over a VGMesh with unknown colors. It supports different kinds of elements, enabling either linear or quadratic color interpolation over the final image. + + The [VGMeshRenderer](@ref vitelotte_user_manual_rendering_page) permits to render a VGMesh with linear or quadratic color interpolation, providing basic OpenGL routines to speed-up implementation. + + MVGReader and MVGWriter work together to save and load a VGMesh in the [MVG file format](@ref vitelotte_user_manual_mvg_file_format_page). It allows users to save meshes at any processing step (i.e., before or after a solve), which can be useful for debugging purposes. + + There are several header to include Vitelotte: + \code + // include everything but OpenGL classes. + #include + + // include OpenGL-based rendering classes, see renderer documentation. + #include + \endcode + + There is currently no easy way to manipulate the topology of a VGMesh, except the low-level mesh manipulation methods that do not preserve constraints. Don't worry, it is planned for the next release which should come very soon. + + + \section vitelotte_user_manual_overview_tools Tools overview + + Vitelotte also comes with a small set of tools that offer a high-level interface to the library: + + - The [mvg toolkit](@ref vitelotte_example_mvgtk_page) (`mvgtk`). Permits to do high level operations on DCMesh. Example: + \code{.sh} + # poring.mvg is a mvg with diffusion curves and point gradient constraints. + # Available from the examples/Vitelotte/samples directory. + # This command generates an image with quadratic interpolation using + # a biharmonic diffusion. + mvgtk -v poring.mvg conv fv c2n finalize solve out out.mvg + \endcode + + See [mvgtk's documentation](@ref vitelotte_example_mvgtk_page) for a complete explanation of this command. + + - [mvg_viewer](@ref vitelotte_example_mvg_viewer_page) is a simple OpenGL viewer. It can display both 2D images and 3D meshes. + - [mvg_editor](@ref vitelotte_example_mvg_editor_page) is a simple editor for mvg files. Its main purpose is to allow users to experiment with the representation rather than being a user-friendly tool, so expect some rough edges. + + */ + } diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_dc_mesh.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_dc_mesh.mdoc new file mode 100644 index 000000000..7679ce216 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_dc_mesh.mdoc @@ -0,0 +1,25 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +namespace Vitelotte { + +/*! + \page vitelotte_user_manual_dc_mesh_page DCMesh + + The class DCMesh is an extension of VGMesh. It permits to manipulate nodes using diffusion curves defined on the mesh instead of directly manipulating nodes. It is the class used behind the scene in the [mvg_editor](@ref vitelotte_example_mvg_editor_page) example. + + DCMesh curves are simply paths of edges on the mesh. You can create a curve with DCMesh::addCurve and add an halfedge to it with DCMesh::addHalfedgeToCurve. Halfedges must be added in order. + + Obviously, each curve has a set of extra information to define how it behaves. First, you can use DCMesh::setFlags to set flags of a curve to a combination of DCMesh::VALUE_TEAR and DCMesh::GRADIENT_TEAR to mark the curve as respectively value discontinuous or/and gradient discontinuous (if the EDGE_GRADIENT attribute is enabled). Then you can edit the [piecewise linear functions](@ref PiecewiseLinearFunction) that constrain the value and the gradient on each side of a curve. If a PiecewiseLinearFunction object contains no sample, it means the values or gradients along the associated curve is unconstrained, otherwise it is constrained. + + Once you set the curves as you wish, you should call DCMesh::setNodesFromCurves. It will clear all nodes on the mesh and set nodes on halfedges belonging to a curve to reflect the curve constraints. You must call VGMesh::finalize after to get a valid input for the solver. + + You can also add point constraints. They can have a value constraint but also a gradient constraint if you use FVElements. + + */ + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_diffusion_images.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_diffusion_images.mdoc new file mode 100644 index 000000000..fcc1a5352 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_diffusion_images.mdoc @@ -0,0 +1,40 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +/*! + \page vitelotte_user_manual_diffusion_images_page Diffusion curve images + + \section vitelotte_user_manual_diffusion_images_sec Diffusion curve images + + Diffusion curve images are images produced by diffusing colors defined on curves and points across some domain. It offers a simple way to create and edit smooth-shaded images exhibiting complex colors gradients. + + Diffusing colors from some constraints is the same as finding a function that interpolates these constraints. There are obviously several ways to do this. We will discuss two of them here: harmonic and biharmonic interpolation. + + To summarize, harmonic interpolation is relatively fast and easy to compute compared to biharmonic; however it has some limitations which makes it much harder to use for drawing. The biggest problem of harmonic interpolation is the fact that constrained curves are necessarily gradient-discontinuous and thus quite much visible. Biharmonic diffusion does not have this limitation and permits more natural color gradients, particularly when the goal is to reproduce shading: + + \image html vitelotte/diffusion_cmp.svg "With harmonic interpolation (left), shading curves are easily noticeable, whereas they are hardly visible with biharmonic interpolation (right). Creating the image on the right with harmonic diffusion would be very hard." + + For the same reason, point constraints are hardly usable with harmonic diffusion because they produce sharp color points instead of a smooth color gradients. + + However, biharmonic diffusion has one drawback: it extrapolates colors. It means that color values might go over the maximum displayable value (which may not be a problem if you do tone mapping) or below 0 (which is a more serious issue). Depending on the color space in which interpolation is done, this may lead to various artifacts, including color shifts. + + Fortunately, biharmonic diffusion also permits to easily control derivatives orthogonal to the curves. While the derivative of a color is not a very intuitive concept, it can be used to limit extrapolation. In practice, curves in the examples provided with Vitelotte either have no derivative constraints or we constrain them to be null. + + + \section vitelotte_user_manual_diffusion_color_spaces_sec Color spaces + + Interpolating colors can be done in various color spaces, with different results. The relevant color space may depend on the application. Here is a quick comparison of different color spaces. + + SRGB: SRGB is the standard color space for common screens. As such, working directly in SRGB has the advantage that it does not require any color conversion for display. Moreover, varying colors intensity linearly in SRGB produces a nearly perceptually-linear color gradient. The main drawback is that intermediate colors when interpolating between two bright and saturated colors tend to be too dark. + + Linear RGB or XYZ: Working in a linear color space may be necessary sometimes. For instance, applying shading on a surface should be done in a linear color space to get good results. It is however not really adapted to the creation of color gradients because of the strong non-linearity of human perception. + + Cie Lab / Cie Luv: These color spaces have been designed specifically to be close to human perception. Computing the distance between two colors in these spaces should give a good approximation of how different the colors are for a human. They produce good results to compute diffusion curve images while avoiding the artifacts of SRGB. Although conversions to/from these color spaces are more expensive, they are the recommended color spaces to work with diffusion images. Luv seems to be a bit better than Lab for color gradients, but the difference is minimal. + + Keep in mind that to avoid artifacts with Vitelotte, you should compute diffusion and interpolate colors over each face using the same color space. Failing to do so may make the edge more easily noticeable. VGMeshRenderer does this properly and support all the above color spaces. + + */ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_fem_solver.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_fem_solver.mdoc new file mode 100644 index 000000000..3d437d4da --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_fem_solver.mdoc @@ -0,0 +1,90 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +namespace Vitelotte { + +/*! + \page vitelotte_user_manual_fem_solver_page The FEM Solver + + \section vitelotte_user_manual_fem_solver_intro_sec Introduction + + FemSolver interpolates values (colors, normals, ...) smoothly through diffusion directly on a VGMesh using the finite element method (FEM). This is a simple way to get smooth color gradients from a sparse set of constraints. + + Vitelotte currently supports two kind of diffusions, with their advantages and drawbacks: + + - **Harmonic** diffusion satisfies the equation + + \f[ \Delta f = 0, \quad f(x_i) = v_i \f] + + in such a way that it interpolates constraints (nodes with a value set). This equation can be solved using very simple elements. Vitelotte currently supports *linear* and *quadratic* triangular elements for this, which uses the same interpolation functions than rendering. + + However, harmonic interpolation has some drawbacks. It does produce have \f$C^1\f$ transitions across constraints, which limits its usefulness in many cases. For instance, to represent smooth shading, biharmonic diffusion produces better results. + + - **Bi-harmonic** diffusion satisfies a similar equation + + \f[ \Delta^2 f = 0, \quad f(x_i) = v_i, \quad \nabla f(x_i) = g_i \f] + + Bi-harmonic diffusion is a bit more costly, but also offers smoother gradients and more control. For instance, it is possible to constrain derivative nodes to add control over gradients. A drawback of bi-harmonic diffusion is that it extrapolates: it can produce values higher or lower than constraints. When diffusing colors, it means that you can have color components below 0 and above the maximum, which may in turn lead to artifacts. + + Theoretically, solving this equation using FEM requires \f$C^1\f$ continuity across the elements, which in turn requires using high order elements. As this is unpractical, we use _non-conforming_ elements that do not meet this requirement but ensure convergence nonetheless: Morley elements and Fraeijs de Veubeke's elements (FV). They are similar to linear and quadratic elements respectively, but add a derivative node over each edge. + + These elements are not even \f$C^0\f$ on their boundaries, they are not suited for rendering. Instead, we interpolate value nodes per-element (thus ignoring gradient nodes), which lead to the linear and quadratic interpolation functions used for harmonic elements. + + + + \section vitelotte_user_manual_fem_solver_usage_sec Using the solver + + Using the solver is quite easy. You first need to include the header `Patate/vitelotte.h` to get access to the required classes. + + FemSolver takes two template parameters: a VGMesh type and an ElementBuilder. Currently, only triangular elements are supported. Here is a short description of available element builders: + + - **LinearElementBuilder** solves harmonic diffusion over linear elements. Input mesh must have the TO_VERTEX_VALUE attribute enabled. + - **QuadraticElementBuilder** solves harmonic diffusion over quadratic elements. Input mesh must have the TO_VERTEX_VALUE and EDGE_VALUE attributes enabled. + - **MorleyElementBuilder** solves biharmonic diffusion using Morley elements. Input mesh must have TO_VERTEX_VALUE and EDGE_GRADIENT attributes enabled. + - **FVElementBuilder** solves biharmonic diffusion using Fraeijs de Veubeke's (FV) elements. Input mesh must have TO_VERTEX_VALUE, EDGE_VALUE and EDGE_GRADIENT attributes enabled. + + These element builders do not support singularities (in practice, the attachment point FROM_VERTEX_VALUE is ignored). To enable it, you must wrap your element builder into a SingularElementDecorator. This only supports up to one singularity by element, so it may be necessary to refine the input mesh in case 2 singularities are directly connected. + + Here is a typical example: + + \code{.cpp} + typedef VGMesh Mesh; + + typedef FVElementBuilder FVBuilder; + typedef SingularElementDecorator ElementBuilder; + typedef FemSolver Solver; + \endcode + + Note that the element builders are parametrized by a scalar type. It will be used internally by the solver instead of the mesh Scalar type. This may be used to avoid numerical problems. In our example, we always use floats for meshes and double in the solver, as above. + + All is left to do is to create a solver and solve the problem: + + \code + Mesh mesh; + // [ Fill the mesh... ] + + Solver solver(); + solver.build(mesh); + solver.solve(mesh); + \endcode + + FemSolver::build() builds the internal matrices and compute factorizations. It does not depend on constraint values, so if you are using the solver in an interactive application (like our `mvg_editor` example), it is not always necessary to call it. FemSolver::solve() effectively solves the diffusion and sets a value for each unknown node of the mesh. + + The solver may fail, either because input is invalid or because of numerical instability. We try to detect errors and to provide meaningful messages in case of problems: + + \code + SolverError::Status status = solver.error().status(); + if(status != SolverError::STATUS_OK) { + cout << ((status == SolverError::STATUS_ERROR)? "Error: ": "Warning: ") + << solver.error().message() << endl; + } + \endcode + + + */ + + } diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_mvg_file_format.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_mvg_file_format.mdoc new file mode 100644 index 000000000..ec3a57c9a --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_mvg_file_format.mdoc @@ -0,0 +1,131 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +/*! + \page vitelotte_user_manual_mvg_file_format_page The MVG File Format + + \section vitelotte_user_manual_mvg_file_format_intro_sec Introduction + + In order to save VGMesh to disk we provide a file format called MVG (for Mesh-based Vector Graphics). MVGs are simple text files with a syntax inspired from the OBJ file format. It supports meshes of arbitrary dimensions, any combination of attributes and nodes (including unknown and invalid nodes). This format is also easily extensible. + + The MVG file format is designed to be easy to use, human-readable and hackable. + + Several sample MVGs are available in `examples/Vitelotte/samples`. + + + \section vitelotte_user_manual_mvg_file_format_description_sec Description + + MVG files start with a header followed by a body. The body contains the block of vertex declarations, followed by the block of nodes declarations and then the block of faces declaration, in this order. Vertices and nodes are referenced by index in the order of their apparition in the file. + + \warning Note that unlike obj files, the first elements have the index 0. + + Blank lines and lines starting with # are ignored, but can not be placed on the first line. It is not possible to place a comment on the same line than a declaration. + + Each line is in the form a declaration and starts with a keyword describing the kind of object declared, like 'v' for vertices of 'f' for faces. Lines starting with an unknown keyword are ignored by the parser. This allow to extend mvg files while keeping them readable by unaware applications. + + + \subsection vitelotte_user_manual_mvg_file_format_description_header_subsec Header + + A MVG file starts with a header. The first line _must_ be + + \code + mvg 1.0 + \endcode + + The following lines declare respectively the number of dimensions, the number of value coefficients and the active attributes of the mesh: + + \code + dimensions 2 + coefficients 4 + attributes none + \endcode + + The parameter of `attributes` can be: + + - `none`: no attributes are enabled. + - `linear`: equivalent to VGMesh::LINEAR_FLAGS. + - `quadratic`: equivalent to VGMesh::QUADRATIC_FLAGS. + - `morley`: equivalent to VGMesh::MORLEY_FLAGS. + - `fv`: equivalent to VGMesh::FV_FLAGS. + - an integer: any combination of flags, directly represented as a number. Human-readable forms should be preferred when possible. + + Finally, you can declare the number of vertices, nodes and faces. These declarations are optional and only serve as hint for the parser to reserve memory (in other words they do not need to be accurate, which helps manual edition of files): + + \code + vertices 1747 + nodes 0 + faces 3475 + \endcode + + + \subsection vitelotte_user_manual_mvg_file_format_description_body_subsec Body + + Vertex declarations are straightforward: + \code + v COORDINATES + \endcode + where \c COORDINATES are the coordinates of the vertex, separated by spaces or tabs. The number of coordinates must match the number of dimensions declared in the header. + + Node declarations are similar to vertex: + \code + n COEFFICIENTS + \endcode + where \c COEFFICIENTS are the coefficients, separated by spaces or tabs. The number of coordinates must match the number of coefficients declared in the header. Unknown nodes are declared like this: + \code + n void + \endcode + + Faces declarations are more involved as they contain connectivity to both vertices and nodes. With no attributes enabled, they are simply: + \code + f VERTEX_INDICES + \endcode + where VERTEX_INDICES is the list of the face vertices indices (or 'x' for invalid nodes), ordered counter-clockwise. If the attirbute TO_VERTEX_VALUE is enabled, the declaration become + \code + f V0/NV0 V1/NV1 V2/NV2 ... + \endcode + where Vi is the index of the ith vertex of the face and NVi is the index of the to-vertex node attached to Vi. If FROM_VERTEX_VALUE is enabled too (to allow singularities), vertex declaration become + \code + Vi/NVi_in/NVi_out + \endcode + Where NVi_in is the node attached to the ingoing halfedge of Vi and NVi_out the node attached to the outgoing halfedge. As most of the meshes only contains a few singular vertices, it is a waste of space to systematically duplicate nodes and it makes the file harder to tweak. So there is a special rule that permits to omit the second node if it is the same as the first, leading to declarations such as + \code + f V0/NV0 V1/NV1_in/NV1_out V2/NV2 + \endcode + where only V1 is singular. + + Edge attributes (EDGE_VALUE and EDGE_GRADIENT), if enabled, are placed after vertices declaration, speared by the '-' token: + \code + f VERTICES - EDGES + \endcode + VERTICES are the vertex declaration as above and EDGES should be as many edge declarations as there is vertex. The first edge declaration correspond to the edge V0-V1, the second to V1-V2 and so on. Edge declarations take the form `N0/N1/...` where Ni are node indices. If there is only one edge attribute enabled, an edge declaration is simply a single node index. If both are enabled, EDGE_VALUE is the first index and EDGE_GRADIENT the second. If none is enabled, the whole edge declaration is discarded, '-' included. + + While not used yet in Vitelotte, you may wish to add faces declaration (for example if you have elements with internal nodes that are not attached to vertices or edges). They should be added after edges node, separated by the '-' token. + + In the end, a complete triangular face declaration with fv attributes and a singular vertex look like this: + \code + f V0/NV0 V1/NV1_in/NV1_out V2/NV2 - NEv0/NEg0 NEv1/NEg1 NEv2/NEg2 + \endcode + + + \section vitelotte_user_manual_mvg_file_format_reading_writing_sec Reading and Writing MVG + + Reading a MVG can be done with the class MVGReader declared in the header `vitelotte_io.h`. In one line: + \code{.cpp} + MVGReader().read(inStream, mesh); + \endcode + where `inStream` is an input iosteam. + + The MVGReader object has two purpose. First, it encapsulate all temporary data required to read a MVG. You can read several MVG with a single reader in order to allocate all these objects only once. The second use is error reporting. When an error or a warning is encountered, MVGReader calls a callback. By default, it just print the message on stderr, but you may wish to customize it. You can change the callbacks with OBJReader::setErrorCallback (MVGReader inherits OBJReader). + + Writing MVGs is very similar to reading: + \code{.cpp} + MVGWriter().write(outStream, mesh); + \endcode + MVGWriter constructor takes the version of the MVG to produce as an optional parameter which default to the latest version (anyway, 1.0 is the only version existing right now). + + + */ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_rendering.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_rendering.mdoc new file mode 100644 index 000000000..5d5080646 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_rendering.mdoc @@ -0,0 +1,98 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +namespace Vitelotte { + +/*! + \page vitelotte_user_manual_rendering_page Rendering + + \section vitelotte_user_manual_rendering_basics_sec Basic usage + + VGMeshRenderer is a simple renderer whose job is to emit the right OpenGL 4 calls to render a given mesh. Currently it supports triangular meshes with linear or quadratic interpolation over each face, up to one singularity per triangle and curved edges. + + Vitelotte is a header-only library, and its job is not to deal with OpenGL context creation and management. In practice, Vitelotte does not even include OpenGL headers because you may wish to use GLEW or a similar library that provides its own header that would lead to conflicts. So, to use the renderer you need to include your OpenGL header *before* `Patate/vitelotte_gl.h` like this: + + \code{.cpp} + // Include some OpenGL header. + #include + + // Include Vitelotte's classes that depends on OpenGL. An OpenGL + // header *must* have been included beforehand. + #include + \endcode + + Note that only `Patate/vitelotte_gl.h` depends on OpenGL. Other Vitelotte headers do not require to do this. + + Creating the renderer is straightforward: + + \code{.cpp} + // As usual, VGMeshRenderer is parametrized by the Mesh class. + VGMeshRenderer renderer; + \endcode + + Every VGMeshRenderer manages some OpenGL resources. They can be released by calling VGMeshRenderer::releaseGLResources(). + + To upload data required for rendering to the GPU, just do + + \code{.cpp} + renderer.updateBuffers(mesh); + \endcode + + This must be called every time the mesh is modified. Note that the renderer does not keep reference to the mesh, so it can be deleted safely even if a renderer is setup to render it. + + Rendering can be done with the following methods: + + \code{.cpp} + // Render the mesh given an Eigen::Matrix4f view matrix + renderer.render(viewMatrix); + + // Render the mesh in wireframe + renderer.renderWireframe(viewMatrix, viewportSize); + \endcode + + Coordinates of the mesh vertices are multiplied by the viewMatrix in the shader to produce the OpenGL clip coordinates. You should build the viewMatrix yourself to place the image correctly on the screen. Wireframe rendering requires the viewport size to get the width of the lines right. + + Note that render() and renderWireframe() should produce samples at the exact same depth. So to render a wireframe on top of a solid render, the easiest way is to set the depth test to GL_LEQUAL. + + + \section vitelotte_user_manual_rendering_advanced_sec Advanced usage + + \subsection vitelotte_user_manual_rendering_sharing_resources Sharing resources + + The renderer treats shaders as global resources that can be shared by several instances of VGMeshRenderer. By default, they are automatically created when needed (when you call a render method). You can allocate them explicitly to share the resources among several renderers: + + \code{.cpp} + // Can be called anytime + VGMeshRendererResources resources; + + // Must be called with the correct OpenGL context active. + resources.initialize(); + \endcode + + Then, you can pass the resources to an instance of VGMeshRenderer on construction or with VGMeshRenderer::setResources(). Resources can be released by calling VGMeshRendererResources::releaseGLResources() when the context is active (in the current implementation, this releases both shaders and buffers). + + \subsection vitelotte_user_manual_rendering_color_spaces Color Spaces + + The renderer support several color spaces natively. It does color interpolation in the space returned by VGMesh::colorSpace() and does the conversion to the screen color space just before outputing colors to the framebuffer. By default, the screen color space is assumed to be SRGB, but it can be changed with VGMeshRenderer::setScreenColorSpace(). If you do rendering with GL_FRAMEBUFFER_SRGB enabled, you should set the screen color space to linear because OpenGL will perform the conversion itself. + + If the mesh values do not represent colors, the color space should be set to COLOR_NONE. In this case, no color conversion is performed. + + \subsection vitelotte_user_manual_rendering_projection Coordinates and value projections + + The input mesh can have an arbitrary number of dimensions and coefficients. By default, if the number of dimensions is smaller or equal to 4, the d first coordinates are sent to OpenGL and missing ones are replaced by 0, except the last one which is set to 1. It works well with 2D and 3D meshes, and with 4D meshes where the 4th dimension is 1 (unless you know what you do). In other cases, you can pass a functor to VGMeshRenderer constructor that takes a VGMesh::Vector reference in parameter and returns an Eigen::Vector4f which is sent to OpenGL. + + A similar mechanism is available for values. By default, single valued coefficients are rendered as grayscale, 2D values are rendered as grayscale + alpha, 3D values are rendered as RGB and 4D as RGBA. Sending more than 4 coefficients to the shaders is not currently supported. + + \subsection vitelotte_user_manual_rendering_custom_shaders Customizing shaders + + If you want to really customize the output, you may need to write your own shaders. You can setup your own OpenGL states and then call VGMeshRenderer::drawGeometry to setup the VAO and do the draw call. This function takes a combination of the NORMAL_TRIANGLES and the SINGULAR_TRIANGLES flags in parameter to determine if it should render normal triangles, singular triangles or both. + + Detailing the way shaders work would be mostly paraphrasing shader code. So if you need to write custom shaders, the easiest way is to start from Vitelotte's shaders (in `Patate/Vitelotte/Utils/VGMeshRendererShaders`) and modify them. If you need help, don't hesitate to send a mail to our mailing list . + + */ + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_tutorial.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_tutorial.mdoc new file mode 100644 index 000000000..7a32a8210 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_tutorial.mdoc @@ -0,0 +1,97 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +namespace Vitelotte { + +/*! + \page vitelotte_user_manual_tutorial_page Vitelotte's Tutorial + + \section vitelotte_user_manual_tutorial_introduction_sec Introduction + + This tutorial will guide you through a simple program while introducing the main concepts of Vitelotte. We will load a `.obj` mesh in a VGMesh, then we will set a few point constraints and solve the diffusion before saving the result in a `.mvg`. While we will not manipulate diffusion curves for the sake of simplicity, we will show enough to get you started. The result can be visualized using the [mvg_viewer example](@ref vitelotte_example_mvg_viewer_page) program. + + \image html vitelotte/tutorial_overview.svg "The final program takes a mesh as input, adds some random color constraints and diffuses them using the solver." + + The complete source is available in `examples/Vitelotte/tutorial`. + + + Our program is composed of 2 steps. First, we will load a mesh (from an obj file) and assign some random color constraints on it. Second, we will send the mesh to the solver to get the final result that will be saved in the mvg file format. + + + \section vitelotte_user_manual_tutorial_preparing_the_mesh_sec Preparing the mesh + + We will first declare our mesh type. As most of the Patate lib, VGMesh is templated so typedefs really help to keep the code simple. + + \snippet Vitelotte/tutorial/tutorial.cpp Declare the Mesh class + + Here we say that we want to use floats for vectors and colors coefficients, and that the dimension of vectors and the number of coefficients will be specified at runtime. Now let's create a `Mesh`: + + \snippet Vitelotte/tutorial/tutorial.cpp Create the mesh + + We declare the mesh to be 3D because we are about to load a `.obj`, and those represent only 3D meshes. The FV_FLAGS parameter tells the mesh that faces represent _FV elements_ (for details about available elements, see the [solver manual](@ref vitelotte_user_manual_fem_solver_page)). In practice, it asks the mesh to enable a specific set of _attributes_ (which are described [here](@ref vitelotte_user_manual_vg_mesh_nodes_sec)), which just allocates some internal buffers. + + Now, we want to load a mesh defined in an `obj` file. VGMesh inherits PatateCommon::SurfaceMesh, a generic mesh representation adapted from the [Surface_mesh](http://opensource.cit-ec.de/projects/surface_mesh) library (with some modifications). This class comes with an other template class PatateCommon::OBJReader that permits to read `obj` files. We can use it directly to load our VGMesh like this: + + \snippet Vitelotte/tutorial/tutorial.cpp Load a .obj + + You might wonder why we need to create a temporary ObjReader object. This can have several uses. One of them is that it permits to customize error reporting: by default it prints errors on `stderr`, but you can set your own callbacks. Another is that you can use the same reader for several files to avoid to reallocate some internal buffers. Other readers like the MVGReader use the same principle. + + We assume that the obj file we just loaded contains only vertices on the x-y plane, so we will discard the z coordinate. This results in a 2D mesh that represents our image. This is simply done by changing the number of dimensions of the mesh with VGMesh::setNDims(). Note that this function is naive: if you reduce the number of dimensions it will just discard extra coordinates, and if you increase it, new coordinates _will not be initialized_. + + \snippet Vitelotte/tutorial/tutorial.cpp Convert the mesh in 2D + + Time for some theory. Each face of the mesh contains a number of _attachment points_ that depend on the currently active attributes. To be a valid input for the solver, each attachment point must be linked to a _node_. Nodes can either be a constraint (when they have an associated value) or be a placeholder for an unknown value that the solver has to compute (we call these _unknown nodes_). Now, we can control the diffusion by wisely connecting nodes to attachment points. For example, if you want to diffuse color across an edge, attachment points on both sides must be linked to the same nodes (constrained or not), which esnures they will have the same color. + + We will now pick a random set of vertices and assign a random color constraint to each one. The rest of the mesh is unconstrained. So, all attachment points around a constrained vertex must point to the same constrained nodes, and all attachment points around the other vertices must point to their respective unknown node. FV elements also have some attachment points on the edges of the mesh that must be set correctly. + + \image html vitelotte/tutorial_constraints.svg "The first step is to add color constraints to some vectices." + + It may seems complicated, but in practice we will use the VGMesh::finalize method to do most of the job. All we have to do is to set _one_ attachment point somewhere around each constrained vertex before calling this method and we are done. This gives us: + + \snippet Vitelotte/tutorial/tutorial.cpp Color dots and finalize + + So, for each vertex we throw a dice such that we have 1 chance out of 10 to add a constraint for this vertex. If so, we just select an outgoing halfedge and assign a node with a random color to the "fromVertexValue" attachment point (it corresponds to the source vertex). We don't need to set this node for each adjacent halfedges as VGMesh::finalize will do it for us. + + Note that finalize uses some heuristics and does not handle all tricky cases. For instance, it is not guaranteed to work for non-local constraints (if a same node is used at different places on the mesh). This method is described in details in [the dedicated section of VGMesh's user manual](@ref vitelotte_user_manual_vg_mesh_finalize_subsec). + + If we display the mesh now with `mvg_viewer`, we see a black image with a few colored points. This is because our viewer shows unknown nodes as black. + + \image html vitelotte/tutorial_finalized.png "The mesh after finalization." + + + \section vitelotte_user_manual_tutorial_solving_diffusion_sec Solving diffusion + + Now we are ready to solve the diffusion. First, we need to create our solver object: + + \snippet Vitelotte/tutorial/tutorial.cpp Create the solver + + The FemSolver class is parametrized with the mesh and an `ElementBuilder`. The element builder describes how to build the stiffness matrix and so varies in function of the element type we want to use. FVElementBuilder uses FV elements that allow to compute biharmonic diffusion and obtain quadratic color interpolation in the end. Element builders do not support singular elements directly, so we need to decorate them with the SingularElementDecorator. This may seem complicated, but in practice we always do this, so there is no need to worry about the details (unless you want to implement your own element type). + + \image html vitelotte/tutorial_solver.svg "The sover interpolates color constraints. Here, we do biharmonic interpolation." + + Solving is now straightforward: + + \snippet Vitelotte/tutorial/tutorial.cpp Solve the diffusion + + Why are there two steps ? FemSolver::build constructs the matrix and inverts it. It is time consuming but does not depend on the right hand side of the problem, i.e. the actual values of the constraints. This means that you don't need to call this costly step if you only modify the values, which typically happens in interactive applications like `mvg_editor`. However, if there is a change in geometry, in connectivity (including nodes) or in the type of some node (switching to unknown or constraint), then you _need_ to call it again. In case of doubt, just call it to be sure. + + FemSolver::solve does what its name states. This will set a value to all unknown nodes, thus producing the final image. Note that the solver may fail, either because the input mesh is invalid or because the problem is numerically unstable, so check for errors. + + \section vitelotte_user_manual_tutorial_output_sec Writing to mvg + + All is left to do is to save our VGMesh in the `.mvg` file format to load it with `mvg_viewer`. We will not describe the format here, it has [its own page](@ref vitelotte_user_manual_mvg_file_format_page). Just know that it is a simple text-based format inspired from the obj mesh format. + + Saving is straightforward and really similar to the code to read the mesh: + + \snippet Vitelotte/tutorial/tutorial.cpp Write the mesh + + We change the attributes of the mesh from VGMesh::FV_FLAGS to VGMesh::QUADRATIC_FLAGS. It has the effect of discarding the attribute VGMesh::EDGE_GRADIENT which is necessary for the solver (with FV elements) but useless for rendering. This permits to slightly reduce the output size. + + This is it ! If you want to dive further in Vitelotte, you should [take a look at the VGMesh class manual page](@ref vitelotte_user_manual_vg_mesh_page), because we overlooked some important details here. + + */ + + } diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_vg_mesh.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_vg_mesh.mdoc new file mode 100644 index 000000000..54b2e4c92 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/Vitelotte/vitelotte_user_manual_vg_mesh.mdoc @@ -0,0 +1,192 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +namespace Vitelotte { + +/*! + \page vitelotte_user_manual_vg_mesh_page The VGMesh class + + \section vitelotte_user_manual_vg_mesh_introduction_sec Introduction + + VGMesh is the heart of Vitelotte: it is both the class that represents mesh-based vector images and the class used as input of the solver. It provides users with low-level controls that we explain in details in the following. If you are only interested in high-level controls of diffusion curves, we recommend the use of the [DCMesh](@ref vitelotte_user_manual_dc_mesh_page) class instead. + + VGMesh inherits PatateCommon::SurfaceMesh, which is adapted from the [Surface_mesh](http://opensource.cit-ec.de/projects/surface_mesh) library. Note that SurfaceMesh is in the namespace PatateCommon because it might be useful for other modules in the future. We will not describe in detail how to manipulate SurfaceMesh objects here so don't hesitate to consult the references. This is an [halfedge-based data structure](https://en.wikipedia.org/wiki/Doubly_connected_edge_list), so if you are familiar with this, using it should be quite straightforward. + + There is a few things to know about surface mesh. First, surface mesh permits to add arbitrary properties to any kind of element. For instance, vertex coordinates are just a vertex property. Secondly, mesh elements (vertices, faces, ...) are manipulated through _handles_. These handles are really just indices that allow you to find elements inside the property arrays. So to access the coordinate of some vertex, you need a handle to that vertex and the "position" vertex property. In practice, standard properties are accessible directly from the mesh, so you end up with this kind of code: + + \code{.cpp} + Vertex vx = [...]; // Some vertex + // Get the outgoing halfedge associated with vx + Halfedge h = mesh.halfedge(vx); + // Set the coordinate of the target vertex of h + mesh.position(mesh.toVertex(h)) = Vector(1., 2.); + \endcode + + + \section vitelotte_user_manual_vg_mesh_dims_coeffs_sec Dimensions and coefficients + + VGMesh allows you to customize the type of coordinates and the type of values through template parameters (we use the term value instead of color as a VGMesh can be used to store colors, normals, displacement vectors or anything else). The first parameter is the underlying scalar type (typically `float` or `double`). The two other set respectively the number of dimensions and the number of coefficients per value. Setting these values at compile time is not always practical, so you can use the special value Vitelotte::Dynamic which allows you to choose them a run time: + + \code + // Declare a Mesh type with dynamic dimensions and values. + typedef VGMesh Mesh; + // Create a 3D mesh with 4 coefficients per value (maybe rgba colors). + Mesh mesh(3, 4); + // [...] + // Modify the number of dimensions / coefficients later: + mesh.setNDims(2); + mesh.setNCoeffs(1); + \endcode + + + \section vitelotte_user_manual_vg_mesh_nodes_sec Nodes + + VGMesh has the concept of _node_. A node either has a given value, in which case it acts as a constraint, or is left unspecified. A single node can be referenced by several _attachment points_. Typically all attachment points around a vertex reference the same node unless the vertex lies on a discontinuity. Also, nodes work like mesh elements: they are just handles and you can define your own properties on the nodes. + + \code + // Create a node + Node constraint = mesh.addNode(Value([...])); + // Add node without parameter creates an unknown node with the special + // value mesh.unconstrainedValue() + Node unknown = mesh.addNode(); + + // Read/write access to some node's value + mesh.value(constraint) = Value([...]); + \endcode + + There is currently 4 attachment points supported by VGMesh (more can be added with properties). All of them are defined by halfedge: + + \image html vitelotte/vgmesh_attributes.svg "VGMesh's attachment points." + + - VGMesh::TO_VERTEX_VALUE: represents the value at the \e target vertex of an halfedge. + - VGMesh::FROM_VERTEX_VALUE: represents the value at the \e source vertex of an halfedge. + - VGMesh::EDGE_VALUE: represents the value on an halfedge (typically, on the midpoint). + - VGMesh::EDGE_GRADIENT: represents the orthogonal \e derivative on an halfedge. Depending on the interpolation function, it can be the derivative at the midpoint (morley elements) or the average derivative over the edge (FV elements). See the [solvers documentation](@ref vitelotte_user_manual_fem_solver_page) for more details. The direction of the derivative depends on the halfedge orientation: if VGMesh::halfedgeOrientation() is true, it is the derivative inward, else the derivative outward. + + Before using one of these attachment points, you must enable the corresponding mesh _attribute_. For instance, to use quadratic interpolation (all of the above attachment points except EDGE_GRADIENT), you can do: + + \code + mesh.setAttributes( Mesh::TO_VERTEX_VALUE_FLAG + | Mesh::FROM_VERTEX_VALUE_FLAG + | Mesh::EDGE_VALUE_FLAG) + + // Or, alternatively + mesh.setAttributes(Mesh::QUADRATIC_FLAGS) + \endcode + + Then, you can get/set nodes this way: + + \code + Halfedge h = [...]; // Some halfedge + Node n = mesh.addNode(Value([...])); // Adds a new node + mesh.fromVertexValueNode(h) = n; + mesh.toVertexValueNode(h) = n; + mesh.edgeValueNode(h) = n; + mesh.edgeGradientNode(h) = n; + + // Alternative methods: + mesh.halfedgeNode(h, Mesh::TO_VERTEX_VALUE) = n; + + // Access to the opposite node (i.e. the node directly on the other side of the edge): + mesh.halfedgeOppositeNode(h, Mesh::TO_VERTEX_VALUE) = n; + \endcode + + There are two value nodes per vertex per triangle. This permits to represent singularities: points that emit a different color in different directions, like the apex of a conical gradient: + + \image html vitelotte/singularities.png "The two extremities of the curve are singularities." + + \warning For simplicity, both FemSolver and VGMeshRenderer support only one singularity per triangle. It is always possible to overcome this limitation by subdividing edges that connect two singularities. As area around singularities typically show fast and complex values variations, it is anyway a good idea to refine these parts to ensure good quality results. + + When used with a solver, nodes can have the special value "unknown" (returned by VGMesh::unconstrainedValue()). This permits to mark nodes that the solver has to find. Nodes with a known value thus behave as constraints the solver must satisfy. Here is an illustration of an input to the solver (for simplicity, when two opposite nodes on an edge are the same, they are merged): + + \image html vitelotte/nodes.svg "A typical input to the solver. White dots are unknown nodes and orange ones are constraints. Numbers are node indices." + + There is no direct way to delete a node. The only way is to call VGMesh::deleteUnusedNodes() that scans the whole mesh and marks all unused nodes for deletion. The rationale behind this is that we can not check efficiently if a node is used on deletion, so deleting a node could result in an invalid mesh. + + + \section vitelotte_user_manual_vg_mesh_vertex_gradient_constraints_sec Vertex gradient constraints + + Elements using the VGMesh::EDGE_GRADIENT attribute support derivative constraints. However, it only applies to edge constraints. Sometimes, it can be useful to have a gradient constraint on a specific vertex. + + Some elements support vertex _gradient_ constraints. Unlike what we have seen before, they are set by vertex and not by halfedge. By default, all vertices are unconstrained. You can manipulate vertex gradient constraints like this: + + \code + Vertex vx = [...]; + + // Set a constraint: + mesh.setGradientConstraint(vx, Gradient([...])); + + // Test if a vertex has a gradient constraint: + if(mesh.hasGradientConstraint(vx)) { + // Get a constraint. WARNING: fails if the vertex has no constraint: + Mesh::Gradient g = mesh.gradientConstraint(vx); + } + + // Remove a gradient constraint: + mesh.removeGradientConstraint(vx); + \endcode + + + \section vitelotte_user_manual_vg_mesh_nodes_manipulation_sec High-level node manipulation + + To ease the assignment of nodes around vertices, we provide the following functions: + + \code + // `n0` and `n1` are nodes and `h0` and `h1` are two halfedges with the same + // _from_ vertex `vx`. + + // Set nodes adjacent to vx in the arc [h0, h1[ (counterclockwise) to n0. + mesh.setVertexNode(n0, h0, h1); + + // Set nodes adjacent to vx in the arc [h0, h1[ (counterclockwise) to + // values interpolated from the value of n0 (in the direction of h0) + // to the value of n1 in the direction of h1). + mesh.setSingularity(n0, n1, h0, h1); + \endcode + + + \subsection vitelotte_user_manual_vg_mesh_finalize_subsec The finalize method + + When preparing the input to the solver, setting all nodes for each attachment point by hand can be tedious. We provide the method VGMesh::finalize() that takes a mesh with a few constraints set and "guesses" all the missing ones using heuristics. + + Finalize processes each vertex and each edge in turn. If attachment points around a vertex all reference an invalid node, a new unknown node is created and assigned to them, thus constraining the result to be continuous. Same thing goes for edges, if both edge-value nodes are invalid, they are set to a new unknown node, and similarly for edge-gradient nodes. + + \image html vitelotte/finalize_invalid.svg "Vertex and edges without valid nodes are bound to a new unknown node. Small black dots represent invalid nodes and numbers represent unknown node IDs." + + Obviously, if two opposite nodes on an edge are set, they are left untouched. + + \image html vitelotte/finalize_edge_constraint.svg "Two opposite nodes that are left untouched. Colored dot represent constraints." + + Vertices are a bit more complicated to process. Let's assume that edges around a vertex have either both of their nodes set or none. For each vertex, the algorithm starts from a valid node and turns around the vertex in search for the next valid node. Now we can set the invalid nodes in-between from the two boundary nodes using the following rules: + + \image html vitelotte/finalize_vertex_simple.svg "When both extremities are the same node, intermediate nodes are assigned their ID." + + \image html vitelotte/finalize_vertex_singular.svg "When both extremities are two different constraint nodes, intermediate edges get their own constraint node. Different colors mean different constraints." + + \image html vitelotte/finalize_vertex_cross.svg "When both extremities are two different unknown nodes, we assign the ID of the first node to all others. This might lead to unexpected results in complex cases like non-local constraints." + + \image html vitelotte/finalize_vertex_continuous_edge.svg "Finally, when two sides of an edge point to the same unknown node, they are discarded before applying previously seen rules." + + What happens with an edge having one valid node and one invalid node? The valid node is simply assigned on both sides, then the above rules are applied. However, it is recommended to avoid this situation when possible or to use it only on simplest cases, like in the tutorial. + + \warning This algorithm is not perfect. As it does simplifications (mainly replacing unknown nodes by others) it may not produce the intended result in case of non-local constraints (when a same unknown node is used on different places on the mesh). + + + \subsection vitelotte_user_manual_vg_mesh_simplify_subsec The simplify method + + The VGMesh::simplify() method is basically the reverse of finalize. It tries to remove as many nodes as possible in such a way that VGMesh::finalize would rebuild the same problem. It is useful to reduce the size of files containing pre-solve meshes. + + \warning As finalize, this algorithm may lead to unexpected results in case of non-local constraints. + + + \section vitelotte_user_manual_vg_mesh_topology Topological operations + + The current version of VGMesh does not support topological operations. You can use surface mesh low-level topological operations, but they do not update nodes and attachment points. We plan to implement similar node-aware operations for input meshes and maybe higher-level refinement in the next release, so stay tunned. + + + */ + + } diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/defines.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/defines.h new file mode 100644 index 000000000..669fb2a2e --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/defines.h @@ -0,0 +1,38 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +#ifndef _PATATE_DEFINES_ +#define _PATATE_DEFINES_ + +//////////////////////////////////////////////////////////////////////////////// +// Compatibility types, macros, functions +// +#ifdef __CUDACC__ +# include +#endif + +#ifdef __CUDA_ARCH__ + #define MULTIARCH_STD_MATH(FUNC) +#else + #define MULTIARCH_STD_MATH(FUNC) using std::FUNC; +#endif + +#ifdef __CUDACC__ +# define MULTIARCH __host__ __device__ +#else +# define MULTIARCH + +// GCC: compile with -std=c++0x +# if defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ >= 5)) +# if defined(nullptr_t) || (__cplusplus > 199711L) || defined(HACK_GCC_ITS_CPP0X) +# define __CPP0X__ +# endif +# endif + +#endif // ifdef __CUDACC__ + +#endif //#ifndef _PATATE_DEFINES_ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/gl_utils/color.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/gl_utils/color.h new file mode 100644 index 000000000..45d9f1d23 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/gl_utils/color.h @@ -0,0 +1,266 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _PATATE_COMMON_GL_UTILS_COLOR_ +#define _PATATE_COMMON_GL_UTILS_COLOR_ + + +#include "Eigen/Dense" + + +namespace PatateCommon +{ + + +enum ColorSpace { + COLOR_NONE, + COLOR_SRGB, + COLOR_LINEAR_RGB, + COLOR_CIE_XYZ, + COLOR_CIE_LAB, + COLOR_CIE_LUV +}; + + +// SRGB <-> linear RGB + +inline Eigen::Vector3f srgbFromLinearRGB(const Eigen::Vector3f& lrgb) +{ + Eigen::Vector3f srgb = lrgb; + for(int i=0; i<3; ++i) + srgb(i) = lrgb(i) > 0.0031308f? + 1.055f * std::pow(lrgb(i), 1.f/2.4f) - .055f: + 12.92f * lrgb(i); + return srgb; +} + + +inline Eigen::Vector3f linearRGBFromSrgb(const Eigen::Vector3f& srgb) +{ + Eigen::Vector3f lrgb = srgb; + for(int i=0; i<3; ++i) + lrgb(i) = lrgb(i) > 0.04045f? + std::pow((lrgb(i)+0.055f) / 1.055f, 2.4f): + lrgb(i) / 12.92f; + return lrgb; +} + + +// linear RGB <-> CIE XYZ + +inline Eigen::Vector3f linearRGBFromCieXYZ(const Eigen::Vector3f& cieXYZ) { + static const Eigen::Matrix3f xyzToRgb = (Eigen::Matrix3f() << + 3.2406f, -1.5372f, -0.4986f, + -0.9689f, 1.8758f, 0.0415f, + 0.0557f, -0.2040f, 1.0570f + ).finished(); + + return xyzToRgb * cieXYZ; +} + + +inline Eigen::Vector3f cieXYZFromLinearRGB(const Eigen::Vector3f& lrgb) { + static const Eigen::Matrix3f rgbToxyz = (Eigen::Matrix3f() << + 0.4124f, 0.3576f, 0.1805f, + 0.2126f, 0.7152f, 0.0722f, + 0.0193f, 0.1192f, 0.9505f + ).finished(); + + return rgbToxyz * lrgb; +} + + +// CIE XYZ <-> CIE LAB + +namespace internal { + + template < typename Scalar > + struct LabHelper { + static const Scalar thresold; + static const Scalar thresold2; + static const Scalar thresold3; + static const Eigen::Matrix white; + + static inline Scalar f(Scalar v) { + return (v > thresold3)? + std::pow(v, Scalar(1./3.)): + Scalar(1.) / (Scalar(3.) * thresold2) * v + Scalar(4. / 29.); + } + + static inline Scalar fInv(Scalar v) { + return (v > thresold)? + std::pow(v, Scalar(3.)): + Scalar(3.) * thresold2 * (v - Scalar(4. / 29.)); + } + }; + + template < typename Scalar > + const Scalar LabHelper::thresold = Scalar(6. / 29.); + template < typename Scalar > + const Scalar LabHelper::thresold2 = + LabHelper::thresold * LabHelper::thresold; + template < typename Scalar > + const Scalar LabHelper::thresold3 = + LabHelper::thresold2 * LabHelper::thresold; + + template < typename Scalar > + const Eigen::Matrix LabHelper::white = + Eigen::Matrix(0.95047f, 1.f, 1.08883f); +} + + + + +inline Eigen::Vector3f cieLabFromCieXYZ(const Eigen::Vector3f& cieXYZ) +{ + typedef internal::LabHelper LH; + + float fy = LH::f(cieXYZ(1) / LH::white(1)); + return Eigen::Vector3f( + 1.16f * fy - 0.16f, + 5.00f * (LH::f(cieXYZ(0) / LH::white(0)) - fy), + 2.00f * (fy - LH::f(cieXYZ(2) / LH::white(2)))); +} + + +inline Eigen::Vector3f cieXYZFromCieLab(const Eigen::Vector3f& cielab) +{ + typedef internal::LabHelper LH; + + float lf = (cielab(0) + 0.16f) / 1.16f; + return Eigen::Vector3f( + LH::white(0) * LH::fInv(lf + cielab(1) / 5.00f), + LH::white(1) * LH::fInv(lf), + LH::white(2) * LH::fInv(lf - cielab(2) / 2.00f)); +} + + +// CIE XYZ <-> CIE LUV + +inline Eigen::Vector3f cieLuvFromCieXYZ(const Eigen::Vector3f& cieXYZ) +{ + const float wu = 0.197839825f; + const float wv = 0.468336303f; + + float l = (cieXYZ(1) <= 0.008856452f)? + 9.03296296296f * cieXYZ(1): + 1.16f * std::pow(cieXYZ(1), 1.f/3.f) - .16f; + float d = cieXYZ(0) + 15.f * cieXYZ(1) + 3.f * cieXYZ(2); + + return Eigen::Vector3f( + l, + (d > .001f)? 13.f * l * (4.f*cieXYZ(0) / d - wu): 0.f, + (d > .001f)? 13.f * l * (9.f*cieXYZ(1) / d - wv): 0.f); +} + + +inline Eigen::Vector3f cieXYZFromCieLuv(const Eigen::Vector3f& cieluv) +{ + const float wu = 0.197839825f; + const float wv = 0.468336303f; + + float up_13l = cieluv(1) + wu * (13.f * cieluv(0)); + float vp_13l = cieluv(2) + wv * (13.f * cieluv(0)); + + float y = (cieluv(0) <= .08f)? + cieluv(0) * 0.1107056f: + std::pow((cieluv(0)+.16f) / 1.16f, 3.f); + return Eigen::Vector3f( + (vp_13l > .001f)? 2.25f * y * up_13l / vp_13l: 0.f, + y, + (vp_13l > .001f)? y * (156.f*cieluv(0) - 3.f*up_13l - 20.f*vp_13l) / (4.f * vp_13l): 0.f); +} + + +inline ColorSpace colorSpaceFromName(const std::string& name, bool* ok=0) { + if(ok) *ok = true; + if( name == "none") return COLOR_NONE; + else if(name == "srgb") return COLOR_SRGB; + else if(name == "linear_rgb") return COLOR_LINEAR_RGB; + else if(name == "cie_xyz") return COLOR_CIE_XYZ; + else if(name == "cie_lab") return COLOR_CIE_LAB; + else if(name == "cie_luv") return COLOR_CIE_LUV; + + if(ok) *ok = false; + return COLOR_NONE; +} + + +inline const char* getColorSpaceName(ColorSpace cs) { + switch(cs) { + case COLOR_NONE: return "none"; + case COLOR_SRGB: return "srgb"; + case COLOR_LINEAR_RGB: return "linear_rgb"; + case COLOR_CIE_XYZ: return "cie_xyz"; + case COLOR_CIE_LAB: return "cie_lab"; + case COLOR_CIE_LUV: return "cie_luv"; + } + return "none"; +} + + +inline Eigen::Vector3f convertColor(const Eigen::Vector3f& fromColor, + ColorSpace from, ColorSpace to) +{ + if(from == COLOR_NONE || to == COLOR_NONE || from == to) + return fromColor; + + Eigen::Vector3f color = fromColor; + + // To XYZ + switch(from) { + case COLOR_NONE: + // Handled above. + break; + case COLOR_SRGB: + color = linearRGBFromSrgb(color); + if(to == COLOR_LINEAR_RGB) return color; + // Fall-through + case COLOR_LINEAR_RGB: + color = cieXYZFromLinearRGB(color); + break; + case COLOR_CIE_XYZ: + // Do nothing. + break; + case COLOR_CIE_LAB: + color = cieXYZFromCieLab(color); + break; + case COLOR_CIE_LUV: + color = cieXYZFromCieLuv(color); + break; + } + + // From XYZ + switch(to) { + case COLOR_NONE: + // Handled above. + break; + case COLOR_SRGB: + case COLOR_LINEAR_RGB: + color = linearRGBFromCieXYZ(color); + if(to == COLOR_SRGB) { + color = srgbFromLinearRGB(color); + } + break; + case COLOR_CIE_XYZ: + // Do nothing. + break; + case COLOR_CIE_LAB: + color = cieLabFromCieXYZ(color); + break; + case COLOR_CIE_LUV: + color = cieLuvFromCieXYZ(color); + break; + } + + return color; +} + + +} + + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/gl_utils/macros.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/gl_utils/macros.h new file mode 100644 index 000000000..4c28af503 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/gl_utils/macros.h @@ -0,0 +1,56 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _PATATE_COMMON_GL_UTILS_MACROS_ +#define _PATATE_COMMON_GL_UTILS_MACROS_ + + +#include +#include +#include + + +#ifndef PATATE_ASSERT_NO_GL_ERROR +#define PATATE_ASSERT_NO_GL_ERROR() _assertNoGlError(__FILE__, __LINE__) +#endif + +#define PATATE_FIELD_OFFSET(_struct, _field) &(static_cast<_struct*>(0)->_field) + +inline const char* glErrorString(GLenum error) +{ + switch(error) + { + case GL_NO_ERROR: + return "NO_ERROR"; + case GL_INVALID_ENUM: + return "INVALID_ENUM"; + case GL_INVALID_VALUE: + return "INVALID_VALUE"; + case GL_INVALID_OPERATION: + return "INVALID_OPERATION"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "INVALID_FRAMEBUFFER_OPERATION"; + case GL_OUT_OF_MEMORY: + return "OUT_OF_MEMORY"; + } + return "Unknown error"; +} + +inline void _assertNoGlError(const char* file, int line) +{ + GLenum error = glGetError(); + + if (error != GL_NO_ERROR) { + printf("OpenGL error in %s:%d: %s (0x%x)\n", + file, line, glErrorString(error), error); + abort(); + } +} + + + +#endif + diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/gl_utils/shader.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/gl_utils/shader.h new file mode 100644 index 000000000..7b47130ad --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/gl_utils/shader.h @@ -0,0 +1,77 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _PATATE_COMMON_GL_UTILS_SHADER_ +#define _PATATE_COMMON_GL_UTILS_SHADER_ + +#include +#include +#include +#include +#include + + +#include "macros.h" + + +namespace PatateCommon { + +class Shader +{ +public: + enum Status + { + UNINITIALIZED, + NOT_COMPILED, + COMPILATION_SUCCESSFULL, + COMPILATION_FAILED + }; + +public: + + inline Shader(); + inline virtual ~Shader(); + + inline bool create(); + inline void destroy(); + + inline void use(); + + inline Status status() const { return m_status; } + + inline void setGLSLVersionHeader(const std::string& header); + inline bool addShaderFromFile(GLenum _ShaderType, const char* _pFilename); + inline bool addShader(GLenum _ShaderType, const char* _pShaderText); + inline void clearShaderList(); + inline bool finalize(); + + inline GLuint getShaderId() { return m_shaderProg; } + + inline void bindAttributeLocation(const char* name, unsigned location); + inline GLint getUniformLocation(const char* _pUniformName); + inline GLint getProgramParam(GLint _param); + +protected: + GLuint m_shaderProg; + +private: + typedef std::list ShaderObjList; + + Status m_status; + std::string m_versionHeader; + ShaderObjList m_shaderObjList; +}; + +//#define INVALID_UNIFORM_LOCATION 0xFFFFFFFF + + +} + +#include "shader.hpp" + + +#endif + diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/gl_utils/shader.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/gl_utils/shader.hpp new file mode 100644 index 000000000..66bcf1b85 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/gl_utils/shader.hpp @@ -0,0 +1,246 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include "shader.h" + + +namespace PatateCommon +{ + + +static const char* pVSName = "VS"; +static const char* pTessCSName = "TessCS"; +static const char* pTessESName = "TessES"; +static const char* pGSName = "GS"; +static const char* pFSName = "FS"; + +inline const char* ShaderType2ShaderName(GLuint _Type) +{ + switch (_Type) + { + case GL_VERTEX_SHADER: + return pVSName; + case GL_TESS_CONTROL_SHADER: + return pTessCSName; + case GL_TESS_EVALUATION_SHADER: + return pTessESName; + case GL_GEOMETRY_SHADER: + return pGSName; + case GL_FRAGMENT_SHADER: + return pFSName; + default: + assert(0); + } + + return NULL; +} + +Shader::Shader() + : m_shaderProg(0), m_status(UNINITIALIZED) +{ +} + + +Shader::~Shader() +{ + if(getShaderId()) + { + destroy(); + } +} + + +bool Shader::create() +{ + m_shaderProg = glCreateProgram(); + + if (m_shaderProg == 0) + { + fprintf(stderr, "Error creating shader program\n"); + return false; + } + + m_status = NOT_COMPILED; + return true; +} + + +void Shader::destroy() +{ + PATATE_ASSERT_NO_GL_ERROR(); + + glDeleteProgram(m_shaderProg); + + if(glGetError() == GL_NO_ERROR) + m_shaderProg = 0; + m_status = UNINITIALIZED; +} + + +void Shader::use() +{ + glUseProgram(m_shaderProg); +} + + +void Shader::setGLSLVersionHeader(const std::string& header) +{ + m_versionHeader = header; +} + + +bool Shader::addShader(GLenum _ShaderType, const char* _pShaderText) +{ + PATATE_ASSERT_NO_GL_ERROR(); + + GLuint ShaderObj = glCreateShader(_ShaderType); + + if (ShaderObj == 0) + { + fprintf(stderr, "Error creating shader type %d\n", _ShaderType); + return false; + } + + m_shaderObjList.push_back(ShaderObj); + + const GLchar* p[2]; + p[0] = m_versionHeader.empty()? 0: &m_versionHeader[0]; + p[1] = _pShaderText; + GLint Lengths[2]; + Lengths[0] = m_versionHeader.size(); + Lengths[1] = strlen(_pShaderText); + glShaderSource(ShaderObj, 2, p, Lengths); + + glCompileShader(ShaderObj); + + GLint success; + glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &success); + + if (!success) + { + GLchar InfoLog[1024]; + glGetShaderInfoLog(ShaderObj, 1024, NULL, InfoLog); + fprintf(stderr, "Error compiling %s: '%s'\n", ShaderType2ShaderName(_ShaderType), InfoLog); + return false; + } + + glAttachShader(m_shaderProg, ShaderObj); + + return glGetError() == GL_NO_ERROR; +} + + +bool Shader::addShaderFromFile(GLenum _ShaderType, const char* _pFilename) +{ + FILE* fp; + size_t filesize; + char* pShaderText; + + fp = fopen(_pFilename, "rb"); + + if(!fp) + { + return 0; + } + + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + pShaderText = new char[filesize + 1]; + if(!pShaderText) + { + return 0; + } + + fread(pShaderText, 1, filesize, fp); + pShaderText[filesize] = 0; + fclose(fp); + + bool res = addShader(_ShaderType, pShaderText); + + delete [] pShaderText; + + return res; +} + + +void Shader::clearShaderList() +{ + for (ShaderObjList::iterator it = m_shaderObjList.begin() ; it != m_shaderObjList.end() ; it++) + { + glDetachShader(m_shaderProg, *it); + glDeleteShader(*it); + } + m_shaderObjList.clear(); +} + + +bool Shader::finalize() +{ + PATATE_ASSERT_NO_GL_ERROR(); + + GLint Success = 0; + GLchar ErrorLog[1024] = { 0 }; + + m_status = COMPILATION_FAILED; + + glLinkProgram(m_shaderProg); + + glGetProgramiv(m_shaderProg, GL_LINK_STATUS, &Success); + if (Success == 0) + { + glGetProgramInfoLog(m_shaderProg, sizeof(ErrorLog), NULL, ErrorLog); + fprintf(stderr, "Error linking shader program: '%s'\n", ErrorLog); + return false; + } + + glValidateProgram(m_shaderProg); + glGetProgramiv(m_shaderProg, GL_VALIDATE_STATUS, &Success); + if (!Success) + { + glGetProgramInfoLog(m_shaderProg, sizeof(ErrorLog), NULL, ErrorLog); + fprintf(stderr, "Invalid shader program: '%s'\n", ErrorLog); + return false; + } + + if(glGetError() == GL_NO_ERROR) + { + clearShaderList(); + m_status = COMPILATION_SUCCESSFULL; + } + + return m_status == COMPILATION_SUCCESSFULL; +} + + +void Shader::bindAttributeLocation(const char* name, unsigned location) +{ + glBindAttribLocation(m_shaderProg, location, name); +} + + +GLint Shader::getUniformLocation(const char* _pUniformName) +{ + GLint location = glGetUniformLocation(m_shaderProg, _pUniformName); + + if (location < 0) + { + fprintf(stderr, "Warning! Unable to get the location of uniform '%s'\n", _pUniformName); + } + + return location; +} + +GLint Shader::getProgramParam(GLint _param) +{ + GLint ret; + glGetProgramiv(m_shaderProg, _param, &ret); + return ret; +} + + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/objReader.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/objReader.h new file mode 100644 index 000000000..54d920774 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/objReader.h @@ -0,0 +1,114 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _PATATE_COMMON_SURFACE_MESH_OBJ_READER_ +#define _PATATE_COMMON_SURFACE_MESH_OBJ_READER_ + + +#include +#include +#include +#include +#include +#include + +#include "surfaceMesh.h" + + +namespace PatateCommon +{ + + +inline bool defaultErrorCallback (const std::string& msg, unsigned line, void* ptr); +inline bool defaultWarningCallback(const std::string& msg, unsigned line, void* ptr); + + +template < typename _Mesh > +class OBJBaseReader +{ +public: + typedef _Mesh Mesh; + typedef typename Mesh::Vector Vector; + + typedef bool (*ErrorCallback)(const std::string& msg, unsigned line, void* ptr); + +public: + inline OBJBaseReader() + : m_error(false), + m_errorCallback(defaultErrorCallback), + m_warningCallback(defaultWarningCallback), + m_errorCallbackPtr(0) {} + virtual ~OBJBaseReader() {} + + bool read(std::istream& in, Mesh& mesh); + + void setErrorCallback(ErrorCallback error, ErrorCallback warning, void* ptr); + +protected: + + virtual void parseHeader(std::istream& in, Mesh& mesh); + virtual bool parseDefinition(const std::string& spec, + std::istream& def, Mesh& mesh) = 0; + + inline bool readLine(std::istream& in); + inline void parseVector(std::istream& in); + inline void parseIndicesList(const std::string& _list, + std::vector& _indices); + + inline void error (const std::string& msg); + inline void warning(const std::string& msg); + +protected: + unsigned m_lineNb; + bool m_error; + + std::string m_line; + std::istringstream m_lineStream; + std::istringstream m_indicesStream; + Vector m_vector; + + ErrorCallback m_errorCallback; + ErrorCallback m_warningCallback; + void* m_errorCallbackPtr; +}; + + +template < typename _Mesh > +class OBJReader: public OBJBaseReader<_Mesh> +{ +public: + typedef _Mesh Mesh; + typedef OBJBaseReader Base; + + typedef typename Mesh::Vector Vector; + typedef typename Mesh::Vertex Vertex; + +public: + inline OBJReader(); + +protected: + using Base::parseVector; + using Base::error; + using Base::warning; + + virtual void parseHeader(std::istream& in, Mesh& mesh); + virtual bool parseDefinition(const std::string& spec, + std::istream& def, Mesh& mesh); + +protected: + using Base::m_vector; + + std::vector m_fVertices; + +}; + + +} // namespace PatateCommon + +#include "objReader.hpp" + + +#endif // _OBJREADER_H_ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/objReader.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/objReader.hpp new file mode 100644 index 000000000..c6264491d --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/objReader.hpp @@ -0,0 +1,245 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include + +#include "objReader.h" + + +namespace PatateCommon +{ + + +bool defaultErrorCallback(const std::string& msg, unsigned line, void* /*ptr*/) +{ + std::cerr << "Parse error: " << line << ": " << msg << "\n"; + return true; +} + + +bool defaultWarningCallback(const std::string& msg, unsigned line, void* /*ptr*/) +{ + std::cerr << "Warning: " << line << ": " << msg << "\n"; + return false; +} + + +template < typename _Mesh > +bool +OBJBaseReader<_Mesh>::read(std::istream& in, Mesh& mesh) +{ + m_lineNb = 0; + m_error = false; + m_line.reserve(256); + std::string spec; + + in.imbue(std::locale::classic()); + + if(!in.good()) + { + error("Can not read input"); + } + + readLine(in); + if(in.good() && !m_error) + parseHeader(in, mesh); + + while(in.good() && !m_error) + { + // comment + if(!m_line.empty() && m_line[0] != '#' && !std::isspace(m_line[0])) + { + m_lineStream >> spec; + parseDefinition(spec, m_lineStream, mesh); + } + + readLine(in); + } + + return !m_error; +} + + +template < typename _Mesh > +void +OBJBaseReader<_Mesh>::setErrorCallback(ErrorCallback error, + ErrorCallback warning, void* ptr) +{ + m_errorCallback = error; + m_warningCallback = warning; + m_errorCallbackPtr = ptr; +} + + +template < typename _Mesh > +void +OBJBaseReader<_Mesh>::parseHeader(std::istream& /*in*/, Mesh& /*mesh*/) +{ +} + + +template < typename _Mesh > +bool +OBJBaseReader<_Mesh>::readLine(std::istream& in) +{ + bool ok = std::getline(in, m_line).good(); + ++m_lineNb; + m_lineStream.str(m_line); + m_lineStream.seekg(0); + return ok; +} + + +template < typename _Mesh > +void +OBJBaseReader<_Mesh>::parseVector(std::istream& in) { + for(unsigned i = 0; i < m_vector.size(); ++i) { + in >> m_vector(i); + } + if(!in) error("Invalid point/vector specification"); + in >> std::ws; +} + + +template < typename _Mesh > +void +OBJBaseReader<_Mesh>::parseIndicesList(const std::string& _list, + std::vector& _indices) +{ + _indices.clear(); + m_indicesStream.str(_list); + m_indicesStream.seekg(0); + while(m_indicesStream.good()) + { + int i; + if(m_indicesStream.peek() == '/' || m_indicesStream.peek() == 'x') + { + i = -1; + m_indicesStream.get(); + } + else + m_indicesStream >> i; + _indices.push_back(i); + + if(m_indicesStream.good() && m_indicesStream.peek() == '/') + m_indicesStream.get(); + else if(!m_indicesStream.eof()) + error("Unexpected character in indices list"); + } + if(!m_indicesStream) + error("Failed to read indices list"); +} + + +template < typename _Mesh > +void +OBJBaseReader<_Mesh>::error(const std::string& msg) +{ + if(m_errorCallback) + { + m_error = m_errorCallback(msg, m_lineNb, m_errorCallbackPtr) || m_error; + } +} + + +template < typename _Mesh > +void +OBJBaseReader<_Mesh>::warning(const std::string& msg) +{ + if(m_warningCallback) + { + m_error = m_warningCallback(msg, m_lineNb, m_errorCallbackPtr) || m_error; + } +} + + +template < typename _Mesh > +OBJReader<_Mesh>::OBJReader() +{ +} + + +template < typename _Mesh > +void +OBJReader<_Mesh>::parseHeader(std::istream& /*in*/, Mesh& /*mesh*/) { + m_vector.resize(3); +} + + +//template < typename _Scalar, int _Dims, int _Coeffs > +//void +//OBJReader >::parseHeader( +// std::istream& /*in*/, Vitelotte::VGMesh<_Scalar, _Dims, _Coeffs>& mesh) { +// mesh.setNDims(3); +// m_vector.resize(3); +//} + + +template < typename _Mesh > +bool +OBJReader<_Mesh>::parseDefinition(const std::string& spec, + std::istream& def, Mesh& mesh) +{ + // vertex + if (spec == "v") + { + parseVector(def); + if(!def.eof()) warning("Too much components"); + mesh.addVertex(m_vector); + } + // normal +// else if (strncmp(s, "vn ", 3) == 0) +// { +// if (sscanf(s, "vn %f %f %f", &x, &y, &z)) +// { +// // problematic as it can be either a vertex property when interpolated +// // or a halfedge property for hard edges +// } +// } + + // texture coordinate +// else if (strncmp(s, "vt ", 3) == 0) +// { +// if (sscanf(s, "vt %f %f", &x, &y)) +// { +// z=1; +// all_tex_coords.push_back(Texture_coordinate(x,y,z)); +// } +// } + + // face + else if (spec == "f") + { + m_fVertices.clear(); + + def >> std::ws; + + while(def.good()) + { + // TODO: Use parseIndiceList to read indices + unsigned idx; + def >> idx; + idx -= 1; + if(!def || idx >= mesh.nVertices()) + { + error("Invalid vertex index"); + return true; + } + m_fVertices.push_back(SurfaceMesh::Vertex(idx)); + } + + mesh.addFace(m_fVertices); + } + else + { + warning("Unknown spec: " + spec); + return false; + } + return true; +} + + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/objWriter.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/objWriter.h new file mode 100644 index 000000000..33320e571 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/objWriter.h @@ -0,0 +1,42 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _PATATE_COMMON_SURFACE_MESH_OBJ_WRITER_ +#define _PATATE_COMMON_SURFACE_MESH_OBJ_WRITER_ + + +#include +#include +#include + +#include "surfaceMesh.h" + + +namespace PatateCommon +{ + + +template < typename _Mesh > +class OBJWriter +{ +public: + typedef _Mesh Mesh; + + typedef typename Mesh::Vector Vector; + +public: + inline OBJWriter(); + + void write(std::ostream& out, const Mesh& mesh); +}; + + +} // namespace Patate + +#include "objWriter.hpp" + + +#endif // _OBJWRITER_H_ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/objWriter.hpp b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/objWriter.hpp new file mode 100644 index 000000000..24443d34d --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/objWriter.hpp @@ -0,0 +1,50 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include "objWriter.h" + + +namespace PatateCommon +{ + + +template < typename _Mesh > +OBJWriter<_Mesh>::OBJWriter() +{ +} + +template < typename _Mesh > +void +OBJWriter<_Mesh>::write(std::ostream& out, const Mesh& mesh) +{ + out.imbue(std::locale::classic()); + + //vertices + for (typename Mesh::VertexIterator vit = mesh.verticesBegin(); + vit != mesh.verticesEnd(); ++vit) + { + out << "v " << mesh.position(*vit).transpose() << "\n"; + } + + //faces + for (typename Mesh::FaceIterator fit = mesh.facesBegin(); + fit != mesh.facesEnd(); ++fit) + { + out << "f"; + typename Mesh::VertexAroundFaceCirculator + fvit = mesh.vertices(*fit), + fvend = fvit; + do + { + out << " " << (*fvit).idx()+1; + } + while (++fvit != fvend); + out << "\n"; + } +} + + +} diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/properties.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/properties.h new file mode 100644 index 000000000..a1d4ba0e3 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/properties.h @@ -0,0 +1,438 @@ +//============================================================================= +// Copyright (C) 2001-2005 by Computer Graphics Group, RWTH Aachen +// Copyright (C) 2011-2013 by Graphics & Geometry Group, Bielefeld University +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public License +// as published by the Free Software Foundation, version 2. +// +// This library 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 +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +//============================================================================= + + +#ifndef _PATATE_COMMON_SURFACE_MESH_PROPERTIES_ +#define _PATATE_COMMON_SURFACE_MESH_PROPERTIES_ + + +//== INCLUDES ================================================================= + + +#include +#include +#include +#include +#include +#include + + +//== NAMESPACE ================================================================ + + +namespace PatateCommon { + + +//== CLASS DEFINITION ========================================================= + + +class BasePropertyArray +{ +public: + + /// Default constructor + BasePropertyArray(const std::string& name) : name_(name) {} + + /// Destructor. + virtual ~BasePropertyArray() {} + + /// Reserve memory for n elements. + virtual void reserve(size_t n) = 0; + + /// Resize storage to hold n elements. + virtual void resize(size_t n) = 0; + + /// Free unused memory. + virtual void freeMemory() = 0; + + /// Extend the number of elements by one. + virtual void pushBack() = 0; + + /// Let two elements swap their storage place. + virtual void swap(size_t i0, size_t i1) = 0; + + /// Return a deep copy of self. + virtual BasePropertyArray* clone () const = 0; + + /// Return the type_info of the property + virtual const std::type_info& type() = 0; + + /// Return the name of the property + const std::string& name() const { return name_; } + + +protected: + + std::string name_; +}; + + + +//== CLASS DEFINITION ========================================================= + + +template +class PropertyArray : public BasePropertyArray +{ +public: + + typedef T ValueType; + typedef std::vector VectorType; + typedef typename VectorType::reference Reference; + typedef typename VectorType::const_reference ConstReference; + + PropertyArray(const std::string& name, T t=T()) : BasePropertyArray(name), value_(t) {} + + +public: // virtual interface of BasePropertyArray + + virtual void reserve(size_t n) + { + data_.reserve(n); + } + + virtual void resize(size_t n) + { + data_.resize(n, value_); + } + + virtual void pushBack() + { + data_.push_back(value_); + } + + virtual void freeMemory() + { + VectorType(data_).swap(data_); + } + + virtual void swap(size_t i0, size_t i1) + { + T d(data_[i0]); + data_[i0]=data_[i1]; + data_[i1]=d; + } + + virtual BasePropertyArray* clone() const + { + PropertyArray* p = new PropertyArray(name_, value_); + p->data_ = data_; + return p; + } + + virtual const std::type_info& type() { return typeid(T); } + + +public: + + /// Get pointer to array (does not work for T==bool) + const T* data() const + { + return &data_[0]; + } + + + /// Get reference to the underlying vector + std::vector& vector() + { + return data_; + } + + + /// Access the i'th element. No range check is performed! + Reference operator[](int _idx) + { + assert( size_t(_idx) < data_.size() ); + return data_[_idx]; + } + + /// Const access to the i'th element. No range check is performed! + ConstReference operator[](int _idx) const + { + assert( size_t(_idx) < data_.size()); + return data_[_idx]; + } + + + +private: + VectorType data_; + ValueType value_; +}; + + +// specialization for bool properties +template <> +inline const bool* +PropertyArray::data() const +{ + assert(false); + return NULL; +} + + + +//== CLASS DEFINITION ========================================================= + + +template +class Property +{ +public: + + typedef typename PropertyArray::Reference Reference; + typedef typename PropertyArray::ConstReference ConstReference; + + friend class PropertyContainer; + friend class SurfaceMesh; + + +public: + + Property(PropertyArray* p=NULL) : parray_(p) {} + + void reset() + { + parray_ = NULL; + } + + operator bool() const + { + return parray_ != NULL; + } + + Reference operator[](int i) + { + assert(parray_ != NULL); + return (*parray_)[i]; + } + + ConstReference operator[](int i) const + { + assert(parray_ != NULL); + return (*parray_)[i]; + } + + const T* data() const + { + assert(parray_ != NULL); + return parray_->data(); + } + + + std::vector& vector() + { + assert(parray_ != NULL); + return parray_->vector(); + } + + +private: + + PropertyArray& array() + { + assert(parray_ != NULL); + return *parray_; + } + + const PropertyArray& array() const + { + assert(parray_ != NULL); + return *parray_; + } + + +private: + PropertyArray* parray_; +}; + + + +//== CLASS DEFINITION ========================================================= + + +class PropertyContainer +{ +public: + + // default constructor + PropertyContainer() : size_(0) {} + + // destructor (deletes all property arrays) + virtual ~PropertyContainer() { clear(); } + + // copy constructor: performs deep copy of property arrays + PropertyContainer(const PropertyContainer& _rhs) { operator=(_rhs); } + + // assignment: performs deep copy of property arrays + PropertyContainer& operator=(const PropertyContainer& _rhs) + { + if (this != &_rhs) + { + clear(); + parrays_.resize(_rhs.nProperties()); + size_ = _rhs.size(); + for (unsigned int i=0; iclone(); + } + return *this; + } + + // returns the current size of the property arrays + size_t size() const { return size_; } + + // returns the number of property arrays + size_t nProperties() const { return parrays_.size(); } + + // returns a vector of all property names + std::vector properties() const + { + std::vector names; + for (unsigned int i=0; iname()); + return names; + } + + + // add a property with name \c name and default value \c t + template Property add(const std::string& name, const T t=T()) + { + // if a property with this name already exists, return an invalid property + for (unsigned int i=0; iname() == name) + { + std::cerr << "[PropertyContainer] A property with name \"" + << name << "\" already exists. Returning invalid property.\n"; + return Property(); + } + } + + // otherwise add the property + PropertyArray* p = new PropertyArray(name, t); + p->resize(size_); + parrays_.push_back(p); + return Property(p); + } + + + // get a property by its name. returns invalid property if it does not exist. + template Property get(const std::string& name) const + { + for (unsigned int i=0; iname() == name) + return Property(dynamic_cast*>(parrays_[i])); + return Property(); + } + + + // returns a property if it exists, otherwise it creates it first. + template Property getOrAdd(const std::string& name, const T t=T()) + { + Property p = get(name); + if (!p) p = add(name, t); + return p; + } + + + // get the type of property by its name. returns typeid(void) if it does not exist. + const std::type_info& getType(const std::string& name) + { + for (unsigned int i=0; iname() == name) + return parrays_[i]->type(); + return typeid(void); + } + + + // delete a property + template void remove(Property& h) + { + std::vector::iterator it=parrays_.begin(), end=parrays_.end(); + for (; it!=end; ++it) + { + if (*it == h.parray_) + { + delete *it; + parrays_.erase(it); + h.reset(); + break; + } + } + } + + + // delete all properties + void clear() + { + for (unsigned int i=0; ireserve(n); + } + + // resize all arrays to size n + void resize(size_t n) + { + for (unsigned int i=0; iresize(n); + size_ = n; + } + + // free unused space in all arrays + void freeMemory() const + { + for (unsigned int i=0; ifreeMemory(); + } + + // add a new element to each vector + void pushBack() + { + for (unsigned int i=0; ipushBack(); + ++size_; + } + + // swap elements i0 and i1 in all arrays + void swap(size_t i0, size_t i1) const + { + for (unsigned int i=0; iswap(i0, i1); + } + + +private: + std::vector parrays_; + size_t size_; +}; + + +//============================================================================= +} // namespace Patate +//============================================================================= +#endif // SURFACE_MESH_PROPERTIES_H +//============================================================================= diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/surfaceMesh.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/surfaceMesh.h new file mode 100644 index 000000000..1c2174809 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/common/surface_mesh/surfaceMesh.h @@ -0,0 +1,4148 @@ +//============================================================================= +// Copyright (C) 2001-2005 by Computer Graphics Group, RWTH Aachen +// Copyright (C) 2011-2013 by Graphics & Geometry Group, Bielefeld University +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public License +// as published by the Free Software Foundation, version 2. +// +// This library 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 +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +//============================================================================= + + +#ifndef _PATATE_COMMON_SURFACE_MESH_SURFACE_MESH_ +#define _PATATE_COMMON_SURFACE_MESH_SURFACE_MESH_ + + +//== INCLUDES ================================================================= + + +#include "properties.h" +#include +#include +#include +#include +#include +#include + + +//== NAMESPACE ================================================================ + + +namespace PatateCommon { + + +//== CLASS DEFINITION ========================================================= + + +/// A halfedge data structure for polygonal meshes. +class SurfaceMesh +{ + +public: //------------------------------------------------------ topology types + + + /// Base class for all topology types (internally it is basically an index) + /// \sa Vertex, Halfedge, Edge, Face + class BaseHandle + { + public: + + /// constructor + explicit BaseHandle(int _idx=-1) : idx_(_idx) {} + + /// Get the underlying index of this handle + int idx() const { return idx_; } + + /// reset handle to be invalid (index=-1) + void reset() { idx_=-1; } + + /// return whether the handle is valid, i.e., the index is not equal to -1. + bool isValid() const { return idx_ != -1; } + + /// are two handles equal? + bool operator==(const BaseHandle& _rhs) const { + return idx_ == _rhs.idx_; + } + + /// are two handles different? + bool operator!=(const BaseHandle& _rhs) const { + return idx_ != _rhs.idx_; + } + + /// compare operator useful for sorting handles + bool operator<(const BaseHandle& _rhs) const { + return idx_ < _rhs.idx_; + } + + private: + friend class VertexIterator; + friend class HalfedgeIterator; + friend class EdgeIterator; + friend class FaceIterator; + friend class SurfaceMesh; + int idx_; + }; + + + /// this type represents a vertex (internally it is basically an index) + /// \sa Halfedge, Edge, Face + struct Vertex : public BaseHandle + { + /// default constructor (with invalid index) + explicit Vertex(int _idx=-1) : BaseHandle(_idx) {} + std::ostream& operator<<(std::ostream& os) const { return os << 'v' << idx(); } + }; + + + /// this type represents a halfedge (internally it is basically an index) + /// \sa Vertex, Edge, Face + struct Halfedge : public BaseHandle + { + /// default constructor (with invalid index) + explicit Halfedge(int _idx=-1) : BaseHandle(_idx) {} + }; + + + /// this type represents an edge (internally it is basically an index) + /// \sa Vertex, Halfedge, Face + struct Edge : public BaseHandle + { + /// default constructor (with invalid index) + explicit Edge(int _idx=-1) : BaseHandle(_idx) {} + }; + + + /// this type represents a face (internally it is basically an index) + /// \sa Vertex, Halfedge, Edge + struct Face : public BaseHandle + { + /// default constructor (with invalid index) + explicit Face(int _idx=-1) : BaseHandle(_idx) {} + }; + + + + +public: //-------------------------------------------------- connectivity types + + /// This type stores the vertex connectivity + /// \sa HalfedgeConnectivity, FaceConnectivity + struct VertexConnectivity + { + /// an outgoing halfedge per vertex (it will be a boundary halfedge for boundary vertices) + Halfedge halfedge; + }; + + + /// This type stores the halfedge connectivity + /// \sa VertexConnectivity, FaceConnectivity + struct HalfedgeConnectivity + { + /// face incident to halfedge + Face face; + /// vertex the halfedge points to + Vertex vertex; + /// next halfedge within a face (or along a boundary) + Halfedge nextHalfedge; + /// previous halfedge within a face (or along a boundary) + Halfedge prevHalfedge; + }; + + + /// This type stores the face connectivity + /// \sa VertexConnectivity, HalfedgeConnectivity + struct FaceConnectivity + { + /// a halfedge that is part of the face + Halfedge halfedge; + }; + + + + +public: //------------------------------------------------------ property types + + /// Vertex property of type T + /// \sa HalfedgeProperty, EdgeProperty, FaceProperty + template class VertexProperty : public Property + { + public: + + /// default constructor + explicit VertexProperty() {} + explicit VertexProperty(Property p) : Property(p) {} + + /// access the data stored for vertex \c v + typename Property::Reference operator[](Vertex v) + { + return Property::operator[](v.idx()); + } + + /// access the data stored for vertex \c v + typename Property::ConstReference operator[](Vertex v) const + { + return Property::operator[](v.idx()); + } + }; + + + /// Halfedge property of type T + /// \sa VertexProperty, EdgeProperty, FaceProperty + template class HalfedgeProperty : public Property + { + public: + + /// default constructor + explicit HalfedgeProperty() {} + explicit HalfedgeProperty(Property p) : Property(p) {} + + /// access the data stored for halfedge \c h + typename Property::Reference operator[](Halfedge h) + { + return Property::operator[](h.idx()); + } + + /// access the data stored for halfedge \c h + typename Property::ConstReference operator[](Halfedge h) const + { + return Property::operator[](h.idx()); + } + }; + + + /// Edge property of type T + /// \sa VertexProperty, HalfedgeProperty, FaceProperty + template class EdgeProperty : public Property + { + public: + + /// default constructor + explicit EdgeProperty() {} + explicit EdgeProperty(Property p) : Property(p) {} + + /// access the data stored for edge \c e + typename Property::Reference operator[](Edge e) + { + return Property::operator[](e.idx()); + } + + /// access the data stored for edge \c e + typename Property::ConstReference operator[](Edge e) const + { + return Property::operator[](e.idx()); + } + }; + + + /// Face property of type T + /// \sa VertexProperty, HalfedgeProperty, EdgeProperty + template class FaceProperty : public Property + { + public: + + /// default constructor + explicit FaceProperty() {} + explicit FaceProperty(Property p) : Property(p) {} + + /// access the data stored for face \c f + typename Property::Reference operator[](Face f) + { + return Property::operator[](f.idx()); + } + + /// access the data stored for face \c f + typename Property::ConstReference operator[](Face f) const + { + return Property::operator[](f.idx()); + } + }; + + + + +public: //------------------------------------------------------ iterator types + + /// this class iterates linearly over all vertices + /// \sa verticesBegin(), verticesEnd() + /// \sa HalfedgeIterator, EdgeIterator, FaceIterator + class VertexIterator + { + public: + + /// Default constructor + VertexIterator(Vertex v=Vertex(), const SurfaceMesh* m=NULL) : hnd_(v), mesh_(m) + { + if (mesh_ && mesh_->garbage()) while (mesh_->isValid(hnd_) && mesh_->isDeleted(hnd_)) ++hnd_.idx_; + } + + /// get the vertex the iterator refers to + Vertex operator*() const { return hnd_; } + + /// are two iterators equal? + bool operator==(const VertexIterator& rhs) const + { + return (hnd_==rhs.hnd_); + } + + /// are two iterators different? + bool operator!=(const VertexIterator& rhs) const + { + return !operator==(rhs); + } + + /// pre-increment iterator + VertexIterator& operator++() + { + ++hnd_.idx_; + assert(mesh_); + while (mesh_->garbage() && mesh_->isValid(hnd_) && mesh_->isDeleted(hnd_)) ++hnd_.idx_; + return *this; + } + + /// pre-decrement iterator + VertexIterator& operator--() + { + --hnd_.idx_; + assert(mesh_); + while (mesh_->garbage() && mesh_->isValid(hnd_) && mesh_->isDeleted(hnd_)) --hnd_.idx_; + return *this; + } + + private: + Vertex hnd_; + const SurfaceMesh* mesh_; + }; + + + /// this class iterates linearly over all halfedges + /// \sa halfedgesBegin(), halfedgesEnd() + /// \sa VertexIterator, EdgeIterator, FaceIterator + class HalfedgeIterator + { + public: + + /// Default constructor + HalfedgeIterator(Halfedge h=Halfedge(), const SurfaceMesh* m=NULL) : hnd_(h), mesh_(m) + { + if (mesh_ && mesh_->garbage()) while (mesh_->isValid(hnd_) && mesh_->isDeleted(hnd_)) ++hnd_.idx_; + } + + /// get the halfedge the iterator refers to + Halfedge operator*() const { return hnd_; } + + /// are two iterators equal? + bool operator==(const HalfedgeIterator& rhs) const + { + return (hnd_==rhs.hnd_); + } + + /// are two iterators different? + bool operator!=(const HalfedgeIterator& rhs) const + { + return !operator==(rhs); + } + + /// pre-increment iterator + HalfedgeIterator& operator++() + { + ++hnd_.idx_; + assert(mesh_); + while (mesh_->garbage() && mesh_->isValid(hnd_) && mesh_->isDeleted(hnd_)) ++hnd_.idx_; + return *this; + } + + /// pre-decrement iterator + HalfedgeIterator& operator--() + { + --hnd_.idx_; + assert(mesh_); + while (mesh_->garbage() && mesh_->isValid(hnd_) && mesh_->isDeleted(hnd_)) --hnd_.idx_; + return *this; + } + + private: + Halfedge hnd_; + const SurfaceMesh* mesh_; + }; + + + /// this class iterates linearly over all edges + /// \sa edgesBegin(), edgesEnd() + /// \sa VertexIterator, HalfedgeIterator, FaceIterator + class EdgeIterator + { + public: + + /// Default constructor + EdgeIterator(Edge e=Edge(), const SurfaceMesh* m=NULL) : hnd_(e), mesh_(m) + { + if (mesh_ && mesh_->garbage()) while (mesh_->isValid(hnd_) && mesh_->isDeleted(hnd_)) ++hnd_.idx_; + } + + /// get the edge the iterator refers to + Edge operator*() const { return hnd_; } + + /// are two iterators equal? + bool operator==(const EdgeIterator& rhs) const + { + return (hnd_==rhs.hnd_); + } + + /// are two iterators different? + bool operator!=(const EdgeIterator& rhs) const + { + return !operator==(rhs); + } + + /// pre-increment iterator + EdgeIterator& operator++() + { + ++hnd_.idx_; + assert(mesh_); + while (mesh_->garbage() && mesh_->isValid(hnd_) && mesh_->isDeleted(hnd_)) ++hnd_.idx_; + return *this; + } + + /// pre-decrement iterator + EdgeIterator& operator--() + { + --hnd_.idx_; + assert(mesh_); + while (mesh_->garbage() && mesh_->isValid(hnd_) && mesh_->isDeleted(hnd_)) --hnd_.idx_; + return *this; + } + + private: + Edge hnd_; + const SurfaceMesh* mesh_; + }; + + + /// this class iterates linearly over all faces + /// \sa facesBegin(), facesEnd() + /// \sa VertexIterator, HalfedgeIterator, EdgeIterator + class FaceIterator + { + public: + + /// Default constructor + FaceIterator(Face f=Face(), const SurfaceMesh* m=NULL) : hnd_(f), mesh_(m) + { + if (mesh_ && mesh_->garbage()) while (mesh_->isValid(hnd_) && mesh_->isDeleted(hnd_)) ++hnd_.idx_; + } + + /// get the face the iterator refers to + Face operator*() const { return hnd_; } + + /// are two iterators equal? + bool operator==(const FaceIterator& rhs) const + { + return (hnd_==rhs.hnd_); + } + + /// are two iterators different? + bool operator!=(const FaceIterator& rhs) const + { + return !operator==(rhs); + } + + /// pre-increment iterator + FaceIterator& operator++() + { + ++hnd_.idx_; + assert(mesh_); + while (mesh_->garbage() && mesh_->isValid(hnd_) && mesh_->isDeleted(hnd_)) ++hnd_.idx_; + return *this; + } + + /// pre-decrement iterator + FaceIterator& operator--() + { + --hnd_.idx_; + assert(mesh_); + while (mesh_->garbage() && mesh_->isValid(hnd_) && mesh_->isDeleted(hnd_)) --hnd_.idx_; + return *this; + } + + private: + Face hnd_; + const SurfaceMesh* mesh_; + }; + + + +public: //-------------------------- containers for C++11 range-based for loops + + /// this helper class is a container for iterating through all + /// vertices using C++11 range-based for-loops. + /// \sa vertices() + class VertexContainer + { + public: + VertexContainer(VertexIterator _begin, VertexIterator _end) : begin_(_begin), end_(_end) {} + VertexIterator begin() const { return begin_; } + VertexIterator end() const { return end_; } + private: + VertexIterator begin_, end_; + }; + + + + /// this helper class is a container for iterating through all + /// halfedge using C++11 range-based for-loops. + /// \sa halfedges() + class HalfedgeContainer + { + public: + HalfedgeContainer(HalfedgeIterator _begin, HalfedgeIterator _end) : begin_(_begin), end_(_end) {} + HalfedgeIterator begin() const { return begin_; } + HalfedgeIterator end() const { return end_; } + private: + HalfedgeIterator begin_, end_; + }; + + + + /// this helper class is a container for iterating through all + /// edges using C++11 range-based for-loops. + /// \sa edges() + class EdgeContainer + { + public: + EdgeContainer(EdgeIterator _begin, EdgeIterator _end) : begin_(_begin), end_(_end) {} + EdgeIterator begin() const { return begin_; } + EdgeIterator end() const { return end_; } + private: + EdgeIterator begin_, end_; + }; + + + + /// this helper class is a container for iterating through all + /// faces using C++11 range-based for-loops. + /// \sa faces() + class FaceContainer + { + public: + FaceContainer(FaceIterator _begin, FaceIterator _end) : begin_(_begin), end_(_end) {} + FaceIterator begin() const { return begin_; } + FaceIterator end() const { return end_; } + private: + FaceIterator begin_, end_; + }; + + + + + +public: //---------------------------------------------------- circulator types + + /// this class circulates through all one-ring neighbors of a vertex. + /// it also acts as a container-concept for C++11 range-based for loops. + /// \sa HalfedgeAroundVertexCirculator, FaceAroundVertexCirculator, vertices(Vertex) + class VertexAroundVertexCirculator + { + public: + + /// default constructor + VertexAroundVertexCirculator(const SurfaceMesh* m=NULL, Vertex v=Vertex()) + : mesh_(m), active_(true) + { + if (mesh_) halfedge_ = mesh_->halfedge(v); + } + + /// are two circulators equal? + bool operator==(const VertexAroundVertexCirculator& rhs) const + { + assert(mesh_); + return (active_ && (mesh_==rhs.mesh_) && (halfedge_==rhs.halfedge_)); + } + + /// are two circulators different? + bool operator!=(const VertexAroundVertexCirculator& rhs) const + { + return !operator==(rhs); + } + + /// pre-increment (rotate couter-clockwise) + VertexAroundVertexCirculator& operator++() + { + assert(mesh_); + halfedge_ = mesh_->ccwRotatedHalfedge(halfedge_); + active_ = true; + return *this; + } + + /// pre-decrement (rotate clockwise) + VertexAroundVertexCirculator& operator--() + { + assert(mesh_); + halfedge_ = mesh_->cwRotatedHalfedge(halfedge_); + return *this; + } + + /// get the vertex the circulator refers to + Vertex operator*() const + { + assert(mesh_); + return mesh_->toVertex(halfedge_); + } + + /// cast to bool: true if vertex is not isolated + operator bool() const { return halfedge_.isValid(); } + + /// return current halfedge + Halfedge halfedge() const { return halfedge_; } + + // helper for C++11 range-based for-loops + VertexAroundVertexCirculator& begin() { active_=!halfedge_.isValid(); return *this; } + // helper for C++11 range-based for-loops + VertexAroundVertexCirculator& end() { active_=true; return *this; } + + private: + const SurfaceMesh* mesh_; + Halfedge halfedge_; + // helper for C++11 range-based for-loops + bool active_; + }; + + + /// this class circulates through all outgoing halfedges of a vertex. + /// it also acts as a container-concept for C++11 range-based for loops. + /// \sa VertexAroundVertexCirculator, FaceAroundVertexCirculator, halfedges(Vertex) + class HalfedgeAroundVertexCirculator + { + public: + + /// default constructor + HalfedgeAroundVertexCirculator(const SurfaceMesh* m=NULL, Vertex v=Vertex()) + : mesh_(m), active_(true) + { + if (mesh_) halfedge_ = mesh_->halfedge(v); + } + + /// are two circulators equal? + bool operator==(const HalfedgeAroundVertexCirculator& rhs) const + { + assert(mesh_); + return (active_ && (mesh_==rhs.mesh_) && (halfedge_==rhs.halfedge_)); + } + + /// are two circulators different? + bool operator!=(const HalfedgeAroundVertexCirculator& rhs) const + { + return !operator==(rhs); + } + + /// pre-increment (rotate couter-clockwise) + HalfedgeAroundVertexCirculator& operator++() + { + assert(mesh_); + halfedge_ = mesh_->ccwRotatedHalfedge(halfedge_); + active_ = true; + return *this; + } + + /// pre-decrement (rotate clockwise) + HalfedgeAroundVertexCirculator& operator--() + { + assert(mesh_); + halfedge_ = mesh_->cwRotatedHalfedge(halfedge_); + return *this; + } + + /// get the halfedge the circulator refers to + Halfedge operator*() const { return halfedge_; } + + /// cast to bool: true if vertex is not isolated + operator bool() const { return halfedge_.isValid(); } + + // helper for C++11 range-based for-loops + HalfedgeAroundVertexCirculator& begin() { active_=!halfedge_.isValid(); return *this; } + // helper for C++11 range-based for-loops + HalfedgeAroundVertexCirculator& end() { active_=true; return *this; } + + private: + const SurfaceMesh* mesh_; + Halfedge halfedge_; + // helper for C++11 range-based for-loops + bool active_; + }; + + + /// this class circulates through all incident faces of a vertex. + /// it also acts as a container-concept for C++11 range-based for loops. + /// \sa VertexAroundVertexCirculator, HalfedgeAroundVertexCirculator, faces(Vertex) + class FaceAroundVertexCirculator + { + public: + + /// construct with mesh and vertex (vertex should not be isolated!) + FaceAroundVertexCirculator(const SurfaceMesh* m=NULL, Vertex v=Vertex()) + : mesh_(m), active_(true) + { + if (mesh_) + { + halfedge_ = mesh_->halfedge(v); + if (halfedge_.isValid() && mesh_->isBoundary(halfedge_)) + operator++(); + } + } + + /// are two circulators equal? + bool operator==(const FaceAroundVertexCirculator& rhs) const + { + assert(mesh_); + return (active_ && (mesh_==rhs.mesh_) && (halfedge_==rhs.halfedge_)); + } + + /// are two circulators different? + bool operator!=(const FaceAroundVertexCirculator& rhs) const + { + return !operator==(rhs); + } + + /// pre-increment (rotates counter-clockwise) + FaceAroundVertexCirculator& operator++() + { + assert(mesh_ && halfedge_.isValid()); + do { + halfedge_ = mesh_->ccwRotatedHalfedge(halfedge_); + } while (mesh_->isBoundary(halfedge_)); + active_ = true; + return *this; + } + + /// pre-decrement (rotate clockwise) + FaceAroundVertexCirculator& operator--() + { + assert(mesh_ && halfedge_.isValid()); + do + halfedge_ = mesh_->cwRotatedHalfedge(halfedge_); + while (mesh_->isBoundary(halfedge_)); + return *this; + } + + /// get the face the circulator refers to + Face operator*() const + { + assert(mesh_ && halfedge_.isValid()); + return mesh_->face(halfedge_); + } + + /// cast to bool: true if vertex is not isolated + operator bool() const { return halfedge_.isValid(); } + + // helper for C++11 range-based for-loops + FaceAroundVertexCirculator& begin() { active_=!halfedge_.isValid(); return *this; } + // helper for C++11 range-based for-loops + FaceAroundVertexCirculator& end() { active_=true; return *this; } + + private: + const SurfaceMesh* mesh_; + Halfedge halfedge_; + // helper for C++11 range-based for-loops + bool active_; + }; + + + /// this class circulates through the vertices of a face. + /// it also acts as a container-concept for C++11 range-based for loops. + /// \sa HalfedgeAroundFaceCirculator, vertices(Face) + class VertexAroundFaceCirculator + { + public: + + /// default constructor + VertexAroundFaceCirculator(const SurfaceMesh* m=NULL, Face f=Face()) + : mesh_(m), active_(true) + { + if (mesh_) halfedge_ = mesh_->halfedge(f); + } + + /// are two circulators equal? + bool operator==(const VertexAroundFaceCirculator& rhs) const + { + assert(mesh_); + return (active_ && (mesh_==rhs.mesh_) && (halfedge_==rhs.halfedge_)); + } + + /// are two circulators different? + bool operator!=(const VertexAroundFaceCirculator& rhs) const + { + return !operator==(rhs); + } + + /// pre-increment (rotates counter-clockwise) + VertexAroundFaceCirculator& operator++() + { + assert(mesh_ && halfedge_.isValid()); + halfedge_ = mesh_->nextHalfedge(halfedge_); + active_ = true; + return *this; + } + + /// pre-decrement (rotates clockwise) + VertexAroundFaceCirculator& operator--() + { + assert(mesh_ && halfedge_.isValid()); + halfedge_ = mesh_->prevHalfedge(halfedge_); + return *this; + } + + /// get the vertex the circulator refers to + Vertex operator*() const + { + assert(mesh_ && halfedge_.isValid()); + return mesh_->toVertex(halfedge_); + } + + // helper for C++11 range-based for-loops + VertexAroundFaceCirculator& begin() { active_=false; return *this; } + // helper for C++11 range-based for-loops + VertexAroundFaceCirculator& end() { active_=true; return *this; } + + private: + const SurfaceMesh* mesh_; + Halfedge halfedge_; + // helper for C++11 range-based for-loops + bool active_; + }; + + + /// this class circulates through all halfedges of a face. + /// it also acts as a container-concept for C++11 range-based for loops. + /// \sa VertexAroundFaceCirculator, halfedges(Face) + class HalfedgeAroundFaceCirculator + { + public: + + /// default constructur + HalfedgeAroundFaceCirculator(const SurfaceMesh* m=NULL, Face f=Face()) + : mesh_(m), active_(true) + { + if (mesh_) halfedge_ = mesh_->halfedge(f); + } + + /// are two circulators equal? + bool operator==(const HalfedgeAroundFaceCirculator& rhs) const + { + assert(mesh_); + return (active_ && (mesh_==rhs.mesh_) && (halfedge_==rhs.halfedge_)); + } + + /// are two circulators different? + bool operator!=(const HalfedgeAroundFaceCirculator& rhs) const + { + return !operator==(rhs); + } + + /// pre-increment (rotates counter-clockwise) + HalfedgeAroundFaceCirculator& operator++() + { + assert(mesh_ && halfedge_.isValid()); + halfedge_ = mesh_->nextHalfedge(halfedge_); + active_ = true; + return *this; + } + + /// pre-decrement (rotates clockwise) + HalfedgeAroundFaceCirculator& operator--() + { + assert(mesh_ && halfedge_.isValid()); + halfedge_ = mesh_->prevHalfedge(halfedge_); + return *this; + } + + /// get the halfedge the circulator refers to + Halfedge operator*() const { return halfedge_; } + + // helper for C++11 range-based for-loops + HalfedgeAroundFaceCirculator& begin() { active_=false; return *this; } + // helper for C++11 range-based for-loops + HalfedgeAroundFaceCirculator& end() { active_=true; return *this; } + + private: + const SurfaceMesh* mesh_; + Halfedge halfedge_; + // helper for C++11 range-based for-loops + bool active_; + }; + + +public: + enum + { + GC_DONT_RELEASE_INDEX_MAPS = 0x01 + }; + + +public: //-------------------------------------------- constructor / destructor + + /// \name Construct, destruct, assignment + //@{ + + /// default constructor + inline SurfaceMesh(); + + inline ~SurfaceMesh(); + + /// copy constructor: copies \c rhs to \c *this. performs a deep copy of all properties. + SurfaceMesh(const SurfaceMesh& rhs) { operator=(rhs); } + + /// assign \c rhs to \c *this. performs a deep copy of all properties. + inline SurfaceMesh& operator=(const SurfaceMesh& rhs); + + /// assign \c rhs to \c *this. does not copy custom properties. + inline SurfaceMesh& assign(const SurfaceMesh& rhs); + + //@} + + + + +//public: //------------------------------------------------------------- file IO + +// /// \name File IO +// //@{ + +// /// read mesh from file \c filename. file extension determines file type. +// /// \sa write(const std::string& filename) +// inline bool read(const std::string& filename); + +// /// write mesh to file \c filename. file extensions determines file type. +// /// \sa read(const std::string& filename) +// inline bool write(const std::string& filename) const; + +// //@} + + + + +public: //----------------------------------------------- add new vertex / face + + /// \name Add new elements by hand + //@{ + + /// add a new vertex + inline Vertex addVertex(); + + /// add a new face with vertex list \c vertices + /// \sa addTriangle, addQuad + inline Face addFace(const std::vector& vertices); + + /// add a new triangle connecting vertices \c v1, \c v2, \c v3 + /// \sa addFace, addQuad + inline Face addTriangle(Vertex v1, Vertex v2, Vertex v3); + + /// add a new quad connecting vertices \c v1, \c v2, \c v3, \c v4 + /// \sa addTriangle, addFace + inline Face addQuad(Vertex v1, Vertex v2, Vertex v3, Vertex v4); + + //@} + + + + +public: //--------------------------------------------------- memory management + + /// \name Memory Management + //@{ + + /// returns number of (deleted and valid) vertices in the mesh + unsigned int verticesSize() const { return (unsigned int) m_vprops.size(); } + /// returns number of (deleted and valid)halfedge in the mesh + unsigned int halfedgesSize() const { return (unsigned int) m_hprops.size(); } + /// returns number of (deleted and valid)edges in the mesh + unsigned int edgesSize() const { return (unsigned int) m_eprops.size(); } + /// returns number of (deleted and valid)faces in the mesh + unsigned int facesSize() const { return (unsigned int) m_fprops.size(); } + + + /// returns number of vertices in the mesh + unsigned int nVertices() const { return verticesSize() - m_deletedVertices; } + /// returns number of halfedge in the mesh + unsigned int nHalfedges() const { return halfedgesSize() - 2*m_deletedEdges; } + /// returns number of edges in the mesh + unsigned int nEdges() const { return edgesSize() - m_deletedEdges; } + /// returns number of faces in the mesh + unsigned int nFaces() const { return facesSize() - m_deleteFaces; } + + + /// returns true iff the mesh is empty, i.e., has no vertices + unsigned int empty() const { return nVertices() == 0; } + + + /// clear mesh: remove all vertices, edges, faces + inline void clear(); + + /// remove unused memory from vectors + inline void freeMemory(); + + /// reserve memory (mainly used in file readers) + inline void reserve(unsigned int nvertices, + unsigned int nedges, + unsigned int nfaces ); + + + /// remove deleted vertices/edges/faces + inline void garbageCollection(unsigned flags = 0); + + inline void releaseGCIndexMaps() + { + std::vector vMap; + std::vector hMap; + std::vector fMap; + m_gcVertexMap .swap(vMap); + m_gcHalfedgeMap.swap(hMap); + m_gcFaceMap .swap(fMap); + } + + inline Vertex gcMap(Vertex v) + { + assert(v.isValid() && v.idx() < int(m_gcVertexMap.size())); + return m_gcVertexMap[v.idx()]; + } + + inline Halfedge gcMap(Halfedge h) + { + assert(h.isValid() && h.idx() < int(m_gcHalfedgeMap.size())); + return m_gcHalfedgeMap[h.idx()]; + } + + inline Face gcMap(Face f) + { + assert(f.isValid() && f.idx() < int(m_gcFaceMap.size())); + return m_gcFaceMap[f.idx()]; + } + + + /// returns whether vertex \c v is deleted + /// \sa garbageCollection() + bool isDeleted(Vertex v) const + { + return m_vdeleted[v]; + } + /// returns whether halfedge \c h is deleted + /// \sa garbageCollection() + bool isDeleted(Halfedge h) const + { + return m_edeleted[edge(h)]; + } + /// returns whether edge \c e is deleted + /// \sa garbageCollection() + bool isDeleted(Edge e) const + { + return m_edeleted[e]; + } + /// returns whether face \c f is deleted + /// \sa garbageCollection() + bool isDeleted(Face f) const + { + return m_fdeleted[f]; + } + + + /// return whether vertex \c v is valid, i.e. the index is stores it within the array bounds. + bool isValid(Vertex v) const + { + return (0 <= v.idx()) && (v.idx() < (int)verticesSize()); + } + /// return whether halfedge \c h is valid, i.e. the index is stores it within the array bounds. + bool isValid(Halfedge h) const + { + return (0 <= h.idx()) && (h.idx() < (int)halfedgesSize()); + } + /// return whether edge \c e is valid, i.e. the index is stores it within the array bounds. + bool isValid(Edge e) const + { + return (0 <= e.idx()) && (e.idx() < (int)edgesSize()); + } + /// return whether face \c f is valid, i.e. the index is stores it within the array bounds. + bool isValid(Face f) const + { + return (0 <= f.idx()) && (f.idx() < (int)facesSize()); + } + + //@} + + + + +public: //---------------------------------------------- low-level connectivity + + /// \name Low-level connectivity + //@{ + + /// returns an outgoing halfedge of vertex \c v. + /// if \c v is a boundary vertex this will be a boundary halfedge. + Halfedge halfedge(Vertex v) const + { + return m_vconn[v].halfedge; + } + + /// set the outgoing halfedge of vertex \c v to \c h + void setHalfedge(Vertex v, Halfedge h) + { + m_vconn[v].halfedge = h; + } + + /// returns whether \c v is a boundary vertex + bool isBoundary(Vertex v) const + { + Halfedge h(halfedge(v)); + return (!(h.isValid() && face(h).isValid())); + } + + /// returns whether \c v is isolated, i.e., not incident to any face + bool isIsolated(Vertex v) const + { + return !halfedge(v).isValid(); + } + + /// returns whether \c v is a manifold vertex (not incident to several patches) + bool isManifold(Vertex v) const + { + // The vertex is non-manifold if more than one gap exists, i.e. + // more than one outgoing boundary halfedge. + int n(0); + HalfedgeAroundVertexCirculator hit = halfedges(v), hend=hit; + if (hit) do + { + if (isBoundary(*hit)) + ++n; + } + while (++hit!=hend); + return n<2; + } + + /// returns the vertex the halfedge \c h points to + Vertex toVertex(Halfedge h) const + { + return m_hconn[h].vertex; + } + + /// returns the vertex the halfedge \c h emanates from + Vertex fromVertex(Halfedge h) const + { + return toVertex(oppositeHalfedge(h)); + } + + /// sets the vertex the halfedge \c h points to to \c v + void setVertex(Halfedge h, Vertex v) + { + m_hconn[h].vertex = v; + } + + /// returns the face incident to halfedge \c h + Face face(Halfedge h) const + { + return m_hconn[h].face; + } + + /// sets the incident face to halfedge \c h to \c f + void setFace(Halfedge h, Face f) + { + m_hconn[h].face = f; + } + + /// returns the next halfedge within the incident face + Halfedge nextHalfedge(Halfedge h) const + { + return m_hconn[h].nextHalfedge; + } + + /// sets the next halfedge of \c h within the face to \c nh + void setNextHalfedge(Halfedge h, Halfedge nh) + { + m_hconn[h].nextHalfedge = nh; + m_hconn[nh].prevHalfedge = h; + } + + /// returns the previous halfedge within the incident face + Halfedge prevHalfedge(Halfedge h) const + { + return m_hconn[h].prevHalfedge; + } + + /// returns the opposite halfedge of \c h + Halfedge oppositeHalfedge(Halfedge h) const + { + return Halfedge((h.idx() & 1) ? h.idx()-1 : h.idx()+1); + } + + /// returns the halfedge that is rotated counter-clockwise around the + /// start vertex of \c h. it is the opposite halfedge of the previous halfedge of \c h. + Halfedge ccwRotatedHalfedge(Halfedge h) const + { + return oppositeHalfedge(prevHalfedge(h)); + } + + /// returns the halfedge that is rotated clockwise around the + /// start vertex of \c h. it is the next halfedge of the opposite halfedge of \c h. + Halfedge cwRotatedHalfedge(Halfedge h) const + { + return nextHalfedge(oppositeHalfedge(h)); + } + + /// return the edge that contains halfedge \c h as one of its two halfedges. + Edge edge(Halfedge h) const + { + return Edge(h.idx() >> 1); + } + + /// returns whether h is a boundary halfege, i.e., if its face does not exist. + bool isBoundary(Halfedge h) const + { + return !face(h).isValid(); + } + + + /// returns the \c i'th halfedge of edge \c e. \c i has to be 0 or 1. + Halfedge halfedge(Edge e, unsigned int i) const + { + assert(i<=1); + return Halfedge((e.idx() << 1) + i); + } + + /// returns the \c i'th vertex of edge \c e. \c i has to be 0 or 1. + Vertex vertex(Edge e, unsigned int i) const + { + assert(i<=1); + return toVertex(halfedge(e, i)); + } + + /// returns the face incident to the \c i'th halfedge of edge \c e. \c i has to be 0 or 1. + Face face(Edge e, unsigned int i) const + { + assert(i<=1); + return face(halfedge(e, i)); + } + + /// returns whether \c e is a boundary edge, i.e., if one of its + /// halfedges is a boundary halfedge. + bool isBoundary(Edge e) const + { + return (isBoundary(halfedge(e, 0)) || isBoundary(halfedge(e, 1))); + } + + /// returns a halfedge of face \c f + Halfedge halfedge(Face f) const + { + return m_fconn[f].halfedge; + } + + /// sets the halfedge of face \c f to \c h + void setHalfedge(Face f, Halfedge h) + { + m_fconn[f].halfedge = h; + } + + /// returns whether \c f is a boundary face, i.e., it one of its edges is a boundary edge. + bool isBoundary(Face f) const + { + Halfedge h = halfedge(f); + Halfedge hh = h; + do + { + if (isBoundary(oppositeHalfedge(h))) + return true; + h = nextHalfedge(h); + } + while (h != hh); + return false; + } + + //@} + + + + +public: //--------------------------------------------------- property handling + + /// \name Property handling + //@{ + + /** add a vertex property of type \c T with name \c name and default value \c t. + fails if a property named \c name exists already, since the name has to be unique. + in this case it returns an invalid property */ + template VertexProperty addVertexProperty(const std::string& name, const T t=T()) + { + return VertexProperty(m_vprops.add(name, t)); + } + /** add a halfedge property of type \c T with name \c name and default value \c t. + fails if a property named \c name exists already, since the name has to be unique. + in this case it returns an invalid property */ + template HalfedgeProperty addHalfedgeProperty(const std::string& name, const T t=T()) + { + return HalfedgeProperty(m_hprops.add(name, t)); + } + /** add a edge property of type \c T with name \c name and default value \c t. + fails if a property named \c name exists already, since the name has to be unique. + in this case it returns an invalid property */ + template EdgeProperty addEdgeProperty(const std::string& name, const T t=T()) + { + return EdgeProperty(m_eprops.add(name, t)); + } + /** add a face property of type \c T with name \c name and default value \c t. + fails if a property named \c name exists already, since the name has to be unique. + in this case it returns an invalid property */ + template FaceProperty addFaceProperty(const std::string& name, const T t=T()) + { + return FaceProperty(m_fprops.add(name, t)); + } + + + /** get the vertex property named \c name of type \c T. returns an invalid + VertexProperty if the property does not exist or if the type does not match. */ + template VertexProperty getVertexProperty(const std::string& name) const + { + return VertexProperty(m_vprops.get(name)); + } + /** get the halfedge property named \c name of type \c T. returns an invalid + VertexProperty if the property does not exist or if the type does not match. */ + template HalfedgeProperty getHalfedgeProperty(const std::string& name) const + { + return HalfedgeProperty(m_hprops.get(name)); + } + /** get the edge property named \c name of type \c T. returns an invalid + VertexProperty if the property does not exist or if the type does not match. */ + template EdgeProperty getEdgeProperty(const std::string& name) const + { + return EdgeProperty(m_eprops.get(name)); + } + /** get the face property named \c name of type \c T. returns an invalid + VertexProperty if the property does not exist or if the type does not match. */ + template FaceProperty getFaceProperty(const std::string& name) const + { + return FaceProperty(m_fprops.get(name)); + } + + + /** if a vertex property of type \c T with name \c name exists, it is returned. + otherwise this property is added (with default value \c t) */ + template VertexProperty vertexProperty(const std::string& name, const T t=T()) + { + return VertexProperty(m_vprops.getOrAdd(name, t)); + } + /** if a halfedge property of type \c T with name \c name exists, it is returned. + otherwise this property is added (with default value \c t) */ + template HalfedgeProperty halfedgeProperty(const std::string& name, const T t=T()) + { + return HalfedgeProperty(m_hprops.getOrAdd(name, t)); + } + /** if an edge property of type \c T with name \c name exists, it is returned. + otherwise this property is added (with default value \c t) */ + template EdgeProperty edgeProperty(const std::string& name, const T t=T()) + { + return EdgeProperty(m_eprops.getOrAdd(name, t)); + } + /** if a face property of type \c T with name \c name exists, it is returned. + otherwise this property is added (with default value \c t) */ + template FaceProperty faceProperty(const std::string& name, const T t=T()) + { + return FaceProperty(m_fprops.getOrAdd(name, t)); + } + + + /// remove the vertex property \c p + template void removeVertexProperty(VertexProperty& p) + { + m_vprops.remove(p); + } + /// remove the halfedge property \c p + template void removeHalfedgeProperty(HalfedgeProperty& p) + { + m_hprops.remove(p); + } + /// remove the edge property \c p + template void removeEdgeProperty(EdgeProperty& p) + { + m_eprops.remove(p); + } + /// remove the face property \c p + template void removeFaceProperty(FaceProperty& p) + { + m_fprops.remove(p); + } + + + /** get the type_info \c T of vertex property named \c name. returns an typeid(void) + if the property does not exist or if the type does not match. */ + const std::type_info& getVertexPropertyType(const std::string& name) + { + return m_vprops.getType(name); + } + /** get the type_info \c T of halfedge property named \c name. returns an typeid(void) + if the property does not exist or if the type does not match. */ + const std::type_info& getHalfedgePropertyType(const std::string& name) + { + return m_hprops.getType(name); + } + /** get the type_info \c T of edge property named \c name. returns an typeid(void) + if the property does not exist or if the type does not match. */ + const std::type_info& getEdgePropertyType(const std::string& name) + { + return m_eprops.getType(name); + } + /** get the type_info \c T of face property named \c name. returns an typeid(void) + if the property does not exist or if the type does not match. */ + const std::type_info& getFacePropertyType(const std::string& name) + { + return m_fprops.getType(name); + } + + + /// returns the names of all vertex properties + std::vector vertexProperties() const + { + return m_vprops.properties(); + } + /// returns the names of all halfedge properties + std::vector halfedgeProperties() const + { + return m_hprops.properties(); + } + /// returns the names of all edge properties + std::vector edgeProperties() const + { + return m_eprops.properties(); + } + /// returns the names of all face properties + std::vector faceProperties() const + { + return m_fprops.properties(); + } + /// prints the names of all properties + inline void propertyStats() const; + + //@} + + + + +public: //--------------------------------------------- iterators & circulators + + /// \name Iterators & Circulators + //@{ + + /// returns start iterator for vertices + VertexIterator verticesBegin() const + { + return VertexIterator(Vertex(0), this); + } + + /// returns end iterator for vertices + VertexIterator verticesEnd() const + { + return VertexIterator(Vertex(verticesSize()), this); + } + + /// returns vertex container for C++11 range-based for-loops + VertexContainer vertices() const + { + return VertexContainer(verticesBegin(), verticesEnd()); + } + + /// returns start iterator for halfedges + HalfedgeIterator halfedgesBegin() const + { + return HalfedgeIterator(Halfedge(0), this); + } + + /// returns end iterator for halfedges + HalfedgeIterator halfedgesEnd() const + { + return HalfedgeIterator(Halfedge(halfedgesSize()), this); + } + + /// returns halfedge container for C++11 range-based for-loops + HalfedgeContainer halfedges() const + { + return HalfedgeContainer(halfedgesBegin(), halfedgesEnd()); + } + + /// returns start iterator for edges + EdgeIterator edgesBegin() const + { + return EdgeIterator(Edge(0), this); + } + + /// returns end iterator for edges + EdgeIterator edgesEnd() const + { + return EdgeIterator(Edge(edgesSize()), this); + } + + /// returns edge container for C++11 range-based for-loops + EdgeContainer edges() const + { + return EdgeContainer(edgesBegin(), edgesEnd()); + } + + /// returns start iterator for faces + FaceIterator facesBegin() const + { + return FaceIterator(Face(0), this); + } + + /// returns end iterator for faces + FaceIterator facesEnd() const + { + return FaceIterator(Face(facesSize()), this); + } + + /// returns face container for C++11 range-based for-loops + FaceContainer faces() const + { + return FaceContainer(facesBegin(), facesEnd()); + } + + /// returns circulator for vertices around vertex \c v + VertexAroundVertexCirculator vertices(Vertex v) const + { + return VertexAroundVertexCirculator(this, v); + } + + /// returns circulator for outgoing halfedges around vertex \c v + HalfedgeAroundVertexCirculator halfedges(Vertex v) const + { + return HalfedgeAroundVertexCirculator(this, v); + } + + /// returns circulator for faces around vertex \c v + FaceAroundVertexCirculator faces(Vertex v) const + { + return FaceAroundVertexCirculator(this, v); + } + + /// returns circulator for vertices of face \c f + VertexAroundFaceCirculator vertices(Face f) const + { + return VertexAroundFaceCirculator(this, f); + } + + /// returns circulator for halfedges of face \c f + HalfedgeAroundFaceCirculator halfedges(Face f) const + { + return HalfedgeAroundFaceCirculator(this, f); + } + + //@} + + + + + +public: //--------------------------------------------- higher-level operations + + /// \name Higher-level Topological Operations + //@{ + + /// returns whether the mesh a triangle mesh. this function simply tests + /// each face, and therefore is not very efficient. + /// \sa trianglate(), triangulate(Face) + inline bool isTriangleMesh() const; + + /// returns whether the mesh a quad mesh. this function simply tests + /// each face, and therefore is not very efficient. + inline bool isQuadMesh() const; + + /// triangulate the entire mesh, by calling triangulate(Face) for each face. + /// \sa trianglate(Face) + inline void triangulate(); + + /// triangulate the face \c f + /// \sa trianglate() + inline void triangulate(Face f); + + + /// returns whether collapsing the halfedge \c h is topologically legal. + /// \attention This function is only valid for triangle meshes. + inline bool isCollapseOk(Halfedge h); + + /** Collapse the halfedge \c h by moving its start vertex into its target + vertex. For non-boundary halfedges this function removes one vertex, three + edges, and two faces. For boundary halfedges it removes one vertex, two + edges and one face. + \attention This function is only valid for triangle meshes. + \attention Halfedge collapses might lead to invalid faces. Call + isCollapseOk(Halfedge) to be sure the collapse is legal. + \attention The removed items are only marked as deleted. You have + to call garbageCollection() to finally remove them. + */ + inline void collapse(Halfedge h); + + + /** Split the face \c f by inserting edges between \c p and the vertices + of \c f. For a triangle this is a standard one-to-three split. + */ + inline void split(Face f, Vertex v); + + + /** Split the edge \c e by connecting vertex \c v it to the two vertices + of the adjacent triangles that are opposite to edge \c e. + \attention This function is only valid for triangle meshes. + */ + inline void split(Edge e, Vertex v); + + + /** Subdivide the edge \c e = (v0,v1) by splitting it into the two edge + (v0,v) and (v,v1). Note that this function does not introduce any other + edge or faces. It simply splits the edge. Returns halfedge that points to \c p. + \sa insertVertex(Halfedge, Vertex) + */ + Halfedge insertVertex(Edge e, Vertex v) + { + return insertVertex(halfedge(e,0), v); + } + + /** Subdivide the edge \c e = (v0,v1) by splitting it into the two edge + (v0,v) and (v,v1). Note that this function does not introduce any other + edge or faces. It simply splits the edge. Returns halfedge that points to \c p. + \sa insertVertex(Edge, Vertex) + */ + inline Halfedge insertVertex(Halfedge h, Vertex v); + + + /// insert edge between the to-vertices v0 of h0 and v1 of h1. + /// returns the new halfedge from v0 to v1. + /// \attention h0 and h1 have to belong to the same face + inline Halfedge insertEdge(Halfedge h0, Halfedge h1); + + + /** Check whether flipping edge \c e is topologically + \attention This function is only valid for triangle meshes. + \sa flip(Edge) + */ + inline bool isFlipOk(Edge e) const; + + /** Flip edge \c e: Remove edge \c e and add an edge between the two vertices + opposite to edge \c e of the two incident triangles. + \attention This function is only valid for triangle meshes. + \sa isFlipOk(Edge) + */ + inline void flip(Edge e); + + + /** returns the valence (number of incident edges or neighboring vertices) + of vertex \c v. */ + inline unsigned int valence(Vertex v) const; + + /// returns the valence of face \c f (its number of vertices) + inline unsigned int valence(Face f) const; + + /// find the halfedge from start to end + inline Halfedge findHalfedge(Vertex start, Vertex end) const; + + /// find the edge (a,b) + inline Edge findEdge(Vertex a, Vertex b) const; + + /// deletes the vertex \c v from the mesh + inline void deleteVertex(Vertex v); + + /// deletes the edge \c e from the mesh + inline void deleteEdge(Edge e); + + /// deletes the face \c f from the mesh + inline void deleteFace(Face f); + + //@} + + +protected: //-------------------------------------------- allocate new elements + + /// allocate a new vertex, resize vertex properties accordingly. + Vertex newVertex() + { + m_vprops.pushBack(); + return Vertex(verticesSize()-1); + } + + /// allocate a new edge, resize edge and halfedge properties accordingly. + Halfedge newEdge(Vertex start, Vertex end) + { + assert(start != end); + + m_eprops.pushBack(); + m_hprops.pushBack(); + m_hprops.pushBack(); + + Halfedge h0(halfedgesSize()-2); + Halfedge h1(halfedgesSize()-1); + + setVertex(h0, end); + setVertex(h1, start); + + return h0; + } + + /// allocate a new face, resize face properties accordingly. + Face newFace() + { + m_fprops.pushBack(); + return Face(facesSize()-1); + } + + + + +protected: //------------------------------------------------- helper functions + + /** make sure that the outgoing halfedge of vertex v is a boundary halfedge + if v is a boundary vertex. */ + inline void adjustOutgoingHalfedge(Vertex v); + + /// Helper for halfedge collapse + inline void removeEdge(Halfedge h); + + /// Helper for halfedge collapse + inline void removeLoop(Halfedge h); + + /// are there deleted vertices, edges or faces? + inline bool garbage() const { return m_garbage; } + + + +protected: //----------------------------------------------------- private data + +// friend inline bool read_poly(SurfaceMesh& mesh, const std::string& filename); + + PropertyContainer m_vprops; + PropertyContainer m_hprops; + PropertyContainer m_eprops; + PropertyContainer m_fprops; + + VertexProperty m_vconn; + HalfedgeProperty m_hconn; + FaceProperty m_fconn; + + VertexProperty m_vdeleted; + EdgeProperty m_edeleted; + FaceProperty m_fdeleted; + + unsigned int m_deletedVertices; + unsigned int m_deletedEdges; + unsigned int m_deleteFaces; + bool m_garbage; + std::vector m_gcVertexMap; + std::vector m_gcHalfedgeMap; + std::vector m_gcFaceMap; + + // helper data for add_face() + typedef std::pair NextCacheEntry; + typedef std::vector NextCache; + std::vector m_addFaceVertices; + std::vector m_addFaceHalfedges; + std::vector m_addFaceIsNew; + std::vector m_addFaceNeedsAdjust; + NextCache m_addFaceNextCache; +}; +//------------------------------------------------------------ output operators + + +inline std::ostream& operator<<(std::ostream& os, SurfaceMesh::Vertex v) +{ + return (os << 'v' << v.idx()); +} + +inline std::ostream& operator<<(std::ostream& os, SurfaceMesh::Halfedge h) +{ + return (os << 'h' << h.idx()); +} + +inline std::ostream& operator<<(std::ostream& os, SurfaceMesh::Edge e) +{ + return (os << 'e' << e.idx()); +} + +inline std::ostream& operator<<(std::ostream& os, SurfaceMesh::Face f) +{ + return (os << 'f' << f.idx()); +} + +//inline bool read_mesh(SurfaceMesh& mesh, const std::string& filename); +//inline bool read_off(SurfaceMesh& mesh, const std::string& filename); +//inline bool read_obj(SurfaceMesh& mesh, const std::string& filename); +//inline bool read_stl(SurfaceMesh& mesh, const std::string& filename); + +//inline bool write_mesh(const SurfaceMesh& mesh, const std::string& filename); +//inline bool write_off(const SurfaceMesh& mesh, const std::string& filename); +//inline bool write_obj(const SurfaceMesh& mesh, const std::string& filename); + + +//== IMPLEMENTATION =========================================================== + + +SurfaceMesh:: +SurfaceMesh() +{ + // allocate standard properties + // same list is used in operator=() and assign() + m_vconn = addVertexProperty("v:connectivity"); + m_hconn = addHalfedgeProperty("h:connectivity"); + m_fconn = addFaceProperty("f:connectivity"); + m_vdeleted = addVertexProperty("v:deleted", false); + m_edeleted = addEdgeProperty("e:deleted", false); + m_fdeleted = addFaceProperty("f:deleted", false); + + m_deletedVertices = m_deletedEdges = m_deleteFaces = 0; + m_garbage = false; +} + + +//----------------------------------------------------------------------------- + + +SurfaceMesh:: +~SurfaceMesh() +{ +} + + +//----------------------------------------------------------------------------- + + +SurfaceMesh& +SurfaceMesh:: +operator=(const SurfaceMesh& rhs) +{ + if (this != &rhs) + { + // deep copy of property containers + m_vprops = rhs.m_vprops; + m_hprops = rhs.m_hprops; + m_eprops = rhs.m_eprops; + m_fprops = rhs.m_fprops; + + // property handles contain pointers, have to be reassigned + m_vconn = vertexProperty("v:connectivity"); + m_hconn = halfedgeProperty("h:connectivity"); + m_fconn = faceProperty("f:connectivity"); + m_vdeleted = vertexProperty("v:deleted"); + m_edeleted = edgeProperty("e:deleted"); + m_fdeleted = faceProperty("f:deleted"); + + // how many elements are deleted? + m_deletedVertices = rhs.m_deletedVertices; + m_deletedEdges = rhs.m_deletedEdges; + m_deleteFaces = rhs.m_deleteFaces; + m_garbage = rhs.m_garbage; + } + + return *this; +} + + +//----------------------------------------------------------------------------- + + +SurfaceMesh& +SurfaceMesh:: +assign(const SurfaceMesh& rhs) +{ + if (this != &rhs) + { + // clear properties + m_vprops.clear(); + m_hprops.clear(); + m_eprops.clear(); + m_fprops.clear(); + + // allocate standard properties + m_vconn = addVertexProperty("v:connectivity"); + m_hconn = addHalfedgeProperty("h:connectivity"); + m_fconn = addFaceProperty("f:connectivity"); + m_vdeleted = addVertexProperty("v:deleted", false); + m_edeleted = addEdgeProperty("e:deleted", false); + m_fdeleted = addFaceProperty("f:deleted", false); + + // copy properties from other mesh + m_vconn.array() = rhs.m_vconn.array(); + m_hconn.array() = rhs.m_hconn.array(); + m_fconn.array() = rhs.m_fconn.array(); + m_vdeleted.array() = rhs.m_vdeleted.array(); + m_edeleted.array() = rhs.m_edeleted.array(); + m_fdeleted.array() = rhs.m_fdeleted.array(); + + // resize (needed by property containers) + m_vprops.resize(rhs.verticesSize()); + m_hprops.resize(rhs.halfedgesSize()); + m_eprops.resize(rhs.edgesSize()); + m_fprops.resize(rhs.facesSize()); + + // how many elements are deleted? + m_deletedVertices = rhs.m_deletedVertices; + m_deletedEdges = rhs.m_deletedEdges; + m_deleteFaces = rhs.m_deleteFaces; + m_garbage = rhs.m_garbage; + } + + return *this; +} + + +//----------------------------------------------------------------------------- + + +//bool +//SurfaceMesh:: +//read(const std::string& filename) +//{ +// return read_mesh(*this, filename); +//} + + +//----------------------------------------------------------------------------- + + +//bool +//SurfaceMesh:: +//write(const std::string& filename) const +//{ +// return write_mesh(*this, filename); +//} + + +//----------------------------------------------------------------------------- + + +void +SurfaceMesh:: +clear() +{ + m_vprops.resize(0); + m_hprops.resize(0); + m_eprops.resize(0); + m_fprops.resize(0); + + m_vprops.freeMemory(); + m_hprops.freeMemory(); + m_eprops.freeMemory(); + m_fprops.freeMemory(); + + m_deletedVertices = m_deletedEdges = m_deleteFaces = 0; + m_garbage = false; +} + + +//----------------------------------------------------------------------------- + + +void +SurfaceMesh:: +freeMemory() +{ + m_vprops.freeMemory(); + m_hprops.freeMemory(); + m_eprops.freeMemory(); + m_fprops.freeMemory(); +} + + +//----------------------------------------------------------------------------- + + +void +SurfaceMesh:: +reserve(unsigned int nvertices, + unsigned int nedges, + unsigned int nfaces ) +{ + m_vprops.reserve(nvertices); + m_hprops.reserve(2*nedges); + m_eprops.reserve(nedges); + m_fprops.reserve(nfaces); +} + + +//----------------------------------------------------------------------------- + + +void +SurfaceMesh:: +propertyStats() const +{ + std::vector props; + + std::cout << "vertex properties:\n"; + props = vertexProperties(); + for (unsigned int i=0; i& vertices) +{ + const unsigned int n(vertices.size()); + assert (n > 2); + + Vertex v; + unsigned int i, ii, id; + Halfedge inner_next, inner_prev, outer_next, outer_prev, boundary_next, boundary_prev, patch_start, patch_end; + + + // use global arrays to avoid new/delete of local arrays!!! + std::vector& halfedges = m_addFaceHalfedges; + std::vector& is_new = m_addFaceIsNew; + std::vector& needs_adjust = m_addFaceNeedsAdjust; + NextCache& next_cache = m_addFaceNextCache; + halfedges.clear(); + halfedges.resize(n); + is_new.clear(); + is_new.resize(n); + needs_adjust.clear(); + needs_adjust.resize(n, false); + next_cache.clear(); + next_cache.reserve(3*n); + + + // test for topological errors + for (i=0, ii=1; ifirst, ncIt->second); + } + + + + // adjust vertices' halfedge handle + for (i=0; io + // <--------------- + // o0 + // + // after: + // + // v0 h0 v h1 v2 + // o------>o------->o + // <------ <------- + // o0 o1 + + Halfedge h2 = nextHalfedge(h0); + Halfedge o0 = oppositeHalfedge(h0); + Halfedge o2 = prevHalfedge(o0); + Vertex v2 = toVertex(h0); + Face fh = face(h0); + Face fo = face(o0); + + Halfedge h1 = newEdge(v, v2); + Halfedge o1 = oppositeHalfedge(h1); + + // adjust halfedge connectivity + setNextHalfedge(h1, h2); + setNextHalfedge(h0, h1); + setVertex(h0, v); + setVertex(h1, v2); + setFace(h1, fh); + + setNextHalfedge(o1, o0); + setNextHalfedge(o2, o1); + setVertex(o1, v); + setFace(o1, fo); + + // adjust vertex connectivity + setHalfedge(v2, o1); + adjustOutgoingHalfedge(v2); + setHalfedge(v, h1); + adjustOutgoingHalfedge(v); + + // adjust face connectivity + if (fh.isValid()) setHalfedge(fh, h0); + if (fo.isValid()) setHalfedge(fo, o1); + + return h0; +} + + +//----------------------------------------------------------------------------- + + +SurfaceMesh::Halfedge +SurfaceMesh:: +insertEdge(Halfedge h0, Halfedge h1) +{ + assert(face(h0) == face(h1)); + assert(face(h0).isValid()); + + Vertex v0 = toVertex(h0); + Vertex v1 = toVertex(h1); + + Halfedge h2 = nextHalfedge(h0); + Halfedge h3 = nextHalfedge(h1); + + Halfedge h4 = newEdge(v0, v1); + Halfedge h5 = oppositeHalfedge(h4); + + Face f0 = face(h0); + Face f1 = newFace(); + + setHalfedge(f0, h0); + setHalfedge(f1, h1); + + setNextHalfedge(h0, h4); + setNextHalfedge(h4, h3); + setFace(h4, f0); + + setNextHalfedge(h1, h5); + setNextHalfedge(h5, h2); + Halfedge h = h2; + do + { + setFace(h, f1); + h = nextHalfedge(h); + } + while (h != h2); + + return h4; +} + + +//----------------------------------------------------------------------------- + + +bool +SurfaceMesh:: +isFlipOk(Edge e) const +{ + // boundary edges cannot be flipped + if (isBoundary(e)) return false; + + // check if the flipped edge is already present in the mesh + + Halfedge h0 = halfedge(e, 0); + Halfedge h1 = halfedge(e, 1); + + Vertex v0 = toVertex(nextHalfedge(h0)); + Vertex v1 = toVertex(nextHalfedge(h1)); + + if (v0 == v1) // this is generally a bad sign !!! + return false; + + if (findHalfedge(v0, v1).isValid()) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- + + +void +SurfaceMesh:: +flip(Edge e) +{ + // CAUTION : Flipping a halfedge may result in + // a non-manifold mesh, hence check for yourself + // whether this operation is allowed or not! + + //let's make it sure it is actually checked + assert(isFlipOk(e)); + + Halfedge a0 = halfedge(e, 0); + Halfedge b0 = halfedge(e, 1); + + Halfedge a1 = nextHalfedge(a0); + Halfedge a2 = nextHalfedge(a1); + + Halfedge b1 = nextHalfedge(b0); + Halfedge b2 = nextHalfedge(b1); + + Vertex va0 = toVertex(a0); + Vertex va1 = toVertex(a1); + + Vertex vb0 = toVertex(b0); + Vertex vb1 = toVertex(b1); + + Face fa = face(a0); + Face fb = face(b0); + + setVertex(a0, va1); + setVertex(b0, vb1); + + setNextHalfedge(a0, a2); + setNextHalfedge(a2, b1); + setNextHalfedge(b1, a0); + + setNextHalfedge(b0, b2); + setNextHalfedge(b2, a1); + setNextHalfedge(a1, b0); + + setFace(a1, fb); + setFace(b1, fa); + + setHalfedge(fa, a0); + setHalfedge(fb, b0); + + if (halfedge(va0) == b0) + setHalfedge(va0, a1); + if (halfedge(vb0) == a0) + setHalfedge(vb0, b1); +} + + +//----------------------------------------------------------------------------- + + +bool +SurfaceMesh:: +isCollapseOk(Halfedge v0v1) +{ + Halfedge v1v0(oppositeHalfedge(v0v1)); + Vertex v0(toVertex(v1v0)); + Vertex v1(toVertex(v0v1)); + Vertex vv, vl, vr; + Halfedge h1, h2; + + + // the edges v1-vl and vl-v0 must not be both boundary edges + if (!isBoundary(v0v1)) + { + vl = toVertex(nextHalfedge(v0v1)); + h1 = nextHalfedge(v0v1); + h2 = nextHalfedge(h1); + if (isBoundary(oppositeHalfedge(h1)) && isBoundary(oppositeHalfedge(h2))) + return false; + } + + + // the edges v0-vr and vr-v1 must not be both boundary edges + if (!isBoundary(v1v0)) + { + vr = toVertex(nextHalfedge(v1v0)); + h1 = nextHalfedge(v1v0); + h2 = nextHalfedge(h1); + if (isBoundary(oppositeHalfedge(h1)) && isBoundary(oppositeHalfedge(h2))) + return false; + } + + + // if vl and vr are equal or both invalid -> fail + if (vl == vr) return false; + + + // edge between two boundary vertices should be a boundary edge + if ( isBoundary(v0) && isBoundary(v1) && + !isBoundary(v0v1) && !isBoundary(v1v0)) + return false; + + + // test intersection of the one-rings of v0 and v1 + VertexAroundVertexCirculator vv_it, vv_end; + vv_it = vv_end = vertices(v0); + do + { + vv = *vv_it; + if (vv != v1 && vv != vl && vv != vr) + if (findHalfedge(vv, v1).isValid()) + return false; + } + while (++vv_it != vv_end); + + + // passed all tests + return true; +} + + +//----------------------------------------------------------------------------- + + +void +SurfaceMesh:: +collapse(Halfedge h) +{ + Halfedge h0 = h; + Halfedge h1 = prevHalfedge(h0); + Halfedge o0 = oppositeHalfedge(h0); + Halfedge o1 = nextHalfedge(o0); + + // remove edge + removeEdge(h0); + + // remove loops + if (nextHalfedge(nextHalfedge(h1)) == h1) + removeLoop(h1); + if (nextHalfedge(nextHalfedge(o1)) == o1) + removeLoop(o1); +} + + +//----------------------------------------------------------------------------- + + +void +SurfaceMesh:: +removeEdge(Halfedge h) +{ + Halfedge hn = nextHalfedge(h); + Halfedge hp = prevHalfedge(h); + + Halfedge o = oppositeHalfedge(h); + Halfedge on = nextHalfedge(o); + Halfedge op = prevHalfedge(o); + + Face fh = face(h); + Face fo = face(o); + + Vertex vh = toVertex(h); + Vertex vo = toVertex(o); + + + + // halfedge -> vertex + HalfedgeAroundVertexCirculator vh_it, vh_end; + vh_it = vh_end = halfedges(vo); + do + { + setVertex(oppositeHalfedge(*vh_it), vh); + } + while (++vh_it != vh_end); + + + // halfedge -> halfedge + setNextHalfedge(hp, hn); + setNextHalfedge(op, on); + + + // face -> halfedge + if (fh.isValid()) setHalfedge(fh, hn); + if (fo.isValid()) setHalfedge(fo, on); + + + // vertex -> halfedge + if (halfedge(vh) == o) setHalfedge(vh, hn); + adjustOutgoingHalfedge(vh); + setHalfedge(vo, Halfedge()); + + + // delete stuff + if (!m_vdeleted) m_vdeleted = vertexProperty("v:deleted", false); + if (!m_edeleted) m_edeleted = edgeProperty("e:deleted", false); + m_vdeleted[vo] = true; ++m_deletedVertices; + m_edeleted[edge(h)] = true; ++m_deletedEdges; + m_garbage = true; +} + + +//----------------------------------------------------------------------------- + + +void +SurfaceMesh:: +removeLoop(Halfedge h) +{ + Halfedge h0 = h; + Halfedge h1 = nextHalfedge(h0); + + Halfedge o0 = oppositeHalfedge(h0); + Halfedge o1 = oppositeHalfedge(h1); + + Vertex v0 = toVertex(h0); + Vertex v1 = toVertex(h1); + + Face fh = face(h0); + Face fo = face(o0); + + + + // is it a loop ? + assert ((nextHalfedge(h1) == h0) && (h1 != o0)); + + + // halfedge -> halfedge + setNextHalfedge(h1, nextHalfedge(o0)); + setNextHalfedge(prevHalfedge(o0), h1); + + + // halfedge -> face + setFace(h1, fo); + + + // vertex -> halfedge + setHalfedge(v0, h1); adjustOutgoingHalfedge(v0); + setHalfedge(v1, o1); adjustOutgoingHalfedge(v1); + + + // face -> halfedge + if (fo.isValid() && halfedge(fo) == o0) + setHalfedge(fo, h1); + + + // delete stuff + if (!m_edeleted) m_edeleted = edgeProperty("e:deleted", false); + if (!m_fdeleted) m_fdeleted = faceProperty("f:deleted", false); + if (fh.isValid()) { m_fdeleted[fh] = true; ++m_deleteFaces; } + m_edeleted[edge(h0)] = true; ++m_deletedEdges; + m_garbage = true; +} + + +//----------------------------------------------------------------------------- + + +void +SurfaceMesh:: +deleteVertex(Vertex v) +{ + if (m_vdeleted[v]) return; + + // collect incident faces + std::vector incident_faces; + incident_faces.reserve(6); + + FaceAroundVertexCirculator fc, fc_end; + fc = fc_end = faces(v); + + if (fc) + do + { + incident_faces.push_back(*fc); + } while (++fc != fc_end); + + // delete incident faces + std::vector::iterator fit(incident_faces.begin()), + fend(incident_faces.end()); + + for (; fit != fend; ++fit) + deleteFace(*fit); + + m_vdeleted[v] = true; + m_deletedVertices++; + m_garbage = true; +} + + +//----------------------------------------------------------------------------- + + +void +SurfaceMesh:: +deleteEdge(Edge e) +{ + if (m_edeleted[e]) return; + + Face f0 = face(halfedge(e, 0)); + Face f1 = face(halfedge(e, 1)); + + if (f0.isValid()) deleteFace(f0); + if (f1.isValid()) deleteFace(f1); +} + + +//----------------------------------------------------------------------------- + +void +SurfaceMesh:: +deleteFace(Face f) +{ + if (m_fdeleted[f]) return; + + // mark face deleted + if (!m_fdeleted[f]) + { + m_fdeleted[f] = true; + m_deleteFaces++; + } + + // boundary edges of face f to be deleted + std::vector deleted_edges; + deleted_edges.reserve(3); + + + // vertices of face f for updating their outgoing halfedge + std::vector vertices; + vertices.reserve(3); + + + // for all halfedges of face f do: + // 1) invalidate face handle. + // 2) collect all boundary halfedges, set them deleted + // 3) store vertex handles + HalfedgeAroundFaceCirculator hc, hc_end; + hc = hc_end = halfedges(f); + + do + { + setFace(*hc, Face()); + + if (isBoundary(oppositeHalfedge(*hc))) + deleted_edges.push_back(edge(*hc)); + + vertices.push_back(toVertex(*hc)); + + } while (++hc != hc_end); + + + // delete all collected (half)edges + // delete isolated vertices + if (!deleted_edges.empty()) + { + std::vector::iterator del_it(deleted_edges.begin()), + del_end(deleted_edges.end()); + + Halfedge h0, h1, next0, next1, prev0, prev1; + Vertex v0, v1; + + for (; del_it!=del_end; ++del_it) + { + h0 = halfedge(*del_it, 0); + v0 = toVertex(h0); + next0 = nextHalfedge(h0); + prev0 = prevHalfedge(h0); + + h1 = halfedge(*del_it, 1); + v1 = toVertex(h1); + next1 = nextHalfedge(h1); + prev1 = prevHalfedge(h1); + + // adjust next and prev handles + setNextHalfedge(prev0, next1); + setNextHalfedge(prev1, next0); + + // mark edge deleted + if (!m_edeleted[*del_it]) + { + m_edeleted[*del_it] = true; + m_deletedEdges++; + } + + // update v0 + if (halfedge(v0) == h1) + { + if (next0 == h1) + { + if (!m_vdeleted[v0]) + { + m_vdeleted[v0] = true; + m_deletedVertices++; + } + } + else setHalfedge(v0, next0); + } + + // update v1 + if (halfedge(v1) == h0) + { + if (next1 == h0) + { + if (!m_vdeleted[v1]) + { + m_vdeleted[v1] = true; + m_deletedVertices++; + } + } + else setHalfedge(v1, next1); + } + } + } + + + // update outgoing halfedge handles of remaining vertices + std::vector::iterator v_it(vertices.begin()), + v_end(vertices.end()); + for (; v_it!=v_end; ++v_it) + adjustOutgoingHalfedge(*v_it); + + m_garbage = true; +} + + +//----------------------------------------------------------------------------- + + +void +SurfaceMesh:: +garbageCollection(unsigned flags) +{ + int i, i0, i1, + nV(verticesSize()), + nE(edgesSize()), + nH(halfedgesSize()), + nF(facesSize()); + + Vertex v; + Halfedge h; + Face f; + + + // setup handle mapping + m_gcVertexMap .resize(nV); + m_gcHalfedgeMap.resize(nH); + m_gcFaceMap .resize(nF); + + for (i=0; i 0) + { + i0=0; i1=nV-1; + + while (1) + { + // find first deleted and last un-deleted + while (!m_vdeleted[Vertex(i0)] && i0 < i1) ++i0; + while ( m_vdeleted[Vertex(i1)] && i0 < i1) --i1; + if (i0 >= i1) break; + + // swap + m_vprops.swap(i0, i1); + std::swap(m_gcVertexMap[i0], m_gcVertexMap[i1]); + }; + + // remember new size + nV = m_vdeleted[Vertex(i0)] ? i0 : i0+1; + } + + + // remove deleted edges + if (nE > 0) + { + i0=0; i1=nE-1; + + while (1) + { + // find first deleted and last un-deleted + while (!m_edeleted[Edge(i0)] && i0 < i1) ++i0; + while ( m_edeleted[Edge(i1)] && i0 < i1) --i1; + if (i0 >= i1) break; + + // swap + m_eprops.swap(i0, i1); + m_hprops.swap(2*i0, 2*i1); + m_hprops.swap(2*i0+1, 2*i1+1); + std::swap(m_gcHalfedgeMap[2*i0 + 0], m_gcHalfedgeMap[2*i1 + 0]); + std::swap(m_gcHalfedgeMap[2*i0 + 1], m_gcHalfedgeMap[2*i1 + 1]); + }; + + // remember new size + nE = m_edeleted[Edge(i0)] ? i0 : i0+1; + nH = 2*nE; + } + + + // remove deleted faces + if (nF > 0) + { + i0=0; i1=nF-1; + + while (1) + { + // find 1st deleted and last un-deleted + while (!m_fdeleted[Face(i0)] && i0 < i1) ++i0; + while ( m_fdeleted[Face(i1)] && i0 < i1) --i1; + if (i0 >= i1) break; + + // swap + m_fprops.swap(i0, i1); + std::swap(m_gcFaceMap[i0], m_gcFaceMap[i1]); + }; + + // remember new size + nF = m_fdeleted[Face(i0)] ? i0 : i0+1; + } + + + // update vertex connectivity + for (i=0; i vertices; +// std::vector all_tex_coords; //individual texture coordinates +// std::vector halfedge_tex_idx; //texture coordinates sorted for halfedges +// Surface_mesh::Halfedge_property tex_coords = mesh.halfedge_property("h:texcoord"); +// bool with_tex_coord=false; + +// // clear mesh +// mesh.clear(); + + +// // open file (in ASCII mode) +// FILE* in = fopen(filename.c_str(), "r"); +// if (!in) return false; + + +// // clear line once +// memset(&s, 0, 200); + + +// // parse line by line (currently only supports vertex positions & faces +// while(in && !feof(in) && fgets(s, 200, in)) +// { +// // comment +// if (s[0] == '#' || isspace(s[0])) continue; + +// // vertex +// else if (strncmp(s, "v ", 2) == 0) +// { +// if (sscanf(s, "v %f %f %f", &x, &y, &z)) +// { +// mesh.add_vertex(Point(x,y,z)); +// } +// } +// // normal +// else if (strncmp(s, "vn ", 3) == 0) +// { +// if (sscanf(s, "vn %f %f %f", &x, &y, &z)) +// { +// // problematic as it can be either a vertex property when interpolated +// // or a halfedge property for hard edges +// } +// } + +// // texture coordinate +// else if (strncmp(s, "vt ", 3) == 0) +// { +// if (sscanf(s, "vt %f %f", &x, &y)) +// { +// z=1; +// all_tex_coords.push_back(Texture_coordinate(x,y,z)); +// } +// } + +// // face +// else if (strncmp(s, "f ", 2) == 0) +// { +// int component(0), nV(0); +// bool endOfVertex(false); +// char *p0, *p1(s+1); + +// vertices.clear(); +// halfedge_tex_idx.clear(); + +// // skip white-spaces +// while (*p1==' ') ++p1; + +// while (p1) +// { +// p0 = p1; + +// // overwrite next separator + +// // skip '/', '\n', ' ', '\0', '\r' <-- don't forget Windows +// while (*p1!='/' && *p1!='\r' && *p1!='\n' && *p1!=' ' && *p1!='\0') ++p1; + +// // detect end of vertex +// if (*p1 != '/') +// { +// endOfVertex = true; +// } + +// // replace separator by '\0' +// if (*p1 != '\0') +// { +// *p1 = '\0'; +// p1++; // point to next token +// } + +// // detect end of line and break +// if (*p1 == '\0' || *p1 == '\n') +// { +// p1 = 0; +// } + +// // read next vertex component +// if (*p0 != '\0') +// { +// switch (component) +// { +// case 0: // vertex +// { +// vertices.push_back( Surface_mesh::Vertex(atoi(p0) - 1) ); +// break; +// } +// case 1: // texture coord +// { +// int idx = atoi(p0)-1; +// halfedge_tex_idx.push_back(idx); +// with_tex_coord=true; +// break; +// } +// case 2: // normal +// break; +// } +// } + +// ++component; + +// if (endOfVertex) +// { +// component = 0; +// nV++; +// endOfVertex = false; +// } +// } + +// Surface_mesh::Face f=mesh.add_face(vertices); + + +// // add texture coordinates +// if(with_tex_coord) +// { +// Surface_mesh::Halfedge_around_face_circulator h_fit = mesh.halfedges(f); +// Surface_mesh::Halfedge_around_face_circulator h_end = h_fit; +// unsigned v_idx =0; +// do +// { +// tex_coords[*h_fit]=all_tex_coords.at(halfedge_tex_idx.at(v_idx)); +// ++v_idx; +// ++h_fit; +// } +// while(h_fit!=h_end); +// } +// } +// // clear line +// memset(&s, 0, 200); +// } + +// fclose(in); +// return true; +//} + + +////----------------------------------------------------------------------------- + + +//bool write_obj(const Surface_mesh& mesh, const std::string& filename) +//{ +// FILE* out = fopen(filename.c_str(), "w"); +// if (!out) +// return false; + +// // comment +// fprintf(out, "# OBJ export from Surface_mesh\n"); + +// //vertices +// Surface_mesh::Vertex_property points = mesh.get_vertex_property("v:point"); +// for (Surface_mesh::Vertex_iterator vit=mesh.vertices_begin(); vit!=mesh.vertices_end(); ++vit) +// { +// const Point& p = points[*vit]; +// fprintf(out, "v %.10f %.10f %.10f\n", p[0], p[1], p[2]); +// } + +// //normals +// Surface_mesh::Vertex_property normals = mesh.get_vertex_property("v:normal"); +// for (Surface_mesh::Vertex_iterator vit=mesh.vertices_begin(); vit!=mesh.vertices_end(); ++vit) +// { +// const Point& p = normals[*vit]; +// fprintf(out, "vn %.10f %.10f %.10f\n", p[0], p[1], p[2]); +// } + +// //optionally texture coordinates +// // do we have them? +// std::vector h_props= mesh.halfedge_properties(); +// bool with_tex_coord = false; +// std::vector::iterator h_prop_end = h_props.end(); +// std::vector::iterator h_prop_start= h_props.begin(); +// while(h_prop_start!=h_prop_end) +// { +// if(0==(*h_prop_start).compare("h:texcoord")) +// { +// with_tex_coord=true; +// } +// ++h_prop_start; +// } + +// //if so then add +// if(with_tex_coord) +// { +// Surface_mesh::Halfedge_property tex_coord = mesh.get_halfedge_property("h:texcoord"); +// for (Surface_mesh::Halfedge_iterator hit=mesh.halfedges_begin(); hit!=mesh.halfedges_end(); ++hit) +// { +// const Texture_coordinate& pt = tex_coord[*hit]; +// fprintf(out, "vt %.10f %.10f %.10f\n", pt[0], pt[1], pt[2]); +// } +// } + +// //faces +// for (Surface_mesh::Face_iterator fit=mesh.faces_begin(); fit!=mesh.faces_end(); ++fit) +// { +// fprintf(out, "f"); +// Surface_mesh::Vertex_around_face_circulator fvit=mesh.vertices(*fit), fvend=fvit; +// Surface_mesh::Halfedge_around_face_circulator fhit=mesh.halfedges(*fit); +// do +// { +// if(with_tex_coord) +// { +// // write vertex index, tex_coord index and normal index +// fprintf(out, " %d/%d/%d", (*fvit).idx()+1, (*fhit).idx()+1, (*fvit).idx()+1); +// ++fhit; +// } +// else +// { +// // write vertex index and normal index +// fprintf(out, " %d//%d", (*fvit).idx()+1, (*fvit).idx()+1); +// } +// } +// while (++fvit != fvend); +// fprintf(out, "\n"); +// } + +// fclose(out); +// return true; +//} + +//// helper function +//template inline void readOff(FILE* in, T& t) +//{ +// int err = 0; +// err = fread(&t, 1, sizeof(t), in); +//} + + +////----------------------------------------------------------------------------- + + +//inline bool read_off_ascii(Surface_mesh& mesh, +// FILE* in, +// const bool has_normals, +// const bool has_texcoords, +// const bool has_colors) +//{ +// char line[200], *lp; +// int nc; +// unsigned int i, j, items, idx; +// unsigned int nV, nF, nE; +// Vec3f p, n, c; +// Vec2f t; +// Surface_mesh::Vertex v; + + +// // properties +// Surface_mesh::Vertex_property normals; +// Surface_mesh::Vertex_property texcoords; +// Surface_mesh::Vertex_property colors; +// if (has_normals) normals = mesh.vertex_property("v:normal"); +// if (has_texcoords) texcoords = mesh.vertex_property("v:texcoord"); +// if (has_colors) colors = mesh.vertex_property("v:color"); + + +// // #Vertice, #Faces, #Edges +// items = fscanf(in, "%d %d %d\n", (int*)&nV, (int*)&nF, (int*)&nE); +// mesh.clear(); +// mesh.reserve(nV, std::max(3*nV, nE), nF); + + +// // read vertices: pos [normal] [color] [texcoord] +// for (i=0; i1.0f || c[1]>1.0f || c[2]>1.0f) c *= (1.0/255.0); +// colors[v] = c; +// } +// lp += nc; +// } + +// // tex coord +// if (has_texcoords) +// { +// items = sscanf(lp, "%f %f%n", &t[0], &t[1], &nc); +// assert(items == 2); +// texcoords[v][0] = t[0]; +// texcoords[v][1] = t[1]; +// lp += nc; +// } +// } + + + +// // read faces: #N v[1] v[2] ... v[n-1] +// std::vector vertices; +// for (i=0; i normals; +// Surface_mesh::Vertex_property texcoords; +// if (has_normals) normals = mesh.vertex_property("v:normal"); +// if (has_texcoords) texcoords = mesh.vertex_property("v:texcoord"); + + +// // #Vertice, #Faces, #Edges +// readOff(in, nV); +// readOff(in, nF); +// readOff(in, nE); +// mesh.clear(); +// mesh.reserve(nV, std::max(3*nV, nE), nF); + + +// // read vertices: pos [normal] [color] [texcoord] +// for (i=0; i vertices; +// for (i=0; i normals = mesh.get_vertex_property("v:normal"); +// Surface_mesh::Vertex_property texcoords = mesh.get_vertex_property("v:texcoord"); +// if (normals) has_normals = true; +// if (texcoords) has_texcoords = true; + + +// // header +// if(has_texcoords) +// fprintf(out, "ST"); +// if(has_normals) +// fprintf(out, "N"); +// fprintf(out, "OFF\n%d %d 0\n", mesh.n_vertices(), mesh.n_faces()); + + +// // vertices, and optionally normals and texture coordinates +// Surface_mesh::Vertex_property points = mesh.get_vertex_property("v:point"); +// for (Surface_mesh::Vertex_iterator vit=mesh.vertices_begin(); vit!=mesh.vertices_end(); ++vit) +// { +// const Point& p = points[*vit]; +// fprintf(out, "%.10f %.10f %.10f", p[0], p[1], p[2]); + +// if (has_normals) +// { +// const Normal& n = normals[*vit]; +// fprintf(out, " %.10f %.10f %.10f", n[0], n[1], n[2]); +// } + +// if (has_texcoords) +// { +// const Texture_coordinate& t = texcoords[*vit]; +// fprintf(out, "% .10f %.10f", t[0], t[1]); +// } + +// fprintf(out, "\n"); +// } + + +// // faces +// for (Surface_mesh::Face_iterator fit=mesh.faces_begin(); fit!=mesh.faces_end(); ++fit) +// { +// int nV = mesh.valence(*fit); +// fprintf(out, "%d", nV); +// Surface_mesh::Vertex_around_face_circulator fvit=mesh.vertices(*fit), fvend=fvit; +// do +// { +// fprintf(out, " %d", (*fvit).idx()); +// } +// while (++fvit != fvend); +// fprintf(out, "\n"); +// } + +// fclose(out); +// return true; +//} + +//// helper function +//template inline size_t readPoly(FILE* in, T& t) +//{ +// return fread((char*)&t, 1, sizeof(t), in); +//} + + +////----------------------------------------------------------------------------- + + +//bool read_poly(Surface_mesh& mesh, const std::string& filename) +//{ +// unsigned int n_items; + +// // open file (in binary mode) +// FILE* in = fopen(filename.c_str(), "rb"); +// if (!in) return false; + + +// // clear mesh +// mesh.clear(); + + +// // how many elements? +// unsigned int nv, ne, nh, nf; +// readPoly(in, nv); +// readPoly(in, ne); +// readPoly(in, nf); +// nh = 2*ne; + + +// // resize containers +// mesh.vprops_.resize(nv); +// mesh.hprops_.resize(nh); +// mesh.eprops_.resize(ne); +// mesh.fprops_.resize(nf); + + +// // get properties +// Surface_mesh::Vertex_property vconn = mesh.vertex_property("v:connectivity"); +// Surface_mesh::Halfedge_property hconn = mesh.halfedge_property("h:connectivity"); +// Surface_mesh::Face_property fconn = mesh.face_property("f:connectivity"); +// Surface_mesh::Vertex_property point = mesh.vertex_property("v:point"); + +// // read properties from file +// n_items = fread((char*)vconn.data(), sizeof(Surface_mesh::Vertex_connectivity), nv, in); +// n_items = fread((char*)hconn.data(), sizeof(Surface_mesh::Halfedge_connectivity), nh, in); +// n_items = fread((char*)fconn.data(), sizeof(Surface_mesh::Face_connectivity), nf, in); +// n_items = fread((char*)point.data(), sizeof(Point), nv, in); + +// fclose(in); +// return true; +//} + +//// helper function +//template inline void readStl(FILE* in, T& t) +//{ +// size_t n_items(0); +// n_items = fread((char*)&t, 1, sizeof(t), in); +// assert(n_items > 0); +//} + + +////----------------------------------------------------------------------------- + + +//// helper class for STL reader +//class CmpVec +//{ +//public: + +// CmpVec(float _eps=FLT_MIN) : eps_(_eps) {} + +// bool operator()(const Vec3f& v0, const Vec3f& v1) const +// { +// if (fabs(v0[0] - v1[0]) <= eps_) +// { +// if (fabs(v0[1] - v1[1]) <= eps_) +// { +// return (v0[2] < v1[2] - eps_); +// } +// else return (v0[1] < v1[1] - eps_); +// } +// else return (v0[0] < v1[0] - eps_); +// } + +//private: +// float eps_; +//}; + + +////----------------------------------------------------------------------------- + + +//bool read_stl(Surface_mesh& mesh, const std::string& filename) +//{ +// char line[100], *c; +// unsigned int i, nT; +// Vec3f p; +// Surface_mesh::Vertex v; +// std::vector vertices(3); +// size_t n_items(0); + +// CmpVec comp(FLT_MIN); +// std::map vMap(comp); +// std::map::iterator vMapIt; + + +// // clear mesh +// mesh.clear(); + + +// // open file (in ASCII mode) +// FILE* in = fopen(filename.c_str(), "r"); +// if (!in) return false; + + +// // ASCII or binary STL? +// c = fgets(line, 6, in); +// assert(c != NULL); +// const bool binary = ((strncmp(line, "SOLID", 5) != 0) && +// (strncmp(line, "solid", 5) != 0)); + + +// // parse binary STL +// if (binary) +// { +// // re-open file in binary mode +// fclose(in); +// in = fopen(filename.c_str(), "rb"); +// if (!in) return false; + +// // skip dummy header +// n_items = fread(line, 1, 80, in); +// assert(n_items > 0); + +// // read number of triangles +// readStl(in, nT); + +// // read triangles +// while (nT) +// { +// // skip triangle normal +// n_items = fread(line, 1, 12, in); +// assert(n_items > 0); +// // triangle's vertices +// for (i=0; i<3; ++i) +// { +// readStl(in, p); + +// // has vector been referenced before? +// if ((vMapIt=vMap.find(p)) == vMap.end()) +// { +// // No : add vertex and remember idx/vector mapping +// v = mesh.add_vertex((Point)p); +// vertices[i] = v; +// vMap[p] = v; +// } +// else +// { +// // Yes : get index from map +// vertices[i] = vMapIt->second; +// } +// } + +// // Add face only if it is not degenerated +// if ((vertices[0] != vertices[1]) && +// (vertices[0] != vertices[2]) && +// (vertices[1] != vertices[2])) +// mesh.add_face(vertices); + +// n_items = fread(line, 1, 2, in); +// assert(n_items > 0); +// --nT; +// } +// } + + +// // parse ASCII STL +// else +// { +// // parse line by line +// while (in && !feof(in) && fgets(line, 100, in)) +// { +// // skip white-space +// for (c=line; isspace(*c) && *c!='\0'; ++c) {}; + +// // face begins +// if ((strncmp(c, "outer", 5) == 0) || +// (strncmp(c, "OUTER", 5) == 0)) +// { +// // read three vertices +// for (i=0; i<3; ++i) +// { +// // read line +// c = fgets(line, 100, in); +// assert(c != NULL); + +// // skip white-space +// for (c=line; isspace(*c) && *c!='\0'; ++c) {}; + +// // read x, y, z +// sscanf(c+6, "%f %f %f", &p[0], &p[1], &p[2]); + +// // has vector been referenced before? +// if ((vMapIt=vMap.find(p)) == vMap.end()) +// { +// // No : add vertex and remember idx/vector mapping +// v = mesh.add_vertex((Point)p); +// vertices[i] = v; +// vMap[p] = v; +// } +// else +// { +// // Yes : get index from map +// vertices[i] = vMapIt->second; +// } +// } + +// // Add face only if it is not degenerated +// if ((vertices[0] != vertices[1]) && +// (vertices[0] != vertices[2]) && +// (vertices[1] != vertices[2])) +// mesh.add_face(vertices); +// } +// } +// } + + +// fclose(in); +// return true; +//} + +//============================================================================= +} // namespace Patate +//============================================================================= +#endif // SURFACE_MESH_H +//============================================================================= diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/grenaille.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/grenaille.h new file mode 100644 index 000000000..4eab9df0e --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/grenaille.h @@ -0,0 +1,39 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + + +#ifndef _PATATE_GRENAILLE_ +#define _PATATE_GRENAILLE_ + +// First inclue Eigen Core +#include + +// Include common stuff +#include "common/defines.h" + +// Include Grenaille Core components +#include "Grenaille/Core/enums.h" +#include "Grenaille/Core/basket.h" + +#include "Grenaille/Core/weightKernel.h" +#include "Grenaille/Core/weightFunc.h" + +#include "Grenaille/Core/plane.h" +#include "Grenaille/Core/covariancePlaneFit.h" + +#include "Grenaille/Core/orientedSphereFit.h" +#include "Grenaille/Core/curvature.h" +#include "Grenaille/Core/gls.h" + +// not supported on cuda +#ifndef __CUDACC__ +# include "Grenaille/Core/unorientedSphereFit.h" +#endif + + +// Include Grenaille Algorithms + +#endif //_PATATE_GRENAILLE_ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/guidelines.txt b/src/plugins_experimental/filter_multiscale_align/lib/Patate/guidelines.txt new file mode 100644 index 000000000..f8f4eb53c --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/guidelines.txt @@ -0,0 +1,188 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +In this file you can find few guidelines for developers. We don't give in-depth details, fell free to inspire you from existing code +and to use things that can make the code easy to read. If their is any lake, send us a mail on the devlist (patate-devel@lists.gforge.inria.fr). + +****** +Naming +****** + + +General Naming Rules +******************** +Function names, variable names, and filenames should be descriptive. In most of case use the CamelCase notation. + +File Names +********** +Use the lowerCamelCase notation : myFileIsCool.cpp + +Use .h for headers, .cpp for sources. If you are using templates use .hpp for the implemented part. + +Type Names +********** +Use the UpperCamelCase notation : MyClassIsReallyCool. Execept for enums, see below. + +Variable Names +************** +Use the lowerCamelCase notation. If you want, hungarian notation is tolerated. + +Use m_ for members variables. +Use _ for function params. + +void function(float _scale) +{ + float scale = _scale; + doSomething(scale); + + m_scale = scale; +} + +With hungarian notation : +void function(float _fScale) +{ + float fScale = _fScale; + doSomething(fScale); + + m_fScale = fScale; +} + + +Constant Name +************* + +Use a k followed by mixed case : kThisIsAConstant + +Function Names +************** +Regular functions have mixed case; accessors and mutators match the name of the variable. You can use get/set suffix if you want. + +Namespace Names +*************** +Namespace names can be in UpperCamelStyle for important or in lowerCamelCase for smaller namespaces. + +For example : + +namespace Granaille +{ + //many things + + namespace internal + { + //few things + } +} + +Enumerator Names +**************** +Use this style : MY_ENUM. +enum COOL_ENUM +{ + VALUE1 = 0, + VALUE2 = 1, + NBVALUE +} + +Macro Names +*********** +Same as enumerator : MY_MACRO_IS_COOL. + +Exceptions to Naming Rules +************************** +If you are naming something that is analogous to an existing C or C++ entity then you can follow the existing naming convention scheme. + + +********** +Formatting +********** + + +This are basics rules for a better reading of the code. We'll don't give more details. Feel free to llok existing code and respect those basic rules. + +Spaces versus Tabs +****************** +We use spaces for indentation (4 spaces for 1 indent level). Do not use tabs in your code. You should set your editor to emit spaces when you hit the tab key. + +Brackets +******** +Use the Allman style : +void function() +{ + while(condition) + { + doSomething(); + } +} + +For empty functions put both brackets on the same line : +void emptyFunction() +{} + +Function Declarations and Definitions +************************************* +Return type on the same line as function name except for templates. Parameters on the same line if the line length is not to long. +Functions look like this: + +ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) +{ + DoSomething(); + ... +} + +If you have too much arguments on one line: +ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2, + Type par_name3) +{ + DoSomething(); + ... +} + +You can also put one argument per line if the function name is very long and have many args. + +Class Format +************ +There is no order and no indentations for public, protected, private sections. you can group +methods by theme if you want. Prefer putting members variables at the end of the class. +Be sure to separate variables and methods even if they are in the same section. + +class MyClass : public OtherClass +{ +public: // No indent! + MyClass(); // Regular 2 space indent. + explicit MyClass(int var); + ~MyClass() {} + + void someFunction(); + void someFunctionThatDoesNothing() + {} + +private: + bool someInternalFunction(); + +private: // Private variables separated from privat methods + int _var; + int _otherVar_; +}; + +Namespace Formatting +******************** +Namespaces do not add an extra level of indentation. Use: + +namespace toto +{ + +void function() //No extra indentation within namespace. +{ + ... +} + +} + +Line Length +*********** +There is no length limit. The developer does with his felling but be moderate and don't have infinite line length... + + diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/patate.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/patate.mdoc new file mode 100644 index 000000000..08a576981 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/patate.mdoc @@ -0,0 +1,90 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +/*! + \mainpage notitle + + \section patate_news_sec New Release 0.5 + + \subsection patate_news_subsec Principal features + + - We worked on Grenaille API: uniformisation of methods/class names + STL iterators support for neighborhood traversal
+ - Strong performances improvements in Grenaille (plane fitting, spatial derivatives) + - Curved edges and color-space aware rendering in Vitelotte
+ - Several bugfixes in both Grenaille and Vitelotte
+ + We also continued to polish our documentations, examples and tests along the process. + See the [complete changelog](@ref patate_changelog) for details. + + \section patate_about_sec About Patate + + Patate is a header only C++/CUDA library for graphics applications released under the MPL license. + + It provides a collection of Computer Graphics techniques that incorporate the latest innovations from INRIA research teams working in the field. It strives for efficiency and ease-of-use by focusing on low-level core operators and key algorithms, organised in modules that each tackle a specific set of issues. The central goal of the library is to drastically reduce the time and efforts required to turn a research paper into a ready-to-use solution, for both commercial and academic purposes. + + The library is still in its infancy and we're actively working on it to bring you the latest of our published research techniques. Modules will be dealing with graphics domains as varied as multi-scale analysis, material appearance, vector graphics, expressive rendering and 2D animation. Okay, right now it's only multi-scale analysis, but if everything goes as planned, this sentence should be properly deleted in the upcoming months! + + \section patate_instructions_sec Basic FAQs + + - How do I get Patates to work with my computer?
+ If by that you mean "How can I download and install your mouth-watering library?", then the best place to start is the \ref patate_getting_started_page section, as you might have guessed from the onset. We do not advocate the conjoint physical use of real patates and computers. + + - What can I cook with Patates? Do you have code recipies?
+ I guess you're looking for a \link patate_overview_page list of available patates|modules \endlink, each briefly described with links to a specific user manual and a few examples? This might give you enough suggestions to start experimenting with your own recipies. + + - Where do these patates come from originally?
+ The Patate library relies on published scientific papers that we have collected in the \link citelist.html Citation\endlink section of the documentation. These will prove useful if you want to dig into the implementation of operators and algorithms. If the PatateLib contributes to a project that leads to a scientific publication or a commercial or free software, all we ask is to tell us about it, or at least cite the project using the bibtex entry provided at the end of this page. + + - What can I do if I get mashed in trouble?
+ If you get stuck or have a problem/bug using patates, please send an email to patate-info@lists.gforge.inria.fr for further assistance. Remember that the library is under development so we are pleased to get your feedback. + + - What does "Patate" mean, actually?
+ "Patate" stands for potato in French. Honestly, we kinda decided to give the library that name with no obvious reason apart from the funny way it sounds in French. But in retrospect, this analogy does make a lot of sense. Indeed, patates|modules provide tasty and nourishing features that are easy to incorporate into your own recipe|pipeline. Although patates are intended to grow seperately from each other, they will reveal even richer flavours when cooked together. And it's all Open source, thus if you want to know more about a specific patate, all you will have to do is to gently dig into the ground|documentation to learn how it works. Ultimately, everybody loves patates! + + - How are these patates licensed? Are their seeds patented?
+ Do not be afraid, our patates are the closest you can get to "organic" in our solanaceous analogy. The source code of the library is licensed under the Mozilla Public License (MPL), v. 2.0. Its main advantage is to grant inclusion of template files as long as the source code itself is not modified (see? It's even somewhat sustainable). For more details, a copy of the MPL can be found at http://mozilla.org/MPL/2.0/ . + + + + + \section patate_credits_sec Credits + + \subsection patate_credits_subsection_crew Crew + + - Nicolas Mellado : conception, implementation and examples
+ - Gautier Ciaudo : testing, documentation, refactoring and examples
+ - Simon Boyé : conception, implementation, documentation and examples
+ - Gael Guennebaud : conception and implementation
+ - Pascal Barla : conception and documentation
+ - Noam Kremen : implementation
+ + \subsection patate_credits_subsection_citation Citation + + If you use our library, please cite it using the following bibtex entry: + \verbatim + @MISC{PatateLib, + author = {Nicolas Mellado and Gautier Ciaudo and Simon Boy\'{e} and Ga\"{e}l Guennebaud and Pascal Barla}, + title = {Patate Lib}, + howpublished = {http://patate.gforge.inria.fr/}, + year = {2013} + } + \endverbatim + + \subsection patate_credits_subsection_users Users + + - Inria - Manao Team
+ - IRIT - Vortex Team
+ - The Foundry - Modo Team
+ - University College London - Smart Geometry Processing Group
+ - Technion - Israel Institute of Technology (link)
+ - Telecom ParisTech - Computer Graphics Group
+ + \subsection patate_credits_subsection_supports Supports + + - V-MUST (European Network of Excellence)
+ - ADT X-Shade (INRIA funding) + +*/ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/patate_changelog.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/patate_changelog.mdoc new file mode 100644 index 000000000..cc2b15d97 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/patate_changelog.mdoc @@ -0,0 +1,12 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +/*! + \page patate_changelog Patate's changelog + + \verbinclude CHANGELOG + +*/ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/patate_getting_started.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/patate_getting_started.mdoc new file mode 100644 index 000000000..dc883ed3e --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/patate_getting_started.mdoc @@ -0,0 +1,45 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +/*! + \page patate_getting_started_page Getting started + + \section getting_started_requirements_sec Requirements + + The only external library that is absolutely necessary to make Patates work is Eigen, the swiss army knife of linear algebra. Additional libraries might be required in specific instances, but only for advanced algorithms. We'll try to keep these dependencies to the minimum necessary, promised. Some examples will require additional libraries as well in an effort to keep them lean and legible. + + The modules are also compatible with CUDA, so that you may run patates on the GPU, provided you own an NVidia graphics card. This is an efficient way to make patates available through CUDA-compatible languages such as Matlab or Python. Eigen can now be compiled by nvcc (NVIDIA CUDA Compiler Driver), see Eigen documentation for more details. + + \section getting_started_download_sec Download + + The Patate library is currently under active development and some functionalities have already been implemented and thoroughly tested. If you want to get a taste of these prime features, use the latest package available here: https://gforge.inria.fr/projects/patate/. + + You may also want to monitor our efforts in developping the library, in which case you will want to access the development repository: + \code + git clone git://scm.gforge.inria.fr/patate/patate.git + \endcode + + \section getting_started_installation_sec Installation + + The Patate lib is an header-only library. It could not be easier to install: the only thing you have to do is to include the header of the module you want to use in your code. + Moreover, each patate|module is independent from the others, hence you may only use the header corresponding to the module you are interested in. For example: + \code + #include + \endcode + + That said, the (quite heavy) use of templates in some modules may increase compilation times. Precompiled headers are recommended if this becomes a source of frustration. You will also have to pre-compile patates using nvcc if you want to use CUDA versions. This is explained in more details in related examples. + + If you want to see Patates in action, you will of course have to compile examples. And the same is true if you want to get the latest version of the documentation you are presently reading (well, not the exact same documentation, unless you've already compiled it, but then why are you reading this?). In both cases, have a look at the readme file, everything is explained there, no need to duplicate that here. + + \section getting_started_first_step_sec First steps + + You're now ready to play with Patates! But where to start from? + Just have a look at the \link patate_overview_page list of available patates|modules.\endlink, or directly access the Patates user manuals (links under Patates names): + - \ref grenaille_user_manual_page "Grenaille": this module provides efficient methods for the fitting and analysis of point-clouds in arbitrary dimensions. + - \ref vitelotte_user_manual_page "Vitelotte": this module provides tool to manipulate mesh-based vector graphics with arbitrary connectivity. + + If you get stuck or have a problem/bug using patates, please register and email us using our public mailing list at https://lists.gforge.inria.fr/mailman/listinfo/patate-info. Remember that the library is under development so we are pleased to get your feedback. + */ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/patate_overview.mdoc b/src/plugins_experimental/filter_multiscale_align/lib/Patate/patate_overview.mdoc new file mode 100644 index 000000000..71be399a1 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/patate_overview.mdoc @@ -0,0 +1,54 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +/*! + \page patate_overview_page Patates + + This section provides a quick tour of currently available patates|modules. Each entry provides a brief description and links to its user manual, examples and references. + +
+ + \section patate_overview_sec_grenaille Grenaille + + \htmlonly +
+ \endhtmlonly + + Nicolas Mellado, Gautier Ciaudo, Gael Guennebaud, Pascal Barla + + This module provides efficient methods for the fitting and analysis of point-clouds in arbitrary dimensions. Sounds a bit abstract, and indeed, it is templated in every corner of the code. + + It's worth the effort though, as it may be used for varied purposes such as curvature computation, surface variation computation, surface reconstruction, scale-space analysis, image processing, sketch vectorization and much more! + + The good news is that the module is quite easy to use and extensible thanks to its programming interface that allows you to customize it to your needs. + +
+ + \ref grenaille_user_manual_page "User Manual"\ref grenaille_example_page "Examples"\ref Grenaille "References" + +
+ +
+ + \section patate_overview_sec_vitelotte Vitelotte + + \htmlonly +
+ \endhtmlonly + + Simon Boyé, Gautier Ciaudo, Gael Guennebaud, Pascal Barla + + This module provides tool to manipulate mesh-based vector graphics with arbitrary connectivity. It includes a generic mesh representation, a diffusion solver to generate colors from a sparse set of constraint, an OpenGL renderer and input/output through the mvg file format. + + This module also include a command-line tool called mvgtk that gives access to most of the library functionalities. + +
+ + \ref vitelotte_user_manual_page "User Manual"\ref vitelotte_example_page "Examples"\ref Vitelotte "References" + +
+ +*/ diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/vitelotte.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/vitelotte.h new file mode 100644 index 000000000..4bdce1581 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/vitelotte.h @@ -0,0 +1,34 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _PATATE_VITELOTTE_ +#define _PATATE_VITELOTTE_ + +#include + +#include "common/surface_mesh/surfaceMesh.h" + +#include "Vitelotte/Core/linearElement.h" +#include "Vitelotte/Core/quadraticElement.h" +#include "Vitelotte/Core/morleyElement.h" +#include "Vitelotte/Core/fvElement.h" + +#include "Vitelotte/Core/vgMesh.h" +#include "Vitelotte/Utils/mvgReader.h" +#include "Vitelotte/Utils/mvgWriter.h" + +#include "Vitelotte/Core/femSolver.h" +#include "Vitelotte/Core/linearElementBuilder.h" +#include "Vitelotte/Core/quadraticElementBuilder.h" +#include "Vitelotte/Core/morleyElementBuilder.h" +#include "Vitelotte/Core/fvElementBuilder.h" +#include "Vitelotte/Core/singularElementDecorator.h" + +#include "Vitelotte/Utils/dcMesh.h" +#include "Vitelotte/Utils/mvgWithCurvesReader.h" +#include "Vitelotte/Utils/mvgWithCurvesWriter.h" + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/lib/Patate/vitelotte_gl.h b/src/plugins_experimental/filter_multiscale_align/lib/Patate/vitelotte_gl.h new file mode 100644 index 000000000..6d92e0752 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/lib/Patate/vitelotte_gl.h @@ -0,0 +1,15 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _PATATE_VITELOTTE_GL_ +#define _PATATE_VITELOTTE_GL_ + +#include + +#include "Vitelotte/Core/vgMesh.h" +#include "Vitelotte/Utils/vgMeshRenderer.h" + +#endif diff --git a/src/plugins_experimental/filter_multiscale_align/multiscale_align.cpp b/src/plugins_experimental/filter_multiscale_align/multiscale_align.cpp new file mode 100644 index 000000000..d78e18a35 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/multiscale_align.cpp @@ -0,0 +1,1422 @@ +//#define _DEBUG_GLS +//#define DEBUG_SINGLE_POINT + +#include "multiscale_align.h" + +#include +#include +//#include + +#include +#include +#include +#include + +#include "utils.h" + + +using namespace vcg; +using namespace std; +using namespace Grenaille; + +class BaseSampler +{ +public: + BaseSampler(CMeshO* _m){m=_m; uvSpaceFlag = false; qualitySampling=false; tex=0;} + CMeshO *m; + QImage* tex; + int texSamplingWidth; + int texSamplingHeight; + bool uvSpaceFlag; + bool qualitySampling; + + void AddVert(const CMeshO::VertexType &p) + { + tri::Allocator::AddVertices(*m,1); + m->vert.back().ImportData(p); + } + + +}; // end class BaseSampler + + + + +// Default Constructor +MultiscaleAlign::MultiscaleAlign() +{ + srand (time(NULL)); +} + +//! This defines the min and max where to perform the comparison +void computeScaleBounds(const DescriptorBase &reference, + const DescriptorBase &toAlign, + float maxScaleRef, float maxScaleToAl, + int& xMin, int& xMax, int& yMin, int& yMax ){ + double logBase=std::log((double)toAlign.multiplier); + double minGlobScale=min(reference.minScale,toAlign.minScale); + xMax= (int)(std::log(maxScaleRef/minGlobScale) / logBase); + yMax= (int)(std::log(maxScaleToAl/minGlobScale) / logBase); + xMin= (int)(std::log(reference.minScale/minGlobScale) / logBase) +1 ; + yMin= (int)(std::log(toAlign.minScale/minGlobScale) / logBase) +1; +} + + +// Precompute the GLS descriptor, only for the selected points +template +void MultiscaleAlign::computeSimilarityMap(const DescriptorBase& reference, + const DescriptorBase& toAlign, + std::vector* toAlignDescr, + float maxscaleToAl){ + + CMeshO::PerVertexAttributeHandle* > descriptorsHandler; + descriptorsHandler = tri::Allocator::GetPerVertexAttribute* >(reference.model->cm, std::string("GLSDescr")); + +//reset quality to 0 + for (CMeshO::VertexIterator vj = reference.model->cm.vert.begin(); vj != reference.model->cm.vert.end(); ++vj) + if(! (*vj).IsS()){ + (*vj).Q() = 0; + (*vj).C() = Color4b(255,255,255,1); + } + + +#pragma omp parallel for + for(int i = 0; i < reference.selection.size(); i++){ + + unsigned int ind = reference.selection[i]; + CVertexO &vj=reference.model->cm.vert[ind]; + + // Use a local variable for each thread + float maxScale = vj.Q(); + int xMin, yMin, xMax, yMax; + computeScaleBounds(reference, toAlign, maxScale, maxscaleToAl, xMin, xMax, yMin, yMax); + + std::vector *descr = descriptorsHandler[ind]; + + DynamicSolver ds; + ds.solve(*toAlignDescr, toAlignDescr->size(), + *descr, descr->size(), + toAlign.multiplier, xMin, xMax, yMin, yMax); + + //std::cout << ind << " " << ds.confidence << std::endl; + + //DynamicProg::VotingStretchEstimation vse; + //float scale = vse.estimate(ds.xBest, ds.yBest, toAlign.multiplier); + + reference.model->cm.vert[ind].Q() = ds.confidence; + //reference.model->cm.vert[ind].Q() = 1.f/scale; + reference.model->cm.vert[ind].C() = Color4b(0,0,255*ds.confidence,1); + } +} + + +////// This function is not used now, but it should analyse the points find the border ones, and assign a maxscale w.r.t. borders +void MultiscaleAlign::checkBorders(DescriptorBase model, float radius){ + + + int borderPoints=0; + std::cout << "Check borders, scale " << 4.2*radius*radius << endl; + for (CMeshO::VertexIterator vj = model.model->cm.vert.begin(); vj != model.model->cm.vert.end(); ++vj) + { + + std::vector n; + std::vector squaredDist; + model.kdTree->doQueryDist((*vj).P(), 2.1*radius, n, squaredDist); + //std::cout << "Neighbors " << n.size() << endl; + if (n.size()<22) + { + //(*vj).C()=Color4b(255,0,0,1); + (*vj).Q()=0.0; + //(*vj).C()=Color4b(0,255,0,1); + borderPoints++; + } + else + { + //(*vj).C()=Color4b(255,0,0,1); + (*vj).Q()=-1.0; + } + } + std::cout << "Border points " << borderPoints << endl; + + std::vector n; + std::vector squaredDist; + if (borderPoints == 0) + { + float bigRad = 1.5*model.model->cm.bbox.Diag(); + for (CMeshO::VertexIterator vj = model.model->cm.vert.begin(); vj != model.model->cm.vert.end(); ++vj) + { + + //MyPoint query = model.model->cm.vert[k]; + model.kdTree->doQueryDist((*vj).P(), bigRad, n, squaredDist); + + //std::cout << "RefCoarse: " << refCoarse << " N size " << n.size() << std::endl; + //model.model->cm.vert[n.back()].C() = Color4b(0, 0, 255, 255); + //std::sort(squaredDist.begin(), squaredDist.end()); + (*vj).Q() = sqrt(squaredDist.back()); + //std::cout << "RefCoarse: " << reference.maxScale << " N size " << n.size() << std::endl; + n.clear(); + } + } + else + { + float radiusSearch = radius; + float bestRadius = 0; + //std::vector n; + while (n.size()cm.vert.size()) + { + n.clear(); + //std::cout << "Radius " << radiusSearch << endl; + for (CMeshO::VertexIterator vj = model.model->cm.vert.begin(); vj != model.model->cm.vert.end(); ++vj) + { + if ((*vj).Q() == 0.0) + { + model.kdTree->doQueryDist((*vj).P(), radiusSearch, n, squaredDist); + if (n.size() >= model.model->cm.vert.size()) break; + for (int i = 0; i < n.size(); i++) + { + //std::cout << "In N " << endl; + if (model.model->cm.vert[n[i]].Q() < 0.0f) + { + //std::cout << "Set " << radiusSearch << endl; + model.model->cm.vert[n[i]].Q() = radiusSearch; + bestRadius = radiusSearch; + } + } + n.clear(); + } + + } + radiusSearch *= 1.1; + } + + for (CMeshO::VertexIterator vj = model.model->cm.vert.begin(); vj != model.model->cm.vert.end(); ++vj) + { + if ((*vj).Q() < bestRadius/4.0f) + { + (*vj).Q() = 0.0; + } + } + + } + +} + +//////// This is the main function decribed inside +float MultiscaleAlign::process_scale(MeshModel* _reference, float radiusRef, float refCoarse, + MeshModel* _toAlign, float radiusToAlign, float toAlignCoarse, + float multiplier, unsigned int nbScales, Options opt){ + + + + float scaleFact=0.0; bool done=false; + + /////// Create the scales vector, and the DescriptorBase objects + + DescriptorBase toAlign(_toAlign, radiusToAlign, toAlignCoarse, multiplier, nbScales); + std::cout << "Compute toAlign KdTree..." << std::flush; + vcg::ConstDataWrapper wrapperVcg(&toAlign.model->cm.vert[0].P(), toAlign.model->cm.vert.size(), size_t(toAlign.model->cm.vert[1].P().V()) - size_t(toAlign.model->cm.vert[0].P().V())); + toAlign.kdTree = new vcg::KdTree(wrapperVcg); + std::cout << "DONE" << std::endl; + + DescriptorBase reference(_reference, radiusRef, refCoarse, multiplier, nbScales); + std::cout << "Compute reference KdTree..." << std::flush; + vcg::ConstDataWrapper wrapperVcgR(&reference.model->cm.vert[0].P(), reference.model->cm.vert.size(), size_t(reference.model->cm.vert[1].P().V()) - size_t(reference.model->cm.vert[0].P().V())); + reference.kdTree = new vcg::KdTree(wrapperVcgR); + std::cout << "DONE" << std::endl; + + if (opt.checkBorders) + { + checkBorders(reference, radiusRef); + checkBorders(toAlign, radiusToAlign); + //return 1; + + } + + std::vector descrList; + std::vector< int > seeds; +#define ONLY_ONE +#ifdef ONLY_ONE + + //int toAlignId = 6701; // gargoself + //int toAlignId = 12111; // gargo200k + //int toAlignId = 16028; // gargo200k_cut + //int toAlignId = 16026; // gargo200k_cut_noscale + //int referenceId = 26051; + int referenceId = 274; // Surrey + //int referenceId = 169; //200 samples + + + typedef typename std::vector Container; + using vcg::tri::Allocator; + + //std::vector n; + //std::vector squaredDist; + //MyPoint query = reference.model->cm.vert[referenceId]; + //reference.kdTree->doQueryDist(reference.model->cm.vert[referenceId].P(), refCoarse, n, squaredDist); + // + ////std::cout << "RefCoarse: " << refCoarse << " N size " << n.size() << std::endl; + //reference.model->cm.vert[n.back()].C() = Color4b(0, 0, 255, 255); + //reference.maxScale = Distance(reference.model->cm.vert[referenceId].P(), reference.model->cm.vert[n.back()].P()) / 1.5f; + ////std::cout << "RefCoarse: " << reference.maxScale << " N size " << n.size() << std::endl; + + reference.maxScale = reference.model->cm.vert[referenceId].Q(); + std::vectorScales; + Utils::sampleScaleInterval(min(radiusToAlign, radiusRef), + max(toAlignCoarse, reference.model->cm.vert[referenceId].Q()), + multiplier, + nbScales, + Scales); + + /*CMeshO::PerVertexAttributeHandle descriptorsHandler; + descriptorsHandler = + Allocator::GetPerVertexAttribute + (reference.model->cm, std::string("GLSDescr")); + + descriptorsHandler[referenceId] = new std::vector(); + + computeDescriptor(reference.model->cm.vert[referenceId], + Scales, + reference, + descriptorsHandler[referenceId]);*/ + + +#endif + + + + //std::cout << "Start" << std::endl; + + //unsigned int ind = 13785; + CMeshO::PerVertexAttributeHandle* > referenceDescriptorsHandler; + referenceDescriptorsHandler = tri::Allocator::GetPerVertexAttribute* >(reference.model->cm, std::string("GLSDescr")); + + referenceDescriptorsHandler[referenceId] = new std::vector(); + computeDescriptor(reference.model->cm.vert[referenceId], Scales, reference, referenceDescriptorsHandler[referenceId]); + + reference.model->cm.vert[referenceId].C() = Color4b(0, 255, 0, 1); + //toAlign.model->cm.vert[toAlignId].C() = Color4b(0, 255, 0, 1); + //float maxScale = MAX_SCALE_MULTIPLIER*reference.model->cm.bbox.Diag(); + + /*for (CMeshO::VertexIterator vj = toAlign.model->cm.vert.begin(); vj != toAlign.model->cm.vert.end(); ++vj) + (*vj).Q()=0.0;*/ + + std::vector, float>> bestScales; + std::pair init(0, 0.0); + std::pair, float> init2(init, 0.0); + for (int i = 0; i < 20; i++) + bestScales.push_back(init2); + + /*toAlign.selection.clear(); + toAlign.selection.push_back(4775); + toAlign.model->cm.vert[4775].C() = Color4b(0, 255, 0, 255); */ + + //std::cout << "Selected points: " << toAlign.selection.size() << std::endl; + +// output seeds for debug purpose + for (int i = 0; i < toAlign.selection.size(); i++) { + + + std::cout << "Seed " << i+1 << " out of " << toAlign.selection.size() << std::endl; + int indToAl = toAlign.selection[i]; + if (toAlign.model->cm.vert[indToAl].Q() > 0) + { + + //n.clear(); + //squaredDist.clear(); + //query = toAlign.model->cm.vert[indToAl]; + //toAlign.kdTree->doQueryDist(toAlign.model->cm.vert[indToAl].P(), toAlignCoarse, n, squaredDist); + + ////std::cout << "toAlignCoarse: " << toAlignCoarse << " N size " << n.size() << std::endl; + //toAlign.model->cm.vert[n.back()].C() = Color4b(0, 0, 255, 255); + //toAlign.maxScale = Distance(toAlign.model->cm.vert[indToAl].P(), toAlign.model->cm.vert[n.back()].P())/1.5f; + ////std::cout << "toAlignCoarse: " << toAlign.maxScale << " N size " << n.size() << std::endl; + + toAlign.maxScale = toAlign.model->cm.vert[indToAl].Q(); + // Use a local variable for each thread + //float maxscaleToAl = MAX_SCALE_MULTIPLIER*toAlign.model->cm.bbox.Diag(); + int xMin, yMin, xMax, yMax; + computeScaleBounds(reference, toAlign, reference.maxScale, toAlign.maxScale, xMin, xMax, yMin, yMax); + + //std::cout << "Computed Scale bounds!: " << std::endl; + + //std::cout << "Computing GLS...: " << std::endl; + + CMeshO::PerVertexAttributeHandle* > descriptorsHandlerAlign; + descriptorsHandlerAlign = tri::Allocator::GetPerVertexAttribute* >(toAlign.model->cm, std::string("GLSDescr")); + + descriptorsHandlerAlign[indToAl] = new std::vector(); + + computeDescriptor(toAlign.model->cm.vert[indToAl], + Scales, + toAlign, + descriptorsHandlerAlign[indToAl]); + + //reference.model->cm.vert[ind].C() = Color4b(0, 255, 0, 1); + std::vector *descrAlign = descriptorsHandlerAlign[indToAl]; + + //std::cout << "Done!: " << std::endl; + + //std::cout << "xMin: " << xMin << "xMax: " << xMax << "yMin: " << yMin << "yMax: " << yMax << "Maxscale: " << reference.maxScale << "MaxScaleToAl: " << toAlign.maxScale << std::endl; + DynamicSolver ds; + ds.solve(*referenceDescriptorsHandler[referenceId], referenceDescriptorsHandler[referenceId]->size(), + *descrAlign, descrAlign->size(), + toAlign.multiplier, xMin, xMax, yMin, yMax); + + while (ds.estScales.size() > 10) + ds.estScales.pop_back(); + + for (int i = 0; i < ds.estScales.size(); i++) + { + //std::cout << "Confidence " << ds.estScales[i].second << std::endl; + ds.estScales[i].second = recalcConfidence(reference, toAlign, ds.estScales[i].first, nbScales, referenceId, indToAl); + //std::cout << "Confidence after " << ds.estScales[i].second << std::endl; + + } + + std::sort(ds.estScales.begin(), ds.estScales.end(), [](auto &left, auto &right) { + return left.second > right.second; + }); + + /*for (int j = 0; j != ds.estScales.size(); j++) + {*/ + std::pair bestFirst(indToAl, ds.estScales[0].first); + if (ds.estScales[0].second > bestScales.back().second) + { + std::pair, float> best(bestFirst, ds.estScales[0].second); + bestScales.push_back(best); + std::sort(bestScales.begin(), bestScales.end(), [](auto &left, auto &right) { + return left.second > right.second; + }); + bestScales.pop_back(); + + + } + //std::cout << "Scale: " << ds.estScales[j].first << " Confidence: " << ds.estScales[j].second << endl; + //} + } + + + } + + for (int j = 0; j != bestScales.size(); j++) + { + toAlign.model->cm.vert[bestScales[j].first.first].C() = Color4b(bestScales[j].second*255, 0, 0, 1); + //toAlign.model->cm.vert[bestScales[j].first.first].Q() = bestScales[j].first.second; + std::cout << "Id: " << bestScales[j].first.first << " Scale: " << bestScales[j].first.second << " Confidence: " << bestScales[j].second << endl; + + } + + return 1.; + +} + +float MultiscaleAlign::recalcConfidence(DescriptorBase _reference, DescriptorBase _toAlign, float scale, unsigned int nbScales, int referenceID, int toAlignID) +{ + float newRadiusToAlign = _toAlign.minScale*scale; + float newToAlignCoarse = _toAlign.maxScale*scale; + + //std::cout << "MinScaleD " << _reference.minScale << " MaxScaleD " << _reference.maxScale << " MinScaleA " << _toAlign.minScale << " MaxScaleA " << _toAlign.maxScale << std::endl; + + //std::cout << "Scale " << scale << " oldMin " << _toAlign.minScale << " newMin " << newRadiusToAlign << " oldMax " << _toAlign.maxScale << " newMax " << newToAlignCoarse << std::endl; + + float minRadius = std::max(_reference.minScale, newRadiusToAlign); + float maxRadius = std::min(_reference.maxScale, newToAlignCoarse); + float step = (maxRadius - minRadius) / nbScales; + + //std::cout << "Step " << step << std::endl; + //std::cout << "MinRadius " << minRadius << " MaxRadius " << maxRadius << std::endl; + + std::vectorScalesDesc; + std::vectorScalesToAl; + for (int i = 0; i < nbScales; i++) + { + ScalesDesc.push_back(minRadius + i*step); + ScalesToAl.push_back(ScalesDesc[i] / scale); + //std::cout << "ScaleDescr " << ScalesDesc[i] << " ScaleToAl " << ScalesToAl[i] << std::endl; + } + + //std::cout << "NbScales " << ScalesDesc.size() << std::endl; + + _toAlign.minScale = ScalesDesc[0]/ scale; + _toAlign.maxScale = ScalesDesc.back() / scale; + _reference.minScale = ScalesDesc[0]; + _reference.maxScale = ScalesDesc.back(); + + std::vector *refDescr = new std::vector(); + std::vector* toAlDescr = new std::vector(); + + computeDescriptor(_reference.model->cm.vert[referenceID], ScalesDesc, _reference, refDescr); + computeDescriptor(_toAlign.model->cm.vert[toAlignID], ScalesToAl, _toAlign, toAlDescr); + + DynamicSolver ds; + ds.solve_givenScale(*refDescr, *toAlDescr, 0, 1, 0, ScalesDesc.size(), 0, ScalesDesc.size()); + + //std::cout << "Confidence " << ds.confidence << std::endl; + + + return ds.confidence/ ScalesDesc.size(); + +} + +////// This function makes the main search for points, see comments inside +std::vector < Cand > MultiscaleAlign::searchMatches(DescriptorBase toAlign, DescriptorBase reference, int ind, std::vectorScales, bool computeAlignDescr) +{ + std::cout << "Computing toAlign descriptor" << std::endl; + + CMeshO::PerVertexAttributeHandle* > descriptorsToAlign; + descriptorsToAlign = tri::Allocator::GetPerVertexAttribute* >(toAlign.model->cm, std::string("GLSDescr")); + + if (computeAlignDescr){ + descriptorsToAlign[ind] = new std::vector(); + computeDescriptor(toAlign.model->cm.vert[ind], Scales, toAlign, descriptorsToAlign[ind]); + } + + + CMeshO::PerVertexAttributeHandle* > descriptorsReference; + descriptorsReference = tri::Allocator::GetPerVertexAttribute* >(reference.model->cm, std::string("GLSDescr")); + + float maxscaleToAl=toAlign.model->cm.vert[ind].Q(); + + + + //////// + // FIRST POINT: Given the first point, all the seams in the reference model are checked, and the best FIRST_POINTS points are put in a list + ////////////// + + std::vector < Cand > firstPoints; + Cand cpoint; cpoint.confidence=0.0; cpoint.ind=-1; + firstPoints.push_back(cpoint); + + std::cout << "Searching first point..." << std::endl; + +#pragma omp parallel for//num_threads(2) + for(int selK = 0; selK < reference.selection.size(); selK++){ + //for (int k =0; k < reference.model->cm.vn; k++) { + unsigned int k = reference.selection[selK]; + + CVertexO vj=reference.model->cm.vert[k]; + + // Use a local variable for each thread + float maxScaleRef = vj.Q(); + int xMin, yMin, xMax, yMax; + computeScaleBounds(reference, toAlign, maxScaleRef, maxscaleToAl, xMin, xMax, yMin, yMax); + + std::vector *descr = descriptorsReference[k]; + std::vector* result = descriptorsToAlign[ind]; + + DynamicSolver ds; + ds.solve(*descr, descr->size(), + *result, result->size(), + toAlign.multiplier, xMin, xMax, yMin, yMax); + +#pragma omp critical + { + std::vector::iterator it; + it = firstPoints.begin(); + //std::cout << "Confidence " << ds.confidence() < vse; + cpoint.confidence=ds.confidence; + cpoint.ind = k; + //std::cout << "Pre-estimate " <FIRST_POINTS) + firstPoints.pop_back(); + break; + } + } + } + } + //}else + // std::cout << "Reject Ind: " << k << std::endl; + } + + + if (firstPoints[0].ind != -1){ + + std::cout << "Ind of Candidate " << ind << std::endl; + for(unsigned int l=0; lcm.vert[firstPoints[l].ind].C() = Color4b(255,0,0,1); + +//// This saves the data of the list of matched points in a cvs file +#ifdef SAVE_CVS + QString filename1=QString("File_Fitness%1.csv") + .arg(l); + QString filename2=QString("File_Kappa%1.csv") + .arg(l); + QString filename3=QString("File_Tau%1.csv") + .arg(l); + QFile file1(filename1); + QFile file2(filename2); + QFile file3(filename3); + file1.open(QFile::WriteOnly); + QTextStream stream1(&file1); + file2.open(QFile::WriteOnly); + QTextStream stream2(&file2); + file3.open(QFile::WriteOnly); + QTextStream stream3(&file3); + + double dMax=0.0; + for (unsigned int i=0; isize(); i++) + { + if(descriptorsToAlign[ind]->at(i).geomVar()>dMax) + { + dMax=descriptorsToAlign[ind]->at(i).geomVar(); + } + } + + MyPoint query (reference.model->cm.vert[firstPoints[l].ind]); + query.normal().normalize(); + + reference.maxScale=reference.model->cm.vert[firstPoints[l].ind].Q(); + + std::vector descr; + computeDescriptor(query, Scales, reference, &descr); + for (unsigned int i = 0; isize(); i++) + { + stream1 << descriptorsToAlign[ind]->at(i).fitness() << "\t" ; // this writes first line with + stream2 << descriptorsToAlign[ind]->at(i).kappa() << "\t" ; // this writes first line with + stream3 << descriptorsToAlign[ind]->at(i).tau() << "\t" ; // this writes first line with + } + stream1 << "\n" ; + stream2 << "\n" ; + stream3 << "\n" ; + //std::cout << "Eval first column" << std::endl; + for (unsigned int j = 0; jcm.vert[queryId].C()=Color4b(0,0,0,1); + std::cout << "Point not matched !! (conf:" + // << ds.confidence + << ")" + << std::endl; + } + + + return firstPoints; + +} + +bool MultiscaleAlign::searchTranf(const DescriptorBase &toAlign, + const DescriptorBase &reference, + const std::vector &seeds, + const std::vector > &seedList, + const std::vector &Scales, Options opt) +{ + const int candSeed=seedList.size()-1; + //std::cout << "Candseed " << candSeed << std::endl; + + for (int i=0; i(opt.expectedScale/1.4)) + { + std::vector > corrs; + //std::cout << "Try a scale " << seedList[j][k].scale << std::endl; + corrs.reserve(seedList[j].size()); + //std::cout << "Try..." << sPoint.scale << " " << seedList[j][k].scale << std::endl; + if( isScaleOk(sPoint.scale,seedList[j][k].scale,toAlign.multiplier) && + isCoupleOk(toAlign.model->cm.vert[seeds[j]], + reference.model->cm.vert[seedList[j][k].ind], + toAlign.model->cm.vert[seeds[candSeed]], + reference.model->cm.vert[sPoint.ind], + sPoint.scale ) ) + { + //std::cout << "Selected!" << std::endl; + corrs.push_back( std::pair( seeds[j], seedList[j][k].ind)); + corrs.push_back( std::pair( seeds[candSeed], sPoint.ind)); + + //std::cout << "Searching third points..." << std::endl; + + for(unsigned int s2=0; s2 thirdPoints = getCandidates(toAlign, reference, seeds[s2], corrs, sPoint.scale, Scales); + if(thirdPoints[0].ind!=-1) + { + //std::cout << "Third point list size " << thirdPoints.size() << std::endl; + + corrs.push_back(std::pair(seeds[s2], thirdPoints[0].ind)); + + if(!opt.useQuadriplets) + { + //std::cout << "Checking Triplets " << g+1 << " out of " << thirdPoints.size() << std::endl; + if(checkTriplets(toAlign, reference, corrs, thirdPoints,sPoint.scale, opt.alignError)) + { + return true; + } + //corrs.pop_back(); + } + else for (unsigned int g=0; gfourthPoints = getCandidates(toAlign, reference, seeds[s3], corrs, sPoint.scale, Scales); + if(fourthPoints[0].ind!=-1) + { + corrs.push_back(std::pair(seeds[s3], fourthPoints[0].ind)); + //std::cout << "Fourth point list size " << fourthPoints.size() << std::endl; + + //std::cout << "Checking Quadriplets " << t+1 << " out of " << fourthPoints.size() << std::endl; + if(checkQuadriplets(toAlign, reference, corrs, fourthPoints,sPoint.scale, opt.alignError)) + { + return true; + } + corrs.pop_back(); + + } + } + } + //corrs.pop_back(); + + + } + corrs.pop_back(); + } + } + } + + + } + } + } + } + } + + return false; + +} + + + +////// Given a set of couples of points and a point on the toAlign model, it finds a list of matches in the right area +std::vector +MultiscaleAlign::getCandidates(const DescriptorBase& toAlign, + const DescriptorBase& reference, + int ind, + const std::vector >& corrs, + float scaleFact, + const std::vector &Scales) +{ + std::vector output; + output.clear(); + +// DynamicProg::VotingStretchEstimation vse; + Cand cpoint; cpoint.confidence=0.0; cpoint.ind=-1; + output.push_back(cpoint); + + + //toAlign.model->cm.vert[ind].C()=Color4b(0,0,255,1); + CMeshO::PerVertexAttributeHandle* > descriptorsToAlign; + descriptorsToAlign = tri::Allocator::GetPerVertexAttribute* >(toAlign.model->cm, std::string("GLSDescr")); + descriptorsToAlign[ind] = new std::vector(); + computeDescriptor(toAlign.model->cm.vert[ind], Scales, toAlign, descriptorsToAlign[ind]); + + CMeshO::PerVertexAttributeHandle* > descriptorsReference; + descriptorsReference = tri::Allocator::GetPerVertexAttribute* >(reference.model->cm, std::string("GLSDescr")); + + float maxscaleToAl=toAlign.model->cm.vert[ind].Q(); + +#pragma omp parallel for + //for (int l =0; l < reference.model->cm.vn; l++) + for(int selK = 0; selK < reference.selection.size(); selK++) + { + unsigned int l = reference.selection[selK]; + if(/*reference.model->cm.vert[l].IsS() && */isReferenceOk(l, reference.model, ind, toAlign.model, corrs, scaleFact)) + { + //std::cout << "Try " << l << " of " << reference.model->cm.vn << std::endl; + //reference->cm.vert[l].C()=Color4b(0,0,255,1); + //std::cout << "Candidato" << std::endl; + CVertexO vj=reference.model->cm.vert[l]; + MyPoint query (vj); + query.normal().normalize(); + float maxScaleRef = vj.Q(); + std::vector *descr = descriptorsReference[l]; +// std::vector descr; +// computeDescriptor(query, Scales, reference, &descr); + std::vector* result = descriptorsToAlign[ind]; + +// double logBase=std::log((double)toAlign.multiplier); +// double minGlobScale=min(reference.minScale,toAlign.minScale); +// int xMax= (int)(std::log(reference.maxScale/minGlobScale) / logBase) + 1; +// int yMax= (int)(std::log(toAlign.maxScale/minGlobScale) / logBase) + 1; +// int xMin= (int)(std::log(reference.minScale/minGlobScale) / logBase) +1 ; +// int yMin= (int)(std::log(toAlign.minScale/minGlobScale) / logBase) +1; + + int xMin, yMin, xMax, yMax; + computeScaleBounds(reference, toAlign, maxScaleRef, maxscaleToAl, xMin, xMax, yMin, yMax); + + //std::cout << "Solve " << std::endl; + /////// This calculates the confidence only for the scale defined by the first match + DynamicSolver ds; + ds.solve_givenScale(*descr, *result, + toAlign.multiplier, scaleFact, xMin, xMax, yMin, yMax); + + //std::cout << ds.confidence << std::endl; + +#pragma omp critical + { + std::vector::iterator it; + it = output.begin(); + + //std::cout << "Confidence " << ds.confidence() <OTHER_POINTS) + output.pop_back(); + break; + } + } + } + } + } + + } + + return output; + + + +} + + +/*! + * \brief MultiscaleAlign::extractSIFTPoints + * \param mesh + * \param list + * + * This method assumes that the mesh descriptor has already been computed. + * Need to construct a dedicated kdtree containing only the selected points + */ +//void MultiscaleAlign::extractSIFTPoints(const DescriptorBase& mesh, +// std::vector< SiftPoint > &list){ +// +// //std::cout << "Init local KdTree" << std::endl; +// +// // Init the local KdTree +// MultiscaleAlignNamespace::KdTree tree (mesh.selection.size()); +// for(int i = 0; i < mesh.selection.size(); i++){ +// const unsigned int& ind = mesh.selection[i]; +// tree.set(ind, mesh.model->cm.vert[ind].cP()); +// } +// tree.finalize(); +// //std::cout << "Done" << std::endl; +// +// CMeshO::PerVertexAttributeHandle* > descriptors; +// descriptors = tri::Allocator::GetPerVertexAttribute* >(mesh.model->cm, std::string("GLSDescr")); +// +// // number of vertices to compare +// tree.setMaxNofNeighbors(16); +// +// //std::cout << "1" << std::endl; +// +////#pragma omp parallel for +// for(int i = 0; i < mesh.selection.size(); i++) +// { +// const unsigned int& ind = mesh.selection[i]; +// //std::cout << "Analyse element " << ind << std::endl; +// +// std::vector* descr = descriptors[ind]; +// +// unsigned int nbScale = descr->size(); +// +// // Collect the neighborhood only when needed +// bool neiCollected = false; +// +// //std::cout << "Data collected" << std::endl; +// +// /// 1 Detect if we are on a local curvature minimum along scale axis +// for(unsigned int s = nbScale-2; s > 0 ; s--){ +// +// float kprev = descr->at(s-1).kappa(); +// float k = descr->at(s ).kappa(); +// float knext = descr->at(s+1).kappa(); +// +// if(! isnan(kprev) && ! isnan(k) && ! isnan(knext) ){ +// +// bool isMin = ( kprev > k && knext > k ); +// bool isMax = ( kprev < k && knext < k ); +// +// if ( isMin || isMax ){ +// //std::cout << "This guy is an extrema at scale "<< s << std::endl; +// +// bool currentExtr = true; +// +// if (! neiCollected){ +// //std::cout << "We need to collect its neighborhood" << std::endl; +// neiCollected = true; +// MyPoint query(mesh.model->cm.vert[ind]); +// +//#pragma omp critical +// tree.doQueryK(query.pos()); +// +// //std::cout << "Done" << std::endl; +// } +// +// for(unsigned int ki = 0; ki!=tree.getNofFoundNeighbors(); ki++){ +// unsigned int nId = tree.getNeighbor(ki).originalId; +// +// //std::cout << "Compare with neighbor " << nId << std::endl; +// +// if (isMin){ +// if (k > descriptors[nId]->at(s-1).kappa() || +// k > descriptors[nId]->at(s ).kappa() || +// k > descriptors[nId]->at(s+1).kappa()) { +// currentExtr = false; +// //std::cout << "Minimum rejected" << std::endl; +// break; +// } +// }else if (isMax){ +// if (k < descriptors[nId]->at(s-1).kappa() || +// k < descriptors[nId]->at(s ).kappa() || +// k < descriptors[nId]->at(s+1).kappa()) { +// currentExtr = false; +// //std::cout << "Maximum rejected" << std::endl; +// break; +// } +// } +// } +// +// // We are currently on a local extrema +// if (currentExtr){ +// //std::cout << "Extrema selected and kept !" << std::endl; +// +// SiftPoint sp; +// sp.ind = ind; +// sp.scaleId = s; +// +//#pragma omp critical +// list.push_back(sp); +// +// //std::cout << "Update Its color" << std::endl; +// mesh.model->cm.vert[ind].C()=Color4b(255, +// 255*int(float(s+1)/float(nbScale)), +// 255*int(float(s+1)/float(nbScale)), +// 1); +// break; +// } +// } +// } +// }// for scales +// }// for points +//} + + +/*! + * \brief MultiscaleAlign::extractSIFTPoints + * \param mesh + * \param list + * + * This method assumes that the mesh descriptor has already been computed. + * Need to construct a dedicated kdtree containing only the selected points + */ +void MultiscaleAlign::extractDescriptivePoints(const DescriptorBase& mesh, + std::vector< DescrPoint > &list){ + + + + const float numSel = (float)mesh.selection.size(); + + //std::cout << "Got size" << std::endl; + CMeshO::PerVertexAttributeHandle* > descriptor; + descriptor = tri::Allocator::GetPerVertexAttribute* >(mesh.model->cm, std::string("GLSDescr")); + + std::vector *descr = descriptor[mesh.selection[0]]; + + //std::cout << "Got descriptor" << std::endl; + + std::vector meanK; + std::vector meanT; + + for(int k=0; ksize(); k++) + { + meanK.push_back(0.0); + meanT.push_back(0.0); + } + + //std::cout << "Initialize" << std::endl; + +//#pragma omp parallel for + std::vector::iterator kIt, tIt; + for(int i = 0; i < mesh.selection.size(); i++) + { + const unsigned int& ind = mesh.selection[i]; + + //std::cout << "Analyse element " << ind << std::endl; + descr = descriptor[ind]; + kIt = meanK.begin(); + tIt = meanT.begin(); + for(std::vector::const_iterator it=descr->begin(); + it != descr->end(); it++, kIt++, tIt++) + { + if(!isnan((*it).kappa())) + { + (*kIt) += ((*it).kappa()/numSel); + (*tIt) += ((*it).tau()/numSel); + } + } + } + /*for(int i = 0; i cm.vn; i++) + { + mesh.model->cm.vert[i].C()=Color4b(0,0,0,1); + + }*/ + + //#pragma omp parallel for + + for(int i = 0; i < mesh.selection.size(); i++) + { + const unsigned int& ind = mesh.selection[i]; + kIt = meanK.begin(); + tIt = meanT.begin(); + + float descript=0.0; float diffK, diffT; + descr = descriptor[ind]; + for(std::vector::const_iterator it=descr->begin(); + it != descr->end(); it++, kIt++, tIt++) + { + if(!isnan((*it).kappa())) + { + diffK = (*it).kappa()- (*kIt); + diffT = (*it).tau() - (*tIt); + descript += (diffK*diffK+diffT*diffT); + } + } + DescrPoint p; p.ind=ind; p.descrip=descript; + list.push_back(p); + /*mesh.model->cm.vert[mesh.selection[i]].Q()=descript; + mesh.model->cm.vert[mesh.selection[i]].C()=Color4b(255*descript,255*descript,255*descript,1);*/ + //std::cout << "Descriptiveness " << descript << std::endl; + + + } + +} + + +// +////////// This is the main function decribed inside +//float MultiscaleAlign::process_scale(MeshModel* _reference, float radiusRef, float refCoarse, +// MeshModel* _toAlign, float radiusToAlign, float toAlignCoarse, +// float multiplier, unsigned int nbScales, Options opt) { +// +// +// +// float scaleFact = 0.0; bool done = false; +// +// /////// Create the scales vector, and the DescriptorBase objects +// std::vectorScales; +// Utils::sampleScaleInterval(min(radiusToAlign, radiusRef), +// max(toAlignCoarse, refCoarse), +// multiplier, +// nbScales, +// Scales); +// +// DescriptorBase toAlign(_toAlign, radiusToAlign, toAlignCoarse, multiplier, nbScales); +// std::cout << "Compute toAlign KdTree..." << std::flush; +// toAlign.kdTree = Utils::makeKNNTree(toAlign.model); +// std::cout << "DONE" << std::endl; +// +// DescriptorBase reference(_reference, radiusRef, refCoarse, multiplier, nbScales); +// std::cout << "Compute reference KdTree..." << std::flush; +// reference.kdTree = Utils::makeKNNTree(reference.model); +// std::cout << "DONE" << std::endl; +// +// std::vector descrList; +// std::vector< int > seeds; +//#define ONLY_ONE +//#ifdef ONLY_ONE +// +// int toAlignId = 12111; +// int referenceId = 13785; +// +// typedef typename std::vector Container; +// using vcg::tri::Allocator; +// +// CMeshO::PerVertexAttributeHandle descriptorsHandler; +// descriptorsHandler = +// Allocator::GetPerVertexAttribute +// (reference.model->cm, std::string("GLSDescr")); +// +// descriptorsHandler[referenceId] = new std::vector(); +// +// computeDescriptor(reference.model->cm.vert[referenceId], +// Scales, +// reference, +// descriptorsHandler[referenceId]); +// +// CMeshO::PerVertexAttributeHandle descriptorsHandler2; +// descriptorsHandler2 = +// Allocator::GetPerVertexAttribute +// (toAlign.model->cm, std::string("GLSDescr")); +// +// descriptorsHandler2[toAlignId] = new std::vector(); +// +// computeDescriptor(toAlign.model->cm.vert[toAlignId], +// Scales, +// toAlign, +// descriptorsHandler2[toAlignId]); +// +//#else +// QTime time; +// time.start(); +// std::cout << "[ToAlign] Computing GLS descriptors ..." << std::endl; +// preComputeDescriptor(toAlign, Scales, true); +// std::cout << "[ToAlign] GLS decriptors computed in " << time.elapsed() / 1000 << " secondes " << endl; +// +// //if (opt.useDescriptive) +// //{ +// // /// Compute GLS descriptor for the toAlign model +// // +// // //time.start(); +// // std::cout << "[ToAlign] Computing GLS descriptors ..." << std::endl; +// // preComputeDescriptor(toAlign, Scales, true); +// // //std::cout << "[ToAlign] GLS decriptors computed in " <* > descriptorsToAlign; +// // descriptorsToAlign = tri::Allocator::GetPerVertexAttribute* >(toAlign.model->cm, std::string("GLSDescr")); +// // std::cout << "Done." << endl; +// +// // +// // int init=rand() % toAlign.selection.size(); +// // std::cout << "init= " << init << " ind= " << toAlign.selection[init] << std::endl; +// +// //#ifdef DEBUG_SINGLE_POINT +// +// // seeds.resize(1); +// +// // seeds[0] = 23285; +// // int seeId = seeds[0]; +// +// // _toAlign->cm.vert[seeId].Q()=MAX_SCALE_MULTIPLIER*_toAlign->cm.bbox.Diag(); +// // std::cout << "Quality of seed: " << _toAlign->cm.vert[seeId].Q() << std::endl; +// +// // reference.selection.clear(); +// // reference.selection.push_back(seeId); +// +// // for (CMeshO::VertexIterator vj = _reference->cm.vert.begin(); vj != _reference->cm.vert.end(); ++vj) +// // (*vj).ClearS(); +// +// // _reference->cm.vert[seeId].SetS(); +// // _reference->cm.vert[seeId].Q()=MAX_SCALE_MULTIPLIER*_reference->cm.bbox.Diag(); +// // _reference->cm.vert[seeId].C()=Color4b(255,255,0,1); +// +// // CMeshO::PerVertexAttributeHandle* > referenceDescriptorsHandler; +// // referenceDescriptorsHandler = tri::Allocator::GetPerVertexAttribute* >(reference.model->cm, std::string("GLSDescr")); +// +// // referenceDescriptorsHandler[seeId] = new std::vector(); +// // computeDescriptor(reference.model->cm.vert[seeId], Scales, reference, referenceDescriptorsHandler[seeId]); +// +// // std::cout << "Testing on one point only" << std::flush; +// +// //#else +// // seeds = selectSeeds(toAlign.model, reference.model, toAlign.selection[init]); +// // //seeds = selectSeeds(toAlign.model, reference.model, -1); +// //#endif +// //} +// +// +// /// Compute GLS descriptor for the reference model +// //QTime time; +// time.start(); +// std::cout << "[Reference] Computing GLS descriptors ..." << std::endl; +// preComputeDescriptor(reference, Scales, true); +// std::cout << "[Reference] GLS decriptors computed in " << time.elapsed() / 1000 << " secondes " << endl; +// +// +// +// +//#ifdef DEBUG_SIMILARITY_MAP +// seeds.resize(1); +// seeds[0] = 6639; +//#endif +// +// // output seeds for debug purpose +// for (unsigned int j = 0; j < seeds.size(); j++) { +// std::cout << "Seed " << j << ": ind " << seeds[j] << ", quality " << toAlign.model->cm.vert[seeds[j]].Q() << std::endl; +// toAlign.model->cm.vert[seeds[j]].C() = Color4b(255, 0, 0, 1); +// } +// +// //return -1; +//#endif +//#define TEST +//#ifdef TEST +// /// Build simimarity map +// +// std::cout << "Start" << std::endl; +// +// //unsigned int ind = 13785; +// CMeshO::PerVertexAttributeHandle* > referenceDescriptorsHandler; +// referenceDescriptorsHandler = tri::Allocator::GetPerVertexAttribute* >(reference.model->cm, std::string("GLSDescr")); +// +// referenceDescriptorsHandler[referenceId] = new std::vector(); +// computeDescriptor(reference.model->cm.vert[referenceId], Scales, reference, referenceDescriptorsHandler[referenceId]); +// +// reference.model->cm.vert[referenceId].C() = Color4b(0, 255, 0, 1); +// //float maxScale = MAX_SCALE_MULTIPLIER*reference.model->cm.bbox.Diag(); +// +// for (CMeshO::VertexIterator vj = toAlign.model->cm.vert.begin(); vj != toAlign.model->cm.vert.end(); ++vj) +// (*vj).Q() = 0.0; +// +// std::cout << "Selected points: " << toAlign.selection.size() << std::endl; +// toAlign.selection.clear(); +// toAlign.selection.push_back(toAlignId); +// //toAlign.selection.push_back(2026); +// +// // output seeds for debug purpose +// for (int i = 0; i < toAlign.selection.size(); i++) { +// +// unsigned int indToAl = toAlign.selection[i]; +// +// // Use a local variable for each thread +// //float maxscaleToAl = MAX_SCALE_MULTIPLIER*toAlign.model->cm.bbox.Diag(); +// int xMin, yMin, xMax, yMax; +// computeScaleBounds(reference, toAlign, refCoarse, toAlignCoarse, xMin, xMax, yMin, yMax); +// +// CMeshO::PerVertexAttributeHandle* > descriptorsHandlerAlign; +// descriptorsHandlerAlign = tri::Allocator::GetPerVertexAttribute* >(toAlign.model->cm, std::string("GLSDescr")); +// +// //reference.model->cm.vert[ind].C() = Color4b(0, 255, 0, 1); +// std::vector *descrAlign = descriptorsHandlerAlign[indToAl]; +// +// //std::cout << "xMin: " << xMin << "xMax: " << xMax << "yMin: " << yMin << "yMax: " << yMax << "Maxscale: " << maxScale << "MaxScaleToAl: " << maxscaleToAl << std::endl; +// DynamicSolver ds; +// ds.solve(*referenceDescriptorsHandler[referenceId], referenceDescriptorsHandler[referenceId]->size(), +// *descrAlign, descrAlign->size(), +// toAlign.multiplier, xMin, xMax, yMin, yMax); +// +// //std::cout << "xBest: " << ds.xBest << "yBest: " << ds.yBest << "multiplier: " << toAlign.multiplier << std::endl; +// +// float scaleEst = Scalar(1.0) / std::pow(toAlign.multiplier, ds.yBest - ds.xBest); +// std::cout << "Scale: " << scaleEst << std::endl; +// toAlign.model->cm.vert[indToAl].Q() = scaleEst; +// int confi = (int)(ds.confidence*255.0); +// toAlign.model->cm.vert[indToAl].C() = Color4b(confi, confi, confi, 1); +// +// } +// +// //culo +// +// //toAlign.model->cm.vert[seeds[0]].C() = Color4b(0, 255, 0, 1); +// //toAlign.model->cm.vert[seeds[0]].Q() = 10; +// +// +// return 1.; +//#endif +// +//#ifdef DEBUG_SIMILARITY_MAP +// /// Build simimarity map +// std::vector* descr = new std::vector(); +// computeDescriptor(toAlign.model->cm.vert[seeds[0]], Scales, toAlign, descr); +// +// float maxscaleToAl = toAlign.model->cm.vert[seeds[0]].Q(); +// +// // Compute the similarity and put it into the quality field +// computeSimilarityMap(reference, toAlign, descr, maxscaleToAl); +// +// toAlign.model->cm.vert[seeds[0]].C() = Color4b(0, 255, 0, 1); +// //toAlign.model->cm.vert[seeds[0]].Q() = 10; +// +// +// return 1.; +//#endif +// //unsigned int count=1; +// //done=true; +// +// //#define ONLY_THE_FIRST_SEARCH +//#ifdef ONLY_THE_FIRST_SEARCH +// CVertexO vj = reference.model->cm.vert[seeds[0]]; +// +// MyPoint query(vj); +// query.normal().normalize(); +// +// // Use a local variable for each thread +// float maxScaleRef = vj.Q(); +// float maxscaleToAl = toAlign.model->cm.vert[seeds[0]].Q(); +// int xMin, yMin, xMax, yMax; +// computeScaleBounds(reference, toAlign, maxScaleRef, maxscaleToAl, xMin, xMax, yMin, yMax); +// //std::cout << "xMin: " << xMin << " xMax: " << xMax << " yMin: " << yMin << " yMax: " << yMax < descrRef, descrAlign; +// computeDescriptor(query, Scales, reference, &descrRef); +// computeDescriptor(toAlign.model->cm.vert[seeds[0]], Scales, toAlign, &descrAlign); +// +// DynamicSolver ds; +// ds.solve(descrRef, descrRef.size(), +// descrAlign, descrAlign.size(), +// toAlign.multiplier, xMin, xMax, yMin, yMax); +// +// float scale = Scalar(1.0) / std::pow(toAlign.multiplier, ds.yBest - ds.xBest); +// std::cout << "Scale: " << scale << std::endl; +// +// return -1; +//#else +// +// std::vector< std::vector < Cand > > seedList; int reIter = 0; +// for (int i = 0; icm.vert[seeds[i]].Q(); +// std::cout << "Computing seed " << i + 1 << " of " << seeds.size() << std::endl; +// //std::cout << "Seed point: " << seeds[0] << " toAlignCoarse: " << toAlign.model->cm.vert[seeds[0]].Q() << std::endl; +// +// ///// Given the first seed, we search for the scale and transformation to find the alignmen +// /// The last parameter avoid to recompute the toAlign descriptor when it is already done +// seedList.push_back(searchMatches(toAlign, reference, seeds[i], Scales, !opt.useDescriptive)); +// +// +// +// //std::cout << "Done searchcorr" << std::endl; +// if (seedList.size()>1) +// { +// done = searchTranf(toAlign, reference, seeds, seedList, Scales, opt); +// //done=true; +// +// } +// +// if (i == seeds.size() - 1) +// { +// reIter++; +// std::cout << "Shuffling the seeds, try n. " << reIter + 1 << std::endl; +// seeds.clear(); +// seedList.clear(); +// //std::cout << "Seeds size " << seeds.size() << std::endl; +// if (!opt.useDescriptive) +// { +// int init = rand() % toAlign.selection.size(); +// std::cout << "init= " << init << " ind= " << toAlign.selection[init] << std::endl; +// seeds = selectSeeds(toAlign.model, reference.model, toAlign.selection[init]); +// i = -1; +// } +// else +// { +// //reIter=5; +// seeds = selectSeedsDescr(toAlign, descrList, true); +// i = -1; +// } +// +// +// } +// +// +// +// } +// +// +// ///// Actually, no scale factor is returned by the function, but the transformation is applied +// if (done == false) +// return -1; +// else +// return getScaleFromTransf(toAlign.model->cm.Tr); +// +//#endif +//} + + diff --git a/src/plugins_experimental/filter_multiscale_align/multiscale_align.h b/src/plugins_experimental/filter_multiscale_align/multiscale_align.h new file mode 100644 index 000000000..eebbb805a --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/multiscale_align.h @@ -0,0 +1,103 @@ +#ifndef MULTISCALE_ALIGN_H +#define MULTISCALE_ALIGN_H +#include + +#include +#include "Patate/grenaille.h" + +//#include "localKdTree.h" +#include "generic_align.h" +#include "struct.h" + +#include + +#define PI 3.14159265 + +//////// Number of samples for the first poisson, it defines the size of the models which will be analysed +#define SUBSAMPLES 20000 +////// size of the first, second and third points list used for matching +#define FIRST_POINTS 10 +#define OTHER_POINTS 5 +//#define THIRD_POINTS 5 + +#define MAX_SCALE_MULTIPLIER 1 + +////// the alignment treshold (10 ok for mm, not so good for meters... it could be calculated based on the size of the model) +//#define ALIGNMENT_THRESHOLD 4.0 + +////// This permits to execute only the first search for a matching, for debug reasons +//#define ONLY_THE_FIRST_SEARCH + +////// This permits to define the first point in the toalign model by hand +//#define DEBUG_SINGLE_POINT + +////// This permits to compute the multiscale distance between a random seed and all the other points in the reference mesh +//#define DEBUG_SIMILARITY_MAP + +#define MULTIPLIER 1.2 + +//#define CHECKBORDERS true + +//#define SAVE_CVS + +using namespace vcg; + + +namespace MultiscaleAlignNamespace{ + template class KdTree; +} + +class MultiscaleAlign: public GenericAlign +{ +private: + +public: + MultiscaleAlign(); + + + + /**************************************************************************/ + /* Input Parameters */ + /**************************************************************************/ + + + /**************************************************************************/ + /* Processing */ + /**************************************************************************/ + +public: + void checkBorders(DescriptorBase model, float radius); + float process_scale(MeshModel* _reference, float radiusRef, float refCoarse, MeshModel* _toAlign, float radiusToAlign, float toAlignCoarse, float multiplier, unsigned int nbScales, Options opt); + float recalcConfidence(DescriptorBase _reference, DescriptorBase _toAlign, float scale, unsigned int nbScales, int referenceID, int toAlignID); + +private: + std::vector < Cand > searchMatches(DescriptorBase toAlign, DescriptorBase reference, int ind, std::vectorScales, bool computeAlignDescr = true); + bool searchTranf(const DescriptorBase& toAlign, + const DescriptorBase& reference, + const std::vector< int >& seeds, + const std::vector< std::vector < Cand > >& seedList, + const std::vector& Scales, + Options opt); + std::vector getCandidates(const DescriptorBase &toAlign, + const DescriptorBase &reference, + int ind, + const std::vector > &corrs, + float scaleFact, + const std::vector &Scales); + /*void extractSIFTPoints(const DescriptorBase& mesh, + std::vector &list);*/ + void extractDescriptivePoints(const DescriptorBase& mesh, + std::vector &list); + + template + void computeSimilarityMap(const DescriptorBase& reference, const DescriptorBase& toAlign, std::vector* toAlignDescr, float maxscaleToAl); + + + /**************************************************************************/ + /* Results */ + /**************************************************************************/ + + +}; + +#endif // MULTISCALE_ALIGN_H diff --git a/src/plugins_experimental/filter_multiscale_align/struct.h b/src/plugins_experimental/filter_multiscale_align/struct.h new file mode 100644 index 000000000..535c4df1f --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/struct.h @@ -0,0 +1,195 @@ +#ifndef STRUCT_H +#define STRUCT_H + + +#include + +#include "Patate/grenaille.h" + +#include "Dynamic/dynamicSolver.h" +#include "Dynamic/needlemanWunsch.h" +#include "Dynamic/scaleEstimation.h" + +//#include "localKdTree.h" +#include + +//// Candidates: the points that could be part of the matching +struct Options +{ + bool checkBorders; + bool useDescriptive; + float alignError; + float useQuadriplets; + float expectedScale; + int seedNumber; + float delta; +}; + +//// Candidates: the points that could be part of the matching +struct Cand +{ + int ind; + float scale; + float confidence; + bool operator()(Cand const &a, Cand const &b) { + return a.confidence > b.confidence; + } +}; + +struct SiftPoint +{ + int ind; + int scaleId; + bool operator()(const SiftPoint &a, const SiftPoint &b) { + return a < b; + } + bool operator<(const SiftPoint &other) const{ + return scaleId < other.scaleId; + } +}; + +struct DescrPoint +{ + int ind; + float descrip; + bool operator()(const DescrPoint &a, const DescrPoint &b) { + return a < b; + } + bool operator<(const DescrPoint &other) const{ + return descrip < other.descrip; + } +}; + + + +struct SeedCandidate +{ + int ind; + float impact; + bool operator()(const SeedCandidate &a, const SeedCandidate &b) { + return a < b; + } + bool operator<(const SeedCandidate &other) const{ + return impact < other.impact; + } +}; + + +// This class defines the input data format +class MyPoint{ +public: + enum {Dim = 3}; + typedef float Scalar; + typedef Eigen::Matrix VectorType; + typedef Eigen::Matrix MatrixType; // Type needed by Grenaille::AlgebraicSphere + MULTIARCH inline MyPoint(const VectorType &pos = VectorType::Zero(), + const VectorType& normal = VectorType::Zero()) + : _pos(pos), _normal(normal) {} + + // \todo Is it correct ? + MULTIARCH inline MyPoint(const CVertexO &v) + { v.P().ToEigenVector(_pos); + v.N().ToEigenVector(_normal); } + MULTIARCH inline const VectorType& pos() const { return _pos; } + MULTIARCH inline const VectorType& normal() const { return _normal; } + MULTIARCH inline VectorType& pos() { return _pos; } + MULTIARCH inline VectorType& normal() { return _normal; } +private: + VectorType _pos, _normal; +}; + +// define types that will be used to process data +typedef MyPoint::Scalar Scalar; +typedef Grenaille::DistWeightFunc > + WeightFunc; +typedef Grenaille::Basket + SphereFit; + +typedef Grenaille::Basket + DerivableSphereFit; + + + +//! \brief Functor used to compare two GLS Fit +template +struct Dynamic_GLS_cmp{ + inline _Scalar eval(const _Fit&f1, const _Fit&f2) const + { + if (f1.isReady() && f2.isReady()){ + double res = 1 -tanh(4.*f1.compareTo(f2)); + if (res<0.0 || isnan(res)) + return 0.0; + else + return res; + } + return 0.0; + } +}; + +typedef DynamicProg::NeedlemanWunsch DynamicProcedure; +typedef DynamicProg::DynamicSolver DynamicSolver; + + + +class DescriptorBase{ +public: + + MeshModel* model; + vcg::KdTree* kdTree; + std::vector selection; + float minScale; + float maxScale; + + float multiplier; + int nBScales; + + // Compute a vector containing all the selected vertices id. + void inline computeSelectionList() { + if (model){ + selection.clear(); + + unsigned int i = 0; + for(CMeshO::VertexIterator vi = model->cm.vert.begin(); vi != model->cm.vert.end(); ++vi, i++){ +#ifdef DEBUG_SIMILARITY_MAP + (*vi).SetS(); + (*vi).Q()=MAX_SCALE_MULTIPLIER*model->cm.bbox.Diag(); + selection.push_back(i); +#else + if((*vi).IsS()) + selection.push_back(i); +#endif + } + }else + std::cerr << "Can estimate selection" << std::endl; + } + + MULTIARCH inline DescriptorBase(MeshModel* _model, float _minScale, float _maxScale, float _multiplier, int _nBScales) + { model=_model; minScale=_minScale; maxScale=_maxScale; multiplier=_multiplier; nBScales=_nBScales; + computeSelectionList(); + } + +private: + +}; + + +static inline +std::ostream & operator<< (std::ostream &o, + const MyPoint &p){ + o << "pos( " << p.pos().transpose(); + o << "), normal(" << p.normal().transpose(); + o << ")"; + return o; +} + +#endif // STRUCT_H diff --git a/src/plugins_experimental/filter_multiscale_align/utils.h b/src/plugins_experimental/filter_multiscale_align/utils.h new file mode 100644 index 000000000..755f5a184 --- /dev/null +++ b/src/plugins_experimental/filter_multiscale_align/utils.h @@ -0,0 +1,192 @@ +#ifndef UTILS_H +#define UTILS_H + +#include + +#include +//#include "localKdTree.h" + +//! Compile time pow +template +base_t POW(base_t base, expo_t expo) +{ + return (expo != 0 )? base * POW(base, expo -1) : 1; +} + + + +template +inline int +UnrollIndexLoop(const Point& coord, int cdim, int gsize){ + return (cdim != 0) + ? ( int(std::floor(coord[cdim]))*POW(gsize, cdim) + + UnrollIndexLoop(coord, cdim-1, gsize) ) + : int(std::floor(coord[cdim])); +} + +namespace Utils{ + +//// This creates the list of the scales, given the min, the max and the number of steps +template +inline +void sampleScaleInterval( + Scalar minScale, + Scalar /*coarseScale*/, + Scalar multiplier, + unsigned int nbScales, + Container& scales){ + scales.reserve(nbScales); + scales.push_back(minScale); + for(unsigned int i = 1; i != nbScales; i++) + scales.push_back(scales[i-1] * multiplier); +} + +///// Calculate the angle between two normal vectors +template +inline +Scalar angleNorm(vec aN, vec rN) +{ + aN=aN.Normalize(); + rN=rN.Normalize(); + return acos(aN.X()*rN.X()+aN.Y()*rN.Y()+aN.Z()*rN.Z())*180.0/M_PI; +} + + +////////////////////////////////////////////////////////// +// This function creates a KNNtree for each "selected" point, storing the n nearest neighbors +/////////////////////////////////////////////////////////////// + + +//template +//inline +//MultiscaleAlignNamespace::KdTree* +//makeKNNTree(MeshModel* model){ +// +// MultiscaleAlignNamespace::KdTree* tree = +// new MultiscaleAlignNamespace::KdTree (model->cm.vert.size()); +// +// int i=0; +// for (CMeshO::VertexIterator vi = model->cm.vert.begin(); +// vi != model->cm.vert.end(); +// vi++, ++i) { +// tree->set(i, vi->cP()); +// } +// +// tree->finalize(); +// +// return tree; +//} + + +namespace internal{ +// Compute the closest points between two 3D line segments and obtain the two +// invariants corresponding to the closet points. This is the "intersection" +// point that determines the invariants. Since the 4 points are not exactly +// planar, we use the center of the line segment connecting the two closest +// points as the "intersection". +template +inline +float _distSegmentToSegment(const Point& u, + const Point& v, + const Point& w, + float& invariant1, + float& invariant2) { + const float kSmallNumber = 0.0000001; + float a = u.dot(u); + float b = u.dot(v); + float c = v.dot(v); + float d = u.dot(w); + float e = v.dot(w); + float f = a * c - b * b; + // s1,s2 and t1,t2 are the parametric representation of the intersection. + // they will be the invariants at the end of this simple computation. + float s1 = 0.0; + float s2 = f; + float t1 = 0.0; + float t2 = f; + + if (f < kSmallNumber) { + s1 = 0.0; + s2 = 1.0; + t1 = e; + t2 = c; + } else { + s1 = (b * e - c * d); + t1 = (a * e - b * d); + if (s1 < 0.0) { + s1 = 0.0; + t1 = e; + t2 = c; + } else if (s1 > s2) { + s1 = s2; + t1 = e + b; + t2 = c; + } + } + + if (t1 < 0.0) { + t1 = 0.0; + if (-d < 0.0) + s1 = 0.0; + else if (-d > a) + s1 = s2; + else { + s1 = -d; + s2 = a; + } + } else if (t1 > t2) { + t1 = t2; + if ((-d + b) < 0.0) + s1 = 0; + else if ((-d + b) > a) + s1 = s2; + else { + s1 = (-d + b); + s2 = a; + } + } + invariant1 = (abs(s1) < kSmallNumber ? 0.0 : s1 / s2); + invariant2 = (abs(t1) < kSmallNumber ? 0.0 : t1 / t2); + return (w + (invariant1*u) - (invariant2*v)).norm(); +} +} // namespace internal + + +// Compute the closest points between two 3D line segments and obtain the two +// invariants corresponding to the closet points. This is the "intersection" +// point that determines the invariants. Since the 4 points are not exactly +// planar, we use the center of the line segment connecting the two closest +// points as the "intersection". +inline +float distSegmentToSegment(const vcg::Point3f& p1, const vcg::Point3f& p2, + const vcg::Point3f& q1, const vcg::Point3f& q2, + float& invariant1, float& invariant2) { + Eigen::Vector3f u, v, w; + + (p2 - p1).ToEigenVector(u); + (q2 - q1).ToEigenVector(v); + (p1 - q1).ToEigenVector(w); + + return internal::_distSegmentToSegment(u, v, w, invariant1, invariant2); +} + + +// Compute the closest points between two 3D line segments and obtain the two +// invariants corresponding to the closet points. This is the "intersection" +// point that determines the invariants. Since the 4 points are not exactly +// planar, we use the center of the line segment connecting the two closest +// points as the "intersection". +inline +float distSegmentToSegment(const Eigen::Vector3f& p1, const Eigen::Vector3f& p2, + const Eigen::Vector3f& q1, const Eigen::Vector3f& q2, + float& invariant1, float& invariant2) { + return internal::_distSegmentToSegment((p2 - p1).eval(), + (q2 - q1).eval(), + (p1 - q1).eval(), + invariant1, + invariant2); +} + +} // namespace Utils + +#endif // UTILS_H