diff --git a/src/main.ts b/src/main.ts
index 62e2d53..bc9abdd 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -20,6 +20,8 @@ import { groundElFor, parseGroundModel, strataRowsFor, type GroundModelFile } fr
import { CORROSION_ALLOWANCE_MM, parseNValue, SEA_DATUM, STRATA_NAMES } from './core/ground';
import { bearingEdgeDistances, integratedCheck, PAIR_ECCENTRICITY_LIMIT_MM, pairEccentricities } from './core/bearing-layout';
import { bearingPlanSvg, type BearingPlanOverlay, PAIR_CHIP_LABELS } from './view/bearing-plan-svg';
+import { bearingSectionSvg, type BearingDrawingRef } from './view/bearing-section-svg';
+import { solplateDetailSvg, type SolplateDrawingRef } from './view/solplate-svg';
import { groundSectionSvg } from './view/ground-svg';
import { parseBearingElevationTable, parseSolplateTable, readDxfTexts } from './core/dxf-table';
import { columnDiameterIssues } from './core/column-check';
@@ -1520,18 +1522,30 @@ function renderBearingBasis(wrap: HTMLDivElement, pier: Pier): void {
let bearingPairIndex = 0;
let bearingOverlayOn = true;
-/** Drawing A, B of the front and back bearings of a pair: the imported drawing rows if any, else the reference table. */
+/**
+ * Drawing values of one bearing for the overlays (승인 2026-10-26): the imported drawing row where it has the value
+ * (elx, h1, h2, A, B), otherwise the reference table (h3, h4, E).
+ */
+function drawingRefFor(pier: Pier, no: number): BearingDrawingRef | undefined {
+ const r = BEARING_ELEVATION_REF.find((x) => x.no === no);
+ if (!r) return undefined;
+ const d = (bearingDrawing[pier.input.name] ?? []).find((x) => x.no === no);
+ const A = d?.A ?? r.A;
+ const B = d?.B ?? r.B;
+ return { elx: d?.elx ?? r.elx, h1: d?.h1 ?? r.h1, h2: d?.h2 ?? r.h2, h3: r.h3, h4: r.h4, E: r.E, A, B };
+}
+
+/** Drawing A, B of the front and back bearings of a pair, for the plan overlay. */
function overlayFor(pier: Pier, frontNo: number, backNo: number): BearingPlanOverlay | undefined {
- const rows = bearingDrawing[pier.input.name] ?? [];
- const pick = (no: number) => {
- const d = rows.find((r) => r.no === no);
- if (d?.A !== undefined && d?.B !== undefined) return { A: d.A, B: d.B };
- const r = BEARING_ELEVATION_REF.find((x) => x.no === no);
- return r ? { A: r.A, B: r.B } : undefined;
- };
- const f = pick(frontNo);
- const b = pick(backNo);
- return f && b ? { front: f, back: b } : undefined;
+ const f = drawingRefFor(pier, frontNo);
+ const b = drawingRefFor(pier, backNo);
+ return f && b ? { front: { A: f.A, B: f.B }, back: { A: b.A, B: b.B } } : undefined;
+}
+
+/** Drawing solplate values of one bearing (reference table), for the solplate detail overlay. */
+function solplateRefFor(no: number): SolplateDrawingRef | undefined {
+ const r = SOLPLATE_REF.find((x) => x.no === no);
+ return r ? { L: r.L, W: r.W, T1: r.T1, T2: r.T2, S: r.s } : undefined;
}
function renderBearingLayout(wrap: HTMLDivElement, pier: Pier): void {
@@ -1568,8 +1582,26 @@ function renderBearingLayout(wrap: HTMLDivElement, pier: Pier): void {
const fig = el('figure', undefined, 'ground-fig');
const overlay = bearingOverlayOn ? overlayFor(pier, current.frontNo, current.backNo) : undefined;
fig.innerHTML = bearingPlanSvg(pier.model, bearingPairIndex, overlay);
- fig.appendChild(el('figcaption', '평면 · 실선 = 모델(솔플레이트, 블록 윤곽) · 점선 = 도면값 · C4·C5는 교각축 직각 방향 코핑 가장자리에서 받침까지', 'muted small'));
+ fig.appendChild(el('figcaption', '평면 · 실선 = 모델(솔플레이트, 블록 윤곽) · 점선 = 도면값 · C4·C5는 교축 방향, 받침 중심에서 블록 바깥 연단까지', 'muted small'));
wrap.appendChild(fig);
+
+ // 단면 (elevation): the blocks, mortar, bearing body and solplates; the drawing heights dashed.
+ const section = el('figure', undefined, 'ground-fig');
+ const refs = bearingOverlayOn ? { front: drawingRefFor(pier, current.frontNo), back: drawingRefFor(pier, current.backNo) } : undefined;
+ section.innerHTML = bearingSectionSvg(pier.model, bearingPairIndex, refs);
+ section.appendChild(el('figcaption', '단면 · EL.X는 모르타르 상단, E = h1 + h2 + h3 + h4 · 점선 = 도면 높이', 'muted small'));
+ wrap.appendChild(section);
+
+ // 솔플레이트 상세 for the two bearings of the pair (plan and taper, drawing dashed).
+ const solGrid = el('div', undefined, 'footing-grid');
+ for (const no of [current.frontNo, current.backNo]) {
+ const block = pier.model.blocks.find((b) => b.no === no)!;
+ const fig2 = el('figure', undefined, 'footing-fig');
+ fig2.innerHTML = solplateDetailSvg(block, bearingOverlayOn ? solplateRefFor(no) : undefined);
+ fig2.appendChild(el('figcaption', `솔플레이트 상세 · ${no}번 · 두께 과장 · 점선 = 도면값`, 'muted small'));
+ solGrid.appendChild(fig2);
+ }
+ wrap.appendChild(solGrid);
}
wrap.appendChild(el('p', `배치 규칙 (모델값 · 편심 한계 ${PAIR_ECCENTRICITY_LIMIT_MM} mm)`, 'lbl'));
const edges = bearingEdgeDistances(pier.model);
diff --git a/src/view/bearing-section-svg.ts b/src/view/bearing-section-svg.ts
new file mode 100644
index 0000000..d416608
--- /dev/null
+++ b/src/view/bearing-section-svg.ts
@@ -0,0 +1,82 @@
+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.
+ */
+export function bearingSectionSvg(m: PierModel, pairIndex: number, refs?: { front?: BearingDrawingRef; back?: BearingDrawingRef }): 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 elc = (f.bottom + k.bottom) / 2;
+ const X = (n: number) => (n - nc) * 1000;
+ const Y = (el: number) => -(el - elc) * 1000;
+ const parts: string[] = [];
+
+ // Blocks: one stepped outline for 일체, else one per bearing.
+ const ib = m.integratedBlocks.find((x) => x.frontNo === pair.frontNo);
+ if (ib) {
+ const x0 = X(ib.n - 1.25);
+ const x1 = X(ib.n + 1.25);
+ const xm = X(ib.n);
+ parts.push(``);
+ } else {
+ for (const b of [f, k]) {
+ parts.push(``);
+ }
+ }
+
+ // Mortar, bearing body and solplate per bearing, from the model.
+ for (const [b, ref] of [[f, refs?.front], [k, refs?.back]] as const) {
+ const pad = 1000;
+ parts.push(``);
+ if (b.plan) {
+ const { A, margin, h3, T } = b.plan;
+ const L = A + margin;
+ const bodyTop = b.padTop + h3 / 1000;
+ parts.push(``);
+ 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(``);
+ }
+ 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(``);
+ parts.push(``);
+ parts.push(``);
+ }
+ // EL.X at the mortar top, bearing number.
+ parts.push(``);
+ parts.push(`${b.no} · EL.X ${b.padTop.toFixed(3)}`);
+ parts.push(`h1 ${Math.round((b.top - b.bottom) * 1000)}`);
+ }
+ return ``;
+}
+
diff --git a/src/view/solplate-svg.ts b/src/view/solplate-svg.ts
new file mode 100644
index 0000000..bdd8823
--- /dev/null
+++ b/src/view/solplate-svg.ts
@@ -0,0 +1,47 @@
+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.
+ */
+export function solplateDetailSvg(block: BlockModel, ref?: SolplateDrawingRef): 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 parts: string[] = [];
+ // Plan (left): L across, W along.
+ parts.push(``);
+ parts.push(`L ${L.toFixed(0)}`);
+ parts.push(`W ${W.toFixed(0)}`);
+ if (ref) {
+ parts.push(``);
+ }
+ // 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(``);
+ parts.push(`T1 ${t1.toFixed(0)}`);
+ parts.push(`T2 ${t2.toFixed(0)}`);
+ parts.push(`S ${plan.S} % · T ${plan.T} (두께 과장 ×${sc})`);
+ if (ref) {
+ const r1 = ref.T1;
+ const r2 = ref.T2;
+ parts.push(``);
+ }
+ const minX = -L / 2 - 200;
+ const maxX = ox + L / 2 + 200;
+ return ``;
+}
diff --git a/tests/bearing-section-svg.test.ts b/tests/bearing-section-svg.test.ts
new file mode 100644
index 0000000..06e2dc0
--- /dev/null
+++ b/tests/bearing-section-svg.test.ts
@@ -0,0 +1,44 @@
+import { describe, expect, it } from 'vitest';
+import { buildPier } from '../src/core/geometry';
+import { bearingSectionSvg } from '../src/view/bearing-section-svg';
+import { solplateDetailSvg } from '../src/view/solplate-svg';
+import { loadP1 } from './helpers';
+
+const model = buildPier(loadP1());
+const integrated = buildPier({ ...loadP1(), bearingLayout: '일체' });
+const ref = { h1: 150, h2: 50, h3: 181, h4: 30, E: 425, A: 700, B: 640 };
+
+describe('교량받침 상세 단면 (승인 2026-10-26)', () => {
+ it('labels EL.X at the mortar top and the block height h1 for each bearing', () => {
+ const svg = bearingSectionSvg(model, 0);
+ expect(svg).toContain('EL.X');
+ expect(svg).toContain('h1 150');
+ });
+
+ it('draws the drawing heights dashed only when given', () => {
+ expect(bearingSectionSvg(model, 0)).not.toContain('stroke-dasharray="40 25"');
+ const svg = bearingSectionSvg(model, 0, { front: ref, back: ref });
+ expect((svg.match(/stroke-dasharray="40 25"/g) ?? []).length).toBeGreaterThan(0);
+ });
+
+ it('일체 draws one stepped outline (a polygon) for the pair', () => {
+ expect(bearingSectionSvg(integrated, 0)).toContain(' {
+ expect(bearingSectionSvg(model, 99)).toBe('');
+ });
+});
+
+describe('솔플레이트 상세 (승인 2026-10-26)', () => {
+ it('shows L, W, T1 and T2 of the model plan, with the drawing dashed when given', () => {
+ const withPlan = buildPier({ ...loadP1(), bearingPlans: [{ no: 1, A: 700, B: 640, S: -1.9 }] });
+ const block = withPlan.blocks.find((b) => b.no === 1)!;
+ const svg = solplateDetailSvg(block);
+ expect(svg).toContain('L ');
+ expect(svg).toContain('T1 ');
+ expect(svg).toContain('T2 ');
+ const withRef = solplateDetailSvg(block, { L: 800, W: 748, T1: 38, T2: 22, S: 1.9 });
+ expect(withRef).toContain('stroke-dasharray="40 25"');
+ });
+});