我想构建一个嵌套正方形的圆圈,如下所示:
目前,我正在用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循环来越来越接近解决方案(直到它因浮动不准确而达到零),这就是为什么它很慢。 那么,公式可用于计算(思考)圈内的下一个方块的宽度,如有必要,可以在其他地方优化代码?
答案 0 :(得分:6)
在圆的中心附近,正方形足够小,你可以用弧长来近似边长( w ) - 也就是说,一个 u多长时间如果你把它画成一个实际的圆圈,那么它就是内圈。这只是弧度(2 π / u )的角度乘以穿过正方形内角的圆的半径。由于代码中r
不同,我会在一个时刻调用特定的半径值 r2 ;这使得弧长为:
w_approx = (2 * Math.PI / u) * r2
但是对于图片中的大多数方块,它与 w 的实际值之间的差异太大了;如果你使用它作为边长,你将得到重叠的正方形。幸运的是,我们也可以直接计算 w 的真实值;它只需要一点三角法。
如果从正方形的内角到圆的中心绘制线条,那么这两条线加上正方形的内侧形成一个三角形。我们知道我们刚刚画出的这两条线有多长;它们等于内半径。我们不知道第三方是多久 - 这是我们正在寻找的 w 的价值 - 但我们确实知道与它相反的角度。这三条信息足以计算 w 。
这是一张显示我正在谈论的内容的图片:
圆圈中心的角度,在图片中标记为α(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);
)
摘录:
// 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;