summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/AboutDialog.h4
-rw-r--r--src/CGAL_Nef_polyhedron.cc3
-rw-r--r--src/CGAL_Nef_polyhedron.h2
-rw-r--r--src/CGAL_Nef_polyhedron_DxfData.cc31
-rw-r--r--src/PolySetCGALEvaluator.cc181
-rw-r--r--src/cgal.h12
-rw-r--r--src/cgalutils.cc28
-rw-r--r--src/cgalutils.h2
-rw-r--r--src/printutils.h22
-rw-r--r--src/svg.cc249
-rw-r--r--src/svg.h28
11 files changed, 477 insertions, 85 deletions
diff --git a/src/AboutDialog.h b/src/AboutDialog.h
index 34122a0..1ae6533 100644
--- a/src/AboutDialog.h
+++ b/src/AboutDialog.h
@@ -3,12 +3,16 @@
#include "ui_AboutDialog.h"
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+
class AboutDialog : public QDialog, public Ui::AboutDialog
{
Q_OBJECT;
public:
AboutDialog(QWidget *) {
setupUi(this);
+ this->setWindowTitle( QString("About OpenSCAD ") + QString(TOSTRING( OPENSCAD_VERSION)) );
this->aboutText->setOpenExternalLinks(true);
QUrl flattr_qurl(":icons/flattr.png" );
this->aboutText->loadResource( QTextDocument::ImageResource, flattr_qurl );
diff --git a/src/CGAL_Nef_polyhedron.cc b/src/CGAL_Nef_polyhedron.cc
index 56232a3..ad2e4e2 100644
--- a/src/CGAL_Nef_polyhedron.cc
+++ b/src/CGAL_Nef_polyhedron.cc
@@ -84,7 +84,8 @@ int CGAL_Nef_polyhedron::weight() const
*/
PolySet *CGAL_Nef_polyhedron::convertToPolyset()
{
- assert(!this->empty());
+ if (this->empty())
+ return new PolySet();
PolySet *ps = NULL;
if (this->dim == 2) {
ps = new PolySet();
diff --git a/src/CGAL_Nef_polyhedron.h b/src/CGAL_Nef_polyhedron.h
index 694b420..e8c505b 100644
--- a/src/CGAL_Nef_polyhedron.h
+++ b/src/CGAL_Nef_polyhedron.h
@@ -20,7 +20,7 @@ public:
CGAL_Nef_polyhedron &operator-=(const CGAL_Nef_polyhedron &other);
CGAL_Nef_polyhedron &minkowski(const CGAL_Nef_polyhedron &other);
CGAL_Nef_polyhedron copy() const;
- std::string dump_p2() const;
+ std::string dump() const;
int weight() const;
class PolySet *convertToPolyset();
class DxfData *convertToDxfData() const;
diff --git a/src/CGAL_Nef_polyhedron_DxfData.cc b/src/CGAL_Nef_polyhedron_DxfData.cc
index 411a340..0d0b8f0 100644
--- a/src/CGAL_Nef_polyhedron_DxfData.cc
+++ b/src/CGAL_Nef_polyhedron_DxfData.cc
@@ -28,6 +28,8 @@
#include "grid.h"
#include "CGAL_Nef_polyhedron.h"
#include "cgal.h"
+#include "cgalutils.h"
+#include "svg.h"
#ifdef ENABLE_CGAL
@@ -77,29 +79,14 @@ DxfData *CGAL_Nef_polyhedron::convertToDxfData() const
return dxfdata;
}
-// dump the 2 dimensional nef_poly.
-std::string CGAL_Nef_polyhedron::dump_p2() const
+std::string CGAL_Nef_polyhedron::dump() const
{
- std::stringstream out;
- CGAL_Nef_polyhedron2::Explorer explorer = this->p2->explorer();
- CGAL_Nef_polyhedron2::Explorer::Vertex_const_iterator i;
- out << "CGAL_Nef_polyhedron::p2 Vertices";
- for (i = explorer.vertices_begin(); i != explorer.vertices_end(); ++i) {
- if ( explorer.is_standard( i ) ) {
- CGAL_Nef_polyhedron2::Explorer::Point point = explorer.point( i );
- out << "\n Point x y: "
- << CGAL::to_double(point.x()) << " "
- << CGAL::to_double(point.y());
- } else {
- CGAL_Nef_polyhedron2::Explorer::Ray ray = explorer.ray( i );
- CGAL_Nef_polyhedron2::Explorer::Point point = ray.point( 0 );
- out << "\n Ray x y dx dy: "
- << CGAL::to_double(point.x()) << " " << CGAL::to_double(point.y()) << " "
- << CGAL::to_double(ray.direction().dx()) << " " << CGAL::to_double(ray.direction().dy());
- }
- }
- out << "\nCGAL_Nef_polyhedron::p2 Vertices end";
- return out.str();
+ if (this->dim==2)
+ return OpenSCAD::dump_svg( *this->p2 );
+ else if (this->dim==3)
+ return OpenSCAD::dump_svg( *this->p3 );
+ else
+ return std::string("Nef Polyhedron with dimension != 2 or 3");
}
#endif // ENABLE_CGAL
diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc
index bca9902..8e08e19 100644
--- a/src/PolySetCGALEvaluator.cc
+++ b/src/PolySetCGALEvaluator.cc
@@ -1,6 +1,8 @@
#include "PolySetCGALEvaluator.h"
#include "cgal.h"
#include "cgalutils.h"
+#include <CGAL/convex_hull_3.h>
+
#include "polyset.h"
#include "CGALEvaluator.h"
#include "projectionnode.h"
@@ -12,53 +14,53 @@
#include "dxftess.h"
#include "module.h"
+#include "svg.h"
#include "printutils.h"
#include "openscad.h" // get_fragments_from_r()
#include <boost/foreach.hpp>
+#include <vector>
/*
-This Visitor is used in the 'cut' process. Essentially, one or more of
-our 3d nef polyhedrons have been 'cut' by the xy-plane, forming a number
-of polygons that are essentially 'flat'. This Visitor object, when
-called repeatedly, collects those flat polygons together and forms a new
-2d nef polyhedron out of them. It keeps track of the 'holes' so that
-in the final resulting polygon, they are preserved properly.
+ZRemover
-The output polygon is stored in the output_nefpoly2d variable.
+This class converts one or more already 'flat' Nef3 polyhedra into a Nef2
+polyhedron by stripping off the 'z' coordinates from the vertices. The
+resulting Nef2 poly is accumulated in the 'output_nefpoly2d' member variable.
-For more information on 3d + 2d nef polyhedrons, facets, halffacets,
-facet cycles, etc, please see these websites:
+The 'z' coordinates will either be all 0s, for an xy-plane intersected Nef3,
+or, they will be a mixture of -eps and +eps, for a thin-box intersected Nef3.
-http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3/Chapter_main.html
-http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3_ref/Class_Nef_polyhedron_3-Traits---Halffacet.html
+Notes on CGAL's Nef Polyhedron2:
-The halffacet iteration 'circulator' code is based on OGL_helper.h
+1. The 'mark' on a 2d Nef face is important when doing unions/intersections.
+ If the 'mark' of a face is wrong the resulting nef2 poly will be unexpected.
+2. The 'mark' can be dependent on the points fed to the Nef2 constructor.
+ This is why we iterate through the 3d faces using the halfedge cycle
+ source()->target() instead of the ordinary source()->source(). The
+ the latter can generate sequences of points that will fail the
+ the CGAL::is_simple_2() test, resulting in improperly marked nef2 polys.
+3. 3d facets have 'two sides'. we throw out the 'down' side to prevent dups.
-Why do we throw out all 'down' half-facets? Imagine a triangle in 3d
-space - it has one 'half face' for one 'side' and another 'half face' for the
-other 'side'. If we iterated over both half-faces we would get the same vertex
-coordinates twice. Instead, we only need one side, in our case, we
-chose the 'up' side.
+The class uses the 'visitor' pattern from the CGAL manual. See also
+http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3/Chapter_main.html
+http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3_ref/Class_Nef_polyhedron3.html
+OGL_helper.h
*/
-class NefShellVisitor_for_cut {
+
+class ZRemover {
public:
- std::stringstream out;
+ logstream log;
CGAL_Nef_polyhedron2::Boundary boundary;
shared_ptr<CGAL_Nef_polyhedron2> tmpnef2d;
shared_ptr<CGAL_Nef_polyhedron2> output_nefpoly2d;
CGAL::Direction_3<CGAL_Kernel3> up;
- bool debug;
- NefShellVisitor_for_cut(bool debug=false)
+ ZRemover()
{
output_nefpoly2d.reset( new CGAL_Nef_polyhedron2() );
boundary = CGAL_Nef_polyhedron2::INCLUDED;
up = CGAL::Direction_3<CGAL_Kernel3>(0,0,1);
- this->debug = debug;
- }
- std::string dump()
- {
- return out.str();
+ log = logstream(5);
}
void visit( CGAL_Nef_polyhedron3::Vertex_const_handle ) {}
void visit( CGAL_Nef_polyhedron3::Halfedge_const_handle ) {}
@@ -66,42 +68,66 @@ public:
void visit( CGAL_Nef_polyhedron3::SHalfloop_const_handle ) {}
void visit( CGAL_Nef_polyhedron3::SFace_const_handle ) {}
void visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet ) {
+ log << " <!-- Halffacet visit. Mark: " << hfacet->mark() << " -->\n";
if ( hfacet->plane().orthogonal_direction() != this->up ) {
- if (debug) out << "down facing half-facet. skipping\n";
+ log << " <!-- down-facing half-facet. skipping -->\n";
+ log << " <!-- Halffacet visit end-->\n";
return;
}
- int numcontours = 0;
- CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator i;
- CGAL_forall_facet_cycles_of( i, hfacet ) {
- CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(i), c2(c1);
- std::list<CGAL_Nef_polyhedron2::Point> contour;
- CGAL_For_all( c1, c2 ) {
- CGAL_Nef_polyhedron3::Point_3 point3d = c1->source()->source()->point();
- CGAL_Nef_polyhedron2::Point point2d( point3d.x(), point3d.y() );
- contour.push_back( point2d );
- }
- tmpnef2d.reset( new CGAL_Nef_polyhedron2( contour.begin(), contour.end(), boundary ) );
- if ( numcontours == 0 ) {
- if (debug) out << " contour is a body. make union(). " << contour.size() << " points.\n" ;
- *output_nefpoly2d += *tmpnef2d;
+ // possible optimization - throw out facets that are 'side facets' between
+ // the top & bottom of the big thin box. (i.e. mixture of z=-eps and z=eps)
+
+ CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator fci;
+ int contour_counter = 0;
+ CGAL_forall_facet_cycles_of( fci, hfacet ) {
+ if ( fci.is_shalfedge() ) {
+ CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(fci), cend(c1);
+ std::vector<CGAL_Nef_polyhedron2::Explorer::Point> contour;
+ CGAL_For_all( c1, cend ) {
+ CGAL_Nef_polyhedron3::Point_3 point3d = c1->source()->target()->point();
+ CGAL_Nef_polyhedron2::Explorer::Point point2d( point3d.x(), point3d.y() );
+ contour.push_back( point2d );
+ }
+
+ if (contour.size()==0) continue;
+
+ log << " <!-- is_simple_2:" << CGAL::is_simple_2( contour.begin(), contour.end() ) << " --> \n";
+
+ tmpnef2d.reset( new CGAL_Nef_polyhedron2( contour.begin(), contour.end(), boundary ) );
+
+ if ( contour_counter == 0 ) {
+ log << " <!-- contour is a body. make union(). " << contour.size() << " points. -->\n" ;
+ *(output_nefpoly2d) += *(tmpnef2d);
+ } else {
+ log << " <!-- contour is a hole. make intersection(). " << contour.size() << " points. -->\n";
+ *(output_nefpoly2d) *= *(tmpnef2d);
+ }
+
+ log << "\n<!-- ======== output tmp nef: ==== -->\n"
+ << OpenSCAD::dump_svg( *tmpnef2d ) << "\n"
+ << "\n<!-- ======== output accumulator: ==== -->\n"
+ << OpenSCAD::dump_svg( *output_nefpoly2d ) << "\n";
+
+ contour_counter++;
} else {
- if (debug) out << " contour is a hole. make intersection(). " << contour.size() << " points.\n";
- *output_nefpoly2d *= *tmpnef2d;
+ log << " <!-- trivial facet cycle skipped -->\n";
}
- numcontours++;
} // next facet cycle (i.e. next contour)
+ log << " <!-- Halffacet visit end -->\n";
} // visit()
};
PolySetCGALEvaluator::PolySetCGALEvaluator(CGALEvaluator &cgalevaluator)
: PolySetEvaluator(cgalevaluator.getTree()), cgalevaluator(cgalevaluator)
{
- this->debug = false;
}
PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
{
+ //openscad_loglevel = 6;
+ logstream log(5);
+
// Before projecting, union all children
CGAL_Nef_polyhedron sum;
BOOST_FOREACH (AbstractNode * v, node.getChildren()) {
@@ -120,38 +146,71 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
}
}
+ //std::cout << sum.dump();
+ //std::cout.flush();
+
CGAL_Nef_polyhedron nef_poly;
if (node.cut_mode) {
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
- // intersect 'sum' with the x-y plane
CGAL_Nef_polyhedron3::Plane_3 xy_plane = CGAL_Nef_polyhedron3::Plane_3( 0,0,1,0 );
*sum.p3 = sum.p3->intersection( xy_plane, CGAL_Nef_polyhedron3::PLANE_ONLY);
-
- // Visit each polygon in sum.p3 and union/intersect into a 2d polygon (with holes)
- // For info on Volumes, Shells, Facets, and the 'visitor' pattern, please see
- // http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3/Chapter_main.html
- NefShellVisitor_for_cut shell_visitor;
+ }
+ catch (const CGAL::Failure_exception &e) {
+ PRINTB("CGAL error in projection node during plane intersection: %s", e.what());
+ try {
+ PRINT("Trying alternative intersection using very large thin box: ");
+ std::vector<CGAL_Point_3> pts;
+ // dont use z of 0. there are bugs in CGAL.
+ double inf = 1e8;
+ double eps = 0.001;
+ CGAL_Point_3 minpt( -inf, -inf, -eps );
+ CGAL_Point_3 maxpt( inf, inf, eps );
+ CGAL_Iso_cuboid_3 bigcuboid( minpt, maxpt );
+ for ( int i=0;i<8;i++ ) pts.push_back( bigcuboid.vertex(i) );
+ CGAL_Polyhedron bigbox;
+ CGAL::convex_hull_3( pts.begin(), pts.end(), bigbox );
+ CGAL_Nef_polyhedron3 nef_bigbox( bigbox );
+ *sum.p3 = nef_bigbox.intersection( *sum.p3 );
+ }
+ catch (const CGAL::Failure_exception &e) {
+ PRINTB("CGAL error in projection node during bigbox intersection: %s", e.what());
+ sum.p3->clear();
+ }
+ }
+
+ if ( sum.p3->is_empty() ) {
+ CGAL::set_error_behaviour(old_behaviour);
+ PRINT("WARNING: projection() failed.");
+ return NULL;
+ }
+
+ // remove z coordinates to make CGAL_Nef_polyhedron2
+ log << OpenSCAD::svg_header( 480, 100000 ) << "\n";
+ try {
+ ZRemover zremover;
CGAL_Nef_polyhedron3::Volume_const_iterator i;
CGAL_Nef_polyhedron3::Shell_entry_const_iterator j;
CGAL_Nef_polyhedron3::SFace_const_handle sface_handle;
for ( i = sum.p3->volumes_begin(); i != sum.p3->volumes_end(); ++i ) {
+ log << "<!-- volume. mark: " << i->mark() << " -->\n";
for ( j = i->shells_begin(); j != i->shells_end(); ++j ) {
+ log << "<!-- shell. mark: " << i->mark() << " -->\n";
sface_handle = CGAL_Nef_polyhedron3::SFace_const_handle( j );
- sum.p3->visit_shell_objects( sface_handle , shell_visitor );
+ sum.p3->visit_shell_objects( sface_handle , zremover );
+ log << "<!-- shell. end. -->\n";
}
+ log << "<!-- volume end. -->\n";
}
- if (debug) std::cout << "shell visitor\n" << shell_visitor.dump() << "\nshell visitor end\n";
-
- nef_poly.p2 = shell_visitor.output_nefpoly2d;
+ nef_poly.p2 = zremover.output_nefpoly2d;
nef_poly.dim = 2;
- if (debug) std::cout << "--\n" << nef_poly.dump_p2() << "\n";
- }
- catch (const CGAL::Failure_exception &e) {
- PRINTB("CGAL error in projection node: %s", e.what());
- return NULL;
+ } catch (const CGAL::Failure_exception &e) {
+ PRINTB("CGAL error in projection node while flattening: %s", e.what());
}
+ log << "</svg>\n";
+
+ CGAL::set_error_behaviour(old_behaviour);
// Extract polygons in the XY plane, ignoring all other polygons
// FIXME: If the polyhedron is really thin, there might be unwanted polygons
@@ -239,7 +298,7 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node)
PolySet *ps = nef_poly.convertToPolyset();
assert( ps != NULL );
ps->convexity = node.convexity;
- if (debug) std::cout << "--\n" << ps->dump() << "\n";
+ logstream(9) << ps->dump() << "\n";
return ps;
}
diff --git a/src/cgal.h b/src/cgal.h
index e5e39dd..a7300c6 100644
--- a/src/cgal.h
+++ b/src/cgal.h
@@ -33,6 +33,8 @@ using boost::uintmax_t;
#include <CGAL/Polygon_with_holes_2.h>
#include <CGAL/minkowski_sum_2.h>
#include <CGAL/minkowski_sum_3.h>
+#include <CGAL/bounding_box.h>
+#include <CGAL/utils.h>
#include <CGAL/assertions_behaviour.h>
#include <CGAL/exceptions.h>
@@ -54,6 +56,16 @@ typedef CGAL::Polyhedron_3<CGAL_Kernel3> CGAL_Polyhedron;
typedef CGAL_Polyhedron::HalfedgeDS CGAL_HDS;
typedef CGAL::Polyhedron_incremental_builder_3<CGAL_HDS> CGAL_Polybuilder;
+typedef CGAL::Point_3<CGAL_Kernel3> CGAL_Point_3;
+typedef CGAL::Iso_cuboid_3<CGAL_Kernel3> CGAL_Iso_cuboid_3;
+
+// CGAL_Nef_polyhedron2 uses CGAL_Kernel2, but Iso_rectangle_2 needs to match
+// CGAL_Nef_polyhedron2::Explorer::Point which is different than
+// CGAL_Kernel2::Point. Hence the suffix 'e'
+typedef CGAL_Nef_polyhedron2::Explorer::Point CGAL_Point_2e;
+typedef CGAL::Iso_rectangle_2< CGAL::Simple_cartesian<NT> > CGAL_Iso_rectangle_2e;
+
+
#ifdef PREV_NDEBUG
#define NDEBUG PREV_NDEBUG
#endif
diff --git a/src/cgalutils.cc b/src/cgalutils.cc
index e402139..51838df 100644
--- a/src/cgalutils.cc
+++ b/src/cgalutils.cc
@@ -145,5 +145,33 @@ CGAL_Polyhedron *createPolyhedronFromPolySet(const PolySet &ps)
return P;
}
+CGAL_Iso_cuboid_3 bounding_box( const CGAL_Nef_polyhedron3 &N )
+{
+ CGAL_Iso_cuboid_3 result(-1,-1,-1,1,1,1);
+ CGAL_Nef_polyhedron3::Vertex_const_iterator vi;
+ std::vector<CGAL_Nef_polyhedron3::Point_3> points;
+ // can be optimized by rewriting bounding_box to accept vertices
+ CGAL_forall_vertices( vi, N )
+ points.push_back( vi->point() );
+ if (points.size())
+ result = CGAL::bounding_box( points.begin(), points.end() );
+ return result;
+}
+
+CGAL_Iso_rectangle_2e bounding_box( const CGAL_Nef_polyhedron2 &N )
+{
+ CGAL_Iso_rectangle_2e result(-1,-1,1,1);
+ CGAL_Nef_polyhedron2::Explorer explorer = N.explorer();
+ CGAL_Nef_polyhedron2::Explorer::Vertex_const_iterator vi;
+ std::vector<CGAL_Point_2e> points;
+ // can be optimized by rewriting bounding_box to accept vertices
+ for ( vi = explorer.vertices_begin(); vi != explorer.vertices_end(); ++vi )
+ if ( explorer.is_standard( vi ) )
+ points.push_back( explorer.point( vi ) );
+ if (points.size())
+ result = CGAL::bounding_box( points.begin(), points.end() );
+ return result;
+}
+
#endif /* ENABLE_CGAL */
diff --git a/src/cgalutils.h b/src/cgalutils.h
index a249697..9093c3f 100644
--- a/src/cgalutils.h
+++ b/src/cgalutils.h
@@ -5,5 +5,7 @@
class PolySet *createPolySetFromPolyhedron(const CGAL_Polyhedron &p);
CGAL_Polyhedron *createPolyhedronFromPolySet(const class PolySet &ps);
+CGAL_Iso_cuboid_3 bounding_box( const CGAL_Nef_polyhedron3 &N );
+CGAL_Iso_rectangle_2e bounding_box( const CGAL_Nef_polyhedron2 &N );
#endif
diff --git a/src/printutils.h b/src/printutils.h
index 521682c..9d99a19 100644
--- a/src/printutils.h
+++ b/src/printutils.h
@@ -22,4 +22,26 @@ void PRINT(const std::string &msg);
void PRINT_NOCACHE(const std::string &msg);
#define PRINTB_NOCACHE(_fmt, _arg) do { PRINT_NOCACHE(str(boost::format(_fmt) % _arg)); } while (0)
+// extremely simple logging, eventually replace with something like boost.log
+// usage: logstream out(5); openscad_loglevel=6; out << "hi";
+static int openscad_loglevel = 0;
+class logstream
+{
+public:
+ std::ostream *out;
+ int loglevel;
+ logstream( int level = 0 ) {
+ loglevel = level;
+ out = &(std::cout);
+ }
+ template <typename T> logstream & operator<<( T const &t ) {
+ if (out && loglevel <= openscad_loglevel) {
+ (*out) << t ;
+ out->flush();
+ }
+ return *this;
+ }
+};
+
+
#endif
diff --git a/src/svg.cc b/src/svg.cc
new file mode 100644
index 0000000..e5130b0
--- /dev/null
+++ b/src/svg.cc
@@ -0,0 +1,249 @@
+#include "cgalutils.h"
+#include "svg.h"
+#include <boost/algorithm/string.hpp>
+#include <map>
+
+namespace OpenSCAD {
+
+// SVG code
+// currently for debugging, not necessarily pretty or useful for users. (yet)
+int svg_cursor_py = 0;
+int svg_px_width = SVG_PXW;
+int svg_px_height = SVG_PXH;
+
+std::string svg_header( int widthpx, int heightpx )
+{
+ std::stringstream out;
+ out << "<svg width='" << widthpx << "px' height='" << heightpx << "px'"
+ << " xmlns='http://www.w3.org/2000/svg' version='1.1'>";
+ return out.str();
+}
+
+std::string svg_label(std::string s)
+{
+ std::stringstream out;
+ out << " <text fill='black' x='20' y='40' font-size='24'>" << s << "</text>";
+ return out.str();
+}
+
+std::string svg_border()
+{
+ std::stringstream out;
+ out << " <!-- border -->\n";
+ out << " <polyline points='0,0 "
+ << svg_px_width << ",0 "
+ << svg_px_width << "," << svg_px_height
+ << " 0," << svg_px_height << "'"
+ << " style='fill:none;stroke:black' />\n";
+ out << " <!-- /border -->";
+ return out.str();
+}
+
+std::string svg_axes()
+{
+ std::stringstream out;
+ out << " <!-- axes -->\n";
+ out << " <polyline points='10,455 10,475 10,465 18,465 2,465 10,465 14,461 6,469 10,465'"
+ << " style='fill:none;stroke:black;' />\n";
+ out << " <!-- /axes -->";
+ return out.str();
+}
+
+CGAL_Point_2e project_svg_3to2( CGAL_Point_3 p, CGAL_Iso_cuboid_3 bbox )
+{
+ NT screenw(svg_px_width);
+ NT screenh(svg_px_height);
+ NT screenxc = screenw / 2;
+ NT screenyc = screenh / 2;
+ NT bboxx = ( bbox.xmax() - bbox.xmin() );
+ NT bboxy = ( bbox.ymax() - bbox.ymin() );
+ NT bboxz = ( bbox.zmax() - bbox.zmin() );
+ NT largest_dim = CGAL::max( CGAL::max( bboxx, bboxy ), bboxz );
+ NT bboxxc = bboxx / 2 + bbox.xmin();
+ NT bboxyc = bboxy / 2 + bbox.ymin();
+ NT bboxzc = bboxz / 2 + bbox.zmin();
+ NT xinbox = ( p.x() - bboxxc ) / largest_dim;
+ NT yinbox = ( p.y() - bboxyc ) / largest_dim;
+ NT zinbox = ( p.z() - bboxzc ) / largest_dim;
+ // do simple fake paralell projection
+ NT tx = screenxc + xinbox * screenw / 1.618 + yinbox * screenh / 3.2;
+ NT ty = screenyc - zinbox * screenh / 1.618 - yinbox * screenh / 3.2;
+ return CGAL_Point_2e( tx, ty );
+}
+
+CGAL_Point_2e project_svg_2to2( CGAL_Point_2e p, CGAL_Iso_rectangle_2e bbox )
+{
+ double screenw = svg_px_width;
+ double screenh = svg_px_height;
+ double borderw = screenw * 0.1618;
+ double borderh = screenh * 0.1618;
+ double vizw = screenw - borderw*2;
+ double vizh = screenh - borderh*2;
+ double bboxw = CGAL::to_double( bbox.xmax() - bbox.xmin() );
+ double bboxh = CGAL::to_double( bbox.ymax() - bbox.ymin() );
+ double xinbox = CGAL::to_double( p.x() ) - CGAL::to_double( bbox.xmin() );
+ double yinbox = CGAL::to_double( p.y() ) - CGAL::to_double( bbox.ymin() );
+ double tx = borderw + ( xinbox / ( bboxw==0?1:bboxw ) ) * ( vizw );
+ double ty = screenh - borderh - ( yinbox / ( bboxh==0?1:bboxh ) ) * ( vizh );
+ return CGAL_Point_2e( tx, ty );
+}
+
+std::string dump_cgal_nef_polyhedron2_face_svg(
+ CGAL_Nef_polyhedron2::Explorer::Halfedge_around_face_const_circulator c1,
+ CGAL_Nef_polyhedron2::Explorer::Halfedge_around_face_const_circulator c2,
+ CGAL_Nef_polyhedron2::Explorer explorer,
+ std::string color,
+ bool mark,
+ CGAL_Iso_rectangle_2e bbox )
+{
+ std::stringstream out;
+ CGAL_For_all(c1, c2) {
+ if ( explorer.is_standard( explorer.target(c1) ) ) {
+ CGAL_Point_2e source = explorer.point( explorer.source( c1 ) );
+ CGAL_Point_2e target = explorer.point( explorer.target( c1 ) );
+ CGAL_Point_2e tp1 = project_svg_2to2( source, bbox );
+ CGAL_Point_2e tp2 = project_svg_2to2( target, bbox );
+ double mod=0;
+ if (color=="green") mod=10;
+ out << " <!-- Halfedge. Mark: " << c1->mark() << " -->\n";
+ out << " <line"
+ << " x1='" << CGAL::to_double(tp1.x()) + mod << "'"
+ << " y1='" << CGAL::to_double(tp1.y()) - mod << "'"
+ << " x2='" << CGAL::to_double(tp2.x()) + mod << "'"
+ << " y2='" << CGAL::to_double(tp2.y()) - mod << "'"
+ << " stroke='" << color << "'";
+ if (!mark) out << " stroke-dasharray='4 4' />\n";
+ else out << " />\n";
+ // crude "arrowhead" to indicate directionality
+ out << " <circle"
+ << " cx='" << CGAL::to_double(tp1.x()+ (tp2.x()-tp1.x())* 7/8) + mod << "'"
+ << " cy='" << CGAL::to_double(tp1.y()+ (tp2.y()-tp1.y())* 7/8) - mod << "'"
+ << " r='2'"
+ << " fill='" << color << "' stroke='" << color << "' />\n";
+ } else {
+ out << " <!-- 2d Nef Rays - not implemented -->\n";
+ }
+ }
+ return out.str();
+}
+
+std::string dump_svg( const CGAL_Nef_polyhedron2 &N )
+{
+ std::stringstream out;
+ CGAL_Nef_polyhedron2::Explorer explorer = N.explorer();
+
+ CGAL_Iso_rectangle_2e bbox = bounding_box( N );
+
+ CGAL_Nef_polyhedron2::Explorer::Face_const_iterator i;
+ out << " <svg y='" << svg_cursor_py << "' width='" << svg_px_width
+ << "' height='" << svg_px_height
+ << "' xmlns='http://www.w3.org/2000/svg' version='1.1'>\n";
+ out << svg_border() << "\n" << svg_axes() << "\n";
+ svg_cursor_py += svg_px_height;
+
+ for ( i = explorer.faces_begin(); i!= explorer.faces_end(); ++i ) {
+ out << " <!-- face begin. mark: " << i->mark() << " -->\n";
+ CGAL_Nef_polyhedron2::Explorer::Halfedge_around_face_const_circulator c1
+ = explorer.face_cycle( i ), c2 ( c1 );
+ out << dump_cgal_nef_polyhedron2_face_svg( c1, c2, explorer, "red", i->mark(), bbox );
+
+ CGAL_Nef_polyhedron2::Explorer::Hole_const_iterator j;
+ for ( j = explorer.holes_begin( i ); j!= explorer.holes_end( i ); ++j ) {
+ out << " <!-- hole begin. mark: " << j->mark() << " -->\n";
+ CGAL_Nef_polyhedron2::Explorer::Halfedge_around_face_const_circulator c3( j ), c4 ( c3 );
+ out << dump_cgal_nef_polyhedron2_face_svg( c3, c4, explorer, "green", j->mark(), bbox );
+ out << " <!-- hole end -->\n";
+ }
+ out << " <!-- face end -->\n";
+ }
+ out << "</svg>";
+ std::string tmp = out.str();
+ boost::replace_all( tmp, "'", "\"" );
+ return tmp;
+}
+
+
+// This uses the Shell Explorer pattern from the CGAL Manual to dump the 3d Nef Polyhedron information
+// http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Nef_3/Chapter_main.html#Subsection_29.7.2
+class NefPoly3_dumper_svg {
+public:
+ std::stringstream out;
+ CGAL_Iso_cuboid_3 bbox;
+ NefPoly3_dumper_svg(const CGAL_Nef_polyhedron3& N)
+ {
+ bbox = bounding_box( N );
+ }
+ void visit(CGAL_Nef_polyhedron3::Vertex_const_handle ) {}
+ void visit(CGAL_Nef_polyhedron3::Halfedge_const_handle ) {}
+ void visit(CGAL_Nef_polyhedron3::SHalfedge_const_handle ) {}
+ void visit(CGAL_Nef_polyhedron3::SHalfloop_const_handle ) {}
+ void visit(CGAL_Nef_polyhedron3::SFace_const_handle ) {}
+ void visit( CGAL_Nef_polyhedron3::Halffacet_const_handle hfacet )
+ {
+ int contour_count = 0;
+ out << " <!-- Halffacet. Mark: " << (*hfacet).mark() << " -->\n";
+ std::string color = "gold";
+ if (!(*hfacet).mark()) color = "green";
+ CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator i;
+ CGAL_forall_facet_cycles_of( i, hfacet ) {
+ CGAL_Nef_polyhedron3::SHalfloop_const_handle shl_handle;
+ out << " <!-- Halffacet cycle: -->\n";
+ if ( contour_count == 0 ) {
+ out << " <!-- Body contour:--> \n";
+ } else {
+ out << " <!-- Hole contour:--> \n" ;
+ }
+ CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(i), c2(c1);
+ CGAL_For_all( c1, c2 ) {
+ out << " <line";
+ // don't know why we use source()->source(), except thats what CGAL does internally
+ CGAL_Point_3 source = c1->source()->source()->point();
+ CGAL_Point_3 target = c1->source()->target()->point();
+ CGAL_Point_2e tp1 = project_svg_3to2 ( source, bbox );
+ CGAL_Point_2e tp2 = project_svg_3to2 ( target, bbox );
+ out << " "
+ << "x1='" << CGAL::to_double(tp1.x()) << "' "
+ << "y1='" << CGAL::to_double(tp1.y()) << "' "
+ << "x2='" << CGAL::to_double(tp2.x()) << "' "
+ << "y2='" << CGAL::to_double(tp2.y()) << "' "
+ << " stroke='" << color << "'";
+ if (!(*hfacet).mark()) out << " stroke-dasharray='4 4' />\n";
+ else out << " />\n";
+ }
+ contour_count++;
+ } // next facet cycle (i.e. next contour)
+ } // visit()
+
+};
+
+
+std::string dump_svg( const CGAL_Nef_polyhedron3 &N )
+{
+ std::stringstream out;
+ out << svg_header() << "\n" << svg_border() << "\n" << svg_axes() << "\n";
+ out << "<!--CGAL_Nef_polyhedron3 dump begin-->\n";
+
+ CGAL_Nef_polyhedron3::Volume_const_iterator c;
+ CGAL_forall_volumes(c,N) {
+ out << " <!--Processing volume...-->\n";
+ out << " <!--Mark: " << (*c).mark() << "-->\n";
+ CGAL_Nef_polyhedron3::Shell_entry_const_iterator it;
+ CGAL_forall_shells_of(it,c) {
+ out << " <!--Processing shell...-->\n";
+ NefPoly3_dumper_svg dumper_svg(N);
+ N.visit_shell_objects(CGAL_Nef_polyhedron3::SFace_const_handle(it), dumper_svg );
+ out << dumper_svg.out.str();
+ out << " <!--Processing shell end-->\n";
+ }
+ out << " <!--Processing volume end-->\n";
+ }
+ out << "<!--CGAL_Nef_polyhedron3 dump end-->\n";
+ out << "</svg>";
+ std::string tmp = out.str();
+ boost::replace_all( tmp, "'", "\"" );
+ return tmp;
+}
+
+} // namespace
+
+
diff --git a/src/svg.h b/src/svg.h
new file mode 100644
index 0000000..828dc39
--- /dev/null
+++ b/src/svg.h
@@ -0,0 +1,28 @@
+#ifndef SVG_H_
+#define SVG_H_
+
+#include "cgal.h"
+#include <boost/algorithm/string.hpp>
+#include <map>
+
+namespace OpenSCAD {
+
+// currently for debugging, not necessarily pretty or useful for users. (yet)
+
+#define SVG_PXW 480
+#define SVG_PXH 480
+extern int svg_cursor_py;
+extern int svg_px_width;
+extern int svg_px_height;
+
+std::string svg_header( int widthpx = SVG_PXW, int heightpx = SVG_PXH );
+std::string svg_label( std::string s );
+std::string svg_border();
+std::string svg_axes();
+std::string dump_svg( const CGAL_Nef_polyhedron2 &N );
+std::string dump_svg( const CGAL_Nef_polyhedron3 &N );
+
+} // namespace
+
+#endif
+
contact: Jan Huwald // Impressum