feat(core,view): footing rebar B1–B12 (plan C5) and the ③ rebar screen v1
- Footing (확대기초철근 (1).md): grid counts from W and L, layer heights, B1–B4 and B5–B8 counts and lengths (B2 13646, B6 11396), B10-1/B10-2, B11 (60) and B12 (47) with their lengths. B9 is not computed yet. - rebarSummary: one 철근표 per pier from the model (bearing, coping, column, footing), with alerts: RB-F08 (B2 needs a splice, Q-B6), RB-F09 (B9 not computed). - ③ screen v1: stage nav button, member tabs (받침블럭 · 코핑 · 기둥 · 확대기초), rebar table with 번호 · 규격 · 개수 · 길이 · 비고, alert list, and the open questions (Q-B5, Q-B6, Q-C9). Drawings and the 강조 연동 are not in v1. - Tests: footing values from the document; P1 summary totals; warnings. - Not verified in a browser. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a403ab9641
commit
3af8729e9f
7 files changed
+464
-3
No files matched your search
+70
-2
@@ -10,6 +10,7 @@ import { EDITABLE_FIELDS, FIELD_LABELS, overrideKeys, pendingKeys, revertDraft,
|
||||
import { adjustRow, layoutText, resolveRowCounts, rowLayout, totalPiles } from './core/piles';
|
||||
import { applyCopy, DEFAULT_COPY_ITEMS, planCopy, type CopyItem } from './core/copy';
|
||||
import { pileGeometry } from './core/geometry';
|
||||
import { rebarSummary, type RebarRow } from './core/rebar/summary';
|
||||
import { allVisibleChecked, setVisibleChecked, type TypeFilter } from './core/selection';
|
||||
import { choose, defaultSummary, isSelectable, SHAPE_GROUPS, UNDEFINED_REASON, type MemberId } from './core/shape-options';
|
||||
import { parseProject, SCHEMA_VERSION, serializeProject, type ProjectFile, type ProjectInfo } from './core/project';
|
||||
@@ -69,7 +70,9 @@ const state = {
|
||||
scene: null as PierScene | null,
|
||||
project: { name: '', bridge: '', client: '', author: '', crs: CRS, memo: '' } as ProjectInfo,
|
||||
/** Which stage screen is shown: ① 형상 생성 or ② 형상계획 (plan.md C1). */
|
||||
stage: 'shape' as 'shape' | 'plan',
|
||||
stage: 'shape' as 'shape' | 'plan' | 'rebar',
|
||||
/** Member tab on ③ (철근표). */
|
||||
rebarMember: 'coping' as RebarRow['member'],
|
||||
/** Member card selected on ② (시안 ② 부재 목록). */
|
||||
member: 'coping' as MemberId,
|
||||
/** True once the project has a file on disk (first save via 시안 ⑤, or an opened file). Later saves go straight to it. */
|
||||
@@ -393,6 +396,7 @@ function renderAll(): void {
|
||||
renderPanels();
|
||||
renderEditCard();
|
||||
renderPlan();
|
||||
renderRebar();
|
||||
scheduleBackup();
|
||||
}
|
||||
|
||||
@@ -666,15 +670,78 @@ function renderShapeOptions(member: MemberId): void {
|
||||
}
|
||||
|
||||
// --- ② 형상계획 (plan.md C1) -------------------------------------------------------------
|
||||
function setStage(stage: 'shape' | 'plan'): void {
|
||||
function setStage(stage: 'shape' | 'plan' | 'rebar'): void {
|
||||
state.stage = stage;
|
||||
$<HTMLElement>('view-shape').hidden = stage !== 'shape';
|
||||
$<HTMLElement>('view-plan').hidden = stage !== 'plan';
|
||||
$<HTMLElement>('view-rebar').hidden = stage !== 'rebar';
|
||||
$<HTMLButtonElement>('nav-shape').setAttribute('aria-current', String(stage === 'shape'));
|
||||
$<HTMLButtonElement>('nav-plan').setAttribute('aria-current', String(stage === 'plan'));
|
||||
$<HTMLButtonElement>('nav-rebar').setAttribute('aria-current', String(stage === 'rebar'));
|
||||
renderAll();
|
||||
}
|
||||
|
||||
// --- ③ 철근배근 (plan C5 · ③ screen v1) --------------------------------------------------
|
||||
const REBAR_MEMBERS: { id: RebarRow['member']; label: string }[] = [
|
||||
{ id: 'bearing', label: '받침블럭' },
|
||||
{ id: 'coping', label: '코핑' },
|
||||
{ id: 'column', label: '기둥' },
|
||||
{ id: 'footing', label: '확대기초' },
|
||||
];
|
||||
|
||||
function renderRebar(): void {
|
||||
const pier = activePier();
|
||||
const tabs = $<HTMLUListElement>('rebar-tabs');
|
||||
tabs.replaceChildren();
|
||||
for (const m of REBAR_MEMBERS) {
|
||||
const li = el('li');
|
||||
const btn = el('button', m.label, 'member');
|
||||
btn.type = 'button';
|
||||
btn.setAttribute('aria-pressed', String(m.id === state.rebarMember));
|
||||
btn.addEventListener('click', () => {
|
||||
state.rebarMember = m.id;
|
||||
renderAll();
|
||||
});
|
||||
li.appendChild(btn);
|
||||
tabs.appendChild(li);
|
||||
}
|
||||
|
||||
const box = $<HTMLDivElement>('rebar-table');
|
||||
box.replaceChildren();
|
||||
const issues = $<HTMLUListElement>('rebar-issues');
|
||||
issues.replaceChildren();
|
||||
if (!pier?.model) {
|
||||
$('rebar-note').textContent = '교각을 생성하면 철근표가 나옵니다.';
|
||||
return;
|
||||
}
|
||||
const { rows, warnings } = rebarSummary(pier.model);
|
||||
$('rebar-note').textContent = `${pier.input.name} · 형상 기준 계산 · 값은 기본값 기준 문서 규칙`;
|
||||
const shown = rows.filter((r) => r.member === state.rebarMember);
|
||||
const table = el('table', undefined, 'grid-table');
|
||||
const head = el('thead');
|
||||
const hr = el('tr');
|
||||
for (const h of ['번호', '규격', '개수', '길이 (mm)', '비고']) hr.appendChild(el('th', h));
|
||||
head.appendChild(hr);
|
||||
table.appendChild(head);
|
||||
const body = el('tbody');
|
||||
for (const r of shown) {
|
||||
const tr = el('tr');
|
||||
tr.append(
|
||||
el('td', r.name),
|
||||
el('td', r.size),
|
||||
el('td', String(r.count)),
|
||||
el('td', r.length === null ? '—' : String(Math.round(r.length * 10) / 10)),
|
||||
el('td', r.note),
|
||||
);
|
||||
body.appendChild(tr);
|
||||
}
|
||||
table.appendChild(body);
|
||||
box.appendChild(table);
|
||||
for (const w of warnings) issues.appendChild(el('li', `${w.code} ${w.message}`));
|
||||
if (issues.childElementCount === 0) issues.appendChild(el('li', '알림이 없습니다.'));
|
||||
$('step3-status').textContent = `계산됨 · 경고 ${warnings.length}`;
|
||||
}
|
||||
|
||||
function renderPlan(): void {
|
||||
const pier = activePier();
|
||||
const members = $<HTMLUListElement>('members');
|
||||
@@ -1065,6 +1132,7 @@ $<HTMLButtonElement>('bulk-regen').addEventListener('click', regenerateChecked);
|
||||
$<HTMLButtonElement>('bulk-copy').addEventListener('click', openCopyDialog);
|
||||
$<HTMLButtonElement>('nav-shape').addEventListener('click', () => setStage('shape'));
|
||||
$<HTMLButtonElement>('nav-plan').addEventListener('click', () => setStage('plan'));
|
||||
$<HTMLButtonElement>('nav-rebar').addEventListener('click', () => setStage('rebar'));
|
||||
$<HTMLButtonElement>('member-regen').addEventListener('click', () => {
|
||||
const pier = activePier();
|
||||
if (pier) regenerateOne(pier.input.name);
|
||||
|
||||
Reference in new issue
Block a user