import type { BarRecord } from './rebar3d'; /** * 코핑 배근 · 정면 and 단면·폐합 (승인 2026-10-26, 추천: 3D 철근 기록을 그대로 투영). * 정면: x = the coping length (s), y = EL. 단면: x = 교축 (n, the local z), y = EL, at one station s0: the bars that cross it. * Colours by bar group: C1–C8 main, C9–C11 closed loops, C12–C13 sides, C14 points, C15–C16 steps. */ const COLOR: Record = { main: '#0F5C66', loop: '#6B3FA0', side: '#2E7D32', step: '#B4531F', other: '#6B7280' }; export function copingBarGroup(name: string): keyof typeof COLOR { const n = Number(/^C(\d+)/.exec(name)?.[1]); if (!Number.isFinite(n)) return 'other'; if (n <= 8) return 'main'; if (n <= 11) return 'loop'; if (n <= 13) return 'side'; return 'step'; } const isCoping = (r: BarRecord) => /^C\d/.test(r.name); /** 정면: the bars seen from the 교축 direction. */ export function copingFrontSvg(records: BarRecord[], lengthMm: number): string { const bars = records.filter(isCoping); const els = bars.flatMap((r) => r.pts.map((p) => p[1])); const top = Math.max(...els); const bottom = Math.min(...els); const Y = (el: number) => (top - el) * 1000; const parts: string[] = []; for (const r of bars) { const pts = r.pts.map((p) => `${p[0] * 1000},${Y(p[1])}`).join(' '); parts.push(``); } parts.push(`코핑 길이 ${lengthMm}`); const h = (top - bottom) * 1000; return `${parts.join('')}`; } /** * 단면 · 폐합 구성 at the station s0 (m): the bars that cross it, seen along the coping length. * A bar crossing s0 is drawn at the crossing point (n, EL); a bar along the 교축 at s0 is drawn as its line. */ export function copingSectionSvg(records: BarRecord[], s0: number): string { const crossings: { name: string; x: number; y: number; group: keyof typeof COLOR }[] = []; const bars = records.filter(isCoping); const els = bars.flatMap((r) => r.pts.map((p) => p[1])); const top = Math.max(...els); for (const r of bars) { for (let i = 0; i < r.pts.length - 1; i++) { const a = r.pts[i]; const b = r.pts[i + 1]; const lo = Math.min(a[0], b[0]); const hi = Math.max(a[0], b[0]); if (s0 < lo || s0 > hi) continue; const t = hi - lo < 1e-9 ? 0 : (s0 - a[0]) / (b[0] - a[0]); crossings.push({ name: r.name, x: a[2] + (b[2] - a[2]) * t, y: a[1] + (b[1] - a[1]) * t, group: copingBarGroup(r.name) }); } } // The same bar is recorded in several pieces, so the same spot can be crossed more than once: draw each spot once, // and write each bar name once (at its first crossing), so the labels do not pile up. const seen = new Set(); const named = new Set(); const unique = crossings.filter((c) => { const key = `${Math.round(c.x * 1e4)}|${Math.round(c.y * 1e4)}|${c.name}`; if (seen.has(key)) return false; seen.add(key); return true; }); const xs = unique.map((c) => c.x * 1000); const minX = Math.min(...xs, 0); const maxX = Math.max(...xs, 0); const parts: string[] = unique.map((c) => { const dot = ``; if (named.has(c.name)) return dot; named.add(c.name); return `${dot}${c.name}`; }); return `${parts.join('')}`; }