mirror of https://github.com/vitalif/openscad
Fixed buggy vertex quantizing and polygon tessellation. Should improve some corner cases
parent
2fb01e5f83
commit
32b2d2ec97
|
@ -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 \
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
87
src/grid.h
87
src/grid.h
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue