Added the possibility of constraining the movement of some seeds onto a specific domain. Now during relaxation you can for example fix some seeds and constrain some other seeds over linear features defined as subset of vertices.

This commit is contained in:
Paolo Cignoni 2013-12-20 02:33:36 +00:00
parent d1a5d53a89
commit ab5869f6c3
2 changed files with 119 additions and 37 deletions

View File

@ -81,15 +81,47 @@ int main( int argc, char **argv )
return -1;
}
tri::Clean<MyMesh>::RemoveUnreferencedVertex(baseMesh);
tri::Allocator<MyMesh>::CompactEveryVector(baseMesh);
tri::UpdateTopology<MyMesh>::VertexFace(baseMesh);
tri::UpdateFlags<MyMesh>::FaceBorderFromVF(baseMesh);
tri::UpdateFlags<MyMesh>::VertexBorderFromFace(baseMesh);
// -- Build the mesh with corners
MyMesh cornerMesh;
tri::SurfaceSampling<MyMesh,tri::TrivialSampler<MyMesh> >::PoissonDiskParam pp;
float radius = tri::SurfaceSampling<MyMesh,tri::TrivialSampler<MyMesh> >::ComputePoissonDiskRadius(baseMesh,sampleNum);
// -- Build a sampling with just corners (Poisson filtered)
MyMesh poissonCornerMesh;
std::vector<Point3f> sampleVec;
tri::TrivialSampler<MyMesh> mps(sampleVec);
tri::SurfaceSampling<MyMesh,tri::TrivialSampler<MyMesh> >::VertexBorderCorner(baseMesh,mps,math::ToRad(150.f));
tri::Build(cornerMesh,sampleVec);
tri::Build(poissonCornerMesh,sampleVec);
tri::io::ExporterPLY<MyMesh>::Save(poissonCornerMesh,"cornerMesh.ply");
sampleVec.clear();
tri::SurfaceSampling<MyMesh,tri::TrivialSampler<MyMesh> >::PoissonDiskPruning(mps, poissonCornerMesh, radius, pp);
tri::Build(poissonCornerMesh,sampleVec);
tri::io::ExporterPLY<MyMesh>::Save(poissonCornerMesh,"poissonCornerMesh.ply");
// Now save the corner as Fixed Seeds for later...
std::vector<MyVertex *> fixedSeedVec;
tri::VoronoiProcessing<MyMesh>::SeedToVertexConversion(baseMesh,sampleVec,fixedSeedVec);
tri::VoronoiProcessing<MyMesh, tri::EuclideanDistance<MyMesh> >::FixVertexVector(baseMesh,fixedSeedVec);
// -- Build a sampling with points on the border
MyMesh borderMesh,poissonBorderMesh;
sampleVec.clear();
tri::SurfaceSampling<MyMesh,tri::TrivialSampler<MyMesh> >::VertexBorder(baseMesh,mps);
tri::Build(borderMesh,sampleVec);
tri::io::ExporterPLY<MyMesh>::Save(borderMesh,"borderMesh.ply");
// -- and then prune the border sampling with poisson strategy using the precomputed corner vertexes.
pp.preGenMesh = &poissonCornerMesh;
pp.preGenFlag=true;
sampleVec.clear();
tri::SurfaceSampling<MyMesh,tri::TrivialSampler<MyMesh> >::PoissonDiskPruning(mps, borderMesh, radius, pp);
tri::Build(poissonBorderMesh,sampleVec);
tri::io::ExporterPLY<MyMesh>::Save(poissonBorderMesh,"PoissonEdgeMesh.ply");
// -- Build the montercarlo sampling of the surface
MyMesh MontecarloSurfaceMesh;
@ -98,12 +130,9 @@ int main( int argc, char **argv )
tri::Build(MontecarloSurfaceMesh,sampleVec);
tri::io::ExporterPLY<MyMesh>::Save(MontecarloSurfaceMesh,"MontecarloSurfaceMesh.ply");
// -- Prune the montecarlo sampling with poisson strategy using the precomputed corner vertexes.
tri::SurfaceSampling<MyMesh,tri::TrivialSampler<MyMesh> >::PoissonDiskParam pp;
pp.preGenMesh = &cornerMesh;
pp.preGenFlag=true;
// -- Prune the montecarlo sampling with poisson strategy using the precomputed vertexes on the border.
pp.preGenMesh = &poissonBorderMesh;
sampleVec.clear();
float radius = tri::SurfaceSampling<MyMesh,tri::TrivialSampler<MyMesh> >::ComputePoissonDiskRadius(baseMesh,sampleNum);
tri::SurfaceSampling<MyMesh,tri::TrivialSampler<MyMesh> >::PoissonDiskPruning(mps, MontecarloSurfaceMesh, radius, pp);
MyMesh PoissonMesh;
tri::Build(PoissonMesh,sampleVec);
@ -111,26 +140,32 @@ int main( int argc, char **argv )
std::vector<MyVertex *> seedVec;
tri::VoronoiProcessing<MyMesh>::SeedToVertexConversion(baseMesh,sampleVec,seedVec);
float eps = baseMesh.bbox.Diag()/10000.0f;
for(size_t i=0;i<cornerMesh.vert.size();++i)
{
for(size_t j=0;j<seedVec.size();++j)
if(Distance(cornerMesh.vert[i].P(),seedVec[j]->P()) < eps)
seedVec[j]->SetS();
// Select all the vertexes on the border to define a constrained domain.
// In our case we select the border vertexes to make sure that the seeds on the border
// relax themselves remaining on the border
for(size_t i=0;i<baseMesh.vert.size();++i){
if(baseMesh.vert[i].IsB())
baseMesh.vert[i].SetS();
}
tri::VoronoiProcessingParameter vpp;
vpp.deleteUnreachedRegionFlag=true;
vpp.fixSelectedSeed=true;
vpp.preserveFixedSeed=true;
vpp.collapseShortEdge=true;
vpp.collapseShortEdgePerc=collapseShortEdgePerc;
vpp.triangulateRegion = true;
vpp.unbiasedSeedFlag =true;
vpp.geodesicRelaxFlag=false;
vpp.constrainSelectedSeed=true;
tri::EuclideanDistance<MyMesh> dd;
int t0=clock();
// And now, at last, the relaxing procedure!
tri::VoronoiProcessing<MyMesh, tri::EuclideanDistance<MyMesh> >::VoronoiRelaxing(baseMesh, seedVec, iterNum, dd, vpp);
int t1=clock();
// Get the result in some pleasant form converting it to a real voronoi diagram.
tri::VoronoiProcessing<MyMesh, tri::EuclideanDistance<MyMesh> >::ConvertVoronoiDiagramToMesh(baseMesh,outMesh,polyMesh, seedVec, dd, vpp);
tri::io::ExporterPLY<MyMesh>::Save(baseMesh,"base.ply",tri::io::Mask::IOM_VERTCOLOR + tri::io::Mask::IOM_VERTQUALITY );
tri::io::ExporterPLY<MyMesh>::Save(outMesh,"out.ply",tri::io::Mask::IOM_VERTCOLOR + tri::io::Mask::IOM_FLAGS );

View File

@ -65,7 +65,8 @@ struct VoronoiProcessingParameter
colorStrategy = DistanceFromSeed;
areaThresholdPerc=0;
deleteUnreachedRegionFlag=false;
fixSelectedSeed=false;
constrainSelectedSeed=false;
preserveFixedSeed=false;
collapseShortEdge=false;
collapseShortEdgePerc = 0.01f;
triangulateRegion=false;
@ -76,9 +77,15 @@ struct VoronoiProcessingParameter
float areaThresholdPerc;
bool deleteUnreachedRegionFlag;
bool unbiasedSeedFlag;
bool fixSelectedSeed; /// the vertexes that are selected are used as fixed seeds:
/// They will not move during relaxing
/// and they will be always included in the final triangulation (if on a border).
bool constrainSelectedSeed; /// If true the selected vertexes define a constraining domain:
/// During relaxation all selected seeds are constrained to move
/// only on other selected vertices.
/// In this way you can constrain some seed to move only on certain
/// domains, for example moving only along some linear features
/// like border of creases.
bool preserveFixedSeed; /// If true the 'fixed' seeds are not moved during relaxation.
/// \see FixVertexVector function to see how to fix a set of seeds.
bool triangulateRegion;
bool collapseShortEdge;
float collapseShortEdgePerc;
@ -819,8 +826,11 @@ struct QuadricSumDistance
}
};
/// Find the new position according to the geodesic rule.
/// For each region, given the frontiers, it chooses the point with the highest distance from the frontier
/// Find the new position
/// For each region it search the vertex that minimize the sum of the squared distance
/// from all the points of the region.
/// It uses a vector of QuadricSumDistances
/// (for simplicity of the size of the vertex but only the ones of the seed are used).
///
static void QuadricRelax(MeshType &m, std::vector<VertexType *> &seedVec, std::vector<VertexPointer> &frontierVec,
std::vector<VertexType *> &newSeeds,
@ -829,15 +839,18 @@ static void QuadricRelax(MeshType &m, std::vector<VertexType *> &seedVec, std::v
newSeeds.clear();
typename MeshType::template PerVertexAttributeHandle<VertexPointer> sources;
sources = tri::Allocator<MeshType>:: template GetPerVertexAttribute<VertexPointer> (m,"sources");
typename MeshType::template PerVertexAttributeHandle<bool> fixed;
fixed = tri::Allocator<MeshType>:: template GetPerVertexAttribute<bool> (m,"fixed");
QuadricSumDistance dz;
std::vector<QuadricSumDistance> dVec(m.vert.size(),dz);
assert((int)m.vert.size()==m.vn);
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
{
assert(sources[vi]!=0);
int seedIndex = tri::Index(m,sources[vi]);
dVec[seedIndex].AddPoint(vi->P());
}
// Search the local maxima for each region and use them as new seeds
std::pair<float,VertexPointer> zz(std::numeric_limits<ScalarType>::max(), static_cast<VertexPointer>(0));
std::vector< std::pair<float,VertexPointer> > seedMaximaVec(m.vert.size(),zz);
@ -847,22 +860,27 @@ static void QuadricRelax(MeshType &m, std::vector<VertexType *> &seedVec, std::v
int seedIndex = tri::Index(m,sources[vi]);
ScalarType val = dVec[seedIndex].Eval(vi->P());
vi->Q()=val;
if(seedMaximaVec[seedIndex].first > val)
// if constrainSelectedSeed we search only among selected vertices
if(!vpp.constrainSelectedSeed || !sources[vi]->IsS() || vi->IsS())
{
seedMaximaVec[seedIndex].first = val;
seedMaximaVec[seedIndex].second = &*vi;
if(seedMaximaVec[seedIndex].first > val)
{
seedMaximaVec[seedIndex].first = val;
seedMaximaVec[seedIndex].second = &*vi;
}
}
}
tri::UpdateColor<MeshType>::PerVertexQualityRamp(m);
// tri::io::ExporterPLY<MeshType>::Save(m,"last.ply",tri::io::Mask::IOM_VERTCOLOR + tri::io::Mask::IOM_VERTQUALITY );
// update the seedvector with the new maxima (For the vertex not selected)
// update the seedvector with the new maxima (For the vertex not fixed)
for(size_t i=0;i<m.vert.size();++i)
if(seedMaximaVec[i].second) // only seeds entries have a non zero pointer
if(seedMaximaVec[i].second) // only updated entries have a non zero pointer
{
if(vpp.fixSelectedSeed && sources[seedMaximaVec[i].second]->IsS())
newSeeds.push_back(sources[seedMaximaVec[i].second]);
VertexPointer curSrc = sources[seedMaximaVec[i].second];
if(vpp.preserveFixedSeed && fixed[curSrc])
newSeeds.push_back(curSrc);
else
newSeeds.push_back(seedMaximaVec[i].second);
}
@ -878,6 +896,8 @@ static void GeodesicRelax(MeshType &m, std::vector<VertexType *> &seedVec, std::
newSeeds.clear();
typename MeshType::template PerVertexAttributeHandle<VertexPointer> sources;
sources = tri::Allocator<MeshType>:: template GetPerVertexAttribute<VertexPointer> (m,"sources");
typename MeshType::template PerVertexAttributeHandle<bool> fixed;
fixed = tri::Allocator<MeshType>:: template GetPerVertexAttribute<bool> (m,"fixed");
std::vector<typename tri::Geodesic<MeshType>::VertDist> biasedFrontierVec;
BuildBiasedSeedVec(m,df,seedVec,frontierVec,biasedFrontierVec,vpp);
@ -895,21 +915,29 @@ static void GeodesicRelax(MeshType &m, std::vector<VertexType *> &seedVec, std::
{
assert(sources[vi]!=0);
int seedIndex = tri::Index(m,sources[vi]);
if(seedMaximaVec[seedIndex].first < (*vi).Q())
if(!vpp.constrainSelectedSeed || !sources[vi]->IsS() || vi->IsS())
{
seedMaximaVec[seedIndex].first=(*vi).Q();
seedMaximaVec[seedIndex].second=&*vi;
if(seedMaximaVec[seedIndex].first < (*vi).Q())
{
seedMaximaVec[seedIndex].first=(*vi).Q();
seedMaximaVec[seedIndex].second=&*vi;
}
}
}
// update the seedvector with the new maxima (For the vertex not selected)
for(size_t i=0;i<seedMaximaVec.size();++i)
if(seedMaximaVec[i].second)
if(seedMaximaVec[i].second)// only updated entries have a non zero pointer
{
if(vpp.fixSelectedSeed && sources[seedMaximaVec[i].second]->IsS())
newSeeds.push_back(sources[seedMaximaVec[i].second]);
VertexPointer curSrc = sources[seedMaximaVec[i].second];
if(vpp.preserveFixedSeed && fixed[curSrc])
newSeeds.push_back(curSrc);
else
newSeeds.push_back(seedMaximaVec[i].second);
// if(vpp.fixSelectedSeed && sources[seedMaximaVec[i].second]->IsS())
// newSeeds.push_back(sources[seedMaximaVec[i].second]);
// else
// newSeeds.push_back(seedMaximaVec[i].second);
}
}
@ -934,18 +962,37 @@ static void PruneSeedByRegionArea(std::vector<VertexType *> &seedVec,
swap(seedVec,newSeedVec);
}
/// Mark a vector of seeds to be fixed.
static void FixVertexVector(MeshType &m, std::vector<VertexType *> &vertToFixVec)
{
typename MeshType::template PerVertexAttributeHandle<bool> fixed;
fixed = tri::Allocator<MeshType>:: template GetPerVertexAttribute<bool> (m,"fixed");
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
fixed[vi]=false;
for(size_t i=0;i<vertToFixVec.size();++i)
fixed[vertToFixVec[i]]=true;
}
/// \brief Perform a Lloyd relaxation cycle over a mesh
///
///
static void VoronoiRelaxing(MeshType &m, std::vector<VertexType *> &seedVec, int relaxIter, DistanceFunctor &df,
VoronoiProcessingParameter &vpp, vcg::CallBackPos *cb=0)
static void VoronoiRelaxing(MeshType &m, std::vector<VertexType *> &seedVec,
int relaxIter, DistanceFunctor &df,
VoronoiProcessingParameter &vpp,
vcg::CallBackPos *cb=0)
{
tri::RequireVFAdjacency(m);
tri::RequireCompactness(m);
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
assert(vi->VFp());
tri::UpdateFlags<MeshType>::FaceBorderFromVF(m);
tri::UpdateFlags<MeshType>::VertexBorderFromFace(m);
typename MeshType::template PerVertexAttributeHandle<VertexPointer> sources;
sources = tri::Allocator<MeshType>:: template GetPerVertexAttribute<VertexPointer> (m,"sources");
typename MeshType::template PerVertexAttributeHandle<bool> fixed;
fixed = tri::Allocator<MeshType>:: template GetPerVertexAttribute<bool> (m,"fixed");
for(int iter=0;iter<relaxIter;++iter)
{