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>
This commit is contained in:
1 parent
f25e36c050
commit
36b26209df
23 files changed
+257
-1081
No files matched your search
@@ -1,19 +1,12 @@
|
||||
import type { PierModel } from '../core/geometry';
|
||||
import { bearingEdgeDistances, pairEccentricities } from '../core/bearing-layout';
|
||||
|
||||
/** Drawing values (A, B of the solplate, mm) of the front and back bearings, to overlay on the model (승인 2026-10-26). */
|
||||
export interface BearingPlanOverlay {
|
||||
front: { A: number; B: number };
|
||||
back: { A: number; B: number };
|
||||
}
|
||||
|
||||
/** 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. The model's solplates are solid, the drawing values
|
||||
* dashed. 일체 draws one 2500 × 1200 outline; 분리 draws two 1200 × 1200 outlines. Dimensions: C1 (bearing to block edge),
|
||||
* 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 교축.
|
||||
*/
|
||||
/**
|
||||
@@ -24,7 +17,7 @@ function rect(cx: number, cy: number, w: number, h: number, skewDeg: number, att
|
||||
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, overlay?: BearingPlanOverlay): string {
|
||||
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 === '전열')!;
|
||||
@@ -55,18 +48,9 @@ export function bearingPlanSvg(m: PierModel, pairIndex: number, overlay?: Bearin
|
||||
}
|
||||
}
|
||||
|
||||
// Solplates: the model's plan (A across the 교축, B along the 교각축), centred on each bearing.
|
||||
// Solplates: the pier's plan (L across the 교축, W along the 교각축), centred on each bearing.
|
||||
for (const b of [f, k]) {
|
||||
if (b.plan) {
|
||||
const { A, B } = b.plan;
|
||||
parts.push(rect(X(b.n), Y(b.s), A, B, b.rowSkewDeg, `fill="#EEF3F7" stroke="#2F6DB5" stroke-width="10"`));
|
||||
}
|
||||
}
|
||||
// Drawing values overlaid (dashed) on the same bearings (#1 겹쳐 보이게).
|
||||
if (overlay) {
|
||||
for (const [b, v] of [[f, overlay.front], [k, overlay.back]] as const) {
|
||||
parts.push(rect(X(b.n), Y(b.s), v.A, v.B, b.rowSkewDeg, `fill="none" stroke="#B4531F" stroke-width="10" stroke-dasharray="40 25"`));
|
||||
}
|
||||
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.
|
||||
|
||||
@@ -1,27 +1,12 @@
|
||||
import type { PierModel } from '../core/geometry';
|
||||
import { pairEccentricities } from '../core/bearing-layout';
|
||||
|
||||
/**
|
||||
* Drawing values of one bearing (도면값), overlaid as dashed outlines (승인 2026-10-26).
|
||||
* All lengths in mm; elx in m.
|
||||
*/
|
||||
export interface BearingDrawingRef {
|
||||
elx?: number;
|
||||
h1: number;
|
||||
h2: number;
|
||||
h3: number;
|
||||
h4: number;
|
||||
E: number;
|
||||
A: number;
|
||||
B: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 교량받침 상세 · 단면 (elevation) for one pair (승인 2026-10-26): the model's blocks, mortar, bearing body,
|
||||
* and solplate per bearing, with the drawing heights overlaid as dashed outlines. x is the 교축 (n), y the EL (up).
|
||||
* Labels: EL.X (mortar top), h1 (block), h2 (mortar), h3 (bearing), h4 (solplate), E = h1 + h2 + h3 + h4.
|
||||
* and solplate per bearing. x is the 교축 (n), y the EL (up). Labels: EL.X (mortar top), h1 (block), h3 (bearing body),
|
||||
* T (solplate, taper T1 at the (−) side, T2 at the (+) side).
|
||||
*/
|
||||
export function bearingSectionSvg(m: PierModel, pairIndex: number, refs?: { front?: BearingDrawingRef; back?: BearingDrawingRef }): string {
|
||||
export function bearingSectionSvg(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 === '전열')!;
|
||||
@@ -46,37 +31,19 @@ export function bearingSectionSvg(m: PierModel, pairIndex: number, refs?: { fron
|
||||
}
|
||||
|
||||
// Mortar, bearing body and solplate per bearing, from the model.
|
||||
for (const [b, ref] of [[f, refs?.front], [k, refs?.back]] as const) {
|
||||
for (const b of [f, k]) {
|
||||
const pad = 1000;
|
||||
parts.push(`<rect x="${X(b.n) - pad / 2}" y="${Y(b.padTop)}" width="${pad}" height="${50}" fill="#B7C7D3" stroke="#6E8496" stroke-width="6"></rect>`);
|
||||
if (b.plan) {
|
||||
const { A, margin, h3, T } = b.plan;
|
||||
const L = A + margin;
|
||||
const bodyTop = b.padTop + h3 / 1000;
|
||||
parts.push(`<rect x="${X(b.n) - (L - 200) / 2}" y="${Y(bodyTop)}" width="${L - 200}" height="${h3}" fill="#8898AA" stroke="#5B6676" stroke-width="6"></rect>`);
|
||||
const drop = (L * Math.abs(b.plan.S)) / 200;
|
||||
const t1 = T + drop;
|
||||
const t2 = T - drop;
|
||||
const x0 = X(b.n) - L / 2;
|
||||
const x1 = X(b.n) + L / 2;
|
||||
parts.push(`<polygon points="${x0},${Y(bodyTop)} ${x1},${Y(bodyTop)} ${x1},${Y(bodyTop + t2 / 1000)} ${x0},${Y(bodyTop + t1 / 1000)}" fill="#F3D9B8" stroke="#B4531F" stroke-width="6"></polygon>`);
|
||||
}
|
||||
if (ref) {
|
||||
// Drawing heights from the block bottom, dashed (overlay).
|
||||
const base = b.bottom;
|
||||
const dBlockTop = base + (ref.h1 ?? 0) / 1000;
|
||||
const dPad = dBlockTop + ref.h2 / 1000;
|
||||
const dBody = dPad + ref.h3 / 1000;
|
||||
const dSol = dBody + ref.h4 / 1000;
|
||||
parts.push(`<rect x="${X(b.n) - 600}" y="${Y(dBlockTop)}" width="1200" height="${(dBlockTop - base) * 1000}" fill="none" stroke="#B4531F" stroke-width="8" stroke-dasharray="40 25"></rect>`);
|
||||
parts.push(`<line x1="${X(b.n) - 700}" y1="${Y(dSol)}" x2="${X(b.n) + 700}" y2="${Y(dSol)}" stroke="#B4531F" stroke-width="8" stroke-dasharray="40 25"></line>`);
|
||||
parts.push(`<line x1="${X(b.n) - 700}" y1="${Y(dBody)}" x2="${X(b.n) + 700}" y2="${Y(dBody)}" stroke="#B4531F" stroke-width="8" stroke-dasharray="40 25"></line>`);
|
||||
}
|
||||
const { L, h3, T1, T2 } = b.plan;
|
||||
const bodyTop = b.padTop + h3 / 1000;
|
||||
parts.push(`<rect x="${X(b.n) - (L - 200) / 2}" y="${Y(bodyTop)}" width="${L - 200}" height="${h3}" fill="#8898AA" stroke="#5B6676" stroke-width="6"></rect>`);
|
||||
const x0 = X(b.n) - L / 2;
|
||||
const x1 = X(b.n) + L / 2;
|
||||
parts.push(`<polygon points="${x0},${Y(bodyTop)} ${x1},${Y(bodyTop)} ${x1},${Y(bodyTop + T2 / 1000)} ${x0},${Y(bodyTop + T1 / 1000)}" fill="#F3D9B8" stroke="#B4531F" stroke-width="6"></polygon>`);
|
||||
// EL.X at the mortar top, bearing number.
|
||||
parts.push(`<circle cx="${X(b.n)}" cy="${Y(b.padTop)}" r="30" fill="#18212B"></circle>`);
|
||||
parts.push(`<text x="${X(b.n) + 60}" y="${Y(b.padTop) - 60}" font-size="90" fill="#18212B">${b.no} · EL.X ${b.padTop.toFixed(3)}</text>`);
|
||||
parts.push(`<text x="${X(b.n)}" y="${Y(b.bottom) + 160}" text-anchor="middle" font-size="90" fill="#55606E">h1 ${Math.round((b.top - b.bottom) * 1000)}</text>`);
|
||||
}
|
||||
return `<svg viewBox="-2000 -1200 4000 2000" width="100%" style="max-width: 560px; height: auto" role="img" aria-label="교량받침 상세 단면: 블록, 모르타르, 받침, 솔플레이트, EL.X, 도면 높이 점선">${parts.join('')}</svg>`;
|
||||
return `<svg viewBox="-2000 -1200 4000 2000" width="100%" style="max-width: 560px; height: auto" role="img" aria-label="교량받침 상세 단면: 블록, 모르타르, 받침, 솔플레이트, EL.X">${parts.join('')}</svg>`;
|
||||
}
|
||||
|
||||
+5
-5
@@ -152,14 +152,14 @@ export function buildPierScene(m: PierModel, terrain?: TerrainGrid, groundEl: nu
|
||||
pad.rotateY(turn);
|
||||
pad.translate(b.s, b.padTop - 0.025, b.n);
|
||||
add('pads', pad, 1, b.k);
|
||||
if (b.plan) {
|
||||
// 교량받침 본체 (승인 2026-10-23): the solplate plan less 100 mm on each side, from the mortar top up to the solplate.
|
||||
const body = new THREE.BoxGeometry((b.plan.B + b.plan.margin) / 1000 - 0.2, b.plan.h3 / 1000, (b.plan.A + b.plan.margin) / 1000 - 0.2);
|
||||
{
|
||||
// 교량받침 본체: the solplate plan less 100 mm on each side, from the mortar top up to the solplate.
|
||||
const body = new THREE.BoxGeometry(b.plan.W / 1000 - 0.2, b.plan.h3 / 1000, b.plan.L / 1000 - 0.2);
|
||||
body.rotateY(turn);
|
||||
body.translate(b.s, b.padTop + b.plan.h3 / 2000, b.n);
|
||||
add('bearings', body, 1, b.k);
|
||||
// Solplate: a wedge that tapers across the 교각축 직각 (T1 at the (−) side), on the bearing (승인 2026-10-23).
|
||||
const sp = solplateGeometry(b, b.plan.h3, { A: b.plan.A, B: b.plan.B, margin: b.plan.margin, T: b.plan.T, slopePct: b.plan.S });
|
||||
// Solplate: a wedge that tapers across the 교각축 직각 (T1 at the (−) side), on the bearing.
|
||||
const sp = solplateGeometry(b, b.plan);
|
||||
add('solplates', wedgeGeometry(sp.corners), 1, b.k);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,24 @@
|
||||
import type { BlockModel } from '../core/geometry';
|
||||
|
||||
/** Drawing values of one solplate (도면값), overlaid as dashed outlines. mm. */
|
||||
export interface SolplateDrawingRef {
|
||||
L: number;
|
||||
W: number;
|
||||
T1: number;
|
||||
T2: number;
|
||||
S: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 솔플레이트 상세 (승인 2026-10-26) for one bearing: the plan (L across the 교축, W along the 교각축) and the
|
||||
* taper in side view (T1 at the (−) side, T2 at the (+) side, S % · T). Model solid, drawing dashed.
|
||||
* 솔플레이트 상세 (승인 2026-10-26, C안 2026-11-03) for one bearing: the plan (L across, W along the 교각축) and the
|
||||
* taper in side view (T1 at the (−) side, T2 at the (+) side, S % · T). Thickness is exaggerated for legibility.
|
||||
*/
|
||||
export function solplateDetailSvg(block: BlockModel, ref?: SolplateDrawingRef): string {
|
||||
export function solplateDetailSvg(block: BlockModel): string {
|
||||
const plan = block.plan;
|
||||
if (!plan) return '';
|
||||
const L = plan.A + plan.margin;
|
||||
const W = plan.B + plan.margin;
|
||||
const drop = (L * Math.abs(plan.S)) / 200;
|
||||
const t1 = plan.T + drop;
|
||||
const t2 = plan.T - drop;
|
||||
const { L, W, T1: t1, T2: t2 } = plan;
|
||||
const parts: string[] = [];
|
||||
// Plan (left): L across, W along.
|
||||
parts.push(`<rect x="${-L / 2}" y="${-W / 2}" width="${L}" height="${W}" fill="#F3D9B8" stroke="#B4531F" stroke-width="8"></rect>`);
|
||||
parts.push(`<text x="0" y="${-W / 2 - 40}" text-anchor="middle" font-size="60" fill="#18212B">L ${L.toFixed(0)}</text>`);
|
||||
parts.push(`<text x="${L / 2 + 40}" y="0" font-size="60" fill="#18212B">W ${W.toFixed(0)}</text>`);
|
||||
if (ref) {
|
||||
parts.push(`<rect x="${-ref.L / 2}" y="${-ref.W / 2}" width="${ref.L}" height="${ref.W}" fill="none" stroke="#B4531F" stroke-width="6" stroke-dasharray="40 25"></rect>`);
|
||||
}
|
||||
// Side view (right): thickness T1 at x = −L/2 and T2 at x = +L/2, shown with the taper.
|
||||
const ox = L / 2 + 500;
|
||||
const sc = 4; // exaggerated thickness for legibility, labelled
|
||||
parts.push(`<polygon points="${ox - L / 2},0 ${ox + L / 2},0 ${ox + L / 2},${-t2 * sc} ${ox - L / 2},${-t1 * sc}" fill="#F3D9B8" stroke="#B4531F" stroke-width="8"></polygon>`);
|
||||
parts.push(`<text x="${ox - L / 2}" y="${-t1 * sc - 40}" font-size="60" fill="#18212B">T1 ${t1.toFixed(0)}</text>`);
|
||||
parts.push(`<text x="${ox + L / 2}" y="${-t2 * sc - 40}" text-anchor="end" font-size="60" fill="#18212B">T2 ${t2.toFixed(0)}</text>`);
|
||||
parts.push(`<text x="${ox}" y="120" text-anchor="middle" font-size="60" fill="#55606E">S ${plan.S} % · T ${plan.T} (두께 과장 ×${sc})</text>`);
|
||||
if (ref) {
|
||||
const r1 = ref.T1;
|
||||
const r2 = ref.T2;
|
||||
parts.push(`<polygon points="${ox - ref.L / 2},0 ${ox + ref.L / 2},0 ${ox + ref.L / 2},${-r2 * sc} ${ox - ref.L / 2},${-r1 * sc}" fill="none" stroke="#B4531F" stroke-width="6" stroke-dasharray="40 25"></polygon>`);
|
||||
}
|
||||
parts.push(`<text x="${ox - L / 2}" y="${-t1 * sc - 40}" font-size="60" fill="#18212B">T1 ${t1.toFixed(1)}</text>`);
|
||||
parts.push(`<text x="${ox + L / 2}" y="${-t2 * sc - 40}" text-anchor="end" font-size="60" fill="#18212B">T2 ${t2.toFixed(1)}</text>`);
|
||||
parts.push(`<text x="${ox}" y="120" text-anchor="middle" font-size="60" fill="#55606E">S ${plan.slopePct} % · T ${plan.T} (두께 과장 ×${sc})</text>`);
|
||||
const minX = -L / 2 - 200;
|
||||
const maxX = ox + L / 2 + 200;
|
||||
return `<svg viewBox="${minX} -900 ${maxX - minX} 1300" width="100%" style="max-width: 560px; height: auto" role="img" aria-label="솔플레이트 상세: 평면 L × W, 두께 T1 · T2 (두께 과장)">${parts.join('')}</svg>`;
|
||||
|
||||
Reference in new issue
Block a user