Files
Pier_v1/src/view/bearing-plan-svg.ts
T
ypshimandClaude Haiku 5.5 36b26209df feat(bearing): 솔플레이트 규칙을 교각별 입력으로 바꾸고 도면 불러오기 제거 (C안)
- 받침 기준 카드, 도면 불러오기(xlsx·csv·dxf), 도면 참조 표·오버레이 제거
- 교각마다 솔플레이트 규칙 입력: 받침높이 H(솔플레이트 포함) 211, 교직 L 1000,
  교축 W 1100, 종단 기울기 S −0.5 % (기본값, 가정 표시)
- 중심 두께 T = ⌈22 + L·|S|/200⌉ 로 계산해 얇은 쪽 T2가 22 mm 이상이 되게 함
  (기본값 T 25, T2 22.5, h3 = H − T = 186)
- geometry·scene·SVG는 교각 규칙에서 만든 같은 솔플레이트 계획을 모든 받침에 사용

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-11-03 11:07:51 +09:00

94 lines
6.3 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 { PierModel } from '../core/geometry';
import { bearingEdgeDistances, pairEccentricities } from '../core/bearing-layout';
/** Number of pairs the 상세 보기 chips can show (①–⑩ in the mockup). */
export const PAIR_CHIP_LABELS = ['①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨', '⑩'];
/**
* 교량받침 상세 · 평면 for one pair (승인 2026-10-26): the bearings keep their actual 교각축 positions (the offset
* between the two bearings is shown as it is), and the blocks and solplates follow them. 일체 draws one 2500 × 1200 outline; 분리 draws two 1200 × 1200 outlines. Dimensions: C1 (bearing to block edge),
* C2 (between the bearings), C3 (block span). C4 and C5 are the outer margins along the 교축.
*/
/**
* A rectangle centred on (cx, cy), turned by the bearing's 방위 (승인 2026-10-26). The angle is −rowSkewDeg, the same
* turn as the 3D block (rotateY(−skew)): the block's sides follow the bearing row.
*/
function rect(cx: number, cy: number, w: number, h: number, skewDeg: number, attrs: string): string {
return `<rect x="${cx - w / 2}" y="${cy - h / 2}" width="${w}" height="${h}" transform="rotate(${-skewDeg} ${cx} ${cy})" ${attrs}></rect>`;
}
export function bearingPlanSvg(m: PierModel, pairIndex: number): string {
const pair = pairEccentricities(m.blocks)[pairIndex];
if (!pair) return '';
const f = m.blocks.find((b) => b.no === pair.frontNo && b.row === '전열')!;
const k = m.blocks.find((b) => b.no === pair.backNo && b.row === '후열')!;
const nc = (f.n + k.n) / 2;
const sc = (f.s + k.s) / 2;
// x: 교축 (n); y: 교각축 (s), the actual positions relative to the pair's mean. The offset is kept, not aligned.
const X = (n: number) => (n - nc) * 1000;
const Y = (s: number) => (s - sc) * 1000;
const eccentricity = Math.abs(f.s - k.s) * 1000;
const parts: string[] = [];
// Axes: 교축 (horizontal, to the right) and 교각축 방향 (vertical, downward).
parts.push(`<line x1="-1700" y1="0" x2="1650" y2="0" stroke="#B4531F" stroke-width="6" stroke-dasharray="120 60 20 60"></line>`);
parts.push(`<polygon points="1650,0 1560,-40 1560,40" fill="#B4531F"></polygon>`);
parts.push(`<text x="1640" y="-60" text-anchor="end" font-size="90" fill="#B4531F">교축</text>`);
parts.push(`<line x1="0" y1="-900" x2="0" y2="900" stroke="#B4531F" stroke-width="6" stroke-dasharray="120 60 20 60"></line>`);
parts.push(`<polygon points="0,-900 -40,-810 40,-810" fill="#B4531F"></polygon>`);
parts.push(`<text x="60" y="-860" font-size="90" fill="#B4531F">교각축 방향</text>`);
// Block outline(s): one row.
const ib = m.integratedBlocks.find((x) => x.frontNo === pair.frontNo);
if (ib) {
parts.push(rect(X(ib.n), Y(ib.s), 2500, 1200, ib.rowSkewDeg, `fill="none" stroke="#6E8496" stroke-width="12"`));
} else {
for (const b of [f, k]) {
parts.push(rect(X(b.n), Y(b.s), 1200, 1200, b.rowSkewDeg, `fill="none" stroke="#6E8496" stroke-width="12"`));
}
}
// Solplates: the pier's plan (L across the 교축, W along the 교각축), centred on each bearing.
for (const b of [f, k]) {
parts.push(rect(X(b.n), Y(b.s), b.plan.L, b.plan.W, b.rowSkewDeg, `fill="#EEF3F7" stroke="#2F6DB5" stroke-width="10"`));
}
// Bearing centres and numbers.
for (const b of [f, k]) {
parts.push(`<circle cx="${X(b.n)}" cy="${Y(b.s)}" r="40" fill="#18212B"></circle>`);
parts.push(`<text x="${X(b.n) + 70}" y="${Y(b.s) - 60}" font-size="90" fill="#18212B">${b.no} · 방위 ${b.rowSkewDeg.toFixed(2)}°</text>`);
}
if (eccentricity > 0.5) {
parts.push(`<text x="0" y="${-700}" text-anchor="middle" font-size="80" fill="#55606E">편심 ${eccentricity.toFixed(0)} mm (교각축 방향, 위치 그대로)</text>`);
}
// Dimensions along the bottom: C1 (bearing to block edge), C2 (between the bearings), C1, and C3 (block span).
const dist = bearingEdgeDistances(m);
const d = (no: number) => dist.find((x) => x.no === no)!;
const xLeft = ib ? X(ib.n - 1.25) : X(f.n) - 600;
const xRight = ib ? X(ib.n + 1.25) : X(k.n) + 600;
const dimLine = (x1: number, x2: number, y: number, label: string) =>
`<line x1="${x1}" y1="${y}" x2="${x2}" y2="${y}" stroke="#55606E" stroke-width="6"></line><line x1="${x1}" y1="${y - 50}" x2="${x1}" y2="${y + 50}" stroke="#55606E" stroke-width="6"></line><line x1="${x2}" y1="${y - 50}" x2="${x2}" y2="${y + 50}" stroke="#55606E" stroke-width="6"></line><text x="${(x1 + x2) / 2}" y="${y + 110}" text-anchor="middle" font-size="90" fill="#18212B">${label}</text>`;
const y1 = 760;
const y2 = 1000;
// C1 and C2 are along the 교축 (horizontal), measured on the drawn positions.
parts.push(dimLine(xLeft, X(f.n), y1, `C1 ${Math.round(X(f.n) - xLeft)}`));
parts.push(dimLine(X(f.n), X(k.n), y1, `C2 ${Math.round(Math.abs(k.n - f.n) * 1000)}`));
parts.push(dimLine(X(k.n), xRight, y1, `C1 ${Math.round(xRight - X(k.n))}`));
parts.push(dimLine(xLeft, xRight, y2, `C3 ${Math.round((xRight - xLeft))}`));
// C4 and C5 as in the 시안: vertical dimensions on the left of the blocks, from the top edge to the bearing row (C4) and
// from the bearing row to the bottom edge (C5). Values: the model's C4 and C5 (outer margins).
const xv = xLeft - 300;
const halfDepth = 600;
const vdim = (y1: number, y2: number, label: string) =>
`<line x1="${xv}" y1="${y1}" x2="${xv}" y2="${y2}" stroke="#55606E" stroke-width="6"></line><line x1="${xv - 50}" y1="${y1}" x2="${xv + 50}" y2="${y1}" stroke="#55606E" stroke-width="6"></line><line x1="${xv - 50}" y1="${y2}" x2="${xv + 50}" y2="${y2}" stroke="#55606E" stroke-width="6"></line><text x="${xv - 90}" y="${(y1 + y2) / 2 + 30}" text-anchor="end" font-size="90" fill="#18212B">${label}</text>`;
// C4: from the low-side block edge to the front bearing; C5: from the back bearing to the high-side block edge.
const lowEdge = ib ? Y(ib.s) - 600 : Y(f.s) - 600;
const highEdge = ib ? Y(ib.s) + 600 : Y(k.s) + 600;
parts.push(vdim(lowEdge, Y(f.s), `C4 ${Math.round(d(f.no).leftMm)}`));
parts.push(vdim(Y(k.s), highEdge, `C5 ${Math.round(d(k.no).rightMm)}`));
return `<svg viewBox="-1900 -1300 3800 2600" width="100%" style="max-width: 560px; height: auto" role="img" aria-label="교량받침 상세 평면: 쌍 ${pair.frontNo}·${pair.backNo}, 블록 한 줄, 교축·교각축 방향, C1·C2·C3, C4·C5, 도면값 겹쳐 보기">${parts.join('')}</svg>`;
}