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 |