fix(계산묶음): 346-1 route 단계 곡선표 채움 — 계약 RouteLine.curves(start · apex · end · radius_m) = buildEditedPolyline 곡선표 · savedLine 도 곡선표 함께 · typecheck 빨강 풀림 · 시험 서버 곡선표와 같음
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Se7ytqbohmBQnfYpwYFnYf
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
* ========================================================================== */
|
||||
|
||||
import type {
|
||||
DesignCurve,
|
||||
GroundOutput,
|
||||
GroundSample,
|
||||
PipelineStation,
|
||||
@@ -38,35 +39,43 @@ import {
|
||||
export interface RouteStageDeps {
|
||||
/** 곡선 기본 반지름(m) — 편집기 `minRadiusM`(계획 기준 · 0 = 없음). */
|
||||
minRadiusM(): number;
|
||||
/** 초안이 없을 때(= 저장분 그대로) 그릴 노선 — 없으면 null. */
|
||||
savedLine(): Vertex[] | null;
|
||||
/** 초안이 없을 때(= 저장분 그대로) 그릴 노선 · 곡선표 — 없으면 null. */
|
||||
savedLine(): { line: Vertex[]; curves: DesignCurve[] } | null;
|
||||
}
|
||||
|
||||
/** 정점마다 누가거리 — 첫 점 0. */
|
||||
export function routeLineOf(line: Vertex[]): RouteLine {
|
||||
export function routeLineOf(line: Vertex[], curves: DesignCurve[] = []): RouteLine {
|
||||
const chainage = [0];
|
||||
for (let i = 1; i < line.length; i += 1) {
|
||||
chainage.push(
|
||||
chainage[i - 1] + Math.hypot(line[i][0] - line[i - 1][0], line[i][1] - line[i - 1][1]),
|
||||
);
|
||||
}
|
||||
return { line, chainage, lengthM: chainage[chainage.length - 1] };
|
||||
return { line, curves, chainage, lengthM: chainage[chainage.length - 1] };
|
||||
}
|
||||
|
||||
/** 초안 → 그린 노선. 길이를 붙든 곡선은 교각에 맞춰 R 을 다시 잡음(편집기 `markEdited` 와 같은 차례). */
|
||||
export function draftLine(draft: RouteDraft, minRadiusM: number): Vertex[] {
|
||||
/** 초안 → 그린 노선 · 곡선표(계약 꼴). 길이를 붙든 곡선은 교각에 맞춰 R 을 다시 잡음(편집기
|
||||
* `markEdited` 와 같은 차례). */
|
||||
export function draftLine(
|
||||
draft: RouteDraft,
|
||||
minRadiusM: number,
|
||||
): { line: Vertex[]; curves: DesignCurve[] } {
|
||||
const nodes = draft.nodes.map((node): Vertex => [node[0], node[1]]);
|
||||
const radius = [...draft.curveRadius];
|
||||
applyArcLocks(nodes, [...draft.curveLock], [...draft.curveArc], radius);
|
||||
return buildEditedPolyline(nodes, draft.curveOn, radius, minRadiusM).vertices;
|
||||
const built = buildEditedPolyline(nodes, draft.curveOn, radius, minRadiusM);
|
||||
return {
|
||||
line: built.vertices,
|
||||
curves: built.curves.map(({ start, apex, end, radius_m }) => ({ start, apex, end, radius_m })),
|
||||
};
|
||||
}
|
||||
|
||||
export function createRouteStage(deps: RouteStageDeps): StageFn<"route"> {
|
||||
return (ctx) => {
|
||||
const draft = ctx.inputs.route;
|
||||
const line = draft ? draftLine(draft, deps.minRadiusM()) : deps.savedLine();
|
||||
if (!line || line.length < 2) throw new Error("그릴 노선이 없습니다.");
|
||||
return routeLineOf(line);
|
||||
const drawn = draft ? draftLine(draft, deps.minRadiusM()) : deps.savedLine();
|
||||
if (!drawn || drawn.line.length < 2) throw new Error("그릴 노선이 없습니다.");
|
||||
return routeLineOf(drawn.line, drawn.curves);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,9 @@ const draft = {
|
||||
rev: 1, nodes: job.nodes, curveOn: Array(n).fill(true), curveRadius: Array(n).fill(null),
|
||||
curveLock: Array(n).fill(null), curveArc: Array(n).fill(null), stationSnap: false, terrain: null,
|
||||
};
|
||||
const routeStage = createRouteStage({ minRadiusM: () => job.minRadius, savedLine: () => job.line });
|
||||
const routeStage = createRouteStage({
|
||||
minRadiusM: () => job.minRadius, savedLine: () => ({ line: job.line, curves: [] }),
|
||||
});
|
||||
const ctxOf = (extra) => ({
|
||||
generation: 1, range: null, signal: new AbortController().signal,
|
||||
inputs: { route: null, alignment: null, cross: null, pipes: null, structures: null },
|
||||
@@ -242,7 +244,14 @@ def test_route_ground_단계가_서버와_같고_범위_밖을_재사용한다(t
|
||||
steps = np.hypot(*np.diff(np.asarray(built.vertices), axis=0).T)
|
||||
assert abs(route["lengthM"] - float(steps.sum())) < 1e-6
|
||||
assert route["chainage"][0] == 0 and len(route["chainage"]) == len(route["line"])
|
||||
assert len(route["curves"]) == len(built.curves) > 0
|
||||
for ts, py in zip(route["curves"], built.curves, strict=True):
|
||||
assert set(ts) == {"start", "apex", "end", "radius_m"}
|
||||
assert abs(ts["radius_m"] - py.radius_m) < 1e-9
|
||||
for key in ("start", "apex", "end"):
|
||||
assert max(abs(a - b) for a, b in zip(ts[key], getattr(py, key))) < 1e-9
|
||||
assert out["saved"]["line"] == line
|
||||
assert out["saved"]["curves"] == []
|
||||
assert out["noRoute"]
|
||||
|
||||
# ground 브라우저 길 = 서버 generate_sections(측점 · 횡단 · 종단)
|
||||
|
||||
Reference in New Issue
Block a user