summaryrefslogtreecommitdiff
path: root/_posts
diff options
context:
space:
mode:
authorBrian Picciano <mediocregopher@gmail.com>2020-07-08 19:12:40 -0600
committerBrian Picciano <mediocregopher@gmail.com>2020-07-08 19:12:40 -0600
commit8d1de40ef7f0210db7e0fcbeaaf8f5f4e2855f73 (patch)
tree5b28bb5e150b0d20759dc2cc5472dcaf37c17284 /_posts
parent7e15864c35a24d6e2897f44652e709075972219c (diff)
viz 3
Diffstat (limited to '_posts')
-rw-r--r--_posts/2018-11-12-viz-2.md2
-rw-r--r--_posts/2020-07-07-viz-3.md154
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>