mirror of
				https://github.com/bspeice/speice.io
				synced 2025-11-03 18:10:32 -05:00 
			
		
		
		
	Render the gasket
Need to get rid of Plotly, unfortuantely - causes issues with hydration. Seems like Victory is better able to handle what I need.
This commit is contained in:
		@ -1,6 +1,6 @@
 | 
				
			|||||||
---
 | 
					---
 | 
				
			||||||
slug: 2024/11/playing-with-fire
 | 
					slug: 2024/11/playing-with-fire
 | 
				
			||||||
title: "Playing with fire: Introduction"
 | 
					title: "Playing with fire: The fractal flame algorithm"
 | 
				
			||||||
date: 2024-11-15 12:00:00
 | 
					date: 2024-11-15 12:00:00
 | 
				
			||||||
authors: [bspeice]
 | 
					authors: [bspeice]
 | 
				
			||||||
tags: []
 | 
					tags: []
 | 
				
			||||||
@ -12,9 +12,9 @@ Wikipedia [describes](https://en.wikipedia.org/wiki/Fractal_flame) fractal flame
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
I think of them a different way: beauty in mathematics.
 | 
					I think of them a different way: beauty in mathematics.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import isDarkMode from "@site/src/isDarkMode";
 | 
					import isDarkMode from '@site/src/isDarkMode'
 | 
				
			||||||
import bannerDark from "./banner-dark.png"
 | 
					import bannerDark from '../banner-dark.png'
 | 
				
			||||||
import bannerLight from "./banner-light.png"
 | 
					import bannerLight from '../banner-light.png'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<center>
 | 
					<center>
 | 
				
			||||||
    <!-- Why are these backwards? -->
 | 
					    <!-- Why are these backwards? -->
 | 
				
			||||||
@ -201,6 +201,8 @@ $$
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
### The chaos game
 | 
					### The chaos game
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import CodeBlock from '@theme/CodeBlock'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Next, how do we find out all the points in $S$? The paper lays out an algorithm called the "chaos game":
 | 
					Next, how do we find out all the points in $S$? The paper lays out an algorithm called the "chaos game":
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$$
 | 
					$$
 | 
				
			||||||
@ -213,3 +215,32 @@ $$
 | 
				
			|||||||
\}
 | 
					\}
 | 
				
			||||||
\end{align*}
 | 
					\end{align*}
 | 
				
			||||||
$$
 | 
					$$
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Let's turn this into code, one piece at a time.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					First, the "bi-unit square" is the range $[-1, 1]$. We can pick a random point like this:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import biunitSource from '!!raw-loader!./biunit'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<CodeBlock language="typescript">{biunitSource}</CodeBlock>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Next, we need to choose a random integer from $0$ to $n - 1$:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import randintSource from '!!raw-loader!./randint'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<CodeBlock language="typescript">{randintSource}</CodeBlock>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Finally, implementing the `plot` function. Web browsers have a [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API)
 | 
				
			||||||
 | 
					we can use for 2D graphics. In our case, the plot function will take an $(x,y)$ coordinate and plot it by
 | 
				
			||||||
 | 
					coloring the corresponding pixel in an [ImageData](https://developer.mozilla.org/en-US/docs/Web/API/ImageData):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import plotSource from '!!raw-loader!./plot'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<CodeBlock language="typescript">{plotSource}</CodeBlock>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Playground from '@theme/Playground'
 | 
				
			||||||
 | 
					import Scope from './scope'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Gasket from '!!raw-loader!./Gasket'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Playground scope={Scope}>{Gasket}</Playground>
 | 
				
			||||||
							
								
								
									
										43
									
								
								blog/2024-11-15-playing-with-fire/1-introduction/Gasket.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								blog/2024-11-15-playing-with-fire/1-introduction/Gasket.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					function Gasket(props) {
 | 
				
			||||||
 | 
					    const iterations = 1000;
 | 
				
			||||||
 | 
					    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()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (var i = 0; i < iterations; i++) {
 | 
				
			||||||
 | 
					            const f = functions[randomInteger(0, functions.length)];
 | 
				
			||||||
 | 
					            [x, y] = f(x, y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (i > 20) {
 | 
				
			||||||
 | 
					                plot(x, y, image);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function onClickRender() {
 | 
				
			||||||
 | 
					        /** @type{HTMLCanvasElement} */
 | 
				
			||||||
 | 
					        const canvas = document.getElementById('canvas-gasket');
 | 
				
			||||||
 | 
					        const context = canvas.getContext('2d');
 | 
				
			||||||
 | 
					        const image = context.createImageData(canvas.width, canvas.height);
 | 
				
			||||||
 | 
					        chaosGame(image);
 | 
				
			||||||
 | 
					        context.putImageData(image, 0, 0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return <div style={{width: '100%'}}>
 | 
				
			||||||
 | 
					        <center>
 | 
				
			||||||
 | 
					            <button onClick={onClickRender}>Play chaos game</button>
 | 
				
			||||||
 | 
					            <hr/>
 | 
				
			||||||
 | 
					        </center>
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					            <canvas
 | 
				
			||||||
 | 
					                id={'canvas-gasket'}
 | 
				
			||||||
 | 
					                style={{width: '100%', aspectRatio: '1 / 1'}}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					export default function randomBiUnit(): number {
 | 
				
			||||||
 | 
					    return Math.random() * 2 - 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										32
									
								
								blog/2024-11-15-playing-with-fire/1-introduction/plot.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								blog/2024-11-15-playing-with-fire/1-introduction/plot.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					export default function plot(x: number, y: number, image: ImageData) {
 | 
				
			||||||
 | 
					    // Translate (x,y) coordinates to pixel coordinates.
 | 
				
			||||||
 | 
					    // The display range we care about is x=[0, 1], y=[0, 1],
 | 
				
			||||||
 | 
					    // so our pixelX and pixelY coordinates are easy to calculate:
 | 
				
			||||||
 | 
					    const pixelX = Math.floor(x * image.width);
 | 
				
			||||||
 | 
					    const pixelY = Math.floor(y * image.height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // If we have an (x,y) coordinate outside the display range,
 | 
				
			||||||
 | 
					    // skip it
 | 
				
			||||||
 | 
					    if (
 | 
				
			||||||
 | 
					        pixelX < 0 ||
 | 
				
			||||||
 | 
					        pixelX > image.width ||
 | 
				
			||||||
 | 
					        pixelY < 0 ||
 | 
				
			||||||
 | 
					        pixelY > image.height
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ImageData is an array that contains four bytes per pixel
 | 
				
			||||||
 | 
					    // (one for each of the red, green, blue, and alpha values).
 | 
				
			||||||
 | 
					    // The (pixelX, pixelY) coordinates are used to find where
 | 
				
			||||||
 | 
					    // in the image we need to write.
 | 
				
			||||||
 | 
					    const index = pixelY * (image.width * 4) + pixelX * 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Set the pixel to black by writing a 0 to the first three
 | 
				
			||||||
 | 
					    // bytes (red, green, blue), and 256 to the last byte (alpha),
 | 
				
			||||||
 | 
					    // starting at our index:
 | 
				
			||||||
 | 
					    image.data[index] = 0;
 | 
				
			||||||
 | 
					    image.data[index + 1] = 0;
 | 
				
			||||||
 | 
					    image.data[index + 2] = 0;
 | 
				
			||||||
 | 
					    image.data[index + 3] = 0xff;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					export default function randomInteger(min: number, max: number): number {
 | 
				
			||||||
 | 
					    return Math.floor(Math.random() * (max - min)) + min;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								blog/2024-11-15-playing-with-fire/1-introduction/scope.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								blog/2024-11-15-playing-with-fire/1-introduction/scope.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import randomBiUnit from './biunit';
 | 
				
			||||||
 | 
					import plot from './plot';
 | 
				
			||||||
 | 
					import randomInteger from './randint';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Scope = {
 | 
				
			||||||
 | 
					    React,
 | 
				
			||||||
 | 
					    plot,
 | 
				
			||||||
 | 
					    randomBiUnit,
 | 
				
			||||||
 | 
					    randomInteger
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export default Scope;
 | 
				
			||||||
@ -1,29 +0,0 @@
 | 
				
			|||||||
/**
 | 
					 | 
				
			||||||
 * Generate a uniform random number in the range (-1, 1)
 | 
					 | 
				
			||||||
 * 
 | 
					 | 
				
			||||||
 * @returns
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export function randomBiUnit() {
 | 
					 | 
				
			||||||
  return Math.random() * 2 - 1;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Generate a uniform random integer in the range [min, max)
 | 
					 | 
				
			||||||
 * 
 | 
					 | 
				
			||||||
 * @param min 
 | 
					 | 
				
			||||||
 * @param max 
 | 
					 | 
				
			||||||
 * @returns 
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export function randomInteger(min: number, max: number) {
 | 
					 | 
				
			||||||
  return Math.floor(Math.random() * (max - min)) + min;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// https://stackoverflow.com/a/34356351
 | 
					 | 
				
			||||||
export function hexToBytes(hex: string) {
 | 
					 | 
				
			||||||
  var bytes = [];
 | 
					 | 
				
			||||||
  for (var i = 0; i < hex.length; i += 2) {
 | 
					 | 
				
			||||||
    bytes.push(parseInt(hex.substring(i, i + 2), 16));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return bytes;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -28,13 +28,14 @@ const config: Config = {
 | 
				
			|||||||
    locales: ['en'],
 | 
					    locales: ['en'],
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  themes: ['@docusaurus/theme-live-codeblock'],
 | 
				
			||||||
  presets: [
 | 
					  presets: [
 | 
				
			||||||
    [
 | 
					    [
 | 
				
			||||||
      'classic',
 | 
					      'classic',
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        docs: false,
 | 
					        docs: false,
 | 
				
			||||||
        blog: {
 | 
					        blog: {
 | 
				
			||||||
          routeBasePath: "/",
 | 
					          routeBasePath: '/',
 | 
				
			||||||
          blogSidebarTitle: 'All posts',
 | 
					          blogSidebarTitle: 'All posts',
 | 
				
			||||||
          blogSidebarCount: 'ALL',
 | 
					          blogSidebarCount: 'ALL',
 | 
				
			||||||
          showReadingTime: true,
 | 
					          showReadingTime: true,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2008
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2008
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										11
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								package.json
									
									
									
									
									
								
							@ -15,19 +15,22 @@
 | 
				
			|||||||
    "typecheck": "tsc"
 | 
					    "typecheck": "tsc"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@docusaurus/core": "^3.6.0",
 | 
					    "@docusaurus/core": "^3.6.1",
 | 
				
			||||||
    "@docusaurus/faster": "^3.5.2",
 | 
					    "@docusaurus/faster": "^3.6.1",
 | 
				
			||||||
    "@docusaurus/preset-classic": "^3.6.0",
 | 
					    "@docusaurus/preset-classic": "^3.6.1",
 | 
				
			||||||
 | 
					    "@docusaurus/theme-live-codeblock": "^3.6.1",
 | 
				
			||||||
    "@mdx-js/react": "^3.0.0",
 | 
					    "@mdx-js/react": "^3.0.0",
 | 
				
			||||||
    "clsx": "^2.0.0",
 | 
					    "clsx": "^2.0.0",
 | 
				
			||||||
    "docusaurus-lunr-search": "^3.5.0",
 | 
					    "docusaurus-lunr-search": "^3.5.0",
 | 
				
			||||||
    "plotly.js": "^2.35.2",
 | 
					    "plotly.js": "^2.35.2",
 | 
				
			||||||
    "prism-react-renderer": "^2.3.0",
 | 
					    "prism-react-renderer": "^2.3.0",
 | 
				
			||||||
 | 
					    "raw-loader": "^4.0.2",
 | 
				
			||||||
    "react": "^18.0.0",
 | 
					    "react": "^18.0.0",
 | 
				
			||||||
    "react-dom": "^18.0.0",
 | 
					    "react-dom": "^18.0.0",
 | 
				
			||||||
    "react-plotly.js": "^2.6.0",
 | 
					    "react-plotly.js": "^2.6.0",
 | 
				
			||||||
    "rehype-katex": "^7.0.1",
 | 
					    "rehype-katex": "^7.0.1",
 | 
				
			||||||
    "remark-math": "^6.0.0"
 | 
					    "remark-math": "^6.0.0",
 | 
				
			||||||
 | 
					    "victory": "^37.3.2"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@docusaurus/module-type-aliases": "^3.6.0",
 | 
					    "@docusaurus/module-type-aliases": "^3.6.0",
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user