10 #include <tracking/trackFindingCDC/display/SVGPrimitivePlotter.h>
12 #include <framework/logging/Logger.h>
17 using namespace TrackFindingCDC;
24 , m_svgContentStream()
25 , m_nIndentationSpaces(s_defaultNIndentationSpaces)
32 , m_svgContentStream()
33 , m_nIndentationSpaces(s_defaultNIndentationSpaces)
34 , m_svgAttributes(svgAttributes)
40 , m_svgContentStream(plotter.m_svgContentStream.str(), std::ostringstream::ate)
41 , m_nIndentationSpaces(plotter.m_nIndentationSpaces)
42 , m_svgAttributes(plotter.m_svgAttributes)
48 return std::make_unique<SVGPrimitivePlotter>(*
this);
60 {
"x1", std::to_string(startX)},
61 {
"x2", std::to_string(endX)},
62 {
"y1", std::to_string(startY)},
63 {
"y2", std::to_string(endY)}
80 {
"x1", std::to_string(startX)},
81 {
"x2", std::to_string(endX)},
82 {
"y1", std::to_string(startY)},
83 {
"y2", std::to_string(endY)},
84 {
"marker-end" ,
"url(#endArrow)"}
99 {
"cx", std::to_string(centerX)},
100 {
"cy", std::to_string(centerY)},
101 {
"r", std::to_string(std::fabs(radius))}
131 const float radiusX = std::fabs(radius);
132 const float radiusY = std::fabs(radius);
133 const float rotationAngle = 0;
135 std::ostringstream pathStream;
137 pathStream <<
"M" <<
' ';
138 pathStream << std::to_string(startX) <<
' ';
139 pathStream << std::to_string(startY) <<
' ';
140 pathStream <<
"A" <<
' ';
141 pathStream << std::to_string(radiusX) <<
' ';
142 pathStream << std::to_string(radiusY) <<
' ';
143 pathStream << std::to_string(rotationAngle) <<
' ';
144 pathStream << std::to_string(longArc) <<
' ';
145 pathStream << std::to_string(sweepFlag) <<
' ';
146 pathStream << std::to_string(endX) <<
' ';
147 pathStream << std::to_string(endY);
149 AttributeMap geometryAttributeMap{{
"d", pathStream.str()}};
155 const std::vector<std::array<float, 2>>& tangents,
159 static const double k = 4.0 / 3.0 * (std::sqrt(2.0) - 1.0);
161 B2ASSERT(
"Expect number of points and tangents to be the same", points.size() == tangents.size());
162 if (points.size() < 2)
return;
166 std::ostringstream pathStream;
169 float startX = std::get<0>(points[0]);
170 float startY = std::get<1>(points[0]);
173 pathStream <<
' ' << std::to_string(startX);
174 pathStream <<
' ' << std::to_string(startY);
176 for (
size_t iCurrent = 0, iNext = 1; iNext < points.size(); iCurrent = iNext, ++iNext) {
178 float currentTX = std::get<0>(tangents[iCurrent]);
179 float currentTY = std::get<1>(tangents[iCurrent]);
181 float nextTX = std::get<0>(tangents[iNext]);
182 float nextTY = std::get<1>(tangents[iNext]);
184 float currentT = std::hypot(currentTX, currentTY);
185 float nextT = std::hypot(nextTX, nextTY);
187 currentTX /= currentT;
188 currentTY /= currentT;
192 float currentX = std::get<0>(points[iCurrent]);
193 float currentY = std::get<1>(points[iCurrent]);
195 float nextX = std::get<0>(points[iNext]);
196 float nextY = std::get<1>(points[iNext]);
199 float alpha = std::atan2(currentTX * nextTY - currentTY * nextTX,
200 currentTX * nextTX + currentTY * nextTY);
202 float distance = std::hypot(currentX - nextX, currentY - nextY);
203 float controlLength = k * distance / 2 / std::cos(alpha / 2);
205 float currentControlX = currentX + currentTX * controlLength;
206 float currentControlY = currentY + currentTY * controlLength;
208 float nextControlX = nextX - nextTX * controlLength;
209 float nextControlY = nextY - nextTY * controlLength;
211 pathStream <<
' ' <<
"C";
213 pathStream <<
' ' << std::to_string(currentControlX);
214 pathStream <<
' ' << std::to_string(currentControlY);
216 pathStream <<
' ' << std::to_string(nextControlX);
217 pathStream <<
' ' << std::to_string(nextControlY);
219 pathStream <<
' ' << std::to_string(nextX);
220 pathStream <<
' ' << std::to_string(nextY);
223 AttributeMap geometryAttributeMap{{
"d", pathStream.str()}};
242 B2WARNING(
"Mismatching calls to startGroup and endGroup detected. "
243 <<
"Proceeding to write the illforamed result.");
249 std::ofstream outputFileStream;
250 outputFileStream.open(fileName);
255 {
"baseProfile",
"full"},
256 {
"ev",
"http://www.w3.org/2001/xml-events"},
258 {
"xlink",
"http://www.w3.org/1999/xlink"},
259 {
"xmlns",
"http://www.w3.org/2000/svg"}
265 std::ostringstream viewBoxStringStream;
268 viewBoxStringStream <<
" ";
270 viewBoxStringStream <<
" ";
272 viewBoxStringStream <<
" ";
278 {
"viewBox", viewBoxStringStream.str()},
283 writeOpeningTag(outputFileStream,
"svg", standardAttributeMap, variableAttributeMap);
292 outputFileStream.close();
317 outputStream <<
"<?xml version=\"1.0\" ?>" << std::endl;
318 outputStream <<
"<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>" << std::endl;
327 {
"viewBox",
"0 0 10 10"},
330 {
"markerUnits",
"strokeWidth"},
331 {
"markerWidth",
"4"},
332 {
"markerHeight",
"3"},
340 {
"points",
"0,0 10,5 0,10 1,5"},
351 const std::string& tagName,
362 writeTagIntern(outputStream, tagName, geometryAttributeMap, styleAttributeMap);
368 outputStream << std::endl;
372 if (geometryAttributeMap.count("_showAt") + styleAttributeMap.count("_showAt")) {
373 const std::string showAt = geometryAttributeMap.count("_showAt")
374 ? geometryAttributeMap.at("_showAt")
375 : styleAttributeMap.at("_showAt");
377 AttributeMap setAttributeMap{
378 {"attributeName", "visibility"},
384 writeStandAloneTag(outputStream, "set", setAttributeMap);
388 void SVGPrimitivePlotter::writeStandAloneTag(std::ostream& outputStream,
389 const std::string& tagName,
390 const AttributeMap& geometryAttributeMap,
391 const AttributeMap& styleAttributeMap)
393 // Special handling for _showAt attribute, which introduces a contained <set> tag
394 if (geometryAttributeMap.count("_showAt") + styleAttributeMap.count("_showAt")) {
395 writeOpeningTag(outputStream, tagName, geometryAttributeMap, styleAttributeMap);
396 writeClosingTag(outputStream, tagName);
400 outputStream << std::string(m_nIndentationSpaces, ' ');
405 // Write contained part
406 writeTagIntern(outputStream, tagName, geometryAttributeMap, styleAttributeMap);
409 outputStream << '/
' << '>
';
412 outputStream << std::endl;
416 void SVGPrimitivePlotter::writeTagIntern(std::ostream& outputStream,
417 const std::string& tagName,
418 const AttributeMap& geometryAttributeMap,
419 const AttributeMap& styleAttributeMap)
422 outputStream << tagName;
424 // First attribute map
425 if (not geometryAttributeMap.empty()) {
427 writeAttributes(outputStream, geometryAttributeMap);
430 // Second attribute map
431 if (not styleAttributeMap.empty()) {
433 writeAttributes(outputStream, styleAttributeMap);
437 void SVGPrimitivePlotter::writeAttributes(std::ostream& outputStream,
438 const AttributeMap& attributeMap)
441 for (const std::pair<std::string, std::string>& attribute : attributeMap) {
443 const std::string& key = attribute.first;
444 const std::string& value = attribute.second;
446 // Skip special attribute
451 // Introduce a space between every attribute
459 outputStream << '=
' << '"';
460 outputStream << value;
465 void SVGPrimitivePlotter::writeClosingTag(std::ostream& outputStream, const std::string& tagName)
470 outputStream << std::string(m_nIndentationSpaces, ' ');
473 outputStream << '<
' << '/
';
476 outputStream << tagName;
482 outputStream << std::endl;