#ifdef ENABLE_CGAL #include "svg.h" #include "cgalutils.h" #include #include #include namespace OpenSCAD { // SVG code // currently for debugging, not necessarily pretty or useful for users. (yet) int svg_px_width = SVG_PXW; int svg_px_height = SVG_PXH; std::string svg_header( int widthpx, int heightpx ) { std::stringstream out; out << ""; return out.str(); } std::string svg_label(std::string s) { std::stringstream out; out << " " << s << ""; return out.str(); } std::string svg_styleblock(std::string strokewidth) { std::stringstream out; // halfedge: f1/f0 = face mark, b1/b0 = body or hole, m1/m0 = halfedge mark out << "\ "; std::string tmp = out.str(); boost::replace_all( tmp, "__STROKEW__", strokewidth ); return tmp; } std::string svg_border() { std::stringstream out; out << " \n"; out << " \n"; out << " "; return out.str(); } std::string svg_axes() { std::stringstream out; out << " \n"; out << " \n"; out << " "; return out.str(); } CGAL_Nef_polyhedron2::Explorer::Point project_svg_3to2( CGAL_Point_3 p, CGAL_Iso_cuboid_3 bbox ) { CGAL_Kernel3::FT screenw(svg_px_width); CGAL_Kernel3::FT screenh(svg_px_height); CGAL_Kernel3::FT screenxc = screenw / 2; CGAL_Kernel3::FT screenyc = screenh / 2; CGAL_Kernel3::FT bboxx = ( bbox.xmax() - bbox.xmin() ); CGAL_Kernel3::FT bboxy = ( bbox.ymax() - bbox.ymin() ); CGAL_Kernel3::FT bboxz = ( bbox.zmax() - bbox.zmin() ); CGAL_Kernel3::FT largest_dim = CGAL::max( CGAL::max( bboxx, bboxy ), bboxz ); CGAL_Kernel3::FT bboxxc = bboxx / 2 + bbox.xmin(); CGAL_Kernel3::FT bboxyc = bboxy / 2 + bbox.ymin(); CGAL_Kernel3::FT bboxzc = bboxz / 2 + bbox.zmin(); CGAL_Kernel3::FT xinbox = ( p.x() - bboxxc ) / largest_dim; CGAL_Kernel3::FT yinbox = ( p.y() - bboxyc ) / largest_dim; CGAL_Kernel3::FT zinbox = ( p.z() - bboxzc ) / largest_dim; // do simple fake paralell projection CGAL_Kernel3::FT tx = screenxc + xinbox * screenw / 1.618 + yinbox * screenh / 3.2; CGAL_Kernel3::FT ty = screenyc - zinbox * screenh / 1.618 - yinbox * screenh / 3.2; return CGAL_Point_2e(CGAL::to_double(tx), CGAL::to_double(ty)); } CGAL_Point_2e project_svg_2to2(const CGAL_Point_2e &p, const 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, bool facemark, bool body ) { std::stringstream style; style << "halfedge_f" << facemark << "_b" << body << "_m"; std::string styleclass = style.str(); std::stringstream out; CGAL_For_all(c1, c2) { if ( explorer.is_standard( explorer.target(c1) ) ) { CGAL_Nef_polyhedron2::Explorer::Point source = explorer.point( explorer.source( c1 ) ); CGAL_Point_2e target = explorer.point( explorer.target( c1 ) ); out << " \n"; std::string he_mark = boost::lexical_cast(c1->mark()); out << " \n"; } else { out << " \n"; } } return out.str(); } static CGAL_Iso_rectangle_2e bounding_box(const CGAL_Nef_polyhedron2 &N) { CGAL_Iso_rectangle_2e result(0,0,0,0); CGAL_Nef_polyhedron2::Explorer explorer = N.explorer(); CGAL_Nef_polyhedron2::Explorer::Vertex_const_iterator vi; std::vector 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; } 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; std::string linewidth = "0.05"; out << "\n"; out << svg_header() << "\n" << svg_styleblock( linewidth ) << "\n"; for ( i = explorer.faces_begin(); i!= explorer.faces_end(); ++i ) { out << " \n"; out << " \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, i->mark(), true ); out << " \n"; CGAL_Nef_polyhedron2::Explorer::Hole_const_iterator j; for ( j = explorer.holes_begin( i ); j!= explorer.holes_end( i ); ++j ) { out << " \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() ); out << " \n"; } out << " \n"; } out << ""; 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 = CGALUtils::boundingBox(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 << " \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 << " \n"; if ( contour_count == 0 ) { out << " \n"; } else { out << " \n" ; } CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(i), c2(c1); CGAL_For_all( c1, c2 ) { // 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 << " \n"; out << " \n"; else out << " />\n"; } contour_count++; out << " \n"; } out << " \n"; } }; std::string dump_svg( const CGAL_Nef_polyhedron3 &N ) { std::stringstream out; std::string linewidth = "0.05"; out << "\n"; out << svg_header() << "\n" << svg_border() << "\n"; out << svg_styleblock( linewidth ) << "\n" << svg_axes() << "\n"; CGAL_Nef_polyhedron3::Volume_const_iterator c; CGAL_forall_volumes(c,N) { out << " \n"; out << " \n"; CGAL_Nef_polyhedron3::Shell_entry_const_iterator it; CGAL_forall_shells_of(it,c) { out << " \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 << " \n"; } out << " \n"; } out << "\n"; out << ""; std::string tmp = out.str(); boost::replace_all( tmp, "'", "\"" ); return tmp; } } // namespace #endif // ENABLE_CGAL