feat(rebar): rebar strength per 규격 prefix as inputs on ③ (D SD300, H SD400, S SD500)
fyH and fyS become a grade per prefix (D · H · S), chosen from SD300 · SD400 · SD500. The defaults are the P1 values, so the P1 results do not change. Each bar takes its grade from its 규격 (fyOf). The 철근표 has a 강도 column. Older files that stored fyH / fyS are read into the grade map. Only fck is still an assumed value (I02). Approved 2026-10-23. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
398fa997e2
commit
b3533b69df
11 files changed
+102
-37
No files matched your search
+16
-12
@@ -13,7 +13,7 @@ import { pileGeometry } from './core/geometry';
|
||||
import { rebarSummary, type RebarRow } from './core/rebar/summary';
|
||||
import { shearSegments } from './core/rebar/coping';
|
||||
import { buildRebar3D, type RebarColorMode, type RebarScene } from './view/rebar3d';
|
||||
import { DEFAULT_MATERIAL, FCK_MEMBERS, isAssumedMaterial, type FckByMember, type FckMember, type Material } from './core/rebar/common';
|
||||
import { DEFAULT_MATERIAL, FCK_MEMBERS, fyOf, GRADE_CHOICES, isAssumedMaterial, materialFrom, type FckByMember, type FckMember, type GradeKey, 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';
|
||||
@@ -838,7 +838,7 @@ function renderRebar(): void {
|
||||
const table = el('table', undefined, 'grid-table rebar-table');
|
||||
const head = el('thead');
|
||||
const hr = el('tr');
|
||||
for (const h of ['구분', '번호', '규격', '간격', '실간격', '위치', '방향', '형상', '길이 (mm)', '개수', '근거', '비고']) hr.appendChild(el('th', h));
|
||||
for (const h of ['구분', '번호', '규격', '강도', '간격', '실간격', '위치', '방향', '형상', '길이 (mm)', '개수', '근거', '비고']) hr.appendChild(el('th', h));
|
||||
head.appendChild(hr);
|
||||
table.appendChild(head);
|
||||
const body = el('tbody');
|
||||
@@ -855,6 +855,7 @@ function renderRebar(): void {
|
||||
el('td', ROLE[r.name] ?? '—'),
|
||||
nameCell,
|
||||
el('td', r.size),
|
||||
el('td', /^[DHS]\d/.test(r.size) ? `SD${fyOf(state.material, r.size)}` : '—'),
|
||||
el('td', r.spacing),
|
||||
el('td', r.actual),
|
||||
el('td', r.position),
|
||||
@@ -952,9 +953,10 @@ 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): 입력을 바꾸면 정착·이음 길이가 달라집니다.' : '입력값을 사용합니다.';
|
||||
for (const k of ['d', 'h', 's'] as const) {
|
||||
$<HTMLSelectElement>(`mat-grade-${k}`).value = String(m.grade[k.toUpperCase() as GradeKey]);
|
||||
}
|
||||
$('mat-note').textContent = assumed ? 'fck 가정값 (I02): 입력을 바꾸면 정착·이음 길이가 달라집니다.' : '입력값을 사용합니다.';
|
||||
}
|
||||
|
||||
/** Sets or clears one member's fck. An empty value clears it, so the member uses the 공통 fck again. */
|
||||
@@ -1001,14 +1003,15 @@ function renderMemberFck(box: HTMLDivElement, member: FckMember): void {
|
||||
box.appendChild(row);
|
||||
}
|
||||
|
||||
function setMaterial(key: keyof Material, raw: string): void {
|
||||
function setMaterial(key: 'fck' | GradeKey, raw: string): void {
|
||||
const value = Number(raw);
|
||||
if (!Number.isFinite(value) || value <= 0) {
|
||||
setStatus('공통 설정 값은 0보다 큰 숫자여야 합니다.');
|
||||
const valid = key === 'fck' ? Number.isFinite(value) && value > 0 : (GRADE_CHOICES as readonly number[]).includes(value);
|
||||
if (!valid) {
|
||||
setStatus(key === 'fck' ? '공통 설정 fck는 0보다 큰 숫자여야 합니다.' : '철근 강도는 SD300 · SD400 · SD500 중 하나여야 합니다.');
|
||||
renderMaterial();
|
||||
return;
|
||||
}
|
||||
state.material = { ...state.material, [key]: value };
|
||||
state.material = key === 'fck' ? { ...state.material, fck: value } : { ...state.material, grade: { ...state.material.grade, [key]: value } };
|
||||
// The 3D bars depend on the material too (their lengths), so the scene is rebuilt with it.
|
||||
setupScene();
|
||||
renderAll();
|
||||
@@ -1180,7 +1183,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.material = materialFrom(parsed.common);
|
||||
state.fckByMember = { ...parsed.fckByMember };
|
||||
state.piers = parsed.piers.map((p) => ({
|
||||
...buildEntry(p.input, false),
|
||||
@@ -1414,8 +1417,9 @@ $<HTMLButtonElement>('nav-shape').addEventListener('click', () => setStage('shap
|
||||
$<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));
|
||||
for (const k of ['D', 'H', 'S'] as const) {
|
||||
$<HTMLSelectElement>(`mat-grade-${k.toLowerCase()}`).addEventListener('change', (ev) => setMaterial(k, (ev.target as HTMLSelectElement).value));
|
||||
}
|
||||
$<HTMLButtonElement>('member-regen').addEventListener('click', () => {
|
||||
const pier = activePier();
|
||||
if (pier) regenerateOne(pier.input.name);
|
||||
|
||||
Reference in new issue
Block a user