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

CyberZHG / SVGDiagram / 20093014786

10 Dec 2025 09:01AM UTC coverage: 95.382% (-1.6%) from 96.996%
20093014786

Pull #4

github

web-flow
Merge e0dfefada into 68adacba8
Pull Request #4: Add subgraph for default group attributes

183 of 207 new or added lines in 2 files covered. (88.41%)

10 existing lines in 2 files now uncovered.

1487 of 1559 relevant lines covered (95.38%)

1072.7 hits per line

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

89.51
/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() {
9✔
10
    _enabledDebug = true;
9✔
11
}
9✔
12

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

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

21
void SVGDiagram::addSVGDraw(std::vector<std::unique_ptr<SVGDraw>> svgDraws) {
×
22
    for (auto& svgDraw : svgDraws) {
×
23
        _svgDraws.emplace_back(std::move(svgDraw));
×
24
    }
25
}
×
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) {
14✔
33
    _backgroundColor = backgroundColor;
14✔
34
}
14✔
35

36
SVGNode& SVGDiagram::defaultNodeAttributes() {
5✔
37
    return _graph.defaultNodeAttributes();
5✔
38
}
39

40
SVGEdge& SVGDiagram::defaultEdgeAttributes() {
8✔
41
    return _graph.defaultEdgeAttributes();
8✔
42
}
43

44
const shared_ptr<SVGNode>& SVGDiagram::addNode(const string& id) {
55✔
45
    if (_nodes.contains(id)) {
55✔
NEW
46
        throw runtime_error("SVGDiagram::addNode: Node ID already exists");
×
47
    }
48
    auto node = make_shared<SVGNode>();
55✔
49
    node->setID(id);
55✔
50
    _graph.addNode(node);
55✔
51
    return _nodes[id] = node;
110✔
52
}
55✔
53

54
void SVGDiagram::addNode(const string& id, shared_ptr<SVGNode>& node) {
49✔
55
    if (_nodes.contains(id)) {
49✔
NEW
56
        throw runtime_error("SVGDiagram::addNode: Node ID already exists");
×
57
    }
58
    node->setID(id);
49✔
59
    _nodes[id] = node;
49✔
60
    _graph.addNode(node);
49✔
61
}
49✔
62

63
const shared_ptr<SVGEdge>& SVGDiagram::addEdge(const string& id) {
117✔
64
    if (_edges.contains(id)) {
117✔
NEW
65
        throw runtime_error("SVGDiagram::addEdge: Edge ID already exists");
×
66
    }
67
    auto edge = make_shared<SVGEdge>();
117✔
68
    edge->setID(id);
117✔
69
    _graph.addEdge(edge);
117✔
70
    return _edges[id] = edge;
234✔
71
}
117✔
72

73
const shared_ptr<SVGEdge>& SVGDiagram::addEdge(const string& from, const string& to) {
117✔
74
    const auto id = newEdgeId();
117✔
75
    const auto& edge = addEdge(id);
117✔
76
    edge->setConnection(from, to);
117✔
77
    return edge;
117✔
78
}
117✔
79

80
void SVGDiagram::addEdge(const string& id, shared_ptr<SVGEdge>& edge) {
24✔
81
    if (_edges.contains(id)) {
24✔
NEW
82
        throw runtime_error("SVGDiagram::addEdge: Edge ID already exists");
×
83
    }
84
    edge->setID(id);
24✔
85
    _edges[id] = edge;
24✔
86
    _graph.addEdge(edge);
24✔
87
}
24✔
88

89
void SVGDiagram::addEdge(shared_ptr<SVGEdge>& edge) {
22✔
90
    const auto id = newEdgeId();
22✔
91
    addEdge(id, edge);
22✔
92
}
22✔
93

94
const shared_ptr<SVGGraph>& SVGDiagram::addSubgraph(const string& id) {
2✔
95
    if (_subgraphs.contains(id)) {
2✔
NEW
96
        throw runtime_error("SVGDiagram::addSubgraph: Subgraph ID already exists");
×
97
    }
98
    auto graph = make_shared<SVGGraph>();
2✔
99
    graph->setID(id);
2✔
100
    _graph.addSubgraph(graph);
2✔
101
    return _subgraphs[id] = graph;
4✔
102
}
2✔
103

NEW
104
void SVGDiagram::addSubgraph(const string& id, shared_ptr<SVGGraph>& subgraph) {
×
NEW
105
    if (_subgraphs.contains(id)) {
×
NEW
106
        throw runtime_error("SVGDiagram::addSubgraph: Subgraph ID already exists");
×
107
    }
NEW
108
    subgraph->setID(id);
×
NEW
109
    _subgraphs[id] = subgraph;
×
NEW
110
    _graph.addSubgraph(subgraph);
×
UNCOV
111
}
×
112

113
string SVGDiagram::render() {
84✔
114
    produceSVGDrawsDynamic();
84✔
115
    const auto [svgElement, gElement] = generateSVGElement();
84✔
116
    unordered_set<string> singletonNames;
84✔
117
    for (const auto& draw : _svgDraws) {
112✔
118
        gElement->addChildren(draw->generateXMLElements());
28✔
119
    }
120
    for (const auto& draw : _svgDrawsDynamic) {
744✔
121
        gElement->addChildren(draw->generateXMLElements());
660✔
122
    }
123
    return svgElement->toString();
168✔
124
}
84✔
125

126
void SVGDiagram::render(const string &filePath) {
45✔
127
    ofstream file(filePath);
45✔
128
    file << render();
45✔
129
    file.close();
45✔
130
}
45✔
131

132
string SVGDiagram::newEdgeId() {
139✔
133
    string newEdgeId;
139✔
134
    while (true) {
135
        newEdgeId = format("edge{}", _edgeIndex++);
139✔
136
        if (!_edges.contains(newEdgeId)) {
139✔
137
            break;
139✔
138
        }
139
    }
140
    return newEdgeId;
139✔
141
}
×
142

143
void SVGDiagram::produceSVGDrawsDynamic() {
84✔
144
    if (_enabledDebug) {
84✔
145
        _graph.enableDebug();
18✔
146
    }
147
    for (const auto& node : _graph.findNodes()) {
243✔
148
        if (!_nodes.contains(node->id())) {
159✔
149
            _nodes[node->id()] = node;
6✔
150
        }
151
    }
84✔
152
    _svgDrawsDynamic = _graph.produceSVGDraws(_nodes);
84✔
153
}
84✔
154

155
pair<XMLElement::ChildType, XMLElement::ChildType> SVGDiagram::generateSVGElement() const {
84✔
156
    double width = _width, height = _height;
84✔
157
    double minX = 0.0, maxX = 0.0, minY = 0.0, maxY = 0.0;
84✔
158
    bool firstEntity = true;
84✔
159
    const auto updateMinMax = [&](const std::vector<std::unique_ptr<SVGDraw>>& svgDraws) {
168✔
160
        for (const auto& draw : svgDraws) {
856✔
161
            if (draw->hasEntity()) {
688✔
162
                const auto boundingBox = draw->boundingBox();
356✔
163
                if (firstEntity) {
356✔
164
                    firstEntity = false;
80✔
165
                    minX = boundingBox.x1;
80✔
166
                    minY = boundingBox.y1;
80✔
167
                    maxX = boundingBox.x2;
80✔
168
                    maxY = boundingBox.y2;
80✔
169
                } else {
170
                    minX = min(minX, boundingBox.x1);
276✔
171
                    minY = min(minY, boundingBox.y1);
276✔
172
                    maxX = max(maxX, boundingBox.x2);
276✔
173
                    maxY = max(maxY, boundingBox.y2);
276✔
174
                }
175
            }
176
        }
177
    };
168✔
178
    updateMinMax(_svgDraws);
84✔
179
    updateMinMax(_svgDrawsDynamic);
84✔
180
    double translateX = _margin.first - minX;
84✔
181
    double translateY = _margin.second - minY;
84✔
182
    if (width == 0.0) {
84✔
183
        width = maxX - minX + _margin.first * 2.0;
80✔
184
    } else {
185
        translateX = (width - maxX - minX) / 2.0;
4✔
186
    }
187
    if (height == 0.0) {
84✔
188
        height = maxY - minY + _margin.second * 2.0;
80✔
189
    } else {
190
        translateY = (height - maxY - minY) / 2.0;
4✔
191
    }
192
    const auto svgElement = make_shared<XMLElement>("svg");
84✔
193
    svgElement->addAttribute("width", width);
168✔
194
    svgElement->addAttribute("height", height);
168✔
195
    svgElement->addAttribute("viewBox", format("0 0 {} {}", width, height));
252✔
196
    svgElement->addAttribute("xmlns", "http://www.w3.org/2000/svg");
336✔
197
    svgElement->addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
336✔
198
    svgElement->addChild(make_shared<XMLElementComment>("Generated by: https://github.com/CyberZHG/SVGDiagram"));
84✔
199
    const auto gElement = make_shared<XMLElement>("g");
84✔
200
    gElement->addAttribute("id", "graph0");
336✔
201
    gElement->addAttribute("class", "graph");
336✔
202
    gElement->addAttribute("transform", format("translate({},{}) scale(1.0)", translateX, translateY));
252✔
203
    if (!_backgroundColor.empty()) {
84✔
204
        const auto rectElement = make_shared<XMLElement>("rect");
24✔
205
        rectElement->addAttribute("x", -translateX);
48✔
206
        rectElement->addAttribute("y", -translateY);
48✔
207
        rectElement->addAttribute("width", width);
48✔
208
        rectElement->addAttribute("height", height);
48✔
209
        rectElement->addAttribute("fill", _backgroundColor);
48✔
210
        gElement->addChild(rectElement);
24✔
211
    }
24✔
212
    svgElement->addChild(gElement);
84✔
213
    return {svgElement, gElement};
168✔
214
}
84✔
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