I've written a loop in JavaScript that will render rings of concentric hexagons around a central hexagon on the HTML canvas.
I start with the innermost ring, draw the hex at 3 o'clock, then continue around in a circle until all hexes are rendered. Then I move on to the next ring and repeat.
When you draw hexagons this way (instead of tiling them using solely x and y offsets) any hexagon that is not divisible by 60 is not the same distance to the center hex as those that are divisible by 60 (because these hexes comprise the flat edges, not the vertices, of the larger hex).
The problem I'm having is these hexes (those not divisible by 60 degrees) are rendering in a slightly off position. I'm not sure if it is a floating point math problem, the problem with my algorithm, the problem with my rusty trig, or just plain stupidity. I'm betting 3 out of 4. To cut to the chase, look at the line if (alpha % 60 !== 0)
in the code below.
As a point of information, I decided to draw the grid this way because I needed an easy way to map the coordinates of each hex into a data structure, with each hex being identified by its ring # and ID# within that ring. If there is a better way to do it I'm all ears, however, I'd still like to know why my rendering is off.
Here is my very amateur code, so bear with me.
<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false);
function eventWindowLoaded() {
canvasApp();
}
function canvasApp(){
var xOrigin;
var yOrigin;
var scaleFactor = 30;
var theCanvas = document.getElementById("canvas");
var context;
if (canvas.getContext) {
context = theCanvas.getContext("2d");
window.addEventListener('resize', resizeCanvas, false);
window.addEventListener('orientationchange', resizeCanvas, false);
resizeCanvas();
}
drawScreen();
function resizeCanvas() {
var imgData = context.getImageData(0,0, theCanvas.width, theCanvas.height);
theCanvas.width = window.innerWidth;
theCanvas.height = window.innerHeight;
context.putImageData(imgData,0,0);
xOrigin = theCanvas.width / 2;
yOrigin = theCanvas.height / 2;
}
function drawScreen() {
var rings = 3;
var alpha = 0;
var modifier = 1;
context.clearRect(0, 0, theCanvas.width, theCanvas.height);
drawHex(0,0);
for (var i = 1; i<=rings; i++) {
for (var j = 1; j<=i*6; j++) {
if (alpha % 60 !== 0) {
var h = modifier * scaleFactor / Math.cos(dtr(360 / (6 * i)));
drawHex(h * (Math.cos(dtr(alpha))), h * Math.sin(dtr(alpha)));
}
else {
drawHex(2 * scaleFactor * i * Math.cos(dtr(alpha)), 2 * scaleFactor * i * Math.sin(dtr(alpha)));
}
alpha += 360 / (i*6);
}
modifier+=2;
}
}
function drawHex(xOff, yOff) {
context.fillStyle = '#aaaaaa';
context.strokeStyle = 'black';
context.lineWidth = 2;
context.lineCap = 'square';
context.beginPath();
context.moveTo(xOrigin+xOff-scaleFactor,yOrigin+yOff-Math.tan(dtr(30))*scaleFactor);
context.lineTo(xOrigin+xOff,yOrigin+yOff-scaleFactor/Math.cos(dtr(30)));
context.lineTo(xOrigin+xOff+scaleFactor,yOrigin+yOff-Math.tan(dtr(30))*scaleFactor);
context.lineTo(xOrigin+xOff+scaleFactor,yOrigin+yOff+Math.tan(dtr(30))*scaleFactor);
context.lineTo(xOrigin+xOff,yOrigin+yOff+scaleFactor/Math.cos(dtr(30)));
context.lineTo(xOrigin+xOff-scaleFactor,yOrigin+yOff+Math.tan(dtr(30))*scaleFactor);
context.closePath();
context.stroke();
}
function dtr(ang) {
return ang * Math.PI / 180;
}
function rtd(ang) {
return ang * 180 / Math.PI;
}
}
</script>
Man it took me longer than I'd like to admit to find the pattern for the hexagonal circles. I'm too tired right now to explain since I think I'll need to make some assisting illustrations in order to explain it.
In short, each "circle" of hexagonal shapes is itself hexagonal. The number of hexagonal shapes along one edge is the same as the number of the steps from the center.