fix(B06): 세월교 · 박스 유출 수평공간 없앰 — 칸 · 기하 · 세트 짝 · 구조물정의 이름 줄(89-4)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYJJRCfHfdCGNhmXTjQKV7
This commit is contained in:
@@ -83,11 +83,12 @@ export const revetKeys = (side: "inlet" | "outlet"): Record<string, string> => (
|
||||
});
|
||||
|
||||
/** 세월교 · 박스 유입 · 유출 흙막이 칸(79-7) — 높이 빈 칸 = 자동(`heightHint` · 횡단도가 셈) ·
|
||||
* 절토부 앞 수평공간 칸(`{side}_revet_berm_m`)을 더함. */
|
||||
* 유입만 절토부 앞 수평공간 칸(`inlet_revet_berm_m` · 유출은 없음 · 89-4)을 더함. */
|
||||
export function bodyGuardItems(side: "inlet" | "outlet", heightHint: string): SpecItem[] {
|
||||
const items = guardItems({ 흙막이: revetKeys(side) }, undefined, true).map((item) =>
|
||||
item.id === "height" ? { ...item, default: undefined, hint: heightHint } : item,
|
||||
);
|
||||
if (side === "outlet") return items;
|
||||
const berm: SpecItem = {
|
||||
id: "berm",
|
||||
label: "수평공간 (m)",
|
||||
|
||||
@@ -43,7 +43,7 @@ export const FORM_OPTION_KEYS: Readonly<Record<string, readonly string[]>> = {
|
||||
...revetKeys("inlet"),
|
||||
...revetKeys("outlet"),
|
||||
"inlet_revet_berm_m", // 흙막이 절토부 앞 수평공간(79-7)
|
||||
"outlet_revet_berm_m",
|
||||
"outlet_revet_berm_m", // 칸 없앰(89-4) — 목록에 남겨 다음 [저장] 때 옛 값을 지움
|
||||
],
|
||||
// 물넘이 포장 = 표 03 명세 키(PLAN 52-3 · `design_structure_ford_pavement`) + 월류 폭 · 높이(65-5) — 옛 경사 ·
|
||||
// 두께 · 길이는 폼에서 빠져 이어 붙임 · 도수로/돌붙임 키는 `FORM_OPTION_PREFIXES`
|
||||
@@ -61,7 +61,7 @@ export const FORM_OPTION_KEYS: Readonly<Record<string, readonly string[]>> = {
|
||||
...revetKeys("inlet"),
|
||||
...revetKeys("outlet"),
|
||||
"inlet_revet_berm_m",
|
||||
"outlet_revet_berm_m",
|
||||
"outlet_revet_berm_m", // 칸 없앰(89-4) — 목록에 남겨 다음 [저장] 때 옛 값을 지움
|
||||
],
|
||||
revetment: ["side", ...revetKeys("inlet"), ...revetKeys("outlet")],
|
||||
};
|
||||
|
||||
@@ -329,8 +329,8 @@ def _guard_set(values: dict[str, Any], body: str, diameter_m: float) -> dict[str
|
||||
for role, key in zip(("inlet", "outlet"), _GUARD_KEYS[body]):
|
||||
guarded = str(values.get(key) or "").strip() == "흙막이"
|
||||
sides[role] = _revet_side(values, role) if guarded else {"role": role, "structure": ""}
|
||||
if guarded:
|
||||
# 절토부 앞 수평공간(79-7) — 흙막이 컨테이너 칸 · 빈 값 = 화면 기본값
|
||||
if guarded and role == "inlet":
|
||||
# 절토부 앞 수평공간(79-7) — 유입만(유출은 없음 · 89-4) · 빈 값 = 화면 기본값
|
||||
sides[role]["revet_berm_m"] = _number(values.get(f"{role}_revet_berm_m"), None)
|
||||
if not any(side["structure"] for side in sides.values()):
|
||||
return None
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 집수정 계류측 두 채널의 입력을 짜고 면적용(미확정 단 제외) 결과를 함께 돌려준다.
|
||||
* ========================================================================== */
|
||||
|
||||
import type { CrossSection, CulvertSideSpec } from "./B06_Section_Api_Fetch";
|
||||
import type { CrossSection } from "./B06_Section_Api_Fetch";
|
||||
import {
|
||||
GUARD_CUT_BERM_M,
|
||||
REVET_EMBED_DEPTH_M,
|
||||
@@ -85,17 +85,14 @@ export function buildCulvertTiers(input: {
|
||||
side: culvert?.hidden_pipe ? culvert.inlet.face_side : null,
|
||||
});
|
||||
// 세월교 · 박스 흙막이 끝 절토 = 컨테이너 수평공간 뒤 절토 경사(79-7) · 배관은 종전(3도 선).
|
||||
const cutBerm = (spec: CulvertSideSpec | undefined) =>
|
||||
const cutBerm = (lengthM: number) =>
|
||||
culvert?.body
|
||||
? {
|
||||
lengthM: Math.max(spec?.revet_berm_m ?? GUARD_CUT_BERM_M, 0),
|
||||
slopeRatio: section.design?.cut_slope_ratio ?? 1.0,
|
||||
}
|
||||
? { lengthM: Math.max(lengthM, 0), slopeRatio: section.design?.cut_slope_ratio ?? 1.0 }
|
||||
: undefined;
|
||||
// 성토부선 + 다단 기슭막이(2026-08-22 — 보호공 삭제, 윗면 선만 성토부선으로).
|
||||
// 유출측과 **집수정 계류측**이 같은 체계를 쓴다: 5m 넘으면 다단을 둘 수 있다.
|
||||
const outletInput = {
|
||||
cutBerm: cutBerm(culvert?.outlet),
|
||||
cutBerm: cutBerm(0), // 유출 수평공간 없음 — 끝이 원지반에 묻히면 바로 절토(89-4)
|
||||
startBottomElevation: input.outletBottom,
|
||||
outward: input.outletInfo.outward,
|
||||
groundAt,
|
||||
@@ -109,7 +106,7 @@ export function buildCulvertTiers(input: {
|
||||
// 독립 기슭막이(관 숨김)는 집수정이 없어 이 채널이 **유입측 벽의 성토부선·다단**이다 —
|
||||
// 유출측(`extras`)과 같은 시작점(관 하단 꼭짓점)·같은 `bextra` 키(2026-08-29 사용자).
|
||||
const basinInput = {
|
||||
cutBerm: cutBerm(culvert?.inlet),
|
||||
cutBerm: cutBerm(culvert?.inlet.revet_berm_m ?? GUARD_CUT_BERM_M),
|
||||
startBottomElevation: input.basinBottom,
|
||||
outward: input.inletInfo.outward,
|
||||
groundAt,
|
||||
|
||||
@@ -213,12 +213,13 @@ export function guardSet(
|
||||
diameterM: number,
|
||||
): CulvertSetSpec | null {
|
||||
const [inletKey, outletKey] = GUARD_KEYS[body];
|
||||
// 절토부 앞 수평공간(79-7) — 흙막이 컨테이너 칸 · 빈 값 = 화면 기본값
|
||||
const side = (role: string, key: string): CulvertSetSpec =>
|
||||
String(values[key] ?? "").trim() === "흙막이"
|
||||
? { ...revetSide(values, role), revet_berm_m: num(values[`${role}_revet_berm_m`], null) }
|
||||
? revetSide(values, role)
|
||||
: { role, structure: "" };
|
||||
const inlet = side("inlet", inletKey);
|
||||
// 절토부 앞 수평공간(79-7) — 유입만(유출은 없음 · 89-4) · 빈 값 = 화면 기본값
|
||||
if (inlet.structure) inlet.revet_berm_m = num(values.inlet_revet_berm_m, null);
|
||||
const outlet = side("outlet", outletKey);
|
||||
if (!inlet.structure && !outlet.structure) return null;
|
||||
return {
|
||||
|
||||
@@ -3418,7 +3418,6 @@
|
||||
"outlet.height": "높이 (m)",
|
||||
"outlet.before": "기준측점 전 (m)",
|
||||
"outlet.after": "기준측점 후 (m)",
|
||||
"outlet.berm": "수평공간 (m)",
|
||||
"soil_add": "흙막이",
|
||||
"soil_add.form": "형태",
|
||||
"soil_add.form=돌쌓기(찰)": "돌 쌓기(찰)",
|
||||
@@ -3489,7 +3488,6 @@
|
||||
"outlet.height": "높이 (m)",
|
||||
"outlet.before": "기준측점 전 (m)",
|
||||
"outlet.after": "기준측점 후 (m)",
|
||||
"outlet.berm": "수평공간 (m)",
|
||||
"soil_add": "흙막이",
|
||||
"soil_add.form": "형태",
|
||||
"soil_add.form=돌쌓기(찰)": "돌 쌓기(찰)",
|
||||
|
||||
@@ -137,9 +137,14 @@ out.boxWing = wingLayout.sides.map((s) => ({
|
||||
// ⑤ 흙막이 끝 절토 = 수평공간 뒤 절토 경사 — 수평공간 칸 값을 따름
|
||||
function cutTails(section, list) {
|
||||
const culvert = computeStoredLayouts(section, list).culvert;
|
||||
return [...culvert.outletFill.segments, ...culvert.basinFill.segments]
|
||||
const tagged = (segments, role) => segments.map((g) => ({ ...g, role }));
|
||||
return [
|
||||
...tagged(culvert.outletFill.segments, "outlet"),
|
||||
...tagged(culvert.basinFill.segments, "inlet"),
|
||||
]
|
||||
.filter((g) => g.kind === "cut")
|
||||
.map((g) => ({
|
||||
role: g.role,
|
||||
points: g.points.length,
|
||||
berm: r3(Math.abs(g.points[1].offset - g.points[0].offset)),
|
||||
flat: r3(g.points[1].elevation - g.points[0].elevation),
|
||||
@@ -148,7 +153,7 @@ function cutTails(section, list) {
|
||||
out.cutDefault = [cutTails(at(352.14), sections), cutTails(at(173.087), sections)];
|
||||
const berm2 = clone(at(352.14));
|
||||
berm2.culvert.inlet.revet_berm_m = 2;
|
||||
berm2.culvert.outlet.revet_berm_m = 2;
|
||||
berm2.culvert.outlet.revet_berm_m = 2; // 유출은 칸이 없어 무시(89-4)
|
||||
out.cutBerm2 = cutTails(
|
||||
berm2,
|
||||
sections.map((s) => (s === at(352.14) ? berm2 : s)),
|
||||
|
||||
@@ -35,7 +35,7 @@ GUARD = [
|
||||
["height", ["흙막이"], None], # 빈 칸 = 본체 끝 높이(79-7 · 횡단도가 셈)
|
||||
["before", ["흙막이"], "5"],
|
||||
["after", ["흙막이"], "5"],
|
||||
["berm", ["흙막이"], "1"], # 절토부 앞 수평공간(79-7)
|
||||
["berm", ["흙막이"], "1"], # 절토부 앞 수평공간(79-7) — 유입만(89-4)
|
||||
]
|
||||
|
||||
|
||||
@@ -55,7 +55,8 @@ def out() -> dict:
|
||||
def test_유입_유출은_구조와_흙막이_칸만(out):
|
||||
for spec in ("ford", "box"):
|
||||
for side in ("inlet", "outlet"):
|
||||
assert out[spec][side] == GUARD, (spec, side)
|
||||
want = GUARD if side == "inlet" else GUARD[:-1]
|
||||
assert out[spec][side] == want, (spec, side)
|
||||
assert [i[0] for i in out[spec]["soil_add"]] == [
|
||||
"form",
|
||||
"length",
|
||||
|
||||
@@ -79,12 +79,14 @@ def test_박스_날개벽_끝_절성토선(shape: dict) -> None:
|
||||
def test_흙막이_끝_절토는_수평공간_뒤(shape: dict) -> None:
|
||||
tails = [tail for group in shape["cutDefault"] for tail in group]
|
||||
assert tails, "절토 끝선이 없음"
|
||||
assert any(tail["role"] == "inlet" for tail in tails)
|
||||
for tail in tails:
|
||||
assert tail["points"] == 3
|
||||
assert tail["flat"] == pytest.approx(0, abs=1e-6)
|
||||
assert tail["berm"] == pytest.approx(1.0, abs=1e-3)
|
||||
# 유출은 수평공간 없음 — 묻히면 바로 절토(89-4)
|
||||
assert tail["berm"] == pytest.approx(1.0 if tail["role"] == "inlet" else 0, abs=1e-3)
|
||||
for tail in shape["cutBerm2"]:
|
||||
assert tail["berm"] == pytest.approx(2.0, abs=1e-3)
|
||||
assert tail["berm"] == pytest.approx(2.0 if tail["role"] == "inlet" else 0, abs=1e-3)
|
||||
|
||||
|
||||
def test_유입_수평공간_시작은_본체_바닥(shape: dict) -> None:
|
||||
|
||||
Reference in New Issue
Block a user