Compare commits
5 commits
eeb1beaea3
...
33aab6af5c
Author | SHA1 | Date | |
---|---|---|---|
![]() |
33aab6af5c | ||
![]() |
d10f77d420 | ||
![]() |
d57d49a194 | ||
![]() |
769043c5cb | ||
![]() |
2655d15bc9 |
3 changed files with 164 additions and 24 deletions
92
index.html
92
index.html
|
@ -6,18 +6,108 @@
|
||||||
<title>p5.js</title>
|
<title>p5.js</title>
|
||||||
<script src="lib/p5.js"></script>
|
<script src="lib/p5.js"></script>
|
||||||
<style>
|
<style>
|
||||||
|
:root {
|
||||||
|
--aside-size: 218px;
|
||||||
|
}
|
||||||
|
.js {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
noscript {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: cornflowerblue;
|
||||||
|
}
|
||||||
body {
|
body {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background-color: #1b1b1b;
|
background-color: #1b1b1b;
|
||||||
overflow-y: hidden;
|
overflow: hidden;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: var(--aside-size) auto;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
aside {
|
||||||
|
padding: 20px;
|
||||||
|
height: 100vh;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
aside > container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
aside button {
|
||||||
|
font-size: 20px;
|
||||||
|
min-width: 27px;
|
||||||
|
}
|
||||||
|
aside input[type="checkbox"] {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
aside > container > div {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
@media screen and (orientation:portrait) {
|
||||||
|
body {
|
||||||
|
grid-template-columns: none;
|
||||||
|
grid-template-rows: auto var(--aside-size);
|
||||||
|
}
|
||||||
|
aside {
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
|
aside > container {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<aside class="js">
|
||||||
|
<container>
|
||||||
|
<div style="width:100%">
|
||||||
|
<button onclick="toggle_physics()">Physics</button>
|
||||||
|
<input id="checkbox-physics" type="checkbox" disabled>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button onclick="toggle_skeleton()">Skeleton</button>
|
||||||
|
<input id="checkbox-skeleton" type="checkbox" disabled>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button onclick="toggle_polytope()">Polytope</button>
|
||||||
|
<input id="checkbox-polytope" type="checkbox" disabled>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button id="button-surface-none" onclick="set_surface(SURFACE_NONE)">Hide surface</button>
|
||||||
|
<button id="button-surface-circles" onclick="set_surface(SURFACE_CIRCLES)">Wireframe</button>
|
||||||
|
<button id="button-surface-earth" onclick="set_surface(SURFACE_EARTH)">Earth</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button onclick="make_charges(0);faces=[]">0</button>
|
||||||
|
<button onclick="make_charges(1);faces=[]">1</button>
|
||||||
|
<button onclick="make_charges(2);faces=[]">2</button>
|
||||||
|
<button onclick="make_charges(3);faces=[]">3</button>
|
||||||
|
<button onclick="make_charges(4);faces=[]">4</button>
|
||||||
|
<button onclick="make_charges(5);faces=[]">5</button>
|
||||||
|
<button onclick="make_charges(6);faces=[]">6</button>
|
||||||
|
<button onclick="make_charges(7);faces=[]">7</button>
|
||||||
|
<button onclick="make_charges(8);faces=[]">8</button>
|
||||||
|
<button onclick="make_charges(9);faces=[]">9</button>
|
||||||
|
</div>
|
||||||
|
</container>
|
||||||
|
</aside>
|
||||||
<main>
|
<main>
|
||||||
</main>
|
</main>
|
||||||
|
<noscript>
|
||||||
|
This page requires JavaScript.
|
||||||
|
<br>
|
||||||
|
Source code: <a href="https://git.atomic.garden/root/sphere-charges">https://git.atomic.garden/root/sphere-charges</a>
|
||||||
|
</noscript>
|
||||||
<script src="thomson-problem.js"></script>
|
<script src="thomson-problem.js"></script>
|
||||||
<script src="sketch.js"></script>
|
<script src="sketch.js"></script>
|
||||||
|
<script>document.querySelectorAll(".js").forEach(e => e.style = "display:initial");</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
90
sketch.js
90
sketch.js
|
@ -4,7 +4,7 @@ let red;
|
||||||
let charges = [];
|
let charges = [];
|
||||||
let faces = [];
|
let faces = [];
|
||||||
|
|
||||||
let sphere_radius = 200;
|
let sphere_radius;
|
||||||
|
|
||||||
const SURFACE_NONE = 0;
|
const SURFACE_NONE = 0;
|
||||||
const SURFACE_CIRCLES = 1;
|
const SURFACE_CIRCLES = 1;
|
||||||
|
@ -13,7 +13,14 @@ const SURFACE_EARTH = 2;
|
||||||
let surface = SURFACE_CIRCLES;
|
let surface = SURFACE_CIRCLES;
|
||||||
let physics = false;
|
let physics = false;
|
||||||
let skeleton = false;
|
let skeleton = false;
|
||||||
let polyhedron = false;
|
let polytope = false;
|
||||||
|
|
||||||
|
let buttons_surface;
|
||||||
|
let checkbox_physics;
|
||||||
|
let checkbox_skeleton;
|
||||||
|
let checkbox_polytope;
|
||||||
|
|
||||||
|
let aside_size;
|
||||||
|
|
||||||
|
|
||||||
function preload() {
|
function preload() {
|
||||||
|
@ -21,25 +28,53 @@ function preload() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setup() {
|
function setup() {
|
||||||
createCanvas(windowWidth, windowHeight, WEBGL);
|
createCanvas(0, 0, WEBGL);
|
||||||
|
aside_size = int(getComputedStyle(document.documentElement).getPropertyValue('--aside-size').replace('px', ''));
|
||||||
|
windowResized();
|
||||||
|
|
||||||
camera = createCamera();
|
camera = createCamera();
|
||||||
red = color(0xbf, 0x00, 0x00);
|
red = color(0xbf, 0x00, 0x00);
|
||||||
|
sphere_radius = min(250, min(width, height) / 2 * 0.8);
|
||||||
|
|
||||||
|
checkbox_physics = document.getElementById("checkbox-physics");
|
||||||
|
checkbox_physics.checked = physics;
|
||||||
|
|
||||||
|
checkbox_skeleton = document.getElementById("checkbox-skeleton");
|
||||||
|
checkbox_skeleton.checked = skeleton;
|
||||||
|
|
||||||
|
checkbox_polytope = document.getElementById("checkbox-polytope");
|
||||||
|
checkbox_polytope.checked = polytope;
|
||||||
|
|
||||||
|
buttons_surface = [
|
||||||
|
document.getElementById("button-surface-none"),
|
||||||
|
document.getElementById("button-surface-circles"),
|
||||||
|
document.getElementById("button-surface-earth"),
|
||||||
|
]
|
||||||
|
buttons_surface[surface].disabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function windowResized() {
|
function windowResized() {
|
||||||
resizeCanvas(windowWidth, windowHeight);
|
if (windowWidth >= windowHeight) {
|
||||||
|
resizeCanvas(windowWidth - aside_size, windowHeight);
|
||||||
|
} else {
|
||||||
|
resizeCanvas(windowWidth, windowHeight - aside_size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw() {
|
function draw() {
|
||||||
orbitControl();
|
|
||||||
background(50);
|
background(50);
|
||||||
|
|
||||||
|
orbitControl();
|
||||||
|
camera.centerX = 0;
|
||||||
|
camera.centerY = 0;
|
||||||
|
camera.centerZ = 0;
|
||||||
|
|
||||||
make_lights();
|
make_lights();
|
||||||
if (physics) move_charges(charges);
|
if (physics) move_charges(charges);
|
||||||
|
|
||||||
draw_charges(sphere_radius);
|
draw_charges(sphere_radius);
|
||||||
if (skeleton) draw_skeleton(sphere_radius);
|
if (skeleton) draw_skeleton(sphere_radius);
|
||||||
if (polyhedron) {
|
if (polytope) {
|
||||||
if (physics || faces.length === 0) find_faces();
|
if (physics || faces.length === 0) find_faces();
|
||||||
draw_faces(sphere_radius);
|
draw_faces(sphere_radius);
|
||||||
}
|
}
|
||||||
|
@ -56,7 +91,7 @@ function find_faces() {
|
||||||
for (let i = 2; i < charges.length; i += 1) {
|
for (let i = 2; i < charges.length; i += 1) {
|
||||||
for (let j = 1; j < i; j += 1) {
|
for (let j = 1; j < i; j += 1) {
|
||||||
for (let k = 0; k < j; k += 1) {
|
for (let k = 0; k < j; k += 1) {
|
||||||
// Check if p1 p2 p3 form a face of the convex polyhedron
|
// Check if p1 p2 p3 form a face of the convex polytope
|
||||||
// enclosing all vertices ...
|
// enclosing all vertices ...
|
||||||
const p1 = charges[i].position;
|
const p1 = charges[i].position;
|
||||||
const p2 = charges[j].position;
|
const p2 = charges[j].position;
|
||||||
|
@ -83,7 +118,7 @@ function find_faces() {
|
||||||
// If L intersects the plane, q1 and q2 are on
|
// If L intersects the plane, q1 and q2 are on
|
||||||
// opposite sides of the plane generated by p1 p2 p3,
|
// opposite sides of the plane generated by p1 p2 p3,
|
||||||
// so p1 p2 p3 can't be a face. If we want the
|
// so p1 p2 p3 can't be a face. If we want the
|
||||||
// polyhedron to be convex. Which we do.
|
// polytope to be convex. Which we do.
|
||||||
//
|
//
|
||||||
// A point k is on the plane generated by p1 p2 p3 iff
|
// A point k is on the plane generated by p1 p2 p3 iff
|
||||||
// dot(k - p1, normal) = 0. Let n := normal.
|
// dot(k - p1, normal) = 0. Let n := normal.
|
||||||
|
@ -118,8 +153,8 @@ function draw_faces(radius) {
|
||||||
push();
|
push();
|
||||||
strokeWeight(2);
|
strokeWeight(2);
|
||||||
stroke(0x00);
|
stroke(0x00);
|
||||||
|
fill(0xbf, 0x7f);
|
||||||
for ([p1, p2, p3] of faces) {
|
for ([p1, p2, p3] of faces) {
|
||||||
fill(0xbf, 0x7f);
|
|
||||||
beginShape(TRIANGLES);
|
beginShape(TRIANGLES);
|
||||||
vertex(p1.x * radius, p1.y * radius, p1.z * radius);
|
vertex(p1.x * radius, p1.y * radius, p1.z * radius);
|
||||||
vertex(p2.x * radius, p2.y * radius, p2.z * radius);
|
vertex(p2.x * radius, p2.y * radius, p2.z * radius);
|
||||||
|
@ -155,13 +190,7 @@ function make_charges(n) {
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
position = createVector(0, -1, 0);
|
position = createVector(0, -1, 0);
|
||||||
} else {
|
} else {
|
||||||
const lat = random(-TAU / 4, TAU / 4);
|
position = p5.Vector.random3D();
|
||||||
const lon = random(0, TAU);
|
|
||||||
position = createVector(
|
|
||||||
cos(lat) * cos(lon),
|
|
||||||
sin(lat),
|
|
||||||
cos(lat) * sin(lon),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
charges.push({
|
charges.push({
|
||||||
position: position,
|
position: position,
|
||||||
|
@ -255,15 +284,36 @@ function make_lights() {
|
||||||
|
|
||||||
function keyPressed() {
|
function keyPressed() {
|
||||||
if (key == ' ') {
|
if (key == ' ') {
|
||||||
physics = !physics;
|
toggle_physics();
|
||||||
} else if (key == 'd') {
|
} else if (key == 'd') {
|
||||||
surface = (surface + 1) % 3;
|
set_surface((surface + 1) % 3);
|
||||||
} else if (key == 'f') {
|
} else if (key == 'f') {
|
||||||
skeleton = !skeleton;
|
toggle_skeleton();
|
||||||
} else if (key == 'g') {
|
} else if (key == 'g') {
|
||||||
polyhedron = !polyhedron;
|
toggle_polytope();
|
||||||
} else if (key >= '0' && key <= '9') {
|
} else if (key >= '0' && key <= '9') {
|
||||||
make_charges(int(key));
|
make_charges(int(key));
|
||||||
faces = [];
|
faces = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggle_physics() {
|
||||||
|
physics = !physics;
|
||||||
|
checkbox_physics.checked = physics;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_surface(value) {
|
||||||
|
surface = value;
|
||||||
|
for (let button of buttons_surface) button.disabled = false;
|
||||||
|
buttons_surface[value].disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle_skeleton() {
|
||||||
|
skeleton = !skeleton;
|
||||||
|
checkbox_skeleton.checked = skeleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle_polytope() {
|
||||||
|
polytope = !polytope;
|
||||||
|
checkbox_polytope.checked = polytope;
|
||||||
|
}
|
||||||
|
|
|
@ -11,11 +11,11 @@ function move_charges(charges) {
|
||||||
let acceleration_mag = 1 / displacement.mag() * 0.001;
|
let acceleration_mag = 1 / displacement.mag() * 0.001;
|
||||||
let ai;
|
let ai;
|
||||||
if (acceleration_mag === Infinity) {
|
if (acceleration_mag === Infinity) {
|
||||||
ai = createVector(random(-1, 1), random(-1, 1), random(-1, 1));
|
ai = p5.Vector.random3D();
|
||||||
} else {
|
} else {
|
||||||
ai = displacement.copy();
|
ai = displacement.copy().normalize();
|
||||||
}
|
}
|
||||||
ai = ai.normalize().mult(acceleration_mag);
|
ai.mult(acceleration_mag);
|
||||||
let aj = p5.Vector.mult(ai, -1);
|
let aj = p5.Vector.mult(ai, -1);
|
||||||
project_onto_plane(ai, charges[i].position);
|
project_onto_plane(ai, charges[i].position);
|
||||||
project_onto_plane(aj, charges[j].position);
|
project_onto_plane(aj, charges[j].position);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue