diff --git a/src/GeometryCache.cc b/src/GeometryCache.cc index 9e97f551..51d491de 100644 --- a/src/GeometryCache.cc +++ b/src/GeometryCache.cc @@ -1,6 +1,9 @@ #include "GeometryCache.h" #include "printutils.h" #include "geometry.h" +#ifdef DEBUG + #include "CGAL_Nef_polyhedron.h" +#endif GeometryCache *GeometryCache::inst = NULL; @@ -8,6 +11,7 @@ bool GeometryCache::insert(const std::string &id, const shared_ptrcache.insert(id, new cache_entry(geom), geom ? geom->memsize() : 0); #ifdef DEBUG + assert(!dynamic_cast(geom.get())); if (inserted) PRINTB("Geometry Cache insert: %s (%d bytes)", id.substr(0, 40) % (geom ? geom->memsize() : 0)); else PRINTB("Geometry Cache insert failed: %s (%d bytes)", diff --git a/src/GeometryEvaluator.cc b/src/GeometryEvaluator.cc index 1e2a3aad..5ce3d4df 100644 --- a/src/GeometryEvaluator.cc +++ b/src/GeometryEvaluator.cc @@ -18,7 +18,6 @@ #include "clipper-utils.h" #include "polyset-utils.h" #include "CGALEvaluator.h" -#include "CGALCache.h" #include "PolySet.h" #include "openscad.h" // get_fragments_from_r() #include "printutils.h" @@ -28,6 +27,9 @@ #include #include +#include +#include + GeometryEvaluator::GeometryEvaluator(const class Tree &tree): tree(tree) { @@ -82,6 +84,7 @@ shared_ptr GeometryEvaluator::evaluateGeometry(const AbstractNod if (N->getDimension() == 2) this->root.reset(N->convertToPolygon2d()); else if (N->getDimension() == 3) this->root.reset(N->convertToPolyset()); else this->root.reset(); + GeometryCache::instance()->insert(this->tree.getIdString(node), this->root); } } @@ -108,6 +111,10 @@ Geometry *GeometryEvaluator::applyToChildren(const AbstractNode &node, OpenSCADO Geometry *GeometryEvaluator::applyToChildren3D(const AbstractNode &node, OpenSCADOperator op) { + if (op == OPENSCAD_HULL) { + return applyHull3D(node); + } + CGAL_Nef_polyhedron *N = new CGAL_Nef_polyhedron; BOOST_FOREACH(const ChildItem &item, this->visitedchildren[node.index()]) { const AbstractNode *chnode = item.first; @@ -125,9 +132,7 @@ Geometry *GeometryEvaluator::applyToChildren3D(const AbstractNode &node, OpenSCA // a node is a valid object. If we inserted as we created them, the // cache could have been modified before we reach this point due to a large // sibling object. - if (!CGALCache::instance()->contains(this->tree.getIdString(*chnode))) { - CGALCache::instance()->insert(this->tree.getIdString(*chnode), chN ? *chN : CGAL_Nef_polyhedron()); - } + smartCache(node, chN); if (chgeom) { if (chgeom->getDimension() == 3) { @@ -146,15 +151,85 @@ Geometry *GeometryEvaluator::applyToChildren3D(const AbstractNode &node, OpenSCA } +Geometry *GeometryEvaluator::applyHull2D(const AbstractNode &node) +{ + std::vector children = collectChildren2D(node); + Polygon2d *geometry = NULL; + + // Collect point cloud + std::list points; + BOOST_FOREACH(const Polygon2d *p, children) { + BOOST_FOREACH(const Outline2d &o, p->outlines()) { + BOOST_FOREACH(const Vector2d &v, o) { + points.push_back(CGAL_Nef_polyhedron2::Point(v[0], v[1])); + } + } + } + if (points.size() > 0) { + // Apply hull + std::list result; + CGAL::convex_hull_2(points.begin(), points.end(), std::back_inserter(result)); + + // Construct Polygon2d + Outline2d outline; + BOOST_FOREACH(const CGAL_Nef_polyhedron2::Point &p, result) { + outline.push_back(Vector2d(CGAL::to_double(p[0]), CGAL::to_double(p[1]))); + } + geometry = new Polygon2d(); + geometry->addOutline(outline); + } + return geometry; +} + +Geometry *GeometryEvaluator::applyHull3D(const AbstractNode &node) +{ + std::vector children = collectChildren3D(node); + + // Collect point cloud + std::list points; + CGAL_Polyhedron P; + BOOST_FOREACH(const Geometry *geometry, children) { + const CGAL_Nef_polyhedron *N = dynamic_cast(geometry); + if (N) { + if (!N->p3->is_simple()) { + PRINT("Hull() currently requires a valid 2-manifold. Please modify your design. See http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/STL_Import_and_Export"); + } + else { + N->p3->convert_to_Polyhedron(P); + std::transform(P.vertices_begin(), P.vertices_end(), std::back_inserter(points), + boost::bind(static_cast(&CGAL_Polyhedron::Vertex::point), _1)); + } + } + else { + const PolySet *ps = dynamic_cast(geometry); + BOOST_FOREACH(const PolySet::Polygon &p, ps->polygons) { + BOOST_FOREACH(const Vector3d &v, p) { + points.push_back(CGAL_Polyhedron::Vertex::Point_3(v[0], v[1], v[2])); + } + } + } + } + if (points.size() > 0) { + // Apply hull + CGAL_Polyhedron P; + if (points.size() > 3) { + CGAL::convex_hull_3(points.begin(), points.end(), P); + } + + return new CGAL_Nef_polyhedron(new CGAL_Nef_polyhedron3(P)); + } + return NULL; +} + Geometry *GeometryEvaluator::applyMinkowski2D(const AbstractNode &node) { - std::vector > children = collectChildren2D(node); + std::vector children = collectChildren2D(node); if (children.size() > 0) { bool first = false; ClipperLib::Polygons result = ClipperUtils::fromPolygon2d(*children[0]); for (int i=1;i &chgeom = children[i]; + const Polygon2d *chgeom = children[i]; ClipperLib::Polygon shape = ClipperUtils::fromOutline2d(chgeom->outlines()[0]); ClipperLib::MinkowkiSum(temp, shape, result, true); } @@ -174,9 +249,9 @@ Geometry *GeometryEvaluator::applyMinkowski2D(const AbstractNode &node) return NULL; } -std::vector > GeometryEvaluator::collectChildren2D(const AbstractNode &node) +std::vector GeometryEvaluator::collectChildren2D(const AbstractNode &node) { - std::vector > children; + std::vector children; BOOST_FOREACH(const ChildItem &item, this->visitedchildren[node.index()]) { const AbstractNode *chnode = item.first; const shared_ptr &chgeom = item.second; @@ -193,7 +268,7 @@ std::vector > GeometryEvaluator::collectChildren2D(c if (chgeom) { if (chgeom->getDimension() == 2) { - shared_ptr polygons = dynamic_pointer_cast(chgeom); + const Polygon2d *polygons = dynamic_cast(chgeom.get()); assert(polygons); children.push_back(polygons); } @@ -205,6 +280,53 @@ std::vector > GeometryEvaluator::collectChildren2D(c return children; } +void GeometryEvaluator::smartCache(const AbstractNode &node, + const shared_ptr &geom) +{ + // Since we can generate both Nef and non-Nef geometry, we need to insert it into + // the appropriate cache + const CGAL_Nef_polyhedron *N = dynamic_cast(geom.get()); + if (N) { + if (!CGALCache::instance()->contains(this->tree.getIdString(node))) { + CGALCache::instance()->insert(this->tree.getIdString(node), *N); + } + } + else { + if (!isCached(node)) { + if (!GeometryCache::instance()->insert(this->tree.getIdString(node), geom)) { + PRINT("WARNING: GeometryEvaluator: Root node didn't fit into cache"); + } + } + } +} + +std::vector GeometryEvaluator::collectChildren3D(const AbstractNode &node) +{ + std::vector children; + BOOST_FOREACH(const ChildItem &item, this->visitedchildren[node.index()]) { + const AbstractNode *chnode = item.first; + const shared_ptr &chgeom = item.second; + // FIXME: Don't use deep access to modinst members + if (chnode->modinst->isBackground()) continue; + + // NB! We insert into the cache here to ensure that all children of + // a node is a valid object. If we inserted as we created them, the + // cache could have been modified before we reach this point due to a large + // sibling object. + smartCache(*chnode, chgeom); + + if (chgeom) { + if (chgeom->getDimension() == 3) { + children.push_back(chgeom.get()); + } + else { + PRINT("ERROR: Only 3D children are supported by this operation!"); + } + } + } + return children; +} + /*! */ @@ -213,6 +335,9 @@ Geometry *GeometryEvaluator::applyToChildren2D(const AbstractNode &node, OpenSCA if (op == OPENSCAD_MINKOWSKI) { return applyMinkowski2D(node); } + else if (op == OPENSCAD_HULL) { + return applyHull2D(node); + } ClipperLib::Clipper sumclipper; bool first = true; @@ -302,11 +427,7 @@ void GeometryEvaluator::addToParent(const State &state, } else { // Root node, insert into cache - if (!isCached(node)) { - if (!GeometryCache::instance()->insert(this->tree.getIdString(node), geom)) { - PRINT("WARNING: GeometryEvaluator: Root node didn't fit into cache"); - } - } + smartCache(node, geom); this->root = geom; } } @@ -822,6 +943,11 @@ Response GeometryEvaluator::visit(State &state, const CgaladvNode &node) geom.reset(geometry); break; } + case HULL: { + const Geometry *geometry = applyToChildren(node, OPENSCAD_HULL); + geom.reset(geometry); + break; + } default: assert(false && "not implemented"); } diff --git a/src/GeometryEvaluator.h b/src/GeometryEvaluator.h index 4d2556b5..1b128296 100644 --- a/src/GeometryEvaluator.h +++ b/src/GeometryEvaluator.h @@ -34,8 +34,12 @@ public: private: bool isCached(const AbstractNode &node) const; - std::vector > collectChildren2D(const AbstractNode &node); + void smartCache(const AbstractNode &node, const shared_ptr &geom); + std::vector collectChildren2D(const AbstractNode &node); + std::vector collectChildren3D(const AbstractNode &node); Geometry *applyMinkowski2D(const AbstractNode &node); + Geometry *applyHull2D(const AbstractNode &node); + Geometry *applyHull3D(const AbstractNode &node); Geometry *applyToChildren2D(const AbstractNode &node, OpenSCADOperator op); Geometry *applyToChildren3D(const AbstractNode &node, OpenSCADOperator op); Geometry *applyToChildren(const AbstractNode &node, OpenSCADOperator op); diff --git a/src/enums.h b/src/enums.h index 339f0d05..70c04d61 100644 --- a/src/enums.h +++ b/src/enums.h @@ -5,7 +5,8 @@ enum OpenSCADOperator { OPENSCAD_UNION, OPENSCAD_INTERSECTION, OPENSCAD_DIFFERENCE, - OPENSCAD_MINKOWSKI + OPENSCAD_MINKOWSKI, + OPENSCAD_HULL }; #endif