Files
Aislo/A00_Common/design/logic/design_calc_node_profile.ts
T

119 lines
4.7 KiB
TypeScript

/* =============================================================================
* 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<AlignmentEdits> | 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<string, () => 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) };
},
};
}