Files
Aislo/A00_Common/design/design_pipeline_probe.ts
T
eomsangdonandClaude Opus 5.5 a02367e615 feat(475-3 요구 275-3): 노선이 바뀌면 자리 칸(암 경계 · 2단 경사 · 손 상단측)은 같은 자리 측점만 넘김 — 새 자리 = 기본값
- 자리 칸 목록 한 벌 = TS `SEAT_KEYS`(Station_Swap · 파이썬 Router_Design 이 그 글을 읽음) — two_stage_slope · rock_boundary_offset_m · cut_slope_ratio_user · section_mode · ditch_side
- 자리 판정 한 벌 = `sameSeat` ↔ 파이썬 `same_seat`(중심 0.01 m · 왼쪽 축 1e-3 · 모르면 같은 자리 = 옛 동작)
- 끌기 세션 넘김 · Node stations.carry = `carryKeyedStates`(목록 측점 자리 · 값마다 쓴 때 자리 `@seat` — 되돌리기로 되살린 값도 제 자리로)
- 미리보기 = `pipelineCrossSource` 가 저장분 · 세션 자리 칸에 자리째 실음 → `crossAtSeat` 가 새 자리면 뺌
- B06 갈아끼움(노선 초안) = 자리 바뀐 측점 설계의 자리 칸 걷고 기본값(`unseated` · 브레인 덧)
- 서버 노선 저장 = `carry_cross_inputs` 자리 칸 같은 자리만 · 손 단면 유형 · 측구 방향 = `compute_default_designs` 입력
- 옛 단언 시험 고침(자리 모름 목록 = 같은 자리 = 손 상단측도 넘김 · 「@seat」 칸 거름) · 새 시험 test_475_3_seat_carry
- 위키 rules 넘김 규칙 줄

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:29:38 +09:00

110 lines
4.6 KiB
TypeScript

/* =============================================================================
* A00_Common/design/design_pipeline_probe.ts
* 계산 묶음 아무 누가거리 한 장(368-4 · 요구 240-21) — 종단 원지반 점 작은 창 = 지금 계획선(초안 포함).
*
* 묶음 출력(노선 · 측점 · 종단 · 횡단 입력 · 관 · 구조물)을 읽기만 하고, 같은 단계 함수(ground · cross ·
* `crossWithUphill` · `stationSection`)를 그 자리 측점 하나로 다시 부름 — [측점 추가] 뒤 묶음이 낼 측점과
* 같은 틀 · 반경 · 확폭(기존 측점 + 그 자리로 셈). 파생 저장 · 캐시 · 묶음 상태는 안 건드림.
* 지형 격자가 없거나 분석 지형 ≠ 설계 지형이면 null(부르는 쪽이 서버 미리 뜨기로).
* ========================================================================== */
import { pagePipeline } from "./design_pipeline_page";
import type {
CrossOutput,
GroundOutput,
StageContext,
StageName,
} from "./logic/design_pipeline_types";
import { probeStation } from "./logic/design_pipeline_probe_station";
import { groundStageDeps } from "../../B05_Profile/B05_Profile_Api_TerrainGrid";
import { createGroundStage } from "../../B05_Profile/logic/B05_Profile_Pipeline_RouteGround";
import {
crossWithShapes,
crossAtSeat,
crossWithUphill,
shapeStatus,
type CrossInput,
} from "../../B05_Profile/logic/B05_Profile_Pipeline_Detail";
import { fetchStructureTypes } from "../../B05_Profile/B05_Profile_Api_Structures";
import { createCrossStage } from "../../B06_Section/logic/B06_Section_Cross_Stage";
import { pipelineCrossAt } from "../../B05_Profile/B05_Profile_UI_RouteEdit_Cross";
import type { CrossPreviewResponse } from "../../B05_Profile/B05_Profile_Api_Replan";
const SAME_M = 1e-6;
/**
* 지금 계획선(초안 포함)으로 그 누가거리 임시 횡단 — 묶음이 아직 안 셌거나 격자 없음 · 분석 지형 ≠ 설계 지형이면 null.
*/
export async function pipelineCrossAtChainage(
projectId: string,
chainageM: number,
intervalM: number,
): Promise<CrossPreviewResponse | null> {
const pipe = pagePipeline(projectId);
const route = pipe?.output("route");
const profile = pipe?.output("profile");
const ground = pipe?.output("ground");
if (!pipe || !route || !profile || !ground) return null;
const deps = groundStageDeps(projectId);
const local = deps.grid();
const terrain = pipe.input("route")?.terrain ?? null;
if (!local || (terrain !== null && terrain !== local.design)) return null;
const inputs = {
route: pipe.input("route"),
alignment: pipe.input("alignment"),
cross: pipe.input("cross"),
pipes: pipe.input("pipes"),
structures: pipe.input("structures"),
};
const existing = ground.stations.find((s) => Math.abs(s.chainageM - chainageM) < SAME_M);
let station = existing ?? null;
if (!station) {
const probe = probeStation(route.line, route.curves, ground.stations, chainageM);
if (!probe) return null;
// ground 단계 그대로 — 측점 하나 · 종단 지반선은 안 뜸(노선 선 빼고 넘김)
const ctx = {
generation: 0,
range: null,
signal: new AbortController().signal,
inputs,
out: { stations: [probe], route: { ...route, line: null } },
before: {},
prev: null,
} as unknown as StageContext<"ground">;
const filled = (await createGroundStage(deps)(ctx)) as GroundOutput;
station = filled.stations[0] ?? null;
}
if (!station) return null;
// 시설 맥락 = 지금 현황을 그 측점에 얹은 그림에서(묶음 cross 와 같음 · 457)
const status = shapeStatus(
inputs.pipes,
inputs.structures,
await fetchStructureTypes(true).catch(() => []),
);
const shaped = crossWithShapes(
crossAtSeat(inputs.cross as CrossInput | null, [station]),
[station],
status,
);
const out = {
profile,
ground: { stations: [station], longitudinal: { chainageM: [], z: [] } },
} as Partial<Record<StageName, unknown>>;
const cross: CrossOutput = createCrossStage({ grid: deps.grid }).stage({
generation: 0,
range: null,
signal: new AbortController().signal,
inputs: { ...inputs, cross: crossWithUphill(shaped, [station]) },
out,
before: {},
prev: null,
} as unknown as StageContext<"cross">);
// 평면 우측 횡단 판과 같은 꼴(그림 · 요약) — 이 측점 하나만 실은 묶음처럼 넘김
const one = {
output: (name: StageName) =>
name === "ground" ? { stations: [station] } : name === "cross" ? cross : null,
} as unknown as Parameters<typeof pipelineCrossAt>[0];
return pipelineCrossAt(one, chainageM, intervalM, undefined);
}