1 #include "SVG.hpp"
2 #include <iostream>
3 
4 #include <boost/nowide/cstdio.hpp>
5 
6 namespace Slic3r {
7 
open(const char * afilename)8 bool SVG::open(const char* afilename)
9 {
10     this->filename = afilename;
11     this->f = boost::nowide::fopen(afilename, "w");
12     if (this->f == NULL)
13         return false;
14     fprintf(this->f,
15         "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
16         "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
17         "<svg height=\"2000\" width=\"2000\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"
18         "   <marker id=\"endArrow\" markerHeight=\"8\" markerUnits=\"strokeWidth\" markerWidth=\"10\" orient=\"auto\" refX=\"1\" refY=\"5\" viewBox=\"0 0 10 10\">\n"
19         "      <polyline fill=\"darkblue\" points=\"0,0 10,5 0,10 1,5\" />\n"
20         "   </marker>\n"
21         );
22     fprintf(this->f, "<rect fill='white' stroke='none' x='0' y='0' width='%f' height='%f'/>\n", 2000.f, 2000.f);
23     return true;
24 }
25 
open(const char * afilename,const BoundingBox & bbox,const coord_t bbox_offset,bool aflipY)26 bool SVG::open(const char* afilename, const BoundingBox &bbox, const coord_t bbox_offset, bool aflipY)
27 {
28     this->filename = afilename;
29     this->origin   = bbox.min - Point(bbox_offset, bbox_offset);
30     this->flipY    = aflipY;
31     this->f        = boost::nowide::fopen(afilename, "w");
32     if (f == NULL)
33         return false;
34     float w = to_svg_coord(bbox.max(0) - bbox.min(0) + 2 * bbox_offset);
35     float h = to_svg_coord(bbox.max(1) - bbox.min(1) + 2 * bbox_offset);
36     this->height   = h;
37     fprintf(this->f,
38         "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
39         "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
40         "<svg height=\"%f\" width=\"%f\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"
41         "   <marker id=\"endArrow\" markerHeight=\"8\" markerUnits=\"strokeWidth\" markerWidth=\"10\" orient=\"auto\" refX=\"1\" refY=\"5\" viewBox=\"0 0 10 10\">\n"
42         "      <polyline fill=\"darkblue\" points=\"0,0 10,5 0,10 1,5\" />\n"
43         "   </marker>\n",
44         h, w);
45     fprintf(this->f, "<rect fill='white' stroke='none' x='0' y='0' width='%f' height='%f'/>\n", w, h);
46     return true;
47 }
48 
draw(const Line & line,std::string stroke,coordf_t stroke_width)49 void SVG::draw(const Line &line, std::string stroke, coordf_t stroke_width)
50 {
51     fprintf(this->f,
52         "   <line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke: %s; stroke-width: %f\"",
53         to_svg_x(line.a(0) - origin(0)), to_svg_y(line.a(1) - origin(1)), to_svg_x(line.b(0) - origin(0)), to_svg_y(line.b(1) - origin(1)), stroke.c_str(), (stroke_width == 0) ? 1.f : to_svg_coord(stroke_width));
54     if (this->arrows)
55         fprintf(this->f, " marker-end=\"url(#endArrow)\"");
56     fprintf(this->f, "/>\n");
57 }
58 
draw(const ThickLine & line,const std::string & fill,const std::string & stroke,coordf_t stroke_width)59 void SVG::draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coordf_t stroke_width)
60 {
61     Vec2d dir(line.b(0)-line.a(0), line.b(1)-line.a(1));
62     Vec2d perp(-dir(1), dir(0));
63     coordf_t len = sqrt(perp(0)*perp(0) + perp(1)*perp(1));
64     coordf_t da  = coordf_t(0.5)*line.a_width/len;
65     coordf_t db  = coordf_t(0.5)*line.b_width/len;
66     fprintf(this->f,
67         "   <polygon points=\"%f,%f %f,%f %f,%f %f,%f\" style=\"fill:%s; stroke: %s; stroke-width: %f\"/>\n",
68         to_svg_x(line.a(0)-da*perp(0)-origin(0)),
69         to_svg_y(line.a(1)-da*perp(1)-origin(1)),
70         to_svg_x(line.b(0)-db*perp(0)-origin(0)),
71         to_svg_y(line.b(1)-db*perp(1)-origin(1)),
72         to_svg_x(line.b(0)+db*perp(0)-origin(0)),
73         to_svg_y(line.b(1)+db*perp(1)-origin(1)),
74         to_svg_x(line.a(0)+da*perp(0)-origin(0)),
75         to_svg_y(line.a(1)+da*perp(1)-origin(1)),
76         fill.c_str(), stroke.c_str(),
77         (stroke_width == 0) ? 1.f : to_svg_coord(stroke_width));
78 }
79 
draw(const Lines & lines,std::string stroke,coordf_t stroke_width)80 void SVG::draw(const Lines &lines, std::string stroke, coordf_t stroke_width)
81 {
82     for (const Line &l : lines)
83         this->draw(l, stroke, stroke_width);
84 }
85 
draw(const IntersectionLines & lines,std::string stroke)86 void SVG::draw(const IntersectionLines &lines, std::string stroke)
87 {
88     for (const IntersectionLine &il : lines)
89         this->draw((Line)il, stroke);
90 }
91 
draw(const ExPolygon & expolygon,std::string fill,const float fill_opacity)92 void SVG::draw(const ExPolygon &expolygon, std::string fill, const float fill_opacity)
93 {
94     this->fill = fill;
95 
96     std::string d;
97     Polygons pp = expolygon;
98     for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) {
99         d += this->get_path_d(*p, true) + " ";
100     }
101     this->path(d, true, 0, fill_opacity);
102 }
103 
draw_outline(const ExPolygon & expolygon,std::string stroke_outer,std::string stroke_holes,coordf_t stroke_width)104 void SVG::draw_outline(const ExPolygon &expolygon, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
105 {
106     draw_outline(expolygon.contour, stroke_outer, stroke_width);
107     for (Polygons::const_iterator it = expolygon.holes.begin(); it != expolygon.holes.end(); ++ it) {
108         draw_outline(*it, stroke_holes, stroke_width);
109     }
110 }
111 
draw(const ExPolygons & expolygons,std::string fill,const float fill_opacity)112 void SVG::draw(const ExPolygons &expolygons, std::string fill, const float fill_opacity)
113 {
114     for (ExPolygons::const_iterator it = expolygons.begin(); it != expolygons.end(); ++it)
115         this->draw(*it, fill, fill_opacity);
116 }
117 
draw_outline(const ExPolygons & expolygons,std::string stroke_outer,std::string stroke_holes,coordf_t stroke_width)118 void SVG::draw_outline(const ExPolygons &expolygons, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
119 {
120     for (ExPolygons::const_iterator it = expolygons.begin(); it != expolygons.end(); ++ it)
121         draw_outline(*it, stroke_outer, stroke_holes, stroke_width);
122 }
123 
draw(const Surface & surface,std::string fill,const float fill_opacity)124 void SVG::draw(const Surface &surface, std::string fill, const float fill_opacity)
125 {
126     draw(surface.expolygon, fill, fill_opacity);
127 }
128 
draw_outline(const Surface & surface,std::string stroke_outer,std::string stroke_holes,coordf_t stroke_width)129 void SVG::draw_outline(const Surface &surface, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
130 {
131     draw_outline(surface.expolygon, stroke_outer, stroke_holes, stroke_width);
132 }
133 
draw(const Surfaces & surfaces,std::string fill,const float fill_opacity)134 void SVG::draw(const Surfaces &surfaces, std::string fill, const float fill_opacity)
135 {
136     for (Surfaces::const_iterator it = surfaces.begin(); it != surfaces.end(); ++it)
137         this->draw(*it, fill, fill_opacity);
138 }
139 
draw_outline(const Surfaces & surfaces,std::string stroke_outer,std::string stroke_holes,coordf_t stroke_width)140 void SVG::draw_outline(const Surfaces &surfaces, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
141 {
142     for (Surfaces::const_iterator it = surfaces.begin(); it != surfaces.end(); ++ it)
143         draw_outline(*it, stroke_outer, stroke_holes, stroke_width);
144 }
145 
draw(const SurfacesPtr & surfaces,std::string fill,const float fill_opacity)146 void SVG::draw(const SurfacesPtr &surfaces, std::string fill, const float fill_opacity)
147 {
148     for (SurfacesPtr::const_iterator it = surfaces.begin(); it != surfaces.end(); ++it)
149         this->draw(*(*it), fill, fill_opacity);
150 }
151 
draw_outline(const SurfacesPtr & surfaces,std::string stroke_outer,std::string stroke_holes,coordf_t stroke_width)152 void SVG::draw_outline(const SurfacesPtr &surfaces, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
153 {
154     for (SurfacesPtr::const_iterator it = surfaces.begin(); it != surfaces.end(); ++ it)
155         draw_outline(*(*it), stroke_outer, stroke_holes, stroke_width);
156 }
157 
draw(const Polygon & polygon,std::string fill)158 void SVG::draw(const Polygon &polygon, std::string fill)
159 {
160     this->fill = fill;
161     this->path(this->get_path_d(polygon, true), !fill.empty(), 0, 1.f);
162 }
163 
draw(const Polygons & polygons,std::string fill)164 void SVG::draw(const Polygons &polygons, std::string fill)
165 {
166     for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it)
167         this->draw(*it, fill);
168 }
169 
draw(const Polyline & polyline,std::string stroke,coordf_t stroke_width)170 void SVG::draw(const Polyline &polyline, std::string stroke, coordf_t stroke_width)
171 {
172     this->stroke = stroke;
173     this->path(this->get_path_d(polyline, false), false, stroke_width, 1.f);
174 }
175 
draw(const Polylines & polylines,std::string stroke,coordf_t stroke_width)176 void SVG::draw(const Polylines &polylines, std::string stroke, coordf_t stroke_width)
177 {
178     for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
179         this->draw(*it, stroke, stroke_width);
180 }
181 
draw(const ThickLines & thicklines,const std::string & fill,const std::string & stroke,coordf_t stroke_width)182 void SVG::draw(const ThickLines &thicklines, const std::string &fill, const std::string &stroke, coordf_t stroke_width)
183 {
184     for (ThickLines::const_iterator it = thicklines.begin(); it != thicklines.end(); ++it)
185         this->draw(*it, fill, stroke, stroke_width);
186 }
187 
draw(const ThickPolylines & polylines,const std::string & stroke,coordf_t stroke_width)188 void SVG::draw(const ThickPolylines &polylines, const std::string &stroke, coordf_t stroke_width)
189 {
190     for (ThickPolylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
191         this->draw((Polyline)*it, stroke, stroke_width);
192 }
193 
draw(const ThickPolylines & thickpolylines,const std::string & fill,const std::string & stroke,coordf_t stroke_width)194 void SVG::draw(const ThickPolylines &thickpolylines, const std::string &fill, const std::string &stroke, coordf_t stroke_width)
195 {
196     for (ThickPolylines::const_iterator it = thickpolylines.begin(); it != thickpolylines.end(); ++ it)
197         draw(it->thicklines(), fill, stroke, stroke_width);
198 }
199 
draw(const Point & point,std::string fill,coord_t iradius)200 void SVG::draw(const Point &point, std::string fill, coord_t iradius)
201 {
202     float radius = (iradius == 0) ? 3.f : to_svg_coord(iradius);
203     std::ostringstream svg;
204     svg << "   <circle cx=\"" << to_svg_x(point(0) - origin(0)) << "\" cy=\"" << to_svg_y(point(1) - origin(1))
205         << "\" r=\"" << radius << "\" "
206         << "style=\"stroke: none; fill: " << fill << "\" />";
207 
208     fprintf(this->f, "%s\n", svg.str().c_str());
209 }
210 
draw(const Points & points,std::string fill,coord_t radius)211 void SVG::draw(const Points &points, std::string fill, coord_t radius)
212 {
213     for (Points::const_iterator it = points.begin(); it != points.end(); ++it)
214         this->draw(*it, fill, radius);
215 }
216 
draw(const ClipperLib::Path & polygon,double scale,std::string stroke,coordf_t stroke_width)217 void SVG::draw(const ClipperLib::Path &polygon, double scale, std::string stroke, coordf_t stroke_width)
218 {
219     this->stroke = stroke;
220     this->path(this->get_path_d(polygon, scale, true), false, stroke_width, 1.f);
221 }
222 
draw(const ClipperLib::Paths & polygons,double scale,std::string stroke,coordf_t stroke_width)223 void SVG::draw(const ClipperLib::Paths &polygons, double scale, std::string stroke, coordf_t stroke_width)
224 {
225     for (ClipperLib::Paths::const_iterator it = polygons.begin(); it != polygons.end(); ++ it)
226         draw(*it, scale, stroke, stroke_width);
227 }
228 
draw_outline(const Polygon & polygon,std::string stroke,coordf_t stroke_width)229 void SVG::draw_outline(const Polygon &polygon, std::string stroke, coordf_t stroke_width)
230 {
231     this->stroke = stroke;
232     this->path(this->get_path_d(polygon, true), false, stroke_width, 1.f);
233 }
234 
draw_outline(const Polygons & polygons,std::string stroke,coordf_t stroke_width)235 void SVG::draw_outline(const Polygons &polygons, std::string stroke, coordf_t stroke_width)
236 {
237     for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++ it)
238         draw_outline(*it, stroke, stroke_width);
239 }
240 
path(const std::string & d,bool fill,coordf_t stroke_width,const float fill_opacity)241 void SVG::path(const std::string &d, bool fill, coordf_t stroke_width, const float fill_opacity)
242 {
243     float lineWidth = 0.f;
244     if (! fill)
245         lineWidth = (stroke_width == 0) ? 2.f : to_svg_coord(stroke_width);
246 
247     fprintf(
248         this->f,
249         "   <path d=\"%s\" style=\"fill: %s; stroke: %s; stroke-width: %f; fill-type: evenodd\" %s fill-opacity=\"%f\" />\n",
250         d.c_str(),
251         fill ? this->fill.c_str() : "none",
252         this->stroke.c_str(),
253         lineWidth,
254         (this->arrows && !fill) ? " marker-end=\"url(#endArrow)\"" : "",
255         fill_opacity
256     );
257 }
258 
get_path_d(const MultiPoint & mp,bool closed) const259 std::string SVG::get_path_d(const MultiPoint &mp, bool closed) const
260 {
261     std::ostringstream d;
262     d << "M ";
263     for (Points::const_iterator p = mp.points.begin(); p != mp.points.end(); ++p) {
264         d << to_svg_x((*p)(0) - origin(0)) << " ";
265         d << to_svg_y((*p)(1) - origin(1)) << " ";
266     }
267     if (closed) d << "z";
268     return d.str();
269 }
270 
get_path_d(const ClipperLib::Path & path,double scale,bool closed) const271 std::string SVG::get_path_d(const ClipperLib::Path &path, double scale, bool closed) const
272 {
273     std::ostringstream d;
274     d << "M ";
275     for (ClipperLib::Path::const_iterator p = path.begin(); p != path.end(); ++p) {
276         d << to_svg_x(scale * p->X - origin(0)) << " ";
277         d << to_svg_y(scale * p->Y - origin(1)) << " ";
278     }
279     if (closed) d << "z";
280     return d.str();
281 }
282 
draw_text(const Point & pt,const char * text,const char * color)283 void SVG::draw_text(const Point &pt, const char *text, const char *color)
284 {
285     fprintf(this->f,
286         "<text x=\"%f\" y=\"%f\" font-family=\"sans-serif\" font-size=\"20px\" fill=\"%s\">%s</text>",
287         to_svg_x(pt(0)-origin(0)),
288         to_svg_y(pt(1)-origin(1)),
289         color, text);
290 }
291 
draw_legend(const Point & pt,const char * text,const char * color)292 void SVG::draw_legend(const Point &pt, const char *text, const char *color)
293 {
294     fprintf(this->f,
295         "<circle cx=\"%f\" cy=\"%f\" r=\"10\" fill=\"%s\"/>",
296         to_svg_x(pt(0)-origin(0)),
297         to_svg_y(pt(1)-origin(1)),
298         color);
299     fprintf(this->f,
300         "<text x=\"%f\" y=\"%f\" font-family=\"sans-serif\" font-size=\"10px\" fill=\"%s\">%s</text>",
301         to_svg_x(pt(0)-origin(0)) + 20.f,
302         to_svg_y(pt(1)-origin(1)),
303         "black", text);
304 }
305 
Close()306 void SVG::Close()
307 {
308     fprintf(this->f, "</svg>\n");
309     fclose(this->f);
310     this->f = NULL;
311 //    printf("SVG written to %s\n", this->filename.c_str());
312 }
313 
export_expolygons(const char * path,const BoundingBox & bbox,const Slic3r::ExPolygons & expolygons,std::string stroke_outer,std::string stroke_holes,coordf_t stroke_width)314 void SVG::export_expolygons(const char *path, const BoundingBox &bbox, const Slic3r::ExPolygons &expolygons, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
315 {
316     SVG svg(path, bbox);
317     svg.draw(expolygons);
318     svg.draw_outline(expolygons, stroke_outer, stroke_holes, stroke_width);
319     svg.Close();
320 }
321 
export_expolygons(const char * path,const std::vector<std::pair<Slic3r::ExPolygons,ExPolygonAttributes>> & expolygons_with_attributes)322 void SVG::export_expolygons(const char *path, const std::vector<std::pair<Slic3r::ExPolygons, ExPolygonAttributes>> &expolygons_with_attributes)
323 {
324     if (expolygons_with_attributes.empty())
325         return;
326 
327     BoundingBox bbox = get_extents(expolygons_with_attributes.front().first);
328     for (size_t i = 0; i < expolygons_with_attributes.size(); ++ i)
329         bbox.merge(get_extents(expolygons_with_attributes[i].first));
330 
331     SVG svg(path, bbox);
332     for (const auto &exp_with_attr : expolygons_with_attributes)
333         svg.draw(exp_with_attr.first, exp_with_attr.second.color_fill, exp_with_attr.second.fill_opacity);
334     for (const auto &exp_with_attr : expolygons_with_attributes) {
335         std::string color_contour = exp_with_attr.second.color_contour;
336         if (color_contour.empty())
337             color_contour = exp_with_attr.second.color_fill;
338         std::string color_holes = exp_with_attr.second.color_holes;
339         if (color_holes.empty())
340             color_holes = color_contour;
341         svg.draw_outline(exp_with_attr.first, color_contour, color_holes, exp_with_attr.second.outline_width);
342     }
343     for (const auto &exp_with_attr : expolygons_with_attributes)
344     	if (exp_with_attr.second.radius_points > 0)
345 			for (const ExPolygon &expoly : exp_with_attr.first)
346     			svg.draw((Points)expoly, exp_with_attr.second.color_points, exp_with_attr.second.radius_points);
347     svg.Close();
348 }
349 
350 }
351