diff options
author | Brian Picciano <mediocregopher@gmail.com> | 2020-07-08 19:12:40 -0600 |
---|---|---|
committer | Brian Picciano <mediocregopher@gmail.com> | 2020-07-08 19:12:40 -0600 |
commit | 8d1de40ef7f0210db7e0fcbeaaf8f5f4e2855f73 (patch) | |
tree | 5b28bb5e150b0d20759dc2cc5472dcaf37c17284 /_posts | |
parent | 7e15864c35a24d6e2897f44652e709075972219c (diff) |
viz 3
Diffstat (limited to '_posts')
-rw-r--r-- | _posts/2018-11-12-viz-2.md | 2 | ||||
-rw-r--r-- | _posts/2020-07-07-viz-3.md | 154 |
2 files changed, 155 insertions, 1 deletions
diff --git a/_posts/2018-11-12-viz-2.md b/_posts/2018-11-12-viz-2.md index 56f7aee..c3e342e 100644 --- a/_posts/2018-11-12-viz-2.md +++ b/_posts/2018-11-12-viz-2.md @@ -14,7 +14,7 @@ git_commit: v2 <script>goog.require("viz.core");</script> <p align="center"><canvas id="viz"></canvas></p> -This visualization builds on the previous. Structurally the cortesian grid has +This visualization builds on the previous. Structurally the cartesian grid has been turned into an isometric one, but this is more of an environmental change than a behavioral one. diff --git a/_posts/2020-07-07-viz-3.md b/_posts/2020-07-07-viz-3.md new file mode 100644 index 0000000..f56dbb6 --- /dev/null +++ b/_posts/2020-07-07-viz-3.md @@ -0,0 +1,154 @@ +--- +title: >- + Visualization 3 +description: >- + All the pixels. +series: viz +--- + +<canvas id="canvas" style="padding-bottom: 2rem;"></canvas> + +This visualization is built from the ground up. On every frame a random set of +pixels is chosen. Each chosen pixel calculates the average of its color and the +color of a random neighbor. Some random color drift is added in as well. It +replaces its own color with that calculated color. + +Choosing a neighbor is done using the "asteroid rule", ie a pixel at the very +top row is considered to be the neighbor of the pixel on the bottom row of the +same column. + +Without the asteroid rule the pixels would all eventually converge into a single +uniform color, generally a light blue, due to the colors at the edge, the reds, +being quickly averaged away. With the asteroid rule in place the canvas has no +edges, thus no position on the canvas is favored and balance can be maintained. + +<script type="text/javascript"> +let rectSize = 12; + +function randn(n) { + return Math.floor(Math.random() * n); +} + +let canvas = document.getElementById("canvas"); +canvas.width = window.innerWidth - (window.innerWidth % rectSize); +canvas.height = window.innerHeight- (window.innerHeight % rectSize); +let ctx = canvas.getContext("2d"); + +let w = canvas.width / rectSize; +let h = canvas.height / rectSize; + +let matrices = new Array(2); +matrices[0] = new Array(w); +matrices[1] = new Array(w); +for (let x = 0; x < w; x++) { + matrices[0][x] = new Array(h); + matrices[1][x] = new Array(h); + for (let y = 0; y < h; y++) { + let el = { + h: 360 * (x / w), + s: "100%", + l: "50%", + }; + matrices[0][x][y] = el; + matrices[1][x][y] = el; + } +} + +// draw initial canvas, from here on out only individual rectangles will be +// filled as they get updated. +for (let x = 0; x < w; x++) { + for (let y = 0; y < h; y++) { + let el = matrices[0][x][y]; + ctx.fillStyle = `hsl(${el.h}, ${el.s}, ${el.l})`; + ctx.fillRect(x * rectSize, y * rectSize, rectSize, rectSize); + } +} + + +let requestAnimationFrame = + window.requestAnimationFrame || + window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || + window.msRequestAnimationFrame; + +let neighbors = [ + [-1, -1], [0, -1], [1, -1], + [-1, 0], [1, 0], + [-1, 1], [0, 1], [1, 1], +]; + +function randNeighborAsteroid(matrix, x, y) { + let neighborCoord = neighbors[randn(neighbors.length)]; + let neighborX = x+neighborCoord[0]; + let neighborY = y+neighborCoord[1]; + neighborX = (neighborX + w) % w; + neighborY = (neighborY + h) % h; + return matrix[neighborX][neighborY]; +} + +function randNeighbor(matrix, x, y) { + while (true) { + let neighborCoord = neighbors[randn(neighbors.length)]; + let neighborX = x+neighborCoord[0]; + let neighborY = y+neighborCoord[1]; + if (neighborX < 0 || neighborX >= w || neighborY < 0 || neighborY >= h) { + continue; + } + return matrix[neighborX][neighborY]; + } +} + +let drift = 10; +function genChildH(elA, elB) { + // set the two h values, h1 <= h2 + let h1 = elA.h; + let h2 = elB.h; + if (h1 > h2) { + h1 = elB.h; + h2 = elA.h; + } + + // diff must be between 0 (inclusive) and 360 (exclusive). If it's greater + // than 180 then it's not the shortest path around, that must be the other + // way around the circle. + let hChild; + let diff = h2 - h1; + if (diff > 180) { + diff = 360 - diff; + hChild = h2 + (diff / 2); + } else { + hChild = h1 + (diff / 2); + } + + hChild += (Math.random() * drift * 2) - drift; + hChild = (hChild + 360) % 360; + return hChild; +} + +let tick = 0; +function doTick() { + tick++; + let currI = tick % 2; + let curr = matrices[currI]; + let lastI = (tick - 1) % 2; + let last = matrices[lastI]; + + for (let i = 0; i < (w * h / 2); i++) { + let x = randn(w); + let y = randn(h); + if (curr[x][y].lastTick == tick) continue; + + let neighbor = randNeighborAsteroid(last, x, y); + curr[x][y].h = genChildH(curr[x][y], neighbor); + curr[x][y].lastTick = tick; + ctx.fillStyle = `hsl(${curr[x][y].h}, ${curr[x][y].s}, ${curr[x][y].l})`; + ctx.fillRect(x * rectSize, y * rectSize, rectSize, rectSize); + } + + matrices[currI] = curr; + requestAnimationFrame(doTick); +} + +requestAnimationFrame(doTick); + +</script> |