diff --git a/src/main.ts b/src/main.ts
index 5e7f64c..217151b 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -24,7 +24,7 @@ import { bearingSectionSvg, type BearingDrawingRef } from './view/bearing-sectio
import { solplateDetailSvg, type SolplateDrawingRef } from './view/solplate-svg';
import { blockRebarSectionSvg } from './view/block-rebar-svg';
import { blockRebarPlanSvg } from './view/block-rebar-plan-svg';
-import { copingFrontSvg, copingSectionSvg } from './view/coping-rebar-svg';
+import { copingEndStation, copingFrontSvg, copingSectionSvg } from './view/coping-rebar-svg';
import { BLOCK_REBAR_RANGES } from './core/rebar/block-check';
import { DEFAULT_BLOCK_REBAR, type BlockRebarSettings } from './core/rebar/bearing-block';
import { groundSectionSvg } from './view/ground-svg';
@@ -1072,7 +1072,6 @@ function renderBlockRebarSettings(box: HTMLDivElement, pier: Pier): void {
/** 코핑 배근 (승인 2026-10-26, 추천): 정면, and the 단면·폐합 at a station the user chooses (default mid-length). */
let copingView: 'front' | 'section' = 'front';
-let copingStation: number | null = null;
function renderCopingDrawings(box: HTMLDivElement, pier: Pier): void {
if (!pier.model || !rebar3d) return;
@@ -1095,20 +1094,19 @@ function renderCopingDrawings(box: HTMLDivElement, pier: Pier): void {
fig.innerHTML = copingFrontSvg(rebar3d.records, L * 1000, pier.model.columns[0]?.s ?? 0);
fig.appendChild(el('figcaption', '정면 · 교축 방향에서 본 코핑 배근 (3D 철근과 같은 데이터)', 'muted small'));
} else {
- const at = copingStation ?? L / 2;
- const station = el('input');
- station.type = 'number';
- station.step = '0.1';
- station.value = String(at);
- station.addEventListener('change', () => {
- copingStation = Number(station.value);
- renderAll();
- });
- const label = el('label', '단면 위치 s (m, 코핑 시작에서) ');
- label.appendChild(station);
- wrap.appendChild(label);
- fig.innerHTML = copingSectionSvg(rebar3d.records, at, pier.model.copingRefTop, pier.model.copingB * 1000);
- fig.appendChild(el('figcaption', `단면 · 폐합 구성 · s = ${at} m에서 자른 철근 (C9–C11 폐합 포함)`, 'muted small'));
+ // 단면 · 폐합 at two fixed stations side by side (승인 2026-10-26): the 단부 (first shear station) and the first column centre.
+ const sEnd = copingEndStation(rebar3d.records);
+ const sCol = pier.model.columns[0]?.s ?? L / 2;
+ const grid = el('div', undefined, 'footing-grid');
+ for (const [st, label] of [[sEnd, `단부 s = ${sEnd.toFixed(2)} m`], [sCol, `기둥 중심 s = ${sCol.toFixed(2)} m`]] as const) {
+ const f = el('figure', undefined, 'footing-fig');
+ f.innerHTML = copingSectionSvg(rebar3d.records, st, pier.model.copingRefTop, pier.model.copingB * 1000);
+ f.appendChild(el('figcaption', label, 'muted small'));
+ grid.appendChild(f);
+ }
+ wrap.appendChild(grid);
+ box.appendChild(wrap);
+ return;
}
wrap.appendChild(fig);
box.appendChild(wrap);
diff --git a/src/view/coping-rebar-svg.ts b/src/view/coping-rebar-svg.ts
index 5844258..618036a 100644
--- a/src/view/coping-rebar-svg.ts
+++ b/src/view/coping-rebar-svg.ts
@@ -53,6 +53,12 @@ export function chainDims(positions: number[], startMm: number, endMm: number):
return out;
}
+/** The 단부 station (m): the first station of the 전단·띠 bars (C9–C11), i.e. the first shear position. */
+export function copingEndStation(records: BarRecord[]): number {
+ const loops = records.filter((r) => /^C(9|10|11)$/.test(r.name));
+ return Math.min(...loops.flatMap((r) => r.pts.map((p) => p[0])));
+}
+
/**
* Bar positions (mm, along the coping): one per bar, its mean s. A loop or a cross bar sits at one station, so its
* points share the same s apart from the bends; the bends are not positions of their own.
@@ -221,7 +227,6 @@ export function copingSectionSvg(records: BarRecord[], s0: number, copingTopEl?:
for (const d of topChain) {
parts.push(`${d.text}`);
}
- parts.push(`상면 간격`);
// 4: the side rows (C12·C13) along the EL, from the coping top EL down to the lowest side bar.
const side = unique.filter((c) => c.group === 'side' && c.x < 0);
diff --git a/tests/coping-rebar-svg.test.ts b/tests/coping-rebar-svg.test.ts
index f6ec916..58fd99d 100644
--- a/tests/coping-rebar-svg.test.ts
+++ b/tests/coping-rebar-svg.test.ts
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest';
import { buildPier } from '../src/core/geometry';
import { buildRebar3D } from '../src/view/rebar3d';
-import { chainDims, copingBarGroup, copingFrontSvg, copingSectionSvg } from '../src/view/coping-rebar-svg';
+import { chainDims, copingBarGroup, copingEndStation, copingFrontSvg, copingSectionSvg } from '../src/view/coping-rebar-svg';
import { loadP1 } from './helpers';
const model = buildPier(loadP1());
@@ -43,7 +43,7 @@ describe('단면 · 폐합 글자 겹침 (승인 2026-10-26)', () => {
for (const label of ['C10', 'C14', 'C16']) {
expect(svg).not.toContain(`>${label}<`);
}
- expect(svg).toContain('상면 간격');
+ expect(svg).not.toContain('상면 간격');
});
});
@@ -96,3 +96,9 @@ describe('아래 치수선의 0 = 기둥 중심 (승인 2026-10-26)', () => {
expect((span![0] + span![1]) / 2).toBeCloseTo(col, 0);
});
});
+
+describe('단부 위치 (승인 2026-10-26)', () => {
+ it('the 단부 section is at the first shear station, 0.3 m from the coping start for P1', () => {
+ expect(copingEndStation(scene.records)).toBeCloseTo(0.3, 6);
+ });
+});