Files
Aislo/B06_Section/B06_Section_UI_Cross_Excavation.ts
T
eomsangdonandClaude Opus 5 bec7e841e0 feat(B06): 횡단도에 터파기 선 — 관은 법정 표값 폭, 벽은 실무 정본 수직
사용자 확정 ④ 「터파기가 횡단도에서 표현이 안 된다 → 교본·실무문서로 구현 →
노티스 → 사용자가 선택」.

- 관: 관 위에서 관 바닥까지 **표값 폭**으로 수직(KCS 44 40 10 그림 3.2-1).
  표에 없는 관경이면 **안 그린다** — 폭을 지어내지 않는다.
- 벽: 벽 바닥선 아래로 **수직**(정본 식에 기울기 항 없음). 폭 = 바닥폭 + 여유 0.2,
  깊이 = 기초유 0.5 / 기초버림 0.1.
- ⚠ `foundation` 이 비어 있으면 **안 그린다** — 고르기 전에는 근거가 없다.
- 말풍선에 **근거의 급을 갈라** 적는다: 관은 「법정 표」, 벽은 「품셈에 규정이 없어
  실무 관행으로 정한 값」.
- 파선(`.b06-chart__excavation`)으로 구조물보다 먼저 그려 벽 밑에 깔린다.

화면 확인: 관 터파기 5개소, Φ1000 → 1.80m · Φ800 → 1.60m (표값 그대로).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 21:35:24 +09:00

120 lines
5.0 KiB
TypeScript

/* =============================================================================
* B06_Section_UI_Cross_Excavation.ts
* 횡단도에 **터파기 선**을 얹는다 — 관·벽 두 갈래(2026-09-09 사용자 확정 ④).
*
* 왜 있나 — 「터파기가 현재 횡단도에서 표현이 안 된다」가 사용자 지적이었다. 수량을
* 세기 전에 **도면에 그것이 보여야** 한다.
*
* ⚠ 근거의 급이 다르므로 **노티스를 갈라 적는다** — 값은 `common_util_excavation` 한 곳에서
* 온다(파이썬 짝과 거울 시험으로 묶여 있다). 여기서는 **그리기만** 한다.
* 관 : 법정 시방서 표(KCS 44 40 10 그림 3.2-1)
* 벽 : 법정 근거 없음 — 실무 정본 식(구조도 기슭막이 xls)
* ⚠ **비탈을 주지 않는다 — 수직으로 그린다.** 정본 식에 기울기 항이 없다.
* ========================================================================== */
import {
BASIS_PIPE,
BASIS_WALL,
WALL_BLINDING_DEPTH_M,
WALL_BLINDING_WIDTH_M,
WALL_FOUNDATION_DEPTH_M,
WALL_FOUNDATION_WIDTH_M,
WALL_TRENCH_CLEARANCE_M,
pipeTrenchWidthM,
} from "@util/common_util_excavation";
import type { WallLayout } from "./B06_Section_UI_Cross_Culvert_Types";
const SVG_NS = "http://www.w3.org/2000/svg";
/** 「기초유」인가 — 저장 제원 `foundation` 의 값. 비어 있으면 **그리지 않는다**(근거 없음). */
export function foundationChoice(value: unknown): "기초유" | "기초버림" | null {
const text = typeof value === "string" ? value.trim() : "";
if (text === "기초유") return "기초유";
if (text === "기초버림") return "기초버림";
return null;
}
function trenchPath(layer: SVGElement, corners: Array<[number, number]>, tooltip: string): void {
const line = document.createElementNS(SVG_NS, "polyline");
line.setAttribute("points", corners.map(([px, py]) => `${px},${py}`).join(" "));
line.setAttribute("class", "b06-chart__excavation");
const title = document.createElementNS(SVG_NS, "title");
title.textContent = tooltip;
line.append(title);
layer.append(line);
}
/**
* 관 터파기 — **표값 폭**으로 관 위 지반선에서 관 바닥까지 수직으로 판다.
* 표에 없는 관경이면 **아무것도 그리지 않는다**(폭을 지어내지 않는다).
*/
export function appendPipeTrench(
layer: SVGElement,
input: {
/** 관 중심 offset(m). */
centerOffsetM: number;
/** 관 바닥(invert) 표고. */
invertM: number;
/** 터파기 윗면 표고 — 관 위 지반(또는 노면)선. */
topM: number;
diameterMm: number | null | undefined;
},
x: (offset: number) => number,
toDisplayY: (elevation: number) => number,
): number | null {
const width = pipeTrenchWidthM(input.diameterMm);
if (width === null || !(input.topM > input.invertM)) return null;
const half = width / 2;
const left = input.centerOffsetM - half;
const right = input.centerOffsetM + half;
trenchPath(
layer,
[
[x(left), toDisplayY(input.topM)],
[x(left), toDisplayY(input.invertM)],
[x(right), toDisplayY(input.invertM)],
[x(right), toDisplayY(input.topM)],
],
`관 터파기 폭 ${width.toFixed(2)}m · 깊이 ${(input.topM - input.invertM).toFixed(2)}m\n` +
${Math.round(Number(input.diameterMm))}㎜ — ${BASIS_PIPE}`,
);
return width;
}
/**
* 벽 터파기 — 벽 바닥선 아래로 **수직**. 폭은 바닥 폭 + 여유 0.2, 깊이는 기초 몫.
* `foundation` 이 비어 있으면 **그리지 않는다** — 사용자가 고르기 전에는 근거가 없다.
*/
export function appendWallTrench(
layer: SVGElement,
wall: WallLayout,
foundation: "기초유" | "기초버림" | null,
x: (offset: number) => number,
toDisplayY: (elevation: number) => number,
): { widthM: number; depthM: number } | null {
if (foundation === null) return null;
const hasFoundation = foundation === "기초유";
const depth = hasFoundation ? WALL_FOUNDATION_DEPTH_M : WALL_BLINDING_DEPTH_M;
// 폭: 기초 몫은 정본이 정한 값(0.9 / 0.7), 다만 벽 바닥이 그보다 넓으면 그 폭 + 여유.
const baseWidth = Math.abs(wall.bottomFront.offset - wall.bottomBack.offset);
const minWidth = hasFoundation ? WALL_FOUNDATION_WIDTH_M : WALL_BLINDING_WIDTH_M;
const width = Math.max(minWidth, baseWidth + (hasFoundation ? WALL_TRENCH_CLEARANCE_M : 0));
const center = (wall.bottomFront.offset + wall.bottomBack.offset) / 2;
const top = Math.max(wall.bottomFront.elevation, wall.bottomBack.elevation);
const bottom = top - depth;
const left = center - width / 2;
const right = center + width / 2;
trenchPath(
layer,
[
[x(left), toDisplayY(top)],
[x(left), toDisplayY(bottom)],
[x(right), toDisplayY(bottom)],
[x(right), toDisplayY(top)],
],
`${foundation} 터파기 폭 ${width.toFixed(2)}m · 깊이 ${depth.toFixed(2)}m (수직)\n` +
`품셈에 규정이 없어 실무 관행으로 정한 값 — ${BASIS_WALL}`,
);
return { widthM: width, depthM: depth };
}