custom charge magnitude

also: s/charge/particle
to reduce confusion
This commit is contained in:
root 2025-04-27 08:03:07 +00:00
parent 5e04ad107f
commit 642d1c5cdd
3 changed files with 54 additions and 48 deletions

View file

@ -86,16 +86,16 @@ aside > container > div {
<button id="button-surface-earth" onclick="set_surface(SURFACE_EARTH)">Earth</button> <button id="button-surface-earth" onclick="set_surface(SURFACE_EARTH)">Earth</button>
</div> </div>
<div> <div>
<button onclick="make_charges(0);faces=[]">0</button> <button onclick="make_particles(0);faces=[]">0</button>
<button onclick="make_charges(1);faces=[]">1</button> <button onclick="make_particles(1);faces=[]">1</button>
<button onclick="make_charges(2);faces=[]">2</button> <button onclick="make_particles(2);faces=[]">2</button>
<button onclick="make_charges(3);faces=[]">3</button> <button onclick="make_particles(3);faces=[]">3</button>
<button onclick="make_charges(4);faces=[]">4</button> <button onclick="make_particles(4);faces=[]">4</button>
<button onclick="make_charges(5);faces=[]">5</button> <button onclick="make_particles(5);faces=[]">5</button>
<button onclick="make_charges(6);faces=[]">6</button> <button onclick="make_particles(6);faces=[]">6</button>
<button onclick="make_charges(7);faces=[]">7</button> <button onclick="make_particles(7);faces=[]">7</button>
<button onclick="make_charges(8);faces=[]">8</button> <button onclick="make_particles(8);faces=[]">8</button>
<button onclick="make_charges(9);faces=[]">9</button> <button onclick="make_particles(9);faces=[]">9</button>
</div> </div>
</container> </container>
</aside> </aside>

View file

@ -1,7 +1,7 @@
let camera; let camera;
let red; let red;
let charges = []; let particles = [];
let faces = []; let faces = [];
let sphere_radius; let sphere_radius;
@ -14,6 +14,7 @@ let surface = SURFACE_CIRCLES;
let physics = false; let physics = false;
let skeleton = false; let skeleton = false;
let polytope = false; let polytope = false;
let charge = -1;
let buttons_surface; let buttons_surface;
let checkbox_physics; let checkbox_physics;
@ -70,9 +71,9 @@ function draw() {
camera.centerZ = 0; camera.centerZ = 0;
make_lights(); make_lights();
if (physics) move_charges(charges, 8e-4); if (physics) move_particles(particles, 8e-4);
draw_charges(sphere_radius); draw_particles(sphere_radius);
if (skeleton) draw_skeleton(sphere_radius); if (skeleton) draw_skeleton(sphere_radius);
if (polytope) { if (polytope) {
if (physics || faces.length === 0) find_faces(); if (physics || faces.length === 0) find_faces();
@ -88,27 +89,27 @@ function face_dist_sq([v1, v2, v3]) {
function find_faces() { function find_faces() {
faces = []; faces = [];
for (let i = 2; i < charges.length; i += 1) { for (let i = 2; i < particles.length; i += 1) {
for (let j = 1; j < i; j += 1) { for (let j = 1; j < i; j += 1) {
for (let k = 0; k < j; k += 1) { for (let k = 0; k < j; k += 1) {
// Check if p1 p2 p3 form a face of the convex polytope // Check if p1 p2 p3 form a face of the convex polytope
// enclosing all vertices ... // enclosing all vertices ...
const p1 = charges[i].position; const p1 = particles[i].position;
const p2 = charges[j].position; const p2 = particles[j].position;
const p3 = charges[k].position; const p3 = particles[k].position;
const normal = p5.Vector.sub(p2, p1).cross(p5.Vector.sub(p3, p1)); const normal = p5.Vector.sub(p2, p1).cross(p5.Vector.sub(p3, p1));
// ... by checking if the other vertices are on the same // ... by checking if the other vertices are on the same
// side of the plane generated by p1 p2 p3 // side of the plane generated by p1 p2 p3
let plane_separates_vertices = false; let plane_separates_vertices = false;
let euler_formula = false; let euler_formula = false;
for (let r = 1; r < charges.length; r += 1) { for (let r = 1; r < particles.length; r += 1) {
for (let s = 0; s < r; s += 1) { for (let s = 0; s < r; s += 1) {
if ( if (
r === i || r === j || r === k || r === i || r === j || r === k ||
s === i || s === j || s === k s === i || s === j || s === k
) continue; ) continue;
const q1 = charges[r].position; const q1 = particles[r].position;
const q2 = charges[s].position; const q2 = particles[s].position;
// Let l(t) := q1 + (q2 - q1) * t. // Let l(t) := q1 + (q2 - q1) * t.
// L := { l(t) : 0 <= t <= 1 } is the line segment // L := { l(t) : 0 <= t <= 1 } is the line segment
// between q1 and q2. L intersects the plane // between q1 and q2. L intersects the plane
@ -133,7 +134,7 @@ function find_faces() {
); );
plane_separates_vertices ||= t >= 0 && t <= 1; plane_separates_vertices ||= t >= 0 && t <= 1;
if (plane_separates_vertices) break; if (plane_separates_vertices) break;
euler_formula ||= charges.length * 2 - faces.length == 4; euler_formula ||= particles.length * 2 - faces.length == 4;
if (euler_formula) break; if (euler_formula) break;
} }
if (plane_separates_vertices || euler_formula) break; if (plane_separates_vertices || euler_formula) break;
@ -170,21 +171,21 @@ function draw_skeleton(radius) {
fill(0xff); fill(0xff);
sphere(4); sphere(4);
stroke(0xbf); stroke(0xbf);
for (let charge of charges) { for (let particle of particles) {
line( line(
0, 0,
0, 0,
0, 0,
charge.position.x * radius, particle.position.x * radius,
charge.position.y * radius, particle.position.y * radius,
charge.position.z * radius, particle.position.z * radius,
); );
} }
pop(); pop();
} }
function make_charges(n) { function make_particles(n) {
charges = []; particles = [];
for (let i = 0; i < n; i += 1) { for (let i = 0; i < n; i += 1) {
let position; let position;
if (i === 0) { if (i === 0) {
@ -192,21 +193,22 @@ function make_charges(n) {
} else { } else {
position = p5.Vector.random3D(); position = p5.Vector.random3D();
} }
charges.push({ particles.push({
position: position, position: position,
velocity: createVector(), velocity: createVector(),
acceleration: createVector(), acceleration: createVector(),
charge: charge,
color: red, color: red,
}); });
} }
} }
function draw_charges(radius) { function draw_particles(radius) {
push(); push();
noStroke(); noStroke();
for (let charge of charges.values()) { for (let particle of particles) {
ambientMaterial(charge.color); ambientMaterial(particle.color);
let position = charge.position.copy(); let position = particle.position.copy();
position.mult(radius); position.mult(radius);
push(); push();
translate(position.x, position.y, position.z); translate(position.x, position.y, position.z);
@ -292,7 +294,7 @@ function keyPressed() {
} else if (key == 'g') { } else if (key == 'g') {
toggle_polytope(); toggle_polytope();
} else if (key >= '0' && key <= '9') { } else if (key >= '0' && key <= '9') {
make_charges(int(key)); make_particles(int(key));
faces = []; faces = [];
} }
} }

View file

@ -1,14 +1,18 @@
function move_charges(charges, force_constant) { function move_particles(particles, force_constant) {
for (let charge of charges) { for (let particle of particles) {
charge.acceleration.setMag(0); particle.acceleration.setMag(0);
} }
for (let i = 0; i < charges.length; i += 1) { for (let i = 0; i < particles.length; i += 1) {
for (let j = 0; j < i; j += 1) { for (let j = 0; j < i; j += 1) {
const displacement = p5.Vector.sub( const displacement = p5.Vector.sub(
charges[i].position, particles[i].position,
charges[j].position, particles[j].position,
);
const force_mag = (
particles[i].charge * particles[j].charge
/ displacement.magSq()
* force_constant
); );
const force_mag = 1 / displacement.magSq() * force_constant;
// XXX possible extension: divide by charge's mass // XXX possible extension: divide by charge's mass
const ai_mag = force_mag; const ai_mag = force_mag;
const aj_mag = force_mag; const aj_mag = force_mag;
@ -23,16 +27,16 @@ function move_charges(charges, force_constant) {
} }
ai.mult(ai_mag); ai.mult(ai_mag);
aj.mult(aj_mag); aj.mult(aj_mag);
project_onto_plane(ai, charges[i].position); project_onto_plane(ai, particles[i].position);
project_onto_plane(aj, charges[j].position); project_onto_plane(aj, particles[j].position);
charges[i].acceleration.add(ai); particles[i].acceleration.add(ai);
charges[j].acceleration.add(aj); particles[j].acceleration.add(aj);
} }
} }
for (let charge of charges) { for (let particle of particles) {
charge.velocity = charge.velocity.add(charge.acceleration); particle.velocity = particle.velocity.add(particle.acceleration);
charge.position = charge.position.add(charge.velocity); particle.position = particle.position.add(particle.velocity);
charge.position.normalize(); particle.position.normalize();
} }
} }