/* ============================================================================= * B06_Section_UI_Cross_Revetment.ts * 독립 기슭막이 **그리기** — 자리 · 단 · 트림(기하)은 `logic/B06_Section_Cross_Revetment_Geom`(472-3). * 그리기(폴리곤·이음선·돌·성토부선)는 배관과 같은 공용 `_Cross_Wall`. * ========================================================================== */ import { tagShape } from "../common_util/common_util_pick_tag"; import { FILL_SLOPE_RATIO_MIN, REVET_EMBED_DEPTH_M, } from "./logic/B06_Section_UI_Cross_Culvert_Const"; import type { RevetmentLayout } from "./logic/B06_Section_Cross_Revetment_Geom"; import { foundationChoice } from "./logic/B06_Section_Cross_Excavation_Geom"; import { appendWallTrench } from "./B06_Section_UI_Cross_Excavation"; import { appendPlanLine, drawRevetWall } from "./B06_Section_UI_Cross_Wall"; const SVG_NS = "http://www.w3.org/2000/svg"; /** 횡단 카드에 독립 기슭막이(1단+다단)를 그린다 — 배관 벽과 같은 도형. 그렸으면 setter. */ export function appendRevetmentOverlay( svg: SVGElement, layout: RevetmentLayout | null, x: (offset: number) => number, y: (elevation: number) => number, onSelect?: () => void, ): ((active: boolean) => void) | null { if (!layout || !layout.walls.length) return null; // 성토부선(벽 사이·벽 아래) 먼저 — 벽 밑에 깔린다. for (const segment of layout.fillSegments) { if (segment.kind === "cut") continue; appendPlanLine( svg, segment.points, x, y, `${layout.name} 성토부선 — 사면길이 ${segment.lengthM.toFixed(2)}m` + (segment.overLimit ? " (법정 5m 이상 — 단 추가 검토)" : " (법정 5m 이내)") + ` · 성토 물매 1:${(segment.ratio ?? 1.2).toFixed(2)}`, ); } // 터파기는 벽보다 **먼저** — 파선이 벽 밑에 깔린다. 기초 축이 비어 있으면 안 그린다. const trenchFoundation = foundationChoice(layout.foundation); for (const wall of layout.walls) { appendWallTrench(svg, wall, trenchFoundation, x, y); } const drawn: SVGPolygonElement[] = []; layout.walls.forEach((wall, index) => { const keyId = index === 0 ? "own" : `own-extra${index - 1}`; const tooltip = `${layout.name} ${wall.form ?? ""} H=${(wall.height + REVET_EMBED_DEPTH_M).toFixed(1)}m` + `(상단 = 성토선 접점, 전면 1:${wall.lean}(품셈 13-4-4 [주]⑪ 표준경사))` + (wall.floatGapM > 0.01 ? ` · ⚠ 바닥 원지반 이격 ${wall.floatGapM.toFixed(2)}m — 하부 지지 별도` : ` · 근입 ${REVET_EMBED_DEPTH_M.toFixed(2)}m`) + (wall.lengthM ? ` · 연장 ${wall.lengthM.toFixed(1)}m` : "") + // 「좌우로 옮겨도 안 움직인다」의 까닭 — 배관 벽 툴팁과 같은 문구(2026-09-07). (wall.shiftFloorM ? ` · ⚠ 좌우 이동 하한 ${wall.shiftFloorM.toFixed(2)}m — 성토선 각도(1:${FILL_SLOPE_RATIO_MIN})를` + ` 지키려면 그만큼은 나가 있어야 함. 그 아래 값을 넣으면 벽이 안 움직임` : ""); const shape = drawRevetWall(svg, wall, x, y, "b06-chart__culvert-revet", tooltip, keyId); if (onSelect) shape.classList.add("is-selectable"); if (layout.linked) shape.classList.add("is-linked"); tagShape(shape, layout.structureId, keyId); // 신원표(323-5) drawn.push(shape); }); // 클릭 판정용 투명 겹면 — **맨 위**에 얹는다. 돌 해칭이 벽 폴리곤을 덮어 직접 핸들러를 // 달면 가운데 클릭이 돌에 먹힌다(배관 벽과 같은 규칙 — 판정면만 위로). if (onSelect) { for (const wall of layout.walls) { const hit = document.createElementNS(SVG_NS, "polygon"); hit.setAttribute( "points", wall.points.map((p) => `${x(p.offset)},${y(p.elevation)}`).join(" "), ); hit.setAttribute("class", "b06-chart__culvert-revet-hit"); hit.addEventListener("click", (event) => { event.stopPropagation(); onSelect(); }); svg.append(hit); } } return (active: boolean): void => { for (const polygon of drawn) polygon.classList.toggle("is-active", active); }; }