gpt4 book ai didi

javascript - 计算单个球体的位置以创建一个由球体组成的球体

转载 作者:可可西里 更新时间:2023-11-01 02:43:45 25 4
gpt4 key购买 nike

我正在尝试使用 THREE.js 重新创建一个原子,但我遇到了第一个问题 - 因为每种类型的原子都有不同数量的质子/中子,所以我正在寻找一种方法自动定位它们,以免发生碰撞,因此它们的最终结果将尽可能接近球体 - 示例请参见此图片

img
(来源:alternativephysics.org)
.

有没有办法计算这个并使用公式轻松分配每个中子/质子位置?或者我是否必须让物理引擎参与进来,将球体挤压在一起并希望每次运行都能获得最佳结果?

我还没有这方面的任何代码,因为我只是想弄清楚从哪里开始这部分。

编辑

我还应该注意,我希望球体在较大球体的空间内被挤压在一起。我并不是要让所有球体都在较大球体的半径上。

编辑 2

我研究过使用物理引擎将它们全部压缩到一个小区域中,但我找不到一个引擎可以让我将场景中的所有对象移动到位置 (0,0,0)一种引力。所有的引擎都只是让重力向下推一个物体。我仍然宁愿使用公式来定位球体,也不愿在我的项目中包含整个物理引擎。

编辑 3,04/06/06

我已经做了一些实验,但我还是做不好。这是它现在的样子:

enter image description here

但如您所见,看起来真的很不对劲。这就是当我制造铀原子而不是碳原子(更多质子/中子/电子)时发生的情况

enter image description here

它可能只是我,但它看起来更像是一些花哨的料理鼠王而不是铀原子。

我是如何来到这里的:

我试图在上面制作我正在寻找的东西,这是前提:

(particleObject是粒子的父对象,粒子会相对于这个对象移动)

  1. 我把所有质子和中子的长度加在一起,这样我就可以循环遍历它们。
  2. 如果 added number % 2 == 0,(这是为了我的测试)我会将旋转设置为 (pi * 2)/2 <-最后两个在那里代表上面两个。
  3. 每次迭代 I 都会增加 l 变量。 (希望)每当 i 等于 loopcount 变量时,这意味着我已经将球体放置在球体形状中。然后我将 loopcount 乘以 3 以找出下一次运行需要多少个球体。我将 l 设置为 0,这样球体的定位将被重置,循环将递增,导致下一行球体被放置在 x 轴外 1 个单位的位置。

(抱歉这里的术语,很难解释。见代码。)

    var PNamount = atomTypes[type].protons + atomTypes[type].neutrons;
var loopcount = 1;
if(PNamount % 2 == 0) {
var rotate = (PI * 2) / 2;
loopcount = 2;
}
var neutrons = 0,
protons = 0,
loop = 1,
l = 0;
for(var i = 0; i < PNamount; i++) {

if(i == loopcount){
loopcount = loopcount * 3;
loop++;
rotate = (PI * 2) / loopcount;
l = 0;
} else {
l++;
}

particleObject.rotation.x = rotate * l;
particleObject.rotation.y = rotate * l;
particleObject.rotation.z = rotate * l;
particle.position.x = loop;
}

老实说,我对 3D 数学一点也不擅长。所以任何帮助都会很有帮助。另外,很有可能我定位它们的方法在各个方面都是完全错误的。谢谢!

你可以看到 code live here .

最佳答案

我肯定会说这是物理引擎的完美用例。在没有物理引擎的情况下进行这个模拟听起来真的很麻烦,所以“包括整个物理引擎”对我来说并没有那么大的成本。无论如何,我发现的大多数 JavaScript 物理引擎都是轻量级的。然而,它需要一些额外的 CPU 能力来进行物理计算!

我坐下来尝试用物理引擎创建类似于您描述的东西 CANNON.js .使基本模拟正常工作非常容易,但要获得恰到好处的参数似乎有点棘手,需要进行更多调整。

你提到你已经尝试过这个但是不能让粒子被引力朝向一个点,使用 CANNON.js(可能还有大多数其他物理引擎)这可以通过对处于负位置的物体施加力来实现方向:

function pullOrigin(body){
body.force.set(
-body.position.x,
-body.position.y,
-body.position.z
);
}

也很容易实现 body 被拉向某个父对象的行为,而这个父对象又被拉向所有其他父对象的平均位置。这样您就可以创建完整的分子。

一件棘手的事情是让电子让质子和中子在一定距离内循环。为了实现这一点,我给它们一个轻微的力向原点,然后一个轻微的力同时远离所有的质子和中子。最重要的是,我还在模拟开始时给了他们一个小的侧向推力,以便他们开始绕过中心。

如果您希望我澄清任何特定部分,请告诉我。

let scene = new THREE.Scene();
let world = new CANNON.World();
world.broadphase = new CANNON.NaiveBroadphase();
world.solver.iterations = 5;

let camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );

let renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

function Proton(){
let radius = 1;

return {
// Cannon
body: new CANNON.Body({
mass: 1, // kg
position: randomPosition(6),
shape: new CANNON.Sphere(radius)
}),
// THREE
mesh: new THREE.Mesh(
new THREE.SphereGeometry( radius, 32, 32 ),
new THREE.MeshPhongMaterial( { color: 0xdd5555, specular: 0x999999, shininess: 13} )
)
}
}

function Neutron(){
let radius = 1;

return {
// Cannon
body: new CANNON.Body({
mass: 1, // kg
position: randomPosition(6),
shape: new CANNON.Sphere(radius)
}),
// THREE
mesh: new THREE.Mesh(
new THREE.SphereGeometry( radius, 32, 32 ),
new THREE.MeshPhongMaterial( { color: 0x55dddd, specular: 0x999999, shininess: 13} )
)
}
}

function Electron(){
let radius = 0.2;

return {
// Cannon
body: new CANNON.Body({
mass: 0.5, // kg
position: randomPosition(10),
shape: new CANNON.Sphere(radius)
}),
// THREE
mesh: new THREE.Mesh(
new THREE.SphereGeometry( radius, 32, 32 ),
new THREE.MeshPhongMaterial( { color: 0xdddd55, specular: 0x999999, shininess: 13} )
)
}
}

function randomPosition(outerRadius){
let x = (2 * Math.random() - 1 ) * outerRadius,
y = (2 * Math.random() - 1 ) * outerRadius,
z = (2 * Math.random() - 1 ) * outerRadius
return new CANNON.Vec3(x, y, z);
}

function addToWorld(object){
world.add(object.body);
scene.add(object.mesh);
}

// create our Atom
let protons = Array(5).fill(0).map( () => Proton() );
let neutrons = Array(5).fill(0).map( () => Neutron() );
let electrons = Array(15).fill(0).map( () => Electron() );

protons.forEach(addToWorld);
neutrons.forEach(addToWorld);
electrons.forEach(addToWorld);


let light = new THREE.AmbientLight( 0x202020 ); // soft white light
scene.add( light );

let directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
directionalLight.position.set( -1, 1, 1 );
scene.add( directionalLight );

camera.position.z = 18;

const timeStep = 1/60;

//Small impulse on the electrons to get them moving in the start
electrons.forEach((electron) => {
let centerDir = electron.body.position.vsub(new CANNON.Vec3(0, 0, 0));
centerDir.normalize();
let impulse = centerDir.cross(new CANNON.Vec3(0, 0, 1));
impulse.scale(2, impulse);
electron.body.applyLocalImpulse(impulse, new CANNON.Vec3(0, 0, 0));
});

function render () {
requestAnimationFrame( render );

// all particles pull towards the center
protons.forEach(pullOrigin);
neutrons.forEach(pullOrigin);
electrons.forEach(pullOrigin);

// electrons should also be pushed by protons and neutrons
electrons.forEach( (electron) => {
let pushForce = new CANNON.Vec3(0, 0, 0 );

protons.forEach((proton) => {
let f = electron.body.position.vsub(proton.body.position);
pushForce.vadd(f, pushForce);
});

neutrons.forEach((neutron) => {
let f = electron.body.position.vsub(neutron.body.position);
pushForce.vadd(f, pushForce);
});

pushForce.scale(0.07, pushForce);
electron.body.force.vadd(pushForce, electron.body.force);
})

// protons and neutrons slows down (like wind resistance)
neutrons.forEach((neutron) => resistance(neutron, 0.95));
protons.forEach((proton) => resistance(proton, 0.95));

// Electrons have a max velocity
electrons.forEach((electron) => {maxVelocity(electron, 5)});

// Step the physics world
world.step(timeStep);
// Copy coordinates from Cannon.js to Three.js
protons.forEach(updateMeshState);
neutrons.forEach(updateMeshState);
electrons.forEach(updateMeshState);

renderer.render(scene, camera);
};

function updateMeshState(object){
object.mesh.position.copy(object.body.position);
object.mesh.quaternion.copy(object.body.quaternion);
}

function pullOrigin(object){
object.body.force.set(
-object.body.position.x,
-object.body.position.y,
-object.body.position.z
);
}

function maxVelocity(object, vel){
if(object.body.velocity.length() > vel)
object.body.force.set(0, 0, 0);
}

function resistance(object, val) {
if(object.body.velocity.length() > 0)
object.body.velocity.scale(val, object.body.velocity);
}
render();
<script src="https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r75/three.min.js"></script>

编辑

我已将粒子模块化为可从 Atom 函数检索的 Atom 对象。如果您不确定任何事情,还可以在代码中添加更多注释。我会建议你真正研究代码,并检查 CANNON.js documentation (真的很彻底)。力相关的东西在Body class Cannon.js 的。我所做的就是将 THREE.Mesh 和 CANNON.Body 组合成一个对象(对于每个粒子)。然后,我模拟了 CANNON.Body 上的所有运动,在渲染 THREE.Mesh 之前,我将位置和旋转从 CANNON.Body 复制到 THREE.Mesh。

这是原子函数(也改变了一些电子物理学):

function Atom(nProtons, nNeutrons, nElectrons, pos = new CANNON.Vec3(0, 0, 0)){

//variable to move the atom, which att the particles will pull towards
let position = pos;

// create our Atom
let protons = Array(nProtons).fill(0).map( () => Proton() );
let neutrons = Array(nNeutrons).fill(0).map( () => Neutron() );
let electrons = Array(nElectrons).fill(0).map( () => Electron() );

// Public Functions
//=================
// add to a three.js and CANNON scene/world
function addToWorld(world, scene) {
protons.forEach((proton) => {
world.add(proton.body);
scene.add(proton.mesh);
});
neutrons.forEach((neutron) => {
world.add(neutron.body);
scene.add(neutron.mesh);
});
electrons.forEach((electron) => {
world.add(electron.body);
scene.add(electron.mesh);
});
}

function simulate() {

protons.forEach(pullParticle);
neutrons.forEach(pullParticle);

//pull electrons if they are further than 5 away
electrons.forEach((electron) => { pullParticle(electron, 5) });
//push electrons if they are closer than 6 away
electrons.forEach((electron) => { pushParticle(electron, 6) });

// give the particles some friction/wind resistance
//electrons.forEach((electron) => resistance(electron, 0.95));
neutrons.forEach((neutron) => resistance(neutron, 0.95));
protons.forEach((proton) => resistance(proton, 0.95));

}

function electronStartingVelocity(vel) {
electrons.forEach((electron) => {
let centerDir = electron.body.position.vsub(position);
centerDir.normalize();
let impulse = centerDir.cross(new CANNON.Vec3(0, 0, 1));
impulse.scale(vel, impulse);
electron.body.applyLocalImpulse(impulse, new CANNON.Vec3(0, 0, 0));
});
}

// Should be called after CANNON has simulated a frame and before THREE renders.
function updateAtomMeshState(){
protons.forEach(updateMeshState);
neutrons.forEach(updateMeshState);
electrons.forEach(updateMeshState);
}


// Private Functions
// =================

// pull a particale towards the atom position (if it is more than distance away)
function pullParticle(particle, distance = 0){

// if particle is close enough, dont pull more
if(particle.body.position.distanceTo(position) < distance)
return false;

//create vector pointing from particle to atom position
let pullForce = position.vsub(particle.body.position);

// same as: particle.body.force = particle.body.force.vadd(pullForce)
particle.body.force.vadd( // add particle force
pullForce, // to pullForce
particle.body.force); // and put it in particle force
}

// Push a particle from the atom position (if it is less than distance away)
function pushParticle(particle, distance = 0){

// if particle is far enough, dont push more
if(particle.body.position.distanceTo(position) > distance)
return false;

//create vector pointing from particle to atom position
let pushForce = particle.body.position.vsub(position);

particle.body.force.vadd( // add particle force
pushForce, // to pushForce
particle.body.force); // and put it in particle force
}

// give a partile some friction
function resistance(particle, val) {
if(particle.body.velocity.length() > 0)
particle.body.velocity.scale(val, particle.body.velocity);
}

// Call this on a particle if you want to limit its velocity
function limitVelocity(particle, vel){
if(particle.body.velocity.length() > vel)
particle.body.force.set(0, 0, 0);
}

// copy ratation and position from CANNON to THREE
function updateMeshState(particle){
particle.mesh.position.copy(particle.body.position);
particle.mesh.quaternion.copy(particle.body.quaternion);
}


// public API
return {
"simulate": simulate,
"electrons": electrons,
"neutrons": neutrons,
"protons": protons,
"position": position,
"updateAtomMeshState": updateAtomMeshState,
"electronStartingVelocity": electronStartingVelocity,
"addToWorld": addToWorld

}
}

function Proton(){
let radius = 1;

return {
// Cannon
body: new CANNON.Body({
mass: 1, // kg
position: randomPosition(0, 6), // random pos from radius 0-6
shape: new CANNON.Sphere(radius)
}),
// THREE
mesh: new THREE.Mesh(
new THREE.SphereGeometry( radius, 32, 32 ),
new THREE.MeshPhongMaterial( { color: 0xdd5555, specular: 0x999999, shininess: 13} )
)
}
}

function Neutron(){
let radius = 1;

return {
// Cannon
body: new CANNON.Body({
mass: 1, // kg
position: randomPosition(0, 6), // random pos from radius 0-6
shape: new CANNON.Sphere(radius)
}),
// THREE
mesh: new THREE.Mesh(
new THREE.SphereGeometry( radius, 32, 32 ),
new THREE.MeshPhongMaterial( { color: 0x55dddd, specular: 0x999999, shininess: 13} )
)
}
}

function Electron(){
let radius = 0.2;

return {
// Cannon
body: new CANNON.Body({
mass: 0.5, // kg
position: randomPosition(3, 7), // random pos from radius 3-8
shape: new CANNON.Sphere(radius)
}),
// THREE
mesh: new THREE.Mesh(
new THREE.SphereGeometry( radius, 32, 32 ),
new THREE.MeshPhongMaterial( { color: 0xdddd55, specular: 0x999999, shininess: 13} )
)
}
}


function randomPosition(innerRadius, outerRadius){

// get random direction
let x = (2 * Math.random() - 1 ),
y = (2 * Math.random() - 1 ),
z = (2 * Math.random() - 1 )

// create vector
let randVec = new CANNON.Vec3(x, y, z);

// normalize
randVec.normalize();
// scale it to the right radius
randVec = randVec.scale( Math.random() * (outerRadius - innerRadius) + innerRadius); //from inner to outer
return randVec;
}

并使用它:

let scene = new THREE.Scene();
let world = new CANNON.World();
world.broadphase = new CANNON.NaiveBroadphase();
world.solver.iterations = 5;

let camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );

let renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

// create a Atom with 3 protons and neutrons, and 5 electrons
// all circulating position (-4, 0, 0)
let atom = Atom(3, 3, 5, new CANNON.Vec3(-4, 0, 0));

// move atom (will not be instant)
//atom.position.x = -2;

// add to THREE scene and CANNON world
atom.addToWorld(world, scene);

let light = new THREE.AmbientLight( 0x202020 ); // soft white light
scene.add( light );

let directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
directionalLight.position.set( -1, 1, 1 );
scene.add( directionalLight );

camera.position.z = 18;

const timeStep = 1/60;

// give the atoms electrons some starting velocity
atom.electronStartingVelocity(2);

function render () {
requestAnimationFrame( render );

// calculate all the particles positions
atom.simulate();

// Step the physics world
world.step(timeStep);

//update the THREE mesh
atom.updateAtomMeshState();

renderer.render(scene, camera);
};


render();

关于javascript - 计算单个球体的位置以创建一个由球体组成的球体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36317734/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com