• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CyberZHG / SVGDiagram / 20497231656

25 Dec 2025 02:02AM UTC coverage: 99.107% (+0.06%) from 99.048%
20497231656

push

github

web-flow
Add svg ribbon demo (#40)

1997 of 2015 relevant lines covered (99.11%)

1930.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.48
/src/svg_diagram.cpp
1
#include "svg_diagram.h"
2

3
#include <format>
4
#include <fstream>
5
#include <unordered_set>
6
using namespace std;
7
using namespace svg_diagram;
8

9
void SVGDiagram::enableDebug() {
25✔
10
    _enabledDebug = true;
25✔
11
}
25✔
12

13
void SVGDiagram::clearSVGDraw() {
12✔
14
    _svgDraws.clear();
12✔
15
}
12✔
16

17
void SVGDiagram::addSVGDraw(std::unique_ptr<SVGDraw> svgDraw) {
17✔
18
    _svgDraws.emplace_back(std::move(svgDraw));
17✔
19
}
17✔
20

21
void SVGDiagram::addSVGDraw(std::vector<std::unique_ptr<SVGDraw>>& svgDraws) {
1✔
22
    for (auto& svgDraw : svgDraws) {
3✔
23
        _svgDraws.emplace_back(std::move(svgDraw));
2✔
24
    }
25
}
1✔
26

27
void SVGDiagram::setCanvasSize(const int width, const int height) {
2✔
28
    _width = width;
2✔
29
    _height = height;
2✔
30
}
2✔
31

32
void SVGDiagram::setBackgroundColor(const std::string& backgroundColor) {
22✔
33
    _backgroundColor = backgroundColor;
22✔
34
}
22✔
35

36
void SVGDiagram::setFixedViewBox(const double x0, const double y0, const double width, const double height) {
3✔
37
    _viewBoxX = x0;
3✔
38
    _viewBoxY = y0;
3✔
39
    _width = width;
3✔
40
    _height = height;
3✔
41
    _isFixedViewBox = true;
3✔
42
}
3✔
43

44
void SVGDiagram::setRotation(const double angle) {
2✔
45
    _rotation = angle;
2✔
46
    _hasRotationCenter = false;
2✔
47
}
2✔
48

49
void SVGDiagram::setRotation(const double angle, const double cx, const double cy) {
1✔
50
    _rotation = angle;
1✔
51
    _rotationCX = cx;
1✔
52
    _rotationCY = cy;
1✔
53
    _hasRotationCenter = true;
1✔
54
}
1✔
55

56
SVGNode& SVGDiagram::defaultNodeAttributes() {
5✔
57
    return _graph.defaultNodeAttributes();
5✔
58
}
59

60
SVGEdge& SVGDiagram::defaultEdgeAttributes() {
8✔
61
    return _graph.defaultEdgeAttributes();
8✔
62
}
63

64
const shared_ptr<SVGNode>& SVGDiagram::addNode(const string& id) {
223✔
65
    if (_nodes.contains(id)) {
223✔
66
        throw runtime_error("SVGDiagram::addNode: Node ID already exists");
1✔
67
    }
68
    auto node = make_shared<SVGNode>();
222✔
69
    node->setID(id);
222✔
70
    _graph.addNode(node);
222✔
71
    return _nodes[id] = node;
444✔
72
}
222✔
73

74
void SVGDiagram::addNode(const string& id, shared_ptr<SVGNode>& node) {
60✔
75
    if (_nodes.contains(id)) {
60✔
76
        throw runtime_error("SVGDiagram::addNode: Node ID already exists");
1✔
77
    }
78
    node->setID(id);
59✔
79
    _nodes[id] = node;
59✔
80
    _graph.addNode(node);
59✔
81
}
59✔
82

83
const shared_ptr<SVGEdge>& SVGDiagram::addEdge(const string& id) {
483✔
84
    if (_edges.contains(id)) {
483✔
85
        throw runtime_error("SVGDiagram::addEdge: Edge ID already exists");
1✔
86
    }
87
    auto edge = make_shared<SVGEdge>();
482✔
88
    edge->setID(id);
482✔
89
    _graph.addEdge(edge);
482✔
90
    return _edges[id] = edge;
964✔
91
}
482✔
92

93
const shared_ptr<SVGEdge>& SVGDiagram::addEdge(const string& from, const string& to) {
481✔
94
    const auto id = newEdgeId();
481✔
95
    const auto& edge = addEdge(id);
481✔
96
    edge->setConnection(from, to);
481✔
97
    return edge;
481✔
98
}
481✔
99

100
void SVGDiagram::addEdge(const string& id, shared_ptr<SVGEdge>& edge) {
30✔
101
    if (_edges.contains(id)) {
30✔
102
        throw runtime_error("SVGDiagram::addEdge: Edge ID already exists");
1✔
103
    }
104
    edge->setID(id);
29✔
105
    _edges[id] = edge;
29✔
106
    _graph.addEdge(edge);
29✔
107
}
29✔
108

109
void SVGDiagram::addEdge(shared_ptr<SVGEdge>& edge) {
27✔
110
    const auto id = newEdgeId();
27✔
111
    addEdge(id, edge);
27✔
112
}
27✔
113

114
const shared_ptr<SVGGraph>& SVGDiagram::addSubgraph(const string& id) {
12✔
115
    if (_subgraphs.contains(id)) {
12✔
116
        throw runtime_error("SVGDiagram::addSubgraph: Subgraph ID already exists");
1✔
117
    }
118
    auto graph = make_shared<SVGGraph>();
11✔
119
    graph->setID(id);
11✔
120
    _graph.addSubgraph(graph);
11✔
121
    return _subgraphs[id] = graph;
22✔
122
}
11✔
123

124
void SVGDiagram::addSubgraph(const string& id, shared_ptr<SVGGraph>& subgraph) {
2✔
125
    if (_subgraphs.contains(id)) {
2✔
126
        throw runtime_error("SVGDiagram::addSubgraph: Subgraph ID already exists");
1✔
127
    }
128
    subgraph->setID(id);
1✔
129
    _subgraphs[id] = subgraph;
1✔
130
    _graph.addSubgraph(subgraph);
1✔
131
}
1✔
132

133
string SVGDiagram::render() {
176✔
134
    _graph.setAttributeIfNotExist(ATTR_KEY_ID, "graph0");
352✔
135
    produceSVGDrawsDynamic();
176✔
136
    const auto [svgElement, gElement] = generateSVGElement();
176✔
137
    unordered_set<string> singletonNames;
176✔
138
    for (const auto& draw : _svgDraws) {
214✔
139
        gElement->addChildren(draw->generateXMLElements());
38✔
140
    }
141
    for (const auto& draw : _svgDrawsDynamic) {
2,060✔
142
        gElement->addChildren(draw->generateXMLElements());
1,884✔
143
    }
144
    return svgElement->toString();
352✔
145
}
176✔
146

147
void SVGDiagram::render(const string &filePath) {
107✔
148
    ofstream file(filePath);
107✔
149
    file << render();
107✔
150
    file.close();
107✔
151
}
107✔
152

153
string SVGDiagram::newEdgeId() {
508✔
154
    string newEdgeId;
508✔
155
    while (true) {
156
        newEdgeId = format("edge{}", _edgeIndex++);
508✔
157
        if (!_edges.contains(newEdgeId)) {
508✔
158
            break;
508✔
159
        }
160
    }
161
    return newEdgeId;
508✔
162
}
×
163

164
void SVGDiagram::produceSVGDrawsDynamic() {
176✔
165
    if (_enabledDebug) {
176✔
166
        _graph.enableDebug();
43✔
167
    }
168
    for (const auto& node : _graph.findNodes()) {
552✔
169
        if (!_nodes.contains(node->id())) {
376✔
170
            _nodes[node->id()] = node;
11✔
171
        }
172
    }
176✔
173
    _svgDrawsDynamic = _graph.produceSVGDraws(_nodes);
176✔
174
}
176✔
175

176
pair<XMLElement::ChildType, XMLElement::ChildType> SVGDiagram::generateSVGElement() const {
176✔
177
    double width = _width, height = _height;
176✔
178
    double minX = 0.0, maxX = 0.0, minY = 0.0, maxY = 0.0;
176✔
179
    bool firstEntity = true;
176✔
180
    const auto updateMinMax = [&](const std::vector<std::unique_ptr<SVGDraw>>& svgDraws) {
352✔
181
        for (const auto& draw : svgDraws) {
2,274✔
182
            if (draw->hasEntity()) {
1,922✔
183
                const auto boundingBox = draw->boundingBox();
987✔
184
                if (firstEntity) {
987✔
185
                    firstEntity = false;
169✔
186
                    minX = boundingBox.x1;
169✔
187
                    minY = boundingBox.y1;
169✔
188
                    maxX = boundingBox.x2;
169✔
189
                    maxY = boundingBox.y2;
169✔
190
                } else {
191
                    minX = min(minX, boundingBox.x1);
818✔
192
                    minY = min(minY, boundingBox.y1);
818✔
193
                    maxX = max(maxX, boundingBox.x2);
818✔
194
                    maxY = max(maxY, boundingBox.y2);
818✔
195
                }
196
            }
197
        }
198
    };
352✔
199
    updateMinMax(_svgDraws);
176✔
200
    updateMinMax(_svgDrawsDynamic);
176✔
201
    double translateX = _margin.first - minX;
176✔
202
    double translateY = _margin.second - minY;
176✔
203
    if (width == 0.0) {
176✔
204
        width = maxX - minX + _margin.first * 2.0;
166✔
205
    } else {
206
        translateX = (width - maxX - minX) / 2.0;
10✔
207
    }
208
    if (height == 0.0) {
176✔
209
        height = maxY - minY + _margin.second * 2.0;
166✔
210
    } else {
211
        translateY = (height - maxY - minY) / 2.0;
10✔
212
    }
213
    const auto svgElement = make_shared<XMLElement>("svg");
176✔
214
    svgElement->addAttribute("width", width);
352✔
215
    svgElement->addAttribute("height", height);
352✔
216
    if (_isFixedViewBox) {
176✔
217
        svgElement->addAttribute("viewBox", format("{} {} {} {}", _viewBoxX, _viewBoxY, width, height));
18✔
218
    } else {
219
        svgElement->addAttribute("viewBox", format("0 0 {} {}", width, height));
510✔
220
    }
221
    svgElement->addAttribute("xmlns", "http://www.w3.org/2000/svg");
704✔
222
    svgElement->addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
704✔
223
    svgElement->addChild(make_shared<XMLElementComment>("Generated by: https://github.com/CyberZHG/SVGDiagram"));
176✔
224
    const auto gElement = make_shared<XMLElement>("g");
176✔
225
    gElement->addAttribute("id", "graph0");
704✔
226
    gElement->addAttribute("class", "graph");
704✔
227
    string rotation;
176✔
228
    if (_rotation != 0.0) {
176✔
229
        double cx = _rotationCX, cy = _rotationCY;
6✔
230
        if (!_hasRotationCenter) {
6✔
231
            if (_isFixedViewBox) {
4✔
232
                cx = _viewBoxX + width / 2.0;
2✔
233
                cy = _viewBoxY + height / 2.0;
2✔
234
            } else {
235
                cx = (minX + maxX) / 2.0;
2✔
236
                cy = (minY + maxY) / 2.0;
2✔
237
            }
238
        }
239
        rotation = format(" rotate({},{},{})", _rotation, cx, cy);
6✔
240
    }
241
    if (_isFixedViewBox) {
176✔
242
        gElement->addAttribute("transform", format("scale(1.0){}", rotation));
18✔
243
    } else {
244
        gElement->addAttribute("transform", format("translate({},{}) scale(1.0){}", translateX, translateY, rotation));
510✔
245
    }
246
    if (!_backgroundColor.empty()) {
176✔
247
        const auto rectElement = make_shared<XMLElement>("rect");
38✔
248
        rectElement->addAttribute("x", -translateX);
76✔
249
        rectElement->addAttribute("y", -translateY);
76✔
250
        rectElement->addAttribute("width", width);
76✔
251
        rectElement->addAttribute("height", height);
76✔
252
        rectElement->addAttribute("fill", _backgroundColor);
76✔
253
        gElement->addChild(rectElement);
38✔
254
    }
38✔
255
    svgElement->addChild(gElement);
176✔
256
    return {svgElement, gElement};
352✔
257
}
176✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc