fix(B06): 조정 단위를 관 길이 1m로·벽 형상 불변·사면 역전 차단

지적 3건:
- 우측(유출) 기슭막이만 좌우로 밀 때 단면이 변했다. 0.1m씩 미니 관 길이 정수의
  끝수를 벽 폭이 흡수해 0.45→0.90까지 뚱뚱해졌다. 조정 단위를 **관 길이 1m**로
  바꾸고, 조정창으로 옮긴 벽은 폭을 절대 건드리지 않게 했다. 자리만 옮기면 정수
  길이가 성립하므로 형상이 그대로다(검증: 두께 0.450 고정, L 1m씩 증감).
- 성토 사면이 역전되는 자리가 있었다. 벽 상단이 노견보다 높아지면 사면선이 위로
  올라간다 — 최소 성토고 0.3m(FILL_MIN_RISE_M)를 두고 그 앞에서 멈춘다.
- 이동이 노견을 넘어 도로 반대편까지 갔다. 벽은 제 노견 바깥에만 서게 막았다.

조정창은 "관 길이 8m · 바깥 2.0m"처럼 관 길이를 같이 적는다.
700줄 유지를 위해 clampWallOffset·cutLength·outletSlopeFactory를 _Solve로 옮겼다.

검증: 이동 ±5m 20케이스 역전 0건·두께 고정, 275.71 유출 이동 11↔7m 정상,
계획고 스윕 24케이스 회귀 문제 0. tsc 통과, pytest 148 pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-21 16:50:45 +09:00
co-authored by Claude Opus 5
parent 4eed191d2a
commit 8564e118d6
7 changed files with 148 additions and 61 deletions
+5 -1
View File
@@ -212,6 +212,8 @@ export function createCrossSectionCard(
let activeRevet: RevetKey | null = revetOffset?.selectedFor(section) ?? null;
let setRevetActive: RevetHighlightSetter = () => undefined;
let showRevetControl: (visible: boolean) => void = () => undefined;
/** 지금 그린 관 길이(m) — 조정창이 "관 길이 8m"으로 적는다. 배수관 측점이 아니면 null. */
let culvertPipeLengthM: number | null = null;
/**
* 기슭막이를 고른다. 구조물 선택과 절·성토 면적 강조는 **같은 레벨**이라 하나를
* 고르면 다른 하나는 풀린다(2026-08-21 사용자). 또 구조물을 고르면 그 **측점 카드도
@@ -450,7 +452,7 @@ export function createCrossSectionCard(
toggleArea,
);
// 배수관 세트 기하를 먼저 계산한다 — 설계선이 기슭막이 밖 성토 경사를 끊는 데 쓴다.
const culvertLayout = computeCulvertLayout(
const culvertLayout: ReturnType<typeof computeCulvertLayout> = computeCulvertLayout(
section,
sourceSamples,
revetOffset
@@ -481,6 +483,7 @@ export function createCrossSectionCard(
);
}
// 배수관 측점 세트(배관·기슭막이·보호공) — 기존 도형 위에 추가만 한다(2026-08-19).
culvertPipeLengthM = culvertLayout?.pipe.lengthM ?? null;
if (culvertLayout) {
setRevetActive = appendCulvertOverlay(
plotLayer,
@@ -571,6 +574,7 @@ export function createCrossSectionCard(
nudge: (key, screenDeltaM) =>
revetOffset?.adjust(section.chainage_m, key, screenDeltaM * outwardOf(key)),
reset: (key) => revetOffset?.reset(section.chainage_m, key),
pipeLengthM: () => culvertPipeLengthM,
close: () => {
if (activeRevet) toggleRevet(activeRevet);
},