공용 파일(main.ts·index.html·style.css)에서 서로 얽혀 있어 한 커밋으로 묶음. - 지반: ① 형상 프로젝트 지반모델 관리창(생성·JSON 불러오기·교각별 비교·선택 적용), 프로젝트 JSON 내장 groundModel 추출, 교대 8행 통합 적용·공통지형 중복격자 제외 - 재료표·수량: 상단 ⑤ 메뉴·배근 바로가기, 결과 교각/탭 기억, 수량 워크북·정합 보정 - 랜딩: 옥성교 6교각 히어로 이미지·투영 강조점·좌→우 광선·지형 라운드/도로 연결 - 검증 스크립트(verify-project-ground/ground-abutments/okseong-user-files/quantity-*/landing-hero 등) 정본: DOC/04_실행/실행_*_2026-11-09_v0.1.00.md Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
51 lines
3.4 KiB
TypeScript
51 lines
3.4 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { abutGroundFromModel, applyProjectGround, groundInputChanged, sampleGroundGrid } from '../src/core/project-ground';
|
|
import type { GroundModelFile } from '../src/core/ground-model';
|
|
import { loadP1 } from './helpers';
|
|
|
|
const model: GroundModelFile = { schema: 2, generated: '', piers: [
|
|
{ id: 'P1', centerEl: -2, excelEl: 3, layers: [{ name: '암반', bottomEl: -10 }] },
|
|
{ id: 'P2', centerEl: null, excelEl: 5 },
|
|
] };
|
|
describe('프로젝트 지반 선택 적용', () => {
|
|
it('교대 IP2에서 4점을 보간하고 외곽 경계도 처리한다', () => {
|
|
const grid = { origin: [100, 200] as [number, number], step: 10, nx: 2, ny: 2, z: [10, 20, 30, 40] };
|
|
expect(sampleGroundGrid(grid, { n: 105, e: 205 })).toBe(25);
|
|
expect(sampleGroundGrid(grid, { n: 110, e: 210 })).toBe(40);
|
|
expect(sampleGroundGrid(grid, { n: 111, e: 205 })).toBeNull();
|
|
expect(sampleGroundGrid({ ...grid, z: [10, null, 30, 40] }, { n: 105, e: 205 })).toBeNull();
|
|
const result = abutGroundFromModel({ ...model, terrainGrid: grid }, { name: 'A1', ip2: { n: 105, e: 205 } });
|
|
expect(result.centerEl).toBe(25); expect(result.source).toBe('IP2 격자 보간'); expect(result.layers).toBeUndefined();
|
|
});
|
|
it('교대 개별 모델값이 있으면 null도 보간으로 대체하지 않는다', () => {
|
|
const result = abutGroundFromModel({ ...model, piers: [{ id: 'A1', centerEl: null, excelEl: 10, layers: [{ name: '암반', bottomEl: -10 }] }] }, { name: 'A1', ip2: { n: 0, e: 0 } });
|
|
expect(result.centerEl).toBeNull(); expect(result.source).toBe('JSON 개별값'); expect(result.layers).toHaveLength(1);
|
|
});
|
|
it('지반고만 적용하면 기존 지층과 N값을 보존한다', () => {
|
|
const p = { ...loadP1(), strataRows: [{ name: '기존', bottomEl: -20, nValue: '50/5' }] };
|
|
const next = applyProjectGround(p, model, { ground: true, layers: false });
|
|
expect(next.groundEl).toBe(-2); expect(next.strataRows).toEqual(p.strataRows);
|
|
expect(p.groundEl).not.toBe(-2); expect(groundInputChanged(p, next)).toBe(true);
|
|
});
|
|
it('지층만 적용하면 지반고와 다른 입력을 보존하고 모델을 복사한다', () => {
|
|
const p = loadP1(), next = applyProjectGround(p, model, { ground: false, layers: true });
|
|
expect(next.groundEl).toBe(p.groundEl); expect(next.copingLengthMm).toBe(p.copingLengthMm);
|
|
expect(next.strataRows).toEqual(model.piers[0].layers); expect(next.strataRows![0]).not.toBe(model.piers[0].layers![0]);
|
|
});
|
|
it('모델 누락과 범위 밖은 기존 값을 보존한다', () => {
|
|
for (const name of ['P2', 'P3']) {
|
|
const p = { ...loadP1(), name, strataRows: [{ name: '기존', bottomEl: -20 }] };
|
|
expect(applyProjectGround(p, model, { ground: true, layers: true })).toEqual(p);
|
|
}
|
|
});
|
|
it('빈 지층으로 기존 지층을 지우지 않는다', () => {
|
|
const p = { ...loadP1(), strataRows: [{ name: '기존', bottomEl: -20 }] };
|
|
const empty = { ...model, piers: [{ ...model.piers[0], layers: [] }] };
|
|
expect(applyProjectGround(p, empty, { ground: false, layers: true })).toEqual(p);
|
|
});
|
|
it('지반고·지층이 같으면 지반 재생성 대상이 아니다', () => {
|
|
const p = loadP1(); expect(groundInputChanged(p, { ...p, copingLengthMm: p.copingLengthMm + 100 })).toBe(false);
|
|
expect(groundInputChanged(p, { ...p, strataRows: [{ name: '층', bottomEl: -5 }] })).toBe(true);
|
|
});
|
|
});
|