mirror of
				https://github.com/bspeice/speice.io
				synced 2025-11-03 18:10:32 -05:00 
			
		
		
		
	Update documentation
This commit is contained in:
		@ -1,5 +1,7 @@
 | 
				
			|||||||
export function plot(x: number, y: number, image: ImageData) {
 | 
					export function plot(x: number, y: number, image: ImageData) {
 | 
				
			||||||
    // Translate (x,y) coordinates to pixel coordinates.
 | 
					    // Translate (x,y) coordinates to pixel coordinates;
 | 
				
			||||||
 | 
					    // also known as a "camera" function.
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
    // The display range we care about is x=[0, 1], y=[0, 1],
 | 
					    // The display range we care about is x=[0, 1], y=[0, 1],
 | 
				
			||||||
    // so our pixelX and pixelY coordinates are easy to calculate:
 | 
					    // so our pixelX and pixelY coordinates are easy to calculate:
 | 
				
			||||||
    const pixelX = Math.floor(x * image.width);
 | 
					    const pixelX = Math.floor(x * image.width);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +0,0 @@
 | 
				
			|||||||
---
 | 
					 | 
				
			||||||
slug: 2024/11/playing-with-fire-log-density
 | 
					 | 
				
			||||||
title: "Playing with fire: Log-density display"
 | 
					 | 
				
			||||||
date: 2024-11-15 14:00:00
 | 
					 | 
				
			||||||
authors: [bspeice]
 | 
					 | 
				
			||||||
tags: []
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Testing
 | 
					 | 
				
			||||||
@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					import {VictoryArea} from "victory";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function F() {
 | 
				
			||||||
 | 
					    return <VictoryArea data={}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					// hidden-start
 | 
				
			||||||
 | 
					import {VictoryChart} from "victory";
 | 
				
			||||||
 | 
					import {camera, histIndex} from "../src/camera";
 | 
				
			||||||
 | 
					// hidden-end
 | 
				
			||||||
 | 
					export class PlotHistogram {
 | 
				
			||||||
 | 
					    public readonly pixels: Uint32Array;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public constructor(private readonly width: number, height: number) {
 | 
				
			||||||
 | 
					        this.pixels = new Uint32Array(width * height);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public plot(x: number, y: number) {
 | 
				
			||||||
 | 
					        const [pixelX, pixelY] = camera(x, y, this.width);
 | 
				
			||||||
 | 
					        const pixelIndex = histIndex(pixelX, pixelY, this.width, 1);
 | 
				
			||||||
 | 
					        this.pixels[pixelIndex] += 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public getHistogram() {
 | 
				
			||||||
 | 
					        const data = new Map<number, number>();
 | 
				
			||||||
 | 
					        this.pixels.forEach(value => {
 | 
				
			||||||
 | 
					            const bucket = 32 - Math.clz32(value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (bucket in data) {
 | 
				
			||||||
 | 
					                data[bucket] += 1;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                data[bucket] = 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const output: {x: number, y: number}[] = [];
 | 
				
			||||||
 | 
					        data.forEach((bucket, value) =>
 | 
				
			||||||
 | 
					            output.push({x: Math.pow(bucket, 2), y: value}));
 | 
				
			||||||
 | 
					        return output;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										28
									
								
								blog/2024-11-15-playing-with-fire/3-log-density/index.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								blog/2024-11-15-playing-with-fire/3-log-density/index.mdx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					---
 | 
				
			||||||
 | 
					slug: 2024/11/playing-with-fire-log-density
 | 
				
			||||||
 | 
					title: "Playing with fire: Log-density display"
 | 
				
			||||||
 | 
					date: 2024-11-15 14:00:00
 | 
				
			||||||
 | 
					authors: [bspeice]
 | 
				
			||||||
 | 
					tags: []
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					So far, our `plot()` function has been fairly simple; map an input coordinate
 | 
				
			||||||
 | 
					to a specific pixel, and color in that pixel.
 | 
				
			||||||
 | 
					This works well for simple function systems (like Sierpinski's Gasket),
 | 
				
			||||||
 | 
					but more complex systems (like our reference parameters) produce grainy images.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Every time we "turn on" pixels that have already been enabled, we're wasting work.
 | 
				
			||||||
 | 
					Can we do something more intelligent with that information?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!-- truncate -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Image histograms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To start with, it's worth demonstrating how much work is actually "wasted."
 | 
				
			||||||
 | 
					We'll render the reference image again, but this time, counting the times
 | 
				
			||||||
 | 
					we tried to turn on a pixel.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import CodeBlock from "@theme/CodeBlock";
 | 
				
			||||||
 | 
					import plotHistogramSource from "!!raw-loader!./PlotHistogram";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<CodeBlock language="typescript">{plotHistogramSource}</CodeBlock>
 | 
				
			||||||
@ -16,6 +16,7 @@ export const PainterContext = createContext<PainterProps>(null);
 | 
				
			|||||||
interface CanvasProps {
 | 
					interface CanvasProps {
 | 
				
			||||||
    width?: number;
 | 
					    width?: number;
 | 
				
			||||||
    height?: number;
 | 
					    height?: number;
 | 
				
			||||||
 | 
					    hidden?: boolean;
 | 
				
			||||||
    children?: React.ReactNode;
 | 
					    children?: React.ReactNode;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -23,31 +24,39 @@ interface CanvasProps {
 | 
				
			|||||||
 * Draw fractal flames to a canvas.
 | 
					 * Draw fractal flames to a canvas.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This component is a bit involved because it attempts to solve
 | 
					 * This component is a bit involved because it attempts to solve
 | 
				
			||||||
 * a couple problems at the same time:
 | 
					 * a couple problems at once:
 | 
				
			||||||
 *  - Incrementally drawing an image to the canvas
 | 
					 *  - Incrementally drawing an image to the canvas
 | 
				
			||||||
 *  - Interrupting drawing with new parameters on demand
 | 
					 *  - Interrupting drawing with new parameters
 | 
				
			||||||
 *  - Dark mode
 | 
					 *  - Dark mode
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Image iterators provide a means to draw incremental images;
 | 
					 * Running a full render is labor-intensive, so we model it
 | 
				
			||||||
 * iterators can easily checkpoint state, and this component will
 | 
					 * as an iterator that yields an image of the current system.
 | 
				
			||||||
 * request the next image on the next animation frame. As a result,
 | 
					 * Internally, that iterator is re-queued on each new image;
 | 
				
			||||||
 * the browser should be responsive even though we run CPU-heavy
 | 
					 * so long as that image is returned quickly, we keep
 | 
				
			||||||
 * code on the main thread.
 | 
					 * the main loop running even with CPU-heavy code.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Swapping a new iterator allows interrupting a render in progress,
 | 
					 * To interrupt drawing, children set the active iterator
 | 
				
			||||||
 * as the canvas completely repaints on each provided image.
 | 
					 * through the context provider. This component doesn't care
 | 
				
			||||||
 | 
					 * about which iterator is in progress, it exists only
 | 
				
			||||||
 | 
					 * to fetch the next image and paint it to our canvas.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Finally, check whether dark mode is active, and invert the most
 | 
					 * Finally, we make a distinction between "render" and "paint" buffers.
 | 
				
			||||||
 * recent image prior to painting if so.
 | 
					 * The render image is provided by the iterator, and then:
 | 
				
			||||||
 | 
					 *  - If light mode is active, draw it to the canvas as-is
 | 
				
			||||||
 | 
					 *  - If dark mode is active, copy the "render" buffer to the "paint" buffer,
 | 
				
			||||||
 | 
					 *    invert colors, and then draw the image
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * PainterContext is used to allow child elements to swap in
 | 
					 * TODO(bspeice): Can we make this "re-queueing iterator" pattern generic?
 | 
				
			||||||
 * new iterators.
 | 
					 * It would be nice to have iterators returning arbitrary objects,
 | 
				
			||||||
 | 
					 * but we rely on contexts to manage the iterator, and there's
 | 
				
			||||||
 | 
					 * no good way to make those generic.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param width Canvas draw width
 | 
					 * @param width Canvas draw width
 | 
				
			||||||
 * @param height Canvas draw height
 | 
					 * @param height Canvas draw height
 | 
				
			||||||
 | 
					 * @param hidden Hide the canvas
 | 
				
			||||||
 * @param children Child elements
 | 
					 * @param children Child elements
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export default function Canvas({width, height, children}: CanvasProps) {
 | 
					export default function Canvas({width, height, hidden, children}: CanvasProps) {
 | 
				
			||||||
    const [canvasCtx, setCanvasCtx] = useState<CanvasRenderingContext2D>(null);
 | 
					    const [canvasCtx, setCanvasCtx] = useState<CanvasRenderingContext2D>(null);
 | 
				
			||||||
    const canvasRef = useCallback(node => {
 | 
					    const canvasRef = useCallback(node => {
 | 
				
			||||||
        if (node !== null) {
 | 
					        if (node !== null) {
 | 
				
			||||||
@ -134,6 +143,7 @@ export default function Canvas({width, height, children}: CanvasProps) {
 | 
				
			|||||||
                    ref={canvasRef}
 | 
					                    ref={canvasRef}
 | 
				
			||||||
                    width={width}
 | 
					                    width={width}
 | 
				
			||||||
                    height={height}
 | 
					                    height={height}
 | 
				
			||||||
 | 
					                    hidden={hidden ?? false}
 | 
				
			||||||
                    style={{
 | 
					                    style={{
 | 
				
			||||||
                        aspectRatio: width / height,
 | 
					                        aspectRatio: width / height,
 | 
				
			||||||
                        width: '80%'
 | 
					                        width: '80%'
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user