scrawl-canvas
Version:
Version 8.9.4 - 19 Nov 2022
759 lines (660 loc) • 21.9 kB
JavaScript
// # Demo Canvas 053
// Display output from a Reaction-Diffusion asset
// [Run code](../../demo/canvas-053.html)
import {
addNativeListener,
library as L,
makeBlock,
makePattern,
makeReactionDiffusionAsset,
makeRender,
makeSpiral,
makeWheel,
observeAndUpdate,
} from '../source/scrawl.js'
import { reportSpeed } from './utilities.js';
// #### Scene setup
let canvas = L.artefact.mycanvas;
// Magic numbers
const canvasDimension = 400,
assetDimension = 80;
// Make the canvas responsive
// canvas.set({
// fit: 'cover',
// checkForResize: true,
// ignoreCanvasCssDimensions: true,
// }).setBase({
// dimensions: [canvasDimension, canvasDimension],
// });
// Build and display the reaction-diffusion asset
const colorStops = {
'stepped-grays': [
[0, '#333'],
[199, '#333'],
[200, '#666'],
[399, '#666'],
[400, '#999'],
[599, '#999'],
[600, '#ccc'],
[799, '#ccc'],
[800, '#fff'],
[999, '#fff'],
],
'red-gradient': [
[0, 'hsl(0 100% 40%)'],
[999, 'hsl(0 100% 100%)'],
],
'red-blue': [
[0, 'rgb(255 0 0)'],
[999, 'rgb(0 0 255)'],
],
'hue-gradient': [
[0, 'hwb(120 10% 10%)'],
[999, 'hwb(240 10% 10%)'],
],
'monochrome': [
[0, 'black'],
[999, 'white'],
],
}
const bespokeEasings = {
'user-steps': (val) => {
if (val < 0.2) return 0.1;
if (val < 0.4) return 0.3;
if (val < 0.6) return 0.5;
if (val < 0.8) return 0.7;
return 0.9;
},
'user-repeat': (val) => (val * 4) % 1,
};
const reactionAsset = makeReactionDiffusionAsset({
name: 'reaction-diffusion-asset',
width: assetDimension,
height: assetDimension,
// @ts-expect-error
colors: colorStops['monochrome'],
});
// Test that the RD asset output is always tileable by displaying it via a Pattern style
const myPattern = makePattern({
name: 'rd-pattern',
asset: reactionAsset,
});
// Display the RD asset output in a block
// + We could also display it in a Picture entity, or use it in a Filter object
makeBlock({
name: 'rd-block',
dimensions: ['100%', '100%'],
fillStyle: 'rd-pattern',
});
// Entitys that can be used by the RD asset for its initialization
// + Start positions and dimensions need to be in absolute coordinates/lengths because the RD asset will `simpleStamp` the entity onto a temporary canvas and populate the scene with the alpha channel values from the resulting ImageData object.
// ```
// ent.simpleStamp(cell, {
// fillStyle: 'white',
// strokeStyle: 'white',
// });
// ```
makeBlock({
name: 'initial-square',
dimensions: [30, 30],
visibility: false,
});
makeWheel({
name: 'initial-circle',
start: [4, 4],
radius: 15,
lineWidth: 6,
method: 'draw',
visibility: false,
});
makeSpiral({
name: 'initial-spiral',
loops: 5,
loopIncrement: 8,
drawFromLoop: 0,
lineWidth: 3,
method: 'draw',
visibility: false,
});
// #### Scene animation
// Function to display frames-per-second data, and other information relevant to the demo
const report = reportSpeed('#reportmessage', function () {
// @ts-expect-error
const {currentGeneration, maxGenerations, initialRandomSeedLevel, diffusionRateA, diffusionRateB, feedRate, killRate, width, height} = reactionAsset;
const mx = myPattern.patternMatrix;
const matrixVals = (mx) ?
`[${mx.a}, ${mx.b}, ${mx.c}, ${mx.d}, ${mx.e}, ${mx.f}]` :
'[]';
// @ts-expect-error
return ` Generation: ${reactionAsset.currentGeneration} of ${reactionAsset.maxGenerations}\n Initial random seed level: ${reactionAsset.initialRandomSeedLevel}\n Diffusion rates - A: ${reactionAsset.diffusionRateA}, B: ${reactionAsset.diffusionRateB}\n Feed rate: ${reactionAsset.feedRate}; Kill rate: ${reactionAsset.killRate}\n Asset dimensions - width: ${reactionAsset.width}, height: ${reactionAsset.height}\n Pattern matrix: ${matrixVals}`;
});
// Create the Display cycle animation
makeRender({
name: 'demo-animation',
target: canvas,
commence: () => reactionAsset.update(),
afterShow: report,
});
// #### User interaction
// Setup form observer functionality
addNativeListener('input', (e) => {
e.preventDefault();
if (e && e.target) {
let val = e.target.value;
switch (val) {
case 'square':
reactionAsset.set({
initialSettingPreference: 'entity',
initialSettingEntity: 'initial-square',
});
break;
case 'circle':
reactionAsset.set({
initialSettingPreference: 'entity',
initialSettingEntity: 'initial-circle',
});
break;
case 'spiral':
reactionAsset.set({
initialSettingPreference: 'entity',
initialSettingEntity: 'initial-spiral',
});
break;
default:
reactionAsset.set({
initialSettingPreference: 'random',
});
}
}
}, '#initialSettingPreference');
addNativeListener(['input', 'change'], (e) => {
e.preventDefault();
if (e && e.target) {
let val = e.target.value;
reactionAsset.set({
colors: colorStops[val],
})
}
}, '#colorStops');
addNativeListener(['input', 'change'], (e) => {
e.preventDefault();
let val = e.target.value;
if (['user-steps', 'user-repeat'].includes(val)) {
reactionAsset.set({
easing: bespokeEasings[val],
});
}
else {
reactionAsset.set({
easing: val,
});
}
}, '#easing');
observeAndUpdate({
event: ['input', 'change'],
origin: '.controlItem',
target: myPattern,
useNativeListener: true,
preventDefault: true,
updates: {
matrixA: ['matrixA', 'float'],
matrixB: ['matrixB', 'float'],
matrixC: ['matrixC', 'float'],
matrixD: ['matrixD', 'float'],
matrixE: ['matrixE', 'round'],
matrixF: ['matrixF', 'round'],
},
});
observeAndUpdate({
event: ['input', 'change'],
origin: '.controlItem',
target: reactionAsset,
useNativeListener: true,
preventDefault: true,
updates: {
initialRandomSeedLevel: ['initialRandomSeedLevel', 'float'],
diffusionRateA: ['diffusionRateA', 'float'],
diffusionRateB: ['diffusionRateB', 'float'],
feedRate: ['feedRate', 'float'],
killRate: ['killRate', 'float'],
drawEvery: ['drawEvery', 'round'],
maxGenerations: ['maxGenerations', 'round'],
canvasWidth: ['width', 'round'],
canvasHeight: ['height', 'round'],
randomEngineSeed: ['randomEngineSeed', 'raw'],
paletteStart: ['paletteStart', 'round'],
paletteEnd: ['paletteEnd', 'round'],
precision: ['precision', 'round'],
cyclePalette: ['cyclePalette', 'boolean'],
colorSpace: ['colorSpace', 'raw'],
returnColorAs: ['returnColorAs', 'raw'],
},
});
// This listener updates the RD asset with the selected preset value. Most of the following code is to update the the Demo's user interface
addNativeListener(['change', 'input'], (e) => {
e.preventDefault();
if (e && e.target) {
let val = e.target.value;
reactionAsset.set({
preset: val,
});
switch (val) {
case 'negativeBubbles':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.098;
// @ts-expect-error
qs_killRate.value = 0.0555;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.05;
break;
case 'positiveBubbles':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.098;
// @ts-expect-error
qs_killRate.value = 0.057;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.1;
break;
case 'precriticalBubbles':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.082;
// @ts-expect-error
qs_killRate.value = 0.059;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.08;
break;
case 'wormsAndLoops':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.082;
// @ts-expect-error
qs_killRate.value = 0.06;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.08;
break;
case 'stableSolitons':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.074;
// @ts-expect-error
qs_killRate.value = 0.064;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.15;
break;
case 'uSkateWorld':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.062;
// @ts-expect-error
qs_killRate.value = 0.0609;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
break;
case 'worms':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.058;
// @ts-expect-error
qs_killRate.value = 0.065;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.1;
break;
case 'wormsJoinIntoMazes':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.046;
// @ts-expect-error
qs_killRate.value = 0.063;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
break;
case 'negatons':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.046;
// @ts-expect-error
qs_killRate.value = 0.0594;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
break;
case 'turingPatterns':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.042;
// @ts-expect-error
qs_killRate.value = 0.059;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
break;
case 'chaosToTuringNegatons':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.039;
// @ts-expect-error
qs_killRate.value = 0.058;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
break;
case 'fingerprints':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.037;
// @ts-expect-error
qs_killRate.value = 0.06;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
break;
case 'chaosWithNegatons':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.0353;
// @ts-expect-error
qs_killRate.value = 0.0566;
// @ts-expect-error
qs_maxGenerations.value = 0;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
break;
case 'spotsAndWorms':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.034;
// @ts-expect-error
qs_killRate.value = 0.0618;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
break;
case 'selfReplicatingSpots':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.03;
// @ts-expect-error
qs_killRate.value = 0.063;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
break;
case 'superResonantMazes':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.03;
// @ts-expect-error
qs_killRate.value = 0.0565;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
break;
case 'mazes':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.029;
// @ts-expect-error
qs_killRate.value = 0.057;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
break;
case 'mazesWithSomeChaos':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.026;
// @ts-expect-error
qs_killRate.value = 0.055;
// @ts-expect-error
qs_maxGenerations.value = 0;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
break;
case 'chaos':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.026;
// @ts-expect-error
qs_killRate.value = 0.051;
// @ts-expect-error
qs_maxGenerations.value = 0;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.009;
break;
case 'warringMicrobes':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.022;
// @ts-expect-error
qs_killRate.value = 0.059;
// @ts-expect-error
qs_maxGenerations.value = 0;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
break;
case 'spotsAndLoops':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.018;
// @ts-expect-error
qs_killRate.value = 0.051;
// @ts-expect-error
qs_maxGenerations.value = 0;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
break;
case 'movingSpots':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.014;
// @ts-expect-error
qs_killRate.value = 0.054;
// @ts-expect-error
qs_maxGenerations.value = 0;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
break;
case 'waves':
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.014;
// @ts-expect-error
qs_killRate.value = 0.045;
// @ts-expect-error
qs_maxGenerations.value = 0;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
break;
default:
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.054;
// @ts-expect-error
qs_killRate.value = 0.062;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.0045;
}
}
}, '#presets');
// Setup form
const qs_presets = document.querySelector('#presets'),
qs_initialRandomSeedLevel = document.querySelector('#initialRandomSeedLevel'),
qs_diffusionRateA = document.querySelector('#diffusionRateA'),
qs_diffusionRateB = document.querySelector('#diffusionRateB'),
qs_feedRate = document.querySelector('#feedRate'),
qs_killRate = document.querySelector('#killRate'),
qs_drawEvery = document.querySelector('#drawEvery'),
qs_maxGenerations = document.querySelector('#maxGenerations'),
qs_canvasWidth = document.querySelector('#canvasWidth'),
qs_canvasHeight = document.querySelector('#canvasHeight'),
qs_matrixA = document.querySelector('#matrixA'),
qs_matrixB = document.querySelector('#matrixB'),
qs_matrixC = document.querySelector('#matrixC'),
qs_matrixD = document.querySelector('#matrixD'),
qs_matrixE = document.querySelector('#matrixE'),
qs_matrixF = document.querySelector('#matrixF'),
qs_initialSettingPreference = document.querySelector('#initialSettingPreference'),
qs_randomEngineSeed = document.querySelector('#randomEngineSeed'),
qs_paletteStart = document.querySelector('#paletteStart'),
qs_paletteEnd = document.querySelector('#paletteEnd'),
qs_precision = document.querySelector('#precision'),
qs_easing = document.querySelector('#easing'),
qs_colorStops = document.querySelector('#colorStops'),
qs_cyclePalette = document.querySelector('#cyclePalette'),
qs_colorSpace = document.querySelector('#colorSpace'),
qs_returnColorAs = document.querySelector('#returnColorAs');
// @ts-expect-error
qs_presets.options.selectedIndex = 0;
// @ts-expect-error
qs_initialRandomSeedLevel.value = 0.1;
// @ts-expect-error
qs_diffusionRateA.value = 0.2097;
// @ts-expect-error
qs_diffusionRateB.value = 0.105;
// @ts-expect-error
qs_feedRate.value = 0.054;
// @ts-expect-error
qs_killRate.value = 0.062;
// @ts-expect-error
qs_drawEvery.value = 10;
// @ts-expect-error
qs_maxGenerations.value = 4000;
// @ts-expect-error
qs_canvasWidth.value = assetDimension;
// @ts-expect-error
qs_canvasHeight.value = assetDimension;
// @ts-expect-error
qs_matrixA.value = 1;
// @ts-expect-error
qs_matrixB.value = 0;
// @ts-expect-error
qs_matrixC.value = 0;
// @ts-expect-error
qs_matrixD.value = 1;
// @ts-expect-error
qs_matrixE.value = 0;
// @ts-expect-error
qs_matrixF.value = 0;
// @ts-expect-error
qs_initialSettingPreference.selectedIndex = 0;
// @ts-expect-error
qs_randomEngineSeed.value = reactionAsset.get('randomEngineSeed');
// @ts-expect-error
qs_paletteStart.value = 0;
// @ts-expect-error
qs_paletteEnd.value = 999;
// @ts-expect-error
qs_precision.value = 1;
// @ts-expect-error
qs_colorStops.options.selectedIndex = 0;
// @ts-expect-error
qs_easing.options.selectedIndex = 0;
// @ts-expect-error
qs_cyclePalette.options.selectedIndex = 0;
// @ts-expect-error
qs_colorSpace.options.selectedIndex = 0;
// @ts-expect-error
qs_returnColorAs.options.selectedIndex = 0;
// #### Development and testing
console.log(L);