diff --git a/blog/2024-11-15-playing-with-fire/1-introduction/chaosGame.js b/blog/2024-11-15-playing-with-fire/1-introduction/chaosGame.js index 49f004f..ca42723 100644 --- a/blog/2024-11-15-playing-with-fire/1-introduction/chaosGame.js +++ b/blog/2024-11-15-playing-with-fire/1-introduction/chaosGame.js @@ -1,24 +1,40 @@ -// Hint: try increasing the iteration count -const iterations = 10000; +function Gasket() { + // Hint: try increasing the iteration count + const iterations = 10000; -// Hint: negating `x` and `y` creates some interesting images -const functions = [ - (x, y) => [x / 2, y / 2], - (x, y) => [(x + 1) / 2, y / 2], - (x, y) => [x / 2, (y + 1) / 2] -] + // Hint: negating `x` and `y` creates some interesting images + const functions = [ + (x, y) => [x / 2, y / 2], + (x, y) => [(x + 1) / 2, y / 2], + (x, y) => [x / 2, (y + 1) / 2] + ] -function chaosGame(image) { - var [x, y] = [randomBiUnit(), randomBiUnit()]; + const image = new ImageData(600, 600); + function* chaosGame() { + var [x, y] = [randomBiUnit(), randomBiUnit()]; - for (var count = 0; count < iterations; count++) { - const i = randomInteger(0, functions.length); - [x, y] = functions[i](x, y); + for (var count = 0; count < iterations; count++) { + const i = randomInteger(0, functions.length); + [x, y] = functions[i](x, y); - if (count > 20) { - plot(x, y, image); + if (count > 20) { + plot(x, y, image); + } + + if (count % 1000 === 0) { + yield image; + } } + + yield image; } + + return ( + + ) } -render() +render() diff --git a/blog/2024-11-15-playing-with-fire/1-introduction/scope.tsx b/blog/2024-11-15-playing-with-fire/1-introduction/scope.tsx index eeefb2c..d5d2d99 100644 --- a/blog/2024-11-15-playing-with-fire/1-introduction/scope.tsx +++ b/blog/2024-11-15-playing-with-fire/1-introduction/scope.tsx @@ -1,30 +1,12 @@ -import React, {useContext, useEffect} from "react"; import randomBiUnit from './biunit'; import plot from './plot'; import randomInteger from './randint'; -import Canvas, {ImageContext} from "../src/Canvas"; - -interface Props { - renderFn: (image: ImageData) => void; -} -function Render({renderFn}: Props) { - const {width, height, setImage} = useContext(ImageContext); - const image = new ImageData(width, height); - - useEffect(() => { - renderFn(image); - setImage(image); - }, []); - - return <>; -} +import Canvas from "../src/Canvas"; const Scope = { plot, randomBiUnit, randomInteger, - Canvas, - Gasket: ({renderFn}: Props) => - + Canvas } export default Scope; \ No newline at end of file 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 2835555..35f3c7a 100644 --- a/blog/2024-11-15-playing-with-fire/src/Canvas.tsx +++ b/blog/2024-11-15-playing-with-fire/src/Canvas.tsx @@ -1,49 +1,54 @@ -import React, {createContext, useCallback, useContext, useEffect, useRef, useState} from "react"; +import React, {useCallback, useEffect, useState} from "react"; import {useColorMode} from "@docusaurus/theme-common"; -interface IImageContext { - width: number; - height: number; - setImage: (image: ImageData) => void; -} -export const ImageContext = createContext(null); - interface Props { width: number; height: number; + painter: Iterator; children?: React.ReactNode; } -export default function Canvas({width, height, children}) { - const [canvasCtx, setCanvasCtx] = useState(null); +export default function Canvas({width, height, painter, children}: Props) { + const {colorMode} = useColorMode(); + const [image, setImage] = useState<[ImageData]>(null); + + const [canvasCtx, setCanvasCtx] = useState(null); const canvasRef = useCallback(node => { if (node !== null) { setCanvasCtx(node.getContext("2d")); } }, []); - const {colorMode} = useColorMode(); - const [image, setImage] = useState(new ImageData(width, height)); const paintImage = new ImageData(width, height); - - useEffect(() => { - if (!canvasCtx) { + const paint = () => { + if (!canvasCtx || !image) { return; } - for (const [index, value] of image.data.entries()) { - // If dark mode is active, invert the color scheme - const adjustValue = colorMode === 'light' ? value : 255 - value; - - // Alpha values never change - paintImage.data[index] = index % 4 === 3 ? value : adjustValue; + for (const [index, value] of image[0].data.entries()) { + if (index % 4 === 3) { + // Alpha values are copied as-is + paintImage.data[index] = value; + } else { + // If dark mode is active, invert the color + paintImage.data[index] = colorMode === 'light' ? value : 255 - value; + } } - console.log("Painting image"); canvasCtx.putImageData(paintImage, 0, 0); - }, [canvasCtx, colorMode, image]); + } + useEffect(paint, [colorMode, image]); + + const animate = () => { + const nextImage = painter.next().value; + if (nextImage) { + setImage([nextImage]) + requestAnimationFrame(animate); + } + } + useEffect(animate, [canvasCtx]); return ( - + <> {children} - + ) } \ No newline at end of file