obstaclesOf function
The obstacles walls make in host's band (spec 08 D7, decision 18),
sorted by a, then b, then handle. frame is host's.
Among the walls that are neither host nor degenerate, and whose
direction is not parallel to host's within wallJoin.angular:
- a T: an end of B that lies strictly inside
host's centreline (07 D4.1's strictlyInside,hostas the through wall). The interval is theu-range ofcap(End(B, k), Tee(host))'s points (07's own T cap onhost's near face, or B's own square end insidehost's body when the mitre limit clamps it) together with the points where B's two faces crosshost's near face, so a clamped T covers B's whole footprint inhost's band (D7 as amended by Task 3's review S1). Judged againsthostonly, whichever wall 07 picks as B's through wall; - an X: B's centreline crosses
host's more thanwallJoin.linearinside both (07 D4's X). The interval is theu-range of the four points where B's two faces crosshost's two faces.
Not obstacles: a free end that pokes into the band off the centreline, and a collinear overlapping wall (07 Ruling 07-4; it is parallel).
Implementation
List<Obstacle> obstaclesOf(
HostFrame frame, WorldWall host, List<WorldWall> walls) {
final toLocal = host.toWorld.invert();
final hn = Vector2(-host.d.y, host.d.x);
final (hl, hr) = host.offsets;
final hLen = (host.e - host.s).length;
Obstacle span(Iterable<Vector2> world, Handle wall) {
final us = [for (final q in world) frame.uOf(toLocal.transformPoint(q))];
return (a: us.reduce(math.min), b: us.reduce(math.max), wall: wall);
}
final out = <Obstacle>[];
for (final b in walls) {
if (b.handle == host.handle || b.degenerate) continue;
final cross = host.d.x * b.d.y - host.d.y * b.d.x;
if (cross.abs() <= wallJoin.angular) continue;
final bn = Vector2(-b.d.y, b.d.x);
final (bl, br) = b.offsets;
var tee = false;
for (final k in const [0, 1]) {
if (!strictlyInside(b.endpoint(k), host)) continue;
tee = true;
final end = End(b, k);
// The near face: the one on the side B's body goes to, as 07's cap.
final near = host.s + hn * (end.a.dot(hn) > 0 ? hl : hr);
out.add(span([
...cap(end, Tee(host)).points,
for (final bo in [bl, br])
if (intersect(near, host.d, b.s + bn * bo, b.d) case final q?) q,
], b.handle));
}
if (tee) continue;
// Where the centrelines cross, as parameters along each.
final w = b.s - host.s;
final uh = (w.x * b.d.y - w.y * b.d.x) / cross;
final ub = (w.x * host.d.y - w.y * host.d.x) / cross;
final bLen = (b.e - b.s).length;
if (!(uh > wallJoin.linear && uh < hLen - wallJoin.linear) ||
!(ub > wallJoin.linear && ub < bLen - wallJoin.linear)) {
continue;
}
out.add(span([
for (final ho in [hl, hr])
for (final bo in [bl, br])
if (intersect(host.s + hn * ho, host.d, b.s + bn * bo, b.d)
case final q?)
q,
], b.handle));
}
out.sort((x, y) {
final c = x.a.compareTo(y.a);
if (c != 0) return c;
final e = x.b.compareTo(y.b);
return e != 0 ? e : x.wall.value.compareTo(y.wall.value);
});
return out;
}