speice.io/posts/2023/06/flam3/3a-binary.ts

79 lines
1.9 KiB
TypeScript
Raw Normal View History

import { histIndex, imageIndex } from "./0-utility";
import { camera } from "./2a-variations";
import { flameFinal, FlameFinal } from "./2c-final";
export abstract class Accumulator {
histogram: number[] = [];
constructor(
protected readonly width: number,
protected readonly height: number
) {
for (var i = 0; i < width * height; i++) {
this.histogram.push(0);
}
}
accumulate(x: number, y: number) {
const [pixelX, pixelY] = camera(x, y, this.width);
if (
pixelX < 0 ||
pixelX >= this.width ||
pixelY < 0 ||
pixelY >= this.height
) {
return;
}
const index = histIndex(pixelX, pixelY, this.width);
this.histogram[index] += 1;
}
abstract render(image: ImageData): void;
}
class AccumulateBinary extends Accumulator {
render(image: ImageData) {
for (var x = 0; x < image.width; x++) {
for (var y = 0; y < image.height; y++) {
const index = histIndex(x, y, image.width);
// Color black if this pixel is part of the solution set, white otherwise
const value = this.histogram[index] > 0 ? 0 : 0xff;
const iIdx = imageIndex(x, y, image.width);
image.data[iIdx + 0] = value;
image.data[iIdx + 1] = value;
image.data[iIdx + 2] = value;
image.data[iIdx + 3] = value ? 0xff : 0;
}
}
}
}
export function render(
flame: FlameFinal,
quality: number,
accumulator: Accumulator,
image: ImageData
) {
const iterations = quality * image.width * image.height;
for (var i = 0; i < iterations; i++) {
flame.step();
if (i > 20) {
const [flameX, flameY] = flame.current();
accumulator.accumulate(flameX, flameY);
}
}
accumulator.render(image);
}
export function renderBinary(image: ImageData) {
const accumulator = new AccumulateBinary(image.width, image.height);
render(flameFinal, 10, accumulator, image);
}