Files
Aislo/B06_Section/B06_Section_UI_Page_Span_Control.ts
T
eomsangdonandClaude Opus 5.5 e51dd2a801 fix(450 B06 측점 갈아끼움): MC 곡선 열쇠 이음 · 자리 같은 단면 서버 표본 유지 · 구간값 죽은 길 걷음 · 표 위 CAD 한도
- 433-4 갈아끼움이 curveMoves 거꾸로 맵으로 옛 MC 횡단을 이어 받음 — 설계 · 측점 id · moves 짝 남음
- 433-11 자리 · 축 같은 앞 단면은 지반 표본 · 상단측 · 중심고 그대로 — 덧 측점 넣기 · 빼기 뒤 누가토량 = 서버
- 433-19 Span_Control.update(culvertopt 큐) 걷음 — 구간 줄은 다단 벽만
- 433-20 표 위 CAD 요소 2000 넘음 = 초안에 안 씀 · 알림(서버 max_length 짝)
- 시험 test_433_4 · test_433_11 · test_433_19_20 새로 · 346_2 단언 · 440 헬퍼 로더 맞춤

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 21:34:51 +09:00

96 lines
4.3 KiB
TypeScript

/* =============================================================================
* B06_Section_UI_Page_Span_Control.ts
* 구조물 **구간값(길이·전/후)** 제어 — 다단 단별 구간값과 유입·유출·집수정 구간값.
*
* `B06_Section_UI_Page_Station_Controls` 에서 떼어냈다(700줄 제한, 2026-09-02).
* 세션 맵(`extraSpans`)과 캐시(design)를 함께 만지는 자리라 한 덩어리로 옮겼다.
* 기준벽 · 집수정 구간값은 폼 칸(관 초안)이 정본 — 여기는 읽기만(433-19 · 444).
* ========================================================================== */
import type { CrossDesign, CrossSection, StoredWallSpan } from "./B06_Section_Api_Fetch";
import {
tierSpanOf,
type SpanValues,
type StructureSpanControl,
} from "./B06_Section_UI_Cross_Culvert_Wire";
import * as CulvertConst from "./logic/B06_Section_UI_Cross_Culvert_Const";
/** 구간값 제어가 페이지·상위 제어기에서 받아 쓰는 창구. */
export interface SpanControlDeps {
detail: () => { cross_sections: CrossSection[] } | null;
refreshCard: (chainageM: number) => void;
/** 연동 구조물의 소유 측점 — 값은 소유 측점 하나에만 담는다. */
ownerOf: (section: CrossSection) => CrossSection | null;
/** 세션 맵(누가거리:벽키 → 구간값)과 저장. */
extraSpans: Map<string, SpanValues>;
persistExtraSpans: () => void;
patchCachedDesign: (chainageM: number, patch: Partial<CrossDesign>) => void;
}
/** 구간값 제어기와 단별 구간값 조회를 만든다. */
export function createSpanControl(deps: SpanControlDeps): {
control: StructureSpanControl;
/** 이 측점의 단별 구간값 전부(세션 우선) — 상위 제어기가 payload에 실을 때 쓴다. */
tierSpansOf: (owner: CrossSection) => Record<string, StoredWallSpan>;
spanKeyOf: (chainageM: number, wall: string) => string;
} {
const { extraSpans, persistExtraSpans, patchCachedDesign, ownerOf } = deps;
const spanKeyOf = (chainageM: number, wall: string): string => `${chainageM.toFixed(2)}:${wall}`;
const storedTierSpan = (owner: CrossSection, wall: string): SpanValues => {
const span = tierSpanOf(owner, wall);
return {
lengthM: Math.round((span.beforeM + span.afterM) * 10) / 10,
beforeM: Math.round(span.beforeM * 10) / 10,
afterM: Math.round(span.afterM * 10) / 10,
};
};
/** 이 측점의 단별 구간값 전부(세션 우선) — 캐시·payload에 실을 모양으로. */
const tierSpansOf = (owner: CrossSection): Record<string, StoredWallSpan> => {
const prefix = `${owner.chainage_m.toFixed(2)}:`;
const result: Record<string, StoredWallSpan> = { ...(owner.design?.extra_spans ?? {}) };
extraSpans.forEach((value, key) => {
if (!key.startsWith(prefix)) return;
result[key.slice(prefix.length)] = {
length_m: value.lengthM,
before_m: value.beforeM,
after_m: value.afterM,
};
});
return result;
};
const control: StructureSpanControl = {
ownerOf,
tierValuesFor: (section, key) => {
const owner = ownerOf(section) ?? section;
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);
const next = CulvertConst.applySpanPatch(current, patch);
extraSpans.set(mapKey, next);
persistExtraSpans();
// 캐시(design)에도 얹는다 — 링크 판정·3D가 순수 함수로 이 값을 읽는다.
patchCachedDesign(owner.chainage_m, { extra_spans: tierSpansOf(owner) });
// 연장이 바뀌면 링크되는 옆 측점이 달라진다 — 옛 연장·새 연장을 합친 구간만.
const reach = Math.max(current.beforeM, current.afterM, next.beforeM, next.afterM);
for (const other of deps.detail()?.cross_sections ?? []) {
if (Math.abs(other.chainage_m - owner.chainage_m) <= reach + 1e-9) {
deps.refreshCard(other.chainage_m);
}
}
},
valuesFor: (section, role) => {
const owner = ownerOf(section);
return owner ? CulvertConst.spanValuesOf(owner, role) : null;
},
};
return { control, tierSpansOf, spanKeyOf };
}