feat(설계묶음): 344-4 계산 묶음 계약 — 단계 입력 · 출력 · 측점 · 노선 초안 모양 · route-draft 캐시 칸 · 결과 칸 셋
- design_pipeline_types.ts 새 — 단계 차례 · 주인 · deps · 키별 모양(측점 · 지반 · 노선 초안 · 발자국) - b_page_state 등록표 — route-draft(draft · project) · design-stations · design-profile · design-footprint(result) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TteK8mfYZetk5P3bEZkyVZ
This commit is contained in:
@@ -208,6 +208,9 @@ export const STATE_REGISTRY = {
|
||||
scope: "route",
|
||||
legacy: (_p, r) => `b05-profile-alignment-draft:${r}`,
|
||||
},
|
||||
/** 노선 초안(344-4) — 노드 · 곡선 · 반경 · 스냅 · 지형 · rev(`RouteDraft` · `design_pipeline_types`).
|
||||
* 노선 번호는 [저장]마다 새로 나므로 project 범위 · 가게 키 `routeDraft`. */
|
||||
"route-draft": { bucket: "draft", scope: "project" },
|
||||
|
||||
/* ── ④ 계산 결과 ─────────────────────────────────────────────────────── */
|
||||
/** 노선·종단 최신 응답. 페이지를 오갈 때 이 값으로 먼저 그린다. */
|
||||
@@ -235,6 +238,10 @@ export const STATE_REGISTRY = {
|
||||
/** B05 계획노선 편집 창 — 마지막에 적용한 초기노선 갈래(로직 · 비중 · 노드 · 비교값).
|
||||
* 노선을 다시 계산해 결과를 버릴 때도 이 값은 새 노선의 출발점이라 되살린다(`_Apply`). */
|
||||
"routeedit-initial": { bucket: "result", scope: "project" },
|
||||
/** 계산 묶음 결과(344-4) — 측점 · 종단 · 평면 발자국 · 가게가 메모리 우선 · 멈춘 뒤 한 번 씀. */
|
||||
"design-stations": { bucket: "result", scope: "project" },
|
||||
"design-profile": { bucket: "result", scope: "project" },
|
||||
"design-footprint": { bucket: "result", scope: "project" },
|
||||
} as const satisfies Record<string, StateEntry>;
|
||||
|
||||
export type StateName = keyof typeof STATE_REGISTRY;
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
/* =============================================================================
|
||||
* A00_Common/design/design_pipeline_types.ts
|
||||
* 실시간 계산 묶음 **계약**(PLAN 344-4) — 키별 모양 · 단계 입력 · 출력. 다른 창(sub1 · sub2 ·
|
||||
* sub3 · sub6 · sub8)은 이 모양대로 단계 함수를 만들어 `createDesignPipeline({ stages })` 에 넣음.
|
||||
* 계산 묶음 본체 = `design_pipeline.ts`.
|
||||
*
|
||||
* 흐름 — 한 방향:
|
||||
* 입력 route(노선 초안) · alignment(계획고) · cross(횡단 입력) · pipes(관) · structures(구조물)
|
||||
* ↓ update(출처, 값, 범위) — 조작마다 차례 번호(generation) +1 · 옛 계산 · 옛 요청 버림
|
||||
* 브라우저 즉시(차례대로 · 단계 사이마다 번호 확인 · 단계마다 결과 바로 내보냄)
|
||||
* route → follow → stations → ground → planline → profile → cross → footprint → masshaul
|
||||
* 뒤(기다리지 않음 · 새 조작이 오면 AbortSignal 로 끊김 · 옛 번호 결과 버림)
|
||||
* haul(흙 배분 · 서버 Node) · drainage(배수유역 「낡음」 표시) · corridor(3D 「낡음」 표시)
|
||||
*
|
||||
* 규칙:
|
||||
* · 단계는 자기 출력만 돌려줌 — 입력 · 다른 단계 출력을 고치지 않음(ctx 의 값은 읽기만).
|
||||
* · 예외 하나 = follow — 노선이 바뀐 차례에만 · pipes · structures · alignment 를 한 번 고쳐
|
||||
* 돌려줌(관 · 구조물 좌표 따라감 · 계획고 옮김). 묶음이 입력에 넣고 다음 단계로 감.
|
||||
* · 단계 함수가 없으면(아직 안 붙음) 건너뜀 — 출력은 앞 값 그대로(처음엔 null).
|
||||
* · 기대는 값(deps)의 판이 그대로면 다시 안 셈(같은 입력 다시 안 셈).
|
||||
* · range = 마지막으로 이 단계가 끝난 뒤 쌓인 바뀐 범위 전부 — 범위만 셈 · 범위 밖은 ctx.prev 재사용.
|
||||
*
|
||||
* 단계 · 주인 · 입력(deps) → 출력:
|
||||
* | 단계 | 주인 | deps | 출력 |
|
||||
* | route | sub8 | input.route | RouteLine |
|
||||
* | follow | sub1 | route | FollowPatch(입력 고침) |
|
||||
* | stations | sub1·sub2| route · input.pipes · input.structures | PipelineStation[](지반 null) |
|
||||
* | ground | sub8 | stations | GroundOutput |
|
||||
* | planline | sub1 | ground | AlignmentBase(첫 계획선) |
|
||||
* | profile | sub1 | planline · input.alignment | ProfileAlignment |
|
||||
* | cross | sub2 | profile · ground · input.cross · pipes · structures | CrossOutput |
|
||||
* | footprint | sub6 | cross · ground | PlanFootprint |
|
||||
* | masshaul | sub2 | cross | unknown(sub2 모양) |
|
||||
* | haul | sub2 | masshaul | unknown(서버 응답) |
|
||||
* | drainage | sub3 | route · input.pipes | 「낡음」 표시(출력 안 씀) |
|
||||
* | corridor | sub5 | profile · cross | 「낡음」 표시(출력 안 씀) |
|
||||
*
|
||||
* 가게 키(`design_store.ts`) — routeDraft(입력 · 세션 즉시) · stations(ground 단계 출력) ·
|
||||
* profile · footprint(메모리 우선 · 멈춘 뒤 한 번 세션) · generation(메모리만).
|
||||
* ========================================================================== */
|
||||
|
||||
import type {
|
||||
AlignmentBase,
|
||||
AlignmentEdits,
|
||||
ProfileAlignment,
|
||||
} from "../../B05_Profile/B05_Profile_UI_Profile_Alignment";
|
||||
|
||||
/** 평면 좌표(m) — 도면 좌표계 x · y. */
|
||||
export type Vertex = [number, number];
|
||||
|
||||
/* ── 입력 ───────────────────────────────────────────────────────────────── */
|
||||
|
||||
/** 노선 초안 — 캐시 칸 `route-draft`(draft · project 범위) · [저장] 때 서버로 · 노선 편집기가 씀. */
|
||||
export interface RouteDraft {
|
||||
/** 놓을 때마다 +1 — 같은 초안인지 보는 판 번호. */
|
||||
rev: number;
|
||||
/** 노드(시점 · 교각점 · 종점) — 사용자가 끄는 꺾임점. 원호는 route 단계가 만듦. */
|
||||
nodes: Vertex[];
|
||||
/** 꺾임점마다 곡선 둠 · 못박은 반경(m · null = 기본) · 붙든 값 · 붙든 곡선 길이(m). 길이 = nodes. */
|
||||
curveOn: boolean[];
|
||||
curveRadius: Array<number | null>;
|
||||
curveLock: Array<"radius" | "arc" | null>;
|
||||
curveArc: Array<number | null>;
|
||||
/** [측점 스냅](요구 224-2) — 켜면 호 안 측점 하나를 곡선 중점으로. */
|
||||
stationSnap: boolean;
|
||||
/** 분석 지형(`las` · `sheet` · null = 확정 지표면) — ground 단계가 고를 격자. */
|
||||
terrain: string | null;
|
||||
}
|
||||
|
||||
/** 묶음이 받는 입력 다섯. 없으면 null(= 저장분 그대로). cross · pipes · structures 모양은 주인 몫. */
|
||||
export interface PipelineInputs {
|
||||
route: RouteDraft | null;
|
||||
alignment: AlignmentEdits | null;
|
||||
/** 횡단 입력(crossdesign · cutslope · berm · std-cross 등 초안 묶음) — 모양 = sub2. */
|
||||
cross: unknown;
|
||||
/** 관 목록(`DetailPipeInput[]`) — 좌표가 정본 · 누가거리는 stations 가 다시 매김. */
|
||||
pipes: unknown;
|
||||
/** 구조물 목록(`StructureInstance[]`) — 위와 같음. */
|
||||
structures: unknown;
|
||||
}
|
||||
|
||||
export type InputKey = keyof PipelineInputs;
|
||||
|
||||
/** 조작 출처 — 노선 · 계획고 · 횡단 · 관 · 구조물. */
|
||||
export type ChangeSource = "route" | "grade" | "cross" | "pipe" | "structure";
|
||||
|
||||
/** 출처 → 입력 칸. */
|
||||
export const SOURCE_INPUT: Record<ChangeSource, InputKey> = {
|
||||
route: "route",
|
||||
grade: "alignment",
|
||||
cross: "cross",
|
||||
pipe: "pipes",
|
||||
structure: "structures",
|
||||
};
|
||||
|
||||
/**
|
||||
* 바뀐 범위(누가거리 m) — 부르는 쪽이 정함:
|
||||
* 노선 = 처음 바뀐 접선점부터 끝(toM = Infinity) · 계획고 = 이웃 꼭짓점 사이 · 횡단 = 그 측점(fromM = toM).
|
||||
* 여러 조작이 쌓이면 합침(가장 앞 ~ 가장 뒤 · keys 는 모음). null = 전 구간.
|
||||
*/
|
||||
export interface ChangeRange {
|
||||
sources: ChangeSource[];
|
||||
fromM: number;
|
||||
toM: number;
|
||||
/** 측점 키로 정한 범위(횡단 조작) — 비면 거리로만. */
|
||||
keys: string[];
|
||||
}
|
||||
|
||||
/** update 가 받는 범위 한 조각 — sources 는 출처로 채움. */
|
||||
export interface RangePart {
|
||||
fromM: number;
|
||||
toM: number;
|
||||
keys?: string[];
|
||||
}
|
||||
|
||||
/* ── 출력 ───────────────────────────────────────────────────────────────── */
|
||||
|
||||
/** route 단계 — 원호가 섞인 그린 노선(`_RouteEdit_Curve` 짝) · 누가거리. */
|
||||
export interface RouteLine {
|
||||
line: Vertex[];
|
||||
/** line 정점마다 누가거리(m). */
|
||||
chainage: number[];
|
||||
lengthM: number;
|
||||
}
|
||||
|
||||
/** follow 단계 — 노선이 바뀐 차례에 한 번만 입력을 고침. 안 고치는 칸은 비움. */
|
||||
export interface FollowPatch {
|
||||
pipes?: unknown;
|
||||
structures?: unknown;
|
||||
alignment?: AlignmentEdits;
|
||||
}
|
||||
|
||||
/** 측점 종류 — 격자(간격) · 구조물 · 관 · 끝(시점 · 종점). */
|
||||
export type StationKind = "grid" | "structure" | "pipe" | "end";
|
||||
|
||||
/** 횡단 지반 표본 한 점 — 중심에서 거리(m · 좌 +) · 높이(null = 빈칸). */
|
||||
export interface GroundSample {
|
||||
offsetM: number;
|
||||
z: number | null;
|
||||
}
|
||||
|
||||
export interface StationGround {
|
||||
centerZ: number | null;
|
||||
/** 중심 → 바깥 차례 · offsetM 은 둘 다 양수. */
|
||||
left: GroundSample[];
|
||||
right: GroundSample[];
|
||||
}
|
||||
|
||||
/** 측점 한 자리 — stations 가 만들고 ground 가 지반을 채움. 가게 키 `stations`. */
|
||||
export interface PipelineStation {
|
||||
/** 측점 키 — 규칙 = 344-3(sub2 · 0.01 m · 격자 = 격자 번호 · 구조물 · 관 = id). */
|
||||
key: string;
|
||||
chainageM: number;
|
||||
x: number;
|
||||
y: number;
|
||||
/** 진행 방향(도 · 북 0 시계) · 좌측 단위 벡터(`_RouteEdit_Frame` 짝). */
|
||||
azimuthDeg: number;
|
||||
left: Vertex;
|
||||
kind: StationKind;
|
||||
/** 구조물 · 관 id — 격자 · 끝은 null. */
|
||||
id: string | null;
|
||||
/** ground 단계 전엔 null. */
|
||||
ground: StationGround | null;
|
||||
/** 평면 곡선반경(m · 직선 null) · 확폭이 붙는 바깥쪽 · 확폭량(m). */
|
||||
planRadiusM: number | null;
|
||||
curveOuterSide: "left" | "right" | null;
|
||||
curveWideningM: number | null;
|
||||
}
|
||||
|
||||
/** ground 단계 — 지반 채운 측점 · 종단 지반선(1 m). */
|
||||
export interface GroundOutput {
|
||||
stations: PipelineStation[];
|
||||
longitudinal: { chainageM: number[]; z: Array<number | null> };
|
||||
}
|
||||
|
||||
/** cross 단계 — 측점 키 → 그 측점 결과(면적은 유토곡선 재료 · detail 모양 = sub2). */
|
||||
export interface CrossStationResult {
|
||||
key: string;
|
||||
chainageM: number;
|
||||
cutAreaM2: number;
|
||||
fillAreaM2: number;
|
||||
detail: unknown;
|
||||
}
|
||||
|
||||
export interface CrossOutput {
|
||||
byKey: Record<string, CrossStationResult>;
|
||||
}
|
||||
|
||||
/** footprint 단계 — 평면 발자국(도로 끝 · 비탈 끝) · 시점 → 종점 차례 · 가게 키 `footprint`. */
|
||||
export interface PlanFootprint {
|
||||
roadLeft: Vertex[];
|
||||
roadRight: Vertex[];
|
||||
slopeLeft: Vertex[];
|
||||
slopeRight: Vertex[];
|
||||
}
|
||||
|
||||
/** 단계 이름 → 출력. drainage · corridor 는 표시만 하므로 출력이 없음(null). */
|
||||
export interface PipelineOutputs {
|
||||
route: RouteLine;
|
||||
follow: FollowPatch;
|
||||
stations: PipelineStation[];
|
||||
ground: GroundOutput;
|
||||
planline: AlignmentBase;
|
||||
profile: ProfileAlignment;
|
||||
cross: CrossOutput;
|
||||
footprint: PlanFootprint;
|
||||
masshaul: unknown;
|
||||
haul: unknown;
|
||||
drainage: null;
|
||||
corridor: null;
|
||||
}
|
||||
|
||||
export type StageName = keyof PipelineOutputs;
|
||||
|
||||
/* ── 단계 함수 ──────────────────────────────────────────────────────────── */
|
||||
|
||||
export interface StageContext<K extends StageName> {
|
||||
/** 이 계산의 차례 번호 — 다르면 이미 버려진 계산. */
|
||||
generation: number;
|
||||
/** 마지막으로 이 단계가 끝난 뒤 쌓인 바뀐 범위(null = 전 구간 · 처음). */
|
||||
range: ChangeRange | null;
|
||||
/** 새 조작이 오면 끊김 — fetch 에 그대로 넘김. */
|
||||
signal: AbortSignal;
|
||||
/** 입력(읽기만). */
|
||||
inputs: Readonly<PipelineInputs>;
|
||||
/** 앞 단계 출력(이번 계산 것 · 없으면 null · 읽기만). */
|
||||
out: Readonly<{ [S in StageName]: PipelineOutputs[S] | null }>;
|
||||
/** 이 단계 앞 출력 — 범위 밖을 재사용. */
|
||||
prev: PipelineOutputs[K] | null;
|
||||
}
|
||||
|
||||
export type StageFn<K extends StageName> = (
|
||||
ctx: StageContext<K>,
|
||||
) => PipelineOutputs[K] | Promise<PipelineOutputs[K]>;
|
||||
|
||||
export type PipelineStages = { [K in StageName]?: StageFn<K> };
|
||||
|
||||
/* ── 시간 기록(window.__aisloPipeline · 개발용) ─────────────────────────── */
|
||||
|
||||
export interface PipelineRunRecord {
|
||||
generation: number;
|
||||
sources: ChangeSource[];
|
||||
/** 단계 → ms(뒤 단계는 끝난 때 채움 · 건너뜀은 안 적음). */
|
||||
stages: Partial<Record<StageName, number>>;
|
||||
/** 건너뛴 단계(같은 입력 · 함수 없음). */
|
||||
skipped: StageName[];
|
||||
/** 브라우저 단계 합 ms. */
|
||||
totalMs: number;
|
||||
/** done = 브라우저 단계 끝 · dropped = 새 조작에 밀려 버림 · error = 단계가 던짐. */
|
||||
outcome: "running" | "done" | "dropped" | "error";
|
||||
}
|
||||
Reference in New Issue
Block a user