refactor(472-5 5-A 나눔): 구조물 · 배수 판 셋 690줄 넘음 — 동작 같게 나눔
- B06_Section_UI_Page_Structures_Panel 700 → 타입을 _Panel_Types 로(다시 내보냄) - B05_Profile_UI_Structures_Panel 693 → 관 목록 받기(임시 · 고른 관 따라감)를 _Panel_Pipes 로 - B05_Profile_UI_Drainage_Panel 691 → 표시 토글 상태를 _Drainage_Show 로 - 시험 test_233_3 — 옮긴 줄을 새 파일에서 읽음(까닭: 같은 줄이 자리만 옮김) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -42,8 +42,8 @@ import { bindDrainageInteractions } from "./B05_Profile_UI_Drainage_Interact";
|
||||
import type { RouteSpanBand } from "./B05_Profile_UI_Drainage_Spans";
|
||||
import { drawDrainageScene } from "./B05_Profile_UI_Drainage_Render";
|
||||
import { computeZoomOutLimit } from "./B05_Profile_UI_Drainage_ZoomRange";
|
||||
import { mountDrainageShow } from "./B05_Profile_UI_Drainage_Show";
|
||||
import {
|
||||
mountDrainageToggles,
|
||||
DRAINAGE_LAYERS,
|
||||
fetchDrainageLayers,
|
||||
createProgressReporter,
|
||||
@@ -159,21 +159,17 @@ export function createDrainagePanel(callbacks: DrainagePanelCallbacks = {}): Dra
|
||||
// 평균 흐름 화살표 — B04가 계산해 둔 것을 그대로 받아 그린다(여기서 계산하지 않는다).
|
||||
let flowArrows: FlowArrow[] = [];
|
||||
let arrowSpacingM = 0;
|
||||
let showArrows = true;
|
||||
// 유입 집중점 — 관 자리를 판단하는 근거. 기본 꺼짐(마커가 관 마커와 겹쳐 읽기 어렵다).
|
||||
let hotspots: Array<{ chainage: number; area: number }> = [];
|
||||
let maxHotspotArea = 0;
|
||||
let showHotspots = false;
|
||||
/** 종단 Z 출처 — 세부유역이 갈리는 근거라 화면에서 확인 가능해야 한다. */
|
||||
let zSource = "";
|
||||
// 유역 안쪽 상류 세류망 — B04가 채택한 기준선을 그대로 받아 강조만 한다.
|
||||
let upstreamLines: Array<Array<[number, number]>> = [];
|
||||
let showUpstream = true;
|
||||
// 계획선 위 유입 강도 색칠 — B04 지도와 같은 색띠·같은 값(2026-08-01 사용자 지시).
|
||||
let strengthSamples: RoutePoint[] = [];
|
||||
let strength: Float64Array<ArrayBuffer> = new Float64Array(0);
|
||||
let maxStrength = 0;
|
||||
let showStrength = true;
|
||||
let scale = 1;
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
@@ -187,42 +183,12 @@ export function createDrainagePanel(callbacks: DrainagePanelCallbacks = {}): Dra
|
||||
let canvasHeight = 0;
|
||||
let canvasDpr = 0;
|
||||
|
||||
// 위성사진은 기본 꺼짐(2026-08-18 사용자 지시) — 어두운 사진이 유역 채움색을 묻는다.
|
||||
backgroundImage.hidden = true;
|
||||
mountDrainageToggles(layerButtons, {
|
||||
initial: {
|
||||
satellite: false,
|
||||
arrows: showArrows,
|
||||
strength: showStrength,
|
||||
hotspots: showHotspots,
|
||||
upstream: showUpstream,
|
||||
},
|
||||
onSatellite: (next) => {
|
||||
backgroundImage.hidden = !next;
|
||||
scheduleDraw();
|
||||
},
|
||||
onSheetLayer: (layer, next) => {
|
||||
if (next) activeLayers.add(layer);
|
||||
else activeLayers.delete(layer);
|
||||
scheduleDraw();
|
||||
},
|
||||
onArrows: (next) => {
|
||||
showArrows = next;
|
||||
scheduleDraw();
|
||||
},
|
||||
onStrength: (next) => {
|
||||
showStrength = next;
|
||||
legend.update(maxStrength, showStrength);
|
||||
scheduleDraw();
|
||||
},
|
||||
onHotspots: (next) => {
|
||||
showHotspots = next;
|
||||
scheduleDraw();
|
||||
},
|
||||
onUpstream: (next) => {
|
||||
showUpstream = next;
|
||||
scheduleDraw();
|
||||
},
|
||||
// 표시 토글(위성 · 도엽 · 화살표 · 강도 · 집중점 · 상류) — 상태는 `_Drainage_Show`.
|
||||
const show = mountDrainageShow(layerButtons, {
|
||||
backgroundImage,
|
||||
activeLayers,
|
||||
onStrength: (next) => legend.update(maxStrength, next),
|
||||
redraw: () => scheduleDraw(),
|
||||
});
|
||||
|
||||
function updateImageTransform(): void {
|
||||
@@ -260,17 +226,17 @@ export function createDrainagePanel(callbacks: DrainagePanelCallbacks = {}): Dra
|
||||
activeLayers,
|
||||
routeLayer,
|
||||
upstreamLines,
|
||||
showUpstream,
|
||||
showUpstream: show.upstream,
|
||||
strengthSamples,
|
||||
strength,
|
||||
maxStrength,
|
||||
showStrength,
|
||||
showStrength: show.strength,
|
||||
flowArrows,
|
||||
arrowSpacingM,
|
||||
showArrows,
|
||||
showArrows: show.arrows,
|
||||
hotspots,
|
||||
maxHotspotArea,
|
||||
showHotspots,
|
||||
showHotspots: show.hotspots,
|
||||
pipeEditor,
|
||||
pipeColor,
|
||||
markedChainage,
|
||||
@@ -352,7 +318,7 @@ export function createDrainagePanel(callbacks: DrainagePanelCallbacks = {}): Dra
|
||||
);
|
||||
strength = built.strength;
|
||||
maxStrength = built.maximum;
|
||||
legend.update(maxStrength, showStrength);
|
||||
legend.update(maxStrength, show.strength);
|
||||
maxHotspotArea = hotspots.reduce((max, spot) => (spot.area > max ? spot.area : max), 0);
|
||||
// 실제 사용된 배관 목록으로 마커를 동기화한다(유역 없는 관 포함).
|
||||
pipeEditor.setPipes(
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/* =============================================================================
|
||||
* B05_Profile_UI_Drainage_Show.ts
|
||||
* 배수유역 판 표시 토글 상태 — 위성 · 도엽 · 화살표 · 강도 · 집중점 · 상류 세류
|
||||
* (`B05_Profile_UI_Drainage_Panel.ts` 에서 나눔 · 472-5 · 동작 같음).
|
||||
* ========================================================================== */
|
||||
|
||||
import { mountDrainageToggles, type DrainageLayer } from "./B05_Profile_UI_Drainage_Parts";
|
||||
|
||||
/** 지금 켜진 표시 — 그리기가 읽음(토글이 고침). */
|
||||
export interface DrainageShow {
|
||||
arrows: boolean;
|
||||
/** 유입 강도 색칠 — B04 지도와 같은 색띠·같은 값(2026-08-01 사용자 지시). */
|
||||
strength: boolean;
|
||||
/** 유입 집중점 — 기본 꺼짐(마커가 관 마커와 겹쳐 읽기 어렵다). */
|
||||
hotspots: boolean;
|
||||
/** 유역 안쪽 상류 세류망 — B04 기준선 강조. */
|
||||
upstream: boolean;
|
||||
}
|
||||
|
||||
/** 토글 줄을 붙이고 상태를 돌려줌 — 위성사진은 기본 꺼짐(2026-08-18 사용자 지시). */
|
||||
export function mountDrainageShow(
|
||||
layerButtons: HTMLElement,
|
||||
deps: {
|
||||
backgroundImage: HTMLImageElement;
|
||||
activeLayers: Set<DrainageLayer>;
|
||||
onStrength: (show: boolean) => void;
|
||||
redraw: () => void;
|
||||
},
|
||||
): DrainageShow {
|
||||
const show: DrainageShow = { arrows: true, strength: true, hotspots: false, upstream: true };
|
||||
deps.backgroundImage.hidden = true;
|
||||
mountDrainageToggles(layerButtons, {
|
||||
initial: { satellite: false, ...show },
|
||||
onSatellite: (next) => {
|
||||
deps.backgroundImage.hidden = !next;
|
||||
deps.redraw();
|
||||
},
|
||||
onSheetLayer: (layer, next) => {
|
||||
if (next) deps.activeLayers.add(layer);
|
||||
else deps.activeLayers.delete(layer);
|
||||
deps.redraw();
|
||||
},
|
||||
onArrows: (next) => {
|
||||
show.arrows = next;
|
||||
deps.redraw();
|
||||
},
|
||||
onStrength: (next) => {
|
||||
show.strength = next;
|
||||
deps.onStrength(next);
|
||||
deps.redraw();
|
||||
},
|
||||
onHotspots: (next) => {
|
||||
show.hotspots = next;
|
||||
deps.redraw();
|
||||
},
|
||||
onUpstream: (next) => {
|
||||
show.upstream = next;
|
||||
deps.redraw();
|
||||
},
|
||||
});
|
||||
return show;
|
||||
}
|
||||
@@ -48,6 +48,7 @@ import { hiddenOptionKeys } from "./B05_Profile_UI_Structures_Define_Form";
|
||||
import { stationLabel as formatStation } from "../common_util/common_util_station";
|
||||
import { PIPE_MATCH_M, resolveSelection } from "../A00_Common/design/design_selection";
|
||||
import { movedStructure, snapOldStations, snapStationFor } from "./B05_Profile_UI_Structures_Snap";
|
||||
import { followPipeList } from "./B05_Profile_UI_Structures_Panel_Pipes";
|
||||
|
||||
/** 공개 타입·표시 상수는 700줄 제한으로 `_Structures_Panel_Types` 로 옮겼다 —
|
||||
* 옛 임포트 경로가 그대로 동작하도록 여기서 다시 내보낸다(2026-09-04). */
|
||||
@@ -573,36 +574,7 @@ export function createStructuresSection(
|
||||
getStructures: () => [...structures],
|
||||
setPipeFacilities(pipes) {
|
||||
pipeFacilities = pipes.map((pipe) => ({ ...pipe }));
|
||||
if (tempPipeChainage !== null) {
|
||||
// 스냅·재계산으로 좌표가 조금 옮겨진 임시 관을 계속 따라간다.
|
||||
const hit = pipeFacilities.find(
|
||||
(pipe) => Math.abs(pipe.chainage_m - tempPipeChainage!) < PIPE_MATCH_M,
|
||||
);
|
||||
if (hit) {
|
||||
tempPipeChainage = hit.chainage_m;
|
||||
if (selectedPipeChainage !== null) selectedPipeChainage = hit.chainage_m;
|
||||
// 재계산이 끝나 도착한 담당 유역 설계유량을 입력 중인 폼에 반영한다 —
|
||||
// 물넘이·세월교 월류 높이가 이 값으로 계산된다(2026-08-18 진단: 재계산
|
||||
// 후 선택 통지가 조용히 끝나 폼이 유량을 못 받던 문제).
|
||||
facilityOptions.setDesignFlow(hit.design_flow_m3s ?? null);
|
||||
} else {
|
||||
// 임시 배치 관이 재계산에서 사라졌다 — 임시 상태만 정리(관은 이미 없다).
|
||||
tempPipeChainage = null;
|
||||
syncButtons();
|
||||
}
|
||||
}
|
||||
const kept = pipeFacilities.find(
|
||||
(pipe) => Math.abs(pipe.chainage_m - (selectedPipeChainage ?? NaN)) < PIPE_MATCH_M,
|
||||
);
|
||||
if (kept)
|
||||
selectedPipeChainage = kept.chainage_m; // 조금 옮겨진 관도 고름 따라감(233-3)
|
||||
else if (selectedPipeChainage !== null) {
|
||||
// 고르고 있던 관이 사라졌다(삭제·재계산 이동) — 폼도 함께 닫는다.
|
||||
selectedPipeChainage = null;
|
||||
facilityOptions.setFacility(null);
|
||||
syncButtons();
|
||||
}
|
||||
renderList();
|
||||
followPipeList(panelContext, pipeFacilities);
|
||||
},
|
||||
selectPipeByChainage(chainageM) {
|
||||
if (chainageM !== null) callbacks.onReveal?.();
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/* =============================================================================
|
||||
* B05_Profile_UI_Structures_Panel_Pipes.ts
|
||||
* 「구조물 배치」 폼이 관 목록을 받는 몫 — 임시 관 · 고른 관이 조금 옮겨져도 따라감
|
||||
* (`B05_Profile_UI_Structures_Panel.ts` setPipeFacilities 에서 나눔 · 472-5 · 동작 같음).
|
||||
* ========================================================================== */
|
||||
|
||||
import { PIPE_MATCH_M } from "../A00_Common/design/design_selection";
|
||||
import type { StructuresEventContext } from "./B05_Profile_UI_Structures_Panel_Events";
|
||||
import type { PipeFacilityItem } from "./B05_Profile_UI_Structures_Panel_Types";
|
||||
|
||||
/** 새 관 목록(`list` = 이미 갈아 낀 폼 목록) — 임시 · 고른 관 누가거리 따라감 · 사라지면 폼 닫음. */
|
||||
export function followPipeList(ctx: StructuresEventContext, list: PipeFacilityItem[]): void {
|
||||
const temp = ctx.tempPipeChainage();
|
||||
if (temp !== null) {
|
||||
// 스냅·재계산으로 좌표가 조금 옮겨진 임시 관을 계속 따라간다.
|
||||
const hit = list.find((pipe) => Math.abs(pipe.chainage_m - temp) < PIPE_MATCH_M);
|
||||
if (hit) {
|
||||
ctx.setTempPipeChainage(hit.chainage_m);
|
||||
if (ctx.selectedPipeChainage() !== null) ctx.setSelectedPipeChainage(hit.chainage_m);
|
||||
// 재계산이 끝나 도착한 담당 유역 설계유량을 입력 중인 폼에 반영한다 —
|
||||
// 물넘이·세월교 월류 높이가 이 값으로 계산된다(2026-08-18 진단: 재계산
|
||||
// 후 선택 통지가 조용히 끝나 폼이 유량을 못 받던 문제).
|
||||
ctx.facilityOptions.setDesignFlow(hit.design_flow_m3s ?? null);
|
||||
} else {
|
||||
// 임시 배치 관이 재계산에서 사라졌다 — 임시 상태만 정리(관은 이미 없다).
|
||||
ctx.setTempPipeChainage(null);
|
||||
ctx.syncButtons();
|
||||
}
|
||||
}
|
||||
const kept = list.find(
|
||||
(pipe) => Math.abs(pipe.chainage_m - (ctx.selectedPipeChainage() ?? NaN)) < PIPE_MATCH_M,
|
||||
);
|
||||
if (kept)
|
||||
ctx.setSelectedPipeChainage(kept.chainage_m); // 조금 옮겨진 관도 고름 따라감(233-3)
|
||||
else if (ctx.selectedPipeChainage() !== null) {
|
||||
// 고르고 있던 관이 사라졌다(삭제·재계산 이동) — 폼도 함께 닫는다.
|
||||
ctx.setSelectedPipeChainage(null);
|
||||
ctx.facilityOptions.setFacility(null);
|
||||
ctx.syncButtons();
|
||||
}
|
||||
ctx.renderList();
|
||||
}
|
||||
@@ -13,11 +13,7 @@
|
||||
* ========================================================================== */
|
||||
|
||||
import type { PipeFacilityItem } from "../A00_Common/b_structures_section";
|
||||
import {
|
||||
bindDesignView,
|
||||
sharedStructures,
|
||||
type DesignPageShared,
|
||||
} from "../A00_Common/design/design_shared";
|
||||
import { bindDesignView, sharedStructures } from "../A00_Common/design/design_shared";
|
||||
import {
|
||||
fetchStructureTypes,
|
||||
staleDefinitionNotice,
|
||||
@@ -58,10 +54,17 @@ import {
|
||||
resetAdjustDock,
|
||||
setAdjustSideSlots,
|
||||
} from "./B06_Section_UI_Adjust_Dock";
|
||||
import type { SectionDetailResponse } from "./B06_Section_Api_Fetch";
|
||||
import { withSpecDefaults } from "./B06_Section_UI_Page_Structures_Spec";
|
||||
import { listPick } from "../common_util/common_util_structure_catalog";
|
||||
import { followMovedPipe, pipeIdAt } from "../A00_Common/design/design_pipe_ids";
|
||||
import type {
|
||||
B06StructuresPanel,
|
||||
B06StructuresPanelDeps,
|
||||
} from "./B06_Section_UI_Page_Structures_Panel_Types";
|
||||
export type {
|
||||
B06StructuresPanel,
|
||||
B06StructuresPanelDeps,
|
||||
} from "./B06_Section_UI_Page_Structures_Panel_Types";
|
||||
|
||||
const PIPE_ADD_GUIDE = "계곡 통과 시설을 넣고 빼려면 프로젝트를 먼저 여세요.";
|
||||
const PIPE_MOVE_GUIDE = "기준 측점 이동은 배수유역을 다시 나눠야 해 B05(종단) 화면에서 합니다.";
|
||||
@@ -74,96 +77,6 @@ const PIPE_PICK_GUIDE = "먼저 횡단도나 목록에서 구조물을 고르세
|
||||
const PIPE_MOVE_NOTICE =
|
||||
"기준 측점을 옮겼습니다 — 횡단도는 바로 보이고, 세부 배수유역은 뒤에서 다시 나눕니다.";
|
||||
|
||||
export interface B06StructuresPanelDeps {
|
||||
projectId: string | null;
|
||||
/** 한 페이지 한 벌 — 「구조물 배치」 폼은 종단 보기와 같은 노드다(계획서 24-3b). */
|
||||
shared: DesignPageShared;
|
||||
/** 횡단 캐시 — 폼 기본값을 조정창과 **같은 원천**(culvert 스펙)에서 읽는다
|
||||
* (2026-08-29 사용자: 예전 조정창이 보여주던 값이 기본값이다). */
|
||||
detail?: () => SectionDetailResponse | null;
|
||||
/** 지금 그려진 벽 높이(m) — 배관 벽은 스펙이 아니라 기하가 정하므로 폼 기본
|
||||
* 높이도 이 값을 쓴다(조정창 표시값과 같다). */
|
||||
wallHeight?: (chainageM: number, role: "inlet" | "outlet" | `extra${number}`) => number | null;
|
||||
/** 지금 그려진 벽 형태 — 조정값(m)이 있으면 그쪽이 정본이라 폼도 이 값을 보인다. */
|
||||
wallForm?: (chainageM: number, role: "inlet" | "outlet" | `extra${number}`) => string | null;
|
||||
/** 그 측점에서 유입·유출 벽이 각각 좌·우 어느 쪽인가(독립 기슭막이 칸 이름표). */
|
||||
wallSideLabels?: (chainageM: number) => { inlet: "좌" | "우"; outlet: "좌" | "우" } | null;
|
||||
/** 측점 간격(m) — 측점번호+잔여거리 환산용. */
|
||||
stationInterval: () => number;
|
||||
/** 목록·폼에서 고른 시설의 측점 카드를 선택·스크롤한다(revealOnly = 고르지도 스크롤도 않음 · 측점 없는 구조물 · 307). */
|
||||
focusChainage: (chainageM: number, revealOnly?: boolean) => void;
|
||||
/** 목록에서 고른 줄을 다시 눌러 풂 — 횡단 카드 선택도 끔(54-8). */
|
||||
releaseCard?: () => void;
|
||||
/** 종단 알약 강조를 목록에서 고른 것에 맞춘다(null = 관 · 해제 — 측점선 짝을 따름 · span 끔 = 범위 테두리 없이 · 137-4). */
|
||||
markStructure?: (structureId: string | null, span?: boolean) => void;
|
||||
/** 목록이 바뀔 때마다 종단 알약 레인에 같은 목록을 넘긴다(2026-09-07 표시 통일). */
|
||||
/** `pipes` = 관 지점(구간 포함) — 종단 띠 측점 계획(322-3). */
|
||||
onMarks?: (
|
||||
structures: StructureInstance[],
|
||||
types: StructureType[],
|
||||
pipes: ReadonlyArray<PipeFacilityItem>,
|
||||
) => void;
|
||||
/** 바뀐 값을 그 측점 횡단 캐시(culvert 스펙)에 얹고 카드를 다시 그린다 —
|
||||
* 조정창과 같은 흐름이라 폼 조작도 도면에 바로 보인다(2026-08-29 사용자 보고). */
|
||||
applyPipeOptions?: (chainageM: number, patch: Record<string, number | string>) => void;
|
||||
/** 관 목록 초안을 고쳤다 — 한 벌 배수유역도가 그 초안으로 다시 선다. */
|
||||
onPipesWritten?: () => void;
|
||||
}
|
||||
|
||||
export interface B06StructuresPanel {
|
||||
/** 「구조물 배치」 섹션(하위 [횡단 조정] 포함) — 좌측 패널에 붙인다. */
|
||||
root: HTMLElement;
|
||||
/** 배치된 구조물 목록 — 하단 고정 dock에 붙인다(B05와 같은 자리). */
|
||||
listRoot: HTMLElement;
|
||||
/** 타입 레지스트리·구조물 정본(미저장분 우선)·관 지점을 받아 채운다. */
|
||||
load: () => Promise<void>;
|
||||
/** 가게(초안 · 없으면 저장분) 관 · 구조물 자리가 목록과 다른가 — 다시 실을 때(노선 초안 따라감 · 평면 마커 옮김 · 440). */
|
||||
listStale: () => boolean;
|
||||
/** 기하가 한계로 자른 실제 값을 폼에 되돌린다 — 입력 숫자만 커지고 그림은 그대로인
|
||||
* 상태를 막는다(2026-08-29 사용자: 재질 높이 한계에 걸린 값). */
|
||||
overrideOptions: (chainageM: number, patch: Record<string, string | number>) => void;
|
||||
/** 고른 횡단도(측점)에 있는 구조물을 폼에 올린다 — 계곡 통과 시설이 먼저고,
|
||||
* 없으면 그 측점을 덮는 구조물 정본 항목. 둘 다 없으면 해제(2026-08-29). 횡단도의 카드 ·
|
||||
* 벽 · 구체 어디를 눌러도 이 길 하나다. 폼이 연 것을 고름 값으로 돌려준다. */
|
||||
showAtChainage: (chainageM: number | null) => DesignSelection;
|
||||
/** 횡단도에서 벽·구체 선택을 풀 때 — 관 계열 폼만 비운다. 구조물 폼은 그대로 둔다(카드를
|
||||
* 갈아 선택하며 옛 카드의 벽 선택이 풀리는 순간 새 카드의 폼을 지우면 안 된다). */
|
||||
releasePipe: () => void;
|
||||
/** 그 측점에서 폼이 열 항목을 **열지 않고** 고름 값으로 풀어 준다(세션에 적는 데 쓴다). */
|
||||
selectionAt: (chainageM: number | null) => DesignSelection;
|
||||
/** 다음 카드 고름이 측점보다 먼저 열 구조물 — 알약 · 넘김값이 누른 것(null = 측점 규칙대로). */
|
||||
prefer: (structureId: string | null) => void;
|
||||
/** 지금 카드 고름이 먼저 열 구조물(목록 · 알약 · 넘김) — 없으면 null(카드만 고름 · 189-1). */
|
||||
preferred: () => string | null;
|
||||
/** 구조물 하나를 폼에 올리고 그 카드로 간다 — 알약을 누른 길(목록 클릭과 같은 흐름). */
|
||||
selectStructure: (structureId: string) => void;
|
||||
/** 측점 없는 관 하나를 폼에 올림 — 카드 안 고름 · 스크롤 없음 · 알약 · 고름 값에 알약 id(요구 178 · 307). */
|
||||
selectPipeAt: (chainageM: number) => void;
|
||||
/** 넘김값(B05 알약 · 3D · 지도)의 카드 고름 — `focus` 가 그 카드를 고름 · 넘긴 구조물이 먼저(52-15) ·
|
||||
* 폼은 id 로 직접 엶(53-1) · 측점 없는 구조물은 카드 고름 · 스크롤 없이 폼만(요구 178 · 307). */
|
||||
focusPicked: (structureId: string | null, focus: () => void) => void;
|
||||
/** 배수유역도가 관 초안에 채운 빈 월류 높이(126-1)를 목록 · 폼에 받는다(298-1). */
|
||||
adoptFordHeights: () => void;
|
||||
/** 지금 목록의 구조물 정본(미저장분 포함) — 카드 찾기에 쓴다. */
|
||||
getStructures: () => StructureInstance[];
|
||||
/** 지금 폼에 올라온 계곡 통과 시설의 누가거리(없으면 null). */
|
||||
currentChainage: () => number | null;
|
||||
/** 종단 그래프 우클릭이 쓰는 길 — 좌측 폼·목록과 **같은 함수**를 탄다(2026-09-12). */
|
||||
addStructureAt: (chainageM: number, typeId: string) => void;
|
||||
removeStructureById: (structureId: string) => boolean;
|
||||
addPipeAt: (chainageM: number) => void;
|
||||
removePipeAt: (chainageM: number) => void;
|
||||
movePipeTo: (fromChainageM: number, toChainageM: number) => void;
|
||||
moveStructureTo: (structureId: string, toChainageM: number) => boolean;
|
||||
/** 유입구 "구조"에 합쳐진 B06 유입측 형식 — 조정창 값과 맞춘다(2026-08-29 지시 5). */
|
||||
facility: {
|
||||
setInletStructure: (value: "auto" | "revet" | "I" | "L" | "U") => void;
|
||||
onInletStructureChange: (handler: (value: "auto" | "revet" | "I" | "L" | "U") => void) => void;
|
||||
/** 횡단 단 수가 바뀜 — 배관 흙막이 컨테이너 수를 맞춤(54-1). */
|
||||
recount: () => void;
|
||||
};
|
||||
}
|
||||
|
||||
type PlaceOf = { chainage_m?: number | null; start_m?: number | null; end_m?: number | null };
|
||||
/** 목록 자리 지문 — 누가거리 · 구간만(옵션 고침은 안 셈 · 440). */
|
||||
const placeKey = (list: ReadonlyArray<PlaceOf>): string =>
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/* =============================================================================
|
||||
* B06_Section_UI_Page_Structures_Panel_Types.ts
|
||||
* 횡단 보기 「구조물 배치」 몫의 타입 — `B06_Section_UI_Page_Structures_Panel.ts` 에서 나눔
|
||||
* (472-5 · 700줄 · 동작 같음 · 그쪽이 다시 내보냄).
|
||||
* ========================================================================== */
|
||||
|
||||
import type { PipeFacilityItem } from "../A00_Common/b_structures_section";
|
||||
import type { DesignPageShared } from "../A00_Common/design/design_shared";
|
||||
import type { DesignSelection } from "../A00_Common/design/design_selection";
|
||||
import type { StructureInstance, StructureType } from "../B05_Profile/B05_Profile_Api_Structures";
|
||||
import type { SectionDetailResponse } from "./B06_Section_Api_Fetch";
|
||||
|
||||
export interface B06StructuresPanelDeps {
|
||||
projectId: string | null;
|
||||
/** 한 페이지 한 벌 — 「구조물 배치」 폼은 종단 보기와 같은 노드다(계획서 24-3b). */
|
||||
shared: DesignPageShared;
|
||||
/** 횡단 캐시 — 폼 기본값을 조정창과 **같은 원천**(culvert 스펙)에서 읽는다
|
||||
* (2026-08-29 사용자: 예전 조정창이 보여주던 값이 기본값이다). */
|
||||
detail?: () => SectionDetailResponse | null;
|
||||
/** 지금 그려진 벽 높이(m) — 배관 벽은 스펙이 아니라 기하가 정하므로 폼 기본
|
||||
* 높이도 이 값을 쓴다(조정창 표시값과 같다). */
|
||||
wallHeight?: (chainageM: number, role: "inlet" | "outlet" | `extra${number}`) => number | null;
|
||||
/** 지금 그려진 벽 형태 — 조정값(m)이 있으면 그쪽이 정본이라 폼도 이 값을 보인다. */
|
||||
wallForm?: (chainageM: number, role: "inlet" | "outlet" | `extra${number}`) => string | null;
|
||||
/** 그 측점에서 유입·유출 벽이 각각 좌·우 어느 쪽인가(독립 기슭막이 칸 이름표). */
|
||||
wallSideLabels?: (chainageM: number) => { inlet: "좌" | "우"; outlet: "좌" | "우" } | null;
|
||||
/** 측점 간격(m) — 측점번호+잔여거리 환산용. */
|
||||
stationInterval: () => number;
|
||||
/** 목록·폼에서 고른 시설의 측점 카드를 선택·스크롤한다(revealOnly = 고르지도 스크롤도 않음 · 측점 없는 구조물 · 307). */
|
||||
focusChainage: (chainageM: number, revealOnly?: boolean) => void;
|
||||
/** 목록에서 고른 줄을 다시 눌러 풂 — 횡단 카드 선택도 끔(54-8). */
|
||||
releaseCard?: () => void;
|
||||
/** 종단 알약 강조를 목록에서 고른 것에 맞춘다(null = 관 · 해제 — 측점선 짝을 따름 · span 끔 = 범위 테두리 없이 · 137-4). */
|
||||
markStructure?: (structureId: string | null, span?: boolean) => void;
|
||||
/** 목록이 바뀔 때마다 종단 알약 레인에 같은 목록을 넘긴다(2026-09-07 표시 통일). */
|
||||
/** `pipes` = 관 지점(구간 포함) — 종단 띠 측점 계획(322-3). */
|
||||
onMarks?: (
|
||||
structures: StructureInstance[],
|
||||
types: StructureType[],
|
||||
pipes: ReadonlyArray<PipeFacilityItem>,
|
||||
) => void;
|
||||
/** 바뀐 값을 그 측점 횡단 캐시(culvert 스펙)에 얹고 카드를 다시 그린다 —
|
||||
* 조정창과 같은 흐름이라 폼 조작도 도면에 바로 보인다(2026-08-29 사용자 보고). */
|
||||
applyPipeOptions?: (chainageM: number, patch: Record<string, number | string>) => void;
|
||||
/** 관 목록 초안을 고쳤다 — 한 벌 배수유역도가 그 초안으로 다시 선다. */
|
||||
onPipesWritten?: () => void;
|
||||
}
|
||||
|
||||
export interface B06StructuresPanel {
|
||||
/** 「구조물 배치」 섹션(하위 [횡단 조정] 포함) — 좌측 패널에 붙인다. */
|
||||
root: HTMLElement;
|
||||
/** 배치된 구조물 목록 — 하단 고정 dock에 붙인다(B05와 같은 자리). */
|
||||
listRoot: HTMLElement;
|
||||
/** 타입 레지스트리·구조물 정본(미저장분 우선)·관 지점을 받아 채운다. */
|
||||
load: () => Promise<void>;
|
||||
/** 가게(초안 · 없으면 저장분) 관 · 구조물 자리가 목록과 다른가 — 다시 실을 때(노선 초안 따라감 · 평면 마커 옮김 · 440). */
|
||||
listStale: () => boolean;
|
||||
/** 기하가 한계로 자른 실제 값을 폼에 되돌린다 — 입력 숫자만 커지고 그림은 그대로인
|
||||
* 상태를 막는다(2026-08-29 사용자: 재질 높이 한계에 걸린 값). */
|
||||
overrideOptions: (chainageM: number, patch: Record<string, string | number>) => void;
|
||||
/** 고른 횡단도(측점)에 있는 구조물을 폼에 올린다 — 계곡 통과 시설이 먼저고,
|
||||
* 없으면 그 측점을 덮는 구조물 정본 항목. 둘 다 없으면 해제(2026-08-29). 횡단도의 카드 ·
|
||||
* 벽 · 구체 어디를 눌러도 이 길 하나다. 폼이 연 것을 고름 값으로 돌려준다. */
|
||||
showAtChainage: (chainageM: number | null) => DesignSelection;
|
||||
/** 횡단도에서 벽·구체 선택을 풀 때 — 관 계열 폼만 비운다. 구조물 폼은 그대로 둔다(카드를
|
||||
* 갈아 선택하며 옛 카드의 벽 선택이 풀리는 순간 새 카드의 폼을 지우면 안 된다). */
|
||||
releasePipe: () => void;
|
||||
/** 그 측점에서 폼이 열 항목을 **열지 않고** 고름 값으로 풀어 준다(세션에 적는 데 쓴다). */
|
||||
selectionAt: (chainageM: number | null) => DesignSelection;
|
||||
/** 다음 카드 고름이 측점보다 먼저 열 구조물 — 알약 · 넘김값이 누른 것(null = 측점 규칙대로). */
|
||||
prefer: (structureId: string | null) => void;
|
||||
/** 지금 카드 고름이 먼저 열 구조물(목록 · 알약 · 넘김) — 없으면 null(카드만 고름 · 189-1). */
|
||||
preferred: () => string | null;
|
||||
/** 구조물 하나를 폼에 올리고 그 카드로 간다 — 알약을 누른 길(목록 클릭과 같은 흐름). */
|
||||
selectStructure: (structureId: string) => void;
|
||||
/** 측점 없는 관 하나를 폼에 올림 — 카드 안 고름 · 스크롤 없음 · 알약 · 고름 값에 알약 id(요구 178 · 307). */
|
||||
selectPipeAt: (chainageM: number) => void;
|
||||
/** 넘김값(B05 알약 · 3D · 지도)의 카드 고름 — `focus` 가 그 카드를 고름 · 넘긴 구조물이 먼저(52-15) ·
|
||||
* 폼은 id 로 직접 엶(53-1) · 측점 없는 구조물은 카드 고름 · 스크롤 없이 폼만(요구 178 · 307). */
|
||||
focusPicked: (structureId: string | null, focus: () => void) => void;
|
||||
/** 배수유역도가 관 초안에 채운 빈 월류 높이(126-1)를 목록 · 폼에 받는다(298-1). */
|
||||
adoptFordHeights: () => void;
|
||||
/** 지금 목록의 구조물 정본(미저장분 포함) — 카드 찾기에 쓴다. */
|
||||
getStructures: () => StructureInstance[];
|
||||
/** 지금 폼에 올라온 계곡 통과 시설의 누가거리(없으면 null). */
|
||||
currentChainage: () => number | null;
|
||||
/** 종단 그래프 우클릭이 쓰는 길 — 좌측 폼·목록과 **같은 함수**를 탄다(2026-09-12). */
|
||||
addStructureAt: (chainageM: number, typeId: string) => void;
|
||||
removeStructureById: (structureId: string) => boolean;
|
||||
addPipeAt: (chainageM: number) => void;
|
||||
removePipeAt: (chainageM: number) => void;
|
||||
movePipeTo: (fromChainageM: number, toChainageM: number) => void;
|
||||
moveStructureTo: (structureId: string, toChainageM: number) => boolean;
|
||||
/** 유입구 "구조"에 합쳐진 B06 유입측 형식 — 조정창 값과 맞춘다(2026-08-29 지시 5). */
|
||||
facility: {
|
||||
setInletStructure: (value: "auto" | "revet" | "I" | "L" | "U") => void;
|
||||
onInletStructureChange: (handler: (value: "auto" | "revet" | "I" | "L" | "U") => void) => void;
|
||||
/** 횡단 단 수가 바뀜 — 배관 흙막이 컨테이너 수를 맞춤(54-1). */
|
||||
recount: () => void;
|
||||
};
|
||||
}
|
||||
@@ -99,7 +99,9 @@ def test_배선() -> None:
|
||||
listing = _read("B05_Profile", "B05_Profile_UI_Structures_List.ts")
|
||||
assert "< PIPE_MATCH_M" in listing and "< 0.05" not in listing
|
||||
structures = _read("B05_Profile", "B05_Profile_UI_Structures_Panel.ts")
|
||||
assert "selectedPipeChainage = kept.chainage_m;" in structures
|
||||
# 관 목록 받기는 472-5 에서 `_Structures_Panel_Pipes` 로 나눔(동작 같음)
|
||||
pipes = _read("B05_Profile", "B05_Profile_UI_Structures_Panel_Pipes.ts")
|
||||
assert "ctx.setSelectedPipeChainage(kept.chainage_m);" in pipes
|
||||
# 저장 · 새로 읽기 뒤 목록 주입이 관 폼을 닫지 않음(닫히며 종단 선까지 꺼졌음)
|
||||
assert "if (!applyingLive && selectedPipeChainage === null)" in structures
|
||||
assert len(structures.splitlines()) <= 700
|
||||
|
||||
Reference in New Issue
Block a user