fix(B06): 인접 측점에 남던 집수정 트림을 지워 성토선을 살린다

trimLinkedLayout이 링크(인접) 측점 카드에서 구조물 도형만 지우고, 그 구조물이
만든 설계선 트림(designTrim)과 성토부선은 스프레드로 통과시켰다. 집수정은 조작량이
0이면 대체 사면선(minSlope/maxSlope)도 없어서, 구조물은 안 그려지는데 설계선만
노견 밖에서 끊겼다 — 4측점(80.0m, 관 84.3m)·14측점(280.0m, 관 275.71m) 성토선 삭제.

- keepTrimSides(): 살아남지 못한 쪽의 트림을 ∓Infinity로 되돌리고 그쪽
  Elevation·Slope를 뺀다. 양쪽 다 없으면 designTrim = null.
- 집수정은 링크 측점에 서지 않으므로 유입 쪽 트림은 언제나 지운다.
- 연장 밖 유출 벽의 outletFill, 집수정에 딸린 basinFill도 함께 비운다.
- 유입이 min·max 어느 쪽인지는 관 양 끝 offset 대소로 가른다.

3D 무영향: trimLinkedLayout은 export되지 않고 유일한 호출자 computeCardCulvert는
B06 Cross_View만 import한다. B05 코리도는 computeCulvertLayout을 직접 부르며 소유
측점에서만 돈다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-26 19:58:05 +09:00
co-authored by Claude Opus 5
parent ce77e0735f
commit d857d3d48a
@@ -7,7 +7,7 @@
import type { CrossSection, SectionSample } from "./B06_Section_Api_Fetch";
import { computeCulvertLayout } from "./B06_Section_UI_Cross_Culvert";
import type { CulvertLayout, InletStructureChoice, RevetKey } from "./B06_Section_UI_Cross_Culvert";
import type { BasinAdjust } from "./B06_Section_UI_Cross_Culvert_Types";
import type { BasinAdjust, CulvertDesignTrim } from "./B06_Section_UI_Cross_Culvert_Types";
import {
basinSpanOfSpec,
materialLabel,
@@ -201,10 +201,36 @@ function spanCovers(span: StructureSpan, deltaM: number): boolean {
return deltaM < 0 ? -deltaM <= span.beforeM + 1e-9 : deltaM <= span.afterM + 1e-9;
}
/**
* 지운 구조물이 남긴 **설계선 트림**을 쪽별로 되돌린다(2026-08-26 사용자 지적 —
* 4·14 측점 성토선 삭제). 구조물은 안 그리는데 트림만 남으면 그 측점 설계선이
* 노견 밖에서 통째로 끊긴다 — 집수정은 대체 사면선(`minSlope`/`maxSlope`)도 조작량이
* 0이면 없어서 아무것도 안 남는다. `-/+Infinity` = 트림 없음(기하가 쓰는 초기값).
*/
function keepTrimSides(
trim: CulvertDesignTrim | null,
keepMin: boolean,
keepMax: boolean,
): CulvertDesignTrim | null {
if (!trim || (!keepMin && !keepMax)) return null;
if (keepMin && keepMax) return trim;
return {
minOffset: keepMin ? trim.minOffset : Number.NEGATIVE_INFINITY,
maxOffset: keepMax ? trim.maxOffset : Number.POSITIVE_INFINITY,
...(keepMin && trim.minElevation != null ? { minElevation: trim.minElevation } : {}),
...(keepMax && trim.maxElevation != null ? { maxElevation: trim.maxElevation } : {}),
...(keepMin && trim.minSlope ? { minSlope: trim.minSlope } : {}),
...(keepMax && trim.maxSlope ? { maxSlope: trim.maxSlope } : {}),
};
}
/**
* 링크 카드에서 **연장이 여기까지 오지 않는 요소**를 지운다(요소별 판정).
* 유입·유출 기슭막이는 각자 길이·전/후를 갖고, 집수정은 자기 연장(기본 2m·1/1)
* 만큼만 이어진다. 관은 한 측점 전용이라 그리기 단계에서 숨긴다(`hidePipe`).
*
* 도형뿐 아니라 그 구조물이 만든 **설계선 트림·성토부선까지 함께** 지운다 — 도형만
* 지우면 없는 구조물이 설계선을 끊는다(2026-08-26).
*/
function trimLinkedLayout(layout: CulvertLayout, link: CulvertLink): CulvertLayout {
const spec = layout.culvert;
@@ -212,16 +238,29 @@ function trimLinkedLayout(layout: CulvertLayout, link: CulvertLink): CulvertLayo
spec[side]?.structure === "집수정" ? basinSpanOfSpec(spec[side]) : revetSpanOfSpec(spec[side]);
const inletIn = spanCovers(spanOf("inlet"), link.deltaM);
const outletIn = spanCovers(spanOf("outlet"), link.deltaM);
// 집수정은 링크 측점에 서지 않으므로(아래 `basin: null`) 그 트림은 언제나 지운다.
const keepInlet = inletIn && !layout.basin;
// 좌표 규약 +offset = 좌측, outward=+1(좌) = trimMax — 유입이 어느 쪽 트림인지는
// 관 양 끝 offset 대소로 가른다(관은 노선을 가로지르므로 좌우가 갈린다).
const inletIsMax = layout.pipe.inlet.offset > layout.pipe.outlet.offset;
const empty = { segments: [], addable: false };
return {
...layout,
walls: layout.walls.filter((wall) => (wall.role === "inlet" ? inletIn : outletIn)),
// 다단(성토부)은 유출 벽에 딸린다.
extraWalls: outletIn ? layout.extraWalls : [],
outletFill: outletIn ? layout.outletFill : empty,
// 집수정은 **소유 측점 횡단도 하나에만** 선다(2026-08-24 사용자 확정) — 기슭막이와
// 달리 옆 측점으로 이어지는 구조물이 아니다. 계류측 다단도 집수정에 딸린 것이라
// 함께 뺀다. 3D에서는 소유 측점 하나가 자기 길이(기본 2m)만큼 스윕한다.
// 달리 옆 측점으로 이어지는 구조물이 아니다. 계류측 다단·성토부선도 집수정에 딸린
// 것이라 함께 뺀다. 3D에서는 소유 측점 하나가 자기 길이(기본 2m)만큼 스윕한다.
basinExtras: [],
basinFill: empty,
basin: null,
designTrim: keepTrimSides(
layout.designTrim,
inletIsMax ? outletIn : keepInlet,
inletIsMax ? keepInlet : outletIn,
),
};
}