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