From 09835586599baa75767cd6103275fb95f804a5cd Mon Sep 17 00:00:00 2001 From: Bradlee Speice Date: Mon, 9 Dec 2024 22:18:13 -0500 Subject: [PATCH] More writing for the main posts --- .../1-introduction/chaosGameWeighted.ts | 6 +- .../1-introduction/index.mdx | 20 ++- .../2-transforms/FlameBlend.tsx | 2 - .../2-transforms/FlamePost.tsx | 2 +- .../2-transforms/chaosGameFinal.ts | 2 + .../2-transforms/index.mdx | 133 +++++++++++------- .../2-transforms/post.ts | 2 +- .../3-log-density/FlameColor.tsx | 14 -- .../3-log-density/chaosGameColor.ts | 22 +-- .../3-log-density/chaosGameHistogram.ts | 33 +++-- .../3-log-density/index.mdx | 4 + .../3-log-density/paintLinear.ts | 16 +-- .../3-log-density/paintLogarithmic.ts | 4 +- .../2024-11-15-playing-with-fire/src/blend.ts | 11 +- .../src/plotBinary.ts | 7 +- src/css/custom.css | 2 +- 16 files changed, 155 insertions(+), 125 deletions(-) diff --git a/blog/2024-11-15-playing-with-fire/1-introduction/chaosGameWeighted.ts b/blog/2024-11-15-playing-with-fire/1-introduction/chaosGameWeighted.ts index e219ed9..ef1966a 100644 --- a/blog/2024-11-15-playing-with-fire/1-introduction/chaosGameWeighted.ts +++ b/blog/2024-11-15-playing-with-fire/1-introduction/chaosGameWeighted.ts @@ -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); diff --git a/blog/2024-11-15-playing-with-fire/1-introduction/index.mdx b/blog/2024-11-15-playing-with-fire/1-introduction/index.mdx index de3897b..befe48d 100644 --- a/blog/2024-11-15-playing-with-fire/1-introduction/index.mdx +++ b/blog/2024-11-15-playing-with-fire/1-introduction/index.mdx @@ -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 = [ 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 diff --git a/blog/2024-11-15-playing-with-fire/2-transforms/FlameBlend.tsx b/blog/2024-11-15-playing-with-fire/2-transforms/FlameBlend.tsx index a05dcf8..03d83a2 100644 --- a/blog/2024-11-15-playing-with-fire/2-transforms/FlameBlend.tsx +++ b/blog/2024-11-15-playing-with-fire/2-transforms/FlameBlend.tsx @@ -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(() => { diff --git a/blog/2024-11-15-playing-with-fire/2-transforms/FlamePost.tsx b/blog/2024-11-15-playing-with-fire/2-transforms/FlamePost.tsx index 539114e..7402383 100644 --- a/blog/2024-11-15-playing-with-fire/2-transforms/FlamePost.tsx +++ b/blog/2024-11-15-playing-with-fire/2-transforms/FlamePost.tsx @@ -17,7 +17,7 @@ export default function FlamePost() { const resetXform2CoefsPost = () => setXform2CoefsPost(params.xform2CoefsPost); const [xform3CoefsPost, setXform3CoefsPost] = useState(params.xform3CoefsPost); - const resetXform3CoefsPost = () => setXform1CoefsPost(params.xform3CoefsPost); + const resetXform3CoefsPost = () => setXform3CoefsPost(params.xform3CoefsPost); const identityXform: Transform = (x, y) => [x, y]; diff --git a/blog/2024-11-15-playing-with-fire/2-transforms/chaosGameFinal.ts b/blog/2024-11-15-playing-with-fire/2-transforms/chaosGameFinal.ts index 147b02b..caacae1 100644 --- a/blog/2024-11-15-playing-with-fire/2-transforms/chaosGameFinal.ts +++ b/blog/2024-11-15-playing-with-fire/2-transforms/chaosGameFinal.ts @@ -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; diff --git a/blog/2024-11-15-playing-with-fire/2-transforms/index.mdx b/blog/2024-11-15-playing-with-fire/2-transforms/index.mdx index 47c60c2..475b36e 100644 --- a/blog/2024-11-15-playing-with-fire/2-transforms/index.mdx +++ b/blog/2024-11-15-playing-with-fire/2-transforms/index.mdx @@ -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. -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' -{coefsSrc} +{variationSource} -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' {linearSrc} +:::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: - -TODO: Is this related to the Julia set? +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"; {blendSource} -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' + +{postSource} + +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) -$$ +
+ If you want a challenge... + + Challenge 1: What post-transform coefficients will give us the previous image? + + Challenge 2: What post-transform coefficients will give us a _mirrored_ image? +
import FlamePost from "./FlamePost"; -## Final transform +## Final transforms + +import chaosGameFinalSource from "!!raw-loader!./chaosGameFinal" + +{chaosGameFinalSource} import FlameFinal from "./FlameFinal"; diff --git a/blog/2024-11-15-playing-with-fire/2-transforms/post.ts b/blog/2024-11-15-playing-with-fire/2-transforms/post.ts index fc9a777..b79ded1 100644 --- a/blog/2024-11-15-playing-with-fire/2-transforms/post.ts +++ b/blog/2024-11-15-playing-with-fire/2-transforms/post.ts @@ -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) \ No newline at end of file + (x, y) => applyCoefs(...transform(x, y), coefs) \ No newline at end of file diff --git a/blog/2024-11-15-playing-with-fire/3-log-density/FlameColor.tsx b/blog/2024-11-15-playing-with-fire/3-log-density/FlameColor.tsx index 84a0db1..b46418e 100644 --- a/blog/2024-11-15-playing-with-fire/3-log-density/FlameColor.tsx +++ b/blog/2024-11-15-playing-with-fire/3-log-density/FlameColor.tsx @@ -44,20 +44,6 @@ const PaletteBar: React.FC = ({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[]; diff --git a/blog/2024-11-15-playing-with-fire/3-log-density/chaosGameColor.ts b/blog/2024-11-15-playing-with-fire/3-log-density/chaosGameColor.ts index 71902e8..4d35735 100644 --- a/blog/2024-11-15-playing-with-fire/3-log-density/chaosGameColor.ts +++ b/blog/2024-11-15-playing-with-fire/3-log-density/chaosGameColor.ts @@ -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(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); 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); diff --git a/blog/2024-11-15-playing-with-fire/3-log-density/chaosGameHistogram.ts b/blog/2024-11-15-playing-with-fire/3-log-density/chaosGameHistogram.ts index b35980d..17e61d6 100644 --- a/blog/2024-11-15-playing-with-fire/3-log-density/chaosGameHistogram.ts +++ b/blog/2024-11-15-playing-with-fire/3-log-density/chaosGameHistogram.ts @@ -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(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); } \ No newline at end of file diff --git a/blog/2024-11-15-playing-with-fire/3-log-density/index.mdx b/blog/2024-11-15-playing-with-fire/3-log-density/index.mdx index 7c96033..5c435d7 100644 --- a/blog/2024-11-15-playing-with-fire/3-log-density/index.mdx +++ b/blog/2024-11-15-playing-with-fire/3-log-density/index.mdx @@ -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: diff --git a/blog/2024-11-15-playing-with-fire/3-log-density/paintLinear.ts b/blog/2024-11-15-playing-with-fire/3-log-density/paintLinear.ts index 05d5c91..077b3a6 100644 --- a/blog/2024-11-15-playing-with-fire/3-log-density/paintLinear.ts +++ b/blog/2024-11-15-playing-with-fire/3-log-density/paintLinear.ts @@ -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; diff --git a/blog/2024-11-15-playing-with-fire/3-log-density/paintLogarithmic.ts b/blog/2024-11-15-playing-with-fire/3-log-density/paintLogarithmic.ts index c79bce0..4e18b1d 100644 --- a/blog/2024-11-15-playing-with-fire/3-log-density/paintLogarithmic.ts +++ b/blog/2024-11-15-playing-with-fire/3-log-density/paintLogarithmic.ts @@ -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(); histogram.forEach(value => histogramLog.push(Math.log(value))); diff --git a/blog/2024-11-15-playing-with-fire/src/blend.ts b/blog/2024-11-15-playing-with-fire/src/blend.ts index a17e47b..d47f2cc 100644 --- a/blog/2024-11-15-playing-with-fire/src/blend.ts +++ b/blog/2024-11-15-playing-with-fire/src/blend.ts @@ -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]; } \ No newline at end of file diff --git a/blog/2024-11-15-playing-with-fire/src/plotBinary.ts b/blog/2024-11-15-playing-with-fire/src/plotBinary.ts index 5b9fb64..fd5b9a8 100644 --- a/blog/2024-11-15-playing-with-fire/src/plotBinary.ts +++ b/blog/2024-11-15-playing-with-fire/src/plotBinary.ts @@ -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; diff --git a/src/css/custom.css b/src/css/custom.css index 5030f55..e4b9fdd 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -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%;*/ }