data/RelationGraphTerritories.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "RelationGraphTerritories.h" | ||
| 2 | |||
| 3 | #include <QLineF> | ||
| 4 | #include <QSet> | ||
| 5 | #include <algorithm> | ||
| 6 | #include <cmath> | ||
| 7 | #include <limits> | ||
| 8 | |||
| 9 | namespace RelationGraphTerritories { | ||
| 10 | |||
| 11 | namespace { | ||
| 12 | |||
| 13 | constexpr double Infinity = std::numeric_limits<double>::infinity(); | ||
| 14 | constexpr double PersonAir = 6; | ||
| 15 | |||
| 16 | // A box as its centre and half size (the signed distance needs nothing else). | ||
| 17 | struct Box { | ||
| 18 | double cx = 0; | ||
| 19 | double cy = 0; | ||
| 20 | double hw = 0; | ||
| 21 | double hh = 0; | ||
| 22 | }; | ||
| 23 | |||
| 24 | 50940 | Box boxOf(const QRectF &rect) { | |
| 25 | 50940 | return {rect.center().x(), rect.center().y(), rect.width() / 2, rect.height() / 2}; | |
| 26 | } | ||
| 27 | |||
| 28 | // The polynomial smooth minimum of a and b, blending within `width`. | ||
| 29 | 76833324 | double smoothMin(double a, double b, double width) { | |
| 30 | 76833324 | const double h = width - std::abs(a - b); | |
| 31 |
2/2✓ Branch 4 → 5 taken 648778 times.
✓ Branch 4 → 6 taken 76184546 times.
|
76833324 | return std::min(a, b) - (h > 0 ? h * h / (4 * width) : 0); |
| 32 | } | ||
| 33 | |||
| 34 | // Signed distance from (px, py) to the box (negative inside). | ||
| 35 | 77378771 | double boxDistance(const Box &box, double px, double py) { | |
| 36 | 77378771 | const double qx = std::abs(px - box.cx) - box.hw; | |
| 37 | 77378771 | const double qy = std::abs(py - box.cy) - box.hh; | |
| 38 | 77378771 | const double ox = std::max(qx, 0.0); | |
| 39 | 77378771 | const double oy = std::max(qy, 0.0); | |
| 40 | 77378771 | return std::sqrt(ox * ox + oy * oy) + std::min(std::max(qx, qy), 0.0); | |
| 41 | } | ||
| 42 | |||
| 43 | // Distance to a group of boxes joined smoothly into one shape. | ||
| 44 | 1111183 | double groupDistance(const std::vector<Box> &group, double px, double py, double join) { | |
| 45 | 1111183 | double distance = Infinity; | |
| 46 |
2/2✓ Branch 12 → 4 taken 77378771 times.
✓ Branch 12 → 13 taken 1111183 times.
|
78489954 | for (const Box &box : group) { |
| 47 |
1/1✓ Branch 5 → 6 taken 77378771 times.
|
77378771 | const double d = boxDistance(box, px, py); |
| 48 |
3/3✓ Branch 6 → 7 taken 76267588 times.
✓ Branch 6 → 9 taken 1111183 times.
✓ Branch 7 → 8 taken 76267588 times.
|
77378771 | distance = distance == Infinity ? d : smoothMin(distance, d, join); |
| 49 | } | ||
| 50 | 1111183 | return distance; | |
| 51 | } | ||
| 52 | |||
| 53 | struct Group { | ||
| 54 | std::vector<Box> boxes; | ||
| 55 | QRectF reach; // beyond these bounds the group cannot change the field | ||
| 56 | }; | ||
| 57 | |||
| 58 | 25615 | QRectF boundsOf(const std::vector<Box> &boxes) { | |
| 59 | 25615 | QRectF bounds; | |
| 60 |
2/2✓ Branch 9 → 4 taken 49537 times.
✓ Branch 9 → 10 taken 25615 times.
|
75152 | for (const Box &box : boxes) |
| 61 | 49537 | bounds |= QRectF(box.cx - box.hw, box.cy - box.hh, 2 * box.hw, 2 * box.hh); | |
| 62 | 25615 | return bounds; | |
| 63 | } | ||
| 64 | |||
| 65 | // Box-distance evaluations one territory may cost (S106-175). | ||
| 66 | constexpr double kMaximumFieldWork = 3.0e6; | ||
| 67 | |||
| 68 | 169 | void fillField(Field *field, const std::vector<Box> &own, const std::vector<Group> &rivals, const Settings &settings) { | |
| 69 | 169 | const double limit = settings.reach; | |
| 70 |
2/2✓ Branch 23 → 3 taken 9274 times.
✓ Branch 23 → 24 taken 169 times.
|
9443 | for (int j = 1; j + 1 < field->ny; ++j) { |
| 71 | 9274 | const double py = field->y + j * field->step; | |
| 72 |
2/2✓ Branch 21 → 4 taken 637916 times.
✓ Branch 21 → 22 taken 9274 times.
|
647190 | for (int i = 1; i + 1 < field->nx; ++i) { |
| 73 | 637916 | const double px = field->x + i * field->step; | |
| 74 | 637916 | const double mine = groupDistance(own, px, py, settings.join); | |
| 75 | 637916 | double value = limit - mine; | |
| 76 | // Well beyond the reach no rival matters: the smooth minimum is limit - mine. | ||
| 77 |
2/2✓ Branch 5 → 6 taken 565736 times.
✓ Branch 5 → 19 taken 72180 times.
|
637916 | if (mine <= limit + settings.corner) { |
| 78 | 565736 | double nearest = Infinity; | |
| 79 |
2/2✓ Branch 16 → 8 taken 3205315 times.
✓ Branch 16 → 17 taken 565736 times.
|
3771051 | for (const Group &rival : rivals) |
| 80 |
2/2✓ Branch 10 → 11 taken 473267 times.
✓ Branch 10 → 14 taken 2732048 times.
|
3205315 | if (rival.reach.contains(px, py)) |
| 81 |
1/1✓ Branch 11 → 12 taken 473267 times.
|
473267 | nearest = std::min(nearest, groupDistance(rival.boxes, px, py, settings.join)); |
| 82 |
1/1✓ Branch 17 → 18 taken 565736 times.
|
565736 | value = smoothMin(value, nearest - mine, settings.corner); |
| 83 | } | ||
| 84 | 637916 | field->values[size_t(j * field->nx + i)] = value; | |
| 85 | } | ||
| 86 | } | ||
| 87 | 169 | } | |
| 88 | |||
| 89 | // The closed zero lines of the field (marching squares). Crossings are keyed | ||
| 90 | // by grid edge — 2 × point for the edge to the right, 2 × point + 1 for the | ||
| 91 | // edge below — and each joins exactly two cells, so it has two links. | ||
| 92 | // Saddles follow the cell's mean. | ||
| 93 | 169 | QList<QList<QPointF>> traceLoops(const Field &field) { | |
| 94 | 169 | const int nx = field.nx; | |
| 95 | 169 | const int ny = field.ny; | |
| 96 | 169 | const std::vector<double> &v = field.values; | |
| 97 | 169 | const size_t size = size_t(2 * nx * ny); | |
| 98 |
1/1✓ Branch 4 → 5 taken 169 times.
|
338 | std::vector<QPointF> points(size); |
| 99 |
1/1✓ Branch 8 → 9 taken 169 times.
|
169 | std::vector<int> first(size, -1); |
| 100 |
1/1✓ Branch 12 → 13 taken 169 times.
|
169 | std::vector<int> second(size, -1); |
| 101 | 169 | std::vector<int> keys; | |
| 102 | 135120 | const auto pointOf = [&](int index) { | |
| 103 | 135120 | return QPointF(field.x + (index % nx) * field.step, field.y + (index / nx) * field.step); | |
| 104 | 169 | }; | |
| 105 | 90080 | const auto crossing = [&](int key, int a, int b) { | |
| 106 |
2/2✓ Branch 3 → 4 taken 45040 times.
✓ Branch 3 → 15 taken 45040 times.
|
90080 | if (first[size_t(key)] == -1) { |
| 107 | 45040 | const double t = v[size_t(a)] / (v[size_t(a)] - v[size_t(b)]); | |
| 108 | 45040 | points[size_t(key)] = pointOf(a) + (pointOf(b) - pointOf(a)) * t; | |
| 109 | 45040 | keys.push_back(key); | |
| 110 | } | ||
| 111 | 90080 | return key; | |
| 112 | 169 | }; | |
| 113 | 45040 | const auto join = [&](int p, int q) { | |
| 114 |
2/2✓ Branch 3 → 4 taken 4273 times.
✓ Branch 3 → 6 taken 40767 times.
|
45040 | (first[size_t(p)] == -1 ? first[size_t(p)] : second[size_t(p)]) = q; |
| 115 |
2/2✓ Branch 9 → 10 taken 40767 times.
✓ Branch 9 → 12 taken 4273 times.
|
45040 | (first[size_t(q)] == -1 ? first[size_t(q)] : second[size_t(q)]) = p; |
| 116 | 45209 | }; | |
| 117 |
2/2✓ Branch 91 → 15 taken 9443 times.
✓ Branch 91 → 92 taken 169 times.
|
9612 | for (int j = 0; j + 1 < ny; ++j) |
| 118 |
2/2✓ Branch 89 → 16 taken 658637 times.
✓ Branch 89 → 90 taken 9443 times.
|
668080 | for (int i = 0; i + 1 < nx; ++i) { |
| 119 | 658637 | const int tl = j * nx + i; | |
| 120 | 658637 | const int tr = tl + 1; | |
| 121 | 658637 | const int bl = tl + nx; | |
| 122 | 658637 | const int br = bl + 1; | |
| 123 |
6/6✓ Branch 17 → 18 taken 444533 times.
✓ Branch 17 → 19 taken 214104 times.
✓ Branch 21 → 22 taken 444533 times.
✓ Branch 21 → 23 taken 214104 times.
✓ Branch 25 → 26 taken 444533 times.
✓ Branch 25 → 27 taken 214104 times.
|
658637 | const int code = (v[size_t(tl)] > 0 ? 8 : 0) | (v[size_t(tr)] > 0 ? 4 : 0) | (v[size_t(br)] > 0 ? 2 : 0) | |
| 124 |
2/2✓ Branch 29 → 30 taken 444533 times.
✓ Branch 29 → 31 taken 214104 times.
|
658637 | (v[size_t(bl)] > 0 ? 1 : 0); |
| 125 |
4/4✓ Branch 32 → 33 taken 468156 times.
✓ Branch 32 → 34 taken 190481 times.
✓ Branch 33 → 34 taken 423116 times.
✓ Branch 33 → 35 taken 45040 times.
|
658637 | if (code == 0 || code == 15) |
| 126 | 613597 | continue; | |
| 127 | 20272 | const auto T = [&] { return crossing(2 * tl, tl, tr); }; | |
| 128 | 20272 | const auto B = [&] { return crossing(2 * bl, bl, br); }; | |
| 129 | 24768 | const auto L = [&] { return crossing(2 * tl + 1, tl, bl); }; | |
| 130 | 24768 | const auto R = [&] { return crossing(2 * tr + 1, tr, br); }; | |
| 131 | 45040 | const bool centre = v[size_t(tl)] + v[size_t(tr)] + v[size_t(br)] + v[size_t(bl)] > 0; | |
| 132 |
6/9✓ Branch 39 → 40 taken 4176 times.
✓ Branch 39 → 43 taken 4273 times.
✓ Branch 39 → 46 taken 16319 times.
✓ Branch 39 → 49 taken 4176 times.
✓ Branch 39 → 52 taken 11823 times.
✓ Branch 39 → 55 taken 4273 times.
✗ Branch 39 → 58 not taken.
✗ Branch 39 → 72 not taken.
✗ Branch 39 → 86 not taken.
|
45040 | switch (code) { |
| 133 |
2/2✓ Branch 40 → 41 taken 4176 times.
✓ Branch 41 → 42 taken 4176 times.
|
4176 | case 1: case 14: join(L(), B()); break; |
| 134 |
2/2✓ Branch 43 → 44 taken 4273 times.
✓ Branch 44 → 45 taken 4273 times.
|
4273 | case 2: case 13: join(B(), R()); break; |
| 135 |
2/2✓ Branch 46 → 47 taken 16319 times.
✓ Branch 47 → 48 taken 16319 times.
|
16319 | case 3: case 12: join(L(), R()); break; |
| 136 |
2/2✓ Branch 49 → 50 taken 4176 times.
✓ Branch 50 → 51 taken 4176 times.
|
4176 | case 4: case 11: join(T(), R()); break; |
| 137 |
2/2✓ Branch 52 → 53 taken 11823 times.
✓ Branch 53 → 54 taken 11823 times.
|
11823 | case 6: case 9: join(T(), B()); break; |
| 138 |
2/2✓ Branch 55 → 56 taken 4273 times.
✓ Branch 56 → 57 taken 4273 times.
|
4273 | case 7: case 8: join(T(), L()); break; |
| 139 | ✗ | case 5: | |
| 140 | ✗ | if (centre) { | |
| 141 | ✗ | join(T(), L()); | |
| 142 | ✗ | join(B(), R()); | |
| 143 | } else { | ||
| 144 | ✗ | join(T(), R()); | |
| 145 | ✗ | join(L(), B()); | |
| 146 | } | ||
| 147 | ✗ | break; | |
| 148 | ✗ | case 10: | |
| 149 | ✗ | if (centre) { | |
| 150 | ✗ | join(T(), R()); | |
| 151 | ✗ | join(L(), B()); | |
| 152 | } else { | ||
| 153 | ✗ | join(T(), L()); | |
| 154 | ✗ | join(B(), R()); | |
| 155 | } | ||
| 156 | ✗ | break; | |
| 157 | ✗ | default: | |
| 158 | ✗ | break; | |
| 159 | } | ||
| 160 | } | ||
| 161 | 169 | QList<QList<QPointF>> loops; | |
| 162 |
1/1✓ Branch 94 → 95 taken 169 times.
|
169 | std::vector<char> done(size, 0); |
| 163 |
2/2✓ Branch 133 → 98 taken 45040 times.
✓ Branch 133 → 134 taken 169 times.
|
45209 | for (int start : keys) { |
| 164 |
2/2✓ Branch 100 → 101 taken 43937 times.
✓ Branch 100 → 102 taken 1103 times.
|
45040 | if (done[size_t(start)]) |
| 165 | 43937 | continue; | |
| 166 | 1103 | QList<QPointF> loop; | |
| 167 | 1103 | int previous = -1; | |
| 168 | 1103 | int key = start; | |
| 169 |
5/6✓ Branch 120 → 121 taken 45040 times.
✓ Branch 120 → 124 taken 1103 times.
✓ Branch 122 → 123 taken 45040 times.
✗ Branch 122 → 124 not taken.
✓ Branch 125 → 103 taken 45040 times.
✓ Branch 125 → 126 taken 1103 times.
|
46143 | while (key != -1 && !done[size_t(key)]) { |
| 170 | 45040 | done[size_t(key)] = 1; | |
| 171 |
1/1✓ Branch 105 → 106 taken 45040 times.
|
45040 | loop.append(points[size_t(key)]); |
| 172 | 45040 | const int a = first[size_t(key)]; | |
| 173 | 45040 | const int b = second[size_t(key)]; | |
| 174 |
8/10✓ Branch 108 → 109 taken 45040 times.
✗ Branch 108 → 112 not taken.
✓ Branch 109 → 110 taken 22520 times.
✓ Branch 109 → 112 taken 22520 times.
✓ Branch 111 → 112 taken 1103 times.
✓ Branch 111 → 118 taken 21417 times.
✓ Branch 112 → 113 taken 23623 times.
✗ Branch 112 → 116 not taken.
✓ Branch 114 → 115 taken 22520 times.
✓ Branch 114 → 116 taken 1103 times.
|
45040 | const int next = a != -1 && a != previous && !done[size_t(a)] ? a : b != -1 && !done[size_t(b)] ? b : -1; |
| 175 | 45040 | previous = key; | |
| 176 | 45040 | key = next; | |
| 177 | } | ||
| 178 |
1/2✓ Branch 127 → 128 taken 1103 times.
✗ Branch 127 → 129 not taken.
|
1103 | if (loop.size() >= 3) |
| 179 |
1/1✓ Branch 128 → 129 taken 1103 times.
|
1103 | loops.append(loop); |
| 180 | 1103 | } | |
| 181 | 338 | return loops; | |
| 182 | 169 | } | |
| 183 | |||
| 184 | 44269 | double overlapArea(const QRectF &a, const QRectF &b) { | |
| 185 | 44269 | const QRectF both = a.intersected(b); | |
| 186 |
2/2✓ Branch 4 → 5 taken 38425 times.
✓ Branch 4 → 6 taken 5844 times.
|
44269 | return both.isEmpty() ? 0 : both.width() * both.height(); |
| 187 | } | ||
| 188 | |||
| 189 | 29341 | double gapBetween(const QRectF &a, const QRectF &b) { | |
| 190 |
1/1✓ Branch 6 → 7 taken 29341 times.
|
29341 | const double dx = std::max({0.0, a.left() - b.right(), b.left() - a.right()}); |
| 191 |
1/1✓ Branch 11 → 12 taken 29341 times.
|
29341 | const double dy = std::max({0.0, a.top() - b.bottom(), b.top() - a.bottom()}); |
| 192 | 29341 | return std::sqrt(dx * dx + dy * dy); | |
| 193 | } | ||
| 194 | |||
| 195 | // The segment from a to b crosses the box (Liang–Barsky). | ||
| 196 | 261956 | bool crosses(const QRectF &box, QPointF a, QPointF b) { | |
| 197 | 261956 | const double dx = b.x() - a.x(); | |
| 198 | 261956 | const double dy = b.y() - a.y(); | |
| 199 | 261956 | double t0 = 0; | |
| 200 | 261956 | double t1 = 1; | |
| 201 | 648940 | const auto clip = [&](double p, double q) { | |
| 202 |
2/2✓ Branch 2 → 3 taken 201086 times.
✓ Branch 2 → 4 taken 447854 times.
|
648940 | if (p == 0) |
| 203 | 201086 | return q >= 0; | |
| 204 | 447854 | const double t = q / p; | |
| 205 |
2/2✓ Branch 4 → 5 taken 237711 times.
✓ Branch 4 → 7 taken 210143 times.
|
447854 | if (p < 0) |
| 206 | 237711 | t0 = std::max(t0, t); | |
| 207 | else | ||
| 208 | 210143 | t1 = std::min(t1, t); | |
| 209 | 447854 | return t0 <= t1; | |
| 210 | 261956 | }; | |
| 211 |
8/8✓ Branch 9 → 10 taken 219348 times.
✓ Branch 9 → 23 taken 42608 times.
✓ Branch 13 → 14 taken 109138 times.
✓ Branch 13 → 23 taken 110210 times.
✓ Branch 17 → 18 taken 58498 times.
✓ Branch 17 → 23 taken 50640 times.
✓ Branch 21 → 22 taken 12578 times.
✓ Branch 21 → 23 taken 45920 times.
|
320454 | return clip(-dx, a.x() - box.left()) && clip(dx, box.right() - a.x()) && clip(-dy, a.y() - box.top()) && |
| 212 | 320454 | clip(dy, box.bottom() - a.y()); | |
| 213 | } | ||
| 214 | |||
| 215 | } // namespace | ||
| 216 | |||
| 217 | 342107 | double Field::at(QPointF point) const { | |
| 218 |
3/4✓ Branch 2 → 3 taken 322904 times.
✓ Branch 2 → 4 taken 19203 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 322904 times.
|
342107 | if (nx < 2 || ny < 2) |
| 219 | 19203 | return -1; | |
| 220 | 322904 | const double gx = (point.x() - x) / step; | |
| 221 | 322904 | const double gy = (point.y() - y) / step; | |
| 222 | 322904 | const int i = int(std::floor(gx)); | |
| 223 | 322904 | const int j = int(std::floor(gy)); | |
| 224 |
8/8✓ Branch 7 → 8 taken 305602 times.
✓ Branch 7 → 11 taken 17302 times.
✓ Branch 8 → 9 taken 279634 times.
✓ Branch 8 → 11 taken 25968 times.
✓ Branch 9 → 10 taken 256520 times.
✓ Branch 9 → 11 taken 23114 times.
✓ Branch 10 → 11 taken 6593 times.
✓ Branch 10 → 12 taken 249927 times.
|
322904 | if (i < 0 || j < 0 || i + 1 >= nx || j + 1 >= ny) |
| 225 | 72977 | return -1; | |
| 226 | 249927 | const double fx = gx - i; | |
| 227 | 249927 | const double fy = gy - j; | |
| 228 | 999708 | const auto value = [&](int ii, int jj) { return values[size_t(jj * nx + ii)]; }; | |
| 229 | 249927 | const double top = value(i, j) * (1 - fx) + value(i + 1, j) * fx; | |
| 230 | 249927 | const double bottom = value(i, j + 1) * (1 - fx) + value(i + 1, j + 1) * fx; | |
| 231 | 249927 | return top * (1 - fy) + bottom * fy; | |
| 232 | } | ||
| 233 | |||
| 234 | 42 | QList<Territory> build(const QList<QRectF> &boxes, const QList<QList<int>> &families, const QRectF &canvas, | |
| 235 | const Settings &settings) { | ||
| 236 | 42 | QList<Territory> territories; | |
| 237 |
4/6✓ Branch 2 → 3 taken 52357 times.
✗ Branch 2 → 9 not taken.
✓ Branch 4 → 5 taken 52357 times.
✗ Branch 4 → 9 not taken.
✓ Branch 7 → 8 taken 52343 times.
✓ Branch 7 → 9 taken 14 times.
|
52357 | const auto drawn = [&](int index) { return index >= 0 && index < boxes.size() && !boxes[index].isEmpty(); }; |
| 238 | 42 | QList<QList<int>> memberSets; | |
| 239 | 42 | QSet<int> inFamily; | |
| 240 |
2/2✓ Branch 32 → 5 taken 171 times.
✓ Branch 32 → 33 taken 42 times.
|
213 | for (const QList<int> &family : families) { |
| 241 | 171 | QList<int> members; | |
| 242 |
2/2✓ Branch 19 → 8 taken 1405 times.
✓ Branch 19 → 20 taken 171 times.
|
1576 | for (int member : family) |
| 243 |
5/6✓ Branch 10 → 11 taken 1403 times.
✓ Branch 10 → 14 taken 2 times.
✓ Branch 12 → 13 taken 1403 times.
✗ Branch 12 → 14 not taken.
✓ Branch 15 → 16 taken 1403 times.
✓ Branch 15 → 17 taken 2 times.
|
1405 | if (drawn(member) && !members.contains(member)) |
| 244 |
1/1✓ Branch 16 → 17 taken 1403 times.
|
1403 | members.append(member); |
| 245 |
1/1✓ Branch 20 → 21 taken 171 times.
|
171 | memberSets.append(members); |
| 246 |
4/4✓ Branch 21 → 22 taken 171 times.
✓ Branch 22 → 23 taken 171 times.
✓ Branch 28 → 24 taken 1403 times.
✓ Branch 28 → 29 taken 171 times.
|
1574 | for (int member : members) |
| 247 |
1/1✓ Branch 25 → 26 taken 1403 times.
|
1403 | inFamily.insert(member); |
| 248 | 171 | } | |
| 249 | // Off-canvas parts are not traced; the margin keeps the canvas's edge out of the outlines. | ||
| 250 | 42 | const QRectF traced = canvas.adjusted(-settings.reach, -settings.reach, settings.reach, settings.reach); | |
| 251 | 42 | const double margin = settings.reach + settings.corner; | |
| 252 |
2/2✓ Branch 143 → 35 taken 171 times.
✓ Branch 143 → 144 taken 42 times.
|
213 | for (int a = 0; a < memberSets.size(); ++a) { |
| 253 | 171 | Territory territory; | |
| 254 |
1/1✓ Branch 35 → 36 taken 171 times.
|
171 | const QList<int> &members = memberSets[a]; |
| 255 | 171 | std::vector<Box> own; | |
| 256 | 171 | QRectF bounds; | |
| 257 |
2/2✓ Branch 46 → 38 taken 1403 times.
✓ Branch 46 → 47 taken 171 times.
|
1574 | for (int member : members) { |
| 258 |
1/1✓ Branch 41 → 42 taken 1403 times.
|
1403 | own.push_back(boxOf(boxes[member])); |
| 259 | 1403 | bounds |= boxes[member]; | |
| 260 | } | ||
| 261 | 171 | const double pad = settings.reach + 2 * settings.step; | |
| 262 | 171 | const QRectF area = bounds.adjusted(-pad, -pad, pad, pad).intersected(traced); | |
| 263 |
6/8✓ Branch 50 → 51 taken 169 times.
✓ Branch 50 → 55 taken 2 times.
✓ Branch 52 → 53 taken 169 times.
✗ Branch 52 → 55 not taken.
✗ Branch 54 → 55 not taken.
✓ Branch 54 → 56 taken 169 times.
✓ Branch 57 → 58 taken 2 times.
✓ Branch 57 → 60 taken 169 times.
|
171 | if (own.empty() || area.width() < 2 * settings.step || area.height() < 2 * settings.step) { |
| 264 |
1/1✓ Branch 58 → 59 taken 2 times.
|
2 | territories.append(territory); |
| 265 | 2 | continue; | |
| 266 | } | ||
| 267 | // Rivals near enough to bound the territory somewhere in its area. | ||
| 268 | 169 | const QRectF near = area.adjusted(-margin, -margin, margin, margin); | |
| 269 | 169 | std::vector<Group> rivals; | |
| 270 | 25623 | const auto addRival = [&](std::vector<Box> group) { | |
| 271 |
2/2✓ Branch 3 → 4 taken 8 times.
✓ Branch 3 → 5 taken 25615 times.
|
25623 | if (group.empty()) |
| 272 | 8 | return; | |
| 273 | 25615 | const QRectF reach = boundsOf(group).adjusted(-margin, -margin, margin, margin); | |
| 274 |
2/2✓ Branch 8 → 9 taken 1523 times.
✓ Branch 8 → 14 taken 24092 times.
|
25615 | if (reach.intersects(near)) |
| 275 | 3046 | rivals.push_back({std::move(group), reach}); | |
| 276 |
1/3✓ Branch 11 → 12 taken 1523 times.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 20 not taken.
|
1523 | }; |
| 277 |
2/2✓ Branch 86 → 62 taken 2757 times.
✓ Branch 86 → 87 taken 169 times.
|
2926 | for (int o = 0; o < memberSets.size(); ++o) { |
| 278 |
2/2✓ Branch 62 → 63 taken 169 times.
✓ Branch 62 → 64 taken 2588 times.
|
2757 | if (o == a) |
| 279 | 169 | continue; | |
| 280 | 2588 | std::vector<Box> group; | |
| 281 |
5/5✓ Branch 64 → 65 taken 2588 times.
✓ Branch 65 → 66 taken 2588 times.
✓ Branch 66 → 67 taken 2588 times.
✓ Branch 77 → 68 taken 26502 times.
✓ Branch 77 → 78 taken 2588 times.
|
29090 | for (int member : memberSets[o]) |
| 282 |
1/2✓ Branch 70 → 71 taken 26502 times.
✗ Branch 70 → 75 not taken.
|
26502 | if (!members.contains(member)) |
| 283 |
1/1✓ Branch 73 → 74 taken 26502 times.
|
26502 | group.push_back(boxOf(boxes[member])); |
| 284 |
1/1✓ Branch 80 → 81 taken 2588 times.
|
2588 | addRival(std::move(group)); |
| 285 | 2588 | } | |
| 286 |
2/2✓ Branch 106 → 88 taken 50952 times.
✓ Branch 106 → 107 taken 169 times.
|
51121 | for (int n = 0; n < boxes.size(); ++n) |
| 287 |
6/6✓ Branch 89 → 90 taken 50940 times.
✓ Branch 89 → 93 taken 12 times.
✓ Branch 91 → 92 taken 23035 times.
✓ Branch 91 → 93 taken 27905 times.
✓ Branch 94 → 95 taken 23035 times.
✓ Branch 94 → 104 taken 27917 times.
|
50952 | if (drawn(n) && !inFamily.contains(n)) |
| 288 |
2/2✓ Branch 99 → 100 taken 23035 times.
✓ Branch 100 → 101 taken 23035 times.
|
69105 | addRival({boxOf(boxes[n])}); |
| 289 | 169 | Field &field = territory.field; | |
| 290 | 169 | field.x = area.x(); | |
| 291 | 169 | field.y = area.y(); | |
| 292 | 169 | field.step = settings.step; | |
| 293 | 169 | field.nx = int(std::ceil(area.width() / settings.step)) + 1; | |
| 294 | 169 | field.ny = int(std::ceil(area.height() / settings.step)) + 1; | |
| 295 | // S106-175: the work of a territory — grid points × the boxes measured | ||
| 296 | // at each — is capped; beyond it the grid gets coarser (the outline is | ||
| 297 | // a soft area, a coarser grid only rounds it more). | ||
| 298 | 169 | qsizetype boxesPerPoint = qsizetype(own.size()); | |
| 299 |
2/2✓ Branch 118 → 114 taken 1523 times.
✓ Branch 118 → 119 taken 169 times.
|
1692 | for (const Group &rival : rivals) |
| 300 | 1523 | boxesPerPoint += qsizetype(rival.boxes.size()); | |
| 301 | 169 | const double work = double(field.nx) * double(field.ny) * double(std::max<qsizetype>(1, boxesPerPoint)); | |
| 302 |
2/2✓ Branch 120 → 121 taken 6 times.
✓ Branch 120 → 124 taken 163 times.
|
169 | if (work > kMaximumFieldWork) { |
| 303 | 6 | field.step = settings.step * std::sqrt(work / kMaximumFieldWork); | |
| 304 | 6 | field.nx = int(std::ceil(area.width() / field.step)) + 1; | |
| 305 | 6 | field.ny = int(std::ceil(area.height() / field.step)) + 1; | |
| 306 | } | ||
| 307 | // fillField sets the inner points; the outer ring stays negative, so every loop closes. | ||
| 308 |
1/1✓ Branch 124 → 125 taken 169 times.
|
169 | field.values.assign(size_t(field.nx * field.ny), -1); |
| 309 |
1/1✓ Branch 125 → 126 taken 169 times.
|
169 | fillField(&field, own, rivals, settings); |
| 310 |
1/1✓ Branch 126 → 127 taken 169 times.
|
169 | territory.loops = traceLoops(field); |
| 311 |
1/1✓ Branch 129 → 130 taken 169 times.
|
169 | territories.append(territory); |
| 312 | 173 | } | |
| 313 | 42 | return territories; | |
| 314 | 42 | } | |
| 315 | |||
| 316 | 76 | int territoryAt(const QList<Territory> &territories, QPointF point) { | |
| 317 | 76 | int best = -1; | |
| 318 | 76 | double depth = 0; | |
| 319 |
2/2✓ Branch 9 → 3 taken 158 times.
✓ Branch 9 → 10 taken 76 times.
|
234 | for (int a = 0; a < territories.size(); ++a) { |
| 320 | 158 | const double value = territories[a].field.at(point); | |
| 321 |
2/2✓ Branch 5 → 6 taken 15 times.
✓ Branch 5 → 7 taken 143 times.
|
158 | if (value > depth) { |
| 322 | 15 | best = a; | |
| 323 | 15 | depth = value; | |
| 324 | } | ||
| 325 | } | ||
| 326 | 76 | return best; | |
| 327 | } | ||
| 328 | |||
| 329 | 7 | QRectF labelSpot(const Territory &territory, const QList<QRectF> &members, QSizeF size, const QList<QRectF> &people, | |
| 330 | const QList<QList<QPointF>> &lines, const QList<QRectF> &keepClear, const QRectF &canvas) { | ||
| 331 | 7 | const Field &field = territory.field; | |
| 332 |
6/8✓ Branch 2 → 3 taken 6 times.
✓ Branch 2 → 6 taken 1 time.
✓ Branch 3 → 4 taken 6 times.
✗ Branch 3 → 6 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 6 times.
✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 10 taken 6 times.
|
7 | if (field.nx < 3 || field.ny < 3 || members.isEmpty()) |
| 333 | 1 | return {}; | |
| 334 | 6 | QPointF middle; | |
| 335 |
2/2✓ Branch 19 → 12 taken 18 times.
✓ Branch 19 → 20 taken 6 times.
|
24 | for (const QRectF &member : members) |
| 336 | 18 | middle += member.center() / double(members.size()); | |
| 337 | 6 | const QRectF area(field.x, field.y, (field.nx - 1) * field.step, (field.ny - 1) * field.step); | |
| 338 | 6 | QList<QRectF> nearPeople; | |
| 339 |
2/2✓ Branch 29 → 23 taken 92 times.
✓ Branch 29 → 30 taken 6 times.
|
98 | for (const QRectF &person : people) |
| 340 |
2/2✓ Branch 25 → 26 taken 25 times.
✓ Branch 25 → 27 taken 67 times.
|
92 | if (person.intersects(area)) |
| 341 |
1/1✓ Branch 26 → 27 taken 25 times.
|
25 | nearPeople.append(person); |
| 342 | 6 | QList<QList<QPointF>> nearLines; | |
| 343 |
2/2✓ Branch 47 → 32 taken 122 times.
✓ Branch 47 → 48 taken 6 times.
|
128 | for (const QList<QPointF> &line : lines) { |
| 344 | 122 | QRectF bounds; | |
| 345 |
2/2✓ Branch 41 → 35 taken 694 times.
✓ Branch 41 → 42 taken 122 times.
|
816 | for (QPointF point : line) |
| 346 | 694 | bounds |= QRectF(point, QSizeF(0.01, 0.01)); | |
| 347 |
2/2✓ Branch 43 → 44 taken 51 times.
✓ Branch 43 → 45 taken 71 times.
|
122 | if (bounds.intersects(area)) |
| 348 |
1/1✓ Branch 44 → 45 taken 51 times.
|
51 | nearLines.append(line); |
| 349 | } | ||
| 350 | 6 | const QRectF inside = canvas.adjusted(4, 4, -4, -4); | |
| 351 | 6 | const double w = size.width(); | |
| 352 | 6 | const double h = size.height(); | |
| 353 | 6 | const QPointF probes[] = {{0, 0}, {0.5, 0}, {1, 0}, {0, 1}, {0.5, 1}, {1, 1}}; | |
| 354 | 6 | QRectF best; | |
| 355 | 6 | double bestScore = Infinity; | |
| 356 | // Every second grid point is a candidate centre. | ||
| 357 |
2/2✓ Branch 118 → 52 taken 258 times.
✓ Branch 118 → 119 taken 6 times.
|
264 | for (int j = 1; j + 1 < field.ny; j += 2) |
| 358 |
2/2✓ Branch 116 → 53 taken 10878 times.
✓ Branch 116 → 117 taken 258 times.
|
11136 | for (int i = 1; i + 1 < field.nx; i += 2) { |
| 359 | 10878 | const double centre = field.values[size_t(j * field.nx + i)]; | |
| 360 |
2/2✓ Branch 54 → 55 taken 3737 times.
✓ Branch 54 → 56 taken 7141 times.
|
10878 | if (centre <= 0) |
| 361 | 3737 | continue; | |
| 362 | 7141 | const QPointF at(field.x + i * field.step, field.y + j * field.step); | |
| 363 | 7141 | const QRectF box(at.x() - w / 2, at.y() - h / 2, w, h); | |
| 364 | // Ranks by weight: off the canvas, then over a person or a control, then lines, then outside. | ||
| 365 |
1/2✓ Branch 61 → 62 taken 7141 times.
✗ Branch 61 → 63 not taken.
|
7141 | double score = inside.contains(box) ? 0 : 1e9; |
| 366 |
2/2✓ Branch 70 → 66 taken 14928 times.
✓ Branch 70 → 71 taken 7141 times.
|
22069 | for (const QRectF &clear : keepClear) |
| 367 | 14928 | score += overlapArea(box, clear) * 1000; | |
| 368 | 7141 | double clearance = 36; | |
| 369 |
4/4✓ Branch 71 → 72 taken 7141 times.
✓ Branch 72 → 73 taken 7141 times.
✓ Branch 81 → 74 taken 29341 times.
✓ Branch 81 → 82 taken 7141 times.
|
36482 | for (const QRectF &person : nearPeople) { |
| 370 | // A few pixels of air around every person's box (text runs to its edge). | ||
| 371 | 29341 | score += overlapArea(box, person.adjusted(-PersonAir, -PersonAir, PersonAir, PersonAir)) * 1000; | |
| 372 |
1/1✓ Branch 77 → 78 taken 29341 times.
|
29341 | clearance = std::min(clearance, gapBetween(box, person)); |
| 373 | } | ||
| 374 |
4/4✓ Branch 82 → 83 taken 7141 times.
✓ Branch 83 → 84 taken 7141 times.
✓ Branch 97 → 85 taken 58140 times.
✓ Branch 97 → 98 taken 7141 times.
|
65281 | for (const QList<QPointF> &line : nearLines) |
| 375 |
2/2✓ Branch 94 → 87 taken 261956 times.
✓ Branch 94 → 95 taken 45562 times.
|
307518 | for (qsizetype k = 0; k + 1 < line.size(); ++k) |
| 376 |
2/2✓ Branch 90 → 91 taken 12578 times.
✓ Branch 90 → 92 taken 249378 times.
|
261956 | if (crosses(box, line[k], line[k + 1])) { |
| 377 | 12578 | score += 900; // a line through the name is cut out around it | |
| 378 | 12578 | break; | |
| 379 | } | ||
| 380 |
2/2✓ Branch 108 → 99 taken 42846 times.
✓ Branch 108 → 109 taken 7141 times.
|
49987 | for (QPointF probe : probes) |
| 381 |
2/2✓ Branch 105 → 106 taken 10619 times.
✓ Branch 105 → 107 taken 32227 times.
|
42846 | if (field.at(QPointF(box.x() + probe.x() * w, box.y() + probe.y() * h)) < 0) |
| 382 | 10619 | score += 600; | |
| 383 |
1/1✓ Branch 110 → 111 taken 7141 times.
|
7141 | score += QLineF(at, middle).length() * 0.15 - 2 * std::min(centre - h / 2, 30.0) - 4 * clearance; |
| 384 |
2/2✓ Branch 112 → 113 taken 111 times.
✓ Branch 112 → 114 taken 7030 times.
|
7141 | if (score < bestScore) { |
| 385 | 111 | bestScore = score; | |
| 386 | 111 | best = box; | |
| 387 | } | ||
| 388 | } | ||
| 389 | 6 | return best; | |
| 390 | 6 | } | |
| 391 | |||
| 392 | } // namespace RelationGraphTerritories | ||
| 393 |