- 받침 기준 카드, 도면 불러오기(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>
94 lines
6.3 KiB
TypeScript
94 lines
6.3 KiB
TypeScript
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>`;
|
||
}
|