Files
Pier_v1/src/core/piles.ts
T

90 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 말뚝 열별 본수 (spec §4.6 · §5.4 · plan C4). A 열 is one row across the footing width; 본 are along the length.
* Default: each 열 gets floor((길이 − 2·단부)/간격) + 1 본, so the centre spacing is at least the minimum.
* The user may change any 열's count. Counts below the minimum spacing are allowed and reported as a warning.
*/
export interface PileGeometry {
footingLength: number;
footingWidth: number;
/** 단부 거리 (700 mm). */
end: number;
/** 최소 중심간격 (2250 mm). */
minSpacing: number;
}
/** A count outside this range is refused: fewer than two piles has no spacing, and the cap guards against typing errors. */
export const PILE_COUNT_LIMITS = { min: 2, max: 30 } as const;
/** Largest count along one length with centres at or above the minimum spacing. */
export function maxCount(length: number, g: PileGeometry): number {
return Math.floor((length - 2 * g.end) / g.minSpacing) + 1;
}
/** Number of 열 across the width. */
export function rowsAcross(g: PileGeometry): number {
return maxCount(g.footingWidth, { ...g, minSpacing: g.minSpacing });
}
export function defaultRowCounts(g: PileGeometry): number[] {
return Array.from({ length: rowsAcross(g) }, () => maxCount(g.footingLength, g));
}
/** Counts to use for a footing: the requested 열별 본수 if any, otherwise the defaults. Refuses bad input. */
export function resolveRowCounts(requested: number[] | undefined, g: PileGeometry): number[] {
if (requested === undefined) return defaultRowCounts(g);
const rows = rowsAcross(g);
if (requested.length !== rows) {
throw new Error(`열별 본수는 ${rows}개여야 합니다 (입력 ${requested.length}개).`);
}
for (const c of requested) {
if (!Number.isInteger(c) || c < PILE_COUNT_LIMITS.min || c > PILE_COUNT_LIMITS.max) {
throw new Error(`본수는 ${PILE_COUNT_LIMITS.min}–${PILE_COUNT_LIMITS.max} 사이의 정수여야 합니다 (입력 ${c}).`);
}
}
return [...requested];
}
export interface RowLayout {
count: number;
/** Centre spacing along the length, in m. */
spacing: number;
/** True when the spacing is below the minimum (warning, not refused). */
belowMinimum: boolean;
}
export function rowLayout(counts: number[], g: PileGeometry): RowLayout[] {
return counts.map((count) => {
const spacing = (g.footingLength - 2 * g.end) / (count - 1);
return { count, spacing, belowMinimum: spacing < g.minSpacing - 1e-9 };
});
}
/** Total piles (sum of the counts along each 열). */
export function totalPiles(counts: number[]): number {
return counts.reduce((sum, c) => sum + c, 0);
}
/** Changes one 열's count by delta, staying inside the limits. Returns a new array. */
export function adjustRow(counts: number[], row: number, delta: number): number[] {
const next = [...counts];
next[row] = Math.min(PILE_COUNT_LIMITS.max, Math.max(PILE_COUNT_LIMITS.min, next[row] + delta));
return next;
}
/**
* Display string. Uniform counts keep the 1단계 form, e.g. "4@2250 × 3@2250 = 20본"
* (gaps along the length × gaps across the width). Non-uniform counts list each 열, e.g. "열1 5@2250 · 열2 6@1800 = 23본".
*/
export function layoutText(counts: number[], g: PileGeometry): string {
const layout = rowLayout(counts, g);
const total = totalPiles(counts);
const mm = (v: number) => Math.round(v * 1000);
const across = (g.footingWidth - 2 * g.end) / (counts.length - 1);
const uniform = counts.every((c) => c === counts[0]);
if (uniform) {
return `${layout[0].count - 1}@${mm(layout[0].spacing)} × ${counts.length - 1}@${mm(across)} = ${total}본`;
}
const parts = layout.map((r, j) => `열${j + 1} ${r.count - 1}@${mm(r.spacing)}`);
return `${parts.join(' · ')} = ${total}본`;
}