Rendering concentric hexes on Canvas

Ray Rackiewicz

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.

https://www.dropbox.com/s/2moe9ysrrw865vh/Hexes.jpg?dl=0

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>
Niddro

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.

var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
c.width = 500;
c.height = 500;


var hexRadius = 20;
var innerCircleRadius = hexRadius/2*Math.sqrt(3);
var TO_RADIANS = Math.PI/180;

function drawHex(x,y) {
    var r = hexRadius;
    ctx.beginPath();
    ctx.moveTo(x,y-r);
    for (var i = 0; i<=6; i++) {
         ctx.lineTo(x+Math.cos((i*60-90)*TO_RADIANS)*r,y+Math.sin((i*60-90)*TO_RADIANS)*r);
    }
    ctx.closePath();
    ctx.stroke();
}

drawHexCircle(250,250,4);

function drawHexCircle(x,y,circles) {
    var rc = innerCircleRadius;
    drawHex(250,250); //center
    
    for (var i = 1; i<=circles; i++) {
        for (var j = 0; j<6; j++) {
            var currentX = x+Math.cos((j*60)*TO_RADIANS)*rc*2*i;
            var currentY = y+Math.sin((j*60)*TO_RADIANS)*rc*2*i;
                drawHex(currentX,currentY);
            for (var k = 1; k<i; k++) {
                var newX = currentX + Math.cos((j*60+120)*TO_RADIANS)*rc*2*k;
                var newY = currentY + Math.sin((j*60+120)*TO_RADIANS)*rc*2*k;
                drawHex(newX,newY);
            }
        }
    }
    
}
canvas {
    border: 1px solid black;
}
<canvas id="canvas"></canvas>

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다debugcn@gmail.com 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

분류에서Dev

Rendering concentric hexes on Canvas

분류에서Dev

Bug rendering text in canvas in Windows

분류에서Dev

Client side rendering and skipping server rendering approach?

분류에서Dev

SVG images not rendering in Safari

분류에서Dev

Conditionally Rendering in React Native

분류에서Dev

Set controller for template rendering

분류에서Dev

JSF Trinidad Rendering not working

분류에서Dev

Ejs - templating data is not rendering

분류에서Dev

HDR rendering on Android

분류에서Dev

Drop location rendering with swing

분류에서Dev

STL rendering with threejs

분류에서Dev

Lists Not Rendering Correctly in IE

분류에서Dev

Twig rendering of a mapped objects

분류에서Dev

OpenGL Layered Rendering

분류에서Dev

Angularjs not rendering $rootscope data

분류에서Dev

node js routing not rendering

분류에서Dev

KnockoutJS component; Template is not rendering

분류에서Dev

Rails redirect_to not rendering

분류에서Dev

Clear canvas context without knowing the canvas dimensions

분류에서Dev

Rendering a wire frame object in Rajawali

분류에서Dev

Why are unicode characters not rendering correctly

분류에서Dev

Ng-repeat not rendering content

분류에서Dev

$.each not rendering JSON in given Order

분류에서Dev

Rendering Partial Views using AJAX

분류에서Dev

Include datatable in highcharts pdf rendering

분류에서Dev

rendering empty partial if its not available

분류에서Dev

rendering issues in safari after transition

분류에서Dev

ng-table not rendering rows

분류에서Dev

Adding (BezierSegment to a) Path to Canvas