feat(rebar): B11·B12 수평간격을 배수 k 입력으로 (기본 k = 8, 방향은 문서 기준)
- 간격 = k·s: B11은 L 방향 격자(교각축 직각 s), B12는 W 방향 격자(교각축 s). 기본 k = 8 (1000 mm, s = 125) - 개수는 격자 k칸마다, 조립 단 엇갈림 2칸 유지. 3D 철근도 같은 간격 - 기본 k = 8이므로 P1 기본값의 B11·B12 개수가 60 / 47 → 30 / 24로 바뀜 (k = 4이면 문서값 60 / 47을 재현, 테스트로 확인) - 패널에 k 두 칸, 규칙 표시에 B11·B12 간격 (k·s) - FT02를 n·kB11·kB12 정수 검사로 확장 Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
48e6811600
commit
0588ffe3f4
9 files changed
+54
-39
No files matched your search
@@ -25,6 +25,10 @@ export interface FootingRebarSettings {
|
||||
eTop: number;
|
||||
/** 조립 단 배수 n (조립 단 간격 = n·s_v). */
|
||||
n: number;
|
||||
/** B11 수평간격 배수 k: 간격 = k · s교각축직각 (L 방향 격자, 승인 2026-10-26). */
|
||||
kB11: number;
|
||||
/** B12 수평간격 배수 k: 간격 = k · s교각축 (W 방향 격자, 승인 2026-10-26). */
|
||||
kB12: number;
|
||||
}
|
||||
|
||||
export const DEFAULT_FOOTING_REBAR: FootingRebarSettings = {
|
||||
@@ -37,6 +41,8 @@ export const DEFAULT_FOOTING_REBAR: FootingRebarSettings = {
|
||||
sV: 200,
|
||||
eTop: 150,
|
||||
n: 2,
|
||||
kB11: 8,
|
||||
kB12: 8,
|
||||
};
|
||||
|
||||
/** Allowed range per input (inclusive, mm; n is a count). */
|
||||
@@ -50,6 +56,8 @@ export const FOOTING_REBAR_RANGES: Record<keyof FootingRebarSettings, [number, n
|
||||
sV: [150, 250],
|
||||
eTop: [100, 200],
|
||||
n: [1, 3],
|
||||
kB11: [2, 8],
|
||||
kB12: [2, 8],
|
||||
};
|
||||
|
||||
export interface FootingSettingsIssue {
|
||||
@@ -68,8 +76,10 @@ export function footingSettingsIssues(s: FootingRebarSettings): FootingSettingsI
|
||||
out.push({ code: 'FT01', key, message: `FT01 ${key} ${v} 범위 밖 (${lo}–${hi})` });
|
||||
}
|
||||
}
|
||||
if (Number.isFinite(s.n) && !Number.isInteger(s.n)) {
|
||||
out.push({ code: 'FT02', key: 'n', message: `FT02 조립 단 배수 ${s.n}는 정수여야 합니다` });
|
||||
for (const key of ['n', 'kB11', 'kB12'] as const) {
|
||||
if (Number.isFinite(s[key]) && !Number.isInteger(s[key])) {
|
||||
out.push({ code: 'FT02', key, message: `FT02 ${key} ${s[key]}는 정수여야 합니다` });
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
+13
-10
@@ -140,16 +140,17 @@ export function assemblyEndOffset(): number {
|
||||
/** B11 (along W, between the B10-2 levels) and B12 (along L), 4s apart with a 2s stagger per level (§8). */
|
||||
export function assemblyBars(L: number, W: number, H: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): FootingBar[] {
|
||||
const levelsJ = assemblyJs(H, f);
|
||||
const perLevel = (n: number, startOdd: number, startEven: number) =>
|
||||
// Bars every k grid steps (승인 2026-10-26: k·s); the levels alternate a 2-step stagger (엇갈림 2s).
|
||||
const perLevel = (n: number, k: number) =>
|
||||
levelsJ.map((_, idx) => {
|
||||
const i0 = idx % 2 === 0 ? startOdd : startEven;
|
||||
return Math.floor((n - i0) / 4) + 1;
|
||||
const i0 = idx % 2 === 0 ? 0 : 2;
|
||||
return Math.floor((n - i0) / k) + 1;
|
||||
});
|
||||
const nW = grid(W, f, 'sPerp').positions - 1; // 61 (B12 uses the 교각축 직각 value, 승인 2026-10-26)
|
||||
const nL = grid(L, f, 'sPerp').positions - 1; // 79 (B11 along L)
|
||||
// B11 is spread along L (n = 79) and B12 along W (n = 61); each level has its own count.
|
||||
const b11 = perLevel(nL, 0, 2).reduce((a, b) => a + b, 0);
|
||||
const b12 = perLevel(nW, 0, 2).reduce((a, b) => a + b, 0);
|
||||
// B11 is spread along L (교각축 직각 grid) and B12 along W (교각축 grid), per 확대기초철근 (1).md §8.
|
||||
const nW = grid(W, f, 'sAxis').positions - 1;
|
||||
const nL = grid(L, f, 'sPerp').positions - 1;
|
||||
const b11 = perLevel(nL, f.kB11).reduce((a, b) => a + b, 0);
|
||||
const b12 = perLevel(nW, f.kB12).reduce((a, b) => a + b, 0);
|
||||
const hook = Math.round(hookLength('main90', d('S16'))); // 246 mm, rounded as in the document (§8.3)
|
||||
const depth1 = sideDepth(NOMINAL_DIAMETER.S29, NOMINAL_DIAMETER.S19, f);
|
||||
const depth2 = sideDepth(NOMINAL_DIAMETER.S25, NOMINAL_DIAMETER.S19, f);
|
||||
@@ -245,7 +246,8 @@ export function footingRuleValues(H: number, f: FootingRebarSettings = DEFAULT_F
|
||||
b6Leg: number;
|
||||
b9Pitch: number;
|
||||
b9Stagger: number;
|
||||
assemblyPlanPitch: number;
|
||||
b11Pitch: number;
|
||||
b12Pitch: number;
|
||||
assemblyVertical: number;
|
||||
assemblyLevels: number;
|
||||
} {
|
||||
@@ -258,7 +260,8 @@ export function footingRuleValues(H: number, f: FootingRebarSettings = DEFAULT_F
|
||||
b6Leg: z.B5 - z.B68,
|
||||
b9Pitch: 4 * f.sAxis,
|
||||
b9Stagger: 2 * f.sAxis,
|
||||
assemblyPlanPitch: 4 * f.sPerp,
|
||||
b11Pitch: f.kB11 * f.sPerp,
|
||||
b12Pitch: f.kB12 * f.sAxis,
|
||||
assemblyVertical: f.n * f.sV,
|
||||
assemblyLevels: assemblyJs(H, f).length,
|
||||
};
|
||||
|
||||
@@ -173,7 +173,9 @@ export function linkedSpacing(name: string, model: PierModel): Pick<RebarRow, 's
|
||||
return { spacing: String(2 * fs.sPerp), actual: String(cycle ? fs.sPerp : 2 * fs.sPerp), basis: basis(fs.sPerp !== fd.sPerp) };
|
||||
}
|
||||
if (name === 'B9') return { spacing: `${4 * fs.sAxis} (4s)`, actual: `${2 * fs.sAxis} (2s 엇갈림)`, basis: basis(fs.sAxis !== fd.sAxis) };
|
||||
if (name === 'B11' || name === 'B12') return { spacing: `${4 * fs.sPerp} (4s)`, actual: String(4 * fs.sPerp), basis: basis(fs.sPerp !== fd.sPerp) };
|
||||
// B11 along L uses the 교각축 직각 grid, B12 along W uses the 교각축 grid; each is k · s (승인 2026-10-26).
|
||||
if (name === 'B11') return { spacing: `${fs.kB11 * fs.sPerp} (${fs.kB11}s)`, actual: String(fs.kB11 * fs.sPerp), basis: basis(fs.sPerp !== fd.sPerp || fs.kB11 !== fd.kB11) };
|
||||
if (name === 'B12') return { spacing: `${fs.kB12 * fs.sAxis} (${fs.kB12}s)`, actual: String(fs.kB12 * fs.sAxis), basis: basis(fs.sAxis !== fd.sAxis || fs.kB12 !== fd.kB12) };
|
||||
if (name === 'B10-1' || name === 'B10-2') return { spacing: String(fs.sV), actual: String(fs.sV), basis: basis(fs.sV !== fd.sV) };
|
||||
if (name === 'S1') return { spacing: String(b.spacingS1), actual: String(b.spacingS1), basis: basis(b.spacingS1 !== bd.spacingS1) };
|
||||
if (name === 'S2') return { spacing: String(b.spacingS2), actual: String(b.spacingS2), basis: basis(b.spacingS2 !== bd.spacingS2) };
|
||||
|
||||
+3
-1
@@ -1303,6 +1303,8 @@ const FOOTING_REBAR_FIELDS: { key: keyof FootingRebarSettings; label: string }[]
|
||||
{ key: 'sV', label: 'B10 높이 간격 s_v (mm)' },
|
||||
{ key: 'eTop', label: 'B5 아래 최소 여유 e_top (mm)' },
|
||||
{ key: 'n', label: '조립 단 배수 n (B10 간격의 n배)' },
|
||||
{ key: 'kB11', label: 'B11 수평간격 배수 k (L 방향, k·s교각축직각)' },
|
||||
{ key: 'kB12', label: 'B12 수평간격 배수 k (W 방향, k·s교각축)' },
|
||||
];
|
||||
|
||||
/** 적용: the values go to the draft and the model is rebuilt only when every check passes (FT01·FT02). */
|
||||
@@ -1350,7 +1352,7 @@ function renderFootingSettings(box: HTMLDivElement, pier: Pier): void {
|
||||
`하면 1·2단 간격 Δ₂ = ${r.delta2} mm (고정)`,
|
||||
`B2 디리 ${r.b2Leg} mm · B6 디리 ${r.b6Leg} mm (자동, B1·B5 높이까지)`,
|
||||
`B9 간격 4·s교각축 ${r.b9Pitch} · 행 엇갈림 2·s교각축 ${r.b9Stagger} mm (규칙)`,
|
||||
`B11·B12 평면 간격 4·s교각축직각 ${r.assemblyPlanPitch} · 연직 n·s_v ${r.assemblyVertical} mm · 조립 단 ${r.assemblyLevels}단 (자동)`,
|
||||
`B11 평면 간격 k·s교각축직각 ${r.b11Pitch} · B12 평면 간격 k·s교각축 ${r.b12Pitch} mm · 연직 n·s_v ${r.assemblyVertical} mm · 조립 단 ${r.assemblyLevels}단 (자동)`,
|
||||
'cycle 배치: 교대 배치 (고정)',
|
||||
].join(' · ');
|
||||
}
|
||||
|
||||
+3
-3
@@ -471,7 +471,7 @@ export function buildRebar3D(m: PierModel, common: Material = DEFAULT_MATERIAL,
|
||||
}
|
||||
// B11 (across W, spread along L) and B12 (along L, spread across W) at the assembly levels, 4s apart, 2s stagger per level.
|
||||
const xsL = gridPositions(Lf * 1000, fs, 'sPerp');
|
||||
const ysW = gridPositions(Wf * 1000, fs, 'sPerp');
|
||||
const ysW = gridPositions(Wf * 1000, fs, 'sAxis');
|
||||
const dAsm = assemblyEndOffset();
|
||||
const nAsm = Wf / 2 - mm(d1 + dAsm);
|
||||
const sAsm = Lf / 2 - mm(d2 + dAsm);
|
||||
@@ -479,11 +479,11 @@ export function buildRebar3D(m: PierModel, common: Material = DEFAULT_MATERIAL,
|
||||
const zA = bottom + mm(fs.cBottom + jA * fs.sV);
|
||||
const zB = zA + dB10;
|
||||
const i0 = k % 2 === 0 ? 0 : 2;
|
||||
for (let iL = i0; iL < xsL.length; iL += 4) {
|
||||
for (let iL = i0; iL < xsL.length; iL += fs.kB11) {
|
||||
const x = f.s + mm(xsL[iL]);
|
||||
polyline('B11', [[x, zA - hookAsm, -nAsm], [x, zA, -nAsm], [x, zA, nAsm], [x, zA - hookAsm, nAsm]], NOMINAL_DIAMETER.S16);
|
||||
}
|
||||
for (let iW = i0; iW < ysW.length; iW += 4) {
|
||||
for (let iW = i0; iW < ysW.length; iW += fs.kB12) {
|
||||
const n = mm(ysW[iW]);
|
||||
polyline('B12', [[f.s - sAsm, zB - hookAsm, n], [f.s - sAsm, zB, n], [f.s + sAsm, zB, n], [f.s + sAsm, zB - hookAsm, n]], NOMINAL_DIAMETER.S16);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user