• Jump To … +
    ./source/asset-management/image-asset.js ./source/asset-management/noise-asset.js ./source/asset-management/raw-asset.js ./source/asset-management/reaction-diffusion-asset.js ./source/asset-management/sprite-asset.js ./source/asset-management/video-asset.js ./source/core/animation-loop.js ./source/core/display-cycle.js ./source/core/document.js ./source/core/events.js ./source/core/init.js ./source/core/library.js ./source/core/snippets.js ./source/core/user-interaction.js ./source/factory/action.js ./source/factory/anchor.js ./source/factory/animation.js ./source/factory/bezier.js ./source/factory/block.js ./source/factory/button.js ./source/factory/canvas.js ./source/factory/cell.js ./source/factory/cog.js ./source/factory/color.js ./source/factory/conic-gradient.js ./source/factory/crescent.js ./source/factory/element.js ./source/factory/emitter.js ./source/factory/enhanced-label.js ./source/factory/filter.js ./source/factory/gradient.js ./source/factory/grid.js ./source/factory/group.js ./source/factory/label.js ./source/factory/line-spiral.js ./source/factory/line.js ./source/factory/loom.js ./source/factory/mesh.js ./source/factory/net.js ./source/factory/oval.js ./source/factory/particle-force.js ./source/factory/particle-spring.js ./source/factory/particle-world.js ./source/factory/particle.js ./source/factory/pattern.js ./source/factory/picture.js ./source/factory/polygon.js ./source/factory/polyline.js ./source/factory/quadratic.js ./source/factory/radial-gradient.js ./source/factory/rectangle.js ./source/factory/render-animation.js ./source/factory/shape.js ./source/factory/spiral.js ./source/factory/stack.js ./source/factory/star.js ./source/factory/tetragon.js ./source/factory/ticker.js ./source/factory/tracer.js ./source/factory/tween.js ./source/factory/unstacked-element.js ./source/factory/wheel.js ./source/helper/array-pool.js ./source/helper/color-engine.js ./source/helper/document-root-elements.js ./source/helper/filter-engine-bluenoise-data.js ./source/helper/filter-engine.js ./source/helper/random-seed.js ./source/helper/shape-path-calculation.js ./source/helper/shared-vars.js ./source/helper/system-flags.js ./source/helper/utilities.js ./source/helper/workstore.js ./source/mixin/anchor.js ./source/mixin/asset-advanced-functionality.js ./source/mixin/asset-consumer.js ./source/mixin/asset.js ./source/mixin/base.js ./source/mixin/button.js ./source/mixin/cascade.js ./source/mixin/cell-key-functions.js ./source/mixin/delta.js ./source/mixin/display-shape.js ./source/mixin/dom.js ./source/mixin/entity.js ./source/mixin/filter.js ./source/mixin/hidden-dom-elements.js ./source/mixin/mimic.js ./source/mixin/path.js ./source/mixin/pattern.js ./source/mixin/pivot.js ./source/mixin/position.js ./source/mixin/shape-basic.js ./source/mixin/shape-curve.js ./source/mixin/styles.js ./source/mixin/text.js ./source/mixin/tween.js ./source/scrawl.js ./source/untracked-factory/cell-fragment.js ./source/untracked-factory/coordinate.js ./source/untracked-factory/drag-zone.js ./source/untracked-factory/keyboard-zone.js ./source/untracked-factory/observe-update.js ./source/untracked-factory/palette.js ./source/untracked-factory/particle-history.js ./source/untracked-factory/quaternion.js ./source/untracked-factory/state.js ./source/untracked-factory/text-style.js ./source/untracked-factory/vector.js
  • §

    RdAsset factory

    Reaction-diffusion systems are mathematical models which can be used to explore various physical phenomena. The Scrawl-canvas version is an interpretation of a two-component activator-inhibitor system.

    This factory was inspired by Jason Webb’s Reaction-Diffusion Playground

  • §

    Imports

    import { constructors, entity } from '../core/library.js';
    
    import { seededRandomNumberGenerator } from '../helper/random-seed.js';
    
    import { constrain, doCreate, mergeOver, λnull, λcloneError, Ωempty } from '../helper/utilities.js';
    
    import { releaseCell, requestCell } from '../untracked-factory/cell-fragment.js';
    
    import baseMix from '../mixin/base.js';
    import assetMix from '../mixin/asset.js';
    import assetAdvancedMix from '../mixin/asset-advanced-functionality.js';
    import patternMix from '../mixin/pattern.js';
  • §

    Shared constants

    import { _floor, ASSET, DEFAULT_SEED, ENTITY, RANDOM, T_RD_ASSET, WHITE, ZERO_STR } from '../helper/shared-vars.js';
  • §

    Local constants

    const RD_SETTINGS_PREF_VALS = ['random', 'entity'];
  • §

    RdAsset constructor

    const RdAsset = function (items = Ωempty) {
    
        this.makeName(items.name);
        this.register();
    
        this.installElement(this.name);
    
        this.subscribers = [];
    
        this.set(this.defs);
    
        this.currentSource = null;
        this.initialSettingEntity = null;
    
        this.set(items);
    
        if (items.subscribe) this.subscribers.push(items.subscribe);
    
        this.currentGeneration = 0;
        this.dataArrays = [];
    
        this.dirtyScene = true;
        this.dirtyOutput = true;
    
        return this;
    };
  • §

    RdAsset prototype

    const P = RdAsset.prototype = doCreate();
    P.type = T_RD_ASSET;
    P.lib = ASSET;
    P.isArtefact = false;
    P.isAsset = true;
  • §

    Mixins

    baseMix(P);
    assetMix(P);
    assetAdvancedMix(P);
    patternMix(P);
  • §

    RdAsset attributes

    const defaultAttributes = {
  • §

    The offscreen canvas dimensions, within which the reaction-diffusion scene will be generated, is set using the width and height attributes. These take Number values.

        width: 300,
        height: 150,
  • §

    The reaction-diffusion scene results will differ based on the initial settings for the feedRate, killRate, diffusionRateA and diffusionRateB attributes. These attributes are all float numbers in restricted ranges between 0.0 and 1.0

    • In general, the diffusion rate of the A reagent should be roughly double that of the B reagent
    • Given the above, the relative settings of the feed and kill rates will determine the stability and eventual outcome of the scene. To find more interesting values, check out the online interactiveReaction-Diffusion Playground
        diffusionRateA: 0.2097,
        diffusionRateB: 0.105,
        feedRate: 0.054,
        killRate: 0.062,
  • §

    The Scrawl-canvas implementation allows users to initialize the scene using pseudo-random values scattered across the canvas, or alternatively use a regular Scrawl-canvas entity to supply a more regular initial shape.

    • Demo Canvas-053 includes Block, Wheel and Spiral entitys which can be used for this purpose

    initialSettingPreference - String - either ‘random’ (default) or ‘entity’

        initialSettingPreference: RANDOM,
  • §

    randomEngineSeed - String acting as the random engine’s seed value. different String values will create different scene outputs, all other attributes not changing.

        randomEngineSeed: DEFAULT_SEED,
  • §

    initialRandomSeedLevel - Number between 0.0 and 1.0 representing the density of reagent A across the scene’s dimensions

        initialRandomSeedLevel: 0.0045,
  • §

    initialSettingEntity - String name of the entity to be used for setting up the scene

        initialSettingEntity: null,
  • §

    drawEvery - positive integer Number - the speed of evolution is affected by how often we draw the current state of the scene onto the canvas.

        drawEvery: 10,
  • §

    maxGenerations - positive integer Number - some interesting outputs can be found as a given scene develops over time. We can halt development to allow these outputs to be used by Picture entitys and Pattern styles by setting this attribute to the appropriate value

    • Halting the calculations when the scene reaches a desired output is also more efficient - allowing a scene to develop forever may cause the entire animation to slow down
    • To allow the scene to develop forever (for instance, to show a more chaotic output) set te value of this attribute to 0
        maxGenerations: 4000,
    };
    P.defs = mergeOver(P.defs, defaultAttributes);
    
    delete P.defs.source;
    delete P.defs.sourceLoaded;
  • §

    Packet management

    This functionality is disabled for Cell objects

    P.stringifyFunction = λnull;
    P.processPacketOut = λnull;
    P.finalizePacketOut = λnull;
    P.saveAsPacket = function () {
    
        return `[${this.name}, ${this.type}, ${this.lib}, {}]`
    };
  • §

    Clone management

    P.clone = λcloneError;
  • §

    Kill management

    No additional kill functionality required

  • §

    Get, Set, deltaSet

    const S = P.setters,
        G = P.getters;
  • §

    subscribers - we disable the ability to set the subscribers Array directly. Picture entitys and Pattern styles will manage their subscription to the asset using their subscribe() and unsubscribe() functions. Filters will check for updates every time they run

    S.subscribers = λnull;
  • §

    Setting any of the following attributes will cause the scene to re-initialize itself

    S.width = function (item) {
    
        if (item.toFixed) {
    
            this.width = item;
            this.sourceNaturalWidth = item;
            this.dirtyScene = true;
        }
    };
    
    S.height = function (item) {
    
        if (item.toFixed) {
    
            this.height = item;
            this.sourceNaturalHeight = item;
            this.dirtyScene = true;
        }
    };
    
    S.initialRandomSeedLevel = function (item) {
    
        if (item.toFixed) {
    
            this.initialRandomSeedLevel = item;
            this.dirtyScene = true;
        }
    };
    
    S.diffusionRateA = function (item) {
    
        if (item.toFixed) {
    
            this.diffusionRateA = item;
            this.dirtyScene = true;
        }
    };
    
    S.diffusionRateB = function (item) {
    
        if (item.toFixed) {
    
            this.diffusionRateB = item;
            this.dirtyScene = true;
        }
    };
    
    S.feedRate = function (item) {
    
        if (item.toFixed) {
    
            this.feedRate = item;
            this.dirtyScene = true;
        }
    };
    
    S.killRate = function (item) {
    
        if (item.toFixed) {
    
            this.killRate = item;
            this.dirtyScene = true;
        }
    };
    
    S.drawEvery = function (item) {
    
        if (item.toFixed) {
    
            this.drawEvery = item;
            this.dirtyScene = true;
        }
    };
    
    S.maxGenerations = function (item) {
    
        if (item.toFixed) {
    
            this.maxGenerations = item;
            this.dirtyScene = true;
        }
    };
    
    S.initialSettingPreference = function (item) {
    
        if (item.substring && RD_SETTINGS_PREF_VALS.includes(item)) {
    
            this.initialSettingPreference = item;
            this.dirtyScene = true;
        }
    };
    
    S.randomEngineSeed = function (item) {
    
        if (item.substring) {
    
            this.randomEngineSeed = item;
            this.dirtyScene = true;
        }
    };
    
    S.initialSettingEntity = function (item) {
    
        if (item) {
    
            if (!item.substring) item = item.name || ZERO_STR;
    
            if (item) {
    
                this.initialSettingEntity = item;
                this.dirtyScene = true;
            }
        }
    };
    
    G.generation = function () {
    
        return this.currentGeneration;
    };
  • §

    This asset comes with a set of predefined effects built in, to make using it easier

    • Reaction-diffusion simulations are very sensitive to their initial conditions and, in most cases, will quickly evolve into all-black or all-white (monochrome) displays
    • These presets pick out some of the more stable initial conditions
    • WARNING! The chaos-related presets will generate strobe-lighting effects which may trigger epileptic fits in people who are sensitive to such displays
    S.preset = function (item) {
    
        if (item.substring) {
    
            switch (item) {
    
                case 'negativeBubbles':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.098;
                    this.killRate = 0.0555;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.05;
                    break;
    
                case 'positiveBubbles':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.098;
                    this.killRate = 0.057;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.1;
                    break;
    
                case 'precriticalBubbles':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.082;
                    this.killRate = 0.059;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.08;
                    break;
    
                case 'wormsAndLoops':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.082;
                    this.killRate = 0.06;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.08;
                    break;
    
                case 'stableSolitons':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.074;
                    this.killRate = 0.064;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.15;
                    break;
    
                case 'uSkateWorld':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.062;
                    this.killRate = 0.0609;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.0045;
                    break;
    
                case 'worms':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.058;
                    this.killRate = 0.065;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.1;
                    break;
    
                case 'wormsJoinIntoMazes':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.046;
                    this.killRate = 0.063;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.0045;
                    break;
    
                case 'negatons':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.046;
                    this.killRate = 0.0594;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.0045;
                    break;
    
                case 'turingPatterns':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.042;
                    this.killRate = 0.059;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.0045;
                    break;
    
                case 'chaosToTuringNegatons':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.039;
                    this.killRate = 0.058;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.0045;
                    break;
    
                case 'fingerprints':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.037;
                    this.killRate = 0.06;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.0045;
                    break;
    
                case 'chaosWithNegatons':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.0353;
                    this.killRate = 0.0566;
                    this.maxGenerations = 0;
                    this.initialRandomSeedLevel = 0.0045;
                    break;
    
                case 'spotsAndWorms':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.034;
                    this.killRate = 0.0618;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.0045;
                    break;
    
                case 'selfReplicatingSpots':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.03;
                    this.killRate = 0.063;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.0045;
                    break;
    
                case 'superResonantMazes':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.03;
                    this.killRate = 0.0565;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.0045;
                    break;
    
                case 'mazes':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.029;
                    this.killRate = 0.057;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.0045;
                    break;
    
                case 'mazesWithSomeChaos':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.026;
                    this.killRate = 0.055;
                    this.maxGenerations = 0;
                    this.initialRandomSeedLevel = 0.0045;
                    break;
    
                case 'chaos':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.026;
                    this.killRate = 0.051;
                    this.maxGenerations = 0;
                    this.initialRandomSeedLevel = 0.009;
                    break;
    
                case 'warringMicrobes':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.022;
                    this.killRate = 0.059;
                    this.maxGenerations = 0;
                    this.initialRandomSeedLevel = 0.0045;
                    break;
    
                case 'spotsAndLoops':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.018;
                    this.killRate = 0.051;
                    this.maxGenerations = 0;
                    this.initialRandomSeedLevel = 0.0045;
                    break;
    
                case 'movingSpots':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.014;
                    this.killRate = 0.054;
                    this.maxGenerations = 0;
                    this.initialRandomSeedLevel = 0.0045;
                    break;
    
                case 'waves':
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.014;
                    this.killRate = 0.045;
                    this.maxGenerations = 0;
                    this.initialRandomSeedLevel = 0.0045;
                    break;
    
                default:
                    this.diffusionRateA = 0.2097;
                    this.diffusionRateB = 0.105;
                    this.feedRate = 0.054;
                    this.killRate = 0.062;
                    this.maxGenerations = 4000;
                    this.initialRandomSeedLevel = 0.0045;
            }
            this.dirtyScene = true;
        }
    };
  • §

    Prototype functions

    update - this function needs to be called once during each Display cycle.

    • Reaction-diffusion systems are, like particle systems, dynamic and time based - the more stable scenes need around 4000 iterations to settle down into their final states.
    P.update = function () {
    
        this.dirtyOutput = true;
    };
  • §

    cleanOutput - internal function called by the notifySubscribers function

    • All reaction-diffusion calculations are coordinated from this function
    • Function also checks to see if scene needs to be (re)initialized - initialization will take place after any of the initial parameters are set to new values
    P.cleanOutput = function (iterations = 0) {
    
        if (this.dirtyScene) this.cleanScene();
    
        if (!this.dirtyScene) {
    
            const { dataArrays, diffusionRateA, diffusionRateB, feedRate, killRate, currentSource, drawEvery, maxGenerations, currentGeneration } = this;
    
            let sourceA, destA, sourceB, destB, a, b, c, cz, da, db;
    
            if (!maxGenerations || currentGeneration < maxGenerations) {
    
                if (currentSource) {
    
                    [destA, sourceA, destB, sourceB] = dataArrays;
                }
                else {
    
                    [sourceA, destA, sourceB, destB] = dataArrays;
                }
    
    
                if (iterations < drawEvery) {
    
                    for (c = 0, cz = sourceA.length; c < cz; c++) {
    
                        a = sourceA[c];
                        b = sourceB[c];
    
                        da = a + diffusionRateA * this.calculateLaplacian(c, sourceA) - a * b * b + feedRate * (1 - a);
                        db = b + diffusionRateB * this.calculateLaplacian(c, sourceB) + a * b * b - (killRate + feedRate) * b;
    
                        destA[c] = constrain(da, 0, 1);
                        destB[c] = constrain(db, 0, 1);
                    }
    
                    this.currentSource = (currentSource) ? 0 : 1;
                    this.currentGeneration = currentGeneration + 1;
                    this.cleanOutput(iterations + 1);
                }
                else this.paintCanvas();
            }
            else if (this.dirtyOutput) this.paintCanvas();
        }
    };
  • §

    cleanScene - internal function called by the cleanOutput function

    • Resets the scene to its initial conditions
    P.cleanScene = function () {
    
        const { element, width, height, dataArrays, initialRandomSeedLevel, initialSettingPreference, randomEngineSeed, initialSettingEntity } = this;
    
        if (width && height) {
    
            if (this.dirtyScene) {
    
                this.dirtyScene = false;
    
                element.width = width;
                element.height = height;
    
                const len = width * height;
    
                dataArrays.length = 0;
  • §

    We use four arrays to contain the current and next state of the scene

                for (let i = 0; i < 4; i++) {
    
                    dataArrays.push(new Float64Array(len))
                }
                this.currentSource = 0;
    
                const [sourceA, destA, sourceB, destB] = dataArrays;
    
                sourceA.fill(1);
                destA.fill(1);
                sourceB.fill(0);
                destB.fill(0);
  • §

    The scene can be set up to include either random values, or we can use Scrawl-canvas entitys to supply the initial state for the second reagent

                if (ENTITY === initialSettingPreference && entity[initialSettingEntity]) {
    
                    const ent = entity[initialSettingEntity],
                        mycell = requestCell();
    
                    const {engine:cellEngine, element:cellElement} = mycell;
    
                    cellElement.width = width;
                    cellElement.height = height;
    
                    ent.simpleStamp(mycell, {
                        fillStyle: WHITE,
                        strokeStyle: WHITE,
                    });
    
                    const initImg = cellEngine.getImageData(0, 0, width, height),
                        initData = initImg.data;
    
                    let counter = 0;
  • §

    get data from alpha channel

                    for (let i = 3, iz = initData.length; i < iz; i += 4) {
    
                        sourceB[counter] = initData[i] / 255;
                        counter++;
                    }
                    releaseCell(mycell);
                }
                else {
  • §

    We use a semi-random generator to supply random values to the scene

    • This ensures that we can discover and recreate particular patterns from a of ‘randomized’ initial state, as the pattern will be tied to the canvas dimensions, the feed and kill rates, the diffusion rates for each reagent, and the String value supplied to the randomEngineSeed attribute.
                    const rndEngine = seededRandomNumberGenerator(randomEngineSeed);
    
                    for (let index = 0; index < len; index++) {
    
                        sourceB[index] = (rndEngine.random() < initialRandomSeedLevel) ? 1 : 0;
                    }
                }
    
                this.currentGeneration = 0;
            }
        }
    };
  • §

    calculateLaplacian - additional internal function invoked by the cleanOutput function

    P.calculateLaplacian = function (index, src) {
    
        const w = this.width;
    
        const row = _floor(index / w),
            rowAbove = this.checkRow(row - 1) * w,
            rowBelow = this.checkRow(row + 1) * w,
            rowHere = row * w,
            col = index - rowHere,
            colLeft = this.checkCol(col - 1),
            colRight = this.checkCol(col + 1);
    
        let res = 0,
            cursor;
  • §

    center

        res += src[index] * -1;
  • §

    topleft

        cursor = rowAbove + colLeft;
        res += src[cursor] * 0.05;
  • §

    topright

        cursor = rowAbove + colRight;
        res += src[cursor] * 0.05;
  • §

    bottomleft

        cursor = rowBelow + colLeft;
        res += src[cursor] * 0.05;
  • §

    bottomright

        cursor = rowBelow + colRight;
        res += src[cursor] * 0.05;
  • §

    top

        cursor = rowAbove + col;
        res += src[cursor] * 0.2;
  • §

    right

        cursor = rowHere + colRight;
        res += src[cursor] * 0.2;
  • §

    left

        cursor = rowHere + colLeft;
        res += src[cursor] * 0.2;
  • §

    bottom

        cursor = rowBelow + col;
        res += src[cursor] * 0.2;
    
        return res;
    };
  • §

    checkRow, checkCol - internal functions invoked by the calculateLaplacian function

    P.checkRow = function (val) {
    
        const h = this.height;
    
        if (val < 0) return h - 1;
        if (val >= h) return 0;
        return val;
    }
    
    P.checkCol = function (val) {
    
        const w = this.width;
    
        if (val < 0) return w - 1;
        if (val >= w) return 0;
        return val;
    }
  • §

    checkOutputValuesExist and getOutputValue are internal variables that must be defined by any asset that makes use of the assetAdvancedFunctionality.js mixin and its paintCanvas function

    P.checkOutputValuesExist = function () {
    
        return (this.dataArrays.length) ? true : false;
    };
    P.getOutputValue = function (index) {
    
        let destA, destB;
    
        const { dataArrays, currentSource } = this;
    
        if (currentSource) {
    
            [destA, , destB, ] = dataArrays;
        }
        else {
    
            [, destA, , destB] = dataArrays;
        }
    
        return (1 + (destA[index] - destB[index])) / 2;
    };
  • §

    Factory

    scrawl.makeReactionDiffusionAsset({
        name: 'my-rd-generator',
        width: 50,
        height: 50,
    });
    
    export const makeReactionDiffusionAsset = function (items) {
    
        if (!items) return false;
        return new RdAsset(items);
    };
    
    constructors.RdAsset = RdAsset;