Mass formatting, fix mobile display, fix issues with image wrap-around

This commit is contained in:
2024-12-15 21:19:09 -05:00
parent 1ce6137c17
commit 456c3a66e5
41 changed files with 1026 additions and 817 deletions

View File

@ -1,174 +1,181 @@
import React, {useContext, useEffect, useMemo, useRef, useState} from "react";
import React, { useContext, useEffect, useMemo, useRef, useState } from "react";
import * as params from "../src/params";
import {PainterContext} from "../src/Canvas";
import {colorFromPalette} from "./colorFromPalette";
import {chaosGameColor, Props as ChaosGameColorProps, TransformColor} from "./chaosGameColor";
import { PainterContext } from "../src/Canvas";
import { colorFromPalette } from "./colorFromPalette";
import { chaosGameColor, Props as ChaosGameColorProps, TransformColor } from "./chaosGameColor";
import styles from "../src/css/styles.module.css";
import {histIndex} from "../src/camera";
import {useColorMode} from "@docusaurus/theme-common";
import { histIndex } from "../src/camera";
import { useColorMode } from "@docusaurus/theme-common";
type PaletteBarProps = {
height: number;
palette: number[];
children?: React.ReactNode;
height: number;
palette: number[];
children?: React.ReactNode;
}
export const PaletteBar: React.FC<PaletteBarProps> = ({height, palette, children}) => {
const sizingRef = useRef<HTMLDivElement>(null);
const [width, setWidth] = useState(0);
useEffect(() => {
if (sizingRef) {
setWidth(sizingRef.current.offsetWidth);
}
}, [sizingRef]);
export const PaletteBar: React.FC<PaletteBarProps> = ({ height, palette, children }) => {
const sizingRef = useRef<HTMLDivElement>(null);
const [width, setWidth] = useState(0);
useEffect(() => {
if (sizingRef) {
setWidth(sizingRef.current.offsetWidth);
}
}, [sizingRef]);
const canvasRef = useRef<HTMLCanvasElement>(null);
const paletteImage = useMemo(() => {
if (width === 0) {
return;
}
const canvasRef = useRef<HTMLCanvasElement>(null);
const paletteImage = useMemo(() => {
if (width === 0) {
return;
}
const image = new ImageData(width, height);
for (let x = 0; x < width; x++) {
const colorIndex = x / width;
const [r, g, b] = colorFromPalette(palette, colorIndex);
const image = new ImageData(width, height);
for (let x = 0; x < width; x++) {
const colorIndex = x / width;
const [r, g, b] = colorFromPalette(palette, colorIndex);
for (let y = 0; y < height; y++) {
const pixelIndex = histIndex(x, y, width, 4);
image.data[pixelIndex] = r * 0xff;
image.data[pixelIndex + 1] = g * 0xff;
image.data[pixelIndex + 2] = b * 0xff;
image.data[pixelIndex + 3] = 0xff;
}
}
for (let y = 0; y < height; y++) {
const pixelIndex = histIndex(x, y, width, 4);
image.data[pixelIndex] = r * 0xff;
image.data[pixelIndex + 1] = g * 0xff;
image.data[pixelIndex + 2] = b * 0xff;
image.data[pixelIndex + 3] = 0xff;
}
}
return image;
}, [width, height, palette]);
return image;
}, [width, height, palette]);
useEffect(() => {
if (canvasRef && paletteImage) {
canvasRef.current.getContext("2d").putImageData(paletteImage, 0, 0);
}
}, [canvasRef, paletteImage]);
useEffect(() => {
if (canvasRef && paletteImage) {
canvasRef.current.getContext("2d").putImageData(paletteImage, 0, 0);
}
}, [canvasRef, paletteImage]);
const canvasStyle = {filter: useColorMode().colorMode === 'dark' ? 'invert(1)' : ''};
const canvasStyle = { filter: useColorMode().colorMode === "dark" ? "invert(1)" : "" };
return (
<>
<div ref={sizingRef} style={{width: '100%', height}}>
{width > 0 ? <canvas ref={canvasRef} width={width} height={height} style={canvasStyle}/> : null}
</div>
{children}
</>
)
}
return (
<>
<div ref={sizingRef} style={{ width: "100%", height }}>
{width > 0 ? <canvas ref={canvasRef} width={width} height={height} style={canvasStyle} /> : null}
</div>
{children}
</>
);
};
type ColorEditorProps = {
title: string;
palette: number[];
transformColor: TransformColor;
setTransformColor: (transformColor: TransformColor) => void;
resetTransformColor: () => void;
children?: React.ReactNode;
title: string;
palette: number[];
transformColor: TransformColor;
setTransformColor: (transformColor: TransformColor) => void;
resetTransformColor: () => void;
children?: React.ReactNode;
}
const ColorEditor: React.FC<ColorEditorProps> = ({title, palette, transformColor, setTransformColor, resetTransformColor, children}) => {
const resetButton = <button className={styles.inputReset} onClick={resetTransformColor}>Reset</button>
const ColorEditor: React.FC<ColorEditorProps> = (
{
title,
palette,
transformColor,
setTransformColor,
resetTransformColor,
children
}) => {
const resetButton = <button className={styles.inputReset} onClick={resetTransformColor}>Reset</button>;
const [r, g, b] = colorFromPalette(palette, transformColor.color);
const colorCss = `rgb(${Math.floor(r * 0xff)},${Math.floor(g * 0xff)},${Math.floor(b * 0xff)})`;
const [r, g, b] = colorFromPalette(palette, transformColor.color);
const colorCss = `rgb(${Math.floor(r * 0xff)},${Math.floor(g * 0xff)},${Math.floor(b * 0xff)})`;
const {colorMode} = useColorMode();
const { colorMode } = useColorMode();
return (
<>
<div className={styles.inputGroup} style={{display: 'grid', gridTemplateColumns: '2fr 2fr 1fr'}}>
<p className={styles.inputTitle} style={{gridColumn: '1/-1'}}>{title} {resetButton}</p>
<div className={styles.inputElement}>
<p>Color: {transformColor.color}</p>
<input type={'range'} min={0} max={1} step={.001} value={transformColor.color}
onInput={e => setTransformColor({...transformColor, color: Number(e.currentTarget.value)})}/>
</div>
<div className={styles.inputElement}>
<p>Speed: {transformColor.colorSpeed}</p>
<input type={'range'} min={0} max={1} step={.001} value={transformColor.colorSpeed}
onInput={e => setTransformColor({...transformColor, colorSpeed: Number(e.currentTarget.value)})}/>
</div>
<div className={styles.inputElement} style={{
width: '100%',
height: '100%',
backgroundColor: colorCss,
filter: colorMode === 'dark' ? 'invert(1)' : ''
}}/>
</div>
{children}
</>
)
}
return (
<>
<div className={styles.inputGroup} style={{ display: "grid", gridTemplateColumns: "2fr 2fr 1fr" }}>
<p className={styles.inputTitle} style={{ gridColumn: "1/-1" }}>{title} {resetButton}</p>
<div className={styles.inputElement}>
<p>Color: {transformColor.color}</p>
<input type={"range"} min={0} max={1} step={.001} value={transformColor.color}
onInput={e => setTransformColor({ ...transformColor, color: Number(e.currentTarget.value) })} />
</div>
<div className={styles.inputElement}>
<p>Speed: {transformColor.colorSpeed}</p>
<input type={"range"} min={0} max={1} step={.001} value={transformColor.colorSpeed}
onInput={e => setTransformColor({ ...transformColor, colorSpeed: Number(e.currentTarget.value) })} />
</div>
<div className={styles.inputElement} style={{
width: "100%",
height: "100%",
backgroundColor: colorCss,
filter: colorMode === "dark" ? "invert(1)" : ""
}} />
</div>
{children}
</>
);
};
type Props = {
quality?: number;
children?: React.ReactElement;
children?: React.ReactElement;
}
export default function FlameColor({quality, children}: Props) {
const {width, height, setPainter} = useContext(PainterContext);
export default function FlameColor({ children }: Props) {
const { width, height, setPainter } = useContext(PainterContext);
const xform1ColorDefault: TransformColor = {color: params.xform1Color, colorSpeed: 0.5};
const [xform1Color, setXform1Color] = useState(xform1ColorDefault);
const resetXform1Color = () => setXform1Color(xform1ColorDefault);
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 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 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);
const xformFinalColorDefault: TransformColor = { color: params.xformFinalColor, colorSpeed: 0 };
const [xformFinalColor, setXformFinalColor] = useState(xformFinalColorDefault);
const resetXformFinalColor = () => setXformFinalColor(xformFinalColorDefault);
useEffect(() => {
const gameParams: ChaosGameColorProps = {
width,
height,
transforms: params.xforms,
final: params.xformFinal,
palette: params.palette,
colors: [xform1Color, xform2Color, xform3Color],
finalColor: xformFinalColor
}
setPainter(chaosGameColor(gameParams));
}, [xform1Color, xform2Color, xform3Color, xformFinalColor]);
useEffect(() => {
const gameParams: ChaosGameColorProps = {
width,
height,
transforms: params.xforms,
final: params.xformFinal,
palette: params.palette,
colors: [xform1Color, xform2Color, xform3Color],
finalColor: xformFinalColor
};
setPainter(chaosGameColor(gameParams));
}, [xform1Color, xform2Color, xform3Color, xformFinalColor]);
return (
<>
<PaletteBar height={40} palette={params.palette}/>
<ColorEditor
title={"Transform 1"}
palette={params.palette}
transformColor={xform1Color}
setTransformColor={setXform1Color}
resetTransformColor={resetXform1Color}/>
<ColorEditor
title={"Transform 2"}
palette={params.palette}
transformColor={xform2Color}
setTransformColor={setXform2Color}
resetTransformColor={resetXform2Color}/>
<ColorEditor
title={"Transform 3"}
palette={params.palette}
transformColor={xform3Color}
setTransformColor={setXform3Color}
resetTransformColor={resetXform3Color}/>
<ColorEditor
title={"Transform Final"}
palette={params.palette}
transformColor={xformFinalColor}
setTransformColor={setXformFinalColor}
resetTransformColor={resetXformFinalColor}/>
{children}
</>
);
return (
<>
<PaletteBar height={40} palette={params.palette} />
<ColorEditor
title={"Transform 1"}
palette={params.palette}
transformColor={xform1Color}
setTransformColor={setXform1Color}
resetTransformColor={resetXform1Color} />
<ColorEditor
title={"Transform 2"}
palette={params.palette}
transformColor={xform2Color}
setTransformColor={setXform2Color}
resetTransformColor={resetXform2Color} />
<ColorEditor
title={"Transform 3"}
palette={params.palette}
transformColor={xform3Color}
setTransformColor={setXform3Color}
resetTransformColor={resetXform3Color} />
<ColorEditor
title={"Transform Final"}
palette={params.palette}
transformColor={xformFinalColor}
setTransformColor={setXformFinalColor}
resetTransformColor={resetXformFinalColor} />
{children}
</>
);
}

View File

@ -1,25 +1,25 @@
import React, {useContext, useEffect} from "react";
import {xforms as transforms, xformFinal as final} from "../src/params";
import {PainterContext} from "../src/Canvas";
import {chaosGameHistogram} from "./chaosGameHistogram";
import React, { useContext, useEffect } from "react";
import { xformFinal as final, xforms as transforms } from "../src/params";
import { PainterContext } from "../src/Canvas";
import { chaosGameHistogram } from "./chaosGameHistogram";
type Props = {
paint: (width: number, height: number, histogram: number[]) => ImageData;
children?: React.ReactElement;
paint: (width: number, height: number, histogram: number[]) => ImageData;
children?: React.ReactElement;
}
export default function FlameHistogram({paint, children}: Props) {
const {width, height, setPainter} = useContext(PainterContext);
export default function FlameHistogram({ paint, children }: Props) {
const { width, height, setPainter } = useContext(PainterContext);
useEffect(() => {
const gameParams = {
width,
height,
transforms,
final,
paint
}
setPainter(chaosGameHistogram(gameParams));
}, [width, height]);
useEffect(() => {
const gameParams = {
width,
height,
transforms,
final,
paint
};
setPainter(chaosGameHistogram(gameParams));
}, [width, height]);
return children;
return children;
}

View File

@ -1,64 +1,135 @@
// hidden-start
import {Props as ChaosGameFinalProps} from "../2-transforms/chaosGameFinal";
import {randomBiUnit} from "../src/randomBiUnit";
import {randomChoice} from "../src/randomChoice";
import {camera, histIndex} from "../src/camera";
import {colorFromPalette} from "./colorFromPalette";
import {mixColor} from "./mixColor";
import {paintColor} from "./paintColor";
import { Props as ChaosGameFinalProps } from "../2-transforms/chaosGameFinal";
import { randomBiUnit } from "../src/randomBiUnit";
import { randomChoice } from "../src/randomChoice";
import { camera, histIndex } from "../src/camera";
import { colorFromPalette } from "./colorFromPalette";
import { mixColor } from "./mixColor";
import { paintColor } from "./paintColor";
const quality = 15;
const step = 100_000;
// hidden-end
export type TransformColor = {
color: number;
colorSpeed: number;
color: number;
colorSpeed: number;
}
export type Props = ChaosGameFinalProps & {
palette: number[];
colors: TransformColor[];
finalColor: TransformColor;
palette: number[];
colors: TransformColor[];
finalColor: TransformColor;
}
export function* chaosGameColor({width, height, transforms, final, palette, colors, finalColor}: Props) {
const imgRed = Array<number>(width * height).fill(0);
const imgGreen = Array<number>(width * height).fill(0);
const imgBlue = Array<number>(width * height).fill(0);
const imgAlpha = Array<number>(width * height).fill(0);
let [x, y] = [randomBiUnit(), randomBiUnit()];
let c = Math.random();
export function* chaosGameColor(
{
width,
height,
transforms,
final,
palette,
colors,
finalColor
}: Props
) {
const pixels = width * height;
const iterations = width * height * quality;
for (let i = 0; i < iterations; i++) {
const [transformIndex, transform] = randomChoice(transforms);
[x, y] = transform(x, y);
// highlight-start
const imgRed = Array<number>(pixels)
.fill(0);
const imgGreen = Array<number>(pixels)
.fill(0);
const imgBlue = Array<number>(pixels)
.fill(0);
const imgAlpha = Array<number>(pixels)
.fill(0);
// highlight-start
const transformColor = colors[transformIndex];
c = mixColor(c, transformColor.color, transformColor.colorSpeed);
// highlight-end
const plotColor = (
x: number,
y: number,
c: number
) => {
const [pixelX, pixelY] =
camera(x, y, width);
const [finalX, finalY] = final(x, y);
if (
pixelX < 0 ||
pixelX >= width ||
pixelY < 0 ||
pixelY >= width
)
return;
if (i > 20) {
const [pixelX, pixelY] = camera(finalX, finalY, width);
const pixelIndex = histIndex(pixelX, pixelY, width, 1);
const hIndex =
histIndex(pixelX, pixelY, width, 1);
if (pixelIndex < 0 || pixelIndex >= imgAlpha.length)
continue;
const [r, g, b] =
colorFromPalette(palette, c);
const colorFinal = mixColor(c, finalColor.color, finalColor.colorSpeed);
const [r, g, b] = colorFromPalette(palette, colorFinal);
imgRed[pixelIndex] += r;
imgGreen[pixelIndex] += g;
imgBlue[pixelIndex] += b;
imgAlpha[pixelIndex] += 1;
}
imgRed[hIndex] += r;
imgGreen[hIndex] += g;
imgBlue[hIndex] += b;
imgAlpha[hIndex] += 1;
}
// highlight-end
if (i % step === 0)
yield paintColor(width, height, imgRed, imgGreen, imgBlue, imgAlpha);
}
let [x, y] = [
randomBiUnit(),
randomBiUnit()
];
let c = Math.random();
yield paintColor(width, height, imgRed, imgGreen, imgBlue, imgAlpha);
const iterations = quality * pixels;
for (let i = 0; i < iterations; i++) {
const [transformIndex, transform] =
randomChoice(transforms);
[x, y] = transform(x, y);
// highlight-start
const transformColor =
colors[transformIndex];
c = mixColor(
c,
transformColor.color,
transformColor.colorSpeed
);
// highlight-end
const [finalX, finalY] = final(x, y);
// highlight-start
const finalC = mixColor(
c,
finalColor.color,
finalColor.colorSpeed
);
// highlight-end
if (i > 20)
plotColor(
finalX,
finalY,
finalC
)
if (i % step === 0)
yield paintColor(
width,
height,
imgRed,
imgGreen,
imgBlue,
imgAlpha
);
}
yield paintColor(
width,
height,
imgRed,
imgGreen,
imgBlue,
imgAlpha
);
}

View File

@ -1,45 +1,78 @@
// hidden-start
import {randomBiUnit} from "../src/randomBiUnit";
import {randomChoice} from "../src/randomChoice";
import {Props as ChaosGameFinalProps} from "../2-transforms/chaosGameFinal";
import {camera, histIndex} from "../src/camera";
import { randomBiUnit } from "../src/randomBiUnit";
import { randomChoice } from "../src/randomChoice";
import { Props as ChaosGameFinalProps } from "../2-transforms/chaosGameFinal";
import { camera, histIndex } from "../src/camera";
const quality = 10;
const step = 100_000;
// hidden-end
export type Props = ChaosGameFinalProps & {
paint: (width: number, height: number, histogram: number[]) => ImageData;
type Props = ChaosGameFinalProps & {
paint: (
width: number,
height: number,
histogram: number[]
) => ImageData;
}
export function* chaosGameHistogram({width, height, transforms, final, paint}: Props) {
let iterations = quality * width * height;
// highlight-start
const histogram = Array<number>(width * height).fill(0);
// highlight-end
export function* chaosGameHistogram(
{
width,
height,
transforms,
final,
paint
}: Props
) {
const pixels = width * height;
const iterations = quality * pixels;
let [x, y] = [randomBiUnit(), randomBiUnit()];
// highlight-start
const hist = Array<number>(pixels)
.fill(0);
for (let i = 0; i < iterations; i++) {
const [_, transform] = randomChoice(transforms);
[x, y] = transform(x, y);
const [finalX, finalY] = final(x, y);
const plotHist = (
x: number,
y: number
) => {
const [pixelX, pixelY] =
camera(x, y, width);
if (i > 20) {
// highlight-start
const [pixelX, pixelY] = camera(finalX, finalY, width);
const hIndex = histIndex(pixelX, pixelY, width, 1);
if (
pixelX < 0 ||
pixelX >= width ||
pixelY < 0 ||
pixelY >= height
)
return;
if (hIndex < 0 || hIndex >= histogram.length) {
continue;
}
const hIndex =
histIndex(pixelX, pixelY, width, 1);
histogram[hIndex] += 1;
// highlight-end
}
hist[hIndex] += 1;
};
// highlight-end
if (i % step === 0)
yield paint(width, height, histogram);
let [x, y] = [
randomBiUnit(),
randomBiUnit()
];
for (let i = 0; i < iterations; i++) {
const [_, transform] =
randomChoice(transforms);
[x, y] = transform(x, y);
const [finalX, finalY] = final(x, y);
if (i > 20) {
// highlight-start
plotHist(finalX, finalY);
// highlight-end
}
yield paint(width, height, histogram);
if (i % step === 0)
yield paint(width, height, hist);
}
yield paint(width, height, hist);
}

View File

@ -1,4 +1,14 @@
export function colorFromPalette(palette: number[], colorIndex: number): [number, number, number] {
const paletteIndex = Math.floor(colorIndex * (palette.length / 3)) * 3;
return [palette[paletteIndex], palette[paletteIndex + 1], palette[paletteIndex + 2]];
export function colorFromPalette(
palette: number[],
colorIndex: number
): [number, number, number] {
const numColors = palette.length / 3;
const paletteIndex = Math.floor(
colorIndex * (numColors)
) * 3;
return [
palette[paletteIndex], // red
palette[paletteIndex + 1], // green
palette[paletteIndex + 2] // blue
];
}

View File

@ -165,7 +165,7 @@ import colorFromPaletteSource from "!!raw-loader!./colorFromPalette";
<details>
<summary>As an alternative...</summary>
...you could also interpolate between colors in the palette.
...you could interpolate between colors in the palette.
For example, `flam3` uses [linear interpolation](https://github.com/scottdraves/flam3/blob/7fb50c82e90e051f00efcc3123d0e06de26594b2/rect.c#L483-L486)
</details>

View File

@ -1,3 +1,8 @@
export function mixColor(color1: number, color2: number, colorSpeed: number) {
return color1 * (1 - colorSpeed) + color2 * colorSpeed;
export function mixColor(
color1: number,
color2: number,
colorSpeed: number
) {
return color1 * (1 - colorSpeed) +
color2 * colorSpeed;
}

View File

@ -1,25 +1,34 @@
// hidden-start
import {colorFromPalette} from "./colorFromPalette";
// hidden-end
export function paintColor(
width: number,
height: number,
red: number[],
green: number[],
blue: number[],
alpha: number[]
width: number,
height: number,
red: number[],
green: number[],
blue: number[],
alpha: number[]
): ImageData {
const image = new ImageData(width, height);
const pixels = width * height;
const img =
new ImageData(width, height);
for (let i = 0; i < width * height; i++) {
const alphaScale = Math.log10(alpha[i]) / (alpha[i] * 1.5);
for (let i = 0; i < pixels; i++) {
const scale =
Math.log10(alpha[i]) /
(alpha[i] * 1.5);
const pixelIndex = i * 4;
image.data[pixelIndex] = red[i] * alphaScale * 0xff;
image.data[pixelIndex + 1] = green[i] * alphaScale * 0xff;
image.data[pixelIndex + 2] = blue[i] * alphaScale * 0xff;
image.data[pixelIndex + 3] = alpha[i] * alphaScale * 0xff;
}
const pixelIndex = i * 4;
return image;
const rVal = red[i] * scale * 0xff;
img.data[pixelIndex] = rVal;
const gVal = green[i] * scale * 0xff;
img.data[pixelIndex + 1] = gVal;
const bVal = blue[i] * scale * 0xff;
img.data[pixelIndex + 2] = bVal;
const aVal = alpha[i] * scale * 0xff;
img.data[pixelIndex + 3] = aVal;
}
return img;
}

View File

@ -1,18 +1,26 @@
export function paintLinear(width: number, height: number, histogram: number[]): ImageData {
const image = new ImageData(width, height);
export function paintLinear(
width: number,
height: number,
hist: number[]
) {
const img =
new ImageData(width, height);
let valueMax = 0;
for (let value of histogram) {
valueMax = Math.max(valueMax, value);
}
let hMax = 0;
for (let value of hist) {
hMax = Math.max(hMax, value);
}
for (let i = 0; i < histogram.length; i++) {
const pixelIndex = i * 4;
image.data[pixelIndex] = 0;
image.data[pixelIndex + 1] = 0;
image.data[pixelIndex + 2] = 0;
image.data[pixelIndex + 3] = histogram[i] / valueMax * 0xff;
}
for (let i = 0; i < hist.length; i++) {
const pixelIndex = i * 4;
return image;
img.data[pixelIndex] = 0;
img.data[pixelIndex + 1] = 0;
img.data[pixelIndex + 2] = 0;
const alpha = hist[i] / hMax * 0xff;
img.data[pixelIndex + 3] = alpha;
}
return img;
}

View File

@ -1,21 +1,29 @@
export function paintLogarithmic(width: number, height: number, histogram: number[]): ImageData {
const image = new ImageData(width, height);
export function paintLogarithmic(
width: number,
height: number,
hist: number[]
) {
const img =
new ImageData(width, height);
const histogramLog: number[] = [];
histogram.forEach(value => histogramLog.push(Math.log(value)));
const histLog = hist.map(Math.log);
let histogramLogMax = -Infinity;
for (let value of histogramLog) {
histogramLogMax = Math.max(histogramLogMax, value);
}
let hLogMax = -Infinity;
for (let value of histLog) {
hLogMax = Math.max(hLogMax, 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;
}
for (let i = 0; i < hist.length; i++) {
const pixelIndex = i * 4;
return image;
img.data[pixelIndex] = 0; // red
img.data[pixelIndex + 1] = 0; // green
img.data[pixelIndex + 2] = 0; // blue
const alpha =
histLog[i] / hLogMax * 0xff;
img.data[pixelIndex + 3] = alpha;
}
return img;
}