This commit is contained in:
Jürg Hallenbarter
2026-04-01 06:34:17 +00:00
parent 68411e3314
commit 10c9cad0a6
8 changed files with 134410 additions and 0 deletions

18
Cubes.js Normal file
View File

@@ -0,0 +1,18 @@
function setup() {
createCanvas(500, 500);
background(255,255,255)
noFill();
for (i = 0; i < 30; i++){
circle(width/2 +random(-width/10, width/10), height/2 +random(-height/10, height/10), random(15)*i);
}
}
function draw() {
}
function mousePressed(){
}

11
RecursiveCubes.js Normal file
View File

@@ -0,0 +1,11 @@
var numberOfSquaresX = 4;
var numberOfSquaresY = 4;
function setup() {
createCanvas(500, 500);
background(255,255,255);
}
function draw() {
}

33
calmCircles.js Normal file
View File

@@ -0,0 +1,33 @@
function setup() {
createCanvas(1000, 1000);
}
function draw() {
background(random(100,200), 0, random(100,200));
for (let i = 0; i <= width * sqrt(2); i += 10){
stroke(random(255),random(255),random(255))
strokeWeight(2)
noFill()
circle(width/2 + random(0,30),height/2 + random(0,30),i)
const strokeValue = i%20 ? 100 : 0;
stroke(strokeValue)
}
for (let i = 0; i < 50; i++){
stroke(random(255),random(255),random(255))
let x = random(0, width)
let y = random(0, height)
line(width/2,height/2,x,y)
}
}
function mousePressed(){
for (let i = 0; i < 50; i++){
stroke(random(255),random(255),random(255))
let x = random(0, width)
let y = random(0, height)
line(mouseX,mouseY,x,y)
}
}

13
css/style.css Normal file
View File

@@ -0,0 +1,13 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #f0f0f0;
}

143
index.html Normal file
View File

@@ -0,0 +1,143 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>p5.js Sketch Selector</title>
<link rel="stylesheet" href="css/style.css">
<script src="js/p5.js"></script>
<script src="js/addons/p5.sound.min.js"></script>
<style>
#sketch-selector {
position: fixed;
top: 10px;
right: 10px;
background: rgba(255, 255, 255, 0.9);
padding: 8px 12px;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0,0,0,0.2);
font-family: sans-serif;
z-index: 1000;
font-size: 14px;
}
select, button {
margin-left: 5px;
}
#status {
position: fixed;
bottom: 10px;
right: 10px;
background: rgba(0,0,0,0.7);
color: white;
padding: 4px 8px;
border-radius: 4px;
font-family: monospace;
font-size: 12px;
z-index: 1000;
}
</style>
</head>
<body>
<div id="sketch-selector">
<label>Sketch: </label>
<select id="sketchSelect"></select>
<button id="loadBtn">Load</button>
</div>
<div id="status">No sketch loaded.</div>
<script>
// Helper: reload page with selected sketch
function reloadWithSketch(filename) {
if (filename) {
window.location.search = `?sketch=${encodeURIComponent(filename)}`;
} else {
window.location.search = ''; // clear
}
}
// Fallback list of sketch files (if auto-detection fails)
const FALLBACK_SKETCHES = [
"calmCircles.js",
"Cubes.js",
"labyrinth.js",
"RecursiveCubes.js"
];
// Auto-detect .js files in the same directory
async function detectSketches() {
try {
const response = await fetch(window.location.pathname);
const html = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const links = doc.querySelectorAll('a');
const jsFiles = [];
for (let link of links) {
const href = link.getAttribute('href');
if (href && href.endsWith('.js') && !href.includes('/') && !href.includes('p5')) {
jsFiles.push(href);
}
}
if (jsFiles.length > 0) return jsFiles;
} catch (err) {
console.warn('Auto-detection failed, using fallback list:', err);
}
return FALLBACK_SKETCHES;
}
// Update dropdown with detected sketch files
function updateDropdown(selectElement, sketches, currentSketch) {
selectElement.innerHTML = '';
for (let sk of sketches) {
const option = document.createElement('option');
option.value = sk;
option.textContent = sk;
if (sk === currentSketch) option.selected = true;
selectElement.appendChild(option);
}
}
// Main initialisation
(async function() {
const sketches = await detectSketches();
const currentSketch = new URLSearchParams(window.location.search).get('sketch');
const selectEl = document.getElementById('sketchSelect');
const loadBtn = document.getElementById('loadBtn');
const statusEl = document.getElementById('status');
// Fill dropdown
updateDropdown(selectEl, sketches, currentSketch);
// Show current sketch in status
if (currentSketch && sketches.includes(currentSketch)) {
statusEl.textContent = `Running: ${currentSketch}`;
} else {
statusEl.textContent = 'No sketch selected.';
}
// Handle load button: reload page with selected sketch
loadBtn.addEventListener('click', () => {
const selected = selectEl.value;
if (selected) {
reloadWithSketch(selected);
}
});
})();
</script>
<!--
Synchronously load the sketch specified in the URL parameter.
This must be placed after the selector UI but before any other dynamic scripts.
It ensures the sketch's setup() and draw() are defined before p5.js finishes loading.
-->
<script>
(function() {
const params = new URLSearchParams(window.location.search);
const sketchFile = params.get('sketch');
if (sketchFile) {
document.write('<script src="' + sketchFile + '"><\/script>');
}
})();
</script>
</body>
</html>

7
js/addons/p5.sound.min.js vendored Normal file

File diff suppressed because one or more lines are too long

133889
js/p5.js Normal file

File diff suppressed because one or more lines are too long

296
labyrinth.js Normal file
View File

@@ -0,0 +1,296 @@
var gridSize = 10;
var labyrinth = [];
var waterParticles = [];
var gravity = 0.5;
var damping = 0.98;
var radius = gridSize / 4;
var substeps = 4;
var wallFriction = 0.99;
var cohesionStrength = 0.1;
var cohesionRadius = radius * 4;
var wallGrid = [];
var rows, cols;
function setup() {
createCanvas(500, 500);
rows = ceil(height / gridSize);
cols = ceil(width / gridSize);
for (let i = 0; i < rows; i++) {
wallGrid[i] = [];
for (let j = 0; j < cols; j++) {
wallGrid[i][j] = [];
}
}
for (let i = 0; i < width; i += gridSize) {
for (let j = 0; j < height; j += gridSize) {
makeLabyrinth(i, j);
}
}
}
function makeLabyrinth(i, j) {
const directions = [
[i - gridSize, j + gridSize],
[i + gridSize, j + gridSize],
[i - gridSize, j - gridSize],
[i + gridSize, j - gridSize]
];
const [x, y] = directions[Math.floor(Math.random() * directions.length)];
const wall = [i, j, x, y];
labyrinth.push(wall);
// Sort directions to make wall detection easier
let minX = min(i, x);
let maxX = max(i, x);
let minY = min(j, y);
let maxY = max(j, y);
let startCol = floor(minX / gridSize);
let endCol = floor(maxX / gridSize);
let startRow = floor(minY / gridSize);
let endRow = floor(maxY / gridSize);
for (let r = startRow; r <= endRow; r++) {
for (let c = startCol; c <= endCol; c++) {
if (r >= 0 && r < rows && c >= 0 && c < cols) {
wallGrid[r][c].push(wall);
}
}
}
}
// ---------- AI generated: pointtosegment distance for wall collision ----------
function pointToSegmentDistance(px, py, x1, y1, x2, y2) {
let ax = px - x1;
let ay = py - y1;
let bx = x2 - x1;
let by = y2 - y1;
let len2 = bx * bx + by * by;
if (len2 === 0) {
let dist = sqrt(ax * ax + ay * ay);
let nx = ax / (dist + 1e-8);
let ny = ay / (dist + 1e-8);
return { dist, nx, ny };
}
let t = (ax * bx + ay * by) / len2;
t = constrain(t, 0, 1);
let closestX = x1 + t * bx;
let closestY = y1 + t * by;
let dx = px - closestX;
let dy = py - closestY;
let dist = sqrt(dx * dx + dy * dy);
let nx = dx / (dist + 1e-8);
let ny = dy / (dist + 1e-8);
return { dist, nx, ny };
}
// ---------- AI generated: wall collision resolution (push out, reflect) ----------
function resolveWallCollision(p, stepX, stepY) {
p.x += stepX;
p.y += stepY;
for (let iter = 0; iter < 3; iter++) {
let cellX = floor(p.x / gridSize);
let cellY = floor(p.y / gridSize);
let minDist = Infinity;
let bestNx = 0, bestNy = 0;
for (let dx = -1; dx <= 1; dx++) {
for (let dy = -1; dy <= 1; dy++) {
let nx = cellX + dx;
let ny = cellY + dy;
if (nx >= 0 && nx < cols && ny >= 0 && ny < rows) {
for (let w of wallGrid[ny][nx]) {
let { dist, nx: nX, ny: nY } = pointToSegmentDistance(p.x, p.y, w[0], w[1], w[2], w[3]);
if (dist < minDist) {
minDist = dist;
bestNx = nX;
bestNy = nY;
}
}
}
}
}
if (minDist < radius) {
let overlap = radius - minDist;
p.x += bestNx * overlap;
p.y += bestNy * overlap;
let dot = p.vx * bestNx + p.vy * bestNy;
if (dot < 0) {
p.vx -= dot * bestNx;
p.vy -= dot * bestNy;
p.vx *= wallFriction;
p.vy *= wallFriction;
}
} else {
break;
}
}
}
// ---------- AI generated: sticky particleparticle collision ----------
function handleParticleCollision(p1, p2) {
let dx = p2.x - p1.x;
let dy = p2.y - p1.y;
let dist = sqrt(dx * dx + dy * dy);
let minDist = radius * 2;
if (dist < minDist) {
let overlap = minDist - dist;
let nx = dx / dist;
let ny = dy / dist;
// Separate
p1.x -= nx * overlap * 0.5;
p1.y -= ny * overlap * 0.5;
p2.x += nx * overlap * 0.5;
p2.y += ny * overlap * 0.5;
// Velocity exchange (low restitution for stickiness)
let vrelx = p2.vx - p1.vx;
let vrely = p2.vy - p1.vy;
let dot = vrelx * nx + vrely * ny;
if (dot < 0) {
let e = 0.2;
let imp = (1 + e) * dot / 2;
p1.vx += imp * nx;
p1.vy += imp * ny;
p2.vx -= imp * nx;
p2.vy -= imp * ny;
}
}
}
// ---------- AI generated: cohesion (attraction between nearby particles) ----------
function applyCohesion(p, particleGrid, cellX, cellY) {
let fx = 0, fy = 0;
for (let dx = -1; dx <= 1; dx++) {
for (let dy = -1; dy <= 1; dy++) {
let nx = cellX + dx;
let ny = cellY + dy;
if (nx >= 0 && nx < cols && ny >= 0 && ny < rows) {
for (let other of particleGrid[ny][nx]) {
if (other === p) continue;
let ddx = other.x - p.x;
let ddy = other.y - p.y;
let dist = sqrt(ddx*ddx + ddy*ddy);
if (dist > 0 && dist < cohesionRadius) {
let strength = cohesionStrength * (1 - dist / cohesionRadius);
fx += ddx * strength;
fy += ddy * strength;
}
}
}
}
}
p.vx += fx;
p.vy += fy;
}
function draw() {
background(255);
for (let w of labyrinth) {
stroke(0);
strokeWeight(1);
line(w[0], w[1], w[2], w[3]);
}
if (mouseIsPressed) {
waterParticles.push({
x: mouseX,
y: mouseY,
vx: random(-1, 1),
vy: random(-1, 1)
});
}
// ---------- AI generated: build particle grid for cohesion & collisions ----------
let particleGrid = [];
for (let i = 0; i < rows; i++) {
particleGrid[i] = [];
for (let j = 0; j < cols; j++) {
particleGrid[i][j] = [];
}
}
for (let p of waterParticles) {
let cellX = floor(p.x / gridSize);
let cellY = floor(p.y / gridSize);
if (cellX >= 0 && cellX < cols && cellY >= 0 && cellY < rows) {
particleGrid[cellY][cellX].push(p);
}
}
for (let p of waterParticles) {
let cellX = floor(p.x / gridSize);
let cellY = floor(p.y / gridSize);
applyCohesion(p, particleGrid, cellX, cellY);
}
for (let p of waterParticles) {
p.vy += gravity;
p.vx *= damping;
p.vy *= damping;
}
for (let p of waterParticles) {
let stepX = p.vx / substeps;
let stepY = p.vy / substeps;
for (let s = 0; s < substeps; s++) {
resolveWallCollision(p, stepX, stepY);
}
p.x = constrain(p.x, radius, width - radius);
p.y = constrain(p.y, radius, height - radius);
if (p.x <= radius || p.x >= width - radius) p.vx *= -0.9;
if (p.y <= radius || p.y >= height - radius) p.vy *= -0.9;
}
for (let i = waterParticles.length-1; i >= 0; i--) {
let p = waterParticles[i];
if (p.y + radius >= height) {
waterParticles.splice(i, 1);
}
}
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
particleGrid[i][j] = [];
}
}
for (let p of waterParticles) {
let cellX = floor(p.x / gridSize);
let cellY = floor(p.y / gridSize);
if (cellX >= 0 && cellX < cols && cellY >= 0 && cellY < rows) {
particleGrid[cellY][cellX].push(p);
}
}
let handled = new Array(waterParticles.length).fill(false);
for (let i = 0; i < waterParticles.length; i++) {
if (handled[i]) continue;
let p1 = waterParticles[i];
let cellX = floor(p1.x / gridSize);
let cellY = floor(p1.y / gridSize);
for (let dx = -1; dx <= 1; dx++) {
for (let dy = -1; dy <= 1; dy++) {
let nx = cellX + dx;
let ny = cellY + dy;
if (nx >= 0 && nx < cols && ny >= 0 && ny < rows) {
for (let p2 of particleGrid[ny][nx]) {
if (p2 === p1) continue;
let j = waterParticles.indexOf(p2);
if (j <= i) continue;
handleParticleCollision(p1, p2);
}
}
}
}
handled[i] = true;
}
fill(0, 0, 255);
noStroke();
for (let p of waterParticles) {
ellipse(p.x, p.y, radius * 2, radius * 2);
}
}