vehicle-path2 4.0.2 → 4.0.3

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