diff --git a/blog/2024-11-15-playing-with-fire/2-transforms/chaosGameFinal.ts b/blog/2024-11-15-playing-with-fire/2-transforms/chaosGameFinal.ts
index d8b7b2a..638846d 100644
--- a/blog/2024-11-15-playing-with-fire/2-transforms/chaosGameFinal.ts
+++ b/blog/2024-11-15-playing-with-fire/2-transforms/chaosGameFinal.ts
@@ -22,11 +22,11 @@ export function* chaosGameFinal({width, height, transforms, final, quality, step
[x, y] = transform(x, y);
// highlight-start
- [x, y] = final(x, y);
+ const [finalX, finalY] = final(x, y);
// highlight-end
if (i > 20)
- plot(x, y, image);
+ plot(finalX, finalY, image);
if (i % step === 0)
yield image;
diff --git a/blog/2024-11-15-playing-with-fire/3-log-density/FlameColor.tsx b/blog/2024-11-15-playing-with-fire/3-log-density/FlameColor.tsx
new file mode 100644
index 0000000..ea3aa49
--- /dev/null
+++ b/blog/2024-11-15-playing-with-fire/3-log-density/FlameColor.tsx
@@ -0,0 +1,37 @@
+import React, {useContext, useEffect, useState} from "react";
+import * as params from "../src/params";
+import {PainterContext} from "../src/Canvas";
+import {chaosGameColor} from "./chaosGameColor";
+
+type Props = {
+ quality?: number;
+ children?: React.ReactElement;
+}
+export default function FlameHistogram({quality, children}: Props) {
+ const {width, height, setPainter} = useContext(PainterContext);
+
+ const [counter, setCount] = useState(0);
+ const [xform1Color, setXform1Color] = useState(params.xform1Color);
+ const [xform2Color, setXform2Color] = useState(params.xform2Color);
+ const [xform3Color, setXform3Color] = useState(params.xform3Color);
+
+ useEffect(() => {
+ const gameParams = {
+ width,
+ height,
+ transforms: params.xforms,
+ final: params.xformFinal,
+ quality,
+ palette: params.palette,
+ colors: [xform1Color, xform2Color, xform3Color]
+ }
+ setPainter(chaosGameColor(gameParams));
+ }, [counter, xform1Color, xform2Color, xform3Color]);
+
+ return (
+ <>
+ {children}
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/blog/2024-11-15-playing-with-fire/3-log-density/chaosGameColor.ts b/blog/2024-11-15-playing-with-fire/3-log-density/chaosGameColor.ts
new file mode 100644
index 0000000..cb02bc1
--- /dev/null
+++ b/blog/2024-11-15-playing-with-fire/3-log-density/chaosGameColor.ts
@@ -0,0 +1,49 @@
+import {ChaosGameFinalProps} from "../2-transforms/chaosGameFinal";
+import {randomBiUnit} from "../src/randomBiUnit";
+import {randomChoice} from "../src/randomChoice";
+import {camera, histIndex} from "../src/camera";
+import {colorFromPalette, paintColor} from "./color";
+
+type ChaosGameHistogramProps = ChaosGameFinalProps & {
+ palette: number[];
+ colors: number[];
+}
+export function* chaosGameColor({width, height, transforms, final, palette, colors, quality, step}: ChaosGameHistogramProps) {
+ let iterations = (quality ?? 1) * width * height;
+ step = step ?? 100_000;
+
+ let currentColor = Math.random();
+ const red = Array(width * height).fill(0);
+ const green = Array(width * height).fill(0);
+ const blue = Array(width * height).fill(0);
+ const alpha = Array(width * height).fill(0);
+
+ let [x, y] = [randomBiUnit(), randomBiUnit()];
+
+ for (let i = 0; i < iterations; i++) {
+ const [transformIndex, transform] = randomChoice(transforms);
+ [x, y] = transform(x, y);
+
+ const [finalX, finalY] = final(x, y);
+
+ if (i > 20) {
+ const [pixelX, pixelY] = camera(finalX, finalY, width);
+ const pixelIndex = histIndex(pixelX, pixelY, width, 1);
+
+ if (pixelIndex < 0 || pixelIndex >= alpha.length)
+ continue;
+
+ currentColor = (currentColor + colors[transformIndex]) / 2;
+ const [r, g, b] = colorFromPalette(palette, currentColor);
+ red[pixelIndex] += r;
+ green[pixelIndex] += g;
+ blue[pixelIndex] += b;
+ alpha[pixelIndex] += 1;
+ }
+
+ if (i % step === 0)
+ yield paintColor(width, height, red, green, blue, alpha);
+ }
+
+ yield paintColor(width, height, red, green, blue, alpha);
+}
\ No newline at end of file
diff --git a/blog/2024-11-15-playing-with-fire/3-log-density/chaosGameHistogram.ts b/blog/2024-11-15-playing-with-fire/3-log-density/chaosGameHistogram.ts
index bc1d8e6..828f31e 100644
--- a/blog/2024-11-15-playing-with-fire/3-log-density/chaosGameHistogram.ts
+++ b/blog/2024-11-15-playing-with-fire/3-log-density/chaosGameHistogram.ts
@@ -8,7 +8,7 @@ export type ChaosGameHistogramProps = ChaosGameFinalProps & {
painter: (width: number, histogram: Uint32Array) => ImageData;
}
export function* chaosGameHistogram({width, height, transforms, final, quality, step, painter}: ChaosGameHistogramProps) {
- let iterations = (quality ?? 10) * width * height;
+ let iterations = (quality ?? 1) * width * height;
step = step ?? 100_000;
const histogram = new Uint32Array(width * height);
diff --git a/blog/2024-11-15-playing-with-fire/3-log-density/color.ts b/blog/2024-11-15-playing-with-fire/3-log-density/color.ts
new file mode 100644
index 0000000..983a059
--- /dev/null
+++ b/blog/2024-11-15-playing-with-fire/3-log-density/color.ts
@@ -0,0 +1,29 @@
+import {histIndex} from "../src/camera";
+
+export function colorFromPalette(palette: number[], colorIndex: number): [number, number, number] {
+ const paletteIndex = Math.floor(colorIndex * (palette.length / 3)) * 3;
+ return [palette[paletteIndex], palette[paletteIndex + 1], palette[paletteIndex + 2]];
+}
+
+export function paintColor(
+ width: number,
+ height: number,
+ red: number[],
+ green: number[],
+ blue: number[],
+ alpha: number[]
+): ImageData {
+ const image = new ImageData(width, height);
+
+ for (let i = 0; i < width * height; i++) {
+ const alphaScale = Math.log10(alpha[i]) / (alpha[i] * 1.5);
+
+ const pixelIndex = i * 4;
+ image.data[pixelIndex] = red[i] * alphaScale * 0xff;
+ image.data[pixelIndex + 1] = green[i] * alphaScale * 0xff;
+ image.data[pixelIndex + 2] = blue[i] * alphaScale * 0xff;
+ image.data[pixelIndex + 3] = alpha[i] * alphaScale * 0xff;
+ }
+
+ return image;
+}
\ No newline at end of file
diff --git a/blog/2024-11-15-playing-with-fire/3-log-density/index.mdx b/blog/2024-11-15-playing-with-fire/3-log-density/index.mdx
index 7554ede..df0efa2 100644
--- a/blog/2024-11-15-playing-with-fire/3-log-density/index.mdx
+++ b/blog/2024-11-15-playing-with-fire/3-log-density/index.mdx
@@ -1,6 +1,6 @@
---
slug: 2024/11/playing-with-fire-log-density
-title: "Playing with fire: Log-density display"
+title: "Playing with fire: Log-density and color"
date: 2024-11-15 14:00:00
authors: [bspeice]
tags: []
@@ -32,10 +32,16 @@ import Canvas from "../src/Canvas";
import FlameHistogram from "./FlameHistogram";
import {paintLinear} from "./paintLinear";
-
+
## Log display
import {paintLogarithmic} from './paintLogarithmic'
-
\ No newline at end of file
+
+
+## Color
+
+import FlameColor from "./FlameColor";
+
+
\ No newline at end of file
diff --git a/blog/2024-11-15-playing-with-fire/4-color.mdx b/blog/2024-11-15-playing-with-fire/4-color.mdx
deleted file mode 100644
index e9a54aa..0000000
--- a/blog/2024-11-15-playing-with-fire/4-color.mdx
+++ /dev/null
@@ -1,7 +0,0 @@
----
-slug: 2024/11/playing-with-fire-color
-title: "Playing with fire: Color"
-date: 2024-11-15 15:00:00
-authors: [bspeice]
-tags: []
----
\ No newline at end of file
diff --git a/blog/2024-11-15-playing-with-fire/src/params.ts b/blog/2024-11-15-playing-with-fire/src/params.ts
index 4501dc2..4b6bdd7 100644
--- a/blog/2024-11-15-playing-with-fire/src/params.ts
+++ b/blog/2024-11-15-playing-with-fire/src/params.ts
@@ -76,7 +76,7 @@ export const xforms: [number, Transform][] = [
export const xformFinal: Transform = applyPost(xformFinalCoefsPost, applyTransform(xformFinalCoefs, xformFinalVariations));
-export const palette =
+export const paletteString =
"7E3037762C45722B496E2A4E6A2950672853652754632656" +
"5C265C5724595322574D2155482153462050451F4E441E4D" +
"431E4C3F1E473F1E453F1E433F1E3F3F1E3B3E1E393E1E37" +
@@ -109,3 +109,13 @@ export const palette =
"E9A411E5A313E1A113DD9F13D99D14D49C15D09815CC9518" +
"C79318C38F1ABE8B1AB9871DB4811FB07D1FAB7621A67123" +
"A16A249C6227975E289256298E502A89482C853F2D803A2E"
+
+function hexToBytes(hex: string) {
+ let bytes: number[] = [];
+ for (let i = 0; i < hex.length; i += 2) {
+ bytes.push(parseInt(hex.substring(i, i + 2), 16));
+ }
+ return bytes;
+}
+
+export const palette = hexToBytes(paletteString).map(value => value / 0xff);
\ No newline at end of file