- 현장타설 기둥·말뚝 형상(cast-in-place-shape)과 주철근·횡철근·커플러 계산(rebar/cast-in-place, cast-layout) - 다주형 코핑 철근(multicol-coping), 기둥 높이 · 양단부 연장 - 철근표 요약 · 재료 수량 · 3D 뷰 연결과 삽도(cast-rebar-svg) - 검사 기준 갱신과 회귀 테스트, 검증 스크립트 보관 Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
73 lines
4.6 KiB
TypeScript
73 lines
4.6 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { castShape, castShapeIssues, castLowerZone } from '../src/core/cast-in-place-shape';
|
|
import { castRebarDefaults } from '../src/core/rebar/cast-in-place';
|
|
import { buildPier } from '../src/core/geometry';
|
|
import { changedKeys } from '../src/core/changes';
|
|
import { parseProject, serializeProject, SCHEMA_VERSION, type ProjectFile } from '../src/core/project';
|
|
import { loadP1 } from './helpers';
|
|
|
|
describe('2026-11-05 cast-in-place drawing and user decisions', () => {
|
|
const input = { ...loadP1(), foundationType: '현장타설말뚝' as const, groundEl: 30, footingBottomEl: 10, pileLengthMm: 24000 };
|
|
it('uses ground level for the pile head and ground minus length for its tip', () => {
|
|
const model = buildPier(input);
|
|
expect(model.piles[0].top).toBe(30);
|
|
expect(model.piles[0].bottom).toBe(6);
|
|
expect(model.columns[0].bottom).toBe(30);
|
|
expect(model.footings).toEqual([]);
|
|
expect(model.derived.soilCover).toBe(0);
|
|
});
|
|
it('uses the clarified positive rock offsets, 9000 mm beta and casing defaults', () => {
|
|
expect(castShape(input)).toMatchObject({ pileBottomEl: 6, betaLengthMm: 9000, betaEl: 21, weatheredRockEl: 11, bedrockEl: 7, casingOn: false, casingTopEl: 33, casingBottomEl: 7, clayBottomEl: undefined });
|
|
});
|
|
it('rejects a pile head above the coping instead of generating an inverted column', () => {
|
|
expect(() => buildPier({ ...input, groundEl: 100 })).toThrow('지반고');
|
|
});
|
|
it('rejects zero or negative cast-in-place pile length', () => {
|
|
for (const pileLengthMm of [0, -1]) expect(() => buildPier({ ...input, pileLengthMm })).toThrow('말뚝 길이');
|
|
});
|
|
it('keeps automatic values linked to geometry changes and manual overrides fixed', () => {
|
|
const auto = castShape({ ...input, groundEl: 40 });
|
|
expect(auto.bedrockEl).toBe(17);
|
|
expect(auto.casingBottomEl).toBe(17);
|
|
expect(auto.casingTopEl).toBe(43);
|
|
const manual = castShape({ ...input, groundEl: 40, castInPlaceShape: { bedrockEl: -2, casingTopEl: 35, weatheredRockEl: 3 } });
|
|
expect(manual.casingBottomEl).toBe(-2);
|
|
expect(manual.casingTopEl).toBe(35);
|
|
expect(manual.weatheredRockEl).toBe(3);
|
|
});
|
|
it('requires independent clay EL and reports reversed lower zone instead of silently swapping it', () => {
|
|
expect(castLowerZone(input)).toBeNull();
|
|
expect(castLowerZone({ ...input, castInPlaceShape: { clayBottomEl: 16 } })).toEqual({ bottomEl: 15, topEl: 18, valid: true });
|
|
expect(castLowerZone({ ...input, castInPlaceShape: { clayBottomEl: 4 } })?.valid).toBe(false);
|
|
});
|
|
it('inherits both round-column main layers, hoop spacing, cover convention and couplers', () => {
|
|
const s = castRebarDefaults(input);
|
|
expect(s.column).toMatchObject({ mainDiameterMm: 31.8, innerMainDiameterMm: 31.8, mainCount: 46, innerMainCount: 46, coverMm: 125, coverReference: 'main-center', tieSpacingMm: 300, endZoneSpacingMm: 120, tieKind: 'hoop' });
|
|
expect(s.pile?.endCoverMm).toBe(250);
|
|
expect(s.connection).toEqual({ mode: 'coupler', couplerLengthMm: 150 });
|
|
});
|
|
it('preserves old explicit clear cover and independent edits', () => {
|
|
const s = castRebarDefaults({ ...input, columnRebar: { nDiv: 48, c: 140 }, castInPlaceRebar: { column: { coverMm: 100, mainCount: 44 }, pile: { endCoverMm: 300 } } });
|
|
expect(s.column?.coverReference).toBe('clear');
|
|
expect(s.column?.coverMm).toBe(100);
|
|
expect(s.column?.mainCount).toBe(44);
|
|
expect(s.pile?.coverMm).toBe(140);
|
|
expect(s.pile?.innerMainCount).toBe(48);
|
|
expect(s.pile?.endCoverMm).toBe(300);
|
|
});
|
|
it('persists explicit shape values without turning auto defaults into fixed values', () => {
|
|
const next = { ...input, castInPlaceShape: { casingOn: true, clayBottomEl: 16, betaLengthMm: 10000 } };
|
|
const p: ProjectFile = { schemaVersion: SCHEMA_VERSION, project: { name: '', bridge: '', client: '', author: '', crs: '', memo: '' }, source: { fileName: '', sheets: [] }, history: [], piers: [{ id: 'P1', input: next, defaults: input, draft: next }] };
|
|
expect(parseProject(serializeProject(p))).toEqual(p);
|
|
expect(changedKeys(input, next)).toEqual(['castInPlaceShape']);
|
|
const invalid = JSON.parse(serializeProject(p));
|
|
invalid.piers[0].draft.castInPlaceShape.betaLengthMm = 0;
|
|
expect(() => parseProject(JSON.stringify(invalid))).toThrow('현장타설');
|
|
});
|
|
it('permits negative elevations but rejects nonfinite elevations and invalid switches', () => {
|
|
expect(castShapeIssues({ bedrockEl: -20, casingBottomEl: -30 })).toEqual([]);
|
|
expect(castShapeIssues({ casingOn: 1 })).toHaveLength(1);
|
|
expect(castShapeIssues({ clayBottomEl: Infinity })).toHaveLength(1);
|
|
});
|
|
});
|