vehicle-path2 1.0.13 → 1.0.15
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.
- package/README.md +168 -168
- package/dist/core/algorithms/vehicleMovement.d.ts +24 -20
- package/dist/core/engine.d.ts +162 -0
- package/dist/core/index.d.ts +3 -2
- package/dist/core.cjs +1 -1
- package/dist/core.js +223 -523
- package/dist/index.d.ts +3 -2
- package/dist/react.cjs +1 -1
- package/dist/react.js +1 -1
- package/dist/useVehicleEvents-Bp4v5sln.cjs +3 -0
- package/dist/{useVehicleEvents-CabztfQ4.js → useVehicleEvents-C0uaNzNP.js} +186 -186
- package/dist/utils.cjs +1 -1
- package/dist/utils.js +1 -1
- package/dist/{vehicle-helpers-BgD4BTAJ.js → vehicle-helpers-DQ-OThWd.js} +20 -20
- package/dist/{vehicle-helpers-DrnYWjm3.cjs → vehicle-helpers-DrCYMVs4.cjs} +1 -1
- package/dist/vehicle-path.cjs +1 -1
- package/dist/vehicle-path.js +49 -46
- package/dist/vehicleMovement-BiYBE_2a.cjs +1 -0
- package/dist/vehicleMovement-TkCt4q_a.js +524 -0
- package/package.json +103 -103
- package/dist/useVehicleEvents-BZVmIugl.cjs +0 -3
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
|
|
@@ -128,26 +128,6 @@ export declare function updateAxlePosition(axleState: AxleState, axleExecution:
|
|
|
128
128
|
execution: AxleExecutionState;
|
|
129
129
|
completed: boolean;
|
|
130
130
|
};
|
|
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
131
|
export interface PreparedPath {
|
|
152
132
|
path: PathResult;
|
|
153
133
|
curveDataMap: Map<number, CurveData>;
|
|
@@ -192,3 +172,27 @@ export interface SegmentCompletionResult {
|
|
|
192
172
|
*/
|
|
193
173
|
export declare function handleArrival(state: VehicleMovementState, ctx: SegmentCompletionContext): SegmentCompletionResult;
|
|
194
174
|
export type { VehicleMovementState as SegmentVehicleState };
|
|
175
|
+
/**
|
|
176
|
+
* Advance both axles of a vehicle by `distance` along a prepared path.
|
|
177
|
+
*
|
|
178
|
+
* This is the low-level tick primitive. It updates both rear and front axles
|
|
179
|
+
* using arc-length parameterization and returns whether the rear axle has
|
|
180
|
+
* reached the end of the path (arrived).
|
|
181
|
+
*
|
|
182
|
+
* @param rear - Current rear axle state
|
|
183
|
+
* @param front - Current front axle state
|
|
184
|
+
* @param rearExecution - Current rear axle execution position in path
|
|
185
|
+
* @param frontExecution - Current front axle execution position in path
|
|
186
|
+
* @param path - The path to follow
|
|
187
|
+
* @param distance - Distance to advance this tick
|
|
188
|
+
* @param linesMap - Scene lines map for position calculation
|
|
189
|
+
* @param curveDataMap - Pre-built bezier curve data for the path's curve segments
|
|
190
|
+
* @returns Updated axle states, execution states, and arrival flag
|
|
191
|
+
*/
|
|
192
|
+
export declare function moveVehicle(rear: AxleState, front: AxleState, rearExecution: AxleExecutionState, frontExecution: AxleExecutionState, path: PathResult, distance: number, linesMap: Map<string, Line>, curveDataMap: Map<number, CurveData>): {
|
|
193
|
+
rear: AxleState;
|
|
194
|
+
front: AxleState;
|
|
195
|
+
rearExecution: AxleExecutionState;
|
|
196
|
+
frontExecution: AxleExecutionState;
|
|
197
|
+
arrived: boolean;
|
|
198
|
+
};
|
|
@@ -0,0 +1,162 @@
|
|
|
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
|
+
wheelbase: number;
|
|
34
|
+
tangentMode: TangentMode;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Simplified dual-axle position state for use with PathEngine.
|
|
38
|
+
* A flatter alternative to the internal Vehicle type.
|
|
39
|
+
*/
|
|
40
|
+
export interface VehiclePathState {
|
|
41
|
+
rear: {
|
|
42
|
+
lineId: string;
|
|
43
|
+
offset: number;
|
|
44
|
+
position: Point;
|
|
45
|
+
};
|
|
46
|
+
front: {
|
|
47
|
+
lineId: string;
|
|
48
|
+
offset: number;
|
|
49
|
+
position: Point;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Active path execution state for a vehicle in motion.
|
|
54
|
+
* Returned by preparePath() and updated by moveVehicle() each tick.
|
|
55
|
+
*/
|
|
56
|
+
export interface PathExecution {
|
|
57
|
+
path: PathResult;
|
|
58
|
+
curveDataMap: Map<number, CurveData>;
|
|
59
|
+
rearSegmentIndex: number;
|
|
60
|
+
rearSegmentDistance: number;
|
|
61
|
+
frontSegmentIndex: number;
|
|
62
|
+
frontSegmentDistance: number;
|
|
63
|
+
/** Resolved absolute target offset for rear axle arrival detection */
|
|
64
|
+
targetLineId: string;
|
|
65
|
+
targetOffset: number;
|
|
66
|
+
}
|
|
67
|
+
export { moveVehicle } from './algorithms/vehicleMovement';
|
|
68
|
+
/**
|
|
69
|
+
* Stateful, imperative vehicle simulation engine.
|
|
70
|
+
*
|
|
71
|
+
* Manages a scene (lines + curves + graph) and provides methods to
|
|
72
|
+
* initialize vehicles, prepare paths, and advance movement per tick.
|
|
73
|
+
*
|
|
74
|
+
* Designed for framework-agnostic use — no React, no render dependencies.
|
|
75
|
+
* The caller is responsible for the animation loop and state storage.
|
|
76
|
+
*/
|
|
77
|
+
export declare class PathEngine {
|
|
78
|
+
private graph;
|
|
79
|
+
private linesMap;
|
|
80
|
+
private curves;
|
|
81
|
+
private config;
|
|
82
|
+
constructor(engineConfig: PathEngineConfig);
|
|
83
|
+
get movementConfig(): MovementConfig;
|
|
84
|
+
get lines(): Line[];
|
|
85
|
+
getCurves(): Curve[];
|
|
86
|
+
/**
|
|
87
|
+
* Replace the entire scene and rebuild the graph.
|
|
88
|
+
*/
|
|
89
|
+
setScene(lines: Line[], curves: Curve[]): void;
|
|
90
|
+
/**
|
|
91
|
+
* Add a single line. Returns false if a line with the same ID already exists.
|
|
92
|
+
*/
|
|
93
|
+
addLine(line: Line): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Update start and/or end coordinates of an existing line.
|
|
96
|
+
*/
|
|
97
|
+
updateLine(lineId: string, updates: {
|
|
98
|
+
start?: Point;
|
|
99
|
+
end?: Point;
|
|
100
|
+
}): boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Update a single endpoint ('start' or 'end') of a line.
|
|
103
|
+
*/
|
|
104
|
+
updateLineEndpoint(lineId: string, endpoint: 'start' | 'end', point: Point): boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Rename a line ID and cascade the change to all connected curves.
|
|
107
|
+
*/
|
|
108
|
+
renameLine(oldId: string, newId: string): {
|
|
109
|
+
success: boolean;
|
|
110
|
+
error?: string;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Remove a line and all curves connected to it.
|
|
114
|
+
*/
|
|
115
|
+
removeLine(lineId: string): boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Add a directional curve (connection) from one line to another.
|
|
118
|
+
*/
|
|
119
|
+
addCurve(curve: Curve): void;
|
|
120
|
+
/**
|
|
121
|
+
* Update a curve by index. Returns false if index is out of bounds.
|
|
122
|
+
*/
|
|
123
|
+
updateCurve(index: number, updates: Partial<Curve>): boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Remove a curve by index. Returns false if index is out of bounds.
|
|
126
|
+
*/
|
|
127
|
+
removeCurve(index: number): boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Initialize a vehicle's dual-axle position on a line.
|
|
130
|
+
*
|
|
131
|
+
* @param lineId - The line to place the vehicle on
|
|
132
|
+
* @param offset - Absolute distance offset along the line
|
|
133
|
+
* @returns Initial VehiclePathState, or null if lineId does not exist
|
|
134
|
+
*/
|
|
135
|
+
initializeVehicle(lineId: string, offset: number): VehiclePathState | null;
|
|
136
|
+
/**
|
|
137
|
+
* Prepare a path from the vehicle's current position to a target.
|
|
138
|
+
*
|
|
139
|
+
* Must be called before moveVehicle(). Returns null if no path exists.
|
|
140
|
+
*
|
|
141
|
+
* @param vehicleState - Current vehicle state (from initializeVehicle or previous tick)
|
|
142
|
+
* @param targetLineId - ID of the target line
|
|
143
|
+
* @param targetOffset - Position on the target line
|
|
144
|
+
* @param isPercentage - If true, targetOffset is 0-1 fraction; if false, absolute distance
|
|
145
|
+
*/
|
|
146
|
+
preparePath(vehicleState: VehiclePathState, targetLineId: string, targetOffset: number, isPercentage?: boolean): PathExecution | null;
|
|
147
|
+
/**
|
|
148
|
+
* Advance a vehicle by `distance` along its prepared path.
|
|
149
|
+
*
|
|
150
|
+
* Call this every tick. The returned `state` and `execution` replace the
|
|
151
|
+
* previous values. When `arrived` is true, the vehicle has reached the target.
|
|
152
|
+
*
|
|
153
|
+
* @param state - Current vehicle state
|
|
154
|
+
* @param execution - Current path execution (from preparePath or previous tick)
|
|
155
|
+
* @param distance - Distance to advance this tick (speed × deltaTime)
|
|
156
|
+
*/
|
|
157
|
+
moveVehicle(state: VehiclePathState, execution: PathExecution, distance: number): {
|
|
158
|
+
state: VehiclePathState;
|
|
159
|
+
execution: PathExecution;
|
|
160
|
+
arrived: boolean;
|
|
161
|
+
};
|
|
162
|
+
}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -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,
|
|
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,
|
|
19
|
+
export { initializeMovingVehicle, createInitialMovementState, initializeAllVehicles, calculateInitialFrontPosition, 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';
|
package/dist/core.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./vehicleMovement-BiYBE_2a.cjs");class g{graph=null;linesMap=new Map;curves=[];config;constructor(e){this.config={wheelbase:e.wheelbase,tangentMode:e.tangentMode}}get movementConfig(){return this.config}get lines(){return Array.from(this.linesMap.values())}getCurves(){return this.curves}setScene(e,i){this.linesMap.clear();for(const n of e)this.linesMap.set(n.id,n);this.curves=i,this.graph=t.buildGraph(e,i,this.config)}addLine(e){return this.linesMap.has(e.id)?!1:(this.linesMap.set(e.id,e),this.graph=t.buildGraph(Array.from(this.linesMap.values()),this.curves,this.config),!0)}updateLine(e,i){const n=this.linesMap.get(e);return n?(i.start&&(n.start=i.start),i.end&&(n.end=i.end),this.graph=t.buildGraph(Array.from(this.linesMap.values()),this.curves,this.config),!0):!1}updateLineEndpoint(e,i,n){return this.updateLine(e,{[i]:n})}renameLine(e,i){const n=i.trim();if(!n)return{success:!1,error:"Name cannot be empty"};if(n===e)return{success:!0};if(this.linesMap.has(n))return{success:!1,error:`"${n}" already exists`};const a=this.linesMap.get(e);if(!a)return{success:!1,error:`Line "${e}" not found`};a.id=n,this.linesMap.delete(e),this.linesMap.set(n,a);for(const s of this.curves)s.fromLineId===e&&(s.fromLineId=n),s.toLineId===e&&(s.toLineId=n);return this.graph=t.buildGraph(Array.from(this.linesMap.values()),this.curves,this.config),{success:!0}}removeLine(e){return this.linesMap.has(e)?(this.linesMap.delete(e),this.curves=this.curves.filter(i=>i.fromLineId!==e&&i.toLineId!==e),this.graph=t.buildGraph(Array.from(this.linesMap.values()),this.curves,this.config),!0):!1}addCurve(e){this.curves.push(e),this.graph=t.buildGraph(Array.from(this.linesMap.values()),this.curves,this.config)}updateCurve(e,i){return e<0||e>=this.curves.length?!1:(this.curves[e]={...this.curves[e],...i},this.graph=t.buildGraph(Array.from(this.linesMap.values()),this.curves,this.config),!0)}removeCurve(e){return e<0||e>=this.curves.length?!1:(this.curves.splice(e,1),this.graph=t.buildGraph(Array.from(this.linesMap.values()),this.curves,this.config),!0)}initializeVehicle(e,i){const n=this.linesMap.get(e);if(!n)return null;const a=t.getLineLength(n),s=Math.min(i,a-this.config.wheelbase),l=t.getPositionFromOffset(n,s),o=t.calculateInitialFrontPosition(e,s,this.config.wheelbase,n);return{rear:{lineId:e,offset:s,position:l},front:{lineId:o.lineId,offset:o.absoluteOffset,position:o.position}}}preparePath(e,i,n,a=!1){if(!this.graph)return null;const s={lineId:e.rear.lineId,offset:e.rear.offset,rear:{lineId:e.rear.lineId,position:e.rear.position,absoluteOffset:e.rear.offset},front:{lineId:e.front.lineId,position:e.front.position,absoluteOffset:e.front.offset}},l={targetLineId:i,targetOffset:n,isPercentage:a},o={graph:this.graph,linesMap:this.linesMap,curves:this.curves,config:this.config},r=t.prepareCommandPath(s,l,o);if(!r)return null;let c=n;const f=this.linesMap.get(i);if(f){const u=t.getLineLength(f),h=Math.max(0,u-this.config.wheelbase);c=a?n*h:Math.min(n,h)}return{path:r.path,curveDataMap:r.curveDataMap,rearSegmentIndex:0,rearSegmentDistance:0,frontSegmentIndex:0,frontSegmentDistance:this.config.wheelbase,targetLineId:i,targetOffset:c}}moveVehicle(e,i,n){const a={lineId:e.rear.lineId,position:e.rear.position,absoluteOffset:e.rear.offset},s={lineId:e.front.lineId,position:e.front.position,absoluteOffset:e.front.offset},l={currentSegmentIndex:i.rearSegmentIndex,segmentDistance:i.rearSegmentDistance},o={currentSegmentIndex:i.frontSegmentIndex,segmentDistance:i.frontSegmentDistance},r=t.moveVehicle(a,s,l,o,i.path,n,this.linesMap,i.curveDataMap);return{state:{rear:{lineId:r.rear.lineId,offset:r.rear.absoluteOffset,position:r.rear.position},front:{lineId:r.front.lineId,offset:r.front.absoluteOffset,position:r.front.position}},execution:{...i,rearSegmentIndex:r.rearExecution.currentSegmentIndex,rearSegmentDistance:r.rearExecution.segmentDistance,frontSegmentIndex:r.frontExecution.currentSegmentIndex,frontSegmentDistance:r.frontExecution.segmentDistance},arrived:r.arrived}}}exports.arcLengthToSegmentPosition=t.arcLengthToSegmentPosition;exports.buildArcLengthTable=t.buildArcLengthTable;exports.buildGraph=t.buildGraph;exports.calculateBezierArcLength=t.calculateBezierArcLength;exports.calculateFrontAxlePosition=t.calculateFrontAxlePosition;exports.calculateInitialFrontPosition=t.calculateInitialFrontPosition;exports.calculatePositionOnCurve=t.calculatePositionOnCurve;exports.calculatePositionOnLine=t.calculatePositionOnLine;exports.calculateTangentLength=t.calculateTangentLength;exports.createBezierCurve=t.createBezierCurve;exports.createInitialMovementState=t.createInitialMovementState;exports.distance=t.distance;exports.distanceToT=t.distanceToT;exports.findPath=t.findPath;exports.getArcLength=t.getArcLength;exports.getCumulativeArcLength=t.getCumulativeArcLength;exports.getLineLength=t.getLineLength;exports.getPointOnBezier=t.getPointOnBezier;exports.getPointOnLine=t.getPointOnLine;exports.getPointOnLineByOffset=t.getPointOnLineByOffset;exports.getPositionFromOffset=t.getPositionFromOffset;exports.handleArrival=t.handleArrival;exports.initializeAllVehicles=t.initializeAllVehicles;exports.initializeMovingVehicle=t.initializeMovingVehicle;exports.isPointNearPoint=t.isPointNearPoint;exports.moveVehicle=t.moveVehicle;exports.normalize=t.normalize;exports.prepareCommandPath=t.prepareCommandPath;exports.resolveFromLineOffset=t.resolveFromLineOffset;exports.resolveToLineOffset=t.resolveToLineOffset;exports.updateAxlePosition=t.updateAxlePosition;exports.PathEngine=g;
|