fix(B06): 원지반 제약 끊음 — 다단 자동 자리 사면 1.0 m · 설계선 끝 수치 판정 · 전면 기울기 칸 아니면 성토 · 「지형에 막힘」 잔재 지움(91-3)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013rJwJMVL4EE31y77TB11AP
This commit is contained in:
2026-10-08 12:12:16 +09:00
co-authored by Claude Opus 5.5
parent 8c04d520f4
commit de93c7b248
14 changed files with 360 additions and 471 deletions
+6 -29
View File
@@ -21,9 +21,9 @@ export interface WallLeanInput {
form?: string | null;
/** 직고(m) — 순수 높이. */
height_m: number;
/** 측점 단면유형 `design.section_mode`. */
/** 측점 단면유형 `design.section_mode` — 기울기 판정에 안 씀(91-3 · 칸 · 성토 기본). */
section_mode?: string | null;
/** 설치 측 — 「자동(성토 쪽)」·「좌」·「우」(없으면 자동). */
/** 설치 측 — 기울기 판정에 안 씀(91-3). */
side?: string | null;
/** 성토/절토 칸 — 고르면 이김. */
face_role?: string | null;
@@ -38,32 +38,10 @@ const FILL = "성토";
const CUT = "절토";
/** 형태 → 찰(true)/메(false) — 파이썬 `REVETMENT_STONE_FORMS`. */
const STONE_FORMS: Record<string, boolean> = { "돌쌓기(찰)": true, "돌쌓기(메)": false };
/** 단면유형 → (좌측 역할, 우측 역할). 좌 = +offset. */
const MODE_ROLES: Record<string, [string, string]> = {
left_cut: [CUT, FILL],
right_cut: [FILL, CUT],
both_cut: [CUT, CUT],
both_fill: [FILL, FILL],
};
const LEFT = new Set(["좌", "left", "L"]);
const RIGHT = new Set(["우", "right", "R"]);
const AUTO = new Set(["자동(성토 쪽)", "자동", "auto", ""]);
/** 성토/절토 — 칸이 이기고, 비면 단면유형 + 설치 측. 못 가르면 `null`(성토로 눅이지 않음). */
export function faceRoleOf(
sectionMode: string | null | undefined,
side: string | null | undefined,
faceRole?: string | null,
): string | null {
/** 성토/절토 — 칸이 이기고, 비면 성토(흙막이 · 사면안정 흙막이 · 옹벽 모두 · 원지반 자동판정 안 씀 · 91-3). */
export function faceRoleOf(faceRole?: string | null): string {
const picked = String(faceRole ?? "").trim();
if (picked === FILL || picked === CUT) return picked;
const roles = MODE_ROLES[String(sectionMode ?? "").trim()];
if (!roles) return null;
const where = String(side ?? "").trim();
if (LEFT.has(where)) return roles[0];
if (RIGHT.has(where)) return roles[1];
if (AUTO.has(where)) return roles[0] === CUT && roles[1] === CUT ? null : FILL;
return null;
return picked === CUT ? CUT : FILL;
}
/** 벽 전면 기울기 n — 표를 못 고르면 종전 0.3. */
@@ -72,8 +50,7 @@ export function wallLeanRatio(table: MasonrySlopeTable, input: WallLeanInput): n
if (wet === undefined) return LEGACY_LEAN_RATIO;
const given = input.face_slope_ratio;
if (typeof given === "number" && Number.isFinite(given) && given > 0) return given;
const face = faceRoleOf(input.section_mode, input.side, input.face_role);
if (!face) return LEGACY_LEAN_RATIO;
const face = faceRoleOf(input.face_role);
const row = table.table?.[wet ? "찰쌓기" : "메쌓기"]?.[face];
if (!row?.length) return LEGACY_LEAN_RATIO;
const steps = table.steps_m ?? [];
@@ -61,16 +61,19 @@ def structure_face_role(section_mode: str | None, side: str | None) -> tuple[str
return None, f"설치 측을 알 수 없음({side!r}) — 성절토를 가를 근거 없음"
#: 성토/절토를 **사용자가 고르는 칸** — 비우면 위 판정(계산이지 기본값이 아님 · 2026-09-14 브레인 판정 ㉲).
#: 성토/절토를 **사용자가 고르는 칸** — 비우면 성토(91-3).
FACE_ROLE_KEY = "face_role"
def structure_face_role_of(
section_mode: str | None, options: dict | None
) -> tuple[str | None, str]:
"""칸 `face_role`(성토·절토)을 골랐으면 그 값, 비었으면 단면유형 + 설치 측 판정."""
"""벽 기울기용 — 칸 `face_role`(성토·절토)을 골랐으면 그 값, 비었으면 성토.
단면유형(원지반 자동판정)은 안 봄(91-3 · TS 짝 `common_util_masonry_slope.faceRoleOf`).
"""
values = options or {}
picked = str(values.get(FACE_ROLE_KEY) or "").strip()
if picked in (FILL, CUT):
return picked, f"사용자 지정 {picked}면(성토/절토 칸)"
return structure_face_role(section_mode, values.get("side"))
return FILL, "성토/절토 칸 비움 → 성토면(기본)"