Implemented 3D transform of PolySets, removed some Grid usage, improved PolySet -> Polyhedron conversion, optimized operations with only one child

527olive
Marius Kintel 2013-12-15 18:27:25 -05:00
parent 2fc3a39cfc
commit 698aa54998
6 changed files with 149 additions and 124 deletions

View File

@ -92,30 +92,39 @@ shared_ptr<const Geometry> 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<const Geometry> &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<const class Geometry> 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<const class Geometry> geom;
shared_ptr<const Geometry> 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<Polygon2d*>(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<const Polygon2d> polygons = dynamic_pointer_cast<const Polygon2d>(geom);
assert(polygons);
// If we got a const object, make a copy
shared_ptr<Polygon2d> newpoly;
if (res.isConst()) newpoly.reset(new Polygon2d(*polygons));
else newpoly = dynamic_pointer_cast<Polygon2d>(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<const PolySet> ps = dynamic_pointer_cast<const PolySet>(geom);
if (ps) {
// If we got a const object, make a copy
shared_ptr<PolySet> newps;
if (res.isConst()) newps.reset(new PolySet(*ps));
else newps = dynamic_pointer_cast<PolySet>(res.ptr());
newps->transform(node.matrix);
geom = newps;
}
else if (geometry->getDimension() == 3) {
CGAL_Nef_polyhedron *N = dynamic_cast<CGAL_Nef_polyhedron*>(geometry);
else {
shared_ptr<const CGAL_Nef_polyhedron> N = dynamic_pointer_cast<const CGAL_Nef_polyhedron>(geom);
assert(N);
N->transform(node.matrix);
geom.reset(N);
// If we got a const object, make a copy
shared_ptr<CGAL_Nef_polyhedron> newN;
if (res.isConst()) newN.reset(new CGAL_Nef_polyhedron(*N));
else newN = dynamic_pointer_cast<CGAL_Nef_polyhedron>(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<Vector3d> 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<const Polygon2d*>(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<const CGAL_Nef_polyhedron*>(geometry);
shared_ptr<const Geometry> newgeom = applyToChildren3D(node, OPENSCAD_UNION).constptr();
if (newgeom) {
shared_ptr<const CGAL_Nef_polyhedron> Nptr = dynamic_pointer_cast<const CGAL_Nef_polyhedron>(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<const Geometry> geom;
if (!isCached(node)) {
const Geometry *geometry = applyToChildren(node, OPENSCAD_UNION);
const CGAL_Nef_polyhedron *N = dynamic_cast<const CGAL_Nef_polyhedron*>(geometry);
geom = applyToChildren(node, OPENSCAD_UNION).constptr();
shared_ptr<const CGAL_Nef_polyhedron> N = dynamic_pointer_cast<const CGAL_Nef_polyhedron>(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));

View File

@ -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<const Geometry> &g) : is_const(true), const_pointer(g) {}
ResultObject(Geometry *g) : is_const(false), pointer(g) {}
ResultObject(shared_ptr<Geometry> &g) : is_const(false), pointer(g) {}
bool isConst() const { return is_const; }
shared_ptr<Geometry> ptr() { assert(!is_const); return pointer; }
shared_ptr<const Geometry> constptr() const {
return is_const ? const_pointer : static_pointer_cast<const Geometry>(pointer);
}
private:
bool is_const;
shared_ptr<Geometry> pointer;
shared_ptr<const Geometry> const_pointer;
};
bool isCached(const AbstractNode &node) const;
void smartCache(const AbstractNode &node, const shared_ptr<const Geometry> &geom);
std::vector<const class Polygon2d *> 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<const Geometry> &geom);
std::map<int, Geometry::ChildList> visitedchildren;

View File

@ -5,6 +5,7 @@
#include "printutils.h"
#include "Polygon2d.h"
#include "polyset-utils.h"
#include "grid.h"
#include "cgal.h"
#include <CGAL/convex_hull_3.h>
@ -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<CGAL_Point_3> 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 << "</svg>\n";
@ -243,73 +244,52 @@ public:
void operator()(CGAL_HDS& hds)
{
CGAL_Polybuilder B(hds, true);
typedef boost::tuple<double, double, double> BuilderVertex;
typedef std::map<BuilderVertex, size_t> BuilderMap;
BuilderMap vertices;
std::vector<size_t> indices(3);
std::vector<CGALPoint> vertices;
Grid3d<int> 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<int,int> 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<vertices.size();vidx++) {
if (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);

View File

@ -4,5 +4,6 @@
#include <boost/shared_ptr.hpp>
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
using boost::static_pointer_cast;
#endif

View File

@ -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<void*>);
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;
}
}
}

View File

@ -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<Vector3d> Polygon;
std::vector<Polygon> polygons;
Grid3d<void*> 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;
};