vehicle-path2 1.0.4 → 1.0.6

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,3 @@
1
+ "use strict";const o=require("react"),A=require("./core.cjs"),R=require("./vehicle-helpers-82D2V4MI.cjs"),te=require("react/jsx-runtime");function U(s){return Array.isArray(s)?{x:s[0],y:s[1]}:s}function G(s){return{id:s.id,start:U(s.start),end:U(s.end)}}function z(s){const c=s.fromIsPercentage!==!1,I=s.toIsPercentage!==!1;return{fromLineId:s.from,toLineId:s.to,fromOffset:s.fromPosition,fromIsPercentage:s.fromPosition!==void 0?c:void 0,toOffset:s.toPosition,toIsPercentage:s.toPosition!==void 0?I:void 0}}function N(s){const c=s.position??0,I=s.isPercentage!==!1;return{vehicleId:s.id,lineId:s.lineId,offset:c,isPercentage:I}}function H(s){const c=s.isPercentage!==!1,I=s.targetPosition??1;return{vehicleId:s.vehicleId,targetLineId:s.targetLineId,targetOffset:I,isPercentage:c,awaitConfirmation:s.wait,payload:s.payload}}function se(s){const c=[],I=new Set;for(const r of s.lines)I.has(r.id)&&c.push(`Duplicate line ID: ${r.id}`),I.add(r.id);if(s.connections)for(const r of s.connections){I.has(r.from)||c.push(`Connection references non-existent line: ${r.from}`),I.has(r.to)||c.push(`Connection references non-existent line: ${r.to}`);const m=r.fromIsPercentage!==!1,t=r.toIsPercentage!==!1;!m&&r.fromPosition===void 0&&c.push("fromPosition is required when fromIsPercentage is false"),!t&&r.toPosition===void 0&&c.push("toPosition is required when toIsPercentage is false"),r.fromPosition!==void 0&&(m&&(r.fromPosition<0||r.fromPosition>1)?c.push(`Invalid fromPosition: ${r.fromPosition} (must be 0-1 for percentage)`):!m&&r.fromPosition<0&&c.push(`Invalid fromPosition: ${r.fromPosition} (must be >= 0 for absolute distance)`)),r.toPosition!==void 0&&(t&&(r.toPosition<0||r.toPosition>1)?c.push(`Invalid toPosition: ${r.toPosition} (must be 0-1 for percentage)`):!t&&r.toPosition<0&&c.push(`Invalid toPosition: ${r.toPosition} (must be >= 0 for absolute distance)`))}return{valid:c.length===0,errors:c}}function J(){const[s,c]=o.useState([]),[I,r]=o.useState([]),[m,t]=o.useState(null),d=o.useCallback((n,v)=>{c(n),r(v),t(null)},[]),C=o.useCallback(n=>{const v=se(n);if(!v.valid)return t(v.errors.join("; ")),{success:!1,errors:v.errors};const u=n.lines.map(G),g=n.connections?.map(z)||[];return d(u,g),{success:!0}},[d]),y=o.useCallback(n=>{if(s.some(u=>u.id===n.id)){const u=`Line with ID '${n.id}' already exists`;return t(u),{success:!1,error:u}}return c(u=>[...u,G(n)]),t(null),{success:!0}},[s]),D=o.useCallback((n,v)=>{if(s.findIndex(g=>g.id===n)===-1){const g=`Line with ID '${n}' not found`;return t(g),{success:!1,error:g}}return c(g=>g.map(M=>M.id!==n?M:{...M,start:v.start?U(v.start):M.start,end:v.end?U(v.end):M.end})),t(null),{success:!0}},[s]),k=o.useCallback(n=>{if(!s.some(u=>u.id===n)){const u=`Line with ID '${n}' not found`;return t(u),{success:!1,error:u}}return c(u=>u.filter(g=>g.id!==n)),r(u=>u.filter(g=>g.fromLineId!==n&&g.toLineId!==n)),t(null),{success:!0}},[s]),V=o.useCallback(n=>{const v=s.some(f=>f.id===n.from),u=s.some(f=>f.id===n.to);if(!v){const f=`Line '${n.from}' not found`;return t(f),{success:!1,error:f}}if(!u){const f=`Line '${n.to}' not found`;return t(f),{success:!1,error:f}}const g=n.fromIsPercentage!==!1,M=n.toIsPercentage!==!1;if(!g&&n.fromPosition===void 0){const f="fromPosition is required when fromIsPercentage is false";return t(f),{success:!1,error:f}}if(!M&&n.toPosition===void 0){const f="toPosition is required when toIsPercentage is false";return t(f),{success:!1,error:f}}if(I.some(f=>f.fromLineId===n.from&&f.toLineId===n.to)){const f=`Connection from '${n.from}' to '${n.to}' already exists`;return t(f),{success:!1,error:f}}return r(f=>[...f,z(n)]),t(null),{success:!0}},[s,I]),p=o.useCallback((n,v,u)=>{const g=I.findIndex(e=>e.fromLineId===n&&e.toLineId===v);if(g===-1){const e=`Connection from '${n}' to '${v}' not found`;return t(e),{success:!1,error:e}}const M=I[g],Q=u.fromIsPercentage??M.fromIsPercentage,f=u.toIsPercentage??M.toIsPercentage;let $;u.fromOffset!==void 0?$=u.fromOffset:M.fromOffset!==void 0&&($=M.fromOffset);let L;if(u.toOffset!==void 0?L=u.toOffset:M.toOffset!==void 0&&(L=M.toOffset),$!==void 0){if(Q!==!1&&($<0||$>1)){const e=`Invalid fromOffset: ${$} (must be 0-1 for percentage)`;return t(e),{success:!1,error:e}}if(Q===!1&&$<0){const e=`Invalid fromOffset: ${$} (must be >= 0 for absolute distance)`;return t(e),{success:!1,error:e}}}if(L!==void 0){if(f!==!1&&(L<0||L>1)){const e=`Invalid toOffset: ${L} (must be 0-1 for percentage)`;return t(e),{success:!1,error:e}}if(f===!1&&L<0){const e=`Invalid toOffset: ${L} (must be >= 0 for absolute distance)`;return t(e),{success:!1,error:e}}}if(Q===!1&&$===void 0){const e="fromOffset is required when fromIsPercentage is false";return t(e),{success:!1,error:e}}if(f===!1&&L===void 0){const e="toOffset is required when toIsPercentage is false";return t(e),{success:!1,error:e}}const E={from:n,to:v,fromPosition:$,fromIsPercentage:Q,toPosition:L,toIsPercentage:f};return r(e=>e.map((i,l)=>l===g?z(E):i)),t(null),{success:!0}},[I]),P=o.useCallback((n,v)=>{if(!I.some(g=>g.fromLineId===n&&g.toLineId===v)){const g=`Connection from '${n}' to '${v}' not found`;return t(g),{success:!1,error:g}}return r(g=>g.filter(M=>!(M.fromLineId===n&&M.toLineId===v))),t(null),{success:!0}},[I]),a=o.useCallback(()=>{c([]),r([]),t(null)},[]);return{lines:s,curves:I,setScene:C,addLine:y,updateLine:D,removeLine:k,addConnection:V,updateConnection:p,removeConnection:P,clear:a,error:m,_loadScene:d}}function B({lines:s,wheelbase:c}){const[I,r]=o.useState([]),[m,t]=o.useState(null),d=o.useRef([]),C=o.useCallback(p=>{d.current=p,r(p),t(null)},[]),y=o.useCallback(p=>{const P=Array.isArray(p)?p:[p],a=[];for(const g of P)d.current.some(Q=>Q.id===g.id)&&a.push(`Vehicle with ID '${g.id}' already exists`);if(a.length>0)return t(a.join("; ")),{success:!1,errors:a};const n=P.map(N),{vehicles:v,errors:u}=R.validateAndCreateVehicles(n,s,c);return u.length>0?(t(u.join("; ")),{success:!1,errors:u}):(d.current=[...d.current,...v],r(d.current),t(null),{success:!0})},[s,c]),D=o.useCallback((p,P)=>{const a=d.current.findIndex(L=>L.id===p);if(a===-1){const L=`Vehicle with ID '${p}' not found`;return t(L),{success:!1,error:L}}const n=d.current[a];if(n.state!=="idle"){const L=`Cannot update vehicle '${p}' while it is ${n.state}. Vehicle must be idle.`;return t(L),{success:!1,error:L}}const v=P.lineId??n.lineId;if(!s.find(L=>L.id===v)){const L=`Line '${v}' not found`;return t(L),{success:!1,error:L}}let g,M;P.lineId!==void 0&&P.position===void 0?(g=0,M=!0):P.position!==void 0?(g=P.position,M=P.isPercentage??!0):(g=n.offset,M=n.isPercentage);const Q={vehicleId:p,lineId:v,offset:g,isPercentage:M},{vehicles:f,errors:$}=R.validateAndCreateVehicles([Q],s,c);return $.length>0?(t($.join("; ")),{success:!1,error:$.join("; ")}):(d.current=d.current.map((L,E)=>E===a?f[0]:L),r(d.current),t(null),{success:!0})},[s,c]),k=o.useCallback(p=>{if(!d.current.some(a=>a.id===p)){const a=`Vehicle with ID '${p}' not found`;return t(a),{success:!1,error:a}}return d.current=d.current.filter(a=>a.id!==p),r(d.current),t(null),{success:!0}},[]),V=o.useCallback(()=>{d.current=[],r([]),t(null)},[]);return{vehicles:I,addVehicles:y,updateVehicle:D,removeVehicle:k,clear:V,error:m,_loadVehicles:C}}function W({vehicles:s,lines:c}){const[I,r]=o.useState(new Map),[m,t]=o.useState(null),d=o.useRef(new Map),C=o.useCallback(()=>d.current,[]),y=o.useCallback(V=>{d.current=V,r(V),t(null)},[]),D=o.useCallback((V,p)=>{if(!s.some(f=>f.id===V)){const f=`Vehicle '${V}' not found`;return t(f),{success:!1,error:f}}const a=c.find(f=>f.id===p.targetLineId);if(!a){const f=`Line '${p.targetLineId}' not found`;return t(f),{success:!1,error:f}}const n=p.isPercentage!==!1,v=A.distance(a.start,a.end);if(!n&&p.targetPosition===void 0){const f="targetPosition is required when isPercentage is false";return t(f),{success:!1,error:f}}const u=p.targetPosition??1;if(n){if(u<0||u>1){const f=`Invalid targetPosition: ${u} (must be 0-1 for percentage)`;return t(f),{success:!1,error:f}}}else{if(u<0){const f=`Invalid targetPosition: ${u} (must be >= 0 for absolute distance)`;return t(f),{success:!1,error:f}}if(u>v){const f=`Position ${u} exceeds line length ${v}`;return t(f),{success:!1,error:f}}}const g=H({vehicleId:V,...p}),M=new Map(d.current),Q=M.get(V)||[];return M.set(V,[...Q,g]),d.current=M,r(M),t(null),{success:!0}},[s,c]),k=o.useCallback(V=>{if(V!==void 0){if(!s.some(a=>a.id===V)){const a=`Vehicle '${V}' not found`;return t(a),{success:!1,error:a}}const P=new Map(d.current);P.delete(V),d.current=P,r(P)}else d.current=new Map,r(new Map);return t(null),{success:!0}},[s]);return{vehicleQueues:I,getVehicleQueues:C,queueMovement:D,clearQueue:k,error:m,_loadQueues:y}}function Z({vehicles:s,lines:c,vehicleQueues:I,getVehicleQueues:r,wheelbase:m,tangentMode:t,curves:d,eventEmitter:C}){const[y,D]=o.useState([]),k=o.useRef([]),V=o.useCallback(E=>{typeof E=="function"?D(e=>{const i=E(e);return k.current=i,i}):(k.current=E,D(E))},[]),p=o.useCallback(()=>k.current,[]),P=o.useMemo(()=>({wheelbase:m,tangentMode:t}),[m,t]),a=o.useMemo(()=>new Map(c.map(E=>[E.id,E])),[c]),n=o.useRef(new Map);o.useEffect(()=>{const{movingVehicles:E,stateMap:e}=A.initializeAllVehicles(s,a);n.current=e;const i=setTimeout(()=>{V(E)},0);return()=>clearTimeout(i)},[s,a]);const v=o.useRef(null);o.useEffect(()=>{c.length>0&&(v.current=A.buildGraph(c,d,P))},[c,d,P]);const u=o.useRef(0),g=o.useRef(!1),M=o.useCallback(E=>{if(!g.current)return!1;let e=!1;for(const[,l]of n.current)if(l.vehicle.state==="moving"){e=!0;break}if(!e)return!1;const i=[];for(const[l,h]of n.current){if(h.vehicle.state!=="moving"||!h.execution)continue;const x=h.execution;let S;if(x.front.currentSegmentIndex<x.path.segments.length){const T=x.path.segments[x.front.currentSegmentIndex];if(T.type==="line"){const b=a.get(T.lineId);b&&(S=Math.sqrt(Math.pow(b.end.x-b.start.x,2)+Math.pow(b.end.y-b.start.y,2)))}}const O=A.updateAxlePosition(h.vehicle.rear,x.rear,x.path,E,a,x.curveDataMap),q=A.updateAxlePosition(h.vehicle.front,x.front,x.path,E,a,x.curveDataMap,S);if(h.vehicle={...h.vehicle,rear:O.axleState,front:q.axleState},h.execution.rear=O.execution,h.execution.front=q.execution,O.completed){const T={linesMap:a,config:P,vehicleQueues:I,curves:d,graphRef:v,prepareCommandPath:A.prepareCommandPath,onCommandComplete:j=>i.push({type:"commandComplete",data:j}),onCommandStart:j=>i.push({type:"commandStart",data:j})},b=A.handleArrival(h,T);h.vehicle=b.vehicle,b.newExecution!==void 0&&(h.execution=b.newExecution),b.vehicle.state!=="moving"&&i.push({type:"stateChange",data:{vehicleId:l,from:"moving",to:b.vehicle.state}});const w=b.vehicle.rear.position,_=b.vehicle.front.position;i.push({type:"positionUpdate",data:{vehicleId:l,rear:w,front:_,center:{x:(w.x+_.x)/2,y:(w.y+_.y)/2},angle:Math.atan2(_.y-w.y,_.x-w.x)}})}}if(V(l=>l.map(h=>{const x=n.current.get(h.id);return x?x.vehicle:h})),C&&i.length>0){const l=u.current;setTimeout(()=>{u.current===l&&i.forEach(({type:h,data:x})=>{C.emit(h,x)})},0)}for(const[,l]of n.current)if(l.vehicle.state==="moving")return!0;return g.current=!1,!1},[a,d,P,I,C]),Q=o.useCallback(()=>{if(g.current)return!0;const E=v.current;if(!E)return!1;const e=r?r():I,i=[];let l=!1;for(const[h,x]of n.current){const S=x.vehicle,O=e.get(h);if(!O||O.length===0)continue;const q=O[0],T={graph:E,linesMap:a,curves:d,config:P},b=A.prepareCommandPath(S,q,T);if(!b){console.warn(`No path found for vehicle ${h}`);continue}const w=A.calculateFrontAxlePosition(b.path,0,0,m);n.current.set(h,{...x,execution:{path:b.path,curveDataMap:b.curveDataMap,currentCommandIndex:0,rear:{currentSegmentIndex:0,segmentDistance:0},front:w?{currentSegmentIndex:w.segmentIndex,segmentDistance:w.segmentDistance}:{currentSegmentIndex:0,segmentDistance:0}},vehicle:{...S,state:"moving"}}),l=!0,S.state!=="moving"&&i.push({id:h,fromState:S.state,command:q,startPosition:{lineId:S.rear.lineId,absoluteOffset:S.rear.absoluteOffset,position:S.rear.position}})}if(!l)return!1;if(g.current=!0,V(h=>h.map(x=>{const S=n.current.get(x.id);return S?S.vehicle:x})),C&&i.length>0){const h=u.current;setTimeout(()=>{u.current===h&&i.forEach(({id:x,fromState:S,command:O,startPosition:q})=>{C.emit("commandStart",{vehicleId:x,command:O,commandIndex:0,startPosition:q}),C.emit("stateChange",{vehicleId:x,from:S,to:"moving"})})},0)}return!0},[a,d,I,r,P,m,C]),f=o.useCallback(()=>{u.current++,g.current=!1;const{movingVehicles:E,stateMap:e}=A.initializeAllVehicles(s,a);n.current=e,V(E)},[s,a]),$=o.useCallback(E=>{const e=n.current.get(E);if(!e||e.vehicle.state!=="waiting")return!1;const i=I.get(E),l=e.execution;if(!l)return!1;const h=l.currentCommandIndex+1;if(i&&h<i.length){const x=v.current;if(x){const S=i[h],O={graph:x,linesMap:a,curves:d,config:P},q=A.prepareCommandPath(e.vehicle,S,O);if(q){const T=A.calculateFrontAxlePosition(q.path,0,0,m);if(e.execution={path:q.path,curveDataMap:q.curveDataMap,currentCommandIndex:h,rear:{currentSegmentIndex:0,segmentDistance:0},front:T?{currentSegmentIndex:T.segmentIndex,segmentDistance:T.segmentDistance}:{currentSegmentIndex:0,segmentDistance:0}},e.vehicle={...e.vehicle,state:"moving"},V(b=>b.map(w=>w.id===E?e.vehicle:w)),C){const b=u.current;setTimeout(()=>{u.current===b&&C.emit("stateChange",{vehicleId:E,from:"waiting",to:"moving"})},0)}return!0}}}if(e.vehicle={...e.vehicle,state:"idle"},e.execution=null,V(x=>x.map(S=>S.id===E?e.vehicle:S)),C){const x=u.current;setTimeout(()=>{u.current===x&&C.emit("stateChange",{vehicleId:E,from:"waiting",to:"idle"})},0)}return!0},[I,a,d,P,m,C]),L=o.useCallback(()=>{for(const[,E]of n.current)if(E.vehicle.state==="moving")return!0;return!1},[]);return{movingVehicles:y,getMovingVehicles:p,prepare:Q,tick:M,reset:f,continueVehicle:$,isMoving:L,isPrepared:g.current}}function re({wheelbase:s,tangentMode:c="proportional-40",eventEmitter:I}){const r=J(),m=B({lines:r.lines,wheelbase:s}),t=W({vehicles:m.vehicles,lines:r.lines,curves:r.curves}),d=Z({vehicles:m.vehicles,lines:r.lines,vehicleQueues:t.vehicleQueues,getVehicleQueues:t.getVehicleQueues,wheelbase:s,tangentMode:c,curves:r.curves,eventEmitter:I}),C=o.useCallback(e=>m.vehicles.filter(i=>i.lineId===e||i.rear.lineId===e),[m.vehicles]),y=o.useCallback(e=>C(e).length>0,[C]),D=o.useCallback(e=>{const i=r.addLine(e);return i.success?{success:!0}:{success:!1,error:i.error}},[r]),k=o.useCallback((e,i)=>{const l=r.updateLine(e,i);return l.success?{success:!0}:{success:!1,error:l.error}},[r]),V=o.useCallback(e=>{const i=[],l=C(e);l.length>0&&(i.push({type:"vehicle_on_removed_line",message:`${l.length} vehicle(s) are on line '${e}'`,details:{lineId:e,vehicleIds:l.map(S=>S.id)}}),l.forEach(S=>{m.removeVehicle(S.id),t.clearQueue(S.id)}));const h=r.curves.filter(S=>S.fromLineId===e||S.toLineId===e);h.length>0&&i.push({type:"orphaned_connection",message:`${h.length} connection(s) will be removed`,details:{lineId:e,connectionCount:h.length}});const x=r.removeLine(e);return x.success?{success:!0,warnings:i.length>0?i:void 0}:{success:!1,error:x.error}},[r,C,m,t]),p=o.useCallback(()=>{r.clear(),m.clear(),t.clearQueue()},[r,m,t]),P=o.useCallback((e,i,l)=>{const h=r.addConnection({from:e,to:i,fromPosition:l?.fromOffset,fromIsPercentage:l?.fromIsPercentage,toPosition:l?.toOffset,toIsPercentage:l?.toIsPercentage});return h.success?{success:!0}:{success:!1,error:h.error}},[r]),a=o.useCallback((e,i,l)=>{const h=r.updateConnection(e,i,l);return h.success?{success:!0}:{success:!1,error:h.error}},[r]),n=o.useCallback((e,i)=>{const l=r.removeConnection(e,i);return l.success?{success:!0}:{success:!1,error:l.error}},[r]),v=o.useCallback(e=>{const i=m.addVehicles(e);return i.success?{success:!0}:{success:!1,error:i.errors?.join("; ")}},[m]),u=o.useCallback((e,i)=>{const l=m.updateVehicle(e,i);return l.success?{success:!0}:{success:!1,error:l.error}},[m]),g=o.useCallback(e=>{const i=[],l=t.vehicleQueues.get(e);l&&l.length>0&&(i.push({type:"movement_queue_cleared",message:`${l.length} queued movement(s) will be cleared for vehicle '${e}'`,details:{vehicleId:e}}),t.clearQueue(e));const h=m.removeVehicle(e);return h.success?{success:!0,warnings:i.length>0?i:void 0}:{success:!1,error:h.error}},[m,t]),M=o.useCallback(()=>{m.clear(),t.clearQueue()},[m,t]),Q=o.useCallback(e=>{const i={targetLineId:e.lineId,targetPosition:e.position??1,isPercentage:e.isPercentage,wait:e.wait,payload:e.payload},l=t.queueMovement(e.id,i);return l.success?{success:!0}:{success:!1,error:l.error}},[t]),f=o.useCallback(e=>{const i=t.clearQueue(e);return i.success?{success:!0}:{success:!1,error:i.error}},[t]),$=o.useCallback(e=>{const i=[],l=[],{scene:h,vehicles:x,movements:S}=R.parseAllDSL(e);h.errors.length>0&&l.push(...h.errors),x.errors.length>0&&l.push(...x.errors),S.errors.length>0&&l.push(...S.errors);const O=h.data.lines.map(G),q=(h.data.connections||[]).map(z),T=x.data.map(N),{vehicles:b,errors:w}=R.validateAndCreateVehicles(T,O,s);w.length>0&&l.push(...w);const _=new Map;for(const j of S.data){const Y=_.get(j.vehicleId)||[];Y.push(H(j)),_.set(j.vehicleId,Y)}return r._loadScene(O,q),m._loadVehicles(b),t._loadQueues(_),l.length>0&&i.push({type:"dsl_parse_error",message:`DSL loading had ${l.length} error(s)`,details:{errors:l}}),{success:!0,warnings:i.length>0?i:void 0}},[r,m,t,s]),L=o.useCallback(e=>{const i=[],l=[],h=e.lines.map(G),x=(e.connections||[]).map(z),S=(e.vehicles||[]).map(N),{vehicles:O,errors:q}=R.validateAndCreateVehicles(S,h,s);q.length>0&&l.push(...q);const T=new Map;for(const b of e.movements||[]){const w=T.get(b.vehicleId)||[];w.push(H({vehicleId:b.vehicleId,targetLineId:b.targetLineId,targetPosition:b.targetPosition,isPercentage:b.isPercentage,wait:b.wait,payload:b.payload})),T.set(b.vehicleId,w)}return r._loadScene(h,x),m._loadVehicles(O),t._loadQueues(T),l.length>0&&i.push({type:"dsl_parse_error",message:`JSON loading had ${l.length} error(s)`,details:{errors:l}}),{success:!0,warnings:i.length>0?i:void 0}},[r,m,t,s]),E=o.useMemo(()=>r.error||m.error||t.error,[r.error,m.error,t.error]);return{lines:r.lines,curves:r.curves,vehicles:m.vehicles,movingVehicles:d.movingVehicles,getMovingVehicles:d.getMovingVehicles,vehicleQueues:t.vehicleQueues,error:E,addLine:D,updateLine:k,removeLine:V,clearScene:p,connect:P,updateConnection:a,disconnect:n,addVehicles:v,updateVehicle:u,removeVehicle:g,clearVehicles:M,goto:Q,clearQueue:f,prepare:d.prepare,tick:d.tick,reset:d.reset,continueVehicle:d.continueVehicle,isMoving:d.isMoving,loadFromDSL:$,loadFromJSON:L,getVehiclesOnLine:C,hasVehiclesOnLine:y}}function F(s){return s.map(c=>({id:c.id,start:c.start,end:c.end}))}function K(s){return s.map(c=>({from:c.fromLineId,to:c.toLineId,fromPosition:c.fromOffset,fromIsPercentage:c.fromIsPercentage,toPosition:c.toOffset,toIsPercentage:c.toIsPercentage}))}function ne(){const[s,c]=o.useState(""),[I,r]=o.useState(null),{lines:m,curves:t,setScene:d}=J(),C=o.useRef(!1),y=o.useRef("");o.useEffect(()=>{y.current=s},[s]),o.useEffect(()=>{if(C.current)return;const p={lines:F(m),connections:t.length>0?K(t):void 0},P=R.generateSceneDSL(p);P!==y.current&&(C.current=!0,c(P),setTimeout(()=>{C.current=!1},50))},[m,t]);const D=o.useCallback(p=>{C.current=!0,c(p);try{const{data:P,errors:a}=R.parseSceneDSL(p);a.length>0&&r(a.join("; "));const n=d(P);!n.success&&n.errors?r(v=>v?`${v}; ${n.errors.join("; ")}`:n.errors.join("; ")):a.length===0&&r(null)}catch(P){r(P instanceof Error?P.message:"Invalid scene definition")}setTimeout(()=>{C.current=!1},50)},[d]),k=o.useCallback(p=>{const P=typeof p=="function"?p(m):p,a=K(t);d({lines:F(P),connections:a.length>0?a:void 0});const n={lines:F(P),connections:a.length>0?a:void 0},v=R.generateSceneDSL(n);C.current=!0,c(v),setTimeout(()=>{C.current=!1},50)},[m,t,d]),V=o.useCallback(p=>{const P=typeof p=="function"?p(t):p,a=K(P);d({lines:F(m),connections:a.length>0?a:void 0});const n={lines:F(m),connections:a.length>0?a:void 0},v=R.generateSceneDSL(n);C.current=!0,c(v),setTimeout(()=>{C.current=!1},50)},[m,t,d]);return{lines:m,curves:t,sceneDefinitionText:s,sceneError:I,isDebouncing:!1,debounceKey:0,setLines:k,setCurves:V,setSceneDefinitionText:D}}function oe({lines:s,wheelbase:c}){const[I,r]=o.useState(""),[m,t]=o.useState(null),{vehicles:d,addVehicles:C,clear:y,error:D}=B({lines:s,wheelbase:c}),k=o.useRef(!1),V=o.useCallback(p=>{k.current=!0,r(p);try{const{data:P,errors:a}=R.parseVehiclesDSL(p),n=[...a];y();for(const v of P){const u=C(v);!u.success&&u.errors&&n.push(...u.errors)}n.length>0?t(n.join(`
2
+ `)):t(null)}catch(P){t(P instanceof Error?P.message:"Invalid initial movement")}setTimeout(()=>{k.current=!1},50)},[C,y]);return{vehicles:d,initialMovementText:I,movementError:m||D,isDebouncing:!1,debounceKey:0,setInitialMovementText:V}}function ce({lines:s,vehicles:c}){const[I,r]=o.useState(""),[m,t]=o.useState([]),[d,C]=o.useState(null),{vehicleQueues:y,queueMovement:D,clearQueue:k,error:V}=W({vehicles:c,lines:s}),p=o.useRef(!1),P=o.useCallback(a=>{p.current=!0,r(a);try{const{data:n,errors:v}=R.parseMovementDSL(a),u=[...v];k();for(const g of n){const M=D(g.vehicleId,{targetLineId:g.targetLineId,targetPosition:g.targetPosition,isPercentage:g.isPercentage,wait:g.wait,payload:g.payload});!M.success&&M.error&&u.push(M.error)}t(n),u.length>0?C(u.join(`
3
+ `)):C(null)}catch(n){C(n instanceof Error?n.message:"Invalid movement sequence"),t([])}setTimeout(()=>{p.current=!1},50)},[D,k]);return{movementSequenceText:I,gotoCommands:m,vehicleQueues:y,sequenceError:d||V,isDebouncing:!1,debounceKey:0,setMovementSequenceText:P}}const X=o.createContext(null);function ee(){const s=o.useContext(X);if(!s)throw new Error("useVehicleEventEmitter must be used within a VehicleEventProvider");return s}function ie(){return o.useMemo(()=>new R.VehicleEventEmitter,[])}function ae(s,c,I=[]){const r=ee();o.useEffect(()=>r.on(s,c),[r,s,...I])}function ue({children:s}){const c=o.useMemo(()=>new R.VehicleEventEmitter,[]);return te.jsx(X.Provider,{value:c,children:s})}exports.VehicleEventContext=X;exports.VehicleEventProvider=ue;exports.useAnimation=Z;exports.useCreateVehicleEventEmitter=ie;exports.useInitialMovement=oe;exports.useMovementQueue=W;exports.useMovementSequence=ce;exports.useScene=J;exports.useSceneDefinition=ne;exports.useVehicleEvent=ae;exports.useVehicleEventEmitter=ee;exports.useVehicleSimulation=re;exports.useVehicles=B;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./core.cjs"),i=require("./vehicle-helpers-82D2V4MI.cjs"),n=require("./animation-loop-fC2LjxCd.cjs"),t=require("./useVehicleEvents-9A3aVGuw.cjs");exports.arcLengthToSegmentPosition=e.arcLengthToSegmentPosition;exports.buildArcLengthTable=e.buildArcLengthTable;exports.buildGraph=e.buildGraph;exports.calculateBezierArcLength=e.calculateBezierArcLength;exports.calculateFrontAxlePosition=e.calculateFrontAxlePosition;exports.calculateInitialFrontPosition=e.calculateInitialFrontPosition;exports.calculatePositionOnCurve=e.calculatePositionOnCurve;exports.calculatePositionOnLine=e.calculatePositionOnLine;exports.createBezierCurve=e.createBezierCurve;exports.createInitialMovementState=e.createInitialMovementState;exports.distance=e.distance;exports.distanceToT=e.distanceToT;exports.findPath=e.findPath;exports.getArcLength=e.getArcLength;exports.getCumulativeArcLength=e.getCumulativeArcLength;exports.getLineLength=e.getLineLength;exports.getPointOnBezier=e.getPointOnBezier;exports.getPointOnLine=e.getPointOnLine;exports.getPointOnLineByOffset=e.getPointOnLineByOffset;exports.getPositionFromOffset=e.getPositionFromOffset;exports.handleArrival=e.handleArrival;exports.initializeAllVehicles=e.initializeAllVehicles;exports.initializeMovingVehicle=e.initializeMovingVehicle;exports.normalize=e.normalize;exports.prepareCommandPath=e.prepareCommandPath;exports.resolveFromLineOffset=e.resolveFromLineOffset;exports.resolveToLineOffset=e.resolveToLineOffset;exports.updateAxlePosition=e.updateAxlePosition;exports.VehicleEventEmitter=i.VehicleEventEmitter;exports.generateMovementDSL=i.generateMovementDSL;exports.generateSceneDSL=i.generateSceneDSL;exports.generateVehiclesDSL=i.generateVehiclesDSL;exports.getNextGotoVehicleId=i.getNextGotoVehicleId;exports.getNextStartVehicleId=i.getNextStartVehicleId;exports.parseAllDSL=i.parseAllDSL;exports.parseMovementDSL=i.parseMovementDSL;exports.parseSceneDSL=i.parseSceneDSL;exports.parseVehiclesDSL=i.parseVehiclesDSL;exports.validateAndCreateVehicles=i.validateAndCreateVehicles;exports.createAnimationLoop=n.createAnimationLoop;exports.useAnimationLoop=n.useAnimationLoop;exports.VehicleEventContext=t.VehicleEventContext;exports.VehicleEventProvider=t.VehicleEventProvider;exports.useAnimation=t.useAnimation;exports.useCreateVehicleEventEmitter=t.useCreateVehicleEventEmitter;exports.useInitialMovement=t.useInitialMovement;exports.useMovement=t.useMovementQueue;exports.useMovementQueue=t.useMovementQueue;exports.useMovementSequence=t.useMovementSequence;exports.useScene=t.useScene;exports.useSceneDefinition=t.useSceneDefinition;exports.useVehicleEvent=t.useVehicleEvent;exports.useVehicleEventEmitter=t.useVehicleEventEmitter;exports.useVehicleMovement=t.useAnimation;exports.useVehicleSimulation=t.useVehicleSimulation;exports.useVehicles=t.useVehicles;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./core.cjs"),i=require("./vehicle-helpers-82D2V4MI.cjs"),n=require("./animation-loop-fC2LjxCd.cjs"),t=require("./useVehicleEvents-DngnyUej.cjs");exports.arcLengthToSegmentPosition=e.arcLengthToSegmentPosition;exports.buildArcLengthTable=e.buildArcLengthTable;exports.buildGraph=e.buildGraph;exports.calculateBezierArcLength=e.calculateBezierArcLength;exports.calculateFrontAxlePosition=e.calculateFrontAxlePosition;exports.calculateInitialFrontPosition=e.calculateInitialFrontPosition;exports.calculatePositionOnCurve=e.calculatePositionOnCurve;exports.calculatePositionOnLine=e.calculatePositionOnLine;exports.createBezierCurve=e.createBezierCurve;exports.createInitialMovementState=e.createInitialMovementState;exports.distance=e.distance;exports.distanceToT=e.distanceToT;exports.findPath=e.findPath;exports.getArcLength=e.getArcLength;exports.getCumulativeArcLength=e.getCumulativeArcLength;exports.getLineLength=e.getLineLength;exports.getPointOnBezier=e.getPointOnBezier;exports.getPointOnLine=e.getPointOnLine;exports.getPointOnLineByOffset=e.getPointOnLineByOffset;exports.getPositionFromOffset=e.getPositionFromOffset;exports.handleArrival=e.handleArrival;exports.initializeAllVehicles=e.initializeAllVehicles;exports.initializeMovingVehicle=e.initializeMovingVehicle;exports.normalize=e.normalize;exports.prepareCommandPath=e.prepareCommandPath;exports.resolveFromLineOffset=e.resolveFromLineOffset;exports.resolveToLineOffset=e.resolveToLineOffset;exports.updateAxlePosition=e.updateAxlePosition;exports.VehicleEventEmitter=i.VehicleEventEmitter;exports.generateMovementDSL=i.generateMovementDSL;exports.generateSceneDSL=i.generateSceneDSL;exports.generateVehiclesDSL=i.generateVehiclesDSL;exports.getNextGotoVehicleId=i.getNextGotoVehicleId;exports.getNextStartVehicleId=i.getNextStartVehicleId;exports.parseAllDSL=i.parseAllDSL;exports.parseMovementDSL=i.parseMovementDSL;exports.parseSceneDSL=i.parseSceneDSL;exports.parseVehiclesDSL=i.parseVehiclesDSL;exports.validateAndCreateVehicles=i.validateAndCreateVehicles;exports.createAnimationLoop=n.createAnimationLoop;exports.useAnimationLoop=n.useAnimationLoop;exports.VehicleEventContext=t.VehicleEventContext;exports.VehicleEventProvider=t.VehicleEventProvider;exports.useAnimation=t.useAnimation;exports.useCreateVehicleEventEmitter=t.useCreateVehicleEventEmitter;exports.useInitialMovement=t.useInitialMovement;exports.useMovement=t.useMovementQueue;exports.useMovementQueue=t.useMovementQueue;exports.useMovementSequence=t.useMovementSequence;exports.useScene=t.useScene;exports.useSceneDefinition=t.useSceneDefinition;exports.useVehicleEvent=t.useVehicleEvent;exports.useVehicleEventEmitter=t.useVehicleEventEmitter;exports.useVehicleMovement=t.useAnimation;exports.useVehicleSimulation=t.useVehicleSimulation;exports.useVehicles=t.useVehicles;
@@ -1,7 +1,7 @@
1
1
  import { arcLengthToSegmentPosition as i, buildArcLengthTable as a, buildGraph as n, calculateBezierArcLength as s, calculateFrontAxlePosition as o, calculateInitialFrontPosition as l, calculatePositionOnCurve as r, calculatePositionOnLine as c, createBezierCurve as u, createInitialMovementState as h, distance as m, distanceToT as v, findPath as g, getArcLength as L, getCumulativeArcLength as V, getLineLength as d, getPointOnBezier as f, getPointOnLine as S, getPointOnLineByOffset as p, getPositionFromOffset as A, handleArrival as P, initializeAllVehicles as x, initializeMovingVehicle as E, normalize as M, prepareCommandPath as O, resolveFromLineOffset as D, resolveToLineOffset as C, updateAxlePosition as z } from "./core.js";
2
2
  import { V as I, g as T, a as B, b as F, c as G, d as N, p as j, e as k, f as q, h as y, v as Q } from "./vehicle-helpers-BXX3GPzk.js";
3
3
  import { c as H, u as J } from "./animation-loop-bZEm2pMN.js";
4
- import { V as R, a as U, u as W, b as X, c as Y, d as Z, d as _, e as $, f as ee, g as te, h as ie, i as ae, u as ne, j as se, k as oe } from "./useVehicleEvents-rTjna5gm.js";
4
+ import { V as R, a as U, u as W, b as X, c as Y, d as Z, d as _, e as $, f as ee, g as te, h as ie, i as ae, u as ne, j as se, k as oe } from "./useVehicleEvents-5-uvxZme.js";
5
5
  export {
6
6
  R as VehicleEventContext,
7
7
  I as VehicleEventEmitter,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vehicle-path2",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Vehicle motion simulator library for dual-axle vehicle movement along paths composed of lines and Bezier curves",
5
5
  "type": "module",
6
6
  "main": "./dist/vehicle-path.cjs",
@@ -1,3 +0,0 @@
1
- "use strict";const n=require("react"),Q=require("./core.cjs"),T=require("./vehicle-helpers-82D2V4MI.cjs"),te=require("react/jsx-runtime");function F(s){return Array.isArray(s)?{x:s[0],y:s[1]}:s}function z(s){return{id:s.id,start:F(s.start),end:F(s.end)}}function j(s){const a=s.fromIsPercentage!==!1,I=s.toIsPercentage!==!1;return{fromLineId:s.from,toLineId:s.to,fromOffset:s.fromPosition,fromIsPercentage:s.fromPosition!==void 0?a:void 0,toOffset:s.toPosition,toIsPercentage:s.toPosition!==void 0?I:void 0}}function N(s){const a=s.position??0,I=s.isPercentage!==!1;return{vehicleId:s.id,lineId:s.lineId,offset:a,isPercentage:I}}function H(s){const a=s.isPercentage!==!1,I=s.targetPosition??1;return{vehicleId:s.vehicleId,targetLineId:s.targetLineId,targetOffset:I,isPercentage:a,awaitConfirmation:s.wait,payload:s.payload}}function se(s){const a=[],I=new Set;for(const r of s.lines)I.has(r.id)&&a.push(`Duplicate line ID: ${r.id}`),I.add(r.id);if(s.connections)for(const r of s.connections){I.has(r.from)||a.push(`Connection references non-existent line: ${r.from}`),I.has(r.to)||a.push(`Connection references non-existent line: ${r.to}`);const d=r.fromIsPercentage!==!1,t=r.toIsPercentage!==!1;!d&&r.fromPosition===void 0&&a.push("fromPosition is required when fromIsPercentage is false"),!t&&r.toPosition===void 0&&a.push("toPosition is required when toIsPercentage is false"),r.fromPosition!==void 0&&(d&&(r.fromPosition<0||r.fromPosition>1)?a.push(`Invalid fromPosition: ${r.fromPosition} (must be 0-1 for percentage)`):!d&&r.fromPosition<0&&a.push(`Invalid fromPosition: ${r.fromPosition} (must be >= 0 for absolute distance)`)),r.toPosition!==void 0&&(t&&(r.toPosition<0||r.toPosition>1)?a.push(`Invalid toPosition: ${r.toPosition} (must be 0-1 for percentage)`):!t&&r.toPosition<0&&a.push(`Invalid toPosition: ${r.toPosition} (must be >= 0 for absolute distance)`))}return{valid:a.length===0,errors:a}}function J(){const[s,a]=n.useState([]),[I,r]=n.useState([]),[d,t]=n.useState(null),m=n.useCallback((o,v)=>{a(o),r(v),t(null)},[]),C=n.useCallback(o=>{const v=se(o);if(!v.valid)return t(v.errors.join("; ")),{success:!1,errors:v.errors};const g=o.lines.map(z),h=o.connections?.map(j)||[];return m(g,h),{success:!0}},[m]),D=n.useCallback(o=>{if(s.some(g=>g.id===o.id)){const g=`Line with ID '${o.id}' already exists`;return t(g),{success:!1,error:g}}return a(g=>[...g,z(o)]),t(null),{success:!0}},[s]),k=n.useCallback((o,v)=>{if(s.findIndex(h=>h.id===o)===-1){const h=`Line with ID '${o}' not found`;return t(h),{success:!1,error:h}}return a(h=>h.map(S=>S.id!==o?S:{...S,start:v.start?F(v.start):S.start,end:v.end?F(v.end):S.end})),t(null),{success:!0}},[s]),E=n.useCallback(o=>{if(!s.some(g=>g.id===o)){const g=`Line with ID '${o}' not found`;return t(g),{success:!1,error:g}}return a(g=>g.filter(h=>h.id!==o)),r(g=>g.filter(h=>h.fromLineId!==o&&h.toLineId!==o)),t(null),{success:!0}},[s]),x=n.useCallback(o=>{const v=s.some(c=>c.id===o.from),g=s.some(c=>c.id===o.to);if(!v){const c=`Line '${o.from}' not found`;return t(c),{success:!1,error:c}}if(!g){const c=`Line '${o.to}' not found`;return t(c),{success:!1,error:c}}const h=o.fromIsPercentage!==!1,S=o.toIsPercentage!==!1;if(!h&&o.fromPosition===void 0){const c="fromPosition is required when fromIsPercentage is false";return t(c),{success:!1,error:c}}if(!S&&o.toPosition===void 0){const c="toPosition is required when toIsPercentage is false";return t(c),{success:!1,error:c}}if(I.some(c=>c.fromLineId===o.from&&c.toLineId===o.to)){const c=`Connection from '${o.from}' to '${o.to}' already exists`;return t(c),{success:!1,error:c}}return r(c=>[...c,j(o)]),t(null),{success:!0}},[s,I]),l=n.useCallback((o,v,g)=>{const h=I.findIndex(e=>e.fromLineId===o&&e.toLineId===v);if(h===-1){const e=`Connection from '${o}' to '${v}' not found`;return t(e),{success:!1,error:e}}const S=I[h],q=g.fromIsPercentage??S.fromIsPercentage,c=g.toIsPercentage??S.toIsPercentage;let M;g.fromOffset!==void 0?M=g.fromOffset:S.fromOffset!==void 0&&(M=S.fromOffset);let P;if(g.toOffset!==void 0?P=g.toOffset:S.toOffset!==void 0&&(P=S.toOffset),M!==void 0){if(q!==!1&&(M<0||M>1)){const e=`Invalid fromOffset: ${M} (must be 0-1 for percentage)`;return t(e),{success:!1,error:e}}if(q===!1&&M<0){const e=`Invalid fromOffset: ${M} (must be >= 0 for absolute distance)`;return t(e),{success:!1,error:e}}}if(P!==void 0){if(c!==!1&&(P<0||P>1)){const e=`Invalid toOffset: ${P} (must be 0-1 for percentage)`;return t(e),{success:!1,error:e}}if(c===!1&&P<0){const e=`Invalid toOffset: ${P} (must be >= 0 for absolute distance)`;return t(e),{success:!1,error:e}}}if(q===!1&&M===void 0){const e="fromOffset is required when fromIsPercentage is false";return t(e),{success:!1,error:e}}if(c===!1&&P===void 0){const e="toOffset is required when toIsPercentage is false";return t(e),{success:!1,error:e}}const w={from:o,to:v,fromPosition:M,fromIsPercentage:q,toPosition:P,toIsPercentage:c};return r(e=>e.map((i,u)=>u===h?j(w):i)),t(null),{success:!0}},[I]),p=n.useCallback((o,v)=>{if(!I.some(h=>h.fromLineId===o&&h.toLineId===v)){const h=`Connection from '${o}' to '${v}' not found`;return t(h),{success:!1,error:h}}return r(h=>h.filter(S=>!(S.fromLineId===o&&S.toLineId===v))),t(null),{success:!0}},[I]),f=n.useCallback(()=>{a([]),r([]),t(null)},[]);return{lines:s,curves:I,setScene:C,addLine:D,updateLine:k,removeLine:E,addConnection:x,updateConnection:l,removeConnection:p,clear:f,error:d,_loadScene:m}}function B({lines:s,wheelbase:a}){const[I,r]=n.useState([]),[d,t]=n.useState(null),m=n.useRef([]),C=n.useCallback(l=>{m.current=l,r(l),t(null)},[]),D=n.useCallback(l=>{const p=Array.isArray(l)?l:[l],f=[];for(const h of p)m.current.some(q=>q.id===h.id)&&f.push(`Vehicle with ID '${h.id}' already exists`);if(f.length>0)return t(f.join("; ")),{success:!1,errors:f};const o=p.map(N),{vehicles:v,errors:g}=T.validateAndCreateVehicles(o,s,a);return g.length>0?(t(g.join("; ")),{success:!1,errors:g}):(m.current=[...m.current,...v],r(m.current),t(null),{success:!0})},[s,a]),k=n.useCallback((l,p)=>{const f=m.current.findIndex(P=>P.id===l);if(f===-1){const P=`Vehicle with ID '${l}' not found`;return t(P),{success:!1,error:P}}const o=m.current[f];if(o.state!=="idle"){const P=`Cannot update vehicle '${l}' while it is ${o.state}. Vehicle must be idle.`;return t(P),{success:!1,error:P}}const v=p.lineId??o.lineId;if(!s.find(P=>P.id===v)){const P=`Line '${v}' not found`;return t(P),{success:!1,error:P}}let h,S;p.lineId!==void 0&&p.position===void 0?(h=0,S=!0):p.position!==void 0?(h=p.position,S=p.isPercentage??!0):(h=o.offset,S=o.isPercentage);const q={vehicleId:l,lineId:v,offset:h,isPercentage:S},{vehicles:c,errors:M}=T.validateAndCreateVehicles([q],s,a);return M.length>0?(t(M.join("; ")),{success:!1,error:M.join("; ")}):(m.current=m.current.map((P,w)=>w===f?c[0]:P),r(m.current),t(null),{success:!0})},[s,a]),E=n.useCallback(l=>{if(!m.current.some(f=>f.id===l)){const f=`Vehicle with ID '${l}' not found`;return t(f),{success:!1,error:f}}return m.current=m.current.filter(f=>f.id!==l),r(m.current),t(null),{success:!0}},[]),x=n.useCallback(()=>{m.current=[],r([]),t(null)},[]);return{vehicles:I,addVehicles:D,updateVehicle:k,removeVehicle:E,clear:x,error:d,_loadVehicles:C}}function W({vehicles:s,lines:a}){const[I,r]=n.useState(new Map),[d,t]=n.useState(null),m=n.useRef(new Map),C=n.useCallback(()=>m.current,[]),D=n.useCallback(x=>{m.current=x,r(x),t(null)},[]),k=n.useCallback((x,l)=>{if(!s.some(c=>c.id===x)){const c=`Vehicle '${x}' not found`;return t(c),{success:!1,error:c}}const f=a.find(c=>c.id===l.targetLineId);if(!f){const c=`Line '${l.targetLineId}' not found`;return t(c),{success:!1,error:c}}const o=l.isPercentage!==!1,v=Q.distance(f.start,f.end);if(!o&&l.targetPosition===void 0){const c="targetPosition is required when isPercentage is false";return t(c),{success:!1,error:c}}const g=l.targetPosition??1;if(o){if(g<0||g>1){const c=`Invalid targetPosition: ${g} (must be 0-1 for percentage)`;return t(c),{success:!1,error:c}}}else{if(g<0){const c=`Invalid targetPosition: ${g} (must be >= 0 for absolute distance)`;return t(c),{success:!1,error:c}}if(g>v){const c=`Position ${g} exceeds line length ${v}`;return t(c),{success:!1,error:c}}}const h=H({vehicleId:x,...l}),S=new Map(m.current),q=S.get(x)||[];return S.set(x,[...q,h]),m.current=S,r(S),t(null),{success:!0}},[s,a]),E=n.useCallback(x=>{if(x!==void 0){if(!s.some(f=>f.id===x)){const f=`Vehicle '${x}' not found`;return t(f),{success:!1,error:f}}const p=new Map(m.current);p.delete(x),m.current=p,r(p)}else m.current=new Map,r(new Map);return t(null),{success:!0}},[s]);return{vehicleQueues:I,getVehicleQueues:C,queueMovement:k,clearQueue:E,error:d,_loadQueues:D}}function Z({vehicles:s,lines:a,vehicleQueues:I,getVehicleQueues:r,wheelbase:d,tangentMode:t,curves:m,eventEmitter:C}){const[D,k]=n.useState([]),E=n.useMemo(()=>({wheelbase:d,tangentMode:t}),[d,t]),x=n.useMemo(()=>new Map(a.map(c=>[c.id,c])),[a]),l=n.useRef(new Map);n.useEffect(()=>{const{movingVehicles:c,stateMap:M}=Q.initializeAllVehicles(s,x);l.current=M;const P=setTimeout(()=>{k(c)},0);return()=>clearTimeout(P)},[s,x]);const p=n.useRef(null);n.useEffect(()=>{a.length>0&&(p.current=Q.buildGraph(a,m,E))},[a,m,E]);const f=n.useRef(0),o=n.useRef(!1),v=n.useCallback(c=>{if(!o.current)return!1;let M=!1;for(const[,w]of l.current)if(w.vehicle.state==="moving"){M=!0;break}if(!M)return!1;const P=[];for(const[w,e]of l.current){if(e.vehicle.state!=="moving"||!e.execution)continue;const i=e.execution;let u;if(i.front.currentSegmentIndex<i.path.segments.length){const L=i.path.segments[i.front.currentSegmentIndex];if(L.type==="line"){const V=x.get(L.lineId);V&&(u=Math.sqrt(Math.pow(V.end.x-V.start.x,2)+Math.pow(V.end.y-V.start.y,2)))}}const b=Q.updateAxlePosition(e.vehicle.rear,i.rear,i.path,c,x,i.curveDataMap),$=Q.updateAxlePosition(e.vehicle.front,i.front,i.path,c,x,i.curveDataMap,u);if(e.vehicle={...e.vehicle,rear:b.axleState,front:$.axleState},e.execution.rear=b.execution,e.execution.front=$.execution,b.completed){const L={linesMap:x,config:E,vehicleQueues:I,curves:m,graphRef:p,prepareCommandPath:Q.prepareCommandPath,onCommandComplete:O=>P.push({type:"commandComplete",data:O}),onCommandStart:O=>P.push({type:"commandStart",data:O})},V=Q.handleArrival(e,L);e.vehicle=V.vehicle,V.newExecution!==void 0&&(e.execution=V.newExecution),V.vehicle.state!=="moving"&&P.push({type:"stateChange",data:{vehicleId:w,from:"moving",to:V.vehicle.state}});const y=V.vehicle.rear.position,A=V.vehicle.front.position;P.push({type:"positionUpdate",data:{vehicleId:w,rear:y,front:A,center:{x:(y.x+A.x)/2,y:(y.y+A.y)/2},angle:Math.atan2(A.y-y.y,A.x-y.x)}})}}if(k(w=>w.map(e=>{const i=l.current.get(e.id);return i?i.vehicle:e})),C&&P.length>0){const w=f.current;setTimeout(()=>{f.current===w&&P.forEach(({type:e,data:i})=>{C.emit(e,i)})},0)}for(const[,w]of l.current)if(w.vehicle.state==="moving")return!0;return o.current=!1,!1},[x,m,E,I,C]),g=n.useCallback(()=>{if(o.current)return!0;const c=p.current;if(!c)return!1;const M=r?r():I,P=[];let w=!1;for(const[e,i]of l.current){const u=i.vehicle,b=M.get(e);if(!b||b.length===0)continue;const $=b[0],L={graph:c,linesMap:x,curves:m,config:E},V=Q.prepareCommandPath(u,$,L);if(!V){console.warn(`No path found for vehicle ${e}`);continue}const y=Q.calculateFrontAxlePosition(V.path,0,0,d);l.current.set(e,{...i,execution:{path:V.path,curveDataMap:V.curveDataMap,currentCommandIndex:0,rear:{currentSegmentIndex:0,segmentDistance:0},front:y?{currentSegmentIndex:y.segmentIndex,segmentDistance:y.segmentDistance}:{currentSegmentIndex:0,segmentDistance:0}},vehicle:{...u,state:"moving"}}),w=!0,u.state!=="moving"&&P.push({id:e,fromState:u.state,command:$,startPosition:{lineId:u.rear.lineId,absoluteOffset:u.rear.absoluteOffset,position:u.rear.position}})}if(!w)return!1;if(o.current=!0,k(e=>e.map(i=>{const u=l.current.get(i.id);return u?u.vehicle:i})),C&&P.length>0){const e=f.current;setTimeout(()=>{f.current===e&&P.forEach(({id:i,fromState:u,command:b,startPosition:$})=>{C.emit("commandStart",{vehicleId:i,command:b,commandIndex:0,startPosition:$}),C.emit("stateChange",{vehicleId:i,from:u,to:"moving"})})},0)}return!0},[x,m,I,r,E,d,C]),h=n.useCallback(()=>{f.current++,o.current=!1;const{movingVehicles:c,stateMap:M}=Q.initializeAllVehicles(s,x);l.current=M,k(c)},[s,x]),S=n.useCallback(c=>{const M=l.current.get(c);if(!M||M.vehicle.state!=="waiting")return!1;const P=I.get(c),w=M.execution;if(!w)return!1;const e=w.currentCommandIndex+1;if(P&&e<P.length){const i=p.current;if(i){const u=P[e],b={graph:i,linesMap:x,curves:m,config:E},$=Q.prepareCommandPath(M.vehicle,u,b);if($){const L=Q.calculateFrontAxlePosition($.path,0,0,d);if(M.execution={path:$.path,curveDataMap:$.curveDataMap,currentCommandIndex:e,rear:{currentSegmentIndex:0,segmentDistance:0},front:L?{currentSegmentIndex:L.segmentIndex,segmentDistance:L.segmentDistance}:{currentSegmentIndex:0,segmentDistance:0}},M.vehicle={...M.vehicle,state:"moving"},k(V=>V.map(y=>y.id===c?M.vehicle:y)),C){const V=f.current;setTimeout(()=>{f.current===V&&C.emit("stateChange",{vehicleId:c,from:"waiting",to:"moving"})},0)}return!0}}}if(M.vehicle={...M.vehicle,state:"idle"},M.execution=null,k(i=>i.map(u=>u.id===c?M.vehicle:u)),C){const i=f.current;setTimeout(()=>{f.current===i&&C.emit("stateChange",{vehicleId:c,from:"waiting",to:"idle"})},0)}return!0},[I,x,m,E,d,C]),q=n.useCallback(()=>{for(const[,c]of l.current)if(c.vehicle.state==="moving")return!0;return!1},[]);return{movingVehicles:D,prepare:g,tick:v,reset:h,continueVehicle:S,isMoving:q,isPrepared:o.current}}function re({wheelbase:s,tangentMode:a="proportional-40",eventEmitter:I}){const r=J(),d=B({lines:r.lines,wheelbase:s}),t=W({vehicles:d.vehicles,lines:r.lines,curves:r.curves}),m=Z({vehicles:d.vehicles,lines:r.lines,vehicleQueues:t.vehicleQueues,getVehicleQueues:t.getVehicleQueues,wheelbase:s,tangentMode:a,curves:r.curves,eventEmitter:I}),C=n.useCallback(e=>d.vehicles.filter(i=>i.lineId===e||i.rear.lineId===e),[d.vehicles]),D=n.useCallback(e=>C(e).length>0,[C]),k=n.useCallback(e=>{const i=r.addLine(e);return i.success?{success:!0}:{success:!1,error:i.error}},[r]),E=n.useCallback((e,i)=>{const u=r.updateLine(e,i);return u.success?{success:!0}:{success:!1,error:u.error}},[r]),x=n.useCallback(e=>{const i=[],u=C(e);u.length>0&&(i.push({type:"vehicle_on_removed_line",message:`${u.length} vehicle(s) are on line '${e}'`,details:{lineId:e,vehicleIds:u.map(L=>L.id)}}),u.forEach(L=>{d.removeVehicle(L.id),t.clearQueue(L.id)}));const b=r.curves.filter(L=>L.fromLineId===e||L.toLineId===e);b.length>0&&i.push({type:"orphaned_connection",message:`${b.length} connection(s) will be removed`,details:{lineId:e,connectionCount:b.length}});const $=r.removeLine(e);return $.success?{success:!0,warnings:i.length>0?i:void 0}:{success:!1,error:$.error}},[r,C,d,t]),l=n.useCallback(()=>{r.clear(),d.clear(),t.clearQueue()},[r,d,t]),p=n.useCallback((e,i,u)=>{const b=r.addConnection({from:e,to:i,fromPosition:u?.fromOffset,fromIsPercentage:u?.fromIsPercentage,toPosition:u?.toOffset,toIsPercentage:u?.toIsPercentage});return b.success?{success:!0}:{success:!1,error:b.error}},[r]),f=n.useCallback((e,i,u)=>{const b=r.updateConnection(e,i,u);return b.success?{success:!0}:{success:!1,error:b.error}},[r]),o=n.useCallback((e,i)=>{const u=r.removeConnection(e,i);return u.success?{success:!0}:{success:!1,error:u.error}},[r]),v=n.useCallback(e=>{const i=d.addVehicles(e);return i.success?{success:!0}:{success:!1,error:i.errors?.join("; ")}},[d]),g=n.useCallback((e,i)=>{const u=d.updateVehicle(e,i);return u.success?{success:!0}:{success:!1,error:u.error}},[d]),h=n.useCallback(e=>{const i=[],u=t.vehicleQueues.get(e);u&&u.length>0&&(i.push({type:"movement_queue_cleared",message:`${u.length} queued movement(s) will be cleared for vehicle '${e}'`,details:{vehicleId:e}}),t.clearQueue(e));const b=d.removeVehicle(e);return b.success?{success:!0,warnings:i.length>0?i:void 0}:{success:!1,error:b.error}},[d,t]),S=n.useCallback(()=>{d.clear(),t.clearQueue()},[d,t]),q=n.useCallback(e=>{const i={targetLineId:e.lineId,targetPosition:e.position??1,isPercentage:e.isPercentage,wait:e.wait,payload:e.payload},u=t.queueMovement(e.id,i);return u.success?{success:!0}:{success:!1,error:u.error}},[t]),c=n.useCallback(e=>{const i=t.clearQueue(e);return i.success?{success:!0}:{success:!1,error:i.error}},[t]),M=n.useCallback(e=>{const i=[],u=[],{scene:b,vehicles:$,movements:L}=T.parseAllDSL(e);b.errors.length>0&&u.push(...b.errors),$.errors.length>0&&u.push(...$.errors),L.errors.length>0&&u.push(...L.errors);const V=b.data.lines.map(z),y=(b.data.connections||[]).map(j),A=$.data.map(N),{vehicles:O,errors:R}=T.validateAndCreateVehicles(A,V,s);R.length>0&&u.push(...R);const U=new Map;for(const G of L.data){const Y=U.get(G.vehicleId)||[];Y.push(H(G)),U.set(G.vehicleId,Y)}return r._loadScene(V,y),d._loadVehicles(O),t._loadQueues(U),u.length>0&&i.push({type:"dsl_parse_error",message:`DSL loading had ${u.length} error(s)`,details:{errors:u}}),{success:!0,warnings:i.length>0?i:void 0}},[r,d,t,s]),P=n.useCallback(e=>{const i=[],u=[],b=e.lines.map(z),$=(e.connections||[]).map(j),L=(e.vehicles||[]).map(N),{vehicles:V,errors:y}=T.validateAndCreateVehicles(L,b,s);y.length>0&&u.push(...y);const A=new Map;for(const O of e.movements||[]){const R=A.get(O.vehicleId)||[];R.push(H({vehicleId:O.vehicleId,targetLineId:O.targetLineId,targetPosition:O.targetPosition,isPercentage:O.isPercentage,wait:O.wait,payload:O.payload})),A.set(O.vehicleId,R)}return r._loadScene(b,$),d._loadVehicles(V),t._loadQueues(A),u.length>0&&i.push({type:"dsl_parse_error",message:`JSON loading had ${u.length} error(s)`,details:{errors:u}}),{success:!0,warnings:i.length>0?i:void 0}},[r,d,t,s]),w=n.useMemo(()=>r.error||d.error||t.error,[r.error,d.error,t.error]);return{lines:r.lines,curves:r.curves,vehicles:d.vehicles,movingVehicles:m.movingVehicles,vehicleQueues:t.vehicleQueues,error:w,addLine:k,updateLine:E,removeLine:x,clearScene:l,connect:p,updateConnection:f,disconnect:o,addVehicles:v,updateVehicle:g,removeVehicle:h,clearVehicles:S,goto:q,clearQueue:c,prepare:m.prepare,tick:m.tick,reset:m.reset,continueVehicle:m.continueVehicle,isMoving:m.isMoving,loadFromDSL:M,loadFromJSON:P,getVehiclesOnLine:C,hasVehiclesOnLine:D}}function _(s){return s.map(a=>({id:a.id,start:a.start,end:a.end}))}function K(s){return s.map(a=>({from:a.fromLineId,to:a.toLineId,fromPosition:a.fromOffset,fromIsPercentage:a.fromIsPercentage,toPosition:a.toOffset,toIsPercentage:a.toIsPercentage}))}function ne(){const[s,a]=n.useState(""),[I,r]=n.useState(null),{lines:d,curves:t,setScene:m}=J(),C=n.useRef(!1),D=n.useRef("");n.useEffect(()=>{D.current=s},[s]),n.useEffect(()=>{if(C.current)return;const l={lines:_(d),connections:t.length>0?K(t):void 0},p=T.generateSceneDSL(l);p!==D.current&&(C.current=!0,a(p),setTimeout(()=>{C.current=!1},50))},[d,t]);const k=n.useCallback(l=>{C.current=!0,a(l);try{const{data:p,errors:f}=T.parseSceneDSL(l);f.length>0&&r(f.join("; "));const o=m(p);!o.success&&o.errors?r(v=>v?`${v}; ${o.errors.join("; ")}`:o.errors.join("; ")):f.length===0&&r(null)}catch(p){r(p instanceof Error?p.message:"Invalid scene definition")}setTimeout(()=>{C.current=!1},50)},[m]),E=n.useCallback(l=>{const p=typeof l=="function"?l(d):l,f=K(t);m({lines:_(p),connections:f.length>0?f:void 0});const o={lines:_(p),connections:f.length>0?f:void 0},v=T.generateSceneDSL(o);C.current=!0,a(v),setTimeout(()=>{C.current=!1},50)},[d,t,m]),x=n.useCallback(l=>{const p=typeof l=="function"?l(t):l,f=K(p);m({lines:_(d),connections:f.length>0?f:void 0});const o={lines:_(d),connections:f.length>0?f:void 0},v=T.generateSceneDSL(o);C.current=!0,a(v),setTimeout(()=>{C.current=!1},50)},[d,t,m]);return{lines:d,curves:t,sceneDefinitionText:s,sceneError:I,isDebouncing:!1,debounceKey:0,setLines:E,setCurves:x,setSceneDefinitionText:k}}function oe({lines:s,wheelbase:a}){const[I,r]=n.useState(""),[d,t]=n.useState(null),{vehicles:m,addVehicles:C,clear:D,error:k}=B({lines:s,wheelbase:a}),E=n.useRef(!1),x=n.useCallback(l=>{E.current=!0,r(l);try{const{data:p,errors:f}=T.parseVehiclesDSL(l),o=[...f];D();for(const v of p){const g=C(v);!g.success&&g.errors&&o.push(...g.errors)}o.length>0?t(o.join(`
2
- `)):t(null)}catch(p){t(p instanceof Error?p.message:"Invalid initial movement")}setTimeout(()=>{E.current=!1},50)},[C,D]);return{vehicles:m,initialMovementText:I,movementError:d||k,isDebouncing:!1,debounceKey:0,setInitialMovementText:x}}function ce({lines:s,vehicles:a}){const[I,r]=n.useState(""),[d,t]=n.useState([]),[m,C]=n.useState(null),{vehicleQueues:D,queueMovement:k,clearQueue:E,error:x}=W({vehicles:a,lines:s}),l=n.useRef(!1),p=n.useCallback(f=>{l.current=!0,r(f);try{const{data:o,errors:v}=T.parseMovementDSL(f),g=[...v];E();for(const h of o){const S=k(h.vehicleId,{targetLineId:h.targetLineId,targetPosition:h.targetPosition,isPercentage:h.isPercentage,wait:h.wait,payload:h.payload});!S.success&&S.error&&g.push(S.error)}t(o),g.length>0?C(g.join(`
3
- `)):C(null)}catch(o){C(o instanceof Error?o.message:"Invalid movement sequence"),t([])}setTimeout(()=>{l.current=!1},50)},[k,E]);return{movementSequenceText:I,gotoCommands:d,vehicleQueues:D,sequenceError:m||x,isDebouncing:!1,debounceKey:0,setMovementSequenceText:p}}const X=n.createContext(null);function ee(){const s=n.useContext(X);if(!s)throw new Error("useVehicleEventEmitter must be used within a VehicleEventProvider");return s}function ie(){return n.useMemo(()=>new T.VehicleEventEmitter,[])}function ae(s,a,I=[]){const r=ee();n.useEffect(()=>r.on(s,a),[r,s,...I])}function ue({children:s}){const a=n.useMemo(()=>new T.VehicleEventEmitter,[]);return te.jsx(X.Provider,{value:a,children:s})}exports.VehicleEventContext=X;exports.VehicleEventProvider=ue;exports.useAnimation=Z;exports.useCreateVehicleEventEmitter=ie;exports.useInitialMovement=oe;exports.useMovementQueue=W;exports.useMovementSequence=ce;exports.useScene=J;exports.useSceneDefinition=ne;exports.useVehicleEvent=ae;exports.useVehicleEventEmitter=ee;exports.useVehicleSimulation=re;exports.useVehicles=B;