我想在给定球体上选择随机点。这个页面解释得很好:
http://mathworld.wolfram.com/SpherePointPicking.html (“获得球体上任何小区域的点......”)
但我不完全确定我是否在 JavaScript 中正确实现了它,因为我几乎没有正确测试它的方法:
var u = random();
var v = random();
var angle1 = 2 * Math.PI * u;
var angle2 = Math.pow(Math.cos (2 * v - 1), -1);
X = X0 + (radius * Math.sin(angle1) * Math.cos(angle2));
Y = Y0 + (radius * Math.sin(angle1) * Math.sin(angle1));
Z = Z0 + (radius * Math.cos(angle1));
我特别不确定我是否正确理解了 cos(-1),我将其实现为“-1 次方的余弦”。
立方体算法不会在球体上提供均匀的分布 - 特别是角投影附近的区域将具有最密集的点分布,而靠近立方体面的中心的区域将是最低的。
您可以直观地理解这一点,因为投影到底层球体上的立方体体积在靠近立方体面中心的角处更大。 事实上,一小块(投影在球体上的一个小圆上)的体积与 从原点通过小圆的中心到球体上相交点的向量大小的立方。
因此,立方体面中心(如 (1,0,0))上的相对体积为 1,但对于角(如 (1,1,1))而言,其相对体积为 sqrt(3) 的立方或 1.73 的立方,约为5.2,密度几乎增加了 5 倍!
spreadPoints() 函数可能会做得更好,但我不确定。
JavaScript 中存在一些错误 - 使用 pow(..,-1) 函数而不是 acos()、混淆角度以及缺少 random() 调用的 Math 对象。,
这里有类似但正确的 JavaScript 来执行 Wolfram 链接所说的操作:
/*
Returns a random point of a sphere, evenly distributed over the sphere.
The sphere is centered at (x0,y0,z0) with the passed in radius.
The returned point is returned as a three element array [x,y,z].
*/
function randomSpherePoint(x0,y0,z0,radius){
var u = Math.random();
var v = Math.random();
var theta = 2 * Math.PI * u;
var phi = Math.acos(2 * v - 1);
var x = x0 + (radius * Math.sin(phi) * Math.cos(theta));
var y = y0 + (radius * Math.sin(phi) * Math.sin(theta));
var z = z0 + (radius * Math.cos(phi));
return [x,y,z];
}
我认为更简单的算法是
[-1,1]x[-1,1]x[-1,1]
立方体内随机选择一个点x*x + y*y + z*z > 1
从 1 开始重复x
、y
和 z
除以 Math.sqrt(x*x + y*y + z*z)
换句话说,只需在球体内部随机选择一个点并将其投影到球体上。不要太担心“循环”,因为点位于球体外部的概率约为 0.4764,平均而言,循环需要的迭代次数少于两次。
您可以在此链接上看到该算法的实际应用。 请注意,如果您使用 chrome,赤道周围会出现一些条带,在我看来,这是 Math.random 中的一个错误,或者只是一个低质量的随机生成器(在 Firefox 或 Safari 上工作正常,但在 Android 上也可以看到同样的问题)浏览器)。点数越多,条带就越明显(例如,我现在使用 10000 个点,而不是 10000 个点来保持动画流畅)。 编辑:此错误现已在 chrome 和 Android 上修复。
请注意,如果您正在寻找一种将点均匀分布在球体上的方法,您可以通过如上所述选择十个随机点,然后仅接受与已选择的点集具有最大 3d 距离的点来做得更好。这仍然是全局随机的(即,具有指定半径的圆盘接收点的概率对于球体上的所有圆盘来说是相同的),但如果您需要对球体进行“采样”,则会更好地分配点。该函数在链接指向的 html 文件中编码为
spreadPoints()
。
您可以在这里看到两种方法之间的区别:
两个球体上都绘制了 1000 个随机点:左侧的球体使用均匀随机点,右侧的球体通过从 10 个随机候选点中选取每个点来做出选择,以最大化与已选择点的距离。
这应该会稍微更有效率。 请参阅 https://karlsims.com/random-in-sphere.html 获取直观说明。
function randomSpherePoint(x0,y0,z0,radius) {
var y = Math.random() * 2 - 1; // random y from -1 to 1
var r = Math.sqrt(1 - y*y); // radius on xz plane at y
var long = Math.random() * 2 * Math.PI; // random longitude
return [x0 + radius * r * Math.sin(long),
y0 + radius * y,
z0 + radius * r * Math.cos(long)];
}