@expofp/floorplan
Version:
Interactive floor plan library for expos and events
2 lines (1 loc) • 7.96 kB
JavaScript
import{degToRad as Me,lineAngle as ae,pointDistance as le}from"@expofp/geometry";import{update as Y}from"@expofp/renderer";import{useEffect as ve,useRef as v}from"react";import{getBounds as $}from"../../../engine/canvases";import Le from"../../../tools/base-runtime-url";import{loadImage as Re}from"../../../utils/loadImage";import{mapCamera as ue}from"../camera";import{createImageCanvas as Se}from"../drawing/config/canvases";import{getRouteLayerRegex as be,getRouteStopLayerRegex as Te,TRAFFIC_VEHICLES as te}from"./trafficLayers";import{UpdateQueue as Ae}from"./UpdateQueue";const ne=2,Fe=ne*3.5,Z=ne*12,de=Z*.7,Pe=8,Ce="transport_locations",_e=150,Ne="#a4aab6",he=1e3,De=2e4,fe=500;export function useManageTraffic({permission:t,scene:r,dataJsonUrl:n,websocketUrl:a}){const e=v(window.devicePixelRatio),s=v(new Map),o=v(new Map),c=v(null),h=v(null),u=v(0),M=v(1),T=v(new Ae),L=v(new Map),x=v(he),f=v(new Map),p=v(void 0);ve(()=>{if(!t||!r)return;let l=!1,P;const R=r.rootLayer.children,g=o.current,y=s.current,I=T.current;async function ye(){try{let d;try{const i=await fetch(n,{method:"GET",headers:{"Content-Type":"application/json"}});if(!i.ok||i.status===204)return;d=await i.json()}catch(i){console.warn("useManageTraffic failed to get JSON",i)}if(!d||d.disabled||d.error)return;e.current=ge(ue()?.state().pixelSize??window.devicePixelRatio);const D=new Map(d.routes.map(({id:i,color:m})=>[i,m])),z=new Map;D.forEach(i=>{z.set(i,Ve({radius:Fe,color:i,scale:window.devicePixelRatio}))});const U=be(),j=R.filter(i=>U.test(i.name)),Q=Te(),q=R.filter(i=>Q.test(i.name));d.routes.slice(0,j.length).forEach(({id:i,color:m,path:w,stops:C,hiddenPoints:S},V)=>{L.current.set(i,w);const E=j[V];if(E){const H=new Map(S?.map(_=>[ce(_),_])??[]);for(let _=1;_<w.length;_++){const b=w[_-1],W=w[_];H.has(ce(b))||H.has(ce(W))||E.children.push({points:{p0:b,p1:W},color:m,width:ne})}E.children.length&&I.add(()=>Y(E))}const A=q[V],F=z.get(m);A&&F&&C?.length&&(C.forEach(H=>{A.children.push({source:F,bounds:$(H.x,H.y,F.width,F.height,e.current)})}),A.children.length&&I.add(()=>Y(A)))});const O=R.find(i=>i.name===te);if(!O){console.warn(`useManageTraffic: the scene has no "${te}" layer`);return}const B=new Map;O.onScale=({pixelSize:i})=>{e.current=ge(i);const m=[];for(const w of q){const C=w.children;for(const S of C){const V=S.source;S.bounds=$(S.bounds.center.x,S.bounds.center.y,V.width,V.height,e.current,S.bounds.rotation),m.push(S)}}for(const w of s.current.values()){const C=w.source;w.bounds=$(w.bounds.center.x,w.bounds.center.y,C.width,C.height,e.current,w.bounds.rotation),m.push(w)}return m},I.add(()=>Y(O)),D.forEach((i,m)=>{B.set(m,me({id:m,width:Z,height:Z,color:i,scale:window.devicePixelRatio}))});const xe=me({id:"orphan",width:Z,height:Z,color:Ne,scale:window.devicePixelRatio});let G=null;try{const i=await Re(`${Le}icons/bus-white.svg`);G=Se(i,de,de,window.devicePixelRatio)}catch(i){console.error(i)}h.current=i=>{try{const m=JSON.parse(i.data);if(m.type!==Ce)return;const w=i.timeStamp||performance.now();M.current=Math.round(w-u.current),u.current=w;const C=Math.max(M.current??fe,fe),S=O.children;let V=!1;m.data.forEach(({id:E,x:A,y:F,route_id:H,hidden:_})=>{g.has(E)&&(g.get(E)?.(),g.delete(E));const b=y.get(E),W=y.get(re(E))??null;if(_&&b&&W){b.hidden=!0,W.hidden=!0,I.add(()=>Y(b,W));return}if(f.current.set(E,performance.now()),!b){const k=B.get(H)??xe,N={source:k,bounds:$(A,F,k.width,k.height,e.current,0)};if(y.set(E,N),G){const X=re(E),ie={source:G,bounds:$(A,F,G.width,G.height,e.current,0)};G.setAttribute("id",X),y.set(X,ie),S.push(N,ie)}else S.push(N);V=!0;return}const Ee=b.bounds.center,ee=We(Ee,{x:A,y:F},L.current.get(H));if(ee.reduce((k,N,X)=>X===0?k:k+oe(ee[X-1],N),0)>=_e||document.hidden)we({updateQueue:I,vehicle:b,vehicleFinery:W,x:A,y:F,scale:e.current,angle:Oe(b.bounds.center,{x:A,y:F})});else{const k=ke(ee,C,N=>{we({updateQueue:I,vehicle:b,vehicleFinery:W,x:N.x,y:N.y,scale:e.current,angle:N.angle})});g.set(E,k)}}),V&&I.add(()=>Y(O))}catch(m){console.error("handleSocketMessage",m)}},u.current=performance.now(),K(),window.addEventListener("online",se),Ie(d.config?(Number(d.config.vehicle_inactive_interval)||20)*1e3:De)}catch(d){console.warn(d)}}ye();function K(){l||c.current?.readyState===WebSocket.OPEN||c.current?.readyState===WebSocket.CONNECTING||(c.current=new WebSocket(a),c.current.onmessage=h.current,c.current.onopen=()=>{x.current=he},c.current.onclose=()=>{if(l||!navigator.onLine)return;P=window.setTimeout(K,x.current);const d=3e4;x.current=Math.min(x.current*2,d)})}function se(){K()}function Ie(d){clearInterval(p.current),p.current=window.setInterval(()=>{const D=performance.now(),z=[],U=[];f.current.forEach((j,Q)=>{if(D-j<d)return;const q=y.get(Q);q&&(q.hidden=!0,U.push(q),z.push(Q));const O=re(Q),B=y.get(O);B&&(B.hidden=!0,U.push(B),z.push(O))}),U.length&&I.add(()=>Y(...U)),z.forEach(j=>f.current.delete(j))},d)}return()=>{l=!0,clearTimeout(P),c.current?.close(),c.current=null;const d=R.find(D=>D.name===te);d&&(d.onScale=void 0),g.forEach(D=>D?.()),g.clear(),y.clear(),I.destroy(),window.removeEventListener("online",se),clearInterval(p.current)}},[t,r,n,a])}function ge(t){return Math.min(t||window.devicePixelRatio,Pe)}function Oe(t,r){return ae(t,r)}function Ve({radius:t,scale:r,color:n}){const a=document.createElement("canvas"),e=a.getContext("2d");e.imageSmoothingEnabled=!1;const s=Math.ceil(t*2*r),o=s/2,c=t*.78,h=t*.33;return a.width=s,a.height=s,e.translate(o,o),e.scale(r,r),e.beginPath(),e.arc(0,0,t,0,2*Math.PI),e.fillStyle="#FFFFFF",e.fill(),e.beginPath(),e.arc(0,0,c,0,2*Math.PI),e.fillStyle=n,e.fill(),e.beginPath(),e.arc(0,0,h,0,2*Math.PI),e.fillStyle="#FFFFFF",e.fill(),a}function me({id:t,width:r,height:n,color:a,scale:e}){const s=2*e,o=r/2*e,c=n*e,h=.6,u=.01,M=c*h,T=c,L=8*e,x=T+s+M/2+L*2,f=o*2+M+s+L*2,p=document.createElement("canvas");p.setAttribute("id",t),p.width=x,p.height=f;const l=p.getContext("2d"),P=x/2,R=f/2;l.save(),l.translate(P,R),l.rotate(-Math.PI/2),l.translate(-P,-R);const g=P,y=R-T/2+o,I=y+M;return l.shadowColor="rgba(16, 24, 40, 0.2)",l.shadowBlur=L/2,l.beginPath(),l.arc(g,y,o,Math.PI,0,!1),l.bezierCurveTo(g+o,y+c*.3,g+u*o,I,g,I),l.bezierCurveTo(g-u*o,I,g-o,y+c*.3,g-o,y),l.closePath(),l.fillStyle=a,l.fill(),l.strokeStyle="#FFFFFF",l.lineWidth=s,l.stroke(),l.restore(),p}function oe(t,r){return le(t,r)}function re(t){return`${t}_finery`}function we({updateQueue:t,vehicle:r,vehicleFinery:n,x:a,y:e,scale:s,angle:o}){const c=r.source;if(r.bounds=$(a,e,c.width,c.height,s,o),r.hidden=!1,n){const h=ue()?.state().roll??0,u=n.source;n.bounds=$(a,e,u.width,u.height,s,Me(h)),n.hidden=!1,t.add(()=>Y(n,r))}else t.add(()=>Y(r))}function J(t,r,n){return{x:t.x+(r.x-t.x)*n,y:t.y+(r.y-t.y)*n}}function pe(t,r){let n=1/0,a={index:0,t:0};for(let e=0;e<r.length-1;e++){const s=r[e],o=r[e+1],c=o.x-s.x,h=o.y-s.y,u=c*c+h*h,M=u?((t.x-s.x)*c+(t.y-s.y)*h)/u:0,T=Math.max(0,Math.min(1,M)),L=J(s,o,T),x=oe(t,L);x<n&&(n=x,a={index:e,t:T})}return a}function He(t,r,n){const a=oe(t,r),e=Math.max(2,Math.ceil(a/n)+1),s=[];for(let o=0;o<e;o++){const c=o/(e-1),h=J(t,r,c);s.push(h)}return s}function We(t,r,n){if(!n||!n.length)return He(t,r,10);const a=pe(t,n),e=pe(r,n),s=[],o=c=>s.push(c);a.index+1<n.length?o(J(n[a.index],n[a.index+1],a.t)):o(n[a.index]);for(let c=a.index+1;c<=e.index&&c<n.length;c++)o(n[c]);return e.index+1<n.length?o(J(n[e.index],n[e.index+1],e.t)):o(n[e.index]),s}function ke(t,r,n,a){if(t.length<2)return()=>{};let e=null,s;const o=[];let c=0;for(let u=0;u<t.length-1;u++){const M=le(t[u],t[u+1]);o.push(M),c+=M}const h=u=>{e||(e=u);const M=u-e,T=Math.min(M/r,1),L=T*c;let x=0,f=0;for(;f<o.length&&x+o[f]<L;)x+=o[f],f++;f>=o.length&&(f=o.length-1);const p=t[f],l=t[Math.min(f+1,t.length-1)],P=o[f]||0,R=P===0?0:(L-x)/P,g=p.x+(l.x-p.x)*R,y=p.y+(l.y-p.y)*R,I=ae(p,l);n({x:g,y,angle:I}),T<1?s=requestAnimationFrame(h):a&&a()};return s=requestAnimationFrame(h),()=>{s&&cancelAnimationFrame(s)}}function ce(t){return`${t.x},${t.y}`}