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
@@ -1,3 +1,4 @@
|
||||
import type { Material } from './rebar/common';
|
||||
import type { PierInput } from './types';
|
||||
|
||||
/** Project file schema (spec §8). Bump only with a migration. */
|
||||
@@ -28,6 +29,8 @@ export interface ProjectFile {
|
||||
source: { fileName: string; sheets: string[] };
|
||||
piers: ProjectPier[];
|
||||
history: { version: string; note: string; at: string }[];
|
||||
/** ③ 공통 설정 (fck · fy). Absent in older files: the defaults are used. */
|
||||
common?: Material;
|
||||
}
|
||||
|
||||
/** Serialises a project to the pretty-printed JSON stored in <교량명>_교각설계.pzr.json. */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { developmentLength, hookLength, NOMINAL_DIAMETER, spliceLength } from './common';
|
||||
import { DEFAULT_MATERIAL, developmentLength, hookLength, Material, NOMINAL_DIAMETER, spliceLength } from './common';
|
||||
import { layers } from './footing';
|
||||
|
||||
/**
|
||||
@@ -90,12 +90,12 @@ const dH19 = NOMINAL_DIAMETER.H19;
|
||||
const dH16 = NOMINAL_DIAMETER.H16;
|
||||
const round = (v: number, d = 1) => Math.round(v * 10 ** d) / 10 ** d;
|
||||
|
||||
export function columnRebar(x: ColumnInput = COLUMN_P1) {
|
||||
export function columnRebar(x: ColumnInput = COLUMN_P1, m: Material = DEFAULT_MATERIAL) {
|
||||
// P1 and P2 main bars (기둥철근.md §3)
|
||||
const r1 = x.D / 2 - x.c;
|
||||
const r2 = r1 - dS32;
|
||||
const zTop1 = x.Hc + x.hcop - x.eP1;
|
||||
const lD2 = developmentLength(dS32, 500, 27);
|
||||
const lD2 = developmentLength(dS32, m.fyS, m.fck);
|
||||
const zTop2 = x.Hc + lD2;
|
||||
const couplerLevels = Array.from({ length: Math.max(0, Math.floor(zTop1 / x.ctcCoupler) - 1) }, (_, i) => (i + 1) * x.ctcCoupler);
|
||||
const couplersP1 = couplerLevels.length;
|
||||
@@ -128,8 +128,8 @@ export function columnRebar(x: ColumnInput = COLUMN_P1) {
|
||||
const p6Hook = hookLength('tie90', dH16);
|
||||
const p6Length = p6Straight + 2 * p6Hook;
|
||||
|
||||
// P3 lap: Q-C6 is open; recommended = 1.3·l_d of H19 (fck 27, fy 400).
|
||||
const lapP3 = spliceLength(dH19, 400, 27);
|
||||
// P3 lap: Q-C6 is open; recommended = 1.3·l_d of H19 with the material inputs.
|
||||
const lapP3 = spliceLength(dH19, m.fyH, m.fck);
|
||||
const p3Length = 2 * Math.PI * tieRadius + lapP3;
|
||||
const p5Length = 2 * Math.PI * tieRadius;
|
||||
|
||||
|
||||
@@ -2,6 +2,23 @@
|
||||
* ③ 철근배근 공통 규칙 (spec §6.2 · plan C1). Lengths in mm, stresses in MPa.
|
||||
* fck and fy are inputs. The defaults below are assumed values and must be shown as such (I02).
|
||||
*/
|
||||
/** Material inputs of ③ 공통 설정 (spec §6.2). fck and fy are inputs; the defaults are assumed values (I02). */
|
||||
export interface Material {
|
||||
/** 콘크리트 강도 fck (MPa). */
|
||||
fck: number;
|
||||
/** H = SD400 항복강도 (MPa). */
|
||||
fyH: number;
|
||||
/** S = SD500 항복강도 (MPa). */
|
||||
fyS: number;
|
||||
}
|
||||
|
||||
export const DEFAULT_MATERIAL: Material = { fck: 27, fyH: 400, fyS: 500 };
|
||||
|
||||
/** True while the values are the assumed defaults (정보 I02 on the rebar screen). */
|
||||
export function isAssumedMaterial(m: Material): boolean {
|
||||
return m.fck === DEFAULT_MATERIAL.fck && m.fyH === DEFAULT_MATERIAL.fyH && m.fyS === DEFAULT_MATERIAL.fyS;
|
||||
}
|
||||
|
||||
export const MATERIAL_DEFAULTS = {
|
||||
fck: 27,
|
||||
/** H = SD400, S = SD500. */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { hookLength, NOMINAL_DIAMETER, spliceLength } from './common';
|
||||
import { DEFAULT_MATERIAL, hookLength, Material, NOMINAL_DIAMETER, spliceLength } from './common';
|
||||
|
||||
/**
|
||||
* C1–C8 상·하면 주철근 (plan C3 continued). Source: RAG/코핑철근.md §5, 2026-10-22 최종 정리.
|
||||
@@ -69,11 +69,11 @@ export function topBarLength(L: number, k: number): number {
|
||||
|
||||
/**
|
||||
* Bottom bar C7·C8 (spliced at the column centre, 하면 겹침이음). Each piece:
|
||||
* path/2 + l_s/2 + l_h, with path = 2·slope + Level and l_s = 1.3·l_d for H29 (nominal 28.6, fck 27, fy 400).
|
||||
* path/2 + l_s/2 + l_h, with path = 2·slope + Level and l_s = 1.3·l_d for H29 (nominal 28.6, with the material inputs).
|
||||
*/
|
||||
export function bottomPiece(fck = 27): { path: number; ls: number; piece: number } {
|
||||
export function bottomPiece(m: Material = DEFAULT_MATERIAL): { path: number; ls: number; piece: number } {
|
||||
const path = 2 * MAIN.slopeEach + MAIN.level;
|
||||
const ls = spliceLength(NOMINAL_DIAMETER.H29, 400, fck);
|
||||
const ls = spliceLength(NOMINAL_DIAMETER.H29, m.fyH, m.fck);
|
||||
const lh = hookLength('main90', NOMINAL_DIAMETER.H29);
|
||||
return { path, ls, piece: path / 2 + ls / 2 + lh };
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { bottomPiece, topBarLength, widthLayout } from './coping-main';
|
||||
import { shearBars, sideRows, C14_COUNTS, c14Total, STEP_BAR_COUNTS, shearULength, SIDE_LENGTHS, STEP_LENGTHS, capLength, PIECES_PER_POSITION } from './coping';
|
||||
import { columnRebar, COLUMN_P1 } from './column';
|
||||
import { bottomBars, crossBars, sideBars, assemblyBars, shearB9 } from './footing';
|
||||
import { splitPieces } from './common';
|
||||
import { DEFAULT_MATERIAL, isAssumedMaterial, Material, splitPieces } from './common';
|
||||
|
||||
/** One line of a 철근표. length is null where the document gives no length yet. */
|
||||
export interface RebarRow {
|
||||
@@ -28,9 +28,12 @@ export const STANDARD_BAR = 12000;
|
||||
* 철근표 for one pier (③ screen v1): bearing blocks, coping, column, footing. Built from the generated model.
|
||||
* Values come from the document rules in src/core/rebar/*. B9 (전단철근) is not computed yet.
|
||||
*/
|
||||
export function rebarSummary(model: PierModel): { rows: RebarRow[]; warnings: RebarWarning[] } {
|
||||
export function rebarSummary(model: PierModel, m: Material = DEFAULT_MATERIAL): { rows: RebarRow[]; warnings: RebarWarning[] } {
|
||||
const rows: RebarRow[] = [];
|
||||
const warnings: RebarWarning[] = [];
|
||||
if (isAssumedMaterial(m)) {
|
||||
warnings.push({ code: 'I02', message: `fck ${m.fck} MPa · fy ${m.fyH} / ${m.fyS} MPa는 가정값입니다. 정착·이음 길이에 반영됩니다. 공통 설정에서 확인하세요.` });
|
||||
}
|
||||
const L = model.copingL * 1000;
|
||||
const B = model.copingB * 1000;
|
||||
|
||||
@@ -46,7 +49,7 @@ export function rebarSummary(model: PierModel): { rows: RebarRow[]; warnings: Re
|
||||
for (const [name, k] of [['C1', 1], ['C2', 2], ['C3', 3], ['C4', 1], ['C5', 2], ['C6', 3]] as const) {
|
||||
rows.push({ member: 'coping', name, size: 'H29', count: perRow, length: topBarLength(L, k), note: '상면, 이음 무시' });
|
||||
}
|
||||
const bottom = bottomPiece();
|
||||
const bottom = bottomPiece(m);
|
||||
rows.push({ member: 'coping', name: 'C7', size: 'H29', count: perRow, length: bottom.piece, note: '하면, 2조각 겹침이음' });
|
||||
rows.push({ member: 'coping', name: 'C8', size: 'H29', count: perRow, length: bottom.piece, note: '하면, 2조각 겹침이음' });
|
||||
const shear = shearBars(L);
|
||||
@@ -71,7 +74,7 @@ export function rebarSummary(model: PierModel): { rows: RebarRow[]; warnings: Re
|
||||
rows.push({ member: 'coping', name: 'C16', size: 'H19', count: STEP_BAR_COUNTS.C16, length: STEP_LENGTHS.C16[0], note: `계단부, 3·4구간 (${STEP_LENGTHS.C16[0]} / ${STEP_LENGTHS.C16[1]})` });
|
||||
|
||||
// Column (기둥철근.md · plan C4), P1 defaults
|
||||
const col = columnRebar(COLUMN_P1);
|
||||
const col = columnRebar(COLUMN_P1, m);
|
||||
rows.push({ member: 'column', name: 'P1', size: 'S32', count: col.mainP1, length: null, note: '외측 r 875, 마디 4697.5 + 갈고리 525 / 3000 ×3 / 3147 (코핑 밖 마디는 기둥철근.md §3.2)' });
|
||||
rows.push({ member: 'column', name: 'P2', size: 'S32', count: col.mainP2, length: null, note: '내측 r 843.2, 마디 4847.5 / 3000 ×3 / 2389' });
|
||||
rows.push({ member: 'column', name: '커플러', size: 'L150 Ø60', count: col.couplers, length: 150, note: '@3000, P2 +150' });
|
||||
@@ -88,7 +91,7 @@ export function rebarSummary(model: PierModel): { rows: RebarRow[]; warnings: Re
|
||||
const Hf = (f.top - f.bottom) * 1000;
|
||||
// Bars longer than the standard length are split in two (추천안, Q-B6). The count is still the bar count.
|
||||
const addBar = (b: { name: string; size: string; count: number; length: number }, note: string) => {
|
||||
const split = splitPieces(b.length, 25.4, 500, 27, STANDARD_BAR);
|
||||
const split = splitPieces(b.length, 25.4, m.fyS, m.fck, STANDARD_BAR);
|
||||
if (split) {
|
||||
rows.push({ member: 'footing', name: b.name, size: b.size, count: b.count, length: split.pieceLength, note: `${note}, 2조각 이음 (추천안, 겹침 ${Math.round(split.lap)})` });
|
||||
warnings.push({ code: 'RB-F08', message: `${b.name} 길이 ${Math.round(b.length)} mm > 정척 ${STANDARD_BAR} mm: 2조각 이음을 추천안으로 적용 (Q-B6).` });
|
||||
|
||||
Reference in new issue
Block a user