vehicle-path2 2.3.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,31 @@
1
+ import type { Point, Line, Curve } from '../types/geometry';
2
+ /**
3
+ * Project a point onto a line segment.
4
+ *
5
+ * Returns:
6
+ * - offset: absolute distance from line.start along the line (clamped to [0, lineLength])
7
+ * - distance: perpendicular distance from point to the nearest point on the line
8
+ */
9
+ export declare function projectPointOnLine(point: Point, line: Line): {
10
+ offset: number;
11
+ distance: number;
12
+ };
13
+ /**
14
+ * Compute the valid [min, max] offset range for placing the rear axle of a
15
+ * multi-axle vehicle on a line.
16
+ *
17
+ * - min is always 0 (rear axle at line start)
18
+ * - max is lineLength - totalAxleSpacing (so all axles fit on the line)
19
+ * - If the vehicle is too long for the line, returns [0, 0]
20
+ */
21
+ export declare function getValidRearOffsetRange(line: Line, axleSpacings: number[]): [number, number];
22
+ /**
23
+ * Compute the minimum length a line must have so that all attached curve
24
+ * offsets (fromOffset / toOffset) remain within valid bounds.
25
+ *
26
+ * Only absolute offsets contribute to the minimum — percentage-based offsets
27
+ * scale with the line and therefore impose no hard minimum.
28
+ *
29
+ * Returns 0 if no curves are attached or all offsets are percentage-based.
30
+ */
31
+ export declare function computeMinLineLength(lineId: string, curves: Curve[]): number;
@@ -9,11 +9,11 @@
9
9
  * ```typescript
10
10
  * import { PathEngine } from 'vehicle-path/core'
11
11
  *
12
- * const engine = new PathEngine({ wheelbase: 30, tangentMode: 'proportional-40' })
12
+ * const engine = new PathEngine({ maxWheelbase: 100, tangentMode: 'proportional-40' })
13
13
  *
14
14
  * engine.setScene(lines, curves)
15
15
  *
16
- * const state = engine.initializeVehicle('line-1', 0)
16
+ * const state = engine.initializeVehicle('line-1', 0, { axleSpacings: [40] })
17
17
  * const execution = engine.preparePath(state, 'line-3', 1.0, true)
18
18
  *
19
19
  * // In your animation/game loop:
@@ -26,6 +26,7 @@
26
26
  * ```
27
27
  */
28
28
  import type { Line, Curve, Point } from './types/geometry';
29
+ import type { VehicleDefinition } from './types/vehicle';
29
30
  import type { MovementConfig, CurveData } from './types/movement';
30
31
  import type { PathResult } from './algorithms/pathFinding';
31
32
  import type { TangentMode } from './types/config';
@@ -37,14 +38,12 @@ export interface PathEngineConfig {
37
38
  * Multi-axle position state for use with PathEngine.
38
39
  * axles[0] = terdepan, axles[N-1] = paling belakang.
39
40
  */
40
- export interface VehiclePathState {
41
+ export interface VehiclePathState extends VehicleDefinition {
41
42
  axles: Array<{
42
43
  lineId: string;
43
44
  offset: number;
44
45
  position: Point;
45
46
  }>;
46
- /** N-1 jarak arc-length antar axle berurutan */
47
- axleSpacings: number[];
48
47
  }
49
48
  /**
50
49
  * Active path execution state for a vehicle in motion.
@@ -127,10 +126,11 @@ export declare class PathEngine {
127
126
  *
128
127
  * @param lineId - The line to place the vehicle on
129
128
  * @param rearOffset - Absolute distance offset untuk axle paling belakang
130
- * @param axleSpacings - Jarak antar axle berurutan (N-1 nilai untuk N axle)
129
+ * @param vehicle - VehicleDefinition (or any object extending it with axleSpacings)
131
130
  * @returns Initial VehiclePathState, or null if lineId does not exist
131
+ * @throws if axleSpacings is empty
132
132
  */
133
- initializeVehicle(lineId: string, rearOffset: number, axleSpacings: number[]): VehiclePathState | null;
133
+ initializeVehicle(lineId: string, rearOffset: number, vehicle: VehicleDefinition): VehiclePathState | null;
134
134
  /**
135
135
  * Prepare a path from the vehicle's current position to a target.
136
136
  *
@@ -11,7 +11,7 @@
11
11
  * ```
12
12
  */
13
13
  export type { Point, Line, BezierCurve, Curve } from './types/geometry';
14
- export type { VehicleState, VehicleStart, Vehicle, AxleState, GotoCommand, GotoCompletionInfo, GotoCompletionCallback } from './types/vehicle';
14
+ export type { VehicleDefinition, VehicleState, VehicleStart, Vehicle, AxleState, GotoCommand, GotoCompletionInfo, GotoCompletionCallback } from './types/vehicle';
15
15
  export type { CurveData, AxleExecutionState, PathExecutionState, VehicleMovementState, MovementConfig, SceneContext } from './types/movement';
16
16
  export type { TangentMode } from './types/config';
17
17
  export type { CoordinateInput, SceneLineInput, SceneConnectionInput, SceneConfig, VehicleInput, VehicleUpdateInput, ConnectionUpdateInput, GotoCommandInput } from './types/api';
@@ -20,3 +20,4 @@ export { initializeMovingVehicle, createInitialMovementState, initializeAllVehic
20
20
  export { PathEngine, type PathEngineConfig, type VehiclePathState, type PathExecution } from './engine';
21
21
  export { distance, normalize, getPointOnLine, getPointOnLineByOffset, getPointOnBezier, createBezierCurve, buildArcLengthTable, distanceToT, getArcLength, calculateTangentLength, isPointNearPoint, type ArcLengthEntry, type CurveOffsetOptions } from './algorithms/math';
22
22
  export { serializeScene, deserializeScene, type SceneSnapshot } from './snapshot';
23
+ export { projectPointOnLine, getValidRearOffsetRange, computeMinLineLength } from './algorithms/geometry';
@@ -10,24 +10,10 @@ export interface SceneSnapshot {
10
10
  toOffset: number;
11
11
  toIsPercentage: boolean;
12
12
  }>;
13
- vehicles: Array<{
14
- id: string;
15
- lineId: string;
16
- axles: Array<{
17
- offset: number;
18
- }>;
19
- axleSpacings: number[];
20
- isPercentage: boolean;
21
- }>;
22
13
  }
23
14
  /**
24
- * Serialize scene state to a JSON string suitable for clipboard or storage.
25
- * Strips derived fields (bezier curves, axle positions) only source-of-truth
26
- * data is included.
27
- *
28
- * Note: lineId is per-vehicle (not per-axle) because this snapshot captures
29
- * static placement where all axles share the same line. For mid-movement state,
30
- * use AxleState directly.
15
+ * Serialize scene state (lines + curves) to a JSON string.
16
+ * Vehicles are NOT included vehicle persistence is the client's responsibility.
31
17
  */
32
18
  export declare function serializeScene(lines: Line[], curves: Array<{
33
19
  id: string;
@@ -37,18 +23,10 @@ export declare function serializeScene(lines: Line[], curves: Array<{
37
23
  fromIsPercentage?: boolean;
38
24
  toOffset: number;
39
25
  toIsPercentage?: boolean;
40
- }>, vehicles: Array<{
41
- id: string;
42
- axles: Array<{
43
- lineId: string;
44
- offset: number;
45
- [key: string]: unknown;
46
- }>;
47
- axleSpacings: number[];
48
- isPercentage?: boolean;
49
26
  }>): string;
50
27
  /**
51
28
  * Deserialize a JSON string back into a SceneSnapshot.
52
- * Throws if the string is not valid JSON or missing required fields.
29
+ * Throws if the string is not valid JSON or missing required fields (lines, curves).
30
+ * Extra fields in the JSON (e.g. legacy "vehicles") are silently ignored.
53
31
  */
54
32
  export declare function deserializeScene(json: string): SceneSnapshot;
@@ -2,6 +2,17 @@
2
2
  * Vehicle-related types
3
3
  */
4
4
  import type { Point } from './geometry';
5
+ /**
6
+ * Base definition of a vehicle's physical structure.
7
+ * Client code is free to extend this with additional fields (id, name, color, etc).
8
+ *
9
+ * @example
10
+ * interface MyVehicle extends VehicleDefinition { id: string; name: string }
11
+ */
12
+ export interface VehicleDefinition {
13
+ /** N-1 arc-length spacings between consecutive axles. axleSpacings[i] = distance from axles[i] to axles[i+1]. */
14
+ axleSpacings: number[];
15
+ }
5
16
  /**
6
17
  * Animation state for a vehicle
7
18
  */
@@ -28,14 +39,13 @@ export interface AxleState {
28
39
  /**
29
40
  * Vehicle with runtime state (used during animation)
30
41
  */
31
- export interface Vehicle {
42
+ export interface Vehicle extends VehicleDefinition {
32
43
  id: string;
33
44
  lineId: string;
34
45
  offset: number;
35
46
  isPercentage: boolean;
36
47
  state: VehicleState;
37
48
  axles: AxleState[];
38
- axleSpacings: number[];
39
49
  }
40
50
  /**
41
51
  * Command to move a vehicle to a target position
package/dist/core.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-BUYdltrL.cjs");function o(r,n,t){const a={lines:r,curves:n.map(i=>({id:i.id,fromLineId:i.fromLineId,toLineId:i.toLineId,fromOffset:i.fromOffset,fromIsPercentage:i.fromIsPercentage??!1,toOffset:i.toOffset,toIsPercentage:i.toIsPercentage??!1})),vehicles:t.map(i=>({id:i.id,lineId:i.axles[0].lineId,axles:i.axles.map(l=>({offset:l.offset})),axleSpacings:i.axleSpacings,isPercentage:i.isPercentage??!1}))};return JSON.stringify(a,null,2)}function s(r){let n;try{n=JSON.parse(r)}catch{throw new Error("deserializeScene: invalid JSON")}if(!n||typeof n!="object"||Array.isArray(n))throw new Error("deserializeScene: expected a JSON object");const t=n;if(!Array.isArray(t.lines))throw new Error('deserializeScene: missing "lines"');if(!Array.isArray(t.curves))throw new Error('deserializeScene: missing "curves"');if(!Array.isArray(t.vehicles))throw new Error('deserializeScene: missing "vehicles"');return{lines:t.lines,curves:t.curves,vehicles:t.vehicles}}exports.PathEngine=e.PathEngine;exports.arcLengthToSegmentPosition=e.arcLengthToSegmentPosition;exports.buildArcLengthTable=e.buildArcLengthTable;exports.buildGraph=e.buildGraph;exports.calculateBezierArcLength=e.calculateBezierArcLength;exports.calculateFrontAxlePosition=e.calculateFrontAxlePosition;exports.calculateInitialAxlePositions=e.calculateInitialAxlePositions;exports.calculatePositionOnCurve=e.calculatePositionOnCurve;exports.calculatePositionOnLine=e.calculatePositionOnLine;exports.calculateTangentLength=e.calculateTangentLength;exports.createBezierCurve=e.createBezierCurve;exports.createInitialMovementState=e.createInitialMovementState;exports.distance=e.distance;exports.distanceToT=e.distanceToT;exports.findPath=e.findPath;exports.getArcLength=e.getArcLength;exports.getCumulativeArcLength=e.getCumulativeArcLength;exports.getLineLength=e.getLineLength;exports.getPointOnBezier=e.getPointOnBezier;exports.getPointOnLine=e.getPointOnLine;exports.getPointOnLineByOffset=e.getPointOnLineByOffset;exports.getPositionFromOffset=e.getPositionFromOffset;exports.handleArrival=e.handleArrival;exports.initializeAllVehicles=e.initializeAllVehicles;exports.initializeMovingVehicle=e.initializeMovingVehicle;exports.isPointNearPoint=e.isPointNearPoint;exports.moveVehicle=e.moveVehicle;exports.normalize=e.normalize;exports.prepareCommandPath=e.prepareCommandPath;exports.resolveFromLineOffset=e.resolveFromLineOffset;exports.resolveToLineOffset=e.resolveToLineOffset;exports.updateAxlePosition=e.updateAxlePosition;exports.deserializeScene=s;exports.serializeScene=o;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-C4FckUel.cjs");function f(r,n){const i={lines:r,curves:n.map(t=>({id:t.id,fromLineId:t.fromLineId,toLineId:t.toLineId,fromOffset:t.fromOffset,fromIsPercentage:t.fromIsPercentage??!1,toOffset:t.toOffset,toIsPercentage:t.toIsPercentage??!1}))};return JSON.stringify(i,null,2)}function g(r){let n;try{n=JSON.parse(r)}catch{throw new Error("deserializeScene: invalid JSON")}if(!n||typeof n!="object"||Array.isArray(n))throw new Error("deserializeScene: expected a JSON object");const i=n;if(!Array.isArray(i.lines))throw new Error('deserializeScene: missing "lines"');if(!Array.isArray(i.curves))throw new Error('deserializeScene: missing "curves"');return{lines:i.lines,curves:i.curves}}function u(r,n){const i=n.end.x-n.start.x,t=n.end.y-n.start.y,o=i*i+t*t;if(o===0)return{offset:0,distance:Math.sqrt((r.x-n.start.x)**2+(r.y-n.start.y)**2)};const a=Math.max(0,Math.min(1,((r.x-n.start.x)*i+(r.y-n.start.y)*t)/o)),s=n.start.x+a*i,c=n.start.y+a*t,l=Math.sqrt((r.x-s)**2+(r.y-c)**2);return{offset:a*Math.sqrt(o),distance:l}}function h(r,n){const i=e.getLineLength(r),t=n.reduce((a,s)=>a+s,0);return[0,Math.max(0,i-t)]}function d(r,n){let i=0;for(const t of n)t.fromLineId===r&&!t.fromIsPercentage&&t.fromOffset!==void 0&&(i=Math.max(i,t.fromOffset)),t.toLineId===r&&!t.toIsPercentage&&t.toOffset!==void 0&&(i=Math.max(i,t.toOffset));return i}exports.PathEngine=e.PathEngine;exports.arcLengthToSegmentPosition=e.arcLengthToSegmentPosition;exports.buildArcLengthTable=e.buildArcLengthTable;exports.buildGraph=e.buildGraph;exports.calculateBezierArcLength=e.calculateBezierArcLength;exports.calculateFrontAxlePosition=e.calculateFrontAxlePosition;exports.calculateInitialAxlePositions=e.calculateInitialAxlePositions;exports.calculatePositionOnCurve=e.calculatePositionOnCurve;exports.calculatePositionOnLine=e.calculatePositionOnLine;exports.calculateTangentLength=e.calculateTangentLength;exports.createBezierCurve=e.createBezierCurve;exports.createInitialMovementState=e.createInitialMovementState;exports.distance=e.distance;exports.distanceToT=e.distanceToT;exports.findPath=e.findPath;exports.getArcLength=e.getArcLength;exports.getCumulativeArcLength=e.getCumulativeArcLength;exports.getLineLength=e.getLineLength;exports.getPointOnBezier=e.getPointOnBezier;exports.getPointOnLine=e.getPointOnLine;exports.getPointOnLineByOffset=e.getPointOnLineByOffset;exports.getPositionFromOffset=e.getPositionFromOffset;exports.handleArrival=e.handleArrival;exports.initializeAllVehicles=e.initializeAllVehicles;exports.initializeMovingVehicle=e.initializeMovingVehicle;exports.isPointNearPoint=e.isPointNearPoint;exports.moveVehicle=e.moveVehicle;exports.normalize=e.normalize;exports.prepareCommandPath=e.prepareCommandPath;exports.resolveFromLineOffset=e.resolveFromLineOffset;exports.resolveToLineOffset=e.resolveToLineOffset;exports.updateAxlePosition=e.updateAxlePosition;exports.computeMinLineLength=d;exports.deserializeScene=g;exports.getValidRearOffsetRange=h;exports.projectPointOnLine=u;exports.serializeScene=f;
package/dist/core.js CHANGED
@@ -1,8 +1,9 @@
1
- import { P as f, a as g, b as h, c as d, d as u, e as m, f as P, g as A, h as v, i as L, j as O, k as p, l as S, m as z, n as y, o as w, p as x, q as I, r as b, s as E, t as T, u as B, v as C, w as N, x as j, y as J, z as F, A as V, B as M, C as k, D as q, E as D } from "./index-D-HctiIv.js";
2
- function n(s, a, i) {
3
- const r = {
4
- lines: s,
5
- curves: a.map((e) => ({
1
+ import { g as f } from "./index-BV93143R.js";
2
+ import { P as y, a as v, b as A, c as S, d as p, e as z, f as I, h as M, i as w, j as b, k as j, l as E, m as T, n as q, o as B, p as C, q as N, r as J, s as V, t as F, u as R, v as k, w as D, x as G, y as X, z as Y, A as H, B as K, C as Q, D as U, E as W } from "./index-BV93143R.js";
3
+ function u(a, t) {
4
+ const s = {
5
+ lines: a,
6
+ curves: t.map((e) => ({
6
7
  id: e.id,
7
8
  fromLineId: e.fromLineId,
8
9
  toLineId: e.toLineId,
@@ -10,69 +11,88 @@ function n(s, a, i) {
10
11
  fromIsPercentage: e.fromIsPercentage ?? !1,
11
12
  toOffset: e.toOffset,
12
13
  toIsPercentage: e.toIsPercentage ?? !1
13
- })),
14
- vehicles: i.map((e) => ({
15
- id: e.id,
16
- lineId: e.axles[0].lineId,
17
- axles: e.axles.map((t) => ({ offset: t.offset })),
18
- axleSpacings: e.axleSpacings,
19
- isPercentage: e.isPercentage ?? !1
20
14
  }))
21
15
  };
22
- return JSON.stringify(r, null, 2);
16
+ return JSON.stringify(s, null, 2);
23
17
  }
24
- function o(s) {
25
- let a;
18
+ function g(a) {
19
+ let t;
26
20
  try {
27
- a = JSON.parse(s);
21
+ t = JSON.parse(a);
28
22
  } catch {
29
23
  throw new Error("deserializeScene: invalid JSON");
30
24
  }
31
- if (!a || typeof a != "object" || Array.isArray(a))
25
+ if (!t || typeof t != "object" || Array.isArray(t))
32
26
  throw new Error("deserializeScene: expected a JSON object");
33
- const i = a;
34
- if (!Array.isArray(i.lines)) throw new Error('deserializeScene: missing "lines"');
35
- if (!Array.isArray(i.curves)) throw new Error('deserializeScene: missing "curves"');
36
- if (!Array.isArray(i.vehicles)) throw new Error('deserializeScene: missing "vehicles"');
27
+ const s = t;
28
+ if (!Array.isArray(s.lines)) throw new Error('deserializeScene: missing "lines"');
29
+ if (!Array.isArray(s.curves)) throw new Error('deserializeScene: missing "curves"');
37
30
  return {
38
- lines: i.lines,
39
- curves: i.curves,
40
- vehicles: i.vehicles
31
+ lines: s.lines,
32
+ curves: s.curves
41
33
  };
42
34
  }
35
+ function h(a, t) {
36
+ const s = t.end.x - t.start.x, e = t.end.y - t.start.y, n = s * s + e * e;
37
+ if (n === 0)
38
+ return { offset: 0, distance: Math.sqrt(
39
+ (a.x - t.start.x) ** 2 + (a.y - t.start.y) ** 2
40
+ ) };
41
+ const r = Math.max(
42
+ 0,
43
+ Math.min(
44
+ 1,
45
+ ((a.x - t.start.x) * s + (a.y - t.start.y) * e) / n
46
+ )
47
+ ), i = t.start.x + r * s, o = t.start.y + r * e, c = Math.sqrt((a.x - i) ** 2 + (a.y - o) ** 2);
48
+ return { offset: r * Math.sqrt(n), distance: c };
49
+ }
50
+ function L(a, t) {
51
+ const s = f(a), e = t.reduce((r, i) => r + i, 0);
52
+ return [0, Math.max(0, s - e)];
53
+ }
54
+ function O(a, t) {
55
+ let s = 0;
56
+ for (const e of t)
57
+ e.fromLineId === a && !e.fromIsPercentage && e.fromOffset !== void 0 && (s = Math.max(s, e.fromOffset)), e.toLineId === a && !e.toIsPercentage && e.toOffset !== void 0 && (s = Math.max(s, e.toOffset));
58
+ return s;
59
+ }
43
60
  export {
44
- f as PathEngine,
45
- g as arcLengthToSegmentPosition,
46
- h as buildArcLengthTable,
47
- d as buildGraph,
48
- u as calculateBezierArcLength,
49
- m as calculateFrontAxlePosition,
50
- P as calculateInitialAxlePositions,
51
- A as calculatePositionOnCurve,
52
- v as calculatePositionOnLine,
53
- L as calculateTangentLength,
54
- O as createBezierCurve,
55
- p as createInitialMovementState,
56
- o as deserializeScene,
57
- S as distance,
58
- z as distanceToT,
59
- y as findPath,
60
- w as getArcLength,
61
- x as getCumulativeArcLength,
62
- I as getLineLength,
63
- b as getPointOnBezier,
64
- E as getPointOnLine,
65
- T as getPointOnLineByOffset,
66
- B as getPositionFromOffset,
67
- C as handleArrival,
68
- N as initializeAllVehicles,
69
- j as initializeMovingVehicle,
70
- J as isPointNearPoint,
71
- F as moveVehicle,
72
- V as normalize,
73
- M as prepareCommandPath,
74
- k as resolveFromLineOffset,
75
- q as resolveToLineOffset,
76
- n as serializeScene,
77
- D as updateAxlePosition
61
+ y as PathEngine,
62
+ v as arcLengthToSegmentPosition,
63
+ A as buildArcLengthTable,
64
+ S as buildGraph,
65
+ p as calculateBezierArcLength,
66
+ z as calculateFrontAxlePosition,
67
+ I as calculateInitialAxlePositions,
68
+ M as calculatePositionOnCurve,
69
+ w as calculatePositionOnLine,
70
+ b as calculateTangentLength,
71
+ O as computeMinLineLength,
72
+ j as createBezierCurve,
73
+ E as createInitialMovementState,
74
+ g as deserializeScene,
75
+ T as distance,
76
+ q as distanceToT,
77
+ B as findPath,
78
+ C as getArcLength,
79
+ N as getCumulativeArcLength,
80
+ f as getLineLength,
81
+ J as getPointOnBezier,
82
+ V as getPointOnLine,
83
+ F as getPointOnLineByOffset,
84
+ R as getPositionFromOffset,
85
+ L as getValidRearOffsetRange,
86
+ k as handleArrival,
87
+ D as initializeAllVehicles,
88
+ G as initializeMovingVehicle,
89
+ X as isPointNearPoint,
90
+ Y as moveVehicle,
91
+ H as normalize,
92
+ K as prepareCommandPath,
93
+ h as projectPointOnLine,
94
+ Q as resolveFromLineOffset,
95
+ U as resolveToLineOffset,
96
+ u as serializeScene,
97
+ W as updateAxlePosition
78
98
  };