mirror of
https://github.com/bspeice/speice.io
synced 2025-09-08 15:45:01 -04:00
Mass formatting, fix mobile display, fix issues with image wrap-around
This commit is contained in:
@ -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}
|
||||
</>
|
||||
);
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
|
||||
);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
|
||||
];
|
||||
}
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
Reference in New Issue
Block a user