mirror of
https://github.com/bspeice/speice.io
synced 2024-12-23 00:58:09 -05:00
143 lines
3.4 KiB
TypeScript
143 lines
3.4 KiB
TypeScript
import { randomInteger } from "./0-utility";
|
|
|
|
type Variation = (x: number, y: number) => [number, number];
|
|
|
|
const r = (x: number, y: number) => {
|
|
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
|
|
};
|
|
const theta = (x: number, y: number) => {
|
|
return Math.atan2(x, y);
|
|
};
|
|
|
|
const linear: Variation = (x, y) => {
|
|
return [x, y];
|
|
};
|
|
|
|
const swirl: Variation = (x, y) => {
|
|
const r2 = Math.pow(r(x, y), 2);
|
|
const sinR2 = Math.sin(r2);
|
|
const cosR2 = Math.cos(r2);
|
|
|
|
return [x * sinR2 - y * cosR2, x * cosR2 + y * sinR2];
|
|
};
|
|
|
|
const polar: Variation = (x, y) => {
|
|
return [theta(x, y) / Math.PI, r(x, y) - 1];
|
|
};
|
|
|
|
const disc: Variation = (x, y) => {
|
|
const thetaOverPi = theta(x, y) / Math.PI;
|
|
const piR = Math.PI * r(x, y);
|
|
return [thetaOverPi * Math.sin(piR), thetaOverPi * Math.cos(piR)];
|
|
};
|
|
|
|
const variations = [linear, swirl, polar, disc];
|
|
|
|
class Coefs {
|
|
constructor(
|
|
public readonly a: number,
|
|
public readonly b: number,
|
|
public readonly c: number,
|
|
public readonly d: number,
|
|
public readonly e: number,
|
|
public readonly f: number
|
|
) {}
|
|
}
|
|
|
|
class Transform {
|
|
constructor(
|
|
public readonly weight: number,
|
|
public readonly coefs: Coefs,
|
|
// Assumes that we have a blend for each variation
|
|
public readonly blend: number[]
|
|
) {}
|
|
|
|
apply(x: number, y: number) {
|
|
const variationX = this.coefs.a * x + this.coefs.b * y + this.coefs.c;
|
|
const variationY = this.coefs.d * x + this.coefs.e * y + this.coefs.f;
|
|
|
|
var transformX = 0;
|
|
var transformY = 0;
|
|
this.blend.forEach((blend, i) => {
|
|
const [perVarX, perVarY] = variations[i](variationX, variationY);
|
|
transformX += blend * perVarX;
|
|
transformY += blend * perVarY;
|
|
});
|
|
|
|
return [transformX, transformY];
|
|
}
|
|
}
|
|
|
|
function plot(x: number, y: number, image: ImageData) {
|
|
const pixelX = Math.floor(((x + 2) * image.width) / 4);
|
|
const pixelY = Math.floor(((y + 2) * image.height) / 4);
|
|
|
|
if (
|
|
pixelX < 0 ||
|
|
pixelX > image.width ||
|
|
pixelY < 0 ||
|
|
pixelY > image.height
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const index = pixelY * (image.width * 4) + pixelX * 4;
|
|
|
|
image.data[index + 0] = 0;
|
|
image.data[index + 1] = 0;
|
|
image.data[index + 2] = 0;
|
|
image.data[index + 3] = 0xff;
|
|
}
|
|
|
|
function render(transforms: Transform[], image: ImageData) {
|
|
const weightSum = transforms.reduce((val, xform) => val + xform.weight, 0);
|
|
|
|
var x = Math.random() * 2 - 1;
|
|
var y = Math.random() * 2 - 1;
|
|
|
|
const iter = 100_000;
|
|
for (var i = 0; i < iter; i++) {
|
|
// Find the tranform we're operating on
|
|
|
|
/*
|
|
var f = Math.random() * weightSum;
|
|
|
|
var transform = transforms[0];
|
|
transforms.forEach((xform, j) => {
|
|
if (f > 0 && f < xform.weight) {
|
|
console.log(`xform=${j}`);
|
|
transform = xform;
|
|
f -= xform.weight;
|
|
}
|
|
});
|
|
*/
|
|
// HACK: Currently assuming weights are equal
|
|
const transform = transforms[randomInteger(0, transforms.length)];
|
|
|
|
// Play the chaos game
|
|
[x, y] = transform.apply(x, y);
|
|
|
|
if (i > 20) {
|
|
plot(x, y, image);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function renderBaseline(image: ImageData) {
|
|
return render(
|
|
[
|
|
new Transform(
|
|
0.5,
|
|
new Coefs(0.982996, 0, -0.219512, 0, 0.982996, -0.1875),
|
|
[1, 0, 0, -0.934]
|
|
),
|
|
new Transform(
|
|
0.5,
|
|
new Coefs(0.966511, -0.256624, 0.050305, 0.256624, 0.966511, -0.235518),
|
|
[0, 0.156, 0.778, 0]
|
|
),
|
|
],
|
|
image
|
|
);
|
|
}
|