Created
September 7, 2022 15:05
-
-
Save jacekkopecky/eaace7ff7a12bf3c9f30b5c5567731bf to your computer and use it in GitHub Desktop.
Hex grid, with code to pick the right hex based on normal coordinates (quick and dirty)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const cos30 = Math.sqrt(3)/2; | |
/** | |
* A grid of hexagons with flat faces top and bottom, and points left and right. | |
*/ | |
export class HexGrid { | |
constructor(height) { | |
this.h = height; | |
this.w = height * cos30; // small width - x distance between centers | |
this.W = height / cos30; // big width - from left point to right point | |
} | |
toHexCoords(xCanvas, yCanvas) { | |
const x = xCanvas / this.w; | |
const y = yCanvas / this.h - x / 2; | |
return {x, y}; | |
} | |
pickHex(xCanvas, yCanvas) { | |
const {x, y} = this.toHexCoords(xCanvas, yCanvas); | |
let retval = null; | |
let minDistSquare = Infinity; | |
const checkCenterDistance = (hex) => { | |
const hexCenter = this.toCanvasCoords(hex.x, hex.y); | |
const distSquare = (hexCenter.x - xCanvas)**2 + (hexCenter.y - yCanvas)**2; | |
if (distSquare < minDistSquare) { | |
minDistSquare = distSquare; | |
retval = hex; | |
} | |
} | |
checkCenterDistance({x: Math.floor(x), y: Math.floor(y)}); | |
checkCenterDistance({x: Math.floor(x), y: Math.ceil(y)}); | |
checkCenterDistance({x: Math.ceil(x), y: Math.floor(y)}); | |
checkCenterDistance({x: Math.ceil(x), y: Math.ceil(y)}); | |
return retval; | |
} | |
toCanvasCoords(xHex, yHex) { | |
return { | |
x: xHex * this.w, | |
y: (yHex+xHex/2) * this.h, | |
} | |
} | |
getHexOutlinePoints(xHex, yHex) { | |
const {x, y} = this.toCanvasCoords(xHex, yHex); | |
return [ | |
{x: x - this.W/4, y: y - this.h/2}, | |
{x: x + this.W/4, y: y - this.h/2}, | |
{x: x + this.W/2, y: y }, | |
{x: x + this.W/4, y: y + this.h/2}, | |
{x: x - this.W/4, y: y + this.h/2}, | |
{x: x - this.W/2, y: y }, | |
]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset='utf-8'> | |
<title>hexes</title> | |
<meta name='viewport' content='width=device-width, initial-scale=1'> | |
<script src='main.js' type="module"></script> | |
<canvas id="c" width="1000" height="1000"> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { HexGrid } from './hex-grid.js'; | |
window.addEventListener('load', init); | |
const grid = new HexGrid(160); | |
function *range(start, end) { | |
if (end > start) { | |
for (let i=start; i<=end; i+=1) { | |
yield i; | |
} | |
} else { | |
for (let i=start; i>=end; i-=1) { | |
yield i; | |
} | |
} | |
} | |
let highlightedHexes = []; | |
function init() { | |
const canvas = document.querySelector("#c"); | |
/** @type {CanvasRenderingContext2D} */ | |
const c = canvas.getContext("2d"); | |
c.fillStyle = "transparent"; | |
const size = Math.ceil(canvas.width / grid.w); | |
for (const x of range(0, size)) { | |
const xoff = Math.round(-x/2); | |
for (const y of range(xoff, size + xoff)) { | |
drawHex(c, x, y); | |
} | |
} | |
highlightedHexes = [{x: Math.round(size/2), y: 0}]; | |
const imageData = c.getImageData(0, 0, canvas.width, canvas.height); | |
draw(); | |
canvas.addEventListener("mousemove", handleMouse); | |
function draw() { | |
c.clearRect(0, 0, c.canvas.width, c.canvas.height); | |
c.putImageData(imageData, 0, 0); | |
c.fillStyle = "yellow"; | |
for (const hex of highlightedHexes) { | |
drawHex(c, hex.x, hex.y); | |
} | |
requestAnimationFrame(draw); | |
} | |
} | |
function handleMouse(e) { | |
const canvas = e.currentTarget; | |
const { left, top } = getClickCoordinates(e, canvas); | |
const {x, y} = grid.toHexCoords(left, top); | |
highlightedHexes = [ grid.pickHex(left, top) ]; | |
} | |
/** | |
* @param {CanvasRenderingContext2D} c | |
*/ | |
function drawHex(c, xHex, yHex, fill=false) { | |
const points = grid.getHexOutlinePoints(xHex, yHex); | |
drawPoly(c, points, fill); | |
} | |
/** | |
* @param {CanvasRenderingContext2D} c | |
*/ | |
function drawPoly(c, points, fill=false) { | |
c.beginPath(); | |
c.moveTo(points.at(-1).x, points.at(-1).y); | |
for (const p of points) { | |
c.lineTo(p.x, p.y); | |
} | |
c.fill(); | |
c.stroke(); | |
} | |
// this function computes the coordinates of a given mouse event in the element | |
// it returns an object like {left: 100, top: 312} | |
function getClickCoordinates(event, element) { | |
const rect = element.getBoundingClientRect(); | |
return { | |
left: event.clientX - rect.left, | |
top: event.clientY - rect.top, | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment