UNPKG

@dominicstop/utils

Version:

Yet another event emitter written in typescript.

280 lines (227 loc) 7.87 kB
import { ArrayHelpers } from "../helpers"; import { BoxedHexagon, HexagonType } from "./BoxedHexagon"; import { Point } from "./Point"; import { Rect } from "./Rect"; const DEBUG_SHOULD_LOG = false; export type HexagonGridConfig = { centerHexagon: BoxedHexagon; hexagonType: HexagonType; maxRingCount: number; }; export type HexagonAxialCoordinate = { ringRowIndex: number; ringColumnIndex: number; }; export class HexagonGrid { centerHexagon: BoxedHexagon; hexagonType: HexagonType; maxRingCount: number; constructor(args: HexagonGridConfig) { this.hexagonType = args.hexagonType; this.maxRingCount = args.maxRingCount; this.centerHexagon = BoxedHexagon.initFromPresetHexagon({ mode: 'relativeToOrigin', origin: args.centerHexagon.origin, circumRadius: args.centerHexagon.circumRadius, hexagonType: args.hexagonType, }); }; convertAxialCoordinatesToCartesian(args: { hexagonCoords: HexagonAxialCoordinate; hexagonType: HexagonType; }): Point { const { hexagonCoords, hexagonType } = args; const { ringRowIndex: q, ringColumnIndex: r } = hexagonCoords; const R = this.centerHexagon.circumRadius; let offsetX: number; let offsetY: number; switch(hexagonType) { case 'pointyTopped': offsetX = R * 3/2 * q; offsetY = R * Math.sqrt(3) * (q + r / 2); break; case 'flatTopped': offsetX = R * Math.sqrt(3) * (q + r / 2); offsetY = R * 3/2 * r; break; default: throw new Error('Invalid hexagonType'); }; return new Point({ x: this.centerHexagon.origin.x + offsetX, y: this.centerHexagon.origin.y + offsetY }); }; // MARK: - Static Methods // ---------------------- static computeHexagonsForFlowerArrangment(args: { circumRadius: number; centerPoint: Point; extraPositionOffset?: number; startEdgeOffset?: number; }): { centerHexagon: BoxedHexagon; outerHexagonRing: Array<BoxedHexagon>; boundingBox: Rect; } { const startEdgeOffset = args.startEdgeOffset ?? 0; const centerHexagon = new BoxedHexagon({ center: args.centerPoint, circumRadius: args.circumRadius, mode: 'relativeToCenter', }); const centerHexagonEdgesRaw = centerHexagon.edgeLines; const centerHexagonEdges = ArrayHelpers.copyArrayWithCyclicOffset( /* sourceArray: */ centerHexagonEdgesRaw, /* offset: */ startEdgeOffset ); const outerHexagonRing = centerHexagonEdges.map((edgeLine) => ( centerHexagon.computeTiledHexagonAlongsideEdge({ edgeLine, extraPositionOffset: args.extraPositionOffset, }) )); const hexagons = [centerHexagon, ...outerHexagonRing]; const allPoints: Array<Point> = hexagons.reduce( (acc, curr) => { acc.push(...curr.cornerPointsAsArray); return acc; }, [...centerHexagon.cornerPointsAsArray] ); const boundingBox = Point.getBoundingBoxForPoints(allPoints); DEBUG_SHOULD_LOG && console.log({ 'HexagonGrid.computeHexagonsForFlowerArrangment.centerHexagonEdgesRaw.length': centerHexagonEdgesRaw.length, 'HexagonGrid.computeHexagonsForFlowerArrangment.centerHexagonEdges.length': centerHexagonEdges.length, 'HexagonGrid.computeHexagonsForFlowerArrangment.allPoints.length': allPoints.length, 'HexagonGrid.computeHexagonsForFlowerArrangment.outerHexagonRing.length': outerHexagonRing.length, 'HexagonGrid.computeHexagonsForFlowerArrangment.hexagons.length': hexagons.length, }); return { centerHexagon, outerHexagonRing, boundingBox, }; }; static computeHexagonsForTriangleArrangement(args: { circumRadius: number; centerPoint: Point; extraPositionOffset?: number; startEdgeOffset?: number; }): { hexagons: Array<BoxedHexagon>; boundingBox: Rect; } { const startEdgeOffset = args.startEdgeOffset ?? 0; const firstHexagon = new BoxedHexagon({ center: args.centerPoint, circumRadius: args.circumRadius, mode: 'relativeToCenter', }); const edgeLinesRaw = firstHexagon.edgeLines; const edgeLines = ArrayHelpers.copyArrayWithCyclicOffset( /* sourceArray: */ edgeLinesRaw, /* offset: */ startEdgeOffset ); const secondHexagon = firstHexagon.computeTiledHexagonAlongsideEdge({ edgeLine: edgeLines[0]!, extraPositionOffset: args.extraPositionOffset, }); const thirdHexagon = firstHexagon.computeTiledHexagonAlongsideEdge({ edgeLine: edgeLines[1]!, extraPositionOffset: args.extraPositionOffset, }); const hexagons = [ firstHexagon, secondHexagon, thirdHexagon, ]; const allPoints: Array<Point> = [ ...firstHexagon.cornerPointsAsArray, ...secondHexagon.cornerPointsAsArray, ...thirdHexagon.cornerPointsAsArray, ]; const boundingBox = Point.getBoundingBoxForPoints(allPoints); BoxedHexagon.recenterHexagonsRelativeToPoint({ hexagons, centerPoint: args.centerPoint, }); return { hexagons, boundingBox }; }; static computeHexagonsForTriangleAndFlowerArrangement(args: { hexagonCount: number; circumRadius: number; centerPoint: Point; extraPositionOffset?: number; }): { hexagons: Array<BoxedHexagon>; boundingBox: Rect; }{ if(args.hexagonCount == 1) { const hexagon = new BoxedHexagon({ mode: 'relativeToCenter', center: args.centerPoint, circumRadius: args.circumRadius, }); return ({ hexagons: [hexagon], boundingBox: hexagon.boundingBox, }); }; if(args.hexagonCount <= 3){ const startEdgeOffset = args.hexagonCount == 2 ? 1 : 0; const shouldReCenter = args.hexagonCount == 2; const hexagonsToRemoveCount = 3 - args.hexagonCount; const hexagonGroup = this.computeHexagonsForTriangleArrangement({ ...args, startEdgeOffset, }); if(hexagonsToRemoveCount > 0){ hexagonGroup.hexagons.splice(-hexagonsToRemoveCount); }; if(shouldReCenter){ BoxedHexagon.recenterHexagonsRelativeToPoint({ hexagons: hexagonGroup.hexagons, centerPoint: args.centerPoint, }); }; return ({ hexagons: hexagonGroup.hexagons, boundingBox: hexagonGroup.boundingBox, }); } else if(args.hexagonCount <= 7){ const startEdgeOffset = args.hexagonCount == 5 || args.hexagonCount == 6 ? 1 : 0; const shouldReCenter = args.hexagonCount == 4; const hexagonsToRemoveCount = 7 - args.hexagonCount; const hexagonGroup = this.computeHexagonsForFlowerArrangment({ ...args, startEdgeOffset, }); const hexagons = [ hexagonGroup.centerHexagon, ...hexagonGroup.outerHexagonRing ]; if(hexagonsToRemoveCount > 0){ hexagons.splice(-hexagonsToRemoveCount); }; if(shouldReCenter){ BoxedHexagon.recenterHexagonsRelativeToPoint({ hexagons, centerPoint: args.centerPoint, }); }; DEBUG_SHOULD_LOG && console.log({ 'HexagonGrid.computeHexagonsForTriangleAndFlowerArrangement.hexagonsToRemoveCount': hexagonsToRemoveCount, 'HexagonGrid.computeHexagonsForTriangleAndFlowerArrangement.shouldReCenter': shouldReCenter, 'HexagonGrid.computeHexagonsForTriangleAndFlowerArrangement.hexagonGroup.outerHexagonRing.length': hexagonGroup.outerHexagonRing.length, 'HexagonGrid.computeHexagonsForTriangleAndFlowerArrangement.hexagons.length': hexagons.length, }); return { hexagons, boundingBox: hexagonGroup.boundingBox }; }; throw new Error("Hexagon count > 6 not supported"); }; };