- logic 새 파일 다섯 — Cross_Wall_Geom(벽 1매) · Cross_Wall_Hatch_Geom(무늬 띠 · 물빼기 · 도수로 띠 · 원지반선) · Cross_Excavation_Geom(벽 터파기) · Cross_Design_Surface_Geom(암선 · 포장층) · Cross_Revetment_Geom(독립 기슭막이 배치) - logic 으로 옮김 — Body_Guard → Cross_Body_Guard · Structure_Layouts · Route_Carry_Patch(내용 그대로 · import 경로만) - inferStationInterval → common_util_station(글자표 없는 자리 · common_util_svg 는 다시 내보냄) — Node 묶음 ui_template_locale 끌림 끊음 - 부르는 쪽 import 줄 · 등록부 ts 경로 · 시험 · 도우미 경로 - test_472_3 GEOMETRY_PULLS 다섯 줄 걷음 · Wall_Hatch · Excavation · Design_Surface 세 줄은 Culvert · Box · Ford 그림 파일(518-2)이 그리기 함수로 부르는 동안 남김 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
113 lines
4.7 KiB
TypeScript
113 lines
4.7 KiB
TypeScript
import type { PavementEntry } from "@util/common_util_structure_pavement";
|
|
import type { CrossDesign } from "./B06_Section_Api_Fetch";
|
|
import {
|
|
pavementLayers,
|
|
rockBoundarySegments,
|
|
} from "./logic/B06_Section_Cross_Design_Surface_Geom";
|
|
|
|
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
|
|
export function appendRockBoundaryOverlay(
|
|
svg: SVGElement,
|
|
samples: Array<{ offset_m?: number; elevation_m?: number | null; valid: boolean }>,
|
|
offsetM: number,
|
|
x: (offset: number) => number,
|
|
y: (elevation: number) => number,
|
|
): void {
|
|
for (const segment of rockBoundarySegments(samples, offsetM)) {
|
|
const polyline = document.createElementNS(SVG_NS, "polyline");
|
|
polyline.setAttribute(
|
|
"points",
|
|
segment.map((p) => `${x(p.offset_m)},${y(p.elevation_m)}`).join(" "),
|
|
);
|
|
polyline.setAttribute("class", "b06-chart__rock-boundary");
|
|
svg.append(polyline);
|
|
}
|
|
}
|
|
|
|
/** 포장사양 → 무늬(88) — 콘크리트 빗금 · 아스콘 점 · 쇄석 돌. 그 밖 = 채움만. */
|
|
const PAVE_MARKS: Record<string, { size: number; marks: string }> = {
|
|
콘크리트: { size: 6, marks: '<path d="M0,6 L6,0" class="b06-chart__pave-mark"/>' },
|
|
아스콘: {
|
|
size: 5,
|
|
marks: '<circle cx="1.5" cy="1.5" r="0.7" class="b06-chart__pave-mark--dot"/>',
|
|
},
|
|
쇄석: {
|
|
size: 9,
|
|
marks:
|
|
'<circle cx="2.5" cy="2.5" r="1.6" class="b06-chart__pave-mark"/>' +
|
|
'<ellipse cx="6.8" cy="6.5" rx="1.8" ry="1.2" class="b06-chart__pave-mark"/>',
|
|
},
|
|
};
|
|
/** 무늬 id 는 문서 안에서 유일 — 카드 · 다시 그리기마다 새 번호(물넘이 빗금과 같은 방식). */
|
|
let paveSeq = 0;
|
|
|
|
type PaveMark = { type_id: string; start_m?: number | null; end_m?: number | null };
|
|
|
|
/** 포장층 범위 표식(종류 · 시작 · 끝)이 그 노면공인가 — 겹친 노면공 가름. */
|
|
function isPaveLayerOf(data: DOMStringMap, mark: PaveMark): boolean {
|
|
const [low, high] = [mark.start_m ?? 0, mark.end_m ?? 0].sort((a, b) => a - b);
|
|
return (
|
|
data.paveType === mark.type_id &&
|
|
Math.abs(Number(data.paveStart) - low) < 0.01 &&
|
|
Math.abs(Number(data.paveEnd) - high) < 0.01
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 고른 노면공의 포장층만 `is-active`(152-5) — 범위 표식(종류 · 시작 · 끝)이 맞는 층. 겹친 노면공은 하나만.
|
|
* `mark` 없음 = 모두 풂.
|
|
*/
|
|
export function highlightPavement(root: ParentNode, mark?: PaveMark): void {
|
|
for (const layer of root.querySelectorAll<SVGElement>(".b06-chart__pavement")) {
|
|
layer.classList.toggle("is-active", !!mark && isPaveLayerOf(layer.dataset, mark));
|
|
}
|
|
}
|
|
|
|
/** 누른 자리의 포장층 → 그 노면공(189-1 · 형상 누름 = 목록 고름) · 범위 표식 없는 층 · 밖 = undefined. */
|
|
export function pavementAt<T extends PaveMark>(
|
|
target: EventTarget | null,
|
|
marks: ReadonlyArray<T>,
|
|
): T | undefined {
|
|
const layer = (target as Element | null)?.closest?.<SVGElement>(".b06-chart__pavement");
|
|
if (!layer?.dataset.paveType) return undefined;
|
|
return marks.find((mark) => isPaveLayerOf(layer.dataset, mark));
|
|
}
|
|
|
|
/**
|
|
* 포장층 — 층 꼭짓점 = `pavementLayers`(두께 · 확폭 켬/끔 · 차례로 쌓음) · 여기는 무늬 · 범위 표식 ·
|
|
* 그리기. 없으면 카드 포장 켬일 때 표준 두께 채움만.
|
|
*/
|
|
export function appendPavementOverlay(
|
|
svg: SVGElement,
|
|
design: CrossDesign,
|
|
x: (offset: number) => number,
|
|
y: (elevation: number) => number,
|
|
pavement?: readonly PavementEntry[] | null,
|
|
): void {
|
|
for (const { entry, corners } of pavementLayers(design, pavement)) {
|
|
const points = corners.map((p) => `${x(p.offset_m)},${y(p.elevation_m)}`).join(" ");
|
|
const polygon = document.createElementNS(SVG_NS, "polygon");
|
|
polygon.setAttribute("points", points);
|
|
polygon.setAttribute("class", "b06-chart__pavement");
|
|
if (entry) {
|
|
// 범위 표식 — 고른 노면공의 포장층만 강조(152-5 · 겹친 노면공 가름)
|
|
polygon.dataset.paveType = entry.type_id;
|
|
polygon.dataset.paveStart = String(entry.start_m);
|
|
polygon.dataset.paveEnd = String(entry.end_m);
|
|
}
|
|
const spec = entry?.spec;
|
|
const mark = spec ? PAVE_MARKS[spec] : undefined;
|
|
if (spec && mark) {
|
|
// 무늬는 바탕 채움째 한 패턴 — 다각형 한 장(B07 도면 수확이 겹선을 안 담음)
|
|
const id = `b06-pave-hatch-${(paveSeq += 1)}`;
|
|
const defs = document.createElementNS(SVG_NS, "defs");
|
|
defs.innerHTML = `<pattern id="${id}" patternUnits="userSpaceOnUse" width="${mark.size}" height="${mark.size}"><rect width="${mark.size}" height="${mark.size}" class="b06-chart__pave-bg"/>${mark.marks}</pattern>`;
|
|
svg.append(defs);
|
|
polygon.style.fill = `url(#${id})`;
|
|
polygon.dataset.paveSpec = spec;
|
|
}
|
|
svg.append(polygon);
|
|
}
|
|
}
|