mirror of
https://github.com/bspeice/speice.io
synced 2024-12-22 08:38:09 -05:00
More writing for the main posts
This commit is contained in:
parent
b608a25146
commit
0983558659
@ -3,7 +3,8 @@ import { randomBiUnit } from "../src/randomBiUnit";
|
||||
import { randomChoice } from "../src/randomChoice";
|
||||
import { plot } from "./plot"
|
||||
import {Transform} from "../src/transform";
|
||||
const iterations = 50_000;
|
||||
|
||||
const quality = 0.5;
|
||||
const step = 1000;
|
||||
// hidden-end
|
||||
export type Props = {
|
||||
@ -20,8 +21,7 @@ export function* chaosGameWeighted(
|
||||
randomBiUnit()
|
||||
];
|
||||
|
||||
// TODO: Explain quality
|
||||
const iterations = width * height * 0.5;
|
||||
const iterations = width * height * quality;
|
||||
for (let c = 0; c < iterations; c++) {
|
||||
// highlight-start
|
||||
const [_, xform] = randomChoice(transforms);
|
||||
|
@ -24,7 +24,7 @@ import banner from '../banner.png'
|
||||
I don't remember exactly when I first learned about fractal flames, but I do remember becoming entranced by the images they created.
|
||||
I also remember their unique appeal to my young engineering mind; this was an art form I could participate in.
|
||||
|
||||
The original [Fractal Flame](https://flam3.com/flame_draves.pdf) describing their structure was too much
|
||||
The original [Fractal Flame Algorithm paper](https://flam3.com/flame_draves.pdf) describing their structure was too much
|
||||
for me to handle at the time (I was ~12 years old), so I was content to play around and enjoy the pictures.
|
||||
But the desire to understand it stuck around. Now, with a graduate degree under my belt, maybe I can make some progress.
|
||||
|
||||
@ -35,7 +35,13 @@ can understand without too much prior knowledge.
|
||||
|
||||
## Iterated function systems
|
||||
|
||||
As mentioned above, fractal flames are a type of "[iterated function system](https://en.wikipedia.org/wiki/Iterated_function_system),"
|
||||
:::note
|
||||
|
||||
This post covers section 2 of the Fractal Flame Algorithm paper
|
||||
|
||||
:::
|
||||
|
||||
As mentioned, fractal flames are a type of "[iterated function system](https://en.wikipedia.org/wiki/Iterated_function_system),"
|
||||
or IFS. Their mathematical foundations come from a paper written by [John E. Hutchinson](https://maths-people.anu.edu.au/~john/Assets/Research%20Papers/fractals_self-similarity.pdf),
|
||||
but reading that paper isn't critical for our purposes. Instead, we'll focus on building a practical understanding
|
||||
of how they work. The formula for an IFS is short, but will take some time to unpack:
|
||||
@ -65,7 +71,11 @@ export const simpleData = [
|
||||
</VictoryChart>
|
||||
|
||||
However, this is a pretty boring image. With fractal flames, rather than listing individual points,
|
||||
we use functions to describe which points are part of the solution. This means there are an infinite
|
||||
we use functions to describe which points are part of the solution.
|
||||
|
||||
TODO: Explain characteristics of the solution - fixed set
|
||||
|
||||
This means there are an infinite
|
||||
number of points, but if we find _enough_ points to plot, we'll end up with a nice picture.
|
||||
And if we choose different functions to start with, our solution set changes, and we'll end up
|
||||
with a new picture.
|
||||
@ -73,10 +83,6 @@ with a new picture.
|
||||
However, it's not clear which points belong in the solution just by staring at the functions.
|
||||
We'll need a computer to figure it out.
|
||||
|
||||
TODO: Other topics worth covering in this section? Maybe in a `details` block?:
|
||||
- Fixed sets: https://en.wiktionary.org/wiki/fixed_set
|
||||
- Compact sets
|
||||
|
||||
### Transformation functions
|
||||
|
||||
Second, $F_i(S)$. At their most basic, each $F_i$ is a function that takes in a 2-dimensional point and transforms
|
||||
|
@ -37,8 +37,6 @@ export default function FlameBlend() {
|
||||
const [xform3Variations, setXform3Variations] = useState(xform3VariationsDefault)
|
||||
const resetXform3Variations = () => setXform3Variations(xform3VariationsDefault);
|
||||
|
||||
// Cheating a bit here; for purposes of code re-use, use the post- and final-transform-enabled chaos game,
|
||||
// and swap in identity components for each
|
||||
const identityXform: Transform = (x, y) => [x, y];
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -17,7 +17,7 @@ export default function FlamePost() {
|
||||
const resetXform2CoefsPost = () => setXform2CoefsPost(params.xform2CoefsPost);
|
||||
|
||||
const [xform3CoefsPost, setXform3CoefsPost] = useState<Coefs>(params.xform3CoefsPost);
|
||||
const resetXform3CoefsPost = () => setXform1CoefsPost(params.xform3CoefsPost);
|
||||
const resetXform3CoefsPost = () => setXform3CoefsPost(params.xform3CoefsPost);
|
||||
|
||||
const identityXform: Transform = (x, y) => [x, y];
|
||||
|
||||
|
@ -25,7 +25,9 @@ export function* chaosGameFinal({width, height, transforms, final}: Props) {
|
||||
// highlight-end
|
||||
|
||||
if (i > 20)
|
||||
// highlight-start
|
||||
plot(finalX, finalY, image);
|
||||
// highlight-end
|
||||
|
||||
if (i % step === 0)
|
||||
yield image;
|
||||
|
@ -7,60 +7,55 @@ tags: []
|
||||
---
|
||||
|
||||
Now that we have a basic chaos game in place, it's time to spice things up. Transforms and variations create the
|
||||
interesting patterns that fractal flames are known for.
|
||||
shapes and patterns that fractal flames are known for.
|
||||
|
||||
<!-- truncate -->
|
||||
|
||||
This blog post uses a set of reference parameters ([available here](../params.flame)) to demonstrate a practical
|
||||
implementation of the fractal flame algorithm. If you're interested in tweaking the parameters, or generating
|
||||
your own art, [Apophysis](https://sourceforge.net/projects/apophysis/) is a good introductory tool.
|
||||
:::note
|
||||
|
||||
TODO: Include the reference image here
|
||||
This post uses a set of [reference parameters](../params.flame) to demonstrate a working
|
||||
implementation of the fractal flame algorithm. If you're interested in tweaking the parameters,
|
||||
or generating your own art, [Apophysis](https://sourceforge.net/projects/apophysis/)
|
||||
can load that file and gives full control over the image.
|
||||
|
||||
:::
|
||||
|
||||
## Transforms and variations
|
||||
|
||||
:::note
|
||||
|
||||
This post covers section 3 of the Fractal Flame Algorithm paper
|
||||
|
||||
:::
|
||||
|
||||
import CodeBlock from '@theme/CodeBlock'
|
||||
|
||||
We previously introduced "transforms" as the "functions" of an "iterated function system." Their general format is:
|
||||
We previously introduced transforms as the "functions" of an "iterated function system," and showed how
|
||||
playing the chaos game leads to an image of Sierpinski's Gasket. Even though we used simple functions,
|
||||
the image it generates is exciting. But it's still not nearly as exciting as the images the Fractal Flame
|
||||
algorithm is known for.
|
||||
|
||||
This leads us to the first big innovation of the Fractal Flame algorithm: using non-linear functions
|
||||
for the transforms. These functions are known as "variations":
|
||||
|
||||
$$
|
||||
F_i(x,y) = (a_i \cdot x + b_i \cdot y + c_i, \hspace{0.2cm} d_i \cdot x + e_i \cdot y + f_i)
|
||||
F_i(x, y) = V_j(a_i \cdot x + b_i \cdot y + c_i, d_i \cdot x + e_i \cdot y + f_i)
|
||||
$$
|
||||
|
||||
import coefsSrc from '!!raw-loader!../src/coefs'
|
||||
import variationSource from '!!raw-loader!../src/variation'
|
||||
|
||||
<CodeBlock language={'typescript'}>{coefsSrc}</CodeBlock>
|
||||
<CodeBlock language="typescript">{variationSource}</CodeBlock>
|
||||
|
||||
We also introduced the Sierpinski Gasket functions ($F_0$, $F_1$, and $F_2$), demonstrating how they are related to
|
||||
the general format. For example:
|
||||
Variations, labeled $V_j$ above, are functions just like transforms (we use $j$ to indicate a specific variation).
|
||||
They take an input point $(x,y)$, and give an output point. However, the sky is the limit for what variation functions do in between
|
||||
input to output. The Fractal Flame paper lists 49 different variation functions,
|
||||
and the official `flam3` implementation supports [98 different functions](https://github.com/scottdraves/flam3/blob/7fb50c82e90e051f00efcc3123d0e06de26594b2/variations.c).
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
F_0(x,y) &= \left({x \over 2}, {y \over 2}\right) \\
|
||||
&= (a_0 \cdot x + b_0 \cdot y + c_0, d_0 \cdot x + e_0 \cdot y + f_0) \\
|
||||
& a_0 = 0.5 \hspace{0.2cm} b_0 = 0 \hspace{0.2cm} c_0 = 0 \\
|
||||
& d_0 = 0 \hspace{0.2cm} e_0 = 0.5 \hspace{0.2cm} f_0 = 0
|
||||
\end{align*}
|
||||
$$
|
||||
|
||||
TODO: Explain the applyCoefs function
|
||||
|
||||
However, these transforms are pretty boring. We can build more exciting images by using additional functions
|
||||
within the transform. These "sub-functions" are called "variations":
|
||||
|
||||
$$
|
||||
F_i(x, y) = V_j(a_i \cdot x + b_i \cdot y + c_i, \hspace{0.2cm} d_i \cdot x + e_i \cdot y + f_i)
|
||||
$$
|
||||
|
||||
The fractal flame paper lists 49 variation functions ($V_j$ above), but the sky's the limit here.
|
||||
For example, the official `flam3` implementation supports
|
||||
[98 variations](https://github.com/scottdraves/flam3/blob/7fb50c82e90e051f00efcc3123d0e06de26594b2/variations.c).
|
||||
|
||||
Our reference image will focus on just four variations:
|
||||
To draw our reference image, we'll focus on four variations:
|
||||
|
||||
### Linear (variation 0)
|
||||
|
||||
This variation returns the $x$ and $y$ coordinates as-is:
|
||||
This variation is dead simple: just return the $x$ and $y$ coordinates as-is.
|
||||
|
||||
$$
|
||||
V_0(x,y) = (x,y)
|
||||
@ -70,11 +65,17 @@ import linearSrc from '!!raw-loader!../src/linear'
|
||||
|
||||
<CodeBlock language={'typescript'}>{linearSrc}</CodeBlock>
|
||||
|
||||
:::tip
|
||||
|
||||
In a way, we've already been using this variation! The functions that define Sierpinski's Gasket
|
||||
apply the affine coefficients to the input point, and use that as the output point.
|
||||
|
||||
:::
|
||||
|
||||
### Julia (variation 13)
|
||||
|
||||
This variation still uses just the $x$ and $y$ coordinates, but does crazy things with them:
|
||||
|
||||
<small>TODO: Is this related to the Julia set?</small>
|
||||
This variation is a good example of the non-linear functions the Fractal Flame Algorithm introduces.
|
||||
It still receives an input point $(x, y)$, but does some crazy things with it:
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
@ -97,8 +98,8 @@ import juliaSrc from '!!raw-loader!../src/julia'
|
||||
|
||||
### Popcorn (variation 17)
|
||||
|
||||
This is known as a "dependent variation" because it depends on knowing the transform coefficients
|
||||
(specifically, $c$ and $f$):
|
||||
Some variations rely on knowing the transform's affine coefficients; these are known as "dependent variations."
|
||||
For the popcorn variation, we use the $c$ and $f$ coefficients:
|
||||
|
||||
$$
|
||||
V_{17}(x,y) = (x + c \cdot \text{sin}(\text{tan }3y), y + f \cdot \text{sin}(\text{tan }3x))
|
||||
@ -110,7 +111,8 @@ import popcornSrc from '!!raw-loader!../src/popcorn'
|
||||
|
||||
### PDJ (variation 24)
|
||||
|
||||
This is known as a "parametric" variation because it has additional parameters given to it:
|
||||
Some variations have extra parameters that the designer can choose; these are known as "parametric variations."
|
||||
For the PDJ variation, there are four extra parameters we can choose:
|
||||
|
||||
$$
|
||||
p_1 = \text{pdj.a} \hspace{0.2cm} p_2 = \text{pdj.b} \hspace{0.2cm} p_3 = \text{pdj.c} \hspace{0.2cm} p_4 = \text{pdj.d} \\
|
||||
@ -123,9 +125,9 @@ import pdjSrc from '!!raw-loader!../src/pdj'
|
||||
|
||||
## Blending
|
||||
|
||||
Now, one variation is fun, but we can also combine variations in a single transform by "blending."
|
||||
Now, one variation is fun, but we can also combine variations in a process called "blending."
|
||||
Each variation receives the same $x$ and $y$ inputs, and we add together each variation's $x$ and $y$ outputs.
|
||||
We'll also give each variation a weight ($v_{ij}$) to control how much it contributes to the transform:
|
||||
We'll also give each variation a weight (called $v_{ij}$) that changes how much it contributes to the transform:
|
||||
|
||||
$$
|
||||
F_i(x,y) = \sum_{j} v_{ij} V_j(a_i \cdot x + b_i \cdot y + c_i, \hspace{0.2cm} d_i \cdot x + e_i \cdot y + f_i)
|
||||
@ -137,10 +139,15 @@ import blendSource from "!!raw-loader!../src/blend";
|
||||
|
||||
<CodeBlock language={'typescript'}>{blendSource}</CodeBlock>
|
||||
|
||||
And with that in place, we have enough to render a first full fractal flame.
|
||||
The sliders below change the variation weights for each transform (the $v_{ij}$ parameters);
|
||||
try changing them around to see which parts of the image are controlled by
|
||||
each transform.
|
||||
With that in place, we have enough to render a first full fractal flame. We'll use the same
|
||||
chaos game as before, but use our new transforms and variations to produce a dramatically different image:
|
||||
|
||||
:::tip
|
||||
This image is interactive! The sliders change the variation weights ($v_{ij}$ parameters)
|
||||
so you can design your own image.
|
||||
|
||||
Try using the sliders to find which parts of the image each transform controls.
|
||||
:::
|
||||
|
||||
import {SquareCanvas} from "../src/Canvas";
|
||||
import FlameBlend from "./FlameBlend";
|
||||
@ -149,20 +156,40 @@ import FlameBlend from "./FlameBlend";
|
||||
|
||||
## Post transforms
|
||||
|
||||
After variation blending, we apply a second set of transform coordinates.
|
||||
Post transforms introduce a second affine transform, this time _after_ variation blending.
|
||||
We'll use introduce some new variables, but the post transform function should look familiar by now:
|
||||
|
||||
The fractal flame below starts with the same initial transforms/variations as the previous fractal flame,
|
||||
$$
|
||||
\begin{align*}
|
||||
P_i(x, y) &= (\alpha_i x + \beta_i y + \gamma_i, \delta_i x + \epsilon_i y + \zeta_i) \\
|
||||
F_i(x, y) &= P_i\left(\sum_{j} v_{ij} V_j(x, y)\right)
|
||||
\end{align*}
|
||||
$$
|
||||
|
||||
import postSource from '!!raw-loader!./post'
|
||||
|
||||
<CodeBlock language="typescript">{postSource}</CodeBlock>
|
||||
|
||||
The image below starts with the same initial transforms/variations as the previous fractal flame,
|
||||
but allows modifying the post-transform coefficients.
|
||||
|
||||
$$
|
||||
P_i(x, y) = (\alpha_i x + \beta_i y + \gamma_i, \delta_i x + \epsilon_i y + \zeta_i)
|
||||
$$
|
||||
<details>
|
||||
<summary>If you want a challenge...</summary>
|
||||
|
||||
Challenge 1: What post-transform coefficients will give us the previous image?
|
||||
|
||||
Challenge 2: What post-transform coefficients will give us a _mirrored_ image?
|
||||
</details>
|
||||
|
||||
import FlamePost from "./FlamePost";
|
||||
|
||||
<SquareCanvas><FlamePost/></SquareCanvas>
|
||||
|
||||
## Final transform
|
||||
## Final transforms
|
||||
|
||||
import chaosGameFinalSource from "!!raw-loader!./chaosGameFinal"
|
||||
|
||||
<CodeBlock language="typescript">{chaosGameFinalSource}</CodeBlock>
|
||||
|
||||
import FlameFinal from "./FlameFinal";
|
||||
|
||||
|
@ -4,4 +4,4 @@ import {Transform} from "../src/transform";
|
||||
import {applyCoefs} from "../src/coefs";
|
||||
// hidden-end
|
||||
export const transformPost = (transform: Transform, coefs: Coefs): Transform =>
|
||||
(x, y): [number, number] => applyCoefs(...transform(x, y), coefs)
|
||||
(x, y) => applyCoefs(...transform(x, y), coefs)
|
@ -44,20 +44,6 @@ const PaletteBar: React.FC<PaletteBarProps> = ({height, palette, children}) => {
|
||||
)
|
||||
}
|
||||
|
||||
const colorSwatchPainter = (palette: number[], color: number) =>
|
||||
(width: number, height: number) => {
|
||||
const [r, g, b] = colorFromPalette(palette, color);
|
||||
const image = new ImageData(width, height);
|
||||
for (let i = 0; i < image.data.length; i += 4) {
|
||||
image.data[i] = r * 0xff;
|
||||
image.data[i + 1] = g * 0xff;
|
||||
image.data[i + 2] = b * 0xff;
|
||||
image.data[i + 3] = 0xff;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
type ColorEditorProps = {
|
||||
title: string;
|
||||
palette: number[];
|
||||
|
@ -1,9 +1,13 @@
|
||||
import {ChaosGameFinalProps} from "../2-transforms/chaosGameFinal";
|
||||
// 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, paintColor} from "./paintColor";
|
||||
|
||||
const quality = 15;
|
||||
const step = 100_000;
|
||||
// hidden-end
|
||||
export type TransformColor = {
|
||||
color: number;
|
||||
colorSpeed: number;
|
||||
@ -13,23 +17,21 @@ function mixColor(color1: number, color2: number, colorSpeed: number) {
|
||||
return color1 * (1 - colorSpeed) + color2 * colorSpeed;
|
||||
}
|
||||
|
||||
export type ChaosGameColorProps = ChaosGameFinalProps & {
|
||||
type Props = ChaosGameFinalProps & {
|
||||
palette: number[];
|
||||
colors: TransformColor[];
|
||||
finalColor: TransformColor;
|
||||
}
|
||||
export function* chaosGameColor({width, height, transforms, final, palette, colors, finalColor, quality, step}: ChaosGameColorProps) {
|
||||
let iterations = (quality ?? 1) * width * height;
|
||||
step = step ?? 10_000;
|
||||
|
||||
export function* chaosGameColor({width, height, transforms, final, palette, colors, finalColor}: Props) {
|
||||
let currentColor = Math.random();
|
||||
const red = Array(width * height).fill(0);
|
||||
const green = Array(width * height).fill(0);
|
||||
const blue = Array(width * height).fill(0);
|
||||
const alpha = Array(width * height).fill(0);
|
||||
const red = Array<number>(width * height).fill(0);
|
||||
const green = Array<number>(width * height).fill(0);
|
||||
const blue = Array<number>(width * height).fill(0);
|
||||
const alpha = Array<number>(width * height).fill(0);
|
||||
|
||||
let [x, y] = [randomBiUnit(), randomBiUnit()];
|
||||
|
||||
const iterations = width * height * quality;
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
const [transformIndex, transform] = randomChoice(transforms);
|
||||
[x, y] = transform(x, y);
|
||||
|
@ -1,17 +1,19 @@
|
||||
// hidden-start
|
||||
import {randomBiUnit} from "../src/randomBiUnit";
|
||||
import {randomChoice} from "../src/randomChoice";
|
||||
import {ChaosGameFinalProps} from "../2-transforms/chaosGameFinal";
|
||||
import {Props as ChaosGameFinalProps} from "../2-transforms/chaosGameFinal";
|
||||
import {camera, histIndex} from "../src/camera";
|
||||
// hidden-end
|
||||
export type ChaosGameHistogramProps = ChaosGameFinalProps & {
|
||||
paint: (width: number, histogram: Uint32Array) => ImageData;
|
||||
}
|
||||
export function* chaosGameHistogram({width, height, transforms, final, quality, step, paint}: ChaosGameHistogramProps) {
|
||||
let iterations = (quality ?? 1) * width * height;
|
||||
step = step ?? 10_000;
|
||||
|
||||
const histogram = new Uint32Array(width * height);
|
||||
const quality = 10;
|
||||
const step = 100_000;
|
||||
// hidden-end
|
||||
export 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;
|
||||
|
||||
const histogram = Array<number>(width * height).fill(0);
|
||||
|
||||
let [x, y] = [randomBiUnit(), randomBiUnit()];
|
||||
|
||||
@ -22,13 +24,18 @@ export function* chaosGameHistogram({width, height, transforms, final, quality,
|
||||
|
||||
if (i > 20) {
|
||||
const [pixelX, pixelY] = camera(finalX, finalY, width);
|
||||
const pixelIndex = histIndex(pixelX, pixelY, width, 1);
|
||||
histogram[pixelIndex] += 1;
|
||||
const hIndex = histIndex(pixelX, pixelY, width, 1);
|
||||
|
||||
if (hIndex < 0 || hIndex >= histogram.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
histogram[hIndex] += 1;
|
||||
}
|
||||
|
||||
if (i % step === 0)
|
||||
yield paint(width, histogram);
|
||||
yield paint(width, height, histogram);
|
||||
}
|
||||
|
||||
yield paint(width, histogram);
|
||||
yield paint(width, height, histogram);
|
||||
}
|
@ -18,6 +18,10 @@ Can we do something more intelligent with that information?
|
||||
|
||||
## Image histograms
|
||||
|
||||
:::note
|
||||
This post covers sections 4 and 5 of the Fractal Flame Algorithm paper
|
||||
:::
|
||||
|
||||
To start with, it's worth demonstrating how much work is actually "wasted."
|
||||
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:
|
||||
|
@ -1,17 +1,17 @@
|
||||
export function paintLinear(width: number, histogram: Uint32Array): ImageData {
|
||||
const image = new ImageData(width, histogram.length / width);
|
||||
export function paintLinear(width: number, height: number, histogram: number[]): ImageData {
|
||||
const image = new ImageData(width, height);
|
||||
|
||||
let countMax = 0;
|
||||
let valueMax = 0;
|
||||
for (let value of histogram) {
|
||||
countMax = Math.max(countMax, value);
|
||||
valueMax = Math.max(valueMax, 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;
|
||||
image.data[pixelIndex] = 0;
|
||||
image.data[pixelIndex + 1] = 0;
|
||||
image.data[pixelIndex + 2] = 0;
|
||||
image.data[pixelIndex + 3] = histogram[i] / valueMax * 0xff;
|
||||
}
|
||||
|
||||
return image;
|
||||
|
@ -1,5 +1,5 @@
|
||||
export function paintLogarithmic(width: number, histogram: Uint32Array): ImageData {
|
||||
const image = new ImageData(width, histogram.length / width);
|
||||
export function paintLogarithmic(width: number, height: number, histogram: number[]): ImageData {
|
||||
const image = new ImageData(width, height);
|
||||
|
||||
const histogramLog = new Array<number>();
|
||||
histogram.forEach(value => histogramLog.push(Math.log(value)));
|
||||
|
@ -5,14 +5,15 @@ export type VariationBlend = [number, Variation][];
|
||||
export function blend(
|
||||
x: number,
|
||||
y: number,
|
||||
variations: VariationBlend): [number, number] {
|
||||
let [finalX, finalY] = [0, 0];
|
||||
variations: VariationBlend
|
||||
): [number, number] {
|
||||
let [outX, outY] = [0, 0];
|
||||
|
||||
for (const [weight, variation] of variations) {
|
||||
const [varX, varY] = variation(x, y);
|
||||
finalX += weight * varX;
|
||||
finalY += weight * varY;
|
||||
outX += weight * varX;
|
||||
outY += weight * varY;
|
||||
}
|
||||
|
||||
return [finalX, finalY];
|
||||
return [outX, outY];
|
||||
}
|
@ -2,14 +2,11 @@ import { camera, histIndex } from "./camera"
|
||||
|
||||
export function plotBinary(x: number, y: number, image: ImageData) {
|
||||
const [pixelX, pixelY] = camera(x, y, image.width);
|
||||
if (
|
||||
pixelX < 0 || pixelX >= image.width || pixelY < 0 || pixelY > image.height
|
||||
) {
|
||||
const pixelIndex = histIndex(pixelX, pixelY, image.width, 4);
|
||||
if (pixelIndex < 0 || pixelIndex > image.data.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pixelIndex = histIndex(pixelX, pixelY, image.width, 4);
|
||||
|
||||
image.data[pixelIndex] = 0;
|
||||
image.data[pixelIndex + 1] = 0;
|
||||
image.data[pixelIndex + 2] = 0;
|
||||
|
@ -8,7 +8,7 @@
|
||||
--ifm-pre-padding: .6rem;
|
||||
|
||||
/* More readable code highlight background */
|
||||
--docusaurus-highlighted-code-line-bg: var(--ifm-color-emphasis-300);
|
||||
--docusaurus-highlighted-code-line-bg: var(--ifm-color-emphasis-200);
|
||||
|
||||
/*--ifm-code-font-size: 85%;*/
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user