discontinuedPackage jet_cad_floor_plan has been discontinued and replaced by jet_cad_2d_plan.

faceRunsAmong function

List<FaceRun> faceRunsAmong(
  1. WorldWall host,
  2. List<WorldWall> walls
)

host's face runs among walls (any walls; host itself, degenerate walls and walls that do not touch it are ignored), spec 09c D3. The left face's runs, then the right face's, each ascending along the frame's direction. Empty for a degenerate host.

  • The face lines are the drawn ones (S-2, T-3): in host's HostFrame the left face passes through startCap.first and endCap.last, the right face through startCap.last and endCap.first, each mapped to world through host.toWorld. A face's direction is the frame's d through toWorld.transformDirection, normalised (never the difference of its cap points); the line passes through its start-cap point; its extent is the interval between the projections of its two cap points. The frame's lOff/rOff are not used (unscaled, they are off the drawn face under a scaled group).
  • FaceRun.m is the normal pointing away from the other face line, FaceRun.thickness |(pLeft − pRight)·m| for the start-cap points.
  • Cuts from the walls obstaclesOf names (its intervals are not used; stretchesOf never is): a T (an end of B strictly inside host) cuts only the face it butts, the left one when toLocal.transformDirection(End(B, k).a) · frame.n > 0 (the host's local frame, S-3); an X cuts each face. Each cut is the range, on that face line, of the two points where B's two drawn face lines (this rule applied to B's own frame, T-2) cross it, so an oblique X cuts each face by its own interval (S-7). Pieces no longer than wallJoin.linear are dropped. Openings do not cut.

Implementation

List<FaceRun> faceRunsAmong(WorldWall host, List<WorldWall> walls) {
  if (host.degenerate) return const [];
  final frame = hostFrameOf(host, walls);
  if (frame == null) return const [];
  final f = _drawnFaces(host, frame);
  final d = f.d;
  final nd = Vector2(-d.y, d.x);
  final mLeft = (f.ls - f.rs).dot(nd) > 0 ? nd : -nd;
  final thickness = (f.ls - f.rs).dot(mLeft).abs();

  final leftCuts = <(double, double)>[], rightCuts = <(double, double)>[];
  final toLocal = host.toWorld.invert();
  final done = <int>{};
  for (final o in obstaclesOf(frame, host, walls)) {
    if (!done.add(o.wall.value)) continue;
    final b = walls.firstWhere((x) => x.handle == o.wall);
    final others = [
      host,
      for (final x in walls)
        if (x.handle != b.handle) x,
    ]..sort((x, y) => x.handle.value.compareTo(y.handle.value));
    final bFrame = hostFrameOf(b, others);
    if (bFrame == null) continue;
    final bf = _drawnFaces(b, bFrame);

    // The range along d, from [p0], of B's two drawn face lines' crossings
    // with the face line through [p0]; null when either is parallel.
    (double, double)? cutOn(Vector2 p0) {
      final q1 = intersect(p0, d, bf.ls, bf.d);
      final q2 = intersect(p0, d, bf.rs, bf.d);
      if (q1 == null || q2 == null) return null;
      final v1 = (q1 - p0).dot(d), v2 = (q2 - p0).dot(d);
      return v1 <= v2 ? (v1, v2) : (v2, v1);
    }

    var tee = false;
    for (final k in const [0, 1]) {
      if (!strictlyInside(b.endpoint(k), host)) continue;
      tee = true;
      final left = toLocal.transformDirection(End(b, k).a).dot(frame.n) > 0;
      final cut = cutOn(left ? f.ls : f.rs);
      if (cut != null) (left ? leftCuts : rightCuts).add(cut);
    }
    if (tee) continue;
    final l = cutOn(f.ls), r = cutOn(f.rs);
    if (l != null) leftCuts.add(l);
    if (r != null) rightCuts.add(r);
  }

  return [
    ..._pieces(f.ls, (f.le - f.ls).dot(d), d, mLeft, leftCuts, thickness,
        host.handle, FaceSide.left),
    ..._pieces(f.rs, (f.re - f.rs).dot(d), d, -mLeft, rightCuts, thickness,
        host.handle, FaceSide.right),
  ];
}