mirror of
https://github.com/lucaspalomodevelop/meshlab.git
synced 2026-03-14 00:24:38 +00:00
Added experimental multiscale alignment filter
Revides evrsion of scale estimation of TVCG paper. Still under re-implementation
This commit is contained in:
parent
f948e80603
commit
3509880f3f
@ -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 <vcg/complex/algorithms/point_sampling.h>
|
||||
#include <vcg/complex/algorithms/create/resampler.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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<CMeshO>::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 <<MS_ALIGN;
|
||||
|
||||
foreach(FilterIDType tt , types())
|
||||
actionList << new QAction(filterName(tt), this);
|
||||
}
|
||||
|
||||
QString FilterMultiscaleAlign::filterName(FilterIDType filterId) const
|
||||
{
|
||||
switch(filterId) {
|
||||
case MS_ALIGN : return QString("Multiscale Align");
|
||||
default : assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Info() must return the longer string describing each filtering action
|
||||
// (this string is used in the About plugin dialog)
|
||||
QString FilterMultiscaleAlign::filterInfo(FilterIDType filterId) const
|
||||
{
|
||||
switch(filterId) {
|
||||
case MS_ALIGN : return QString("Create a Multiscale Align");
|
||||
default : assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
// This function define the needed parameters for each filter. Return true if the filter has some parameters
|
||||
// it is called every time, so you can set the default value of parameters according to the mesh
|
||||
// For each parmeter you need to define,
|
||||
// - the name of the parameter,
|
||||
// - the string shown in the dialog
|
||||
// - the default value
|
||||
// - a possibly long string describing the meaning of that parameter (shown as a popup help in the dialog)
|
||||
void FilterMultiscaleAlign::initParameterSet(QAction *action,MeshDocument & md, RichParameterSet & parlst)
|
||||
{
|
||||
MeshModel *target;
|
||||
switch(ID(action)) {
|
||||
|
||||
case MS_ALIGN :
|
||||
target= md.mm();
|
||||
foreach (target, md.meshList)
|
||||
if (target != md.mm()) break;
|
||||
|
||||
parlst.addParam(new RichMesh ("RefMesh", md.mm(),&md, "Reference Mesh",
|
||||
"The mesh with correct scale values and high level of details"));
|
||||
parlst.addParam(new RichMesh ("SecondMesh", target,&md, "Second Mesh",
|
||||
"The mesh for which a scale factor is searched and then used for align in the reference frame."));
|
||||
parlst.addParam(new RichEnum("Seed extraction method", 0, QStringList()<<"Descriptive"<<"Random", tr("Seed extraction method"),
|
||||
QString("The seed extraction method")));
|
||||
parlst.addParam(new RichBool("Check Borders",true,"Check for border points","Check for border points"));
|
||||
parlst.addParam(new RichBool("Use Quadriplets",true,"Use quadriplets","Use quadriplets"));
|
||||
parlst.addParam(new RichFloat("Error Threshold",4.0,"Average alignment error for seleted seeds","Average alignment error for seleted seeds"));
|
||||
parlst.addParam(new RichFloat("Expected scale",2.0,"Expected scale","Expected scale (to fasten the search)"));
|
||||
|
||||
parlst.addParam(new RichInt ("Seed Number", 2000, "Seed Number", "Seed Number"));
|
||||
|
||||
break;
|
||||
default : return;
|
||||
}
|
||||
}
|
||||
|
||||
// The Real Core Function doing the actual mesh processing.
|
||||
bool FilterMultiscaleAlign::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos * /*cb*/)
|
||||
{
|
||||
switch(ID(filter))
|
||||
{
|
||||
case MS_ALIGN:
|
||||
{
|
||||
|
||||
MeshModel *refMesh = par.getMesh("RefMesh");
|
||||
MeshModel *toAlignMesh = par.getMesh("SecondMesh");
|
||||
|
||||
Options opt;
|
||||
|
||||
bool useDescriptive;
|
||||
|
||||
switch(par.getEnum("Seed extraction method"))
|
||||
{
|
||||
case 0: opt.useDescriptive=true; break;
|
||||
case 1: opt.useDescriptive=false; break;
|
||||
}
|
||||
|
||||
opt.checkBorders=par.getBool("Check Borders");
|
||||
opt.alignError=par.getFloat("Error Threshold");
|
||||
opt.useQuadriplets=par.getBool("Use Quadriplets");
|
||||
opt.expectedScale=par.getFloat("Expected scale");
|
||||
|
||||
opt.seedNumber= par.getInt("Seed Number");
|
||||
|
||||
tri::RequirePerVertexNormal(refMesh->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<CMeshO,BaseSampler>::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<CMeshO,BaseSampler>::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<CMeshO,BaseSampler>::
|
||||
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<CMeshO,BaseSampler>::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<CMeshO,BaseSampler>::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<CMeshO,BaseSampler>::PoissonDiskParam pp;
|
||||
//tri::SurfaceSampling<CMeshO,BaseSampler>::PoissonDiskParam::Stat pds; pp.pds=&pds;
|
||||
pp.bestSampleChoiceFlag=true;
|
||||
pp.bestSamplePoolSize =10;
|
||||
|
||||
tri::SurfaceSampling<CMeshO,BaseSampler>::PoissonDiskPruning(mps, *presampledMesh, radius,pp);
|
||||
//tri::SurfaceSampling<CMeshO,BaseSampler>::PoissonDisk(curMM->cm, mps, *presampledMesh, radius,pp);
|
||||
vcg::tri::UpdateBounding<CMeshO>::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<CMeshO>::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)
|
||||
@ -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 <common/interfaces.h>
|
||||
#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
|
||||
@ -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
|
||||
@ -0,0 +1,433 @@
|
||||
#include "generic_align.h"
|
||||
|
||||
#include <vcg/space/point_matching.h>
|
||||
|
||||
#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<float>(firstToAl.N(), secondToAl.N());
|
||||
const float angleRef=Utils::angleNorm<float>(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 <std::pair<int, int> >& corrs)
|
||||
{
|
||||
bool ok=true;
|
||||
for (unsigned int i=0; i<corrs.size(); i++)
|
||||
{
|
||||
float dist=Distance(toAlign->cm.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<qual)
|
||||
{
|
||||
ok=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
////// 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
|
||||
GenericAlign::isReferenceOk(int ref,
|
||||
MeshModel* reference,
|
||||
int al,
|
||||
MeshModel* toAlign,
|
||||
const std::vector <std::pair<int, int> > &corrs,
|
||||
float scale)
|
||||
{
|
||||
|
||||
bool ok=true;
|
||||
for (unsigned int i=0; i<corrs.size(); i++)
|
||||
{
|
||||
float distToAl=Distance(toAlign->cm.vert[al].P(), toAlign->cm.vert[corrs[i].first].P());
|
||||
float angleToAl=Utils::angleNorm<float>(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<float>(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<DescrPoint>& 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; i<descrList.size(); i++)
|
||||
{
|
||||
CVertexO point=toAlign.model->cm.vert[descrList[i].ind];
|
||||
if(descrList[i].descrip*point.Q()>bestD)
|
||||
{
|
||||
bool ok=true;
|
||||
for(int j=0; j<seeds.size(); j++)
|
||||
{
|
||||
if(Distance(point.P(),toAlign.model->cm.vert[seeds[j]].P())<bboxDist)
|
||||
{
|
||||
ok=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(ok)
|
||||
{
|
||||
bestD=descrList[i].descrip*point.Q();
|
||||
cand=descrList[i].ind;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(cand==-1)
|
||||
{
|
||||
bboxDist*=bboxDistReducFactor;
|
||||
qMax*=bboxDistReducFactor;
|
||||
}
|
||||
else
|
||||
{
|
||||
seeds.push_back(cand);
|
||||
//std::cout << "bestD " << bestD << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return seeds;
|
||||
|
||||
}
|
||||
|
||||
/////////// This chooses a set of points for which matches could be found. Very naive for now...
|
||||
std::vector< int >
|
||||
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(dist<halfDist)
|
||||
{
|
||||
ok=false;
|
||||
//break;
|
||||
}
|
||||
}
|
||||
if(ok)
|
||||
{
|
||||
//std::cout << "Chosen cand: " << i << std::endl;
|
||||
cand=i;
|
||||
//std::cout << " NewMaxQ: " << toAlign->cm.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<std::pair<int, int> >corrs,
|
||||
const std::vector<Cand> &thirdPoints,
|
||||
float scale,
|
||||
float error)
|
||||
{
|
||||
float bestError=10000000;
|
||||
Matrix44f res;
|
||||
|
||||
#pragma omp parallel for
|
||||
for(int r=0; r<thirdPoints.size(); r++)
|
||||
{
|
||||
res.SetIdentity(); Matrix33f rot; rot.SetIdentity();
|
||||
|
||||
std::vector<Point3f> ref; std::vector<Point3f> refN;
|
||||
std::vector<Point3f> toAl; std::vector<Point3f> 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<float>(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 (totError<bestError && isScaleOk(scale,getScaleFromTransf(res),toAlign.multiplier))
|
||||
{
|
||||
std::cout << "Error after alignment " << totError/3.0f << std::endl;
|
||||
bestError=totError/3.0f;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(bestError<error)
|
||||
{
|
||||
toAlign.model->cm.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<std::pair<int, int> >corrs,
|
||||
const std::vector<Cand> &fourthPoints,
|
||||
float scale,
|
||||
float error)
|
||||
{
|
||||
float bestError=10000000;
|
||||
Matrix44f res;
|
||||
|
||||
#pragma omp parallel for
|
||||
for(int r=0; r<fourthPoints.size(); r++)
|
||||
{
|
||||
res.SetIdentity(); Matrix33f rot; rot.SetIdentity();
|
||||
|
||||
std::vector<Point3f> ref; std::vector<Point3f> refN;
|
||||
std::vector<Point3f> toAl; std::vector<Point3f> 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<float>(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 (totError<bestError && isScaleOk(scale,getScaleFromTransf(res),toAlign.multiplier))
|
||||
{
|
||||
std::cout << "Error after alignment " << totError/4.0f << std::endl;
|
||||
bestError=totError/4.0f;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(bestError<error)
|
||||
{
|
||||
toAlign.model->cm.Tr=res;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
274
src/plugins_experimental/filter_multiscale_align/generic_align.h
Normal file
274
src/plugins_experimental/filter_multiscale_align/generic_align.h
Normal file
@ -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 <common/meshmodel.h>
|
||||
|
||||
#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 <std::pair<int, int> >& corrs);
|
||||
|
||||
|
||||
bool isReferenceOk(int ref,
|
||||
MeshModel* reference,
|
||||
int al,
|
||||
MeshModel* toAlign,
|
||||
const std::vector <std::pair<int, int> > &corrs,
|
||||
float scale);
|
||||
|
||||
std::vector< int >
|
||||
selectSeeds(MeshModel* toAlign,
|
||||
MeshModel* /*reference*/ ,
|
||||
int start=0);
|
||||
|
||||
std::vector< int >
|
||||
selectSeedsDescr(DescriptorBase toAlign,
|
||||
const std::vector<DescrPoint> &descrList,
|
||||
bool shuffle);
|
||||
|
||||
bool checkTriplets(const DescriptorBase& toAlign,
|
||||
const DescriptorBase& reference,
|
||||
std::vector<std::pair<int, int> > corrs,
|
||||
const std::vector<Cand>& thirdPoints,
|
||||
float scale,
|
||||
float error);
|
||||
bool checkQuadriplets(const DescriptorBase& toAlign,
|
||||
const DescriptorBase& reference,
|
||||
std::vector<std::pair<int, int> > corrs,
|
||||
const std::vector<Cand>& thirdPoints,
|
||||
float scale,
|
||||
float error);
|
||||
|
||||
// Create space to store GLS descriptors and computes it
|
||||
inline
|
||||
void preComputeDescriptor(DescriptorBase& mesh,
|
||||
const std::vector<float> &scales,
|
||||
bool ompParallel)
|
||||
{ _preAllocateDescriptor<true>(mesh, scales, ompParallel); }
|
||||
|
||||
// Create space to store GLS descriptors without computing it
|
||||
inline
|
||||
void preAllocateDescriptor(DescriptorBase& mesh,
|
||||
const std::vector<float> &scales,
|
||||
bool ompParallel)
|
||||
{ _preAllocateDescriptor<false>(mesh, scales, ompParallel); }
|
||||
|
||||
|
||||
template <class Fit>
|
||||
inline void computeDescriptor( const MyPoint &query,
|
||||
const std::vector<float>& scales,
|
||||
const DescriptorBase& base,
|
||||
std::vector<Fit>* result, bool overwrite = true);
|
||||
|
||||
float ComputeLCP( const vcg::Matrix44f& transformation,
|
||||
float epsilon,
|
||||
const DescriptorBase& P,
|
||||
const DescriptorBase& Q,
|
||||
float prevLCP);
|
||||
|
||||
private:
|
||||
template <bool compute>
|
||||
inline void
|
||||
_preAllocateDescriptor(DescriptorBase& mesh,
|
||||
const std::vector<float>& scales,
|
||||
bool ompParallel);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// Compute the GLS descriptor for at the position query and for a given set of scales
|
||||
template <class Fit>
|
||||
void
|
||||
GenericAlign::computeDescriptor( const MyPoint &query,
|
||||
const std::vector<float>& scales,
|
||||
const DescriptorBase& base,
|
||||
std::vector<Fit>* 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<unsigned int> n;
|
||||
std::vector<Scalar> 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 <bool compute>
|
||||
void
|
||||
GenericAlign::_preAllocateDescriptor(DescriptorBase& mesh,
|
||||
const std::vector<float>& scales,
|
||||
bool ompParallel){
|
||||
typedef typename std::vector<DerivableSphereFit> Container;
|
||||
using vcg::tri::Allocator;
|
||||
|
||||
CMeshO::PerVertexAttributeHandle<Container* > descriptorsHandler;
|
||||
descriptorsHandler =
|
||||
Allocator<CMeshO>::GetPerVertexAttribute<Container* >
|
||||
(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<DerivableSphereFit>();
|
||||
|
||||
if(compute)
|
||||
computeDescriptor(mesh.model->cm.vert[ind],
|
||||
scales,
|
||||
mesh,
|
||||
descriptorsHandler[ind]);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // GENERIC_ALIGN_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 <typename Scalar>
|
||||
class DynamicStep {
|
||||
public:
|
||||
Scalar value;
|
||||
int relationFlags;
|
||||
|
||||
inline DynamicStep(Scalar v = 0,
|
||||
int flags = DynamicRelation::Nothing)
|
||||
:value(v), relationFlags(flags) {}
|
||||
}; //struct DynamicStep
|
||||
|
||||
template <typename Scalar>
|
||||
inline bool operator< (const DynamicStep<Scalar>& s1,
|
||||
const DynamicStep<Scalar>& s2){
|
||||
return s1.value < s2.value;
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
inline bool operator> (const DynamicStep<Scalar>& s1,
|
||||
const DynamicStep<Scalar>& s2){
|
||||
return s1.value > s2.value;
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
inline bool operator<= (const DynamicStep<Scalar>& s1,
|
||||
const DynamicStep<Scalar>& s2){
|
||||
return s1.value <= s2.value;
|
||||
}
|
||||
|
||||
|
||||
template <typename Scalar>
|
||||
static inline
|
||||
std::ostream & operator<< (std::ostream &o,
|
||||
const DynamicStep<Scalar> &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 <typename Scalar>
|
||||
struct DynamicStepCoordinates {
|
||||
unsigned int x, y;
|
||||
Scalar value;
|
||||
}; //struct DynamicStep
|
||||
template <typename Scalar>
|
||||
static inline
|
||||
std::ostream & operator<< (std::ostream &o,
|
||||
const DynamicStepCoordinates<Scalar> &dstep){
|
||||
o << "step( " << dstep.x;
|
||||
o << " , " << dstep.y;
|
||||
o << " ) = " << dstep.value;
|
||||
o << "";
|
||||
return o;
|
||||
}
|
||||
|
||||
} // namespace DynamicProg
|
||||
|
||||
#endif // _DYNAMIC_SOLVER_
|
||||
|
||||
@ -0,0 +1,309 @@
|
||||
#ifndef _DYNAMIC_SOLVER_
|
||||
#define _DYNAMIC_SOLVER_
|
||||
|
||||
#include "Eigen/Core"
|
||||
#include <vector>
|
||||
|
||||
#include "basics.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <float.h> // for _isnan() on VC++
|
||||
#define isnan(x) _isnan(x) // VC++ uses _isnan() instead of isnan()
|
||||
//#else
|
||||
//#include <math.h> // for isnan() everywhere else
|
||||
#endif
|
||||
|
||||
namespace DynamicProg{
|
||||
|
||||
/*!
|
||||
Generic class that delegates the computation to DynProcess. You can use for
|
||||
instance NeedlemanWunsch.
|
||||
*/
|
||||
template <typename Scalar, class DynProcess>
|
||||
class DynamicSolver{
|
||||
public:
|
||||
typedef Eigen::Matrix<DynamicStep<Scalar>, Eigen::Dynamic, Eigen::Dynamic> StepMatrix;
|
||||
|
||||
int xMin,xMax,yMin,yMax;
|
||||
//std::vector<std::pair<float, float>> estScales;
|
||||
//int xBest,yBest;
|
||||
std::vector<std::pair<float, float>> 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<Scalar> > _path;
|
||||
|
||||
public:
|
||||
template <class DataArray>
|
||||
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<float, float> 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<float, float> 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; j<yMax; j++){
|
||||
// for (int i=xMin; i<xMax; i++)
|
||||
// {
|
||||
|
||||
// if(normalize){
|
||||
// int diag = std::max(i,j);
|
||||
// int offset = std::max(std::abs(diag-i), std::abs(diag-j));
|
||||
// float norm = diag-offset;
|
||||
// norm *= norm;
|
||||
// _matrix(i,j).value /= norm;
|
||||
// }
|
||||
|
||||
// if(_matrix(i,j).value>best)
|
||||
// {
|
||||
// best=_matrix(i,j).value;
|
||||
// xBest=i;
|
||||
// yBest=j;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
//vector<float> maxVal;
|
||||
//vector<float> 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; j<yMax; j++)
|
||||
// for (int i=xMin; i<xMax; i++)
|
||||
// {
|
||||
// if(_matrix(i,j).value>best)
|
||||
// {
|
||||
// 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; i<d2.size(); i++)
|
||||
{
|
||||
if(abs(d2[i].kappa())>dMax)
|
||||
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 <class DataArray>
|
||||
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<double> compare;
|
||||
|
||||
//std::cout << "Calc diff" << std::endl;
|
||||
|
||||
for (unsigned int j = yMin; j != yMax; j++)
|
||||
{
|
||||
if ((j+shift)>xMin && (j+shift)<xMax)
|
||||
{
|
||||
float val;
|
||||
if (compare.size()==0)
|
||||
val=(_delegate.eval_couples(d1[j+shift], d2[j], j+shift, j, _matrix, multiplier));
|
||||
else
|
||||
val=compare.back() + (_delegate.eval_couples(d1[j+shift], d2[j], j+shift, j, _matrix, multiplier));
|
||||
//std::cout << "Val " << val << " d1 " << j+shift << " d2 " << j << endl;
|
||||
compare.push_back(val);
|
||||
}
|
||||
}
|
||||
confidence = compare.back();
|
||||
//std::cout << "End calc diff" << std::endl;
|
||||
//////
|
||||
// This is a final normalization of the value, I'm not sure it will be useful... ask me to explain, in the case
|
||||
/////
|
||||
/*best=0.0;
|
||||
size=std::min(Scalar(yBest-yMin)+1,Scalar(xBest-xMin)+1);
|
||||
for (int j=0; j<compare.size(); j++)
|
||||
{
|
||||
if(compare[j]>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; i<d2.size(); i++)
|
||||
{
|
||||
if(abs(d2[i].kappa())>dMax)
|
||||
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_
|
||||
|
||||
@ -0,0 +1,111 @@
|
||||
#ifndef _DYNAMIC_NEEDLEMANWUNSCH_
|
||||
#define _DYNAMIC_NEEDLEMANWUNSCH_
|
||||
|
||||
#include "Eigen/Core"
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#include "basics.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <float.h> // for _isnan() on VC++
|
||||
#define isnan(x) _isnan(x) // VC++ uses _isnan() instead of isnan()
|
||||
//#else
|
||||
//#include <math.h> // for isnan() everywhere else
|
||||
#endif
|
||||
|
||||
namespace DynamicProg{
|
||||
/*!
|
||||
Global alignment
|
||||
|
||||
cmp: must define an eval function
|
||||
*/
|
||||
template <typename Scalar, class DataType, template <typename, class> class Cmp >
|
||||
class NeedlemanWunsch{
|
||||
private:
|
||||
Cmp<Scalar, DataType> _cmp;
|
||||
Scalar _gapPenalty;
|
||||
Scalar _confidence;
|
||||
|
||||
public:
|
||||
typedef typename Eigen::Matrix<DynamicStep<Scalar>, Eigen::Dynamic, Eigen::Dynamic> StepMatrix;
|
||||
|
||||
public:
|
||||
inline NeedlemanWunsch(): _gapPenalty(-1), _confidence(0) {}
|
||||
|
||||
inline DynamicStep<Scalar> 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 <typename Scalar, class DataType, template <typename, class> class Cmp >
|
||||
DynamicStep<Scalar>
|
||||
NeedlemanWunsch<Scalar, DataType, Cmp >::eval( const DataType& v1,
|
||||
const DataType& v2,
|
||||
unsigned int x,
|
||||
unsigned int y,
|
||||
const StepMatrix& matrix, double multiplier) const{
|
||||
|
||||
//std::cout << "here" << std::endl;
|
||||
DynamicStep<Scalar>nei[3]; // will contain top, left and topleft
|
||||
|
||||
|
||||
nei[2] = DynamicStep<Scalar> (matrix(x-1, y-1).value + _cmp.eval(v1, v2),
|
||||
DynamicRelation::TopLeft);
|
||||
|
||||
|
||||
return nei[2];
|
||||
|
||||
|
||||
}
|
||||
|
||||
template <typename Scalar, class DataType, template <typename, class> class Cmp >
|
||||
Scalar
|
||||
NeedlemanWunsch<Scalar, DataType, Cmp >::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 <typename Scalar, class DataType, template <typename, class> class Cmp >
|
||||
void
|
||||
NeedlemanWunsch<Scalar, DataType, Cmp >::setConfidence(double conf)
|
||||
{
|
||||
|
||||
_confidence = conf;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace DynamicProg
|
||||
|
||||
|
||||
#endif // _DYNAMIC_PATH
|
||||
|
||||
@ -0,0 +1,117 @@
|
||||
#ifndef _SCALE_ESTIMATION_
|
||||
#define _SCALE_ESTIMATION_
|
||||
|
||||
#include "Eigen/Core"
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include "basics.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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 <typename Scalar>
|
||||
class VotingStretchEstimation{
|
||||
public:
|
||||
typedef typename Eigen::Matrix<DynamicStep<Scalar>, Eigen::Dynamic, Eigen::Dynamic> StepMatrix;
|
||||
typedef typename std::vector< DynamicStepCoordinates<Scalar> > Path;
|
||||
|
||||
public:
|
||||
/*!
|
||||
*/
|
||||
inline Scalar estimate(int xBest, int yBest, double multiplier)const {
|
||||
|
||||
//if (xBest<yBest)
|
||||
return 1.0/pow(multiplier, yBest-xBest);
|
||||
//else
|
||||
// return pow(multiplier, xBest-yBest);
|
||||
|
||||
}
|
||||
|
||||
}; //class DynamicSolver
|
||||
|
||||
/*!
|
||||
Simulate a convolution by getting the best score from the bottom and right borders
|
||||
of the StepMatrix
|
||||
*/
|
||||
template <typename Scalar>
|
||||
class ConvolutionStretchEstimation{
|
||||
public:
|
||||
typedef typename Eigen::Matrix<DynamicStep<Scalar>, Eigen::Dynamic, Eigen::Dynamic> StepMatrix;
|
||||
typedef typename std::vector< DynamicStepCoordinates<Scalar> > 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 <class stream>
|
||||
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_
|
||||
|
||||
@ -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})
|
||||
@ -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<DataPoint, _WFunctor>
|
||||
{
|
||||
private:
|
||||
|
||||
typedef PrimitiveBase<DataPoint, _WFunctor> 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<Scalar>::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<Scalar>::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_
|
||||
@ -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<DataPoint, _WFunctor, T>::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<DataPoint, _WFunctor, T>::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<DataPoint, _WFunctor, T>::primitiveGradient( const VectorType &_q ) const
|
||||
{
|
||||
// turn to centered basis
|
||||
const VectorType lq = _q-m_p;
|
||||
return (m_ul + Scalar(2.f) * m_uq * lq);
|
||||
}
|
||||
|
||||
@ -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, class, typename T> class Forward: public T {};
|
||||
}
|
||||
|
||||
|
||||
#define BASKET_TP(I) template <class, class, typename> 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 <PointImpl, // Implementation of PointConcept
|
||||
WeightFuncImpl, // Implementation of WeightFuncConcept (could use WeightKernelConcept)
|
||||
FittingProcedureImpl, // Implementation of FittingProcedureConcept
|
||||
FittingExtensionImpl1, //
|
||||
FittingExtensionImpl2, // Implementations of FittingExtensionConcept
|
||||
... , //
|
||||
FittingExtensionImpln //
|
||||
> myFit; // Final structure to fit and extend a primitive over weighted samples
|
||||
\endcode
|
||||
|
||||
*/
|
||||
template < class P, class W, template <class, class, typename>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<P,W, Ext10<P,W, Ext9<P,W, Ext8<P,W, Ext7<P,W, Ext6<P,W, Ext5<P,W, Ext4<P,W, Ext3<P,W, Ext2<P,W, Ext1<P,W, Ext0<P,W, Fit<P,W,void> > > > > > > > > > > > >
|
||||
{
|
||||
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 <typename Iterator>
|
||||
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
|
||||
@ -0,0 +1,285 @@
|
||||
/*
|
||||
Copyright (C) 2014 Nicolas Mellado <nmellado0@gmail.com>
|
||||
Copyright (C) 2015 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
|
||||
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 <Eigen/Eigenvalues>
|
||||
#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<MatrixType> 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<DataPoint::Dim>(); }
|
||||
|
||||
/*! \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<Scalar, DataPoint::Dim, NbDerivatives, DerStorageOrder> VectorArray;
|
||||
|
||||
/*! \brief Static array of scalars with a size adapted to the differentiation type */
|
||||
typedef Eigen::Matrix<Scalar, 1, NbDerivatives> 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<DataPoint, _WFunctor, T, internal::FitScaleDer>
|
||||
{
|
||||
protected:
|
||||
/*! \brief Inherited class */
|
||||
typedef internal::CovariancePlaneDer<DataPoint, _WFunctor, T, internal::FitScaleDer> 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<DataPoint, _WFunctor, T, internal::FitSpaceDer>
|
||||
{
|
||||
protected:
|
||||
/*! \brief Inherited class */
|
||||
typedef internal::CovariancePlaneDer<DataPoint, _WFunctor, T, internal::FitSpaceDer> 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<DataPoint, _WFunctor, T, internal::FitSpaceDer | internal::FitScaleDer>
|
||||
{
|
||||
protected:
|
||||
/*! \brief Inherited class */
|
||||
typedef internal::CovariancePlaneDer<DataPoint, _WFunctor, T, internal::FitSpaceDer | internal::FitScaleDer> 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
|
||||
@ -0,0 +1,201 @@
|
||||
/*
|
||||
Copyright (C) 2014 Nicolas Mellado <nmellado0@gmail.com>
|
||||
Copyright (C) 2015 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
|
||||
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<DataPoint, _WFunctor, T>::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<DataPoint, _WFunctor, T>::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<DataPoint, _WFunctor, T>::finalize ()
|
||||
{
|
||||
Scalar epsilon = Eigen::NumTraits<Scalar>::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<DataPoint, _WFunctor, T>::Scalar
|
||||
CovariancePlaneFit<DataPoint, _WFunctor, T>::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<DataPoint, _WFunctor, T, Type>::init(const VectorType& _evalPos)
|
||||
{
|
||||
Base::init(_evalPos);
|
||||
|
||||
m_dCog = VectorArray::Zero();
|
||||
m_dSumW = ScalarArray::Zero();
|
||||
for(int k=0; k<NbDerivatives; ++k)
|
||||
m_dCov[k].setZero();
|
||||
}
|
||||
|
||||
|
||||
template < class DataPoint, class _WFunctor, typename T, int Type>
|
||||
bool
|
||||
CovariancePlaneDer<DataPoint, _WFunctor, T, Type>::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<int(DataPoint::Dim)>(spaceId) = -Base::m_w.spacedw(q, _nei).transpose();
|
||||
|
||||
m_dSumW += dw;
|
||||
m_dCog += q * dw;
|
||||
for(int k=0; k<NbDerivatives; ++k)
|
||||
m_dCov[k] += dw[k] * q * q.transpose();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template < class DataPoint, class _WFunctor, typename T, int Type>
|
||||
FIT_RESULT
|
||||
CovariancePlaneDer<DataPoint, _WFunctor, T, Type>::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<Scalar>::epsilon();
|
||||
Scalar consider_as_zero = Scalar(2) * std::numeric_limits<Scalar>::denorm_min();
|
||||
Eigen::Matrix<Scalar,2,1> 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<NbDerivatives; ++k)
|
||||
{
|
||||
// Finalize the computation of dCov.
|
||||
// Note that at this stage m_dCog = sum_i dw_i * (p_i-c).
|
||||
// Since the covariance matrix is translation invariant,
|
||||
// this step natuarally cancels the centered basis.
|
||||
VectorType shifted_cog = Base::m_cog - Base::m_evalPos;
|
||||
m_dCov[k] = m_dCov[k]
|
||||
- shifted_cog * m_dCog.col(k).transpose()
|
||||
- m_dCog.col(k) * shifted_cog.transpose()
|
||||
+ m_dSumW[k] * shifted_cog * shifted_cog.transpose();
|
||||
|
||||
// cancel centered basis of dCog:
|
||||
m_dCog.col(k) += m_dSumW[k] * Base::m_evalPos;
|
||||
|
||||
// apply normalization by sumW:
|
||||
m_dCog.col(k) = (m_dCog.col(k) - m_dSumW(k) * Base::m_cog) / Base::m_sumW;
|
||||
|
||||
VectorType normal = Base::normal();
|
||||
// The derivative of 'normal' is the derivative of the smallest eigenvector.
|
||||
// Since the covariance matrix is real and symmetric, it is equal to:
|
||||
// n' = - (C - lambda_0 I)^+ C' n
|
||||
// Where ^+ denotes the pseudo-inverse.
|
||||
// Since we already performed the eigenvalue decomposition of the matrix C,
|
||||
// we can directly apply the pseudo inverse by observing that:
|
||||
// (C - lambda_0 I) = V (L - lambda_0 I) V^T
|
||||
// where V is the eigenvector matrix, and L the eigenvalue diagonal matrix.
|
||||
Eigen::Matrix<Scalar,2,1> 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
|
||||
@ -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 <Eigen/Eigenvalues>
|
||||
|
||||
#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
|
||||
@ -0,0 +1,87 @@
|
||||
/*
|
||||
Copyright (C) 2015 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
|
||||
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<DataPoint, _WFunctor, T>::finalize()
|
||||
{
|
||||
typedef typename VectorType::Index Index;
|
||||
typedef Eigen::Matrix<Scalar,3,2> Mat32;
|
||||
typedef Eigen::Matrix<Scalar,2,2> 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<DataPoint::Dim>(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<Mat22> 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)<abs(m_k2))
|
||||
{
|
||||
#ifdef __CUDACC__
|
||||
Scalar tmpk = m_k1;
|
||||
m_k1 = m_k2;
|
||||
m_k2 = tmpk;
|
||||
VectorType tmpv = m_v1;
|
||||
m_v1 = m_v2;
|
||||
m_v2 = tmpv;
|
||||
#else
|
||||
std::swap(m_k1, m_k2);
|
||||
std::swap(m_v1, m_v2);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
/*
|
||||
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_ENUMS_
|
||||
#define _GRENAILLE_ENUMS_
|
||||
|
||||
namespace Grenaille
|
||||
{
|
||||
/*! Enum corresponding to the state of a fitting method (and what the finalize function can return) */
|
||||
enum FIT_RESULT
|
||||
{
|
||||
/*! \brief The fitting is stable an ready to use (and having more than 6
|
||||
neighbours)*/
|
||||
STABLE = 0,
|
||||
/*! \brief The fitting is ready to use but it can be unstable (and
|
||||
having between 3 and 6 neighbors)*/
|
||||
UNSTABLE = 1,
|
||||
/*! \brief The fitting is undefined, you can't use it for valid results
|
||||
(and having less than 3 neighbors)*/
|
||||
UNDEFINED = 2,
|
||||
/*! \brief The fitting procedure needs to analyse the neighborhood
|
||||
another time*/
|
||||
NEED_OTHER_PASS = 3,
|
||||
NBMAX /*!< \brief Nb enums */
|
||||
};
|
||||
|
||||
namespace internal
|
||||
{
|
||||
enum
|
||||
{
|
||||
FitScaleDer = 0x01, /*!< \brief Flag indicating a scale differentiation. */
|
||||
FitSpaceDer = 0x02 /*!< \brief Flag indicating a space differentiation. */
|
||||
};
|
||||
} // end namespace internal
|
||||
|
||||
} //namespace Grenaille
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,250 @@
|
||||
/*
|
||||
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 <Eigen/Eigenvalues>
|
||||
|
||||
#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<DataPoint, _WFunctor, T>& _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<DataPoint,_WFunctor,T>
|
||||
{
|
||||
typedef CurvatureEstimator<DataPoint,_WFunctor,T> 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
|
||||
@ -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 <DataPoint, _WFunctor, T>::ScalarArray
|
||||
GLSDer <DataPoint, _WFunctor, T>::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<DataPoint::Dim>() += Base::m_ul;
|
||||
|
||||
return (dfield * prattNorm - Base::m_uc * cfactor * Base::dprattNorm2()) / prattNorm2;
|
||||
}
|
||||
|
||||
|
||||
template < class DataPoint, class _WFunctor, typename T>
|
||||
typename GLSDer <DataPoint, _WFunctor, T>::VectorArray
|
||||
GLSDer <DataPoint, _WFunctor, T>::deta() const
|
||||
{
|
||||
return Base::dNormal();
|
||||
}
|
||||
|
||||
|
||||
template < class DataPoint, class _WFunctor, typename T>
|
||||
typename GLSDer <DataPoint, _WFunctor, T>::ScalarArray
|
||||
GLSDer <DataPoint, _WFunctor, T>::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 <DataPoint, _WFunctor, T>::ScalarArray
|
||||
GLSDer <DataPoint, _WFunctor, T>::dtau_normalized() const
|
||||
{
|
||||
return dtau();
|
||||
}
|
||||
|
||||
|
||||
template < class DataPoint, class _WFunctor, typename T>
|
||||
typename GLSDer <DataPoint, _WFunctor, T>::VectorArray
|
||||
GLSDer <DataPoint, _WFunctor, T>::deta_normalized() const
|
||||
{
|
||||
return Base::m_t * deta();
|
||||
}
|
||||
|
||||
|
||||
template < class DataPoint, class _WFunctor, typename T>
|
||||
typename GLSDer <DataPoint, _WFunctor, T>::ScalarArray
|
||||
GLSDer <DataPoint, _WFunctor, T>::dkappa_normalized() const
|
||||
{
|
||||
return dkappa() * Base::m_t * Base::m_t;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template < class DataPoint, class _WFunctor, typename T>
|
||||
typename GLSGeomVar <DataPoint, _WFunctor, T>::Scalar
|
||||
GLSGeomVar <DataPoint, _WFunctor, T>::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;
|
||||
}
|
||||
@ -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<DataPoint, _WFunctor>
|
||||
{
|
||||
private:
|
||||
|
||||
typedef AlgebraicSphere<DataPoint, _WFunctor> 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<DataPoint, _WFunctor, T, internal::FitScaleDer>
|
||||
{
|
||||
protected:
|
||||
/*! \brief Inherited class */
|
||||
typedef internal::OrientedSphereDer<DataPoint, _WFunctor, T, internal::FitScaleDer> 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<DataPoint, _WFunctor, T, internal::FitSpaceDer>
|
||||
{
|
||||
protected:
|
||||
/*! \brief Inherited class */
|
||||
typedef internal::OrientedSphereDer<DataPoint, _WFunctor, T, internal::FitSpaceDer> 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<DataPoint, _WFunctor, T, internal::FitSpaceDer | internal::FitScaleDer>
|
||||
{
|
||||
protected:
|
||||
/*! \brief Inherited class */
|
||||
typedef internal::OrientedSphereDer<DataPoint, _WFunctor, T, internal::FitSpaceDer | internal::FitScaleDer> 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
|
||||
@ -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<DataPoint, _WFunctor, T>::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<DataPoint, _WFunctor, T>::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<DataPoint, _WFunctor, T>::finalize ()
|
||||
{
|
||||
MULTIARCH_STD_MATH(sqrt);
|
||||
MULTIARCH_STD_MATH(max);
|
||||
MULTIARCH_STD_MATH(abs);
|
||||
|
||||
// 1. finalize sphere fitting
|
||||
Scalar epsilon = Eigen::NumTraits<Scalar>::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<DataPoint, _WFunctor, T, Type>::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<DataPoint, _WFunctor, T, Type>::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<int(DataPoint::Dim)>() = -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<DataPoint, _WFunctor, T, Type>::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 <DataPoint, _WFunctor, T, Type>::VectorArray
|
||||
OrientedSphereDer<DataPoint, _WFunctor, T, Type>::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<DataPoint::Dim>().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 <DataPoint, _WFunctor, T, Type>::ScalarArray
|
||||
OrientedSphereDer<DataPoint, _WFunctor, T, Type>::dPotential() const
|
||||
{
|
||||
ScalarArray dfield = m_dUc;
|
||||
if(this->isScaleDer())
|
||||
dfield.template tail<DataPoint::Dim>() += Base::m_ul;
|
||||
return dfield;
|
||||
}
|
||||
|
||||
template < class DataPoint, class _WFunctor, typename T, int Type>
|
||||
bool
|
||||
OrientedSphereDer<DataPoint, _WFunctor, T, Type>::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
|
||||
@ -0,0 +1,129 @@
|
||||
/*
|
||||
Copyright (C) 2014 Nicolas Mellado <nmellado0@gmail.com>
|
||||
|
||||
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<DataPoint, _WFunctor>
|
||||
{
|
||||
private:
|
||||
|
||||
typedef PrimitiveBase<DataPoint, _WFunctor> 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<DataPoint::Dim>() = _dir.normalized();
|
||||
m_p.template tail<1>()<< -_pos.dot(m_p.template head<DataPoint::Dim>());
|
||||
}
|
||||
|
||||
//! \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<DataPoint::Dim>().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<DataPoint::Dim>();
|
||||
}
|
||||
|
||||
//! \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<DataPoint::Dim>();
|
||||
}
|
||||
|
||||
|
||||
}; //class Plane
|
||||
|
||||
}
|
||||
#endif // _GRENAILLE_PLANE_
|
||||
@ -0,0 +1,72 @@
|
||||
/*
|
||||
Copyright (C) 2014 Nicolas Mellado <nmellado0@gmail.com>
|
||||
|
||||
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_
|
||||
@ -0,0 +1,288 @@
|
||||
/*
|
||||
Copyright (C) 2013 Gael Guennebaud <gael.guennebaud@inria.fr>
|
||||
|
||||
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 <Eigen/Dense>
|
||||
#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<DataPoint, _WFunctor>
|
||||
{
|
||||
private:
|
||||
typedef AlgebraicSphere<DataPoint, _WFunctor> 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<Scalar, Dim+1, 1> VectorB;
|
||||
typedef Eigen::Matrix<Scalar, Dim+1, Dim+1> 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 <Scalar, DataPoint::Dim, GLS_DER_NB_DERIVATIVES(Type,DataPoint::Dim)> 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)> 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<DataPoint, _WFunctor, T, internal::FitScaleDer>
|
||||
{
|
||||
protected:
|
||||
/*! \brief Inherited class */
|
||||
typedef internal::UnorientedSphereDer<DataPoint, _WFunctor, T, internal::FitScaleDer> 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<DataPoint, _WFunctor, T, internal::FitSpaceDer>
|
||||
{
|
||||
protected:
|
||||
/*! \brief Inherited class */
|
||||
typedef internal::UnorientedSphereDer<DataPoint, _WFunctor, T, internal::FitSpaceDer> 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<DataPoint, _WFunctor, T, internal::FitSpaceDer | internal::FitScaleDer>
|
||||
{
|
||||
protected:
|
||||
/*! \brief Inherited class */
|
||||
typedef internal::UnorientedSphereDer<DataPoint, _WFunctor, T, internal::FitSpaceDer | internal::FitScaleDer> Base;
|
||||
enum
|
||||
{
|
||||
PROVIDES_ALGEBRAIC_SPHERE_SCALE_DERIVATIVE,
|
||||
PROVIDES_ALGEBRAIC_SPHERE_SPACE_DERIVATIVE
|
||||
};
|
||||
};
|
||||
|
||||
#endif // end TOBEIMPLEMENTED
|
||||
|
||||
#include "unorientedSphereFit.hpp"
|
||||
|
||||
} //namespace Grenaille
|
||||
|
||||
|
||||
#endif
|
||||
@ -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 ; i<nofSamples ; ++i)
|
||||
{
|
||||
VectorDd p = pNeighborhood->getNeighbor(i).position().cast<double>();
|
||||
VectorDd n = pNeighborhood->getNeighbor(i).normal().cast<double>();
|
||||
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<MatrixBB> 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<Dim>().cast<Real>();
|
||||
uQuad() = 0.5*eivec(Dim);
|
||||
uConstant() = -(1./sumOfWeights)*(eivec.start<Dim>().dot(sumP) + 0.5*eivec(Dim) * sumDotPP);
|
||||
|
||||
#endif
|
||||
|
||||
template < class DataPoint, class _WFunctor, typename T>
|
||||
void
|
||||
UnorientedSphereFit<DataPoint, _WFunctor, T>::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<DataPoint, _WFunctor, T>::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<DataPoint, _WFunctor, T>::finalize ()
|
||||
{
|
||||
MULTIARCH_STD_MATH(sqrt);
|
||||
|
||||
// 1. finalize sphere fitting
|
||||
Scalar invSumW;
|
||||
Scalar epsilon = Eigen::NumTraits<Scalar>::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<Dim,Dim>().setIdentity();
|
||||
Q.col(Dim).template head<Dim>() = m_sumP * invSumW;
|
||||
Q.row(Dim).template head<Dim>() = m_sumP * invSumW;
|
||||
Q(Dim,Dim) = m_sumDotPP * invSumW;
|
||||
m_matA *= invSumW;
|
||||
|
||||
MatrixBB M = Q.inverse() * m_matA;
|
||||
Eigen::EigenSolver<MatrixBB> 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<Dim>();
|
||||
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<DataPoint, _WFunctor, T, Type>::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<DataPoint, _WFunctor, T, Type>::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<DataPoint, _WFunctor, T, Type>::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<DataPoint, _WFunctor, T, Type>::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
|
||||
@ -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 DataPoint, class WeightKernel>
|
||||
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_
|
||||
@ -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 <class DataPoint, class WeightKernel>
|
||||
typename DistWeightFunc<DataPoint, WeightKernel>::Scalar
|
||||
DistWeightFunc<DataPoint, WeightKernel>::w( const VectorType& _q,
|
||||
const DataPoint&) const
|
||||
{
|
||||
Scalar d = _q.norm();
|
||||
return (d <= m_t) ? m_wk.f(d/m_t) : Scalar(0.);
|
||||
}
|
||||
|
||||
template <class DataPoint, class WeightKernel>
|
||||
typename DistWeightFunc<DataPoint, WeightKernel>::VectorType
|
||||
DistWeightFunc<DataPoint, WeightKernel>::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 <class DataPoint, class WeightKernel>
|
||||
typename DistWeightFunc<DataPoint, WeightKernel>::Scalar
|
||||
DistWeightFunc<DataPoint, WeightKernel>::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.);
|
||||
}
|
||||
@ -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 <typename _Scalar>
|
||||
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 <typename _Scalar>
|
||||
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_
|
||||
@ -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},
|
||||
}
|
||||
|
||||
@ -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 <PointImpl, // Implementation of PointConcept
|
||||
|
||||
WeightFuncImpl, // Implementation of WeightFuncConcept (could use WeightKernelConcept)
|
||||
|
||||
FittingProcedureImpl, // Implementation of FittingProcedureConcept
|
||||
|
||||
FittingExtensionImpl1, //
|
||||
FittingExtensionImpl2, // Implementations of FittingExtensionConcept
|
||||
... , //
|
||||
FittingExtensionImpln //
|
||||
|
||||
> 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<MyPointStructure,MyWeightingFunction,MyFittingProcedure> 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<MyPointStructure,MyWeightingFunction,MyFittingProcedure, MyExtension1, MyExtension2, ...> 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 <typename _Scalar>
|
||||
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 Point>
|
||||
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<Scalar, Dim, 1> 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<Scalar, Dim, 1> 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
|
||||
@ -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.
|
||||
|
||||
*/
|
||||
@ -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
|
||||
*/
|
||||
@ -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
|
||||
*/
|
||||
@ -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
|
||||
*/
|
||||
@ -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 <strong>eigen dev branch</strong> 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
|
||||
*/
|
||||
@ -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
|
||||
*/
|
||||
@ -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 <a href="http://http://pointclouds.org/">Point Cloud Library (PCL)</a>.
|
||||
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
|
||||
*/
|
||||
@ -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 <strong>eigen dev branch</strong> 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
|
||||
*/
|
||||
@ -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 <Patate/grenaille.h>
|
||||
\endcode
|
||||
|
||||
\subsection grenaille_user_manual_cuda Cuda
|
||||
Grenaille can be used directly on GPU, thanks to several mechanisms:
|
||||
- Eigen Cuda capabilities, see <a href="http://eigen.tuxfamily.org/dox-devel/TopicCUDA.html" target="_blank">Eigen documentation</a> 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<Scalar, Dim, 1> VectorType;
|
||||
typedef Eigen::Matrix<Scalar, Dim, Dim> 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 <CODE>typedef</CODE> helpers for <CODE>Scalar</CODE> and
|
||||
<CODE>Vector</CODE> 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<MyPoint,SmoothWeightKernel<Scalar> > 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<MyPoint,WeightFunc,OrientedSphereFit, GLSParam> 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 <CODE>finalize</CODE> is called at the end:
|
||||
\code
|
||||
// Iterate over samples and fit the primitive
|
||||
for(vector<MyPoint>::iterator it = vecs.begin(); it != vecs.end(); it++)
|
||||
fit.addNeighbor(*it);
|
||||
|
||||
//finalize fitting
|
||||
fit.finalize();
|
||||
\endcode
|
||||
|
||||
After calling <CODE>finalize</CODE> or <CODE>compute</CODE>, it is better to test the return state of the fitting before using it. There are diffent states but the most important one is <CODE>STABLE</CODE>.
|
||||
|
||||
\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<MyPoint, WeightFunc, OrientedSphereFit, // Sphere fitting
|
||||
GLSParam> 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<MyPoint, WeightFunc, OrientedSphereFit, // Sphere fitting
|
||||
GLSParam, // GLS reparametrization
|
||||
OrientedSphereSpaceDer, // Spatial derivatives
|
||||
GLSDer > 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 <CODE>fit.deta()</CODE>, using an implicit cast from <CODE>VectorArray</CODE> to <CODE>MatrixType</CODE>. 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<MyPoint, WeightFunc, OrientedSphereFit, // Sphere fitting
|
||||
GLSParam, // GLS reparametrization
|
||||
OrientedSphereSpaceDer, // Spatial derivatives
|
||||
GLSDer, // GLS differentiation
|
||||
GLSCurvatureHelper > 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 <a href="namespace_grenaille.html"><b>reference</b></a>. 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.
|
||||
*/
|
||||
}
|
||||
@ -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})
|
||||
@ -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 <cassert>
|
||||
#include <vector>
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
|
||||
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<Vector> 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<Vector> getSegment(unsigned si) const {
|
||||
assert(si < nSegments());
|
||||
return BezierSegment<Vector>(type(si), &m_points[m_segments[si].firstPoint]);
|
||||
}
|
||||
|
||||
private:
|
||||
struct Segment {
|
||||
BezierSegmentType type;
|
||||
unsigned firstPoint;
|
||||
};
|
||||
|
||||
typedef std::vector<Vector> PointList;
|
||||
typedef std::vector<Segment> SegmentList;
|
||||
|
||||
private:
|
||||
PointList m_points;
|
||||
SegmentList m_segments;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -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 <string>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
#include <Eigen/Sparse>
|
||||
|
||||
|
||||
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<Scalar, Mesh::DimsAtCompileTime, 1> Vector;
|
||||
typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> Matrix;
|
||||
typedef Eigen::Triplet<Scalar> 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
|
||||
@ -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 <cassert>
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Sparse>
|
||||
|
||||
#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<Mesh, ElementBuilder> 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<Scalar, Eigen::Dynamic, Eigen::Dynamic> Matrix;
|
||||
typedef Eigen::Triplet<Scalar> Triplet;
|
||||
typedef Eigen::SparseMatrix<Scalar> StiffnessMatrix;
|
||||
|
||||
typedef std::vector<Triplet> TripletVector;
|
||||
typedef typename TripletVector::iterator TripletVectorIterator;
|
||||
|
||||
typedef std::vector<bool> BoolVector;
|
||||
typedef std::vector<unsigned> IndexMap;
|
||||
|
||||
typedef Eigen::SimplicialLDLT<StiffnessMatrix, Eigen::Lower> 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<Block> 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<BlockIndex> 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
|
||||
@ -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 <typename Solver>
|
||||
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 <typename Solver>
|
||||
bool checkEigenSolverError(const Solver& solver, SolverError& error)
|
||||
{
|
||||
return CheckEigenSolverError<Solver>::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<Derived>& 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<Scalar>();
|
||||
}
|
||||
}
|
||||
|
||||
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<Self> 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<typename Mesh::Scalar>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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<Self> 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
|
||||
@ -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 <Eigen/Core>
|
||||
|
||||
|
||||
namespace Vitelotte
|
||||
{
|
||||
|
||||
|
||||
template <class Derived0, class Derived1>
|
||||
inline typename Eigen::MatrixBase<Derived0>::Scalar det2(
|
||||
const Eigen::MatrixBase<Derived0>& _v0, const Eigen::MatrixBase<Derived1>& _v1);
|
||||
|
||||
|
||||
} // namespace Vitelotte
|
||||
|
||||
#include "femUtils.hpp"
|
||||
|
||||
|
||||
#endif
|
||||
@ -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 <class Derived0, class Derived1>
|
||||
inline typename Eigen::MatrixBase<Derived0>::Scalar det2(const Eigen::MatrixBase<Derived0>& _v0, const Eigen::MatrixBase<Derived1>& _v1)
|
||||
{
|
||||
return _v0.x() * _v1.y() - _v0.y() * _v1.x();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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 <Eigen/Core>
|
||||
|
||||
#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<Scalar> Base;
|
||||
|
||||
typedef Eigen::Matrix<Scalar, 2, 1> Vector;
|
||||
typedef Eigen::Matrix<Scalar, 9, 1> Values;
|
||||
typedef Eigen::Matrix<Scalar, 9, 2> Jacobian;
|
||||
typedef Eigen::Matrix<Scalar, 2, 2> Hessian;
|
||||
|
||||
typedef Eigen::Matrix<Scalar, 3, 1> BarycentricCoord;
|
||||
|
||||
protected:
|
||||
typedef Eigen::Matrix<Scalar, 3, 1> Vector3;
|
||||
typedef Eigen::Matrix<Scalar, 3, 3> Matrix3;
|
||||
typedef Eigen::Matrix<Scalar, 2, 3> 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<Derived0>& p0,
|
||||
const Eigen::MatrixBase<Derived1>& p1,
|
||||
const Eigen::MatrixBase<Derived2>& 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
|
||||
@ -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 <Eigen/Core>
|
||||
#include <Eigen/Sparse>
|
||||
|
||||
#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<Scalar, 3, 1> Vector3;
|
||||
typedef Eigen::Matrix<Scalar, 6, 1> Vector6;
|
||||
|
||||
typedef typename Mesh::Halfedge Halfedge;
|
||||
typedef std::map<unsigned, Halfedge> 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
|
||||
@ -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 <Eigen/Dense>
|
||||
|
||||
#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<Scalar, 9, 9> 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<Scalar>();
|
||||
}
|
||||
|
||||
for(int i = 0; i < 9; ++i)
|
||||
{
|
||||
if(nodes[i] < 0)
|
||||
{
|
||||
if(error) error->error("Invalid node");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
typedef FVElement<Scalar> Elem;
|
||||
Elem elem(p);
|
||||
|
||||
if(elem.doubleArea() <= 0)
|
||||
{
|
||||
if(error) error->warning("Degenerated or reversed triangle");
|
||||
}
|
||||
|
||||
typedef Eigen::Array<Scalar, 3, 1> 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<Scalar, 9, 1> 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<Scalar>());
|
||||
|
||||
++hit;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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 <Eigen/Core>
|
||||
|
||||
#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<Scalar, 2, 1> Vector;
|
||||
typedef Eigen::Matrix<Scalar, 3, 1> Values;
|
||||
typedef Eigen::Matrix<Scalar, 3, 2> Jacobian;
|
||||
typedef Eigen::Matrix<Scalar, 2, 2> Hessian;
|
||||
|
||||
typedef Eigen::Matrix<Scalar, 3, 1> BarycentricCoord;
|
||||
|
||||
protected:
|
||||
typedef Eigen::Matrix<Scalar, 3, 1> Vector3;
|
||||
|
||||
typedef Eigen::Matrix<Scalar, 2, 2> Matrix2;
|
||||
typedef Eigen::Matrix<Scalar, 3, 3> Matrix3;
|
||||
|
||||
typedef Eigen::Matrix<Scalar, 3, 2> Matrix3x2;
|
||||
typedef Eigen::Matrix<Scalar, 2, 3> 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<Derived0>& p0,
|
||||
const Eigen::MatrixBase<Derived1>& p1,
|
||||
const Eigen::MatrixBase<Derived2>& 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<const Matrix3, 3, 2> 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<Derived0>& p0,
|
||||
const Eigen::MatrixBase<Derived1>& p1,
|
||||
const Eigen::MatrixBase<Derived2>& 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
|
||||
@ -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 <Eigen/Core>
|
||||
#include <Eigen/Sparse>
|
||||
|
||||
|
||||
#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
|
||||
@ -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<Scalar>();
|
||||
++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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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 <Eigen/Core>
|
||||
|
||||
#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<Scalar> Base;
|
||||
|
||||
typedef Eigen::Matrix<Scalar, 2, 1> Vector;
|
||||
typedef Eigen::Matrix<Scalar, 6, 1> Values;
|
||||
typedef Eigen::Matrix<Scalar, 6, 2> Jacobian;
|
||||
typedef Eigen::Matrix<Scalar, 2, 2> Hessian;
|
||||
|
||||
typedef Eigen::Matrix<Scalar, 3, 1> BarycentricCoord;
|
||||
|
||||
protected:
|
||||
typedef Eigen::Matrix<Scalar, 3, 1> Vector3;
|
||||
|
||||
typedef Eigen::Matrix<Scalar, 2, 3> Matrix2x3;
|
||||
typedef Eigen::Matrix<Scalar, 3, 3> 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<Derived0>& p0,
|
||||
const Eigen::MatrixBase<Derived1>& p1,
|
||||
const Eigen::MatrixBase<Derived2>& 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
|
||||
@ -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 <Eigen/Core>
|
||||
#include <Eigen/Sparse>
|
||||
|
||||
|
||||
#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<Scalar, 6, 1> 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
|
||||
@ -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 <Eigen/Dense>
|
||||
|
||||
#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<Scalar>();
|
||||
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<Scalar>();
|
||||
}
|
||||
|
||||
for(int i = 0; i < 6; ++i)
|
||||
{
|
||||
if(nodes[i] < 0)
|
||||
{
|
||||
if(error) error->error("Invalid node");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
typedef MorleyElement<Scalar> Elem;
|
||||
Elem elem(p);
|
||||
|
||||
if(elem.doubleArea() <= 0 && error)
|
||||
{
|
||||
error->warning("Degenerated or reversed triangle");
|
||||
}
|
||||
|
||||
typedef Eigen::Matrix<Scalar, 3, 1> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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 <Eigen/Core>
|
||||
|
||||
#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<Scalar> Base;
|
||||
|
||||
typedef Eigen::Matrix<Scalar, 2, 1> Vector;
|
||||
typedef Eigen::Matrix<Scalar, 6, 1> Values;
|
||||
typedef Eigen::Matrix<Scalar, 6, 2> Jacobian;
|
||||
typedef Eigen::Matrix<Scalar, 2, 2> Hessian;
|
||||
|
||||
typedef Eigen::Matrix<Scalar, 3, 1> 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<Derived0>& p0,
|
||||
const Eigen::MatrixBase<Derived1>& p1,
|
||||
const Eigen::MatrixBase<Derived2>& 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
|
||||
@ -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 <Eigen/Core>
|
||||
#include <Eigen/Sparse>
|
||||
|
||||
|
||||
#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<Scalar, 6, 6> 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
|
||||
@ -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<Scalar>();
|
||||
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;
|
||||
|
||||
|
||||
}
|
||||
@ -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 <vector>
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Sparse>
|
||||
|
||||
#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
|
||||
@ -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<Derived>& 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<Mesh, Inserter> SInserter;
|
||||
SInserter sInserter(inserter, mesh, element);
|
||||
Base::addCoefficients(sInserter, mesh, element, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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 <string>
|
||||
|
||||
|
||||
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
|
||||
@ -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 <cassert>
|
||||
#include <climits>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <Eigen/Core>
|
||||
|
||||
#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<Scalar, DimsAtCompileTime, CoeffsAtCompileTime> Self;
|
||||
|
||||
typedef Eigen::Matrix<Scalar, DimsAtCompileTime, 1> Vector;
|
||||
typedef Eigen::Matrix<Scalar, CoeffsAtCompileTime, 1> Value;
|
||||
typedef Eigen::Matrix<Scalar, CoeffsAtCompileTime, DimsAtCompileTime> Gradient;
|
||||
|
||||
typedef BezierSegment<Vector> CurvedEdge;
|
||||
|
||||
protected:
|
||||
typedef Eigen::Matrix<Scalar, DimsAtCompileTime, Eigen::Dynamic> VectorMatrix;
|
||||
typedef Eigen::Matrix<Scalar, CoeffsAtCompileTime, Eigen::Dynamic> 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 T> class NodeProperty : public PatateCommon::Property<T>
|
||||
{
|
||||
public:
|
||||
|
||||
/// default constructor
|
||||
explicit NodeProperty() {}
|
||||
explicit NodeProperty(PatateCommon::Property<T> p)
|
||||
: PatateCommon::Property<T>(p) {}
|
||||
|
||||
/// access the data stored for vertex \c v
|
||||
typename PatateCommon::Property<T>::Reference operator[](Node n)
|
||||
{
|
||||
return PatateCommon::Property<T>::operator[](n.idx());
|
||||
}
|
||||
|
||||
/// access the data stored for vertex \c v
|
||||
typename PatateCommon::Property<T>::ConstReference operator[](Node n) const
|
||||
{
|
||||
return PatateCommon::Property<T>::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 <typename Derived>
|
||||
inline Vertex addVertex(const Eigen::DenseBase<Derived>& 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 <class T> NodeProperty<T> addNodeProperty(const std::string& name, const T t=T())
|
||||
{
|
||||
return NodeProperty<T>(m_nprops.add<T>(name, t));
|
||||
}
|
||||
|
||||
template <class T> NodeProperty<T> getNodeProperty(const std::string& name) const
|
||||
{
|
||||
return NodeProperty<T>(m_nprops.get<T>(name));
|
||||
}
|
||||
|
||||
template <class T> NodeProperty<T> nodeProperty(const std::string& name, const T t=T())
|
||||
{
|
||||
return NodeProperty<T>(m_nprops.getOrAdd<T>(name, t));
|
||||
}
|
||||
|
||||
template <class T> void removeNodeProperty(NodeProperty<T>& p)
|
||||
{
|
||||
m_nprops.remove(p);
|
||||
}
|
||||
|
||||
const std::type_info& getNodePropertyType(const std::string& name)
|
||||
{
|
||||
return m_nprops.getType(name);
|
||||
}
|
||||
|
||||
std::vector<std::string> 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 <typename Derived>
|
||||
inline Node addNode(const Eigen::DenseBase<Derived>& 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<Scalar>::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<Vertex, Gradient> VertexGradientPair;
|
||||
typedef std::map<Vertex, Gradient, std::less<Vertex>,
|
||||
Eigen::aligned_allocator<VertexGradientPair> > VertexGradientMap;
|
||||
|
||||
typedef std::pair<Edge, CurvedEdge> EdgeCurvePair;
|
||||
typedef std::map<Edge, CurvedEdge, std::less<Edge>,
|
||||
Eigen::aligned_allocator<EdgeCurvePair> > 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<Halfedge>& 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<Node> m_halfedgeAttributes[HALFEDGE_ATTRIB_COUNT];
|
||||
|
||||
unsigned m_deletedNodes;
|
||||
NodeMatrix m_nodes;
|
||||
NodeProperty<bool> m_ndeleted;
|
||||
std::vector<Node> m_gcNodeMap;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Vitelotte
|
||||
|
||||
|
||||
#include "vgMesh.hpp"
|
||||
|
||||
|
||||
#endif // VGMESH_H
|
||||
|
||||
@ -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<bool>("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<bool>("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<Node> 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<Derived>& 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<Node>(_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<Self*>(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<Self*>(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 Derived>
|
||||
typename VGMesh<_Scalar, _Dim, _Chan>::Node
|
||||
VGMesh<_Scalar, _Dim, _Chan>::addNode(const Eigen::DenseBase<Derived>& 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<Halfedge> consEdges;
|
||||
consEdges.reserve(12);
|
||||
|
||||
for(VertexIterator vit = verticesBegin();
|
||||
vit != verticesEnd(); ++vit)
|
||||
{
|
||||
consEdges.clear();
|
||||
findConstrainedEdgesSimplify(*vit, consEdges);
|
||||
|
||||
Halfedge prev = consEdges.back();
|
||||
std::vector<Halfedge>::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<Halfedge> 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<Halfedge>::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<Node>(_halfedgeAttrName[ai]);
|
||||
}
|
||||
|
||||
m_deletedNodes = rhs.m_deletedNodes;
|
||||
m_nodes = rhs.m_nodes/*.template cast<Scalar>()*/;
|
||||
m_ndeleted = nodeProperty<bool>("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<Halfedge>& 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
|
||||
@ -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 <vector>
|
||||
|
||||
#include <Eigen/StdVector>
|
||||
|
||||
#include "../Core/vgMesh.h"
|
||||
|
||||
|
||||
namespace Vitelotte
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* \brief A 1D piecewise linear function.
|
||||
*/
|
||||
template <typename _Value>
|
||||
class PiecewiseLinearFunction
|
||||
{
|
||||
public:
|
||||
typedef _Value Value;
|
||||
|
||||
private:
|
||||
typedef std::pair<float, Value> FloatValuePair;
|
||||
typedef std::map<float, Value, std::less<float>, Eigen::aligned_allocator<FloatValuePair> > 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<Value> ValueFunction;
|
||||
|
||||
typedef Vitelotte::BezierPath<Vector> 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<Scalar>::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<PointConstraint> m_pointConstraintConn;
|
||||
typename Base::template HalfedgeProperty<HalfedgeCurveConnectivity> m_halfedgeCurveConn;
|
||||
|
||||
std::vector<PointConstraintInfo, Eigen::aligned_allocator<PointConstraintInfo> > m_pointConstraints;
|
||||
std::vector<CurveInfo> m_curves;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "dcMesh.hpp"
|
||||
|
||||
|
||||
#endif
|
||||
@ -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 _Value>
|
||||
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<PointConstraint>(
|
||||
"v:pointConstraintConnectivity");
|
||||
m_halfedgeCurveConn = Base::template addHalfedgeProperty<HalfedgeCurveConnectivity>(
|
||||
"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<PointConstraint>(
|
||||
"v:pointConstraintConnectivity");
|
||||
m_halfedgeCurveConn = Base::template addHalfedgeProperty<HalfedgeCurveConnectivity>(
|
||||
"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<DCMesh*>(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<DCMesh*>(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<PointConstraint>(
|
||||
"v:pointConstraintConnectivity");
|
||||
m_halfedgeCurveConn = Base::template halfedgeProperty<HalfedgeCurveConnectivity>(
|
||||
"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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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 <cassert>
|
||||
#include <stdexcept>
|
||||
#include <istream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#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<Mesh> 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<Vertex> m_fVertices;
|
||||
std::string m_tmp;
|
||||
Value m_value;
|
||||
Gradient m_gradient;
|
||||
std::vector<unsigned> m_faceIndices;
|
||||
std::vector<unsigned> 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_
|
||||
|
||||
@ -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<Mesh> 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
|
||||
@ -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
|
||||
@ -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 <string>
|
||||
#include <sstream>
|
||||
|
||||
#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
|
||||
@ -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
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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 <cassert>
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
|
||||
|
||||
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<int> 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<Mesh>::Version version=MVGWriter<Mesh>::LATEST_VERSION);
|
||||
|
||||
template < typename Mesh >
|
||||
void writeMvgToFile(const std::string& filename, const Mesh& mesh,
|
||||
typename MVGWriter<Mesh>::Version version=MVGWriter<Mesh>::LATEST_VERSION);
|
||||
|
||||
|
||||
} // namespace Vitelotte
|
||||
|
||||
#include "mvgWriter.hpp"
|
||||
|
||||
|
||||
#endif // _QVGWRITER_H_
|
||||
@ -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 <vector>
|
||||
|
||||
#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<bool> 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<Mesh>::Version version)
|
||||
{
|
||||
MVGWriter<Mesh> writer(version);
|
||||
writer.write(out, mesh);
|
||||
}
|
||||
|
||||
template < typename Mesh >
|
||||
void writeMvgToFile(const std::string& filename, const Mesh& mesh,
|
||||
typename MVGWriter<Mesh>::Version version)
|
||||
{
|
||||
std::ofstream out(filename.c_str());
|
||||
writeMvg(out, mesh, version);
|
||||
}
|
||||
|
||||
|
||||
} // namespace Vitelotte
|
||||
@ -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 <Eigen/Dense>
|
||||
#include <Eigen/StdVector>
|
||||
|
||||
#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 _Mesh::Vector>,
|
||||
typename _ValueProj = DefaultValueProj<typename _Mesh::Value> >
|
||||
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<unsigned> IndicesVector;
|
||||
typedef std::vector<Vector4, Eigen::aligned_allocator<Vector4> > Vector4Vector;
|
||||
|
||||
struct GlVertex {
|
||||
Vector4 position;
|
||||
Vector3 normal;
|
||||
};
|
||||
typedef std::vector<GlVertex, Eigen::aligned_allocator<GlVertex> > 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
|
||||
@ -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<Vector>::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<Value>::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<float>(); break;
|
||||
default: c = value.template head<4>()
|
||||
.template cast<float>(); 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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
//};
|
||||
|
||||
|
||||
|
||||
//}
|
||||
|
||||
}
|
||||
@ -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.
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
@ -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 <tt>common</tt> directory under the <tt>example/vitelotte</tt> 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.
|
||||
- <tt>plotObj.h</tt> contains a function that convert a 2D mvg file in a 3D plot. Used by the <tt>plot</tt> command of mvgtk and useful for debugging.
|
||||
- <tt>textFormatter.h</tt> contains a simple function to break paragraph into multiple lines. Used by mvgtk for help display.
|
||||
- <tt>shaders.cpp</tt> contains the shaders required by the various OpenGL classes in this directory. It is an automatically generated file that regroup the content of the <tt>shaders</tt> directory in a single .cpp.
|
||||
|
||||
*/
|
||||
@ -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).
|
||||
|
||||
*/
|
||||
@ -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 <filename>
|
||||
\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.
|
||||
|
||||
*/
|
||||
@ -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. <tt>mvgtk</tt> 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 <tt>mvgtk</tt>:
|
||||
\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 <tt>-v</tt> 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 <tt>conv fv</tt> command (convert) sets the mesh attributes to represent FV elements. In practice, just call \c VGMesh::setAttributes(VGMesh::FV_FLAGS).
|
||||
2. The <tt>finalize</tt> command finalizes the mesh (see \ref vitelotte_user_manual_vg_mesh_finalize_subsec). Just call <tt>VGMesh::finalize()</tt> as you may guess.
|
||||
3. The <tt>solve</tt> 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 <tt>out out.mvg</tt> command outputs the resulting mesh in the file <tt>out.mvg</tt>. 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 <tt>mvgtk --help</tt>.
|
||||
|
||||
|
||||
\section vitelotte_example_mvgtk_options_sec Global options
|
||||
|
||||
- <tt>-h</tt>, <tt>--help</tt>: print help and exit.
|
||||
- <tt>-v</tt>, <tt>--verbose</tt>: verbose mode.
|
||||
|
||||
|
||||
\section vitelotte_example_mvgtk_commands_sec Commands
|
||||
|
||||
\subsection vitelotte_example_mvgtk_check_command_sub Check command
|
||||
|
||||
The <tt>check</tt> 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 <tt>compact</tt> 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 <tt>convert ATTR</tt> (or <tt>conv</tt>) 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:
|
||||
- <tt>none</tt>: removes all attributes.
|
||||
- <tt>linear</tt>: set attributes to represent linear elements. See VGMesh::LINEAR_FLAGS.
|
||||
- <tt>quadratic</tt>: set attributes to represent quadratic elements. See VGMesh::QUADRATIC_FLAGS.
|
||||
- <tt>morley</tt>: set attributes to represent morley elements. See VGMesh::MORLEY_FLAGS.
|
||||
- <tt>fv</tt>: set attributes to represent FV elements. See VGMesh::FV_FLAGS.
|
||||
|
||||
If you use <tt>convert</tt> to remove attributes from the mesh, it may be worth calling <tt>compact</tt> 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 <tt>curves-to-nodes</tt> (or <tt>c2n</tt>) 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 <tt>finalize</tt> afterward.
|
||||
|
||||
\include Vitelotte/mvgtk/curvesToNodesCommand.cpp
|
||||
|
||||
|
||||
\subsection vitelotte_example_mvgtk_finalize_command_sub Finalize command
|
||||
|
||||
The <tt>finalize</tt> 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 <tt>output FILENAME</tt> (or <tt>out</tt>) command output the mesh in FILENAME using the MVG file format.
|
||||
|
||||
|
||||
\subsection vitelotte_example_mvgtk_plot_command_sub Plot command
|
||||
|
||||
The <tt>plot COEFF SUBDIV FILENAME</tt> 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 <tt>convert</tt> before calling <tt>plot</tt> to change the elements used.
|
||||
|
||||
\include Vitelotte/mvgtk/plotCommand.cpp
|
||||
|
||||
|
||||
\subsection vitelotte_example_mvgtk_simplify_command_sub Simplify command
|
||||
|
||||
The <tt>simplify</tt> (or <tt>simp</tt>) command simplifies a mesh by removing the nodes that can be reconstructed with <tt>finalize</tt>. 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 <tt>solve</tt> 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
|
||||
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
@ -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
|
||||
|
||||
*/
|
||||
@ -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
|
||||
|
||||
*/
|
||||
@ -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 <Patate/vitelotte.h>
|
||||
|
||||
// include OpenGL-based rendering classes, see renderer documentation.
|
||||
#include <Patate/vitelotte_gl.h>
|
||||
\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.
|
||||
|
||||
*/
|
||||
}
|
||||
@ -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.
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
@ -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.
|
||||
|
||||
<b>SRGB:</b> 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.
|
||||
|
||||
<b>Linear RGB or XYZ:</b> 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.
|
||||
|
||||
<b>Cie Lab / Cie Luv:</b> 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.
|
||||
|
||||
*/
|
||||
@ -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<float, [...]> Mesh;
|
||||
|
||||
typedef FVElementBuilder<Mesh, double> FVBuilder;
|
||||
typedef SingularElementDecorator<FVBuilder> ElementBuilder;
|
||||
typedef FemSolver<Mesh, ElementBuilder> 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
|
||||
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
@ -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<Mesh>().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<Mesh>().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).
|
||||
|
||||
|
||||
*/
|
||||
@ -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 <GL/glew.h>
|
||||
|
||||
// Include Vitelotte's classes that depends on OpenGL. An OpenGL
|
||||
// header *must* have been included beforehand.
|
||||
#include <Patate/vitelotte_gl.h>
|
||||
\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<Mesh> 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 <a href="mailto:patate-info@lists.gforge.inria.fr"> our mailing list </a> .
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user