vehicle-path2 1.0.7 → 1.0.8

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