我正在使用THREE.js在球体上创建点,类似于periodic table of elements example
我的数据集是一个不规则大小的圆圈,我希望将它们均匀地分布在球体表面周围。经过几个小时的网络搜索后,我意识到很多比听起来更难。
以下是这个想法的实例:
任何人都可以帮我指点找到一个允许我这样做的算法吗?打包率不需要非常高,理想情况下,它可以快速轻松地在javascript中进行计算,以便在THREE.js(笛卡尔坐标系或坐标系)中进行渲染。效率是关键。
编辑:圆弧半径可以有很大差异。以下是使用周期表代码的示例:
答案 0 :(得分:7)
这是一种尝试的方法:使用模拟排斥力的迭代搜索。
<强>算法强>
首先通过在任何类型的算法中在表面上排列圆来初始化数据集。这仅用于初始化,因此它不一定非常好。周期表代码可以很好地完成。另外,使用半径作为质量值为每个圆圈指定一个“质量”。
现在开始迭代以收敛解决方案。对于每次通过主循环的操作,请执行以下操作:
计算每个圆圈的排斥力。在重力公式之后对你的排斥力进行建模,并进行两项调整:(a)物体彼此相互推开,彼此不相互吸引,(b)你需要调整“力常数”值以适合您的模型的比例。根据您的数学能力,您可以在计划期间计算出良好的常数值;其他明智的只是先做一点实验,你会发现一个很好的价值。
计算每个圆上的总力(如果你不确定如何做的话,请查看n体问题),沿着其总计算力的向量移动每个圆,使用长度矢量作为移动的距离。在这里您可能会发现必须调整力常数值。首先,你需要长度小于球体半径5%的运动。
步骤2中的移动会将圆圈推离球体表面(因为它们相互排斥)。现在将每个圆圈移回球体表面,朝向球体中心的方向。
对于每个圆圈,计算圆圈旧位置到新位置的距离。移动的最大距离是主循环中此迭代的移动长度。
继续迭代主循环一段时间。随着时间的推移,移动长度应该变得越来越小,因为圆圈的相对位置稳定成符合您标准的排列。当运动长度下降到一个非常小的值以下时退出循环。
<强>扭捏强>
您可能会发现必须调整力计算以使算法收敛于解决方案。你如何调整取决于你正在寻找的结果类型。首先调整力常数。如果这不起作用,您可能需要向上或向下更改质量值。或者可以在力计算中更改半径的指数。例如,而不是:
f = ( k * m[i] * m[j] ) / ( r * r );
你可以试试这个:
f = ( k * m[i] * m[j] ) / pow( r, p );
然后你可以尝试不同的p值。
您还可以尝试使用不同的算法进行初始分发。
反复试验的数量取决于您的设计目标。
答案 1 :(得分:4)
这是你可以建立的东西。它会沿着球体随机分布你的球体。稍后我们将迭代这个起点以获得均匀分布。
//random point on sphere of radius R
var sphereCenters = []
var numSpheres = 100;
for(var i = 0; i < numSpheres; i++) {
var R = 1.0;
var vec = new THREE.Vector3(Math.random(), Math.random(), Math.random()).normalize();
var sphereCenter = new THREE.Vector3().copy(vec).multiplyScalar(R);
sphereCenter.radius = Math.random() * 5; // RANDOM SPHERE SIZE, plug in your sizes here
sphereCenters.push(sphereCenter);
//Create a THREE.js sphere at sphereCenter
...
}
然后运行以下代码几次以有效地打包球体:
for(var i = 0; i < sphereCenters.length; i++) {
for(var j = 0; j < sphereCenters.length; j++) {
if(i === j) continue;
//calculate the distance between sphereCenters[i] and sphereCenters[j]
var dist = new THREE.Vector3().copy(sphereCenters[i]).sub(sphereCenters[j]);
if(dist.length() < sphereSize) {
//move the center of this sphere to to compensate
//how far do we have to move?
var mDist = sphereSize - dist.length();
//perturb the sphere in direction of dist magnitude mDist
var mVec = new THREE.Vector3().copy(dist).normalize();
mVec.multiplyScalar(mDist);
//offset the actual sphere
sphereCenters[i].add(mVec).normalize().multiplyScalar(R);
}
}
}
多次运行第二部分将“收敛”您正在寻找的解决方案。您必须选择应运行多少次才能找到速度和准确度之间的最佳折衷。
更新了准确性
答案 2 :(得分:-1)
您可以使用与元素周期表中相同的代码。 那里的矩形没有触摸,所以你可以通过使用相同的代码实现与圆圈相同的效果。
以下是他们的代码:
var vector = new THREE.Vector3();
for ( var i = 0, l = objects.length; i < l; i ++ ) {
var phi = Math.acos( -1 + ( 2 * i ) / l );
var theta = Math.sqrt( l * Math.PI ) * phi;
var object = new THREE.Object3D();
object.position.x = 800 * Math.cos( theta ) * Math.sin( phi );
object.position.y = 800 * Math.sin( theta ) * Math.sin( phi );
object.position.z = 800 * Math.cos( phi );
vector.copy( object.position ).multiplyScalar( 2 );
object.lookAt( vector );
targets.sphere.push( object );
}