diff options
author | don bright <hugh.m.bright@gmail.com> | 2012-10-28 13:56:23 (GMT) |
---|---|---|
committer | don bright <hugh.m.bright@gmail.com> | 2012-10-28 13:56:23 (GMT) |
commit | 4ecd9fa8a4ceeb49ec62a50197f4fa4da9276796 (patch) | |
tree | c298e527789aeecb2c742044683471a48c9acf1a | |
parent | 1dbcd7f4689fd0ed84997e2fb80a1b77e06f6b26 (diff) |
refactor, cleanup, put code where it belongs, make simple logging class
-rw-r--r-- | openscad.pro | 4 | ||||
-rw-r--r-- | src/CGAL_Nef_polyhedron_DxfData.cc | 5 | ||||
-rw-r--r-- | src/PolySetCGALEvaluator.cc | 269 | ||||
-rw-r--r-- | src/cgal.h | 10 | ||||
-rw-r--r-- | src/cgalutils.cc | 256 | ||||
-rw-r--r-- | src/cgalutils.h | 7 | ||||
-rw-r--r-- | src/func.cc | 1 | ||||
-rw-r--r-- | src/printutils.cc | 3 | ||||
-rw-r--r-- | src/printutils.h | 24 | ||||
-rw-r--r-- | src/svg.cc | 248 | ||||
-rw-r--r-- | src/svg.h | 34 |
11 files changed, 415 insertions, 446 deletions
diff --git a/openscad.pro b/openscad.pro index b5321cc..91bd735 100644 --- a/openscad.pro +++ b/openscad.pro @@ -214,7 +214,8 @@ HEADERS += src/version_check.h \ src/memory.h \ src/linalg.h \ src/system-gl.h \ - src/stl-utils.h + src/stl-utils.h \ + src/svg.h SOURCES += src/version_check.cc \ src/ProgressWidget.cc \ @@ -271,6 +272,7 @@ SOURCES += src/version_check.cc \ src/dxftess-glu.cc \ src/dxftess-cgal.cc \ src/CSGTermEvaluator.cc \ + src/svg.cc \ \ src/openscad.cc \ src/mainwin.cc diff --git a/src/CGAL_Nef_polyhedron_DxfData.cc b/src/CGAL_Nef_polyhedron_DxfData.cc index 26b9353..0d0b8f0 100644 --- a/src/CGAL_Nef_polyhedron_DxfData.cc +++ b/src/CGAL_Nef_polyhedron_DxfData.cc @@ -29,6 +29,7 @@ #include "CGAL_Nef_polyhedron.h" #include "cgal.h" #include "cgalutils.h" +#include "svg.h" #ifdef ENABLE_CGAL @@ -81,9 +82,9 @@ DxfData *CGAL_Nef_polyhedron::convertToDxfData() const std::string CGAL_Nef_polyhedron::dump() const { if (this->dim==2) - return dump_cgal_nef_polyhedron2_svg( *this->p2 ); + return OpenSCAD::dump_svg( *this->p2 ); else if (this->dim==3) - return dump_cgal_nef_polyhedron3_svg( *this->p3 ); + return OpenSCAD::dump_svg( *this->p3 ); else return std::string("Nef Polyhedron with dimension != 2 or 3"); } diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc index 99b5938..f6fb1d4 100644 --- a/src/PolySetCGALEvaluator.cc +++ b/src/PolySetCGALEvaluator.cc @@ -22,123 +22,55 @@ #include <vector> #include <deque> -typedef CGAL_Nef_polyhedron3::Point_3 Point_3; +#include "svg.h" + /* -This class converts multiple 3d-CGAL Nef polyhedrons into a single 2d by -stripping off the z coordinate of each face vertex and doing unions and -intersections. It uses the 'visitor' pattern from the CGAL manual. -Output is in the 'output_nefpoly2d' variable. +ZRemover + +This class converts an already 'flat' Nef3 polyhedra into a Nef2 +polyhedron by stripping off the 'z' coordinate. -Note that the input 3d Nef polyhedron, as used here, is typically of -two types. The first is the result of an intersection between -the 3d Nef polyhedron and the xy-plane, with all z set to 0. +The class uses the 'visitor' pattern from the CGAL manual -- multiple 3d +Nef polys fed to this class, with the resulting Nef2 poly accumulating +in the 'output_nefpoly2d' member variable. -The second is the result of an intersection between the 3d nef -polyhedron and a very large, very thin box. This is used when CGAL -crashes during plane intersection. The thin box is used to 'simulate' -the xy plane. The result is that the 'top' of the box and the -'bottom' of the box will both contain 2d projections, quite similar -to what one would get at xy=0, but not exactly. these are then -unioned together. +Some notes on CGAL's Nef Polyhedron2: -Some key things to know about Nef Polyhedron2: +0. The method for iterating through CGAL Nef2 poly and Nef3 polys is different. + Nef2 requires 'Explorer', which uses it's own Point type that is not strictly + the same as Nef2::Point_2. Nef3, in contrast, uses straightforward + iterators and circulators using the standard Nef3::Point_3 type. +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. -1. The 'mark' on a face is important when doing unions/intersections -2. The 'mark' can be non-deterministic based on the constructor. - Possible factors include whether 'is_simple_2' returns true on the - inputted points, and also perhaps the order of points fed to the constructor. +Debugging output is in heavily commented SVG format. 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 Flattener { -public: - std::ostringstream out; - 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; - Flattener(bool debug=false) - { - 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(); - } - 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 ) { - out.str(""); - out << " <!-- Halffacet visit -->\n"; - out << " <!-- mark:" << hfacet->mark() << " -->\n"; - if ( hfacet->plane().orthogonal_direction() != this->up ) { - out << "\ndown facing half-facet. skipping\n"; - out << " <!-- Halffacet visit end-->\n"; - std::cout << out.str(); - return; - } - int contour_counter = 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 ( contour_counter == 0 ) { - out << "\n <!-- contour is a body. make union(). " << contour.size() << " points. -->\n" ; - *(output_nefpoly2d) += *(tmpnef2d); - } else { - *(output_nefpoly2d) *= *(tmpnef2d); - if (debug) out << "\n<!-- contour is a hole. make intersection(). " << contour.size() << " points. -->\n"; - } - out << "\n<!-- ======== output tmp nef2d: ====== -->\n"; - out << dump_cgal_nef_polyhedron2_svg( *tmpnef2d ); - out << "\n<!-- ======== output accumulator: ==== -->\n"; - out << dump_cgal_nef_polyhedron2_svg( *output_nefpoly2d ); - contour_counter++; - } // next facet cycle (i.e. next contour) - out << " <!-- Halffacet visit end -->\n"; - std::cout << out.str(); - } // visit() -}; - - - -class Flattener2 { +class ZRemover { public: - std::ostringstream 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; - Flattener2(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 ) {} @@ -146,86 +78,54 @@ 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 ) { - out.str(""); - out << " <!-- Halffacet visit -->\n"; - out << " <!-- mark:" << hfacet->mark() << " -->\n"; + log << " <!-- Halffacet visit. Mark: " << hfacet->mark() << " -->\n"; if ( hfacet->plane().orthogonal_direction() != this->up ) { - out << "\ndown facing half-facet. not skipping\n"; - out << " <!-- Halffacet visit end-->\n"; - std::cout << out.str(); + log << " <!-- down-facing half-facet. skipping -->\n"; + log << " <!-- Halffacet visit end-->\n"; return; } - CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator i; -/* bool skip=false; - CGAL_forall_facet_cycles_of( i, hfacet ) { - CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1a(i), c2a(c1a); - CGAL_For_all( c1a, c2a ) { - CGAL_Nef_polyhedron3::Point_3 point3d = c1a->source()->source()->point(); - if (CGAL::to_double(point3d.z())!=floor) skip=true; - } - } - if (skip) { - out << "\n facet not on floor plane (z=" << floor <<"). skipping\n"; - out << " <!-- Halffacet visit end-->\n"; - std::cout << out.str(); - return; - } -*/ + // 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( i, hfacet ) { - if ( i.is_shalfedge() ) { - CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(i), c2(c1); + 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, c2 ) { - out << "around facet. c1 mark:" << c1->mark() << "\n"; - // c1->source() gives us an SVertex for the SHalfedge - // c1->source()->target() gives us a Vertex?? + CGAL_For_all( c1, cend ) { + // c1->source()->target() seems to work better than c1->source()->source() CGAL_Nef_polyhedron3::Point_3 point3d = c1->source()->target()->point(); CGAL_Nef_polyhedron2::Explorer::Point point2d( point3d.x(), point3d.y() ); - out << "around facet. point3d:" << CGAL::to_double(point3d.x()) << "," << CGAL::to_double(point3d.y()) << "\n";; - out << "around facet. point2d:" << CGAL::to_double(point2d.x()) << "," << CGAL::to_double(point2d.y()) << "\n";; - if (contour.size()) out << "equality:" << (contour.back() == point2d) << "\n";; - out << "equality2 :" << ( c1->target()->source() == c1->source()->target() ) << "\n";; contour.push_back( point2d ); } - // Type given to Polygon_2 has to match Nef2::Explorer::Point - // (which is not the same as CGAL_Kernel2::Point) - std::vector<CGAL_Nef_polyhedron2::Explorer::Point>::iterator xx; - for ( xx=contour.begin(); xx!=contour.end(); ++xx ) { - out << "pdump: " << CGAL::to_double(xx->x()) << "," << CGAL::to_double(xx->y()) << "\n"; - } - out << "is simple 2:" << CGAL::is_simple_2( contour.begin(), contour.end() ) << "\n"; - //CGAL::Polygon_2<CGAL::Simple_cartesian<NT> > plainpoly2( contour.begin(), contour.end() ); - //out << "clockwise orientation: " << plainpoly2.is_clockwise_oriented() << "\n"; - tmpnef2d.reset( new CGAL_Nef_polyhedron2( contour.begin(), contour.end(), boundary ) ); - // *(tmpnef2d) = tmpnef2d->regularization(); - // mark here. + assert(contour.size()>1); - out << "\n<!-- ======== output accumulator 0: ==== -->\n"; - out << dump_cgal_nef_polyhedron2_svg( *output_nefpoly2d ); + 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 ) { - out << "\n <!-- contour is a body. make union(). " << contour.size() << " points. -->\n" ; + log << " <!-- contour is a body. make union(). " << contour.size() << " points. -->\n" ; *(output_nefpoly2d) += *(tmpnef2d); } else { *(output_nefpoly2d) *= *(tmpnef2d); - if (debug) out << "\n<!-- contour is a hole. make intersection(). " << contour.size() << " points. -->\n"; + log << " <!-- contour is a hole. make intersection(). " << contour.size() << " points. -->\n"; } - out << "\n<!-- ======== output tmp nef2d: ====== -->\n"; - out << dump_cgal_nef_polyhedron2_svg( *tmpnef2d ); - out << "\n<!-- ======== output accumulator 1: ==== -->\n"; - out << dump_cgal_nef_polyhedron2_svg( *output_nefpoly2d ); + log << "\n<!-- ======== output tmp nef: ==== -->\n"; + log << OpenSCAD::dump_svg( *tmpnef2d ) << "\n"; + log << "\n<!-- ======== output accumulator: ==== -->\n"; + log << OpenSCAD::dump_svg( *output_nefpoly2d ) << "\n"; contour_counter++; } else { - out << "trivial facet cycle skipped\n"; + log << " <!-- trivial facet cycle skipped -->\n"; } } // next facet cycle (i.e. next contour) - out << " <!-- Halffacet visit end -->\n"; - std::cout << out.str(); + log << " <!-- Halffacet visit end -->\n"; } // visit() }; @@ -235,11 +135,13 @@ public: 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()) { @@ -258,37 +160,31 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node) } } - // std::cout << sum.dump_svg() << std::flush; // input dump - CGAL_Nef_polyhedron nef_poly; if (node.cut_mode) { CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION); - bool plane_intersect_fail = false; try { 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); } catch (const CGAL::Failure_exception &e) { PRINTB("CGAL error in projection node during plane intersection: %s", e.what()); - plane_intersect_fail = true; - } - if (plane_intersect_fail) { try { PRINT("Trying alternative intersection using very large thin box: "); double inf = 1e8, eps = 0.001; double x1 = -inf, x2 = +inf, y1 = -inf, y2 = +inf, z1 = -eps, z2 = eps; // dont use z of 0. there are bugs in CGAL. - std::vector<Point_3> pts; - pts.push_back( Point_3( x1, y1, z1 ) ); - pts.push_back( Point_3( x1, y2, z1 ) ); - pts.push_back( Point_3( x2, y2, z1 ) ); - pts.push_back( Point_3( x2, y1, z1 ) ); - pts.push_back( Point_3( x1, y1, z2 ) ); - pts.push_back( Point_3( x1, y2, z2 ) ); - pts.push_back( Point_3( x2, y2, z2 ) ); - pts.push_back( Point_3( x2, y1, z2 ) ); + std::vector<CGAL_Nef_polyhedron3::Point_3> pts; + pts.push_back( CGAL_Nef_polyhedron3::Point_3( x1, y1, z1 ) ); + pts.push_back( CGAL_Nef_polyhedron3::Point_3( x1, y2, z1 ) ); + pts.push_back( CGAL_Nef_polyhedron3::Point_3( x2, y2, z1 ) ); + pts.push_back( CGAL_Nef_polyhedron3::Point_3( x2, y1, z1 ) ); + pts.push_back( CGAL_Nef_polyhedron3::Point_3( x1, y1, z2 ) ); + pts.push_back( CGAL_Nef_polyhedron3::Point_3( x1, y2, z2 ) ); + pts.push_back( CGAL_Nef_polyhedron3::Point_3( x2, y2, z2 ) ); + pts.push_back( CGAL_Nef_polyhedron3::Point_3( x2, y1, z2 ) ); CGAL_Polyhedron bigbox; CGAL::convex_hull_3( pts.begin(), pts.end(), bigbox ); @@ -297,36 +193,37 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node) } catch (const CGAL::Failure_exception &e) { PRINTB("CGAL error in projection node during bigbox intersection: %s", e.what()); - // can we just return empty polyset? - CGAL::set_error_behaviour(old_behaviour); - return NULL; + sum.reset(); } } + CGAL::set_error_behaviour(old_behaviour); + + if (sum.empty()) { + PRINT("WARNING: Projection failed."); + return NULL; + } + // remove z coordinates to make CGAL_Nef_polyhedron2 - std::cout << "<svg width=\"480px\" height=\"100000px\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"; + log << "<svg width=\"480px\" height=\"100000px\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"; try { - Flattener2 flattener(true); + 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 ) { - std::cout << "<!-- volume. mark: " << i->mark() << " -->\n"; + log << "<!-- volume. mark: " << i->mark() << " -->\n"; for ( j = i->shells_begin(); j != i->shells_end(); ++j ) { - std::cout << "<!-- shell. mark: " << i->mark() << " -->\n"; -// if (i->mark()==1) { - sface_handle = CGAL_Nef_polyhedron3::SFace_const_handle( j ); - sum.p3->visit_shell_objects( sface_handle , flattener ); -// } - std::cout << "<!-- shell. end. -->\n"; + log << "<!-- shell. mark: " << i->mark() << " -->\n"; + sface_handle = CGAL_Nef_polyhedron3::SFace_const_handle( j ); + sum.p3->visit_shell_objects( sface_handle , zremover ); + log << "<!-- shell. end. -->\n"; } - std::cout << "<!-- volume end. -->\n"; + log << "<!-- volume end. -->\n"; } - //std::cout << flattener.out.str() << "\n"; - std::cout << "</svg>" << std::flush; + log << "</svg>\n"; - //std::cout << "------- flattener dump \n" << flattener.dump() << "\n"; - nef_poly.p2 = flattener.output_nefpoly2d; + nef_poly.p2 = zremover.output_nefpoly2d; nef_poly.dim = 2; } catch (const CGAL::Failure_exception &e) { PRINTB("CGAL error in projection node while flattening: %s", e.what()); @@ -334,10 +231,6 @@ PolySet *PolySetCGALEvaluator::evaluatePolySet(const ProjectionNode &node) CGAL::set_error_behaviour(old_behaviour); - //std::cout << sum.dump_svg() << std::flush; // cut dump - //std::cout << nef_poly.dump_svg() << std::flush; // post-flattener dump - - //std::cout << "------- 2d output dump \n" << nef_poly.dump_svg() << "\n"; // Extract polygons in the XY plane, ignoring all other polygons // FIXME: If the polyhedron is really thin, there might be unwanted polygons // in the XY plane, causing the resulting 2D polygon to be self-intersection @@ -424,7 +317,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; } @@ -33,6 +33,7 @@ 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/assertions_behaviour.h> #include <CGAL/exceptions.h> @@ -54,6 +55,15 @@ 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; + +// The type given to Iso_rectangle_2 needs to match CGAL_Nef2::Explorer::Point +// which is different than a 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 94e990a..601b6f3 100644 --- a/src/cgalutils.cc +++ b/src/cgalutils.cc @@ -4,17 +4,6 @@ #include "polyset.h" #include "printutils.h" #include "cgal.h" -#include <CGAL/bounding_box.h> -typedef CGAL::Point_3<CGAL_Kernel3> CGAL_Point_3; -typedef CGAL::Iso_cuboid_3<CGAL_Kernel3> CGAL_Iso_cuboid_3; - -typedef CGAL_Nef_polyhedron2::Explorer::Point CGAL_Point_2; -// Iso_rectangle needs to match CGAL_Nef2::Explorer::Point -// which is different than CGAL_Kernel2::Point -typedef CGAL::Iso_rectangle_2< CGAL::Simple_cartesian<NT> > CGAL_Iso_rectangle_2; - -#include <boost/algorithm/string.hpp> -#include <map> PolySet *createPolySetFromPolyhedron(const CGAL_Polyhedron &p) { @@ -154,45 +143,12 @@ CGAL_Polyhedron *createPolyhedronFromPolySet(const PolySet &ps) } -std::string svg_header() -{ - std::stringstream out; - out << "<svg width='480px' height='480px'" - << " 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 480,0 480,480 0,480'" - << " 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_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()) @@ -200,12 +156,13 @@ CGAL_Iso_cuboid_3 bounding_box( const CGAL_Nef_polyhedron3 &N ) return result; } -CGAL_Iso_rectangle_2 bounding_box( const CGAL_Nef_polyhedron2 &N ) +CGAL_Iso_rectangle_2e bounding_box( const CGAL_Nef_polyhedron2 &N ) { - CGAL_Iso_rectangle_2 result(-1,-1,1,1); - CGAL_Nef_polyhedron2::Explorer explorer = N.explorer(); + 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_2> points; + 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 ) ); @@ -214,208 +171,7 @@ CGAL_Iso_rectangle_2 bounding_box( const CGAL_Nef_polyhedron2 &N ) return result; } -CGAL_Point_2 project_svg_3to2( CGAL_Point_3 p, CGAL_Iso_cuboid_3 bbox ) -{ - // do simple fake isometric projection - double x = CGAL::to_double( p.x() ); - double y = CGAL::to_double( p.y() ); - double z = CGAL::to_double( p.z() ); - double screenw = 480; - double screenh = 480; - double borderw = screenw * 0.1618; - double borderh = screenh * 0.1618; - double vizw = screenw - borderw*2; - double vizh = screenh - borderh*2; - double bboxx = CGAL::to_double( bbox.xmax() - bbox.xmin() ); - double bboxy = CGAL::to_double( bbox.ymax() - bbox.ymin() ); - double bboxz = CGAL::to_double( bbox.zmax() - bbox.zmin() ); - 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 zinbox = CGAL::to_double( p.z() ) - CGAL::to_double( bbox.zmin() ); - double tx = borderw + ( xinbox / ( bboxx==0?1:bboxx ) ) * ( vizw ); - double ty = screenh - borderh - ( zinbox / ( bboxz==0?1:bboxz ) ) * ( vizh ); - tx += ( yinbox / ( bboxy==0?1:bboxy ) ) / 3; - ty -= ( yinbox / ( bboxy==0?1:bboxy ) ) / 3; - return CGAL_Point_2( tx, ty ); -} - -CGAL_Point_2 project_svg_2to2( CGAL_Point_2 p, CGAL_Iso_rectangle_2 bbox ) -{ - double x = CGAL::to_double( p.x() ); - double y = CGAL::to_double( p.y() ); - double screenw = 480; - double screenh = 480; - 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 ); -/* std::cout << "\nx, y " << x << "," << y << "\n"; - std::cout << "bbw, bbh " << bboxw << "," << bboxh << "\n"; - std::cout << "xinb, yinb " << xinbox << "," << yinbox << "\n"; - std::cout << "vizw, vizh " << vizw << "," << vizh << "\n"; - std::cout << "tx, ty " << tx << "," << ty << "\n"; -*/ - return CGAL_Point_2( tx, ty ); -} - -// for debugging, not necessarily pretty or useful for users. -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_2 bbox ) -{ - std::stringstream out; - CGAL_For_all(c1, c2) { - if ( explorer.is_standard( explorer.target(c1) ) ) { - CGAL_Point_2 source = explorer.point( explorer.source( c1 ) ); - CGAL_Point_2 target = explorer.point( explorer.target( c1 ) ); - CGAL_Point_2 tp1 = project_svg_2to2( source, bbox ); - CGAL_Point_2 tp2 = project_svg_2to2( target, bbox ); - double mod=0; - if (color=="green") mod=10; - out << " <!-- 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"; - } - } - return out.str(); -} -std::string dump_cgal_nef_polyhedron2_svg( const CGAL_Nef_polyhedron2 &N ) -{ - std::stringstream out; - CGAL_Nef_polyhedron2::Explorer explorer = N.explorer(); - - CGAL_Iso_rectangle_2 bbox = bounding_box( N ); - - CGAL_Nef_polyhedron2::Explorer::Face_const_iterator i; - out << " <svg y='" << svg_counter << "' width='480px' height='480px' xmlns='http://www.w3.org/2000/svg' version='1.1'>\n"; - out << svg_border() << "\n" << svg_axes() << "\n"; - svg_counter+=480; - if ((svg_counter/480)%3==0) svg_counter += 24; - if ((svg_counter/480)%3==1) out << svg_label("old accumulator") << "\n"; - if ((svg_counter/480)%3==2) out << svg_label("new nef poly") << "\n"; - if ((svg_counter/480)%3==0) out << svg_label("new accumulator") << "\n"; - - 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 v) {} - void visit(CGAL_Nef_polyhedron3::Halfedge_const_handle ) {} - void visit(CGAL_Nef_polyhedron3::SHalfedge_const_handle ) {} - void visit(CGAL_Nef_polyhedron3::SHalfloop_const_handle shh ){} - 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"; - 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_2 tp1 = project_svg_3to2 ( source, bbox ); - CGAL_Point_2 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='red' />\n"; - } - contour_count++; - } // next facet cycle (i.e. next contour) - } // visit() - -}; - - -std::string dump_cgal_nef_polyhedron3_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; -} #endif /* ENABLE_CGAL */ diff --git a/src/cgalutils.h b/src/cgalutils.h index 3fb9360..9093c3f 100644 --- a/src/cgalutils.h +++ b/src/cgalutils.h @@ -5,10 +5,7 @@ class PolySet *createPolySetFromPolyhedron(const CGAL_Polyhedron &p); CGAL_Polyhedron *createPolyhedronFromPolySet(const class PolySet &ps); -std::string dump_cgal_nef_polyhedron2( const CGAL_Nef_polyhedron2 &N ); -std::string dump_cgal_nef_polyhedron3( const CGAL_Nef_polyhedron3 &N ); -std::string dump_cgal_nef_polyhedron2_svg( const CGAL_Nef_polyhedron2 &N ); -std::string dump_cgal_nef_polyhedron3_svg( const CGAL_Nef_polyhedron3 &N ); -static int svg_counter = 0; +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/func.cc b/src/func.cc index e427bf2..21c6f33 100644 --- a/src/func.cc +++ b/src/func.cc @@ -530,3 +530,4 @@ void register_builtin_functions() Builtins::init("version", new BuiltinFunction(&builtin_version)); Builtins::init("version_num", new BuiltinFunction(&builtin_version_num)); } + diff --git a/src/printutils.cc b/src/printutils.cc index a8b62aa..57c6b49 100644 --- a/src/printutils.cc +++ b/src/printutils.cc @@ -49,3 +49,6 @@ void PRINT_NOCACHE(const std::string &msg) outputhandler(msg, outputhandler_data); } } + + + diff --git a/src/printutils.h b/src/printutils.h index 521682c..935463e 100644 --- a/src/printutils.h +++ b/src/printutils.h @@ -3,7 +3,9 @@ #include <string> #include <list> +#include <map> #include <iostream> +#include <sstream> #include <boost/format.hpp> typedef void (OutputHandlerFunc)(const std::string &msg, void *userdata); @@ -22,4 +24,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..a40bd9f --- /dev/null +++ b/src/svg.cc @@ -0,0 +1,248 @@ +#include "cgalutils.h" +#include "svg.h" +#include <boost/algorithm/string.hpp> +#include <map> + +namespace OpenSCAD { + +std::string svg_header() +{ + std::stringstream out; + out << "<svg width='480px' height='480px'" + << " 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 480,0 480,480 0,480'" + << " 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 ) +{ + // do simple fake isometric projection + double x = CGAL::to_double( p.x() ); + double y = CGAL::to_double( p.y() ); + double z = CGAL::to_double( p.z() ); + double screenw = 480; + double screenh = 480; + double borderw = screenw * 0.1618; + double borderh = screenh * 0.1618; + double vizw = screenw - borderw*2; + double vizh = screenh - borderh*2; + double bboxx = CGAL::to_double( bbox.xmax() - bbox.xmin() ); + double bboxy = CGAL::to_double( bbox.ymax() - bbox.ymin() ); + double bboxz = CGAL::to_double( bbox.zmax() - bbox.zmin() ); + 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 zinbox = CGAL::to_double( p.z() ) - CGAL::to_double( bbox.zmin() ); + double tx = borderw + ( xinbox / ( bboxx==0?1:bboxx ) ) * ( vizw ); + double ty = screenh - borderh - ( zinbox / ( bboxz==0?1:bboxz ) ) * ( vizh ); + tx += ( yinbox / ( bboxy==0?1:bboxy ) ) / 3; + ty -= ( yinbox / ( bboxy==0?1:bboxy ) ) / 3; + return CGAL_Point_2e( tx, ty ); +} + +CGAL_Point_2e project_svg_2to2( CGAL_Point_2e p, CGAL_Iso_rectangle_2e bbox ) +{ + double x = CGAL::to_double( p.x() ); + double y = CGAL::to_double( p.y() ); + double screenw = 480; + double screenh = 480; + 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 ); +/* std::cout << "\nx, y " << x << "," << y << "\n"; + std::cout << "bbw, bbh " << bboxw << "," << bboxh << "\n"; + std::cout << "xinb, yinb " << xinbox << "," << yinbox << "\n"; + std::cout << "vizw, vizh " << vizw << "," << vizh << "\n"; + std::cout << "tx, ty " << tx << "," << ty << "\n"; +*/ + return CGAL_Point_2e( tx, ty ); +} + +// for debugging, not necessarily pretty or useful for users. +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"; + } + } + 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 << "' width='480px' height='480px' xmlns='http://www.w3.org/2000/svg' version='1.1'>\n"; + out << svg_border() << "\n" << svg_axes() << "\n"; + svg_cursor+=480; + if ((svg_cursor/480)%3==0) svg_cursor += 24; + if ((svg_cursor/480)%3==1) out << svg_label("old accumulator") << "\n"; + if ((svg_cursor/480)%3==2) out << svg_label("new nef poly") << "\n"; + if ((svg_cursor/480)%3==0) out << svg_label("new accumulator") << "\n"; + + 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 v) {} + void visit(CGAL_Nef_polyhedron3::Halfedge_const_handle ) {} + void visit(CGAL_Nef_polyhedron3::SHalfedge_const_handle ) {} + void visit(CGAL_Nef_polyhedron3::SHalfloop_const_handle shh ){} + 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"; + 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='red' />\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..c7c7e1a --- /dev/null +++ b/src/svg.h @@ -0,0 +1,34 @@ +#ifndef SVG_H_ +#define SVG_H_ + +#include "cgal.h" +#include <boost/algorithm/string.hpp> +#include <map> + +namespace OpenSCAD { + +static int svg_cursor = 0; + +std::string svg_header(); +std::string svg_label(std::string s); +std::string svg_border(); +std::string svg_axes(); +CGAL_Point_2e project_svg_3to2( CGAL_Point_3 p, CGAL_Iso_cuboid_3 bbox ); +CGAL_Point_2e project_svg_2to2( CGAL_Point_2e p, CGAL_Iso_rectangle_2e bbox ); + +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::string dump_svg( const CGAL_Nef_polyhedron2 &N ); +class NefPoly3_dumper_svg; +std::string dump_svg( const CGAL_Nef_polyhedron3 &N ); + + +} // namespace + +#endif + |