Preliminary large refactoring: Created GeometryEvaluator as a replacement for PolySetEvaluator. Use ClipperLib for 2D CSG (first baby steps). This design is far from perfect but sacrifice design for staying in the green. This version is broken, but can render primitives.

customizer
Marius Kintel 2013-10-31 00:45:00 -04:00
parent acd08b6f1a
commit b8c15cfb8a
48 changed files with 1210 additions and 584 deletions

View File

@ -13,3 +13,4 @@ include(eigen.pri)
include(boost.pri)
include(glib-2.0.pri)
include(sparkle.pri)
include(clipper.pri)

View File

@ -155,6 +155,7 @@ CONFIG += opencsg
CONFIG += boost
CONFIG += eigen
CONFIG += glib-2.0
CONFIG += clipper
#Uncomment the following line to enable QCodeEdit
#CONFIG += qcodeedit
@ -235,6 +236,7 @@ HEADERS += src/typedefs.h \
src/handle_dep.h \
src/Geometry.h \
src/Polygon2d.h \
src/clipper-utils.h \
src/polyset.h \
src/printutils.h \
src/fileutils.h \
@ -249,6 +251,7 @@ HEADERS += src/typedefs.h \
src/ModuleCache.h \
src/GeometryCache.h \
src/PolySetEvaluator.h \
src/GeometryEvaluator.h \
src/CSGTermEvaluator.h \
src/Tree.h \
src/mathc99.h \
@ -290,6 +293,7 @@ SOURCES += src/version_check.cc \
src/csgtermnormalizer.cc \
src/Geometry.cc \
src/Polygon2d.cc \
src/clipper-utils.cc \
src/polyset.cc \
src/csgops.cc \
src/transform.cc \
@ -315,6 +319,7 @@ SOURCES += src/version_check.cc \
src/nodedumper.cc \
src/traverser.cc \
src/PolySetEvaluator.cc \
src/GeometryEvaluator.cc \
src/ModuleCache.cc \
src/GeometryCache.cc \
src/Tree.cc \
@ -377,7 +382,8 @@ HEADERS += src/cgal.h \
src/CGALRenderer.h \
src/CGAL_Nef_polyhedron.h \
src/CGAL_Nef3_workaround.h \
src/cgalworker.h
src/cgalworker.h \
src/Polygon2d-CGAL.h
SOURCES += src/cgalutils.cc \
src/CGALEvaluator.cc \
@ -387,7 +393,8 @@ SOURCES += src/cgalutils.cc \
src/CGAL_Nef_polyhedron.cc \
src/CGAL_Nef_polyhedron_DxfData.cc \
src/cgaladv_minkowski2.cc \
src/cgalworker.cc
src/cgalworker.cc \
src/Polygon2d-CGAL.cc
}
macx {

View File

@ -1,5 +1,7 @@
#include "CGALCache.h"
#include "CGALEvaluator.h"
#include "GeometryEvaluator.h"
#include "traverser.h"
#include "visitor.h"
#include "state.h"
#include "module.h" // FIXME: Temporarily for ModuleInstantiation
@ -9,6 +11,7 @@
#include "cgaladvnode.h"
#include "transformnode.h"
#include "polyset.h"
#include "Polygon2d.h"
#include "dxfdata.h"
#include "dxftess.h"
#include "Tree.h"
@ -346,6 +349,29 @@ Response CGALEvaluator::visit(State &state, const TransformNode &node)
return ContinueTraversal;
}
/*!
Leaf nodes can create their own geometry, so let them do that
*/
Response CGALEvaluator::visit(State &state, const LeafNode &node)
{
if (state.isPrefix()) {
CGAL_Nef_polyhedron N;
if (!isCached(node)) {
shared_ptr<const Geometry> geom(node.createGeometry());
if (geom) N = createNefPolyhedronFromGeometry(*geom);
node.progress_report();
}
else {
N = CGALCache::instance()->get(this->tree.getIdString(node));
}
addToParent(state, node, N);
}
return PruneTraversal;
}
/*!
Handles non-leaf PolyNodes; extrudes, projection
*/
Response CGALEvaluator::visit(State &state, const AbstractPolyNode &node)
{
if (state.isPrefix() && isCached(node)) return PruneTraversal;
@ -353,14 +379,9 @@ Response CGALEvaluator::visit(State &state, const AbstractPolyNode &node)
CGAL_Nef_polyhedron N;
if (!isCached(node)) {
// Apply polyset operation
shared_ptr<Geometry> geom = this->psevaluator.getGeometry(node, false);
shared_ptr<PolySet> ps = dynamic_pointer_cast<PolySet>(geom);
if (ps) {
N = evaluateCGALMesh(*ps);
// print_messages_pop();
node.progress_report();
}
// else FIXME: Support other Geometry instances
shared_ptr<const Geometry> geom = this->geomevaluator.evaluateGeometry(node);
if (geom) N = createNefPolyhedronFromGeometry(*geom);
node.progress_report();
}
else {
N = CGALCache::instance()->get(this->tree.getIdString(node));
@ -427,290 +448,3 @@ void CGALEvaluator::addToParent(const State &state, const AbstractNode &node, co
this->root = N;
}
}
CGAL_Nef_polyhedron CGALEvaluator::evaluateCGALMesh(const PolySet &ps)
{
if (ps.empty()) return CGAL_Nef_polyhedron(ps.is2d ? 2 : 3);
if (ps.is2d)
{
#if 0
// This version of the code causes problems in some cases.
// Example testcase: import_dxf("testdata/polygon8.dxf");
//
typedef std::list<CGAL_Nef_polyhedron2::Point> point_list_t;
typedef point_list_t::iterator point_list_it;
std::list< point_list_t > pdata_point_lists;
std::list < std::pair < point_list_it, point_list_it > > pdata;
Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
for (int i = 0; i < ps.polygons.size(); i++) {
pdata_point_lists.push_back(point_list_t());
for (int j = 0; j < ps.polygons[i].size(); j++) {
double x = ps.polygons[i][j].x;
double y = ps.polygons[i][j].y;
CGAL_Nef_polyhedron2::Point p;
if (grid.has(x, y)) {
p = grid.data(x, y);
} else {
p = CGAL_Nef_polyhedron2::Point(x, y);
grid.data(x, y) = p;
}
pdata_point_lists.back().push_back(p);
}
pdata.push_back(std::make_pair(pdata_point_lists.back().begin(),
pdata_point_lists.back().end()));
}
CGAL_Nef_polyhedron2 N(pdata.begin(), pdata.end(), CGAL_Nef_polyhedron2::POLYGONS);
return CGAL_Nef_polyhedron(N);
#endif
#if 0
// This version of the code works fine but is pretty slow.
//
CGAL_Nef_polyhedron2 N;
Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
for (int i = 0; i < ps.polygons.size(); i++) {
std::list<CGAL_Nef_polyhedron2::Point> plist;
for (int j = 0; j < ps.polygons[i].size(); j++) {
double x = ps.polygons[i][j].x;
double y = ps.polygons[i][j].y;
CGAL_Nef_polyhedron2::Point p;
if (grid.has(x, y)) {
p = grid.data(x, y);
} else {
p = CGAL_Nef_polyhedron2::Point(x, y);
grid.data(x, y) = p;
}
plist.push_back(p);
}
N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
}
return CGAL_Nef_polyhedron(N);
#endif
#if 1
// This version of the code does essentially the same thing as the 2nd
// version but merges some triangles before sending them to CGAL. This adds
// complexity but speeds up things..
//
struct PolyReducer
{
Grid2d<int> grid;
std::map<std::pair<int,int>, std::pair<int,int> > edge_to_poly;
std::map<int, CGAL_Nef_polyhedron2::Point> points;
typedef std::map<int, std::vector<int> > PolygonMap;
PolygonMap polygons;
int poly_n;
void add_edges(int pn)
{
for (unsigned int j = 1; j <= this->polygons[pn].size(); j++) {
int a = this->polygons[pn][j-1];
int b = this->polygons[pn][j % this->polygons[pn].size()];
if (a > b) { a = a^b; b = a^b; a = a^b; }
if (this->edge_to_poly[std::pair<int,int>(a, b)].first == 0)
this->edge_to_poly[std::pair<int,int>(a, b)].first = pn;
else if (this->edge_to_poly[std::pair<int,int>(a, b)].second == 0)
this->edge_to_poly[std::pair<int,int>(a, b)].second = pn;
else
abort();
}
}
void del_poly(int pn)
{
for (unsigned int j = 1; j <= this->polygons[pn].size(); j++) {
int a = this->polygons[pn][j-1];
int b = this->polygons[pn][j % this->polygons[pn].size()];
if (a > b) { a = a^b; b = a^b; a = a^b; }
if (this->edge_to_poly[std::pair<int,int>(a, b)].first == pn)
this->edge_to_poly[std::pair<int,int>(a, b)].first = 0;
if (this->edge_to_poly[std::pair<int,int>(a, b)].second == pn)
this->edge_to_poly[std::pair<int,int>(a, b)].second = 0;
}
this->polygons.erase(pn);
}
PolyReducer(const PolySet &ps) : grid(GRID_COARSE), poly_n(1)
{
int point_n = 1;
for (size_t i = 0; i < ps.polygons.size(); i++) {
for (size_t j = 0; j < ps.polygons[i].size(); j++) {
double x = ps.polygons[i][j][0];
double y = ps.polygons[i][j][1];
if (this->grid.has(x, y)) {
int idx = this->grid.data(x, y);
// Filter away two vertices with the same index (due to grid)
// This could be done in a more general way, but we'd rather redo the entire
// grid concept instead.
std::vector<int> &poly = this->polygons[this->poly_n];
if (std::find(poly.begin(), poly.end(), idx) == poly.end()) {
poly.push_back(this->grid.data(x, y));
}
} else {
this->grid.align(x, y) = point_n;
this->polygons[this->poly_n].push_back(point_n);
this->points[point_n] = CGAL_Nef_polyhedron2::Point(x, y);
point_n++;
}
}
if (this->polygons[this->poly_n].size() >= 3) {
add_edges(this->poly_n);
this->poly_n++;
}
else {
this->polygons.erase(this->poly_n);
}
}
}
int merge(int p1, int p1e, int p2, int p2e)
{
for (unsigned int i = 1; i < this->polygons[p1].size(); i++) {
int j = (p1e + i) % this->polygons[p1].size();
this->polygons[this->poly_n].push_back(this->polygons[p1][j]);
}
for (unsigned int i = 1; i < this->polygons[p2].size(); i++) {
int j = (p2e + i) % this->polygons[p2].size();
this->polygons[this->poly_n].push_back(this->polygons[p2][j]);
}
del_poly(p1);
del_poly(p2);
add_edges(this->poly_n);
return this->poly_n++;
}
void reduce()
{
std::deque<int> work_queue;
BOOST_FOREACH(const PolygonMap::value_type &i, polygons) {
work_queue.push_back(i.first);
}
while (!work_queue.empty()) {
int poly1_n = work_queue.front();
work_queue.pop_front();
if (this->polygons.find(poly1_n) == this->polygons.end()) continue;
for (unsigned int j = 1; j <= this->polygons[poly1_n].size(); j++) {
int a = this->polygons[poly1_n][j-1];
int b = this->polygons[poly1_n][j % this->polygons[poly1_n].size()];
if (a > b) { a = a^b; b = a^b; a = a^b; }
if (this->edge_to_poly[std::pair<int,int>(a, b)].first != 0 &&
this->edge_to_poly[std::pair<int,int>(a, b)].second != 0) {
int poly2_n = this->edge_to_poly[std::pair<int,int>(a, b)].first +
this->edge_to_poly[std::pair<int,int>(a, b)].second - poly1_n;
int poly2_edge = -1;
for (unsigned int k = 1; k <= this->polygons[poly2_n].size(); k++) {
int c = this->polygons[poly2_n][k-1];
int d = this->polygons[poly2_n][k % this->polygons[poly2_n].size()];
if (c > d) { c = c^d; d = c^d; c = c^d; }
if (a == c && b == d) {
poly2_edge = k-1;
continue;
}
int poly3_n = this->edge_to_poly[std::pair<int,int>(c, d)].first +
this->edge_to_poly[std::pair<int,int>(c, d)].second - poly2_n;
if (poly3_n < 0)
continue;
if (poly3_n == poly1_n)
goto next_poly1_edge;
}
work_queue.push_back(merge(poly1_n, j-1, poly2_n, poly2_edge));
goto next_poly1;
}
next_poly1_edge:;
}
next_poly1:;
}
}
CGAL_Nef_polyhedron2 *toNef()
{
CGAL_Nef_polyhedron2 *N = new CGAL_Nef_polyhedron2;
BOOST_FOREACH(const PolygonMap::value_type &i, polygons) {
std::list<CGAL_Nef_polyhedron2::Point> plist;
for (unsigned int j = 0; j < i.second.size(); j++) {
int p = i.second[j];
plist.push_back(points[p]);
}
*N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
}
return N;
}
};
PolyReducer pr(ps);
pr.reduce();
return CGAL_Nef_polyhedron(pr.toNef());
#endif
#if 0
// This is another experimental version. I should run faster than the above,
// is a lot simpler and has only one known weakness: Degenerate polygons, which
// get repaired by GLUTess, might trigger a CGAL crash here. The only
// known case for this is triangle-with-duplicate-vertex.dxf
// FIXME: If we just did a projection, we need to recreate the border!
if (ps.polygons.size() > 0) assert(ps.borders.size() > 0);
CGAL_Nef_polyhedron2 N;
Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
for (int i = 0; i < ps.borders.size(); i++) {
std::list<CGAL_Nef_polyhedron2::Point> plist;
for (int j = 0; j < ps.borders[i].size(); j++) {
double x = ps.borders[i][j].x;
double y = ps.borders[i][j].y;
CGAL_Nef_polyhedron2::Point p;
if (grid.has(x, y)) {
p = grid.data(x, y);
} else {
p = CGAL_Nef_polyhedron2::Point(x, y);
grid.data(x, y) = p;
}
plist.push_back(p);
}
// FIXME: If a border (path) has a duplicate vertex in dxf,
// the CGAL_Nef_polyhedron2 constructor will crash.
N ^= CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
}
return CGAL_Nef_polyhedron(N);
#endif
}
else // not (this->is2d)
{
CGAL_Nef_polyhedron3 *N = NULL;
bool plane_error = false;
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
CGAL_Polyhedron P;
bool err = createPolyhedronFromPolySet(ps,P);
if (!err) N = new CGAL_Nef_polyhedron3(P);
}
catch (const CGAL::Assertion_exception &e) {
if (std::string(e.what()).find("Plane_constructor")!=std::string::npos) {
if (std::string(e.what()).find("has_on")!=std::string::npos) {
PRINT("PolySet has nonplanar faces. Attempting alternate construction");
plane_error=true;
}
} else {
PRINTB("CGAL error in CGAL_Nef_polyhedron3(): %s", e.what());
}
}
if (plane_error) try {
PolySet ps2;
CGAL_Polyhedron P;
tessellate_3d_faces( ps, ps2 );
bool err = createPolyhedronFromPolySet(ps2,P);
if (!err) N = new CGAL_Nef_polyhedron3(P);
}
catch (const CGAL::Assertion_exception &e) {
PRINTB("Alternate construction failed. CGAL error in CGAL_Nef_polyhedron3(): %s", e.what());
}
CGAL::set_error_behaviour(old_behaviour);
if (N) return CGAL_Nef_polyhedron(N);
}
return CGAL_Nef_polyhedron(ps.is2d?2:3);
}

View File

@ -3,7 +3,6 @@
#include "visitor.h"
#include "CGAL_Nef_polyhedron.h"
#include "PolySetCGALEvaluator.h"
#include <string>
#include <map>
@ -13,18 +12,19 @@ class CGALEvaluator : public Visitor
{
public:
enum CsgOp {CGE_UNION, CGE_INTERSECTION, CGE_DIFFERENCE, CGE_MINKOWSKI};
CGALEvaluator(const class Tree &tree) : tree(tree), psevaluator(*this) {}
CGALEvaluator(const class Tree &tree, class GeometryEvaluator &geomevaluator) :
tree(tree), geomevaluator(geomevaluator) {}
virtual ~CGALEvaluator() {}
virtual Response visit(State &state, const AbstractNode &node);
virtual Response visit(State &state, const AbstractIntersectionNode &node);
virtual Response visit(State &state, const CsgNode &node);
virtual Response visit(State &state, const TransformNode &node);
virtual Response visit(State &state, const LeafNode &node);
virtual Response visit(State &state, const AbstractPolyNode &node);
virtual Response visit(State &state, const CgaladvNode &node);
CGAL_Nef_polyhedron evaluateCGALMesh(const AbstractNode &node);
CGAL_Nef_polyhedron evaluateCGALMesh(const PolySet &polyset);
const Tree &getTree() const { return this->tree; }
@ -45,7 +45,7 @@ private:
public:
// FIXME: Do we need to make this visible? Used for cache management
// Note: psevaluator constructor needs this->tree to be initialized first
PolySetCGALEvaluator psevaluator;
class GeometryEvaluator &geomevaluator;
};
#endif

View File

@ -125,10 +125,8 @@ void CGAL_Nef_polyhedron::transform( const Transform3d &matrix )
ps.is2d = true;
dxf_tesselate(&ps, *dd, 0, Vector2d(1,1), true, false, 0);
Tree nulltree;
CGALEvaluator tmpeval(nulltree);
CGAL_Nef_polyhedron N = tmpeval.evaluateCGALMesh(ps);
if ( N.p2 ) this->p2.reset( new CGAL_Nef_polyhedron2( *N.p2 ) );
CGAL_Nef_polyhedron N = createNefPolyhedronFromGeometry(ps);
if (N.p2) this->p2.reset(new CGAL_Nef_polyhedron2(*N.p2));
delete dd;
}
}

View File

@ -9,7 +9,7 @@
#include "rendernode.h"
#include "cgaladvnode.h"
#include "printutils.h"
#include "PolySetEvaluator.h"
#include "GeometryEvaluator.h"
#include "polyset.h"
#include <string>
@ -86,16 +86,16 @@ Response CSGTermEvaluator::visit(State &state, const AbstractIntersectionNode &n
return ContinueTraversal;
}
static shared_ptr<CSGTerm> evaluate_csg_term_from_ps(const State &state,
static shared_ptr<CSGTerm> evaluate_csg_term_from_geometry(const State &state,
std::vector<shared_ptr<CSGTerm> > &highlights,
std::vector<shared_ptr<CSGTerm> > &background,
const shared_ptr<PolySet> &ps,
const shared_ptr<const Geometry> &geom,
const ModuleInstantiation *modinst,
const AbstractNode &node)
{
std::stringstream stream;
stream << node.name() << node.index();
shared_ptr<CSGTerm> t(new CSGTerm(ps, state.matrix(), state.color(), stream.str()));
shared_ptr<CSGTerm> t(new CSGTerm(geom, state.matrix(), state.color(), stream.str()));
if (modinst->isHighlight()) {
t->flag = CSGTerm::FLAG_HIGHLIGHT;
highlights.push_back(t);
@ -111,15 +111,11 @@ Response CSGTermEvaluator::visit(State &state, const AbstractPolyNode &node)
{
if (state.isPrefix()) {
shared_ptr<CSGTerm> t1;
if (this->psevaluator) {
shared_ptr<Geometry> geom = this->psevaluator->getGeometry(node, true);
if (this->geomevaluator) {
shared_ptr<const Geometry> geom = this->geomevaluator->evaluateGeometry(node);
if (geom) {
shared_ptr<PolySet> ps = dynamic_pointer_cast<PolySet>(geom);
if (!ps) {
// FIXME: Convert geom to polyset. Refacting may resume here
}
t1 = evaluate_csg_term_from_ps(state, this->highlights, this->background,
ps, node.modinst, node);
t1 = evaluate_csg_term_from_geometry(state, this->highlights, this->background,
geom, node.modinst, node);
node.progress_report();
}
}
@ -181,15 +177,14 @@ Response CSGTermEvaluator::visit(State &state, const RenderNode &node)
{
if (state.isPrefix()) {
shared_ptr<CSGTerm> t1;
shared_ptr<PolySet> ps;
if (this->psevaluator) {
shared_ptr<Geometry> geom = this->psevaluator->getGeometry(node, true);
ps = dynamic_pointer_cast<PolySet>(geom);
shared_ptr<const Geometry> geom;
if (this->geomevaluator) {
geom = this->geomevaluator->evaluateGeometry(node);
node.progress_report();
}
if (ps) {
t1 = evaluate_csg_term_from_ps(state, this->highlights, this->background,
ps, node.modinst, node);
if (geom) {
t1 = evaluate_csg_term_from_geometry(state, this->highlights, this->background,
geom, node.modinst, node);
}
this->stored_term[node.index()] = t1;
addToParent(state, node);
@ -202,14 +197,13 @@ Response CSGTermEvaluator::visit(State &state, const CgaladvNode &node)
if (state.isPrefix()) {
shared_ptr<CSGTerm> t1;
// FIXME: Calling evaluator directly since we're not a PolyNode. Generalize this.
shared_ptr<PolySet> ps;
if (this->psevaluator) {
shared_ptr<Geometry> geom = this->psevaluator->getGeometry(node, true);
ps = dynamic_pointer_cast<PolySet>(geom);
shared_ptr<const Geometry> geom;
if (this->geomevaluator) {
geom = this->geomevaluator->evaluateGeometry(node);
}
if (ps) {
t1 = evaluate_csg_term_from_ps(state, this->highlights, this->background,
ps, node.modinst, node);
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;

View File

@ -11,8 +11,8 @@
class CSGTermEvaluator : public Visitor
{
public:
CSGTermEvaluator(const class Tree &tree, class PolySetEvaluator *psevaluator = NULL)
: tree(tree), psevaluator(psevaluator) {
CSGTermEvaluator(const class Tree &tree, class GeometryEvaluator *geomevaluator = NULL)
: tree(tree), geomevaluator(geomevaluator) {
}
virtual ~CSGTermEvaluator() {}
@ -44,7 +44,7 @@ public:
std::vector<shared_ptr<CSGTerm> > highlights;
std::vector<shared_ptr<CSGTerm> > background;
const Tree &tree;
class PolySetEvaluator *psevaluator;
class GeometryEvaluator *geomevaluator;
};
#endif

View File

@ -4,7 +4,7 @@
#include "OffscreenView.h"
#include "csgterm.h"
#include "Tree.h"
#include "CGALEvaluator.h"
#include "GeometryEvaluator.h"
#include "CSGTermEvaluator.h"
#include "csgtermnormalizer.h"
#include "rendersettings.h"
@ -40,8 +40,8 @@ public:
bool compile_chains( const Tree &tree )
{
const AbstractNode *root_node = tree.root();
CGALEvaluator cgalevaluator(tree);
CSGTermEvaluator evaluator(tree, &cgalevaluator.psevaluator);
GeometryEvaluator geomevaluator(tree);
CSGTermEvaluator evaluator(tree, &geomevaluator);
boost::shared_ptr<CSGTerm> root_raw_term = evaluator.evaluateCSGTerm( *root_node, this->highlight_terms, this->background_terms );
if (!root_raw_term) {

View File

@ -8,9 +8,13 @@
class Geometry
{
public:
virtual ~Geometry() {}
virtual size_t memsize() const = 0;
virtual BoundingBox getBoundingBox() const = 0;
virtual std::string dump() const = 0;
virtual unsigned int getDimension() const = 0;
virtual unsigned int getConvexity() const { return 1; };
};
#endif

View File

@ -4,9 +4,16 @@
GeometryCache *GeometryCache::inst = NULL;
void GeometryCache::insert(const std::string &id, const shared_ptr<Geometry> &ps)
bool GeometryCache::insert(const std::string &id, const shared_ptr<const Geometry> &geom)
{
this->cache.insert(id, new cache_entry(ps), ps ? ps->memsize() : 0);
bool inserted = this->cache.insert(id, new cache_entry(geom), geom ? geom->memsize() : 0);
#ifdef DEBUG
if (inserted) PRINTB("Geometry Cache insert: %s (%d bytes)",
id.substr(0, 40) % geom->memsize());
else PRINTB("Geometry Cache insert failed: %s (%d bytes)",
id.substr(0, 40) % geom->memsize());
#endif
return inserted;
}
size_t GeometryCache::maxSize() const
@ -21,11 +28,12 @@ void GeometryCache::setMaxSize(size_t limit)
void GeometryCache::print()
{
PRINTB("Geometrys in cache: %d", this->cache.size());
PRINTB("Geometries in cache: %d", this->cache.size());
PRINTB("Geometry cache size in bytes: %d", this->cache.totalCost());
}
GeometryCache::cache_entry::cache_entry(const shared_ptr<Geometry> &ps) : ps(ps)
GeometryCache::cache_entry::cache_entry(const shared_ptr<const Geometry> &geom)
: geom(geom)
{
if (print_messages_stack.size() > 0) this->msg = print_messages_stack.back();
}

View File

@ -13,8 +13,8 @@ public:
static GeometryCache *instance() { if (!inst) inst = new GeometryCache; return inst; }
bool contains(const std::string &id) const { return this->cache.contains(id); }
shared_ptr<class Geometry> get(const std::string &id) const { return this->cache[id]->ps; }
void insert(const std::string &id, const shared_ptr<Geometry> &ps);
shared_ptr<const class Geometry> get(const std::string &id) const { return this->cache[id]->geom; }
bool insert(const std::string &id, const shared_ptr<const Geometry> &geom);
size_t maxSize() const;
void setMaxSize(size_t limit);
void clear() { cache.clear(); }
@ -24,9 +24,9 @@ private:
static GeometryCache *inst;
struct cache_entry {
shared_ptr<class Geometry> ps;
shared_ptr<const class Geometry> geom;
std::string msg;
cache_entry(const shared_ptr<Geometry> &ps);
cache_entry(const shared_ptr<const Geometry> &geom);
~cache_entry() { }
};

218
src/GeometryEvaluator.cc Normal file
View File

@ -0,0 +1,218 @@
#include "GeometryEvaluator.h"
#include "traverser.h"
#include "tree.h"
#include "GeometryCache.h"
#include "Polygon2d.h"
#include "module.h"
#include "state.h"
#include "transformnode.h"
#include "clipper-utils.h"
#include "CGALEvaluator.h"
#include "CGALCache.h"
#include "PolySet.h"
#include <boost/foreach.hpp>
GeometryEvaluator::GeometryEvaluator(const class Tree &tree):
tree(tree)
{
this->cgalevaluator = new CGALEvaluator(tree, *this);
}
/*!
Factory method returning a Geometry from the given node. If the
node is already cached, the cached Geometry will be returned
otherwise a new Geometry will be created from the node. If cache is
true, the newly created Geometry will be cached.
FIXME: This looks redundant
*/
shared_ptr<const Geometry> GeometryEvaluator::getGeometry(const AbstractNode &node, bool cache)
{
std::string cacheid = this->tree.getIdString(node);
if (GeometryCache::instance()->contains(cacheid)) {
#ifdef DEBUG
// For cache debugging
PRINTB("GeometryCache hit: %s", cacheid.substr(0, 40));
#endif
return GeometryCache::instance()->get(cacheid);
}
shared_ptr<const Geometry> geom(this->evaluateGeometry(node));
if (cache) GeometryCache::instance()->insert(cacheid, geom);
return geom;
}
bool GeometryEvaluator::isCached(const AbstractNode &node) const
{
return GeometryCache::instance()->contains(this->tree.getIdString(node));
}
// FIXME: This doesn't insert into cache. Fix this here or in client code
shared_ptr<const Geometry> GeometryEvaluator::evaluateGeometry(const AbstractNode &node)
{
if (!isCached(node)) {
Traverser trav(*this, node, Traverser::PRE_AND_POSTFIX);
trav.execute();
return this->root;
}
return GeometryCache::instance()->get(this->tree.getIdString(node));
}
/*!
*/
Geometry *GeometryEvaluator::applyToChildren(const AbstractNode &node, OpenSCADOperator op)
{
// FIXME: Support other operators than UNION
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);
}
assert(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);
}
chnode->progress_report();
}
ClipperLib::Clipper clipper;
clipper.AddPolygons(ClipperUtils::fromPolygon2d(sum), ClipperLib::ptSubject);
ClipperLib::Polygons result;
clipper.Execute(ClipperLib::ctUnion, result);
if (result.size() == 0) return NULL;
return ClipperUtils::toPolygon2d(result);
}
/*!
Adds ourself to out parent's list of traversed children.
Call this for _every_ node which affects output during traversal.
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.
*/
void GeometryEvaluator::addToParent(const State &state,
const AbstractNode &node,
const shared_ptr<const Geometry> &geom)
{
this->visitedchildren.erase(node.index());
if (state.parent()) {
this->visitedchildren[state.parent()->index()].push_back(std::make_pair(&node, geom));
}
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");
}
}
this->root = geom;
}
}
/*!
Fallback: If we don't know how to handle a node, send it to CGAL
*/
Response GeometryEvaluator::visit(State &state, const AbstractNode &node)
{
if (state.isPrefix() && isCached(node)) return PruneTraversal;
if (state.isPostfix()) {
CGAL_Nef_polyhedron N = this->cgalevaluator->evaluateCGALMesh(node);
CGALCache::instance()->insert(this->tree.getIdString(node), N);
PolySet *ps = NULL;
if (!N.isNull()) ps = N.convertToPolyset();
shared_ptr<const Geometry> geom(ps);
GeometryCache::instance()->insert(this->tree.getIdString(node), geom);
addToParent(state, node, geom);
}
return ContinueTraversal;
}
/*!
Leaf nodes can create their own geometry, so let them do that
*/
Response GeometryEvaluator::visit(State &state, const LeafNode &node)
{
if (state.isPrefix()) {
shared_ptr<const Geometry> geom;
if (!isCached(node)) geom.reset(node.createGeometry());
else geom = GeometryCache::instance()->get(this->tree.getIdString(node));
addToParent(state, node, geom);
}
return PruneTraversal;
}
Response GeometryEvaluator::visit(State &state, const TransformNode &node)
{
if (state.isPrefix() && isCached(node)) return PruneTraversal;
if (state.isPostfix()) {
shared_ptr<const class Geometry> geom;
if (!isCached(node)) {
// First union all children
geom.reset(applyToChildren(node, CGE_UNION));
if (matrix_contains_infinity(node.matrix) || matrix_contains_nan(node.matrix)) {
// due to the way parse/eval works we can't currently distinguish between NaN and Inf
PRINT("Warning: Transformation matrix contains Not-a-Number and/or Infinity - removing object.");
geom.reset();
}
//FIXME: Handle 2D vs. 3D
shared_ptr<const Polygon2d> polygons = dynamic_pointer_cast<const Polygon2d>(geom);
if (polygons) {
// FIXME: Convert from mat3 to mat2: Transform2d mat2(node.matrix);
Transform2d mat2;
// polygons->transform(mat2);
}
else {
// FIXME: Handle 3D transfer
}
}
else {
geom = GeometryCache::instance()->get(this->tree.getIdString(node));
}
addToParent(state, node, geom);
}
return ContinueTraversal;
}
/*!
Handles non-leaf PolyNodes; extrusions, projection
*/
Response GeometryEvaluator::visit(State &state, const AbstractPolyNode &node)
{
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;
}

45
src/GeometryEvaluator.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef GEOMETRYEVALUATOR_H_
#define GEOMETRYEVALUATOR_H_
#include "visitor.h"
#include "enums.h"
#include "memory.h"
#include <utility>
#include <list>
#include <map>
class GeometryEvaluator : public Visitor
{
public:
GeometryEvaluator(const class Tree &tree);
virtual ~GeometryEvaluator() {}
shared_ptr<const class Geometry> getGeometry(const AbstractNode &node, bool cache);
shared_ptr<const class Geometry> evaluateGeometry(const AbstractNode &node);
virtual Response visit(State &state, const AbstractNode &node);
virtual Response visit(State &state, const AbstractPolyNode &node);
virtual Response visit(State &state, const LeafNode &node);
virtual Response visit(State &state, const TransformNode &node);
const Tree &getTree() const { return this->tree; }
private:
bool isCached(const AbstractNode &node) const;
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;
typedef std::list<ChildItem> ChildList;
std::map<int, ChildList> visitedchildren;
const Tree &tree;
shared_ptr<const Geometry> root;
public:
// FIXME: Deal with visibility
class CGALEvaluator *cgalevaluator;
};
#endif

View File

@ -38,13 +38,13 @@ class OpenCSGPrim : public OpenCSG::Primitive
public:
OpenCSGPrim(OpenCSG::Operation operation, unsigned int convexity) :
OpenCSG::Primitive(operation, convexity) { }
shared_ptr<PolySet> ps;
shared_ptr<const Geometry> geom;
Transform3d m;
PolySet::csgmode_e csgmode;
Renderer::csgmode_e csgmode;
virtual void render() {
glPushMatrix();
glMultMatrixd(m.data());
ps->render_surface(csgmode, m);
Renderer::render_surface(geom, csgmode, m);
glPopMatrix();
}
};
@ -90,7 +90,7 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo,
const Color4f &c = j_obj.color;
glPushMatrix();
glMultMatrixd(j_obj.matrix.data());
PolySet::csgmode_e csgmode = j_obj.type == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL;
csgmode_e csgmode = j_obj.type == CSGTerm::TYPE_DIFFERENCE ? CSGMODE_DIFFERENCE : CSGMODE_NORMAL;
ColorMode colormode = COLORMODE_NONE;
if (background) {
if (j_obj.flag & CSGTerm::FLAG_HIGHLIGHT) {
@ -99,11 +99,11 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo,
else {
colormode = COLORMODE_BACKGROUND;
}
csgmode = PolySet::csgmode_e(csgmode + 10);
csgmode = csgmode_e(csgmode + 10);
} else if (j_obj.type == CSGTerm::TYPE_DIFFERENCE) {
if (j_obj.flag & CSGTerm::FLAG_HIGHLIGHT) {
colormode = COLORMODE_HIGHLIGHT;
csgmode = PolySet::csgmode_e(csgmode + 20);
csgmode = csgmode_e(csgmode + 20);
}
else {
colormode = COLORMODE_CUTOUT;
@ -111,7 +111,7 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo,
} else {
if (j_obj.flag & CSGTerm::FLAG_HIGHLIGHT) {
colormode = COLORMODE_HIGHLIGHT;
csgmode = PolySet::csgmode_e(csgmode + 20);
csgmode = csgmode_e(csgmode + 20);
}
else {
colormode = COLORMODE_MATERIAL;
@ -120,7 +120,7 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo,
setColor(colormode, c.data(), shaderinfo);
j_obj.polyset->render_surface(csgmode, j_obj.matrix, shaderinfo);
render_surface(j_obj.geom, csgmode, j_obj.matrix, shaderinfo);
glPopMatrix();
}
if (shaderinfo) glUseProgram(0);
@ -134,12 +134,13 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo,
if (last) break;
OpenCSGPrim *prim = new OpenCSGPrim(i_obj.type == CSGTerm::TYPE_DIFFERENCE ?
OpenCSG::Subtraction : OpenCSG::Intersection, i_obj.polyset->convexity);
prim->ps = i_obj.polyset;
OpenCSG::Subtraction : OpenCSG::Intersection, i_obj.geom->getConvexity());
prim->geom = i_obj.geom;
prim->m = i_obj.matrix;
prim->csgmode = i_obj.type == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL;
if (highlight) prim->csgmode = PolySet::csgmode_e(prim->csgmode + 20);
else if (background) prim->csgmode = PolySet::csgmode_e(prim->csgmode + 10);
prim->csgmode = i_obj.type == CSGTerm::TYPE_DIFFERENCE ? CSGMODE_DIFFERENCE : CSGMODE_NORMAL;
if (highlight) prim->csgmode = csgmode_e(prim->csgmode + 20);
else if (background) prim->csgmode = csgmode_e(prim->csgmode + 10);
primitives.push_back(prim);
}
std::for_each(primitives.begin(), primitives.end(), del_fun<OpenCSG::Primitive>());

View File

@ -3,7 +3,12 @@
#include "cgalutils.h"
#include <CGAL/convex_hull_3.h>
#include "Polygon2d.h"
#include "Polygon2d-CGAL.h"
#include "polyset.h"
#include <polyclipping/clipper.hpp>
#include "clipper-utils.h"
#include "CGALEvaluator.h"
#include "projectionnode.h"
#include "linearextrudenode.h"
@ -287,40 +292,135 @@ static void add_slice(PolySet *ps, const DxfData &dxf, DxfData::Path &path,
}
}
static Polygon2d *evaluate2DTree(const AbstractNode &node)
{
// FIXME:
// o visitor walking the tree
// o On supported node, evaluate directly
// o What about e.g projection?
// o Use CGALEvaluator instead and add a 2D evaluator function?
Outline2d o;
o.push_back(Vector2d(0,0));
o.push_back(Vector2d(1,0));
o.push_back(Vector2d(1,1));
o.push_back(Vector2d(0,1));
Polygon2d *p = new Polygon2d();
p->addOutline(o);
return p;
}
Geometry *PolySetCGALEvaluator::evaluateGeometry(const LinearExtrudeNode &node)
{
DxfData *dxf;
Geometry *geom = NULL;
if (node.filename.empty())
{
if (node.filename.empty()) {
// Before extruding, union all (2D) children nodes
// to a single DxfData, then tesselate this into a Geometry
CGAL_Nef_polyhedron sum;
Polygon2d sum;
BOOST_FOREACH (AbstractNode * v, node.getChildren()) {
if (v->modinst->isBackground()) continue;
CGAL_Nef_polyhedron N = this->cgalevaluator.evaluateCGALMesh(*v);
if (!N.isNull()) {
if (N.dim != 2) {
PRINT("ERROR: linear_extrude() is not defined for 3D child objects!");
}
else {
if (sum.isNull()) sum = N.copy();
else sum += N;
Polygon2d *polygons = evaluate2DTree(*v);
// FIXME: If evaluate2DTree encounters a 3D object, we should print an error
if (polygons) {
BOOST_FOREACH(const Outline2d &o, polygons->outlines()) {
sum.addOutline(o);
}
}
else {
//FIXME: PRINT("ERROR: linear_extrude() is not defined for 3D child objects!");
}
}
ClipperLib::Clipper clipper;
clipper.AddPolygons(ClipperUtils::fromPolygon2d(sum), ClipperLib::ptSubject);
ClipperLib::Polygons result;
clipper.Execute(ClipperLib::ctUnion, result);
if (sum.isNull()) return NULL;
dxf = sum.convertToDxfData();;
if (result.size() == 0) return NULL;
Polygon2d *polygon = ClipperUtils::toPolygon2d(result);
geom = extrudePolygon(node, *polygon);
delete polygon;
} else {
dxf = new DxfData(node.fn, node.fs, node.fa, node.filename, node.layername, node.origin_x, node.origin_y, node.scale_x);
geom = extrudeDxfData(node, *dxf);
delete dxf;
}
Geometry *geom = extrudeDxfData(node, *dxf);
delete dxf;
return geom;
}
/*!
Input to extrude should be clean. This means non-intersecting etc.,
the input coming from a library like Clipper.
*/
Geometry *PolySetCGALEvaluator::extrudePolygon(const LinearExtrudeNode &node,
const Polygon2d &poly)
{
PolySet *ps = new PolySet();
ps->convexity = node.convexity;
if (node.height <= 0) return ps;
double h1, h2;
if (node.center) {
h1 = -node.height/2.0;
h2 = +node.height/2.0;
} else {
h1 = 0;
h2 = node.height;
}
// bool first_open_path = true;
// BOOST_FOREACH(const Outline2d &outline, poly.outlines) {
/*
if (node.has_twist) {
dxf_tesselate(ps, dxf, 0, Vector2d(1,1), false, true, h1); // bottom
if (node.scale_x > 0 || node.scale_y > 0) {
dxf_tesselate(ps, dxf, node.twist, Vector2d(node.scale_x, node.scale_y), true, true, h2); // top
}
for (int j = 0; j < node.slices; j++) {
double t1 = node.twist*j / node.slices;
double t2 = node.twist*(j+1) / node.slices;
double g1 = h1 + (h2-h1)*j / node.slices;
double g2 = h1 + (h2-h1)*(j+1) / node.slices;
double s1x = 1 - (1-node.scale_x)*j / node.slices;
double s1y = 1 - (1-node.scale_y)*j / node.slices;
double s2x = 1 - (1-node.scale_x)*(j+1) / node.slices;
double s2y = 1 - (1-node.scale_y)*(j+1) / node.slices;
for (size_t i = 0; i < dxf.paths.size(); i++) {
if (!dxf.paths[i].is_closed) continue;
add_slice(ps, dxf, dxf.paths[i], t1, t2, g1, g2, s1x, s1y, s2x, s2y);
}
}
}
else
{
*/
// FIXME: Tessellate outlines into 2D triangles
ps = poly.tessellate();
/*
dxf_tesselate(ps, dxf, 0, Vector2d(1,1), false, true, h1); //bottom
if (node.scale_x > 0 || node.scale_y > 0) {
dxf_tesselate(ps, dxf, 0, Vector2d(node.scale_x, node.scale_y), true, true, h2); // top
}
for (size_t i = 0; i < dxf.paths.size(); i++) {
if (!dxf.paths[i].is_closed) continue;
add_slice(ps, dxf, dxf.paths[i], 0, 0, h1, h2, 1, 1, node.scale_x, node.scale_y);
}
}
*/
return ps;
}
Geometry *PolySetCGALEvaluator::extrudeDxfData(const LinearExtrudeNode &node, DxfData &dxf)
{
PolySet *ps = new PolySet();

View File

@ -5,7 +5,7 @@
/*!
This is a Geometry evaluator which uses the CGALEvaluator to support building
geometrys.
geometries.
*/
class PolySetCGALEvaluator : public PolySetEvaluator
{
@ -21,6 +21,7 @@ public:
protected:
Geometry *extrudeDxfData(const LinearExtrudeNode &node, class DxfData &dxf);
Geometry *rotateDxfData(const RotateExtrudeNode &node, class DxfData &dxf);
Geometry *extrudePolygon(const LinearExtrudeNode &node, const class Polygon2d &poly);
CGALEvaluator &cgalevaluator;
};

View File

@ -17,7 +17,7 @@
otherwise a new Geometry will be created from the node. If cache is
true, the newly created Geometry will be cached.
*/
shared_ptr<Geometry> PolySetEvaluator::getGeometry(const AbstractNode &node, bool cache)
shared_ptr<const Geometry> PolySetEvaluator::getGeometry(const AbstractNode &node, bool cache)
{
std::string cacheid = this->tree.getIdString(node);

View File

@ -11,7 +11,7 @@ public:
const Tree &getTree() const { return this->tree; }
virtual shared_ptr<class Geometry> getGeometry(const class AbstractNode &, bool cache);
virtual shared_ptr<const class Geometry> getGeometry(const class AbstractNode &, bool cache);
virtual Geometry *evaluateGeometry(const class ProjectionNode &) { return NULL; }
virtual Geometry *evaluateGeometry(const class LinearExtrudeNode &) { return NULL; }

141
src/Polygon2d-CGAL.cc Normal file
View File

@ -0,0 +1,141 @@
#include "Polygon2d-CGAL.h"
#include "polyset.h"
#include "printutils.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
#include <CGAL/Triangulation_face_base_with_info_2.h>
#include <CGAL/Polygon_2.h>
#include <iostream>
#include <boost/foreach.hpp>
namespace Polygon2DCGAL {
struct FaceInfo
{
FaceInfo() {}
int nesting_level;
bool in_domain() { return nesting_level%2 == 1; }
};
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Triangulation_vertex_base_2<K> Vb;
typedef CGAL::Triangulation_face_base_with_info_2<FaceInfo,K> Fbb;
typedef CGAL::Constrained_triangulation_face_base_2<K,Fbb> Fb;
typedef CGAL::Triangulation_data_structure_2<Vb,Fb> TDS;
typedef CGAL::Exact_predicates_tag Itag;
typedef CGAL::Constrained_Delaunay_triangulation_2<K, TDS, Itag> CDT;
typedef CDT::Point Point;
typedef CGAL::Polygon_2<K> Polygon_2;
void
mark_domains(CDT &ct,
CDT::Face_handle start,
int index,
std::list<CDT::Edge> &border)
{
if (start->info().nesting_level != -1) return;
std::list<CDT::Face_handle> queue;
queue.push_back(start);
while (!queue.empty()) {
CDT::Face_handle fh = queue.front();
queue.pop_front();
if (fh->info().nesting_level == -1) {
fh->info().nesting_level = index;
for (int i = 0; i < 3; i++) {
CDT::Edge e(fh,i);
CDT::Face_handle n = fh->neighbor(i);
if (n->info().nesting_level == -1) {
if (ct.is_constrained(e)) border.push_back(e);
else queue.push_back(n);
}
}
}
}
}
// Explore set of facets connected with non constrained edges,
// and attribute to each such set a nesting level.
// We start from facets incident to the infinite vertex, with a nesting
// level of 0. Then we recursively consider the non-explored facets incident
// to constrained edges bounding the former set and increase the nesting level by 1.
// Facets in the domain are those with an odd nesting level.
void
mark_domains(CDT &cdt)
{
for(CDT::All_faces_iterator it = cdt.all_faces_begin(); it != cdt.all_faces_end(); ++it) {
it->info().nesting_level = -1;
}
int index = 0;
std::list<CDT::Edge> border;
mark_domains(cdt, cdt.infinite_face(), index++, border);
while (!border.empty()) {
CDT::Edge e = border.front();
border.pop_front();
CDT::Face_handle n = e.first->neighbor(e.second);
if (n->info().nesting_level == -1) {
mark_domains(cdt, n, e.first->info().nesting_level+1, border);
}
}
}
}
#define OPENSCAD_CGAL_ERROR_BEGIN \
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); \
try {
#define OPENSCAD_CGAL_ERROR_END(errorstr, onerror) \
} \
catch (const CGAL::Precondition_exception &e) { \
PRINTB(errorstr ": %s", e.what()); \
CGAL::set_error_behaviour(old_behaviour); \
onerror; \
} \
CGAL::set_error_behaviour(old_behaviour);
/*!
Triangulates this polygon2d and returns a 2D PolySet.
*/
PolySet *Polygon2d::tessellate() const
{
PolySet *polyset = new PolySet;
polyset->is2d = true;
Polygon2DCGAL::CDT cdt; // Uses a constrained Delaunay triangulator.
OPENSCAD_CGAL_ERROR_BEGIN;
// Adds all vertices, and add all contours as constraints.
BOOST_FOREACH(const Outline2d &outline, this->outlines()) {
// Start with last point
Polygon2DCGAL::CDT::Vertex_handle prev = cdt.insert(Polygon2DCGAL::Point(outline[outline.size()-1][0], outline[outline.size()-1][1]));
BOOST_FOREACH(const Vector2d &v, outline) {
Polygon2DCGAL::CDT::Vertex_handle curr = cdt.insert(Polygon2DCGAL::Point(v[0], v[1]));
if (prev != curr) { // Ignore duplicate vertices
cdt.insert_constraint(prev, curr);
prev = curr;
}
}
}
OPENSCAD_CGAL_ERROR_END("CGAL error in Polygon2d::tesselate()", return NULL);
// To extract triangles which is part of our polygon, we need to filter away
// triangles inside holes.
mark_domains(cdt);
for (Polygon2DCGAL::CDT::Finite_faces_iterator fit=cdt.finite_faces_begin();
fit!=cdt.finite_faces_end();++fit) {
if (fit->info().in_domain()) {
polyset->append_poly();
for (int i=0;i<3;i++) polyset->append_vertex(fit->vertex(i)->point()[0],
fit->vertex(i)->point()[1],
0);
}
}
return polyset;
}

12
src/Polygon2d-CGAL.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef POLYGON2D_CGAL_H_
#define POLYGON2D_CGAL_H_
#include "Polygon2d.h"
#include "cgal.h"
#include "CGAL_Nef_polyhedron.h"
namespace Polygon2DCGAL {
CGAL_Nef_polyhedron toNefPolyhedron();
};
#endif

View File

@ -4,7 +4,7 @@
size_t Polygon2d::memsize() const
{
size_t mem = 0;
BOOST_FOREACH(const Outline2d &o, this->outlines) {
BOOST_FOREACH(const Outline2d &o, this->outlines()) {
mem += o.size() * sizeof(Vector2d) + sizeof(Outline2d);
}
mem += sizeof(Polygon2d);
@ -14,7 +14,7 @@ size_t Polygon2d::memsize() const
BoundingBox Polygon2d::getBoundingBox() const
{
BoundingBox bbox;
BOOST_FOREACH(const Outline2d &o, this->outlines) {
BOOST_FOREACH(const Outline2d &o, this->outlines()) {
BOOST_FOREACH(const Vector2d &v, o) {
bbox.extend(Vector3d(v[0], v[1], 0));
}
@ -54,7 +54,11 @@ std::string Polygon2d::dump() const
return out.str();
}
// PolySet *Polygon2d::tessellate()
// {
// return NULL;
// }
void Polygon2d::transform(const Transform2d &mat)
{
BOOST_FOREACH(Outline2d &outline, this->theoutlines) {
BOOST_FOREACH(Vector2d &v, outline) {
v = mat * v;
}
}
}

View File

@ -7,17 +7,24 @@
typedef std::vector<Vector2d> Outline2d;
class Polygon2d : public Geometry
{
public:
virtual size_t memsize() const;
virtual BoundingBox getBoundingBox() const;
virtual std::string dump() const;
virtual unsigned int getDimension() const { return 2; }
void addOutline(const Outline2d &outline);
// class PolySet *tessellate();
void addOutline(const Outline2d &outline) { this->theoutlines.push_back(outline); }
class PolySet *tessellate() const;
typedef std::vector<Outline2d> Outlines2d;
const Outlines2d &outlines() const { return theoutlines; }
void transform(const Transform2d &mat);
private:
std::vector<Outline2d> outlines;
Outlines2d theoutlines;
};
#endif

View File

@ -63,20 +63,20 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight,
bool fberror) const
{
glDepthFunc(GL_LEQUAL);
boost::unordered_map<std::pair<PolySet*,const Transform3d*>,int> polySetVisitMark;
boost::unordered_map<std::pair<const Geometry*,const Transform3d*>,int> geomVisitMark;
BOOST_FOREACH(const CSGChainObject &obj, chain->objects) {
if (polySetVisitMark[std::make_pair(obj.polyset.get(), &obj.matrix)]++ > 0)
if (geomVisitMark[std::make_pair(obj.geom.get(), &obj.matrix)]++ > 0)
continue;
const Transform3d &m = obj.matrix;
const Color4f &c = obj.color;
glPushMatrix();
glMultMatrixd(m.data());
PolySet::csgmode_e csgmode = obj.type == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL;
csgmode_e csgmode = obj.type == CSGTerm::TYPE_DIFFERENCE ? CSGMODE_DIFFERENCE : CSGMODE_NORMAL;
ColorMode colormode = COLORMODE_NONE;
ColorMode edge_colormode = COLORMODE_NONE;
if (highlight) {
csgmode = PolySet::csgmode_e(csgmode + 20);
csgmode = csgmode_e(csgmode + 20);
colormode = COLORMODE_HIGHLIGHT;
edge_colormode = COLORMODE_HIGHLIGHT_EDGES;
} else if (background) {
@ -86,16 +86,16 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight,
else {
colormode = COLORMODE_BACKGROUND;
}
csgmode = PolySet::csgmode_e(csgmode + 10);
csgmode = csgmode_e(csgmode + 10);
edge_colormode = COLORMODE_BACKGROUND_EDGES;
} else if (fberror) {
if (highlight) csgmode = PolySet::csgmode_e(csgmode + 20);
else if (background) csgmode = PolySet::csgmode_e(csgmode + 10);
else csgmode = PolySet::csgmode_e(csgmode);
if (highlight) csgmode = csgmode_e(csgmode + 20);
else if (background) csgmode = csgmode_e(csgmode + 10);
else csgmode = csgmode_e(csgmode);
} else if (obj.type == CSGTerm::TYPE_DIFFERENCE) {
if (obj.flag & CSGTerm::FLAG_HIGHLIGHT) {
colormode = COLORMODE_HIGHLIGHT;
csgmode = PolySet::csgmode_e(csgmode + 20);
csgmode = csgmode_e(csgmode + 20);
}
else {
colormode = COLORMODE_CUTOUT;
@ -104,7 +104,7 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight,
} else {
if (obj.flag & CSGTerm::FLAG_HIGHLIGHT) {
colormode = COLORMODE_HIGHLIGHT;
csgmode = PolySet::csgmode_e(csgmode + 20);
csgmode = csgmode_e(csgmode + 20);
}
else {
colormode = COLORMODE_MATERIAL;
@ -113,11 +113,11 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight,
}
setColor(colormode, c.data());
obj.polyset->render_surface(csgmode, m);
render_surface(obj.geom, csgmode, m);
if (showedges) {
// FIXME? glColor4f((c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0);
setColor(edge_colormode);
obj.polyset->render_edges(csgmode);
render_edges(obj.geom, csgmode);
}
glPopMatrix();

View File

@ -3,10 +3,12 @@
#include "cgalutils.h"
#include "polyset.h"
#include "printutils.h"
#include "Polygon2d.h"
#include "cgal.h"
#include <map>
#include <boost/foreach.hpp>
bool createPolySetFromPolyhedron(const CGAL_Polyhedron &p, PolySet &ps)
{
@ -222,5 +224,297 @@ void ZRemover::visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet )
log << " <!-- ZRemover Halffacet visit end -->\n";
}
static CGAL_Nef_polyhedron createNefPolyhedronFromPolySet(const PolySet &ps)
{
if (ps.empty()) return CGAL_Nef_polyhedron(ps.is2d ? 2 : 3);
if (ps.is2d)
{
#if 0
// This version of the code causes problems in some cases.
// Example testcase: import_dxf("testdata/polygon8.dxf");
//
typedef std::list<CGAL_Nef_polyhedron2::Point> point_list_t;
typedef point_list_t::iterator point_list_it;
std::list< point_list_t > pdata_point_lists;
std::list < std::pair < point_list_it, point_list_it > > pdata;
Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
for (int i = 0; i < ps.polygons.size(); i++) {
pdata_point_lists.push_back(point_list_t());
for (int j = 0; j < ps.polygons[i].size(); j++) {
double x = ps.polygons[i][j].x;
double y = ps.polygons[i][j].y;
CGAL_Nef_polyhedron2::Point p;
if (grid.has(x, y)) {
p = grid.data(x, y);
} else {
p = CGAL_Nef_polyhedron2::Point(x, y);
grid.data(x, y) = p;
}
pdata_point_lists.back().push_back(p);
}
pdata.push_back(std::make_pair(pdata_point_lists.back().begin(),
pdata_point_lists.back().end()));
}
CGAL_Nef_polyhedron2 N(pdata.begin(), pdata.end(), CGAL_Nef_polyhedron2::POLYGONS);
return CGAL_Nef_polyhedron(N);
#endif
#if 0
// This version of the code works fine but is pretty slow.
//
CGAL_Nef_polyhedron2 N;
Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
for (int i = 0; i < ps.polygons.size(); i++) {
std::list<CGAL_Nef_polyhedron2::Point> plist;
for (int j = 0; j < ps.polygons[i].size(); j++) {
double x = ps.polygons[i][j].x;
double y = ps.polygons[i][j].y;
CGAL_Nef_polyhedron2::Point p;
if (grid.has(x, y)) {
p = grid.data(x, y);
} else {
p = CGAL_Nef_polyhedron2::Point(x, y);
grid.data(x, y) = p;
}
plist.push_back(p);
}
N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
}
return CGAL_Nef_polyhedron(N);
#endif
#if 1
// This version of the code does essentially the same thing as the 2nd
// version but merges some triangles before sending them to CGAL. This adds
// complexity but speeds up things..
//
struct PolyReducer
{
Grid2d<int> grid;
std::map<std::pair<int,int>, std::pair<int,int> > edge_to_poly;
std::map<int, CGAL_Nef_polyhedron2::Point> points;
typedef std::map<int, std::vector<int> > PolygonMap;
PolygonMap polygons;
int poly_n;
void add_edges(int pn)
{
for (unsigned int j = 1; j <= this->polygons[pn].size(); j++) {
int a = this->polygons[pn][j-1];
int b = this->polygons[pn][j % this->polygons[pn].size()];
if (a > b) { a = a^b; b = a^b; a = a^b; }
if (this->edge_to_poly[std::pair<int,int>(a, b)].first == 0)
this->edge_to_poly[std::pair<int,int>(a, b)].first = pn;
else if (this->edge_to_poly[std::pair<int,int>(a, b)].second == 0)
this->edge_to_poly[std::pair<int,int>(a, b)].second = pn;
else
abort();
}
}
void del_poly(int pn)
{
for (unsigned int j = 1; j <= this->polygons[pn].size(); j++) {
int a = this->polygons[pn][j-1];
int b = this->polygons[pn][j % this->polygons[pn].size()];
if (a > b) { a = a^b; b = a^b; a = a^b; }
if (this->edge_to_poly[std::pair<int,int>(a, b)].first == pn)
this->edge_to_poly[std::pair<int,int>(a, b)].first = 0;
if (this->edge_to_poly[std::pair<int,int>(a, b)].second == pn)
this->edge_to_poly[std::pair<int,int>(a, b)].second = 0;
}
this->polygons.erase(pn);
}
PolyReducer(const PolySet &ps) : grid(GRID_COARSE), poly_n(1)
{
int point_n = 1;
for (size_t i = 0; i < ps.polygons.size(); i++) {
for (size_t j = 0; j < ps.polygons[i].size(); j++) {
double x = ps.polygons[i][j][0];
double y = ps.polygons[i][j][1];
if (this->grid.has(x, y)) {
int idx = this->grid.data(x, y);
// Filter away two vertices with the same index (due to grid)
// This could be done in a more general way, but we'd rather redo the entire
// grid concept instead.
std::vector<int> &poly = this->polygons[this->poly_n];
if (std::find(poly.begin(), poly.end(), idx) == poly.end()) {
poly.push_back(this->grid.data(x, y));
}
} else {
this->grid.align(x, y) = point_n;
this->polygons[this->poly_n].push_back(point_n);
this->points[point_n] = CGAL_Nef_polyhedron2::Point(x, y);
point_n++;
}
}
if (this->polygons[this->poly_n].size() >= 3) {
add_edges(this->poly_n);
this->poly_n++;
}
else {
this->polygons.erase(this->poly_n);
}
}
}
int merge(int p1, int p1e, int p2, int p2e)
{
for (unsigned int i = 1; i < this->polygons[p1].size(); i++) {
int j = (p1e + i) % this->polygons[p1].size();
this->polygons[this->poly_n].push_back(this->polygons[p1][j]);
}
for (unsigned int i = 1; i < this->polygons[p2].size(); i++) {
int j = (p2e + i) % this->polygons[p2].size();
this->polygons[this->poly_n].push_back(this->polygons[p2][j]);
}
del_poly(p1);
del_poly(p2);
add_edges(this->poly_n);
return this->poly_n++;
}
void reduce()
{
std::deque<int> work_queue;
BOOST_FOREACH(const PolygonMap::value_type &i, polygons) {
work_queue.push_back(i.first);
}
while (!work_queue.empty()) {
int poly1_n = work_queue.front();
work_queue.pop_front();
if (this->polygons.find(poly1_n) == this->polygons.end()) continue;
for (unsigned int j = 1; j <= this->polygons[poly1_n].size(); j++) {
int a = this->polygons[poly1_n][j-1];
int b = this->polygons[poly1_n][j % this->polygons[poly1_n].size()];
if (a > b) { a = a^b; b = a^b; a = a^b; }
if (this->edge_to_poly[std::pair<int,int>(a, b)].first != 0 &&
this->edge_to_poly[std::pair<int,int>(a, b)].second != 0) {
int poly2_n = this->edge_to_poly[std::pair<int,int>(a, b)].first +
this->edge_to_poly[std::pair<int,int>(a, b)].second - poly1_n;
int poly2_edge = -1;
for (unsigned int k = 1; k <= this->polygons[poly2_n].size(); k++) {
int c = this->polygons[poly2_n][k-1];
int d = this->polygons[poly2_n][k % this->polygons[poly2_n].size()];
if (c > d) { c = c^d; d = c^d; c = c^d; }
if (a == c && b == d) {
poly2_edge = k-1;
continue;
}
int poly3_n = this->edge_to_poly[std::pair<int,int>(c, d)].first +
this->edge_to_poly[std::pair<int,int>(c, d)].second - poly2_n;
if (poly3_n < 0)
continue;
if (poly3_n == poly1_n)
goto next_poly1_edge;
}
work_queue.push_back(merge(poly1_n, j-1, poly2_n, poly2_edge));
goto next_poly1;
}
next_poly1_edge:;
}
next_poly1:;
}
}
CGAL_Nef_polyhedron2 *toNef()
{
CGAL_Nef_polyhedron2 *N = new CGAL_Nef_polyhedron2;
BOOST_FOREACH(const PolygonMap::value_type &i, polygons) {
std::list<CGAL_Nef_polyhedron2::Point> plist;
for (unsigned int j = 0; j < i.second.size(); j++) {
int p = i.second[j];
plist.push_back(points[p]);
}
*N += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
}
return N;
}
};
PolyReducer pr(ps);
pr.reduce();
return CGAL_Nef_polyhedron(pr.toNef());
#endif
#if 0
// This is another experimental version. I should run faster than the above,
// is a lot simpler and has only one known weakness: Degenerate polygons, which
// get repaired by GLUTess, might trigger a CGAL crash here. The only
// known case for this is triangle-with-duplicate-vertex.dxf
// FIXME: If we just did a projection, we need to recreate the border!
if (ps.polygons.size() > 0) assert(ps.borders.size() > 0);
CGAL_Nef_polyhedron2 N;
Grid2d<CGAL_Nef_polyhedron2::Point> grid(GRID_COARSE);
for (int i = 0; i < ps.borders.size(); i++) {
std::list<CGAL_Nef_polyhedron2::Point> plist;
for (int j = 0; j < ps.borders[i].size(); j++) {
double x = ps.borders[i][j].x;
double y = ps.borders[i][j].y;
CGAL_Nef_polyhedron2::Point p;
if (grid.has(x, y)) {
p = grid.data(x, y);
} else {
p = CGAL_Nef_polyhedron2::Point(x, y);
grid.data(x, y) = p;
}
plist.push_back(p);
}
// FIXME: If a border (path) has a duplicate vertex in dxf,
// the CGAL_Nef_polyhedron2 constructor will crash.
N ^= CGAL_Nef_polyhedron2(plist.begin(), plist.end(), CGAL_Nef_polyhedron2::INCLUDED);
}
return CGAL_Nef_polyhedron(N);
#endif
}
else // not (this->is2d)
{
CGAL_Nef_polyhedron3 *N = NULL;
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
// FIXME: Are we leaking memory for the CGAL_Polyhedron object?
CGAL_Polyhedron *P = createPolyhedronFromPolySet(ps);
if (P) {
N = new CGAL_Nef_polyhedron3(*P);
}
}
catch (const CGAL::Assertion_exception &e) {
PRINTB("CGAL error in CGAL_Nef_polyhedron3(): %s", e.what());
}
CGAL::set_error_behaviour(old_behaviour);
return CGAL_Nef_polyhedron(N);
}
return CGAL_Nef_polyhedron();
}
static CGAL_Nef_polyhedron createNefPolyhedronFromPolygon2d(const Polygon2d &polygon)
{
shared_ptr<PolySet> ps(polygon.tessellate());
return createNefPolyhedronFromPolySet(*ps);
}
CGAL_Nef_polyhedron createNefPolyhedronFromGeometry(const Geometry &geom)
{
const PolySet *ps = dynamic_cast<const PolySet*>(&geom);
if (ps) {
return createNefPolyhedronFromPolySet(*ps);
}
else {
const Polygon2d *poly2d = dynamic_cast<const Polygon2d*>(&geom);
if (poly2d) return createNefPolyhedronFromPolygon2d(*poly2d);
}
assert(false && "CGALEvaluator::evaluateCGALMesh(): Unsupported geometry type");
return CGAL_Nef_polyhedron();
}
#endif /* ENABLE_CGAL */

View File

@ -3,6 +3,9 @@
#include <cgal.h>
#include "polyset.h"
#include "CGAL_Nef_polyhedron.h"
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

@ -2,6 +2,7 @@
#include <QThread>
#include "Tree.h"
#include "GeometryEvaluator.h"
#include "CGALEvaluator.h"
#include "progress.h"
#include "printutils.h"
@ -29,8 +30,8 @@ void CGALWorker::work()
{
CGAL_Nef_polyhedron *root_N = NULL;
try {
CGALEvaluator evaluator(*this->tree);
root_N = new CGAL_Nef_polyhedron(evaluator.evaluateCGALMesh(*this->tree->root()));
GeometryEvaluator evaluator(*this->tree);
root_N = new CGAL_Nef_polyhedron(evaluator.cgalevaluator->evaluateCGALMesh(*this->tree->root()));
}
catch (const ProgressCancelException &e) {
PRINT("Rendering cancelled.");

31
src/clipper-utils.cc Normal file
View File

@ -0,0 +1,31 @@
#include "clipper-utils.h"
#include <boost/foreach.hpp>
namespace ClipperUtils {
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));
}
result.push_back(p);
}
return result;
}
Polygon2d *toPolygon2d(const ClipperLib::Polygons &poly) {
Polygon2d *result = new Polygon2d;
BOOST_FOREACH(const ClipperLib::Polygon &p, poly) {
Outline2d outline;
BOOST_FOREACH(const ClipperLib::IntPoint &ip, p) {
outline.push_back(Vector2d(1.0*ip.X/CLIPPER_SCALE,
1.0*ip.Y/CLIPPER_SCALE));
}
result->addOutline(outline);
}
return result;
}
};

16
src/clipper-utils.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef CLIPPER_UTILS_H_
#define CLIPPER_UTILS_H_
#include <polyclipping/clipper.hpp>
#include "Polygon2d.h"
namespace ClipperUtils {
static const unsigned int CLIPPER_SCALE = 100000;
ClipperLib::Polygons fromPolygon2d(const class Polygon2d &poly);
Polygon2d *toPolygon2d(const ClipperLib::Polygons &poly);
};
#endif

View File

@ -25,7 +25,7 @@
*/
#include "csgterm.h"
#include "polyset.h"
#include "Geometry.h"
#include "linalg.h"
#include <sstream>
#include <boost/foreach.hpp>
@ -34,7 +34,7 @@
\class CSGTerm
A CSGTerm is either a "primitive" or a CSG operation with two
children terms. A primitive in this context is any PolySet, which
children terms. A primitive in this context is any Geometry, which
may or may not have a subtree which is already evaluated (e.g. using
the render() module).
@ -103,8 +103,8 @@ shared_ptr<CSGTerm> CSGTerm::createCSGTerm(type_e type, CSGTerm *left, CSGTerm *
return createCSGTerm(type, shared_ptr<CSGTerm>(left), shared_ptr<CSGTerm>(right));
}
CSGTerm::CSGTerm(const shared_ptr<PolySet> &polyset, const Transform3d &matrix, const Color4f &color, const std::string &label)
: type(TYPE_PRIMITIVE), polyset(polyset), label(label), flag(CSGTerm::FLAG_NONE), m(matrix), color(color)
CSGTerm::CSGTerm(const shared_ptr<const Geometry> &geom, const Transform3d &matrix, const Color4f &color, const std::string &label)
: type(TYPE_PRIMITIVE), geom(geom), label(label), flag(CSGTerm::FLAG_NONE), m(matrix), color(color)
{
initBoundingBox();
}
@ -128,7 +128,7 @@ CSGTerm::~CSGTerm()
void CSGTerm::initBoundingBox()
{
if (this->type == TYPE_PRIMITIVE) {
this->bbox = this->m * this->polyset->getBoundingBox();
this->bbox = this->m * this->geom->getBoundingBox();
}
else {
const BoundingBox &leftbox = this->left->getBoundingBox();
@ -186,7 +186,7 @@ void CSGChain::import(shared_ptr<CSGTerm> term, CSGTerm::type_e type, CSGTerm::F
{
CSGTerm::Flag newflag = (CSGTerm::Flag)(term->flag | flag);
if (term->type == CSGTerm::TYPE_PRIMITIVE) {
this->objects.push_back(CSGChainObject(term->polyset, term->m, term->color, type, term->label, newflag));
this->objects.push_back(CSGChainObject(term->geom, term->m, term->color, type, term->label, newflag));
} else {
assert(term->left && term->right);
import(term->left, type, newflag);
@ -209,7 +209,7 @@ std::string CSGChain::dump(bool full)
dump << " *";
dump << obj.label;
if (full) {
dump << " polyset: \n" << obj.polyset->dump() << "\n";
dump << " polyset: \n" << obj.geom->dump() << "\n";
dump << " matrix: \n" << obj.matrix.matrix() << "\n";
dump << " color: \n" << obj.color << "\n";
}
@ -223,7 +223,7 @@ BoundingBox CSGChain::getBoundingBox() const
BoundingBox bbox;
BOOST_FOREACH(const CSGChainObject &obj, this->objects) {
if (obj.type != CSGTerm::TYPE_DIFFERENCE) {
BoundingBox psbox = obj.polyset->getBoundingBox();
BoundingBox psbox = obj.geom->getBoundingBox();
if (!psbox.isNull()) {
bbox.extend(obj.matrix * psbox);
}

View File

@ -6,7 +6,7 @@
#include "memory.h"
#include "linalg.h"
class PolySet;
class Geometry;
class CSGTerm
{
@ -28,14 +28,14 @@ public:
static shared_ptr<CSGTerm> createCSGTerm(type_e type, CSGTerm *left, CSGTerm *right);
type_e type;
shared_ptr<PolySet> polyset;
shared_ptr<const Geometry> geom;
std::string label;
shared_ptr<CSGTerm> left;
shared_ptr<CSGTerm> right;
BoundingBox bbox;
Flag flag;
CSGTerm(const shared_ptr<PolySet> &polyset, const Transform3d &matrix, const Color4f &color, const std::string &label);
CSGTerm(const shared_ptr<const Geometry> &geom, const Transform3d &matrix, const Color4f &color, const std::string &label);
~CSGTerm();
const BoundingBox &getBoundingBox() const { return this->bbox; }
@ -56,15 +56,15 @@ private:
class CSGChainObject
{
public:
CSGChainObject(shared_ptr<PolySet> polyset,
CSGChainObject(shared_ptr<const Geometry> geom,
const Transform3d &matrix,
const Color4f &color,
CSGTerm::type_e type,
const std::string &label,
CSGTerm::Flag flag = CSGTerm::FLAG_NONE)
: polyset(polyset), matrix(matrix), color(color), type(type), label(label), flag(flag) {}
: geom(geom), matrix(matrix), color(color), type(type), label(label), flag(flag) {}
shared_ptr<PolySet> polyset;
shared_ptr<const Geometry> geom;
Transform3d matrix;
Color4f color;
CSGTerm::type_e type;

11
src/enums.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef ENUMS_H_
#define ENUMS_H_
enum OpenSCADOperator {
CGE_UNION,
CGE_INTERSECTION,
CGE_DIFFERENCE,
CGE_MINKOWSKI
};
#endif

View File

@ -182,7 +182,7 @@ void read_stl_facet( std::ifstream &f, stl_facet &facet )
#endif
}
Geometry *ImportNode::evaluate_geometry(class PolySetEvaluator *) const
Geometry *ImportNode::createGeometry() const
{
PolySet *p = NULL;

View File

@ -12,10 +12,10 @@ enum import_type_e {
TYPE_DXF
};
class ImportNode : public AbstractPolyNode
class ImportNode : public LeafNode
{
public:
ImportNode(const ModuleInstantiation *mi, import_type_e type) : AbstractPolyNode(mi), type(type) { }
ImportNode(const ModuleInstantiation *mi, import_type_e type) : LeafNode(mi), type(type) { }
virtual Response accept(class State &state, Visitor &visitor) const {
return visitor.visit(state, *this);
}
@ -28,7 +28,8 @@ public:
int convexity;
double fn, fs, fa;
double origin_x, origin_y, scale;
virtual Geometry *evaluate_geometry(class PolySetEvaluator *) const;
virtual Geometry *evaluate_geometry(class PolySetEvaluator *) const { return createGeometry(); }
virtual Geometry *createGeometry() const;
};
#endif

View File

@ -17,8 +17,10 @@ using Eigen::Matrix3d;
using Eigen::Matrix4d;
#if EIGEN_WORLD_VERSION >= 3
#define Transform3d Eigen::Affine3d
#define Transform2d Eigen::Affine2d
#else
using Eigen::Transform3d;
using Eigen::Transform2d;
#endif
bool matrix_contains_infinity( const Transform3d &m );

View File

@ -87,7 +87,7 @@
#include "CGALCache.h"
#include "CGALEvaluator.h"
#include "PolySetCGALEvaluator.h"
#include "GeometryEvaluator.h"
#include "CGALRenderer.h"
#include "CGAL_Nef_polyhedron.h"
#include "cgal.h"
@ -791,12 +791,11 @@ void MainWindow::compileCSG(bool procevents)
progress_report_prep(this->root_node, report_func, this);
try {
#ifdef ENABLE_CGAL
CGALEvaluator cgalevaluator(this->tree);
PolySetCGALEvaluator psevaluator(cgalevaluator);
GeometryEvaluator geomevaluator(this->tree);
#else
PolySetEvaluator psevaluator(this->tree);
// FIXME: Will we support this?
#endif
CSGTermEvaluator csgrenderer(this->tree, &psevaluator);
CSGTermEvaluator csgrenderer(this->tree, &geomevaluator);
if (procevents) QApplication::processEvents();
this->root_raw_term = csgrenderer.evaluateCSGTerm(*root_node, highlight_terms, background_terms);
if (!root_raw_term) {

View File

@ -62,6 +62,11 @@ Response AbstractPolyNode::accept(class State &state, Visitor &visitor) const
return visitor.visit(state, *this);
}
Response LeafNode::accept(class State &state, Visitor &visitor) const
{
return visitor.visit(state, *this);
}
std::string AbstractNode::toString() const
{
return this->name() + "()";

View File

@ -83,6 +83,15 @@ public:
};
};
class LeafNode : public AbstractPolyNode
{
public:
LeafNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { };
virtual ~LeafNode() { };
virtual Response accept(class State &state, class Visitor &visitor) const;
virtual Geometry *createGeometry() const = 0;
};
std::ostream &operator<<(std::ostream &stream, const AbstractNode &node);
AbstractNode *find_root_tag(AbstractNode *n);

View File

@ -209,8 +209,7 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
parser_init(application_path);
Tree tree;
#ifdef ENABLE_CGAL
CGALEvaluator cgalevaluator(tree);
PolySetCGALEvaluator psevaluator(cgalevaluator);
GeometryEvaluator geomevaluator(tree);
#endif
const char *stl_output_file = NULL;
const char *off_output_file = NULL;
@ -313,7 +312,7 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
std::vector<shared_ptr<CSGTerm> > highlight_terms;
std::vector<shared_ptr<CSGTerm> > background_terms;
CSGTermEvaluator csgRenderer(tree, &psevaluator);
CSGTermEvaluator csgRenderer(tree, &geomevaluator);
shared_ptr<CSGTerm> root_raw_term = csgRenderer.evaluateCSGTerm(*root_node, highlight_terms, background_terms);
fs::current_path(original_path);
@ -335,7 +334,7 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
if ((echo_output_file || png_output_file) && !(renderer==Render::CGAL)) {
// echo or OpenCSG png -> don't necessarily need CGALMesh evaluation
} else {
root_N = cgalevaluator.evaluateCGALMesh(*tree.root());
root_N = geomevaluator.cgalevaluator->evaluateCGALMesh(*tree.root());
}
fs::current_path(original_path);

View File

@ -150,7 +150,7 @@ static void gl_draw_triangle(GLint *shaderinfo, const Vector3d &p0, const Vector
}
}
void PolySet::render_surface(csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo) const
void PolySet::render_surface(Renderer::csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo) const
{
bool mirrored = m.matrix().determinant() < 0;
#ifdef ENABLE_OPENCSG
@ -248,7 +248,7 @@ void PolySet::render_surface(csgmode_e csgmode, const Transform3d &m, GLint *sha
}
}
void PolySet::render_edges(csgmode_e csgmode) const
void PolySet::render_edges(Renderer::csgmode_e csgmode) const
{
glDisable(GL_LIGHTING);
if (this->is2d) {

View File

@ -5,6 +5,7 @@
#include "system-gl.h"
#include "grid.h"
#include "linalg.h"
#include "renderer.h"
#include <vector>
#include <string>
@ -20,32 +21,20 @@ public:
int convexity;
PolySet();
~PolySet();
virtual ~PolySet();
virtual size_t memsize() const;
virtual BoundingBox getBoundingBox() const;
virtual std::string dump() const;
virtual unsigned int getDimension() const { return this->is2d ? 2 : 3; }
virtual unsigned int getConvexity() const {return this->convexity; }
bool empty() const { return polygons.size() == 0; }
void append_poly();
void append_vertex(double x, double y, double z = 0.0);
void insert_vertex(double x, double y, double z = 0.0);
size_t memsize() const;
BoundingBox getBoundingBox() const;
#define CSGMODE_DIFFERENCE_FLAG 0x10
enum csgmode_e {
CSGMODE_NONE = 0x00,
CSGMODE_NORMAL = 0x01,
CSGMODE_DIFFERENCE = CSGMODE_NORMAL | CSGMODE_DIFFERENCE_FLAG,
CSGMODE_BACKGROUND = 0x02,
CSGMODE_BACKGROUND_DIFFERENCE = CSGMODE_BACKGROUND | CSGMODE_DIFFERENCE_FLAG,
CSGMODE_HIGHLIGHT = 0x03,
CSGMODE_HIGHLIGHT_DIFFERENCE = CSGMODE_HIGHLIGHT | CSGMODE_DIFFERENCE_FLAG
};
void render_surface(csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo = NULL) const;
void render_edges(csgmode_e csgmode) const;
void render_surface(Renderer::csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo = NULL) const;
void render_edges(Renderer::csgmode_e csgmode) const;
};
#endif

View File

@ -64,10 +64,10 @@ private:
Value lookup_radius(const Context &ctx, const std::string &radius_var, const std::string &diameter_var) const;
};
class PrimitiveNode : public AbstractPolyNode
class PrimitiveNode : public LeafNode
{
public:
PrimitiveNode(const ModuleInstantiation *mi, primitive_type_e type) : AbstractPolyNode(mi), type(type) { }
PrimitiveNode(const ModuleInstantiation *mi, primitive_type_e type) : LeafNode(mi), type(type) { }
virtual Response accept(class State &state, Visitor &visitor) const {
return visitor.visit(state, *this);
}
@ -107,7 +107,8 @@ public:
primitive_type_e type;
int convexity;
Value points, paths, faces;
virtual Geometry *evaluate_geometry(class PolySetEvaluator *) const;
virtual Geometry *evaluate_geometry(class PolySetEvaluator *) const { return createGeometry(); }
virtual Geometry *createGeometry() const;
};
/**
@ -292,7 +293,7 @@ static void generate_circle(point2d *circle, double r, int fragments)
}
}
Geometry *PrimitiveNode::evaluate_geometry(class PolySetEvaluator *) const
Geometry *PrimitiveNode::createGeometry() const
{
Geometry *g = NULL;
@ -502,166 +503,76 @@ sphere_next_r2:
}
}
if (this->type == SQUARE && x > 0 && y > 0)
if (this->type == SQUARE && this->x > 0 && this->y > 0)
{
/*
Polygon2d *p = new Polygon2d();
g = p;
double x1, x2, y1, y2;
Vector2d v1(0, 0);
Vector2d v2(this->x, this->y);
if (this->center) {
x1 = -this->x/2;
x2 = +this->x/2;
y1 = -this->y/2;
y2 = +this->y/2;
} else {
x1 = y1 = 0;
x2 = this->x;
y2 = this->y;
v1 -= Vector2d(this->x/2, this->y/2);
v2 -= Vector2d(this->x/2, this->y/2);
}
Outline2d o(4);
o.push_back(Vector2d(x1, y1));
o.push_back(Vector2d(x2, y1));
o.push_back(Vector2d(x2, y2));
o.push_back(Vector2d(x1, y2));
*/
PolySet *p = new PolySet();
g = p;
double x1, x2, y1, y2;
if (this->center) {
x1 = -this->x/2;
x2 = +this->x/2;
y1 = -this->y/2;
y2 = +this->y/2;
} else {
x1 = y1 = 0;
x2 = this->x;
y2 = this->y;
}
p->is2d = true;
p->append_poly();
p->append_vertex(x1, y1);
p->append_vertex(x2, y1);
p->append_vertex(x2, y2);
p->append_vertex(x1, y2);
o[0] = v1;
o[1] = Vector2d(v2[0], v1[1]);
o[2] = v2;
o[3] = Vector2d(v1[0], v2[1]);
p->addOutline(o);
}
if (this->type == CIRCLE)
if (this->type == CIRCLE && this->r1 > 0)
{
PolySet *p = new PolySet();
Polygon2d *p = new Polygon2d();
g = p;
int fragments = Calc::get_fragments_from_r(this->r1, this->fn, this->fs, this->fa);
p->is2d = true;
p->append_poly();
Outline2d o(fragments);
for (int i=0; i < fragments; i++) {
double phi = (M_PI*2*i) / fragments;
p->append_vertex(this->r1*cos(phi), this->r1*sin(phi));
o[i] = Vector2d(this->r1*cos(phi), this->r1*sin(phi));
}
p->addOutline(o);
}
if (this->type == POLYGON)
{
PolySet *p = new PolySet();
Polygon2d *p = new Polygon2d();
g = p;
DxfData dd;
for (size_t i=0; i<this->points.toVector().size(); i++) {
double x,y;
if (!this->points.toVector()[i].getVec2(x, y)) {
PRINTB("ERROR: Unable to convert point at index %d to a vec2 of numbers", i);
delete p;
return NULL;
}
dd.points.push_back(Vector2d(x, y));
}
if (this->paths.toVector().size() == 0)
{
if (dd.points.size() <= 2) { // Ignore malformed polygons
delete p;
return NULL;
}
dd.paths.push_back(DxfData::Path());
for (size_t i=0; i<dd.points.size(); i++) {
assert(i < dd.points.size()); // FIXME: Not needed, but this used to be an 'if'
dd.paths.back().indices.push_back(i);
}
if (dd.paths.back().indices.size() > 0) {
dd.paths.back().indices.push_back(dd.paths.back().indices.front());
dd.paths.back().is_closed = true;
}
}
else
{
for (size_t i=0; i<this->paths.toVector().size(); i++)
{
dd.paths.push_back(DxfData::Path());
for (size_t j=0; j<this->paths.toVector()[i].toVector().size(); j++) {
unsigned int idx = this->paths.toVector()[i].toVector()[j].toDouble();
if (idx < dd.points.size()) {
dd.paths.back().indices.push_back(idx);
}
}
if (dd.paths.back().indices.empty()) {
dd.paths.pop_back();
} else {
dd.paths.back().indices.push_back(dd.paths.back().indices.front());
dd.paths.back().is_closed = true;
}
}
}
p->is2d = true;
p->convexity = convexity;
dxf_tesselate(p, dd, 0, Vector2d(1,1), true, false, 0);
dxf_border_to_ps(p, dd);
/*
Polygon2D *p = new Polygon2D();
g = p;
// Collect vertices
std::vector<Vector2d> vertices;
for (size_t i=0; i<this->points.toVector().size(); i++) {
double x,y;
if (!this->points.toVector()[i].getVec2(x, y)) {
PRINTB("ERROR: Unable to convert point at index %d to a vec2 of numbers", i);
double x,y;
const Value::VectorType &vec = this->points.toVector();
for (int i=0;i<vec.size();i++) {
const Value &val = vec[i];
if (!val.getVec2(x, y)) {
PRINTB("ERROR: Unable to convert point %s at index %d to a vec2 of numbers",
val.toString() % i);
delete p;
return NULL;
}
vertices.push_back(Vector2d(x, y));
}
Polygon2d polygon;
// If no indices, assume one single polygon
if (this->paths.toVector().size() == 0) {
assert(vertices.size() >= 3); // FIXME: Fail gracefully
Outline2d outline;
BOOST_FOREACH(const Vector2d &p, vertices) outline.push_back(p);
polygon.addOutline(outline);
if (this->paths.toVector().size() == 0 && vertices.size() > 2) {
p->addOutline(vertices);
}
else {
BOOST_FOREACH(const Value &val, this->paths.toVector()) {
Outline2d outline;
BOOST_FOREACH(const Value &idxval, val.toVector()) {
unsigned int idx = idxval.toDouble();
if (idx < vertices.size()) outline.push_back(vertices[idx]);
BOOST_FOREACH(const Value &polygon, this->paths.toVector()) {
Outline2d curroutline;
BOOST_FOREACH(const Value &index, polygon.toVector()) {
unsigned int idx = index.toDouble();
if (idx < vertices.size()) {
curroutline.push_back(vertices[idx]);
}
// FIXME: Warning on out of bounds?
}
polygon.addOutline(outline);
p->addOutline(curroutline);
}
}
ScadPolygon::tessellate(polygons);
ScadPolygon::createPolyset(*p, polygons);
p->is2d = true;
p->convexity = convexity;
// dxf_tesselate(p, dd, 0, true, false, 0);
// dxf_border_to_ps(p, dd);
*/
// FIXME: p->convexity = convexity;
}
// FIXME: IF the above failed, create an empty polyset as that's required later on

View File

@ -1,5 +1,8 @@
#include "renderer.h"
#include "rendersettings.h"
#include "Geometry.h"
#include "polyset.h"
#include "Polygon2d.h"
bool Renderer::getColor(Renderer::ColorMode colormode, Color4f &col) const
{
@ -80,3 +83,34 @@ void Renderer::setColor(ColorMode colormode, GLint *shaderinfo) const
float c[4] = {-1,-1,-1,-1};
setColor(colormode, c, shaderinfo);
}
void Renderer::render_surface(shared_ptr<const Geometry> geom, csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo)
{
shared_ptr<const PolySet> ps;
shared_ptr<const Polygon2d> p2d = dynamic_pointer_cast<const Polygon2d>(geom);
if (p2d) {
ps.reset(p2d->tessellate());
}
else {
ps = dynamic_pointer_cast<const PolySet>(geom);
}
if (ps) {
ps->render_surface(csgmode, m, shaderinfo);
}
}
void Renderer::render_edges(shared_ptr<const Geometry> geom, csgmode_e csgmode)
{
shared_ptr<const PolySet> ps;
shared_ptr<const Polygon2d> p2d = dynamic_pointer_cast<const Polygon2d>(geom);
if (p2d) {
ps.reset(p2d->tessellate());
}
else {
ps = dynamic_pointer_cast<const PolySet>(geom);
}
if (ps) {
ps->render_edges(csgmode);
}
}

View File

@ -3,6 +3,7 @@
#include "system-gl.h"
#include "linalg.h"
#include "memory.h"
#ifdef _MSC_VER // NULL
#include <cstdlib>
@ -14,6 +15,17 @@ public:
virtual ~Renderer() {}
virtual void draw(bool showfaces, bool showedges) const = 0;
#define CSGMODE_DIFFERENCE_FLAG 0x10
enum csgmode_e {
CSGMODE_NONE = 0x00,
CSGMODE_NORMAL = 0x01,
CSGMODE_DIFFERENCE = CSGMODE_NORMAL | CSGMODE_DIFFERENCE_FLAG,
CSGMODE_BACKGROUND = 0x02,
CSGMODE_BACKGROUND_DIFFERENCE = CSGMODE_BACKGROUND | CSGMODE_DIFFERENCE_FLAG,
CSGMODE_HIGHLIGHT = 0x03,
CSGMODE_HIGHLIGHT_DIFFERENCE = CSGMODE_HIGHLIGHT | CSGMODE_DIFFERENCE_FLAG
};
enum ColorMode {
COLORMODE_NONE,
COLORMODE_MATERIAL,
@ -30,6 +42,10 @@ public:
virtual void setColor(const float color[4], GLint *shaderinfo = NULL) const;
virtual void setColor(ColorMode colormode, GLint *shaderinfo = NULL) const;
virtual void setColor(ColorMode colormode, const float color[4], GLint *shaderinfo = NULL) const;
static void render_surface(shared_ptr<const class Geometry> geom, csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo = NULL);
static void render_edges(shared_ptr<const Geometry> geom, csgmode_e csgmode);
};
#endif // RENDERER_H

View File

@ -54,10 +54,10 @@ public:
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
};
class SurfaceNode : public AbstractPolyNode
class SurfaceNode : public LeafNode
{
public:
SurfaceNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { }
SurfaceNode(const ModuleInstantiation *mi) : LeafNode(mi) { }
virtual Response accept(class State &state, Visitor &visitor) const {
return visitor.visit(state, *this);
}
@ -67,7 +67,8 @@ public:
Filename filename;
bool center;
int convexity;
virtual Geometry *evaluate_geometry(class PolySetEvaluator *) const;
virtual Geometry *evaluate_geometry(class PolySetEvaluator *) const { return createGeometry(); }
virtual Geometry *createGeometry() const;
};
AbstractNode *SurfaceModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
@ -98,7 +99,7 @@ AbstractNode *SurfaceModule::instantiate(const Context *ctx, const ModuleInstant
return node;
}
Geometry *SurfaceNode::evaluate_geometry(class PolySetEvaluator *) const
Geometry *SurfaceNode::createGeometry() const
{
handle_dep(filename);
std::ifstream stream(filename.c_str());

View File

@ -16,6 +16,9 @@ public:
virtual Response visit(class State &state, const class AbstractPolyNode &node) {
return visit(state, (const class AbstractNode &)node);
}
virtual Response visit(class State &state, const class LeafNode &node) {
return visit(state, (const class AbstractPolyNode &)node);
}
virtual Response visit(class State &state, const class CgaladvNode &node) {
return visit(state, (const class AbstractNode &)node);
}
@ -29,10 +32,10 @@ public:
return visit(state, (const class AbstractPolyNode &)node);
}
virtual Response visit(class State &state, const class ImportNode &node) {
return visit(state, (const class AbstractPolyNode &)node);
return visit(state, (const class LeafNode &)node);
}
virtual Response visit(class State &state, const class PrimitiveNode &node) {
return visit(state, (const class AbstractPolyNode &)node);
return visit(state, (const class LeafNode &)node);
}
virtual Response visit(class State &state, const class ProjectionNode &node) {
return visit(state, (const class AbstractPolyNode &)node);
@ -41,7 +44,7 @@ public:
return visit(state, (const class AbstractNode &)node);
}
virtual Response visit(class State &state, const class SurfaceNode &node) {
return visit(state, (const class AbstractPolyNode &)node);
return visit(state, (const class LeafNode &)node);
}
virtual Response visit(class State &state, const class TransformNode &node) {
return visit(state, (const class AbstractNode &)node);

View File

@ -418,6 +418,29 @@ find_package(GLIB2 2.2.0 REQUIRED)
add_definitions(${GLIB2_DEFINITIONS})
inclusion(GLIB2_DIR GLIB2_INCLUDE_DIRS)
# Clipper
if (NOT $ENV{CLIPPERDIR} STREQUAL "")
set(CLIPPER_DIR "$ENV{CLIPPERDIR}")
elseif (NOT $ENV{OPENSCAD_LIBRARIES} STREQUAL "")
set(CLIPPER_DIR "$ENV{OPENSCAD_LIBRARIES}")
endif()
if (NOT CLIPPER_INCLUDE_DIR)
message(STATUS "CLIPPER_DIR: " ${CLIPPER_DIR})
find_path(CLIPPER_INCLUDE_DIR
polyclipping/clipper.hpp
HINTS ${CLIPPER_DIR}/include)
find_library(CLIPPER_LIBRARY
polyclipping
HINTS ${CLIPPER_DIR}/lib)
if (NOT CLIPPER_INCLUDE_DIR OR NOT CLIPPER_LIBRARY)
message(FATAL_ERROR "Clipper not found")
else()
message(STATUS "Clipper include found in " ${CLIPPER_INCLUDE_DIR})
message(STATUS "Clipper library found in " ${CLIPPER_LIBRARY})
endif()
endif()
inclusion(CLIPPER_DIR CLIPPER_INCLUDE_DIR)
# Imagemagick
if (SKIP_IMAGEMAGICK)
@ -561,13 +584,16 @@ set(CGAL_SOURCES
../src/PolySetCGALEvaluator.cc
../src/CGAL_Nef_polyhedron_DxfData.cc
../src/cgaladv_minkowski2.cc
../src/svg.cc)
../src/Polygon2d-CGAL.cc
../src/svg.cc
../src/GeometryEvaluator.cc)
set(COMMON_SOURCES
../src/nodedumper.cc
../src/traverser.cc
../src/PolySetEvaluator.cc
../src/GeometryCache.cc
../src/clipper-utils.cc
../src/Tree.cc
../src/lodepng.cpp)
@ -627,14 +653,14 @@ target_link_libraries(csgtexttest tests-nocgal ${TESTS-NOCGAL-LIBRARIES})
#
add_executable(cgalcachetest cgalcachetest.cc)
set_target_properties(cgalcachetest PROPERTIES COMPILE_FLAGS "-DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}")
target_link_libraries(cgalcachetest tests-cgal ${TESTS-CGAL-LIBRARIES} ${GLEW_LIBRARY} ${COCOA_LIBRARY})
target_link_libraries(cgalcachetest tests-cgal ${TESTS-CGAL-LIBRARIES} ${CLIPPER_LIBRARY} ${GLEW_LIBRARY} ${COCOA_LIBRARY})
#
# openscad no-qt
#
add_executable(openscad_nogui ../src/openscad.cc)
set_target_properties(openscad_nogui PROPERTIES COMPILE_FLAGS "-fno-strict-aliasing -DEIGEN_DONT_ALIGN -DENABLE_CGAL -DENABLE_OPENCSG ${CGAL_CXX_FLAGS_INIT}")
target_link_libraries(openscad_nogui tests-offscreen tests-cgal tests-nocgal ${TESTS-CORE-LIBRARIES} ${TESTS-CGAL-LIBRARIES} ${GLEW_LIBRARY} ${OPENCSG_LIBRARY} ${COCOA_LIBRARY} ${APP_SERVICES_LIBRARY})
target_link_libraries(openscad_nogui tests-offscreen tests-cgal tests-nocgal ${TESTS-CORE-LIBRARIES} ${TESTS-CGAL-LIBRARIES} ${GLEW_LIBRARY} ${Boost_LIBRARIES} ${OPENCSG_LIBRARY} ${COCOA_LIBRARY} ${APP_SERVICES_LIBRARY})
#
# GUI binary tests

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB