feat(core): 일체기초 footings for 2·3주형 with per-footing pile grid
- Columns closer than one footing length (10400) share a 일체기초 whose length is the outer-column distance + 10400, centred between them (spec §4.6). - Pile grid is computed from each footing's length (rows across 8150, pile count along the length from (length − 1400) / 2250 + 1). - Blinding concrete follows each combined footing. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5466b3522e
commit
ad59c0c74d
3 files changed
+64
-22
No files matched your search
+40
-21
@@ -240,21 +240,23 @@ export function buildPier(input: PierInput): PierModel {
|
||||
const pileLength = input.pileLengthMm / 1000;
|
||||
const pileTip = input.footingBottomEl - pileLength;
|
||||
|
||||
for (const s of colS) {
|
||||
if (input.foundationType === '현장타설말뚝') {
|
||||
if (input.foundationType === '현장타설말뚝') {
|
||||
for (const s of colS) {
|
||||
piles.push({ s, n: 0, top: input.footingBottomEl, bottom: pileTip, diameter: DIM.columnDiameter });
|
||||
continue;
|
||||
}
|
||||
footings.push({ s, top: footingTop, bottom: input.footingBottomEl, length: DIM.footingL, width: DIM.footingW });
|
||||
blindings.push({
|
||||
s,
|
||||
top: input.footingBottomEl,
|
||||
length: DIM.footingL + DIM.blindingMargin,
|
||||
width: DIM.footingW + DIM.blindingMargin,
|
||||
thickness: DIM.blindingT,
|
||||
});
|
||||
if (input.foundationType === '말뚝기초') {
|
||||
piles.push(...pilesFor(s, input.footingBottomEl, pileLength));
|
||||
} else {
|
||||
for (const g of footingGroups(colS)) {
|
||||
footings.push({ s: g.s, top: footingTop, bottom: input.footingBottomEl, length: g.length, width: DIM.footingW });
|
||||
blindings.push({
|
||||
s: g.s,
|
||||
top: input.footingBottomEl,
|
||||
length: g.length + DIM.blindingMargin,
|
||||
width: DIM.footingW + DIM.blindingMargin,
|
||||
thickness: DIM.blindingT,
|
||||
});
|
||||
if (input.foundationType === '말뚝기초') {
|
||||
piles.push(...pilesFor(g.s, input.footingBottomEl, pileLength, g.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +308,7 @@ export function buildPier(input: PierInput): PierModel {
|
||||
columnHeight: levelBottom - columns[0].bottom,
|
||||
soilCover: input.groundEl - (input.foundationType === '현장타설말뚝' ? input.footingBottomEl : footingTop),
|
||||
pileCount: piles.length,
|
||||
pileLayout: input.foundationType === '말뚝기초' ? pileLayoutText() : null,
|
||||
pileLayout: input.foundationType === '말뚝기초' ? pileLayoutText(footings[0].length) : null,
|
||||
pileTipEl: piles.length ? pileTip : null,
|
||||
blockHeightFront,
|
||||
blockHeightBack,
|
||||
@@ -326,30 +328,47 @@ function segmentTop(segments: Segment[], s: number): number {
|
||||
return hit.top;
|
||||
}
|
||||
|
||||
/**
|
||||
* Footings (spec §4.6). Adjacent columns closer than one footing length share a 일체기초 whose length is
|
||||
* the distance between its outer columns + DIM.footingL, centred between them. Other columns get their own footing.
|
||||
*/
|
||||
export function footingGroups(colS: number[]): { s: number; length: number }[] {
|
||||
const groups: number[][] = [];
|
||||
for (const s of [...colS].sort((a, b) => a - b)) {
|
||||
const last = groups[groups.length - 1];
|
||||
if (last && s - last[last.length - 1] < DIM.footingL) last.push(s);
|
||||
else groups.push([s]);
|
||||
}
|
||||
return groups.map((g) => ({
|
||||
s: (g[0] + g[g.length - 1]) / 2,
|
||||
length: g[g.length - 1] - g[0] + DIM.footingL,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Piles under one footing (spec §4.6): rows across the footing width, piles along the length,
|
||||
* both with an end distance of 0.7 m and an even spacing that never exceeds 2.25 m.
|
||||
*/
|
||||
export function pileGrid(): { countS: number; countN: number; spacingS: number; spacingN: number } {
|
||||
export function pileGrid(footingLength: number = DIM.footingL): { countS: number; countN: number; spacingS: number; spacingN: number } {
|
||||
const span = (size: number) => Math.floor((size - 2 * DIM.pileEnd) / DIM.pileSpacing) + 1;
|
||||
const countS = span(DIM.footingL);
|
||||
const countS = span(footingLength);
|
||||
const countN = span(DIM.footingW);
|
||||
return {
|
||||
countS,
|
||||
countN,
|
||||
spacingS: (DIM.footingL - 2 * DIM.pileEnd) / (countS - 1),
|
||||
spacingS: (footingLength - 2 * DIM.pileEnd) / (countS - 1),
|
||||
spacingN: (DIM.footingW - 2 * DIM.pileEnd) / (countN - 1),
|
||||
};
|
||||
}
|
||||
|
||||
/** Display string such as "4@2250 × 3@2250 = 20본" (gaps along the length × gaps across the width). */
|
||||
export function pileLayoutText(): string {
|
||||
const g = pileGrid();
|
||||
export function pileLayoutText(footingLength: number = DIM.footingL): string {
|
||||
const g = pileGrid(footingLength);
|
||||
return `${g.countS - 1}@${Math.round(g.spacingS * 1000)} × ${g.countN - 1}@${Math.round(g.spacingN * 1000)} = ${g.countS * g.countN}본`;
|
||||
}
|
||||
|
||||
export function pilesFor(s: number, topEl: number, length: number): PileModel[] {
|
||||
const { countS, countN, spacingS, spacingN } = pileGrid();
|
||||
export function pilesFor(s: number, topEl: number, length: number, footingLength: number = DIM.footingL): PileModel[] {
|
||||
const { countS, countN, spacingS, spacingN } = pileGrid(footingLength);
|
||||
const out: PileModel[] = [];
|
||||
for (let i = 0; i < countS; i++) {
|
||||
for (let j = 0; j < countN; j++) {
|
||||
|
||||
@@ -8,7 +8,6 @@ export const NOT_IMPLEMENTED_RULES = [
|
||||
'E04 fck 미입력 (배근 단계, 3단계)',
|
||||
'W06 속성 복사 건너뜀 (2단계)',
|
||||
'W08 솔플레이트 T1−T2 검사 (2단계)',
|
||||
'일체기초 판정 (2·3주형)',
|
||||
] as const;
|
||||
|
||||
/** Input-level checks (spec §10 E01, E02). Returns false when the pier must not be built. */
|
||||
|
||||
@@ -106,3 +106,27 @@ describe('bearing and block checks (spec §10 W03, W04)', () => {
|
||||
expect(issues.map((i) => i.code)).toContain('W04');
|
||||
});
|
||||
});
|
||||
|
||||
describe('일체기초 (spec §4.6)', () => {
|
||||
it('2주형 L 12000: columns 7200 apart share one footing of 17600 with 4 × 8 piles', () => {
|
||||
const m = buildPier({ ...loadP1(), type: '2주형', columnCount: 2, copingLengthMm: 12000, foundationType: '말뚝기초' });
|
||||
expect(m.footings).toHaveLength(1);
|
||||
expect(m.footings[0].length).toBeCloseTo(17.6, 6);
|
||||
expect(m.footings[0].s).toBeCloseTo(6.0, 6);
|
||||
expect(m.blindings[0].length).toBeCloseTo(17.8, 6);
|
||||
expect(m.piles).toHaveLength(32);
|
||||
expect(m.derived.pileLayout).toBe('7@2314 × 3@2250 = 32본');
|
||||
});
|
||||
|
||||
it('3주형 L 12000: three columns 3600 apart share one footing', () => {
|
||||
const m = buildPier({ ...loadP1(), type: '3주형', columnCount: 3, copingLengthMm: 12000, foundationType: '직접기초' });
|
||||
expect(m.footings).toHaveLength(1);
|
||||
expect(m.footings[0].length).toBeCloseTo(17.6, 6);
|
||||
});
|
||||
|
||||
it('2주형 with columns 18000 apart: separate footings of 10400', () => {
|
||||
const m = buildPier({ ...loadP1(), type: '2주형', columnCount: 2, copingLengthMm: 30000, foundationType: '직접기초' });
|
||||
expect(m.footings).toHaveLength(2);
|
||||
expect(m.footings.every((f) => Math.abs(f.length - 10.4) < 1e-9)).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user