import L from 'leaflet'; import type { AxialCoord } from '../../core/types.js'; import { axialToPixel, computeHexGeometry } from '../../core/coords.js'; import { getHexesInBounds, type PixelBounds } from '../../core/hex-grid.js'; import type { HexMap } from '../../core/hex-map.js'; import { renderHex } from '../svg/renderer.js'; export interface HexLayerOptions { hexSize: number; hexMap: HexMap; origin?: { x: number; y: number }; selectedHex?: AxialCoord | null; showGrid?: boolean; opacity?: number; } /** * Leaflet GridLayer that renders the hex overlay using Canvas. * Uses L.GridLayer.extend() for compatibility with Leaflet's class system. */ export function createHexLayer(options: HexLayerOptions): L.GridLayer & { setSelectedHex: (coord: AxialCoord | null) => void; setShowGrid: (show: boolean) => void; setHexOpacity: (opacity: number) => void; } { let hexSize = options.hexSize; let hexMap = options.hexMap; const origin = options.origin ?? { x: 0, y: 0 }; let selectedHex: AxialCoord | null = options.selectedHex ?? null; let showGrid = options.showGrid ?? true; let hexOpacity = options.opacity ?? 0.7; const HexLayer = L.GridLayer.extend({ createTile(coords: L.Coords): HTMLCanvasElement { const canvas = document.createElement('canvas'); const tileSize = this.getTileSize(); canvas.width = tileSize.x; canvas.height = tileSize.y; const ctx = canvas.getContext('2d')!; const nwPoint = coords.scaleBy(tileSize); const sePoint = nwPoint.add(tileSize); const zoom = coords.z; const maxZoom = this._map?.getMaxZoom() ?? 6; const scale = Math.pow(2, maxZoom - zoom); const bounds: PixelBounds = { minX: nwPoint.x * scale, minY: nwPoint.y * scale, maxX: sePoint.x * scale, maxY: sePoint.y * scale, }; const hexCoords = getHexesInBounds(bounds, hexSize, origin); for (const coord of hexCoords) { const terrain = hexMap.getTerrain(coord); const pixelCenter = axialToPixel(coord, hexSize, origin); const localX = (pixelCenter.x - bounds.minX) / scale; const localY = (pixelCenter.y - bounds.minY) / scale; const localSize = hexSize / scale; const geom = computeHexGeometry(localX, localY, localSize); const isSelected = selectedHex !== null && selectedHex.q === coord.q && selectedHex.r === coord.r; renderHex(ctx, geom, terrain, { opacity: hexOpacity, showGrid, selected: isSelected, }); } return canvas; }, }); const layer = new HexLayer() as L.GridLayer & { setSelectedHex: (coord: AxialCoord | null) => void; setShowGrid: (show: boolean) => void; setHexOpacity: (opacity: number) => void; }; layer.setSelectedHex = (coord: AxialCoord | null) => { selectedHex = coord; layer.redraw(); }; layer.setShowGrid = (show: boolean) => { showGrid = show; layer.redraw(); }; layer.setHexOpacity = (opacity: number) => { hexOpacity = opacity; layer.redraw(); }; return layer; }