#container
View Compiled
document, body {
margin: 0;
min-height: 100vh;
}
body {
align-items: center;
display: flex;
justify-content: center;
}
#container {
align-items: center;
display: flex;
flex-direction: column;
}
button {
max-width: 200px;
margin-top: 10px;
}
View Compiled
import { SVG } from 'https://cdn.skypack.dev/@svgdotjs/svg.js'
import { Vec2 } from 'https://cdn.skypack.dev/wtc-math';
import fitCurve from 'https://cdn.skypack.dev/fit-curves';
console.clear();
const config = {
seed: 1337,
nscale: .005,
sscale: 20,
stepSize: 1,
r: 10,
k: 512,
stagePadding: 40
};
const vars = {
newPositions: [],
activeList: [],
fullList: [],
walkers: [],
grid: null,
newPosition: null
}
let noise;
const dimensions = new Vec2(750, 500);
var drawing = SVG().addTo('#container').size(dimensions.x, dimensions.y);
/// Create the download button
const dl = document.createElement('button');
dl.innerText = 'download';
dl.addEventListener('click', () => {
const fileName = "untitled.svg"
const url = "data:image/svg+xml;utf8," + encodeURIComponent(drawing.svg());
const link = document.createElement("a");
link.download = fileName;
link.href = url;
link.click();
});
document.body.querySelector('#container').appendChild(dl);
let running;
const setup = (x=dimensions.x/2, y=dimensions.y/2) => {
running = false;
setTimeout(() => {
running = true;
vars.currentPosition = [];
vars.activeList = [];
config.seed = floatRandomBetween(0, 99999);
config.nscale = floatRandomBetween(.001, .01);
config.sscale = floatRandomBetween(Math.PI, Math.PI*.25);
noise = new SimplexNoise(config.seed);
vars.stageHeight = dimensions.y + config.stagePadding * 2.;
vars.stepsize = vars.stageHeight / config.numwalkers;
vars.position = new Vec2(0,-config.stagePadding);
config.r = 4;
vars.grid = new Grid({
size: dimensions.addScalarNew(config.r / Math.SQRT2).array,
cellSize: config.r*1.3,
fill: -1
});
const poses = [
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y),
new Vec2(Math.random()*dimensions.x, Math.random()*dimensions.y)
];
poses.forEach((p) => {
const pos = new Vec2(p.x, p.y);
const n = noise.noise2D(pos.clone().scale(config.nscale).array)*config.sscale;
const body = new Body(pos, n, 0);
vars.grid.addChildAtPosition(body, pos);
vars.activeList.push(body);
vars.newPositions.push(body);
})
drawing.clear();
draw();
}, 10);
};
class Grid {
static #defaults = {
size: dimensions.clone(),
cellSize: new Vec2(50,50),
fill: null
};
#grid = [];
#size;
#cellSize;
constructor(settings) {
settings = Object.assign({}, Grid.#defaults, settings);
this.#size = Vec2.interpolate(settings.size) || Grid.#defaults.size;
this.#cellSize = Vec2.interpolate(settings.cellSize) || Grid.#defaults.cellSize;
this.#grid.length = this.gridSize.area;
this.#grid.fill(settings.fill || Grid.#defaults.fill);
}
addChild(child, i) {
this.#grid[i] = child;
}
addChildAtPosition(child, pos) {
this.#grid[this.getArrayPosition(pos)] = child;
}
addChildAtGridPosition(child, gridPos) {
this.#grid[this.getArrayPositionFromGridPos(gridPos)] = child;
}
getChild(i) {
return this.#grid[i];
}
getChildAtPosition(pos) {
return this.#grid[this.getArrayPosition(pos)];
}
getChildAtGridPosition(gridPos) {
return this.#grid[this.getArrayPositionFromGridPos(gridPos)];
}
getGridPositionForIndex(i) {
const gsize = this.gridSize;
return new Vec2(i%gsize.x, Math.floor(i/gsize.x));
}
getArrayPositionFromGridPos(gpos) {
const gsize = this.gridSize;
if(
gpos.x < 0 || gpos.x >= gsize.x ||
gpos.y < 0 || gpos.y >= gsize.y )
return null;
gpos.x = gpos.x % gsize.x;
const arraypos = (gpos.x) + (gpos.y*gsize.x);
return arraypos;
}
getArrayPosition(realPos) {
const gpos = this.getGridPos(realPos);
return this.getArrayPositionFromGridPos(gpos);
}
getGridPos(realPos) {
if(realPos instanceof Vec2) {
return realPos.divideNew(this.#cellSize).floor();
}
// Throw an error
}
getSubPos(realPos) {
if(realPos instanceof Vec2) {
return realPos.modNew(this.#cellSize);
}
// Throw an error
}
getRealPos(gridPos) {
if(gridPos instanceof Vec2) {
return gridPos.multiplyNew(this.#cellSize);
}
// Throw an error
}
get size() {
return this.#size;
}
get gridSize() {
return this.#size.divideNew(this.#cellSize).floor();
}
get cellSize() {
return this.#cellSize;
}
get length() {
return this.#grid.length;
}
}
class Body {
constructor(pos, r, a) {
this.pos = pos;
this.r = r;
this.a = a;
}
}
let a=0;
const drawStep = () => {
if(!running) return;
if(vars.newPositions.length > 0) {
// vars.newPositions.forEach(c => {
// vars.grid.addChildAtPosition(c, c.pos);
// });
}
vars.newPositions = [];
if(vars.activeList.length > 0) {
vars.activeList.forEach((c, ri) => {
if(Math.random() > .1 && ri > 1) return;
let numfound = 0;
for(var i = 0; i < config.k; i++) {
// const a = floatRandomBetween(0, Math.PI*2);
// a+=.01;
// const l = floatRandomBetween(config.r, config.r*2);
let a = c.a || 0;
const n = noise.noise2D(c.pos.clone().scale(config.nscale).array)*config.sscale;
a += n*.002;
const l = floatRandomBetween(config.r, config.r*2);
// const l = (Math.random()*.5*((n+1)*.6)+1)*config.r;
const pos = new Vec2(Math.cos(a)*l, Math.sin(a)*l);
// a = pos.angle;
c.a = a;
pos.add(c.pos);
if(vars.grid.getChildAtPosition(pos) === -1) {
const body = new Body(pos, n, n*Math.PI);
// const body = new Body(pos, n, pos.subtractNew(c.pos).angle);
vars.grid.addChildAtPosition(body, pos);
vars.activeList.push(body);
vars.newPositions.push(body);
drawing.line([[c.pos.x, c.pos.y], [pos.x, pos.y]]).fill("none").stroke('#f06');
numfound++;
break;
}
}
if(numfound === 0) {
vars.activeList.splice(ri, 1);
}
});
// const ri = Math.floor(0, floatRandomBetween(vars.activeList.length));
// const c = vars.activeList[ri];
requestAnimationFrame(drawStep);
} else {
console.log('end')
}
}
const draw = () => {
drawing.rect(dimensions.width, dimensions.height).fill("none").stroke('#f06');
requestAnimationFrame(drawStep);
}
const floatRandomBetween = (min, max) => {
return Math.random() * (max - min) + min;
};
setup();
const svg = document.body.querySelector('svg');
svg.addEventListener('click', (e) => {
const os = svg.getBoundingClientRect();
setup(e.clientX - os.x, e.clientY - os.y);
});
////////////////////////////////////////////////////////////////
// Simplex Noise utility code. Created by Reinder Nijhoff 2020
// https://turtletoy.net/turtle/6e4e06d42e
// Based on: http://webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
////////////////////////////////////////////////////////////////
function SimplexNoise(seed = 1) {
const grad = [ [1, 1, 0], [-1, 1, 0], [1, -1, 0], [-1, -1, 0],
[1, 0, 1], [-1, 0, 1], [1, 0, -1], [-1, 0, -1],
[0, 1, 1], [0, -1, 1], [0, 1, -1], [0, -1, -1] ];
const perm = new Uint8Array(512);
const F2 = (Math.sqrt(3) - 1) / 2, F3 = 1/3;
const G2 = (3 - Math.sqrt(3)) / 6, G3 = 1/6;
const dot2 = (a, b) => a[0] * b[0] + a[1] * b[1];
const sub2 = (a, b) => [a[0] - b[0], a[1] - b[1]];
const dot3 = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
const sub3 = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
class SimplexNoise {
constructor(seed = 1) {
for (let i = 0; i < 512; i++) {
perm[i] = i & 255;
}
for (let i = 0; i < 255; i++) {
const r = (seed = this.hash(i+seed)) % (256 - i) + i;
const swp = perm[i];
perm[i + 256] = perm[i] = perm[r];
perm[r + 256] = perm[r] = swp;
}
}
noise2D(p) {
const s = dot2(p, [F2, F2]);
const c = [Math.floor(p[0] + s), Math.floor(p[1] + s)];
const i = c[0] & 255, j = c[1] & 255;
const t = dot2(c, [G2, G2]);
const p0 = sub2(p, sub2(c, [t, t]));
const o = p0[0] > p0[1] ? [1, 0] : [0, 1];
const p1 = sub2(sub2(p0, o), [-G2, -G2]);
const p2 = sub2(p0, [1-2*G2, 1-2*G2]);
let n = Math.max(0, 0.5-dot2(p0, p0))**4 * dot2(grad[perm[i+perm[j]] % 12], p0);
n += Math.max(0, 0.5-dot2(p1, p1))**4 * dot2(grad[perm[i+o[0]+perm[j+o[1]]] % 12], p1);
n += Math.max(0, 0.5-dot2(p2, p2))**4 * dot2(grad[perm[i+1+perm[j+1]] % 12], p2);
return 70 * n;
}
hash(i) {
i = 1103515245 * ((i >> 1) ^ i);
const h32 = 1103515245 * (i ^ (i>>3));
return h32 ^ (h32 >> 16);
}
}
return new SimplexNoise(seed);
}
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.