diff options
Diffstat (limited to 'src/svg.cc')
-rw-r--r-- | src/svg.cc | 249 |
1 files changed, 249 insertions, 0 deletions
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 + + |