UNPKG

orthogonal-path-finding

Version:

Compute an orthogonal line from start to end with obstacles

2 lines (1 loc) 7.48 kB
(function(y,A){typeof exports=="object"&&typeof module<"u"?A(exports):typeof define=="function"&&define.amd?define(["exports"],A):(y=typeof globalThis<"u"?globalThis:y||self,A(y.OrthogonalPathFinding={}))})(this,function(y){"use strict";const{floor:A}=Math,{min:L}=Math,u=function(e,t){return e<t?-1:e>t?1:0},Q=function(e,t,s=0,n=null,i=u){let o;if(s<0)throw new Error("lo must be non-negative");for(n==null&&(n=e.length);s<n;)o=A((s+n)/2),i(t,e[o])<0?n=o:s=o+1;return e.splice(s,0,t),t},R=function(e,t,s=u){e.push(t),P(e,0,e.length-1,s)},N=function(e,t=u){let s;const n=e.pop();return e.length?(s=e[0],e[0]=n,m(e,0,t)):s=n,s},$=function(e,t,s=u){const n=e[0];return e[0]=t,m(e,0,s),n},I=function(e,t,s=u){return e.length&&s(e[0],t)<0&&([e[0],t]=[t,e[0]],m(e,0,s)),t},O=function(e,t=u){const s=A(e.length/2);for(let n=s-1;n>=0;n--)m(e,n,t)},q=function(e,t,s=u){const n=e.indexOf(t);n!==-1&&(P(e,0,n,s),m(e,n,s))},B=function(e,t,s=u){if(t<=0)return[];const n=e.slice(0,t);if(!n.length)return n;O(n,s);for(const i of e.slice(t))I(n,i,s);return n.sort(s).reverse()},H=function(e,t,s=u){if(t<=0)return[];if(t*10<=e.length){const i=e.slice(0,t).sort(s);if(!i.length)return i;let o=i[i.length-1];for(const l of e.slice(t))s(l,o)<0&&(Q(i,l,0,null,s),i.pop(),o=i[i.length-1]);return i}O(e,s);const n=[];for(let i=0;i<L(t,e.length);i++)n.push(N(e,s));return n},P=function(e,t,s,n=u){const i=e[s];for(;s>t;){const o=s-1>>1,l=e[o];if(n(i,l)<0){e[s]=l,s=o;continue}break}e[s]=i},m=function(e,t,s=u){const n=e.length,i=t,o=e[t];let l=2*t+1;for(;l<n;){let r=l+1;r<n&&!(s(e[l],e[r])<0)&&(l=r),e[t]=e[l],t=l,l=2*t+1}e[t]=o,P(e,i,t,s)};class v{constructor(t=u){this.insert=this.push,this.top=this.peek,this.front=this.peek,this.has=this.contains,this.copy=this.clone,this.cmp=t,this.nodes=[]}push(t){R(this.nodes,t,this.cmp)}pop(){return N(this.nodes,this.cmp)}peek(){return this.nodes[0]}contains(t){return this.nodes.indexOf(t)!==-1}replace(t){return $(this.nodes,t,this.cmp)}pushpop(t){return I(this.nodes,t,this.cmp)}heapify(){O(this.nodes,this.cmp)}updateItem(t){q(this.nodes,t,this.cmp)}clear(){this.nodes=[]}empty(){return this.nodes.length===0}size(){return this.nodes.length}clone(){const t=new v(this.cmp);return t.nodes=this.nodes.slice(0),t}toArray(){return this.nodes.slice(0)}static push(t,s,n=u){R(t,s,n)}static pop(t,s=u){return N(t,s)}static replace(t,s,n=u){return $(t,s,n)}static pushpop(t,s,n=u){return I(t,s,n)}static heapify(t,s=u){O(t,s)}static updateItem(t,s,n=u){q(t,s,n)}static nlargest(t,s,n=u){return B(t,s,n)}static nsmallest(t,s,n=u){return H(t,s,n)}}class z{constructor(t,s,n=!0){this.x=t,this.y=s,this.walkable=n}}var x=(e=>(e[e.always=1]="always",e[e.never=2]="never",e[e.ifAtMostOneObstacle=3]="ifAtMostOneObstacle",e[e.onlyWhenNoObstacles=4]="onlyWhenNoObstacles",e))(x||{});class C{constructor(t,s,n){let i;typeof t!="object"?i=t:(s=t.length,i=t[0].length,n=t),this.width=i,this.height=s,this.nodes=this._buildNodes(i,s,n)}_buildNodes(t,s,n){const i=new Array(s);for(let o=0;o<s;++o){i[o]=new Array(t);for(let l=0;l<t;++l)i[o][l]=new z(l,o)}if(n===void 0)return i;if(n.length!==s||n[0].length!==t)throw new Error("Matrix size does not fit");for(let o=0;o<s;++o)for(let l=0;l<t;++l)n[o][l]&&(i[o][l].walkable=!1);return i}getNodeAt(t,s){return this.nodes[s][t]}isWalkableAt(t,s){return this.isInside(t,s)&&!!this.nodes[s][t].walkable}isInside(t,s){return t>=0&&t<this.width&&s>=0&&s<this.height}setWalkableAt(t,s,n){this.nodes[s][t].walkable=n}getNeighbors(t,s){const{x:n,y:i}=t,o=[];let l=!1,r=!1,a=!1,d=!1,p=!1,b=!1,f=!1,g=!1;const{nodes:h}=this;if(this.isWalkableAt(n,i-1)&&(o.push(h[i-1][n]),l=!0),this.isWalkableAt(n+1,i)&&(o.push(h[i][n+1]),a=!0),this.isWalkableAt(n,i+1)&&(o.push(h[i+1][n]),p=!0),this.isWalkableAt(n-1,i)&&(o.push(h[i][n-1]),f=!0),s===x.never)return o;if(s===x.onlyWhenNoObstacles)r=f&&l,d=l&&a,b=a&&p,g=p&&f;else if(s===x.ifAtMostOneObstacle)r=f||l,d=l||a,b=a||p,g=p||f;else if(s===x.always)r=!0,d=!0,b=!0,g=!0;else throw new Error("Incorrect value of diagonalMovement");return r&&this.isWalkableAt(n-1,i-1)&&o.push(h[i-1][n-1]),d&&this.isWalkableAt(n+1,i-1)&&o.push(h[i-1][n+1]),b&&this.isWalkableAt(n+1,i+1)&&o.push(h[i+1][n+1]),g&&this.isWalkableAt(n-1,i+1)&&o.push(h[i+1][n-1]),o}clone(){const{width:t,height:s,nodes:n}=this,i=new C(t,s),o=new Array(s);for(let l=0;l<s;++l){o[l]=new Array(t);for(let r=0;r<t;++r)o[l][r]=new z(r,l,n[l][r].walkable)}return i.nodes=o,i}}function J(e){const t=[[e.x,e.y]];for(;e.parent;)e=e.parent,t.push([e.x,e.y]);return t.reverse()}function K(e){if(e.length<3)return e;const t=[],s=e[0][0],n=e[0][1];let i=e[1][0],o=e[1][1],l=i-s,r=o-n,a,d,p,b,f,g;for(f=Math.sqrt(l*l+r*r),l/=f,r/=f,t.push([s,n]),g=2;g<e.length;g++)a=i,d=o,p=l,b=r,i=e[g][0],o=e[g][1],l=i-a,r=o-d,f=Math.sqrt(l*l+r*r),l/=f,r/=f,(l!==p||r!==b)&&t.push([a,d]);return t.push([i,o]),t}const T={manhattan(e,t){return e+t},euclidean(e,t){return Math.sqrt(e*e+t*t)},octile(e,t){const s=Math.SQRT2-1;return e<t?s*e+t:s*t+e},chebyshev(e,t){return Math.max(e,t)}};class U{constructor(t={}){this.allowDiagonal=t.allowDiagonal||!1,this.dontCrossCorners=t.dontCrossCorners||!1,this.heuristic=t.heuristic||T.manhattan,this.weight=t.weight||1,this.diagonalMovement=t.diagonalMovement||x.never,this.diagonalMovement||(this.allowDiagonal?this.dontCrossCorners?this.diagonalMovement=x.onlyWhenNoObstacles:this.diagonalMovement=x.ifAtMostOneObstacle:this.diagonalMovement=x.never),this.diagonalMovement===x.never?this.heuristic=t.heuristic||T.manhattan:this.heuristic=t.heuristic||T.octile}getTurnWeight(t){const{parent:s}=t;if(!s)return 0;const n=s.parent;if(!n)return 0;let i=0;return n.x===s.x&&t.x!==s.x&&(i+=1),n.y===s.y&&t.y!==s.y&&(i+=1),i}findPath(t,s,n,i,o){const l=new v((_,tt)=>_.f-tt.f),r=o.getNodeAt(t,s),a=o.getNodeAt(n,i),{heuristic:d}=this,{diagonalMovement:p}=this,{weight:b}=this,{abs:f,SQRT2:g}=Math;let h,w,c,k,M,S,E,F,G=0;for(r.g=0,r.f=0,r.t=0,l.push(r),r.opened=!0;!l.empty();){if(h=l.pop(),h.closed=!0,h===a)return J(a);for(w=o.getNeighbors(h,p),k=0,M=w.length;k<M;++k)c=w[k],!c.closed&&(S=c.x,E=c.y,F=h.g+(S-h.x===0||E-h.y===0?1:g),(!c.opened||F<c.g)&&(c.parent=h,c.g=F,c.h=c.h||b*d(f(S-n),f(E-i)),c.t=h.t+this.getTurnWeight(c),c.t!==0&&(G+=.01,c.t+=G),c.f=c.g+c.h+c.t,c.opened?l.updateItem(c):(l.push(c),c.opened=!0)))}return[]}}const V=e=>e.reduce((t,s,n)=>n===0?`M${s.x}, ${s.y}`:`${t} L${s.x}, ${s.y}`,""),Z=(e,t)=>({x:e.x-t,y:e.y-t,width:e.width+t*2,height:e.height+t*2}),X=(e,t)=>e.x>=t.x&&e.x<=t.x+t.width&&e.y>=t.y&&e.y<=t.y+t.height,Y=(e,t,s=0)=>{const{x:n,y:i}=e;return t.some(o=>X({x:n,y:i},Z(o,s)))},W=(e,t)=>{let s=0,n=e.length-1;for(;s<=n;){const i=Math.floor((s+n)/2);if(e[i]===t)return i;e[i]<t?s=i+1:n=i-1}return-1};let j;const D=(e,t,s,n={})=>{n=Object.assign({debug:!1,padding:5},n);const i=[...s.flatMap(h=>[h.x,h.x+h.width]),e.x,e.x,t.x,t.x].sort((h,w)=>h-w),o=[...s.flatMap(h=>[h.y,h.y+h.height]),e.y,e.y,t.y,t.y].sort((h,w)=>h-w),l=i.slice(1).map((h,w)=>(h+i[w])/2),r=o.slice(1).map((h,w)=>(h+o[w])/2),a={x:W(l,e.x),y:W(r,e.y)},d={x:W(l,t.x),y:W(r,t.y)},p=new C(l.length,r.length),b=[];l.forEach((h,w)=>{r.forEach((c,k)=>{const M={x:h,y:c};Y(M,s,n.padding)?p.setWalkableAt(w,k,!1):b.push(M)})}),j||(j=new U);const f=K(j.findPath(a.x,a.y,d.x,d.y,p)).map(h=>({x:l[h[0]],y:r[h[1]]})),g=V(f);return{...n.debug?{walkablePoints:b,xs:i,ys:o,indexGrid:p,startIndexPoint:a,endIndexPoint:d,xCenters:l,yCenters:r}:{},path:f,d:g}};y.orthogonalPathFinding=D,Object.defineProperty(y,Symbol.toStringTag,{value:"Module"})});