NDEVR
API Documentation
BasicVectoImageExporter.h
1#include <DGtal/base/Common.h>
2#include <DGtal/helpers/StdDefs.h>
3#include <DGtal/geometry/curves/FrechetShortcut.h>
4#include <DGtal/shapes/Mesh.h>
5
6#ifndef BASICVECTOIMAGEEXPORTER_H
7#define BASICVECTOIMAGEEXPORTER_H
8class BasicVectoImageExporter
9{
10 std::string LINE_COLOR; // = " 0.8 0.1 0.1 ";
11 std::string POINT_COLOR; // = " 0.1 0.1 0.8 ";
12 const float emptyCntWidth = 0; // used in % (larger for better inskape rendering)
13 const float meshCntWidth = 0;
14
15 typedef enum
16 {
17 EpsExport,
18 SvgExport,
19 UnknowExport
20 } ExportType;
21
22public:
23 typedef DGtal::Z2i::RealPoint Point2D;
24 typedef std::vector<Point2D> Contour2D;
25
26 void closeFigure();
27
28 void fillHeader();
29
30 void fillSVGHeader();
31
32 void fillEPSHeader();
33
34 std::string getHexCode(const DGtal::Color &c);
35
36 std::string getExportType();
37
38 template <typename CoordType>
39 CoordType reverseYCoord(CoordType y)
40 {
41 return myHeight - y;
42 }
43
44 int mod(int value, int m)
45 {
46 int res = value % m;
47
48 return res < 0 ? res + m : res;
49 }
50
51 template <typename TContour2D>
52 void addPathContent(const TContour2D &contour)
53 {
54 if (contour.size() == 0)
55 {
56 return;
57 }
58 if (myExportType == EpsExport)
59 {
60 myOutputStream << contour[0].first[0] << " " << contour[0].first[1] << " moveto" << std::endl;
61 for (unsigned int i = 1; i < contour.size(); i++)
62 {
63 myOutputStream << contour[i].first[0] << " " << contour[i].first[1] << " lineto" << std::endl;
64 }
65 myOutputStream << contour[0].first[0] << " " << contour[0].first[1] << " lineto" << std::endl;
66 }
67 else if (myExportType == SvgExport)
68 {
69 if (contour.size() >= 3)
70 {
71 typedef enum
72 {
73 LINE,
74 CURVE,
75 } DrawType;
76 typedef std::pair<double, double> Point;
77 typedef std::pair<DrawType, std::vector<Point>> Path;
78
79 std::vector<Path> paths;
80
81 int size = contour.size();
82 double scale = .25;
83 for (int i = 0; i < size; i++)
84 {
85 // If the point is on a triple point
86 bool isLine = contour[i].second || contour[mod(i + 1, size)].second;
87
88 std::vector<Point> path;
89 double pointA[2] = {contour[mod(i, size)].first[0], contour[mod(i, size)].first[1]};
90 double pointB[2] = {contour[mod(i + 1, size)].first[0], contour[mod(i + 1, size)].first[1]};
91 if (isLine)
92 {
93 path.push_back(std::make_pair(contour[mod(i + 1, size)].first[0], reverseYCoord(contour[mod(i + 1, size)].first[1])));
94
95 paths.push_back(std::make_pair(LINE, path));
96 }
97 else
98 {
99 // Compute first control point
100 double pointBefore[2] = {contour[mod(i - 1, size)].first[0], contour[mod(i - 1, size)].first[1]};
101 double vector1[2] = {(pointB[0] - pointBefore[0]), (pointB[1] - pointBefore[1])};
102 vector1[0] *= scale;
103 vector1[1] *= scale;
104 double firstControl[2] = {(pointA[0] + vector1[0]), (pointA[1] + vector1[1])};
105
106 // Compute second controle point
107 double pointAfter[2] = {contour[mod(i + 2, size)].first[0], contour[mod(i + 2, size)].first[1]};
108 double vector2[2] = {(pointAfter[0] - pointA[0]), (pointAfter[1] - pointA[1])};
109 vector2[0] *= scale;
110 vector2[1] *= scale;
111 double secondControl[2] = {(pointB[0] - vector2[0]), (pointB[1] - vector2[1])};
112
113 path.push_back(std::make_pair(firstControl[0], reverseYCoord(firstControl[1])));
114 path.push_back(std::make_pair(secondControl[0], reverseYCoord(secondControl[1])));
115 path.push_back(std::make_pair(pointB[0], reverseYCoord(pointB[1])));
116
117 paths.push_back(std::make_pair(CURVE, path));
118 }
119 }
120
121 myOutputStream << "M " << contour[0].first[0] << " " << reverseYCoord(contour[0].first[1]);
122 for (auto const &path : paths)
123 {
124 switch (path.first)
125 {
126 default:
127 case LINE:
128 myOutputStream << " L " << path.second[0].first << " " << path.second[0].second;
129 break;
130
131 case CURVE:
132 myOutputStream << " C " << path.second[0].first << " " << path.second[0].second;
133 myOutputStream << ", " << path.second[1].first << " " << path.second[1].second;
134 myOutputStream << ", " << path.second[2].first << " " << path.second[2].second;
135 break;
136 }
137 }
138 myOutputStream << " Z";
139 }
140 else
141 {
142 myOutputStream << "M ";
143 myOutputStream << contour[0].first[0] << "," << reverseYCoord(contour[0].first[1]) << " ";
144 for (unsigned int i = 1; i < contour.size(); i++)
145 {
146 myOutputStream << contour[i].first[0] << "," << reverseYCoord(contour[i].first[1]) << " ";
147 }
148 myOutputStream << contour[0].first[0] << "," << reverseYCoord(contour[0].first[1]) << " z ";
149 }
150 }
151 };
152
153 // Not yet used (@todo later add SVG case)
154 template <typename TContour>
155 void addPathContentBezierP0P1P2P3(const TContour &contour)
156 {
157 if (myExportType == EpsExport)
158 {
159 if (contour.size() == 0)
160 {
161 return;
162 }
163 myOutputStream << contour[0][0] << " " << contour[0][1] << " moveto" << std::endl;
164
165 for (int i = 1; i < (int)(contour.size()) - 2; i = i + 3)
166 {
167
168 myOutputStream << contour[i][0] << " " << contour[i][1] << " ";
169 myOutputStream << contour[i + 1][0] << " " << contour[i + 1][1] << " ";
170 myOutputStream << contour[i + 2][0] << " " << contour[i + 2][1] << " curveto" << std::endl;
171 }
172 }
173 else if (myExportType == SvgExport)
174 {
175 if (contour.size < 1)
176 return;
177
178 myOutputStream << "M " << contour[0][0] << " " << contour[0][1];
179
180 for (int i = 1; i < contour.size(); i += 2)
181 {
182 myOutputStream << " Q " << contour[i % contour.size()][0] << " " << contour[i % contour.size()][1]
183 << ", " << contour[(i + 1) % contour.size()][0] << " "
184 << contour[(i + 1) % contour.size()][1];
185 }
186 }
187 };
188
189 // Used (@todo tp add SVG case)
190 template <typename TContour>
191 void addRegion(const TContour &contour,
192 const DGtal::Color &color, double linewidth)
193 {
194 if (myExportType == EpsExport)
195 {
196 myOutputStream << "newpath" << std::endl;
197 addPathContent(contour);
198 myOutputStream << "closepath" << std::endl;
199 if (myDisplayMesh)
200 {
201 myOutputStream << "gsave" << std::endl;
202 }
203 float r, g, b;
204 r = color.red() / 255.0;
205 g = color.green() / 255.0;
206 b = color.blue() / 255.0;
207 myOutputStream << r << " " << g << " " << b << " setrgbcolor" << std::endl;
208 myOutputStream << "fill" << std::endl;
209 if (myDisplayMesh)
210 {
211 myOutputStream << "grestore" << std::endl;
212 myOutputStream << linewidth << " setlinewidth 0.7 0.2 0.2 setrgbcolor" << std::endl;
213 myOutputStream << "stroke" << std::endl;
214 }
215 else
216 {
217 myOutputStream << "grestore" << std::endl;
218 myOutputStream << emptyCntWidth << " setlinewidth " << r << " " << g << " " << b << " setrgbcolor"
219 << std::endl;
220 myOutputStream << "stroke" << std::endl;
221 }
222 }
223 else if (myExportType == SvgExport)
224 {
225 if (!myDisplayMesh)
226 {
227 myOutputStream << "<path \n style=\"fill:#" << getHexCode(color);
228 myOutputStream << "; fill-opacity:1,fill-rule:evenodd;stroke:#" << getHexCode(color) << ";stroke-width:"
229 << emptyCntWidth << "px;";
230 myOutputStream << "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1\"" << std::endl;
231 myOutputStream << "d=\"";
232 }
233 else
234 {
235 myOutputStream << "<path \n style=\"fill:#" << getHexCode(color);
236 myOutputStream << "; fill-opacity:1,fill-rule:evenodd;stroke:red;stroke-width:" << meshCntWidth
237 << "px;";
238 myOutputStream << "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1\"" << std::endl;
239 myOutputStream << "d=\"";
240 }
241
242 addPathContent(contour);
243 myOutputStream << "\"\n";
244 myOutputStream << "id=\"path" << myCurrentIdPath << "\" \n";
245 myOutputStream << "inkscape:connector-curvature=\"0\" ",
246 myOutputStream << "sodipodi:nodetypes=\"cccccccccc\" />\n";
247 myCurrentIdPath++;
248 }
249 }
250
251 // Used (@todo tp add SVG case)
252 template <typename TContour> // std::vector<std::pair<DGtal::TVTriangulation::RealPoint, bool>>
253 void addRegions(const std::vector<TContour> &contours, const DGtal::Color &color)
254 {
255 if (myExportType == EpsExport)
256 {
257 myOutputStream << emptyCntWidth << " setlinewidth" << std::endl;
258 float r, g, b;
259 r = color.red() / 255.0;
260 g = color.green() / 255.0;
261 b = color.blue() / 255.0;
262 myOutputStream << r << " " << g << " " << b << " setrgbcolor" << std::endl;
263
264 myOutputStream << "newpath" << std::endl;
265 for (auto const &cnt : contours)
266 {
267 addPathContent(cnt);
268 }
269 myOutputStream << " closepath " << std::endl;
270 // myOutputStream << "gsave" << std::endl;
271
272 myOutputStream << "gsave" << std::endl;
273 myOutputStream << "fill" << std::endl;
274 myOutputStream << "grestore" << std::endl;
275 myOutputStream << "stroke" << std::endl;
276
277 /* if (myDisplayMesh) // TODO : uncomment
278 {
279 myOutputStream << "grestore" << std::endl;
280 myOutputStream << LINE_COLOR << " setrgbcolor" << std::endl;
281 myOutputStream << meshCntWidth << " setlinewidth" << std::endl;
282 myOutputStream << "stroke" << std::endl;
283 myOutputStream << POINT_COLOR << " setrgbcolor" << std::endl;
284 for (auto const &cnt : contours)
285 {
286 addContourPoints(cnt);
287 }
288 }*/
289 }
290 else if (myExportType == SvgExport)
291 {
292 if (!myDisplayMesh)
293 {
294 myOutputStream << "<path \n style=\"fill:#" << getHexCode(color);
295 myOutputStream << "; fill-opacity:1,fill-rule:evenodd;stroke:#" << getHexCode(color) << ";stroke-width:"
296 << emptyCntWidth << ";";
297 myOutputStream << "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1\"" << std::endl;
298 myOutputStream << "d=\"";
299 }
300 else
301 {
302 myOutputStream << "<path \n style=\"fill:#" << getHexCode(color);
303 myOutputStream << "; fill-opacity:1,fill-rule:evenodd;stroke:red;stroke-width:" << meshCntWidth << ";";
304 myOutputStream << "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1\"" << std::endl;
305 myOutputStream << "d=\"";
306 }
307 for (auto const &cnt : contours)
308 {
309 addPathContent(cnt);
310 }
311 myOutputStream << "\"\n";
312 myOutputStream << "id=\"path" << myCurrentIdPath << "\" \n";
313 myOutputStream << "inkscape:connector-curvature=\"0\" ",
314 myOutputStream << "sodipodi:nodetypes=\"cccccccccc\" />\n";
315 myCurrentIdPath++;
316 /*if (myDisplayMesh) // TODO : uncomment
317 {
318 for (auto const &cnt : contours)
319 {
320 addContourPoints(cnt);
321 }
322 }*/
323 }
324 };
325
326 template <typename TContour>
327 void addRegionWithHoles(const TContour &contour,
328 const std::vector<TContour> &listHoles,
329 const DGtal::Color &color)
330 {
331 float r, g, b;
332 r = color.red() / 255.0;
333 g = color.green() / 255.0;
334 b = color.blue() / 255.0;
335 if (myExportType == EpsExport)
336 {
337 myOutputStream << "newpath" << std::endl;
338 addPathContent(contour);
339 for (auto const &hole : listHoles)
340 {
341 addPathContent(hole);
342 }
343 myOutputStream << "closepath" << std::endl;
344
345 myOutputStream << r << " " << g << " " << b << " setrgbcolor" << std::endl;
346 myOutputStream << "fill" << std::endl;
347 }
348 else if (myExportType == SvgExport)
349 {
350 myOutputStream << "<path \n style=\"fill:#" << getHexCode(color);
351 myOutputStream << "; fill-opacity:1,fill-rule:evenodd;stroke:#" << getHexCode(color) << ";stroke-width:"
352 << emptyCntWidth << "px;";
353 myOutputStream << "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1\"" << std::endl;
354 myOutputStream << "d=\"";
355 addPathContent(contour);
356 for (auto const &hole : listHoles)
357 {
358 addPathContent(hole);
359 }
360 myOutputStream << "\"\n";
361 myOutputStream << "id=\"path" << myCurrentIdPath << "\" \n";
362 myOutputStream << "inkscape:connector-curvature=\"0\" ",
363 myOutputStream << "sodipodi:nodetypes=\"cccccccccc\" />\n";
364 myCurrentIdPath++;
365 }
366 }
367
368 // Used (@todo tp add SVG case)
369 template <typename TPoint2D>
370 void drawLine(const TPoint2D &pt1, const TPoint2D &pt2,
371 const DGtal::Color &color, double lineWidth = 2.0)
372 {
373 float r, g, b;
374 r = color.red() / 255.0;
375 g = color.green() / 255.0;
376 b = color.blue() / 255.0;
377 if (myExportType == EpsExport)
378 {
379 myOutputStream << r << " " << g << " " << b << " setrgbcolor" << std::endl;
380 myOutputStream << lineWidth << " setlinewidth" << std::endl;
381 myOutputStream << pt1[0] << " " << pt1[1] << " moveto" << std::endl;
382 myOutputStream << pt2[0] << " " << pt2[1] << " lineto" << std::endl;
383 myOutputStream << "stroke" << std::endl;
384 }
385 else if (myExportType == SvgExport)
386 {
387 myOutputStream << "draw line not implemented in SVG" << std::endl;
388 }
389 }
390
391 template <typename TPoint2D>
392 void addContour(const std::vector<TPoint2D> &contour,
393 const DGtal::Color &color, double lineWidth = 1.0)
394 {
395 if (myExportType == EpsExport)
396 {
397 myOutputStream << "newpath" << std::endl;
398 addPathContent(contour);
399 myOutputStream << "closepath" << std::endl;
400 float r, g, b;
401 r = color.red() / 255.0;
402 g = color.green() / 255.0;
403 b = color.blue() / 255.0;
404 myOutputStream << r << " " << g << " " << b << " setrgbcolor" << std::endl;
405
406 myOutputStream << lineWidth << " setlinewidth stroke" << std::endl;
407 }
408 else if (myExportType == SvgExport)
409 {
410
411 myOutputStream << "<path \n style=\"stroke:#" << getHexCode(color);
412 myOutputStream << "; fill:none; stroke-opacity:1;stroke-width:" << lineWidth << ";";
413 myOutputStream << "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1\"" << std::endl;
414 myOutputStream << "d=\"";
415
416 addPathContent(contour);
417
418 myOutputStream << "\"\n";
419 myOutputStream << "id=\"path" << myCurrentIdPath << "\" \n";
420 myOutputStream << "inkscape:connector-curvature=\"0\" ",
421 myOutputStream << "sodipodi:nodetypes=\"cccccccccc\" />\n";
422 myCurrentIdPath++;
423 }
424 }
425
426 // Not yet used (@todo later add SVG case)
427 template <typename TContour>
428 void addPathContentBezier(const TContour &contour)
429 {
430 if (myExportType == EpsExport)
431 {
432 if (contour.size() <= 1)
433 {
434 return;
435 }
436 // format from dominantPointPolygonalisation_Bezier() (in VectorisationHelper)
437 myOutputStream << contour[2][0] << " " << contour[2][1] << " moveto" << std::endl;
438 for (int i = 0; i < (int)(contour.size()); i = i + 4)
439 {
440 myOutputStream << contour[(i + 1) % contour.size()][0] << " " << contour[(i + 1) % contour.size()][1]
441 << " ";
442 myOutputStream << contour[(i + 4) % contour.size()][0] << " " << contour[(i + 4) % contour.size()][1]
443 << " ";
444 myOutputStream << contour[(i + 6) % contour.size()][0] << " " << contour[(i + 6) % contour.size()][1]
445 << " curveto" << std::endl;
446 }
447 }
448 else if (myExportType == SvgExport)
449 {
450 if (contour.size() < 1)
451 return;
452
453 myOutputStream << "M " << contour[0][0] << " " << contour[0][1];
454 for (int i = 1; i < contour.size(); i += 2)
455 {
456 myOutputStream << " Q " << contour[i % contour.size()][0] << " " << contour[i % contour.size()][1]
457 << ", " << contour[(i + 1) % contour.size()][0] << " "
458 << contour[(i + 1) % contour.size()][1];
459 }
460 }
461 }
462
463 // Not yet used (@todo later add SVG case)
464 template <typename TContour>
465 void addRegionsBezier(const std::vector<TContour> &contours, const DGtal::Color &color, bool basicOrder = false)
466 {
467 myOutputStream << "newpath" << std::endl;
468 for (auto const &cnt : contours)
469 {
470 if (basicOrder)
471 {
472 addPathContentBezierP0P1P2P3(cnt);
473 }
474 else
475 {
476 addPathContentBezier(cnt);
477 }
478 }
479 myOutputStream << "closepath" << std::endl;
480 if (myDisplayMesh)
481 {
482 myOutputStream << "gsave" << std::endl;
483 }
484
485 float r, g, b;
486 r = color.red() / 255.0;
487 g = color.green() / 255.0;
488 b = color.blue() / 255.0;
489 myOutputStream << r << " " << g << " " << b << " setrgbcolor" << std::endl;
490 myOutputStream << "fill" << std::endl;
491 if (myDisplayMesh)
492 {
493 myOutputStream << "grestore" << std::endl;
494 myOutputStream << LINE_COLOR << "setrgbcolor" << std::endl;
495 myOutputStream << "0.1 setlinewidth" << std::endl;
496 myOutputStream << "stroke" << std::endl;
497 myOutputStream << POINT_COLOR << "setrgbcolor" << std::endl;
498 for (auto const &cnt : contours)
499 {
500 addContourPoints(cnt);
501 }
502 }
503 }
504
505 template <typename TContour>
506 void addContourPoints(const TContour &contour, const DGtal::Color &color = DGtal::Color::Red, double radius = 2.0)
507 {
508 float r, g, b;
509 r = color.red() / 255.0;
510 g = color.green() / 255.0;
511 b = color.blue() / 255.0;
512 if (myExportType == EpsExport)
513 {
514 myOutputStream << r << " " << g << " " << b << " setrgbcolor" << std::endl;
515 if (contour.size() == 0)
516 {
517 return;
518 }
519
520 for (const auto &p : contour)
521 {
522 myOutputStream << p[0] << " " << p[1] << " moveto" << std::endl;
523 myOutputStream << std::fixed << p[0] << " " << p[1] << " " << radius << " 0 360 arc" << std::endl;
524 myOutputStream << "fill" << std::endl;
525 }
526 }
527 else if (myExportType == SvgExport)
528 {
529 for (const auto &p : contour)
530 {
531 myOutputStream << "<circle cx=\"" << p[0] << "\" cy=\"" << reverseYCoord(p[1]) << "\" r=\"" << radius
532 << "\" fill = \"#" << getHexCode(color) << "\"/>";
533 }
534 }
535 };
536
537 BasicVectoImageExporter(const std::string &imageName, unsigned int width, unsigned int height,
538 bool displayMesh = false, double scale = 1.0);
539
540 ~BasicVectoImageExporter() { myOutputStream.close(); };
541
542protected:
543 unsigned int myWidth = 200;
544 unsigned int myHeight = 200;
545 double myScale = 1.0;
546
547 double myShiftX = 0.0;
548 double myShiftY = 0.0;
549 int myCurrentIdPath = 1;
550 std::vector<Contour2D> myPlainContours;
551 std::vector<Contour2D> myHoleContours;
552 std::string myImageName;
553 std::ofstream myOutputStream;
554 bool myDisplayMesh;
555
556 // Associate for each plain contours a set of index representing the contour holes.
557 std::map<unsigned int, std::vector<unsigned int>> mapHoles;
558 std::map<unsigned int, DGtal::Color> colorMap;
559 ExportType myExportType = UnknowExport;
560};
561
562#endif // BASICVECTOIMAGEEXPORTER_H