feat(ground): 지반·지층 card inputs, layer rows, water level, section drawing
- types/ground.ts: 위치 (육상·하상·해상), 지하수위 고려 and EL, 지층 행 (name and 하단 EL, from the top down). Checks G01 (layer order), G02 (water level range), G03 (layer name). - The 지반고 still feeds the soil cover check; the layer rows and water level are inputs and do not change the 3D model. - view/ground-svg.ts: the 삽도 drawn from the inputs and the generated footings and piles on one EL scale. - 일괄 적용 copies 위치 and 지하수위 to the checked piers (속성 복사 item, off by default). Changes list shows arrays and booleans as text. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0e70cfb969
commit
16ab317094
8 files changed
+400
-6
No files matched your search
@@ -0,0 +1,105 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { buildPier } from '../src/core/geometry';
|
||||
import { validateGround } from '../src/core/ground';
|
||||
import { changedKeys } from '../src/core/changes';
|
||||
import { COPY_ITEMS, applyCopy, planCopy } from '../src/core/copy';
|
||||
import type { Issue, PierInput } from '../src/core/types';
|
||||
import { groundSectionSvg } from '../src/view/ground-svg';
|
||||
import { loadP1 } from './helpers';
|
||||
|
||||
const base = loadP1();
|
||||
|
||||
function withGround(extra: Partial<PierInput>): PierInput {
|
||||
return { ...base, groundEl: 28.931, ...extra };
|
||||
}
|
||||
|
||||
function ground(input: PierInput): string[] {
|
||||
const issues: Issue[] = [];
|
||||
validateGround(input, issues);
|
||||
return issues.map((i) => i.code);
|
||||
}
|
||||
|
||||
const GOOD_ROWS = [
|
||||
{ name: '매립토', bottomEl: 27.5 },
|
||||
{ name: '풍화토', bottomEl: 22 },
|
||||
{ name: '연암', bottomEl: 10 },
|
||||
];
|
||||
|
||||
describe('지반·지층 입력 검사 (G01–G03)', () => {
|
||||
it('no issues for a well-ordered set of rows and a water level inside the range', () => {
|
||||
expect(ground(withGround({ strataRows: GOOD_ROWS, groundWaterOn: true, groundWaterEl: 25 }))).toEqual([]);
|
||||
});
|
||||
|
||||
it('G01: a layer whose bottom EL is not below the boundary above it', () => {
|
||||
const rows = [{ name: '매립토', bottomEl: 27.5 }, { name: '풍화토', bottomEl: 28 }];
|
||||
expect(ground(withGround({ strataRows: rows }))).toEqual(['G01']);
|
||||
});
|
||||
|
||||
it('G01: the first layer must be below the ground EL', () => {
|
||||
expect(ground(withGround({ strataRows: [{ name: '매립토', bottomEl: 29 }] }))).toEqual(['G01']);
|
||||
});
|
||||
|
||||
it('G03: a layer name outside the strata list', () => {
|
||||
expect(ground(withGround({ strataRows: [{ name: '점토', bottomEl: 27 }] }))).toEqual(['G03']);
|
||||
});
|
||||
|
||||
it('G02: the water level above the ground, below the lowest layer, or missing while on', () => {
|
||||
expect(ground(withGround({ strataRows: GOOD_ROWS, groundWaterOn: true, groundWaterEl: 29 }))).toEqual(['G02']);
|
||||
expect(ground(withGround({ strataRows: GOOD_ROWS, groundWaterOn: true, groundWaterEl: 5 }))).toEqual(['G02']);
|
||||
expect(ground(withGround({ groundWaterOn: true }))).toEqual(['G02']);
|
||||
});
|
||||
|
||||
it('the water level is not checked when it is off', () => {
|
||||
expect(ground(withGround({ strataRows: GOOD_ROWS, groundWaterOn: false, groundWaterEl: 99 }))).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('일괄 적용 (속성 복사 · 위치·지하수위)', () => {
|
||||
it('copies only the position and the water level, not the layer rows', () => {
|
||||
const source = withGround({ groundPosition: '하상', groundWaterOn: true, groundWaterEl: 25, strataRows: GOOD_ROWS });
|
||||
const target = withGround({ groundPosition: '육상', strataRows: [] });
|
||||
const plan = planCopy(source, target, ['ground-position']);
|
||||
expect(plan.changes.map((c) => c.key).sort()).toEqual(['groundPosition', 'groundWaterEl', 'groundWaterOn']);
|
||||
const next = applyCopy(target, plan);
|
||||
expect(next.groundPosition).toBe('하상');
|
||||
expect(next.strataRows).toEqual([]);
|
||||
});
|
||||
|
||||
it('the item is off by default, so the existing 속성 복사 defaults do not change', () => {
|
||||
expect(COPY_ITEMS['ground-position'].defaultOn).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('변경 추적 (지반·지층 키)', () => {
|
||||
it('a changed layer row or water level is reported as a change', () => {
|
||||
const a = withGround({ strataRows: GOOD_ROWS });
|
||||
const b = withGround({ strataRows: [...GOOD_ROWS, { name: '경암', bottomEl: 5 }] });
|
||||
expect(changedKeys(a, b)).toEqual(['strataRows']);
|
||||
expect(changedKeys(a, { ...a, groundWaterEl: 20 })).toEqual(['groundWaterEl']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('단면 그림 (삽도)', () => {
|
||||
const model = buildPier(base);
|
||||
|
||||
it('shows the ground EL and one band per layer row, with the 연암 top line', () => {
|
||||
const svg = groundSectionSvg(withGround({ strataRows: GOOD_ROWS }), model);
|
||||
expect(svg).toContain('지반고 28.931');
|
||||
expect(svg).toContain('연암층 상면 EL 22.000');
|
||||
expect((svg.match(/fill="#(EADFC8|DCCBA8|C9B38D)"/g) ?? []).length).toBeGreaterThanOrEqual(3);
|
||||
});
|
||||
|
||||
it('draws the water level only when it is on and has a value', () => {
|
||||
expect(groundSectionSvg(withGround({ strataRows: GOOD_ROWS, groundWaterOn: true, groundWaterEl: 25 }), model)).toContain('지하수위 25.000');
|
||||
// The dashed water line and its label are absent (the aria-label always names 지하수위).
|
||||
const off = groundSectionSvg(withGround({ strataRows: GOOD_ROWS, groundWaterOn: false, groundWaterEl: 25 }), model);
|
||||
expect(off).not.toContain('stroke-dasharray="6 4"');
|
||||
expect(off).not.toContain('지하수위 25');
|
||||
});
|
||||
|
||||
it('with no layer rows, the ground band still fills the drawing down to its bottom', () => {
|
||||
const svg = groundSectionSvg(withGround({}), model);
|
||||
expect(svg).toContain('<svg');
|
||||
expect(svg).toContain('지반고 28.931');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user