Files
Pier_v1/src/view/coping-rebar-svg.ts
T
ypshimandClaude Haiku 5.5 32cf01a29f fix(rebar): section labels once per bar and spots once
- The coping section listed every piece of a bar as its own crossing, so the
  same spot and name were drawn many times. Each spot is drawn once and each
  bar name is written once.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-26 14:28:33 +09:00

79 lines
4.0 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.
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<string, string> = { 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(`<polyline points="${pts}" fill="none" stroke="${COLOR[copingBarGroup(r.name)]}" stroke-width="10"></polyline>`);
}
parts.push(`<text x="${lengthMm / 2}" y="${-200}" text-anchor="middle" font-size="260" fill="#18212B">코핑 길이 ${lengthMm}</text>`);
const h = (top - bottom) * 1000;
return `<svg viewBox="-400 -700 ${lengthMm + 800} ${h + 1000}" width="100%" style="max-width: 760px; height: auto" role="img" aria-label="코핑 배근 정면">${parts.join('')}</svg>`;
}
/**
* 단면 · 폐합 구성 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<string>();
const named = new Set<string>();
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 = `<circle cx="${c.x * 1000}" cy="${(top - c.y) * 1000}" r="${c.group === 'step' ? 22 : 30}" fill="${COLOR[c.group]}"></circle>`;
if (named.has(c.name)) return dot;
named.add(c.name);
return `${dot}<text x="${c.x * 1000 + 60}" y="${(top - c.y) * 1000 - 60}" font-size="120" fill="#18212B">${c.name}</text>`;
});
return `<svg viewBox="${minX - 800} -800 ${maxX - minX + 1600} ${(top - Math.min(...els)) * 1000 + 1600}" width="100%" style="max-width: 560px; height: auto" role="img" aria-label="코핑 배근 단면 · 폐합 구성 at s = ${s0} m">${parts.join('')}</svg>`;
}