sphere-electrons/sketch.js
2025-04-24 11:12:53 +00:00

178 lines
3.9 KiB
JavaScript

let charges = [];
let sphere_mode = 'circles';
let sphere_radius = 200;
let physics = false;
function preload() {
earth = loadImage("atlas1.jpg");
}
function setup() {
createCanvas(600, 600, WEBGL);
}
function draw() {
orbitControl();
background(50);
make_lights();
if (physics) {
move_charges();
}
draw_charges(sphere_radius);
draw_sphere(sphere_radius, 25);
}
function move_charges() {
for (charge of charges) {
charge.acceleration.setMag(0);
}
for (let i = 0; i < charges.length; i += 1) {
for (let j = 0; j < i; j += 1) {
const displacement = p5.Vector.sub(
charges[i].position,
charges[j].position,
);
const acceleration_mag = 1 / displacement.mag() * 0.005;
let ai = displacement.copy().normalize().mult(acceleration_mag);
let aj = p5.Vector.mult(ai, -1);
let ai_norm = project_onto_plane(ai, charges[i].position);
let aj_norm = project_onto_plane(aj, charges[j].position);
charges[i].acceleration.add(ai_norm);
charges[j].acceleration.add(aj_norm);
}
}
for (let i = 0; i < charges.length; i += 1) {
let charge = charges[i];
charge.velocity = charge.velocity.add(charge.acceleration);
//charge.velocity = project_onto_plane(charge.velocity, charge.position);
charge.position = charge.position.add(charge.velocity);
charge.position.normalize();
}
}
function project_onto_unit_vector(v, unit_vector) {
let size = p5.Vector.dot(v, unit_vector);
return p5.Vector.mult(unit_vector, size);
}
/// Project `v` onto the plane normal to `unit_vector`
function project_onto_plane(v, unit_vector) {
let v_proj = project_onto_unit_vector(v, unit_vector);
return v.sub(v_proj)
}
function make_charges(n) {
charges = [];
for (let i = 0; i < n; i += 1) {
let position;
if (i === 0) {
position = createVector(0, -1, 0);
} else {
const lat = random(-TAU / 4, TAU / 4);
const lon = random(0, TAU);
position = createVector(
cos(lat) * cos(lon),
sin(lat),
cos(lat) * sin(lon),
);
}
charges.push({
position: position,
velocity: createVector(),
acceleration: createVector(),
});
}
}
function draw_charges(radius) {
push();
noStroke();
ambientMaterial(0xbf, 0x00, 0x00);
for (let charge of charges.values()) {
let position = charge.position.copy();
position.mult(radius);
push();
translate(position.x, position.y, position.z);
sphere(15);
pop();
}
pop();
}
function draw_sphere(radius, n_axis_circles) {
stroke(0x3f);
noFill();
push();
rotateX(TAU / 4);
draw_circles(
radius,
sphere_mode === 'earth' ? 2 : n_axis_circles,
color(0x00, 0x9f, 0xff),
color(0xff, 0x9f, 0x00),
);
pop();
push();
rotateY(TAU / 4);
draw_circles(
radius,
sphere_mode === 'earth' ? 2 : n_axis_circles,
color(0xff, 0x00, 0xff),
color(0x00, 0xff, 0x00),
);
pop();
if (sphere_mode === 'earth') {
noStroke();
noFill();
tint(0xff, 0x9f);
texture(earth);
push();
rotateY(TAU / 4);
sphere(radius);
pop();
}
}
function draw_circles(radius, n_circles, pole_1_color, pole_2_color) {
push();
stroke(pole_1_color);
translate(0, 0, -radius);
point(0, 0);
pop();
for (let i = 1; i < n_circles; i += 1) {
const angle = map(i, 0, n_circles - 1, -TAU / 4, TAU / 4);
const circle_radius = radius * cos(angle);
push();
translate(0, 0, radius * sin(angle));
circle(0, 0, circle_radius * 2);
pop();
}
push();
stroke(pole_2_color);
translate(0, 0, radius);
point(0, 0);
pop();
}
function make_lights() {
let light = createVector(0, 1, -1);
light.normalize();
directionalLight(0x1f, 0x1f, 0x1f, light);
ambientLight(0xbf);
}
function keyPressed() {
if (key == 'd') {
sphere_mode = sphere_mode === 'earth' ? 'circles' : 'earth';
} else if (key == ' ') {
physics = !physics;
} else if (key >= '0' && key <= '9') {
make_charges(int(key));
}
}