discontinuedPackage jet_cad_floor_plan has been discontinued and replaced by jet_cad_2d_plan.

obstaclesOf function

List<Obstacle> obstaclesOf(
  1. HostFrame frame,
  2. WorldWall host,
  3. List<WorldWall> walls
)

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, host as the through wall). The interval is the u-range of cap(End(B, k), Tee(host))'s points (07's own T cap on host's near face, or B's own square end inside host's body when the mitre limit clamps it) together with the points where B's two faces cross host's near face, so a clamped T covers B's whole footprint in host's band (D7 as amended by Task 3's review S1). Judged against host only, whichever wall 07 picks as B's through wall;
  • an X: B's centreline crosses host's more than wallJoin.linear inside both (07 D4's X). The interval is the u-range of the four points where B's two faces cross host'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;
}