feat(B06): 세월교 · 박스 흙막이 횡단 그림 — 관보호공 흙막이와 같은 벽 · 자리 · 연동 · 경사(73-4)

- 유입 · 유출 구조 「흙막이」면 배관 흙막이와 같은 세트(관 숨김 · body)를 같은 측점 culvert 자리에 함께 얹음(파이썬 _guard_set ↔ TS guardSet 짝 · 거울 시험)
- 자동 자리 = 벽 전면이 관(세월교) · 본체(박스) 끝 · 좌우 x 는 그 자리 기준(본체를 늘리면 벽도 따라감) · 상하 d 는 배관과 같음
- 연동 옆 측점 · 3D 연동 해제 자리는 본체 기준 x 를 절대값으로 넘겨 받음 · 3D 만료 해시에 흙막이 값 포함
- 설계선 트림 = 벽 선 쪽은 흙막이 · 안 선 쪽은 본체 · 벽 선 쪽 본체 성토선은 걷음(두 줄 방지) · 관 연장은 안 실음
- 좌측 폼 → 캐시: 세트 다시 짓기 · 높이 · 형태 · 추가 흙막이 상자 수 = 유출 다단 수
- [연동][경사] 회색 풀림(옆 측점에 벽이 서면 연동 · 구간 있으면 경사) — 배관과 같은 규칙
- 시험: test_73_4_body_guard(벽 전면 = 본체 끝 · 박스 +1 m 따라감 · 연동 옆 측점 벽 · 서버 Node 면적 = 화면) · 거울 시험 흙막이 칸

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYJJRCfHfdCGNhmXTjQKV7
This commit is contained in:
2026-10-07 08:23:48 +09:00
co-authored by Claude Opus 5.5
parent 5e3ad45248
commit ffbf772f3c
18 changed files with 619 additions and 52 deletions
@@ -37,9 +37,9 @@ export function structureHashParts(section: CrossSection): Array<string | number
);
}
const box = section.box;
// 세월교 · 박스에 흙막이(`culvert`)가 함께 서면 그 값도 이어 붙임(73-4) — 일찍 돌려주지 않음.
if (box) {
return [
...extras,
extras.push(
"bx",
box.inner_width_m,
box.inner_height_m,
@@ -54,7 +54,7 @@ export function structureHashParts(section: CrossSection): Array<string | number
const value = section.design?.box_adjust?.[side];
return value ? `${side}:${value.lengthM},${value.riseM}` : "";
}),
];
);
}
const ford = section.ford;
if (ford) {
@@ -63,8 +63,7 @@ export function structureHashParts(section: CrossSection): Array<string | number
const value = adjust?.[role];
return value ? `${value.heightM ?? ""},${value.lateralM},${value.slopeM}` : "";
};
return [
...extras,
extras.push(
"fd",
ford.diameter_m,
ford.pipe_count,
@@ -73,7 +72,7 @@ export function structureHashParts(section: CrossSection): Array<string | number
ford.wing_out.slab_extend_m,
wall("inlet"),
wall("outlet"),
];
);
}
const culvert = section.culvert;
if (!culvert) return extras;
@@ -13,6 +13,7 @@
import type { CrossSection } from "../B06_Section/B06_Section_Api_Fetch";
import type { CulvertLayout } from "../B06_Section/B06_Section_UI_Cross_Culvert";
import { computeCulvertLayout } from "../B06_Section/B06_Section_UI_Cross_Culvert";
import { bodyAbsolute } from "../B06_Section/B06_Section_UI_Cross_Body_Guard";
import type { WallAdjust } from "../B06_Section/B06_Section_UI_Cross_Culvert_Types";
import { ZERO_ADJUST } from "../B06_Section/B06_Section_UI_Cross_Culvert_Types";
import {
@@ -136,7 +137,17 @@ function detachedAdjusts(
owner: CrossSection,
section: CrossSection,
): ReturnType<typeof storedAdjusts> {
const base = storedAdjusts(owner);
// 세월교 · 박스 흙막이 — 본체 기준 x 를 절대값으로(옆 측점엔 본체가 없음 · 횡단도와 같음 · 73-4)
const ownerBody = owner.culvert?.body
? computeCulvertLayout(
owner,
owner.samples,
storedAdjusts(owner),
"revet",
owner.design?.basin_adjust,
)
: null;
const base = ownerBody ? bodyAbsolute(ownerBody) : storedAdjusts(owner);
const mine = section.design?.revet_adjust;
const counts = owner.design?.extra_wall_counts;
const pick = (role: string, fallback?: WallAdjust): WallAdjust =>