mirror of
https://github.com/bspeice/speice.io
synced 2025-09-08 15:45:01 -04:00
Flame blending example
This commit is contained in:
142
blog/2024-11-15-playing-with-fire/2-transforms/FlameBlend.tsx
Normal file
142
blog/2024-11-15-playing-with-fire/2-transforms/FlameBlend.tsx
Normal file
@ -0,0 +1,142 @@
|
||||
import {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 {
|
||||
pdjParams,
|
||||
xform1Coefs,
|
||||
xform1Weight,
|
||||
xform2Coefs,
|
||||
xform2Weight,
|
||||
xform3Coefs,
|
||||
xform3Weight
|
||||
} from "../src/params";
|
||||
import {randomChoice} from "../src/randomChoice";
|
||||
import {plotBinary} from "../src/plotBinary"
|
||||
import Canvas from "../src/Canvas"
|
||||
|
||||
import styles from "../src/css/styles.module.css"
|
||||
|
||||
type VariationBlend = {
|
||||
linear: number,
|
||||
julia: number,
|
||||
popcorn: number,
|
||||
pdj: number
|
||||
}
|
||||
|
||||
export default function FlameBlend() {
|
||||
const image = new ImageData(400, 400);
|
||||
const quality = 2;
|
||||
const step = 5000;
|
||||
|
||||
const xform1Default: VariationBlend = {
|
||||
linear: 0,
|
||||
julia: 1,
|
||||
popcorn: 0,
|
||||
pdj: 0,
|
||||
}
|
||||
const [xform1Variations, setXform1Variations] = useState(xform1Default)
|
||||
|
||||
const xform2Default: VariationBlend = {
|
||||
linear: 1,
|
||||
julia: 0,
|
||||
popcorn: 1,
|
||||
pdj: 0
|
||||
}
|
||||
const [xform2Variations, setXform2Variations] = useState(xform2Default)
|
||||
|
||||
const xform3Default: VariationBlend = {
|
||||
linear: 0,
|
||||
julia: 0,
|
||||
popcorn: 0,
|
||||
pdj: 1
|
||||
}
|
||||
const [xform3Variations, setXform3Variations] = useState(xform3Default)
|
||||
|
||||
function buildTransform(coefs: Coefs, variations: VariationBlend): Transform {
|
||||
return (x: number, y: number) => {
|
||||
const [varX, varY] = applyCoefs(x, y, coefs);
|
||||
const varFunctions: [number, Variation][] = [
|
||||
[variations.linear, linear],
|
||||
[variations.julia, julia],
|
||||
[variations.popcorn, popcorn(coefs)],
|
||||
[variations.pdj, pdj(pdjParams)]
|
||||
]
|
||||
|
||||
return blend(varX, varY, varFunctions);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 (
|
||||
<>
|
||||
<Canvas
|
||||
width={image.width}
|
||||
height={image.height}
|
||||
painter={chaosGame()}/>
|
||||
<div style={{paddingTop: '1em', display: 'grid', gridTemplateColumns: 'auto auto auto auto'}}>
|
||||
{variationEditor("Transform 1", xform1Variations, setXform1Variations)}
|
||||
{variationEditor("Transform 2", xform2Variations, setXform2Variations)}
|
||||
{variationEditor("Transform 3", xform3Variations, setXform3Variations)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
14
blog/2024-11-15-playing-with-fire/2-transforms/blend.ts
Normal file
14
blog/2024-11-15-playing-with-fire/2-transforms/blend.ts
Normal file
@ -0,0 +1,14 @@
|
||||
// hidden-start
|
||||
import { Variation } from "../src/variation"
|
||||
// hidden-end
|
||||
export function blend(x: number, y: number, variations: [number, Variation][]): [number, number] {
|
||||
let [finalX, finalY] = [0, 0];
|
||||
|
||||
for (const [weight, variation] of variations) {
|
||||
const [varX, varY] = variation(x, y);
|
||||
finalX += weight * varX;
|
||||
finalY += weight * varY;
|
||||
}
|
||||
|
||||
return [finalX, finalY];
|
||||
}
|
@ -15,6 +15,8 @@ This blog post uses a set of reference parameters ([available here](../params.fl
|
||||
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.
|
||||
|
||||
TODO: Include the reference image here
|
||||
|
||||
## Transforms and variations
|
||||
|
||||
import CodeBlock from '@theme/CodeBlock'
|
||||
@ -120,8 +122,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."
|
||||
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:
|
||||
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
|
||||
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)
|
||||
@ -132,3 +135,7 @@ The formula looks intimidating, but it's not hard to implement:
|
||||
TODO: Blending implementation?
|
||||
|
||||
And with that in place, we have enough to render a first full fractal flame:
|
||||
|
||||
import FlameBlend from "./FlameBlend";
|
||||
|
||||
<FlameBlend/>
|
Reference in New Issue
Block a user