Public
Edited
Apr 13, 2023
1 fork
10 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
createBrushStroke = (
count,
translate,
size,
positions,
scales,
translations
) => {
positions = positions || [];
scales = scales || [];
translations = translations || [];
const xScaleRandom = d3.randomNormal();
const yScaleRandom = d3.randomNormal();
const xTranslateRandom = d3.randomNormal(translate.x, size.width);
const yTranslateRandom = d3.randomNormal(translate.y, size.height);
_.times(count, (i) => {
const xScale = xScaleRandom();
const yScale = yScaleRandom();
const xTranslate = xTranslateRandom();
const yTranslate = yTranslateRandom();
_.each(unitSquare, ([x, y]) => {
positions.push(x);
positions.push(y);
scales.push(xScale);
scales.push(yScale);
translations.push(xTranslate);
translations.push(yTranslate);
});
});

return { positions, scales, translations };
}
Insert cell
Insert cell
Insert cell
createStraightMotion = () => {
const position = vec2.fromValues(0, height * 0.25); // initialize at left middle
const velocity = vec2.create();
const acceleration = vec2.fromValues(0.025, 0.005);

return { position, velocity, acceleration };
}
Insert cell
{
const canvas = DOM.canvas(width, height);
const gl = canvas.getContext("webgl");

// 1. create shader program
const program = initShaderProgram(gl, vertexSource, fragmentSource);

// 2. get data ready
const { position, velocity, acceleration } = createStraightMotion();
const positions = [];
const scales = [];
const translations = [];

// initialize data
const positionAttribLoc = gl.getAttribLocation(program, "a_position");
const scaleAttribLoc = gl.getAttribLocation(program, "a_scale");
const translateAttribLoc = gl.getAttribLocation(program, "a_translation");
const resolutionUniLoc = gl.getUniformLocation(program, "u_resolution");

// 3. render
// gl.clearColor(0, 0, 0, 1);
// gl.clear(gl.COLOR_BUFFER_BIT);
gl.viewport(0, 0, width, height);
gl.useProgram(program);

// animate
while (position[0] < width) {
const numSquares = 25;

vec2.add(velocity, velocity, acceleration);
vec2.add(position, position, velocity);
createBrushStroke(
numSquares,
{ x: position[0], y: position[1] },
{ width: 0.5, height: 10 },
positions,
scales,
translations
);

const positionBuffer = initPositionBuffer(gl, positions);
const scaleBuffer = initPositionBuffer(gl, scales);
const translateBuffer = initPositionBuffer(gl, translations);

// set data on the program
setPositionAttribute(gl, positionBuffer, positionAttribLoc);
setPositionAttribute(gl, scaleBuffer, scaleAttribLoc);
setPositionAttribute(gl, translateBuffer, translateAttribLoc);
gl.uniform2f(resolutionUniLoc, width, height);
// draw!
gl.drawArrays(gl.TRIANGLES, 0, positions.length / 2);
yield Promises.delay(1, canvas);
}

return canvas;
}
Insert cell
Insert cell
createBrushStrokeNoise = (
count,
translate,
offset,
positions,
scales,
translations
) => {
const height = Math.round(p5noise(offset + 20000) * 160);
_.times(count, (i) => {
const xScale = d3.randomNormal(2, 1)();
const yScale = d3.randomNormal(0.5, 0.5)();
const xTranslate = translate.x + p5noise(i * 1000 + offset);
const yTranslate =
translate.y + height * (p5noise(100000 + i * 1000 + offset) - 0.5);
_.each(unitSquare, ([x, y]) => {
positions.push(x);
positions.push(y);
scales.push(xScale);
scales.push(yScale);
translations.push(xTranslate);
translations.push(yTranslate);
});
});

return { positions, scales, translations };
}
Insert cell
Insert cell
Insert cell
varyAcceleration = (position, velocity, acceleration) => {
const [x, y] = position;
// vary acceleration
const noiseScale = 0.01; // for 2D noise
vec2.set(
acceleration,
d3.randomNormal(0, 2)() / 30,
d3.randomNormal(0, 2)() / 60
// (p5noise(x * noiseScale, y * noiseScale) - 0.5) / 10,
// (p5noise(x * noiseScale + 1000, y * noiseScale + 1000) - 0.5) / 20
);

// if it goes out of bounds, then reverse direction
const [velocityX, velocityY] = velocity;
if (x < 0 || width < x) {
vec2.set(velocity, -1 * velocityX, -1 * velocityY);
}
if (y < 0 || height < y) {
vec2.set(velocity, velocityX, -1 * velocityY);
}
// const [accX, accY] = acceleration;
// if (x < 0 || width < x) {
// vec2.set(acceleration, -1 * accX, -1 * accY);
// }
// if (y < 0 || height < y) {
// vec2.set(acceleration, accX, -1 * accY);
// }
}
Insert cell
Insert cell
Insert cell
Insert cell
ch8

- fractal: “a rough or fragmented geometric shape that can be split into parts, each of which is (at least approximately) a reduced-size copy of the whole.”
- a stochastic fractal, meaning that it is built out of probabilities and randomness. Unlike the deterministic (or predictable) tree-branching structure, it is statistically self-similar.
- Fractals are characterized by having a fine structure at small scales
- Another fundamental component of fractal geometry is recursion
- Nevertheless, I am going to tackle this problem in a different manner by treating each segment of the Koch curve as an individual object. This will open up some design possibilities. For example, if each segment is an object, it could move independently from its original position and participate in a physics simulation. In addition, the visual appearance of each segment could vary as the object include properties for color, line thickness, and so on.
- L-systems (short for Lindenmayer systems) can be used to generate the recursive fractal patterns demonstrated so far in this chapter. L-systems are additionally useful because they provide a mechanism for keeping track of fractal structures that require complex and multi-faceted production rules.
- Alphabet. An L-system’s alphabet is comprised of the valid characters that can be included. For example, I could say the alphabet is “ABC,” meaning that any valid “sentence” (a string of characters) in an L-system can only include these three characters.
Axiom. The axiom is a sentence (created with characters from the alphabet) that describes the initial state of the system. For example, with the alphabet “ABC,” some example axioms are “AAA” or “B” or “ACBAB.”
Rules. The “production” rules of an L-system are applied to the axiom and then recursively, generating new sentences over and over again. An L-system rule includes two sentences, a “predecessor” and a “successor.” For example, with the Rule “A —> AB” means that when an “A” occurs in a sentence, it is replaced with “AB” in the next generation.
- consider each successive application of the L-system rules to be a generation. Generation 0 is, by definition, the axiom.

ch7
- A cellular automaton is a model of a system of “cell” objects with the following characteristics.

The cells live on a grid. (I’ll include examples in both one and two dimensions in this chapter, though a cellular automaton can exist in any finite number of dimensions.)
Each cell has a state. The number of state possibilities is typically finite. The simplest example has the two possibilities of 1 and 0 (otherwise referred to as “on” and “off” or “alive” and “dead”).
Each cell has a neighborhood. This can be defined in any number of ways, but it is typically a list of adjacent cells.
- CA living over a period of time, which could also be called a generation and, in this case, will likely refer to the frame count of an animation.
- a cell’s new state is a function of all the states in the cell’s neighborhood at the previous generation (time
t
1
t−1).
Insert cell
Insert cell
import {
initShaderProgram,
initPositionBuffer,
setPositionAttribute
} from "@sxywu/00-webgl-setup"
Insert cell
glMatrix = import("https://unpkg.com/gl-matrix@3.4.3/esm/index.js?module")
Insert cell
vec2 = glMatrix.vec2
Insert cell
p5 = require("p5")
Insert cell
p5noise = p5.prototype.noise
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more