【问题标题】:Rendering concentric hexes on Canvas在画布上渲染同心六边形
【发布时间】:2015-06-14 17:49:55
【问题描述】:

我用 JavaScript 编写了一个循环,它将围绕 HTML 画布上的中心六边形渲染同心六边形环。

我从最里面的环开始,在 3 点钟位置画出六角形,然后继续绕一圈,直到所有的六角形都被渲染出来。然后我转到下一个环并重复。

当您以这种方式绘制六边形时(而不是仅使用 x 和 y 偏移平铺它们),任何不能被 60 整除的六边形到中心六边形的距离与可被 60 整除的六边形的距离不同(因为这些六边形包括较大的十六进制的平边,而不是顶点)。

我遇到的问题是这些六边形(不能被 60 度整除的)渲染在稍微偏离的位置。我不确定这是一个浮点数学问题,我的算法问题,我生锈的三角函数的问题,或者只是愚蠢的问题。我赌 4 分中的 3 分。要切入正题,请查看下面代码中的 if (alpha % 60 !== 0) 行。

作为一个信息点,我决定以这种方式绘制网格,因为我需要一种简单的方法将每个十六进制的坐标映射到一个数据结构中,每个十六进制由它的环 # 和该环内的 ID# 标识.如果有更好的方法,我会全力以赴,但是,我仍然想知道为什么我的渲染关闭了。

这是我非常业余的代码,请耐心等待。

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

【问题讨论】:

  • 这是hexagonal grids上的一个很好的资源
  • 我要指出我已经阅读了该资源,但它并没有解决我的具体问题。我在编写算法后阅读了这篇文章,它确实提供了一些关于我可以解决这个问题的另一种方法的见解。我很固执,我想弄清楚为什么我的计算会有轻微的扰动。谢谢!

标签: javascript html5-canvas trigonometry 2d-games


【解决方案1】:

伙计,我花了比我想承认的时间更长的时间来找到六边形圆圈的图案。我现在太累了,无法解释,因为我想我需要做一些辅助插图才能解释它。

简而言之,六角形的每个“圆”本身就是六角形。沿一条边的六边形数与距中心的步数相同。

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;
}
&lt;canvas id="canvas"&gt;&lt;/canvas&gt;

【讨论】:

  • 为出色的工作点赞!这是代码的彩色版本,说明了其正确性:jsfiddle.net/m1erickson/76vfeoyc
  • 哇!多谢你们。这就是我喜欢 Stackoverflow 的原因!
【解决方案2】:

我认为您正在尝试将径向坐标用于不是圆形的物体。 正如您正确指出的那样,顶点六边形的(中心)确实布置在一个圆圈中,您可以使用基本的径向定位来布置它们。但是,非顶点的不是放在那个圆的弧上,而是放在它的弦上(连接两个顶点六边形的线)。因此,尝试对这些六边形使用常量 h(半径)值的算法将无法正确布置它们。

您可以尝试从顶点六边形插入非顶点六边形:Kth(在N之外)非顶点六边形H在顶点六边形VH1VH2之间的位置是:

Pos(H) = Pos(VH1) + (K / (N + 1)) * (Pos(VH2)-Pos(VH1))

例如在每条边有 4 个六边形(即 2 个非顶点六边形)的环中,查看 3 点钟和 5 点钟之间的六边形线:3 点钟在这条线上的 0%,之后的一个是 1/3 的路,下一个是 2/3 的路,5 点钟是 100% 的路。或者,您可以将沿着该线的每个六边形视为在两个顶点之间的方向上“前进”一个预定向量,直到到达线的末端。

所以基本上你的算法可以通过 6 个主要顶点六边形,每次将六边形从当前顶点六边形插入到下一个顶点。因此,您可能应该有三个嵌套循环:一个用于环,一个用于环上的角度(总是六步),以及一个用于沿给定角度插入六边形(根据环号的步数)。

【讨论】:

  • 这是正确的,这就是我的代码所做的。我有两个内部循环(一个用于alpha % 60 ==== 0,另一个用于alpha % 60 !== 0。我会看看你的偏移代码并告诉你。
  • 您的代码用一个循环替换了内部的两个循环,这可能很好,但随后尝试通过计算它们的距离来根据它们的角度放置非顶点六边形,而不是走(六边形)任意两个顶点六边形之间的线。本质上,对于非顶点十六进制,您的位置必须包括两个术语,一个“起始位置”加上一些“偏移”。另请参阅另一个答案中的工作示例。
猜你喜欢
  • 2013-12-10
  • 1970-01-01
  • 2013-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多