构造一个嵌套正方形的圆

时间:2014-12-22 17:06:07

标签: javascript html5 math canvas

我想构建一个嵌套正方形的圆圈,如下所示:

circleOfSquares

目前,我正在用JavaScript / HTML5画布编程。这是我的代码:

<html>
<head>
<title>Circle of squares</title>
<script type="text/javascript">
var r = 150, u = 20, nests = 200;       //radius in pixels, circumference in squares, nests in squares
var w = r;                              //any number != 0

function getNewW()
{
    if(u < 3)
        alert("Error: u < 3 (" + u + " < 3)!");

    var tangents = new Array(new Array(0, w/2), new Array(Math.sin((1/u*360)*(Math.PI/180))*(w/2), -Math.cos((1/u*360)*(Math.PI/180))*(w/2)));
    var sta = new Array(new Array(r, 0), new Array(Math.cos((1/u*360)*(Math.PI/180))*r, Math.sin((1/u*360)*(Math.PI/180))*r));
    var end = new Array(new Array(sta[0][0]+tangents[0][0], sta[0][1]+tangents[0][1]), new Array(sta[1][0]+tangents[1][0], sta[1][1]+tangents[1][1]));
    var pts = new Array(sta[0], end[0], sta[1], end[1]);
    var intersect = new Array(((pts[0][0]*pts[1][1]-pts[0][1]*pts[1][0])*(pts[2][0]-pts[3][0]) - (pts[0][0]-pts[1][0])*(pts[2][0]*pts[3][1]-pts[2][1]*pts[3][0])) / ((pts[0][0]-pts[1][0])*(pts[2][1]-pts[3][1]) - (pts[0][1]-pts[1][1])*(pts[2][0]-pts[3][0])), ((pts[0][0]*pts[1][1]-pts[0][1]*pts[1][0])*(pts[2][1]-pts[3][1]) - (pts[0][1]-pts[1][1])*(pts[2][0]*pts[3][1]-pts[2][1]*pts[3][0])) / ((pts[0][0]-pts[1][0])*(pts[2][1]-pts[3][1]) - (pts[0][1]-pts[1][1])*(pts[2][0]-pts[3][0])));        //Formula from http://en.wikipedia.org/wiki/Line%E2%80%93line_intersection


    //distTo0 should be equal to distTo1
    var distTo0 = Math.sqrt(Math.pow(sta[0][0]-intersect[0], 2) + Math.pow(sta[0][1]-intersect[1], 2));
    var distTo1 = Math.sqrt(Math.pow(sta[1][0]-intersect[0], 2) + Math.pow(sta[1][1]-intersect[1], 2));
    if(Math.round(distTo0*100)/100 != Math.round(distTo1*100)/100)
        alert("Error: distTo0 != distTo1 (" + distTo0 + " != " + distTo1 + ")!");

    return distTo0*2;
}

function start()
{
    var canvas = document.getElementById("outputCanvas");

    canvas.setAttribute("width",  600);
    canvas.setAttribute("height", 600);

    if(canvas.getContext)
    {
        var ctx = canvas.getContext("2d");
        ctx.translate(300, 300);

        w = getNewW();
        for(var i=0; i<u; i++)
        {
            ctx.rotate((1/u*360)*(Math.PI/180));
            ctx.fillRect(r, -w/2, w, w);
        }

        for(var j=1; j<nests; j++)
        {
            var oldr = r;
            var temp1 = 1/(10*j+1);
            while(r+w > oldr)        //This is the while-loop that makes the program slow
            {
                r -= temp1;
                w = getNewW();
            }
            if(r < 0)       //When the radius gets smaller than 0, the center is reached -> no new squares have to be drawn
                break;

            var temp2 = (1/u*360)*(Math.PI/180);
            for(var i=0; i<u; i++)
            {
                ctx.rotate(temp2);
                ctx.fillRect(r, -w/2, w, w);
            }
        }
    }
}
</script>
</head>
<body id="main" onload="start()">
<canvas style="border:1px #000000 solid;" width="0" height="0" id="outputCanvas">Canvas not supported...</canvas>
<div id="info"> </div>
</body>
</html>

但是由于我没有解决方案的公式,我使用while循环来越来越接近解决方案(直到它因浮动不准确而达到零),这就是为什么它很慢。 那么,公式可用于计算(思考)圈内的下一个方块的宽度,如有必要,可以在其他地方优化代码?

2 个答案:

答案 0 :(得分:6)

在圆的中心附近,正方形足够小,你可以用弧长来近似边长( w ) - 也就是说,一个 u多长时间如果你把它画成一个实际的圆圈,那么它就是内圈。这只是弧度(2 π / u )的角度乘以穿过正方形内角的圆的半径。由于代码中r不同,我会在一个时刻调用特定的半径值 r2 ;这使得弧长为:

w_approx = (2 * Math.PI / u) * r2

但是对于图片中的大多数方块,它与 w 的实际值之间的差异太大了;如果你使用它作为边长,你将得到重叠的正方形。幸运的是,我们也可以直接计算 w 的真实值;它只需要一点三角法。

如果从正方形的内角到圆的中心绘制线条,那么这两条线加上正方形的内侧形成一个三角形。我们知道我们刚刚画出的这两条线有多长;它们等于内半径。我们不知道第三方是多久 - 这是我们正在寻找的 w 的价值 - 但我们确实知道与它相反的角度。这三条信息足以计算 w

这是一张显示我正在谈论的内容的图片:

enter image description here

圆圈中心的角度,在图片中标记为α(alpha),只是整个圆圈的一个 u ,即2 π / u 弧度(或360 / u 度,但trig函数都需要弧度):

alpha = 2 * Math.PI / u

三角形的另外两个角度是相等的(它们必须是,因为它们是相等长度的相对边),因此它们都被标记为β。由于三角形的三个角总是加起来为π弧度(或180º),我们可以计算β;它等于(π - α)/ 2弧度:

beta = (Math.PI - alpha)/2

通过Law of Sines,如果你将任意三角形任何一边的长度除以与该边相反的角度的正弦,无论你选择哪三边,结果都是一样的。这告诉我们 w / sin α必须与 r2 / sin β。解决 w 的等式得到了我们:

w = r2 * Math.sin(alpha) / Math.sin(beta)

答案 1 :(得分:2)

解决方案非常简单:
参数是什么?
•圆的起始半径 •圆的结束半径 •每圈的平方数。

然后你需要计算什么?
•要在两个圆圈之间执行的旋转:简单,只需一个完整的旋转除以每个圆的平方数:

var angle = 2 * Math.PI / squaresPerCircle;

•给定当前半径的每个方块的大小。也简单:计算当前圆的周长(2 * PI *半径),然后一个正方形的大小约为这个圆周除以平方数(因为你想填充圆):

squareSize = 2 * Math.PI * currentRadius / squaresPerCircle;
即使每个圆圈有10个方格,

近似也足够好。

(    否则,真正的&#39;当你有半径和角度时获得高度的方法是:

squareSize = 2 * currentRadius * Math.tan(angle/2);

enter image description here

摘录:

&#13;
&#13;
// parameters
var startRadius = 5;
var maxRadius = 200;
var squaresPerCircle = 20;

function start() {
  // boilerplate
  var canvas = document.getElementById("outputCanvas");
  var ctx = canvas.getContext("2d");
  canvas.width = 600;
  canvas.height = 600;
  //
  ctx.save();
  ctx.translate(canvas.width / 2, canvas.height / 2);

  var currentRadius = startRadius;
  var angle = 2 * Math.PI / squaresPerCircle;
  // loop on each ring
  do {
    squareSize = 2 * Math.PI * currentRadius / squaresPerCircle;
    // squareSize = 2 * currentRadius * Math.tan(angle/2);      
    ctx.save();
    // loop on every square of a single ring
    for (var cIndex = 0; cIndex < squaresPerCircle; cIndex++) {
      ctx.fillRect(currentRadius, -squareSize / 2,
        squareSize, squareSize);
      ctx.rotate(angle);
    };
    ctx.restore();
    currentRadius += squareSize;
  } while (currentRadius < maxRadius);

  ctx.restore();
}

onload = start;
&#13;
<canvas style="border:1px #000000 solid;" width="0" height="0" id="outputCanvas">Canvas not supported...</canvas>
&#13;
&#13;
&#13;