vehicle-path2 1.0.14 → 2.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.
Files changed (40) hide show
  1. package/README.md +168 -168
  2. package/dist/core/algorithms/pathFinding.d.ts +2 -2
  3. package/dist/core/algorithms/vehicleMovement.d.ts +29 -20
  4. package/dist/core/engine.d.ts +160 -0
  5. package/dist/core/index.d.ts +3 -2
  6. package/dist/core/types/api.d.ts +9 -0
  7. package/dist/core/types/movement.d.ts +2 -3
  8. package/dist/core/types/vehicle.d.ts +4 -2
  9. package/dist/core.cjs +1 -1
  10. package/dist/core.js +33 -541
  11. package/dist/index.d.ts +4 -34
  12. package/dist/utils/event-emitter.d.ts +4 -6
  13. package/dist/vehicle-path.cjs +1 -1
  14. package/dist/vehicle-path.js +729 -59
  15. package/package.json +83 -103
  16. package/dist/animation-loop-bZEm2pMN.js +0 -37
  17. package/dist/animation-loop-fC2LjxCd.cjs +0 -1
  18. package/dist/react/dsl-hooks/useInitialMovement.d.ts +0 -24
  19. package/dist/react/dsl-hooks/useMovementSequence.d.ts +0 -27
  20. package/dist/react/dsl-hooks/useSceneDefinition.d.ts +0 -22
  21. package/dist/react/hooks/useAnimation.d.ts +0 -47
  22. package/dist/react/hooks/useMovementQueue.d.ts +0 -53
  23. package/dist/react/hooks/useScene.d.ts +0 -78
  24. package/dist/react/hooks/useVehicleSimulation.d.ts +0 -128
  25. package/dist/react/hooks/useVehicles.d.ts +0 -55
  26. package/dist/react/index.d.ts +0 -48
  27. package/dist/react/providers/useVehicleEvents.d.ts +0 -78
  28. package/dist/react.cjs +0 -1
  29. package/dist/react.js +0 -18
  30. package/dist/useVehicleEvents-BZVmIugl.cjs +0 -3
  31. package/dist/useVehicleEvents-CabztfQ4.js +0 -940
  32. package/dist/utils/animation-loop.d.ts +0 -105
  33. package/dist/utils/dsl-parser.d.ts +0 -151
  34. package/dist/utils/index.d.ts +0 -15
  35. package/dist/utils/type-converters.d.ts +0 -40
  36. package/dist/utils/vehicle-helpers.d.ts +0 -8
  37. package/dist/utils.cjs +0 -1
  38. package/dist/utils.js +0 -17
  39. package/dist/vehicle-helpers-BgD4BTAJ.js +0 -275
  40. package/dist/vehicle-helpers-DrnYWjm3.cjs +0 -7
package/README.md CHANGED
@@ -1,168 +1,168 @@
1
- # vehicle-path2
2
-
3
- Library untuk simulasi pergerakan kendaraan dual-axle sepanjang jalur.
4
-
5
- ## Instalasi
6
-
7
- ```bash
8
- npm install vehicle-path2
9
- ```
10
-
11
- ## Quick Start
12
-
13
- ```tsx
14
- import { useVehicleSimulation } from 'vehicle-path2/react'
15
-
16
- function App() {
17
- const sim = useVehicleSimulation({ wheelbase: 30 })
18
-
19
- // Buat jalur
20
- sim.addLine({ id: 'line1', start: [0, 0], end: [400, 0] })
21
- sim.addLine({ id: 'line2', start: [400, 0], end: [400, 300] })
22
- sim.connect('line1', 'line2')
23
-
24
- // Tambah kendaraan
25
- sim.addVehicles({ id: 'v1', lineId: 'line1', position: 0 })
26
-
27
- // Gerakkan ke tujuan
28
- sim.goto({ id: 'v1', lineId: 'line2', position: 1.0 })
29
-
30
- // Jalankan animasi
31
- sim.prepare()
32
- sim.tick(5) // panggil di animation loop
33
- }
34
- ```
35
-
36
- ## API
37
-
38
- ### Format Posisi
39
-
40
- Semua nilai posisi menggunakan format **0-1** untuk persentase:
41
- - `0` = 0% (awal line)
42
- - `0.5` = 50% (tengah line)
43
- - `1` = 100% (ujung line)
44
-
45
- Untuk posisi absolut (dalam satuan koordinat), gunakan `isPercentage: false`.
46
-
47
- ### Setup
48
-
49
- ```ts
50
- const sim = useVehicleSimulation({
51
- wheelbase: 30, // jarak antar roda
52
- tangentMode: 'proportional-40' // mode kurva (opsional)
53
- })
54
- ```
55
-
56
- ### Scene
57
-
58
- ```ts
59
- sim.addLine({ id: 'line1', start: [0, 0], end: [400, 0] })
60
- sim.updateLine('line1', { end: [500, 100] })
61
- sim.removeLine('line1')
62
- sim.clearScene()
63
- ```
64
-
65
- ### Koneksi
66
-
67
- ```ts
68
- sim.connect('line1', 'line2')
69
- sim.connect('line1', 'line2', { fromOffset: 0.8, toOffset: 0.2 })
70
- sim.connect('line1', 'line2', { fromOffset: 150, fromIsPercentage: false, toOffset: 50, toIsPercentage: false })
71
- sim.updateConnection('line1', 'line2', { fromOffset: 0.5 }) // update offset
72
- sim.updateConnection('line1', 'line2', { toOffset: 100, toIsPercentage: false }) // absolute
73
- sim.disconnect('line1', 'line2')
74
- ```
75
-
76
- ### Kendaraan
77
-
78
- ```ts
79
- sim.addVehicles({ id: 'v1', lineId: 'line1', position: 0 })
80
- sim.addVehicles({ id: 'v2', lineId: 'line1', position: 150, isPercentage: false }) // absolute
81
- sim.updateVehicle('v1', { position: 0.5 }) // pindah ke 50%
82
- sim.updateVehicle('v1', { lineId: 'line2' }) // pindah ke line lain
83
- sim.updateVehicle('v1', { lineId: 'line2', position: 0.8 }) // pindah ke 80% di line2
84
- sim.removeVehicle('v1')
85
- sim.clearVehicles()
86
- ```
87
-
88
- ### Pergerakan
89
-
90
- ```ts
91
- sim.goto({ id: 'v1', lineId: 'line2' }) // default position = 1.0 (ujung)
92
- sim.goto({ id: 'v1', lineId: 'line2', position: 0.5 }) // 0.5 = tengah line
93
- sim.goto({ id: 'v1', lineId: 'line2', position: 150, isPercentage: false }) // absolute
94
- sim.goto({ id: 'v1', lineId: 'line2', payload: { orderId: '123' } }) // dengan payload
95
- sim.clearQueue('v1')
96
- ```
97
-
98
- ### Animasi
99
-
100
- ```ts
101
- sim.prepare() // siapkan sebelum animasi
102
- sim.tick(5) // gerakkan 5 pixel per tick
103
- sim.reset() // kembali ke posisi awal
104
- sim.isMoving() // cek ada yang bergerak
105
- sim.continueVehicle('v1') // lanjutkan vehicle yang wait
106
- ```
107
-
108
- ### Load dari DSL
109
-
110
- ```ts
111
- sim.loadFromDSL(`
112
- line1 : (0, 0) -> (400, 0)
113
- line2 : (400, 0) -> (400, 300)
114
- line1 -> line2
115
- `)
116
- ```
117
-
118
- ### State
119
-
120
- ```ts
121
- sim.lines // semua line
122
- sim.curves // semua koneksi
123
- sim.vehicles // kendaraan (posisi awal)
124
- sim.movingVehicles // kendaraan (posisi saat animasi)
125
- ```
126
-
127
- ## Contoh Lengkap
128
-
129
- ```tsx
130
- import { useVehicleSimulation } from 'vehicle-path2/react'
131
- import { useEffect } from 'react'
132
-
133
- function AnimatedVehicle() {
134
- const sim = useVehicleSimulation({ wheelbase: 30 })
135
-
136
- useEffect(() => {
137
- sim.addLine({ id: 'line1', start: [100, 100], end: [500, 100] })
138
- sim.addVehicles({ id: 'v1', lineId: 'line1', position: 0 })
139
- sim.goto({ id: 'v1', lineId: 'line1', position: 1.0 })
140
- sim.prepare()
141
-
142
- const animate = () => {
143
- if (sim.tick(3)) {
144
- requestAnimationFrame(animate)
145
- }
146
- }
147
- requestAnimationFrame(animate)
148
- }, [])
149
-
150
- return (
151
- <svg width={600} height={200}>
152
- {sim.movingVehicles.map(v => (
153
- <circle
154
- key={v.id}
155
- cx={v.rear.position.x}
156
- cy={v.rear.position.y}
157
- r={10}
158
- fill="blue"
159
- />
160
- ))}
161
- </svg>
162
- )
163
- }
164
- ```
165
-
166
- ## License
167
-
168
- MIT
1
+ # vehicle-path2
2
+
3
+ Library untuk simulasi pergerakan kendaraan dual-axle sepanjang jalur.
4
+
5
+ ## Instalasi
6
+
7
+ ```bash
8
+ npm install vehicle-path2
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```tsx
14
+ import { useVehicleSimulation } from 'vehicle-path2/react'
15
+
16
+ function App() {
17
+ const sim = useVehicleSimulation({ wheelbase: 30 })
18
+
19
+ // Buat jalur
20
+ sim.addLine({ id: 'line1', start: [0, 0], end: [400, 0] })
21
+ sim.addLine({ id: 'line2', start: [400, 0], end: [400, 300] })
22
+ sim.connect('line1', 'line2')
23
+
24
+ // Tambah kendaraan
25
+ sim.addVehicles({ id: 'v1', lineId: 'line1', position: 0 })
26
+
27
+ // Gerakkan ke tujuan
28
+ sim.goto({ id: 'v1', lineId: 'line2', position: 1.0 })
29
+
30
+ // Jalankan animasi
31
+ sim.prepare()
32
+ sim.tick(5) // panggil di animation loop
33
+ }
34
+ ```
35
+
36
+ ## API
37
+
38
+ ### Format Posisi
39
+
40
+ Semua nilai posisi menggunakan format **0-1** untuk persentase:
41
+ - `0` = 0% (awal line)
42
+ - `0.5` = 50% (tengah line)
43
+ - `1` = 100% (ujung line)
44
+
45
+ Untuk posisi absolut (dalam satuan koordinat), gunakan `isPercentage: false`.
46
+
47
+ ### Setup
48
+
49
+ ```ts
50
+ const sim = useVehicleSimulation({
51
+ wheelbase: 30, // jarak antar roda
52
+ tangentMode: 'proportional-40' // mode kurva (opsional)
53
+ })
54
+ ```
55
+
56
+ ### Scene
57
+
58
+ ```ts
59
+ sim.addLine({ id: 'line1', start: [0, 0], end: [400, 0] })
60
+ sim.updateLine('line1', { end: [500, 100] })
61
+ sim.removeLine('line1')
62
+ sim.clearScene()
63
+ ```
64
+
65
+ ### Koneksi
66
+
67
+ ```ts
68
+ sim.connect('line1', 'line2')
69
+ sim.connect('line1', 'line2', { fromOffset: 0.8, toOffset: 0.2 })
70
+ sim.connect('line1', 'line2', { fromOffset: 150, fromIsPercentage: false, toOffset: 50, toIsPercentage: false })
71
+ sim.updateConnection('line1', 'line2', { fromOffset: 0.5 }) // update offset
72
+ sim.updateConnection('line1', 'line2', { toOffset: 100, toIsPercentage: false }) // absolute
73
+ sim.disconnect('line1', 'line2')
74
+ ```
75
+
76
+ ### Kendaraan
77
+
78
+ ```ts
79
+ sim.addVehicles({ id: 'v1', lineId: 'line1', position: 0 })
80
+ sim.addVehicles({ id: 'v2', lineId: 'line1', position: 150, isPercentage: false }) // absolute
81
+ sim.updateVehicle('v1', { position: 0.5 }) // pindah ke 50%
82
+ sim.updateVehicle('v1', { lineId: 'line2' }) // pindah ke line lain
83
+ sim.updateVehicle('v1', { lineId: 'line2', position: 0.8 }) // pindah ke 80% di line2
84
+ sim.removeVehicle('v1')
85
+ sim.clearVehicles()
86
+ ```
87
+
88
+ ### Pergerakan
89
+
90
+ ```ts
91
+ sim.goto({ id: 'v1', lineId: 'line2' }) // default position = 1.0 (ujung)
92
+ sim.goto({ id: 'v1', lineId: 'line2', position: 0.5 }) // 0.5 = tengah line
93
+ sim.goto({ id: 'v1', lineId: 'line2', position: 150, isPercentage: false }) // absolute
94
+ sim.goto({ id: 'v1', lineId: 'line2', payload: { orderId: '123' } }) // dengan payload
95
+ sim.clearQueue('v1')
96
+ ```
97
+
98
+ ### Animasi
99
+
100
+ ```ts
101
+ sim.prepare() // siapkan sebelum animasi
102
+ sim.tick(5) // gerakkan 5 pixel per tick
103
+ sim.reset() // kembali ke posisi awal
104
+ sim.isMoving() // cek ada yang bergerak
105
+ sim.continueVehicle('v1') // lanjutkan vehicle yang wait
106
+ ```
107
+
108
+ ### Load dari DSL
109
+
110
+ ```ts
111
+ sim.loadFromDSL(`
112
+ line1 : (0, 0) -> (400, 0)
113
+ line2 : (400, 0) -> (400, 300)
114
+ line1 -> line2
115
+ `)
116
+ ```
117
+
118
+ ### State
119
+
120
+ ```ts
121
+ sim.lines // semua line
122
+ sim.curves // semua koneksi
123
+ sim.vehicles // kendaraan (posisi awal)
124
+ sim.movingVehicles // kendaraan (posisi saat animasi)
125
+ ```
126
+
127
+ ## Contoh Lengkap
128
+
129
+ ```tsx
130
+ import { useVehicleSimulation } from 'vehicle-path2/react'
131
+ import { useEffect } from 'react'
132
+
133
+ function AnimatedVehicle() {
134
+ const sim = useVehicleSimulation({ wheelbase: 30 })
135
+
136
+ useEffect(() => {
137
+ sim.addLine({ id: 'line1', start: [100, 100], end: [500, 100] })
138
+ sim.addVehicles({ id: 'v1', lineId: 'line1', position: 0 })
139
+ sim.goto({ id: 'v1', lineId: 'line1', position: 1.0 })
140
+ sim.prepare()
141
+
142
+ const animate = () => {
143
+ if (sim.tick(3)) {
144
+ requestAnimationFrame(animate)
145
+ }
146
+ }
147
+ requestAnimationFrame(animate)
148
+ }, [])
149
+
150
+ return (
151
+ <svg width={600} height={200}>
152
+ {sim.movingVehicles.map(v => (
153
+ <circle
154
+ key={v.id}
155
+ cx={v.rear.position.x}
156
+ cy={v.rear.position.y}
157
+ r={10}
158
+ fill="blue"
159
+ />
160
+ ))}
161
+ </svg>
162
+ )
163
+ }
164
+ ```
165
+
166
+ ## License
167
+
168
+ MIT
@@ -41,7 +41,7 @@ export declare function calculateBezierArcLength(bezier: BezierCurve, segments?:
41
41
  *
42
42
  * Effective range: [wheelbase, lineLength]
43
43
  */
44
- export declare function resolveFromLineOffset(line: Line, offset: number | undefined, isPercentage: boolean | undefined, defaultPercentage: number, wheelbase: number): number;
44
+ export declare function resolveFromLineOffset(line: Line, offset: number | undefined, isPercentage: boolean | undefined, defaultPercentage: number, maxWheelbase: number): number;
45
45
  /**
46
46
  * Resolve offset untuk TO line (garis tujuan kurva)
47
47
  * - 0% → 0 (awal garis)
@@ -49,7 +49,7 @@ export declare function resolveFromLineOffset(line: Line, offset: number | undef
49
49
  *
50
50
  * Effective range: [0, lineLength - wheelbase]
51
51
  */
52
- export declare function resolveToLineOffset(line: Line, offset: number | undefined, isPercentage: boolean | undefined, defaultPercentage: number, wheelbase: number): number;
52
+ export declare function resolveToLineOffset(line: Line, offset: number | undefined, isPercentage: boolean | undefined, defaultPercentage: number, maxWheelbase: number): number;
53
53
  /**
54
54
  * Membangun graph dari lines dan curves
55
55
  */
@@ -55,6 +55,16 @@ export declare function calculateFrontAxlePosition(path: PathResult, rearSegment
55
55
  segmentIndex: number;
56
56
  segmentDistance: number;
57
57
  } | null;
58
+ /**
59
+ * Hitung posisi awal semua axle dari posisi rearmost axle.
60
+ *
61
+ * @param lineId - Line ID tempat vehicle diinisialisasi
62
+ * @param rearOffset - Absolute offset axle paling belakang (axles[N-1])
63
+ * @param axleSpacings - Jarak antar axle berurutan (N-1 nilai untuk N axle)
64
+ * @param line - Line object untuk kalkulasi posisi
65
+ * @returns Array AxleState, axles[0] = terdepan, axles[N-1] = paling belakang
66
+ */
67
+ export declare function calculateInitialAxlePositions(lineId: string, rearOffset: number, axleSpacings: number[], line: Line): AxleState[];
58
68
  /**
59
69
  * Calculate initial front axle position from rear position
60
70
  *
@@ -128,26 +138,6 @@ export declare function updateAxlePosition(axleState: AxleState, axleExecution:
128
138
  execution: AxleExecutionState;
129
139
  completed: boolean;
130
140
  };
131
- export interface MoveVehicleInput {
132
- rear: AxleState;
133
- front: AxleState;
134
- rearExecution: AxleExecutionState;
135
- frontExecution: AxleExecutionState;
136
- }
137
- export interface MoveVehicleResult {
138
- rear: AxleState;
139
- front: AxleState;
140
- rearExecution: AxleExecutionState;
141
- frontExecution: AxleExecutionState;
142
- arrived: boolean;
143
- }
144
- /**
145
- * Move a dual-axle vehicle along a path by a given distance.
146
- *
147
- * Coordinates rear and front axle updates, computes front maxOffset,
148
- * detects arrival at target, and clamps both axles to preserve wheelbase.
149
- */
150
- export declare function moveVehicle(input: MoveVehicleInput, path: PathResult, distance: number, linesMap: Map<string, Line>, curveDataMap: Map<number, CurveData>, targetLineId: string, targetOffset: number, wheelbase: number): MoveVehicleResult;
151
141
  export interface PreparedPath {
152
142
  path: PathResult;
153
143
  curveDataMap: Map<number, CurveData>;
@@ -192,3 +182,22 @@ export interface SegmentCompletionResult {
192
182
  */
193
183
  export declare function handleArrival(state: VehicleMovementState, ctx: SegmentCompletionContext): SegmentCompletionResult;
194
184
  export type { VehicleMovementState as SegmentVehicleState };
185
+ /**
186
+ * Advance semua axle vehicle oleh `distance` sepanjang path.
187
+ *
188
+ * Ini adalah low-level tick primitive. Update semua axle menggunakan
189
+ * arc-length parameterization. Arrival = axles[0] (terdepan) mencapai ujung path.
190
+ *
191
+ * @param axleStates - Array AxleState saat ini, axles[0] = terdepan
192
+ * @param axleExecutions - Array AxleExecutionState sesuai urutan axleStates
193
+ * @param path - Path yang diikuti
194
+ * @param distance - Jarak yang dimaju per tick
195
+ * @param linesMap - Map line ID ke Line object
196
+ * @param curveDataMap - Pre-built bezier curve data
197
+ * @returns Updated axles, axleExecutions, dan arrival flag
198
+ */
199
+ export declare function moveVehicle(axleStates: AxleState[], axleExecutions: AxleExecutionState[], path: PathResult, distance: number, linesMap: Map<string, Line>, curveDataMap: Map<number, CurveData>): {
200
+ axles: AxleState[];
201
+ axleExecutions: AxleExecutionState[];
202
+ arrived: boolean;
203
+ };
@@ -0,0 +1,160 @@
1
+ /**
2
+ * PathEngine - Imperative, framework-agnostic vehicle simulation engine
3
+ *
4
+ * This module provides a class-based API over the vehicle-path core algorithms.
5
+ * Use this when you need stateful scene/vehicle management outside of React
6
+ * (e.g. game loops, Three.js useFrame, server-side simulations).
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { PathEngine } from 'vehicle-path/core'
11
+ *
12
+ * const engine = new PathEngine({ wheelbase: 30, tangentMode: 'proportional-40' })
13
+ *
14
+ * engine.setScene(lines, curves)
15
+ *
16
+ * const state = engine.initializeVehicle('line-1', 0)
17
+ * const execution = engine.preparePath(state, 'line-3', 1.0, true)
18
+ *
19
+ * // In your animation/game loop:
20
+ * function tick(deltaTime: number) {
21
+ * const result = engine.moveVehicle(state, execution, speed * deltaTime)
22
+ * state = result.state
23
+ * execution = result.execution
24
+ * if (result.arrived) { ... }
25
+ * }
26
+ * ```
27
+ */
28
+ import type { Line, Curve, Point } from './types/geometry';
29
+ import type { MovementConfig, CurveData } from './types/movement';
30
+ import type { PathResult } from './algorithms/pathFinding';
31
+ import type { TangentMode } from './types/config';
32
+ export interface PathEngineConfig {
33
+ maxWheelbase: number;
34
+ tangentMode: TangentMode;
35
+ }
36
+ /**
37
+ * Multi-axle position state for use with PathEngine.
38
+ * axles[0] = terdepan, axles[N-1] = paling belakang.
39
+ */
40
+ export interface VehiclePathState {
41
+ axles: Array<{
42
+ lineId: string;
43
+ offset: number;
44
+ position: Point;
45
+ }>;
46
+ /** N-1 jarak arc-length antar axle berurutan */
47
+ axleSpacings: number[];
48
+ }
49
+ /**
50
+ * Active path execution state for a vehicle in motion.
51
+ * Returned by preparePath() and updated by moveVehicle() each tick.
52
+ */
53
+ export interface PathExecution {
54
+ path: PathResult;
55
+ curveDataMap: Map<number, CurveData>;
56
+ /** Execution state per axle, sesuai urutan VehiclePathState.axles */
57
+ axleExecutions: Array<{
58
+ segmentIndex: number;
59
+ segmentDistance: number;
60
+ }>;
61
+ targetLineId: string;
62
+ targetOffset: number;
63
+ }
64
+ export { moveVehicle } from './algorithms/vehicleMovement';
65
+ /**
66
+ * Stateful, imperative vehicle simulation engine.
67
+ *
68
+ * Manages a scene (lines + curves + graph) and provides methods to
69
+ * initialize vehicles, prepare paths, and advance movement per tick.
70
+ *
71
+ * Designed for framework-agnostic use — no React, no render dependencies.
72
+ * The caller is responsible for the animation loop and state storage.
73
+ */
74
+ export declare class PathEngine {
75
+ private graph;
76
+ private linesMap;
77
+ private curves;
78
+ private config;
79
+ constructor(engineConfig: PathEngineConfig);
80
+ get movementConfig(): MovementConfig;
81
+ get lines(): Line[];
82
+ getCurves(): Curve[];
83
+ /**
84
+ * Replace the entire scene and rebuild the graph.
85
+ */
86
+ setScene(lines: Line[], curves: Curve[]): void;
87
+ /**
88
+ * Add a single line. Returns false if a line with the same ID already exists.
89
+ */
90
+ addLine(line: Line): boolean;
91
+ /**
92
+ * Update start and/or end coordinates of an existing line.
93
+ */
94
+ updateLine(lineId: string, updates: {
95
+ start?: Point;
96
+ end?: Point;
97
+ }): boolean;
98
+ /**
99
+ * Update a single endpoint ('start' or 'end') of a line.
100
+ */
101
+ updateLineEndpoint(lineId: string, endpoint: 'start' | 'end', point: Point): boolean;
102
+ /**
103
+ * Rename a line ID and cascade the change to all connected curves.
104
+ */
105
+ renameLine(oldId: string, newId: string): {
106
+ success: boolean;
107
+ error?: string;
108
+ };
109
+ /**
110
+ * Remove a line and all curves connected to it.
111
+ */
112
+ removeLine(lineId: string): boolean;
113
+ /**
114
+ * Add a directional curve (connection) from one line to another.
115
+ */
116
+ addCurve(curve: Curve): void;
117
+ /**
118
+ * Update a curve by index. Returns false if index is out of bounds.
119
+ */
120
+ updateCurve(index: number, updates: Partial<Curve>): boolean;
121
+ /**
122
+ * Remove a curve by index. Returns false if index is out of bounds.
123
+ */
124
+ removeCurve(index: number): boolean;
125
+ /**
126
+ * Initialize a vehicle's N-axle position on a line.
127
+ *
128
+ * @param lineId - The line to place the vehicle on
129
+ * @param rearOffset - Absolute distance offset untuk axle paling belakang
130
+ * @param axleSpacings - Jarak antar axle berurutan (N-1 nilai untuk N axle)
131
+ * @returns Initial VehiclePathState, or null if lineId does not exist
132
+ */
133
+ initializeVehicle(lineId: string, rearOffset: number, axleSpacings: number[]): VehiclePathState | null;
134
+ /**
135
+ * Prepare a path from the vehicle's current position to a target.
136
+ *
137
+ * Must be called before moveVehicle(). Returns null if no path exists.
138
+ *
139
+ * @param vehicleState - Current vehicle state (from initializeVehicle or previous tick)
140
+ * @param targetLineId - ID of the target line
141
+ * @param targetOffset - Position on the target line
142
+ * @param isPercentage - If true, targetOffset is 0-1 fraction; if false, absolute distance
143
+ */
144
+ preparePath(vehicleState: VehiclePathState, targetLineId: string, targetOffset: number, isPercentage?: boolean): PathExecution | null;
145
+ /**
146
+ * Advance a vehicle by `distance` along its prepared path.
147
+ *
148
+ * Call this every tick. The returned `state` and `execution` replace the
149
+ * previous values. When `arrived` is true, the vehicle has reached the target.
150
+ *
151
+ * @param state - Current vehicle state
152
+ * @param execution - Current path execution (from preparePath or previous tick)
153
+ * @param distance - Distance to advance this tick (speed × deltaTime)
154
+ */
155
+ moveVehicle(state: VehiclePathState, execution: PathExecution, distance: number): {
156
+ state: VehiclePathState;
157
+ execution: PathExecution;
158
+ arrived: boolean;
159
+ };
160
+ }
@@ -12,9 +12,10 @@
12
12
  */
13
13
  export type { Point, Line, BezierCurve, Curve } from './types/geometry';
14
14
  export type { VehicleState, VehicleStart, Vehicle, AxleState, GotoCommand, GotoCompletionInfo, GotoCompletionCallback } from './types/vehicle';
15
- export type { CurveData, PathExecutionState, AxleExecutionState, VehicleMovementState, MovementConfig, SceneContext } from './types/movement';
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';
18
18
  export { buildGraph, findPath, calculateBezierArcLength, resolveFromLineOffset, resolveToLineOffset, type Graph, type GraphEdge, type PathSegment, type PathResult, type VehiclePosition } from './algorithms/pathFinding';
19
- export { initializeMovingVehicle, createInitialMovementState, initializeAllVehicles, calculateInitialFrontPosition, type InitializationResult, updateAxlePosition, moveVehicle, type MoveVehicleInput, type MoveVehicleResult, calculatePositionOnLine, calculatePositionOnCurve, calculateFrontAxlePosition, getCumulativeArcLength, arcLengthToSegmentPosition, prepareCommandPath, type PreparedPath, handleArrival, type SegmentCompletionContext, type SegmentCompletionResult, type SegmentVehicleState, getPositionFromOffset, getLineLength } from './algorithms/vehicleMovement';
19
+ export { initializeMovingVehicle, createInitialMovementState, initializeAllVehicles, calculateInitialAxlePositions, type InitializationResult, updateAxlePosition, calculatePositionOnLine, calculatePositionOnCurve, calculateFrontAxlePosition, getCumulativeArcLength, arcLengthToSegmentPosition, prepareCommandPath, type PreparedPath, handleArrival, type SegmentCompletionContext, type SegmentCompletionResult, type SegmentVehicleState, moveVehicle, getPositionFromOffset, getLineLength } from './algorithms/vehicleMovement';
20
+ export { PathEngine, type PathEngineConfig, type VehiclePathState, type PathExecution } from './engine';
20
21
  export { distance, normalize, getPointOnLine, getPointOnLineByOffset, getPointOnBezier, createBezierCurve, buildArcLengthTable, distanceToT, getArcLength, calculateTangentLength, isPointNearPoint, type ArcLengthEntry, type CurveOffsetOptions } from './algorithms/math';
@@ -70,6 +70,15 @@ export interface VehicleInput {
70
70
  lineId: string;
71
71
  position?: number;
72
72
  isPercentage?: boolean;
73
+ /**
74
+ * Jarak arc-length antar axle berurutan.
75
+ * axleSpacings[i] = jarak antara axles[i] dan axles[i+1].
76
+ * axles[0] = terdepan, axles[N-1] = paling belakang.
77
+ * Contoh truk biasa: [30] → 2 axle, jarak 30px
78
+ * Contoh truck+trailer: [20, 45] → 3 axle
79
+ * Total panjang vehicle = sum(axleSpacings) harus ≤ maxWheelbase
80
+ */
81
+ axleSpacings: number[];
73
82
  }
74
83
  /**
75
84
  * Vehicle update input for updateVehicle()
@@ -27,8 +27,7 @@ export interface PathExecutionState {
27
27
  path: import('../algorithms/pathFinding').PathResult;
28
28
  curveDataMap: Map<number, CurveData>;
29
29
  currentCommandIndex: number;
30
- rear: AxleExecutionState;
31
- front: AxleExecutionState;
30
+ axles: AxleExecutionState[];
32
31
  }
33
32
  /**
34
33
  * Movement state container for a vehicle
@@ -41,7 +40,7 @@ export interface VehicleMovementState {
41
40
  * Configuration for vehicle movement
42
41
  */
43
42
  export interface MovementConfig {
44
- wheelbase: number;
43
+ maxWheelbase: number;
45
44
  tangentMode: TangentMode;
46
45
  }
47
46
  /**
@@ -14,6 +14,8 @@ export interface VehicleStart {
14
14
  lineId: string;
15
15
  offset: number;
16
16
  isPercentage: boolean;
17
+ /** Optional: defaults to [maxWheelbase] jika tidak disediakan */
18
+ axleSpacings?: number[];
17
19
  }
18
20
  /**
19
21
  * State for a single axle (Front or Rear)
@@ -32,8 +34,8 @@ export interface Vehicle {
32
34
  offset: number;
33
35
  isPercentage: boolean;
34
36
  state: VehicleState;
35
- rear: AxleState;
36
- front: AxleState;
37
+ axles: AxleState[];
38
+ axleSpacings: number[];
37
39
  }
38
40
  /**
39
41
  * Command to move a vehicle to a target position