feat(core,view): ③ 공통 설정 — fck and fy as inputs (spec §6.2)
- Material {fck, fyH, fyS}, defaults 27 / 400 / 500 as assumed values (I02).
The rebar calculations take the material: the C7/C8 splice, the P2 development
length, the P3 lap, and the B2 split use it instead of fixed numbers.
- ③ screen: 공통 설정 inputs with a note that the defaults are assumed (I02).
- Project file stores the material as an optional field; older files open with
the defaults.
- Tests: fck 27 → 35 shortens the laps by √(27/35); the C7 piece follows fck;
a non-default fy drops I02; the file round trip.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
64d415f891
commit
9ba7b55eab
11 files changed
+123
-16
No files matched your search
+32
-1
@@ -11,6 +11,7 @@ import { adjustRow, layoutText, resolveRowCounts, rowLayout, totalPiles } from '
|
||||
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 { DEFAULT_MATERIAL, isAssumedMaterial, type Material } from './core/rebar/common';
|
||||
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';
|
||||
@@ -71,6 +72,8 @@ const state = {
|
||||
project: { name: '', bridge: '', client: '', author: '', crs: CRS, memo: '' } as ProjectInfo,
|
||||
/** Which stage screen is shown: ① 형상 생성 or ② 형상계획 (plan.md C1). */
|
||||
stage: 'shape' as 'shape' | 'plan' | 'rebar',
|
||||
/** ③ 공통 설정 (fck · fy). */
|
||||
material: { ...DEFAULT_MATERIAL } as Material,
|
||||
/** Member tab on ③ (철근표). */
|
||||
rebarMember: 'coping' as RebarRow['member'],
|
||||
/** Member card selected on ② (시안 ② 부재 목록). */
|
||||
@@ -718,7 +721,7 @@ function renderRebar(): void {
|
||||
$('rebar-note').textContent = '교각을 생성하면 철근표가 나옵니다.';
|
||||
return;
|
||||
}
|
||||
const { rows, warnings } = rebarSummary(pier.model);
|
||||
const { rows, warnings } = rebarSummary(pier.model, state.material);
|
||||
$('rebar-note').textContent = `${pier.input.name} · 형상 기준 계산 · 값은 기본값 기준 문서 규칙`;
|
||||
const shown = rows.filter((r) => r.member === state.rebarMember);
|
||||
const table = el('table', undefined, 'grid-table rebar-table');
|
||||
@@ -744,6 +747,28 @@ function renderRebar(): void {
|
||||
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}`;
|
||||
renderMaterial();
|
||||
}
|
||||
|
||||
/** ③ 공통 설정 inputs. Changes apply on 'change' and regenerate the rebar screen. */
|
||||
function renderMaterial(): void {
|
||||
const m = state.material;
|
||||
const assumed = isAssumedMaterial(m);
|
||||
$<HTMLInputElement>('mat-fck').value = String(m.fck);
|
||||
$<HTMLInputElement>('mat-fyh').value = String(m.fyH);
|
||||
$<HTMLInputElement>('mat-fys').value = String(m.fyS);
|
||||
$('mat-note').textContent = assumed ? '가정값 (I02): 입력을 바꾸면 정착·이음 길이가 달라집니다.' : '입력값을 사용합니다.';
|
||||
}
|
||||
|
||||
function setMaterial(key: keyof Material, raw: string): void {
|
||||
const value = Number(raw);
|
||||
if (!Number.isFinite(value) || value <= 0) {
|
||||
setStatus('공통 설정 값은 0보다 큰 숫자여야 합니다.');
|
||||
renderMaterial();
|
||||
return;
|
||||
}
|
||||
state.material = { ...state.material, [key]: value };
|
||||
renderAll();
|
||||
}
|
||||
|
||||
function renderPlan(): void {
|
||||
@@ -810,6 +835,7 @@ function snapshot(): string {
|
||||
project: state.project,
|
||||
source: state.source,
|
||||
piers: state.piers.map((p) => ({ input: p.input, defaults: p.defaults, draft: p.draft })),
|
||||
material: state.material,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -824,6 +850,7 @@ function currentProjectText(): string {
|
||||
source: state.source,
|
||||
piers: state.piers.map((p) => ({ id: p.input.name, input: p.input, defaults: p.defaults, draft: p.draft })),
|
||||
history: state.history,
|
||||
common: state.material,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -907,6 +934,7 @@ function openProjectText(text: string, saved: boolean): void {
|
||||
state.project = { ...parsed.project };
|
||||
state.source = parsed.source;
|
||||
state.history = parsed.history;
|
||||
state.material = parsed.common ? { ...parsed.common } : { ...DEFAULT_MATERIAL };
|
||||
state.piers = parsed.piers.map((p) => ({
|
||||
...buildEntry(p.input, false),
|
||||
defaults: p.defaults ?? p.input,
|
||||
@@ -1137,6 +1165,9 @@ $<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'));
|
||||
$<HTMLInputElement>('mat-fck').addEventListener('change', (ev) => setMaterial('fck', (ev.target as HTMLInputElement).value));
|
||||
$<HTMLInputElement>('mat-fyh').addEventListener('change', (ev) => setMaterial('fyH', (ev.target as HTMLInputElement).value));
|
||||
$<HTMLInputElement>('mat-fys').addEventListener('change', (ev) => setMaterial('fyS', (ev.target as HTMLInputElement).value));
|
||||
$<HTMLButtonElement>('member-regen').addEventListener('click', () => {
|
||||
const pier = activePier();
|
||||
if (pier) regenerateOne(pier.input.name);
|
||||
|
||||
Reference in new issue
Block a user