Refactor to use a step function

This commit is contained in:
Bradlee Speice 2023-07-02 19:30:56 +00:00
parent 4bcc321c4b
commit 79394519c7
6 changed files with 38 additions and 44 deletions

View File

@ -28,7 +28,7 @@ export const Canvas: React.FC<{ f: renderFn }> = ({ f }) => {
} }
}); });
return <canvas ref={canvasRef} width={600} height={600} />; return <canvas ref={canvasRef} width={400} height={400} />;
}; };
export function randomInteger(min: number, max: number) { export function randomInteger(min: number, max: number) {

View File

@ -10,7 +10,7 @@ function plot(x: number, y: number, image: ImageData) {
// pixelY = Math.floor((y + 1) * image.height / 2) // pixelY = Math.floor((y + 1) * image.height / 2)
// //
// However, that produces a mirror image (across both X and Y) // However, that produces a mirror image (across both X and Y)
// from the paper. We'll invert X and Y to compensate. // from the paper. We'll negate X and Y to compensate.
// Second, because the gasket solution only contains points in // Second, because the gasket solution only contains points in
// the range [0, 1), the naive plot above would waste 75% of // the range [0, 1), the naive plot above would waste 75% of
// the pixels available. We'll keep the shift by 1 (to compensate // the pixels available. We'll keep the shift by 1 (to compensate

View File

@ -17,7 +17,7 @@ function r(x: number, y: number) {
} }
function theta(x: number, y: number) { function theta(x: number, y: number) {
return Math.atan2(x, y); return Math.atan2(y, x);
} }
function omega(): number { function omega(): number {
@ -93,6 +93,18 @@ export function weightedChoice<T>(choices: [number, T][]) {
throw "unreachable"; throw "unreachable";
} }
export class Flame {
x: number = Math.random() * 2 - 1;
y: number = Math.random() * 2 - 1;
constructor(public readonly transforms: [number, Transform][]) {}
step() {
const transform = weightedChoice(this.transforms);
[this.x, this.y] = transform.apply(this.x, this.y);
}
}
export function plot(x: number, y: number, image: ImageData) { export function plot(x: number, y: number, image: ImageData) {
const pixelX = Math.floor(((x + 2) * image.width) / 4); const pixelX = Math.floor(((x + 2) * image.width) / 4);
const pixelY = Math.floor(((y + 2) * image.height) / 4); const pixelY = Math.floor(((y + 2) * image.height) / 4);
@ -114,23 +126,13 @@ export function plot(x: number, y: number, image: ImageData) {
image.data[index + 3] = 0xff; image.data[index + 3] = 0xff;
} }
export class Flame { export function render(flame: Flame, quality: number, image: ImageData) {
constructor(public readonly transforms: [number, Transform][]) {} const iterations = quality * image.width * image.height;
render(quality: number, image: ImageData) { for (var i = 0; i < iterations; i++) {
var x = Math.random() * 2 - 1; flame.step();
var y = Math.random() * 2 - 1; if (i > 20) {
plot(flame.x, flame.y, image);
const iter = quality * (image.width * image.height);
for (var i = 0; i < iter; i++) {
const transform = weightedChoice(this.transforms);
// Play the chaos game
[x, y] = transform.apply(x, y);
if (i > 20) {
plot(x, y, image);
}
} }
} }
} }
@ -185,5 +187,5 @@ export function renderBaseline(image: ImageData) {
[transform3Weight, transform3], [transform3Weight, transform3],
]); ]);
flame.render(1, image); render(flame, 1, image);
} }

View File

@ -7,6 +7,7 @@ import {
julia, julia,
popcorn, popcorn,
pdj, pdj,
render,
transform1Coefs, transform1Coefs,
transform1Weight, transform1Weight,
transform2Coefs, transform2Coefs,
@ -75,5 +76,6 @@ export function renderPost(image: ImageData) {
[transform2Weight, transform2], [transform2Weight, transform2],
[transform3Weight, transform3], [transform3Weight, transform3],
]); ]);
flame.render(1, image);
render(flame, 1, image);
} }

View File

@ -13,8 +13,7 @@ import {
transform3Coefs, transform3Coefs,
transform3Pdj, transform3Pdj,
transform3Weight, transform3Weight,
weightedChoice, render,
plot,
} from "./2a-variations"; } from "./2a-variations";
import { TransformPost, transform2Post } from "./2b-post"; import { TransformPost, transform2Post } from "./2b-post";
@ -26,22 +25,9 @@ export class FlameFinal extends Flame {
super(transforms); super(transforms);
} }
render(quality: number, image: ImageData) { step() {
var x = Math.random() * 2 - 1; super.step();
var y = Math.random() * 2 - 1; [this.x, this.y] = this.final.apply(this.x, this.y);
const iter = quality * (image.width * image.height);
for (var i = 0; i < iter; i++) {
const transform = weightedChoice(this.transforms);
[x, y] = transform.apply(x, y);
// This line is the only thing that changes:
[x, y] = this.final.apply(x, y);
if (i > 20) {
plot(x, y, image);
}
}
} }
} }
@ -82,5 +68,5 @@ export function renderFinal(image: ImageData) {
transformFinal transformFinal
); );
flame.render(1, image); render(flame, 1, image);
} }

View File

@ -14,10 +14,14 @@ export default function () {
}); });
return ( return (
<Layout> <Layout>
<Canvas f={gasket} /> <div>
<Canvas f={renderBaseline} /> <Canvas f={gasket} />
<Canvas f={renderPost} /> <Canvas f={renderBaseline} />
<Canvas f={renderFinal} /> </div>
<div>
<Canvas f={renderPost} />
<Canvas f={renderFinal} />
</div>
</Layout> </Layout>
); );
} }