xxxxxxxxxx
247
let segments;
let particles;
let count;
let num_wild;
let quant;
// let caption;
// let fnt;
// Adapted from https://editor.p5js.org/peanutscratch/sketches/rk7Mi9USz
// which was adapted from code by Paul Bourke.
function intersect(p1, p2, p3, p4) {
const den = (p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y);
const numA = (p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x);
const numB = (p2.x - p1.x) * (p1.y - p3.y) - (p2.y - p1.y) * (p1.x - p3.x);
//Coincident? - If true, displays intersection in center of line segment
if (abs(numA) == 0 && abs(numB) == 0 && abs(den) == 0) {
return null;
}
//Parallel? - No intersection
if (abs(den) == 0) {
return null;
}
//Intersection?
const uA = numA / den;
const uB = numB / den;
//If both lie w/in the range of 0 to 1 then the intersection point is within both line segments.
if (uA < 0 || uA > 1 || uB < 0 || uB > 1) {
return null;
}
return createVector(p1.x + uA * (p2.x - p1.x), p1.y + uA * (p2.y - p1.y));
}
function createSystem() {
segments = [];
particles = [];
quant = int(random(8,13));
count = int(random(10,30));
num_wild = 0;
for (let idx = 0; idx < count; ++idx) {
let ang = random(TWO_PI);
let wild = true;
if( random(1) < 0.9 ) {
ang = int(random(quant)) * TWO_PI/quant;
wild = false;
} else {
++num_wild;
}
const p = createVector(10+random(width-20), 10+random(height-100));
particles.push({
start: p,
end: p,
dir: createVector(cos(ang), sin(ang)),
wild: wild
});
}
segments.push({
start: createVector(5, 5),
end: createVector(width-5, 5),
dir: createVector(1, 0),
});
segments.push({
start: createVector(5, height - 80),
end: createVector(width-5, height - 80),
dir: createVector(1, 0),
});
segments.push({
start: createVector(5, 5),
end: createVector(5, height-80),
dir: createVector(0, 1),
});
segments.push({
start: createVector(width-5, 5),
end: createVector(width-5, height-80),
dir: createVector(0, 1),
});
// caption = "" + (count-num_wild) + "+" + num_wild + " [" + quant + "]";
}
function tick() {
let p, q, ibest, dbest, lbest;
let nparticles = [];
function check(s) {
const isect = intersect(p, q, s.start, s.end);
if (isect != null) {
if (ibest == null) {
ibest = isect;
dbest = dist(q.x, q.y, isect.x, isect.y);
lbest = s;
} else {
const d = dist(q.x, q.y, isect.x, isect.y);
if (d < dbest) {
dbest = d;
ibest = isect;
lbest = s;
}
}
}
}
for (let idx = 0; idx < particles.length; ++idx) {
p = particles[idx].end;
q = p5.Vector.add(p, particles[idx].dir);
ibest = null;
dbest = null;
lbest = null;
// Check for intersections with baked line segments
for (let s of segments) {
check(s);
}
// Also check active particles
for (let jdx = 0; jdx < particles.length; ++jdx) {
if (jdx == idx) {
continue;
}
check(particles[jdx]);
}
if (ibest != null) {
// There was an intersection. Bounce off it it
// and update.
if (particles[idx].start.dist(ibest) < 1) {
// Too close to last intersection. Just kill this
// particle.
continue;
}
segments.push({
start: particles[idx].start,
end: ibest,
dir: particles[idx].dir,
wild: particles[idx].wild
});
particles[idx].start = ibest;
// Classic reflection calculation
const n = lbest.dir; // createVector(-lbest.dir.y, lbest.dir.x);
const v = particles[idx].dir;
particles[idx].dir = p5.Vector.sub(p5.Vector.mult(n, 2 * n.dot(v)), v);
particles[idx].dir.normalize();
if( abs( v.dot( particles[idx].dir ) + 1 ) < 1e-4 ) {
// Too close to normal reflection.
continue;
}
// TODO -- this isn't quite right.
particles[idx].end = p5.Vector.add(
ibest,
p5.Vector.mult(particles[idx].dir, 0.001)
);
nparticles.push(particles[idx]);
} else {
// just extend the current particle trace
particles[idx].end = q;
nparticles.push(particles[idx]);
}
}
particles = nparticles;
}
/*
function preload()
{
fnt = loadFont( 'WoolkarthBoldBold-q355.ttf' );
}
*/
function setup() {
createCanvas(400, 400);
createSystem();
}
function draw() {
background( 0, 35, 102 );
strokeWeight( 3.0 );
strokeCap( ROUND );
stroke( 255, 160 );
for( let idx = 0; idx < 3; ++idx ) {
tick();
}
for (let s of segments) {
stroke( s.wild ? color(255,255,0,160) : color(255,160) );
line(s.start.x, s.start.y, s.end.x, s.end.y);
}
for (let p of particles) {
stroke( p.wild ? color(255,255,0,160) : color(255,160) );
line(p.start.x, p.start.y, p.end.x, p.end.y);
}
stroke( 255, 160 );
noFill();
rect( 5, height-75, 120, 70 );
rect( 130, height-75, width-135, 70 );
if( particles.length == 0 ) {
noLoop();
for( let idx = 0; idx < count; ++idx ) {
stroke( idx>=(count-num_wild) ? color(255,255,0,160) : color(255,160) );
line( 140+idx*5, height-60, 140+idx*5, height-20 );
}
fill( 255, 160 );
noStroke();
for( let idx = 0; idx < quant; ++idx ) {
const x = width-20-int(idx/3)*15;
const y = height-60 + 20*(idx%3);
ellipse( x, y, 10, 10 );
}
}
noStroke();
fill( 255, 160 );
/* textFont( fnt );
textSize( 12 );
textAlign( CENTER );
text( caption, 62, height-45 ); */
}
function keyPressed()
{
if( key == ' ' ) {
createSystem();
loop();
} else if( key == 'g' ) {
createSystem();
loop();
saveGif("output", 600, { units: "frames", delay: 0 });
}
}