speice.io/posts/2023/06/flam3/1-gasket.ts

58 lines
1.7 KiB
TypeScript
Raw Normal View History

import { randomBiUnit, randomInteger, renderFn, imageIndex } from "./0-utility";
2023-06-25 21:56:33 -04:00
function plot(x: number, y: number, image: ImageData) {
// A trivial `plot` implementation would take the range [-1, 1],
// shift it to [0, 2], then scale by the width or height
// as appropriate:
// pixelX = Math.floor((x + 1) * image.width / 2)
// pixelY = Math.floor((y + 1) * image.height / 2)
//
// However, that produces a mirror image (across both X and Y)
2023-07-02 15:30:56 -04:00
// from the paper. We'll negate X and Y to compensate.
2023-06-25 21:56:33 -04:00
// Second, because the gasket solution only contains points in
// the range [0, 1), the naive plot above would waste 75% of
// the pixels available. We'll keep the shift by 1 (to compensate
// for mirroring X and Y), but scale by the full image width or
// height so we'll plot the specific quadrant we care about.
var pixelX = Math.floor((-x + 1) * image.width);
var pixelY = Math.floor((-y + 1) * image.height);
// Set the pixel black:
const index = imageIndex(pixelX, pixelY, image.width);
2023-06-25 21:56:33 -04:00
image.data[index + 0] = 0;
image.data[index + 1] = 0;
image.data[index + 2] = 0;
image.data[index + 3] = 0xff;
}
type Xform = (x: number, y: number) => [number, number];
export const gasket: renderFn = (image) => {
const F: Xform[] = [
(x, y) => {
return [x / 2, y / 2];
},
(x, y) => {
return [(x + 1) / 2, y / 2];
},
(x, y) => {
return [x / 2, (y + 1) / 2];
},
];
let x = randomBiUnit();
let y = randomBiUnit();
2023-06-25 21:56:33 -04:00
// Plot with quality 1
const iterations = image.height * image.width;
2023-06-25 21:56:33 -04:00
for (var i = 0; i < iterations; i++) {
2023-06-25 21:56:33 -04:00
const Fi = randomInteger(0, F.length);
[x, y] = F[Fi](x, y);
if (i >= 20) {
plot(x, y, image);
}
}
};