From ce1873147cb417b569ea73e1999d754ee64da8eb Mon Sep 17 00:00:00 2001 From: Bradlee Speice Date: Mon, 2 Dec 2024 22:36:25 -0500 Subject: [PATCH] Color render working --- .../2-transforms/chaosGameFinal.ts | 4 +- .../3-log-density/FlameColor.tsx | 37 ++++++++++++++ .../3-log-density/chaosGameColor.ts | 49 +++++++++++++++++++ .../3-log-density/chaosGameHistogram.ts | 2 +- .../3-log-density/color.ts | 29 +++++++++++ .../3-log-density/index.mdx | 12 +++-- blog/2024-11-15-playing-with-fire/4-color.mdx | 7 --- .../src/params.ts | 12 ++++- 8 files changed, 138 insertions(+), 14 deletions(-) create mode 100644 blog/2024-11-15-playing-with-fire/3-log-density/FlameColor.tsx create mode 100644 blog/2024-11-15-playing-with-fire/3-log-density/chaosGameColor.ts create mode 100644 blog/2024-11-15-playing-with-fire/3-log-density/color.ts delete mode 100644 blog/2024-11-15-playing-with-fire/4-color.mdx 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