fix(평면): 평면 비탈 끝 · B07 계획평면도 = 벽 측점 횡단 최종 모양이 땅과 만나는 점 (PLAN 578-2 · 요구 438 · 439)
- B06 STRUCTURE_ROW_KEYS · 서버 _AREA_KEYS 에 slope_end_left_m · slope_end_right_m — 벽 트림 측점만 · 트림 사면 → 단마다 벽 앞 → 사이 성토부선 윤곽이 처음 땅에 닿는 오프셋 - 평면 발자국 finalSlopeEnd · B07 final_slope_end(짝) — 얹은 값 먼저 · 없으면 설계선 끝(구조물 없는 측점 그대로) - 시험 test_578_2_plan_slope_end 새로 · test_346_2 거울에 벽 측점 줄 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -73,6 +73,8 @@ _AREA_KEYS = (
|
||||
"box_ground_slope_pct",
|
||||
"box_length_m",
|
||||
"ford_pipe_length_m",
|
||||
"slope_end_left_m",
|
||||
"slope_end_right_m",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import type {
|
||||
import { computeFordLayout, fordAdjustOf } from "./B06_Section_Cross_Ford_Geom";
|
||||
import type { FordLayout } from "./B06_Section_Cross_Ford_Geom";
|
||||
import { computeRevetmentLayout } from "./B06_Section_Cross_Revetment_Geom";
|
||||
import type { RevetmentLayout } from "./B06_Section_Cross_Revetment_Geom";
|
||||
|
||||
/** 같은 측점으로 볼 누가거리 오차(m) — 정본 반올림 자릿수보다 크게 잡는다. */
|
||||
const CHAINAGE_TOLERANCE_M = 0.02;
|
||||
@@ -163,6 +164,9 @@ export const STRUCTURE_ROW_KEYS = [
|
||||
"box_length_m",
|
||||
// B08 자재 집계표 세월교 관 한 련 길이(131) — 그린 관 길이 1 m 올림 · 같은 길로 실음.
|
||||
"ford_pipe_length_m",
|
||||
// 평면 비탈 끝(578-2) — 벽이 선 쪽 횡단 최종 모양이 땅과 만나는 오프셋 · 평면 발자국 · B07 계획평면도가 읽음.
|
||||
"slope_end_left_m",
|
||||
"slope_end_right_m",
|
||||
] as const;
|
||||
|
||||
/** 박스 두 끝(구체 끝) 원지반 경사(%) — 유입 − 유출 ÷ 수평거리. 본체는 늘 수평이라 원지반으로(110). */
|
||||
@@ -191,6 +195,28 @@ function fordPipeLengthM(ford: FordLayout): number | null {
|
||||
return length > 0 ? Math.ceil(length - 1e-6) : null;
|
||||
}
|
||||
|
||||
/** 벽(옹벽 · 흙막이 · 돌쌓기)이 선 쪽 — 구조물 처리 뒤 횡단이 땅과 만나는 오프셋(578-2).
|
||||
* 윤곽 = 트림 사면(노견 → 벽 이음) → 단마다 벽 앞(성토 위 이음 → 전면 발끝) · 사이 성토부선(3D 실루엣 차례).
|
||||
* 위에서 내려와 처음 땅에 닿는 자리 · 끝까지 안 닿으면(뜬 벽) 마지막 발끝 · 노견부터 땅 밑이면 null. */
|
||||
function wallSlopeEnd(section: CrossSection, own: RevetmentLayout): number | null {
|
||||
const groundAt = groundInterpolator(section.samples);
|
||||
const trim = own.designTrim;
|
||||
const chain = [...((own.side === "left" ? trim.maxSlope : trim.minSlope)?.points ?? [])];
|
||||
const segments = own.fillSegments.filter((segment) => segment.kind !== "cut");
|
||||
own.walls.forEach((wall, index) => {
|
||||
chain.push(wall.fillTop, wall.bottomFront, ...(segments[index]?.points ?? []));
|
||||
});
|
||||
if (!groundAt || chain.length < 2) return null;
|
||||
let [prev, above] = [chain[0], chain[0].elevation - groundAt(chain[0].offset)];
|
||||
if (above <= 0) return null;
|
||||
for (const point of chain.slice(1)) {
|
||||
const now = point.elevation - groundAt(point.offset);
|
||||
if (now <= 0) return prev.offset + ((point.offset - prev.offset) * above) / (above - now);
|
||||
[prev, above] = [point, now];
|
||||
}
|
||||
return prev.offset;
|
||||
}
|
||||
|
||||
/** 폐회로 면적 입력(설계선·지반선·트림) — 트림이나 설계선이 모자라면 null. */
|
||||
function areaInputOf(section: CrossSection, layouts: StoredLayouts) {
|
||||
const trim = trimOfLayouts(layouts);
|
||||
@@ -254,6 +280,11 @@ function areaRowOf(
|
||||
const fordLength =
|
||||
layouts.ford && isOwner(section.ford?.chainage_m) ? fordPipeLengthM(layouts.ford) : null;
|
||||
if (fordLength !== null) ownRow.ford_pipe_length_m = fordLength;
|
||||
// 벽 트림이 그려지는 측점만(관 · 세월교 · BOX 트림이 앞서면 그쪽 모양 · 578-2 범위 밖)
|
||||
const own = layouts.own;
|
||||
const wallEnd =
|
||||
own && trimOfLayouts(layouts) === own.designTrim ? wallSlopeEnd(section, own) : null;
|
||||
if (own && wallEnd !== null) ownRow[`slope_end_${own.side}_m`] = Number(wallEnd.toFixed(4));
|
||||
const pipeRow = Object.keys(ownRow).length > 1 ? ownRow : null;
|
||||
const areaInput = areaInputOf(section, layouts);
|
||||
if (!areaInput) return pipeRow;
|
||||
|
||||
Reference in New Issue
Block a user