Post/final transform implementation

This commit is contained in:
Bradlee Speice 2024-11-30 17:35:42 -05:00
parent 1aa45e3f59
commit 6c4d73f081
17 changed files with 360 additions and 120 deletions

View File

@ -28,8 +28,8 @@ export default function GasketWeighted() {
const weightInput = (title, weight, setWeight) => ( const weightInput = (title, weight, setWeight) => (
<> <>
<div className={styles.inputDiv}> <div className={styles.inputElement}>
<p><TeX>{title}</TeX> weight:<span style={{float: 'right'}}>{weight}</span></p> <p><TeX>{title}</TeX> weight: {weight}</p>
<input type={'range'} min={0} max={1} step={.01} style={{width: '100%'}} value={weight} <input type={'range'} min={0} max={1} step={.01} style={{width: '100%'}} value={weight}
onInput={e => setWeight(Number(e.currentTarget.value))}/> onInput={e => setWeight(Number(e.currentTarget.value))}/>
</div> </div>
@ -38,7 +38,7 @@ export default function GasketWeighted() {
return ( return (
<> <>
<div style={{paddingTop: '1em', display: 'grid', gridTemplateColumns: 'auto auto auto'}}> <div className={styles.inputGroup} style={{display: 'grid', gridTemplateColumns: 'auto auto auto'}}>
{weightInput("F_0", f0Weight, setF0Weight)} {weightInput("F_0", f0Weight, setF0Weight)}
{weightInput("F_1", f1Weight, setF1Weight)} {weightInput("F_1", f1Weight, setF1Weight)}
{weightInput("F_2", f2Weight, setF2Weight)} {weightInput("F_2", f2Weight, setF2Weight)}

View File

@ -0,0 +1,48 @@
import TeX from "@matejmazur/react-katex";
import {Coefs} from "../src/coefs";
import styles from "../src/css/styles.module.css";
export interface Props {
title: String;
isPost: boolean;
coefs: Coefs;
setCoefs: (coefs: Coefs) => void;
}
export const CoefEditor = ({title, isPost, coefs, setCoefs}: Props) => {
return (
<div className={styles.inputGroup} style={{display: 'grid', gridTemplateColumns: 'auto auto auto'}}>
<p className={styles.inputTitle} style={{gridColumn: '1/-1'}}>{title}</p>
<div className={styles.inputElement}>
<p>{isPost ? <TeX>\alpha</TeX> : 'a'}: {coefs.a}</p>
<input type={'range'} min={0} max={2} step={0.01} style={{width: '100%'}} value={coefs.a}
onInput={e => setCoefs({...coefs, a: Number(e.currentTarget.value)})}/>
</div>
<div className={styles.inputElement}>
<p>{isPost ? <TeX>\beta</TeX> : 'b'}: {coefs.b}</p>
<input type={'range'} min={0} max={2} step={0.01} style={{width: '100%'}} value={coefs.b}
onInput={e => setCoefs({...coefs, b: Number(e.currentTarget.value)})}/>
</div>
<div className={styles.inputElement}>
<p>{isPost ? <TeX>\gamma</TeX> : 'c'}: {coefs.c}</p>
<input type={'range'} min={0} max={2} step={0.01} style={{width: '100%'}} value={coefs.c}
onInput={e => setCoefs({...coefs, c: Number(e.currentTarget.value)})}/>
</div>
<div className={styles.inputElement}>
<p>{isPost ? <TeX>\delta</TeX> : 'd'}: {coefs.d}</p>
<input type={'range'} min={0} max={2} step={0.01} style={{width: '100%'}} value={coefs.d}
onInput={e => setCoefs({...coefs, d: Number(e.currentTarget.value)})}/>
</div>
<div className={styles.inputElement}>
<p>{isPost ? <TeX>\epsilon</TeX> : 'e'}: {coefs.e}</p>
<input type={'range'} min={0} max={2} step={0.01} style={{width: '100%'}} value={coefs.e}
onInput={e => setCoefs({...coefs, e: Number(e.currentTarget.value)})}/>
</div>
<div className={styles.inputElement}>
<p>{isPost ? <TeX>\zeta</TeX> : 'f'}: {coefs.f}</p>
<input type={'range'} min={0} max={2} step={0.01} style={{width: '100%'}} value={coefs.f}
onInput={e => setCoefs({...coefs, f: Number(e.currentTarget.value)})}/>
</div>
</div>
)
}

View File

@ -1,15 +1,7 @@
import {useContext, useEffect, useState} from "react"; import {useContext, useEffect, useState} from "react";
import { blend } from "./blend";
import { applyCoefs, Coefs } from "../src/coefs"
import {randomBiUnit} from "../src/randomBiUnit";
import {linear} from "../src/linear";
import {julia} from "../src/julia";
import {popcorn} from "../src/popcorn";
import {pdj} from "../src/pdj";
import {Variation} from "../src/variation";
import {Transform} from "../src/transform"; import {Transform} from "../src/transform";
import { import {
pdjParams, identityCoefs,
xform1Coefs, xform1Coefs,
xform1Weight, xform1Weight,
xform2Coefs, xform2Coefs,
@ -17,26 +9,15 @@ import {
xform3Coefs, xform3Coefs,
xform3Weight xform3Weight
} from "../src/params"; } from "../src/params";
import {randomChoice} from "../src/randomChoice";
import {plotBinary} from "../src/plotBinary"
import {PainterContext} from "../src/Canvas" import {PainterContext} from "../src/Canvas"
import {buildBlend, buildTransform} from "./buildTransform"
import styles from "../src/css/styles.module.css" import {chaosGameFinal} from "./chaosGameFinal"
import {VariationEditor, VariationProps} from "./VariationEditor"
type VariationBlend = {
linear: number,
julia: number,
popcorn: number,
pdj: number
}
export default function FlameBlend() { export default function FlameBlend() {
const quality = 2;
const step = 5000;
const {width, height, setPainter} = useContext(PainterContext); const {width, height, setPainter} = useContext(PainterContext);
const xform1Default: VariationBlend = { const xform1Default: VariationProps = {
linear: 0, linear: 0,
julia: 1, julia: 1,
popcorn: 0, popcorn: 0,
@ -44,7 +25,7 @@ export default function FlameBlend() {
} }
const [xform1Variations, setXform1Variations] = useState(xform1Default) const [xform1Variations, setXform1Variations] = useState(xform1Default)
const xform2Default: VariationBlend = { const xform2Default: VariationProps = {
linear: 1, linear: 1,
julia: 0, julia: 0,
popcorn: 1, popcorn: 1,
@ -52,7 +33,7 @@ export default function FlameBlend() {
} }
const [xform2Variations, setXform2Variations] = useState(xform2Default) const [xform2Variations, setXform2Variations] = useState(xform2Default)
const xform3Default: VariationBlend = { const xform3Default: VariationProps = {
linear: 0, linear: 0,
julia: 0, julia: 0,
popcorn: 0, popcorn: 0,
@ -60,80 +41,21 @@ export default function FlameBlend() {
} }
const [xform3Variations, setXform3Variations] = useState(xform3Default) const [xform3Variations, setXform3Variations] = useState(xform3Default)
function buildTransform(coefs: Coefs, variations: VariationBlend): Transform { // Cheating a bit here; for purposes of code re-use, use the post- and final-transform-enabled chaos game,
return (x: number, y: number) => { // and swap in identity components for each
const [varX, varY] = applyCoefs(x, y, coefs); const identityXform: Transform = (x, y) => [x, y];
const varFunctions: [number, Variation][] = [
[variations.linear, linear],
[variations.julia, julia],
[variations.popcorn, popcorn(coefs)],
[variations.pdj, pdj(pdjParams)]
]
return blend(varX, varY, varFunctions); useEffect(() => setPainter(chaosGameFinal(width, height, [
} [xform1Weight, buildTransform(xform1Coefs, buildBlend(xform1Coefs, xform1Variations))],
} [xform2Weight, buildTransform(xform2Coefs, buildBlend(xform2Coefs, xform2Variations))],
[xform3Weight, buildTransform(xform3Coefs, buildBlend(xform3Coefs, xform3Variations))]
const image = new ImageData(width, height); ], identityXform)), [xform1Variations, xform2Variations, xform3Variations]);
function* chaosGame() {
let [x, y] = [randomBiUnit(), randomBiUnit()];
const transforms: [number, Transform][] = [
[xform1Weight, buildTransform(xform1Coefs, xform1Variations)],
[xform2Weight, buildTransform(xform2Coefs, xform2Variations)],
[xform3Weight, buildTransform(xform3Coefs, xform3Variations)]
]
const iterations = quality * image.width * image.height;
for (let i = 0; i < iterations; i++) {
let [_, transform] = randomChoice(transforms);
[x, y] = transform(x, y);
if (i > 20)
plotBinary(x, y, image);
if (i % step === 0) {
console.log(`Checking in; iterations=${i}`)
yield image;
}
}
yield image;
}
useEffect(() => setPainter(chaosGame()), [xform1Variations, xform2Variations, xform3Variations]);
const variationEditor = (title, variations, setVariations) => {
return (
<>
<p style={{gridColumn: '1/-1'}}>{title}:</p>
<div className={styles.inputDiv}>
<p>Linear: {variations.linear}</p>
<input type={'range'} min={0} max={1} step={0.01} style={{width: '100%'}} value={variations.linear}
onInput={e => setVariations({...variations, linear: Number(e.currentTarget.value)})}/>
</div>
<div className={styles.inputDiv}>
<p>Julia: {variations.julia}</p>
<input type={'range'} min={0} max={1} step={0.01} style={{width: '100%'}} value={variations.julia}
onInput={e => setVariations({...variations, julia: Number(e.currentTarget.value)})}/>
</div>
<div className={styles.inputDiv}>
<p>Popcorn: {variations.popcorn}</p>
<input type={'range'} min={0} max={1} step={0.01} style={{width: '100%'}} value={variations.popcorn}
onInput={e => setVariations({...variations, popcorn: Number(e.currentTarget.value)})}/>
</div>
<div className={styles.inputDiv}>
<p>PDJ: {variations.pdj}</p>
<input type={'range'} min={0} max={1} step={0.01} style={{width: '100%'}} value={variations.pdj}
onInput={e => setVariations({...variations, pdj: Number(e.currentTarget.value)})}/>
</div>
</>
)
}
return ( return (
<div style={{paddingTop: '1em', display: 'grid', gridTemplateColumns: 'auto auto auto auto'}}> <>
{variationEditor("Transform 1", xform1Variations, setXform1Variations)} <VariationEditor title={"Transform 1"} variations={xform1Variations} setVariations={setXform1Variations}/>
{variationEditor("Transform 2", xform2Variations, setXform2Variations)} <VariationEditor title={"Transform 2"} variations={xform2Variations} setVariations={setXform2Variations}/>
{variationEditor("Transform 3", xform3Variations, setXform3Variations)} <VariationEditor title={"Transform 3"} variations={xform3Variations} setVariations={setXform3Variations}/>
</div> </>
) )
} }

View File

@ -0,0 +1,64 @@
import {useContext, useEffect, useState} from "react";
import {Coefs} from "../src/coefs"
import {
xform1Coefs,
xform1Weight,
xform1Variations,
xform1CoefsPost,
xform2Coefs,
xform2Weight,
xform2Variations,
xform2CoefsPost,
xform3Coefs,
xform3Weight,
xform3Variations,
xform3CoefsPost,
xformFinalCoefs as xformFinalCoefsDefault,
xformFinalCoefsPost as xformFinalCoefsPostDefault,
} from "../src/params";
import {PainterContext} from "../src/Canvas"
import {buildBlend, buildTransform} from "./buildTransform";
import {transformPost} from "./post";
import {chaosGameFinal} from "./chaosGameFinal"
import {VariationEditor, VariationProps} from "./VariationEditor";
import {CoefEditor} from "./CoefEditor";
import {Transform} from "../src/transform";
export default function FlameFinal() {
const {width, height, setPainter} = useContext(PainterContext);
const [xformFinalCoefs, setXformFinalCoefs] = useState<Coefs>(xformFinalCoefsDefault);
const xformFinalVariationsDefault: VariationProps = {
linear: 0,
julia: 1,
popcorn: 0,
pdj: 0
}
const [xformFinalVariations, setXformFinalVariations] = useState<VariationProps>(xformFinalVariationsDefault);
const [xformFinalCoefsPost, setXformFinalCoefsPost] = useState<Coefs>(xformFinalCoefsPostDefault);
useEffect(() => {
const transforms: [number, Transform][] = [
[xform1Weight, transformPost(buildTransform(xform1Coefs, xform1Variations), xform1CoefsPost)],
[xform2Weight, transformPost(buildTransform(xform2Coefs, xform2Variations), xform2CoefsPost)],
[xform3Weight, transformPost(buildTransform(xform3Coefs, xform3Variations), xform3CoefsPost)]
];
const finalBlend = buildBlend(xformFinalCoefs, xformFinalVariations);
const finalTransform = buildTransform(xformFinalCoefs, finalBlend);
const finalPost = transformPost(finalTransform, xformFinalCoefsPost);
setPainter(chaosGameFinal(width, height, transforms, finalPost));
}, [xformFinalCoefs, xformFinalVariations, xformFinalCoefsPost]);
return (
<>
<CoefEditor title={"Final Transform"} isPost={false} coefs={xformFinalCoefs} setCoefs={setXformFinalCoefs}/>
<VariationEditor title={"Final Transform Variations"} variations={xformFinalVariations}
setVariations={setXformFinalVariations}/>
<CoefEditor title={"Final Transform Post"} isPost={true} coefs={xformFinalCoefsPost} setCoefs={setXformFinalCoefsPost}/>
</>
)
}

View File

@ -0,0 +1,46 @@
import {useContext, useEffect, useState} from "react";
import {Coefs} from "../src/coefs"
import {Transform} from "../src/transform";
import {
xform1Coefs,
xform1Weight,
xform1Variations,
xform1CoefsPost as xform1CoefsPostDefault,
xform2Coefs,
xform2Weight,
xform2Variations,
xform2CoefsPost as xform2CoefsPostDefault,
xform3Coefs,
xform3Weight,
xform3Variations,
xform3CoefsPost as xform3CoefsPostDefault
} from "../src/params";
import {PainterContext} from "../src/Canvas"
import {chaosGameFinal} from "./chaosGameFinal"
import {CoefEditor} from "./CoefEditor"
import {transformPost} from "./post";
import {buildTransform} from "./buildTransform";
export default function FlamePost() {
const {width, height, setPainter} = useContext(PainterContext);
const [xform1CoefsPost, setXform1PostCoefs] = useState<Coefs>(xform1CoefsPostDefault);
const [xform2CoefsPost, setXform2PostCoefs] = useState<Coefs>(xform2CoefsPostDefault);
const [xform3CoefsPost, setXform3PostCoefs] = useState<Coefs>(xform3CoefsPostDefault);
const identityXform: Transform = (x, y) => [x, y];
useEffect(() => setPainter(chaosGameFinal(width, height, [
[xform1Weight, transformPost(buildTransform(xform1Coefs, xform1Variations), xform1CoefsPost)],
[xform2Weight, transformPost(buildTransform(xform2Coefs, xform2Variations), xform2CoefsPost)],
[xform3Weight, transformPost(buildTransform(xform3Coefs, xform3Variations), xform3CoefsPost)]
], identityXform)), [xform1CoefsPost, xform2CoefsPost, xform3CoefsPost]);
return (
<>
<CoefEditor title={"Transform 1 Post"} isPost={true} coefs={xform1CoefsPost} setCoefs={setXform1PostCoefs}/>
<CoefEditor title={"Transform 2 Post"} isPost={true} coefs={xform2CoefsPost} setCoefs={setXform2PostCoefs}/>
<CoefEditor title={"Transform 3 Post"} isPost={true} coefs={xform3CoefsPost} setCoefs={setXform3PostCoefs}/>
</>
)
}

View File

@ -0,0 +1,42 @@
import styles from "../src/css/styles.module.css"
export interface VariationProps {
linear: number;
julia: number;
popcorn: number;
pdj: number;
}
export interface Props {
title: String;
variations: VariationProps;
setVariations: (variations: VariationProps) => void;
}
export const VariationEditor = ({title, variations, setVariations}: Props) => {
return (
<div className={styles.inputGroup} style={{display: 'grid', gridTemplateColumns: 'auto auto auto auto'}}>
<p className={styles.inputTitle} style={{gridColumn: '1/-1'}}>{title}</p>
<div className={styles.inputElement}>
<span>Linear: {variations.linear}</span>
<input type={'range'} min={0} max={1} step={0.01} style={{width: '100%'}} value={variations.linear}
onInput={e => setVariations({...variations, linear: Number(e.currentTarget.value)})}/>
</div>
<div className={styles.inputElement}>
<span>Julia: {variations.julia}</span>
<input type={'range'} min={0} max={1} step={0.01} style={{width: '100%'}} value={variations.julia}
onInput={e => setVariations({...variations, julia: Number(e.currentTarget.value)})}/>
</div>
<div className={styles.inputElement}>
<span>Popcorn: {variations.popcorn}</span>
<input type={'range'} min={0} max={1} step={0.01} style={{width: '100%'}} value={variations.popcorn}
onInput={e => setVariations({...variations, popcorn: Number(e.currentTarget.value)})}/>
</div>
<div className={styles.inputElement}>
<span>PDJ: {variations.pdj}</span>
<input type={'range'} min={0} max={1} step={0.01} style={{width: '100%'}} value={variations.pdj}
onInput={e => setVariations({...variations, pdj: Number(e.currentTarget.value)})}/>
</div>
</div>
)
}

View File

@ -1,7 +1,10 @@
// hidden-start // hidden-start
import { Variation } from "../src/variation" import {VariationBlend} from "../src/variationBlend";
// hidden-end // hidden-end
export function blend(x: number, y: number, variations: [number, Variation][]): [number, number] { export function blend(
x: number,
y: number,
variations: VariationBlend): [number, number] {
let [finalX, finalY] = [0, 0]; let [finalX, finalY] = [0, 0];
for (const [weight, variation] of variations) { for (const [weight, variation] of variations) {

View File

@ -0,0 +1,26 @@
import {applyCoefs, Coefs} from "../src/coefs";
import {VariationProps} from "./VariationEditor";
import {Transform} from "../src/transform";
import {linear} from "../src/linear";
import {julia} from "../src/julia";
import {popcorn} from "../src/popcorn";
import {pdj} from "../src/pdj";
import {pdjParams} from "../src/params";
import {blend} from "./blend";
import {VariationBlend} from "../src/variationBlend";
export function buildBlend(coefs: Coefs, variations: VariationProps): VariationBlend {
return [
[variations.linear, linear],
[variations.julia, julia],
[variations.popcorn, popcorn(coefs)],
[variations.pdj, pdj(pdjParams)]
]
}
export function buildTransform(coefs: Coefs, variations: VariationBlend): Transform {
return (x: number, y: number) => {
[x, y] = applyCoefs(x, y, coefs);
return blend(x, y, variations);
}
}

View File

@ -0,0 +1,29 @@
// hidden-start
import { randomBiUnit } from "../src/randomBiUnit";
import { randomChoice } from "../src/randomChoice";
import { plotBinary as plot } from "../src/plotBinary"
import {Transform} from "../src/transform";
const iterations = 500_000;
const step = 1000;
// hidden-end
export function* chaosGameFinal(width: number, height: number, transforms: [number, Transform][], final: Transform) {
let image = new ImageData(width, height);
let [x, y] = [randomBiUnit(), randomBiUnit()];
for (let i = 0; i < iterations; i++) {
const [_, transform] = randomChoice(transforms);
[x, y] = transform(x, y);
// highlight-start
[x, y] = final(x, y);
// highlight-end
if (i > 20)
plot(x, y, image);
if (i % step === 0)
yield image;
}
yield image;
}

View File

@ -37,12 +37,14 @@ the general format. For example:
$$ $$
\begin{align*} \begin{align*}
F_0(x,y) &= \left({x \over 2}, {y \over 2}\right) \\ 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 \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 \\ & 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 & d_0 = 0 \hspace{0.2cm} e_0 = 0.5 \hspace{0.2cm} f_0 = 0
\end{align*} \end{align*}
$$ $$
TODO: Explain the applyCoefs function
However, these transforms are pretty boring. We can build more exciting images by using 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": within the transform. These "sub-functions" are called "variations":
@ -79,10 +81,10 @@ $$
r &= \sqrt{x^2 + y^2} \\ r &= \sqrt{x^2 + y^2} \\
\theta &= \text{arctan}(x / y) \\ \theta &= \text{arctan}(x / y) \\
\Omega &= \left\{ \Omega &= \left\{
\begin{array}{lr} \begin{array}{lr}
0 \hspace{0.4cm} \text{w.p. } 0.5 \\ 0 \hspace{0.4cm} \text{w.p. } 0.5 \\
\pi \hspace{0.4cm} \text{w.p. } 0.5 \\ \pi \hspace{0.4cm} \text{w.p. } 0.5 \\
\end{array} \end{array}
\right\} \\ \right\} \\
V_{13}(x, y) &= \sqrt{r} \cdot (\text{cos} ( \theta / 2 + \Omega ), \text{sin} ( \theta / 2 + \Omega )) V_{13}(x, y) &= \sqrt{r} \cdot (\text{cos} ( \theta / 2 + \Omega ), \text{sin} ( \theta / 2 + \Omega ))
@ -123,8 +125,7 @@ import pdjSrc from '!!raw-loader!../src/pdj'
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 single transform by "blending."
Each variation receives the same $x$ and $y$ inputs, and we add together each variation's $x$ and $y$ outputs. 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_j$) that scales the output, which allows us to control We'll also give each variation a weight ($v_{ij}$) to control how much it contributes to the transform:
how much each variation 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) 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)
@ -136,7 +137,10 @@ import blendSource from "!!raw-loader!./blend";
<CodeBlock language={'typescript'}>{blendSource}</CodeBlock> <CodeBlock language={'typescript'}>{blendSource}</CodeBlock>
And with that in place, we have enough to render a first full fractal flame: 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.
import BrowserOnly from "@docusaurus/BrowserOnly"; import BrowserOnly from "@docusaurus/BrowserOnly";
import Canvas from "../src/Canvas"; import Canvas from "../src/Canvas";
@ -145,3 +149,28 @@ import FlameBlend from "./FlameBlend";
<Canvas width={500} height={500}> <Canvas width={500} height={500}>
<BrowserOnly>{() => <FlameBlend/>}</BrowserOnly> <BrowserOnly>{() => <FlameBlend/>}</BrowserOnly>
</Canvas> </Canvas>
## Post transforms
After variation blending, we apply a second set of transform coordinates.
The fractal flame 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)
$$
import FlamePost from "./FlamePost";
<Canvas width={500} height={500}>
<BrowserOnly>{() => <FlamePost/>}</BrowserOnly>
</Canvas>
## Final transform
import FlameFinal from "./FlameFinal";
<Canvas width={500} height={500}>
<BrowserOnly>{() => <FlameFinal/>}</BrowserOnly>
</Canvas>

View File

@ -0,0 +1,7 @@
// hidden-start
import {Coefs} from "../src/coefs";
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)

View File

@ -3,7 +3,7 @@ export interface Coefs {
d: number, e: number, f: number d: number, e: number, f: number
} }
export function applyCoefs(x: number, y: number, coefs: Coefs) { export function applyCoefs(x: number, y: number, coefs: Coefs): [number, number] {
return [ return [
(x * coefs.a + y * coefs.b + coefs.c), (x * coefs.a + y * coefs.b + coefs.c),
(x * coefs.d + y * coefs.e + coefs.f) (x * coefs.d + y * coefs.e + coefs.f)

View File

@ -1,4 +1,23 @@
.inputDiv { .inputGroup {
padding: .5em;
margin: .5em;
border: 1px solid;
border-radius: var(--ifm-global-radius);
border-color: var(--ifm-color-emphasis-500);
}
.inputTitle {
border: 0 solid;
border-bottom: 1px solid;
border-color: var(--ifm-color-emphasis-500);
margin-bottom: .5em;
}
.inputElement {
padding-left: .5em; padding-left: .5em;
padding-right: 1em; padding-right: 1em;
} }
.inputElement > p {
margin: 0
}

View File

@ -3,11 +3,14 @@ import { Variation } from './variation'
// hidden-end // hidden-end
export const julia: Variation = (x, y) => { export const julia: Variation = (x, y) => {
const r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); const r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
const theta = Math.atan2(x, y); const theta = Math.atan2(x, y);
const omega = Math.random() > 0.5 ? 0 : Math.PI; const omega = Math.random() > 0.5 ? 0 : Math.PI;
const sqrtR = Math.sqrt(r);
const thetaVal = theta / 2 + omega;
return [ return [
r * Math.cos(theta / 2 + omega), sqrtR * Math.cos(thetaVal),
r * Math.sin(theta / 2 + omega) sqrtR * Math.sin(thetaVal)
] ]
} }

View File

@ -4,11 +4,11 @@
*/ */
import { Coefs } from './coefs'; import { Coefs } from './coefs';
import {VariationBlend} from "./variationBlend";
import { linear } from './linear' import { linear } from './linear'
import { julia } from './julia' import { julia } from './julia'
import { popcorn } from './popcorn' import { popcorn } from './popcorn'
import {pdj, PdjParams} from './pdj' import {pdj, PdjParams} from './pdj'
import {Variation} from "./variation"
export const identityCoefs: Coefs = { export const identityCoefs: Coefs = {
a: 1, b: 0, c: 0, a: 1, b: 0, c: 0,
@ -25,7 +25,7 @@ export const xform1Coefs = {
d: 1.381068, e: -1.381068, f: 0, d: 1.381068, e: -1.381068, f: 0,
} }
export const xform1CoefsPost = identityCoefs; export const xform1CoefsPost = identityCoefs;
export const xform1Variations = [ export const xform1Variations: VariationBlend = [
[1, julia] [1, julia]
] ]
export const xform1Color = 0; export const xform1Color = 0;
@ -39,7 +39,7 @@ export const xform2CoefsPost = {
a: 1, b: 0, c: 0.241352, a: 1, b: 0, c: 0.241352,
d: 0, e: 1, f: 0.271521, d: 0, e: 1, f: 0.271521,
} }
export const xform2Variations = [ export const xform2Variations: VariationBlend = [
[1, linear], [1, linear],
[1, popcorn(xform2Coefs)] [1, popcorn(xform2Coefs)]
] ]
@ -51,7 +51,7 @@ export const xform3Coefs = {
d: 0.740356, e: -1.455964, f: -0.362059, d: 0.740356, e: -1.455964, f: -0.362059,
} }
export const xform3CoefsPost = identityCoefs; export const xform3CoefsPost = identityCoefs;
export const xform3Variations = [ export const xform3Variations: VariationBlend = [
[1, pdj(pdjParams)] [1, pdj(pdjParams)]
]; ];
export const xform3Color = 0.349; export const xform3Color = 0.349;
@ -61,7 +61,7 @@ export const xformFinalCoefs = {
d: 0, e: 2, f: 0 d: 0, e: 2, f: 0
} }
export const xformFinalCoefsPost = identityCoefs; export const xformFinalCoefsPost = identityCoefs;
export const xformFinalVariations = [ export const xformFinalVariations: VariationBlend = [
[1, julia] [1, julia]
] ]
export const xformFinalColor = 0; export const xformFinalColor = 0;

View File

@ -0,0 +1,2 @@
import {Variation} from "./variation";
export type VariationBlend = [number, Variation][];