diff --git a/blog/2024-11-15-playing-with-fire/2-transforms/CoefEditor.tsx b/blog/2024-11-15-playing-with-fire/2-transforms/CoefEditor.tsx
index 1eeeb86..4a91a38 100644
--- a/blog/2024-11-15-playing-with-fire/2-transforms/CoefEditor.tsx
+++ b/blog/2024-11-15-playing-with-fire/2-transforms/CoefEditor.tsx
@@ -18,32 +18,32 @@ export const CoefEditor = ({title, isPost, coefs, setCoefs, resetCoefs}: Props)
{title} {resetButton}
{isPost ? \alpha : 'a'}: {coefs.a}
-
setCoefs({...coefs, a: Number(e.currentTarget.value)})}/>
{isPost ? \beta : 'b'}: {coefs.b}
-
setCoefs({...coefs, b: Number(e.currentTarget.value)})}/>
{isPost ? \gamma : 'c'}: {coefs.c}
-
setCoefs({...coefs, c: Number(e.currentTarget.value)})}/>
{isPost ? \delta : 'd'}: {coefs.d}
-
setCoefs({...coefs, d: Number(e.currentTarget.value)})}/>
{isPost ? \epsilon : 'e'}: {coefs.e}
-
setCoefs({...coefs, e: Number(e.currentTarget.value)})}/>
{isPost ? \zeta : 'f'}: {coefs.f}
-
setCoefs({...coefs, f: Number(e.currentTarget.value)})}/>
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
index 0a80624..c125281 100644
--- 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
@@ -1,41 +1,15 @@
import React, {useContext, useEffect, useMemo, useRef, useState} from "react";
import * as params from "../src/params";
-import {PainterContext} from "../src/Canvas";
-import {chaosGameColor} from "./chaosGameColor";
+import {InvertibleCanvas, PainterContext} from "../src/Canvas";
+import {chaosGameColor, ChaosGameColorProps, TransformColor} from "./chaosGameColor";
import styles from "../src/css/styles.module.css";
-import {palette} from "../src/params";
-
-const colorSwatch = (width: number, height: number, palette: number[], color: number): ImageData => {
- return new ImageData(width, height);
-}
-
-type ColorSliderProps = {
- title: string;
- palette: number[];
- color: number;
- setColor: (color: number) => void;
- children?: React.ReactNode;
-}
-const ColorSlider: React.FC = ({title, palette, color, setColor, children}) => {
- const swatch = useMemo(() => colorSwatch(50, 20, palette, color), [palette, color]);
-
- return (
- <>
-
-
{title}: {color}
-
setColor(Number(e.currentTarget.value))}/>
-
- {children}
- >
- )
-}
+import {colorFromPalette} from "@site/blog/2024-11-15-playing-with-fire/3-log-density/color";
type PaletteBarProps = {
height: number;
palette: number[];
- sizingStyle: any;
+ sizingStyle?: any;
children?: React.ReactNode;
}
const PaletteBar: React.FC = ({height, palette, sizingStyle, children}) => {
@@ -61,6 +35,76 @@ const PaletteBar: React.FC = ({height, palette, sizingStyle, ch
)
}
+type AutoSizingCanvasProps = {
+ painter: (width: number, height: number) => ImageData;
+}
+const AutoSizingCanvas: React.FC = ({painter}) => {
+ const sizingRef = useRef(null);
+ const [width, setWidth] = useState(0);
+ const [height, setHeight] = useState(0);
+
+ useEffect(() => {
+ if (sizingRef) {
+ setWidth(sizingRef.current.offsetWidth);
+ setHeight(sizingRef.current.offsetHeight)
+ }
+ }, [sizingRef]);
+
+ const image: [ImageData] = useMemo(() => (width && height) ? [painter(width, height)] : null, [painter, width, height]);
+
+ return (
+
+ )
+}
+
+const colorSwatchPainter = (palette: number[], color: number) =>
+ (width: number, height: number) => {
+ const [r, g, b] = colorFromPalette(palette, color);
+ const image = new ImageData(width, height);
+ for (let i = 0; i < image.data.length; i += 4) {
+ image.data[i] = r * 0xff;
+ image.data[i + 1] = g * 0xff;
+ image.data[i + 2] = b * 0xff;
+ image.data[i + 3] = 0xff;
+ }
+
+ return image;
+ }
+
+type ColorEditorProps = {
+ title: string;
+ palette: number[];
+ transformColor: TransformColor;
+ setTransformColor: (transformColor: TransformColor) => void;
+ resetTransformColor: () => void;
+ children?: React.ReactNode;
+}
+const ColorEditor: React.FC = ({title, palette, transformColor, setTransformColor, resetTransformColor, children}) => {
+ const painter = useMemo(() => colorSwatchPainter(palette, transformColor.color), [palette, transformColor]);
+ const resetButton =
+
+ return (
+ <>
+
+
{title} {resetButton}
+
+
Color: {transformColor.color}
+
setTransformColor({...transformColor, color: Number(e.currentTarget.value)})}/>
+
+
+
Color speed: {transformColor.colorSpeed}
+
setTransformColor({...transformColor, colorSpeed: Number(e.currentTarget.value)})}/>
+
+
+
+ >
+ )
+}
+
type Props = {
quality?: number;
children?: React.ReactElement;
@@ -68,36 +112,64 @@ type Props = {
export default function FlameColor({quality, children}: Props) {
const {width, height, setPainter} = useContext(PainterContext);
- const [xform1Color, setXform1Color] = useState(params.xform1Color);
- const [xform2Color, setXform2Color] = useState(params.xform2Color);
- const [xform3Color, setXform3Color] = useState(params.xform3Color);
- const resetColor = () => {
- setXform1Color(params.xform1Color);
- setXform2Color(params.xform2Color);
- setXform3Color(params.xform3Color);
- }
+ const xform1ColorDefault: TransformColor = {color: params.xform1Color, colorSpeed: 0.5};
+ const [xform1Color, setXform1Color] = useState(xform1ColorDefault);
+ const resetXform1Color = () => setXform1Color(xform1ColorDefault);
+
+ const xform2ColorDefault: TransformColor = {color: params.xform2Color, colorSpeed: 0.5};
+ const [xform2Color, setXform2Color] = useState(xform2ColorDefault);
+ const resetXform2Color = () => setXform2Color(xform2ColorDefault);
+
+ const xform3ColorDefault: TransformColor = {color: params.xform3Color, colorSpeed: 0.5};
+ const [xform3Color, setXform3Color] = useState(xform3ColorDefault);
+ const resetXform3Color = () => setXform3Color(xform3ColorDefault);
+
+ const xformFinalColorDefault: TransformColor = {color: params.xformFinalColor, colorSpeed: 0};
+ const [xformFinalColor, setXformFinalColor] = useState(xformFinalColorDefault);
+ const resetXformFinalColor = () => setXformFinalColor(xformFinalColorDefault);
useEffect(() => {
- const gameParams = {
+ const gameParams: ChaosGameColorProps = {
width,
height,
transforms: params.xforms,
final: params.xformFinal,
quality,
palette: params.palette,
- colors: [xform1Color, xform2Color, xform3Color]
+ colors: [xform1Color, xform2Color, xform3Color],
+ finalColor: xformFinalColor
}
setPainter(chaosGameColor(gameParams));
- }, [xform1Color, xform2Color, xform3Color]);
+ }, [xform1Color, xform2Color, xform3Color, xformFinalColor]);
- const resetButton =
return (
-
-
Transform Colors {resetButton}
-
-
-
-
-
+ <>
+
+
+
+
+
+ {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
index cb02bc1..11b6ed6 100644
--- 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
@@ -4,11 +4,21 @@ import {randomChoice} from "../src/randomChoice";
import {camera, histIndex} from "../src/camera";
import {colorFromPalette, paintColor} from "./color";
-type ChaosGameHistogramProps = ChaosGameFinalProps & {
- palette: number[];
- colors: number[];
+export type TransformColor = {
+ color: number;
+ colorSpeed: number;
}
-export function* chaosGameColor({width, height, transforms, final, palette, colors, quality, step}: ChaosGameHistogramProps) {
+
+function mixColor(color1: number, color2: number, colorSpeed: number) {
+ return color1 * (1 - colorSpeed) + color2 * colorSpeed;
+}
+
+export type ChaosGameColorProps = ChaosGameFinalProps & {
+ palette: number[];
+ colors: TransformColor[];
+ finalColor: TransformColor;
+}
+export function* chaosGameColor({width, height, transforms, final, palette, colors, finalColor, quality, step}: ChaosGameColorProps) {
let iterations = (quality ?? 1) * width * height;
step = step ?? 100_000;
@@ -33,8 +43,11 @@ export function* chaosGameColor({width, height, transforms, final, palette, colo
if (pixelIndex < 0 || pixelIndex >= alpha.length)
continue;
- currentColor = (currentColor + colors[transformIndex]) / 2;
- const [r, g, b] = colorFromPalette(palette, currentColor);
+ const transformColor = colors[transformIndex];
+ currentColor = mixColor(currentColor, transformColor.color, transformColor.colorSpeed);
+
+ const colorFinal = mixColor(currentColor, finalColor.color, finalColor.colorSpeed);
+ const [r, g, b] = colorFromPalette(palette, colorFinal);
red[pixelIndex] += r;
green[pixelIndex] += g;
blue[pixelIndex] += b;
diff --git a/blog/2024-11-15-playing-with-fire/src/Canvas.tsx b/blog/2024-11-15-playing-with-fire/src/Canvas.tsx
index 9a9c998..e4b8fbe 100644
--- a/blog/2024-11-15-playing-with-fire/src/Canvas.tsx
+++ b/blog/2024-11-15-playing-with-fire/src/Canvas.tsx
@@ -26,7 +26,7 @@ type InvertibleCanvasProps = {
* @param hidden Hide the canvas element
* @param image Image data to draw on the canvas
*/
-const InvertibleCanvas: React.FC = ({width, height, image}) => {
+export const InvertibleCanvas: React.FC = ({width, height, image}) => {
const [canvasCtx, setCanvasCtx] = useState(null);
const canvasRef = useCallback(node => {
if (node !== null) {
diff --git a/blog/2024-11-15-playing-with-fire/src/css/styles.module.css b/blog/2024-11-15-playing-with-fire/src/css/styles.module.css
index 4ceecaf..6c16604 100644
--- a/blog/2024-11-15-playing-with-fire/src/css/styles.module.css
+++ b/blog/2024-11-15-playing-with-fire/src/css/styles.module.css
@@ -22,6 +22,10 @@
margin: 0
}
+.inputElement > input {
+ width: 100%;
+}
+
.inputReset {
display: flex;
float: right;