data/RelationGraphCompact.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // RelationGraphForce::compact — pulls separated blocks together (declared in | ||
| 2 | // RelationGraphForce.h, split from RelationGraphForce.cpp). | ||
| 3 | #include "RelationGraphForce.h" | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <cmath> | ||
| 7 | #include <numeric> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | namespace RelationGraphForce { | ||
| 11 | |||
| 12 | 7 | void compact(QList<QPointF> *positions, const QList<QSizeF> &sizes, const QList<bool> &pinned, double gap, | |
| 13 | const QList<QPair<int, int>> &links, int limit) { | ||
| 14 | 7 | QList<QPointF> &pos = *positions; | |
| 15 | 7 | const int n = int(pos.size()); | |
| 16 |
3/4✓ Branch 3 → 4 taken 4 times.
✓ Branch 3 → 5 taken 3 times.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 4 times.
|
7 | if (n < 2 || n > limit) |
| 17 | 3 | return; | |
| 18 | 4 | QPointF middle; | |
| 19 |
2/2✓ Branch 13 → 9 taken 34 times.
✓ Branch 13 → 14 taken 4 times.
|
38 | for (const QPointF &p : std::as_const(pos)) |
| 20 | 34 | middle += p; | |
| 21 | 4 | middle /= n; | |
| 22 |
1/1✓ Branch 15 → 16 taken 4 times.
|
4 | QList<QList<int>> neighbours(n); |
| 23 |
2/2✓ Branch 32 → 18 taken 83 times.
✓ Branch 32 → 33 taken 4 times.
|
87 | for (const auto &[a, b] : links) |
| 24 |
5/10✓ Branch 21 → 22 taken 83 times.
✗ Branch 21 → 30 not taken.
✓ Branch 22 → 23 taken 83 times.
✗ Branch 22 → 30 not taken.
✓ Branch 23 → 24 taken 83 times.
✗ Branch 23 → 30 not taken.
✓ Branch 24 → 25 taken 83 times.
✗ Branch 24 → 30 not taken.
✓ Branch 25 → 26 taken 83 times.
✗ Branch 25 → 30 not taken.
|
83 | if (a >= 0 && b >= 0 && a < n && b < n && a != b) { |
| 25 |
2/2✓ Branch 26 → 27 taken 83 times.
✓ Branch 27 → 28 taken 83 times.
|
83 | neighbours[a].append(b); |
| 26 |
2/2✓ Branch 28 → 29 taken 83 times.
✓ Branch 29 → 30 taken 83 times.
|
83 | neighbours[b].append(a); |
| 27 | } | ||
| 28 | 419 | const auto clear = [&](int i, QPointF at) { | |
| 29 |
2/2✓ Branch 27 → 3 taken 2644 times.
✓ Branch 27 → 28 taken 14 times.
|
2658 | for (int j = 0; j < n; ++j) |
| 30 |
6/6✓ Branch 3 → 4 taken 2552 times.
✓ Branch 3 → 23 taken 92 times.
✓ Branch 12 → 13 taken 1105 times.
✓ Branch 12 → 23 taken 1447 times.
✓ Branch 24 → 25 taken 405 times.
✓ Branch 24 → 26 taken 2239 times.
|
3749 | if (j != i && std::abs(at.x() - pos.at(j).x()) < (sizes.at(i).width() + sizes.at(j).width()) / 2 + gap && |
| 31 |
2/2✓ Branch 21 → 22 taken 405 times.
✓ Branch 21 → 23 taken 700 times.
|
1105 | std::abs(at.y() - pos.at(j).y()) < (sizes.at(i).height() + sizes.at(j).height()) / 2 + gap) |
| 32 | 405 | return false; | |
| 33 | 14 | return true; | |
| 34 | 4 | }; | |
| 35 | // Moves `i` towards `target` to the closest free point, in 5 % steps. | ||
| 36 | 16 | const auto pull = [&](int i, QPointF target) { | |
| 37 | 16 | const QPointF from = pos.at(i); | |
| 38 |
2/2✓ Branch 13 → 4 taken 151 times.
✓ Branch 13 → 14 taken 3 times.
|
154 | for (int step = 20; step > 0; --step) { |
| 39 | 151 | const QPointF at = target + (from - target) * (20 - step) / 20.0; | |
| 40 |
2/2✓ Branch 9 → 10 taken 13 times.
✓ Branch 9 → 12 taken 138 times.
|
151 | if (clear(i, at)) { |
| 41 |
1/1✓ Branch 10 → 11 taken 13 times.
|
13 | pos[i] = at; |
| 42 | 13 | return; | |
| 43 | } | ||
| 44 | } | ||
| 45 | 4 | }; | |
| 46 | // Then, beside its neighbours: the free place nearest to `target` on rings | ||
| 47 | // around it, when that is closer than the line got (a wide block on the | ||
| 48 | // line stops the pull at its far rim). Up to 150 rectangles (cost). | ||
| 49 | 4 | const auto settle = [&](int i, QPointF target) { | |
| 50 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 4 times.
|
4 | if (n > 150) |
| 51 | ✗ | return; | |
| 52 | 4 | const double distance = std::hypot(pos.at(i).x() - target.x(), pos.at(i).y() - target.y()); | |
| 53 | 4 | const double step = std::min(sizes.at(i).width(), sizes.at(i).height()) / 2 + gap; | |
| 54 |
3/4✓ Branch 37 → 38 taken 18 times.
✗ Branch 37 → 39 not taken.
✓ Branch 38 → 16 taken 15 times.
✓ Branch 38 → 39 taken 3 times.
|
18 | for (int ring = 1; ring <= 24 && ring * step < distance; ++ring) { |
| 55 | 15 | QPointF best; | |
| 56 | 15 | double bestDistance = distance; | |
| 57 |
2/2✓ Branch 32 → 17 taken 296 times.
✓ Branch 32 → 33 taken 15 times.
|
311 | for (int k = 0; k < 8 * ring; ++k) { |
| 58 | 296 | const double angle = 2 * M_PI * k / (8 * ring); | |
| 59 | 296 | const QPointF at = target + QPointF(std::cos(angle), std::sin(angle)) * (ring * step); | |
| 60 | 296 | const double d = std::hypot(at.x() - target.x(), at.y() - target.y()); | |
| 61 |
6/6✓ Branch 24 → 25 taken 268 times.
✓ Branch 24 → 28 taken 28 times.
✓ Branch 26 → 27 taken 1 time.
✓ Branch 26 → 28 taken 267 times.
✓ Branch 29 → 30 taken 1 time.
✓ Branch 29 → 31 taken 295 times.
|
296 | if (d < bestDistance && clear(i, at)) { |
| 62 | 1 | best = at; | |
| 63 | 1 | bestDistance = d; | |
| 64 | } | ||
| 65 | } | ||
| 66 |
2/2✓ Branch 33 → 34 taken 1 time.
✓ Branch 33 → 36 taken 14 times.
|
15 | if (bestDistance < distance) { |
| 67 |
1/1✓ Branch 34 → 35 taken 1 time.
|
1 | pos[i] = best; |
| 68 | 1 | return; | |
| 69 | } | ||
| 70 | } | ||
| 71 | 4 | }; | |
| 72 |
1/1✓ Branch 35 → 36 taken 4 times.
|
4 | std::vector<int> order(static_cast<size_t>(n)); |
| 73 | 4 | std::iota(order.begin(), order.end(), 0); | |
| 74 |
1/1✓ Branch 42 → 43 taken 4 times.
|
4 | std::sort(order.begin(), order.end(), [&](int a, int b) { |
| 75 | 144 | const double da = std::hypot(pos.at(a).x() - middle.x(), pos.at(a).y() - middle.y()); | |
| 76 | 144 | const double db = std::hypot(pos.at(b).x() - middle.x(), pos.at(b).y() - middle.y()); | |
| 77 |
2/2✓ Branch 14 → 15 taken 140 times.
✓ Branch 14 → 16 taken 4 times.
|
144 | return da != db ? da < db : a < b; |
| 78 | }); | ||
| 79 | // Screens are wider than tall: first onto the middle line (a wide band), then towards the middle. | ||
| 80 |
2/2✓ Branch 56 → 45 taken 34 times.
✓ Branch 56 → 57 taken 4 times.
|
38 | for (const int i : order) |
| 81 |
3/3✓ Branch 46 → 47 taken 34 times.
✓ Branch 47 → 48 taken 6 times.
✓ Branch 47 → 54 taken 28 times.
|
34 | if (!pinned.value(i)) { |
| 82 |
1/1✓ Branch 52 → 53 taken 6 times.
|
6 | pull(i, QPointF(pos.at(i).x(), middle.y())); |
| 83 |
1/1✓ Branch 53 → 54 taken 6 times.
|
6 | pull(i, middle); |
| 84 | } | ||
| 85 | // Then towards the linked ones; the larger blocks first stay, small ones come to them. | ||
| 86 |
1/1✓ Branch 59 → 60 taken 4 times.
|
4 | std::stable_sort(order.begin(), order.end(), [&](int a, int b) { |
| 87 | 112 | return sizes.at(a).width() * sizes.at(a).height() > sizes.at(b).width() * sizes.at(b).height(); | |
| 88 | }); | ||
| 89 |
2/2✓ Branch 92 → 61 taken 8 times.
✓ Branch 92 → 93 taken 4 times.
|
12 | for (int round = 0; round < 2; ++round) |
| 90 |
2/2✓ Branch 90 → 63 taken 68 times.
✓ Branch 90 → 91 taken 8 times.
|
76 | for (const int i : order) { |
| 91 |
7/7✓ Branch 64 → 65 taken 68 times.
✓ Branch 65 → 66 taken 12 times.
✓ Branch 65 → 69 taken 56 times.
✓ Branch 68 → 69 taken 8 times.
✓ Branch 68 → 70 taken 4 times.
✓ Branch 71 → 72 taken 64 times.
✓ Branch 71 → 73 taken 4 times.
|
68 | if (pinned.value(i) || neighbours.at(i).isEmpty()) |
| 92 | 64 | continue; | |
| 93 | 4 | QPointF target; | |
| 94 |
2/2✓ Branch 81 → 76 taken 26 times.
✓ Branch 81 → 82 taken 4 times.
|
30 | for (int j : neighbours.at(i)) |
| 95 | 26 | target += pos.at(j); | |
| 96 | 4 | target /= neighbours.at(i).size(); | |
| 97 |
1/1✓ Branch 85 → 86 taken 4 times.
|
4 | pull(i, target); |
| 98 |
1/1✓ Branch 86 → 87 taken 4 times.
|
4 | settle(i, target); |
| 99 | } | ||
| 100 | 4 | } | |
| 101 | |||
| 102 | } // namespace RelationGraphForce | ||
| 103 |