diff --git a/app/7/delaunay.js b/app/7/delaunay.js new file mode 100644 index 0000000..8538d91 --- /dev/null +++ b/app/7/delaunay.js @@ -0,0 +1,237 @@ +// from https://github.com/ironwallaby/delaunay +var Delaunay; + +(function() { + "use strict"; + + var EPSILON = 1.0 / 1048576.0; + + function supertriangle(vertices) { + var xmin = Number.POSITIVE_INFINITY, + ymin = Number.POSITIVE_INFINITY, + xmax = Number.NEGATIVE_INFINITY, + ymax = Number.NEGATIVE_INFINITY, + i, dx, dy, dmax, xmid, ymid; + + for(i = vertices.length; i--; ) { + if(vertices[i][0] < xmin) xmin = vertices[i][0]; + if(vertices[i][0] > xmax) xmax = vertices[i][0]; + if(vertices[i][1] < ymin) ymin = vertices[i][1]; + if(vertices[i][1] > ymax) ymax = vertices[i][1]; + } + + dx = xmax - xmin; + dy = ymax - ymin; + dmax = Math.max(dx, dy); + xmid = xmin + dx * 0.5; + ymid = ymin + dy * 0.5; + + return [ + [xmid - 20 * dmax, ymid - dmax], + [xmid , ymid + 20 * dmax], + [xmid + 20 * dmax, ymid - dmax] + ]; + } + + function circumcircle(vertices, i, j, k) { + var x1 = vertices[i][0], + y1 = vertices[i][1], + x2 = vertices[j][0], + y2 = vertices[j][1], + x3 = vertices[k][0], + y3 = vertices[k][1], + fabsy1y2 = Math.abs(y1 - y2), + fabsy2y3 = Math.abs(y2 - y3), + xc, yc, m1, m2, mx1, mx2, my1, my2, dx, dy; + + /* Check for coincident points */ + if(fabsy1y2 < EPSILON && fabsy2y3 < EPSILON) + throw new Error("Eek! Coincident points!"); + + if(fabsy1y2 < EPSILON) { + m2 = -((x3 - x2) / (y3 - y2)); + mx2 = (x2 + x3) / 2.0; + my2 = (y2 + y3) / 2.0; + xc = (x2 + x1) / 2.0; + yc = m2 * (xc - mx2) + my2; + } + + else if(fabsy2y3 < EPSILON) { + m1 = -((x2 - x1) / (y2 - y1)); + mx1 = (x1 + x2) / 2.0; + my1 = (y1 + y2) / 2.0; + xc = (x3 + x2) / 2.0; + yc = m1 * (xc - mx1) + my1; + } + + else { + m1 = -((x2 - x1) / (y2 - y1)); + m2 = -((x3 - x2) / (y3 - y2)); + mx1 = (x1 + x2) / 2.0; + mx2 = (x2 + x3) / 2.0; + my1 = (y1 + y2) / 2.0; + my2 = (y2 + y3) / 2.0; + xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2); + yc = (fabsy1y2 > fabsy2y3) ? + m1 * (xc - mx1) + my1 : + m2 * (xc - mx2) + my2; + } + + dx = x2 - xc; + dy = y2 - yc; + return {i: i, j: j, k: k, x: xc, y: yc, r: dx * dx + dy * dy}; + } + + function dedup(edges) { + var i, j, a, b, m, n; + + for(j = edges.length; j; ) { + b = edges[--j]; + a = edges[--j]; + + for(i = j; i; ) { + n = edges[--i]; + m = edges[--i]; + + if((a === m && b === n) || (a === n && b === m)) { + edges.splice(j, 2); + edges.splice(i, 2); + break; + } + } + } + } + + Delaunay = { + triangulate: function(vertices, key) { + var n = vertices.length, + i, j, indices, st, open, closed, edges, dx, dy, a, b, c; + + /* Bail if there aren't enough vertices to form any triangles. */ + if(n < 3) + return []; + + /* Slice out the actual vertices from the passed objects. (Duplicate the + * array even if we don't, though, since we need to make a supertriangle + * later on!) */ + vertices = vertices.slice(0); + + if(key) + for(i = n; i--; ) + vertices[i] = vertices[i][key]; + + /* Make an array of indices into the vertex array, sorted by the + * vertices' x-position. Force stable sorting by comparing indices if + * the x-positions are equal. */ + indices = new Array(n); + + for(i = n; i--; ) + indices[i] = i; + + indices.sort(function(i, j) { + var diff = vertices[j][0] - vertices[i][0]; + return diff !== 0 ? diff : i - j; + }); + + /* Next, find the vertices of the supertriangle (which contains all other + * triangles), and append them onto the end of a (copy of) the vertex + * array. */ + st = supertriangle(vertices); + vertices.push(st[0], st[1], st[2]); + + /* Initialize the open list (containing the supertriangle and nothing + * else) and the closed list (which is empty since we havn't processed + * any triangles yet). */ + open = [circumcircle(vertices, n + 0, n + 1, n + 2)]; + closed = []; + edges = []; + + /* Incrementally add each vertex to the mesh. */ + for(i = indices.length; i--; edges.length = 0) { + c = indices[i]; + + /* For each open triangle, check to see if the current point is + * inside it's circumcircle. If it is, remove the triangle and add + * it's edges to an edge list. */ + for(j = open.length; j--; ) { + /* If this point is to the right of this triangle's circumcircle, + * then this triangle should never get checked again. Remove it + * from the open list, add it to the closed list, and skip. */ + dx = vertices[c][0] - open[j].x; + if(dx > 0.0 && dx * dx > open[j].r) { + closed.push(open[j]); + open.splice(j, 1); + continue; + } + + /* If we're outside the circumcircle, skip this triangle. */ + dy = vertices[c][1] - open[j].y; + if(dx * dx + dy * dy - open[j].r > EPSILON) + continue; + + /* Remove the triangle and add it's edges to the edge list. */ + edges.push( + open[j].i, open[j].j, + open[j].j, open[j].k, + open[j].k, open[j].i + ); + open.splice(j, 1); + } + + /* Remove any doubled edges. */ + dedup(edges); + + /* Add a new triangle for each edge. */ + for(j = edges.length; j; ) { + b = edges[--j]; + a = edges[--j]; + open.push(circumcircle(vertices, a, b, c)); + } + } + + /* Copy any remaining open triangles to the closed list, and then + * remove any triangles that share a vertex with the supertriangle, + * building a list of triplets that represent triangles. */ + for(i = open.length; i--; ) + closed.push(open[i]); + open.length = 0; + + for(i = closed.length; i--; ) + if(closed[i].i < n && closed[i].j < n && closed[i].k < n) + open.push(closed[i].i, closed[i].j, closed[i].k); + + /* Yay, we're done! */ + return open; + }, + contains: function(tri, p) { + /* Bounding box test first, for quick rejections. */ + if((p[0] < tri[0][0] && p[0] < tri[1][0] && p[0] < tri[2][0]) || + (p[0] > tri[0][0] && p[0] > tri[1][0] && p[0] > tri[2][0]) || + (p[1] < tri[0][1] && p[1] < tri[1][1] && p[1] < tri[2][1]) || + (p[1] > tri[0][1] && p[1] > tri[1][1] && p[1] > tri[2][1])) + return null; + + var a = tri[1][0] - tri[0][0], + b = tri[2][0] - tri[0][0], + c = tri[1][1] - tri[0][1], + d = tri[2][1] - tri[0][1], + i = a * d - b * c; + + /* Degenerate tri. */ + if(i === 0.0) + return null; + + var u = (d * (p[0] - tri[0][0]) - b * (p[1] - tri[0][1])) / i, + v = (a * (p[1] - tri[0][1]) - c * (p[0] - tri[0][0])) / i; + + /* If we're outside the tri, fail. */ + if(u < 0.0 || v < 0.0 || (u + v) > 1.0) + return null; + + return [u, v]; + } + }; + + if(typeof module !== "undefined") + module.exports = Delaunay; +})(); diff --git a/app/7/index.js b/app/7/index.js new file mode 100644 index 0000000..31c7b5b --- /dev/null +++ b/app/7/index.js @@ -0,0 +1,159 @@ +const Delaunay = require('./delaunay.js'); + +new p5(sketch => { + sketch.disableFriendlyErrors = false; + // 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' + ); + // shaders.blurH = sketch.loadShader( + // '../shaders/base.vert', + // '../shaders/blur-two-pass.frag' + // ); + // shaders.blurV = sketch.loadShader( + // '../shaders/base.vert', + // '../shaders/blur-two-pass.frag' + // ); + } + + sketch.setup = () => { + filenamePrefix = 'seigler-p5-7-estrellas-'; + 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(); + }; + + seed = window.location.hash.substr(1); + sketch.colorMode(sketch.HSL, 1); + + sketch.createCanvas(sketch.windowWidth, sketch.windowHeight); + + layers.stars = sketch.createGraphics(width, height); + layers.stars.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); + + generate(); + }; + + sketch.draw = () => { + }; + + sketch.keyReleased = () => { + if (sketch.key == ' ') { + seed = null; + generate(); + } else if (sketch.key == 's') { + sketch.saveCanvas(filenamePrefix + seed + '.jpg', 'jpg'); + } + }; + + sketch.doubleClicked = () => { + seed = null; + generate(); + }; + + let resizeTimer; + sketch.windowResized = () => { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(() => { + window.location.reload(); + }, 100); + }; + + function generate() { + if (seed) { + sketch.randomSeed(seed); + } else { + let seed = Math.floor(sketch.random(1000000000000)); + window.location.hash = seed; + sketch.randomSeed(seed); + } + + sketch.noiseSeed(sketch.random(0, 1000000000)); + + layers.stars.clear(); + layers.stars.fill(1); + layers.stars.noStroke(); + addStars(layers.stars); + + 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.stars, Math.round(-width * overdraw/2), Math.round(-height * overdraw/2)); + sketch.blendMode(sketch.OVERLAY); + sketch.image(layers.noise, 0, 0); + } + + function addStars(layer) { + const bigStars = new Array(Math.round(width * height / 2000)); + const littleStars = new Array(Math.round(width * height / 200)); + for (let i = 0; i < bigStars.length; i++) { + bigStars[i] = [sketch.random(width), sketch.random(height)]; + } + for (let i = 0; i < littleStars.length; i++) { + littleStars[i] = [sketch.random(width), sketch.random(height)]; + } + layer.noStroke(); + layer.fill(1); + littleStars.forEach(star => { + layer.circle(star[0], star[1], sketch.random(0.25, 0.5)); + }); + const triangles = Delaunay.triangulate(bigStars); + layer.stroke(235/360, 0.82, 0.42, 0.28); + layer.strokeWeight(2); + layer.strokeCap(sketch.SQUARE); + layer.noFill(); + for (let i = 0; i < triangles.length; i += 3) { + if (sketch.random() > 0.85) { + layer.triangle( + bigStars[triangles[i ]][0], bigStars[triangles[i ]][1], + bigStars[triangles[i + 1]][0], bigStars[triangles[i + 1]][1], + bigStars[triangles[i + 2]][0], bigStars[triangles[i + 2]][1] + ); + } + } + layer.noStroke(); + layer.fill(1); + bigStars.forEach(star => { + layer.circle(star[0], star[1], sketch.random(0.75, 2)); + }); + } + + // give Perlin noise 0-1 a uniform distribution + function flattenPerlin(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) { + var AB = Math.sqrt(Math.pow(B.x-A.x,2)+ Math.pow(B.y-A.y,2)); + var BC = Math.sqrt(Math.pow(B.x-C.x,2)+ Math.pow(B.y-C.y,2)); + var 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)); + } +}); diff --git a/app/7/index.static.hbs b/app/7/index.static.hbs new file mode 100644 index 0000000..58732d0 --- /dev/null +++ b/app/7/index.static.hbs @@ -0,0 +1,7 @@ +--- +title: Cielo +created: 2019-11-16 +index: 7 +_options: + layout: app/layouts/sketch.hbs +--- diff --git a/app/assets/7/example.jpg b/app/assets/7/example.jpg new file mode 100644 index 0000000..e4549cc Binary files /dev/null and b/app/assets/7/example.jpg differ diff --git a/app/index.static.hbs b/app/index.static.hbs index 3f32e7a..0d83b46 100644 --- a/app/index.static.hbs +++ b/app/index.static.hbs @@ -14,6 +14,12 @@ _options:

P5.js generative art

+ {{> preview + index="7" + date="2019-11-16" + title="Estrellas" + alt="a field of stars with triangular blue constellations highlighted" + }} {{> preview index="6" date="2019-11-14"