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:
@@ -6,7 +6,7 @@
|
||||
* 캐시(`section.culvert`·조정창 제어기)만 고친다 — 영구저장은 [저장]·[확정] 몫이다.
|
||||
* ========================================================================== */
|
||||
|
||||
import { pipeDiameterM } from "@util/common_util_culvert_sets";
|
||||
import { guardSet, pipeDiameterM } from "@util/common_util_culvert_sets";
|
||||
import type { DetailPipeInput } from "../B04_PreProcess/B04_PreProcess_Api_Fetch";
|
||||
import { PIPE_MATCH_M } from "../A00_Common/design/design_selection";
|
||||
import type { SectionDetailResponse } from "./B06_Section_Api_Fetch";
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
revetWallSpec,
|
||||
WALL_MIN_PURE_HEIGHT_M,
|
||||
} from "./B06_Section_UI_Cross_Culvert_Const";
|
||||
import type { CrossSection } from "./B06_Section_Api_Fetch";
|
||||
import type { CrossSection, CulvertSet } from "./B06_Section_Api_Fetch";
|
||||
import type { createStationControls } from "./B06_Section_UI_Page_Station_Controls";
|
||||
|
||||
type StationControls = ReturnType<typeof createStationControls>;
|
||||
@@ -69,16 +69,14 @@ export function applyPipeOptionsToCache(
|
||||
if (!owner) return;
|
||||
// 세월교는 스펙 자리(`section.ford`)가 배수관과 달라 조정창 제어기로 보낸다 —
|
||||
// 로직은 그쪽 것을 쓰고 프론트만 폼 UI다(2026-08-30 사용자 확정).
|
||||
if (owner.ford) {
|
||||
applyFordFormOptions(ctx.ford, owner.chainage_m, patch);
|
||||
return;
|
||||
}
|
||||
if (owner.ford) applyFordFormOptions(ctx.ford, owner.chainage_m, patch);
|
||||
// BOX암거도 스펙 자리가 따로다(`section.box`) — 같은 규칙으로 제어기에 보낸다
|
||||
// (2026-08-30 사용자: 세월교와 같은 문제).
|
||||
if (owner.box) {
|
||||
applyBoxFormOptions(ctx.box, owner.chainage_m, patch);
|
||||
return;
|
||||
}
|
||||
else if (owner.box) applyBoxFormOptions(ctx.box, owner.chainage_m, patch);
|
||||
// 세월교 · 박스 흙막이(73-4) — 폼 칸 전부로 세트를 다시 짓고 아래 배관 흙막이 길(높이 · 형태 ·
|
||||
// 추가 흙막이)을 그대로 탐. 일부만 온 되얹기는 세트를 못 지어 건너뜀.
|
||||
const body = owner.ford ? "ford" : owner.box ? "box" : null;
|
||||
if (body && (!whole || !syncBodyGuard(owner, body, patch))) return;
|
||||
const culvert = owner.culvert;
|
||||
if (!culvert) return;
|
||||
const num = (key: string): number | undefined => {
|
||||
@@ -111,7 +109,7 @@ export function applyPipeOptionsToCache(
|
||||
own(side, "revet_length_m", num(`${prefix}_revet_length_m`));
|
||||
own(side, "revet_before_m", num(`${prefix}_revet_before_m`));
|
||||
own(side, "revet_after_m", num(`${prefix}_revet_after_m`));
|
||||
put(side, "structure", patch[`${prefix}_type`]);
|
||||
if (!body) put(side, "structure", patch[`${prefix}_type`]); // 세월교 구조 칸 = 흙막이 고르기
|
||||
}
|
||||
// 배관 벽의 **높이·형태는 스펙이 아니라 조정값**(revet_adjust.h·m)이 정한다
|
||||
// — `revetWallSpec`이 배관에서는 spec.revet_height_m을 쓰지 않기 때문이다.
|
||||
@@ -144,7 +142,14 @@ export function applyPipeOptionsToCache(
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!culvert.hidden_pipe) applySoilTiers(ctx, owner, patch, whole);
|
||||
// 세월교 · 박스 추가 흙막이 = 폼 상자 수가 유출 다단 수(배관은 횡단이 정함 · 73-4)
|
||||
const soil = body ? soilCount(patch) : null;
|
||||
if (soil !== null && soil !== (owner.design?.extra_wall_counts?.outlet ?? 0))
|
||||
// 늦게 불러옴 — 흙막이 세트 때만 쓰는 횡단 기하 묶음(배관 폼 길은 안 끌어옴)
|
||||
void import("../A00_Common/design/design_structure_pipe_calc").then((calc) =>
|
||||
calc.setPipeExtraCount(owner.chainage_m, soil),
|
||||
);
|
||||
if (!culvert.hidden_pipe || body) applySoilTiers(ctx, owner, patch, whole);
|
||||
own(culvert.inlet, "basin_length_m", num("inlet_basin_length_m"));
|
||||
own(culvert.inlet, "basin_before_m", num("inlet_basin_before_m"));
|
||||
own(culvert.inlet, "basin_after_m", num("inlet_basin_after_m"));
|
||||
@@ -165,6 +170,34 @@ export function applyPipeOptionsToCache(
|
||||
}
|
||||
}
|
||||
|
||||
/** 세월교 · 박스 흙막이 세트를 폼 칸 전부로 다시 지음(73-4) — 흙막이가 없으면 걷음. */
|
||||
function syncBodyGuard(
|
||||
owner: CrossSection,
|
||||
body: "box" | "ford",
|
||||
patch: Record<string, number | string>,
|
||||
): boolean {
|
||||
const box = owner.box;
|
||||
const diameter = box
|
||||
? box.top_thickness_m + box.inner_height_m + box.slab_thickness_m
|
||||
: (owner.ford?.diameter_m ?? 1);
|
||||
const guard = guardSet(patch, body, Number(diameter.toFixed(3)));
|
||||
if (!guard) delete owner.culvert;
|
||||
else
|
||||
owner.culvert = {
|
||||
...(guard as unknown as CulvertSet),
|
||||
chainage_m: ((box ?? owner.ford) as { chainage_m?: number } | undefined)?.chainage_m,
|
||||
};
|
||||
return !!guard;
|
||||
}
|
||||
|
||||
/** 추가 흙막이 상자 수 — 가장 큰 `soil_add_{n}_*` 의 n. */
|
||||
function soilCount(patch: Record<string, number | string>): number {
|
||||
return Math.max(
|
||||
0,
|
||||
...Object.keys(patch).map((key) => Number(/^soil_add_(\d+)_/.exec(key)?.[1] ?? 0)),
|
||||
);
|
||||
}
|
||||
|
||||
/** 흙막이 컨테이너가 마지막으로 보낸 높이 · 형태(`측점:extra{n}`) — 기하가 다시 그릴 때마다 키를 지형에
|
||||
* 맞춰 고쳐 되받으므로(syncApplied) 되받은 값과 견주면 안 바꾼 단도 다시 보냄(54-10). */
|
||||
const soilSent = new Map<string, { h: number | null; m: string | null }>();
|
||||
|
||||
Reference in New Issue
Block a user