Made CGAL_Nef_polyhedron a Geometry subtype, implemented 3D transforms, implemented projection, implemented render

527olive
Marius Kintel 2013-11-21 01:25:15 -05:00
parent 44b5ee1d6d
commit 87f73263fc
13 changed files with 489 additions and 131 deletions

View File

@ -12,17 +12,17 @@ const CGAL_Nef_polyhedron &CGALCache::get(const std::string &id) const
{
const CGAL_Nef_polyhedron &N = *this->cache[id];
#ifdef DEBUG
PRINTB("CGAL Cache hit: %s (%d bytes)", id.substr(0, 40) % N.weight());
PRINTB("CGAL Cache hit: %s (%d bytes)", id.substr(0, 40) % N.memsize());
#endif
return N;
}
bool CGALCache::insert(const std::string &id, const CGAL_Nef_polyhedron &N)
{
bool inserted = this->cache.insert(id, new CGAL_Nef_polyhedron(N), N.weight());
bool inserted = this->cache.insert(id, new CGAL_Nef_polyhedron(N), N.memsize());
#ifdef DEBUG
if (inserted) PRINTB("CGAL Cache insert: %s (%d bytes)", id.substr(0, 40) % N.weight());
else PRINTB("CGAL Cache insert failed: %s (%d bytes)", id.substr(0, 40) % N.weight());
if (inserted) PRINTB("CGAL Cache insert: %s (%d bytes)", id.substr(0, 40) % N.memsize());
else PRINTB("CGAL Cache insert failed: %s (%d bytes)", id.substr(0, 40) % N.memsize());
#endif
return inserted;
}

View File

@ -61,12 +61,12 @@ bool CGALEvaluator::isCached(const AbstractNode &node) const
*/
void CGALEvaluator::process(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, OpenSCADOperator op)
{
if (target.dim != 2 && target.dim != 3) {
if (target.getDimension() != 2 && target.getDimension() != 3) {
assert(false && "Dimension of Nef polyhedron must be 2 or 3");
}
if (src.isEmpty()) return; // Empty polyhedron. This can happen for e.g. square([0,0])
if (target.isEmpty() && op != OPENSCAD_UNION) return; // empty op <something> => empty
if (target.dim != src.dim) return; // If someone tries to e.g. union 2d and 3d objects
if (target.getDimension() != src.getDimension()) 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 {
@ -136,11 +136,11 @@ CGAL_Nef_polyhedron CGALEvaluator::applyHull(const CgaladvNode &node)
const CGAL_Nef_polyhedron &chN = item.second;
// FIXME: Don't use deep access to modinst members
if (chnode->modinst->isBackground()) continue;
if (chN.dim == 0) continue; // Ignore object with dimension 0 (e.g. echo)
if (chN.getDimension() == 0) continue; // Ignore object with dimension 0 (e.g. echo)
if (dim == 0) {
dim = chN.dim;
dim = chN.getDimension();
}
else if (dim != chN.dim) {
else if (dim != chN.getDimension()) {
PRINT("WARNING: hull() does not support mixing 2D and 3D objects.");
continue;
}
@ -214,7 +214,7 @@ CGAL_Nef_polyhedron CGALEvaluator::applyResize(const CgaladvNode &node)
CGAL_Iso_cuboid_3 bb;
if ( N.dim == 2 ) {
if ( N.getDimension() == 2 ) {
CGAL_Iso_rectangle_2e bbox = bounding_box( *N.p2 );
CGAL_Point_2e min2(bbox.min()), max2(bbox.max());
CGAL_Point_3 min3(CGAL::to_double(min2.x()), CGAL::to_double(min2.y()), 0),
@ -231,7 +231,7 @@ CGAL_Nef_polyhedron CGALEvaluator::applyResize(const CgaladvNode &node)
bbox_size.push_back( bb.ymax()-bb.ymin() );
bbox_size.push_back( bb.zmax()-bb.zmin() );
int newsizemax_index = 0;
for (int i=0;i<N.dim;i++) {
for (int i=0;i<N.getDimension();i++) {
if (node.newsize[i]) {
if (bbox_size[i]==NT3(0)) {
PRINT("WARNING: Resize in direction normal to flat object is not implemented");
@ -248,7 +248,7 @@ CGAL_Nef_polyhedron CGALEvaluator::applyResize(const CgaladvNode &node)
NT3 autoscale = NT3( 1 );
if ( node.newsize[ newsizemax_index ] != 0 )
autoscale = NT3( node.newsize[ newsizemax_index ] ) / bbox_size[ newsizemax_index ];
for (int i=0;i<N.dim;i++) {
for (int i=0;i<N.getDimension();i++) {
if (node.autosize[i] && node.newsize[i]==0)
scale[i] = autoscale;
}
@ -346,7 +346,11 @@ Response CGALEvaluator::visit(State &state, const AbstractPolyNode &node)
if (!isCached(node)) {
// Apply polyset operation
shared_ptr<const Geometry> geom = this->geomevaluator.evaluateGeometry(node);
if (geom) N = createNefPolyhedronFromGeometry(*geom);
if (geom) {
CGAL_Nef_polyhedron *Nptr = createNefPolyhedronFromGeometry(*geom);
N = *Nptr;
delete Nptr;
}
node.progress_report();
}
else {

View File

@ -47,7 +47,7 @@ CGALRenderer::CGALRenderer(const CGAL_Nef_polyhedron &root) : root(root)
this->polyhedron = NULL;
this->polyset = NULL;
}
else if (root.dim == 2) {
else if (root.getDimension() == 2) {
DxfData *dd = root.convertToDxfData();
this->polyhedron = NULL;
this->polyset = new PolySet();
@ -55,7 +55,7 @@ CGALRenderer::CGALRenderer(const CGAL_Nef_polyhedron &root) : root(root)
dxf_tesselate(this->polyset, *dd, 0, Vector2d(1,1), true, false, 0);
delete dd;
}
else if (root.dim == 3) {
else if (root.getDimension() == 3) {
this->polyset = NULL;
this->polyhedron = new Polyhedron();
// FIXME: Make independent of Preferences
@ -82,7 +82,7 @@ CGALRenderer::~CGALRenderer()
void CGALRenderer::draw(bool showfaces, bool showedges) const
{
if (this->root.isNull()) return;
if (this->root.dim == 2) {
if (this->root.getDimension() == 2) {
// Draw 2D polygons
glDisable(GL_LIGHTING);
// FIXME: const QColor &col = Preferences::inst()->color(Preferences::CGAL_FACE_2D_COLOR);
@ -135,7 +135,7 @@ void CGALRenderer::draw(bool showfaces, bool showedges) const
glEnable(GL_DEPTH_TEST);
}
else if (this->root.dim == 3) {
else if (this->root.getDimension() == 3) {
if (showfaces) this->polyhedron->set_style(SNC_BOUNDARY);
else this->polyhedron->set_style(SNC_SKELETON);

View File

@ -59,7 +59,7 @@ CGAL_Nef_polyhedron &CGAL_Nef_polyhedron::minkowski(const CGAL_Nef_polyhedron &o
return *this;
}
int CGAL_Nef_polyhedron::weight() const
size_t CGAL_Nef_polyhedron::memsize() const
{
if (this->isNull()) return 0;

View File

@ -1,12 +1,13 @@
#ifndef CGAL_NEF_POLYHEDRON_H_
#define CGAL_NEF_POLYHEDRON_H_
#include "Geometry.h"
#include "cgal.h"
#include "memory.h"
#include <string>
#include "linalg.h"
class CGAL_Nef_polyhedron
class CGAL_Nef_polyhedron : public Geometry
{
public:
CGAL_Nef_polyhedron(int dim = 0) : dim(dim) {}
@ -14,6 +15,11 @@ public:
CGAL_Nef_polyhedron(CGAL_Nef_polyhedron3 *p);
~CGAL_Nef_polyhedron() {}
virtual size_t memsize() const;
virtual BoundingBox getBoundingBox() const {}; // FIXME: Implement
virtual std::string dump() const;
virtual unsigned int getDimension() const { return this->dim; }
// 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)
@ -24,14 +30,14 @@ public:
CGAL_Nef_polyhedron &operator-=(const CGAL_Nef_polyhedron &other);
CGAL_Nef_polyhedron &minkowski(const CGAL_Nef_polyhedron &other);
CGAL_Nef_polyhedron copy() const;
std::string dump() const;
int weight() const;
class PolySet *convertToPolyset();
class DxfData *convertToDxfData() const;
class Polygon2d *convertToPolygon2d() const;
void transform( const Transform3d &matrix );
int dim;
shared_ptr<CGAL_Nef_polyhedron2> p2;
shared_ptr<CGAL_Nef_polyhedron3> p3;
protected:
int dim;
};
#endif

View File

@ -25,6 +25,7 @@
*/
#include "dxfdata.h"
#include "Polygon2d.h"
#include "grid.h"
#include "CGAL_Nef_polyhedron.h"
#include "cgal.h"
@ -38,49 +39,74 @@
#ifdef ENABLE_CGAL
DxfData *CGAL_Nef_polyhedron::convertToDxfData() const
{
assert(this->dim == 2);
DxfData *dxfdata = new DxfData();
Grid2d<int> grid(GRID_COARSE);
typedef CGAL_Nef_polyhedron2::Explorer Explorer;
typedef Explorer::Face_const_iterator fci_t;
typedef Explorer::Halfedge_around_face_const_circulator heafcc_t;
Explorer E = this->p2->explorer();
for (fci_t fit = E.faces_begin(), facesend = E.faces_end(); fit != facesend; ++fit)
{
heafcc_t fcirc(E.halfedge(fit)), fend(fcirc);
int first_point = -1, last_point = -1;
CGAL_For_all(fcirc, fend) {
if (E.is_standard(E.target(fcirc))) {
Explorer::Point ep = E.point(E.target(fcirc));
double x = to_double(ep.x()), y = to_double(ep.y());
int this_point = -1;
if (grid.has(x, y)) {
this_point = grid.align(x, y);
} else {
this_point = grid.align(x, y) = dxfdata->points.size();
dxfdata->points.push_back(Vector2d(x, y));
}
if (first_point < 0) {
dxfdata->paths.push_back(DxfData::Path());
first_point = this_point;
}
if (this_point != last_point) {
dxfdata->paths.back().indices.push_back(this_point);
last_point = this_point;
}
}
}
if (first_point >= 0) {
dxfdata->paths.back().is_closed = 1;
dxfdata->paths.back().indices.push_back(first_point);
}
}
dxfdata->fixup_path_direction();
return dxfdata;
}
Polygon2d *CGAL_Nef_polyhedron::convertToPolygon2d() const
{
assert(this->dim == 2);
DxfData *dxfdata = new DxfData();
Grid2d<int> grid(GRID_COARSE);
Polygon2d *poly = new Polygon2d;
typedef CGAL_Nef_polyhedron2::Explorer Explorer;
typedef Explorer::Face_const_iterator fci_t;
typedef Explorer::Halfedge_around_face_const_circulator heafcc_t;
Explorer E = this->p2->explorer();
for (fci_t fit = E.faces_begin(), facesend = E.faces_end(); fit != facesend; ++fit)
{
for (fci_t fit = E.faces_begin(), facesend = E.faces_end(); fit != facesend; ++fit) {
heafcc_t fcirc(E.halfedge(fit)), fend(fcirc);
int first_point = -1, last_point = -1;
Outline2d outline;
CGAL_For_all(fcirc, fend) {
if (E.is_standard(E.target(fcirc))) {
Explorer::Point ep = E.point(E.target(fcirc));
double x = to_double(ep.x()), y = to_double(ep.y());
int this_point = -1;
if (grid.has(x, y)) {
this_point = grid.align(x, y);
} else {
this_point = grid.align(x, y) = dxfdata->points.size();
dxfdata->points.push_back(Vector2d(x, y));
}
if (first_point < 0) {
dxfdata->paths.push_back(DxfData::Path());
first_point = this_point;
}
if (this_point != last_point) {
dxfdata->paths.back().indices.push_back(this_point);
last_point = this_point;
}
outline.push_back(Vector2d(to_double(ep.x()),
to_double(ep.y())));
}
}
if (first_point >= 0) {
dxfdata->paths.back().is_closed = 1;
dxfdata->paths.back().indices.push_back(first_point);
}
if (outline.size() > 0) poly->addOutline(outline);
}
dxfdata->fixup_path_direction();
return dxfdata;
return poly;
}
std::string CGAL_Nef_polyhedron::dump() const
@ -125,7 +151,9 @@ void CGAL_Nef_polyhedron::transform( const Transform3d &matrix )
ps.is2d = true;
dxf_tesselate(&ps, *dd, 0, Vector2d(1,1), true, false, 0);
CGAL_Nef_polyhedron N = createNefPolyhedronFromGeometry(ps);
CGAL_Nef_polyhedron *Nptr = createNefPolyhedronFromGeometry(ps);
CGAL_Nef_polyhedron N = *Nptr;
delete Nptr;
if (N.p2) this->p2.reset(new CGAL_Nef_polyhedron2(*N.p2));
delete dd;
}

View File

@ -10,11 +10,18 @@
#include "rotateextrudenode.h"
#include "csgnode.h"
#include "cgaladvnode.h"
#include "projectionnode.h"
#include "CGAL_Nef_polyhedron.h"
#include "cgalutils.h"
#include <CGAL/convex_hull_3.h>
#include "rendernode.h"
#include "clipper-utils.h"
#include "CGALEvaluator.h"
#include "CGALCache.h"
#include "PolySet.h"
#include "openscad.h" // get_fragments_from_r()
#include "printutils.h"
#include "svg.h"
#include <boost/foreach.hpp>
@ -66,37 +73,136 @@ shared_ptr<const Geometry> GeometryEvaluator::evaluateGeometry(const AbstractNod
return GeometryCache::instance()->get(this->tree.getIdString(node));
}
/*!
*/
Geometry *GeometryEvaluator::applyToChildren2D(const AbstractNode &node, OpenSCADOperator op)
Geometry *GeometryEvaluator::applyToChildren(const AbstractNode &node, OpenSCADOperator op)
{
// FIXME: Support other operators than UNION
Polygon2d sum;
unsigned int dim = 0;
BOOST_FOREACH(const ChildItem &item, this->visitedchildren[node.index()]) {
if (item.second) {
if (!dim) dim = item.second->getDimension();
else if (dim != item.second->getDimension()) {
return NULL;
}
}
}
if (dim == 2) return applyToChildren2D(node, op);
else if (dim == 3) return applyToChildren3D(node, op);
return NULL;
}
/*!
Modifies target by applying op to target and src:
target = target [op] src
*/
static void process(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, OpenSCADOperator op)
{
if (target.getDimension() != 2 && target.getDimension() != 3) {
assert(false && "Dimension of Nef polyhedron must be 2 or 3");
}
if (src.isEmpty()) return; // Empty polyhedron. This can happen for e.g. square([0,0])
if (target.isEmpty() && op != OPENSCAD_UNION) return; // empty op <something> => empty
if (target.getDimension() != src.getDimension()) 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 OPENSCAD_UNION:
if (target.isEmpty()) target = src.copy();
else target += src;
break;
case OPENSCAD_INTERSECTION:
target *= src;
break;
case OPENSCAD_DIFFERENCE:
target -= src;
break;
case OPENSCAD_MINKOWSKI:
target.minkowski(src);
break;
}
}
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());
// Errors can result in corrupt polyhedrons, so put back the old one
target = src;
}
CGAL::set_error_behaviour(old_behaviour);
}
Geometry *GeometryEvaluator::applyToChildren3D(const AbstractNode &node, OpenSCADOperator op)
{
CGAL_Nef_polyhedron *N = new CGAL_Nef_polyhedron;
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;
shared_ptr<const CGAL_Nef_polyhedron> chN = dynamic_pointer_cast<const CGAL_Nef_polyhedron>(chgeom);
if (!chN) {
shared_ptr<const PolySet> chP = dynamic_pointer_cast<const PolySet>(chgeom);
if (chP) chN.reset(createNefPolyhedronFromGeometry(*chP));
}
// 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);
GeometryCache::instance()->insert(this->tree.getIdString(*chnode), chN);
}
if (chgeom->getDimension() == 2) {
shared_ptr<const Polygon2d> polygons = dynamic_pointer_cast<const Polygon2d>(chgeom);
assert(polygons);
BOOST_FOREACH(const Outline2d &o, polygons->outlines()) {
sum.addOutline(o);
if (chgeom) {
if (chgeom->getDimension() == 3) {
// Initialize N on first iteration with first expected geometric object
if (N->isNull() && !N->isEmpty()) *N = chN->copy();
else process(*N, *chN, op);
}
else {
// FIXME: Fix error message
PRINT("ERROR: this operation is not defined for 2D child objects!");
}
}
else {
PRINT("ERROR: linear_extrude() is not defined for 3D child objects!");
chnode->progress_report();
}
return N;
}
/*!
*/
Geometry *GeometryEvaluator::applyToChildren2D(const AbstractNode &node, OpenSCADOperator op)
{
Polygon2d sum;
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);
BOOST_FOREACH(const Outline2d &o, polygons->outlines()) {
sum.addOutline(o);
}
}
else {
// FIXME: Wrong error message
PRINT("ERROR: linear_extrude() is not defined for 3D child objects!");
}
}
chnode->progress_report();
}
@ -137,6 +243,8 @@ Geometry *GeometryEvaluator::applyToChildren2D(const AbstractNode &node, OpenSCA
Usually, this should be called from the postfix stage, but for some nodes,
we defer traversal letting other components (e.g. CGAL) render the subgraph,
and we'll then call this from prefix and prune further traversal.
The added geometry can be NULL if it wasn't possible to evaluate it.
*/
void GeometryEvaluator::addToParent(const State &state,
const AbstractNode &node,
@ -166,16 +274,13 @@ Response GeometryEvaluator::visit(State &state, const AbstractNode &node)
if (state.isPostfix()) {
shared_ptr<const class Geometry> geom;
if (!isCached(node)) {
const Geometry *geometry = applyToChildren2D(node, OPENSCAD_UNION);
const Polygon2d *polygons = dynamic_cast<const Polygon2d*>(geometry);
assert(polygons);
const Geometry *geometry = applyToChildren(node, OPENSCAD_UNION);
geom.reset(geometry);
}
else {
geom = GeometryCache::instance()->get(this->tree.getIdString(node));
}
addToParent(state, node, geom);
// FIXME: if 3d node, CGAL?
}
return ContinueTraversal;
}
@ -205,6 +310,9 @@ Response GeometryEvaluator::visit(State &state, const AbstractNode &node)
/*!
Leaf nodes can create their own geometry, so let them do that
input: None
output: PolySet or Polygon2d
*/
Response GeometryEvaluator::visit(State &state, const LeafNode &node)
{
@ -219,26 +327,36 @@ Response GeometryEvaluator::visit(State &state, const LeafNode &node)
return PruneTraversal;
}
/*!
input: List of 2D or 3D objects (not mixed)
output: Polygon2d or 3D PolySet
operation:
o Perform csg op on children
*/
Response GeometryEvaluator::visit(State &state, const CsgNode &node)
{
if (state.isPrefix() && isCached(node)) return PruneTraversal;
if (state.isPostfix()) {
shared_ptr<const class Geometry> geom;
if (!isCached(node)) {
const Geometry *geometry = applyToChildren2D(node, node.type);
const Polygon2d *polygons = dynamic_cast<const Polygon2d*>(geometry);
assert(polygons);
const Geometry *geometry = applyToChildren(node, node.type);
geom.reset(geometry);
}
else {
geom = GeometryCache::instance()->get(this->tree.getIdString(node));
}
addToParent(state, node, geom);
// FIXME: if 3d node, CGAL?
}
return ContinueTraversal;
}
/*!
input: List of 2D or 3D objects (not mixed)
output: Polygon2d or 3D PolySet
operation:
o Union all children
o Perform transform
*/
Response GeometryEvaluator::visit(State &state, const TransformNode &node)
{
if (state.isPrefix() && isCached(node)) return PruneTraversal;
@ -251,20 +369,25 @@ Response GeometryEvaluator::visit(State &state, const TransformNode &node)
}
else {
// First union all children
Geometry *geometry = applyToChildren2D(node, OPENSCAD_UNION);
Polygon2d *polygons = dynamic_cast<Polygon2d*>(geometry);
//FIXME: Handle 2D vs. 3D
if (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);
}
else {
// FIXME: Handle 3D transform
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);
}
else if (geometry->getDimension() == 3) {
CGAL_Nef_polyhedron *N = dynamic_cast<CGAL_Nef_polyhedron*>(geometry);
assert(N);
N->transform(node.matrix);
geom.reset(N);
}
}
}
}
@ -382,6 +505,13 @@ static Geometry *extrudePolygon(const LinearExtrudeNode &node, const Polygon2d &
return ps;
}
/*!
input: List of 2D objects
output: 3D PolySet
operation:
o Union all children
o Perform extrude
*/
Response GeometryEvaluator::visit(State &state, const LinearExtrudeNode &node)
{
if (state.isPrefix() && isCached(node)) return PruneTraversal;
@ -451,6 +581,13 @@ static Geometry *rotatePolygon(const RotateExtrudeNode &node, const Polygon2d &p
return ps;
}
/*!
input: List of 2D objects
output: 3D PolySet
operation:
o Union all children
o Perform extrude
*/
Response GeometryEvaluator::visit(State &state, const RotateExtrudeNode &node)
{
if (state.isPrefix() && isCached(node)) return PruneTraversal;
@ -474,31 +611,178 @@ Response GeometryEvaluator::visit(State &state, const RotateExtrudeNode &node)
}
/*!
Handles non-leaf PolyNodes; extrusions, projection
Handles non-leaf PolyNodes; projection
*/
Response GeometryEvaluator::visit(State &state, const AbstractPolyNode &node)
{
assert(false && "Implement");
if (state.isPrefix() && isCached(node)) return PruneTraversal;
if (state.isPrefix()) {
/*
shared_ptr<CSGTerm> t1;
if (this->psevaluator) {
shared_ptr<const Geometry> geom = this->psevaluator->getGeometry(node, true);
if (geom) {
t1 = evaluate_csg_term_from_geometry(state, this->highlights, this->background,
geom, node.modinst, node);
node.progress_report();
}
}
this->stored_term[node.index()] = t1;
addToParent(state, node);
*/
return PruneTraversal;
}
return ContinueTraversal;
assert(false);
}
static CGAL_Nef_polyhedron project_node(const ProjectionNode &node,
const CGAL_Nef_polyhedron &N)
{
CGAL_Nef_polyhedron &inputN = const_cast<CGAL_Nef_polyhedron&>(N);
logstream log(5);
CGAL_Nef_polyhedron nef_poly(2);
if (inputN.getDimension() != 3) return nef_poly;
CGAL_Nef_polyhedron newN;
if (node.cut_mode) {
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
CGAL_Nef_polyhedron3::Plane_3 xy_plane = CGAL_Nef_polyhedron3::Plane_3(0,0,1,0);
newN.p3.reset(new CGAL_Nef_polyhedron3(inputN.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());
try {
PRINT("Trying alternative intersection using very large thin box: ");
std::vector<CGAL_Point_3> pts;
// dont use z of 0. there are bugs in CGAL.
double inf = 1e8;
double eps = 0.001;
CGAL_Point_3 minpt( -inf, -inf, -eps );
CGAL_Point_3 maxpt( inf, inf, eps );
CGAL_Iso_cuboid_3 bigcuboid( minpt, maxpt );
for ( int i=0;i<8;i++ ) pts.push_back( bigcuboid.vertex(i) );
CGAL_Polyhedron bigbox;
CGAL::convex_hull_3(pts.begin(), pts.end(), bigbox);
CGAL_Nef_polyhedron3 nef_bigbox( bigbox );
newN.p3.reset(new CGAL_Nef_polyhedron3(nef_bigbox.intersection(*inputN.p3)));
}
catch (const CGAL::Failure_exception &e) {
PRINTB("CGAL error in projection node during bigbox intersection: %s", e.what());
}
}
if (!newN.p3 || newN.p3->is_empty()) {
CGAL::set_error_behaviour(old_behaviour);
PRINT("WARNING: projection() failed.");
return nef_poly;
}
log << OpenSCAD::svg_header( 480, 100000 ) << "\n";
try {
ZRemover zremover;
CGAL_Nef_polyhedron3::Volume_const_iterator i;
CGAL_Nef_polyhedron3::Shell_entry_const_iterator j;
CGAL_Nef_polyhedron3::SFace_const_handle sface_handle;
for ( i = newN.p3->volumes_begin(); i != newN.p3->volumes_end(); ++i ) {
log << "<!-- volume. mark: " << i->mark() << " -->\n";
for ( j = i->shells_begin(); j != i->shells_end(); ++j ) {
log << "<!-- shell. mark: " << i->mark() << " -->\n";
sface_handle = CGAL_Nef_polyhedron3::SFace_const_handle( j );
newN.p3->visit_shell_objects( sface_handle , zremover );
log << "<!-- shell. end. -->\n";
}
log << "<!-- volume end. -->\n";
}
nef_poly.p2 = zremover.output_nefpoly2d;
} catch (const CGAL::Failure_exception &e) {
PRINTB("CGAL error in projection node while flattening: %s", e.what());
}
log << "</svg>\n";
CGAL::set_error_behaviour(old_behaviour);
}
// In projection mode all the triangles are projected manually into the XY plane
else {
PolySet *ps3 = inputN.convertToPolyset();
if (!ps3) return nef_poly;
for (size_t i = 0; i < ps3->polygons.size(); i++) {
int min_x_p = -1;
double min_x_val = 0;
for (size_t j = 0; j < ps3->polygons[i].size(); j++) {
double x = ps3->polygons[i][j][0];
if (min_x_p < 0 || x < min_x_val) {
min_x_p = j;
min_x_val = x;
}
}
int min_x_p1 = (min_x_p+1) % ps3->polygons[i].size();
int min_x_p2 = (min_x_p+ps3->polygons[i].size()-1) % ps3->polygons[i].size();
double ax = ps3->polygons[i][min_x_p1][0] - ps3->polygons[i][min_x_p][0];
double ay = ps3->polygons[i][min_x_p1][1] - ps3->polygons[i][min_x_p][1];
double at = atan2(ay, ax);
double bx = ps3->polygons[i][min_x_p2][0] - ps3->polygons[i][min_x_p][0];
double by = ps3->polygons[i][min_x_p2][1] - ps3->polygons[i][min_x_p][1];
double bt = atan2(by, bx);
double eps = 0.000001;
if (fabs(at - bt) < eps || (fabs(ax) < eps && fabs(ay) < eps) ||
(fabs(bx) < eps && fabs(by) < eps)) {
// this triangle is degenerated in projection
continue;
}
std::list<CGAL_Nef_polyhedron2::Point> plist;
for (size_t j = 0; j < ps3->polygons[i].size(); j++) {
double x = ps3->polygons[i][j][0];
double y = ps3->polygons[i][j][1];
CGAL_Nef_polyhedron2::Point p = CGAL_Nef_polyhedron2::Point(x, y);
if (at > bt)
plist.push_front(p);
else
plist.push_back(p);
}
// FIXME: Should the CGAL_Nef_polyhedron2 be cached?
if (nef_poly.isEmpty()) {
nef_poly.p2.reset(new CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED));
}
else {
(*nef_poly.p2) += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
}
}
delete ps3;
}
return nef_poly;
}
/*!
input: List of 3D objects
output: Polygon2d
operation:
o Union all children
o Perform projection
*/
Response GeometryEvaluator::visit(State &state, const ProjectionNode &node)
{
if (state.isPrefix() && isCached(node)) return PruneTraversal;
if (state.isPostfix()) {
shared_ptr<const class Geometry> geom;
if (!isCached(node)) {
const Geometry *geometry = applyToChildren3D(node, OPENSCAD_UNION);
if (geometry) {
const CGAL_Nef_polyhedron *Nptr = dynamic_cast<const CGAL_Nef_polyhedron*>(geometry);
if (!Nptr) {
// FIXME: delete this object
Nptr = createNefPolyhedronFromGeometry(*geometry);
}
if (!Nptr->isNull()) {
CGAL_Nef_polyhedron nef_poly = project_node(node, *Nptr);
Polygon2d *poly = nef_poly.convertToPolygon2d();
assert(poly);
poly->setConvexity(node.convexity);
geom.reset(poly);
delete geometry;
}
}
}
else {
geom = GeometryCache::instance()->get(this->tree.getIdString(node));
}
addToParent(state, node, geom);
}
return ContinueTraversal;
}
/*!
input: List of 2D or 3D objects (not mixed)
output: PolySet (FIXME: implement Polygon2d)
operation:
o Perform cgal operation
*/
Response GeometryEvaluator::visit(State &state, const CgaladvNode &node)
{
if (state.isPrefix() && isCached(node)) return PruneTraversal;
@ -525,3 +809,36 @@ Response GeometryEvaluator::visit(State &state, const CgaladvNode &node)
return ContinueTraversal;
}
/*!
input: List of 2D or 3D objects (not mixed)
output: PolySet (FIXME: implement Polygon2d?)
operation:
o Render to PolySet (using CGAL or Clipper)
*/
Response GeometryEvaluator::visit(State &state, const RenderNode &node)
{
if (state.isPrefix() && isCached(node)) return PruneTraversal;
if (state.isPostfix()) {
shared_ptr<const Geometry> geom;
if (!isCached(node)) {
// FIXME: Handle 2D nodes separately
CGAL_Nef_polyhedron N = this->cgalevaluator->evaluateCGALMesh(node);
PolySet *ps = NULL;
if (!N.isNull()) {
if (N.getDimension() == 3 && !N.p3->is_simple()) {
PRINT("WARNING: Body of render() isn't valid 2-manifold!");
}
else {
ps = N.convertToPolyset();
if (ps) ps->setConvexity(node.convexity);
}
}
geom.reset(ps);
}
else {
geom = GeometryCache::instance()->get(this->tree.getIdString(node));
}
addToParent(state, node, geom);
}
return ContinueTraversal;
}

View File

@ -26,12 +26,16 @@ public:
virtual Response visit(State &state, const TransformNode &node);
virtual Response visit(State &state, const CsgNode &node);
virtual Response visit(State &state, const CgaladvNode &node);
virtual Response visit(State &state, const RenderNode &node);
virtual Response visit(State &state, const ProjectionNode &node);
const Tree &getTree() const { return this->tree; }
private:
bool isCached(const AbstractNode &node) const;
Geometry *applyToChildren2D(const AbstractNode &node, OpenSCADOperator op);
Geometry *applyToChildren3D(const AbstractNode &node, OpenSCADOperator op);
Geometry *applyToChildren(const AbstractNode &node, OpenSCADOperator op);
void addToParent(const State &state, const AbstractNode &node, const shared_ptr<const Geometry> &geom);
typedef std::pair<const AbstractNode *, shared_ptr<const Geometry> > ChildItem;

View File

@ -41,7 +41,7 @@ Geometry *PolySetCGALEvaluator::evaluateGeometry(const ProjectionNode &node)
BOOST_FOREACH (AbstractNode * v, node.getChildren()) {
if (v->modinst->isBackground()) continue;
CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v);
if (N.dim == 3) {
if (N.getDimension() == 3) {
if (sum.isNull()) sum = N.copy();
else sum += N;
}
@ -500,7 +500,7 @@ Geometry *PolySetCGALEvaluator::evaluateGeometry(const RotateExtrudeNode &node)
if (v->modinst->isBackground()) continue;
CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v);
if (!N.isNull()) {
if (N.dim != 2) {
if (N.getDimension() != 2) {
PRINT("ERROR: rotate_extrude() is not defined for 3D child objects!");
}
else {
@ -538,7 +538,7 @@ Geometry *PolySetCGALEvaluator::evaluateGeometry(const RenderNode &node)
CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(node);
PolySet *ps = NULL;
if (!N.isNull()) {
if (N.dim == 3 && !N.p3->is_simple()) {
if (N.getDimension() == 3 && !N.p3->is_simple()) {
PRINT("WARNING: Body of render() isn't valid 2-manifold!");
}
else {

View File

@ -224,9 +224,9 @@ void ZRemover::visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet )
log << " <!-- ZRemover Halffacet visit end -->\n";
}
static CGAL_Nef_polyhedron createNefPolyhedronFromPolySet(const PolySet &ps)
static CGAL_Nef_polyhedron *createNefPolyhedronFromPolySet(const PolySet &ps)
{
if (ps.empty()) return CGAL_Nef_polyhedron(ps.is2d ? 2 : 3);
if (ps.empty()) return new CGAL_Nef_polyhedron(ps.is2d ? 2 : 3);
if (ps.is2d)
{
@ -259,7 +259,7 @@ static CGAL_Nef_polyhedron createNefPolyhedronFromPolySet(const PolySet &ps)
}
CGAL_Nef_polyhedron2 N(pdata.begin(), pdata.end(), CGAL_Nef_polyhedron2::POLYGONS);
return CGAL_Nef_polyhedron(N);
return new CGAL_Nef_polyhedron(N);
#endif
#if 0
// This version of the code works fine but is pretty slow.
@ -284,7 +284,7 @@ static CGAL_Nef_polyhedron createNefPolyhedronFromPolySet(const PolySet &ps)
N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
}
return CGAL_Nef_polyhedron(N);
return new CGAL_Nef_polyhedron(N);
#endif
#if 1
// This version of the code does essentially the same thing as the 2nd
@ -440,7 +440,7 @@ static CGAL_Nef_polyhedron createNefPolyhedronFromPolySet(const PolySet &ps)
PolyReducer pr(ps);
pr.reduce();
return CGAL_Nef_polyhedron(pr.toNef());
return new CGAL_Nef_polyhedron(pr.toNef());
#endif
#if 0
// This is another experimental version. I should run faster than the above,
@ -471,7 +471,7 @@ static CGAL_Nef_polyhedron createNefPolyhedronFromPolySet(const PolySet &ps)
N ^= CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
}
return CGAL_Nef_polyhedron(N);
return new CGAL_Nef_polyhedron(N);
#endif
}
@ -490,19 +490,18 @@ static CGAL_Nef_polyhedron createNefPolyhedronFromPolySet(const PolySet &ps)
PRINTB("CGAL error in CGAL_Nef_polyhedron3(): %s", e.what());
}
CGAL::set_error_behaviour(old_behaviour);
return CGAL_Nef_polyhedron(N);
return new CGAL_Nef_polyhedron(N);
}
return CGAL_Nef_polyhedron();
return NULL;
}
static CGAL_Nef_polyhedron createNefPolyhedronFromPolygon2d(const Polygon2d &polygon)
static CGAL_Nef_polyhedron *createNefPolyhedronFromPolygon2d(const Polygon2d &polygon)
{
shared_ptr<PolySet> ps(polygon.tessellate());
return createNefPolyhedronFromPolySet(*ps);
}
CGAL_Nef_polyhedron createNefPolyhedronFromGeometry(const Geometry &geom)
CGAL_Nef_polyhedron *createNefPolyhedronFromGeometry(const Geometry &geom)
{
const PolySet *ps = dynamic_cast<const PolySet*>(&geom);
if (ps) {
@ -513,7 +512,7 @@ CGAL_Nef_polyhedron createNefPolyhedronFromGeometry(const Geometry &geom)
if (poly2d) return createNefPolyhedronFromPolygon2d(*poly2d);
}
assert(false && "CGALEvaluator::evaluateCGALMesh(): Unsupported geometry type");
return CGAL_Nef_polyhedron();
return NULL;
}
#endif /* ENABLE_CGAL */

View File

@ -5,7 +5,7 @@
#include "polyset.h"
#include "CGAL_Nef_polyhedron.h"
CGAL_Nef_polyhedron createNefPolyhedronFromGeometry(const class Geometry &geom);
CGAL_Nef_polyhedron *createNefPolyhedronFromGeometry(const class Geometry &geom);
bool createPolySetFromPolyhedron(const CGAL_Polyhedron &p, PolySet &ps);
bool createPolyhedronFromPolySet(const PolySet &ps, CGAL_Polyhedron &p);
CGAL_Iso_cuboid_3 bounding_box( const CGAL_Nef_polyhedron3 &N );

View File

@ -1315,7 +1315,7 @@ void MainWindow::actionRenderCGALDone(CGAL_Nef_polyhedron *root_N)
CGALCache::instance()->print();
#endif
if (!root_N->isNull()) {
if (root_N->dim == 2) {
if (root_N->getDimension() == 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"));
@ -1327,7 +1327,7 @@ void MainWindow::actionRenderCGALDone(CGAL_Nef_polyhedron *root_N)
PRINTB(" ConnComp: %6d", root_N->p2->explorer().number_of_connected_components());
}
if (root_N->dim == 3) {
if (root_N->getDimension() == 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"));
@ -1441,7 +1441,7 @@ void MainWindow::actionExportSTLorOFF(bool)
return;
}
if (this->root_N->dim != 3) {
if (this->root_N->getDimension() != 3) {
PRINT("Current top level object is not a 3D object.");
clearCurrentOutput();
return;
@ -1501,7 +1501,7 @@ void MainWindow::actionExportDXF()
return;
}
if (this->root_N->dim != 2) {
if (this->root_N->getDimension() != 2) {
PRINT("Current top level object is not a 2D object.");
clearCurrentOutput();
return;

View File

@ -359,7 +359,7 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
}
if (stl_output_file) {
if (root_N.dim != 3) {
if (root_N.getDimension() != 3) {
PRINT("Current top level object is not a 3D object.\n");
return 1;
}
@ -378,7 +378,7 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
}
if (off_output_file) {
if (root_N.dim != 3) {
if (root_N.getDimension() != 3) {
PRINT("Current top level object is not a 3D object.\n");
return 1;
}
@ -397,7 +397,7 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
}
if (dxf_output_file) {
if (root_N.dim != 2) {
if (root_N.getDimension() != 2) {
PRINT("Current top level object is not a 2D object.\n");
return 1;
}