/* ============================================================================= * A00_Common/design/logic/design_calc_node_profile.ts * 계산 Node 입구(`design_calc_node.ts`)의 종단 몫(PLAN 511-B B-3) — 첫 계획선 꼭짓점 · 계획선 짓기 · * 노선 바뀜 계획고 옮김. 브라우저 묶음이 쓰는 같은 TS · 파이썬은 `B05_Profile_Engine_Grade_Node.py` 로 * (늘 떠 있는 Node · 출력 = 반올림 전 값 · 정본 꼴 0.000001 m 는 파이썬이 적음). * * profile.base 입력 profile { chainage · ground(파이썬 `ground_profile`) · policy(`AlignmentPolicy.as_dict`) · * ground_following { stations: [측점 m] · start_offset_m · end_offset_m } } → * 꼭짓점(`groundFollowingNodes` · 반올림 전) · 못 서는 땅 null(파이썬 2~4 순위) * profile.alignment 입력 profile { chainage · ground · stations: [{ station_id · chainage_m }] · policy · * base_pvi(없으면 profile.base 출력) · edits · only_explicit_curves } → `buildAlignment` * profile.carry 입력 carry_profile { old_xy · new_xy · old_base · edits · stations · half_m · * seat?{ ground_s · ground_z · policy · base_pvi · born } } → * { edits(`carryEdits`) · base_pvi(`seatRouteMoved` · 높이 고친 측점 = 옮긴 편집 열쇠) } * ========================================================================== */ import { buildAlignment, emptyEdits, type AlignmentEdits, type AlignmentNode, type AlignmentPolicy, } from "../../../B05_Profile/logic/B05_Profile_UI_Profile_Alignment"; import { groundFollowingNodes } from "../../../B05_Profile/logic/B05_Profile_Grade_Ground"; import { carryEdits } from "../../../B05_Profile/logic/B05_Profile_Grade_Carry"; import { seatRouteMoved } from "../../../B05_Profile/logic/B05_Profile_Pipeline_Grade"; type Line = Array<[number, number]>; const xyOf = (line: Line) => line.map(([x, y]) => ({ x, y })); export interface ProfileInputs { profile?: { chainage: number[]; ground: number[]; policy: AlignmentPolicy; stations?: Array<{ station_id: string | null; chainage_m: number }>; base_pvi?: AlignmentNode[] | null; edits?: AlignmentEdits | null; only_explicit_curves?: boolean; ground_following?: { stations: number[]; start_offset_m?: number; end_offset_m?: number }; }; carry_profile?: { old_xy: Line; new_xy: Line; old_base: AlignmentNode[]; edits?: Partial | null; stations: number[]; half_m: number; seat?: { ground_s: number[]; ground_z: number[]; policy: AlignmentPolicy; base_pvi: AlignmentNode[]; born: number[]; } | null; }; } /** id → 셈(입구 `RUNNERS` 에 얹음) — 계획선은 꼭짓점이 없으면 같은 요청의 첫 계획선 꼭짓점 위. */ export function profileRunners(inputs: ProfileInputs): Record unknown> { let nodes: AlignmentNode[] | null | undefined; const base = (): AlignmentNode[] | null => { const profile = inputs.profile; const follow = profile?.ground_following; if (nodes === undefined) nodes = profile && follow ? groundFollowingNodes(profile.chainage, profile.ground, { stations: follow.stations, policy: profile.policy, startOffsetM: follow.start_offset_m ?? 0, endOffsetM: follow.end_offset_m ?? 0, }) : null; return nodes; }; return { "profile.base": base, "profile.alignment": () => { const profile = inputs.profile; const basePvi = profile?.base_pvi ?? base(); if (!profile || !basePvi) return null; return buildAlignment( { policy: profile.policy, onlyExplicitCurves: profile.only_explicit_curves === true, basePvi, chainage: profile.chainage, ground: profile.ground, stations: profile.stations ?? [], }, profile.edits ?? emptyEdits(), ); }, "profile.carry": () => { const carry = inputs.carry_profile; if (!carry) return null; const edits = carryEdits( xyOf(carry.old_xy), xyOf(carry.new_xy), carry.old_base, carry.edits, carry.stations, carry.half_m, ); const seat = carry.seat; if (!seat) return { edits, base_pvi: null }; const held = Object.keys(edits.station_offsets).map(Number); const base = { chainage: seat.ground_s, ground: seat.ground_z, policy: seat.policy, basePvi: seat.base_pvi, }; return { edits, base_pvi: seatRouteMoved(base, seat.born, held) }; }, }; }