vehicle-path 1.0.0 → 1.0.1

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.
@@ -1,14 +1,14 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("react");function R(e,t){const n=t.x-e.x,s=t.y-e.y;return Math.sqrt(n*n+s*s)}function K(e,t){const n=t.x-e.x,s=t.y-e.y,r=Math.sqrt(n*n+s*s);return r===0?{x:0,y:0}:{x:n/r,y:s/r}}function we(e,t){return t*(e==="proportional-40"?.4:.5522)}function ne(e,t,n,s=!1,r){const{wheelbase:c,tangentMode:o}=n;let i;r?.fromOffset!==void 0?i=H(e,r.fromOffset,r.fromIsPercentage??!1):i=e.end;let l;r?.toOffset!==void 0?l=H(t,r.toOffset,r.toIsPercentage??!1):l=t.start;const a=K(e.start,e.end),u=s?{x:i.x-a.x*c,y:i.y-a.y*c}:i,m=K(e.start,e.end),g=K(t.start,t.end),I=R(u,l),p=we(o,I),d=s?{x:u.x-m.x*p,y:u.y-m.y*p}:{x:u.x+m.x*p,y:u.y+m.y*p},v={x:l.x-g.x*p,y:l.y-g.y*p};return{p0:u,p1:d,p2:v,p3:l}}function le(e,t){return{x:e.start.x+(e.end.x-e.start.x)*t,y:e.start.y+(e.end.y-e.start.y)*t}}function H(e,t,n){const s=R(e.start,e.end);let r;return n?r=t/100:r=s>0?t/s:0,r=Math.max(0,Math.min(1,r)),le(e,r)}function Y(e,t){const{p0:n,p1:s,p2:r,p3:c}=e,o=1-t,i=o*o,l=i*o,a=t*t,u=a*t;return{x:l*n.x+3*i*t*s.x+3*o*a*r.x+u*c.x,y:l*n.y+3*i*t*s.y+3*o*a*r.y+u*c.y}}function de(e,t=100){const n=[{t:0,distance:0}];let s=e.p0,r=0;for(let c=1;c<=t;c++){const o=c/t,i=Y(e,o);r+=R(s,i),n.push({t:o,distance:r}),s=i}return n}function me(e,t){if(t<=0)return 0;const n=e[e.length-1].distance;if(t>=n)return 1;let s=0,r=e.length-1;for(;s<r-1;){const u=Math.floor((s+r)/2);e[u].distance<t?s=u:r=u}const c=e[s].distance,o=e[r].distance,i=e[s].t,l=e[r].t;if(o===c)return i;const a=(t-c)/(o-c);return i+a*(l-i)}function Te(e){return e[e.length-1].distance}function ge(e,t=100){let n=0,s=e.p0;for(let r=1;r<=t;r++){const c=r/t,o=Y(e,c);n+=R(s,o),s=o}return n}function Ee(e,t,n,s){const r=R(e.start,e.end);return t===void 0?s/100*r:n?t/100*r:t}function se(e,t,n,s,r){const c=R(e.start,e.end),o=c-r;if(o<=0)return c;let i;if(t===void 0)i=s;else if(n)i=t;else return Math.max(r,Math.min(t,c));return r+i/100*o}function re(e,t,n,s,r){const o=R(e.start,e.end)-r;if(o<=0)return 0;let i;if(t===void 0)i=s;else if(n)i=t;else return Math.max(0,Math.min(t,o));return i/100*o}function he(e,t,n){const s=new Map,r=new Map,c=new Map;for(const o of e)r.set(o.id,o),c.set(o.id,R(o.start,o.end)),s.set(o.id,[]);for(let o=0;o<t.length;o++){const i=t[o],l=r.get(i.fromLineId),a=r.get(i.toLineId);if(!l||!a)continue;const u=se(l,i.fromOffset,i.fromIsPercentage,100,n.wheelbase),m=re(a,i.toOffset,i.toIsPercentage,0,n.wheelbase),g=ne(l,a,n,!1,{fromOffset:u,fromIsPercentage:!1,toOffset:m,toIsPercentage:!1}),I=ge(g),p={curveIndex:o,fromLineId:i.fromLineId,toLineId:i.toLineId,fromOffset:u,toOffset:m,curveLength:I};s.get(i.fromLineId).push(p)}return{adjacency:s,lines:r,lineLengths:c}}function oe(e,t,n,s,r=!1){const{adjacency:c,lines:o,lineLengths:i}=e;if(!o.get(n))return null;const a=i.get(n),u=r?s/100*a:s,m=[],g=new Map,I=(d,v)=>`${d}:${Math.round(v)}`;if(t.lineId===n&&u>=t.offset){const d=u-t.offset;return{segments:[{type:"line",lineId:t.lineId,startOffset:t.offset,endOffset:u,length:d}],totalDistance:d}}const p=c.get(t.lineId)||[];for(const d of p){if(d.fromOffset<t.offset)continue;const v=d.fromOffset-t.offset,O=v+d.curveLength,L={type:"line",lineId:t.lineId,startOffset:t.offset,endOffset:d.fromOffset,length:v},h={type:"curve",curveIndex:d.curveIndex,startOffset:0,endOffset:d.curveLength,length:d.curveLength};m.push({lineId:d.toLineId,entryOffset:d.toOffset,totalDistance:O,path:[L,h]})}for(m.sort((d,v)=>d.totalDistance-v.totalDistance);m.length>0;){const d=m.shift(),v=I(d.lineId,d.entryOffset),O=g.get(v);if(O!==void 0&&O<=d.totalDistance)continue;if(g.set(v,d.totalDistance),d.lineId===n){const h=Math.abs(u-d.entryOffset);if(u>=d.entryOffset){const x={type:"line",lineId:n,startOffset:d.entryOffset,endOffset:u,length:h};return{segments:[...d.path,x],totalDistance:d.totalDistance+h}}}const L=c.get(d.lineId)||[];for(const h of L){if(h.fromOffset<d.entryOffset)continue;const x=h.fromOffset-d.entryOffset,M=d.totalDistance+x+h.curveLength,C=I(h.toLineId,h.toOffset),V=g.get(C);if(V!==void 0&&V<=M)continue;const D={type:"line",lineId:d.lineId,startOffset:d.entryOffset,endOffset:h.fromOffset,length:x},y={type:"curve",curveIndex:h.curveIndex,startOffset:0,endOffset:h.curveLength,length:h.curveLength};m.push({lineId:h.toLineId,entryOffset:h.toOffset,totalDistance:M,path:[...d.path,D,y]})}m.sort((h,x)=>h.totalDistance-x.totalDistance)}return null}function Ve(e,t,n,s,r=!1){return oe(e,t,n,s,r)!==null}function $e(e,t,n){return(e.adjacency.get(t)||[]).filter(r=>r.fromOffset>=n)}function ie(e,t){const n=Math.sqrt(Math.pow(e.end.x-e.start.x,2)+Math.pow(e.end.y-e.start.y,2)),s=n>0?t/n:0;return{x:e.start.x+(e.end.x-e.start.x)*Math.min(1,Math.max(0,s)),y:e.start.y+(e.end.y-e.start.y)*Math.min(1,Math.max(0,s))}}function pe(e){return Math.sqrt(Math.pow(e.end.x-e.start.x,2)+Math.pow(e.end.y-e.start.y,2))}function ve(e,t,n){let s=0;for(let r=0;r<t;r++)s+=e.segments[r].length;return s+=n,s}function Ie(e,t){let n=0;for(let s=0;s<e.segments.length;s++){const r=e.segments[s],c=n+r.length;if(t<c)return{segmentIndex:s,segmentDistance:t-n};if(t===c)return s+1<e.segments.length?{segmentIndex:s+1,segmentDistance:0}:{segmentIndex:s,segmentDistance:r.length};n+=r.length}return null}function X(e,t,n,s){const c=ve(e,t,n)+s;return Ie(e,c)}function ce(e,t,n){const s=e.execution,r=n.vehicleQueues.get(e.vehicle.id),c=r?.[s.currentCommandIndex];if(c&&n.onCommandComplete&&n.onCommandComplete({vehicleId:e.vehicle.id,command:c,finalPosition:{lineId:e.vehicle.rear.lineId,absoluteOffset:e.vehicle.rear.absoluteOffset,position:e.vehicle.rear.position},payload:c.payload}),c?.awaitConfirmation)return{handled:!0,vehicle:{...e.vehicle,state:"waiting"},newExecution:s,isWaiting:!0};const o=s.currentCommandIndex+1;if(r&&o<r.length){const l=r[o],a=n.graphRef.current;if(a){const u={graph:a,linesMap:n.linesMap,curves:n.curves,config:n.config},m=n.prepareCommandPath(e.vehicle,l,u);if(m){const g=X(m.path,0,t,n.config.wheelbase),I={path:m.path,curveDataMap:m.curveDataMap,currentCommandIndex:o,rear:{currentSegmentIndex:0,segmentDistance:t},front:g?{currentSegmentIndex:g.segmentIndex,segmentDistance:g.segmentDistance}:{currentSegmentIndex:0,segmentDistance:t}};return{handled:!0,vehicle:{...e.vehicle,state:"moving"},newExecution:I}}}}return{handled:!0,vehicle:{...e.vehicle,state:"idle"},newExecution:null}}function be(e,t){const n=e.execution;return n?n.rear.currentSegmentIndex>=n.path.segments.length?ce(e,n.rear.segmentDistance,t):{handled:!1,vehicle:e.vehicle}:{handled:!1,vehicle:e.vehicle}}function k(e,t){return{position:ie(e,t),lineId:e.id,absoluteOffset:t}}function U(e,t){const n=me(e.arcLengthTable,t);return{position:Y(e.bezier,n)}}function Z(e,t,n,s,r,c,o){const i=n.segments[t.currentSegmentIndex],l=t.segmentDistance+s;if(l>=i.length){const u=l-i.length,m=t.currentSegmentIndex+1;if(m>=n.segments.length){if(o!==void 0&&i.type==="line"){const d=r.get(i.lineId),v=i.startOffset+l;if(v<=o){const L=k(d,v);return{axleState:{...e,...L},execution:{...t,segmentDistance:l},completed:!1}}const O=k(d,o);return{axleState:{...e,...O},execution:{...t,segmentDistance:o-i.startOffset},completed:!0}}const p=i.type==="line"?k(r.get(i.lineId),i.endOffset):U(c.get(i.curveIndex),i.length);return{axleState:{...e,...p},execution:{...t,segmentDistance:i.length},completed:!0}}const g=n.segments[m],I=g.type==="line"?k(r.get(g.lineId),g.startOffset+u):U(c.get(g.curveIndex),u);return{axleState:{...e,...I},execution:{currentSegmentIndex:m,segmentDistance:u},completed:!1}}const a=i.type==="line"?k(r.get(i.lineId),i.startOffset+l):U(c.get(i.curveIndex),l);return{axleState:{...e,...a},execution:{...t,segmentDistance:l},completed:!1}}function xe(e,t,n,s){const r=Math.sqrt(Math.pow(s.end.x-s.start.x,2)+Math.pow(s.end.y-s.start.y,2));let c=t+n;c=Math.min(c,r);const o=ie(s,c);return{lineId:e,position:o,absoluteOffset:c}}function ye(e,t){return{...e,state:"idle"}}function Se(e){return{vehicle:e,execution:null}}function ee(e,t){const n=[],s=new Map;for(const r of e){if(!t.get(r.lineId))continue;const o=ye(r);n.push(o);const i=Se(o);s.set(r.id,i)}return{movingVehicles:n,stateMap:s}}function Re(e,t,n,s){const r=new Map;for(const c of e.segments)if(c.type==="curve"&&c.curveIndex!==void 0){const o=t[c.curveIndex];if(o){const i=n.get(o.fromLineId),l=n.get(o.toLineId);if(i&&l){const a=se(i,o.fromOffset,o.fromIsPercentage,100,s.wheelbase),u=re(l,o.toOffset,o.toIsPercentage,0,s.wheelbase),m=ne(i,l,s,!1,{fromOffset:a,fromIsPercentage:!1,toOffset:u,toIsPercentage:!1}),g=de(m);r.set(c.curveIndex,{bezier:m,arcLengthTable:g})}}}return r}function W(e,t,n){const{graph:s,linesMap:r,curves:c,config:o}=n,i=r.get(t.targetLineId);if(!i)return null;const a=pe(i)-o.wheelbase;if(a<=0)return null;const u=t.isPercentage?t.targetOffset/100*a:Math.min(t.targetOffset,a),m=oe(s,{lineId:e.lineId,offset:e.rear.absoluteOffset},t.targetLineId,u,!1);if(!m)return null;const g=Re(m,c,r,o);return{path:m,curveDataMap:g}}function Ae({vehicles:e,lines:t,vehicleQueues:n,velocity:s,wheelbase:r,tangentMode:c,curves:o,eventEmitter:i}){const[l,a]=f.useState("stopped"),[u,m]=f.useState([]),g=f.useMemo(()=>({wheelbase:r,tangentMode:c}),[r,c]),I=f.useMemo(()=>new Map(t.map(y=>[y.id,y])),[t]),p=f.useRef(null),d=f.useRef(new Map);f.useEffect(()=>{const{movingVehicles:y,stateMap:S}=ee(e,I);d.current=S;const P=setTimeout(()=>{m(y)},0);return()=>clearTimeout(P)},[e,I]);const v=f.useRef(null);f.useEffect(()=>{t.length>0&&(v.current=he(t,o,g))},[t,o,g]);const O=f.useRef(!1),L=f.useRef(()=>{}),h=f.useRef(0),x=f.useRef(new Set);f.useEffect(()=>{L.current=()=>{const y=s;let S=!1;for(const[,P]of d.current)P.vehicle.state==="moving"&&(S=!0);if(!S){O.current=!1,h.current=0;return}m(P=>P.map($=>{const w=d.current.get($.id);if(!w||$.state!=="moving"||!w.execution)return $;const T=w.execution;let A;if(T.front.currentSegmentIndex<T.path.segments.length){const F=T.path.segments[T.front.currentSegmentIndex];if(F.type==="line"){const E=I.get(F.lineId);E&&(A=Math.sqrt(Math.pow(E.end.x-E.start.x,2)+Math.pow(E.end.y-E.start.y,2)))}}const q=Z($.rear,T.rear,T.path,y,I,T.curveDataMap),b=Z($.front,T.front,T.path,y,I,T.curveDataMap,A);if(q.completed){const F={linesMap:I,config:g,vehicleQueues:n,curves:o,graphRef:v,prepareCommandPath:W,onCommandComplete:i?j=>i.emit("commandComplete",j):void 0},E={...$,rear:q.axleState,front:b.axleState};w.vehicle=E,w.execution.rear=q.execution,w.execution.front=b.execution;const ae=q.execution.segmentDistance,z=ce(w,ae,F);if(w.vehicle=z.vehicle,z.newExecution!==void 0&&(w.execution=z.newExecution),i){const j=z.vehicle.rear.position,G=z.vehicle.front.position;i.emit("positionUpdate",{vehicleId:z.vehicle.id,rear:j,front:G,center:{x:(j.x+G.x)/2,y:(j.y+G.y)/2},angle:Math.atan2(G.y-j.y,G.x-j.x)})}return z.vehicle}const N={...$,rear:q.axleState,front:b.axleState};if(w.vehicle=N,w.execution.rear=q.execution,w.execution.front=b.execution,i){const F=N.rear.position,E=N.front.position;i.emit("positionUpdate",{vehicleId:N.id,rear:F,front:E,center:{x:(F.x+E.x)/2,y:(F.y+E.y)/2},angle:Math.atan2(E.y-F.y,E.x-F.x)})}return N})),O.current&&(p.current=requestAnimationFrame(()=>L.current()))}},[s,I,o,g,n,i]),f.useEffect(()=>(l==="running"?(O.current=!0,p.current=requestAnimationFrame(()=>L.current())):O.current=!1,()=>{p.current&&cancelAnimationFrame(p.current)}),[l]);const M=f.useCallback(()=>{if(l==="running")return;const y=v.current;y&&(h.current=0,x.current.clear(),m(S=>S.map(P=>{const $=n.get(P.id);if(!$||$.length===0)return P;const w=$[0],A=W(P,w,{graph:y,linesMap:I,curves:o,config:g});if(!A)return console.warn(`No path found for vehicle ${P.id}`),P;const q=d.current.get(P.id);if(q){const b=X(A.path,0,0,r);q.execution={path:A.path,curveDataMap:A.curveDataMap,currentCommandIndex:0,rear:{currentSegmentIndex:0,segmentDistance:0},front:b?{currentSegmentIndex:b.segmentIndex,segmentDistance:b.segmentDistance}:{currentSegmentIndex:0,segmentDistance:0}},q.vehicle={...P,state:"moving"}}return{...P,state:"moving"}})),a("running"))},[l,I,o,n,g,r]),C=f.useCallback(()=>{p.current&&(cancelAnimationFrame(p.current),p.current=null),a("paused")},[]),V=f.useCallback(()=>{p.current&&(cancelAnimationFrame(p.current),p.current=null);const{movingVehicles:y,stateMap:S}=ee(e,I);d.current=S,m(y),a("stopped")},[e,I]),D=f.useCallback(y=>{const S=d.current.get(y);if(!S||S.vehicle.state!=="waiting")return!1;const P=n.get(y),$=S.execution;if(!$)return!1;const w=$.currentCommandIndex+1;if(P&&w<P.length){const T=v.current;if(T){const A=P[w],q={graph:T,linesMap:I,curves:o,config:g},b=W(S.vehicle,A,q);if(b){const N=X(b.path,0,0,r);return S.execution={path:b.path,curveDataMap:b.curveDataMap,currentCommandIndex:w,rear:{currentSegmentIndex:0,segmentDistance:0},front:N?{currentSegmentIndex:N.segmentIndex,segmentDistance:N.segmentDistance}:{currentSegmentIndex:0,segmentDistance:0}},S.vehicle={...S.vehicle,state:"moving"},m(F=>F.map(E=>E.id===y?S.vehicle:E)),l!=="running"&&a("running"),!0}}}return S.vehicle={...S.vehicle,state:"idle"},S.execution=null,m(T=>T.map(A=>A.id===y?S.vehicle:A)),!0},[n,I,o,g,r,l]);return{movingVehicles:u,playbackState:l,handleRun:M,handlePause:C,handleReset:V,continueVehicle:D}}class Le{listeners=new Map;on(t,n){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(n),()=>{this.listeners.get(t)?.delete(n)}}emit(t,n){this.listeners.get(t)?.forEach(s=>{try{s(n)}catch(r){console.error(`Error in event listener for "${t}":`,r)}})}off(t){t?this.listeners.delete(t):this.listeners.clear()}listenerCount(t){return this.listeners.get(t)?.size??0}}const Me=f.createContext(null);function Oe(){const e=f.useContext(Me);if(!e)throw new Error("useVehicleEventEmitter must be used within a VehicleEventProvider");return e}function qe(){return f.useMemo(()=>new Le,[])}function Fe(e,t,n=[]){const s=Oe();f.useEffect(()=>s.on(e,t),[s,e,...n])}function te(e){const t=[],n=[],s=[],r=e.trim().split(`
2
- `);for(const c of r){const o=c.trim();if(!o||o.startsWith("#"))continue;const i=o.match(/^(\w+)\s*:\s*\((\d+),\s*(\d+)\)\s*->\s*\((\d+),\s*(\d+)\)/);if(i){t.push({id:i[1],start:{x:parseInt(i[2]),y:parseInt(i[3])},end:{x:parseInt(i[4]),y:parseInt(i[5])}});continue}const l=o.match(/^(\w+)(?:\s+(\d+(?:\.\d+)?)(%?))??\s*->\s*(\w+)(?:\s+(\d+(?:\.\d+)?)(%?))?/);if(l){const u={fromLineId:l[1],toLineId:l[4]};l[2]&&(u.fromOffset=parseFloat(l[2]),u.fromIsPercentage=l[3]==="%"),l[5]&&(u.toOffset=parseFloat(l[5]),u.toIsPercentage=l[6]==="%"),n.push(u);continue}const a=o.match(/^(\w+)\s+start\s+(\w+)\s+(\d+(?:\.\d+)?)(%?)/);if(a){const u=parseFloat(a[3]),m=a[4]==="%";s.push({vehicleId:a[1],lineId:a[2],offset:u,isPercentage:m});continue}}return{lines:t,curves:n,vehicles:s}}function Ce(e){const t=[];return e.lines.forEach(n=>{t.push(`${n.id} : (${Math.round(n.start.x)}, ${Math.round(n.start.y)}) -> (${Math.round(n.end.x)}, ${Math.round(n.end.y)})`)}),e.lines.length>0&&e.curves.length>0&&t.push(""),e.curves.forEach(n=>{let s=`${n.fromLineId}`;n.fromOffset!==void 0&&(s+=` ${n.fromOffset}${n.fromIsPercentage?"%":""}`),s+=" -> ",s+=`${n.toLineId}`,n.toOffset!==void 0&&(s+=` ${n.toOffset}${n.toIsPercentage?"%":""}`),t.push(s)}),(e.lines.length>0||e.curves.length>0)&&e.vehicles.length>0&&t.push(""),e.vehicles.forEach(n=>{const s=n.isPercentage?`${n.offset}%`:`${n.offset}`;t.push(`${n.vehicleId} start ${n.lineId} ${s}`)}),t.join(`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("react");function R(e,t){const n=t.x-e.x,s=t.y-e.y;return Math.sqrt(n*n+s*s)}function K(e,t){const n=t.x-e.x,s=t.y-e.y,r=Math.sqrt(n*n+s*s);return r===0?{x:0,y:0}:{x:n/r,y:s/r}}function we(e,t){return t*(e==="proportional-40"?.4:.5522)}function ne(e,t,n,s=!1,r){const{wheelbase:c,tangentMode:i}=n;let o;r?.fromOffset!==void 0?o=H(e,r.fromOffset,r.fromIsPercentage??!1):o=e.end;let l;r?.toOffset!==void 0?l=H(t,r.toOffset,r.toIsPercentage??!1):l=t.start;const a=K(e.start,e.end),u=s?{x:o.x-a.x*c,y:o.y-a.y*c}:o,g=K(e.start,e.end),m=K(t.start,t.end),x=R(u,l),p=we(i,x),d=s?{x:u.x-g.x*p,y:u.y-g.y*p}:{x:u.x+g.x*p,y:u.y+g.y*p},v={x:l.x-m.x*p,y:l.y-m.y*p};return{p0:u,p1:d,p2:v,p3:l}}function le(e,t){return{x:e.start.x+(e.end.x-e.start.x)*t,y:e.start.y+(e.end.y-e.start.y)*t}}function H(e,t,n){const s=R(e.start,e.end);let r;return n?r=t/100:r=s>0?t/s:0,r=Math.max(0,Math.min(1,r)),le(e,r)}function Y(e,t){const{p0:n,p1:s,p2:r,p3:c}=e,i=1-t,o=i*i,l=o*i,a=t*t,u=a*t;return{x:l*n.x+3*o*t*s.x+3*i*a*r.x+u*c.x,y:l*n.y+3*o*t*s.y+3*i*a*r.y+u*c.y}}function de(e,t=100){const n=[{t:0,distance:0}];let s=e.p0,r=0;for(let c=1;c<=t;c++){const i=c/t,o=Y(e,i);r+=R(s,o),n.push({t:i,distance:r}),s=o}return n}function ge(e,t){if(t<=0)return 0;const n=e[e.length-1].distance;if(t>=n)return 1;let s=0,r=e.length-1;for(;s<r-1;){const u=Math.floor((s+r)/2);e[u].distance<t?s=u:r=u}const c=e[s].distance,i=e[r].distance,o=e[s].t,l=e[r].t;if(i===c)return o;const a=(t-c)/(i-c);return o+a*(l-o)}function Te(e){return e[e.length-1].distance}function me(e,t=100){let n=0,s=e.p0;for(let r=1;r<=t;r++){const c=r/t,i=Y(e,c);n+=R(s,i),s=i}return n}function Ve(e,t,n,s){const r=R(e.start,e.end);return t===void 0?s/100*r:n?t/100*r:t}function se(e,t,n,s,r){const c=R(e.start,e.end),i=c-r;if(i<=0)return c;let o;if(t===void 0)o=s;else if(n)o=t;else return Math.max(r,Math.min(t,c));return r+o/100*i}function re(e,t,n,s,r){const i=R(e.start,e.end)-r;if(i<=0)return 0;let o;if(t===void 0)o=s;else if(n)o=t;else return Math.max(0,Math.min(t,i));return o/100*i}function he(e,t,n){const s=new Map,r=new Map,c=new Map;for(const i of e)r.set(i.id,i),c.set(i.id,R(i.start,i.end)),s.set(i.id,[]);for(let i=0;i<t.length;i++){const o=t[i],l=r.get(o.fromLineId),a=r.get(o.toLineId);if(!l||!a)continue;const u=se(l,o.fromOffset,o.fromIsPercentage,100,n.wheelbase),g=re(a,o.toOffset,o.toIsPercentage,0,n.wheelbase),m=ne(l,a,n,!1,{fromOffset:u,fromIsPercentage:!1,toOffset:g,toIsPercentage:!1}),x=me(m),p={curveIndex:i,fromLineId:o.fromLineId,toLineId:o.toLineId,fromOffset:u,toOffset:g,curveLength:x};s.get(o.fromLineId).push(p)}return{adjacency:s,lines:r,lineLengths:c}}function oe(e,t,n,s,r=!1){const{adjacency:c,lines:i,lineLengths:o}=e;if(!i.get(n))return null;const a=o.get(n),u=r?s/100*a:s,g=[],m=new Map,x=(d,v)=>`${d}:${Math.round(v)}`;if(t.lineId===n&&u>=t.offset){const d=u-t.offset;return{segments:[{type:"line",lineId:t.lineId,startOffset:t.offset,endOffset:u,length:d}],totalDistance:d}}const p=c.get(t.lineId)||[];for(const d of p){if(d.fromOffset<t.offset)continue;const v=d.fromOffset-t.offset,O=v+d.curveLength,L={type:"line",lineId:t.lineId,startOffset:t.offset,endOffset:d.fromOffset,length:v},h={type:"curve",curveIndex:d.curveIndex,startOffset:0,endOffset:d.curveLength,length:d.curveLength};g.push({lineId:d.toLineId,entryOffset:d.toOffset,totalDistance:O,path:[L,h]})}for(g.sort((d,v)=>d.totalDistance-v.totalDistance);g.length>0;){const d=g.shift(),v=x(d.lineId,d.entryOffset),O=m.get(v);if(O!==void 0&&O<=d.totalDistance)continue;if(m.set(v,d.totalDistance),d.lineId===n){const h=Math.abs(u-d.entryOffset);if(u>=d.entryOffset){const y={type:"line",lineId:n,startOffset:d.entryOffset,endOffset:u,length:h};return{segments:[...d.path,y],totalDistance:d.totalDistance+h}}}const L=c.get(d.lineId)||[];for(const h of L){if(h.fromOffset<d.entryOffset)continue;const y=h.fromOffset-d.entryOffset,M=d.totalDistance+y+h.curveLength,C=x(h.toLineId,h.toOffset),$=m.get(C);if($!==void 0&&$<=M)continue;const P={type:"line",lineId:d.lineId,startOffset:d.entryOffset,endOffset:h.fromOffset,length:y},I={type:"curve",curveIndex:h.curveIndex,startOffset:0,endOffset:h.curveLength,length:h.curveLength};g.push({lineId:h.toLineId,entryOffset:h.toOffset,totalDistance:M,path:[...d.path,P,I]})}g.sort((h,y)=>h.totalDistance-y.totalDistance)}return null}function $e(e,t,n,s,r=!1){return oe(e,t,n,s,r)!==null}function be(e,t,n){return(e.adjacency.get(t)||[]).filter(r=>r.fromOffset>=n)}function ie(e,t){const n=Math.sqrt(Math.pow(e.end.x-e.start.x,2)+Math.pow(e.end.y-e.start.y,2)),s=n>0?t/n:0;return{x:e.start.x+(e.end.x-e.start.x)*Math.min(1,Math.max(0,s)),y:e.start.y+(e.end.y-e.start.y)*Math.min(1,Math.max(0,s))}}function pe(e){return Math.sqrt(Math.pow(e.end.x-e.start.x,2)+Math.pow(e.end.y-e.start.y,2))}function ve(e,t,n){let s=0;for(let r=0;r<t;r++)s+=e.segments[r].length;return s+=n,s}function Ie(e,t){let n=0;for(let s=0;s<e.segments.length;s++){const r=e.segments[s],c=n+r.length;if(t<c)return{segmentIndex:s,segmentDistance:t-n};if(t===c)return s+1<e.segments.length?{segmentIndex:s+1,segmentDistance:0}:{segmentIndex:s,segmentDistance:r.length};n+=r.length}return null}function X(e,t,n,s){const c=ve(e,t,n)+s;return Ie(e,c)}function ce(e,t,n){const s=e.execution,r=n.vehicleQueues.get(e.vehicle.id),c=r?.[s.currentCommandIndex];if(c&&n.onCommandComplete&&n.onCommandComplete({vehicleId:e.vehicle.id,command:c,finalPosition:{lineId:e.vehicle.rear.lineId,absoluteOffset:e.vehicle.rear.absoluteOffset,position:e.vehicle.rear.position},payload:c.payload}),c?.awaitConfirmation)return{handled:!0,vehicle:{...e.vehicle,state:"waiting"},newExecution:s,isWaiting:!0};const i=s.currentCommandIndex+1;if(r&&i<r.length){const l=r[i],a=n.graphRef.current;if(a){const u={graph:a,linesMap:n.linesMap,curves:n.curves,config:n.config},g=n.prepareCommandPath(e.vehicle,l,u);if(g){const m=X(g.path,0,t,n.config.wheelbase),x={path:g.path,curveDataMap:g.curveDataMap,currentCommandIndex:i,rear:{currentSegmentIndex:0,segmentDistance:t},front:m?{currentSegmentIndex:m.segmentIndex,segmentDistance:m.segmentDistance}:{currentSegmentIndex:0,segmentDistance:t}};return{handled:!0,vehicle:{...e.vehicle,state:"moving"},newExecution:x}}}}return{handled:!0,vehicle:{...e.vehicle,state:"idle"},newExecution:null}}function Ee(e,t){const n=e.execution;return n?n.rear.currentSegmentIndex>=n.path.segments.length?ce(e,n.rear.segmentDistance,t):{handled:!1,vehicle:e.vehicle}:{handled:!1,vehicle:e.vehicle}}function k(e,t){return{position:ie(e,t),lineId:e.id,absoluteOffset:t}}function U(e,t){const n=ge(e.arcLengthTable,t);return{position:Y(e.bezier,n)}}function Z(e,t,n,s,r,c,i){const o=n.segments[t.currentSegmentIndex],l=t.segmentDistance+s;if(l>=o.length){const u=l-o.length,g=t.currentSegmentIndex+1;if(g>=n.segments.length){if(i!==void 0&&o.type==="line"){const d=r.get(o.lineId),v=o.startOffset+l;if(v<=i){const L=k(d,v);return{axleState:{...e,...L},execution:{...t,segmentDistance:l},completed:!1}}const O=k(d,i);return{axleState:{...e,...O},execution:{...t,segmentDistance:i-o.startOffset},completed:!0}}const p=o.type==="line"?k(r.get(o.lineId),o.endOffset):U(c.get(o.curveIndex),o.length);return{axleState:{...e,...p},execution:{...t,segmentDistance:o.length},completed:!0}}const m=n.segments[g],x=m.type==="line"?k(r.get(m.lineId),m.startOffset+u):U(c.get(m.curveIndex),u);return{axleState:{...e,...x},execution:{currentSegmentIndex:g,segmentDistance:u},completed:!1}}const a=o.type==="line"?k(r.get(o.lineId),o.startOffset+l):U(c.get(o.curveIndex),l);return{axleState:{...e,...a},execution:{...t,segmentDistance:l},completed:!1}}function xe(e,t,n,s){const r=Math.sqrt(Math.pow(s.end.x-s.start.x,2)+Math.pow(s.end.y-s.start.y,2));let c=t+n;c=Math.min(c,r);const i=ie(s,c);return{lineId:e,position:i,absoluteOffset:c}}function ye(e,t){return{...e,state:"idle"}}function Se(e){return{vehicle:e,execution:null}}function ee(e,t){const n=[],s=new Map;for(const r of e){if(!t.get(r.lineId))continue;const i=ye(r);n.push(i);const o=Se(i);s.set(r.id,o)}return{movingVehicles:n,stateMap:s}}function Re(e,t,n,s){const r=new Map;for(const c of e.segments)if(c.type==="curve"&&c.curveIndex!==void 0){const i=t[c.curveIndex];if(i){const o=n.get(i.fromLineId),l=n.get(i.toLineId);if(o&&l){const a=se(o,i.fromOffset,i.fromIsPercentage,100,s.wheelbase),u=re(l,i.toOffset,i.toIsPercentage,0,s.wheelbase),g=ne(o,l,s,!1,{fromOffset:a,fromIsPercentage:!1,toOffset:u,toIsPercentage:!1}),m=de(g);r.set(c.curveIndex,{bezier:g,arcLengthTable:m})}}}return r}function W(e,t,n){const{graph:s,linesMap:r,curves:c,config:i}=n,o=r.get(t.targetLineId);if(!o)return null;const a=pe(o)-i.wheelbase;if(a<=0)return null;const u=t.isPercentage?t.targetOffset/100*a:Math.min(t.targetOffset,a),g=oe(s,{lineId:e.lineId,offset:e.rear.absoluteOffset},t.targetLineId,u,!1);if(!g)return null;const m=Re(g,c,r,i);return{path:g,curveDataMap:m}}function Ae({vehicles:e,lines:t,vehicleQueues:n,velocity:s,wheelbase:r,tangentMode:c,curves:i,eventEmitter:o}){const[l,a]=f.useState("stopped"),[u,g]=f.useState([]),m=f.useMemo(()=>({wheelbase:r,tangentMode:c}),[r,c]),x=f.useMemo(()=>new Map(t.map(I=>[I.id,I])),[t]),p=f.useRef(null),d=f.useRef(new Map);f.useEffect(()=>{const{movingVehicles:I,stateMap:S}=ee(e,x);d.current=S;const D=setTimeout(()=>{g(I)},0);return()=>clearTimeout(D)},[e,x]);const v=f.useRef(null);f.useEffect(()=>{t.length>0&&(v.current=he(t,i,m))},[t,i,m]);const O=f.useRef(!1),L=f.useRef(()=>{}),h=f.useRef(0),y=f.useRef(new Set);f.useEffect(()=>{L.current=()=>{const I=s;let S=!1;for(const[,D]of d.current)D.vehicle.state==="moving"&&(S=!0);if(!S){O.current=!1,h.current=0;return}g(D=>D.map(b=>{const w=d.current.get(b.id);if(!w||b.state!=="moving"||!w.execution)return b;const T=w.execution;let A;if(T.front.currentSegmentIndex<T.path.segments.length){const F=T.path.segments[T.front.currentSegmentIndex];if(F.type==="line"){const V=x.get(F.lineId);V&&(A=Math.sqrt(Math.pow(V.end.x-V.start.x,2)+Math.pow(V.end.y-V.start.y,2)))}}const q=Z(b.rear,T.rear,T.path,I,x,T.curveDataMap),E=Z(b.front,T.front,T.path,I,x,T.curveDataMap,A);if(q.completed){const F={linesMap:x,config:m,vehicleQueues:n,curves:i,graphRef:v,prepareCommandPath:W,onCommandComplete:o?j=>o.emit("commandComplete",j):void 0},V={...b,rear:q.axleState,front:E.axleState};w.vehicle=V,w.execution.rear=q.execution,w.execution.front=E.execution;const ae=q.execution.segmentDistance,z=ce(w,ae,F);if(w.vehicle=z.vehicle,z.newExecution!==void 0&&(w.execution=z.newExecution),o&&z.vehicle.state!=="moving"&&o.emit("stateChange",{vehicleId:b.id,from:"moving",to:z.vehicle.state}),o){const j=z.vehicle.rear.position,G=z.vehicle.front.position;o.emit("positionUpdate",{vehicleId:z.vehicle.id,rear:j,front:G,center:{x:(j.x+G.x)/2,y:(j.y+G.y)/2},angle:Math.atan2(G.y-j.y,G.x-j.x)})}return z.vehicle}const N={...b,rear:q.axleState,front:E.axleState};if(w.vehicle=N,w.execution.rear=q.execution,w.execution.front=E.execution,o){const F=N.rear.position,V=N.front.position;o.emit("positionUpdate",{vehicleId:N.id,rear:F,front:V,center:{x:(F.x+V.x)/2,y:(F.y+V.y)/2},angle:Math.atan2(V.y-F.y,V.x-F.x)})}return N})),O.current&&(p.current=requestAnimationFrame(()=>L.current()))}},[s,x,i,m,n,o]),f.useEffect(()=>(l==="running"?(O.current=!0,p.current=requestAnimationFrame(()=>L.current())):O.current=!1,()=>{p.current&&cancelAnimationFrame(p.current)}),[l]);const M=f.useCallback(()=>{if(l==="running")return;const I=v.current;I&&(h.current=0,y.current.clear(),g(S=>S.map(D=>{const b=n.get(D.id);if(!b||b.length===0)return D;const w=b[0],A=W(D,w,{graph:I,linesMap:x,curves:i,config:m});if(!A)return console.warn(`No path found for vehicle ${D.id}`),D;const q=d.current.get(D.id);if(q){const E=X(A.path,0,0,r);q.execution={path:A.path,curveDataMap:A.curveDataMap,currentCommandIndex:0,rear:{currentSegmentIndex:0,segmentDistance:0},front:E?{currentSegmentIndex:E.segmentIndex,segmentDistance:E.segmentDistance}:{currentSegmentIndex:0,segmentDistance:0}},q.vehicle={...D,state:"moving"},o&&D.state!=="moving"&&o.emit("stateChange",{vehicleId:D.id,from:D.state,to:"moving"})}return{...D,state:"moving"}})),a("running"))},[l,x,i,n,m,r,o]),C=f.useCallback(()=>{p.current&&(cancelAnimationFrame(p.current),p.current=null),a("paused")},[]),$=f.useCallback(()=>{p.current&&(cancelAnimationFrame(p.current),p.current=null);const{movingVehicles:I,stateMap:S}=ee(e,x);d.current=S,g(I),a("stopped")},[e,x]),P=f.useCallback(I=>{const S=d.current.get(I);if(!S||S.vehicle.state!=="waiting")return!1;const D=n.get(I),b=S.execution;if(!b)return!1;const w=b.currentCommandIndex+1;if(D&&w<D.length){const T=v.current;if(T){const A=D[w],q={graph:T,linesMap:x,curves:i,config:m},E=W(S.vehicle,A,q);if(E){const N=X(E.path,0,0,r);return S.execution={path:E.path,curveDataMap:E.curveDataMap,currentCommandIndex:w,rear:{currentSegmentIndex:0,segmentDistance:0},front:N?{currentSegmentIndex:N.segmentIndex,segmentDistance:N.segmentDistance}:{currentSegmentIndex:0,segmentDistance:0}},S.vehicle={...S.vehicle,state:"moving"},o&&o.emit("stateChange",{vehicleId:I,from:"waiting",to:"moving"}),g(F=>F.map(V=>V.id===I?S.vehicle:V)),l!=="running"&&a("running"),!0}}}return S.vehicle={...S.vehicle,state:"idle"},S.execution=null,o&&o.emit("stateChange",{vehicleId:I,from:"waiting",to:"idle"}),g(T=>T.map(A=>A.id===I?S.vehicle:A)),!0},[n,x,i,m,r,l,o]);return{movingVehicles:u,playbackState:l,handleRun:M,handlePause:C,handleReset:$,continueVehicle:P}}class Le{listeners=new Map;on(t,n){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(n),()=>{this.listeners.get(t)?.delete(n)}}emit(t,n){this.listeners.get(t)?.forEach(s=>{try{s(n)}catch(r){console.error(`Error in event listener for "${t}":`,r)}})}off(t){t?this.listeners.delete(t):this.listeners.clear()}listenerCount(t){return this.listeners.get(t)?.size??0}}const Me=f.createContext(null);function Oe(){const e=f.useContext(Me);if(!e)throw new Error("useVehicleEventEmitter must be used within a VehicleEventProvider");return e}function qe(){return f.useMemo(()=>new Le,[])}function Fe(e,t,n=[]){const s=Oe();f.useEffect(()=>s.on(e,t),[s,e,...n])}function te(e){const t=[],n=[],s=[],r=e.trim().split(`
2
+ `);for(const c of r){const i=c.trim();if(!i||i.startsWith("#"))continue;const o=i.match(/^(\w+)\s*:\s*\((\d+),\s*(\d+)\)\s*->\s*\((\d+),\s*(\d+)\)/);if(o){t.push({id:o[1],start:{x:parseInt(o[2]),y:parseInt(o[3])},end:{x:parseInt(o[4]),y:parseInt(o[5])}});continue}const l=i.match(/^(\w+)(?:\s+(\d+(?:\.\d+)?)(%?))??\s*->\s*(\w+)(?:\s+(\d+(?:\.\d+)?)(%?))?/);if(l){const u={fromLineId:l[1],toLineId:l[4]};l[2]&&(u.fromOffset=parseFloat(l[2]),u.fromIsPercentage=l[3]==="%"),l[5]&&(u.toOffset=parseFloat(l[5]),u.toIsPercentage=l[6]==="%"),n.push(u);continue}const a=i.match(/^(\w+)\s+start\s+(\w+)\s+(\d+(?:\.\d+)?)(%?)/);if(a){const u=parseFloat(a[3]),g=a[4]==="%";s.push({vehicleId:a[1],lineId:a[2],offset:u,isPercentage:g});continue}}return{lines:t,curves:n,vehicles:s}}function Ce(e){const t=[];return e.lines.forEach(n=>{t.push(`${n.id} : (${Math.round(n.start.x)}, ${Math.round(n.start.y)}) -> (${Math.round(n.end.x)}, ${Math.round(n.end.y)})`)}),e.lines.length>0&&e.curves.length>0&&t.push(""),e.curves.forEach(n=>{let s=`${n.fromLineId}`;n.fromOffset!==void 0&&(s+=` ${n.fromOffset}${n.fromIsPercentage?"%":""}`),s+=" -> ",s+=`${n.toLineId}`,n.toOffset!==void 0&&(s+=` ${n.toOffset}${n.toIsPercentage?"%":""}`),t.push(s)}),(e.lines.length>0||e.curves.length>0)&&e.vehicles.length>0&&t.push(""),e.vehicles.forEach(n=>{const s=n.isPercentage?`${n.offset}%`:`${n.offset}`;t.push(`${n.vehicleId} start ${n.lineId} ${s}`)}),t.join(`
3
3
  `)}function B(e){const t=[],n=e.trim().split(`
4
4
  `);for(const s of n){const r=s.trim();if(!r||r.startsWith("#"))continue;const c=r.match(/^(\w+)\s+start\s+(\w+)\s+(\d+(?:\.\d+)?)(%?)/);c&&t.push({vehicleId:c[1],lineId:c[2],offset:parseFloat(c[3]),isPercentage:c[4]==="%"})}return t}function De(e){return e.map(t=>{const n=t.isPercentage?`${t.offset}%`:`${t.offset}`;return`${t.vehicleId} start ${t.lineId} ${n}`}).join(`
5
5
  `)}function Q(e){const t=[],n=e.trim().split(`
6
- `);for(const s of n){const r=s.trim();if(!r||r.startsWith("#"))continue;const c=r.match(/^(\w+)\s+goto\s+(\w+)\s+(\d+(?:\.\d+)?)(%?)/);if(c){const o=r.slice(c[0].length),i=o.includes("--wait");let l;const a=o.match(/--payload\s+(\{.*\})/);if(a)try{l=JSON.parse(a[1])}catch{console.warn(`Invalid JSON payload in goto command: ${a[1]}`)}t.push({vehicleId:c[1],targetLineId:c[2],targetOffset:parseFloat(c[3]),isPercentage:c[4]==="%",awaitConfirmation:i,payload:l})}}return t}function Pe(e){return e.map(t=>{const n=t.isPercentage?`${t.targetOffset}%`:`${t.targetOffset}`,s=t.awaitConfirmation?" --wait":"",r=t.payload!==void 0?` --payload ${JSON.stringify(t.payload)}`:"";return`${t.vehicleId} goto ${t.targetLineId} ${n}${s}${r}`}).join(`
7
- `)}function Ne(){const[e,t]=f.useState([]),[n,s]=f.useState([]),[r,c]=f.useState(""),[o,i]=f.useState(null),[l,a]=f.useState(!1),[u,m]=f.useState(0),g=f.useRef(!1),I=f.useRef("");return f.useEffect(()=>{I.current=r},[r]),f.useEffect(()=>{if(g.current)return;const p=te(I.current),d=Ce({lines:e,curves:n,vehicles:p.vehicles});d!==I.current&&(g.current=!0,c(d),setTimeout(()=>{g.current=!1},50))},[e,n]),f.useEffect(()=>{if(g.current)return;const p=setTimeout(()=>{a(!0),m(v=>v+1)},0),d=setTimeout(()=>{try{const v=te(r);a(!1);const O=JSON.stringify(v.lines)!==JSON.stringify(e),L=JSON.stringify(v.curves)!==JSON.stringify(n);(O||L)&&(g.current=!0,O&&t(v.lines),L&&s(v.curves),setTimeout(()=>{g.current=!1},100)),i(null)}catch(v){a(!1),i(v instanceof Error?v.message:"Invalid scene definition")}},2e3);return()=>{clearTimeout(p),clearTimeout(d)}},[r,e,n]),{lines:e,curves:n,sceneDefinitionText:r,sceneError:o,isDebouncing:l,debounceKey:u,setLines:t,setCurves:s,setSceneDefinitionText:c}}function J(e,t,n=0){const s=[],r=[],c=new Set;for(const o of e){if(c.has(o.vehicleId)){r.push(`Duplicate vehicle ID: ${o.vehicleId}`);continue}c.add(o.vehicleId);const i=t.find(p=>p.id===o.lineId);if(!i){r.push(`Vehicle ${o.vehicleId}: Line "${o.lineId}" not found`);continue}const l=R(i.start,i.end),a=Math.max(0,l-n);let u;if(o.isPercentage){if(o.offset<0||o.offset>100){r.push(`Vehicle ${o.vehicleId}: Offset ${o.offset}% must be between 0% and 100%`);continue}u=o.offset/100*a}else{if(o.offset<0||o.offset>l){r.push(`Vehicle ${o.vehicleId}: Offset ${o.offset} exceeds line length ${l.toFixed(2)}`);continue}u=Math.min(o.offset,a)}const m=H(i,u,!1),g={lineId:o.lineId,position:m,absoluteOffset:u},I=xe(o.lineId,u,n,i);s.push({id:o.vehicleId,lineId:o.lineId,offset:o.offset,isPercentage:o.isPercentage,state:"idle",rear:g,front:I})}return{vehicles:s,errors:r}}function ze(e){const t=e.map(s=>{const r=s.vehicleId.match(/^v(\d+)$/);return r?parseInt(r[1]):0}).filter(s=>s>0);return`v${(t.length>0?Math.max(...t):0)+1}`}function _(e,t,n){const s=[],r=[],c=new Map,o=new Set(t.map(a=>a.id)),i=new Set(n.map(a=>a.id)),l=new Map(n.map(a=>{const u=a.end.x-a.start.x,m=a.end.y-a.start.y;return[a.id,Math.sqrt(u*u+m*m)]}));for(const a of e){if(!o.has(a.vehicleId)){s.push(`Vehicle "${a.vehicleId}" does not exist`);continue}if(!i.has(a.targetLineId)){s.push(`Line "${a.targetLineId}" does not exist`);continue}const u=l.get(a.targetLineId),m=a.isPercentage?a.targetOffset/100*u:a.targetOffset;if(m<0||m>u){s.push(`Offset ${a.targetOffset}${a.isPercentage?"%":""} is out of bounds for ${a.targetLineId}`);continue}r.push(a);const g=c.get(a.vehicleId)||[];g.push(a),c.set(a.vehicleId,g)}return{commands:r,errors:s,vehicleQueues:c}}function je(e,t){if(t.length===0)return null;if(e.length===0)return t[0].id;const n=new Map;for(const s of t)n.set(s.id,0);for(const s of e){const r=n.get(s.vehicleId)||0;n.set(s.vehicleId,r+1)}return t[0].id}function ke({lines:e,wheelbase:t}){const[n,s]=f.useState([]),[r,c]=f.useState(""),[o,i]=f.useState(null),[l,a]=f.useState(!1),[u,m]=f.useState(0),g=f.useRef(!1),I=f.useRef(e),p=f.useRef(e),d=f.useRef(t),v=f.useRef(t);f.useEffect(()=>{I.current=e},[e]),f.useEffect(()=>{d.current=t},[t]),f.useEffect(()=>{if(g.current)return;const L=setTimeout(()=>{a(!0),m(x=>x+1)},0),h=setTimeout(()=>{try{const x=B(r),{vehicles:M,errors:C}=J(x,I.current,d.current);a(!1),C.length>0?(i(C.join(`
8
- `)),s(M)):(i(null),s(M))}catch(x){a(!1),i(x instanceof Error?x.message:"Invalid initial movement"),s([])}},2e3);return()=>{clearTimeout(L),clearTimeout(h)}},[r]),f.useEffect(()=>{if(!(JSON.stringify(p.current)!==JSON.stringify(e))||(p.current=e,g.current||!r.trim()))return;const h=setTimeout(()=>{try{const x=B(r),{vehicles:M,errors:C}=J(x,e,d.current);C.length>0?(i(C.join(`
9
- `)),s(M)):(i(null),s(M))}catch(x){i(x instanceof Error?x.message:"Invalid initial movement"),s([])}},0);return()=>clearTimeout(h)},[e,r]),f.useEffect(()=>{if(v.current===t||(v.current=t,g.current||!r.trim()))return;const L=setTimeout(()=>{try{const h=B(r),{vehicles:x,errors:M}=J(h,I.current,t);M.length>0?(i(M.join(`
10
- `)),s(x)):(i(null),s(x))}catch(h){i(h instanceof Error?h.message:"Invalid initial movement"),s([])}},0);return()=>clearTimeout(L)},[t,r]);const O=f.useCallback(()=>{const L=B(r),h=ze(L),x=e.length>0?e[0]:null;if(!x){i("No lines available. Please create at least one line first.");return}const M={vehicleId:h,lineId:x.id,offset:0,isPercentage:!1},C=[...L,M],V=De(C),{vehicles:D,errors:y}=J(C,e,t);g.current=!0,c(V),y.length>0?i(y.join(`
11
- `)):i(null),s(D),setTimeout(()=>{g.current=!1},50)},[r,e,t]);return{vehicles:n,initialMovementText:r,movementError:o,isDebouncing:l,debounceKey:u,setInitialMovementText:c,handleAddStartCommand:O}}function Ge({lines:e,vehicles:t}){const[n,s]=f.useState(""),[r,c]=f.useState([]),[o,i]=f.useState(new Map),[l,a]=f.useState(null),[u,m]=f.useState(!1),[g,I]=f.useState(0),p=f.useRef(!1),d=f.useRef(t),v=f.useRef(e);f.useEffect(()=>{d.current=t,v.current=e},[t,e]),f.useEffect(()=>{if(p.current)return;const L=setTimeout(()=>{m(!0),I(x=>x+1)},0),h=setTimeout(()=>{try{const x=Q(n),{commands:M,errors:C,vehicleQueues:V}=_(x,d.current,v.current);m(!1),C.length>0?a(C.join(`
12
- `)):a(null),c(M),i(V)}catch(x){m(!1),a(x instanceof Error?x.message:"Invalid movement sequence"),c([]),i(new Map)}},2e3);return()=>{clearTimeout(L),clearTimeout(h)}},[n]),f.useEffect(()=>{if(p.current||!n.trim())return;const L=setTimeout(()=>{try{const h=Q(n),{commands:x,errors:M,vehicleQueues:C}=_(h,t,e);M.length>0?a(M.join(`
13
- `)):a(null),c(x),i(C)}catch(h){a(h instanceof Error?h.message:"Invalid movement sequence"),c([]),i(new Map)}},0);return()=>clearTimeout(L)},[t,e,n]);const O=f.useCallback(()=>{if(t.length===0){a("No vehicles available. Please create at least one vehicle first.");return}if(e.length===0){a("No lines available. Please create at least one line first.");return}const L=Q(n),h=je(L,t);if(!h){a("No vehicles available.");return}const x=e[0],M={vehicleId:h,targetLineId:x.id,targetOffset:100,isPercentage:!0},C=[...L,M],V=Pe(C),{commands:D,errors:y,vehicleQueues:S}=_(C,t,e);p.current=!0,s(V),y.length>0?a(y.join(`
14
- `)):a(null),c(D),i(S),setTimeout(()=>{p.current=!1},50)},[n,t,e]);return{movementSequenceText:n,gotoCommands:r,vehicleQueues:o,sequenceError:l,isDebouncing:u,debounceKey:g,setMovementSequenceText:s,handleAddGotoCommand:O}}function ue(e,t,n){if(!t||!n)return{x:0,y:0};const s=t.getBoundingClientRect();return{x:e.clientX-s.left,y:e.clientY-s.top}}function fe(e,t,n=10){for(const s of t){if(R(e,s.start)<n)return{lineId:s.id,endpoint:"start"};if(R(e,s.end)<n)return{lineId:s.id,endpoint:"end"}}return null}function Be({canvasRef:e,containerRef:t,drawMode:n,lines:s,curves:r,setLines:c,setCurves:o,lineCounterRef:i}){const[l,a]=f.useState(!1),[u,m]=f.useState(null),[g,I]=f.useState(null),[p,d]=f.useState(null),[v,O]=f.useState(null),[L,h]=f.useState({x:0,y:0});return{handleMouseDown:V=>{const D=ue(V,e.current,t.current),y=fe(D,s);if(n==="line")y||(a(!0),m({start:D,current:D}));else if(n==="drag")y&&d(y);else if(n==="curve")if(y)if(!v)O(y);else{if(v.lineId!==y.lineId){const S={fromLineId:v.lineId,toLineId:y.lineId};o([...r,S])}O(null)}else v&&O(null)},handleMouseMove:V=>{const D=ue(V,e.current,t.current);if(h(D),l&&u)m({...u,current:D});else if(p){const y=s.map(S=>S.id===p.lineId?p.endpoint==="start"?{...S,start:D}:{...S,end:D}:S);c(y)}else{const y=fe(D,s);I(y)}},handleMouseUp:()=>{if(l&&u){if(R(u.start,u.current)>20){const D={id:`line${String(i.current).padStart(3,"0")}`,start:u.start,end:u.current};i.current++,c([...s,D])}m(null),a(!1)}else p&&d(null)},tempLine:u,hoveredEndpoint:g,curveStart:v,mousePos:L}}exports.VehicleEventContext=Me;exports.VehicleEventEmitter=Le;exports.arcLengthToSegmentPosition=Ie;exports.buildArcLengthTable=de;exports.buildGraph=he;exports.calculateBezierArcLength=ge;exports.calculateFrontAxlePosition=X;exports.calculateInitialFrontPosition=xe;exports.calculatePositionOnCurve=U;exports.calculatePositionOnLine=k;exports.canReachTarget=Ve;exports.checkRearCompletion=be;exports.createBezierCurve=ne;exports.createInitialMovementState=Se;exports.distance=R;exports.distanceToT=me;exports.findPath=oe;exports.generateGotoCommands=Pe;exports.generateSceneDefinition=Ce;exports.generateVehicleStarts=De;exports.getArcLength=Te;exports.getCumulativeArcLength=ve;exports.getLineLength=pe;exports.getPointOnBezier=Y;exports.getPointOnLine=le;exports.getPointOnLineByOffset=H;exports.getPositionFromOffset=ie;exports.getReachableCurves=$e;exports.handleArrival=ce;exports.initializeAllVehicles=ee;exports.initializeMovingVehicle=ye;exports.normalize=K;exports.parseGotoCommands=Q;exports.parseSceneDefinition=te;exports.parseVehicleStarts=B;exports.prepareCommandPath=W;exports.resolveFromLineOffset=se;exports.resolveOffset=Ee;exports.resolveToLineOffset=re;exports.updateAxlePosition=Z;exports.useCanvasInteraction=Be;exports.useCreateVehicleEventEmitter=qe;exports.useInitialMovement=ke;exports.useMovementSequence=Ge;exports.useSceneDefinition=Ne;exports.useVehicleEvent=Fe;exports.useVehicleEventEmitter=Oe;exports.useVehicleMovement=Ae;
6
+ `);for(const s of n){const r=s.trim();if(!r||r.startsWith("#"))continue;const c=r.match(/^(\w+)\s+goto\s+(\w+)\s+(\d+(?:\.\d+)?)(%?)/);if(c){const i=r.slice(c[0].length),o=i.includes("--wait");let l;const a=i.match(/--payload\s+(\{.*\})/);if(a)try{l=JSON.parse(a[1])}catch{console.warn(`Invalid JSON payload in goto command: ${a[1]}`)}t.push({vehicleId:c[1],targetLineId:c[2],targetOffset:parseFloat(c[3]),isPercentage:c[4]==="%",awaitConfirmation:o,payload:l})}}return t}function Pe(e){return e.map(t=>{const n=t.isPercentage?`${t.targetOffset}%`:`${t.targetOffset}`,s=t.awaitConfirmation?" --wait":"",r=t.payload!==void 0?` --payload ${JSON.stringify(t.payload)}`:"";return`${t.vehicleId} goto ${t.targetLineId} ${n}${s}${r}`}).join(`
7
+ `)}function Ne(){const[e,t]=f.useState([]),[n,s]=f.useState([]),[r,c]=f.useState(""),[i,o]=f.useState(null),[l,a]=f.useState(!1),[u,g]=f.useState(0),m=f.useRef(!1),x=f.useRef("");return f.useEffect(()=>{x.current=r},[r]),f.useEffect(()=>{if(m.current)return;const p=te(x.current),d=Ce({lines:e,curves:n,vehicles:p.vehicles});d!==x.current&&(m.current=!0,c(d),setTimeout(()=>{m.current=!1},50))},[e,n]),f.useEffect(()=>{if(m.current)return;const p=setTimeout(()=>{a(!0),g(v=>v+1)},0),d=setTimeout(()=>{try{const v=te(r);a(!1);const O=JSON.stringify(v.lines)!==JSON.stringify(e),L=JSON.stringify(v.curves)!==JSON.stringify(n);(O||L)&&(m.current=!0,O&&t(v.lines),L&&s(v.curves),setTimeout(()=>{m.current=!1},100)),o(null)}catch(v){a(!1),o(v instanceof Error?v.message:"Invalid scene definition")}},2e3);return()=>{clearTimeout(p),clearTimeout(d)}},[r,e,n]),{lines:e,curves:n,sceneDefinitionText:r,sceneError:i,isDebouncing:l,debounceKey:u,setLines:t,setCurves:s,setSceneDefinitionText:c}}function J(e,t,n=0){const s=[],r=[],c=new Set;for(const i of e){if(c.has(i.vehicleId)){r.push(`Duplicate vehicle ID: ${i.vehicleId}`);continue}c.add(i.vehicleId);const o=t.find(p=>p.id===i.lineId);if(!o){r.push(`Vehicle ${i.vehicleId}: Line "${i.lineId}" not found`);continue}const l=R(o.start,o.end),a=Math.max(0,l-n);let u;if(i.isPercentage){if(i.offset<0||i.offset>100){r.push(`Vehicle ${i.vehicleId}: Offset ${i.offset}% must be between 0% and 100%`);continue}u=i.offset/100*a}else{if(i.offset<0||i.offset>l){r.push(`Vehicle ${i.vehicleId}: Offset ${i.offset} exceeds line length ${l.toFixed(2)}`);continue}u=Math.min(i.offset,a)}const g=H(o,u,!1),m={lineId:i.lineId,position:g,absoluteOffset:u},x=xe(i.lineId,u,n,o);s.push({id:i.vehicleId,lineId:i.lineId,offset:i.offset,isPercentage:i.isPercentage,state:"idle",rear:m,front:x})}return{vehicles:s,errors:r}}function ze(e){const t=e.map(s=>{const r=s.vehicleId.match(/^v(\d+)$/);return r?parseInt(r[1]):0}).filter(s=>s>0);return`v${(t.length>0?Math.max(...t):0)+1}`}function _(e,t,n){const s=[],r=[],c=new Map,i=new Set(t.map(a=>a.id)),o=new Set(n.map(a=>a.id)),l=new Map(n.map(a=>{const u=a.end.x-a.start.x,g=a.end.y-a.start.y;return[a.id,Math.sqrt(u*u+g*g)]}));for(const a of e){if(!i.has(a.vehicleId)){s.push(`Vehicle "${a.vehicleId}" does not exist`);continue}if(!o.has(a.targetLineId)){s.push(`Line "${a.targetLineId}" does not exist`);continue}const u=l.get(a.targetLineId),g=a.isPercentage?a.targetOffset/100*u:a.targetOffset;if(g<0||g>u){s.push(`Offset ${a.targetOffset}${a.isPercentage?"%":""} is out of bounds for ${a.targetLineId}`);continue}r.push(a);const m=c.get(a.vehicleId)||[];m.push(a),c.set(a.vehicleId,m)}return{commands:r,errors:s,vehicleQueues:c}}function je(e,t){if(t.length===0)return null;if(e.length===0)return t[0].id;const n=new Map;for(const s of t)n.set(s.id,0);for(const s of e){const r=n.get(s.vehicleId)||0;n.set(s.vehicleId,r+1)}return t[0].id}function ke({lines:e,wheelbase:t}){const[n,s]=f.useState([]),[r,c]=f.useState(""),[i,o]=f.useState(null),[l,a]=f.useState(!1),[u,g]=f.useState(0),m=f.useRef(!1),x=f.useRef(e),p=f.useRef(e),d=f.useRef(t),v=f.useRef(t);f.useEffect(()=>{x.current=e},[e]),f.useEffect(()=>{d.current=t},[t]),f.useEffect(()=>{if(m.current)return;const L=setTimeout(()=>{a(!0),g(y=>y+1)},0),h=setTimeout(()=>{try{const y=B(r),{vehicles:M,errors:C}=J(y,x.current,d.current);a(!1),C.length>0?(o(C.join(`
8
+ `)),s(M)):(o(null),s(M))}catch(y){a(!1),o(y instanceof Error?y.message:"Invalid initial movement"),s([])}},2e3);return()=>{clearTimeout(L),clearTimeout(h)}},[r]),f.useEffect(()=>{if(!(JSON.stringify(p.current)!==JSON.stringify(e))||(p.current=e,m.current||!r.trim()))return;const h=setTimeout(()=>{try{const y=B(r),{vehicles:M,errors:C}=J(y,e,d.current);C.length>0?(o(C.join(`
9
+ `)),s(M)):(o(null),s(M))}catch(y){o(y instanceof Error?y.message:"Invalid initial movement"),s([])}},0);return()=>clearTimeout(h)},[e,r]),f.useEffect(()=>{if(v.current===t||(v.current=t,m.current||!r.trim()))return;const L=setTimeout(()=>{try{const h=B(r),{vehicles:y,errors:M}=J(h,x.current,t);M.length>0?(o(M.join(`
10
+ `)),s(y)):(o(null),s(y))}catch(h){o(h instanceof Error?h.message:"Invalid initial movement"),s([])}},0);return()=>clearTimeout(L)},[t,r]);const O=f.useCallback(()=>{const L=B(r),h=ze(L),y=e.length>0?e[0]:null;if(!y){o("No lines available. Please create at least one line first.");return}const M={vehicleId:h,lineId:y.id,offset:0,isPercentage:!1},C=[...L,M],$=De(C),{vehicles:P,errors:I}=J(C,e,t);m.current=!0,c($),I.length>0?o(I.join(`
11
+ `)):o(null),s(P),setTimeout(()=>{m.current=!1},50)},[r,e,t]);return{vehicles:n,initialMovementText:r,movementError:i,isDebouncing:l,debounceKey:u,setInitialMovementText:c,handleAddStartCommand:O}}function Ge({lines:e,vehicles:t}){const[n,s]=f.useState(""),[r,c]=f.useState([]),[i,o]=f.useState(new Map),[l,a]=f.useState(null),[u,g]=f.useState(!1),[m,x]=f.useState(0),p=f.useRef(!1),d=f.useRef(t),v=f.useRef(e);f.useEffect(()=>{d.current=t,v.current=e},[t,e]),f.useEffect(()=>{if(p.current)return;const L=setTimeout(()=>{g(!0),x(y=>y+1)},0),h=setTimeout(()=>{try{const y=Q(n),{commands:M,errors:C,vehicleQueues:$}=_(y,d.current,v.current);g(!1),C.length>0?a(C.join(`
12
+ `)):a(null),c(M),o($)}catch(y){g(!1),a(y instanceof Error?y.message:"Invalid movement sequence"),c([]),o(new Map)}},2e3);return()=>{clearTimeout(L),clearTimeout(h)}},[n]),f.useEffect(()=>{if(p.current||!n.trim())return;const L=setTimeout(()=>{try{const h=Q(n),{commands:y,errors:M,vehicleQueues:C}=_(h,t,e);M.length>0?a(M.join(`
13
+ `)):a(null),c(y),o(C)}catch(h){a(h instanceof Error?h.message:"Invalid movement sequence"),c([]),o(new Map)}},0);return()=>clearTimeout(L)},[t,e,n]);const O=f.useCallback(()=>{if(t.length===0){a("No vehicles available. Please create at least one vehicle first.");return}if(e.length===0){a("No lines available. Please create at least one line first.");return}const L=Q(n),h=je(L,t);if(!h){a("No vehicles available.");return}const y=e[0],M={vehicleId:h,targetLineId:y.id,targetOffset:100,isPercentage:!0},C=[...L,M],$=Pe(C),{commands:P,errors:I,vehicleQueues:S}=_(C,t,e);p.current=!0,s($),I.length>0?a(I.join(`
14
+ `)):a(null),c(P),o(S),setTimeout(()=>{p.current=!1},50)},[n,t,e]);return{movementSequenceText:n,gotoCommands:r,vehicleQueues:i,sequenceError:l,isDebouncing:u,debounceKey:m,setMovementSequenceText:s,handleAddGotoCommand:O}}function ue(e,t,n){if(!t||!n)return{x:0,y:0};const s=t.getBoundingClientRect();return{x:e.clientX-s.left,y:e.clientY-s.top}}function fe(e,t,n=10){for(const s of t){if(R(e,s.start)<n)return{lineId:s.id,endpoint:"start"};if(R(e,s.end)<n)return{lineId:s.id,endpoint:"end"}}return null}function Be({canvasRef:e,containerRef:t,drawMode:n,lines:s,curves:r,setLines:c,setCurves:i,lineCounterRef:o}){const[l,a]=f.useState(!1),[u,g]=f.useState(null),[m,x]=f.useState(null),[p,d]=f.useState(null),[v,O]=f.useState(null),[L,h]=f.useState({x:0,y:0});return{handleMouseDown:$=>{const P=ue($,e.current,t.current),I=fe(P,s);if(n==="line")I||(a(!0),g({start:P,current:P}));else if(n==="drag")I&&d(I);else if(n==="curve")if(I)if(!v)O(I);else{if(v.lineId!==I.lineId){const S={fromLineId:v.lineId,toLineId:I.lineId};i([...r,S])}O(null)}else v&&O(null)},handleMouseMove:$=>{const P=ue($,e.current,t.current);if(h(P),l&&u)g({...u,current:P});else if(p){const I=s.map(S=>S.id===p.lineId?p.endpoint==="start"?{...S,start:P}:{...S,end:P}:S);c(I)}else{const I=fe(P,s);x(I)}},handleMouseUp:()=>{if(l&&u){if(R(u.start,u.current)>20){const P={id:`line${String(o.current).padStart(3,"0")}`,start:u.start,end:u.current};o.current++,c([...s,P])}g(null),a(!1)}else p&&d(null)},tempLine:u,hoveredEndpoint:m,curveStart:v,mousePos:L}}exports.VehicleEventContext=Me;exports.VehicleEventEmitter=Le;exports.arcLengthToSegmentPosition=Ie;exports.buildArcLengthTable=de;exports.buildGraph=he;exports.calculateBezierArcLength=me;exports.calculateFrontAxlePosition=X;exports.calculateInitialFrontPosition=xe;exports.calculatePositionOnCurve=U;exports.calculatePositionOnLine=k;exports.canReachTarget=$e;exports.checkRearCompletion=Ee;exports.createBezierCurve=ne;exports.createInitialMovementState=Se;exports.distance=R;exports.distanceToT=ge;exports.findPath=oe;exports.generateGotoCommands=Pe;exports.generateSceneDefinition=Ce;exports.generateVehicleStarts=De;exports.getArcLength=Te;exports.getCumulativeArcLength=ve;exports.getLineLength=pe;exports.getPointOnBezier=Y;exports.getPointOnLine=le;exports.getPointOnLineByOffset=H;exports.getPositionFromOffset=ie;exports.getReachableCurves=be;exports.handleArrival=ce;exports.initializeAllVehicles=ee;exports.initializeMovingVehicle=ye;exports.normalize=K;exports.parseGotoCommands=Q;exports.parseSceneDefinition=te;exports.parseVehicleStarts=B;exports.prepareCommandPath=W;exports.resolveFromLineOffset=se;exports.resolveOffset=Ve;exports.resolveToLineOffset=re;exports.updateAxlePosition=Z;exports.useCanvasInteraction=Be;exports.useCreateVehicleEventEmitter=qe;exports.useInitialMovement=ke;exports.useMovementSequence=Ge;exports.useSceneDefinition=Ne;exports.useVehicleEvent=Fe;exports.useVehicleEventEmitter=Oe;exports.useVehicleMovement=Ae;