From 789f62adf00af89f85b254d44805d3c94da7d144 Mon Sep 17 00:00:00 2001 From: ypshim Date: Tue, 3 Nov 2026 15:31:54 +0900 Subject: [PATCH] =?UTF-8?q?refactor(plan):=20=ED=98=95=EC=83=81=EA=B3=84?= =?UTF-8?q?=ED=9A=8D=20=E2=91=A1=EC=9D=98=20=EA=B5=90=EB=8C=80=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EC=A0=95=EB=B3=B4=20=EC=9E=85=EB=A0=A5=EC=B9=B8=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0,=20=EC=82=BD=EB=8F=84=EC=97=90=20EL=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ②의 교대 편집 영역에서 기본 정보 6칸과 기본값 복원 버튼을 뺌. 기본 정보 편집과 복원은 ①에만 남김 - ②의 삽도에 교좌면·기초 하단·지반고 값을 읽기 전용 라벨로 표시 (abut-section-svg.ts의 AbutLevels) - 날개벽 형식은 ②의 날개벽 카드에 그대로 둠 - 테스트 631건 통과, 타입 검사·빌드 통과. 브라우저 시각 확인은 미실시 Co-Authored-By: Claude Haiku 5.5 --- src/main.ts | 14 ++++++++++---- src/view/abut-section-svg.ts | 12 ++++++++++-- tests/abut-section-svg.test.ts | 5 +++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/main.ts b/src/main.ts index 0f29089..7bfa2e8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -693,13 +693,19 @@ function fillAbutSlot(slot: (typeof ABUT_SLOTS)[keyof typeof ABUT_SLOTS], rec: A const msg = $(slot.msg); basic.replaceChildren(); // ② puts the wing forms in their own card; ① keeps them in the basic items. - const wingBox = 'wing' in slot ? $(slot.wing) : basic; + // ② (plan card) shows no basic items: they are edited in ① only, and ② reads them off the 삽도 labels (2026-11-03). + const planCard = 'wing' in slot; + const wingBox = planCard ? $(slot.wing) : basic; wingBox.replaceChildren(); bearings.replaceChildren(); shape.replaceChildren(); // 삽도 (벽체·흉벽 개략 단면): the values it names are the card's values, so the drawing follows each change. const drawing = el('div', undefined, 'abut-section'); - drawing.innerHTML = abutSectionSvg(resolveShape(rec.shape), { ...ABUT_SHAPE_DEFAULT, ...(rec.shape ?? {}) }); + drawing.innerHTML = abutSectionSvg( + resolveShape(rec.shape), + { ...ABUT_SHAPE_DEFAULT, ...(rec.shape ?? {}) }, + { seatEl: rec.seatEl, footingBottomEl: rec.foundationBottomEl, groundEl: rec.groundEl }, + ); shape.append(drawing); msg.textContent = ''; const apply = (next: AbutRecord) => { @@ -721,7 +727,7 @@ function fillAbutSlot(slot: (typeof ABUT_SLOTS)[keyof typeof ABUT_SLOTS], rec: A }; const idOf = (suffix: string) => `${slot.prefix}-${rec.name}-${suffix}`; - for (const f of ABUT_EDIT_FIELDS) { + for (const f of planCard ? [] : ABUT_EDIT_FIELDS) { const id = idOf(f.key); const lab = el('label', `${f.label}${f.unit ? ` (${f.unit})` : ''}`); lab.htmlFor = id; @@ -752,7 +758,7 @@ function fillAbutSlot(slot: (typeof ABUT_SLOTS)[keyof typeof ABUT_SLOTS], rec: A const def = state.abutDefaults.find((a) => a.name === rec.name); if (def) apply(restoreAbutDefault(def)); }); - basic.appendChild(restore); + if (!planCard) basic.appendChild(restore); for (const b of rec.bearings) { const zId = idOf(`bz-${b.no}`); diff --git a/src/view/abut-section-svg.ts b/src/view/abut-section-svg.ts index f893de5..2b41faf 100644 --- a/src/view/abut-section-svg.ts +++ b/src/view/abut-section-svg.ts @@ -1,17 +1,24 @@ import { type AbutShape, type ResolvedShape } from '../core/abut/shape'; +/** EL values (m) the drawing names as read-only labels; they come from the record, not from this card. */ +export interface AbutLevels { + seatEl: number; + footingBottomEl: number; + groundEl: number; +} + /** * 벽체·흉벽 개략 단면 (시안 ② 삽도). Not to scale: the heights are fixed for legibility, * and the labels show the card's values in mm, so the numbers the user types are what the drawing names. * t runs along the 교대 직각 방향 (배면 −, 전면 +); the 교대축 is t = 0. */ -export function abutSectionSvg(r: ResolvedShape, v: AbutShape): string { +export function abutSectionSvg(r: ResolvedShape, v: AbutShape, levels?: AbutLevels): string { const S = 100; // px per m const YMAX = 4.6; // highest drawn height (m above the footing bottom) const tMin = r.footingBackM - 0.4; const tMax = r.footingFrontM + 0.6; const W = (tMax - tMin) * S; - const H = (YMAX + 0.5) * S; + const H = (YMAX + 0.8) * S; const X = (t: number) => (t - tMin) * S; const Y = (h: number) => (YMAX - h) * S; const fT = r.footingThicknessM; @@ -36,6 +43,7 @@ export function abutSectionSvg(r: ResolvedShape, v: AbutShape): string { text(4, Y(fT / 2) + 4, `저판 두께 ${v.footingThicknessMm}`), text(X(r.footingBackM), Y(0) + 18, `저판 뒷굽 ${v.footingBackMm}`), text(X(r.footingFrontM), Y(0) + 18, `저판 앞굽 ${v.footingFrontMm}`, 'end'), + levels ? text(4, H - 44, `교좌면 ${levels.seatEl.toFixed(3)} · 기초 하단 ${levels.footingBottomEl.toFixed(3)} · 지반고 ${levels.groundEl.toFixed(3)} m`, 'start') : '', text(4, H - 22, '개략 도시 · 축척 아님', 'start'), text(4, H - 6, `흉벽 상단 Level 좌 ${v.parapetLevelLeftMm} · 우 ${v.parapetLevelRightMm} (계산 미사용, QA-4)`, 'start'), '', diff --git a/tests/abut-section-svg.test.ts b/tests/abut-section-svg.test.ts index 367de05..f18bcbc 100644 --- a/tests/abut-section-svg.test.ts +++ b/tests/abut-section-svg.test.ts @@ -20,6 +20,11 @@ describe('교대 삽도 (벽체·흉벽 개략 단면, 시안 ②)', () => { expect(svg).toContain('축척 아님'); }); + it('names the 교좌면, 기초 하단 and 지반고 EL as read-only labels when given', () => { + const svg = abutSectionSvg(resolveShape(), ABUT_SHAPE_DEFAULT, { seatEl: 44.367, footingBottomEl: 40.895, groundEl: 33.984 }); + expect(svg).toContain('교좌면 44.367 · 기초 하단 40.895 · 지반고 33.984 m'); + }); + it('draws the footing, wall and parapet as separate shapes', () => { const svg = abutSectionSvg(resolveShape(), ABUT_SHAPE_DEFAULT); expect(svg.match(/