diff --git a/app/9/index.js b/app/9/index.js new file mode 100644 index 0000000..5a6f963 --- /dev/null +++ b/app/9/index.js @@ -0,0 +1,254 @@ +new p5(sketch => { + sketch.disableFriendlyErrors = true; + // reused dimensions and a seed + let seed, width, height, noiseResolution, overdraw, blurQuality; + const layers = {}; // offscreen layers + const shaders = {}; // shaders + const lib = {}; // libraries + const assets = {}; // fonts, images, sound files + + sketch.preload = () => { + // shaders.whiteNoise = sketch.loadShader( + // "../shaders/base.vert", + // "../shaders/white-noise.frag" + // ); + assets.planet = sketch.loadImage("./dwarf_planet.png"); + }; + + sketch.setup = () => { + filenamePrefix = "seigler-p5-9-hot-space-trails-"; + overdraw = 0.1; + width = Math.floor(sketch.windowWidth * (1 + overdraw)); + height = Math.floor(sketch.windowHeight * (1 + overdraw)); + noiseResolution = [0.2, 0.1, 0.05, 2]; + blurQuality = 2; + + window.onhashchange = () => { + seed = window.location.hash.substr(1); + generate(); + }; + + sketch.colorMode(sketch.HSL, 1); + + sketch.createCanvas(sketch.windowWidth, sketch.windowHeight); + + layers.base = sketch.createGraphics(width, height); + layers.base.colorMode(sketch.HSL, 1); + + // layers.noise = sketch.createGraphics(width, height, sketch.WEBGL); + + // layers.blur1 = sketch.createGraphics(width, height, sketch.WEBGL); + // layers.blur2 = sketch.createGraphics(width, height, sketch.WEBGL); + + window.onhashchange(); + generate(); + }; + + sketch.draw = () => {}; + + sketch.keyReleased = () => { + if (sketch.key == " ") { + sketch.doubleClicked(); + } else if (sketch.key == "s") { + sketch.saveCanvas(filenamePrefix + seed + ".jpg", "jpg"); + } + }; + + sketch.doubleClicked = () => { + window.location.hash = ''; + }; + + let resizeTimer; + sketch.windowResized = () => { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(() => { + window.location.reload(); + }, 100); + }; + + function generate() { + if (seed) { + sketch.randomSeed(seed); + } else { + seed = Math.floor(sketch.random(1000000000000)); + if (window.location.replace) { + window.location.replace(`#${seed}`); + } else { + window.location.hash = seed; + } + return; + } + + sketch.noiseSeed(sketch.random(0, 1000000000)); + lib.simplex = new SimplexNoise(sketch.random(0, 1000000000)); + + makeMainDrawing(layers.base); + + sketch.blendMode(sketch.BLEND); + sketch.background(0); + // layers.noise.shader(shaders.whiteNoise); + // shaders.whiteNoise.setUniform("u_resolution", [width, height]); + // shaders.whiteNoise.setUniform("u_alpha", 0.05); + // layers.noise.rect(0, 0, width, height); + sketch.image( + layers.base, + Math.round((-width * overdraw) / 2), + Math.round((-height * overdraw) / 2) + ); + // sketch.blendMode(sketch.OVERLAY); + // sketch.image(layers.noise, 0, 0); + } + + function makeMainDrawing(layer) { + layer.clear(); + const noiseScale = 1.5 / (width + height); + const quant = Math.round(width * height / 1920 / 1080 * 100); + layers.base.imageMode(sketch.CENTER); + const directAngle = sketch.random() * Math.PI * 2; + + const drawStar = ({x, y}) => { + layer.fill(1); + layer.circle( + x + sketch.random(-10, 10), + y + sketch.random(-10, 10), + 0.5 + Math.pow(sketch.random(), 10) * 3 + ); + }; + + const drawPlanet = ({x, y}) => { + layer.noStroke(); + if (sketch.random() > 0.5) { + const { noise } = noisePlus(x, y, noiseScale); + const size = Math.pow(sketch.random(), 5) * 200; + layer.tint(0.8 * noise); + layer.image(assets.planet, x, y, size, size); + } + }; + + const drawTrail = (point) => { + layer.noTint(); + const x = point.x + sketch.random(-50, 50); + const y = point.y + sketch.random(-50, 50); + const noise = fractalNoise(x, y, noiseScale); + const wild = sketch.random() > 0.8; + const fatness = sketch.random(2, 10); + const directMod = !wild && sketch.random() > 0.5 ? Math.PI : 0; + layer.strokeCap(wild ? sketch.ROUND : sketch.SQUARE); + layer.stroke(wild ? sketch.color(sketch.random(0.05), 1, 0.5) : '#BBF7'); + for (let i = 0, max = 200, aX = x, aY = y; i < max; i++) { + let {angle, noise} = noisePlus(aX, aY, noiseScale); + if (!wild) { + angle = directAngle; + } + const dX = Math.cos(angle + directMod + sketch.HALF_PI) * 4; + const dY = Math.sin(angle + directMod + sketch.HALF_PI) * 4; + layer.strokeWeight( + (wild ? fatness : 1) * Math.pow(Math.sin(i/max*Math.PI), 2) + ); + layer.line(aX, aY, aX + dX, aY + dY); + aX += dX; + aY += dY; + } + }; + + const stars = buildGrid(0, 0, width, height, 100 * quant); + stars.forEach(drawStar); + const planets = buildGrid(0, 0, width, height, 3 * quant, drawPlanet); + const trails = buildGrid(0, 0, width, height, quant, drawTrail); + + const mix = [...planets, ...trails]; + shuffle(mix); + mix.forEach(({x, y, drawFunction}) => drawFunction({x, y})); + } + + function buildGrid(minX, minY, maxX, maxY, approxPoints, drawFunction) { + const width = maxX - minX; + const height = maxY - minY; + const unit = Math.sqrt(width * height / Math.sin(Math.PI / 3) / approxPoints); + const rows = + Math.max(1, Math.round(height / unit / Math.sin(Math.PI / 3))) + 1; + const cols = Math.max(1, Math.round(width / unit)) + 1; + const grid = []; + for (let index = 0; index < rows * cols; index++) { + const col = index % cols; + const row = Math.floor(index / cols); + grid.push({ + x: minX + (width / (cols - 1)) * col + (((row % 2) - 0.5) * unit) / 2, + y: minY + (height / (rows - 1)) * row, + drawFunction, + }); + } + return grid; + } + + function noisePlus( + x, + y, + noiseScale = 2 / (width + height), + noiseOffset = 0 + ) { + const noise = fractalNoise(x, y, noiseScale, noiseOffset); + const dX = fractalNoise(x + 1, y, noiseScale, noiseOffset) - noise; + const dY = fractalNoise(x, y + 1, noiseScale, noiseOffset) - noise; + const angle = Math.atan2(dY, dX); + const length = sketch.dist(0, 0, dX, dY); + return ({noise, angle, length}); + } + + function fractalNoise( + x, + y, + noiseScale = 2 / (width + height), + noiseOffset = 0 + ) { + return 0.5 + 0.5 * ( + lib.simplex.noise2D( + noiseOffset + noiseScale * x, + noiseOffset + noiseScale * y + ) + + 0.5 * lib.simplex.noise2D( + noiseOffset + 2 * noiseScale * x, + noiseOffset + 2 * noiseScale * y + ) + + 0.25 * lib.simplex.noise2D( + noiseOffset + 4 * noiseScale * x, + noiseOffset + 4 * noiseScale * y + ) + ) / 1.75; + } + + // give normally distributed values from 0-1 a uniform distribution + function flattenDistribution(x) { + return ( + 23.8615 * Math.pow(x, 5) - + 59.6041 * Math.pow(x, 4) + + 47.2472 * Math.pow(x, 3) - + 11.3053 * Math.pow(x, 2) + + 0.806219 * x - + 0.00259101 + ); + } + + // returns abs angle from a to b to c + function three_point_angle(A, B, C) { + const AB = Math.sqrt(Math.pow(B.x - A.x, 2) + Math.pow(B.y - A.y, 2)); + const BC = Math.sqrt(Math.pow(B.x - C.x, 2) + Math.pow(B.y - C.y, 2)); + const AC = Math.sqrt(Math.pow(C.x - A.x, 2) + Math.pow(C.y - A.y, 2)); + return Math.acos((BC * BC + AB * AB - AC * AC) / (2 * BC * AB)); + } + + /** + * Shuffles array in place. + * @param {Array} a items An array containing the items. + */ + function shuffle(a) { + var j, x, i; + for (i = a.length - 1; i > 0; i--) { + j = Math.floor(sketch.random() * (i + 1)); + x = a[i]; + a[i] = a[j]; + a[j] = x; + } + return a; + } +}); diff --git a/app/9/index.static.hbs b/app/9/index.static.hbs new file mode 100644 index 0000000..386a7dc --- /dev/null +++ b/app/9/index.static.hbs @@ -0,0 +1,7 @@ +--- +title: Hot Space Trails +created: 2021-02-07 +index: 9 +_options: + layout: app/layouts/sketch.hbs +--- diff --git a/app/assets/9/dwarf_planet.png b/app/assets/9/dwarf_planet.png new file mode 100644 index 0000000..90ae427 Binary files /dev/null and b/app/assets/9/dwarf_planet.png differ diff --git a/app/assets/9/example.jpg b/app/assets/9/example.jpg new file mode 100644 index 0000000..7a92e18 Binary files /dev/null and b/app/assets/9/example.jpg differ diff --git a/app/index.static.hbs b/app/index.static.hbs index 8ffa435..b4f2fff 100644 --- a/app/index.static.hbs +++ b/app/index.static.hbs @@ -14,6 +14,12 @@ _options: