feat(b06,b08): 다단 벽 몸이 성토 폐회로에도 든 겹침을 줄 사유로 드러냄 — 고치지 않음(2026-09-06 확정 「구조물 면적 안 뺌」 · 판정은 사용자)
서버 Node 가 벽 몸 ∩ 성토 폐회로 넓이를 재 design.extra_walls[].fill_overlap_m2 로 남기고 B08 다단 줄 비고에 「벽 몸 N㎡ × 연장 ≈ ㎥ 두 번 셈」 · 258.12 1단 1.335㎡ × 10m ≈ 13.3㎥ · 금액 변화 0 · 미확정 단은 안 붙임 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
This commit is contained in:
@@ -72,12 +72,10 @@ function interpolator(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 구조물이 선 뒤의 절·성토 면적. 트림 바깥은 구조물이 그리는 폴리라인을 따르고, 그 선이
|
||||
* 없으면 트림 경계 표고에서 끊어 **지반선에 붙인다**(그 바깥은 손대지 않은 원지반이라
|
||||
* 면적이 0이 된다).
|
||||
*/
|
||||
export function computeStructureAreas(input: StructureAreaInput): StructureAreaResult | null {
|
||||
/** 폐회로의 위(실제로 그려지는 설계선)·아래(지반선) 보간기 — 면적과 벽 겹침이 같은 선을 봄. */
|
||||
function drawnProfile(
|
||||
input: StructureAreaInput,
|
||||
): { drawnZ: (offset: number) => number; ground: (offset: number) => number } | null {
|
||||
const design = interpolator(
|
||||
input.designLine.map((point) => ({ offset: point.offset_m, elevation: point.elevation_m })),
|
||||
);
|
||||
@@ -117,6 +115,19 @@ export function computeStructureAreas(input: StructureAreaInput): StructureAreaR
|
||||
}
|
||||
return design(offset);
|
||||
};
|
||||
return { drawnZ, ground };
|
||||
}
|
||||
|
||||
/**
|
||||
* 구조물이 선 뒤의 절·성토 면적. 트림 바깥은 구조물이 그리는 폴리라인을 따르고, 그 선이
|
||||
* 없으면 트림 경계 표고에서 끊어 **지반선에 붙인다**(그 바깥은 손대지 않은 원지반이라
|
||||
* 면적이 0이 된다).
|
||||
*/
|
||||
export function computeStructureAreas(input: StructureAreaInput): StructureAreaResult | null {
|
||||
const profile = drawnProfile(input);
|
||||
if (!profile) return null;
|
||||
const { drawnZ, ground } = profile;
|
||||
const { trim } = input;
|
||||
|
||||
// 적분 격자 = 지반 샘플 ∪ 설계선 꼭짓점 ∪ 트림 경계 ∪ 구조물 폴리라인 꼭짓점.
|
||||
// 꺾이는 자리를 모두 넣어야 사다리꼴 적분이 모서리를 잘라먹지 않는다.
|
||||
@@ -154,3 +165,39 @@ export function computeStructureAreas(input: StructureAreaInput): StructureAreaR
|
||||
cutRockAreaM2: cutRock,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 벽 몸 도형(볼록 다각형) 중 **성토 폐회로 안에 든 넓이**(㎡).
|
||||
* 폐회로는 구조물 면적을 안 뺌(2026-09-06 확정)이라 벽 몸이 성토에도 셈 — 고치지 않고 그 겹침을
|
||||
* 재서 수량 사유로 올림(2026-09-14 브레인). 세로줄로 잘라 [max(몸 밑, 지반), min(몸 위, 설계선)] 을 쌓음.
|
||||
*/
|
||||
export function wallBodyInFillM2(
|
||||
points: Array<{ offset: number; elevation: number }>,
|
||||
input: StructureAreaInput,
|
||||
steps = 400,
|
||||
): number {
|
||||
const profile = drawnProfile(input);
|
||||
if (!profile || points.length < 3) return 0;
|
||||
const offsets = points.map((point) => point.offset);
|
||||
const lo = Math.min(...offsets);
|
||||
const hi = Math.max(...offsets);
|
||||
if (!(hi > lo)) return 0;
|
||||
const dx = (hi - lo) / steps;
|
||||
let area = 0;
|
||||
for (let index = 0; index < steps; index += 1) {
|
||||
const x = lo + (index + 0.5) * dx;
|
||||
const hits: number[] = [];
|
||||
points.forEach((p, k) => {
|
||||
const q = points[(k + 1) % points.length];
|
||||
if ((p.offset - x) * (q.offset - x) > 0 || Math.abs(q.offset - p.offset) < 1e-12) return;
|
||||
hits.push(
|
||||
p.elevation + ((q.elevation - p.elevation) * (x - p.offset)) / (q.offset - p.offset),
|
||||
);
|
||||
});
|
||||
if (hits.length < 2) continue;
|
||||
const top = Math.min(Math.max(...hits), profile.drawnZ(x));
|
||||
const bottom = Math.max(Math.min(...hits), profile.ground(x));
|
||||
if (top > bottom) area += (top - bottom) * dx;
|
||||
}
|
||||
return area;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user