Fixed buggy vertex quantizing and polygon tessellation. Should improve some corner cases

master
Marius Kintel 2015-01-05 18:22:58 -05:00
parent 2fb01e5f83
commit 32b2d2ec97
7 changed files with 125 additions and 64 deletions

View File

@ -410,6 +410,7 @@ SOURCES += src/version_check.cc \
src/QGLView.cc \
src/AutoUpdater.cc \
\
src/grid.cc \
src/builtin.cc \
src/calc.cc \
src/export.cc \

View File

@ -48,17 +48,22 @@ namespace /* anonymous */ {
void operator()(HDS& hds) {
CGAL_Polybuilder B(hds, true);
std::vector<CGALPoint> vertices;
Grid3d<int> grid(GRID_FINE);
std::vector<size_t> indices(3);
std::vector<CGALPoint> vertices;
std::vector<std::vector<size_t> > indices;
// Align all vertices to grid and build vertex array in vertices
BOOST_FOREACH(const Polygon &p, ps.polygons) {
indices.push_back(std::vector<size_t>());
indices.back().reserve(p.size());
BOOST_REVERSE_FOREACH(Vector3d v, p) {
if (!grid.has(v)) {
// align v to the grid; the CGALPoint will receive the aligned vertex
grid.align(v) = vertices.size();
vertices.push_back(CGALPoint(v[0], v[1], v[2]));
// align v to the grid; the CGALPoint will receive the aligned vertex
size_t idx = grid.align(v);
if (idx == vertices.size()) {
CGALPoint p(v[0], v[1], v[2]);
vertices.push_back(p);
}
indices.back().push_back(idx);
}
}
@ -70,28 +75,24 @@ namespace /* anonymous */ {
BOOST_FOREACH(const CGALPoint &p, vertices) {
B.add_vertex(p);
}
BOOST_FOREACH(const Polygon &p, ps.polygons) {
BOOST_FOREACH(std::vector<size_t> &pindices, indices) {
#ifdef GEN_SURFACE_DEBUG
if (pidx++ > 0) printf(",");
#endif
indices.clear();
BOOST_FOREACH(const Vector3d &v, p) {
indices.push_back(grid.data(v));
}
// We remove duplicate indices since there is a bug in CGAL's
// Polyhedron_incremental_builder_3::test_facet() which fails to detect this
std::vector<size_t>::iterator last = std::unique(indices.begin(), indices.end());
std::vector<size_t>::iterator last = std::unique(pindices.begin(), pindices.end());
std::advance(last, -1);
if (*last != indices.front()) last++; // In case the first & last are equal
indices.erase(last, indices.end());
if (indices.size() >= 3 && B.test_facet(indices.begin(), indices.end())) {
B.add_facet(indices.begin(), indices.end());
if (*last != pindices.front()) last++; // In case the first & last are equal
pindices.erase(last, pindices.end());
if (pindices.size() >=3 && B.test_facet(pindices.begin(), pindices.end())) {
B.add_facet(pindices.begin(), pindices.end());
}
#ifdef GEN_SURFACE_DEBUG
printf("[");
int fidx = 0;
BOOST_REVERSE_FOREACH(size_t i, indices) {
BOOST_REVERSE_FOREACH(size_t i, pindices) {
if (fidx++ > 0) printf(",");
printf("%ld", i);
}

View File

@ -21,14 +21,6 @@
#include <boost/foreach.hpp>
#include <boost/unordered_set.hpp>
namespace Eigen {
size_t hash_value(Vector3d const &v) {
size_t seed = 0;
for (int i=0;i<3;i++) boost::hash_combine(seed, v[i]);
return seed;
}
}
namespace /* anonymous */ {
template<typename Result, typename V>
Result vector_convert(V const& v) {
@ -50,10 +42,11 @@ static CGAL_Nef_polyhedron *createNefPolyhedronFromPolySet(const PolySet &ps)
if (ps_tri.is_convex()) {
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
// Collect point cloud
// FIXME: Use unordered container (need hash)
std::set<K::Point_3> points;
for (int i = 0; i < ps.polygons.size(); i++) {
for (int j = 0; j < ps.polygons[i].size(); j++) {
points.insert(vector_convert<K::Point_3>(ps.polygons[i][j]));
for (int i = 0; i < psq.polygons.size(); i++) {
for (int j = 0; j < psq.polygons[i].size(); j++) {
points.insert(vector_convert<K::Point_3>(psq.polygons[i][j]));
}
}
@ -72,7 +65,7 @@ static CGAL_Nef_polyhedron *createNefPolyhedronFromPolySet(const PolySet &ps)
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
CGAL_Polyhedron P;
bool err = CGALUtils::createPolyhedronFromPolySet(ps, P);
bool err = CGALUtils::createPolyhedronFromPolySet(psq, P);
if (!err) {
PRINTDB("Polyhedron is closed: %d", P.is_closed());
PRINTDB("Polyhedron is valid: %d", P.is_valid(false, 0));
@ -463,7 +456,7 @@ namespace CGALUtils {
}
// union && difference assert triggered by testdata/scad/bugs/rotate-diff-nonmanifold-crash.scad and testdata/scad/bugs/issue204.scad
catch (const CGAL::Failure_exception &e) {
std::string opstr = op == OPENSCAD_INTERSECTION ? "intersection" : op == OPENSCAD_DIFFERENCE ? "difference" : op == OPENSCAD_MINKOWSKI ? "minkowski" : "UNKNOWN";
std::string opstr = op == OPENSCAD_INTERSECTION ? "intersection" : op == OPENSCAD_DIFFERENCE ? "difference" : op == OPENSCAD_UNION ? "union" : "UNKNOWN";
PRINTB("ERROR: CGAL error in CGALUtils::applyBinaryOperator %s: %s", opstr % e.what());
}
CGAL::set_error_behaviour(old_behaviour);

14
src/grid.cc Normal file
View File

@ -0,0 +1,14 @@
#include "grid.h"
namespace Eigen {
size_t hash_value(Vector3d const &v) {
size_t seed = 0;
for (int i=0;i<3;i++) boost::hash_combine(seed, v[i]);
return seed;
}
size_t hash_value(Eigen::Matrix<int64_t, 3, 1> const &v) {
size_t seed = 0;
for (int i=0;i<3;i++) boost::hash_combine(seed, v[i]);
return seed;
}
}

View File

@ -87,59 +87,88 @@ public:
}
};
typedef Eigen::Matrix<int64_t, 3, 1> Vector3l;
namespace Eigen {
size_t hash_value(Vector3d const &v);
size_t hash_value(Vector3l const &v);
}
template <typename T>
class Grid3d
{
public:
double res;
boost::unordered_map<std::pair<std::pair<int64_t,int64_t>,int64_t>, T> db;
typedef Vector3l Key;
typedef boost::unordered_map<Key, T> GridContainer;
GridContainer db;
Grid3d(double resolution) {
res = resolution;
}
T &align(Vector3d &v) {
int64_t ix = (int64_t)round(v[0] / res);
int64_t iy = (int64_t)round(v[1] / res);
int64_t iz = (int64_t)round(v[2] / res);
if (db.find(std::make_pair(std::make_pair(ix, iy), iz)) == db.end()) {
int dist = 10;
for (int64_t jx = ix - 1; jx <= ix + 1; jx++) {
for (int64_t jy = iy - 1; jy <= iy + 1; jy++) {
for (int64_t jz = iz - 1; jz <= iz + 1; jz++) {
if (db.find(std::make_pair(std::make_pair(jx, jy), jz)) == db.end())
continue;
int d = abs(int(ix-jx)) + abs(int(iy-jy)) + abs(int(iz-jz));
inline void createGridVertex(const Vector3d &v, Vector3l &i) {
i[0] = int64_t(v[0] / this->res);
i[1] = int64_t(v[1] / this->res);
i[2] = int64_t(v[2] / this->res);
}
// Aligns vertex to the grid. Returns index of the vertex.
// Will automatically increase the index as new unique vertices are added.
T align(Vector3d &v) {
Vector3l key;
createGridVertex(v, key);
typename GridContainer::iterator iter = db.find(key);
if (iter == db.end()) {
int dist = 10; // > max possible distance
for (int64_t jx = key[0] - 1; jx <= key[0] + 1; jx++) {
for (int64_t jy = key[1] - 1; jy <= key[1] + 1; jy++) {
for (int64_t jz = key[2] - 1; jz <= key[2] + 1; jz++) {
Vector3l k(jx, jy, jz);
typename GridContainer::iterator tmpiter = db.find(k);
if (tmpiter == db.end()) continue;
int d = (key-k).norm();
if (d < dist) {
dist = d;
ix = jx;
iy = jy;
iz = jz;
iter = tmpiter;
}
}
}
}
}
v[0] = ix * res, v[1] = iy * res, v[2] = iz * res;
return db[std::make_pair(std::make_pair(ix, iy), iz)];
if (iter == db.end()) { // Not found: insert using key
T &data = db[key] = db.size();
return data;
}
// If found, align vertex and return existing data
key = iter->first;
v[0] = key[0] * this->res;
v[1] = key[1] * this->res;
v[2] = key[2] * this->res;
return iter->second;
}
bool has(const Vector3d &v) {
int64_t ix = (int64_t)round(v[0] / res);
int64_t iy = (int64_t)round(v[1] / res);
int64_t iz = (int64_t)round(v[2] / res);
if (db.find(std::make_pair(std::make_pair(ix, iy), iz)) != db.end())
bool has(const Vector3d &v, T *data = NULL) {
Vector3l key = createGridVertex(v);
typename GridContainer::iterator pos = db.find(key);
if (pos != db.end()) {
if (data) *data = pos->second;
return true;
for (int64_t jx = ix - 1; jx <= ix + 1; jx++)
for (int64_t jy = iy - 1; jy <= iy + 1; jy++)
for (int64_t jz = iz - 1; jz <= iz + 1; jz++) {
if (db.find(std::make_pair(std::make_pair(jx, jy), jz)) != db.end())
return true;
}
for (int64_t jx = key[0] - 1; jx <= key[0] + 1; jx++)
for (int64_t jy = key[1] - 1; jy <= key[1] + 1; jy++)
for (int64_t jz = key[2] - 1; jz <= key[2] + 1; jz++) {
pos = db.find(Vector3l(jx, jy, jz));
if (pos != db.end()) {
if (data) *data = pos->second;
return true;
}
}
return false;
}
T &data(Vector3d v) {
T data(Vector3d v) {
return align(v);
}

View File

@ -176,13 +176,35 @@ void PolySet::resize(Vector3d newsize, const Eigen::Matrix<bool,3,1> &autosize)
this->transform(t);
}
/*!
Quantizes vertices by gridding them as well as merges close vertices belonging to
neighboring grids.
May reduce the number of polygons if polygons collapse into < 3 vertices.
*/
void PolySet::quantizeVertices()
{
Grid3d<int> grid(GRID_FINE);
BOOST_FOREACH(Polygon &p, this->polygons) {
BOOST_FOREACH(Vector3d &v, p) {
// align v to the grid
if (!grid.has(v)) grid.align(v);
int numverts = 0;
std::vector<int> indices; // Vertex indices in one polygon
for (std::vector<Polygon>::iterator iter = this->polygons.begin(); iter != this->polygons.end();) {
Polygon &p = *iter;
indices.resize(p.size());
// Quantize all vertices. Build index list
for (int i=0;i<p.size();i++) indices[i] = grid.align(p[i]);
// Remove consequtive duplicate vertices
Polygon::iterator currp = p.begin();
for (int i=0;i<indices.size();i++) {
if (indices[i] != indices[(i+1)%indices.size()]) {
(*currp++) = p[i];
}
}
p.erase(currp, p.end());
if (p.size() < 3) {
PRINTD("Removing collapsed polygon due to quantizing");
this->polygons.erase(iter);
}
else {
iter++;
}
}
}

View File

@ -617,6 +617,7 @@ set(CORE_SOURCES
../src/handle_dep.cc
../src/value.cc
../src/calc.cc
../src/grid.cc
../src/expr.cc
../src/func.cc
../src/stackcheck.cc