mirror of
https://github.com/bspeice/speice.io
synced 2025-07-01 05:46:13 -04:00
Log density visualization
This commit is contained in:
@ -1,30 +1,27 @@
|
||||
import {VictoryChart, VictoryLine, VictoryScatter, VictoryTheme} from "victory";
|
||||
import {useContext, useEffect, useState} from "react";
|
||||
import React, {useContext, useEffect} from "react";
|
||||
import * as params from "../src/params";
|
||||
import {PainterContext} from "../src/Canvas";
|
||||
import {chaosGameHistogram} from "./chaosGameHistogram";
|
||||
import {PlotData, plotHistogram} from "./plotHistogram";
|
||||
import {chaosGameHistogram} from "@site/blog/2024-11-15-playing-with-fire/3-log-density/chaosGameHistogram";
|
||||
|
||||
function* plotChaosGame(width: number, height: number, setPdf: (data: PlotData) => void, setCdf: (data: PlotData) => void) {
|
||||
const emptyImage = new ImageData(width, height);
|
||||
for (let histogram of chaosGameHistogram(width, height)) {
|
||||
const plotData = plotHistogram(histogram);
|
||||
setPdf(plotData);
|
||||
yield emptyImage;
|
||||
}
|
||||
type Props = {
|
||||
quality?: number;
|
||||
paintFn: (width: number, histogram: Uint32Array) => ImageData;
|
||||
children?: React.ReactElement;
|
||||
}
|
||||
|
||||
export default function FlameHistogram() {
|
||||
export default function FlameHistogram({quality, paintFn, children}: Props) {
|
||||
const {width, height, setPainter} = useContext(PainterContext);
|
||||
const [pdfData, setPdfData] = useState<{ x: number, y: number }[]>(null);
|
||||
|
||||
useEffect(() => setPainter(plotChaosGame(width, height, setPdfData, null)), []);
|
||||
useEffect(() => {
|
||||
const gameParams = {
|
||||
width,
|
||||
height,
|
||||
transforms: params.xforms,
|
||||
final: params.xformFinal,
|
||||
quality,
|
||||
painter: paintFn
|
||||
}
|
||||
setPainter(chaosGameHistogram(gameParams));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<VictoryChart theme={VictoryTheme.clean}>
|
||||
<VictoryLine
|
||||
data={pdfData}
|
||||
interpolation='natural'
|
||||
/>
|
||||
</VictoryChart>
|
||||
)
|
||||
return children;
|
||||
}
|
@ -1,34 +1,34 @@
|
||||
import {plot} from "./plotHistogram";
|
||||
// hidden-start
|
||||
import {randomBiUnit} from "../src/randomBiUnit";
|
||||
import {randomChoice} from "../src/randomChoice";
|
||||
import {buildTransform} from "../2-transforms/buildTransform";
|
||||
import {transformPost} from "../2-transforms/post";
|
||||
import {transforms} from "../2-transforms/FlameFinal";
|
||||
import * as params from "../src/params";
|
||||
import {ChaosGameFinalProps} from "../2-transforms/chaosGameFinal";
|
||||
import {camera, histIndex} from "../src/camera";
|
||||
// hidden-end
|
||||
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;
|
||||
step = step ?? 100_000;
|
||||
|
||||
const finalTransform = buildTransform(params.xformFinalCoefs, params.xformFinalVariations);
|
||||
const finalTransformPost = transformPost(finalTransform, params.xformFinalCoefsPost);
|
||||
|
||||
const step = 1000;
|
||||
const quality = 1;
|
||||
|
||||
export function* chaosGameHistogram(width: number, height: number) {
|
||||
let iterations = quality * width * height;
|
||||
let histogram = new Uint32Array(width * height);
|
||||
const histogram = new Uint32Array(width * height);
|
||||
|
||||
let [x, y] = [randomBiUnit(), randomBiUnit()];
|
||||
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
const [_, transform] = randomChoice(transforms);
|
||||
[x, y] = transform(x, y);
|
||||
[x, y] = finalTransformPost(x, y);
|
||||
[x, y] = final(x, y);
|
||||
|
||||
if (i > 20)
|
||||
plot(x, y, width, histogram);
|
||||
if (i > 20) {
|
||||
const [pixelX, pixelY] = camera(x, y, width);
|
||||
const pixelIndex = histIndex(pixelX, pixelY, width, 1);
|
||||
histogram[pixelIndex] += 1;
|
||||
}
|
||||
|
||||
if (i % step === 0)
|
||||
yield histogram;
|
||||
yield painter(width, histogram);
|
||||
}
|
||||
|
||||
yield histogram;
|
||||
yield painter(width, histogram);
|
||||
}
|
@ -19,17 +19,23 @@ Can we do something more intelligent with that information?
|
||||
## 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.
|
||||
We'll render the reference image again, but this time, set each pixel's transparency
|
||||
based on how many times we encounter it in the chaos game:
|
||||
|
||||
import CodeBlock from "@theme/CodeBlock";
|
||||
import plotHistogramSource from "!!raw-loader!./plotHistogram";
|
||||
|
||||
<CodeBlock language="typescript">{plotHistogramSource}</CodeBlock>
|
||||
import paintLinearSource from "!!raw-loader!./paintLinear"
|
||||
|
||||
<CodeBlock language="typescript">{paintLinearSource}</CodeBlock>
|
||||
|
||||
import Canvas from "../src/Canvas";
|
||||
import FlameHistogram from "./FlameHistogram";
|
||||
import {paintLinear} from "./paintLinear";
|
||||
|
||||
<Canvas width={400} height={400} hidden={true}>
|
||||
<FlameHistogram/>
|
||||
</Canvas>
|
||||
<Canvas><FlameHistogram quality={5} paintFn={paintLinear}/></Canvas>
|
||||
|
||||
## Log display
|
||||
|
||||
import {paintLogarithmic} from './paintLogarithmic'
|
||||
|
||||
<Canvas><FlameHistogram quality={10} paintFn={paintLogarithmic}/></Canvas>
|
@ -0,0 +1,18 @@
|
||||
export function paintLinear(width: number, histogram: Uint32Array): ImageData {
|
||||
const image = new ImageData(width, histogram.length / width);
|
||||
|
||||
let countMax = 0;
|
||||
for (let value of histogram) {
|
||||
countMax = Math.max(countMax, value);
|
||||
}
|
||||
|
||||
for (let i = 0; i < histogram.length; i++) {
|
||||
const pixelIndex = i * 4;
|
||||
image.data[pixelIndex] = 0; // red
|
||||
image.data[pixelIndex + 1] = 0; // green
|
||||
image.data[pixelIndex + 2] = 0; // blue
|
||||
image.data[pixelIndex + 3] = Number(histogram[i]) / countMax * 0xff;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
export function paintLogarithmic(width: number, histogram: Uint32Array): ImageData {
|
||||
const image = new ImageData(width, histogram.length / width);
|
||||
|
||||
const histogramLog = new Array<number>();
|
||||
histogram.forEach(value => histogramLog.push(Math.log(value)));
|
||||
|
||||
let histogramLogMax = -Infinity;
|
||||
for (let value of histogramLog) {
|
||||
histogramLogMax = Math.max(histogramLogMax, value);
|
||||
}
|
||||
|
||||
for (let i = 0; i < histogram.length; i++) {
|
||||
const pixelIndex = i * 4;
|
||||
image.data[pixelIndex] = 0; // red
|
||||
image.data[pixelIndex + 1] = 0; // green
|
||||
image.data[pixelIndex + 2] = 0; // blue
|
||||
image.data[pixelIndex + 3] = histogramLog[i] / histogramLogMax * 0xff;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
// hidden-start
|
||||
import {camera, histIndex} from "../src/camera";
|
||||
// hidden-end
|
||||
export function plot(x: number, y: number, width: number, hitCount: Uint32Array) {
|
||||
const [pixelX, pixelY] = camera(x, y, width);
|
||||
const pixelIndex = histIndex(pixelX, pixelY, width, 1);
|
||||
hitCount[pixelIndex] += 1;
|
||||
}
|
||||
|
||||
export type PlotData = {x: number, y: number}[];
|
||||
export function plotHistogram(hitCount: Uint32Array) {
|
||||
const data = new Map<number, number>();
|
||||
hitCount.forEach(value => {
|
||||
const bucket = 32 - Math.clz32(value);
|
||||
const currentCount = data.get(bucket) ?? 0;
|
||||
data.set(bucket, currentCount + 1);
|
||||
})
|
||||
|
||||
const output: PlotData = [];
|
||||
data.forEach((value, bucket) =>
|
||||
output.push({x: Math.pow(2, bucket), y: value}));
|
||||
return output;
|
||||
}
|
Reference in New Issue
Block a user