Belle II Software  release-08-01-10
SVGPrimitivePlotter.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 #include <tracking/trackFindingCDC/display/SVGPrimitivePlotter.h>
9 
10 #include <framework/logging/Logger.h>
11 
12 #include <fstream>
13 
14 using namespace Belle2;
15 using namespace TrackFindingCDC;
16 
19 
22  , m_svgContentStream()
23  , m_nIndentationSpaces(s_defaultNIndentationSpaces)
24  , m_svgAttributes()
25 {
26 }
27 
30  , m_svgContentStream()
31  , m_nIndentationSpaces(s_defaultNIndentationSpaces)
32  , m_svgAttributes(svgAttributes)
33 {
34 }
35 
37  : PrimitivePlotter(plotter)
38  , m_svgContentStream(plotter.m_svgContentStream.str(), std::ostringstream::ate)
39  , m_nIndentationSpaces(plotter.m_nIndentationSpaces)
40  , m_svgAttributes(plotter.m_svgAttributes)
41 {
42 }
43 
44 std::unique_ptr<PrimitivePlotter> SVGPrimitivePlotter::clone() const
45 {
46  return std::make_unique<SVGPrimitivePlotter>(*this);
47 }
48 
50  float startY,
51  float endX,
52  float endY,
53  const AttributeMap& attributeMap)
54 {
55  PrimitivePlotter::drawLine(startX, startY, endX, endY, attributeMap);
56 
57  AttributeMap geometryAttributeMap {
58  {"x1", std::to_string(startX)},
59  {"x2", std::to_string(endX)},
60  {"y1", std::to_string(startY)},
61  {"y2", std::to_string(endY)}
62  };
63 
64  writeStandAloneTag(m_svgContentStream, "line", geometryAttributeMap, attributeMap);
65 
66 }
67 
68 
70  float startY,
71  float endX,
72  float endY,
73  const AttributeMap& attributeMap)
74 {
75  PrimitivePlotter::drawArrow(startX, startY, endX, endY, attributeMap);
76 
77  AttributeMap geometryAttributeMap {
78  {"x1", std::to_string(startX)},
79  {"x2", std::to_string(endX)},
80  {"y1", std::to_string(startY)},
81  {"y2", std::to_string(endY)},
82  {"marker-end", "url(#endArrow)"}
83  };
84 
85  writeStandAloneTag(m_svgContentStream, "line", geometryAttributeMap, attributeMap);
86 }
87 
88 
90  float centerY,
91  float radius,
92  const AttributeMap& attributeMap)
93 {
94  PrimitivePlotter::drawCircle(centerX, centerY, radius, attributeMap);
95 
96  AttributeMap geometryAttributeMap {
97  {"cx", std::to_string(centerX)},
98  {"cy", std::to_string(centerY)},
99  {"r", std::to_string(std::fabs(radius))}
100  };
101 
102  writeStandAloneTag(m_svgContentStream, "circle", geometryAttributeMap, attributeMap);
103 
104 }
105 
106 
108  float startY,
109  float endX,
110  float endY,
111  float radius,
112  bool longArc,
113  bool sweepFlag,
114  const AttributeMap& attributeMap)
115 {
117  startY,
118  endX,
119  endY,
120  radius,
121  longArc,
122  sweepFlag,
123  attributeMap);
124 
125  // Compile the path for a ellipses arc
126  // spelling out all the parts in case somebody want adopt
127  // although only the simpler circle arc case is needed.
128 
129  const float radiusX = std::fabs(radius);
130  const float radiusY = std::fabs(radius);
131  const float rotationAngle = 0;
132 
133  std::ostringstream pathStream;
134 
135  pathStream << "M" << ' ';
136  pathStream << std::to_string(startX) << ' ';
137  pathStream << std::to_string(startY) << ' ';
138  pathStream << "A" << ' ';
139  pathStream << std::to_string(radiusX) << ' ';
140  pathStream << std::to_string(radiusY) << ' ';
141  pathStream << std::to_string(rotationAngle) << ' ';
142  pathStream << std::to_string(longArc) << ' ';
143  pathStream << std::to_string(sweepFlag) << ' ';
144  pathStream << std::to_string(endX) << ' ';
145  pathStream << std::to_string(endY);
146 
147  AttributeMap geometryAttributeMap{{"d", pathStream.str()}};
148 
149  writeStandAloneTag(m_svgContentStream, "path", geometryAttributeMap, attributeMap);
150 }
151 
152 void SVGPrimitivePlotter::drawCurve(const std::vector<std::array<float, 2>>& points,
153  const std::vector<std::array<float, 2>>& tangents,
154  const AttributeMap& attributeMap)
155 {
156  // Magic number for circle approximation with splines
157  static const double k = 4.0 / 3.0 * (std::sqrt(2.0) - 1.0);
158 
159  B2ASSERT("Expect number of points and tangents to be the same", points.size() == tangents.size());
160  if (points.size() < 2) return;
161 
162  PrimitivePlotter::drawCurve(points, tangents, attributeMap);
163 
164  std::ostringstream pathStream;
165 
166  // Move to point
167  float startX = std::get<0>(points[0]);
168  float startY = std::get<1>(points[0]);
169 
170  pathStream << "M";
171  pathStream << ' ' << std::to_string(startX);
172  pathStream << ' ' << std::to_string(startY);
173 
174  for (size_t iCurrent = 0, iNext = 1; iNext < points.size(); iCurrent = iNext, ++iNext) {
175 
176  float currentTX = std::get<0>(tangents[iCurrent]);
177  float currentTY = std::get<1>(tangents[iCurrent]);
178 
179  float nextTX = std::get<0>(tangents[iNext]);
180  float nextTY = std::get<1>(tangents[iNext]);
181 
182  float currentT = std::hypot(currentTX, currentTY);
183  float nextT = std::hypot(nextTX, nextTY);
184 
185  currentTX /= currentT;
186  currentTY /= currentT;
187  nextTX /= nextT;
188  nextTY /= nextT;
189 
190  float currentX = std::get<0>(points[iCurrent]);
191  float currentY = std::get<1>(points[iCurrent]);
192 
193  float nextX = std::get<0>(points[iNext]);
194  float nextY = std::get<1>(points[iNext]);
195 
196  // Circle arc angle
197  float alpha = std::atan2(currentTX * nextTY - currentTY * nextTX,
198  currentTX * nextTX + currentTY * nextTY);
199 
200  float distance = std::hypot(currentX - nextX, currentY - nextY);
201  float controlLength = k * distance / 2 / std::cos(alpha / 2);
202 
203  float currentControlX = currentX + currentTX * controlLength;
204  float currentControlY = currentY + currentTY * controlLength;
205 
206  float nextControlX = nextX - nextTX * controlLength;
207  float nextControlY = nextY - nextTY * controlLength;
208 
209  pathStream << ' ' << "C";
210 
211  pathStream << ' ' << std::to_string(currentControlX);
212  pathStream << ' ' << std::to_string(currentControlY);
213 
214  pathStream << ' ' << std::to_string(nextControlX);
215  pathStream << ' ' << std::to_string(nextControlY);
216 
217  pathStream << ' ' << std::to_string(nextX);
218  pathStream << ' ' << std::to_string(nextY);
219  }
220 
221  AttributeMap geometryAttributeMap{{"d", pathStream.str()}};
222 
223  writeStandAloneTag(m_svgContentStream, "path", geometryAttributeMap, attributeMap);
224 }
225 
227 {
228  writeOpeningTag(m_svgContentStream, "g", attributeMap);
229 }
230 
232 {
234 }
235 
236 const std::string SVGPrimitivePlotter::save(const std::string& fileName)
237 {
238  // Check indention
240  B2WARNING("Mismatching calls to startGroup and endGroup detected. "
241  << "Proceeding to write the illforamed result.");
242  }
243 
244  int savedNIndentationSpaces = m_nIndentationSpaces;
246 
247  std::ofstream outputFileStream;
248  outputFileStream.open(fileName);
249 
250  writeSVGHeader(outputFileStream);
251 
252  AttributeMap standardAttributeMap {
253  {"baseProfile", "full"},
254  {"ev", "http://www.w3.org/2001/xml-events"},
255  {"version", "1.1"},
256  {"xlink", "http://www.w3.org/1999/xlink"},
257  {"xmlns", "http://www.w3.org/2000/svg"}
258  };
259 
260 
261  // Combine the viewbox specification from the bounding box
262  // Format "{left} {bottom} {width} {height}"
263  std::ostringstream viewBoxStringStream;
264 
265  viewBoxStringStream << getBoundingBox().getLeft();
266  viewBoxStringStream << " ";
267  viewBoxStringStream << getBoundingBox().getBottom();
268  viewBoxStringStream << " ";
269  viewBoxStringStream << getBoundingBox().getWidth();
270  viewBoxStringStream << " ";
271  viewBoxStringStream << getBoundingBox().getHeight();
272 
273  AttributeMap variableAttributeMap{
274  {"height", std::to_string(getCanvasHeight())},
275  {"width", std::to_string(getCanvasWidth())},
276  {"viewBox", viewBoxStringStream.str()},
277  };
278 
279  variableAttributeMap.insert(m_svgAttributes.begin(), m_svgAttributes.end());
280 
281  writeOpeningTag(outputFileStream, "svg", standardAttributeMap, variableAttributeMap);
282 
283  writeSVGDefs(outputFileStream);
284 
285  // Copy the stream the output and rewind it to its original position.
286  outputFileStream << m_svgContentStream.str();
287 
288  writeClosingTag(outputFileStream, "svg");
289 
290  outputFileStream.close();
291  m_nIndentationSpaces = savedNIndentationSpaces;
292 
293  return fileName;
294 }
295 
297 {
299  m_svgContentStream.str("");
301 }
302 
304 {
306 }
307 
309 {
311 }
312 
313 void SVGPrimitivePlotter::writeSVGHeader(std::ostream& outputStream)
314 {
315  outputStream << "<?xml version=\"1.0\" ?>" << std::endl;
316  outputStream << "<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>" << std::endl;
317 }
318 
319 void SVGPrimitivePlotter::writeSVGDefs(std::ostream& outputStream)
320 {
321  writeOpeningTag(outputStream, "defs");
322 
323  AttributeMap markerAttributes {
324  { "id", "endArrow"},
325  { "viewBox", "0 0 10 10"},
326  { "refX", "1"},
327  { "refY", "5"},
328  { "markerUnits", "strokeWidth"},
329  { "markerWidth", "4"},
330  { "markerHeight", "3"},
331  {"orient", "auto"}
332  };
333 
334  writeOpeningTag(outputStream, "marker", markerAttributes);
335 
336 
337  AttributeMap polylineAttributes {
338  {"points", "0,0 10,5 0,10 1,5"},
339  {"fill", "black"}
340  };
341 
342  writeStandAloneTag(outputStream, "polyline", polylineAttributes);
343 
344  writeClosingTag(outputStream, "marker");
345  writeClosingTag(outputStream, "defs");
346 }
347 
348 void SVGPrimitivePlotter::writeOpeningTag(std::ostream& outputStream,
349  const std::string& tagName,
350  const AttributeMap& geometryAttributeMap,
351  const AttributeMap& styleAttributeMap)
352 {
353  // Indentation
354  outputStream << std::string(m_nIndentationSpaces, ' ');
355 
356  // Opening braket
357  outputStream << '<';
358 
359  // Write contained part
360  writeTagIntern(outputStream, tagName, geometryAttributeMap, styleAttributeMap);
361 
362  // Closing bracket
363  outputStream << '>';
364 
365  // New line
366  outputStream << std::endl;
367 
368  indent();
369 
370  if (geometryAttributeMap.count("_showAt") + styleAttributeMap.count("_showAt")) {
371  const std::string showAt = geometryAttributeMap.count("_showAt")
372  ? geometryAttributeMap.at("_showAt")
373  : styleAttributeMap.at("_showAt");
374 
375  AttributeMap setAttributeMap{
376  {"attributeName", "visibility"},
377  {"to", "hidden"},
378  {"begin", "0s"},
379  {"end", showAt}
380  };
381 
382  writeStandAloneTag(outputStream, "set", setAttributeMap);
383  }
384 }
385 
386 void SVGPrimitivePlotter::writeStandAloneTag(std::ostream& outputStream,
387  const std::string& tagName,
388  const AttributeMap& geometryAttributeMap,
389  const AttributeMap& styleAttributeMap)
390 {
391  // Special handling for _showAt attribute, which introduces a contained <set> tag
392  if (geometryAttributeMap.count("_showAt") + styleAttributeMap.count("_showAt")) {
393  writeOpeningTag(outputStream, tagName, geometryAttributeMap, styleAttributeMap);
394  writeClosingTag(outputStream, tagName);
395  } else {
396 
397  // Indentation
398  outputStream << std::string(m_nIndentationSpaces, ' ');
399 
400  // Opening braket
401  outputStream << '<';
402 
403  // Write contained part
404  writeTagIntern(outputStream, tagName, geometryAttributeMap, styleAttributeMap);
405 
406  // Closing bracket
407  outputStream << '/' << '>';
408 
409  // New line
410  outputStream << std::endl;
411  }
412 }
413 
414 void SVGPrimitivePlotter::writeTagIntern(std::ostream& outputStream,
415  const std::string& tagName,
416  const AttributeMap& geometryAttributeMap,
417  const AttributeMap& styleAttributeMap)
418 {
419  // Tag name
420  outputStream << tagName;
421 
422  // First attribute map
423  if (not geometryAttributeMap.empty()) {
424  outputStream << ' ';
425  writeAttributes(outputStream, geometryAttributeMap);
426  }
427 
428  // Second attribute map
429  if (not styleAttributeMap.empty()) {
430  outputStream << ' ';
431  writeAttributes(outputStream, styleAttributeMap);
432  }
433 }
434 
435 void SVGPrimitivePlotter::writeAttributes(std::ostream& outputStream,
436  const AttributeMap& attributeMap)
437 {
438  bool first = true;
439  for (const std::pair<std::string, std::string> attribute : attributeMap) {
440 
441  const std::string& key = attribute.first;
442  const std::string& value = attribute.second;
443 
444  // Skip special attribute
445  if ('_' == key[0]) {
446  continue;
447  }
448 
449  // Introduce a space between every attribute
450  if (first) {
451  first = false;
452  } else {
453  outputStream << ' ';
454  }
455 
456  outputStream << key;
457  outputStream << '=' << '"';
458  outputStream << value;
459  outputStream << '"';
460  }
461 }
462 
463 void SVGPrimitivePlotter::writeClosingTag(std::ostream& outputStream, const std::string& tagName)
464 {
465  dedent();
466 
467  // Indentation
468  outputStream << std::string(m_nIndentationSpaces, ' ');
469 
470  // Opening braket
471  outputStream << '<' << '/';
472 
473  // Tag name
474  outputStream << tagName;
475 
476  // Closing bracket
477  outputStream << '>';
478 
479  // New line
480  outputStream << std::endl;
481 }
float getBottom() const
Getter for the location of the bottom of the bounding box rectangle (lower y bound)....
Definition: BoundingBox.h:75
float getWidth() const
Getter for the width of the bounding box rectangle.
Definition: BoundingBox.h:63
float getLeft() const
Getter for the location of the left of the bounding box rectangle (lower x bound)....
Definition: BoundingBox.h:71
float getHeight() const
Getter for the height of the bounding box rectangle.
Definition: BoundingBox.h:67
A base class for plots of primitive objects.
float getCanvasWidth()
Getter for the canvas width in pixels.
virtual void drawCircleArc(float startX, float startY, float endX, float endY, float radius, bool longArc, bool sweepFlag, const AttributeMap &attributeMap=AttributeMap())
Adds a circle arc to the plot.
virtual void drawArrow(float startX, float startY, float endX, float endY, const AttributeMap &attributeMap=AttributeMap())
Adds an arrow to the plot.
Belle2::TrackFindingCDC::AttributeMap AttributeMap
A map type for attributes names to values for additional drawing information.
float getCanvasHeight()
Getter for the canvas height in pixels.
virtual void drawCurve(const std::vector< std::array< float, 2 >> &points, const std::vector< std::array< float, 2 >> &tangents, const AttributeMap &attributeMap=AttributeMap())
Adds a smooth curve to the plot.
virtual void clear()
Clears all drawed elements from the plotter.
virtual void drawLine(float startX, float startY, float endX, float endY, const AttributeMap &attributeMap=AttributeMap())
Adds a line to the plot.
const BoundingBox & getBoundingBox() const
Getter for the bounding box of all drawed objects.
virtual void drawCircle(float centerX, float centerY, float radius, const AttributeMap &attributeMap=AttributeMap())
Adds a circle to the plot.
A concrete plotter that can draw primitive objects to standalone SVG files.
void startGroup(const AttributeMap &attributeMap=AttributeMap()) override
Indicates the start of a group of drawn elements.
std::unique_ptr< PrimitivePlotter > clone() const override
Returns a newly created plotter instance containing all information of this.
void indent()
Increases the current indention by one.
void drawCurve(const std::vector< std::array< float, 2 >> &points, const std::vector< std::array< float, 2 >> &tangents, const AttributeMap &attributeMap=AttributeMap()) override
Adds a smooth curve to the plot.
void writeStandAloneTag(std::ostream &outputStream, const std::string &tagName, const AttributeMap &geometryAttributeMap=AttributeMap(), const AttributeMap &styleAttributeMap=AttributeMap())
Writes a stand alone xml tag to the given output stream taking attributes from two sources.
std::ostringstream m_svgContentStream
Memory for the plotted elements. This contains only the fragment that is inbetween the svg tags and c...
int m_nIndentationSpaces
Memory for the number of spaces that shall be prepended to each line.
void writeSVGDefs(std::ostream &outputStream)
Writes a preamble of definitions that define an arrow cap which can be referenced by lines.
static const int s_defaultNIndentationSpaces
Constant for the number of indention space to be used within the svg block.
AttributeMap m_svgAttributes
Memory for additional attributes to the toplevel svg element.
void drawCircleArc(float startX, float startY, float endX, float endY, float radius, bool longArc, bool sweepFlag, const AttributeMap &attributeMap=AttributeMap()) override
Adds a circle arc to the plot.
void drawCircle(float centerX, float centerY, float radius, const AttributeMap &attributeMap=AttributeMap()) override
Adds a circle to the plot.
SVGPrimitivePlotter()
Default constructor for ROOT compatibility.
void writeSVGHeader(std::ostream &outputStream)
Writes the xml header that indicates that this document will be a SVG document to the given output st...
void drawLine(float startX, float startY, float endX, float endY, const AttributeMap &attributeMap=AttributeMap()) override
Adds a line to the plot.
void drawArrow(float startX, float startY, float endX, float endY, const AttributeMap &attributeMap=AttributeMap()) override
Adds an arrow to the plot.
void writeOpeningTag(std::ostream &outputStream, const std::string &tagName, const AttributeMap &geometryAttributeMap=AttributeMap(), const AttributeMap &styleAttributeMap=AttributeMap())
Writes an opening xml tag to the given output stream taking attributes from two sources.
void dedent()
Decreases the current indention by one.
void clear() override
Clears all drawed elements from the plotter.
static const int s_addtionalNIndentationSpaces
Constant for the additional number of space to be prepended with each open tag group.
const std::string save(const std::string &fileName) override
Saves the current plot state to a file.
void writeTagIntern(std::ostream &outputStream, const std::string &tagName, const AttributeMap &geometryAttributeMap=AttributeMap(), const AttributeMap &styleAttributeMap=AttributeMap())
Writes part that belongs between the <, > brakets.
void endGroup() override
Indicates the end of a group of drawn elements.
void writeClosingTag(std::ostream &outputStream, const std::string &tagName)
Writes a closing xml tag to the given output stream.
double sqrt(double a)
sqrt for double
Definition: beamHelpers.h:28
Abstract base class for different kinds of events.