From 698aa54998fbbea3ee0a18da25e30c30a84648cb Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Sun, 15 Dec 2013 18:27:25 -0500 Subject: [PATCH] Implemented 3D transform of PolySets, removed some Grid usage, improved PolySet -> Polyhedron conversion, optimized operations with only one child --- src/GeometryEvaluator.cc | 119 +++++++++++++++++++++++---------------- src/GeometryEvaluator.h | 22 +++++++- src/cgalutils.cc | 112 +++++++++++++++--------------------- src/memory.h | 1 + src/polyset.cc | 15 +++-- src/polyset.h | 4 +- 6 files changed, 149 insertions(+), 124 deletions(-) diff --git a/src/GeometryEvaluator.cc b/src/GeometryEvaluator.cc index b2e3776a..49d37bd4 100644 --- a/src/GeometryEvaluator.cc +++ b/src/GeometryEvaluator.cc @@ -92,30 +92,39 @@ shared_ptr GeometryEvaluator::evaluateGeometry(const AbstractNod return GeometryCache::instance()->get(this->tree.getIdString(node)); } -Geometry *GeometryEvaluator::applyToChildren(const AbstractNode &node, OpenSCADOperator op) +GeometryEvaluator::ResultObject GeometryEvaluator::applyToChildren(const AbstractNode &node, OpenSCADOperator op) { unsigned int dim = 0; BOOST_FOREACH(const Geometry::ChildItem &item, this->visitedchildren[node.index()]) { if (item.second) { if (!dim) dim = item.second->getDimension(); else if (dim != item.second->getDimension()) { - return NULL; + return ResultObject(); } } } - if (dim == 2) return applyToChildren2D(node, op); + if (dim == 2) return ResultObject(applyToChildren2D(node, op)); else if (dim == 3) return applyToChildren3D(node, op); - return NULL; + return ResultObject(); } -Geometry *GeometryEvaluator::applyToChildren3D(const AbstractNode &node, OpenSCADOperator op) +/*! + Applies the operator to all child nodes of the given node. + + May return NULL or any 3D Geometry object (can be either PolySet or CGAL_Nef_polyhedron) +*/ +GeometryEvaluator::ResultObject GeometryEvaluator::applyToChildren3D(const AbstractNode &node, OpenSCADOperator op) { if (op == OPENSCAD_HULL) { - return applyHull3D(node); + return ResultObject(applyHull3D(node)); } Geometry::ChildList children = collectChildren3D(node); + if (children.size() == 0) return ResultObject(); + // Only one child -> this is a noop + if (children.size() == 1) return ResultObject(children.front().second); + CGAL_Nef_polyhedron *N = new CGAL_Nef_polyhedron; BOOST_FOREACH(const Geometry::ChildItem &item, children) { const shared_ptr &chgeom = item.second; @@ -167,7 +176,7 @@ Geometry *GeometryEvaluator::applyToChildren3D(const AbstractNode &node, OpenSCA chnode->progress_report(); } */ - return N; + return ResultObject(N); } @@ -432,8 +441,7 @@ Response GeometryEvaluator::visit(State &state, const AbstractNode &node) if (state.isPostfix()) { shared_ptr geom; if (!isCached(node)) { - const Geometry *geometry = applyToChildren(node, OPENSCAD_UNION); - geom.reset(geometry); + geom = applyToChildren(node, OPENSCAD_UNION).constptr(); } else { geom = GeometryCache::instance()->get(this->tree.getIdString(node)); @@ -508,10 +516,9 @@ Response GeometryEvaluator::visit(State &state, const CsgNode &node) { if (state.isPrefix() && isCached(node)) return PruneTraversal; if (state.isPostfix()) { - shared_ptr geom; + shared_ptr geom; if (!isCached(node)) { - const Geometry *geometry = applyToChildren(node, node.type); - geom.reset(geometry); + geom = applyToChildren(node, node.type).constptr(); } else { geom = GeometryCache::instance()->get(this->tree.getIdString(node)); @@ -540,24 +547,44 @@ Response GeometryEvaluator::visit(State &state, const TransformNode &node) } else { // First union all children - Geometry *geometry = applyToChildren(node, OPENSCAD_UNION); - if (geometry) { - if (geometry->getDimension() == 2) { - Polygon2d *polygons = dynamic_cast(geometry); - assert(polygons); - Transform2d mat2; - mat2.matrix() << - 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,0), node.matrix(3,1), node.matrix(3,3); - polygons->transform(mat2); - geom.reset(polygons); + ResultObject res = applyToChildren(node, OPENSCAD_UNION); + geom = res.constptr(); + if (geom->getDimension() == 2) { + shared_ptr polygons = dynamic_pointer_cast(geom); + assert(polygons); + + // If we got a const object, make a copy + shared_ptr newpoly; + if (res.isConst()) newpoly.reset(new Polygon2d(*polygons)); + else newpoly = dynamic_pointer_cast(res.ptr()); + + Transform2d mat2; + mat2.matrix() << + 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,0), node.matrix(3,1), node.matrix(3,3); + newpoly->transform(mat2); + geom = newpoly; + } + else if (geom->getDimension() == 3) { + shared_ptr ps = dynamic_pointer_cast(geom); + if (ps) { + // If we got a const object, make a copy + shared_ptr newps; + if (res.isConst()) newps.reset(new PolySet(*ps)); + else newps = dynamic_pointer_cast(res.ptr()); + newps->transform(node.matrix); + geom = newps; } - else if (geometry->getDimension() == 3) { - CGAL_Nef_polyhedron *N = dynamic_cast(geometry); + else { + shared_ptr N = dynamic_pointer_cast(geom); assert(N); - N->transform(node.matrix); - geom.reset(N); + // If we got a const object, make a copy + shared_ptr newN; + if (res.isConst()) newN.reset(new CGAL_Nef_polyhedron(*N)); + else newN = dynamic_pointer_cast(res.ptr()); + newN->transform(node.matrix); + geom = newN; } } } @@ -739,17 +766,19 @@ static Geometry *rotatePolygon(const RotateExtrudeNode &node, const Polygon2d &p ps->setConvexity(node.convexity); BOOST_FOREACH(const Outline2d &o, poly.outlines()) { + double min_x = 0; double max_x = 0; BOOST_FOREACH(const Vector2d &v, o) { - if (v[0] < 0) { - PRINT("ERROR: all points for rotate_extrude() must have non-negative X coordinates"); - PRINTB("[Point (%f, %f)]", v[0] % v[1]); + min_x = fmin(min_x, v[0]); + max_x = fmax(max_x, v[0]); + + if ((max_x - min_x) > max_x && (max_x - min_x) > fabs(min_x)) { + PRINTB("ERROR: all points for rotate_extrude() must have the same X coordinate sign (range is %.2f -> %.2f)", min_x % max_x); delete ps; return NULL; } - max_x = fmax(max_x, v[0]); } - int fragments = get_fragments_from_r(max_x, node.fn, node.fs, node.fa); + int fragments = get_fragments_from_r(max_x - min_x, node.fn, node.fs, node.fa); std::vector rings[2]; rings[0].reserve(o.size()); @@ -799,7 +828,6 @@ Response GeometryEvaluator::visit(State &state, const RotateExtrudeNode &node) if (geometry) { const Polygon2d *polygons = dynamic_cast(geometry); Geometry *rotated = rotatePolygon(node, *polygons); - assert(rotated); geom.reset(rotated); delete geometry; } @@ -898,12 +926,11 @@ Response GeometryEvaluator::visit(State &state, const ProjectionNode &node) if (sumresult.size() > 0) geom.reset(ClipperUtils::toPolygon2d(sumresult)); } else { - const Geometry *geometry = applyToChildren3D(node, OPENSCAD_UNION); - if (geometry) { - const CGAL_Nef_polyhedron *Nptr = dynamic_cast(geometry); + shared_ptr newgeom = applyToChildren3D(node, OPENSCAD_UNION).constptr(); + if (newgeom) { + shared_ptr Nptr = dynamic_pointer_cast(newgeom); if (!Nptr) { - // FIXME: delete this object - Nptr = createNefPolyhedronFromGeometry(*geometry); + Nptr.reset(createNefPolyhedronFromGeometry(*newgeom)); } if (!Nptr->isNull()) { CGAL_Nef_polyhedron nef_poly = CGALUtils::project(*Nptr, node.cut_mode); @@ -911,7 +938,6 @@ Response GeometryEvaluator::visit(State &state, const ProjectionNode &node) assert(poly); poly->setConvexity(node.convexity); geom.reset(poly); - delete geometry; } } } @@ -938,13 +964,11 @@ Response GeometryEvaluator::visit(State &state, const CgaladvNode &node) if (!isCached(node)) { switch (node.type) { case MINKOWSKI: { - const Geometry *geometry = applyToChildren(node, OPENSCAD_MINKOWSKI); - geom.reset(geometry); + geom = applyToChildren(node, OPENSCAD_MINKOWSKI).constptr(); break; } case HULL: { - const Geometry *geometry = applyToChildren(node, OPENSCAD_HULL); - geom.reset(geometry); + geom = applyToChildren(node, OPENSCAD_HULL).constptr(); break; } default: @@ -991,8 +1015,8 @@ Response GeometryEvaluator::visit(State &state, const RenderNode &node) if (state.isPostfix()) { shared_ptr geom; if (!isCached(node)) { - const Geometry *geometry = applyToChildren(node, OPENSCAD_UNION); - const CGAL_Nef_polyhedron *N = dynamic_cast(geometry); + geom = applyToChildren(node, OPENSCAD_UNION).constptr(); + shared_ptr N = dynamic_pointer_cast(geom); if (N) { PolySet *ps = NULL; if (!N->isNull()) { @@ -1006,9 +1030,6 @@ Response GeometryEvaluator::visit(State &state, const RenderNode &node) } geom.reset(ps); } - else { - geom.reset(geometry); - } } else { geom = GeometryCache::instance()->get(this->tree.getIdString(node)); diff --git a/src/GeometryEvaluator.h b/src/GeometryEvaluator.h index 18ee4781..168eaa17 100644 --- a/src/GeometryEvaluator.h +++ b/src/GeometryEvaluator.h @@ -34,6 +34,24 @@ public: const Tree &getTree() const { return this->tree; } private: + class ResultObject { + public: + ResultObject() : is_const(true) {} + ResultObject(const Geometry *g) : is_const(true), const_pointer(g) {} + ResultObject(shared_ptr &g) : is_const(true), const_pointer(g) {} + ResultObject(Geometry *g) : is_const(false), pointer(g) {} + ResultObject(shared_ptr &g) : is_const(false), pointer(g) {} + bool isConst() const { return is_const; } + shared_ptr ptr() { assert(!is_const); return pointer; } + shared_ptr constptr() const { + return is_const ? const_pointer : static_pointer_cast(pointer); + } + private: + bool is_const; + shared_ptr pointer; + shared_ptr const_pointer; + }; + bool isCached(const AbstractNode &node) const; void smartCache(const AbstractNode &node, const shared_ptr &geom); std::vector collectChildren2D(const AbstractNode &node); @@ -42,8 +60,8 @@ private: Polygon2d *applyHull2D(const AbstractNode &node); Geometry *applyHull3D(const AbstractNode &node); Polygon2d *applyToChildren2D(const AbstractNode &node, OpenSCADOperator op); - Geometry *applyToChildren3D(const AbstractNode &node, OpenSCADOperator op); - Geometry *applyToChildren(const AbstractNode &node, OpenSCADOperator op); + ResultObject applyToChildren3D(const AbstractNode &node, OpenSCADOperator op); + ResultObject applyToChildren(const AbstractNode &node, OpenSCADOperator op); void addToParent(const State &state, const AbstractNode &node, const shared_ptr &geom); std::map visitedchildren; diff --git a/src/cgalutils.cc b/src/cgalutils.cc index 156de78b..40551acb 100644 --- a/src/cgalutils.cc +++ b/src/cgalutils.cc @@ -5,6 +5,7 @@ #include "printutils.h" #include "Polygon2d.h" #include "polyset-utils.h" +#include "grid.h" #include "cgal.h" #include @@ -101,7 +102,7 @@ namespace CGALUtils { catch (const CGAL::Failure_exception &e) { // union && difference assert triggered by testdata/scad/bugs/rotate-diff-nonmanifold-crash.scad and testdata/scad/bugs/issue204.scad std::string opstr = op == OPENSCAD_UNION ? "union" : op == OPENSCAD_INTERSECTION ? "intersection" : op == OPENSCAD_DIFFERENCE ? "difference" : op == OPENSCAD_MINKOWSKI ? "minkowski" : "UNKNOWN"; - PRINTB("CGAL error in CGAL_Nef_polyhedron's %s operator: %s", opstr % e.what()); + PRINTB("CGAL error in CGALUtils::applyBinaryOperator %s: %s", opstr % e.what()); // Errors can result in corrupt polyhedrons, so put back the old one target = src; @@ -123,7 +124,7 @@ namespace CGALUtils { newN.p3.reset(new CGAL_Nef_polyhedron3(N.p3->intersection(xy_plane, CGAL_Nef_polyhedron3::PLANE_ONLY))); } catch (const CGAL::Failure_exception &e) { - PRINTB("CGAL error in projection node during plane intersection: %s", e.what()); + PRINTB("CGALUtils::project during plane intersection: %s", e.what()); try { PRINT("Trying alternative intersection using very large thin box: "); std::vector pts; @@ -140,7 +141,7 @@ namespace CGALUtils { newN.p3.reset(new CGAL_Nef_polyhedron3(nef_bigbox.intersection(*N.p3))); } catch (const CGAL::Failure_exception &e) { - PRINTB("CGAL error in projection node during bigbox intersection: %s", e.what()); + PRINTB("CGAL error in CGALUtils::project during bigbox intersection: %s", e.what()); } } @@ -168,7 +169,7 @@ namespace CGALUtils { } nef_poly.p2 = zremover.output_nefpoly2d; } catch (const CGAL::Failure_exception &e) { - PRINTB("CGAL error in projection node while flattening: %s", e.what()); + PRINTB("CGAL error in CGALUtils::project while flattening: %s", e.what()); } log << "\n"; @@ -243,73 +244,52 @@ public: void operator()(CGAL_HDS& hds) { CGAL_Polybuilder B(hds, true); + typedef boost::tuple BuilderVertex; + typedef std::map BuilderMap; + BuilderMap vertices; + std::vector indices(3); - std::vector vertices; - Grid3d vertices_idx(GRID_FINE); - - for (size_t i = 0; i < ps.polygons.size(); i++) { - const PolySet::Polygon *poly = &ps.polygons[i]; - for (size_t j = 0; j < poly->size(); j++) { - const Vector3d &p = poly->at(j); - if (!vertices_idx.has(p[0], p[1], p[2])) { - vertices_idx.data(p[0], p[1], p[2]) = vertices.size(); - vertices.push_back(CGALPoint(p[0], p[1], p[2])); + // Estimating same # of vertices as polygons (very rough) + B.begin_surface(ps.polygons.size(), ps.polygons.size()); + int pidx = 0; + printf("polyhedron(triangles=["); + BOOST_FOREACH(const PolySet::Polygon &p, ps.polygons) { + if (pidx++ > 0) printf(","); + indices.clear(); + BOOST_FOREACH(const Vector3d &v, p) { + size_t idx; + BuilderVertex bv = boost::make_tuple(v[0], v[1], v[2]); + if (vertices.count(bv) > 0) indices.push_back(vertices[bv]); + else { + indices.push_back(vertices.size()); + vertices[bv] = vertices.size(); + B.add_vertex(CGALPoint(v[0], v[1], v[2])); } } - } - - B.begin_surface(vertices.size(), ps.polygons.size()); -#ifdef GEN_SURFACE_DEBUG - printf("=== CGAL Surface ===\n"); -#endif - - for (size_t i = 0; i < vertices.size(); i++) { - const CGALPoint &p = vertices[i]; - B.add_vertex(p); -#ifdef GEN_SURFACE_DEBUG - printf("%d: %f %f %f\n", i, p.x().to_double(), p.y().to_double(), p.z().to_double()); -#endif - } - - for (size_t i = 0; i < ps.polygons.size(); i++) { - const PolySet::Polygon *poly = &ps.polygons[i]; - std::map fc; - bool facet_is_degenerated = false; - for (size_t j = 0; j < poly->size(); j++) { - const Vector3d &p = poly->at(j); - int v = vertices_idx.data(p[0], p[1], p[2]); - if (fc[v]++ > 0) - facet_is_degenerated = true; + B.begin_facet(); + printf("["); + int fidx = 0; + BOOST_FOREACH(size_t i, indices) { + B.add_vertex_to_facet(i); + if (fidx++ > 0) printf(","); + printf("%ld", i); } - - if (!facet_is_degenerated) - B.begin_facet(); -#ifdef GEN_SURFACE_DEBUG - printf("F:"); -#endif - for (size_t j = 0; j < poly->size(); j++) { - const Vector3d &p = poly->at(j); -#ifdef GEN_SURFACE_DEBUG - printf(" %d (%f,%f,%f)", vertices_idx.data(p[0], p[1], p[2]), p[0], p[1], p[2]); -#endif - if (!facet_is_degenerated) - B.add_vertex_to_facet(vertices_idx.data(p[0], p[1], p[2])); - } -#ifdef GEN_SURFACE_DEBUG - if (facet_is_degenerated) - printf(" (degenerated)"); - printf("\n"); -#endif - if (!facet_is_degenerated) - B.end_facet(); + printf("]"); + B.end_facet(); } - -#ifdef GEN_SURFACE_DEBUG - printf("====================\n"); -#endif B.end_surface(); + printf("],\n"); - #undef PointKey + printf("points=["); + int vidx = 0; + for (int vidx=0;vidx 0) printf(","); + const BuilderMap::const_iterator it = + std::find_if(vertices.begin(), vertices.end(), + boost::bind(&BuilderMap::value_type::second, _1) == vidx); + printf("[%g,%g,%g]", it->first.get<0>(), it->first.get<1>(), it->first.get<2>()); + } + printf("]);\n"); } }; @@ -322,7 +302,7 @@ bool createPolyhedronFromPolySet(const PolySet &ps, CGAL_Polyhedron &p) p.delegate(builder); } catch (const CGAL::Assertion_exception &e) { - PRINTB("CGAL error in CGAL_Build_PolySet: %s", e.what()); + PRINTB("CGAL error in CGALUtils::createPolyhedronFromPolySet: %s", e.what()); err = true; } CGAL::set_error_behaviour(old_behaviour); @@ -672,7 +652,7 @@ static CGAL_Nef_polyhedron *createNefPolyhedronFromPolySet(const PolySet &ps) } } catch (const CGAL::Assertion_exception &e) { - PRINTB("CGAL error in CGAL_Nef_polyhedron3(): %s", e.what()); + PRINTB("CGAL error in CGALUtils::createNefPolyhedronFromPolySet(): %s", e.what()); } CGAL::set_error_behaviour(old_behaviour); return new CGAL_Nef_polyhedron(N); diff --git a/src/memory.h b/src/memory.h index 01668465..38d5d0ba 100644 --- a/src/memory.h +++ b/src/memory.h @@ -4,5 +4,6 @@ #include using boost::shared_ptr; using boost::dynamic_pointer_cast; +using boost::static_pointer_cast; #endif diff --git a/src/polyset.cc b/src/polyset.cc index 7a11a3a0..a6f2c3f6 100644 --- a/src/polyset.cc +++ b/src/polyset.cc @@ -43,11 +43,11 @@ */ -PolySet::PolySet() : grid(GRID_FINE), is2d(false) +PolySet::PolySet() : is2d(false) { } -PolySet::PolySet(const Polygon2d &origin) : grid(GRID_FINE), is2d(true), polygon(origin) +PolySet::PolySet(const Polygon2d &origin) : is2d(true), polygon(origin) { } @@ -90,7 +90,6 @@ void PolySet::append_vertex(double x, double y, double z) void PolySet::append_vertex(Vector3d v) { - grid.align(v[0], v[1], v[2]); polygons.back().push_back(v); } @@ -101,7 +100,6 @@ void PolySet::insert_vertex(double x, double y, double z) void PolySet::insert_vertex(Vector3d v) { - grid.align(v[0], v[1], v[2]); polygons.back().insert(polygons.back().begin(), v); } @@ -329,7 +327,6 @@ size_t PolySet::memsize() const size_t mem = 0; BOOST_FOREACH(const Polygon &p, this->polygons) mem += p.size() * sizeof(Vector3d); mem += this->polygon.memsize() - sizeof(this->polygon); - mem += this->grid.db.size() * (3 * sizeof(int64_t) + sizeof(void*)) + sizeof(Grid3d); mem += sizeof(PolySet); return mem; } @@ -339,3 +336,11 @@ void PolySet::append(const PolySet &ps) this->polygons.insert(this->polygons.end(), ps.polygons.begin(), ps.polygons.end()); } +void PolySet::transform(const Transform3d &mat) +{ + BOOST_FOREACH(Polygon &p, this->polygons) { + BOOST_FOREACH(Vector3d &v, p) { + v = mat * v; + } + } +} diff --git a/src/polyset.h b/src/polyset.h index 8055029a..84bcb406 100644 --- a/src/polyset.h +++ b/src/polyset.h @@ -3,7 +3,6 @@ #include "Geometry.h" #include "system-gl.h" -#include "grid.h" #include "linalg.h" #include "renderer.h" #include "Polygon2d.h" @@ -15,7 +14,6 @@ class PolySet : public Geometry public: typedef std::vector Polygon; std::vector polygons; - Grid3d grid; bool is2d; @@ -39,6 +37,8 @@ public: void render_surface(Renderer::csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo = NULL) const; void render_edges(Renderer::csgmode_e csgmode) const; + void transform(const Transform3d &mat); + private: Polygon2d polygon; };