sphere-electrons/sketch.js
root 838eae2cbf polyhedron alg progress: face separates vertices
the algorithm so far:
- select 3 vertices
- draw a triangle between them
- for all vertex pairs (non-triangle vertices):
-- draw a line between them
-- if line intersects with triangle plane, red
-- otherwise, green

if the line is red the two vertices are on other sides of the
triangle plane, and this triangle can't be a face, because we
want the polyhedron to be convex

to demo: press 9, press g, press space; press 9 over and over
2025-04-25 17:15:00 +00:00

237 lines
5.1 KiB
JavaScript

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