You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
4.2 KiB
147 lines
4.2 KiB
<!--
|
|
Simulate a chaos game on an iterated function set (IFS)
|
|
and plot the result on a canvas of given dimensions.
|
|
-->
|
|
|
|
<script>
|
|
import { onMount, onDestroy, tick, afterUpdate } from 'svelte';
|
|
import { Matrix } from 'ml-matrix';
|
|
import { choose } from './util.js';
|
|
|
|
/** Width of the canvas on which the chaos figure is rendered. */
|
|
export let width = 500;
|
|
|
|
/** Height of the canvas on which the chaos figure is rendered. */
|
|
export let height = 500;
|
|
|
|
/** Total number of iterations to perform. */
|
|
export let totalIterations = 100000;
|
|
|
|
/** Number of iterations to perform in one frame. */
|
|
export let iterationsPerFrame = 1000;
|
|
|
|
/**
|
|
* Iterated function set to use for rendering the figure as a list of
|
|
* pairs, each containing the weight of the function and the function
|
|
* as a 3×3 matrix.
|
|
*/
|
|
export let ifs = [[1, Matrix.identity(3)]];
|
|
|
|
/** Initial point for the iteration, in homogeneous coordinates. */
|
|
export let start = Matrix.columnVector([0, 0, 1]);
|
|
|
|
// Canvas and drawing context on which the figure is rendered
|
|
let canvas;
|
|
let ctx;
|
|
|
|
// Handle to the next scheduled frame
|
|
let nextFrame = null;
|
|
|
|
/**
|
|
* Perform random chaos iteration on the given point, leaving traces
|
|
* behind as the iteration goes.
|
|
*
|
|
* @param point Starting point for the iteration.
|
|
* @param ifs Iterated function set to use.
|
|
* @param iterations Number of iterations to perform.
|
|
*/
|
|
const randomIterate = (point, ifs, iterations) =>
|
|
{
|
|
// Only perform a limited amount of iterations and
|
|
// defer the remaining ones to the next frame
|
|
for (let i = 0; i < iterationsPerFrame; ++i)
|
|
{
|
|
const [_, matrix] = choose(ifs);
|
|
point = matrix.mmul(point);
|
|
|
|
const x = Math.floor(point.get(0, 0));
|
|
const y = Math.floor(point.get(1, 0));
|
|
|
|
if (x >= 0 && x < width && y >= 0 && y < height)
|
|
{
|
|
ctx.fillRect(x, y, 1, 1);
|
|
}
|
|
}
|
|
|
|
if (iterations > iterationsPerFrame)
|
|
{
|
|
nextFrame = window.requestAnimationFrame(() => randomIterate(
|
|
point, ifs,
|
|
iterations - iterationsPerFrame
|
|
));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Perform deterministic chaos iteration on each of the given points,
|
|
* leaving traces behind as the iteration goes.
|
|
*
|
|
* @param points Seed points, modified in-place.
|
|
* @param index Index from which to iterate in the points array.
|
|
* @param ifs Iterated function set to use.
|
|
* @param iterations Number of iterations to perform.
|
|
*/
|
|
const deterministicIterate = (points, index, ifs, iterations) =>
|
|
{
|
|
let i = index;
|
|
|
|
for (
|
|
let frameIterations = 0;
|
|
frameIterations < iterationsPerFrame;
|
|
frameIterations += ifs.length,
|
|
i = (i + ifs.length) % points.length
|
|
)
|
|
{
|
|
points.splice(
|
|
i, 1,
|
|
...ifs.map(([_, matrix]) =>
|
|
{
|
|
const nextPoint = matrix.mmul(points[i]);
|
|
const x = Math.floor(nextPoint.get(0, 0));
|
|
const y = Math.floor(nextPoint.get(1, 0));
|
|
|
|
if (x >= 0 && x < width && y >= 0 && y < height)
|
|
{
|
|
ctx.fillRect(x, y, 1, 1);
|
|
}
|
|
|
|
return nextPoint;
|
|
})
|
|
);
|
|
}
|
|
|
|
if (iterations > iterationsPerFrame)
|
|
{
|
|
nextFrame = window.requestAnimationFrame(() => deterministicIterate(
|
|
points, i, ifs,
|
|
iterations - iterationsPerFrame
|
|
));
|
|
}
|
|
};
|
|
|
|
/** Interrupt the iteration process. */
|
|
const stopLoop = () =>
|
|
{
|
|
window.cancelAnimationFrame(nextFrame);
|
|
};
|
|
|
|
$: {
|
|
stopLoop();
|
|
|
|
if (ctx && ifs.length)
|
|
{
|
|
ctx.clearRect(0, 0, width, height);
|
|
ctx.fillStyle = 'black';
|
|
deterministicIterate([start], 0, ifs, totalIterations);
|
|
}
|
|
}
|
|
|
|
onMount(() => ctx = canvas.getContext('2d'));
|
|
onDestroy(() => stopLoop());
|
|
</script>
|
|
|
|
<canvas
|
|
bind:this={canvas}
|
|
draggable=false
|
|
width={width} height={height}
|
|
/>
|
|
|