You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

409 lines
13 KiB

/*****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004-2022 \/)\/ *
* 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. *
* *
****************************************************************************/
#ifndef __VCGLIB_CLUSTERING
#define __VCGLIB_CLUSTERING
#include <vcg/complex/algorithms/clean.h>
#include <vcg/complex/complex.h>
#include <vcg/space/index/grid_util.h>
#include <vcg/space/triangle3.h>
#include <iostream>
#include <math.h>
#include <unordered_map>
#include <unordered_set>
namespace std {
template<>
struct hash<vcg::Point3i>
{
typedef vcg::Point3i argument_type;
std::size_t operator()(const vcg::Point3i& s) const
{
return std::hash<int>()(s[0]) ^ std::hash<int>()(s[1]) ^ std::hash<int>()(s[2]);
}
};
} // namespace std
namespace vcg {
namespace tri {
template<class MeshType>
class NearestToCenter
{
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::FaceType FaceType;
typedef BasicGrid<typename MeshType::ScalarType> GridType;
public:
inline void AddVertex(MeshType& /*m*/, GridType& g, Point3i& pi, VertexType& v)
{
CoordType c;
g.IPiToBoxCenter(pi, c);
ScalarType newDist = Distance(c, v.cP());
if (!valid || newDist < bestDist) {
valid = true;
bestDist = newDist;
bestPos = v.cP();
bestN = v.cN();
orig = &v;
}
}
inline void AddFaceVertex(MeshType& /*m*/, FaceType& /*f*/, int /*i*/) { assert(0); }
NearestToCenter() : valid(false) {}
CoordType bestPos;
CoordType bestN;
ScalarType bestDist;
bool valid;
int id;
VertexType* orig;
CoordType Pos() const
{
assert(valid);
return bestPos;
}
Color4b Col() const { return Color4b::White; }
CoordType N() const { return bestN; }
VertexType* Ptr() const { return orig; }
};
template<class MeshType>
class AverageColorCell
{
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::VertexType VertexType;
typedef BasicGrid<typename MeshType::ScalarType> GridType;
public:
inline void AddFaceVertex(const MeshType&, const FaceType& f, int i)
{
p += f.cV(i)->cP();
c += CoordType(f.cV(i)->C()[0], f.cV(i)->C()[1], f.cV(i)->C()[2]);
// we prefer to use the un-normalized face normal so small faces facing away are dropped out
// and the resulting average is weighed with the size of the faces falling here.
n += f.cN();
cnt++;
}
inline void AddVertex(const MeshType& m, GridType& /*g*/, Point3i& /*pi*/, const VertexType& v)
{
p += v.cP();
n += v.cN();
if (tri::HasPerVertexColor(m))
c += CoordType(v.C()[0], v.C()[1], v.C()[2]);
cnt++;
}
AverageColorCell() : p(0, 0, 0), n(0, 0, 0), c(0, 0, 0), cnt(0) {}
CoordType p;
CoordType n;
CoordType c;
int cnt;
int id;
Color4b Col() const { return Color4b(c[0] / cnt, c[1] / cnt, c[2] / cnt, 255); }
CoordType N() const { return n; }
VertexType* Ptr() const { return 0; }
CoordType Pos() const { return p / cnt; }
};
/*
Metodo di clustering
*/
template<class MeshType, class CellType>
class Clustering
{
public:
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FaceIterator FaceIterator;
// The constructor takes three parameters
// _mbb the bounding box of the grid
// _size is the approximate total number of cells composing the grid surrounding the objects
// (usually a large number)
// eg _size==1.000.000 means a 100x100x100 grid
// _cellsize is the absolute length of the edge of the grid cell.
// eg _cellsize==2.0 means that all the vertexes in a 2.0x2.0x2.0 cell are clustered
// togheter
// _dupFaceParam: means that during the clustering doublesided surface (like a thin
// shell) that would be clustered to a single surface will be merged into two identical but
// opposite faces. So in practice: DuplicateFace=true a model with looks ok if you enable
// backface culling DuplicateFace=false a model with looks ok if you enable doublesided lighting
// and disable backfaceculling
// Notes:
// _size is used only if the cell edge IS zero.
// _cellsize gives you an absolute measure of the maximum error introduced
// during the simplification (e.g. half of the cell edge length)
Clustering(
Box3<ScalarType> _mbb,
int _size,
ScalarType _cellsize = 0,
bool _dupFaceParam = true) :
DuplicateFaceParam(_dupFaceParam)
{
Init(_mbb, _size, _cellsize);
};
Point3i gridSize() const
{
return Grid.siz;
}
Point3<ScalarType> gridVoxel() const
{
return Grid.voxel;
}
void AddPointSet(MeshType& m, bool UseOnlySelected = false)
{
for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
if (!(*vi).IsD())
if (!UseOnlySelected || (*vi).IsS()) {
Point3i pi;
Grid.PToIP((*vi).cP(), pi);
GridCell[pi].AddVertex(m, Grid, pi, *(vi));
}
}
void AddMesh(MeshType& m)
{
FaceIterator fi;
for (fi = m.face.begin(); fi != m.face.end(); ++fi) {
if (!(*fi).IsD()) {
Point3i pi;
SimpleTri st;
for (int i = 0; i < 3; ++i) {
Grid.PToIP((*fi).cV(i)->cP(), pi);
st.v[i] = &(GridCell[pi]);
st.v[i]->AddFaceVertex(m, *(fi), i);
}
if ((st.v[0] != st.v[1]) && (st.v[0] != st.v[2]) &&
(st.v[1] !=
st.v[2])) { // if we allow the duplication of faces we sort the vertex only
// partially (to maintain the original face orientation)
if (DuplicateFaceParam)
st.sortOrient();
else
st.sort();
TriSet.insert(st);
}
// printf("Inserted %8i triangles, clustered to %8i tri and %i
// cells\n",distance(m.face.begin(),fi),TriSet.size(),GridCell.size());
}
}
}
int CountPointSet() { return GridCell.size(); }
void SelectPointSet(MeshType& m)
{
typename std::unordered_map<Point3i, CellType>::iterator gi;
UpdateSelection<MeshType>::VertexClear(m);
for (gi = GridCell.begin(); gi != GridCell.end(); ++gi) {
VertexType* ptr = (*gi).second.Ptr();
if (ptr && (ptr >= &*m.vert.begin()) && (ptr <= &*(m.vert.end() - 1)))
ptr->SetS();
}
}
void ExtractPointSet(MeshType& m)
{
m.Clear();
if (GridCell.empty())
return;
Allocator<MeshType>::AddVertices(m, GridCell.size());
typename std::unordered_map<Point3i, CellType>::iterator gi;
int i = 0;
for (gi = GridCell.begin(); gi != GridCell.end(); ++gi) {
m.vert[i].P() = (*gi).second.Pos();
m.vert[i].N() = (*gi).second.N();
if (HasPerVertexColor(m))
m.vert[i].C() = (*gi).second.Col();
++i;
}
}
void ExtractMesh(MeshType& m)
{
m.Clear();
if (GridCell.empty())
return;
Allocator<MeshType>::AddVertices(m, GridCell.size());
typename std::unordered_map<Point3i, CellType>::iterator gi;
int i = 0;
for (gi = GridCell.begin(); gi != GridCell.end(); ++gi) {
m.vert[i].P() = (*gi).second.Pos();
m.vert[i].N() = (*gi).second.N();
if (HasPerVertexColor(m))
m.vert[i].C() = (*gi).second.Col();
(*gi).second.id = i;
++i;
}
Allocator<MeshType>::AddFaces(m, TriSet.size());
TriHashSetIterator ti;
i = 0;
for (ti = TriSet.begin(); ti != TriSet.end(); ++ti) {
m.face[i].V(0) = &(m.vert[(*ti).v[0]->id]);
m.face[i].V(1) = &(m.vert[(*ti).v[1]->id]);
m.face[i].V(2) = &(m.vert[(*ti).v[2]->id]);
// if we are merging faces even when opposite we choose
// the best orientation according to the averaged normal
if (!DuplicateFaceParam) {
CoordType N = TriangleNormal(m.face[i]);
int badOrient = 0;
if (N.dot((*ti).v[0]->N()) < 0)
++badOrient;
if (N.dot((*ti).v[1]->N()) < 0)
++badOrient;
if (N.dot((*ti).v[2]->N()) < 0)
++badOrient;
if (badOrient > 2)
std::swap(m.face[i].V(0), m.face[i].V(1));
}
i++;
}
}
private:
// This class keeps the references to the three cells where a face has its vertexes.
class SimpleTri
{
public:
CellType* v[3];
int ii(int i) const { return *((int*) (&(v[i]))); }
bool operator<(const SimpleTri& p) const
{
return (v[2] != p.v[2]) ? (v[2] < p.v[2]) :
(v[1] != p.v[1]) ? (v[1] < p.v[1]) :
(v[0] < p.v[0]);
}
// Sort the vertex of the face maintaining the original face orientation (it only ensure
// that v0 is the minimum)
void sortOrient()
{
if (v[1] < v[0] && v[1] < v[2]) {
std::swap(v[0], v[1]);
std::swap(v[1], v[2]);
return;
} // v1 was the minimum
if (v[2] < v[0] && v[2] < v[1]) {
std::swap(v[0], v[2]);
std::swap(v[1], v[2]);
return;
} // v2 was the minimum
return; // v0 was the minimum;
}
void sort()
{
if (v[0] > v[1])
std::swap(v[0], v[1]); // now v0 < v1
if (v[0] > v[2])
std::swap(v[0], v[2]); // now v0 is the minimum
if (v[1] > v[2])
std::swap(v[1], v[2]); // sorted!
}
bool operator==(const SimpleTri& pt) const
{
return (pt.v[0] == v[0]) && (pt.v[1] == v[1]) && (pt.v[2] == v[2]);
}
// Hashing Function;
size_t operator()(const SimpleTri& pt) const
{
// return (ii(0)*HASH_P0 ^ ii(1)*HASH_P1 ^ ii(2)*HASH_P2);
return std::hash<CellType*>()(pt.v[0]) ^ std::hash<CellType*>()(pt.v[1]) ^
std::hash<CellType*>()(pt.v[2]);
}
};
// DuplicateFace == bool means that during the clustering doublesided surface (like a thin
// shell) that would be clustered to a single surface will be merged into two identical but
// opposite faces. So in practice: DuplicateFace=true a model with looks ok if you enable
// backface culling DuplicateFace=false a model with looks ok if you enable doublesided lighting
// and disable backfaceculling
bool DuplicateFaceParam = true;
BasicGrid<ScalarType> Grid;
std::unordered_set<SimpleTri, SimpleTri> TriSet;
typedef typename std::unordered_set<SimpleTri, SimpleTri>::iterator TriHashSetIterator;
std::unordered_map<Point3i, CellType> GridCell;
// The init function Take two parameters
// _size is the approximate total number of cells composing the grid surrounding the objects
// (usually a large number)
// eg _size==1.000.000 means a 100x100x100 grid
// _cellsize is the absolute length of the edge of the grid cell.
// eg _cellsize==2.0 means that all the vertexes in a 2.0x2.0x2.0 cell are clustered
// togheter
// Notes:
// _size is used only if the cell edge IS zero.
// _cellsize gives you an absolute measure of the maximum error introduced
// during the simplification (e.g. half of the cell edge length)
void Init(Box3<ScalarType> _mbb, int _size, ScalarType _cellsize = 0)
{
GridCell.clear();
TriSet.clear();
Grid.bbox = _mbb;
/// inflate the bb calculated
ScalarType infl = (_cellsize == (ScalarType) 0) ? (Grid.bbox.Diag() / _size) : (_cellsize);
Grid.bbox.min -= CoordType(infl, infl, infl);
Grid.bbox.max += CoordType(infl, infl, infl);
Grid.dim = Grid.bbox.max - Grid.bbox.min;
if (_cellsize == 0)
BestDim(_size, Grid.dim, Grid.siz);
else
Grid.siz = Point3i::Construct(Grid.dim / _cellsize);
// find voxel size
Grid.voxel[0] = Grid.dim[0] / Grid.siz[0];
Grid.voxel[1] = Grid.dim[1] / Grid.siz[1];
Grid.voxel[2] = Grid.dim[2] / Grid.siz[2];
}
}; // end class clustering
} // namespace tri
} // namespace vcg
#endif