@caveworld/honeycomb-grid
Version:
Create hexagon grids easily
2 lines (1 loc) • 12.1 kB
JavaScript
(function(o,b){typeof exports=="object"&&typeof module<"u"?b(exports):typeof define=="function"&&define.amd?define(["exports"],b):(o=typeof globalThis<"u"?globalThis:o||self,b(o.Honeycomb={}))})(this,function(o){"use strict";const b=t=>typeof t=="object"&&t!==null,mt=t=>b(t)&&Number.isFinite(t.q)&&Number.isFinite(t.r),X=t=>typeof t=="function",y=t=>b(t)&&Number.isFinite(t.col)&&Number.isFinite(t.row),G=t=>b(t)&&Number.isFinite(t.x)&&Number.isFinite(t.y),q=t=>Array.isArray(t)&&Number.isFinite(t[0])&&Number.isFinite(t[1]),F=(t,e)=>e+t*(e&1)>>1;function wt(t,e){return(t%e+e)%e}const N=([t,e,n=-t-e])=>({q:t,r:e,s:n});var Y=(t=>(t[t.N=0]="N",t[t.E=2]="E",t[t.S=4]="S",t[t.W=6]="W",t))(Y||{}),B=(t=>(t[t.NE=1]="NE",t[t.SE=3]="SE",t[t.SW=5]="SW",t[t.NW=7]="NW",t))(B||{}),a=(t=>(t[t.N=0]="N",t[t.NE=1]="NE",t[t.E=2]="E",t[t.SE=3]="SE",t[t.S=4]="S",t[t.SW=5]="SW",t[t.W=6]="W",t[t.NW=7]="NW",t))(a||{});class m{static N=0;static NE=1;static E=2;static SE=3;static S=4;static SW=5;static W=6;static NW=7;static Cardinal;static Ordinal;static of(e=0){return new m(e)}static isCardinal(e){return!!Y[e]}static isOrdinal(e){return!!B[e]}static rotate(e,n){return wt(e+n,8)}direction;constructor(e=0){this.direction=typeof e=="number"?e:a[e]}isCardinal(){return m.isCardinal(this.direction)}isOrdinal(){return m.isOrdinal(this.direction)}rotate(e){return m.rotate(this.direction,e)}}const $=(t,e,n)=>{const r=t-F(n,e),i=e,s=-r-i;return{q:r,r:i,s}},_=(t,e,n)=>{const r=t,i=e-F(n,t),s=-r-i;return{q:r,r:i,s}},H=({offset:t,isPointy:e},{col:n,row:r})=>e?$(n,r,t):_(n,r,t);function W(t,e){const{q:n,r,s:i=-n-r}=y(e)?H(t,e):q(e)?N(e):e;return{q:n,r,s:i}}const R=t=>b(t)&&!!Object.getPrototypeOf(t).__isHoneycombHex;function D(t){const{width:e,height:n}=t,{x:r,y:i}=R(t)?t:t.origin;return{x:e/2-r,y:n/2-i}}const p=(t,e={})=>{if(y(e)){const{col:n,row:r,...i}=e,s=H(t,{col:n,row:r});return Object.assign(Object.create(Object.getPrototypeOf(t)),t,s,i)}return e=q(e)?N(e):e,Object.assign(Object.create(Object.getPrototypeOf(t)),t,e)};var g=(t=>(t.FLAT="FLAT",t.POINTY="POINTY",t))(g||{});const J=t=>t*2,z=t=>t*Math.sqrt(3),tt=({orientation:t,dimensions:{yRadius:e}})=>t===g.POINTY?J(e):z(e),A=({orientation:t,dimensions:{xRadius:e,yRadius:n},origin:{x:r,y:i},q:s,r:c})=>t===g.POINTY?{x:e*Math.sqrt(3)*(s+c/2)-r,y:n*3/2*c-i}:{x:e*3/2*s-r,y:n*Math.sqrt(3)*(c+s/2)-i},K=t=>t*Math.sqrt(3),U=t=>t*2,et=({orientation:t,dimensions:{xRadius:e}})=>t===g.POINTY?K(e):U(e),nt=(t,e,{x:n,y:r})=>[{x:n+t*.5,y:r-e*.25},{x:n+t*.5,y:r+e*.25},{x:n,y:r+e*.5},{x:n-t*.5,y:r+e*.25},{x:n-t*.5,y:r-e*.25},{x:n,y:r-e*.5}],rt=(t,e,{x:n,y:r})=>[{x:n+t*.25,y:r-e*.5},{x:n+t*.5,y:r},{x:n+t*.25,y:r+e*.5},{x:n-t*.25,y:r+e*.5},{x:n-t*.5,y:r},{x:n-t*.25,y:r-e*.5}];function it(t){const{orientation:e,dimensions:{xRadius:n,yRadius:r}}=t,i=R(t)?A(t):t.origin;return e===g.POINTY?nt(K(n),J(r),i):rt(U(n),z(r),i)}const Q=(t,e={q:0,r:0})=>{if(R(t))return t.clone(e);if(y(e)){const{col:n,row:r,...i}=e,s=H(t,{col:n,row:r});return Object.assign(Object.create(t),s,i)}return e=q(e)?N(e):e,Object.assign(Object.create(t),e)};function st(t,e){if(y(t)&&y(e))return t.col===e.col&&t.row===e.row;if(Object.prototype.hasOwnProperty.call(t,"col")||Object.prototype.hasOwnProperty.call(e,"col"))throw new Error(`Can't compare coordinates where one are offset coordinates. Either pass two offset coordinates or two axial/cube coordinates. Received: ${JSON.stringify(t)} and ${JSON.stringify(e)}`);const n=q(t)?N(t):t,r=q(e)?N(e):e;return n.q===r.q&&n.r===r.r}const ot=(t,e,n)=>({col:t+F(n,e),row:e}),ct=(t,e,n)=>({col:t,row:e+F(n,t)}),v=({q:t,r:e,offset:n,isPointy:r})=>r?ot(t,e,n):ct(t,e,n),ut=({orientation:t})=>t===g.FLAT,ft=({orientation:t})=>t===g.POINTY,ht=({q:t,r:e})=>`${t},${e}`,at={dimensions:{xRadius:1,yRadius:1},orientation:g.POINTY,origin:{x:0,y:0},offset:-1},lt=t=>{const e=new WeakMap,n={...at,clone(r){return p(this,r)},equals(r){return st(this,y(r)?H(this,r):r)},toString(){return ht(this)},...t};return Object.defineProperties(n,{[Symbol.toStringTag]:{value:"Hex"},__isHoneycombHex:{value:!0,writable:!1},center:{get(){return D(this)}},col:{get(){return v(this).col}},corners:{get(){return it(this)}},dimensions:{value:Pt(n)},height:{get(){return tt(this)}},isFlat:{get(){return ut(this)}},isPointy:{get(){return ft(this)}},orientation:{value:gt(n)},offset:{value:Et(n)},row:{get(){return v(this).row}},s:{get(){const r=e.get(this);return Number.isFinite(r)?r:-this.q-this.r},set(r){e.set(this,r)}},width:{get(){return et(this)}},x:{get(){return A(this).x}},y:{get(){return A(this).y}}}),Object.defineProperties(n,{origin:{value:Ft(n)}})};function Pt(t){const{dimensions:e}=t;if(b(e)){if(e.xRadius>0&&e.yRadius>0)return{...e};const{width:n,height:r}=e;if(n>0&&r>0)return gt(t)===g.POINTY?{xRadius:n/Math.sqrt(3),yRadius:r/2}:{xRadius:n/2,yRadius:r/Math.sqrt(3)}}if(e>0)return{xRadius:e,yRadius:e};throw new TypeError(`Invalid dimensions: ${JSON.stringify(e)}. Dimensions must be expressed as an Ellipse ({ xRadius: number, yRadius: number }), a Rectangle ({ width: number, height: number }) or a number.`)}function gt({orientation:t}){return t.toUpperCase()}function Et({offset:t}){if(!Number.isFinite(t))throw new TypeError(`Invalid offset: ${t}. Offset must be a number.`);return t}function Ft(t){const{origin:e}=t;if(G(e))return{...e};if(e==="topLeft")return{x:t.width*-.5,y:t.height*-.5};if(X(e))return e(t);throw new TypeError(`Invalid origin: ${JSON.stringify(e)}. Origin must be expressed as a Point ({ x: number, y: number }), 'topLeft' or a function that returns a Point.`)}const j=({q:t,r:e,s:n=-t-e})=>{let r=Math.round(t),i=Math.round(e),s=Math.round(n);const c=Math.abs(t-r),u=Math.abs(e-i),f=Math.abs(n-s);return c>u&&c>f?r=-i-s:u>f?i=-r-s:s=-r-i,{q:r,r:i,s}},bt=({dimensions:{xRadius:t,yRadius:e},origin:n,isPointy:r},{x:i,y:s})=>(i+=n.x,s+=n.y,j(r?{q:Math.sqrt(3)*i/(3*t)-s/(3*e),r:2/3*(s/e)}:{q:2/3*(i/t),r:Math.sqrt(3)*s/(3*e)-i/(3*t)}));function I(t,e,n){const{q:r,r:i,s=-r-i}=W(t,e),{q:c,r:u,s:f=-c-u}=W(t,n);return Math.max(Math.abs(r-c),Math.abs(i-u),Math.abs(s-f))}const Ht=[null,{q:1,r:-1},{q:1,r:0},{q:0,r:1},null,{q:-1,r:1},{q:-1,r:0},{q:0,r:-1}],Wt=[{q:0,r:-1},{q:1,r:-1},null,{q:1,r:0},{q:0,r:1},{q:-1,r:1},null,{q:-1,r:0}],dt=({offset:t,q:e,r:n,col:r,row:i},s)=>{if(s===a.S||s===a.N){const u=s===a.S?i+1:i-1;return $(r,u,t)}const c=Ht[s];return{q:e+c.q,r:n+c.r}},Ot=({offset:t,q:e,r:n,col:r,row:i},s)=>{if(s===a.E||s===a.W){const u=s===a.E?r+1:r-1;return _(u,i,t)}const c=Wt[s];return{q:e+c.q,r:n+c.r}},w=(t,e)=>t.clone(t.isPointy?dt(t,e):Ot(t,e));function P(t){return Array.isArray(t)?function(n,r){const i=[];let s=r;for(const c of t)for(const u of c(n,s))i.push(s=u);return i}:t}const It=(...t)=>e=>t.map(e);function C(t){return Mt(t)?Rt(t):At(t)}function Mt(t){return t.direction in a}function Rt({start:t,direction:e,length:n}){return function(i,s){const c=[];let f=i(t??s);!t&&s&&(f=w(f,e));for(let h=0;h<n;h++)c.push(f),f=w(f,e);return c}}function At({start:t,stop:e}){return function(r,i){const s=[],c=r(t??i),u=yt(c),f=yt(W(c,e)),h=vt(u,f),d=I(c,c,e),E=1/Math.max(d,1);let S=!t&&i?1:0;for(S;S<=d;S++){const l=j(h(E*S));s.push(r(l))}return s}}function yt({q:t,r:e,s:n}){return{q:t+1e-6,r:e+1e-6,s:n+-2e-6}}function vt(t,e){return n=>{const r=t.q*(1-n)+e.q*n,i=t.r*(1-n)+e.r*n;return{q:r,r:i}}}const jt=t=>(e,n)=>[w(e(n),t)];function k(t,e,{includeSource:n=!0}={}){return function(i,s){const c=[];for(const u of P(t)(i,s)){n&&c.push(u);for(const f of P(e)(i,u))c.push(f)}return c}}function Ct(t,e){return function(r,i){const{width:s,height:c,start:u,direction:f=a.E}=e?Lt(t,e,r()):t,h=r(u??i),d=k(C({start:h,direction:m.rotate(f,2),length:c}),C({direction:f,length:s-1}))(r,h);return!u&&i?d.slice(1):d}}function Lt(t,e,{isPointy:n,offset:r}){const{col:i,row:s}=Tt(t,n,r),{col:c,row:u}=Tt(e,n,r),f=i<c?"A":"B",h=s<u?"A":"B",d=f+h,{swapWidthHeight:E,direction:S}=xt[d],l=Math.abs(i-c)+1,x=Math.abs(s-u)+1;return{width:E?x:l,height:E?l:x,start:t,direction:S}}function Tt(t,e,n){if(y(t))return t;const{q:r,r:i}=q(t)?N(t):t;return v({q:r,r:i,isPointy:e,offset:n})}const xt={AA:{swapWidthHeight:!1,direction:a.E},AB:{swapWidthHeight:!0,direction:a.N},BA:{swapWidthHeight:!0,direction:a.S},BB:{swapWidthHeight:!1,direction:a.W}};function Yt(t,e){return P(Array.from({length:t},()=>P(e)))}var L=(t=>(t.CLOCKWISE="CLOCKWISE",t.COUNTERCLOCKWISE="COUNTERCLOCKWISE",t))(L||{});function St(t){const{center:e,rotation:n=L.CLOCKWISE}=t;return function(i,s){const c=n.toUpperCase(),u=[];let{radius:f}=t,h;Number.isFinite(f)?(h=i(e),h.q+=f):(h=i(t.start??s),f=I(h,e,h));const{q:d,r:E,s:S}=W(h,e);let l=i({q:d,r:E-f,s:S+f});if(c===L.CLOCKWISE)for(let O=0;O<6;O++)for(let M=0;M<f;M++){const{q:Z,r:V}=qt[O];l=i({q:l.q+Z,r:l.r+V}),u.push(l)}else for(let O=5;O>=0;O--)for(let M=0;M<f;M++){const{q:Z,r:V}=qt[O];l=i({q:l.q-Z,r:l.r-V}),u.push(l)}const x=!t.start&&s,Nt=u.findIndex(O=>O.equals(h));return u.slice(Nt+(x?1:0)).concat(u.slice(0,Nt))}}const qt=[{q:1,r:0},{q:0,r:1},{q:-1,r:1},{q:-1,r:0},{q:0,r:-1},{q:1,r:-1}];function Bt({radius:t,start:e,rotation:n}){return function(i,s){const c=i(e??s);return k(C({start:e,direction:a.N,length:t}),St({center:c,rotation:n}))(i,s)}}class T{static fromIterable(e){const n=e[Symbol.iterator]().next().value;if(!n)throw new Error(`Can't create grid from empty iterable: ${JSON.stringify(e)}`);return new T(Object.getPrototypeOf(n),e)}static fromJSON({hexSettings:e,coordinates:n}){const r=lt(e);return new T(r,n.map(i=>Q(r,i)))}[Symbol.toStringTag]="Grid";get size(){return this.#t.size}[Symbol.iterator](){return this.#t.values()}hexPrototype;#t=new Map;constructor(e,n=[]){if(e instanceof T){this.hexPrototype=e.hexPrototype,this.setHexes(e);return}this.hexPrototype=e,this.setHexes(this.#n(n))}createHex(e){return Q(this.hexPrototype,e)}getHex(e){const n=this.createHex(e);return this.#t.get(n.toString())}hasHex(e){return this.#t.has(e.toString())}setHexes(e){for(const n of e)this.#e(n);return this}filter(e){const n=new T(this.hexPrototype);for(const r of this)e(r)&&n.#e(r);return n}map(e){const n=new T(this.hexPrototype);for(const r of this)n.#e(e(r));return n}traverse(e,{bail:n=!1}={}){const r=new T(this.hexPrototype);for(const i of this.#n(e)){const s=this.getHex(i);if(s)r.#e(s);else if(!n)return r}return r}forEach(e){for(const n of this)e(n);return this}reduce(e,n){if(n===void 0){let i,s,c;for(const u of this)s=c,c=u,s&&(i=e(s,c));return i}let r=n;for(const i of this)r=e(r,i);return r}toArray(){return Array.from(this)}toJSON(){return{hexSettings:this.hexPrototype,coordinates:this.toArray()}}toString(){return`Grid(${this.size})`}pointToHex(e,{allowOutside:n=!0}={}){const r=bt(this.hexPrototype,e);if(n)return this.createHex(r);const i=this.getHex(r);return i||null}distance(e,n,{allowOutside:r=!0}={}){if(r)return I(this.hexPrototype,e,n);const i=this.getHex(e),s=this.getHex(n);return!i||!s?null:I(this.hexPrototype,i,s)}neighborOf(e,n,{allowOutside:r=!0}={}){if(r)return w(e,n);const i=this.getHex(e),s=w(e,n);return!i||!s?null:w(e,n)}#e(e){this.#t.set(e.toString(),e)}#n(e){return this.#r(e)?this.#i(e):Array.isArray(e)&&this.#r(e[0])?this.#i(P(e)):e}#r(e){return X(e)}#i(e){return e(this.createHex.bind(this))}}o.CardinalCompassDirection=Y,o.Compass=m,o.CompassDirection=a,o.Grid=T,o.OrdinalCompassDirection=B,o.Orientation=g,o.Rotation=L,o.assertCubeCoordinates=W,o.center=D,o.cloneHex=p,o.concat=P,o.corners=it,o.cornersFlat=rt,o.cornersPointy=nt,o.createHex=Q,o.createHexPrototype=lt,o.defaultHexSettings=at,o.distance=I,o.equals=st,o.fromCoordinates=It,o.height=tt,o.heightFlat=z,o.heightPointy=J,o.hexToOffset=v,o.hexToOffsetFlat=ct,o.hexToOffsetPointy=ot,o.hexToPoint=A,o.isAxial=mt,o.isFlat=ut,o.isHex=R,o.isOffset=y,o.isPoint=G,o.isPointy=ft,o.isTuple=q,o.line=C,o.move=jt,o.neighborOf=w,o.neighborOfFlat=Ot,o.neighborOfPointy=dt,o.offsetFromZero=F,o.offsetToCube=H,o.offsetToCubeFlat=_,o.offsetToCubePointy=$,o.pointToCube=bt,o.rectangle=Ct,o.repeat=Yt,o.repeatWith=k,o.ring=St,o.round=j,o.spiral=Bt,o.toString=ht,o.tupleToCube=N,o.width=et,o.widthFlat=U,o.widthPointy=K,Object.defineProperties(o,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});