Вычисление положения отдельных сфер для создания сферы из сфер

Я пытаюсь воссоздать atom с помощью THREE.js и сталкиваюсь с моей первой проблемой - поскольку каждый тип атома имеет разное количество протонов/нейтронов, я пытаюсь найти способ расположить их автоматически, чтобы не было столкновений, и, таким образом, конечный результат их всех вместе сделает что-то как можно ближе к сфере - см. это изображение для примера

img
(источник: alternativephysics.org)
,

Есть ли способ рассчитать это и легко назначить каждой позиции нейтронов/протонов с формулой? Или мне нужно задействовать физический движок, чтобы просто сжать сферы и надеяться на лучший результат с каждым прогоном?

У меня пока нет никакого кода, поскольку я просто пытаюсь понять, с чего начать эту часть.

EDIT

Я должен также отметить, что я хочу, чтобы сферы были сжаты вместе в пространстве большей сферы. Я НЕ пытаюсь заставить все сферы идти по радиусу большей сферы.

ОБНОВЛЕНИЕ 2

Я пытался использовать физический движок, чтобы раздавить их всех на небольшой площади, но я не могу найти движок, который позволил бы мне перемещать все объекты в моей сцене в положение (0,0,0) с гравитационной силой, Все двигатели просто притягивают гравитацию к объекту. Я все же предпочел бы использовать формулу для позиционирования сфер, а не включать весь физический движок в свой проект.

ОБНОВЛЕНИЕ 3, 04/06/06

Я немного поэкспериментировал, но все еще не могу сделать это правильно. Вот как это выглядит сейчас:

enter image description here

Но, как вы можете видеть, выглядит действительно не так. Вот что происходит, когда я делаю atom урана вместо атома углерода (больше протонов/нейтронов/электронов)

enter image description here

Это может быть только я, но это больше похоже на какой-то необычный рататуй, чем atom урана.

Как я сюда попал:

Я пытался сделать то, что искал выше, и вот предпосылка:

(particleObject является родителем частицы, частица будет двигаться относительно этого объекта)

  1. Я добавил все длины протонов и нейтронов вместе, чтобы я мог перебрать их всех.
  2. Если added number % 2 == 0, (что это для моего тестирования), я бы установил поворот на (pi * 2) / 2 & lt; - последние два были там, чтобы представить два выше.
  3. Каждая итерация I будет увеличивать переменную l. (надеюсь) всякий раз, когда i будет равняться переменной loopcount, это будет означать, что я поместил сферу вокруг в форме сферы. Затем я умножил loopcount на 3, чтобы узнать, сколько сферы понадобится для следующего запуска. Я бы установил l на 0, чтобы позиционирование сферы было сброшено, а цикл увеличился бы, в результате чего следующий ряд сферы был бы размещен на 1 единицу по оси x.

(Извините за терминологию, это очень сложно объяснить. См. код.)

    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 математике. Так что любая помощь будет очень полезна. Плюс, вполне возможно, что мой метод их позиционирования абсолютно неверен во всех отношениях. Спасибо!

Вы можете увидеть код в прямом эфире здесь.

Ответ 1

Я бы определенно сказал, что это идеальный вариант использования движка физики. Выполнение этой симуляции без физического двигателя звучит как настоящая проблема, поэтому "включая весь физический движок" не кажется такой большой ценой для меня. Большинство логических движков JavaScript, которые я нашел, все равно являются весом. Тем не менее, он потребует некоторую дополнительную мощность процессора для вычислений физики!

Я сел и попытался создать нечто похожее на то, что вы описываете с помощью физического механизма CANNON.js. Было довольно легко получить базовое симуляционное моделирование, но для того, чтобы получить нужные параметры, это кажется немного сложным, и ему потребуется больше настроек.

Вы упомянули, что вы уже пробовали это, но не могли заставить частицы тяготеть к точке, а CANNON.js(и, возможно, большинство других физических движков), это может быть достигнуто применением силы к объекту в отрицательном положении направление:

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

Также легко достичь поведения, когда тела тянутся к определенному родительскому объекту, что в свою очередь тянет к среднему положению всех других родительских объектов. Таким образом, вы можете создавать целые молекулы.

Одна сложная вещь заключалась в том, чтобы позволить электронам циркулировать протоны и нейтроны на расстоянии. Для этого я даю им небольшую силу по отношению к происхождению, а затем небольшую силу от всех протонов и нейтронов одновременно. Кроме того, я также даю им небольшой толчок в боковом направлении в начале моделирования, чтобы они начали циркулировать по центру.

Пожалуйста, дайте мне знать, если вы хотите, чтобы я уточнил какую-либо конкретную часть.

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>

Ответ 2

Я столкнулся с одной и той же проблемой, а также сделал решение с использованием Cannon.js. Однако при визуализации более тяжелых элементов это может вызвать значительную нагрузку, особенно на мобильные устройства.

Я придумал идею захвата конечной позиции нуклонов после их установки и сохранения в json файле для всех элементов.

Тогда нуклоны могут быть сделаны для орбиты ядра линейно без физики.

Ответ 3

одним из решений было бы использовать алгоритм икосферы для вычисления положения нейтронов/протонов с использованием вершины точки сгенерированной сферы.

Вы можете найти алгоритм ad usefoul here

расстояние между точками остается равным по всей поверхности