viz 3
This commit is contained in:
parent
7e15864c35
commit
8d1de40ef7
@ -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.
|
||||
|
||||
|
154
_posts/2020-07-07-viz-3.md
Normal file
154
_posts/2020-07-07-viz-3.md
Normal file
@ -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>
|
Loading…
Reference in New Issue
Block a user