From 5e04ad107f9db307c476945110f3bf1698f5b21c Mon Sep 17 00:00:00 2001 From: root <> Date: Sun, 27 Apr 2025 07:57:42 +0000 Subject: [PATCH 1/7] physics oopsie: coulomb's law is 1/r^2 not 1/r --- sketch.js | 2 +- thomson-problem.js | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/sketch.js b/sketch.js index a04f4ac..d0a1b95 100644 --- a/sketch.js +++ b/sketch.js @@ -70,7 +70,7 @@ function draw() { camera.centerZ = 0; make_lights(); - if (physics) move_charges(charges); + if (physics) move_charges(charges, 8e-4); draw_charges(sphere_radius); if (skeleton) draw_skeleton(sphere_radius); diff --git a/thomson-problem.js b/thomson-problem.js index bb7466c..d13cc31 100644 --- a/thomson-problem.js +++ b/thomson-problem.js @@ -1,4 +1,4 @@ -function move_charges(charges) { +function move_charges(charges, force_constant) { for (let charge of charges) { charge.acceleration.setMag(0); } @@ -8,15 +8,21 @@ function move_charges(charges) { charges[i].position, charges[j].position, ); - let acceleration_mag = 1 / displacement.mag() * 0.001; + const force_mag = 1 / displacement.magSq() * force_constant; + // XXX possible extension: divide by charge's mass + const ai_mag = force_mag; + const aj_mag = force_mag; let ai; - if (acceleration_mag === Infinity) { + let aj; + if (force_mag === Infinity) { ai = p5.Vector.random3D(); + aj = p5.Vector.random3D(); } else { ai = displacement.copy().normalize(); + aj = ai.copy().mult(-1); } - ai.mult(acceleration_mag); - let aj = p5.Vector.mult(ai, -1); + ai.mult(ai_mag); + aj.mult(aj_mag); project_onto_plane(ai, charges[i].position); project_onto_plane(aj, charges[j].position); charges[i].acceleration.add(ai); From 642d1c5cdd23dc25ef458f355864c126eb876b78 Mon Sep 17 00:00:00 2001 From: root <> Date: Sun, 27 Apr 2025 08:03:07 +0000 Subject: [PATCH 2/7] custom charge magnitude also: s/charge/particle to reduce confusion --- index.html | 20 +++++++++---------- sketch.js | 48 ++++++++++++++++++++++++---------------------- thomson-problem.js | 34 +++++++++++++++++--------------- 3 files changed, 54 insertions(+), 48 deletions(-) diff --git a/index.html b/index.html index 0e3fca4..3295298 100644 --- a/index.html +++ b/index.html @@ -86,16 +86,16 @@ aside > container > div {
- - - - - - - - - - + + + + + + + + + +
diff --git a/sketch.js b/sketch.js index d0a1b95..b8f9c3d 100644 --- a/sketch.js +++ b/sketch.js @@ -1,7 +1,7 @@ let camera; let red; -let charges = []; +let particles = []; let faces = []; let sphere_radius; @@ -14,6 +14,7 @@ let surface = SURFACE_CIRCLES; let physics = false; let skeleton = false; let polytope = false; +let charge = -1; let buttons_surface; let checkbox_physics; @@ -70,9 +71,9 @@ function draw() { camera.centerZ = 0; 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 (polytope) { if (physics || faces.length === 0) find_faces(); @@ -88,27 +89,27 @@ function face_dist_sq([v1, v2, v3]) { function find_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 k = 0; k < j; k += 1) { // Check if p1 p2 p3 form a face of the convex polytope // enclosing all vertices ... - const p1 = charges[i].position; - const p2 = charges[j].position; - const p3 = charges[k].position; + const p1 = particles[i].position; + const p2 = particles[j].position; + const p3 = particles[k].position; const normal = p5.Vector.sub(p2, p1).cross(p5.Vector.sub(p3, p1)); // ... by checking if the other vertices are on the same // side of the plane generated by p1 p2 p3 let plane_separates_vertices = 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) { if ( r === i || r === j || r === k || s === i || s === j || s === k ) continue; - const q1 = charges[r].position; - const q2 = charges[s].position; + const q1 = particles[r].position; + const q2 = particles[s].position; // Let l(t) := q1 + (q2 - q1) * t. // L := { l(t) : 0 <= t <= 1 } is the line segment // between q1 and q2. L intersects the plane @@ -133,7 +134,7 @@ function find_faces() { ); plane_separates_vertices ||= t >= 0 && t <= 1; 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 (plane_separates_vertices || euler_formula) break; @@ -170,21 +171,21 @@ function draw_skeleton(radius) { fill(0xff); sphere(4); stroke(0xbf); - for (let charge of charges) { + for (let particle of particles) { line( 0, 0, 0, - charge.position.x * radius, - charge.position.y * radius, - charge.position.z * radius, + particle.position.x * radius, + particle.position.y * radius, + particle.position.z * radius, ); } pop(); } -function make_charges(n) { - charges = []; +function make_particles(n) { + particles = []; for (let i = 0; i < n; i += 1) { let position; if (i === 0) { @@ -192,21 +193,22 @@ function make_charges(n) { } else { position = p5.Vector.random3D(); } - charges.push({ + particles.push({ position: position, velocity: createVector(), acceleration: createVector(), + charge: charge, color: red, }); } } -function draw_charges(radius) { +function draw_particles(radius) { push(); noStroke(); - for (let charge of charges.values()) { - ambientMaterial(charge.color); - let position = charge.position.copy(); + for (let particle of particles) { + ambientMaterial(particle.color); + let position = particle.position.copy(); position.mult(radius); push(); translate(position.x, position.y, position.z); @@ -292,7 +294,7 @@ function keyPressed() { } else if (key == 'g') { toggle_polytope(); } else if (key >= '0' && key <= '9') { - make_charges(int(key)); + make_particles(int(key)); faces = []; } } diff --git a/thomson-problem.js b/thomson-problem.js index d13cc31..3843784 100644 --- a/thomson-problem.js +++ b/thomson-problem.js @@ -1,14 +1,18 @@ -function move_charges(charges, force_constant) { - for (let charge of charges) { - charge.acceleration.setMag(0); +function move_particles(particles, force_constant) { + for (let particle of particles) { + 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) { const displacement = p5.Vector.sub( - charges[i].position, - charges[j].position, + particles[i].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 const ai_mag = force_mag; const aj_mag = force_mag; @@ -23,16 +27,16 @@ function move_charges(charges, force_constant) { } ai.mult(ai_mag); aj.mult(aj_mag); - project_onto_plane(ai, charges[i].position); - project_onto_plane(aj, charges[j].position); - charges[i].acceleration.add(ai); - charges[j].acceleration.add(aj); + project_onto_plane(ai, particles[i].position); + project_onto_plane(aj, particles[j].position); + particles[i].acceleration.add(ai); + particles[j].acceleration.add(aj); } } - for (let charge of charges) { - charge.velocity = charge.velocity.add(charge.acceleration); - charge.position = charge.position.add(charge.velocity); - charge.position.normalize(); + for (let particle of particles) { + particle.velocity = particle.velocity.add(particle.acceleration); + particle.position = particle.position.add(particle.velocity); + particle.position.normalize(); } } From be6d6ba000c81128d41bcc1fa382d1d5aaaa420c Mon Sep 17 00:00:00 2001 From: root <> Date: Sun, 27 Apr 2025 10:05:59 +0000 Subject: [PATCH 3/7] interface: set charge magnitude --- index.html | 14 ++++++++++++-- sketch.js | 7 +++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 3295298..c7f8f79 100644 --- a/index.html +++ b/index.html @@ -68,10 +68,16 @@ aside > container > div { @@ -118,6 +125,7 @@ Source code: https://g document.querySelectorAll(".js").forEach(e => e.style = "display:initial"); const input_charge = document.getElementById("input-charge"); input_charge.oninput(); +const button_particles = document.getElementById("button-particles"); diff --git a/sketch.js b/sketch.js index a13c15a..75eafd4 100644 --- a/sketch.js +++ b/sketch.js @@ -20,6 +20,7 @@ let buttons_surface; let checkbox_physics; let checkbox_skeleton; let checkbox_polytope; +let input_particles; let aside_size; @@ -52,6 +53,9 @@ function setup() { document.getElementById("button-surface-earth"), ] buttons_surface[surface].disabled = true; + + input_particles = document.getElementById("input-particles"); + input_particles.valueAsNumber = particles.length; } function windowResized() { @@ -71,11 +75,15 @@ function draw() { camera.centerZ = 0; make_lights(); - if (physics) move_particles(particles, 8e-4); + const polytope_is_slow = physics && particles.length > 40 || particles.length > 120; + const polytope_if_fast = polytope && !polytope_is_slow; + if (physics) { + move_particles(particles, 8e-4); + } draw_particles(sphere_radius); if (skeleton) draw_skeleton(sphere_radius); - if (polytope) { + if (polytope_if_fast) { if (physics || faces.length === 0) find_faces(); draw_faces(sphere_radius); } @@ -185,6 +193,8 @@ function draw_skeleton(radius) { } function make_particles(n) { + faces = []; + input_particles.value = `${n}`; particles = []; for (let i = 0; i < n; i += 1) { let position; @@ -295,7 +305,6 @@ function keyPressed() { toggle_polytope(); } else if (key >= '0' && key <= '9') { make_particles(int(key)); - faces = []; } } From afd33926119a2c2b14dd1b9f2654852683a7f52a Mon Sep 17 00:00:00 2001 From: root <> Date: Sun, 27 Apr 2025 10:09:16 +0000 Subject: [PATCH 5/7] bugfix: wrong polytope faces reproduce: 1. polytope on 2. polytope off 3. toggle physics on then off (want big change) 4. polytope on --- sketch.js | 1 + 1 file changed, 1 insertion(+) diff --git a/sketch.js b/sketch.js index 75eafd4..69cfa08 100644 --- a/sketch.js +++ b/sketch.js @@ -78,6 +78,7 @@ function draw() { const polytope_is_slow = physics && particles.length > 40 || particles.length > 120; const polytope_if_fast = polytope && !polytope_is_slow; if (physics) { + if (!polytope_if_fast) faces = []; move_particles(particles, 8e-4); } From 72a24ce13134260ba1a94a036cc9b52c3749f7cb Mon Sep 17 00:00:00 2001 From: root <> Date: Sun, 27 Apr 2025 10:11:07 +0000 Subject: [PATCH 6/7] keyboard shortcuts; ignore when some inputs active --- sketch.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sketch.js b/sketch.js index 69cfa08..79b2c4a 100644 --- a/sketch.js +++ b/sketch.js @@ -297,7 +297,26 @@ function make_lights() { function keyPressed() { if (key == ' ') { + if ( + document.activeElement !== input_charge && + document.activeElement !== document.body + ) return; toggle_physics(); + } else if (key == 's') { + toggle_physics(); + } else if (key == 'a') { + input_charge.valueAsNumber = 0; + input_charge.oninput(); + } else if (key == '[') { + input_charge.valueAsNumber -= Number(input_charge.step); + input_charge.oninput(); + } else if (key == ']') { + input_charge.valueAsNumber += Number(input_charge.step); + input_charge.oninput(); + } else if (key == '-') { + make_particles(max(0, int(particles.length) - 1)); + } else if (key == '=') { + make_particles(min(360, int(particles.length) + 1)); } else if (key == 'd') { set_surface((surface + 1) % 3); } else if (key == 'f') { @@ -305,6 +324,7 @@ function keyPressed() { } else if (key == 'g') { toggle_polytope(); } else if (key >= '0' && key <= '9') { + if (document.activeElement === input_particles) return; make_particles(int(key)); } } From 0fad69fda83795cf746713f0d1dda6cb289d6539 Mon Sep 17 00:00:00 2001 From: root <> Date: Sun, 27 Apr 2025 10:11:26 +0000 Subject: [PATCH 7/7] interface: bigger now --- index.html | 10 +++++----- sketch.js | 11 +++++++---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/index.html b/index.html index df02afb..9ed52e4 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,8 @@