박스 위 성토선이 빠져 있었다. 물매 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>
109 lines
4.4 KiB
TypeScript
109 lines
4.4 KiB
TypeScript
/* =============================================================================
|
||
* 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}m·${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);
|
||
}
|