diff options
author | Marius Kintel <marius@kintel.net> | 2013-01-08 18:26:25 (GMT) |
---|---|---|
committer | Marius Kintel <marius@kintel.net> | 2013-01-08 18:26:25 (GMT) |
commit | 810f1a86189555023240507848ff9eebb161b8de (patch) | |
tree | 287007ba314d94a551d2ef721dfc796598fede6c | |
parent | 8cea6834f68cbbfb85c2568d388bf3b2e707cca5 (diff) |
Don't just ignore geometric nodes having zero volume/area - when doing difference/intersection, they tend to turn negative objects into positive ones. Fixes #221
-rw-r--r-- | src/CGALEvaluator.cc | 98 | ||||
-rw-r--r-- | src/CGALRenderer.cc | 11 | ||||
-rw-r--r-- | src/CGAL_Nef_polyhedron.cc | 5 | ||||
-rw-r--r-- | src/CGAL_Nef_polyhedron.h | 7 | ||||
-rw-r--r-- | src/PolySetCGALEvaluator.cc | 28 | ||||
-rw-r--r-- | src/mainwin.cc | 48 | ||||
-rw-r--r-- | testdata/scad/features/difference-tests.scad | 1 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/cgalpngtest.cc | 16 | ||||
-rw-r--r-- | tests/cgalstlsanitytest.cc | 2 | ||||
-rw-r--r-- | tests/cgaltest.cc | 2 |
11 files changed, 113 insertions, 106 deletions
diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc index a4744c2..4deb3b3 100644 --- a/src/CGALEvaluator.cc +++ b/src/CGALEvaluator.cc @@ -61,14 +61,16 @@ void CGALEvaluator::process(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedr if (target.dim != 2 && target.dim != 3) { assert(false && "Dimension of Nef polyhedron must be 2 or 3"); } - if (src.empty()) return; // Empty polyhedron. This can happen for e.g. square([0,0]) + if (src.isEmpty()) return; // Empty polyhedron. This can happen for e.g. square([0,0]) + if (target.isEmpty() && op != CGE_UNION) return; // empty op <something> => empty if (target.dim != src.dim) return; // If someone tries to e.g. union 2d and 3d objects CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); try { switch (op) { case CGE_UNION: - target += src; + if (target.isEmpty()) target = src.copy(); + else target += src; break; case CGE_INTERSECTION: target *= src; @@ -110,7 +112,8 @@ CGAL_Nef_polyhedron CGALEvaluator::applyToChildren(const AbstractNode &node, CGA if (!isCached(*chnode)) { CGALCache::instance()->insert(this->tree.getIdString(*chnode), chN); } - if (N.empty()) N = chN.copy(); + // Initialize N on first iteration with first expected geometric object + if (N.isNull() && !N.isEmpty()) N = chN.copy(); else process(N, chN, op); chnode->progress_report(); @@ -245,7 +248,6 @@ Response CGALEvaluator::visit(State &state, const TransformNode &node) if (!isCached(node)) { // First union all children N = applyToChildren(node, CGE_UNION); - if ( matrix_contains_infinity( node.matrix ) || matrix_contains_nan( node.matrix ) ) { // due to the way parse/eval works we can't currently distinguish between NaN and Inf PRINT("Warning: Transformation matrix contains Not-a-Number and/or Infinity - removing object."); @@ -253,51 +255,53 @@ Response CGALEvaluator::visit(State &state, const TransformNode &node) } // Then apply transform - // If there is no geometry under the transform, N will be empty and of dim 0, - // just just silently ignore such nodes - if (N.dim == 2) { - // Unfortunately CGAL provides no transform method for CGAL_Nef_polyhedron2 - // objects. So we convert in to our internal 2d data format, transform it, - // tesselate it and create a new CGAL_Nef_polyhedron2 from it.. What a hack! - - Eigen::Matrix2f testmat; - testmat << node.matrix(0,0), node.matrix(0,1), node.matrix(1,0), node.matrix(1,1); - if (testmat.determinant() == 0) { - PRINT("Warning: Scaling a 2D object with 0 - removing object"); - N.reset(); - } - else { - CGAL_Aff_transformation2 t( - node.matrix(0,0), node.matrix(0,1), node.matrix(0,3), - node.matrix(1,0), node.matrix(1,1), node.matrix(1,3), node.matrix(3,3)); + // If there is no geometry under the transform, N will be empty + // just silently ignore such nodes + if (!N.isNull()) { + if (N.dim == 2) { + // Unfortunately CGAL provides no transform method for CGAL_Nef_polyhedron2 + // objects. So we convert in to our internal 2d data format, transform it, + // tesselate it and create a new CGAL_Nef_polyhedron2 from it.. What a hack! - DxfData *dd = N.convertToDxfData(); - for (size_t i=0; i < dd->points.size(); i++) { - CGAL_Kernel2::Point_2 p = CGAL_Kernel2::Point_2(dd->points[i][0], dd->points[i][1]); - p = t.transform(p); - dd->points[i][0] = to_double(p.x()); - dd->points[i][1] = to_double(p.y()); + Eigen::Matrix2f testmat; + testmat << node.matrix(0,0), node.matrix(0,1), node.matrix(1,0), node.matrix(1,1); + if (testmat.determinant() == 0) { + PRINT("Warning: Scaling a 2D object with 0 - removing object"); + N.reset(); + } + else { + CGAL_Aff_transformation2 t( + node.matrix(0,0), node.matrix(0,1), node.matrix(0,3), + node.matrix(1,0), node.matrix(1,1), node.matrix(1,3), node.matrix(3,3)); + + DxfData *dd = N.convertToDxfData(); + for (size_t i=0; i < dd->points.size(); i++) { + CGAL_Kernel2::Point_2 p = CGAL_Kernel2::Point_2(dd->points[i][0], dd->points[i][1]); + p = t.transform(p); + dd->points[i][0] = to_double(p.x()); + dd->points[i][1] = to_double(p.y()); + } + + PolySet ps; + ps.is2d = true; + dxf_tesselate(&ps, *dd, 0, true, false, 0); + + N = evaluateCGALMesh(ps); + delete dd; } - - PolySet ps; - ps.is2d = true; - dxf_tesselate(&ps, *dd, 0, true, false, 0); - - N = evaluateCGALMesh(ps); - delete dd; - } - } - else if (N.dim == 3) { - if (node.matrix.matrix().determinant() == 0) { - PRINT("Warning: Scaling a 3D object with 0 - removing object"); - N.reset(); } - else { - CGAL_Aff_transformation t( - node.matrix(0,0), node.matrix(0,1), node.matrix(0,2), node.matrix(0,3), - node.matrix(1,0), node.matrix(1,1), node.matrix(1,2), node.matrix(1,3), - node.matrix(2,0), node.matrix(2,1), node.matrix(2,2), node.matrix(2,3), node.matrix(3,3)); - N.p3->transform(t); + else if (N.dim == 3) { + if (node.matrix.matrix().determinant() == 0) { + PRINT("Warning: Scaling a 3D object with 0 - removing object"); + N.reset(); + } + else { + CGAL_Aff_transformation t( + node.matrix(0,0), node.matrix(0,1), node.matrix(0,2), node.matrix(0,3), + node.matrix(1,0), node.matrix(1,1), node.matrix(1,2), node.matrix(1,3), + node.matrix(2,0), node.matrix(2,1), node.matrix(2,2), node.matrix(2,3), node.matrix(3,3)); + N.p3->transform(t); + } } } } @@ -388,7 +392,7 @@ void CGALEvaluator::addToParent(const State &state, const AbstractNode &node, co CGAL_Nef_polyhedron CGALEvaluator::evaluateCGALMesh(const PolySet &ps) { - if (ps.empty()) return CGAL_Nef_polyhedron(); + if (ps.empty()) return CGAL_Nef_polyhedron(ps.is2d ? 2 : 3); if (ps.is2d) { diff --git a/src/CGALRenderer.cc b/src/CGALRenderer.cc index adf1217..4357e44 100644 --- a/src/CGALRenderer.cc +++ b/src/CGALRenderer.cc @@ -43,7 +43,11 @@ CGALRenderer::CGALRenderer(const CGAL_Nef_polyhedron &root) : root(root) { - if (root.dim == 2) { + if (this->root.isNull()) { + this->polyhedron = NULL; + this->polyset = NULL; + } + else if (root.dim == 2) { DxfData *dd = root.convertToDxfData(); this->polyhedron = NULL; this->polyset = new PolySet(); @@ -67,10 +71,6 @@ CGALRenderer::CGALRenderer(const CGAL_Nef_polyhedron &root) : root(root) CGAL::OGL::Nef3_Converter<CGAL_Nef_polyhedron3>::convert_to_OGLPolyhedron(*this->root.p3, this->polyhedron); this->polyhedron->init(); } - else { - this->polyhedron = NULL; - this->polyset = NULL; - } } CGALRenderer::~CGALRenderer() @@ -81,6 +81,7 @@ CGALRenderer::~CGALRenderer() void CGALRenderer::draw(bool showfaces, bool showedges) const { + if (this->root.isNull()) return; if (this->root.dim == 2) { // Draw 2D polygons glDisable(GL_LIGHTING); diff --git a/src/CGAL_Nef_polyhedron.cc b/src/CGAL_Nef_polyhedron.cc index ad2e4e2..8906595 100644 --- a/src/CGAL_Nef_polyhedron.cc +++ b/src/CGAL_Nef_polyhedron.cc @@ -61,7 +61,7 @@ CGAL_Nef_polyhedron &CGAL_Nef_polyhedron::minkowski(const CGAL_Nef_polyhedron &o int CGAL_Nef_polyhedron::weight() const { - if (this->empty()) return 0; + if (this->isNull()) return 0; size_t memsize = sizeof(CGAL_Nef_polyhedron); if (this->dim == 2) { @@ -84,8 +84,7 @@ int CGAL_Nef_polyhedron::weight() const */ PolySet *CGAL_Nef_polyhedron::convertToPolyset() { - if (this->empty()) - return new PolySet(); + if (this->isNull()) return new PolySet(); PolySet *ps = NULL; if (this->dim == 2) { ps = new PolySet(); diff --git a/src/CGAL_Nef_polyhedron.h b/src/CGAL_Nef_polyhedron.h index e8c505b..d949a2a 100644 --- a/src/CGAL_Nef_polyhedron.h +++ b/src/CGAL_Nef_polyhedron.h @@ -8,12 +8,15 @@ class CGAL_Nef_polyhedron { public: - CGAL_Nef_polyhedron() : dim(0) {} + CGAL_Nef_polyhedron(int dim = 0) : dim(dim) {} CGAL_Nef_polyhedron(CGAL_Nef_polyhedron2 *p); CGAL_Nef_polyhedron(CGAL_Nef_polyhedron3 *p); ~CGAL_Nef_polyhedron() {} - bool empty() const { return (dim == 0 || (!p2 && !p3)); } + // Empty means it is a geometric node which has zero area/volume + bool isEmpty() const { return (dim > 0 && !p2 && !p3); } + // Null means the node doesn't contain any geometry (for whatever reason) + bool isNull() const { return !p2 && !p3; } void reset() { dim=0; p2.reset(); p3.reset(); } CGAL_Nef_polyhedron &operator+=(const CGAL_Nef_polyhedron &other); CGAL_Nef_polyhedron &operator*=(const CGAL_Nef_polyhedron &other); diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc index 8e08e19..224e657 100644 --- a/src/PolySetCGALEvaluator.cc +++ b/src/PolySetCGALEvaluator.cc @@ -134,11 +134,11 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node) if (v->modinst->isBackground()) continue; CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v); if (N.dim == 3) { - if (sum.empty()) sum = N.copy(); + if (sum.isNull()) sum = N.copy(); else sum += N; } } - if (sum.empty()) return NULL; + if (sum.isNull()) return NULL; if (!sum.p3->is_simple()) { if (!node.cut_mode) { PRINT("WARNING: Body of projection(cut = false) isn't valid 2-manifold! Modify your design.."); @@ -149,7 +149,7 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node) //std::cout << sum.dump(); //std::cout.flush(); - CGAL_Nef_polyhedron nef_poly; + CGAL_Nef_polyhedron nef_poly(2); if (node.cut_mode) { CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); @@ -180,7 +180,7 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node) } } - if ( sum.p3->is_empty() ) { + if (sum.p3->is_empty()) { CGAL::set_error_behaviour(old_behaviour); PRINT("WARNING: projection() failed."); return NULL; @@ -204,7 +204,6 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node) log << "<!-- volume end. -->\n"; } nef_poly.p2 = zremover.output_nefpoly2d; - nef_poly.dim = 2; } catch (const CGAL::Failure_exception &e) { PRINTB("CGAL error in projection node while flattening: %s", e.what()); } @@ -284,8 +283,7 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node) plist.push_back(p); } // FIXME: Should the CGAL_Nef_polyhedron2 be cached? - if (nef_poly.empty()) { - nef_poly.dim = 2; + if (nef_poly.isEmpty()) { nef_poly.p2.reset(new CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED)); } else { @@ -385,18 +383,18 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const LinearExtrudeNode &node) BOOST_FOREACH (AbstractNode * v, node.getChildren()) { if (v->modinst->isBackground()) continue; CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v); - if (!N.empty()) { + if (!N.isNull()) { if (N.dim != 2) { PRINT("ERROR: linear_extrude() is not defined for 3D child objects!"); } else { - if (sum.empty()) sum = N.copy(); + if (sum.isNull()) sum = N.copy(); else sum += N; } } } - if (sum.empty()) return NULL; + if (sum.isNull()) return NULL; dxf = sum.convertToDxfData();; } else { dxf = new DxfData(node.fn, node.fs, node.fa, node.filename, node.layername, node.origin_x, node.origin_y, node.scale); @@ -485,18 +483,18 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const RotateExtrudeNode &node) BOOST_FOREACH (AbstractNode * v, node.getChildren()) { if (v->modinst->isBackground()) continue; CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v); - if (!N.empty()) { + if (!N.isNull()) { if (N.dim != 2) { PRINT("ERROR: rotate_extrude() is not defined for 3D child objects!"); } else { - if (sum.empty()) sum = N.copy(); + if (sum.isNull()) sum = N.copy(); else sum += N; } } } - if (sum.empty()) return NULL; + if (sum.isNull()) return NULL; dxf = sum.convertToDxfData(); } else { dxf = new DxfData(node.fn, node.fs, node.fa, node.filename, node.layername, node.origin_x, node.origin_y, node.scale); @@ -511,7 +509,7 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const CgaladvNode &node) { CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(node); PolySet *ps = NULL; - if (!N.empty()) { + if (!N.isNull()) { ps = N.convertToPolyset(); if (ps) ps->convexity = node.convexity; } @@ -523,7 +521,7 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const RenderNode &node) { CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(node); PolySet *ps = NULL; - if (!N.empty()) { + if (!N.isNull()) { if (N.dim == 3 && !N.p3->is_simple()) { PRINT("WARNING: Body of render() isn't valid 2-manifold!"); } diff --git a/src/mainwin.cc b/src/mainwin.cc index dc5b79b..dde6761 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -1236,35 +1236,37 @@ void MainWindow::actionRenderCGALDone(CGAL_Nef_polyhedron *root_N) PolySetCache::instance()->print(); CGALCache::instance()->print(); - if (root_N->dim == 2) { - PRINT(" Top level object is a 2D object:"); - PRINTB(" Empty: %6s", (root_N->p2->is_empty() ? "yes" : "no")); - PRINTB(" Plane: %6s", (root_N->p2->is_plane() ? "yes" : "no")); - PRINTB(" Vertices: %6d", root_N->p2->explorer().number_of_vertices()); - PRINTB(" Halfedges: %6d", root_N->p2->explorer().number_of_halfedges()); - PRINTB(" Edges: %6d", root_N->p2->explorer().number_of_edges()); - PRINTB(" Faces: %6d", root_N->p2->explorer().number_of_faces()); - PRINTB(" FaceCycles: %6d", root_N->p2->explorer().number_of_face_cycles()); - PRINTB(" ConnComp: %6d", root_N->p2->explorer().number_of_connected_components()); - } - - if (root_N->dim == 3) { - PRINT(" Top level object is a 3D object:"); - PRINTB(" Simple: %6s", (root_N->p3->is_simple() ? "yes" : "no")); - PRINTB(" Valid: %6s", (root_N->p3->is_valid() ? "yes" : "no")); - PRINTB(" Vertices: %6d", root_N->p3->number_of_vertices()); - PRINTB(" Halfedges: %6d", root_N->p3->number_of_halfedges()); - PRINTB(" Edges: %6d", root_N->p3->number_of_edges()); - PRINTB(" Halffacets: %6d", root_N->p3->number_of_halffacets()); - PRINTB(" Facets: %6d", root_N->p3->number_of_facets()); - PRINTB(" Volumes: %6d", root_N->p3->number_of_volumes()); + if (!root_N->isNull()) { + if (root_N->dim == 2) { + PRINT(" Top level object is a 2D object:"); + PRINTB(" Empty: %6s", (root_N->p2->is_empty() ? "yes" : "no")); + PRINTB(" Plane: %6s", (root_N->p2->is_plane() ? "yes" : "no")); + PRINTB(" Vertices: %6d", root_N->p2->explorer().number_of_vertices()); + PRINTB(" Halfedges: %6d", root_N->p2->explorer().number_of_halfedges()); + PRINTB(" Edges: %6d", root_N->p2->explorer().number_of_edges()); + PRINTB(" Faces: %6d", root_N->p2->explorer().number_of_faces()); + PRINTB(" FaceCycles: %6d", root_N->p2->explorer().number_of_face_cycles()); + PRINTB(" ConnComp: %6d", root_N->p2->explorer().number_of_connected_components()); + } + + if (root_N->dim == 3) { + PRINT(" Top level object is a 3D object:"); + PRINTB(" Simple: %6s", (root_N->p3->is_simple() ? "yes" : "no")); + PRINTB(" Valid: %6s", (root_N->p3->is_valid() ? "yes" : "no")); + PRINTB(" Vertices: %6d", root_N->p3->number_of_vertices()); + PRINTB(" Halfedges: %6d", root_N->p3->number_of_halfedges()); + PRINTB(" Edges: %6d", root_N->p3->number_of_edges()); + PRINTB(" Halffacets: %6d", root_N->p3->number_of_halffacets()); + PRINTB(" Facets: %6d", root_N->p3->number_of_facets()); + PRINTB(" Volumes: %6d", root_N->p3->number_of_volumes()); + } } int s = this->progresswidget->elapsedTime() / 1000; PRINTB("Total rendering time: %d hours, %d minutes, %d seconds", (s / (60*60)) % ((s / 60) % 60) % (s % 60)); this->root_N = root_N; - if (!this->root_N->empty()) { + if (!this->root_N->isNull()) { this->cgalRenderer = new CGALRenderer(*this->root_N); // Go to CGAL view mode if (viewActionCGALGrid->isChecked()) { diff --git a/testdata/scad/features/difference-tests.scad b/testdata/scad/features/difference-tests.scad index 186772f..b770764 100644 --- a/testdata/scad/features/difference-tests.scad +++ b/testdata/scad/features/difference-tests.scad @@ -29,6 +29,7 @@ translate([24,0,0]) difference() { translate([0,0,6.99]) cylinder(r=4, h=4, center=true); } +// Subtracting something from nothing translate([24,12,0]) difference() { cube([0,10,10], center=true); # cylinder(r=4, h=20, center=true); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 18f4469..87e5319 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -773,7 +773,6 @@ set_test_config(Heavy opencsgtest_minkowski3-tests cgalpngtest_minkowski3-tests cgalpngtest_for-tests cgalpngtest_for-nested-tests - cgalpngtest_difference-tests cgalpngtest_intersection-tests) foreach(FILE ${EXAMPLE_FILES}) diff --git a/tests/cgalpngtest.cc b/tests/cgalpngtest.cc index 08e539e..56861c6 100644 --- a/tests/cgalpngtest.cc +++ b/tests/cgalpngtest.cc @@ -153,30 +153,30 @@ int main(int argc, char **argv) exit(1); } - CGALRenderer cgalRenderer(N); - + CGALRenderer cgalRenderer(N); + BoundingBox bbox; if (cgalRenderer.polyhedron) { CGAL::Bbox_3 cgalbbox = cgalRenderer.polyhedron->bbox(); bbox = BoundingBox(Vector3d(cgalbbox.xmin(), cgalbbox.ymin(), cgalbbox.zmin()), - Vector3d(cgalbbox.xmax(), cgalbbox.ymax(), cgalbbox.zmax())); + Vector3d(cgalbbox.xmax(), cgalbbox.ymax(), cgalbbox.zmax())); } else if (cgalRenderer.polyset) { bbox = cgalRenderer.polyset->getBoundingBox(); } - + Vector3d center = getBoundingCenter(bbox); double radius = getBoundingRadius(bbox); - + Vector3d cameradir(1, 1, -0.5); Vector3d camerapos = center - radius*2*cameradir; csgInfo.glview->setCamera(camerapos, center); - - + + csgInfo.glview->setRenderer(&cgalRenderer); csgInfo.glview->paintGL(); csgInfo.glview->save(outfile); - + delete root_node; delete root_module; diff --git a/tests/cgalstlsanitytest.cc b/tests/cgalstlsanitytest.cc index 137f626..52cfb41 100644 --- a/tests/cgalstlsanitytest.cc +++ b/tests/cgalstlsanitytest.cc @@ -129,7 +129,7 @@ int main(int argc, char **argv) CGAL_Nef_polyhedron N = cgalevaluator.evaluateCGALMesh(*root_node); current_path(original_path); - if (!N.empty()) { + if (!N.isNull()) { std::ofstream outfile; outfile.open(outfilename); diff --git a/tests/cgaltest.cc b/tests/cgaltest.cc index e4761db..b546286 100644 --- a/tests/cgaltest.cc +++ b/tests/cgaltest.cc @@ -122,7 +122,7 @@ int main(int argc, char **argv) CGAL_Nef_polyhedron N = cgalevaluator.evaluateCGALMesh(*root_node); current_path(original_path); - if (!N.empty()) { + if (!N.isNull()) { export_stl(&N, std::cout); } |