mirror of https://github.com/vitalif/openscad
Implemented minkowski (2D and 3D)
parent
54ddd63f08
commit
a67452711b
|
@ -303,7 +303,7 @@ Response CGALEvaluator::visit(State &state, const AbstractPolyNode &node)
|
|||
CGAL_Nef_polyhedron N;
|
||||
if (!isCached(node)) {
|
||||
// Apply polyset operation
|
||||
shared_ptr<const Geometry> geom = this->geomevaluator.evaluateGeometry(node);
|
||||
shared_ptr<const Geometry> geom = this->geomevaluator.evaluateGeometry(node, true);
|
||||
if (geom) {
|
||||
shared_ptr<const CGAL_Nef_polyhedron> Nptr = dynamic_pointer_cast<const CGAL_Nef_polyhedron>(geom);
|
||||
if (!Nptr) {
|
||||
|
|
|
@ -112,7 +112,7 @@ Response CSGTermEvaluator::visit(State &state, const AbstractPolyNode &node)
|
|||
if (state.isPrefix()) {
|
||||
shared_ptr<CSGTerm> t1;
|
||||
if (this->geomevaluator) {
|
||||
shared_ptr<const Geometry> geom = this->geomevaluator->evaluateGeometry(node);
|
||||
shared_ptr<const Geometry> geom = this->geomevaluator->evaluateGeometry(node, false);
|
||||
if (geom) {
|
||||
t1 = evaluate_csg_term_from_geometry(state, this->highlights, this->background,
|
||||
geom, node.modinst, node);
|
||||
|
@ -179,7 +179,7 @@ Response CSGTermEvaluator::visit(State &state, const RenderNode &node)
|
|||
shared_ptr<CSGTerm> t1;
|
||||
shared_ptr<const Geometry> geom;
|
||||
if (this->geomevaluator) {
|
||||
geom = this->geomevaluator->evaluateGeometry(node);
|
||||
geom = this->geomevaluator->evaluateGeometry(node, false);
|
||||
node.progress_report();
|
||||
}
|
||||
if (geom) {
|
||||
|
@ -199,7 +199,7 @@ Response CSGTermEvaluator::visit(State &state, const CgaladvNode &node)
|
|||
// FIXME: Calling evaluator directly since we're not a PolyNode. Generalize this.
|
||||
shared_ptr<const Geometry> geom;
|
||||
if (this->geomevaluator) {
|
||||
geom = this->geomevaluator->evaluateGeometry(node);
|
||||
geom = this->geomevaluator->evaluateGeometry(node, false);
|
||||
}
|
||||
if (geom) {
|
||||
t1 = evaluate_csg_term_from_geometry(state, this->highlights, this->background,
|
||||
|
|
|
@ -54,7 +54,7 @@ shared_ptr<const Geometry> GeometryEvaluator::getGeometry(const AbstractNode &no
|
|||
return GeometryCache::instance()->get(cacheid);
|
||||
}
|
||||
|
||||
shared_ptr<const Geometry> geom(this->evaluateGeometry(node));
|
||||
shared_ptr<const Geometry> geom(this->evaluateGeometry(node, true));
|
||||
|
||||
if (cache) GeometryCache::instance()->insert(cacheid, geom);
|
||||
return geom;
|
||||
|
@ -66,11 +66,25 @@ bool GeometryEvaluator::isCached(const AbstractNode &node) const
|
|||
}
|
||||
|
||||
// FIXME: This doesn't insert into cache. Fix this here or in client code
|
||||
shared_ptr<const Geometry> GeometryEvaluator::evaluateGeometry(const AbstractNode &node)
|
||||
/*!
|
||||
Set allownef to false to force the result to _not_ be a Nef polyhedron
|
||||
*/
|
||||
shared_ptr<const Geometry> GeometryEvaluator::evaluateGeometry(const AbstractNode &node,
|
||||
bool allownef)
|
||||
{
|
||||
if (!isCached(node)) {
|
||||
Traverser trav(*this, node, Traverser::PRE_AND_POSTFIX);
|
||||
trav.execute();
|
||||
|
||||
if (!allownef) {
|
||||
shared_ptr<const CGAL_Nef_polyhedron> N = dynamic_pointer_cast<const CGAL_Nef_polyhedron>(this->root);
|
||||
if (N) {
|
||||
if (N->getDimension() == 2) this->root.reset(N->convertToPolygon2d());
|
||||
else if (N->getDimension() == 3) this->root.reset(N->convertToPolyset());
|
||||
else this->root.reset();
|
||||
}
|
||||
}
|
||||
|
||||
return this->root;
|
||||
}
|
||||
return GeometryCache::instance()->get(this->tree.getIdString(node));
|
||||
|
@ -132,11 +146,74 @@ Geometry *GeometryEvaluator::applyToChildren3D(const AbstractNode &node, OpenSCA
|
|||
}
|
||||
|
||||
|
||||
Geometry *GeometryEvaluator::applyMinkowski2D(const AbstractNode &node)
|
||||
{
|
||||
std::vector<shared_ptr<const Polygon2d> > children = collectChildren2D(node);
|
||||
if (children.size() > 0) {
|
||||
bool first = false;
|
||||
ClipperLib::Polygons result = ClipperUtils::fromPolygon2d(*children[0]);
|
||||
for (int i=1;i<children.size();i++) {
|
||||
ClipperLib::Polygon &temp = result[0];
|
||||
const shared_ptr<const Polygon2d> &chgeom = children[i];
|
||||
ClipperLib::Polygon shape = ClipperUtils::fromOutline2d(chgeom->outlines()[0]);
|
||||
ClipperLib::MinkowkiSum(temp, shape, result, true);
|
||||
}
|
||||
|
||||
// The results may contain holes due to ClipperLib failing to maintain
|
||||
// solidity of minkowski results:
|
||||
// https://sourceforge.net/p/polyclipping/discussion/1148419/thread/8488d4e8/
|
||||
ClipperLib::Clipper clipper;
|
||||
BOOST_FOREACH(ClipperLib::Polygon &p, result) {
|
||||
if (ClipperLib::Orientation(p)) std::reverse(p.begin(), p.end());
|
||||
clipper.AddPath(p, ClipperLib::ptSubject, true);
|
||||
}
|
||||
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
|
||||
|
||||
return ClipperUtils::toPolygon2d(result);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::vector<shared_ptr<const Polygon2d> > GeometryEvaluator::collectChildren2D(const AbstractNode &node)
|
||||
{
|
||||
std::vector<shared_ptr<const Polygon2d> > children;
|
||||
BOOST_FOREACH(const ChildItem &item, this->visitedchildren[node.index()]) {
|
||||
const AbstractNode *chnode = item.first;
|
||||
const shared_ptr<const Geometry> &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.
|
||||
if (!isCached(*chnode)) {
|
||||
GeometryCache::instance()->insert(this->tree.getIdString(*chnode), chgeom);
|
||||
}
|
||||
|
||||
if (chgeom) {
|
||||
if (chgeom->getDimension() == 2) {
|
||||
shared_ptr<const Polygon2d> polygons = dynamic_pointer_cast<const Polygon2d>(chgeom);
|
||||
assert(polygons);
|
||||
children.push_back(polygons);
|
||||
}
|
||||
else {
|
||||
PRINT("ERROR: Only 2D children are supported by this operation!");
|
||||
}
|
||||
}
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
*/
|
||||
Geometry *GeometryEvaluator::applyToChildren2D(const AbstractNode &node, OpenSCADOperator op)
|
||||
{
|
||||
if (op == OPENSCAD_MINKOWSKI) {
|
||||
return applyMinkowski2D(node);
|
||||
}
|
||||
|
||||
ClipperLib::Clipper sumclipper;
|
||||
bool first = true;
|
||||
BOOST_FOREACH(const ChildItem &item, this->visitedchildren[node.index()]) {
|
||||
|
@ -156,6 +233,7 @@ Geometry *GeometryEvaluator::applyToChildren2D(const AbstractNode &node, OpenSCA
|
|||
if (chgeom) {
|
||||
if (chgeom->getDimension() == 2) {
|
||||
shared_ptr<const Polygon2d> polygons = dynamic_pointer_cast<const Polygon2d>(chgeom);
|
||||
// FIXME: This will trigger on e.g. linear_extrude of minkowski sums.
|
||||
assert(polygons);
|
||||
// The first Clipper operation will sanitize the polygon, ensuring
|
||||
// contours/holes have the correct winding order
|
||||
|
@ -738,14 +816,33 @@ Response GeometryEvaluator::visit(State &state, const CgaladvNode &node)
|
|||
if (state.isPostfix()) {
|
||||
shared_ptr<const Geometry> geom;
|
||||
if (!isCached(node)) {
|
||||
switch (node.type) {
|
||||
case MINKOWSKI: {
|
||||
const Geometry *geometry = applyToChildren(node, OPENSCAD_MINKOWSKI);
|
||||
geom.reset(geometry);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false && "not implemented");
|
||||
}
|
||||
// MINKOWSKI
|
||||
// 2D children -> Polygon2d, apply Clipper minkowski
|
||||
// 3D children -> Nef, apply CGAL minkowski
|
||||
// HULL
|
||||
// 2D children -> Polygon2d (or really point clouds), apply 2D hull (CGAL)
|
||||
// 3D children -> PolySet (or really point clouds), apply 2D hull (CGAL)
|
||||
// RESIZE
|
||||
// 2D children -> Polygon2d -> union -> apply resize
|
||||
// 3D children -> PolySet -> union -> apply resize
|
||||
|
||||
// if (node.type == RESIZE) {
|
||||
// const Geometry *geometry = applyToChildren2D(node, OPENSCAD_UNION);
|
||||
// // FIXME: find size and transform
|
||||
// }
|
||||
// else {
|
||||
CGAL_Nef_polyhedron N = this->cgalevaluator->evaluateCGALMesh(node);
|
||||
PolySet *ps = N.isNull() ? NULL : N.convertToPolyset();
|
||||
geom.reset(ps);
|
||||
// CGAL_Nef_polyhedron N = this->cgalevaluator->evaluateCGALMesh(node);
|
||||
// PolySet *ps = N.isNull() ? NULL : N.convertToPolyset();
|
||||
// geom.reset(ps);
|
||||
// }
|
||||
// FIXME: handle 3D
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <utility>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
class GeometryEvaluator : public Visitor
|
||||
|
@ -16,7 +17,7 @@ public:
|
|||
virtual ~GeometryEvaluator() {}
|
||||
|
||||
shared_ptr<const class Geometry> getGeometry(const AbstractNode &node, bool cache);
|
||||
shared_ptr<const class Geometry> evaluateGeometry(const AbstractNode &node);
|
||||
shared_ptr<const class Geometry> evaluateGeometry(const AbstractNode &node, bool allownef);
|
||||
|
||||
virtual Response visit(State &state, const AbstractNode &node);
|
||||
virtual Response visit(State &state, const AbstractPolyNode &node);
|
||||
|
@ -33,6 +34,8 @@ public:
|
|||
|
||||
private:
|
||||
bool isCached(const AbstractNode &node) const;
|
||||
std::vector<shared_ptr<const class Polygon2d> > collectChildren2D(const AbstractNode &node);
|
||||
Geometry *applyMinkowski2D(const AbstractNode &node);
|
||||
Geometry *applyToChildren2D(const AbstractNode &node, OpenSCADOperator op);
|
||||
Geometry *applyToChildren3D(const AbstractNode &node, OpenSCADOperator op);
|
||||
Geometry *applyToChildren(const AbstractNode &node, OpenSCADOperator op);
|
||||
|
|
|
@ -3,18 +3,22 @@
|
|||
|
||||
namespace ClipperUtils {
|
||||
|
||||
ClipperLib::Polygon fromOutline2d(const Outline2d &outline) {
|
||||
ClipperLib::Polygon p;
|
||||
BOOST_FOREACH(const Vector2d &v, outline) {
|
||||
p.push_back(ClipperLib::IntPoint(v[0]*CLIPPER_SCALE, v[1]*CLIPPER_SCALE));
|
||||
}
|
||||
// Make sure all polygons point up, since we project also
|
||||
// back-facing polygon in PolysetUtils::project()
|
||||
if (!ClipperLib::Orientation(p)) std::reverse(p.begin(), p.end());
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
ClipperLib::Polygons fromPolygon2d(const Polygon2d &poly) {
|
||||
ClipperLib::Polygons result;
|
||||
BOOST_FOREACH(const Outline2d &outline, poly.outlines()) {
|
||||
ClipperLib::Polygon p;
|
||||
BOOST_FOREACH(const Vector2d &v, outline) {
|
||||
p.push_back(ClipperLib::IntPoint(v[0]*CLIPPER_SCALE, v[1]*CLIPPER_SCALE));
|
||||
}
|
||||
// Make sure all polygons point up, since we project also
|
||||
// back-facing polygon in PolysetUtils::project()
|
||||
if (!ClipperLib::Orientation(p)) std::reverse(p.begin(), p.end());
|
||||
|
||||
result.push_back(p);
|
||||
result.push_back(fromOutline2d(outline));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,9 @@ namespace ClipperUtils {
|
|||
|
||||
static const unsigned int CLIPPER_SCALE = 100000;
|
||||
|
||||
ClipperLib::Polygons fromPolygon2d(const class Polygon2d &poly);
|
||||
ClipperLib::Polygon fromOutline2d(const Outline2d &poly);
|
||||
ClipperLib::Polygons fromPolygon2d(const Polygon2d &poly);
|
||||
Polygon2d *toPolygon2d(const ClipperLib::Polygon &poly);
|
||||
Polygon2d *toPolygon2d(const ClipperLib::Polygons &poly);
|
||||
ClipperLib::Polygons process(const ClipperLib::Polygons &polygons,
|
||||
ClipperLib::ClipType, ClipperLib::PolyFillType);
|
||||
|
|
Loading…
Reference in New Issue