UNPKG

holosphere

Version:

Holonic Geospatial Communication Infrastructure

335 lines (334 loc) 13.7 kB
// Generated code -- CC0 -- No Rights Reserved -- http://www.redblobgames.com/grids/hexagons/ export class Point { constructor(x, y) { this.x = x; this.y = y; } } export class Hex { constructor(q, r, s) { this.q = q; this.r = r; this.s = s; if (Math.round(q + r + s) !== 0) throw "q + r + s must be 0"; } add(b) { return new Hex(this.q + b.q, this.r + b.r, this.s + b.s); } subtract(b) { return new Hex(this.q - b.q, this.r - b.r, this.s - b.s); } scale(k) { return new Hex(this.q * k, this.r * k, this.s * k); } rotateLeft() { return new Hex(-this.s, -this.q, -this.r); } rotateRight() { return new Hex(-this.r, -this.s, -this.q); } static direction(direction) { return Hex.directions[direction]; } neighbor(direction) { return this.add(Hex.direction(direction)); } diagonalNeighbor(direction) { return this.add(Hex.diagonals[direction]); } len() { return (Math.abs(this.q) + Math.abs(this.r) + Math.abs(this.s)) / 2; } distance(b) { return this.subtract(b).len(); } round() { var qi = Math.round(this.q); var ri = Math.round(this.r); var si = Math.round(this.s); var q_diff = Math.abs(qi - this.q); var r_diff = Math.abs(ri - this.r); var s_diff = Math.abs(si - this.s); if (q_diff > r_diff && q_diff > s_diff) { qi = -ri - si; } else if (r_diff > s_diff) { ri = -qi - si; } else { si = -qi - ri; } return new Hex(qi, ri, si); } lerp(b, t) { return new Hex(this.q * (1.0 - t) + b.q * t, this.r * (1.0 - t) + b.r * t, this.s * (1.0 - t) + b.s * t); } linedraw(b) { var N = this.distance(b); var a_nudge = new Hex(this.q + 1e-06, this.r + 1e-06, this.s - 2e-06); var b_nudge = new Hex(b.q + 1e-06, b.r + 1e-06, b.s - 2e-06); var results = []; var step = 1.0 / Math.max(N, 1); for (var i = 0; i <= N; i++) { results.push(a_nudge.lerp(b_nudge, step * i).round()); } return results; } } Hex.directions = [new Hex(1, 0, -1), new Hex(1, -1, 0), new Hex(0, -1, 1), new Hex(-1, 0, 1), new Hex(-1, 1, 0), new Hex(0, 1, -1)]; Hex.diagonals = [new Hex(2, -1, -1), new Hex(1, -2, 1), new Hex(-1, -1, 2), new Hex(-2, 1, 1), new Hex(-1, 2, -1), new Hex(1, 1, -2)]; export class OffsetCoord { constructor(col, row) { this.col = col; this.row = row; } static qoffsetFromCube(offset, h) { var col = h.q; var row = h.r + (h.q + offset * (h.q & 1)) / 2; if (offset !== OffsetCoord.EVEN && offset !== OffsetCoord.ODD) { throw "offset must be EVEN (+1) or ODD (-1)"; } return new OffsetCoord(col, row); } static qoffsetToCube(offset, h) { var q = h.col; var r = h.row - (h.col + offset * (h.col & 1)) / 2; var s = -q - r; if (offset !== OffsetCoord.EVEN && offset !== OffsetCoord.ODD) { throw "offset must be EVEN (+1) or ODD (-1)"; } return new Hex(q, r, s); } static roffsetFromCube(offset, h) { var col = h.q + (h.r + offset * (h.r & 1)) / 2; var row = h.r; if (offset !== OffsetCoord.EVEN && offset !== OffsetCoord.ODD) { throw "offset must be EVEN (+1) or ODD (-1)"; } return new OffsetCoord(col, row); } static roffsetToCube(offset, h) { var q = h.col - (h.row + offset * (h.row & 1)) / 2; var r = h.row; var s = -q - r; if (offset !== OffsetCoord.EVEN && offset !== OffsetCoord.ODD) { throw "offset must be EVEN (+1) or ODD (-1)"; } return new Hex(q, r, s); } } OffsetCoord.EVEN = 1; OffsetCoord.ODD = -1; export class DoubledCoord { constructor(col, row) { this.col = col; this.row = row; } static qdoubledFromCube(h) { var col = h.q; var row = 2 * h.r + h.q; return new DoubledCoord(col, row); } qdoubledToCube() { var q = this.col; var r = (this.row - this.col) / 2; var s = -q - r; return new Hex(q, r, s); } static rdoubledFromCube(h) { var col = 2 * h.q + h.r; var row = h.r; return new DoubledCoord(col, row); } rdoubledToCube() { var q = (this.col - this.row) / 2; var r = this.row; var s = -q - r; return new Hex(q, r, s); } } export class Orientation { constructor(f0, f1, f2, f3, b0, b1, b2, b3, start_angle) { this.f0 = f0; this.f1 = f1; this.f2 = f2; this.f3 = f3; this.b0 = b0; this.b1 = b1; this.b2 = b2; this.b3 = b3; this.start_angle = start_angle; } } export class Layout { constructor(orientation, size, origin) { this.orientation = orientation; this.size = size; this.origin = origin; } hexToPixel(h) { var M = this.orientation; var size = this.size; var origin = this.origin; var x = (M.f0 * h.q + M.f1 * h.r) * size.x; var y = (M.f2 * h.q + M.f3 * h.r) * size.y; return new Point(x + origin.x, y + origin.y); } pixelToHex(p) { var M = this.orientation; var size = this.size; var origin = this.origin; var pt = new Point((p.x - origin.x) / size.x, (p.y - origin.y) / size.y); var q = M.b0 * pt.x + M.b1 * pt.y; var r = M.b2 * pt.x + M.b3 * pt.y; return new Hex(q, r, -q - r); } hexCornerOffset(corner) { var M = this.orientation; var size = this.size; var angle = 2.0 * Math.PI * (M.start_angle - corner) / 6.0; return new Point(size.x * Math.cos(angle), size.y * Math.sin(angle)); } polygonCorners(h) { var corners = []; var center = this.hexToPixel(h); for (var i = 0; i < 6; i++) { var offset = this.hexCornerOffset(i); corners.push(new Point(center.x + offset.x, center.y + offset.y)); } return corners; } } Layout.pointy = new Orientation(Math.sqrt(3.0), Math.sqrt(3.0) / 2.0, 0.0, 3.0 / 2.0, Math.sqrt(3.0) / 3.0, -1.0 / 3.0, 0.0, 2.0 / 3.0, 0.5); Layout.flat = new Orientation(3.0 / 2.0, 0.0, Math.sqrt(3.0) / 2.0, Math.sqrt(3.0), 2.0 / 3.0, 0.0, -1.0 / 3.0, Math.sqrt(3.0) / 3.0, 0.0); class Tests { constructor() { } static equalHex(name, a, b) { if (!(a.q === b.q && a.s === b.s && a.r === b.r)) { complain(name); } } static equalOffsetcoord(name, a, b) { if (!(a.col === b.col && a.row === b.row)) { complain(name); } } static equalDoubledcoord(name, a, b) { if (!(a.col === b.col && a.row === b.row)) { complain(name); } } static equalInt(name, a, b) { if (!(a === b)) { complain(name); } } static equalHexArray(name, a, b) { Tests.equalInt(name, a.length, b.length); for (var i = 0; i < a.length; i++) { Tests.equalHex(name, a[i], b[i]); } } static testHexArithmetic() { Tests.equalHex("hex_add", new Hex(4, -10, 6), new Hex(1, -3, 2).add(new Hex(3, -7, 4))); Tests.equalHex("hex_subtract", new Hex(-2, 4, -2), new Hex(1, -3, 2).subtract(new Hex(3, -7, 4))); } static testHexDirection() { Tests.equalHex("hex_direction", new Hex(0, -1, 1), Hex.direction(2)); } static testHexNeighbor() { Tests.equalHex("hex_neighbor", new Hex(1, -3, 2), new Hex(1, -2, 1).neighbor(2)); } static testHexDiagonal() { Tests.equalHex("hex_diagonal", new Hex(-1, -1, 2), new Hex(1, -2, 1).diagonalNeighbor(3)); } static testHexDistance() { Tests.equalInt("hex_distance", 7, new Hex(3, -7, 4).distance(new Hex(0, 0, 0))); } static testHexRotateRight() { Tests.equalHex("hex_rotate_right", new Hex(1, -3, 2).rotateRight(), new Hex(3, -2, -1)); } static testHexRotateLeft() { Tests.equalHex("hex_rotate_left", new Hex(1, -3, 2).rotateLeft(), new Hex(-2, -1, 3)); } static testHexRound() { var a = new Hex(0.0, 0.0, 0.0); var b = new Hex(1.0, -1.0, 0.0); var c = new Hex(0.0, -1.0, 1.0); Tests.equalHex("hex_round 1", new Hex(5, -10, 5), new Hex(0.0, 0.0, 0.0).lerp(new Hex(10.0, -20.0, 10.0), 0.5).round()); Tests.equalHex("hex_round 2", a.round(), a.lerp(b, 0.499).round()); Tests.equalHex("hex_round 3", b.round(), a.lerp(b, 0.501).round()); Tests.equalHex("hex_round 4", a.round(), new Hex(a.q * 0.4 + b.q * 0.3 + c.q * 0.3, a.r * 0.4 + b.r * 0.3 + c.r * 0.3, a.s * 0.4 + b.s * 0.3 + c.s * 0.3).round()); Tests.equalHex("hex_round 5", c.round(), new Hex(a.q * 0.3 + b.q * 0.3 + c.q * 0.4, a.r * 0.3 + b.r * 0.3 + c.r * 0.4, a.s * 0.3 + b.s * 0.3 + c.s * 0.4).round()); } static testHexLinedraw() { Tests.equalHexArray("hex_linedraw", [new Hex(0, 0, 0), new Hex(0, -1, 1), new Hex(0, -2, 2), new Hex(1, -3, 2), new Hex(1, -4, 3), new Hex(1, -5, 4)], new Hex(0, 0, 0).linedraw(new Hex(1, -5, 4))); } static testLayout() { var h = new Hex(3, 4, -7); var flat = new Layout(Layout.flat, new Point(10.0, 15.0), new Point(35.0, 71.0)); Tests.equalHex("layout", h, flat.pixelToHex(flat.hexToPixel(h)).round()); var pointy = new Layout(Layout.pointy, new Point(10.0, 15.0), new Point(35.0, 71.0)); Tests.equalHex("layout", h, pointy.pixelToHex(pointy.hexToPixel(h)).round()); } static testOffsetRoundtrip() { var a = new Hex(3, 4, -7); var b = new OffsetCoord(1, -3); Tests.equalHex("conversion_roundtrip even-q", a, OffsetCoord.qoffsetToCube(OffsetCoord.EVEN, OffsetCoord.qoffsetFromCube(OffsetCoord.EVEN, a))); Tests.equalOffsetcoord("conversion_roundtrip even-q", b, OffsetCoord.qoffsetFromCube(OffsetCoord.EVEN, OffsetCoord.qoffsetToCube(OffsetCoord.EVEN, b))); Tests.equalHex("conversion_roundtrip odd-q", a, OffsetCoord.qoffsetToCube(OffsetCoord.ODD, OffsetCoord.qoffsetFromCube(OffsetCoord.ODD, a))); Tests.equalOffsetcoord("conversion_roundtrip odd-q", b, OffsetCoord.qoffsetFromCube(OffsetCoord.ODD, OffsetCoord.qoffsetToCube(OffsetCoord.ODD, b))); Tests.equalHex("conversion_roundtrip even-r", a, OffsetCoord.roffsetToCube(OffsetCoord.EVEN, OffsetCoord.roffsetFromCube(OffsetCoord.EVEN, a))); Tests.equalOffsetcoord("conversion_roundtrip even-r", b, OffsetCoord.roffsetFromCube(OffsetCoord.EVEN, OffsetCoord.roffsetToCube(OffsetCoord.EVEN, b))); Tests.equalHex("conversion_roundtrip odd-r", a, OffsetCoord.roffsetToCube(OffsetCoord.ODD, OffsetCoord.roffsetFromCube(OffsetCoord.ODD, a))); Tests.equalOffsetcoord("conversion_roundtrip odd-r", b, OffsetCoord.roffsetFromCube(OffsetCoord.ODD, OffsetCoord.roffsetToCube(OffsetCoord.ODD, b))); } static testOffsetFromCube() { Tests.equalOffsetcoord("offset_from_cube even-q", new OffsetCoord(1, 3), OffsetCoord.qoffsetFromCube(OffsetCoord.EVEN, new Hex(1, 2, -3))); Tests.equalOffsetcoord("offset_from_cube odd-q", new OffsetCoord(1, 2), OffsetCoord.qoffsetFromCube(OffsetCoord.ODD, new Hex(1, 2, -3))); } static testOffsetToCube() { Tests.equalHex("offset_to_cube even-", new Hex(1, 2, -3), OffsetCoord.qoffsetToCube(OffsetCoord.EVEN, new OffsetCoord(1, 3))); Tests.equalHex("offset_to_cube odd-q", new Hex(1, 2, -3), OffsetCoord.qoffsetToCube(OffsetCoord.ODD, new OffsetCoord(1, 2))); } static testDoubledRoundtrip() { var a = new Hex(3, 4, -7); var b = new DoubledCoord(1, -3); Tests.equalHex("conversion_roundtrip doubled-q", a, DoubledCoord.qdoubledFromCube(a).qdoubledToCube()); Tests.equalDoubledcoord("conversion_roundtrip doubled-q", b, DoubledCoord.qdoubledFromCube(b.qdoubledToCube())); Tests.equalHex("conversion_roundtrip doubled-r", a, DoubledCoord.rdoubledFromCube(a).rdoubledToCube()); Tests.equalDoubledcoord("conversion_roundtrip doubled-r", b, DoubledCoord.rdoubledFromCube(b.rdoubledToCube())); } static testDoubledFromCube() { Tests.equalDoubledcoord("doubled_from_cube doubled-q", new DoubledCoord(1, 5), DoubledCoord.qdoubledFromCube(new Hex(1, 2, -3))); Tests.equalDoubledcoord("doubled_from_cube doubled-r", new DoubledCoord(4, 2), DoubledCoord.rdoubledFromCube(new Hex(1, 2, -3))); } static testDoubledToCube() { Tests.equalHex("doubled_to_cube doubled-q", new Hex(1, 2, -3), new DoubledCoord(1, 5).qdoubledToCube()); Tests.equalHex("doubled_to_cube doubled-r", new Hex(1, 2, -3), new DoubledCoord(4, 2).rdoubledToCube()); } static testAll() { Tests.testHexArithmetic(); Tests.testHexDirection(); Tests.testHexNeighbor(); Tests.testHexDiagonal(); Tests.testHexDistance(); Tests.testHexRotateRight(); Tests.testHexRotateLeft(); Tests.testHexRound(); Tests.testHexLinedraw(); Tests.testLayout(); Tests.testOffsetRoundtrip(); Tests.testOffsetFromCube(); Tests.testOffsetToCube(); Tests.testDoubledRoundtrip(); Tests.testDoubledFromCube(); Tests.testDoubledToCube(); } } // Tests function complain(name) { console.log("FAIL", name); } Tests.testAll(); // Export Hex as the default since it's the main class export default Hex;