mirror of https://github.com/vitalif/openscad
Reinstate Grid to fix problems introduced due to floating point inaccuracy. Grid does a certain job at vertex melding across objects and also help keeping plans planar
parent
3288447e70
commit
548b9c7c93
127
src/cgalutils.cc
127
src/cgalutils.cc
|
@ -772,6 +772,96 @@ public:
|
||||||
const PolySet &ps;
|
const PolySet &ps;
|
||||||
CGAL_Build_PolySet(const PolySet &ps) : ps(ps) { }
|
CGAL_Build_PolySet(const PolySet &ps) : ps(ps) { }
|
||||||
|
|
||||||
|
/*
|
||||||
|
Using Grid here is important for performance reasons. See following model.
|
||||||
|
If we don't grid the geometry before converting to a Nef Polyhedron, the quads
|
||||||
|
in the cylinders to tessellated into triangles since floating point
|
||||||
|
incertainty causes the faces to not be 100% planar. The incertainty is exaggerated
|
||||||
|
by the transform. This wasn't a problem earlier since we used Nef for everything,
|
||||||
|
but optimizations since then has made us keep it in floating point space longer.
|
||||||
|
|
||||||
|
minkowski() {
|
||||||
|
cube([200, 50, 7], center = true);
|
||||||
|
rotate([90,0,0]) cylinder($fn = 8, h = 1, r = 8.36, center = true);
|
||||||
|
rotate([0,90,0]) cylinder($fn = 8, h = 1, r = 8.36, center = true);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#if 1 // Use Grid
|
||||||
|
void operator()(CGAL_HDS& hds) {
|
||||||
|
CGAL_Polybuilder B(hds, true);
|
||||||
|
|
||||||
|
std::vector<CGALPoint> vertices;
|
||||||
|
Grid3d<int> grid(GRID_FINE);
|
||||||
|
std::vector<size_t> indices(3);
|
||||||
|
|
||||||
|
BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) {
|
||||||
|
BOOST_REVERSE_FOREACH(Vector3d v, p) {
|
||||||
|
if (!grid.has(v[0], v[1], v[2])) {
|
||||||
|
// align v to the grid; the CGALPoint will receive the aligned vertex
|
||||||
|
grid.align(v[0], v[1], v[2]) = vertices.size();
|
||||||
|
vertices.push_back(CGALPoint(v[0], v[1], v[2]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("polyhedron(faces=[");
|
||||||
|
int pidx = 0;
|
||||||
|
#endif
|
||||||
|
B.begin_surface(vertices.size(), ps.polygons.size());
|
||||||
|
BOOST_FOREACH(const CGALPoint &p, vertices) {
|
||||||
|
B.add_vertex(p);
|
||||||
|
}
|
||||||
|
BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (pidx++ > 0) printf(",");
|
||||||
|
#endif
|
||||||
|
indices.clear();
|
||||||
|
BOOST_FOREACH(const Vector3d &v, p) {
|
||||||
|
indices.push_back(grid.data(v[0], v[1], v[2]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// We perform this test since there is a bug in CGAL's
|
||||||
|
// Polyhedron_incremental_builder_3::test_facet() which
|
||||||
|
// fails to detect duplicate indices
|
||||||
|
bool err = false;
|
||||||
|
for (std::size_t i = 0; i < indices.size(); ++i) {
|
||||||
|
// check if vertex indices[i] is already in the sequence [0..i-1]
|
||||||
|
for (std::size_t k = 0; k < i && !err; ++k) {
|
||||||
|
if (indices[k] == indices[i]) {
|
||||||
|
err = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!err && B.test_facet(indices.begin(), indices.end())) {
|
||||||
|
B.add_facet(indices.begin(), indices.end());
|
||||||
|
}
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("[");
|
||||||
|
int fidx = 0;
|
||||||
|
BOOST_FOREACH(size_t i, indices) {
|
||||||
|
if (fidx++ > 0) printf(",");
|
||||||
|
printf("%ld", i);
|
||||||
|
}
|
||||||
|
printf("]");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
B.end_surface();
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("],\n");
|
||||||
|
#endif
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("points=[");
|
||||||
|
for (int i=0;i<vertices.size();i++) {
|
||||||
|
if (i > 0) printf(",");
|
||||||
|
const CGALPoint &p = vertices[i];
|
||||||
|
printf("[%g,%g,%g]", CGAL::to_double(p.x()), CGAL::to_double(p.y()), CGAL::to_double(p.z()));
|
||||||
|
}
|
||||||
|
printf("]);\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#else // Don't use Grid
|
||||||
void operator()(CGAL_HDS& hds)
|
void operator()(CGAL_HDS& hds)
|
||||||
{
|
{
|
||||||
CGAL_Polybuilder B(hds, true);
|
CGAL_Polybuilder B(hds, true);
|
||||||
|
@ -790,36 +880,37 @@ public:
|
||||||
if (pidx++ > 0) printf(",");
|
if (pidx++ > 0) printf(",");
|
||||||
#endif
|
#endif
|
||||||
indices.clear();
|
indices.clear();
|
||||||
BOOST_FOREACH(const Vector3d &v, p) {
|
BOOST_REVERSE_FOREACH(const Vector3d &v, p) {
|
||||||
size_t s = vertices.size();
|
size_t s = vertices.size();
|
||||||
size_t idx = vertices.lookup(v);
|
size_t idx = vertices.lookup(v);
|
||||||
// If we added a vertex, also add it to the CGAL builder
|
// If we added a vertex, also add it to the CGAL builder
|
||||||
if (idx == s) B.add_vertex(CGALPoint(v[0], v[1], v[2]));
|
if (idx == s) B.add_vertex(CGALPoint(v[0], v[1], v[2]));
|
||||||
indices.push_back(idx);
|
indices.push_back(idx);
|
||||||
}
|
}
|
||||||
std::map<size_t,int> fc;
|
// We perform this test since there is a bug in CGAL's
|
||||||
bool facet_is_degenerate = false;
|
// Polyhedron_incremental_builder_3::test_facet() which
|
||||||
BOOST_REVERSE_FOREACH(size_t i, indices) {
|
// fails to detect duplicate indices
|
||||||
if (fc[i]++ > 0) facet_is_degenerate = true;
|
bool err = false;
|
||||||
|
for (std::size_t i = 0; i < indices.size(); ++i) {
|
||||||
|
// check if vertex indices[i] is already in the sequence [0..i-1]
|
||||||
|
for (std::size_t k = 0; k < i && !err; ++k) {
|
||||||
|
if (indices[k] == indices[i]) {
|
||||||
|
err = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (!facet_is_degenerate) {
|
}
|
||||||
B.begin_facet();
|
}
|
||||||
|
if (!err && B.test_facet(indices.begin(), indices.end())) {
|
||||||
|
B.add_facet(indices.begin(), indices.end());
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printf("[");
|
printf("[");
|
||||||
#endif
|
|
||||||
int fidx = 0;
|
int fidx = 0;
|
||||||
std::map<int,int> fc;
|
BOOST_FOREACH(size_t i, indices) {
|
||||||
BOOST_REVERSE_FOREACH(size_t i, indices) {
|
|
||||||
B.add_vertex_to_facet(i);
|
|
||||||
#ifdef DEBUG
|
|
||||||
if (fidx++ > 0) printf(",");
|
if (fidx++ > 0) printf(",");
|
||||||
printf("%ld", i);
|
printf("%ld", i);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#ifdef DEBUG
|
|
||||||
printf("]");
|
printf("]");
|
||||||
#endif
|
#endif
|
||||||
B.end_facet();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
B.end_surface();
|
B.end_surface();
|
||||||
|
@ -835,6 +926,7 @@ public:
|
||||||
printf("]);\n");
|
printf("]);\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
bool createPolyhedronFromPolySet(const PolySet &ps, CGAL_Polyhedron &p)
|
bool createPolyhedronFromPolySet(const PolySet &ps, CGAL_Polyhedron &p)
|
||||||
|
@ -916,6 +1008,11 @@ static CGAL_Nef_polyhedron *createNefPolyhedronFromPolySet(const PolySet &ps)
|
||||||
try {
|
try {
|
||||||
CGAL_Polyhedron P;
|
CGAL_Polyhedron P;
|
||||||
bool err = createPolyhedronFromPolySet(ps, P);
|
bool err = createPolyhedronFromPolySet(ps, P);
|
||||||
|
// if (!err) {
|
||||||
|
// PRINTB("Polyhedron is closed: %d", P.is_closed());
|
||||||
|
// PRINTB("Polyhedron is valid: %d", P.is_valid(true, 0));
|
||||||
|
// }
|
||||||
|
|
||||||
if (!err) N = new CGAL_Nef_polyhedron3(P);
|
if (!err) N = new CGAL_Nef_polyhedron3(P);
|
||||||
}
|
}
|
||||||
catch (const CGAL::Assertion_exception &e) {
|
catch (const CGAL::Assertion_exception &e) {
|
||||||
|
|
|
@ -50,6 +50,7 @@ public:
|
||||||
x = ix * res, y = iy * res;
|
x = ix * res, y = iy * res;
|
||||||
return db[std::make_pair(ix, iy)];
|
return db[std::make_pair(ix, iy)];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has(double x, double y) const {
|
bool has(double x, double y) const {
|
||||||
int64_t ix = (int64_t)round(x / res);
|
int64_t ix = (int64_t)round(x / res);
|
||||||
int64_t iy = (int64_t)round(y / res);
|
int64_t iy = (int64_t)round(y / res);
|
||||||
|
@ -62,6 +63,7 @@ public:
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool eq(double x1, double y1, double x2, double y2) {
|
bool eq(double x1, double y1, double x2, double y2) {
|
||||||
align(x1, y1);
|
align(x1, y1);
|
||||||
align(x2, y2);
|
align(x2, y2);
|
||||||
|
@ -87,6 +89,7 @@ public:
|
||||||
Grid3d(double resolution) {
|
Grid3d(double resolution) {
|
||||||
res = resolution;
|
res = resolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
T &align(double &x, double &y, double &z) {
|
T &align(double &x, double &y, double &z) {
|
||||||
int64_t ix = (int64_t)round(x / res);
|
int64_t ix = (int64_t)round(x / res);
|
||||||
int64_t iy = (int64_t)round(y / res);
|
int64_t iy = (int64_t)round(y / res);
|
||||||
|
@ -112,6 +115,7 @@ public:
|
||||||
x = ix * res, y = iy * res, z = iz * res;
|
x = ix * res, y = iy * res, z = iz * res;
|
||||||
return db[std::make_pair(std::make_pair(ix, iy), iz)];
|
return db[std::make_pair(std::make_pair(ix, iy), iz)];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has(double x, double y, double z) {
|
bool has(double x, double y, double z) {
|
||||||
int64_t ix = (int64_t)round(x / res);
|
int64_t ix = (int64_t)round(x / res);
|
||||||
int64_t iy = (int64_t)round(y / res);
|
int64_t iy = (int64_t)round(y / res);
|
||||||
|
@ -125,8 +129,8 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool eq(double x1, double y1, double z1, double x2, double y2, double z2) {
|
bool eq(double x1, double y1, double z1, double x2, double y2, double z2) {
|
||||||
align(x1, y1, z1);
|
align(x1, y1, z1);
|
||||||
align(x2, y2, z2);
|
align(x2, y2, z2);
|
||||||
|
@ -134,9 +138,11 @@ public:
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
T &data(double x, double y, double z) {
|
T &data(double x, double y, double z) {
|
||||||
return align(x, y, z);
|
return align(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
T &operator()(double x, double y, double z) {
|
T &operator()(double x, double y, double z) {
|
||||||
return align(x, y, z);
|
return align(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue