mirror of https://github.com/vitalif/openscad
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.
parent
acd08b6f1a
commit
b8c15cfb8a
|
@ -13,3 +13,4 @@ include(eigen.pri)
|
|||
include(boost.pri)
|
||||
include(glib-2.0.pri)
|
||||
include(sparkle.pri)
|
||||
include(clipper.pri)
|
||||
|
|
11
openscad.pro
11
openscad.pro
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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() { }
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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>());
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
294
src/cgalutils.cc
294
src/cgalutils.cc
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef ENUMS_H_
|
||||
#define ENUMS_H_
|
||||
|
||||
enum OpenSCADOperator {
|
||||
CGE_UNION,
|
||||
CGE_INTERSECTION,
|
||||
CGE_DIFFERENCE,
|
||||
CGE_MINKOWSKI
|
||||
};
|
||||
|
||||
#endif
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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() + "()";
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 |
Loading…
Reference in New Issue