fix(B06): 다단 구간값 기본을 소유 벽 연장 상속으로 바꾼다

고정 10m(전/후 5)를 기본으로 두니 소유 벽 연장(유출 12m·유입 21.5m)보다 짧아,
+5.9m 떨어진 링크 카드에서 기준벽은 서는데 다단만 사라졌다. 손대기 전에는 기준벽
연장을 그대로 따르고, 손으로 잡은 단만 그 값으로 고정한다.

- tierSpanOf: 저장값 → 소유 벽 연장(유출측은 유출 벽, 유입측은 유입 벽/집수정)
  → 마지막으로 10m(5/5). 그리기·링크 판정·조정창 표시가 이 함수 하나를 쓴다

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-29 11:01:07 +09:00
co-authored by Claude Opus 5
parent 36d09dbf00
commit 213737289e
2 changed files with 22 additions and 18 deletions
@@ -139,12 +139,18 @@ export interface CulvertLink {
/**
* 다단 한 단의 구간값 — 단별로 따로 잡은 값(`design.extra_spans`)이 있으면 그것,
* 없으면 고정 기본 10m(5/5). 기준벽 연장을 상속하지 않는다(2026-08-29 사용자).
* **없으면 소유 벽 연장을 상속**한다(2026-08-30 사용자: 손대기 전에는 기준벽과 같이
* 옆 측점에 서야 한다). 고정 기본 10m(5/5)는 소유 벽 제원조차 없을 때의 마지막 값이다.
* 손으로 한 번 잡은 단은 그 값으로 고정된다 — 계곡부·능선부에서 단마다 연장이 다르다.
*/
export function tierSpanOf(section: CrossSection, key: string): StructureSpan {
const stored = section.design?.extra_spans?.[key];
if (!stored) return { ...EXTRA_SPAN_DEFAULT };
return { beforeM: Math.max(stored.before_m, 0), afterM: Math.max(stored.after_m, 0) };
if (stored) return { beforeM: Math.max(stored.before_m, 0), afterM: Math.max(stored.after_m, 0) };
// 유출측 다단(extra…)은 유출 벽, 유입측 다단(bextra…)은 유입 벽(집수정이면 집수정)
// 연장을 따른다 — 그 벽에 딸린 성토부 계단이기 때문이다.
const spec = key.startsWith("bextra") ? section.culvert?.inlet : section.culvert?.outlet;
if (!spec) return { ...EXTRA_SPAN_DEFAULT };
return spec.structure === "집수정" ? basinSpanOfSpec(spec) : revetSpanOfSpec(spec);
}
/** 구조물이 종방향으로 덮는 범위(기준측점 전/후 m) — 기슭막이·집수정·다단 중 큰 값. */
@@ -24,7 +24,7 @@ import type {
StructureSpanControl,
} from "./B06_Section_UI_Cross_View";
import * as CulvertConst from "./B06_Section_UI_Cross_Culvert_Const";
import { culvertOwnerFor } from "./B06_Section_UI_Cross_Culvert_Wire";
import { culvertOwnerFor, tierSpanOf } from "./B06_Section_UI_Cross_Culvert_Wire";
import { createCulvertOptionWriter } from "./B06_Section_Api_Culvert_Options";
import { createBodyControls } from "./B06_Section_UI_Page_Ford_Controls";
import { createLinkFlagSession } from "./B06_Section_UI_Page_Link_Session";
@@ -552,14 +552,17 @@ export function createStationControls(deps: StationControlDeps): StationControls
}
}
/** 정본(design)에 남은 단별 구간값 — 세션에 없을 때의 다음 후보. */
const storedTierSpan = (owner: CrossSection, wall: string): SpanValues | null => {
const stored = owner.design?.extra_spans?.[wall];
if (!stored) return null;
/**
* 세션에 없을 때의 단별 구간값 — 정본(`design.extra_spans`) → **소유 벽 연장 상속**
* 순서다(2026-08-30 사용자: 손대기 전에는 기준벽과 같이 옆 측점에 서야 한다).
* 상속 규칙은 그리기·링크 판정이 쓰는 `tierSpanOf`와 같은 함수 하나로 맞춘다.
*/
const storedTierSpan = (owner: CrossSection, wall: string): SpanValues => {
const span = tierSpanOf(owner, wall);
return {
lengthM: stored.length_m,
beforeM: stored.before_m,
afterM: stored.after_m,
lengthM: Math.round((span.beforeM + span.afterM) * 10) / 10,
beforeM: Math.round(span.beforeM * 10) / 10,
afterM: Math.round(span.afterM * 10) / 10,
};
};
@@ -582,18 +585,13 @@ export function createStationControls(deps: StationControlDeps): StationControls
ownerOf,
tierValuesFor: (section, key) => {
const owner = ownerOf(section) ?? section;
return (
extraSpans.get(spanKeyOf(owner.chainage_m, key)) ??
storedTierSpan(owner, key) ??
CulvertConst.EXTRA_SPAN_DEFAULT
);
return extraSpans.get(spanKeyOf(owner.chainage_m, key)) ?? storedTierSpan(owner, key);
},
updateTier: (section, key, patch) => {
const owner = ownerOf(section);
if (!owner) return;
const mapKey = spanKeyOf(owner.chainage_m, key);
const current =
extraSpans.get(mapKey) ?? storedTierSpan(owner, key) ?? CulvertConst.EXTRA_SPAN_DEFAULT;
const current = extraSpans.get(mapKey) ?? storedTierSpan(owner, key);
const next = CulvertConst.applySpanPatch(current, patch);
extraSpans.set(mapKey, next);
persistExtraSpans();