MailJDTourDownloadDocsQualityTestsCoverage

Which source lines and branches the tests execute. Lines 91.5 %, branches 75.0 % (minimum 85 % and 72 %). main @ 399ffe3d · 21 October 2026 · pipeline 7238.

GCC Code Coverage Report


Directory: src/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 94.5% 275 / 0 / 291
Functions: 100.0% 25 / 0 / 25
Branches: 85.7% 234 / 0 / 273

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