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