discontinuedPackage jet_cad_floor_plan has been discontinued and replaced by jet_cad_2d_plan.

outerContours function

List<List<Vector2>> outerContours(
  1. List<RoomInput> inputs,
  2. Vector2 origin
)

The outer contour of each connected component of the arrangement of inputs (spec 10 D19; the re-review's T-8 cache, adopted at execution), its points relative to origin: the component's most negative face cycle, the walk around its outside, as traceRoom's step 8 finds a hole's contour. A component with no area (a free separator, or a free tree of them) is kept too: its walk goes out and back along each edge, so the crossing number of any point off it is even.

What the Room tool's hover cache reads: a point off every input that lies inside no contour (pointInRingXY, the tracer's own test) is in the arrangement's unbounded face, so traceRoom among the same inputs answers Unbounded there. The arrangement is traceRoom's own (steps 3-6, the same tolerance, every input), taken in the frame of origin rather than of a seed: the two can differ only in a decision within rounding of roomTrace.linear, that is for a point within about roomTrace.linear of an input, which the tracer answers SeedInWall. Both answers make no room.

Implementation

List<List<Vector2>> outerContours(List<RoomInput> inputs, Vector2 origin) {
  final sorted = [...inputs]
    ..sort((a, b) => a.source.value.compareTo(b.source.value));
  final arrangement = _arrange(sorted, [
    for (final input in sorted) [for (final p in input.points) p - origin],
  ]);
  final cycles = arrangement.cycles;
  final areas = arrangement.areas;
  final comp = arrangement.comp;
  final contour = <int, int>{}; // component -> its most negative cycle
  for (var c = 0; c < cycles.length; c++) {
    final best = contour[comp[c]];
    if (best == null || areas[c] < areas[best]) contour[comp[c]] = c;
  }
  final verts = arrangement.verts, from = arrangement.from;
  return [
    for (final c in contour.values) [for (final h in cycles[c]) verts[from[h]]],
  ];
}