import type { AxialCoord, PixelCoord } from './types.js'; import { axialToPixel, pixelToAxial, hexWidth, hexHeight } from './coords.js'; /** Rectangular pixel bounds */ export interface PixelBounds { minX: number; minY: number; maxX: number; maxY: number; } /** * Iterate all hex coordinates that overlap a rectangular pixel region. * Returns axial coords for every hex whose center falls within * the bounds (with one hex margin to avoid edge clipping). */ export function getHexesInBounds( bounds: PixelBounds, size: number, origin: PixelCoord = { x: 0, y: 0 }, ): AxialCoord[] { const w = hexWidth(size); const h = hexHeight(size); const colStep = w * 3 / 4; // horizontal distance between hex centers const rowStep = h; // vertical distance between hex centers // Expand bounds by one hex to catch partial overlaps const expandedBounds: PixelBounds = { minX: bounds.minX - w, minY: bounds.minY - h, maxX: bounds.maxX + w, maxY: bounds.maxY + h, }; // Find the approximate q range const qMin = Math.floor((expandedBounds.minX - origin.x) / colStep) - 1; const qMax = Math.ceil((expandedBounds.maxX - origin.x) / colStep) + 1; const result: AxialCoord[] = []; for (let q = qMin; q <= qMax; q++) { // For this q column, find the r range const colCenterX = origin.x + size * (3 / 2) * q; const colOffsetY = origin.y + size * (Math.sqrt(3) / 2) * q; const rMin = Math.floor((expandedBounds.minY - colOffsetY) / rowStep) - 1; const rMax = Math.ceil((expandedBounds.maxY - colOffsetY) / rowStep) + 1; for (let r = rMin; r <= rMax; r++) { const pixel = axialToPixel({ q, r }, size, origin); // Check if hex center is within the expanded bounds if ( pixel.x >= expandedBounds.minX && pixel.x <= expandedBounds.maxX && pixel.y >= expandedBounds.minY && pixel.y <= expandedBounds.maxY ) { result.push({ q, r }); } } } return result; } /** * Get the bounding box (in pixels) of the entire hex grid * that covers a given image size. */ export function gridBoundsForImage( imageWidth: number, imageHeight: number, size: number, origin: PixelCoord = { x: 0, y: 0 }, ): { coords: AxialCoord[]; bounds: PixelBounds } { const bounds: PixelBounds = { minX: 0, minY: 0, maxX: imageWidth, maxY: imageHeight, }; const coords = getHexesInBounds(bounds, size, origin); return { coords, bounds }; } /** * Find the hex coordinate at a given pixel position. */ export function hexAtPixel( pixel: PixelCoord, size: number, origin: PixelCoord = { x: 0, y: 0 }, ): AxialCoord { return pixelToAxial(pixel, size, origin); }