Files
Aislo/B06_Section/B06_Section_UI_Cross_Box.ts
T
eomsangdonandClaude Opus 5 55e97b8925 fix(B06): BOX암거 성토선 추가 — 박스 최상단을 지나고 노견은 수평 연장
박스 위 성토선이 빠져 있었다. 물매 1:1.2를 고정하고 성토선이 **박스 최상단
모서리**를 지나게 하면, 박스를 길게 잡을수록 노견이 저절로 수평 연장되고 각도는
그대로다(기슭막이 "벽이 밖으로 나간 만큼 노폭 연장"과 같은 규칙, 2026-08-25 사용자).

- 구체 길이 = 노폭 + 2 × (노면 − 박스 상단) × 1.2. 노면이 크라운이라 좌·우 필요량이
  다르므로 큰 쪽에 맞춰 대칭으로 잡고, 남는 쪽은 노견을 수평 연장한다
- 성토선 = 노견 → (수평 연장) → 박스 최상단 모서리 → 1:1.2로 원지반까지
- 노견 밖 설계선은 이 성토선이 대신하므로 다시 트림한다

검증(10+0.9): 구체 ±2.744m, 저판 8.32m, 좌 성토선 물매 1.200·1.200,
우 성토선 수평 0.144m 뒤 1.200·1.200.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 12:52:40 +09:00

109 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =============================================================================
* B06_Section_UI_Cross_Box.ts
* BOX암거 측점 횡단 카드의 구체(상판·저판·내공) **오버레이 그리기**.
*
* 기하 계산은 `B06_Section_UI_Cross_Box_Geom.ts`가 맡는다(700줄 제한 분리).
* 여기서는 계산 결과(`BoxLayout`)를 SVG 도형으로 옮기기만 한다 — 치수 결정 금지.
*
* 그리는 순서 = 시공 순서: ① 저판 → ② 상판 → ③ 내공 윤곽 → ④ 성토선 → 라벨.
* 콘크리트 색은 세월교 바닥판 클래스를 그대로 쓴다.
* ========================================================================== */
import type { BoxLayout } from "./B06_Section_UI_Cross_Box_Geom";
import type { OffsetPoint } from "./B06_Section_UI_Cross_Culvert_Types";
export { computeBoxLayout } from "./B06_Section_UI_Cross_Box_Geom";
export type { BoxLayout } from "./B06_Section_UI_Cross_Box_Geom";
const SVG_NS = "http://www.w3.org/2000/svg";
/** BOX암거 구체 오버레이 — computeBoxLayout 결과를 그린다. */
export function appendBoxOverlay(
layer: SVGElement,
layout: BoxLayout,
x: (offset: number) => number,
toDisplayY: (elevation: number) => number,
): void {
const { box } = layout;
const toXY = (point: OffsetPoint): [number, number] => [
x(point.offset),
toDisplayY(point.elevation),
];
const polygon = (
points: OffsetPoint[],
className: string,
tooltip: string,
): SVGPolygonElement => {
const shape = document.createElementNS(SVG_NS, "polygon");
shape.setAttribute("points", points.map((point) => toXY(point).join(",")).join(" "));
shape.setAttribute("class", className);
const title = document.createElementNS(SVG_NS, "title");
title.textContent = tooltip;
shape.append(title);
return shape;
};
const wingText = (wing: {
installed: boolean;
length_m: number | null;
angle_deg: number | null;
}): string => (wing.installed ? `${wing.length_m ?? 0}${wing.angle_deg ?? 0}°` : "없음");
// ① 저판 — 날개벽 투영만큼 계류 상·하류로 길어진다.
layer.append(
polygon(
layout.bottomSlab,
"b06-chart__ford-slab",
`BOX암거 저판 — 길이 ${layout.slabLengthM.toFixed(2)}m · 두께 ${box.slab_thickness_m.toFixed(2)}m` +
` (날개벽 유입 ${wingText(box.wing_in)} / 유출 ${wingText(box.wing_out)} 투영 연장 반영)` +
(layout.bedGapM > 0.05
? ` · 계류 하상보다 ${layout.bedGapM.toFixed(2)}m 높음`
: layout.bedGapM < -0.05
? ` · 계류 하상보다 ${Math.abs(layout.bedGapM).toFixed(2)}m 낮음(터파기)`
: ""),
),
);
// ② 상판 — 윗면이 노면 아래 복토 두께만큼 내려온다.
layer.append(
polygon(
layout.topSlab,
"b06-chart__ford-slab",
`BOX암거 상판 — 두께 ${box.top_thickness_m.toFixed(2)}m · 윗면이 노면 ${box.cover_m.toFixed(2)}m(복토)`,
),
);
// ③ 내공(유로) — 면은 비우고 윤곽만 점선으로 두른다.
const barrel = polygon(
layout.barrel,
"b06-chart__box-barrel",
`BOX암거 내공(유로) ${box.inner_width_m.toFixed(1)}×${box.inner_height_m.toFixed(1)}m` +
` · 측벽 두께 ${box.wall_thickness_m.toFixed(2)}m`,
);
layer.append(barrel);
// ④ 성토선 — 노견에서 (연장 후) 박스 최상단 모서리를 지나 원지반까지, 물매 1:1.2.
for (const line of layout.fillLines) {
if (line.length < 2) continue;
const polyline = document.createElementNS(SVG_NS, "polyline");
polyline.setAttribute("points", line.map((point) => toXY(point).join(",")).join(" "));
polyline.setAttribute("class", "b06-chart__design-cross");
const title = document.createElementNS(SVG_NS, "title");
const height = line[0].elevation - line[line.length - 1].elevation;
title.textContent =
`BOX암거 성토선 — 물매 1:1.2 · 성토고 ${height.toFixed(2)}m` +
" (박스 최상단 모서리를 지난다)";
polyline.append(title);
layer.append(polyline);
}
const label = document.createElementNS(SVG_NS, "text");
const [labelX, labelY] = toXY(layout.label.at);
label.setAttribute("x", String(labelX));
label.setAttribute("y", String(labelY));
label.setAttribute("text-anchor", "middle");
label.setAttribute("class", "b06-chart__culvert-label");
label.textContent = layout.label.text;
layer.append(label);
}