Estoy tratando de dibujar una cuadrícula hexagonal en forma de panal. Hasta ahora, puedo dibujarlo en forma rectangular, pero no sé cómo convertir mi bucle for para que tenga forma de panal.
Esto es lo que tengo actualmente
<html> <body> <canvas width='1080' height='720' id='hexmap'></canvas> </body> <script> window.addEventListener('DOMContentLoaded', (event) => { var canvas = document.getElementById('hexmap'); var hexHeight, hexRadius, hexRectangleHeight, hexRectangleWidth, hexagonAngle = 0.523598776, // 30 degrees in radians sideLength = 36, boardWidth = 10, boardHeight = 10; hexHeight = Math.sin(hexagonAngle) * sideLength; hexRadius = Math.cos(hexagonAngle) * sideLength; hexRectangleHeight = sideLength + 2 * hexHeight; hexRectangleWidth = 2 * hexRadius; var ctx = canvas.getContext('2d'); ctx.fillStyle = "#000000"; ctx.strokeStyle = "#CCCCCC"; ctx.lineWidth = 1; drawBoard(ctx, boardWidth, boardHeight); function drawBoard(canvasContext, width, height) { var i,j; //this loop generates a rectangular hexagon grid for(i = 0; i < width; ++i) { for(j = 0; j < height; ++j) { drawHexagon( ctx, i * hexRectangleWidth + ((j % 2) * hexRadius), j * (sideLength + hexHeight), false ); } } } function drawHexagon(canvasContext, x, y, fill) { var fill = fill || false; canvasContext.beginPath(); canvasContext.moveTo(x + hexRadius, y); canvasContext.lineTo(x + hexRectangleWidth, y + hexHeight); canvasContext.lineTo(x + hexRectangleWidth, y + hexHeight + sideLength); canvasContext.lineTo(x + hexRadius, y + hexRectangleHeight); canvasContext.lineTo(x, y + sideLength + hexHeight); canvasContext.lineTo(x, y + hexHeight); canvasContext.closePath(); if(fill) { canvasContext.fill(); } else { canvasContext.stroke(); } } }) </script> </html>
Sin embargo, lo que me gustaría lograr es una forma como esta. 
Pude hacerlo usando como 13 bucles for separados, cambiando el hexágono manualmente cada vez, pero no fue muy práctico ni automatizado.
Si establecemos algunas condiciones, podemos derivar un algoritmo con bastante facilidad. Sean las condiciones:
Ahora echemos un vistazo a su forma, que cumple con la condición de que su ancho y alto son 13. Una mirada más cercana revela que tenemos 7 hexágonos en la primera fila, 8 en la segunda, 9 en la tercera y así sucesivamente hasta 13 hexágonos en fila 7. Posteriormente el número de hexágonos disminuye en uno por fila hasta llegar a la última fila 13.
Entonces, el número de hexágonos por fila se puede expresar como:
hexagons = width - (Math.abs(Math.floor(width / 2) - i));
Donde i es la fila.
Asimismo, la posición inicial horizontal de cada fila disminuye medio hexágono de ancho hasta llegar al centro.
xStart = (width - 3) % 4 == 0 ? Math.ceil((width - hexagons) / 2) : Math.floor((width - hexagons) / 2);
Ahora todo lo que queda por hacer es modificar su ciclo for para que comience en xStart hasta xStart+hexagons .
for (j = xStart; j < xStart+hexagons; j++)Aquí hay un ejemplo completo:
var canvas = document.getElementById('hexmap'); var hexHeight, hexRadius, hexRectangleHeight, hexRectangleWidth, hexagonAngle = 0.523598776, // 30 degrees in radians sideLength = 9, boardWidth = 13, boardHeight = 13; hexHeight = Math.sin(hexagonAngle) * sideLength; hexRadius = Math.cos(hexagonAngle) * sideLength; hexRectangleHeight = sideLength + 2 * hexHeight; hexRectangleWidth = 2 * hexRadius; var ctx = canvas.getContext('2d'); ctx.fillStyle = "#000000"; ctx.strokeStyle = "#CCCCCC"; ctx.lineWidth = 1; drawBoard(ctx, boardWidth, boardHeight); function drawBoard(canvasContext, width, height) { var i, j, hexagons, xStart; //this loop generates a rectangular hexagon grid for (i = 0; i < height; i++) { hexagons = width - (Math.abs(Math.floor(width / 2) - i)); xStart = (width - 3) % 4 == 0 ? Math.ceil((width - hexagons) / 2) : Math.floor((width - hexagons) / 2); for (j = xStart; j < xStart + hexagons; j++) { drawHexagon( ctx, j * hexRectangleWidth + ((i % 2) * hexRadius), i * (sideLength + hexHeight), false ); } } } function drawHexagon(canvasContext, x, y, fill) { var fill = fill || false; canvasContext.beginPath(); canvasContext.moveTo(x + hexRadius, y); canvasContext.lineTo(x + hexRectangleWidth, y + hexHeight); canvasContext.lineTo(x + hexRectangleWidth, y + hexHeight + sideLength); canvasContext.lineTo(x + hexRadius, y + hexRectangleHeight); canvasContext.lineTo(x, y + sideLength + hexHeight); canvasContext.lineTo(x, y + hexHeight); canvasContext.closePath(); if (fill) { canvasContext.fill(); } else { canvasContext.stroke(); } } document.getElementById("slider").oninput = (e) => { ctx.clearRect(0, 0, canvas.width, canvas.height) drawBoard(ctx, e.target.value, e.target.value); } <input type="range" min="3" max="27" value="13" step="2" id="slider"><br> <canvas width='400' height='300' id='hexmap'></canvas>