- Jump To … +
  ./source/core/animation-loop.js ./source/core/display-cycle.js ./source/core/document-root-elements.js ./source/core/document.js ./source/core/events.js ./source/core/init.js ./source/core/library.js ./source/core/random-seed.js ./source/core/shared-vars.js ./source/core/snippets.js ./source/core/system-flags.js ./source/core/user-interaction.js ./source/core/utilities.js ./source/factory/action.js ./source/factory/anchor.js ./source/factory/animation.js ./source/factory/array-pool.js ./source/factory/bezier.js ./source/factory/block.js ./source/factory/canvas.js ./source/factory/cell-fragment.js ./source/factory/cell.js ./source/factory/cog.js ./source/factory/color.js ./source/factory/conic-gradient.js ./source/factory/coordinate.js ./source/factory/crescent.js ./source/factory/drag-zone.js ./source/factory/element.js ./source/factory/emitter.js ./source/factory/filter-engine-bluenoise-data.js ./source/factory/filter-engine.js ./source/factory/filter.js ./source/factory/font-attributes.js ./source/factory/gradient.js ./source/factory/grid.js ./source/factory/group.js ./source/factory/image-asset.js ./source/factory/keyboard-zone.js ./source/factory/line-spiral.js ./source/factory/line.js ./source/factory/loom.js ./source/factory/mesh.js ./source/factory/net.js ./source/factory/noise-asset.js ./source/factory/observe-update.js ./source/factory/oval.js ./source/factory/palette.js ./source/factory/particle-force.js ./source/factory/particle-history.js ./source/factory/particle-spring.js ./source/factory/particle-world.js ./source/factory/particle.js ./source/factory/pattern.js ./source/factory/phrase.js ./source/factory/picture.js ./source/factory/polygon.js ./source/factory/polyline.js ./source/factory/quadratic.js ./source/factory/quaternion.js ./source/factory/radial-gradient.js ./source/factory/raw-asset.js ./source/factory/reaction-diffusion-asset.js ./source/factory/rectangle.js ./source/factory/render-animation.js ./source/factory/shape.js ./source/factory/spiral.js ./source/factory/sprite-asset.js ./source/factory/stack.js ./source/factory/star.js ./source/factory/state.js ./source/factory/tetragon.js ./source/factory/ticker.js ./source/factory/tracer.js ./source/factory/tween.js ./source/factory/unstacked-element.js ./source/factory/vector.js ./source/factory/video-asset.js ./source/factory/wheel.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/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/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/shape-path-calculation.js ./source/mixin/styles.js ./source/mixin/tween.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

- §

  #### Demos:

  - Canvas-053 - Display output from a Reaction-Diffusion asset
- §

  #### Imports

      import { constructors, entity } from '../core/library.js';

      import { seededRandomNumberGenerator } from '../core/random-seed.js';

      import { constrain, doCreate, mergeOver, λnull, λthis, Ωempty } from '../core/utilities.js';

      import { releaseCell, requestCell } from './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';

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

- §

  #### RdAsset constructor

      const RdAsset = function (items = Ωempty) {

          this.makeName(items.name);
          this.register();

          this.installElement(this.name);

          this.subscribers = [];

          this.set(this.defs);
          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

  - base
  - asset
  - assetAdvancedFunctionality
  - pattern

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

- §

  #### RdAsset attributes

  - Attributes defined in the base mixin: name.
  - Attributes defined in the asset mixin: source, subscribers.
  - Attributes defined in the assetAdvancedFunctionality mixin: color, monochromeStart, monochromeRange, gradientStart, gradientEnd, hueStart, hueRange, saturation, luminosity.
  - Attributes defined in the pattern mixin: repeat, patternMatrix, matrixA, matrixB, matrixC, matrixD, matrixE, matrixF.

      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 = λthis;

- §

  #### Kill management

  No additional kill functionality required

- §

  #### Get, Set, deltaSet

      const S = P.setters;

- §

  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;
              }
          }
      };

- §

  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],
                          cell = requestCell();

                      const {engine:cellEngine, element:cellElement} = cell;

                      cellElement.width = width;
                      cellElement.height = height;

                      ent.simpleStamp(cell, {
                          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(cell);
                  }
                  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;