feat(rebar): block rebar settings card and C–C section on the ③ bearing tab

- Six inputs with their ranges and P1 defaults; a change regenerates the pier.
- The 적용 블럭 line shows the blocks (분리 n개 or 일체 n쌍, 전체).
- view/block-rebar-svg.ts: the section C–C along the 교축 with the S2 dots, the
  S1 lines and legs, and the dimensions (block widths, the S2 layout).
- 받침블럭 철근 설정 is tracked for changes (defaults vs the current draft).

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-10-26 14:12:36 +09:00
1 parent a6f362df56
commit 38bb26eda5
4 files changed
+130 -1

No files matched your search

+55
View File
@@ -0,0 +1,55 @@
import type { PierModel } from '../core/geometry';
import { barDepths, barsAcross, type BlockRebarSettings } from '../core/rebar/bearing-block';
import { INTEGRATED_BLOCK } from '../core/integrated-block';
/**
* 받침블럭 배근 · 단면 C–C (승인 2026-10-26, 시안 ③): a section along the 교축, through the blocks. x = 교축 (n), y = EL
* (down from the highest block top). S2 bars are dots along the top, S1 bars are lines with legs down into the coping.
* Dimensions: the block widths and gap, the bar layout (end · n@spacing = span), and the top cover.
*/
export function blockRebarSectionSvg(m: PierModel): string {
const s: BlockRebarSettings = m.blockRebar;
const depth = barDepths(s);
const ib = m.integratedBlocks;
const parts: string[] = [];
const topRef = Math.max(...m.blocks.map((b) => b.top));
const Y = (el: number) => (topRef - el) * 1000;
// Spans to draw: one 2500 span per 일체 pair, else one 1200 span per bearing.
const spans = ib.length > 0
? ib.map((x) => ({ nc: x.n, width: INTEGRATED_BLOCK.length, bottom: x.bottom, topL: x.topFront, topR: x.topBack, depthTopL: x.topFront, depthTopR: x.topBack, stepped: true }))
: m.blocks.map((b) => ({ nc: b.n, width: 1200, bottom: b.bottom, topL: b.top, topR: b.top, depthTopL: b.top, depthTopR: b.top, stepped: false }));
const nAll = spans.map((x) => x.nc);
const nc = (Math.min(...nAll) + Math.max(...nAll)) / 2;
const X = (n: number) => (n - nc) * 1000;
for (const sp of spans) {
const x0 = X(sp.nc - sp.width / 2000);
const x1 = X(sp.nc + sp.width / 2000);
if (sp.stepped) {
const xm = X(sp.nc);
parts.push(`<polygon points="${x0},${Y(sp.bottom)} ${x1},${Y(sp.bottom)} ${x1},${Y(sp.topR)} ${xm},${Y(sp.topR)} ${xm},${Y(sp.topL)} ${x0},${Y(sp.topL)}" fill="#D6DDE5" stroke="#6E8496" stroke-width="8"></polygon>`);
} else {
parts.push(`<rect x="${x0}" y="${Y(sp.topL)}" width="${x1 - x0}" height="${(sp.topL - sp.bottom) * 1000}" fill="#D6DDE5" stroke="#6E8496" stroke-width="8"></rect>`);
}
// S2: dots along the top at the S2 depth (each half at its own top when stepped).
const a2 = barsAcross(sp.width, s.spacingS2, s);
for (let i = 0; i < a2.count; i++) {
const xb = x0 + a2.firstEnd + i * a2.spacing;
const top = sp.stepped && xb > (x0 + x1) / 2 ? sp.depthTopR : sp.depthTopL;
parts.push(`<circle cx="${xb}" cy="${Y(top - depth.S2 / 1000)}" r="16" fill="#0F5C66"></circle>`);
}
// S1: a line across the block at the S1 depth, legs down into the coping at both ends.
const low = sp.stepped ? Math.min(sp.topL, sp.topR) : sp.topL;
const z1 = low - depth.S1 / 1000;
const leg = s.legLength;
parts.push(`<line x1="${x0 + s.coverSide / 1}" y1="${Y(z1)}" x2="${x1 - s.coverSide / 1}" y2="${Y(z1)}" stroke="#B4531F" stroke-width="10"></line>`);
parts.push(`<line x1="${x0 + s.coverSide / 1}" y1="${Y(z1)}" x2="${x0 + s.coverSide / 1}" y2="${Y(z1) + leg}" stroke="#B4531F" stroke-width="10"></line>`);
parts.push(`<line x1="${x1 - s.coverSide / 1}" y1="${Y(z1)}" x2="${x1 - s.coverSide / 1}" y2="${Y(z1) + leg}" stroke="#B4531F" stroke-width="10"></line>`);
// Dimensions: the S2 layout under the span.
const yDim = Y(sp.bottom) + 120;
parts.push(`<text x="${(x0 + x1) / 2}" y="${yDim + 90}" text-anchor="middle" font-size="70" fill="#18212B">${a2.firstEnd.toFixed(1)} · ${a2.count - 1}@${a2.spacing}=${(a2.count - 1) * a2.spacing} · ${a2.firstEnd.toFixed(1)}</text>`);
parts.push(`<text x="${(x0 + x1) / 2}" y="${Y(topRef) - 80}" text-anchor="middle" font-size="70" fill="#18212B">${(sp.width).toFixed(0)}</text>`);
}
return `<svg viewBox="-1900 -700 3800 1900" width="100%" style="max-width: 680px; height: auto" role="img" aria-label="받침블럭 배근 단면 C–C: S2 점, S1 선과 다리, 치수">${parts.join('')}</svg>`;
}