From cec27160f4e75883f5353ebf273f3b092b6609a4 Mon Sep 17 00:00:00 2001 From: root <> Date: Fri, 25 Apr 2025 17:14:31 +0000 Subject: [PATCH] convex polyhedron --- sketch.js | 149 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 83 insertions(+), 66 deletions(-) diff --git a/sketch.js b/sketch.js index f97897e..db98094 100644 --- a/sketch.js +++ b/sketch.js @@ -1,3 +1,6 @@ +let camera; +let red; + let charges = []; let sphere_radius = 200; @@ -7,8 +10,6 @@ let earth = false; let skeleton = false; let planes = false; -let red; -let yellow; function preload() { earth_image = loadImage("atlas1.jpg"); @@ -16,8 +17,8 @@ function preload() { function setup() { createCanvas(600, 600, WEBGL); + camera = createCamera(); red = color(0xbf, 0x00, 0x00); - yellow = color(0xff, 0xff, 0x00); } function draw() { @@ -25,76 +26,92 @@ function draw() { background(50); make_lights(); - if (physics) { - move_charges(charges); - } - if (skeleton) { - draw_skeleton(sphere_radius); - } + if (physics) move_charges(charges); - 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); + draw_charges(sphere_radius); + draw_sphere(sphere_radius, 25); + if (skeleton) draw_skeleton(sphere_radius); + if (planes) draw_planes(sphere_radius); +} + +function face_dist_sq([v1, v2, v3]) { + const center = p5.Vector.add(v1, v2).add(v3).mult(1 / 3); + return createVector(camera.eyeX, camera.eyeY, camera.eyeZ).sub(center).magSq(); +}; + +function draw_planes(radius) { + let faces = []; + for (let i = 2; i < charges.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 polyhedron + // enclosing all vertices ... + const p1 = charges[i].position; + const p2 = charges[j].position; + const p3 = charges[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; + for (let r = 1; r < charges.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; + // 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 + // generated by p1 p2 p3 iff l(t) intersects with + // the plane for some 0 <= t <= 1. + // + // If L intersects the plane, q1 and q2 are on + // opposite sides of the plane generated by p1 p2 p3, + // so p1 p2 p3 can't be a face. If we want the + // polyhedron to be convex. Which we do. + // + // A point k is on the plane generated by p1 p2 p3 iff + // dot(k - p1, normal) = 0. Let n := normal. + // + // dot(l(t) - p1, n) = 0 + // iff dot(q1 + (q2 - q1) * t - p1, n) = 0 + // iff dot(q1 - p1, n) + dot(q2 - q2, n) * t = 0 + // iff t = dot(p1 - q1, n) / dot(q2 - q2, n) + const t = ( + p5.Vector.dot(p5.Vector.sub(p1, q1), normal) / + p5.Vector.dot(p5.Vector.sub(q2, q1), normal) + ); + plane_separates_vertices ||= t >= 0 && t <= 1; + if (plane_separates_vertices) break; + } + if (plane_separates_vertices) break; + } + if (!plane_separates_vertices) { + faces.push([ + p5.Vector.mult(p1, radius), + p5.Vector.mult(p2, radius), + p5.Vector.mult(p3, radius), + ]); + } + } + } + } + // fix OpenGL stacking alpha behaviour + faces.sort((a, b) => face_dist_sq(b) - face_dist_sq(a)); + push(); + strokeWeight(2); + stroke(0x00); + for ([v1, v2, v3] of faces) { + fill(0xbf, 0x7f); 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); + pop(); } function draw_skeleton(radius) {