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 8887b59..9e6758c 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,7 +51,7 @@ 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? +TODO: What is a stationary point? How does it relate to the chaos game? Why does the chaos game work? For example, if we say $S = \{(0,0), (1, 1), (2, 2)\}$, there are three points to plot: @@ -187,4 +187,6 @@ import Gasket from '!!raw-loader!./Gasket' Note: The image our chaos game generates is different than the fractal flame paper, but I think the version displayed here is correct. As confirmation, the next post will re-create the same image using a different method. - \ No newline at end of file + + +TODO: Explanation of function weights $w_i$ \ No newline at end of file diff --git a/blog/2024-11-15-playing-with-fire/2-transforms/BaselineRender.ts b/blog/2024-11-15-playing-with-fire/2-transforms/BaselineRender.ts new file mode 100644 index 0000000..315aacb --- /dev/null +++ b/blog/2024-11-15-playing-with-fire/2-transforms/BaselineRender.ts @@ -0,0 +1,5 @@ +import {useColorMode, useThemeConfig} from "@docusaurus/theme-common"; + +export default function BaselineRender({children}) { + const {colorMode, setColorMode} = useColorMode(); +} \ No newline at end of file diff --git a/blog/2024-11-15-playing-with-fire/2-transforms/baseline.ts b/blog/2024-11-15-playing-with-fire/2-transforms/baseline.ts new file mode 100644 index 0000000..1708e81 --- /dev/null +++ b/blog/2024-11-15-playing-with-fire/2-transforms/baseline.ts @@ -0,0 +1,22 @@ +// hidden-start +import { Coefs } from './coefs' +import { Variation } from './variations' +// hidden-end +export function applyTransform( + x: number, + y: number, + coefs: Coefs, + variations: [number, Variation][]) +{ + const transformX = coefs.a * x + coefs.b * y + coefs.c; + const transformY = coefs.d * x + coefs.e * y + coefs.f; + + var finalX = 0; + var finalY = 0; + for (const [blend, variation] of variations) { + const [variationX, variationY] = variation(transformX, transformY); + finalX += blend * variationX; + finalY += blend * variationY; + } + return [finalX, finalY]; +} \ 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 index b67bbdd..ddccc50 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 @@ -25,6 +25,10 @@ $$ 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) $$ +import coefsSrc from '!!raw-loader!../src/coefs' + +{coefsSrc} + 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: @@ -37,7 +41,7 @@ F_0(x,y) &= \left({x \over 2}, {y \over 2}\right) \\ \end{align*} $$ -However, these transforms are pretty boring. We can build more exciting images by using some additional functions +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": $$ @@ -52,18 +56,18 @@ Our reference image will focus on just four variations: ### Linear (variation 0) -This variation returns the $x$ and $y$ coordinates as-is. As mentioned, the Sierpinski Gasket is -a fractal flame using only the linear variation: +This variation returns the $x$ and $y$ coordinates as-is: $$ V_0(x,y) = (x,y) $$ -```typescript -function linear(x: number, y: number) { - return [x, y]; -} -``` +import linearSrc from '!!raw-loader!../src/linear' + +{linearSrc} + +Before we move on, it's worth mentioning the relationship between this variation and the Sierpinski Gasket. +Specifically, we can think of the Gasket as a fractal flame that uses only the linear variation. ### Julia (variation 13) @@ -86,18 +90,9 @@ V_{13}(x, y) &= \sqrt{r} \cdot (\text{cos} ( \theta / 2 + \Omega ), \text{sin} ( \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; +import juliaSrc from '!!raw-loader!../src/julia' - return [ - r * Math.cos(theta / 2 + omega), - r * Math.sin(theta / 2 + omega) - ] -} -``` +{juliaSrc} ### Popcorn (variation 17) @@ -108,14 +103,9 @@ $$ V_{17}(x,y) = (x + c \cdot \text{sin}(\text{tan }3y), y + f \cdot \text{sin}(\text{tan }3x)) $$ -```typescript -function popcorn(coefs: {c: number, f: number}) { - return (x: number, y: number) => [ - x + coefs.c * Math.sin(Math.tan(3 * y)), - y + coefs.f * Math.sin(Math.tan(3 * x)) - ] -} -``` +import popcornSrc from '!!raw-loader!../src/popcorn' + +{popcornSrc} ### PDJ (variation 24) @@ -126,11 +116,27 @@ p_1 = \text{pdj.a} \hspace{0.2cm} p_2 = \text{pdj.b} \hspace{0.2cm} p_3 = \text{ 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 +import pdjSrc from '!!raw-loader!../src/pdj' + +{pdjSrc} + +### Blending + +Now, one variation is fun, but we can also combine variations in a single transform by "blending." +First, each variation is assigned a value that describes how much it affects the transform function ($v_j$). +Afterward, sum up the $x$ and $y$ values respectively: + +$$ +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) +$$ + +The formula looks intimidating, but it's not hard to implement: + +import baselineSrc from '!!raw-loader!./baseline' + +{baselineSrc} + +TODO: Mention that the Sierpinski Gasket is just a blend with linear weight 1, all others 0. Maybe replace comment above about Sierpinski Gasket and linear transform? + +And with that in place, we have enough to render a first full fractal flame: + diff --git a/blog/2024-11-15-playing-with-fire/2-transforms/coefs.ts b/blog/2024-11-15-playing-with-fire/src/coefs.ts similarity index 100% rename from blog/2024-11-15-playing-with-fire/2-transforms/coefs.ts rename to blog/2024-11-15-playing-with-fire/src/coefs.ts diff --git a/blog/2024-11-15-playing-with-fire/src/julia.ts b/blog/2024-11-15-playing-with-fire/src/julia.ts new file mode 100644 index 0000000..5d21045 --- /dev/null +++ b/blog/2024-11-15-playing-with-fire/src/julia.ts @@ -0,0 +1,13 @@ +// hidden-start +import { Variation } from './variations' +// hidden-end +export const julia: Variation = (x, y) => { + 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) + ] +} \ No newline at end of file diff --git a/blog/2024-11-15-playing-with-fire/src/linear.ts b/blog/2024-11-15-playing-with-fire/src/linear.ts new file mode 100644 index 0000000..cbea1e7 --- /dev/null +++ b/blog/2024-11-15-playing-with-fire/src/linear.ts @@ -0,0 +1,4 @@ +// hidden-start +import {Variation} from "./variations" +//hidden-end +export const linear: Variation = (x, y) => [x, y]; \ No newline at end of file diff --git a/blog/2024-11-15-playing-with-fire/params.ts b/blog/2024-11-15-playing-with-fire/src/params.ts similarity index 62% rename from blog/2024-11-15-playing-with-fire/params.ts rename to blog/2024-11-15-playing-with-fire/src/params.ts index b18c647..485347d 100644 --- a/blog/2024-11-15-playing-with-fire/params.ts +++ b/blog/2024-11-15-playing-with-fire/src/params.ts @@ -3,8 +3,12 @@ * translated into something that's easier to work with. */ -import {Coefs, Transform} from "./types"; -import { julia, linear, pdj, popcorn } from "./variation"; +import { Coefs } from './coefs'; +import { Transform } from './transform'; +import { linear } from './linear' +import { julia } from './julia' +import { popcorn } from './popcorn' +import { pdj } from './pdj' export const identityCoefs: Coefs = { a: 1, b: 0, c: 0, @@ -12,53 +16,51 @@ export const identityCoefs: Coefs = { } export const xform1Weight = 0.56453495; -export const xform1: Transform = { - coefs: { - a: -1.381068, b: -1.381068, c: 0, - d: 1.381068, e: -1.381068, f: 0, - }, - coefsPost: identityCoefs, - variations: [[1, julia]], - color: 0 +export const xform1Coefs = { + a: -1.381068, b: -1.381068, c: 0, + d: 1.381068, e: -1.381068, f: 0, } +export const xform1CoefsPost = identityCoefs; +export const xform1Variations = [ + [1, julia] +] +export const xform1Color = 0; -const xform2Weight = 0.013135; -export const xform2: Transform = { - coefs: { - a: 0.031393, b: 0.031367, c: 0, - d: -0.031367, e: 0.031393, f: 0, - }, - coefsPost: { - a: 1, b: 0, c: 0.241352, - d: 0, e: 1, f: 0.271521, - }, - variations: [ - [1, linear], - [1, popcorn] - ], - color: 0.844 +export const xform2Weight = 0.013135; +export const xform2Coefs = { + a: 0.031393, b: 0.031367, c: 0, + d: -0.031367, e: 0.031393, f: 0, } +export const xform2CoefsPost = { + a: 1, b: 0, c: 0.241352, + d: 0, e: 1, f: 0.271521, +} +export const xform2Variations = [ + [1, linear], + [1, popcorn(xform2Coefs)] +] +export const xform2Color = 0.844; export const xform3Weight = 0.42233; -export const xform3: Transform = { - coefs: { - a: 1.51523, b: -3.048677, c: 0.724135, - d: 0.740356, e: -1.455964, f: -0.362059, - }, - coefsPost: identityCoefs, - variations: [[1, pdj(1.09358, 2.13048, 2.54127, 2.37267)]], - color: 0.349 +export const xform3Coefs = { + a: 1.51523, b: -3.048677, c: 0.724135, + d: 0.740356, e: -1.455964, f: -0.362059, } +export const xform3CoefsPost = identityCoefs; +export const xform3Variations = [ + [1, pdj(1.09358, 2.13048, 2.54127, 2.37267)] +]; +export const xform3Color = 0.349; -export const xformFinal: Transform = { - coefs: { - a: 2, b: 0, c: 0, - d: 0, e: 2, f: 0 - }, - coefsPost: identityCoefs, - variations: [[1, julia]], - color: 0 +export const xformFinalCoefs = { + a: 2, b: 0, c: 0, + d: 0, e: 2, f: 0 } +export const xformFinalCoefsPost = identityCoefs; +export const xformFinalVariations = [ + [1, julia] +] +export const xformFinalColor = 0; export const palette = "7E3037762C45722B496E2A4E6A2950672853652754632656" + diff --git a/blog/2024-11-15-playing-with-fire/src/pdj.ts b/blog/2024-11-15-playing-with-fire/src/pdj.ts new file mode 100644 index 0000000..5360ced --- /dev/null +++ b/blog/2024-11-15-playing-with-fire/src/pdj.ts @@ -0,0 +1,9 @@ +// hidden-start +import { Variation } from './variations' +//hidden-end +export function pdj(a: number, b: number, c: number, d: number): Variation { + return (x, y) => [ + Math.sin(a * y) - Math.cos(b * x), + Math.sin(c * x) - Math.cos(d * y) + ] +} \ No newline at end of file diff --git a/blog/2024-11-15-playing-with-fire/src/popcorn.ts b/blog/2024-11-15-playing-with-fire/src/popcorn.ts new file mode 100644 index 0000000..4652960 --- /dev/null +++ b/blog/2024-11-15-playing-with-fire/src/popcorn.ts @@ -0,0 +1,10 @@ +// hidden-start +import {Coefs} from './coefs' +import {Variation} from './variations' +// hidden-end +export function popcorn({c, f}: Coefs): Variation { + return (x, y) => [ + x + c * Math.sin(Math.tan(3 * y)), + y + f * Math.sin(Math.tan(3 * x)) + ]; +} \ No newline at end of file diff --git a/blog/2024-11-15-playing-with-fire/src/transform.ts b/blog/2024-11-15-playing-with-fire/src/transform.ts new file mode 100644 index 0000000..e719c57 --- /dev/null +++ b/blog/2024-11-15-playing-with-fire/src/transform.ts @@ -0,0 +1,9 @@ +import { Coefs } from './coefs' +import { Variation } from './variations' + +export interface Transform { + coefs: Coefs, + variations: [number, Variation][], + coefsPost: Coefs, + color: number +} \ No newline at end of file diff --git a/blog/2024-11-15-playing-with-fire/src/variations.ts b/blog/2024-11-15-playing-with-fire/src/variations.ts new file mode 100644 index 0000000..3b90d18 --- /dev/null +++ b/blog/2024-11-15-playing-with-fire/src/variations.ts @@ -0,0 +1 @@ +export type Variation = (x: number, y: number) => [number, number]; \ No newline at end of file diff --git a/blog/2024-11-15-playing-with-fire/types.ts b/blog/2024-11-15-playing-with-fire/types.ts deleted file mode 100644 index 7d43ec5..0000000 --- a/blog/2024-11-15-playing-with-fire/types.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Affine transformation coefficients - */ -export type Coefs = { - a: number; - b: number; - c: number; - d: number; - e: number; - f: number; -}; - -export type Variation = ( - x: number, - y: number, - coefs: Coefs -) => [number, number]; - -export type Transform = { - coefs: Coefs, - coefsPost: Coefs, - variations: [number, Variation][], - color: number -} diff --git a/blog/2024-11-15-playing-with-fire/variation.ts b/blog/2024-11-15-playing-with-fire/variation.ts deleted file mode 100644 index ddf9ce6..0000000 --- a/blog/2024-11-15-playing-with-fire/variation.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Variation } from "./types"; - -export const linear: Variation = (x, y) => [x, y]; - -function r(x: number, y: number) { - return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); -} - -function theta(x: number, y: number) { - return Math.atan2(x, y); -} - -function omega(): number { - return Math.random() > 0.5 ? Math.PI : 0; -} - -export const julia: Variation = (x, y) => { - const sqrtR = Math.sqrt(r(x, y)); - const thetaVal = theta(x, y) / 2 + omega(); - return [sqrtR * Math.cos(thetaVal), sqrtR * Math.sin(thetaVal)]; -}; - -export const popcorn: Variation = (x, y, transformCoefs) => { - return [ - x + transformCoefs.c * Math.sin(Math.tan(3 * y)), - y + transformCoefs.f * Math.sin(Math.tan(3 * x)), - ]; -}; - -export const pdj: ( - pdjA: number, - pdjB: number, - pdjC: number, - pdjD: number -) => Variation = (pdjA, pdjB, pdjC, pdjD) => { - return (x, y) => [ - Math.sin(pdjA * y) - Math.cos(pdjB * x), - Math.sin(pdjC * x) - Math.cos(pdjD * y), - ]; -}; diff --git a/docusaurus.config.ts b/docusaurus.config.ts index e2ee794..72d1a9c 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -81,7 +81,19 @@ const config: Config = { prism: { theme: prismThemes.oneLight, darkTheme: prismThemes.oneDark, - additionalLanguages: ['bash', 'java', 'julia', 'nasm'] + additionalLanguages: ['bash', 'java', 'julia', 'nasm'], + magicComments: [ + // Remember to extend the default highlight class name as well! + { + className: 'theme-code-block-highlighted-line', + line: 'highlight-next-line', + block: {start: 'highlight-start', end: 'highlight-end'}, + }, + { + className: 'code-block-hidden', + block: {start: 'hidden-start', end: 'hidden-end'} + } + ] }, } satisfies Preset.ThemeConfig, plugins: [require.resolve('docusaurus-lunr-search')], diff --git a/src/DualImage/index.tsx b/src/DualImage/index.tsx deleted file mode 100644 index 2e94211..0000000 --- a/src/DualImage/index.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import {useColorMode} from "@docusaurus/theme-common"; -import React from "react"; - -interface Props { - srcLight: string; - srcDark: string; - alt: string; -} -const DualImage = ({srcLight, srcDark, alt}: Props) => { - const {colorMode} = useColorMode(); - return {alt} -} -export default DualImage; \ No newline at end of file diff --git a/src/css/custom.css b/src/css/custom.css index 7d691f0..9a455f7 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -29,4 +29,11 @@ adapted for Victory charts */ [data-theme='dark'] .VictoryContainer { filter: invert(75%) hue-rotate(180deg); +} + +/* +Custom magic comment for Prism - hide parts of the code in display + */ +.code-block-hidden { + display: none; } \ No newline at end of file