我试图在一个单位球面上大致均匀地排列点。
有人告诉我,虽然这个问题很困难,但斐波那契晶格提供了一个非常好的解决方案。
我已经尝试了几天,按照链接文档中提供的非常简单的方法进行操作,但我无法让它看起来正确。
我正在使用javascript,并且我有一个对象数组e
,每个对象都公开了lat
和lon
参数。以下是我用于在球面上布置点的函数:(暂时假设点的数量始终为奇数)
function arrangeEntries(e)
{
var p = e.length;
var N = (p - 1) / 2;
for (var i = -N; i <= N; i++)
{
e[i + N].lat = Math.asin((2 * i) / (2 * N + 1));
e[i + N].lon = mod(i, 1.618034) * 3.883222;
}
}
使用
function mod(a, b)
{
return a - Math.floor(a / b) * b;
}
与文档中不同,我的“lat”和“lon”是以弧度为单位而非角度。这样做是为了之后使用JavaScript的“Math.sin”和“Math.cos”函数,在X/Y/Z坐标系中绘制它们。这些函数接受弧度而非角度。对于“lat”的第一行很直观。我在文档中省略了180/π的因子,因为我希望结果保持在弧度中。对于“lon”的第二行,我使用黄金比例的索引模数,并且不是乘以360/Phi的因子来以度数给出答案,而是乘以(360/Phi)*(Pi/180)以给出弧度。由于三角函数不关心弧度的范围,因此我不需要确保“lat”和“lon”在(-pi, pi]的范围内。要渲染这些点:
function render(e)
{
var offsetX = Math.floor(canvas.width / 2);
var offsetY = Math.floor(canvas.height / 2);
var r = Math.min(canvas.width, canvas.height) * 0.4;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < e.length; i++)
{
var x = Math.cos(e[i].lat) * Math.sin(e[i].lon);
var y = Math.sin(e[i].lat) * Math.sin(e[i].lon);
var z = Math.cos(e[i].lon);
// Make z go from 0.1 to 1 for scaling:
z += 1;
z /= 2;
z *= 0.9;
z += 0.1;
ctx.beginPath();
ctx.arc(r * x + offsetX, r * y + offsetY, z*5, 0, 2 * Math.PI, false);
ctx.fillStyle = "#990000";
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = "#FF0000";
ctx.stroke();
ctx.closePath();
}
}
为了在未添加旋转效果前营造出深度的错觉,我将点的半径乘以z坐标,并将其线性缩放为[0.1,1.0]的范围。
以下是包含所有代码的JSFiddle链接:https://jsfiddle.net/wexpwngc/。如果你将点数从101增加到像1001这样的更大数值,那么你会发现在极点周围有很多聚集的点,并且某些地方点比较稀疏。
我已经困扰了一段时间了。有人能看出我哪里出错了吗?
2D,3D
是准确的。 - Spektre