mirror of https://github.com/vitalif/openscad
Moved shared CGAL operations to cgalutils
parent
97f0155d9c
commit
73256de438
|
@ -55,48 +55,6 @@ bool CGALEvaluator::isCached(const AbstractNode &node) const
|
||||||
return CGALCache::instance()->contains(this->tree.getIdString(node));
|
return CGALCache::instance()->contains(this->tree.getIdString(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Modifies target by applying op to target and src:
|
|
||||||
target = target [op] src
|
|
||||||
*/
|
|
||||||
void CGALEvaluator::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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
*/
|
*/
|
||||||
CGAL_Nef_polyhedron CGALEvaluator::applyToChildren(const AbstractNode &node, OpenSCADOperator op)
|
CGAL_Nef_polyhedron CGALEvaluator::applyToChildren(const AbstractNode &node, OpenSCADOperator op)
|
||||||
|
@ -117,7 +75,7 @@ CGAL_Nef_polyhedron CGALEvaluator::applyToChildren(const AbstractNode &node, Ope
|
||||||
}
|
}
|
||||||
// Initialize N on first iteration with first expected geometric object
|
// Initialize N on first iteration with first expected geometric object
|
||||||
if (N.isNull() && !N.isEmpty()) N = chN.copy();
|
if (N.isNull() && !N.isEmpty()) N = chN.copy();
|
||||||
else process(N, chN, op);
|
else CGAL_binary_operator(N, chN, op);
|
||||||
|
|
||||||
chnode->progress_report();
|
chnode->progress_report();
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,12 +77,9 @@ size_t CGAL_Nef_polyhedron::memsize() const
|
||||||
/*!
|
/*!
|
||||||
Creates a new PolySet and initializes it with the data from this polyhedron
|
Creates a new PolySet and initializes it with the data from this polyhedron
|
||||||
|
|
||||||
This method is not const since convert_to_Polyhedron() wasn't const
|
|
||||||
in earlier versions of CGAL.
|
|
||||||
|
|
||||||
Note: Can return NULL if an error occurred
|
Note: Can return NULL if an error occurred
|
||||||
*/
|
*/
|
||||||
PolySet *CGAL_Nef_polyhedron::convertToPolyset()
|
PolySet *CGAL_Nef_polyhedron::convertToPolyset() const
|
||||||
{
|
{
|
||||||
if (this->isNull()) return new PolySet();
|
if (this->isNull()) return new PolySet();
|
||||||
PolySet *ps = NULL;
|
PolySet *ps = NULL;
|
||||||
|
@ -101,7 +98,10 @@ PolySet *CGAL_Nef_polyhedron::convertToPolyset()
|
||||||
std::string errmsg("");
|
std::string errmsg("");
|
||||||
CGAL_Polyhedron P;
|
CGAL_Polyhedron P;
|
||||||
try {
|
try {
|
||||||
err = nefworkaround::convert_to_Polyhedron<CGAL_Kernel3>( *(this->p3), P );
|
// Cast away constness:
|
||||||
|
// convert_to_Polyhedron() wasn't const in earlier versions of CGAL.
|
||||||
|
CGAL_Nef_polyhedron3 *nonconst_nef3 = const_cast<CGAL_Nef_polyhedron3*>(this->p3.get());
|
||||||
|
err = nefworkaround::convert_to_Polyhedron<CGAL_Kernel3>( *(nonconst_nef3), P );
|
||||||
//this->p3->convert_to_Polyhedron(P);
|
//this->p3->convert_to_Polyhedron(P);
|
||||||
}
|
}
|
||||||
catch (const CGAL::Failure_exception &e) {
|
catch (const CGAL::Failure_exception &e) {
|
||||||
|
|
|
@ -30,7 +30,7 @@ public:
|
||||||
CGAL_Nef_polyhedron &operator-=(const CGAL_Nef_polyhedron &other);
|
CGAL_Nef_polyhedron &operator-=(const CGAL_Nef_polyhedron &other);
|
||||||
CGAL_Nef_polyhedron &minkowski(const CGAL_Nef_polyhedron &other);
|
CGAL_Nef_polyhedron &minkowski(const CGAL_Nef_polyhedron &other);
|
||||||
CGAL_Nef_polyhedron copy() const;
|
CGAL_Nef_polyhedron copy() const;
|
||||||
class PolySet *convertToPolyset();
|
class PolySet *convertToPolyset() const;
|
||||||
class DxfData *convertToDxfData() const;
|
class DxfData *convertToDxfData() const;
|
||||||
class Polygon2d *convertToPolygon2d() const;
|
class Polygon2d *convertToPolygon2d() const;
|
||||||
void transform( const Transform3d &matrix );
|
void transform( const Transform3d &matrix );
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include "projectionnode.h"
|
#include "projectionnode.h"
|
||||||
#include "CGAL_Nef_polyhedron.h"
|
#include "CGAL_Nef_polyhedron.h"
|
||||||
#include "cgalutils.h"
|
#include "cgalutils.h"
|
||||||
#include <CGAL/convex_hull_3.h>
|
|
||||||
#include "rendernode.h"
|
#include "rendernode.h"
|
||||||
#include "clipper-utils.h"
|
#include "clipper-utils.h"
|
||||||
#include "CGALEvaluator.h"
|
#include "CGALEvaluator.h"
|
||||||
|
@ -91,48 +90,6 @@ Geometry *GeometryEvaluator::applyToChildren(const AbstractNode &node, OpenSCADO
|
||||||
return NULL;
|
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)
|
Geometry *GeometryEvaluator::applyToChildren3D(const AbstractNode &node, OpenSCADOperator op)
|
||||||
{
|
{
|
||||||
CGAL_Nef_polyhedron *N = new CGAL_Nef_polyhedron;
|
CGAL_Nef_polyhedron *N = new CGAL_Nef_polyhedron;
|
||||||
|
@ -160,7 +117,7 @@ Geometry *GeometryEvaluator::applyToChildren3D(const AbstractNode &node, OpenSCA
|
||||||
if (chgeom->getDimension() == 3) {
|
if (chgeom->getDimension() == 3) {
|
||||||
// Initialize N on first iteration with first expected geometric object
|
// Initialize N on first iteration with first expected geometric object
|
||||||
if (N->isNull() && !N->isEmpty()) *N = chN->copy();
|
if (N->isNull() && !N->isEmpty()) *N = chN->copy();
|
||||||
else process(*N, *chN, op);
|
else CGAL_binary_operator(*N, *chN, op);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// FIXME: Fix error message
|
// FIXME: Fix error message
|
||||||
|
@ -643,127 +600,6 @@ Response GeometryEvaluator::visit(State &state, const AbstractPolyNode &node)
|
||||||
assert(false);
|
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
|
input: List of 3D objects
|
||||||
output: Polygon2d
|
output: Polygon2d
|
||||||
|
@ -785,7 +621,7 @@ Response GeometryEvaluator::visit(State &state, const ProjectionNode &node)
|
||||||
Nptr = createNefPolyhedronFromGeometry(*geometry);
|
Nptr = createNefPolyhedronFromGeometry(*geometry);
|
||||||
}
|
}
|
||||||
if (!Nptr->isNull()) {
|
if (!Nptr->isNull()) {
|
||||||
CGAL_Nef_polyhedron nef_poly = project_node(node, *Nptr);
|
CGAL_Nef_polyhedron nef_poly = CGAL_project(*Nptr, node.cut_mode);
|
||||||
Polygon2d *poly = nef_poly.convertToPolygon2d();
|
Polygon2d *poly = nef_poly.convertToPolygon2d();
|
||||||
assert(poly);
|
assert(poly);
|
||||||
poly->setConvexity(node.convexity);
|
poly->setConvexity(node.convexity);
|
||||||
|
@ -846,19 +682,24 @@ Response GeometryEvaluator::visit(State &state, const RenderNode &node)
|
||||||
if (state.isPostfix()) {
|
if (state.isPostfix()) {
|
||||||
shared_ptr<const Geometry> geom;
|
shared_ptr<const Geometry> geom;
|
||||||
if (!isCached(node)) {
|
if (!isCached(node)) {
|
||||||
// FIXME: Handle 2D nodes separately
|
const Geometry *geometry = applyToChildren(node, OPENSCAD_UNION);
|
||||||
CGAL_Nef_polyhedron N = this->cgalevaluator->evaluateCGALMesh(node);
|
const CGAL_Nef_polyhedron *N = dynamic_cast<const CGAL_Nef_polyhedron*>(geometry);
|
||||||
PolySet *ps = NULL;
|
if (N) {
|
||||||
if (!N.isNull()) {
|
PolySet *ps = NULL;
|
||||||
if (N.getDimension() == 3 && !N.p3->is_simple()) {
|
if (!N->isNull()) {
|
||||||
PRINT("WARNING: Body of render() isn't valid 2-manifold!");
|
if (N->getDimension() == 3 && !N->p3->is_simple()) {
|
||||||
}
|
PRINT("WARNING: Body of render() isn't valid 2-manifold!");
|
||||||
else {
|
}
|
||||||
ps = N.convertToPolyset();
|
else {
|
||||||
if (ps) ps->setConvexity(node.convexity);
|
ps = N->convertToPolyset();
|
||||||
|
if (ps) ps->setConvexity(node.convexity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
geom.reset(ps);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
geom.reset(geometry);
|
||||||
}
|
}
|
||||||
geom.reset(ps);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
geom = GeometryCache::instance()->get(this->tree.getIdString(node));
|
geom = GeometryCache::instance()->get(this->tree.getIdString(node));
|
||||||
|
|
161
src/cgalutils.cc
161
src/cgalutils.cc
|
@ -6,6 +6,7 @@
|
||||||
#include "Polygon2d.h"
|
#include "Polygon2d.h"
|
||||||
|
|
||||||
#include "cgal.h"
|
#include "cgal.h"
|
||||||
|
#include <CGAL/convex_hull_3.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
@ -515,5 +516,165 @@ CGAL_Nef_polyhedron *createNefPolyhedronFromGeometry(const Geometry &geom)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Modifies target by applying op to target and src:
|
||||||
|
target = target [op] src
|
||||||
|
*/
|
||||||
|
void CGAL_binary_operator(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
CGAL_Nef_polyhedron CGAL_project(const CGAL_Nef_polyhedron &N, bool cut)
|
||||||
|
{
|
||||||
|
logstream log(5);
|
||||||
|
CGAL_Nef_polyhedron nef_poly(2);
|
||||||
|
if (N.getDimension() != 3) return nef_poly;
|
||||||
|
|
||||||
|
CGAL_Nef_polyhedron newN;
|
||||||
|
if (cut) {
|
||||||
|
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(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());
|
||||||
|
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(*N.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 = N.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;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* ENABLE_CGAL */
|
#endif /* ENABLE_CGAL */
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <cgal.h>
|
#include <cgal.h>
|
||||||
#include "polyset.h"
|
#include "polyset.h"
|
||||||
#include "CGAL_Nef_polyhedron.h"
|
#include "CGAL_Nef_polyhedron.h"
|
||||||
|
#include "enums.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 createPolySetFromPolyhedron(const CGAL_Polyhedron &p, PolySet &ps);
|
||||||
|
@ -11,6 +12,9 @@ bool createPolyhedronFromPolySet(const PolySet &ps, CGAL_Polyhedron &p);
|
||||||
CGAL_Iso_cuboid_3 bounding_box( const CGAL_Nef_polyhedron3 &N );
|
CGAL_Iso_cuboid_3 bounding_box( const CGAL_Nef_polyhedron3 &N );
|
||||||
CGAL_Iso_rectangle_2e bounding_box( const CGAL_Nef_polyhedron2 &N );
|
CGAL_Iso_rectangle_2e bounding_box( const CGAL_Nef_polyhedron2 &N );
|
||||||
|
|
||||||
|
void CGAL_binary_operator(CGAL_Nef_polyhedron &target, const CGAL_Nef_polyhedron &src, OpenSCADOperator op);
|
||||||
|
CGAL_Nef_polyhedron CGAL_project(const CGAL_Nef_polyhedron &N, bool cut);
|
||||||
|
|
||||||
#include "svg.h"
|
#include "svg.h"
|
||||||
#include "printutils.h"
|
#include "printutils.h"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue