data/RelationGraphPack.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Packing the overview's clusters (declared in RelationGraphClusters.h, | ||
| 2 | // split from RelationGraphClusters.cpp). | ||
| 3 | #include "RelationGraphClusters.h" | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <cmath> | ||
| 7 | #include <numeric> | ||
| 8 | |||
| 9 | namespace RelationGraphLayout { | ||
| 10 | |||
| 11 | namespace { | ||
| 12 | |||
| 13 | // Shelf packing at one row width: the rectangles in `order`, a new row when | ||
| 14 | // the next one does not fit; the centres around (0, 0) and the bounds. | ||
| 15 | 375 | QList<QPointF> packRows(const QList<QSizeF> &sizes, const QList<int> &order, double rowWidth, double gap, | |
| 16 | QSizeF *bounds) { | ||
| 17 |
1/1✓ Branch 3 → 4 taken 375 times.
|
375 | QList<QPointF> centres(sizes.size()); |
| 18 |
3/5✓ Branch 4 → 5 taken 375 times.
✓ Branch 6 → 7 taken 375 times.
✓ Branch 6 → 8 taken 375 times.
✗ Branch 75 → 76 not taken.
✗ Branch 75 → 77 not taken.
|
750 | QList<QList<int>> rowsOf{QList<int>()}; |
| 19 | 375 | double cursor = 0; | |
| 20 |
2/2✓ Branch 30 → 10 taken 7600 times.
✓ Branch 30 → 31 taken 375 times.
|
7975 | for (int i : order) { |
| 21 |
7/7✓ Branch 11 → 12 taken 7600 times.
✓ Branch 13 → 14 taken 7225 times.
✓ Branch 13 → 18 taken 375 times.
✓ Branch 16 → 17 taken 796 times.
✓ Branch 16 → 18 taken 6429 times.
✓ Branch 19 → 20 taken 796 times.
✓ Branch 19 → 24 taken 6804 times.
|
7600 | if (!rowsOf.last().isEmpty() && cursor + sizes.at(i).width() > rowWidth) { |
| 22 |
1/1✓ Branch 21 → 22 taken 796 times.
|
796 | rowsOf.append(QList<int>()); |
| 23 | 796 | cursor = 0; | |
| 24 | } | ||
| 25 |
2/2✓ Branch 24 → 25 taken 7600 times.
✓ Branch 25 → 26 taken 7600 times.
|
7600 | rowsOf.last().append(i); |
| 26 | 7600 | cursor += sizes.at(i).width() + gap; | |
| 27 | } | ||
| 28 | 375 | double top = 0, widest = 0; | |
| 29 |
2/2✓ Branch 61 → 34 taken 1171 times.
✓ Branch 61 → 62 taken 375 times.
|
1546 | for (const QList<int> &row : std::as_const(rowsOf)) { |
| 30 | 1171 | double width = -gap, height = 0; | |
| 31 |
2/2✓ Branch 45 → 37 taken 7600 times.
✓ Branch 45 → 46 taken 1171 times.
|
8771 | for (int i : row) { |
| 32 | 7600 | width += sizes.at(i).width() + gap; | |
| 33 | 7600 | height = std::max(height, sizes.at(i).height()); | |
| 34 | } | ||
| 35 | 1171 | widest = std::max(widest, width); | |
| 36 | 1171 | double left = -width / 2; | |
| 37 |
2/2✓ Branch 58 → 49 taken 7600 times.
✓ Branch 58 → 59 taken 1171 times.
|
8771 | for (int i : row) { |
| 38 |
1/1✓ Branch 53 → 54 taken 7600 times.
|
7600 | centres[i] = QPointF(left + sizes.at(i).width() / 2, top + height / 2); |
| 39 | 7600 | left += sizes.at(i).width() + gap; | |
| 40 | } | ||
| 41 | 1171 | top += height + gap; | |
| 42 | } | ||
| 43 | 375 | const double shift = (top - gap) / 2; | |
| 44 |
4/4✓ Branch 62 → 63 taken 375 times.
✓ Branch 63 → 64 taken 375 times.
✓ Branch 69 → 65 taken 7600 times.
✓ Branch 69 → 70 taken 375 times.
|
7975 | for (QPointF &c : centres) |
| 45 | 7600 | c.ry() -= shift; | |
| 46 | 375 | *bounds = QSizeF(widest, top - gap); | |
| 47 | 375 | return centres; | |
| 48 | 375 | } | |
| 49 | |||
| 50 | } // namespace | ||
| 51 | |||
| 52 | 21 | QList<QPointF> pack(const QList<QSizeF> &sizes, double aspect, double gap) { | |
| 53 | 21 | const int count = int(sizes.size()); | |
| 54 |
2/2✓ Branch 3 → 4 taken 6 times.
✓ Branch 3 → 5 taken 15 times.
|
21 | if (count == 0) |
| 55 | 6 | return {}; | |
| 56 |
1/1✓ Branch 5 → 6 taken 15 times.
|
15 | QList<int> order(count); |
| 57 |
3/3✓ Branch 6 → 7 taken 15 times.
✓ Branch 7 → 8 taken 15 times.
✓ Branch 8 → 9 taken 15 times.
|
15 | std::iota(order.begin(), order.end(), 0); |
| 58 |
3/3✓ Branch 9 → 10 taken 15 times.
✓ Branch 10 → 11 taken 15 times.
✓ Branch 11 → 12 taken 15 times.
|
15 | std::stable_sort(order.begin(), order.end(), [&](int a, int b) { |
| 59 | 1538 | return sizes.at(a).width() * sizes.at(a).height() > sizes.at(b).width() * sizes.at(b).height(); | |
| 60 | }); | ||
| 61 | 15 | double widest = 0, total = 0; | |
| 62 |
2/2✓ Branch 20 → 14 taken 304 times.
✓ Branch 20 → 21 taken 15 times.
|
319 | for (const QSizeF &s : sizes) { |
| 63 | 304 | widest = std::max(widest, s.width()); | |
| 64 | 304 | total += s.width() + gap; | |
| 65 | } | ||
| 66 | // Row widths from the widest rectangle to all in one row; the packing whose | ||
| 67 | // bounds come nearest the asked shape wins (on a tie the narrower). | ||
| 68 | 15 | const double target = std::max(0.5, aspect); | |
| 69 | 15 | QList<QPointF> best; | |
| 70 | 15 | double bestMiss = 1e18; | |
| 71 | 15 | constexpr int Steps = 24; | |
| 72 |
2/2✓ Branch 33 → 23 taken 375 times.
✓ Branch 33 → 34 taken 15 times.
|
390 | for (int step = 0; step <= Steps; ++step) { |
| 73 | 375 | const double rowWidth = widest * std::pow(std::max(1.0, total / widest), double(step) / Steps); | |
| 74 | 375 | QSizeF bounds; | |
| 75 |
1/1✓ Branch 24 → 25 taken 375 times.
|
375 | const QList<QPointF> centres = packRows(sizes, order, rowWidth, gap, &bounds); |
| 76 | 375 | const double miss = std::abs(std::log(bounds.width() / std::max(1.0, bounds.height()) / target)); | |
| 77 |
2/2✓ Branch 29 → 30 taken 61 times.
✓ Branch 29 → 31 taken 314 times.
|
375 | if (miss < bestMiss - 1e-9) { |
| 78 | 61 | bestMiss = miss; | |
| 79 | 61 | best = centres; | |
| 80 | } | ||
| 81 | 375 | } | |
| 82 | 15 | return best; | |
| 83 | 15 | } | |
| 84 | |||
| 85 | } // namespace RelationGraphLayout | ||
| 86 |