presentation/GraphPainterSink.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // GraphGeometry::PainterSink: the relationship graph's lines and areas | ||
| 2 | // drawn with a QPainter, for Qt Quick's software renderer (Sprint 105 | ||
| 3 | // S105.4.2; split from GraphGeometry.cpp for its size). | ||
| 4 | #include "GraphGeometry.h" | ||
| 5 | |||
| 6 | #include <QLinearGradient> | ||
| 7 | #include <QPainterPath> | ||
| 8 | #include <QRegion> | ||
| 9 | |||
| 10 | #include <algorithm> | ||
| 11 | |||
| 12 | namespace GraphGeometry { | ||
| 13 | |||
| 14 | namespace { | ||
| 15 | 6986 | QRectF boundsOf(const QList<QPointF> &points) { | |
| 16 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 6986 times.
|
6986 | if (points.isEmpty()) |
| 17 | ✗ | return {}; | |
| 18 | 6986 | double left = points.first().x(), right = left, top = points.first().y(), bottom = top; | |
| 19 |
2/2✓ Branch 22 → 11 taken 41636 times.
✓ Branch 22 → 23 taken 6986 times.
|
48622 | for (const QPointF &p : points) { |
| 20 | 41636 | left = std::min(left, p.x()); | |
| 21 | 41636 | right = std::max(right, p.x()); | |
| 22 | 41636 | top = std::min(top, p.y()); | |
| 23 | 41636 | bottom = std::max(bottom, p.y()); | |
| 24 | } | ||
| 25 | 6986 | return QRectF(QPointF(left, top), QPointF(right, bottom)); | |
| 26 | } | ||
| 27 | } // namespace | ||
| 28 | |||
| 29 | // Always a clip region — the whole image, or the image without the boxes: | ||
| 30 | // switching clipping off and a region on again did not clip (Qt 6.8 raster). | ||
| 31 | 1862 | void PainterSink::clipFor(const Cutouts *cuts) { | |
| 32 |
2/2✓ Branch 2 → 3 taken 1528 times.
✓ Branch 2 → 5 taken 334 times.
|
1862 | const qsizetype boxes = cuts ? cuts->boxes().size() : 0; |
| 33 |
2/2✓ Branch 6 → 7 taken 1528 times.
✓ Branch 6 → 8 taken 334 times.
|
1862 | const Cutouts *wanted = boxes > 0 ? cuts : nullptr; |
| 34 |
5/6✓ Branch 9 → 10 taken 1790 times.
✓ Branch 9 → 13 taken 72 times.
✓ Branch 10 → 11 taken 1740 times.
✓ Branch 10 → 13 taken 50 times.
✓ Branch 11 → 12 taken 1740 times.
✗ Branch 11 → 13 not taken.
|
1862 | if (m_clipSet && m_clipped == wanted && m_clippedBoxes == boxes) |
| 35 | 1740 | return; | |
| 36 | // The viewport is in the painter's (screen) coordinates, margin included. | ||
| 37 |
2/2✓ Branch 14 → 15 taken 119 times.
✓ Branch 14 → 16 taken 3 times.
|
122 | const QRect whole = !m_viewport.isEmpty() ? m_viewport.toAlignedRect() |
| 38 |
2/3✓ Branch 16 → 17 taken 3 times.
✓ Branch 17 → 18 taken 3 times.
✗ Branch 17 → 23 not taken.
|
3 | : m_painter->device() ? QRect(0, 0, m_painter->device()->width(), m_painter->device()->height()) |
| 39 |
4/4✓ Branch 18 → 19 taken 3 times.
✓ Branch 19 → 20 taken 3 times.
✓ Branch 20 → 21 taken 3 times.
✓ Branch 21 → 22 taken 3 times.
|
3 | : QRect(); |
| 40 |
1/1✓ Branch 25 → 26 taken 122 times.
|
122 | QRegion region(whole.adjusted(-64, -64, 64, 64)); |
| 41 |
2/2✓ Branch 26 → 27 taken 54 times.
✓ Branch 26 → 40 taken 68 times.
|
122 | if (wanted) |
| 42 |
2/2✓ Branch 38 → 30 taken 1124 times.
✓ Branch 38 → 39 taken 54 times.
|
1178 | for (const QRectF &box : wanted->boxes()) |
| 43 |
2/2✓ Branch 33 → 34 taken 1124 times.
✓ Branch 34 → 35 taken 1124 times.
|
1124 | region -= box.adjusted(-Cutouts::Clear - 1, -Cutouts::Clear, Cutouts::Clear + 1, Cutouts::Clear).toAlignedRect(); |
| 44 |
1/1✓ Branch 40 → 41 taken 122 times.
|
122 | m_painter->setClipRegion(region); |
| 45 | 122 | m_clipSet = true; | |
| 46 | 122 | m_clipped = wanted; | |
| 47 | 122 | m_clippedBoxes = boxes; | |
| 48 | 122 | } | |
| 49 | |||
| 50 | 1210 | void PainterSink::stroke(const QList<QPointF> &points, double width, const QList<double> &dash, const QColor &colour, | |
| 51 | double alpha, const Cutouts *cuts, bool closed) { | ||
| 52 |
3/6✓ Branch 3 → 4 taken 1210 times.
✗ Branch 3 → 5 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1210 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1210 times.
|
1210 | if (points.size() < 2 || alpha <= 0) |
| 53 | 44 | return; | |
| 54 |
7/7✓ Branch 10 → 11 taken 1208 times.
✓ Branch 10 → 16 taken 2 times.
✓ Branch 11 → 12 taken 1208 times.
✓ Branch 14 → 15 taken 44 times.
✓ Branch 14 → 16 taken 1164 times.
✓ Branch 17 → 18 taken 44 times.
✓ Branch 17 → 19 taken 1166 times.
|
1210 | if (!m_viewport.isEmpty() && !boundsOf(points).adjusted(-width - 2, -width - 2, width + 2, width + 2).intersects(m_viewport)) |
| 55 | 44 | return; | |
| 56 |
1/1✓ Branch 19 → 20 taken 1166 times.
|
1166 | clipFor(cuts); |
| 57 | 1166 | QColor shown = colour; | |
| 58 |
1/1✓ Branch 22 → 23 taken 1166 times.
|
1166 | shown.setAlphaF(std::clamp(colour.alphaF() * alpha, 0.0, 1.0)); |
| 59 |
2/2✓ Branch 24 → 25 taken 1166 times.
✓ Branch 25 → 26 taken 1166 times.
|
1166 | QPen pen(shown, std::max(width, 0.5), Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); |
| 60 |
2/2✓ Branch 28 → 29 taken 271 times.
✓ Branch 28 → 41 taken 895 times.
|
1166 | if (!dash.isEmpty()) { |
| 61 | 271 | QList<qreal> pattern; // QPen counts in pen widths | |
| 62 |
2/2✓ Branch 37 → 31 taken 668 times.
✓ Branch 37 → 38 taken 271 times.
|
939 | for (double d : dash) |
| 63 |
1/1✓ Branch 34 → 35 taken 668 times.
|
668 | pattern.append(std::max(0.1, d / std::max(width, 0.5))); |
| 64 |
1/1✓ Branch 38 → 39 taken 271 times.
|
271 | pen.setDashPattern(pattern); |
| 65 | 271 | } | |
| 66 |
1/1✓ Branch 41 → 42 taken 1166 times.
|
1166 | m_painter->setRenderHint(QPainter::Antialiasing, true); |
| 67 |
1/1✓ Branch 42 → 43 taken 1166 times.
|
1166 | m_painter->setPen(pen); |
| 68 |
1/1✓ Branch 43 → 44 taken 1166 times.
|
1166 | m_painter->setBrush(Qt::NoBrush); |
| 69 |
1/1✓ Branch 45 → 46 taken 1166 times.
|
1166 | QPainterPath path(points.first()); |
| 70 |
2/2✓ Branch 51 → 47 taken 16776 times.
✓ Branch 51 → 52 taken 1166 times.
|
17942 | for (qsizetype i = 1; i < points.size(); ++i) |
| 71 |
1/1✓ Branch 48 → 49 taken 16776 times.
|
16776 | path.lineTo(points.at(i)); |
| 72 |
2/2✓ Branch 52 → 53 taken 228 times.
✓ Branch 52 → 54 taken 938 times.
|
1166 | if (closed) |
| 73 |
1/1✓ Branch 53 → 54 taken 228 times.
|
228 | path.closeSubpath(); |
| 74 |
1/1✓ Branch 54 → 55 taken 1166 times.
|
1166 | m_painter->drawPath(path); |
| 75 | 1166 | } | |
| 76 | |||
| 77 | 49 | void PainterSink::fill(const QList<QPointF> &triangles, const QColor &colour, double alpha) { | |
| 78 |
3/6✓ Branch 2 → 3 taken 49 times.
✗ Branch 2 → 5 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 49 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 49 times.
|
49 | if (alpha <= 0 || triangles.size() < 3) |
| 79 | ✗ | return; | |
| 80 |
1/1✓ Branch 9 → 10 taken 49 times.
|
49 | clipFor(nullptr); |
| 81 | 49 | QColor shown = colour; | |
| 82 |
1/1✓ Branch 12 → 13 taken 49 times.
|
49 | shown.setAlphaF(std::clamp(colour.alphaF() * alpha, 0.0, 1.0)); |
| 83 | 49 | QPainterPath path; | |
| 84 |
1/1✓ Branch 14 → 15 taken 49 times.
|
49 | path.setFillRule(Qt::WindingFill); |
| 85 |
2/2✓ Branch 25 → 16 taken 5678 times.
✓ Branch 25 → 26 taken 49 times.
|
5727 | for (qsizetype i = 0; i + 2 < triangles.size(); i += 3) { |
| 86 |
1/1✓ Branch 17 → 18 taken 5678 times.
|
5678 | path.moveTo(triangles.at(i)); |
| 87 |
1/1✓ Branch 19 → 20 taken 5678 times.
|
5678 | path.lineTo(triangles.at(i + 1)); |
| 88 |
1/1✓ Branch 21 → 22 taken 5678 times.
|
5678 | path.lineTo(triangles.at(i + 2)); |
| 89 |
1/1✓ Branch 22 → 23 taken 5678 times.
|
5678 | path.closeSubpath(); |
| 90 | } | ||
| 91 |
1/1✓ Branch 26 → 27 taken 49 times.
|
49 | m_painter->setRenderHint(QPainter::Antialiasing, false); // no seams between the triangles |
| 92 |
1/1✓ Branch 27 → 28 taken 49 times.
|
49 | m_painter->setPen(Qt::NoPen); |
| 93 |
2/2✓ Branch 28 → 29 taken 49 times.
✓ Branch 29 → 30 taken 49 times.
|
49 | m_painter->setBrush(shown); |
| 94 |
2/2✓ Branch 31 → 32 taken 49 times.
✓ Branch 32 → 33 taken 49 times.
|
49 | m_painter->drawPath(path.simplified()); |
| 95 | 49 | } | |
| 96 | |||
| 97 | 597 | void PainterSink::triangle(QPointF a, QPointF b, QPointF c, const QColor &colour, double alpha, const Cutouts *cuts) { | |
| 98 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 597 times.
|
597 | if (alpha <= 0) |
| 99 | ✗ | return; | |
| 100 |
1/1✓ Branch 4 → 5 taken 597 times.
|
597 | clipFor(cuts); |
| 101 | 597 | QColor shown = colour; | |
| 102 |
1/1✓ Branch 7 → 8 taken 597 times.
|
597 | shown.setAlphaF(std::clamp(colour.alphaF() * alpha, 0.0, 1.0)); |
| 103 |
1/1✓ Branch 8 → 9 taken 597 times.
|
597 | m_painter->setRenderHint(QPainter::Antialiasing, true); |
| 104 |
1/1✓ Branch 9 → 10 taken 597 times.
|
597 | m_painter->setPen(Qt::NoPen); |
| 105 |
2/2✓ Branch 10 → 11 taken 597 times.
✓ Branch 11 → 12 taken 597 times.
|
597 | m_painter->setBrush(shown); |
| 106 | 597 | const QPointF corners[3] = {a, b, c}; | |
| 107 |
1/1✓ Branch 13 → 14 taken 597 times.
|
597 | m_painter->drawPolygon(corners, 3); |
| 108 | } | ||
| 109 | |||
| 110 | 50 | void PainterSink::band(const QList<QPointF> &outer, const QList<QPointF> &inner, const QColor &colour, double outerAlpha, | |
| 111 | double innerAlpha, bool closed) { | ||
| 112 | 50 | const int n = int(std::min(outer.size(), inner.size())); | |
| 113 |
1/2✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 50 times.
|
50 | if (n < 2) |
| 114 | ✗ | return; | |
| 115 |
1/1✓ Branch 7 → 8 taken 50 times.
|
50 | clipFor(nullptr); |
| 116 |
1/1✓ Branch 8 → 9 taken 50 times.
|
50 | m_painter->setRenderHint(QPainter::Antialiasing, false); |
| 117 |
1/1✓ Branch 9 → 10 taken 50 times.
|
50 | m_painter->setPen(Qt::NoPen); |
| 118 | 50 | QColor from = colour, to = colour; | |
| 119 |
1/1✓ Branch 12 → 13 taken 50 times.
|
50 | from.setAlphaF(std::clamp(colour.alphaF() * outerAlpha, 0.0, 1.0)); |
| 120 |
1/1✓ Branch 15 → 16 taken 50 times.
|
50 | to.setAlphaF(std::clamp(colour.alphaF() * innerAlpha, 0.0, 1.0)); |
| 121 |
1/2✓ Branch 16 → 17 taken 50 times.
✗ Branch 16 → 18 not taken.
|
50 | const int segments = closed ? n : n - 1; |
| 122 |
2/2✓ Branch 50 → 20 taken 5778 times.
✓ Branch 50 → 51 taken 50 times.
|
5828 | for (int i = 0; i < segments; ++i) { |
| 123 | 5778 | const int j = (i + 1) % n; | |
| 124 | 5778 | const QPointF quad[4] = {outer.at(i), outer.at(j), inner.at(j), inner.at(i)}; | |
| 125 |
8/12✓ Branch 25 → 26 taken 5778 times.
✗ Branch 25 → 31 not taken.
✓ Branch 26 → 27 taken 5778 times.
✓ Branch 27 → 28 taken 5778 times.
✓ Branch 29 → 30 taken 626 times.
✓ Branch 29 → 31 taken 5152 times.
✓ Branch 32 → 33 taken 5778 times.
✗ Branch 32 → 34 not taken.
✓ Branch 34 → 35 taken 626 times.
✓ Branch 34 → 36 taken 5152 times.
✗ Branch 60 → 61 not taken.
✗ Branch 60 → 62 not taken.
|
5778 | if (!m_viewport.isEmpty() && !boundsOf({quad[0], quad[1], quad[2], quad[3]}).intersects(m_viewport)) |
| 126 | 626 | continue; | |
| 127 |
1/1✓ Branch 40 → 41 taken 5152 times.
|
5152 | QLinearGradient gradient((quad[0] + quad[1]) / 2, (quad[2] + quad[3]) / 2); |
| 128 |
1/1✓ Branch 41 → 42 taken 5152 times.
|
5152 | gradient.setColorAt(0, from); |
| 129 |
1/1✓ Branch 42 → 43 taken 5152 times.
|
5152 | gradient.setColorAt(1, to); |
| 130 |
2/2✓ Branch 43 → 44 taken 5152 times.
✓ Branch 44 → 45 taken 5152 times.
|
5152 | m_painter->setBrush(gradient); |
| 131 |
1/1✓ Branch 46 → 47 taken 5152 times.
|
5152 | m_painter->drawPolygon(quad, 4); |
| 132 | 5152 | } | |
| 133 | } | ||
| 134 | |||
| 135 | } // namespace GraphGeometry | ||
| 136 |