vehicle-path2 2.0.0 → 2.1.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,732 @@
1
+ function y(t, e) {
2
+ const s = e.x - t.x, n = e.y - t.y;
3
+ return Math.sqrt(s * s + n * n);
4
+ }
5
+ function w(t, e) {
6
+ const s = e.x - t.x, n = e.y - t.y, i = Math.sqrt(s * s + n * n);
7
+ return i === 0 ? { x: 0, y: 0 } : { x: s / i, y: n / i };
8
+ }
9
+ function k(t, e) {
10
+ return e * (t === "proportional-40" ? 0.4 : 0.5522);
11
+ }
12
+ function q(t, e, s, n = !1, i) {
13
+ const { maxWheelbase: c, tangentMode: o } = s;
14
+ let r;
15
+ i?.fromOffset !== void 0 ? r = V(t, i.fromOffset, i.fromIsPercentage ?? !1) : r = t.end;
16
+ let a;
17
+ i?.toOffset !== void 0 ? a = V(e, i.toOffset, i.toIsPercentage ?? !1) : a = e.start;
18
+ const l = w(t.start, t.end), f = n ? {
19
+ // Transition with flip: kurva dimulai dari P (baseP0 - wheelbase in line direction)
20
+ x: r.x - l.x * c,
21
+ y: r.y - l.y * c
22
+ } : r, g = w(t.start, t.end), d = w(e.start, e.end), h = y(f, a), p = k(o, h), u = n ? { x: f.x - g.x * p, y: f.y - g.y * p } : { x: f.x + g.x * p, y: f.y + g.y * p }, v = {
23
+ x: a.x - d.x * p,
24
+ y: a.y - d.y * p
25
+ };
26
+ return { p0: f, p1: u, p2: v, p3: a };
27
+ }
28
+ function K(t, e) {
29
+ return {
30
+ x: t.start.x + (t.end.x - t.start.x) * e,
31
+ y: t.start.y + (t.end.y - t.start.y) * e
32
+ };
33
+ }
34
+ function V(t, e, s) {
35
+ const n = y(t.start, t.end);
36
+ let i;
37
+ return s ? i = e : i = n > 0 ? e / n : 0, i = Math.max(0, Math.min(1, i)), K(t, i);
38
+ }
39
+ function T(t, e) {
40
+ const { p0: s, p1: n, p2: i, p3: c } = t, o = 1 - e, r = o * o, a = r * o, l = e * e, f = l * e;
41
+ return {
42
+ x: a * s.x + 3 * r * e * n.x + 3 * o * l * i.x + f * c.x,
43
+ y: a * s.y + 3 * r * e * n.y + 3 * o * l * i.y + f * c.y
44
+ };
45
+ }
46
+ function ne(t, e, s = 10) {
47
+ return y(t, e) <= s;
48
+ }
49
+ function N(t, e = 100) {
50
+ const s = [{ t: 0, distance: 0 }];
51
+ let n = t.p0, i = 0;
52
+ for (let c = 1; c <= e; c++) {
53
+ const o = c / e, r = T(t, o);
54
+ i += y(n, r), s.push({ t: o, distance: i }), n = r;
55
+ }
56
+ return s;
57
+ }
58
+ function F(t, e) {
59
+ if (e <= 0) return 0;
60
+ const s = t[t.length - 1].distance;
61
+ if (e >= s) return 1;
62
+ let n = 0, i = t.length - 1;
63
+ for (; n < i - 1; ) {
64
+ const f = Math.floor((n + i) / 2);
65
+ t[f].distance < e ? n = f : i = f;
66
+ }
67
+ const c = t[n].distance, o = t[i].distance, r = t[n].t, a = t[i].t;
68
+ if (o === c) return r;
69
+ const l = (e - c) / (o - c);
70
+ return r + l * (a - r);
71
+ }
72
+ function se(t) {
73
+ return t[t.length - 1].distance;
74
+ }
75
+ function G(t, e = 100) {
76
+ let s = 0, n = t.p0;
77
+ for (let i = 1; i <= e; i++) {
78
+ const c = i / e, o = T(t, c);
79
+ s += y(n, o), n = o;
80
+ }
81
+ return s;
82
+ }
83
+ function E(t, e, s, n, i) {
84
+ const c = y(t.start, t.end), o = c - i;
85
+ if (o <= 0)
86
+ return c;
87
+ let r;
88
+ if (e === void 0)
89
+ r = n;
90
+ else if (s)
91
+ r = e;
92
+ else {
93
+ const a = Math.max(0, Math.min(e, o));
94
+ return i + a;
95
+ }
96
+ return i + r * o;
97
+ }
98
+ function W(t, e, s, n, i) {
99
+ const o = y(t.start, t.end) - i;
100
+ if (o <= 0)
101
+ return 0;
102
+ let r;
103
+ if (e === void 0)
104
+ r = n;
105
+ else if (s)
106
+ r = e;
107
+ else
108
+ return Math.max(0, Math.min(e, o));
109
+ return r * o;
110
+ }
111
+ function I(t, e, s) {
112
+ const n = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Map();
113
+ for (const o of t)
114
+ i.set(o.id, o), c.set(o.id, y(o.start, o.end)), n.set(o.id, []);
115
+ for (let o = 0; o < e.length; o++) {
116
+ const r = e[o], a = i.get(r.fromLineId), l = i.get(r.toLineId);
117
+ if (!a || !l) continue;
118
+ const f = E(a, r.fromOffset, r.fromIsPercentage, 1, s.maxWheelbase), g = W(l, r.toOffset, r.toIsPercentage, 0, s.maxWheelbase), d = q(
119
+ a,
120
+ l,
121
+ s,
122
+ !1,
123
+ // willFlip is always false now
124
+ {
125
+ fromOffset: f,
126
+ fromIsPercentage: !1,
127
+ // Already resolved to absolute
128
+ toOffset: g,
129
+ toIsPercentage: !1
130
+ // Already resolved to absolute
131
+ }
132
+ ), h = G(d), p = {
133
+ curveIndex: o,
134
+ fromLineId: r.fromLineId,
135
+ toLineId: r.toLineId,
136
+ fromOffset: f,
137
+ toOffset: g,
138
+ curveLength: h
139
+ };
140
+ n.get(r.fromLineId).push(p);
141
+ }
142
+ return { adjacency: n, lines: i, lineLengths: c };
143
+ }
144
+ function z(t, e) {
145
+ return t.curveCount !== e.curveCount ? t.curveCount - e.curveCount : t.totalDistance - e.totalDistance;
146
+ }
147
+ function Q(t, e, s, n, i = !1) {
148
+ const { adjacency: c, lines: o, lineLengths: r } = t;
149
+ if (!o.get(s)) return null;
150
+ const l = r.get(s), f = i ? n / 100 * l : n, g = [], d = /* @__PURE__ */ new Map(), h = (u, v) => `${u}:${Math.round(v)}`;
151
+ if (e.lineId === s && f >= e.offset) {
152
+ const u = f - e.offset;
153
+ return {
154
+ segments: [{
155
+ type: "line",
156
+ lineId: e.lineId,
157
+ startOffset: e.offset,
158
+ endOffset: f,
159
+ length: u
160
+ }],
161
+ totalDistance: u,
162
+ curveCount: 0
163
+ };
164
+ }
165
+ const p = c.get(e.lineId) || [];
166
+ for (const u of p) {
167
+ if (u.fromOffset < e.offset) continue;
168
+ const v = u.fromOffset - e.offset, x = v + u.curveLength, L = {
169
+ type: "line",
170
+ lineId: e.lineId,
171
+ startOffset: e.offset,
172
+ endOffset: u.fromOffset,
173
+ length: v
174
+ }, m = {
175
+ type: "curve",
176
+ curveIndex: u.curveIndex,
177
+ startOffset: 0,
178
+ endOffset: u.curveLength,
179
+ length: u.curveLength
180
+ };
181
+ g.push({
182
+ lineId: u.toLineId,
183
+ entryOffset: u.toOffset,
184
+ totalDistance: x,
185
+ curveCount: 1,
186
+ path: [L, m]
187
+ });
188
+ }
189
+ for (g.sort(z); g.length > 0; ) {
190
+ const u = g.shift(), v = h(u.lineId, u.entryOffset), x = d.get(v);
191
+ if (x !== void 0 && (x.curveCount < u.curveCount || x.curveCount === u.curveCount && x.distance <= u.totalDistance))
192
+ continue;
193
+ if (d.set(v, { curveCount: u.curveCount, distance: u.totalDistance }), u.lineId === s) {
194
+ const m = Math.abs(f - u.entryOffset);
195
+ if (f >= u.entryOffset) {
196
+ const C = {
197
+ type: "line",
198
+ lineId: s,
199
+ startOffset: u.entryOffset,
200
+ endOffset: f,
201
+ length: m
202
+ };
203
+ return {
204
+ segments: [...u.path, C],
205
+ totalDistance: u.totalDistance + m,
206
+ curveCount: u.curveCount
207
+ };
208
+ }
209
+ }
210
+ const L = c.get(u.lineId) || [];
211
+ for (const m of L) {
212
+ if (m.fromOffset < u.entryOffset) continue;
213
+ const C = m.fromOffset - u.entryOffset, b = u.totalDistance + C + m.curveLength, P = u.curveCount + 1, B = h(m.toLineId, m.toOffset), D = d.get(B);
214
+ if (D !== void 0 && (D.curveCount < P || D.curveCount === P && D.distance <= b))
215
+ continue;
216
+ const j = {
217
+ type: "line",
218
+ lineId: u.lineId,
219
+ startOffset: u.entryOffset,
220
+ endOffset: m.fromOffset,
221
+ length: C
222
+ }, $ = {
223
+ type: "curve",
224
+ curveIndex: m.curveIndex,
225
+ startOffset: 0,
226
+ endOffset: m.curveLength,
227
+ length: m.curveLength
228
+ };
229
+ g.push({
230
+ lineId: m.toLineId,
231
+ entryOffset: m.toOffset,
232
+ totalDistance: b,
233
+ curveCount: P,
234
+ path: [...u.path, j, $]
235
+ });
236
+ }
237
+ g.sort(z);
238
+ }
239
+ return null;
240
+ }
241
+ function A(t, e) {
242
+ const s = Math.sqrt(
243
+ Math.pow(t.end.x - t.start.x, 2) + Math.pow(t.end.y - t.start.y, 2)
244
+ ), n = s > 0 ? e / s : 0;
245
+ return {
246
+ x: t.start.x + (t.end.x - t.start.x) * Math.min(1, Math.max(0, n)),
247
+ y: t.start.y + (t.end.y - t.start.y) * Math.min(1, Math.max(0, n))
248
+ };
249
+ }
250
+ function O(t) {
251
+ return Math.sqrt(
252
+ Math.pow(t.end.x - t.start.x, 2) + Math.pow(t.end.y - t.start.y, 2)
253
+ );
254
+ }
255
+ function R(t, e, s) {
256
+ let n = 0;
257
+ for (let i = 0; i < e; i++)
258
+ n += t.segments[i].length;
259
+ return n += s, n;
260
+ }
261
+ function _(t, e) {
262
+ let s = 0;
263
+ for (let n = 0; n < t.segments.length; n++) {
264
+ const i = t.segments[n], c = s + i.length;
265
+ if (e < c)
266
+ return {
267
+ segmentIndex: n,
268
+ segmentDistance: e - s
269
+ };
270
+ if (e === c)
271
+ return n + 1 < t.segments.length ? {
272
+ segmentIndex: n + 1,
273
+ segmentDistance: 0
274
+ } : {
275
+ segmentIndex: n,
276
+ segmentDistance: i.length
277
+ };
278
+ s += i.length;
279
+ }
280
+ return null;
281
+ }
282
+ function ie(t, e, s, n) {
283
+ const c = R(
284
+ t,
285
+ e,
286
+ s
287
+ ) + n;
288
+ return _(t, c);
289
+ }
290
+ function H(t, e, s, n) {
291
+ const i = O(n), c = s.length + 1, o = new Array(c);
292
+ o[c - 1] = {
293
+ lineId: t,
294
+ absoluteOffset: e,
295
+ position: A(n, e)
296
+ };
297
+ let r = e;
298
+ for (let a = c - 2; a >= 0; a--)
299
+ r = Math.min(r + s[a], i), o[a] = {
300
+ lineId: t,
301
+ absoluteOffset: r,
302
+ position: A(n, r)
303
+ };
304
+ return o;
305
+ }
306
+ function J(t, e) {
307
+ return {
308
+ ...t,
309
+ state: "idle"
310
+ };
311
+ }
312
+ function U(t) {
313
+ return {
314
+ vehicle: t,
315
+ execution: null
316
+ };
317
+ }
318
+ function re(t, e) {
319
+ const s = [], n = /* @__PURE__ */ new Map();
320
+ for (const i of t) {
321
+ if (!e.get(i.lineId)) continue;
322
+ const o = J(i);
323
+ s.push(o);
324
+ const r = U(o);
325
+ n.set(i.id, r);
326
+ }
327
+ return { movingVehicles: s, stateMap: n };
328
+ }
329
+ function M(t, e) {
330
+ return { position: A(t, e), lineId: t.id, absoluteOffset: e };
331
+ }
332
+ function S(t, e) {
333
+ const s = F(t.arcLengthTable, e);
334
+ return { position: T(t.bezier, s) };
335
+ }
336
+ function X(t, e, s, n, i, c, o) {
337
+ const r = s.segments[e.currentSegmentIndex], a = e.segmentDistance + n;
338
+ if (a >= r.length) {
339
+ const f = a - r.length, g = e.currentSegmentIndex + 1;
340
+ if (g >= s.segments.length) {
341
+ if (o !== void 0 && r.type === "line") {
342
+ const u = i.get(r.lineId), v = r.startOffset + a;
343
+ if (v <= o) {
344
+ const L = M(u, v);
345
+ return {
346
+ axleState: { ...t, ...L },
347
+ execution: { ...e, segmentDistance: a },
348
+ completed: !1
349
+ };
350
+ }
351
+ const x = M(u, o);
352
+ return {
353
+ axleState: { ...t, ...x },
354
+ execution: { ...e, segmentDistance: o - r.startOffset },
355
+ completed: !0
356
+ };
357
+ }
358
+ const p = r.type === "line" ? M(
359
+ i.get(r.lineId),
360
+ r.endOffset
361
+ ) : S(
362
+ c.get(r.curveIndex),
363
+ r.length
364
+ );
365
+ return {
366
+ axleState: { ...t, ...p },
367
+ execution: { ...e, segmentDistance: r.length },
368
+ completed: !0
369
+ };
370
+ }
371
+ const d = s.segments[g], h = d.type === "line" ? M(
372
+ i.get(d.lineId),
373
+ d.startOffset + f
374
+ ) : S(
375
+ c.get(d.curveIndex),
376
+ f
377
+ );
378
+ return {
379
+ axleState: { ...t, ...h },
380
+ execution: {
381
+ currentSegmentIndex: g,
382
+ segmentDistance: f
383
+ },
384
+ completed: !1
385
+ };
386
+ }
387
+ const l = r.type === "line" ? M(
388
+ i.get(r.lineId),
389
+ r.startOffset + a
390
+ ) : S(
391
+ c.get(r.curveIndex),
392
+ a
393
+ );
394
+ return {
395
+ axleState: { ...t, ...l },
396
+ execution: { ...e, segmentDistance: a },
397
+ completed: !1
398
+ };
399
+ }
400
+ function Y(t, e, s, n) {
401
+ const i = /* @__PURE__ */ new Map();
402
+ for (const c of t.segments)
403
+ if (c.type === "curve" && c.curveIndex !== void 0) {
404
+ const o = e[c.curveIndex];
405
+ if (o) {
406
+ const r = s.get(o.fromLineId), a = s.get(o.toLineId);
407
+ if (r && a) {
408
+ const l = E(
409
+ r,
410
+ o.fromOffset,
411
+ o.fromIsPercentage,
412
+ 1,
413
+ // Default: 100% = 1.0
414
+ n.maxWheelbase
415
+ ), f = W(
416
+ a,
417
+ o.toOffset,
418
+ o.toIsPercentage,
419
+ 0,
420
+ n.maxWheelbase
421
+ ), g = q(
422
+ r,
423
+ a,
424
+ n,
425
+ !1,
426
+ // willFlip is always false now
427
+ {
428
+ fromOffset: l,
429
+ fromIsPercentage: !1,
430
+ // Already resolved to absolute
431
+ toOffset: f,
432
+ toIsPercentage: !1
433
+ // Already resolved to absolute
434
+ }
435
+ ), d = N(g);
436
+ i.set(c.curveIndex, { bezier: g, arcLengthTable: d });
437
+ }
438
+ }
439
+ }
440
+ return i;
441
+ }
442
+ function Z(t, e, s) {
443
+ const { graph: n, linesMap: i, curves: c, config: o } = s, r = i.get(e.targetLineId);
444
+ if (!r) return null;
445
+ const a = t.axleSpacings.reduce((u, v) => u + v, 0), f = O(r) - a;
446
+ if (f <= 0) return null;
447
+ const g = e.isPercentage ? e.targetOffset * f : Math.min(e.targetOffset, f), d = t.axles[t.axles.length - 1], h = Q(
448
+ n,
449
+ { lineId: d.lineId, offset: d.absoluteOffset },
450
+ e.targetLineId,
451
+ g,
452
+ !1
453
+ );
454
+ if (!h) return null;
455
+ const p = Y(h, c, i, o);
456
+ return { path: h, curveDataMap: p };
457
+ }
458
+ function oe(t, e) {
459
+ const s = t.execution, i = e.vehicleQueues.get(t.vehicle.id)?.[s.currentCommandIndex];
460
+ return i && e.onCommandComplete && e.onCommandComplete({
461
+ vehicleId: t.vehicle.id,
462
+ command: i,
463
+ finalPosition: {
464
+ lineId: t.vehicle.axles[t.vehicle.axles.length - 1].lineId,
465
+ absoluteOffset: t.vehicle.axles[t.vehicle.axles.length - 1].absoluteOffset,
466
+ position: t.vehicle.axles[t.vehicle.axles.length - 1].position
467
+ },
468
+ payload: i.payload
469
+ }), {
470
+ handled: !0,
471
+ vehicle: { ...t.vehicle, state: "waiting" },
472
+ newExecution: s,
473
+ // Keep execution state for resume
474
+ isWaiting: !0
475
+ };
476
+ }
477
+ function ee(t, e, s, n, i, c) {
478
+ let o;
479
+ const r = e[0];
480
+ if (r.currentSegmentIndex < s.segments.length) {
481
+ const l = s.segments[r.currentSegmentIndex];
482
+ if (l.type === "line") {
483
+ const f = i.get(l.lineId);
484
+ f && (o = O(f));
485
+ }
486
+ }
487
+ const a = t.map((l, f) => {
488
+ const g = f === 0 ? o : void 0;
489
+ return X(l, e[f], s, n, i, c, g);
490
+ });
491
+ return {
492
+ axles: a.map((l) => l.axleState),
493
+ axleExecutions: a.map((l) => l.execution),
494
+ arrived: a[0].completed
495
+ // axles[0] = terdepan menentukan arrived
496
+ };
497
+ }
498
+ class ce {
499
+ graph = null;
500
+ linesMap = /* @__PURE__ */ new Map();
501
+ curves = [];
502
+ config;
503
+ constructor(e) {
504
+ this.config = {
505
+ maxWheelbase: e.maxWheelbase,
506
+ tangentMode: e.tangentMode
507
+ };
508
+ }
509
+ // ---------------------------------------------------------------------------
510
+ // Accessors
511
+ // ---------------------------------------------------------------------------
512
+ get movementConfig() {
513
+ return this.config;
514
+ }
515
+ get lines() {
516
+ return Array.from(this.linesMap.values());
517
+ }
518
+ getCurves() {
519
+ return this.curves;
520
+ }
521
+ // ---------------------------------------------------------------------------
522
+ // Scene management
523
+ // ---------------------------------------------------------------------------
524
+ /**
525
+ * Replace the entire scene and rebuild the graph.
526
+ */
527
+ setScene(e, s) {
528
+ this.linesMap.clear();
529
+ for (const n of e)
530
+ this.linesMap.set(n.id, n);
531
+ this.curves = s, this.graph = I(e, s, this.config);
532
+ }
533
+ /**
534
+ * Add a single line. Returns false if a line with the same ID already exists.
535
+ */
536
+ addLine(e) {
537
+ return this.linesMap.has(e.id) ? !1 : (this.linesMap.set(e.id, e), this.graph = I(Array.from(this.linesMap.values()), this.curves, this.config), !0);
538
+ }
539
+ /**
540
+ * Update start and/or end coordinates of an existing line.
541
+ */
542
+ updateLine(e, s) {
543
+ const n = this.linesMap.get(e);
544
+ return n ? (s.start && (n.start = s.start), s.end && (n.end = s.end), this.graph = I(Array.from(this.linesMap.values()), this.curves, this.config), !0) : !1;
545
+ }
546
+ /**
547
+ * Update a single endpoint ('start' or 'end') of a line.
548
+ */
549
+ updateLineEndpoint(e, s, n) {
550
+ return this.updateLine(e, { [s]: n });
551
+ }
552
+ /**
553
+ * Rename a line ID and cascade the change to all connected curves.
554
+ */
555
+ renameLine(e, s) {
556
+ const n = s.trim();
557
+ if (!n) return { success: !1, error: "Name cannot be empty" };
558
+ if (n === e) return { success: !0 };
559
+ if (this.linesMap.has(n)) return { success: !1, error: `"${n}" already exists` };
560
+ const i = this.linesMap.get(e);
561
+ if (!i) return { success: !1, error: `Line "${e}" not found` };
562
+ i.id = n, this.linesMap.delete(e), this.linesMap.set(n, i);
563
+ for (const c of this.curves)
564
+ c.fromLineId === e && (c.fromLineId = n), c.toLineId === e && (c.toLineId = n);
565
+ return this.graph = I(Array.from(this.linesMap.values()), this.curves, this.config), { success: !0 };
566
+ }
567
+ /**
568
+ * Remove a line and all curves connected to it.
569
+ */
570
+ removeLine(e) {
571
+ return this.linesMap.has(e) ? (this.linesMap.delete(e), this.curves = this.curves.filter((s) => s.fromLineId !== e && s.toLineId !== e), this.graph = I(Array.from(this.linesMap.values()), this.curves, this.config), !0) : !1;
572
+ }
573
+ /**
574
+ * Add a directional curve (connection) from one line to another.
575
+ */
576
+ addCurve(e) {
577
+ this.curves.push(e), this.graph = I(Array.from(this.linesMap.values()), this.curves, this.config);
578
+ }
579
+ /**
580
+ * Update a curve by index. Returns false if index is out of bounds.
581
+ */
582
+ updateCurve(e, s) {
583
+ return e < 0 || e >= this.curves.length ? !1 : (this.curves[e] = { ...this.curves[e], ...s }, this.graph = I(Array.from(this.linesMap.values()), this.curves, this.config), !0);
584
+ }
585
+ /**
586
+ * Remove a curve by index. Returns false if index is out of bounds.
587
+ */
588
+ removeCurve(e) {
589
+ return e < 0 || e >= this.curves.length ? !1 : (this.curves.splice(e, 1), this.graph = I(Array.from(this.linesMap.values()), this.curves, this.config), !0);
590
+ }
591
+ // ---------------------------------------------------------------------------
592
+ // Vehicle operations
593
+ // ---------------------------------------------------------------------------
594
+ /**
595
+ * Initialize a vehicle's N-axle position on a line.
596
+ *
597
+ * @param lineId - The line to place the vehicle on
598
+ * @param rearOffset - Absolute distance offset untuk axle paling belakang
599
+ * @param axleSpacings - Jarak antar axle berurutan (N-1 nilai untuk N axle)
600
+ * @returns Initial VehiclePathState, or null if lineId does not exist
601
+ */
602
+ initializeVehicle(e, s, n) {
603
+ const i = this.linesMap.get(e);
604
+ if (!i) return null;
605
+ const c = n.reduce((l, f) => l + f, 0), o = O(i), r = Math.min(s, o - c);
606
+ return {
607
+ axles: H(e, r, n, i).map((l) => ({ lineId: l.lineId, offset: l.absoluteOffset, position: l.position })),
608
+ axleSpacings: n
609
+ };
610
+ }
611
+ /**
612
+ * Prepare a path from the vehicle's current position to a target.
613
+ *
614
+ * Must be called before moveVehicle(). Returns null if no path exists.
615
+ *
616
+ * @param vehicleState - Current vehicle state (from initializeVehicle or previous tick)
617
+ * @param targetLineId - ID of the target line
618
+ * @param targetOffset - Position on the target line
619
+ * @param isPercentage - If true, targetOffset is 0-1 fraction; if false, absolute distance
620
+ */
621
+ preparePath(e, s, n, i = !1) {
622
+ if (!this.graph) return null;
623
+ const c = e.axleSpacings.reduce((h, p) => h + p, 0), o = e.axles[e.axles.length - 1], r = {
624
+ lineId: o.lineId,
625
+ offset: o.offset,
626
+ axles: e.axles.map((h) => ({
627
+ lineId: h.lineId,
628
+ position: h.position,
629
+ absoluteOffset: h.offset
630
+ })),
631
+ axleSpacings: e.axleSpacings
632
+ }, a = Z(r, {
633
+ targetLineId: s,
634
+ targetOffset: n,
635
+ isPercentage: i
636
+ }, {
637
+ graph: this.graph,
638
+ linesMap: this.linesMap,
639
+ curves: this.curves,
640
+ config: this.config
641
+ });
642
+ if (!a) return null;
643
+ let l = n;
644
+ const f = this.linesMap.get(s);
645
+ if (f) {
646
+ const h = Math.max(0, O(f) - c);
647
+ l = i ? n * h : Math.min(n, h);
648
+ }
649
+ let g = 0;
650
+ const d = [
651
+ { segmentIndex: 0, segmentDistance: c }
652
+ // axles[0] = front
653
+ ];
654
+ for (let h = 0; h < e.axleSpacings.length; h++)
655
+ g += e.axleSpacings[h], d.push({ segmentIndex: 0, segmentDistance: c - g });
656
+ return {
657
+ path: a.path,
658
+ curveDataMap: a.curveDataMap,
659
+ axleExecutions: d,
660
+ targetLineId: s,
661
+ targetOffset: l
662
+ };
663
+ }
664
+ /**
665
+ * Advance a vehicle by `distance` along its prepared path.
666
+ *
667
+ * Call this every tick. The returned `state` and `execution` replace the
668
+ * previous values. When `arrived` is true, the vehicle has reached the target.
669
+ *
670
+ * @param state - Current vehicle state
671
+ * @param execution - Current path execution (from preparePath or previous tick)
672
+ * @param distance - Distance to advance this tick (speed × deltaTime)
673
+ */
674
+ moveVehicle(e, s, n) {
675
+ const i = e.axles.map((r) => ({
676
+ lineId: r.lineId,
677
+ position: r.position,
678
+ absoluteOffset: r.offset
679
+ })), c = s.axleExecutions.map((r) => ({
680
+ currentSegmentIndex: r.segmentIndex,
681
+ segmentDistance: r.segmentDistance
682
+ })), o = ee(i, c, s.path, n, this.linesMap, s.curveDataMap);
683
+ return {
684
+ state: {
685
+ axles: o.axles.map((r) => ({ lineId: r.lineId, offset: r.absoluteOffset, position: r.position })),
686
+ axleSpacings: e.axleSpacings
687
+ },
688
+ execution: {
689
+ ...s,
690
+ axleExecutions: o.axleExecutions.map((r) => ({
691
+ segmentIndex: r.currentSegmentIndex,
692
+ segmentDistance: r.segmentDistance
693
+ }))
694
+ },
695
+ arrived: o.arrived
696
+ };
697
+ }
698
+ }
699
+ export {
700
+ w as A,
701
+ Z as B,
702
+ E as C,
703
+ W as D,
704
+ X as E,
705
+ ce as P,
706
+ _ as a,
707
+ N as b,
708
+ I as c,
709
+ G as d,
710
+ ie as e,
711
+ H as f,
712
+ S as g,
713
+ M as h,
714
+ k as i,
715
+ q as j,
716
+ U as k,
717
+ y as l,
718
+ F as m,
719
+ Q as n,
720
+ se as o,
721
+ R as p,
722
+ O as q,
723
+ T as r,
724
+ K as s,
725
+ V as t,
726
+ A as u,
727
+ oe as v,
728
+ re as w,
729
+ J as x,
730
+ ne as y,
731
+ ee as z
732
+ };