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:
ypshimandClaude Haiku 5.5 committed 2026-10-22 09:01:40 +09:00
1 parent 5466b3522e
commit ad59c0c74d
3 files changed
+64 -22

No files matched your search

+40 -21
View File
@@ -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++) {