let charges = []; let sphere_radius = 200; let physics = false; let earth = false; let skeleton = false; let planes = false; let red; let yellow; function preload() { earth_image = loadImage("atlas1.jpg"); } function setup() { createCanvas(600, 600, WEBGL); red = color(0xbf, 0x00, 0x00); yellow = color(0xff, 0xff, 0x00); } function draw() { orbitControl(); background(50); make_lights(); if (physics) { move_charges(charges); } if (skeleton) { draw_skeleton(sphere_radius); } if (planes) { let p_charge = charges[0]; let a_charge = charges[1]; let b_charge = charges[2]; p_charge.color = yellow; a_charge.color = yellow; b_charge.color = yellow; let p = p_charge.position; let a = a_charge.position; let b = b_charge.position; let n = p5.Vector.sub(p, a).cross(p5.Vector.sub(p, b)); n.normalize(); // unnecessary push(); strokeWeight(5); stroke(0x7f); line( 0, 0, 0, n.x * sphere_radius, n.y * sphere_radius, n.z * sphere_radius, ) fill(0xff); strokeWeight(3); stroke(0x00); beginShape(TRIANGLES); const v1 = p5.Vector.mult(p, sphere_radius); const v2 = p5.Vector.mult(a, sphere_radius); const v3 = p5.Vector.mult(b, sphere_radius); vertex(v1.x, v1.y, v1.z); vertex(v2.x, v2.y, v2.z); vertex(v3.x, v3.y, v3.z); endShape(); pop(); for (let i = 4; i < charges.length; i += 1) { for (let j = 3; j < i; j += 1) { push(); const u = charges[i].position; const v = charges[j].position; const t = p5.Vector.dot(p5.Vector.sub(p, u), n) / p5.Vector.dot(p5.Vector.sub(v, u), n); const intersects_plane = t >= 0 && t <= 1; if (intersects_plane) { stroke(0xff, 0x1f, 0x00); } else { stroke(0x00, 0xff, 0x00); } strokeWeight(3); line( charges[i].position.x * sphere_radius, charges[i].position.y * sphere_radius, charges[i].position.z * sphere_radius, charges[j].position.x * sphere_radius, charges[j].position.y * sphere_radius, charges[j].position.z * sphere_radius, ); pop(); } } } else { for (let charge of charges) { charge.color = red; } } draw_charges(sphere_radius); draw_sphere(sphere_radius, 25); } function draw_skeleton(radius) { push(); noStroke(); fill(0xff); sphere(4); stroke(0xbf); for (let charge of charges) { line( 0, 0, 0, charge.position.x * radius, charge.position.y * radius, charge.position.z * radius, ); } pop(); } 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(), color: red, }); } } function draw_charges(radius) { push(); noStroke(); for (let charge of charges.values()) { ambientMaterial(charge.color); 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, earth ? 2 : n_axis_circles, color(0x00, 0x9f, 0xff), color(0xff, 0x9f, 0x00), ); pop(); push(); rotateY(TAU / 4); draw_circles( radius, earth ? 2 : n_axis_circles, color(0xff, 0x00, 0xff), color(0x00, 0xff, 0x00), ); pop(); if (earth) { noStroke(); noFill(); tint(0xff, 0x9f); texture(earth_image); 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 == ' ') { physics = !physics; } else if (key == 'd') { earth = !earth; } else if (key == 'f') { skeleton = !skeleton; } else if (key == 'g') { planes = !planes; } else if (key >= '0' && key <= '9') { make_charges(int(key)); } } // TODO draw faces // algorithm: choose 3 vertices until 2-partition of other vertices has one empty set // done when V - E + F = 2. V is known. count E and F while creating faces