From e1735404ae4a32f8fe15c91d4dd40f47403e7ecf Mon Sep 17 00:00:00 2001 From: Bradlee Speice Date: Mon, 18 Nov 2024 22:01:31 -0500 Subject: [PATCH] Start on the transforms post --- .../1-introduction/biunit.ts | 2 +- .../1-introduction/index.mdx | 2 + .../1-introduction/randint.ts | 2 +- .../2-transforms.mdx | 7 - .../2-transforms/coefs.ts | 4 + .../2-transforms/index.mdx | 150 ++++++++++++++++++ 6 files changed, 158 insertions(+), 9 deletions(-) delete mode 100644 blog/2024-11-15-playing-with-fire/2-transforms.mdx create mode 100644 blog/2024-11-15-playing-with-fire/2-transforms/coefs.ts create mode 100644 blog/2024-11-15-playing-with-fire/2-transforms/index.mdx diff --git a/blog/2024-11-15-playing-with-fire/1-introduction/biunit.ts b/blog/2024-11-15-playing-with-fire/1-introduction/biunit.ts index c8e17c6..21bf035 100644 --- a/blog/2024-11-15-playing-with-fire/1-introduction/biunit.ts +++ b/blog/2024-11-15-playing-with-fire/1-introduction/biunit.ts @@ -1,3 +1,3 @@ -export default function randomBiUnit(): number { +export default function randomBiUnit() { return Math.random() * 2 - 1; } \ No newline at end of file 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 efd01b5..8887b59 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 @@ -51,6 +51,8 @@ First, $S$. We're generating images, so everything is in two dimensions: $S \in all points that are "in the system." To generate our final image, we just plot every point in the system like a coordinate chart. +TODO: What is a stationary point? How does it relate to the chaos game? + For example, if we say $S = \{(0,0), (1, 1), (2, 2)\}$, there are three points to plot: import {VictoryChart, VictoryTheme, VictoryScatter, VictoryLegend} from "victory"; diff --git a/blog/2024-11-15-playing-with-fire/1-introduction/randint.ts b/blog/2024-11-15-playing-with-fire/1-introduction/randint.ts index 0e922ba..07d79a3 100644 --- a/blog/2024-11-15-playing-with-fire/1-introduction/randint.ts +++ b/blog/2024-11-15-playing-with-fire/1-introduction/randint.ts @@ -1,3 +1,3 @@ -export default function randomInteger(min: number, max: number): number { +export default function randomInteger(min: number, max: number) { return Math.floor(Math.random() * (max - min)) + min; } \ No newline at end of file diff --git a/blog/2024-11-15-playing-with-fire/2-transforms.mdx b/blog/2024-11-15-playing-with-fire/2-transforms.mdx deleted file mode 100644 index ed2fdfa..0000000 --- a/blog/2024-11-15-playing-with-fire/2-transforms.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -slug: 2024/11/playing-with-fire-transforms -title: "Playing with fire: Transforms and variations" -date: 2024-11-15 13:00:00 -authors: [bspeice] -tags: [] ---- \ No newline at end of file diff --git a/blog/2024-11-15-playing-with-fire/2-transforms/coefs.ts b/blog/2024-11-15-playing-with-fire/2-transforms/coefs.ts new file mode 100644 index 0000000..da5b1aa --- /dev/null +++ b/blog/2024-11-15-playing-with-fire/2-transforms/coefs.ts @@ -0,0 +1,4 @@ +export interface Coefs { + a: number, b: number, c: number, + d: number, e: number, f: number +} \ No newline at end of file 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 new file mode 100644 index 0000000..120fbc7 --- /dev/null +++ b/blog/2024-11-15-playing-with-fire/2-transforms/index.mdx @@ -0,0 +1,150 @@ +--- +slug: 2024/11/playing-with-fire-transforms +title: "Playing with fire: Transforms and variations" +date: 2024-11-15 13:00:00 +authors: [bspeice] +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. + + + +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. + +## Transforms and variations + +import CodeBlock from '@theme/CodeBlock' + +We previously introduced "transforms" as the "functions" of an "iterated function system." Their general format is: + +$$ +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) +$$ + +Let's also start defining some types we can use in our code. The transform coefficients are a good place to start: + +```typescript +interface Coefs { + a: number, b: number, c: number, + d: number, e: number, f: number +} +``` + +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: + +$$ +\begin{align*} +F_0(x,y) &= \left({x \over 2}, {y \over 2}\right) \\ +&= (a_0 \cdot x + b_0 \cdot y + c_o, 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*} +$$ + +However, these transforms are pretty boring. We can build more exciting images by using some 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). + +In code, variations are pretty easy: + +```typescript +type Variation = (x: number, y: number) => [number, number]; +```` + +Our reference image will focus on just four variations: + +### Linear (variation 0) + +This variation returns the $x$ and $y$ coordinates as-is. In a way, the Sierpinski Gasket is +a fractal flame using only the linear variation. + +$$ +V_0(x,y) = (x,y) +$$ + +```typescript +function linear(x: number, y: number) { + return [x, y]; +} +``` + +### Julia (variation 13) + +This variation still uses just the $x$ and $y$ coordinates, but does some crazy things with them: + +TODO: Is this related to the Julia set? + +$$ +\begin{align*} +r &= \sqrt{x^2 + y^2} \\ +\theta &= \text{arctan}(x / y) \\ +\Omega &= \left\{ + \begin{array}{lr} + 0 \hspace{0.4cm} \text{w.p. } 0.5 \\ + \pi \hspace{0.4cm} \text{w.p. } 0.5 \\ + \end{array} +\right\} \\ + +V_{13}(x, y) &= \sqrt{r} \cdot (\text{cos} ( \theta / 2 + \Omega ), \text{sin} ( \theta / 2 + \Omega )) +\end{align*} +$$ + +```typescript +function julia(x: number, y: number) { + const r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); + const theta = Math.atan2(x, y); + const omega = Math.random() > 0.5 ? 0 : Math.PI; + + return [ + r * Math.cos(theta / 2 + omega), + r * Math.sin(theta / 2 + omega) + ] +} +``` + +### Popcorn (variation 17) + +This is known as a "dependent variation" because it depends on knowing the transform coefficients: + +$$ +V_{17}(x,y) = (x + c \cdot \text{sin}(\text{tan }3y), y + f \cdot \text{sin}(\text{tan }3x)) +$$ + +```typescript +function popcorn({c, f}: Coefs) { + return (x: number, y: number) => [ + x + c * Math.sin(Math.tan(3 * y)), + y + f * Math.sin(Math.tan(3 * x)) + ] +} +``` + +### PDJ (variation 24) + +This is known as a "parametric" variation because it has additional parameters given to it: + +$$ +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} \\ +V_{24} = (\text{sin}(p_1 \cdot y) - \text{cos}(p_2 \cdot x), \text{sin}(p_3 \cdot x) - \text{cos}(p_4 \cdot y)) +$$ + +```typescript +function pdj(a: number, b: number, c: number, d: number) { + return (x: number, y: number) => [ + Math.sin(a * y) - Math.cos(b * x), + Math.sin(c * x) - Math.cos(d * y) + ] +} +``` \ No newline at end of file