Files
Aislo/B06_Section/B06_Section_UI_Cross_Box.ts
T
eomsangdonandClaude Opus 5 d4c8de738a feat(B05·B06): BOX암거 횡단도 · 3D 중공 셸·날개벽·관통 보어
BOX암거를 세월교와 같은 계류 방향 절단면으로 그리고, 3D는 속이 빈 서피스 셸로
만든다. 관통 컷·중공 튜브는 배수관 세트와 세월교에도 함께 적용한다.

B06 횡단도
- `_box_set()` — 내공 폭·높이(B05 옵션), 부재 두께 세월교 승계(측벽 0.2·상하판 0.3),
  복토 0.5m(별표2 교차 참조), 날개벽 투영 저판 연장
- 상판 윗면 = 노면 − 복토, 구체는 수평 설치(교본 3장). 계류 하상과의 고저차는 툴팁 표기
- 구체가 노면 아래 묻히므로 계획선은 자르지 않는다(세월교와 다른 점)

3D
- 로프트가 **링별 폴리곤**을 받는다 — 구조물을 위·아래 조각으로 갈라 링마다 틈을
  벌려 빈 공간을 만든다. 틈 0이면 속 찬 단면 그대로라 토폴로지가 유지된다
- BOX = 내공 구간만 틈을 벌려 측벽이 저절로 생기는 셸 + 날개벽 4매(날개 축 프레임)
- 관통 보어 = 링 y에서 틈 높이 2·√(r²−(y−y₀)²) → 배관 외측 기준 원형 구멍.
  기슭막이·집수정·세월교 측벽에 적용
- 배관은 중공 튜브(바깥·안쪽 원통 + 양 끝 고리)로 내부 유로를 보인다

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

94 lines
3.6 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);
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);
}