MailJDTourDownloadDocsQualityTestsCoverage

Which source lines and branches the tests execute. Lines 91.5 %, branches 75.0 % (minimum 85 % and 72 %). main @ 399ffe3d · 21 October 2026 · pipeline 7238.

GCC Code Coverage Report


Directory: src/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 91.6% 261 / 0 / 285
Functions: 96.4% 27 / 0 / 28
Branches: 75.5% 246 / 0 / 326

presentation/GraphLinesItem.cpp
Line Branch Exec Source
1 #include "GraphLinesItem.h"
2
3 #include "GraphAreasItem.h"
4 #include "GraphPaintCache.h"
5
6 #include <QElapsedTimer>
7 #include <QFont>
8 #include <QFontMetricsF>
9 #include <QHash>
10 #include <QLoggingCategory>
11 #include <QPainter>
12 #include <QQuickWindow>
13 #include <QSGGeometryNode>
14 #include <QSGImageNode>
15 #include <QSGRendererInterface>
16 #include <QSGVertexColorMaterial>
17 #include <QtQml>
18
19 #include <cmath>
20 #include <cstring>
21 #include <functional>
22 #include <mutex>
23
24
4/7
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 8 taken 572 times.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 2 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
574 Q_LOGGING_CATEGORY(lcGraphLines, "mailjd.graph.lines")
25
26 namespace {
27
28 1239 QList<double> numbers(const QVariantList &values) {
29 1239 QList<double> out;
30
1/1
✓ Branch 3 → 4 taken 1239 times.
1239 out.reserve(values.size());
31 // S106-946: QML numbers are screened at the seam — NaN and ±inf become 0.
32
2/2
✓ Branch 15 → 6 taken 53984 times.
✓ Branch 15 → 16 taken 1239 times.
55223 for (const QVariant &value : values) {
33
1/1
✓ Branch 7 → 8 taken 53984 times.
53984 const double number = value.toDouble();
34
2/3
✓ Branch 9 → 10 taken 53984 times.
✗ Branch 9 → 11 not taken.
✓ Branch 12 → 13 taken 53984 times.
53984 out.append(std::isfinite(number) ? number : 0.0);
35 }
36 1239 return out;
37 ✗ }
38
39 } // namespace
40
41
1/1
✓ Branch 23 → 24 taken 28 times.
28 GraphLinesItem::GraphLinesItem(QQuickItem *parent) : QQuickItem(parent) {
42
1/1
✓ Branch 24 → 25 taken 28 times.
28 setFlag(ItemHasContents, true);
43
1/1
✓ Branch 25 → 26 taken 28 times.
28 connect(this, &GraphLinesItem::changed, this, [this] {
44 1676 m_contentChanged = true;
45 1676 update();
46 1676 });
47 // The software renderer's picture after a zoom has rested (GraphPaintCache).
48
1/1
✓ Branch 27 → 28 taken 28 times.
28 m_refresh.setSingleShot(true);
49
1/1
✓ Branch 28 → 29 taken 28 times.
28 m_refresh.setInterval(120);
50
1/1
✓ Branch 29 → 30 taken 28 times.
28 connect(&m_refresh, &QTimer::timeout, this, [this] {
51 4 m_contentChanged = true;
52 4 update();
53 4 });
54 28 }
55
56 2477 void GraphLinesItem::registerTypes() {
57 static std::once_flag once;
58
1/1
✓ Branch 2 → 3 taken 2477 times.
2477 std::call_once(once, [] {
59 27 qmlRegisterType<GraphLinesItem>("MailJD.Graph", 1, 0, "GraphLines");
60 27 qmlRegisterType<GraphAreasItem>("MailJD.Graph", 1, 0, "GraphAreas");
61 27 });
62 2477 }
63
64 ✗ QObject *GraphLinesItem::graph() const { return m_graph.data(); }
65
66 28 void GraphLinesItem::setGraph(QObject *graph) {
67
1/1
✓ Branch 2 → 3 taken 28 times.
28 auto *typed = qobject_cast<QuickContactGraph *>(graph);
68
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 28 times.
28 if (typed == m_graph)
69 ✗ return;
70
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 10 taken 28 times.
28 if (m_graph)
71 ✗ disconnect(m_graph, nullptr, this, nullptr);
72
1/1
✓ Branch 10 → 11 taken 28 times.
28 m_graph = typed;
73
1/2
✓ Branch 12 → 13 taken 28 times.
✗ Branch 12 → 20 not taken.
28 if (m_graph) {
74
1/1
✓ Branch 14 → 15 taken 28 times.
28 connect(m_graph, &QuickContactGraph::viewChanged, this, &QQuickItem::update);
75
1/1
✓ Branch 17 → 18 taken 28 times.
28 connect(m_graph, &QuickContactGraph::layoutChanged, this, [this] {
76 158 m_contentChanged = true;
77 158 update();
78 158 });
79 }
80
1/1
✓ Branch 20 → 21 taken 28 times.
28 emit changed();
81 }
82
83 305 void GraphLinesItem::setAlphas(const QVariantList &values) {
84 305 m_alphasIn = values;
85
1/1
✓ Branch 3 → 4 taken 305 times.
305 m_alphas = numbers(values);
86 305 emit changed();
87 305 }
88
89 270 void GraphLinesItem::setBoosts(const QVariantList &values) {
90 270 m_boostsIn = values;
91
1/1
✓ Branch 3 → 4 taken 270 times.
270 m_boosts = numbers(values);
92 270 emit changed();
93 270 }
94
95 279 void GraphLinesItem::setColours(const QVariantList &values) {
96 279 m_coloursIn = values;
97 279 m_colours.clear();
98 279 m_colours.reserve(values.size());
99
2/2
✓ Branch 13 → 8 taken 7437 times.
✓ Branch 13 → 14 taken 279 times.
7716 for (const QVariant &value : values)
100
2/2
✓ Branch 9 → 10 taken 7437 times.
✓ Branch 10 → 11 taken 7437 times.
7437 m_colours.append(value.value<QColor>());
101 279 emit changed();
102 279 }
103
104 43 void GraphLinesItem::setIndices(const QVariantList &values) {
105 43 m_indicesIn = values;
106 43 m_indices.clear();
107
2/2
✓ Branch 11 → 6 taken 72 times.
✓ Branch 11 → 12 taken 43 times.
115 for (const QVariant &value : values)
108
2/2
✓ Branch 7 → 8 taken 72 times.
✓ Branch 8 → 9 taken 72 times.
72 m_indices.append(value.toInt());
109 43 emit changed();
110 43 }
111
112 194 void GraphLinesItem::setRadii(const QVariantList &values) {
113 194 m_radiiIn = values;
114
1/1
✓ Branch 3 → 4 taken 194 times.
194 m_radii = numbers(values);
115 194 emit changed();
116 194 }
117
118 424 void GraphLinesItem::setCutouts(const QVariantList &values) {
119 424 m_cutoutsIn = values;
120
1/1
✓ Branch 3 → 4 taken 424 times.
424 m_cutouts = numbers(values);
121 424 emit changed();
122 424 }
123
124 46 void GraphLinesItem::setScreenCutouts(const QVariantList &values) {
125 46 m_screenCutoutsIn = values;
126
1/1
✓ Branch 3 → 4 taken 46 times.
46 m_screenCutouts = numbers(values);
127 46 emit changed();
128 46 }
129
130 25 void GraphLinesItem::setLinkDrag(const QVariantMap &value) {
131 25 m_linkDrag = value;
132 25 emit changed();
133 25 }
134
135 58 void GraphLinesItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) {
136 58 QQuickItem::geometryChange(newGeometry, oldGeometry);
137 58 update();
138 58 }
139
140 833 QList<double> GraphLinesItem::dashOf(const QString &style) {
141
2/2
✓ Branch 4 → 5 taken 61 times.
✓ Branch 4 → 6 taken 772 times.
833 if (style == QLatin1String("friend"))
142 61 return {9, 5};
143
2/2
✓ Branch 8 → 9 taken 6 times.
✓ Branch 8 → 10 taken 766 times.
772 if (style == QLatin1String("acquaintance"))
144 6 return {4, 5};
145
6/6
✓ Branch 12 → 13 taken 748 times.
✓ Branch 12 → 16 taken 18 times.
✓ Branch 15 → 16 taken 46 times.
✓ Branch 15 → 17 taken 702 times.
✓ Branch 18 → 19 taken 64 times.
✓ Branch 18 → 20 taken 702 times.
766 if (style == QLatin1String("work") || style == QLatin1String("hierarchy"))
146 64 return {10, 4, 2, 4};
147
2/2
✓ Branch 22 → 23 taken 115 times.
✓ Branch 22 → 24 taken 587 times.
702 if (style == QLatin1String("via"))
148 115 return {1, 4};
149
1/2
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 28 taken 587 times.
587 if (style == QLatin1String("suggestion"))
150 ✗ return {1, 7};
151
1/2
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 587 times.
587 if (style == QLatin1String("layer"))
152 ✗ return {3, 4};
153
2/2
✓ Branch 34 → 35 taken 6 times.
✓ Branch 34 → 36 taken 581 times.
587 if (style == QLatin1String("expartner"))
154 6 return {5, 4};
155 581 return {};
156 }
157
158
2/4
✓ Branch 2 → 3 taken 1047 times.
✗ Branch 2 → 7 not taken.
✓ Branch 4 → 5 taken 1047 times.
✗ Branch 4 → 7 not taken.
1047 double GraphLinesItem::alphaOf(int index) const { return index >= 0 && index < m_alphas.size() ? m_alphas.at(index) : 1; }
159
160
2/4
✓ Branch 2 → 3 taken 786 times.
✗ Branch 2 → 7 not taken.
✓ Branch 4 → 5 taken 786 times.
✗ Branch 4 → 7 not taken.
786 double GraphLinesItem::radiusOf(int node) const { return node >= 0 && node < m_radii.size() ? m_radii.at(node) : 16; }
161
162 // The route on the screen: straight, a quadratic curve, the two ends of a
163 // bundle (the trunk is drawn once, as a trunk), or a polyline with rounded
164 // corners (the family tree's elbows), as the Canvas traced them.
165 716 QList<QPointF> GraphLinesItem::screenPath(const QuickContactGraph::DrawnEdge &edge) const {
166 716 QList<QPointF> p;
167
1/1
✓ Branch 3 → 4 taken 716 times.
716 p.reserve(edge.points.size());
168
2/2
✓ Branch 11 → 6 taken 2437 times.
✓ Branch 11 → 12 taken 716 times.
3153 for (const QPointF &point : edge.points)
169
1/1
✓ Branch 8 → 9 taken 2437 times.
2437 p.append(toScreen(point));
170
1/2
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 21 taken 716 times.
716 if (p.size() < 2)
171 ✗ return {toScreen(edge.path.value(0)), toScreen(edge.path.value(edge.path.size() - 1))};
172
5/6
✓ Branch 23 → 24 taken 32 times.
✓ Branch 23 → 27 taken 684 times.
✓ Branch 25 → 26 taken 32 times.
✗ Branch 25 → 27 not taken.
✓ Branch 28 → 29 taken 32 times.
✓ Branch 28 → 34 taken 684 times.
716 if (edge.shape == QLatin1String("curve") && p.size() == 3)
173
1/1
✓ Branch 32 → 33 taken 32 times.
32 return GraphGeometry::quadratic(p.at(0), p.at(1), p.at(2));
174
2/6
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 40 taken 684 times.
✗ Branch 38 → 39 not taken.
✗ Branch 38 → 40 not taken.
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 43 taken 684 times.
684 if (edge.shape == QLatin1String("bundle") && p.size() == 6)
175 ✗ return {}; // drawn as its two ends in paintEdge
176
1/1
✓ Branch 43 → 44 taken 684 times.
684 return GraphGeometry::rounded(p, 10);
177 716 }
178
179 1597 void GraphLinesItem::paintEdge(GraphGeometry::Sink *mesh, int index, const GraphGeometry::Cutouts &cuts,
180 QList<int> *pairs) {
181 1597 const QuickContactGraph::DrawnEdge &edge = m_graph->drawnEdges().at(index);
182
6/6
✓ Branch 7 → 8 taken 1561 times.
✓ Branch 7 → 11 taken 36 times.
✓ Branch 10 → 11 taken 728 times.
✓ Branch 10 → 12 taken 833 times.
✓ Branch 13 → 14 taken 764 times.
✓ Branch 13 → 15 taken 833 times.
1597 if (edge.kind == QLatin1String("member") || edge.shape == QLatin1String("none"))
183 764 return;
184 833 const double alpha = alphaOf(index);
185
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 833 times.
833 if (alpha <= 0)
186 ✗ return;
187
2/4
✓ Branch 19 → 20 taken 833 times.
✗ Branch 19 → 25 not taken.
✓ Branch 22 → 23 taken 833 times.
✗ Branch 22 → 25 not taken.
833 const QColor colour = index < m_colours.size() && m_colours.at(index).isValid() ? m_colours.at(index) : QColor(Qt::gray);
188
1/2
✓ Branch 27 → 28 taken 833 times.
✗ Branch 27 → 30 not taken.
833 const double width = edge.width + (index < m_boosts.size() ? m_boosts.at(index) : 0);
189
1/1
✓ Branch 31 → 32 taken 833 times.
833 const QList<double> dash = dashOf(edge.style);
190
4/4
✓ Branch 34 → 35 taken 744 times.
✓ Branch 34 → 38 taken 89 times.
✓ Branch 37 → 38 taken 6 times.
✓ Branch 37 → 39 taken 738 times.
833 const bool pair = edge.style == QLatin1String("partner") || edge.style == QLatin1String("expartner");
191
5/6
✓ Branch 42 → 43 taken 28 times.
✓ Branch 42 → 46 taken 805 times.
✓ Branch 44 → 45 taken 28 times.
✗ Branch 44 → 46 not taken.
✓ Branch 47 → 48 taken 28 times.
✓ Branch 47 → 67 taken 805 times.
833 if (edge.shape == QLatin1String("bundle") && edge.points.size() == 6) {
192 // Only the two ends: into the first hub and out of the second.
193 56 const QList<QPointF> into = GraphGeometry::quadratic(toScreen(edge.points.at(0)), toScreen(edge.points.at(1)),
194
1/1
✓ Branch 54 → 55 taken 28 times.
84 toScreen(edge.points.at(2)));
195 56 const QList<QPointF> out = GraphGeometry::quadratic(toScreen(edge.points.at(3)), toScreen(edge.points.at(4)),
196
1/1
✓ Branch 61 → 62 taken 28 times.
84 toScreen(edge.points.at(5)));
197
1/1
✓ Branch 62 → 63 taken 28 times.
28 mesh->stroke(into, width, dash, colour, alpha, &cuts);
198
1/1
✓ Branch 63 → 64 taken 28 times.
28 mesh->stroke(out, width, dash, colour, alpha, &cuts);
199
7/8
✓ Branch 67 → 68 taken 95 times.
✓ Branch 67 → 74 taken 710 times.
✓ Branch 70 → 71 taken 89 times.
✓ Branch 70 → 74 taken 6 times.
✓ Branch 72 → 73 taken 89 times.
✗ Branch 72 → 74 not taken.
✓ Branch 75 → 76 taken 89 times.
✓ Branch 75 → 104 taken 716 times.
833 } else if (pair && edge.shape == QLatin1String("line") && edge.points.size() == 2) {
200 // Two strokes side by side: a bond, not a line of descent.
201 89 const QPointF a = toScreen(edge.points.at(0)), b = toScreen(edge.points.at(1));
202 89 const double length = std::max(1.0, std::hypot(b.x() - a.x(), b.y() - a.y()));
203 89 const QPointF normal(-(b.y() - a.y()) / length * 2.2, (b.x() - a.x()) / length * 2.2);
204
2/2
✓ Branch 101 → 92 taken 178 times.
✓ Branch 101 → 102 taken 89 times.
267 for (int side : {1, -1})
205
2/2
✓ Branch 97 → 98 taken 178 times.
✓ Branch 98 → 99 taken 178 times.
178 mesh->stroke({a + normal * side, b + normal * side}, std::max(1.2, width * 0.6), dash, colour, alpha, &cuts);
206
1/1
✓ Branch 102 → 103 taken 89 times.
89 pairs->append(index);
207 } else {
208
2/2
✓ Branch 104 → 105 taken 716 times.
✓ Branch 105 → 106 taken 716 times.
716 mesh->stroke(screenPath(edge), width, dash, colour, alpha, &cuts);
209
2/2
✓ Branch 107 → 108 taken 6 times.
✓ Branch 107 → 109 taken 710 times.
716 if (pair)
210
1/1
✓ Branch 108 → 109 taken 6 times.
6 pairs->append(index);
211 }
212 // An arrowhead just before the avatar the edge points at (none in the family tree).
213
6/8
✓ Branch 109 → 110 taken 833 times.
✗ Branch 109 → 114 not taken.
✓ Branch 110 → 111 taken 596 times.
✓ Branch 110 → 114 taken 237 times.
✓ Branch 112 → 113 taken 596 times.
✗ Branch 112 → 114 not taken.
✓ Branch 115 → 116 taken 596 times.
✓ Branch 115 → 153 taken 237 times.
833 if (m_arrows && edge.arrowTo >= 0 && edge.path.size() >= 2) {
214
1/4
✗ Branch 117 → 118 not taken.
✓ Branch 117 → 119 taken 596 times.
✗ Branch 118 → 119 not taken.
✗ Branch 118 → 120 not taken.
596 const bool atEnd = edge.points.size() >= 2 || edge.arrowTo == edge.to;
215 596 const int n = int(edge.path.size());
216
1/2
✓ Branch 122 → 123 taken 596 times.
✗ Branch 122 → 124 not taken.
596 const QPointF tip = toScreen(atEnd ? edge.path.at(n - 1) : edge.path.at(0));
217
1/2
✓ Branch 126 → 127 taken 596 times.
✗ Branch 126 → 128 not taken.
596 const QPointF from = toScreen(atEnd ? edge.path.at(n - 2) : edge.path.at(1));
218 596 const double length = std::hypot(tip.x() - from.x(), tip.y() - from.y());
219
1/2
✓ Branch 134 → 135 taken 596 times.
✗ Branch 134 → 152 not taken.
596 if (length >= 1) {
220 596 const double radius = radiusOf(edge.arrowTo);
221 596 const QPointF u = (tip - from) / length;
222
2/2
✓ Branch 138 → 139 taken 46 times.
✓ Branch 138 → 140 taken 550 times.
596 const double size = radius < 10 ? 5 : 8;
223 596 const QPointF at = tip - u * (radius + 3);
224 596 const QPointF back = at - u * size;
225 596 const QPointF side(-u.y() * size * 0.6, u.x() * size * 0.6);
226
1/1
✓ Branch 150 → 151 taken 596 times.
596 mesh->triangle(at, back + side, back - side, colour, alpha, &cuts);
227 }
228 }
229 833 }
230
231 87 void GraphLinesItem::build(GraphGeometry::Sink *mesh) {
232
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 87 times.
87 if (!m_graph)
233 ✗ return;
234 87 m_k = m_graph->viewScale();
235
2/2
✓ Branch 8 → 9 taken 87 times.
✓ Branch 10 → 11 taken 87 times.
87 m_t = QPointF(m_graph->viewX(), m_graph->viewY());
236 87 const QList<QuickContactGraph::DrawnEdge> &edges = m_graph->drawnEdges();
237
238 87 GraphGeometry::Cutouts cuts;
239
2/2
✓ Branch 29 → 15 taken 1169 times.
✓ Branch 29 → 30 taken 87 times.
1256 for (qsizetype i = 0; i + 5 < m_cutouts.size(); i += 6) {
240 1169 const QPointF anchor = toScreen(QPointF(m_cutouts.at(i), m_cutouts.at(i + 1)));
241
1/1
✓ Branch 26 → 27 taken 1169 times.
3507 cuts.add(QRectF(anchor.x() + m_cutouts.at(i + 2), anchor.y() + m_cutouts.at(i + 3), m_cutouts.at(i + 4),
242 3507 m_cutouts.at(i + 5)));
243 }
244
1/2
✗ Branch 39 → 31 not taken.
✓ Branch 39 → 40 taken 87 times.
87 for (qsizetype i = 0; i + 3 < m_screenCutouts.size(); i += 4)
245 ✗ cuts.add(QRectF(m_screenCutouts.at(i), m_screenCutouts.at(i + 1), m_screenCutouts.at(i + 2), m_screenCutouts.at(i + 3)));
246
247 // Which lines are drawn, in order: the overlay its indices (pointed-at ones
248 // last), the base every line with the boosted ones (the path) last.
249 87 QList<int> order, later;
250
2/2
✓ Branch 40 → 41 taken 44 times.
✓ Branch 40 → 62 taken 43 times.
87 if (m_overlay) {
251
2/2
✓ Branch 60 → 44 taken 80 times.
✓ Branch 60 → 61 taken 44 times.
124 for (int index : std::as_const(m_indices))
252
3/6
✓ Branch 45 → 46 taken 80 times.
✗ Branch 45 → 49 not taken.
✓ Branch 47 → 48 taken 80 times.
✗ Branch 47 → 49 not taken.
✓ Branch 50 → 51 taken 80 times.
✗ Branch 50 → 58 not taken.
80 if (index >= 0 && index < edges.size())
253
4/5
✓ Branch 52 → 53 taken 80 times.
✗ Branch 52 → 56 not taken.
✓ Branch 54 → 55 taken 5 times.
✓ Branch 54 → 56 taken 75 times.
✓ Branch 57 → 58 taken 80 times.
80 (index < m_boosts.size() && m_boosts.at(index) > 1 ? later : order).append(index);
254 } else {
255
2/2
✓ Branch 72 → 63 taken 1517 times.
✓ Branch 72 → 73 taken 43 times.
1560 for (int index = 0; index < edges.size(); ++index)
256
3/5
✓ Branch 64 → 65 taken 1517 times.
✗ Branch 64 → 68 not taken.
✗ Branch 66 → 67 not taken.
✓ Branch 66 → 68 taken 1517 times.
✓ Branch 69 → 70 taken 1517 times.
1517 (index < m_boosts.size() && m_boosts.at(index) > 0 ? later : order).append(index);
257 }
258
1/1
✓ Branch 73 → 74 taken 87 times.
87 order.append(later);
259
260 // The couples' signs and years sit on a gap in the lines: their places are
261 // cut out of every line first.
262 struct Bond {
263 int index;
264 QPointF middle;
265 };
266 87 QList<Bond> bonds;
267
1/1
✓ Branch 74 → 75 taken 87 times.
87 QFont yearFont;
268
1/1
✓ Branch 75 → 76 taken 87 times.
87 yearFont.setPixelSize(11);
269
1/1
✓ Branch 76 → 77 taken 87 times.
87 yearFont.setWeight(QFont::Medium);
270
1/1
✓ Branch 77 → 78 taken 87 times.
87 const QFontMetricsF yearMetrics(yearFont);
271
2/2
✓ Branch 141 → 81 taken 1597 times.
✓ Branch 141 → 142 taken 87 times.
1684 for (int index : std::as_const(order)) {
272 1597 const QuickContactGraph::DrawnEdge &edge = edges.at(index);
273
6/6
✓ Branch 85 → 86 taken 1508 times.
✓ Branch 85 → 90 taken 89 times.
✓ Branch 88 → 89 taken 1502 times.
✓ Branch 88 → 90 taken 6 times.
✓ Branch 91 → 92 taken 1502 times.
✓ Branch 91 → 93 taken 95 times.
1597 if (edge.style != QLatin1String("partner") && edge.style != QLatin1String("expartner"))
274 1502 continue;
275
4/8
✓ Branch 95 → 96 taken 95 times.
✗ Branch 95 → 101 not taken.
✓ Branch 98 → 99 taken 95 times.
✗ Branch 98 → 101 not taken.
✗ Branch 100 → 101 not taken.
✓ Branch 100 → 102 taken 95 times.
✗ Branch 103 → 104 not taken.
✓ Branch 103 → 105 taken 95 times.
95 if (edge.kind == QLatin1String("member") || edge.shape == QLatin1String("none") || alphaOf(index) <= 0)
276 ✗ continue;
277 95 QList<QPointF> path;
278
2/2
✓ Branch 112 → 107 taken 304 times.
✓ Branch 112 → 113 taken 95 times.
399 for (const QPointF &p : edge.path)
279
1/1
✓ Branch 109 → 110 taken 304 times.
304 path.append(toScreen(p));
280 95 double length = 0;
281
1/1
✓ Branch 113 → 114 taken 95 times.
95 const QPointF middle = GraphGeometry::middle(path, &length);
282
1/2
✗ Branch 116 → 117 not taken.
✓ Branch 116 → 118 taken 95 times.
95 if (length - radiusOf(edge.from) - radiusOf(edge.to) < m_ringSize + 10)
283 ✗ continue;
284
1/1
✓ Branch 118 → 119 taken 95 times.
95 bonds.append({index, middle});
285
1/1
✓ Branch 122 → 123 taken 95 times.
95 cuts.add(QRectF(middle.x() - m_ringSize / 2 - 3, middle.y() - m_ringSize * 0.3 - 4, m_ringSize + 6,
286 95 m_ringSize * 0.6 + 8));
287
4/4
✓ Branch 123 → 124 taken 85 times.
✓ Branch 123 → 133 taken 10 times.
✓ Branch 124 → 125 taken 77 times.
✓ Branch 124 → 133 taken 8 times.
95 if (m_years && edge.year > 0) {
288
2/2
✓ Branch 125 → 126 taken 77 times.
✓ Branch 126 → 127 taken 77 times.
77 const double w = yearMetrics.horizontalAdvance(QString::number(edge.year));
289 77 const double above = middle.y() - m_ringSize * 0.3 - 3;
290
1/1
✓ Branch 131 → 132 taken 77 times.
77 cuts.add(QRectF(middle.x() - w / 2 - 3, above - 14, w + 6, 14));
291 }
292 95 }
293
294 // S103.5.5: the relationship being dragged.
295
5/7
✓ Branch 142 → 143 taken 44 times.
✓ Branch 142 → 146 taken 43 times.
✓ Branch 143 → 144 taken 44 times.
✗ Branch 144 → 145 not taken.
✓ Branch 144 → 146 taken 44 times.
✗ Branch 147 → 148 not taken.
✓ Branch 147 → 220 taken 87 times.
87 if (m_overlay && !m_linkDrag.isEmpty()) {
296 ✗ const int from = m_linkDrag.value(QStringLiteral("from")).toInt();
297 ✗ const QVariantList nodes = m_graph->nodes();
298 ✗ if (from >= 0 && from < nodes.size()) {
299 ✗ const QVariantMap node = nodes.at(from).toMap();
300 ✗ const QPointF start = toScreen(QPointF(node.value(QStringLiteral("x")).toDouble(), node.value(QStringLiteral("y")).toDouble()));
301 ✗ const QPointF end(m_linkDrag.value(QStringLiteral("x")).toDouble(), m_linkDrag.value(QStringLiteral("y")).toDouble());
302 ✗ mesh->stroke({start, end}, 2, {4, 3}, m_dragColour, 1, nullptr);
303 ✗ }
304 ✗ }
305
306 87 QList<int> pairs;
307
2/2
✓ Branch 227 → 223 taken 1597 times.
✓ Branch 227 → 228 taken 87 times.
1684 for (int index : std::as_const(order))
308
1/1
✓ Branch 224 → 225 taken 1597 times.
1597 paintEdge(mesh, index, cuts, &pairs);
309
310 // The rings of the couples, on top of every line (⚭; ⚮ for former partners).
311
2/2
✓ Branch 276 → 231 taken 95 times.
✓ Branch 276 → 277 taken 87 times.
182 for (const Bond &bond : std::as_const(bonds)) {
312
1/2
✗ Branch 233 → 234 not taken.
✓ Branch 233 → 235 taken 95 times.
95 if (!pairs.contains(bond.index))
313 ✗ continue;
314 95 const QuickContactGraph::DrawnEdge &edge = edges.at(bond.index);
315
2/4
✓ Branch 237 → 238 taken 95 times.
✗ Branch 237 → 243 not taken.
✓ Branch 240 → 241 taken 95 times.
✗ Branch 240 → 243 not taken.
95 const QColor colour = bond.index < m_colours.size() && m_colours.at(bond.index).isValid() ? m_colours.at(bond.index)
316 95 : QColor(Qt::gray);
317 95 const double alpha = alphaOf(bond.index);
318 95 const double radius = m_ringSize * 0.3, offset = radius * 0.62;
319 95 const double lineWidth = std::max(1.3, m_ringSize / 9);
320
2/2
✓ Branch 259 → 248 taken 190 times.
✓ Branch 259 → 260 taken 95 times.
285 for (int side : {-1, 1}) {
321 190 QList<QPointF> circle;
322
2/2
✓ Branch 253 → 249 taken 5320 times.
✓ Branch 253 → 254 taken 190 times.
5510 for (int k = 0; k < 28; ++k)
323
1/1
✓ Branch 251 → 252 taken 5320 times.
5320 circle.append(bond.middle + QPointF(side * offset + radius * std::cos(k * 2 * M_PI / 28),
324 5320 radius * std::sin(k * 2 * M_PI / 28)));
325
1/1
✓ Branch 255 → 256 taken 190 times.
190 mesh->stroke(circle, lineWidth, {}, colour, alpha, nullptr, true);
326 190 }
327
2/2
✓ Branch 262 → 263 taken 6 times.
✓ Branch 262 → 273 taken 89 times.
95 if (edge.style == QLatin1String("expartner"))
328
2/2
✓ Branch 268 → 269 taken 6 times.
✓ Branch 269 → 270 taken 6 times.
6 mesh->stroke({bond.middle - QPointF(0, radius * 1.45), bond.middle + QPointF(0, radius * 1.45)}, lineWidth, {},
329 colour, alpha, nullptr);
330 }
331
332 // The trunks of the bundles, each once, as strong as its most visible line.
333
2/2
✓ Branch 277 → 278 taken 43 times.
✓ Branch 277 → 347 taken 44 times.
87 if (!m_overlay) {
334 struct Trunk {
335 QPointF a, b;
336 int count = 0;
337 double alpha = 0;
338 };
339 43 QHash<QString, Trunk> trunks;
340 43 QStringList keys;
341
2/2
✓ Branch 323 → 279 taken 1517 times.
✓ Branch 323 → 324 taken 43 times.
1560 for (int index = 0; index < edges.size(); ++index) {
342 1517 const QuickContactGraph::DrawnEdge &edge = edges.at(index);
343
5/6
✓ Branch 282 → 283 taken 24 times.
✓ Branch 282 → 285 taken 1493 times.
✗ Branch 284 → 285 not taken.
✓ Branch 284 → 286 taken 24 times.
✓ Branch 287 → 288 taken 1493 times.
✓ Branch 287 → 289 taken 24 times.
1517 if (edge.shape != QLatin1String("bundle") || edge.points.size() != 6)
344 1493 continue;
345 24 const QPointF a = edge.points.at(2), b = edge.points.at(3);
346 48 const QString key = QStringLiteral("%1,%2,%3,%4")
347
4/4
✓ Branch 296 → 297 taken 24 times.
✓ Branch 299 → 300 taken 24 times.
✓ Branch 302 → 303 taken 24 times.
✓ Branch 305 → 306 taken 24 times.
96 .arg(std::lround(a.x())).arg(std::lround(a.y())).arg(std::lround(b.x())).arg(std::lround(b.y()));
348
2/2
✓ Branch 312 → 313 taken 12 times.
✓ Branch 312 → 315 taken 12 times.
24 if (!trunks.contains(key)) {
349
1/1
✓ Branch 313 → 314 taken 12 times.
12 trunks.insert(key, {a, b, edge.bundle, 0});
350
1/1
✓ Branch 314 → 315 taken 12 times.
12 keys.append(key);
351 }
352
2/2
✓ Branch 316 → 317 taken 24 times.
✓ Branch 318 → 319 taken 24 times.
24 trunks[key].alpha = std::max(trunks[key].alpha, alphaOf(index));
353 24 }
354
2/2
✓ Branch 343 → 327 taken 12 times.
✓ Branch 343 → 344 taken 43 times.
55 for (const QString &key : std::as_const(keys)) {
355 12 const Trunk &trunk = trunks.value(key);
356
1/2
✗ Branch 329 → 330 not taken.
✓ Branch 329 → 331 taken 12 times.
12 if (trunk.alpha <= 0)
357 ✗ continue;
358
2/2
✓ Branch 336 → 337 taken 12 times.
✓ Branch 337 → 338 taken 12 times.
12 mesh->stroke({toScreen(trunk.a), toScreen(trunk.b)}, 1.5 + std::min(trunk.count, 6) * 0.5, {}, m_trunkColour,
359 24 std::min(0.8, trunk.alpha), &cuts);
360 }
361 43 }
362 87 }
363
364 574 QSGNode *GraphLinesItem::updatePaintNode(QSGNode *old, UpdatePaintNodeData *) {
365 574 QElapsedTimer timer;
366 574 timer.start();
367
1/2
✓ Branch 4 → 5 taken 574 times.
✗ Branch 4 → 7 not taken.
574 const double k = m_graph ? m_graph->viewScale() : 1;
368
3/4
✓ Branch 9 → 10 taken 574 times.
✗ Branch 9 → 15 not taken.
✓ Branch 11 → 12 taken 574 times.
✓ Branch 13 → 14 taken 574 times.
574 const QPointF t = m_graph ? QPointF(m_graph->viewX(), m_graph->viewY()) : QPointF();
369 574 bool refresh = false;
370 574 int vertices = m_vertexCount;
371
1/1
✓ Branch 17 → 18 taken 574 times.
661 QSGNode *node = m_cache.update(this, old, k, t, m_contentChanged, [this](GraphGeometry::Sink *sink) { build(sink); },
372 &vertices, &refresh);
373 574 m_contentChanged = false;
374 574 m_refreshPending = refresh;
375 574 m_vertexCount = vertices;
376 574 m_buildMs = timer.nsecsElapsed() / 1e6;
377
11/11
✓ Branch 20 → 21 taken 574 times.
✓ Branch 24 → 25 taken 574 times.
✓ Branch 25 → 26 taken 288 times.
✓ Branch 25 → 27 taken 286 times.
✓ Branch 28 → 29 taken 574 times.
✓ Branch 29 → 30 taken 574 times.
✓ Branch 30 → 31 taken 574 times.
✓ Branch 31 → 32 taken 574 times.
✓ Branch 32 → 33 taken 574 times.
✓ Branch 36 → 22 taken 574 times.
✓ Branch 36 → 37 taken 574 times.
1148 qCDebug(lcGraphLines) << (m_overlay ? "overlay" : "lines") << m_vertexCount << "vertices in" << m_buildMs << "ms";
378
1/1
✓ Branch 37 → 38 taken 574 times.
574 QMetaObject::invokeMethod(this, [this, refresh] {
379
2/2
✓ Branch 2 → 3 taken 484 times.
✓ Branch 2 → 4 taken 88 times.
572 if (refresh)
380 484 m_refresh.start();
381 572 emit built();
382 572 }, Qt::QueuedConnection);
383 574 return node;
384 }
385