UNPKG

@caveworld/honeycomb-grid

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