"""ファミレスの店の形 第2版：席を「列」で置く。

決まり（2026-10-21 エイイチと決めたこと）
- 基本は4人掛けの背中合わせのソファの列（島）。壁ぞいはソファの片側の列
- 狭い所は2人席で埋める
- 家族・団体用に6人のブース（L字ソファ・6人ボックス）を入れる
- 店の色は2つ
    効率重視型：仕切り壁なし・通路は狭め（1.2m）・2人席多め
    ゆったり型：仕切り壁多め・通路は広め（1.5m）・家族用ブース多め
- 入口の位置はパターンごとに変える（角・横の壁・正面の中央）

測ること：待機場所の前から各卓まで、ロボット（仮に幅70cm）が届くか・何m走るか
使い方:  python3 famires_gen2.py
"""
import json
from pathlib import Path

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle, Circle, Polygon

from famires_gen import analyze as _analyze, overlap

plt.rcParams["font.family"] = "Noto Sans CJK JP"
HERE = Path(__file__).parent
OUT = HERE / "variants2"

INK, MUTED, BACK, SEAT, SOFA = "#1f2328", "#8a9099", "#eceef1", "#ffffff", "#dee2e6"
ROBOT, DRINK, DISH, NG = "#e8590c", "#1c7ed6", "#2f9e44", "#e03131"

UNIT = 1.85          # ボックス1つの長さ（ベンチ0.55＋テーブル0.75＋ベンチ0.55）
CROSS = {4: 1.2, 6: 1.8}   # ベンチの長さ（2人掛け・3人掛け）
BACKGAP = 0.1        # 背中合わせのソファの背の厚み
PART = 0.15          # 仕切り壁の厚み


# ---------------------------------------------------------------- 席の部品
def booth(x, y, axis, seats):
    """ボックス席1つ。axis='v' はベンチが上下（列が縦に並ぶ）、'h' はベンチが左右"""
    c = CROSS[seats]
    w, h = (c, UNIT) if axis == "v" else (UNIT, c)
    return dict(type="booth", axis=axis, x=round(x, 2), y=round(y, 2), w=w, h=h, seats=seats)


def strip(x, y, axis, n, seats=4, walls=None):
    """ボックスの列。walls を渡すと、ボックスの間に仕切り壁を入れる（ゆったり型）"""
    out = []
    for i in range(n):
        if axis == "v":
            out.append(booth(x, y + i * UNIT, "v", seats))
        else:
            out.append(booth(x + i * UNIT, y, "h", seats))
    if walls is not None:
        c = CROSS[seats]
        for i in range(1, n):
            if axis == "v":
                walls.append(dict(label="仕切り", joint=True, rect=[x, y + i * UNIT - PART / 2, x + c, y + i * UNIT + PART / 2]))
            else:
                walls.append(dict(label="仕切り", joint=True, rect=[x + i * UNIT - PART / 2, y, x + i * UNIT + PART / 2, y + c]))
    return out


def island(x, y, axis, n, seats=4, walls=None):
    """背中合わせのソファの列（島）。walls を渡すと背中に仕切り壁を立て、ボックスの間も仕切る"""
    c = CROSS[seats]
    back = PART if walls is not None else BACKGAP
    if axis == "v":
        a = strip(x, y, "v", n, seats, walls)
        b = strip(x + c + back, y, "v", n, seats, walls)
        if walls is not None:
            walls.append(dict(label="仕切り", joint=True, rect=[x + c, y - 0.2, x + c + back, y + n * UNIT + 0.2]))
    else:
        a = strip(x, y, "h", n, seats, walls)
        b = strip(x, y + c + back, "h", n, seats, walls)
        if walls is not None:
            walls.append(dict(label="仕切り", joint=True, rect=[x - 0.2, y + c, x + n * UNIT + 0.2, y + c + back]))
    return a + b


def island_width(seats=4, walls=False):
    return 2 * CROSS[seats] + (PART if walls else BACKGAP)


def two_seat_row(x, y, side, n, pitch=1.1):
    """壁ぞいの2人席の列（壁側はベンチ、通路側は椅子）。side は壁の位置"""
    out = []
    for i in range(n):
        if side in ("bottom", "top"):
            out.append(dict(type="t2", side=side, x=round(x + i * pitch, 2), y=y, w=0.8, h=1.4, seats=2))
        else:
            out.append(dict(type="t2", side=side, x=x, y=round(y + i * pitch, 2), w=1.4, h=0.8, seats=2))
    return out


def lsofa(x, y, w, h, flip=False, mirror=False):
    return dict(type="booth6L", x=x, y=y, w=w, h=h, seats=6, flip=flip, mirror=mirror)


def register(counter, staff):
    """レジカウンター（長さ約2.5〜3m）と、その内側の店員が立つ所"""
    return [dict(label="レジ", rect=counter, kind="guest"),
            dict(label="レジ内", rect=staff, kind="back")]


def toilet_block(rect, door, W, D, thick=0.3, door_w=0.9, approach=1.2):
    """客用トイレ。客席に面する側は厚い壁（約30cm）、入口（door の側）の前は1.2mあけて席を置かない"""
    x0, y0, x1, y1 = rect
    walls = []
    sides = {"left": x0 > 0, "right": x1 < W, "bottom": y0 > 0, "top": y1 < D}   # 外壁でない側
    for side, interior in sides.items():
        if not interior:
            continue
        if side in ("left", "right"):
            xa = x0 if side == "left" else x1 - thick
            segs = [(y0, y1)]
            if side == door:
                m = (y0 + y1) / 2
                segs = [(y0, m - door_w / 2), (m + door_w / 2, y1)]
            walls += [dict(label="トイレの壁", rect=[xa, a, xa + thick, b]) for a, b in segs]
        else:
            ya = y0 if side == "bottom" else y1 - thick
            segs = [(x0, x1)]
            if side == door:
                m = (x0 + x1) / 2
                segs = [(x0, m - door_w / 2), (m + door_w / 2, x1)]
            walls += [dict(label="トイレの壁", rect=[a, ya, b, ya + thick]) for a, b in segs]
    m = ((x0 + x1) / 2, (y0 + y1) / 2)
    zone = {"left": [x0 - approach, m[1] - door_w / 2, x0, m[1] + door_w / 2],
            "right": [x1, m[1] - door_w / 2, x1 + approach, m[1] + door_w / 2],
            "bottom": [m[0] - door_w / 2, y0 - approach, m[0] + door_w / 2, y0],
            "top": [m[0] - door_w / 2, y1, m[0] + door_w / 2, y1 + approach]}[door]
    return dict(label="トイレ", rect=list(rect), kind="guest"), walls, zone


# ---------------------------------------------------------------- 裏側（厨房など）
def boh_right(W, D, px, fy, ty, pw=2.0, sw=2.4, wash_gap=1.0):
    """厨房が右。パントリーは縦長で、受け渡し口は下の端（ベースの店と同じ形）"""
    rooms = [
        dict(label="パントリー", rect=[px + 0.3, fy, px + 0.3 + pw, ty], kind="back"),
        dict(label="キッチン", rect=[px + 0.3 + pw, fy, W - sw, ty], kind="back"),
        dict(label="冷蔵・冷凍・保存", rect=[W - sw, (fy + ty) / 2, W, ty], kind="back"),
        dict(label="更衣室・社員トイレ", rect=[W - sw, fy, W, (fy + ty) / 2], kind="back"),
        dict(label="洗い場", rect=[px + 0.3 + pw, ty + wash_gap, W, D], kind="back"),
    ]
    walls = [dict(label="パントリーの壁", rect=[px, fy, px + 0.3, ty])]
    dish = [dict(label="受け渡し口", rect=[px + 0.3, fy, px + 0.3 + pw, fy + 0.4])]
    standby = dict(label="ロボット待機・充電", rect=[px + 0.5 + pw, fy - 0.85, px + 0.5 + pw + 2.4, fy - 0.05], slots=3)
    return rooms, walls, dish, standby, [px + 0.3 + pw / 2, fy - 0.45]


def boh_top(W, D, x0, x1, fy, staff):
    """厨房が奥（上）。パントリーは横長で、受け渡し口は真ん中"""
    mid = (x0 + x1) / 2
    rooms = [
        dict(label="パントリー", rect=[x0, fy, x1, fy + 1.2], kind="back"),
        dict(label="キッチン", rect=[x0, fy + 1.2, x1, D - 3.0], kind="back"),
        dict(label="洗い場", rect=[x0, D - 3.0, mid, D], kind="back"),
        dict(label="冷蔵・冷凍・保存", rect=[mid, D - 3.0, x1, D], kind="back"),
        dict(label="更衣室・社員トイレ", rect=staff, kind="back"),
    ]
    dish = [dict(label="受け渡し口", rect=[mid - 1.5, fy, mid + 1.5, fy + 0.4])]
    standby = dict(label="ロボット待機・充電", rect=[mid + 1.7, fy - 0.85, mid + 4.1, fy - 0.05], slots=3)
    return rooms, [], dish, standby, [mid, fy - 0.45]


# ---------------------------------------------------------------- 5パターン
def p1_efficient_corner():
    W, D = 24.0, 16.0
    rooms, walls, dish, sb, start = boh_right(W, D, 14.0, 6.5, 13.8)
    toilet, twalls, tdoor = toilet_block([0.0, 13.8, 2.8, 16.0], "right", W, D)   # 左上の角。入口は右向き
    rooms += [toilet]
    walls += twalls
    rooms += [dict(label="待合", rect=[0.0, 0.9, 0.5, 3.3], kind="guest"),
              # 縦のカウンター。客は入口の側（左）に立ち、入口へ向かって並ぶ。下の通路をふさがない（2026-10-23 エイイチと決めた）
              *register([3.3, 0.4, 4.0, 2.9], [4.0, 0.4, 4.9, 2.9]),
              dict(label="ドリンクバー", rect=[16.8, 2.8, 20.8, 3.5], kind="drink")]
    t = strip(0.0, 4.6, "v", 4)                       # 左の壁ぞいのソファ（トイレの手前で止める）
    t += island(2.4, 4.8, "v", 4)                     # 背中合わせのソファの列（間の通路1.2m）
    t += island(6.1, 4.8, "v", 5)
    t += island(9.8, 4.8, "v", 4)
    t += strip(6.8, 0.0, "h", 3, seats=6)             # 家族用の6人ボックス（下の壁ぞい・レジ内の出入りを空ける）
    t += two_seat_row(14.6, 0.0, "bottom", 8)         # 2人席（右下の壁ぞい）
    t += [lsofa(21.2, 3.0, 2.8, 3.5, flip=True, mirror=True)]
    return dict(name="③ 効率重視型・入口が左下の角", style="効率重視", outer=dict(w=W, d=D),
                entrance=dict(side="bottom", a0=0.6, a1=3.0), rooms=rooms, walls=walls, toilet_doors=[tdoor],
                dish_up=dish, robot_standby=sb, start=start, tables=t)


def p2_efficient_narrow():
    W, D = 17.0, 12.0
    rooms, walls, dish, sb, start = boh_right(W, D, 10.0, 5.0, 10.0, pw=1.5, sw=1.8, wash_gap=0.8)
    toilet, twalls, tdoor = toilet_block([0.0, 10.0, 2.4, 12.0], "bottom", W, D)   # 左上の角。入口は下向き
    rooms += [toilet]
    walls += twalls
    sb["rect"] = [12.0, 4.15, 13.6, 4.95]
    sb["slots"] = 2
    rooms += [dict(label="待合", rect=[16.5, 3.2, 17.0, 4.8], kind="guest"),
              *register([13.6, 0.3, 14.3, 3.0], [12.9, 0.3, 13.6, 3.0]),   # 入口に向いた縦のカウンター
              dict(label="ドリンクバー", rect=[10.4, 1.6, 12.4, 2.2], kind="drink")]
    t = two_seat_row(0.0, 2.2, "left", 5)             # 左の壁ぞいの2人席（トイレの入口の手前で止める）
    t += two_seat_row(3.5, 10.6, "top", 6, pitch=1.05)   # 上の壁ぞいの2人席（トイレから1m以上離す）
    t += island(2.6, 2.9, "v", 3)
    t += island(6.3, 2.9, "v", 3)
    t += strip(1.6, 0.0, "h", 2, seats=6)             # 家族用の6人ボックス
    t += two_seat_row(5.6, 0.0, "bottom", 3)
    return dict(name="④ 効率重視型・狭い店・入口が右の壁", style="効率重視", outer=dict(w=W, d=D),
                entrance=dict(side="right", a0=0.6, a1=3.0), rooms=rooms, walls=walls, toilet_doors=[tdoor],
                dish_up=dish, robot_standby=sb, start=start, tables=t)


def p3_relaxed_center():
    W, D = 26.0, 17.0
    rooms, walls, dish, sb, start = boh_top(W, D, 9.0, 19.0, 9.4, staff=[19.0, 14.5, 23.0, 17.0])
    toilet, twalls, tdoor = toilet_block([0.0, 14.6, 3.0, 17.0], "right", W, D)   # 左上の角。入口は右向き
    walls += twalls
    rooms += [toilet,
              *register([11.2, 0.3, 11.9, 2.8], [10.4, 0.3, 11.2, 2.8]),   # 入口の横に縦向き（前の通路をふさがない）
              dict(label="待合", rect=[15.4, 0.3, 15.9, 2.3], kind="guest"),   # 入口の右に縦向き（6人席の前をふさがない）
              dict(label="ドリンクバー", rect=[15.0, 4.6, 18.6, 5.3], kind="drink")]
    t = strip(0.0, 1.4, "v", 6, walls=walls)          # 左の壁ぞい（ボックスごとに仕切り・トイレの手前で止める）
    t += island(2.7, 3.4, "v", 5, walls=walls)
    t += island(6.75, 3.4, "v", 3, walls=walls)
    t += strip(24.8, 1.4, "v", 6, walls=walls)        # 右の壁ぞい
    t += island(20.75, 3.4, "v", 5, walls=walls)
    t += strip(1.6, 0.0, "h", 4, seats=6, walls=walls)    # 家族用の6人ボックス（下の壁ぞい・左）
    t += strip(16.4, 0.0, "h", 4, seats=6, walls=walls)   # 同（右）。入口の右側
    return dict(name="⑤ ゆったり型・入口が正面の中央", style="ゆったり", outer=dict(w=W, d=D),
                entrance=dict(side="bottom", a0=12.0, a1=15.0), rooms=rooms, walls=walls, toilet_doors=[tdoor],
                dish_up=dish, robot_standby=sb, start=start, tables=t)


def p4_relaxed_side():
    W, D = 24.0, 17.0
    rooms, walls, dish, sb, start = boh_right(W, D, 13.0, 6.5, 14.5)
    toilet, twalls, tdoor = toilet_block([9.4, 14.8, 12.4, 17.0], "bottom", W, D)   # 上の壁の真ん中。入口は下向き
    rooms += [toilet]
    walls += twalls
    rooms += [dict(label="待合", rect=[0.6, 0.0, 3.0, 0.5], kind="guest"),
              # 縦のカウンター。客は入口の側（左）に立ち、入口へ向かって並ぶ。下の通路をふさがない（2026-10-23 エイイチと決めた）
              *register([2.8, 0.7, 3.5, 3.2], [3.5, 0.7, 4.4, 3.2]),
              dict(label="ドリンクバー", rect=[16.0, 3.4, 20.0, 4.1], kind="drink")]
    t = strip(0.3, 15.2, "h", 4, seats=6, walls=walls)    # 家族用の6人ボックス（上の壁ぞい）
    t += strip(0.0, 5.2, "v", 5, walls=walls)              # 左の壁ぞい
    t += island(2.7, 5.6, "v", 4, walls=walls)
    t += island(6.75, 5.6, "v", 4, walls=walls)
    t += strip(24.0 - 5 * UNIT, 0.0, "h", 5, seats=6, walls=walls)  # 家族用の6人ボックス（右下・右の壁まで）
    t += two_seat_row(6.6, 0.0, "bottom", 7)
    t += [lsofa(21.2, 3.0, 2.8, 3.5, flip=True, mirror=True)]   # 下のボックスとの間を1.2mあける
    return dict(name="⑥ ゆったり型・入口が左の壁", style="ゆったり", outer=dict(w=W, d=D),
                entrance=dict(side="left", a0=1.0, a1=3.4), rooms=rooms, walls=walls, toilet_doors=[tdoor],
                dish_up=dish, robot_standby=sb, start=start, tables=t)


def p5_crammed():
    """③と同じ卓・同じ席数のまま、列の間の通路だけを1.2mから0.95mに詰めた比較用。
    はじめは0.8m（BellaBot の通れる幅ちょうど）だったが、Unity でロボットがほとんど動けず、
    右端の列の横に詰めた分の余りがあったので、島を右へずらして0.95mにした（2026-10-23 エイイチと決めた）"""
    L = p1_efficient_corner()
    L["name"] = "⑦ ③の列の間の通路を95cmに（卓と席数は③と同じ）"
    AISLE = 0.95
    t = strip(0.0, 4.6, "v", 4)
    x = 1.2 + AISLE
    for n in (4, 5, 4):                               # ③と同じ島（4・5・4ボックス）
        t += island(x, 4.8, "v", n)
        x += island_width() + AISLE
    t += strip(6.8, 0.0, "h", 3, seats=6)
    t += two_seat_row(14.6, 0.0, "bottom", 8)
    t += [lsofa(21.2, 3.0, 2.8, 3.5, flip=True, mirror=True)]
    L["tables"] = t
    return L


# ---------------------------------------------------------------- 測る・確かめる
def footprint(t):
    return (t["x"], t["y"], t["x"] + t["w"], t["y"] + t["h"])


def check_overlaps(L):
    """卓どうし・卓と部屋が重なっていないか"""
    fps = [footprint(t) for t in L["tables"]]
    bad = []
    for i, a in enumerate(fps):
        for j in range(i + 1, len(fps)):
            if overlap(a, fps[j], -0.01):
                bad.append(("卓どうし", i, j))
        for r in L["rooms"]:
            if overlap(a, r["rect"], -0.01):
                bad.append(("卓と" + r["label"], i))
    return bad


def rect_gap(a, b):
    dx = max(0.0, max(a[0], b[0]) - min(a[2], b[2]))
    dy = max(0.0, max(a[1], b[1]) - min(a[3], b[3]))
    return (dx * dx + dy * dy) ** 0.5


def check_toilet(L, near=1.0):
    """決まり：トイレから1m以内に席を置かない。トイレの入口の前1.2mには何も置かない"""
    toilets = [r["rect"] for r in L["rooms"] if r["label"] == "トイレ"]
    doors = L.get("toilet_doors", [])
    others = [r["rect"] for r in L["rooms"] if r["label"] != "トイレ"]
    bad = []
    for i, t in enumerate(L["tables"]):
        fp = footprint(t)
        if any(rect_gap(fp, tr) < near for tr in toilets):
            bad.append(("トイレから1m以内の席", i))
        if any(overlap(fp, d, -0.01) for d in doors):
            bad.append(("トイレの入口の前に席", i))
    for r in others:
        if any(overlap(r, d, -0.01) for d in doors):
            bad.append(("トイレの入口の前に部屋", r))
    return bad


def analyze(L):
    # famires_gen.analyze は type ごとの形を知っているので、ここでは矩形として渡す
    tmp = dict(L)
    tmp["tables"] = [dict(type="rect", x=t["x"], y=t["y"], w=t["w"], h=t["h"], seats=t["seats"]) for t in L["tables"]]
    return _analyze(tmp)


# ---------------------------------------------------------------- 描く
def draw_seat(ax, t, ng, part):
    ec = NG if ng else INK
    x, y, w, h = t["x"], t["y"], t["w"], t["h"]
    lab = str(t["seats"])
    if t["type"] == "booth":
        ax.add_patch(Rectangle((x, y), w, h, fc=SEAT, ec=ec, lw=0.9, zorder=3))
        for b in ((x, y, w, 0.55), (x, y + h - 0.55, w, 0.55)) if t["axis"] == "v" else ((x, y, 0.55, h), (x + w - 0.55, y, 0.55, h)):
            ax.add_patch(Rectangle(b[:2], b[2], b[3], fc=SOFA, ec=ec, lw=0.7, zorder=3))
        ax.text(x + w / 2, y + h / 2, lab, ha="center", va="center", fontsize=8,
                color=ec, weight="bold" if t["seats"] == 6 else "normal", zorder=5)
    elif t["type"] == "t2":
        ax.add_patch(Rectangle((x, y), w, h, fc="none", ec="none", zorder=3))
        side = t["side"]
        if side == "bottom":
            bench, tab, chair = (x, y, w, 0.45), (x + 0.05, y + 0.5, 0.7, 0.6), (x + w / 2, y + 1.2)
        elif side == "top":
            bench, tab, chair = (x, y + h - 0.45, w, 0.45), (x + 0.05, y + 0.3, 0.7, 0.6), (x + w / 2, y + 0.2)
        elif side == "left":
            bench, tab, chair = (x, y, 0.45, h), (x + 0.5, y + 0.05, 0.6, 0.7), (x + 1.2, y + h / 2)
        else:
            bench, tab, chair = (x + w - 0.45, y, 0.45, h), (x + 0.3, y + 0.05, 0.6, 0.7), (x + 0.2, y + h / 2)
        ax.add_patch(Rectangle(bench[:2], bench[2], bench[3], fc=SOFA, ec=ec, lw=0.7, zorder=3))
        ax.add_patch(Rectangle(tab[:2], tab[2], tab[3], fc=SEAT, ec=ec, lw=0.9, zorder=3))
        ax.add_patch(Circle(chair, 0.18, fc=SEAT, ec=ec, lw=0.7, zorder=3))
        ax.text(tab[0] + tab[2] / 2, tab[1] + tab[3] / 2, lab, ha="center", va="center", fontsize=7, color=ec, zorder=5)
    elif t["type"] == "booth6L":
        pts = [(x, y), (x + w, y), (x + w, y + h * 0.45), (x + w * 0.55, y + h * 0.45), (x + w * 0.55, y + h), (x, y + h)]
        lx, ly = x + w * 0.3, y + h * 0.4
        if t.get("flip"):
            pts = [(px, 2 * y + h - py) for px, py in pts]
            ly = y + h * 0.6
        if t.get("mirror"):
            pts = [(2 * x + w - px, py) for px, py in pts]
            lx = x + w * 0.7
        ax.add_patch(Polygon(pts, closed=True, fc=SOFA, ec=ec, lw=1.0, zorder=3))
        ax.text(lx, ly, lab, ha="center", va="center", fontsize=8, color=ec, weight="bold", zorder=5)


def draw(ax, L, results, stats):
    W, D = L["outer"]["w"], L["outer"]["d"]
    ax.set_xlim(-1.2, W + 1.2)
    ax.set_ylim(-1.8, D + 0.6)
    ax.set_aspect("equal")
    ax.axis("off")

    e = L["entrance"]
    segs = {"bottom": [((0, 0), (W, 0))], "top": [((0, D), (W, D))], "left": [((0, 0), (0, D))], "right": [((W, 0), (W, D))]}
    for side, ((xa, ya), (xb, yb)) in [(k, v[0]) for k, v in segs.items()]:
        if side != e["side"]:
            ax.plot([xa, xb], [ya, yb], color=INK, lw=3)
        elif side in ("bottom", "top"):
            ax.plot([0, e["a0"]], [ya, ya], color=INK, lw=3)
            ax.plot([e["a1"], W], [ya, ya], color=INK, lw=3)
        else:
            ax.plot([xa, xa], [0, e["a0"]], color=INK, lw=3)
            ax.plot([xa, xa], [e["a1"], D], color=INK, lw=3)
    m = (e["a0"] + e["a1"]) / 2
    pos = {"bottom": (m, -0.5), "top": (m, D + 0.4), "left": (-0.7, m), "right": (W + 0.7, m)}[e["side"]]
    ax.text(*pos, "入口" if e["side"] in ("bottom", "top") else "入\n口", ha="center", va="center", fontsize=10, weight="bold")

    for r in L["rooms"]:
        fc = {"back": BACK, "guest": "#f8f9fa", "drink": "#d0ebff"}[r["kind"]]
        ec = DRINK if r["kind"] == "drink" else INK
        x0, y0, x1, y1 = r["rect"]
        ax.add_patch(Rectangle((x0, y0), x1 - x0, y1 - y0, fc=fc, ec=ec, lw=1.0, zorder=2))
        tall = (y1 - y0) > (x1 - x0) * 1.6
        text = "\n".join(r["label"]) if tall and len(r["label"]) <= 5 else r["label"]
        ax.text((x0 + x1) / 2, (y0 + y1) / 2, text, ha="center", va="center",
                fontsize=8 if len(r["label"]) > 5 else 9, color=DRINK if r["kind"] == "drink" else INK, zorder=5)
    for w in L["walls"]:
        x0, y0, x1, y1 = w["rect"]
        ax.add_patch(Rectangle((x0, y0), x1 - x0, y1 - y0, fc="#868e96", ec="#495057", lw=0.5, zorder=4))
    for d in L["dish_up"]:
        x0, y0, x1, y1 = d["rect"]
        ax.add_patch(Rectangle((x0, y0), x1 - x0, y1 - y0, fc=DISH, ec=DISH, zorder=4))

    for r in results:
        if r["reach"]:
            xs, ys = zip(*r["path"])
            ax.plot(xs, ys, color=ROBOT, lw=0.9, alpha=0.3, zorder=2.5)
    for t, r in zip(L["tables"], results):
        draw_seat(ax, t, not r["reach"], False)

    sb = L["robot_standby"]
    x0, y0, x1, y1 = sb["rect"]
    ax.add_patch(Rectangle((x0, y0), x1 - x0, y1 - y0, fc="#fff4e6", ec=ROBOT, lw=1.4, zorder=5))
    for i in range(sb["slots"]):
        ax.add_patch(Circle((x0 + (x1 - x0) * (i + 0.5) / sb["slots"], (y0 + y1) / 2), 0.22, fc=ROBOT, ec="white", lw=0.8, zorder=6))

    by = {}
    for t in L["tables"]:
        by[t["seats"]] = by.get(t["seats"], 0) + 1
    mix = "・".join(f"{k}人席{v}" for k, v in sorted(by.items()))
    ng = f'　届かない卓 {stats["tables"] - stats["reachable"]}（{stats["unreachable_seats"]}席）' if stats["reachable"] < stats["tables"] else ""
    ax.text(0, -1.15, f'{L["name"]}　{stats["seats"]}席（{mix}）・{W:g}m×{D:g}m', fontsize=10, color=INK, va="center")
    ax.text(0, -1.6, f'受け渡し口から卓まで 平均{stats["mean_dist"]}m・最長{stats["max_dist"]}m{ng}　'
            f'（オレンジの線＝ロボットの道・幅は仮に70cm／赤＝届かない卓／寸法はすべて仮）',
            fontsize=8.5, color=ROBOT if not ng else NG, va="center")


def main():
    OUT.mkdir(exist_ok=True)
    builds = [("p1_efficient_corner", p1_efficient_corner), ("p2_efficient_narrow", p2_efficient_narrow),
              ("p3_relaxed_center", p3_relaxed_center), ("p4_relaxed_side", p4_relaxed_side),
              ("p5_crammed", p5_crammed)]
    summary = []
    for nm, fn in builds:
        L = fn()
        bad = check_overlaps(L) + check_toilet(L)
        results, stats = analyze(L)
        fig, ax = plt.subplots(figsize=(14, 10.5), dpi=120)
        draw(ax, L, results, stats)
        fig.savefig(OUT / f"{nm}.png", bbox_inches="tight", facecolor="white")
        plt.close(fig)
        json.dump(L, open(OUT / f"{nm}.json", "w", encoding="utf-8"), ensure_ascii=False, indent=2)
        summary.append(dict(file=nm, name=L["name"], overlaps=len(bad), **stats))
        print(nm, stats, "重なり:", bad[:5])
    json.dump(summary, open(OUT / "summary.json", "w", encoding="utf-8"), ensure_ascii=False, indent=2)


if __name__ == "__main__":
    main()
