- 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

- §

  # Rectangle factory

  A factory for generating rectangular shape-based entitys, including round-cornered rectangles

  Path-defined entitys represent a diverse range of shapes rendered onto a DOM <canvas> element using the Canvas API’s Path2D interface. They use the shapeBasic and shapePathCalculation (some also use shapeCurve) mixins to define much of their functionality.

  All path-defined entitys can be positioned, cloned, filtered etc:

  - Positioning functionality for the entity is supplied by the position mixin, while rendering functionality comes from the entity mixin.
  - Dimensions, however, have little meaning for path-defined entitys - their width and height are determined by their SVG path data Strings; use `scale` instead.
  - Path-defined entitys can use CSS color Strings for their fillStyle and strokeStyle values, alongside Gradient, RadialGradient, Color and Pattern objects.
  - They will also accept Filter objects.
  - They can use Anchor objects for user navigation.
  - They can be rendered to the canvas by including them in a Cell object’s Group.
  - They can be animated directly, or using delta animation, or act as the target for Tween animations.
  - Path-defined entitys can be cloned, and killed.
- §

  #### Using path-defined entitys as Scrawl-canvas paths

  A path is a track - straight, or curved, or as complex as required - placed across a container which artefacts can use as a source of their positioning data. We can animate an artifact to move along the path:

  - To enable a path-defined entity to be used as a path by other artefacts, set its `useAsPath` flag to `true`.
  - The artefact can then set its `path` attribute to the path-defined entity’s name-String (or the entity itself), and set its `lockTo` Array values to `"path"`.
  - We position the artefact by setting its `pathPosition` attribute to a float Number value between `0.0 - 1.0`, with `0` being the start of the path, and `1` being its end.
  - Path-defined entitys can use other path-defined entitys as a path.
  - Phrase entitys can use a path to position their text block; they can also use a path to position each letter individually along the path.
  - Artefacts (and letters) can be rotated so that they match the rotation at that point along the path - tangential rotation by setting their `addPathRotation` flag to `true`.
  - Animate an artefact along the path by either using the artefact’s `delta` object, or triggering a Tween to perform the movement.
- §

  #### Demos:

  - Canvas-011 - Shape entity (make, clone, method); drag and drop shape entitys
  - Canvas-012 - Shape entity position; shape entity as a path for other artefacts to follow
  - Canvas-013 - Path-defined entitys: oval, rectangle, line, quadratic, bezier, tetragon, polygon, star, spiral
  - Canvas-014 - Line, Quadratic and Bezier entitys - control lock alternatives
  - Canvas-018 - Phrase entity - text along a path
  - Canvas-024 - Loom entity functionality
  - Canvas-030 - Polyline entity functionality
  - Canvas-038 - Responsive Shape-based entitys
  - DOM-015 - Use stacked DOM artefact corners as pivot points
  - Packets-002 - Scrawl-canvas packets - save and load a range of different entitys
- §

  #### Imports

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

      import { addStrings, doCreate, mergeOver, Ωempty } from '../core/utilities.js';

      import baseMix from '../mixin/base.js';
      import shapeMix from '../mixin/shape-basic.js';

      import { ENTITY, RADIUS_ARRAY_ALL, RADIUS_ARRAY_BOTTOM, RADIUS_ARRAY_BOTTOM_LEFT, RADIUS_ARRAY_BOTTOM_LEFT_X, RADIUS_ARRAY_BOTTOM_LEFT_Y, RADIUS_ARRAY_BOTTOM_RIGHT, RADIUS_ARRAY_BOTTOM_RIGHT_X, RADIUS_ARRAY_BOTTOM_RIGHT_Y, RADIUS_ARRAY_BOTTOM_X, RADIUS_ARRAY_BOTTOM_Y, RADIUS_ARRAY_LEFT, RADIUS_ARRAY_LEFT_X, RADIUS_ARRAY_LEFT_Y, RADIUS_ARRAY_RIGHT, RADIUS_ARRAY_RIGHT_X, RADIUS_ARRAY_RIGHT_Y, RADIUS_ARRAY_TOP, RADIUS_ARRAY_TOP_LEFT, RADIUS_ARRAY_TOP_LEFT_X, RADIUS_ARRAY_TOP_LEFT_Y, RADIUS_ARRAY_TOP_RIGHT, RADIUS_ARRAY_TOP_RIGHT_X, RADIUS_ARRAY_TOP_RIGHT_Y, RADIUS_ARRAY_TOP_X, RADIUS_ARRAY_TOP_Y, RADIUS_ARRAY_X, RADIUS_ARRAY_Y, RECTANGLE, T_RECTANGLE, ZERO_PATH } from '../core/shared-vars.js';

- §

  #### Rectangle constructor

      const Rectangle = function (items = Ωempty) {

          this.shapeInit(items);

          this.currentRectangleWidth = 1;
          this.currentRectangleHeight = 1;

          return this;
      };

- §

  #### Rectangle prototype

      const P = Rectangle.prototype = doCreate();
      P.type = T_RECTANGLE;
      P.lib = ENTITY;
      P.isArtefact = true;
      P.isAsset = false;

- §

  #### Mixins

  - base
  - shapeBasic

      baseMix(P);
      shapeMix(P);

- §

  #### Rectangle attributes

  - Attributes defined in the base mixin: name.
  - Attributes defined in the position mixin: group, visibility, order, calculateOrder, stampOrder, start, startX, startY, handle, handleX, handleY, offset, offsetX, offsetY, dimensions, width, height, pivoted, mimicked, lockTo, lockXTo, lockYTo, scale, roll, noUserInteraction, noPositionDependencies, noCanvasEngineUpdates, noFilters, noPathUpdates, purge, bringToFrontOnDrag.
  - Attributes defined in the delta mixin: delta, noDeltaUpdates.
  - Attributes defined in the pivot mixin: pivot, pivotCorner, addPivotHandle, addPivotOffset, addPivotRotation.
  - Attributes defined in the mimic mixin: mimic, useMimicDimensions, useMimicScale, useMimicStart, useMimicHandle, useMimicOffset, useMimicRotation, useMimicFlip, addOwnDimensionsToMimic, addOwnScaleToMimic, addOwnStartToMimic, addOwnHandleToMimic, addOwnOffsetToMimic, addOwnRotationToMimic.
  - Attributes defined in the path mixin: path, pathPosition, addPathHandle, addPathOffset, addPathRotation, constantPathSpeed.
  - Attributes defined in the entity mixin: method, pathObject, winding, flipReverse, flipUpend, scaleOutline, lockFillStyleToEntity, lockStrokeStyleToEntity, onEnter, onLeave, onDown, onUp, fillStyle, strokeStyle, globalAlpha, globalCompositeOperation, lineWidth, lineCap, lineJoin, lineDash, lineDashOffset, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, filter.
  - Attributes defined in the anchor mixin: anchor.
  - Attributes defined in the filter mixin: filters, isStencil.
  - Attributes defined in the shapeBasic mixin: species, useAsPath, precision, pathDefinition, showBoundingBox, boundingBoxColor, minimumBoundingBoxDimensions, constantPathSpeed.

      const defaultAttributes = {

          rectangleWidth: 10,
          rectangleHeight: 10,
          radiusTLX: 0,
          radiusTLY: 0,
          radiusTRX: 0,
          radiusTRY: 0,
          radiusBRX: 0,
          radiusBRY: 0,
          radiusBLX: 0,
          radiusBLY: 0,
          offshootA: 0.55,
          offshootB: 0,
      };
      P.defs = mergeOver(P.defs, defaultAttributes);

- §

  #### Packet management

  No additional packet functionality required

- §

  #### Clone management

  No additional clone functionality required

- §

  #### Kill management

  No additional kill functionality required

- §

  #### Get, Set, deltaSet

      const S = P.setters,
          D = P.deltaSetters;

      S.radius = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_ALL);
      };
      S.radiusX = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_X);
      };
      S.radiusY = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_Y);
      };
      S.radiusT = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_TOP);
      };
      S.radiusB = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_BOTTOM);
      };
      S.radiusL = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_LEFT);
      };
      S.radiusR = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_RIGHT);
      };
      S.radiusTX = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_TOP_X);
      };
      S.radiusBX = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_BOTTOM_X);
      };
      S.radiusLX = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_LEFT_X);
      };
      S.radiusRX = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_RIGHT_X);
      };
      S.radiusTY = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_TOP_Y);
      };
      S.radiusBY = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_BOTTOM_Y);
      };
      S.radiusLY = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_LEFT_Y);
      };
      S.radiusRY = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_RIGHT_Y);
      };
      S.radiusTL = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_TOP_LEFT);
      };
      S.radiusTR = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_TOP_RIGHT);
      };
      S.radiusBL = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_BOTTOM_LEFT);
      };
      S.radiusBR = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_BOTTOM_RIGHT);
      };
      S.radiusTLX = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_TOP_LEFT_X);
      };
      S.radiusTLY = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_TOP_LEFT_Y);
      };
      S.radiusTRX = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_TOP_RIGHT_X);
      };
      S.radiusTRY = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_TOP_RIGHT_Y);
      };
      S.radiusBRX = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_BOTTOM_RIGHT_X);
      };
      S.radiusBRY = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_BOTTOM_RIGHT_Y);
      };
      S.radiusBLX = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_BOTTOM_LEFT_X);
      };
      S.radiusBLY = function (item) {

          this.setRectHelper(item, RADIUS_ARRAY_BOTTOM_LEFT_Y);
      };
      D.radius = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_ALL);
      };
      D.radiusX = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_X);
      };
      D.radiusY = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_Y);
      };
      D.radiusT = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_TOP);
      };
      D.radiusB = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_BOTTOM);
      };
      D.radiusL = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_LEFT);
      };
      D.radiusR = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_RIGHT);
      };
      D.radiusTX = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_TOP_X);
      };
      D.radiusBX = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_BOTTOM_X);
      };
      D.radiusLX = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_LEFT_X);
      };
      D.radiusRX = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_RIGHT_X);
      };
      D.radiusTY = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_TOP_Y);
      };
      D.radiusBY = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_BOTTOM_Y);
      };
      D.radiusLY = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_LEFT_Y);
      };
      D.radiusRY = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_RIGHT_Y);
      };
      D.radiusTL = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_TOP_LEFT);
      };
      D.radiusTR = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_TOP_RIGHT);
      };
      D.radiusBL = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_BOTTOM_LEFT);
      };
      D.radiusBR = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_BOTTOM_RIGHT);
      };
      D.radiusTLX = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_TOP_LEFT_X);
      };
      D.radiusTLY = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_TOP_LEFT_Y);
      };
      D.radiusTRX = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_TOP_RIGHT_X);
      };
      D.radiusTRY = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_TOP_RIGHT_Y);
      };
      D.radiusBRX = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_BOTTOM_RIGHT_X);
      };
      D.radiusBRY = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_BOTTOM_RIGHT_Y);
      };
      D.radiusBLX = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_BOTTOM_LEFT_X);
      };
      D.radiusBLY = function (item) {

          this.deltaRectHelper(item, RADIUS_ARRAY_BOTTOM_LEFT_Y);
      };

      S.offshootA = function (item) {

          this.offshootA = item;
          this.updateDirty();
      };
      S.offshootB = function (item) {

          this.offshootB = item;
          this.updateDirty();
      };
      D.offshootA = function (item) {

          if (item.toFixed) {

              this.offshootA += item;
              this.updateDirty();
          }
      };
      D.offshootB = function (item) {

          if (item.toFixed) {

              this.offshootB += item;
              this.updateDirty();
          }
      };

      S.rectangleWidth = function (val) {

          if (val != null) {

              this.rectangleWidth = val;
              this.dirtyDimensions = true;
              this.dirtyFilterIdentifier = true;
          }
      };
      S.rectangleHeight = function (val) {

          if (val != null) {

              this.rectangleHeight = val;
              this.dirtyDimensions = true;
              this.dirtyFilterIdentifier = true;
          }
      };
      D.rectangleWidth = function (val) {

          this.rectangleWidth = addStrings(this.rectangleWidth, val);
          this.dirtyDimensions = true;
          this.dirtyFilterIdentifier = true;
      };
      D.rectangleHeight = function (val) {

          this.rectangleHeight = addStrings(this.rectangleHeight, val);
          this.dirtyDimensions = true;
          this.dirtyFilterIdentifier = true;
      };

- §

  #### Prototype functions

- §

  `setRectHelper` - internal setter helper function

      P.setRectHelper = function (item, corners) {

          this.updateDirty();

          corners.forEach(corner => {

              this[corner] = item;
          }, this);
      };

- §

  `deltaRectHelper` - internal setter helper function

      P.deltaRectHelper = function (item, corners) {

          this.updateDirty();

          corners.forEach(corner => {

              this[corner] = addStrings(this[corner], item);
          }, this);
      };

- §

  `cleanSpecies` - internal helper function - called by `prepareStamp`

      P.cleanSpecies = function () {

          this.dirtySpecies = false;
          this.pathDefinition = this.makeRectanglePath();
      };

- §

  `cleanDimensions` - internal helper function called by `prepareStamp`

  - Unlike other Shape entitys, Rectangles have settable dimensions `rectangleWidth` and `rectangleWidth`.

      P.cleanDimensions = function () {

          this.dirtyDimensions = false;

          const host = this.getHost();

          if (host) {

              const hostDims = (host.currentDimensions) ? host.currentDimensions : [host.w, host.h],
                  oldW = this.currentRectangleWidth || 1,
                  oldH = this.currentRectangleHeight || 1;

              let w = this.rectangleWidth,
                  h = this.rectangleHeight;

              if (w.substring) w = (parseFloat(w) / 100) * hostDims[0];

              if (h.substring) h = (parseFloat(h) / 100) * hostDims[1];

              const mimic = this.mimic;

              let mimicDims;

              if (mimic && mimic.name && this.useMimicDimensions) mimicDims = mimic.currentDimensions;

              if (mimicDims) {

                  this.currentRectangleWidth = (this.addOwnDimensionsToMimic) ? mimicDims[0] + w : mimicDims[0];
                  this.currentRectangleHeight = (this.addOwnDimensionsToMimic) ? mimicDims[1] + h : mimicDims[1];
              }
              else {

                  this.currentRectangleWidth = w;
                  this.currentRectangleHeight = h;
              }

              this.currentDimensions[0] = this.currentRectangleWidth;
              this.currentDimensions[1] = this.currentRectangleHeight;

              this.dirtyStart = true;
              this.dirtyHandle = true;
              this.dirtyOffset = true;

              if (oldW !== this.currentRectangleWidth || oldH !== this.currentRectangleHeight) this.dirtyPositionSubscribers = true;

              if (this.mimicked && this.mimicked.length) this.dirtyMimicDimensions = true;
          }
          else this.dirtyDimensions = true;
      };

- §

  `makeRectanglePath` - internal helper function - called by `cleanSpecies`

      P.makeRectanglePath = function () {

          if (this.dirtyDimensions) this.cleanDimensions()

          const width = this.currentRectangleWidth,
              height = this.currentRectangleHeight;

          const A = this.offshootA,
              B = this.offshootB;

          let _tlx = this.radiusTLX,
              _tly = this.radiusTLY,
              _trx = this.radiusTRX,
              _try = this.radiusTRY,
              _brx = this.radiusBRX,
              _bry = this.radiusBRY,
              _blx = this.radiusBLX,
              _bly = this.radiusBLY;

          if (_tlx.substring || _tly.substring || _trx.substring || _try.substring || _brx.substring || _bry.substring || _blx.substring || _bly.substring) {

              _tlx = (_tlx.substring) ? (parseFloat(_tlx) / 100) * width : _tlx;
              _tly = (_tly.substring) ? (parseFloat(_tly) / 100) * height : _tly;
              _trx = (_trx.substring) ? (parseFloat(_trx) / 100) * width : _trx;
              _try = (_try.substring) ? (parseFloat(_try) / 100) * height : _try;
              _brx = (_brx.substring) ? (parseFloat(_brx) / 100) * width : _brx;
              _bry = (_bry.substring) ? (parseFloat(_bry) / 100) * height : _bry;
              _blx = (_blx.substring) ? (parseFloat(_blx) / 100) * width : _blx;
              _bly = (_bly.substring) ? (parseFloat(_bly) / 100) * height : _bly;
          }

          let myData = ZERO_PATH;

          if (width - _tlx - _trx !== 0) myData += `h${width - _tlx - _trx}`;

          if (_trx + _try !== 0) myData += `c${_trx * A},${_try * B} ${_trx - (_trx * B)},${_try - (_try * A)}, ${_trx},${_try}`;

          if (height - _try - _bry !== 0) myData += `v${height - _try - _bry}`;

          if (_brx + _bry !== 0) myData += `c${-_brx * B},${_bry * A} ${-_brx + (_brx * A)},${_bry - (_bry * B)} ${-_brx},${_bry}`;

          if (-width + _blx + _brx !== 0) myData += `h${-width + _blx + _brx}`;

          if (_blx + _bly !== 0) myData += `c${-_blx * A},${-_bly * B} ${-_blx + (_blx * B)},${-_bly + (_bly * A)} ${-_blx},${-_bly}`;

          if (-height + _tly + _bly !== 0) myData += `v${-height + _tly + _bly}`;

          if (_tlx + _tly !== 0) myData += `c${_tlx * B},${-_tly * A} ${_tlx - (_tlx * A)},${-_tly + (_tly * B)} ${_tlx},${-_tly}`;

          myData += 'z';

          return myData;
      };

      P.calculateLocalPathAdditionalActions = function () {

          const [x, y] = this.localBox;

          this.pathDefinition = this.pathDefinition.replace(ZERO_PATH, `m${-x},${-y}`);

          this.pathCalculatedOnce = false;

- §

  ALWAYS, when invoking `calculateLocalPath` from `calculateLocalPathAdditionalActions`, include the second argument, set to `true`! Failure to do this leads to an infinite loop which will make your machine weep.

  - We need to recalculate the local path to take into account the offset required to put the Rectangle entity’s start coordinates at the top-left of the local box, and to recalculate the data used by other artefacts to place themselves on, or move along, its path.

          this.calculateLocalPath(this.pathDefinition, true);
      };

- §

  #### Factories

- §

  ##### makeRectangle

  Essentially this Shape looks like a Block with rounded corners

  Rectangle Shapes are unique in that they require width and height dimensions, supplied in the rectangleWidth and rectangleHeight attributes.

  Internally, Scrawl-canvas uses quadratic curves to construct the corners. The bend of these corners is set by the quadratic’s control point which doesn’t have its own coordinate but is rather calculated using two float Number variables: offshootA (default: `0.55`) and offshootB (default: `0`) - change these values to make the corners more or less bendy.

  Each corner of the rectangle can be rounded using radius values. Like the oval Shape, the corner has both a horizontal `x` radius and a vertical `y` radius. Thus to draw a rectangle, we need to supply a total of 8 radius measurements:

  - radiusTLX - the __T__op __L__eft corner’s `x` radius
  - radiusTLY - the __T__op __L__eft corner’s `y` radius
  - radiusTRX - the __T__op __R__ight corner’s `x` radius
  - radiusTRY - the __T__op __R__ight corner’s `y` radius
  - radiusBRX - the __B__ottom __R__ight corner’s `x` radius
  - radiusBRY - the __B__ottom __R__ight corner’s `y` radius
  - radiusBLX - the __B__ottom __L__eft corner’s `x` radius
  - radiusBLY - the __B__ottom __L__eft corner’s `y` radius

  For convenience a lot of pseudo-attributes are supplied, which make defining the radius of each corner a bit easier. We achieve this by adding a letter or combination of letters to the word `'radius'`:

  - radius - all 8 radius values are set to the given distance (measured in px)
  - radiusX - the 4 `x` radius values
  - radiusY - the 4 `y` radius values
  - radiusT - the 4 `top` radius values
  - radiusTX - both `x` radius values for the `top` corners
  - radiusTY - both `y` radius values for the `top` corners
  - radiusB - the 4 `bottom` radius values
  - radiusBX - both `x` radius values for the `bottom` corners
  - radiusBY - both `y` radius values for the `bottom` corners
  - radiusL - the 4 `left` radius values
  - radiusLX - both `x` radius values for the `left` corners
  - radiusLY - both `y` radius values for the `left` corners
  - radiusR - the 4 `right` radius values
  - radiusRX - both `x` radius values for the `right` corners
  - radiusRY - both `y` radius values for the `right` corners
  - radiusTL - both radius values for the `top left` corner
  - radiusTR - both radius values for the `top right` corner
  - radiusBL - both radius values for the `bottom left` corner
  - radiusBR - both radius values for the `bottom right` corner

      scrawl.makeRectangle({

          name: 'tab',

          startX: 20,
          startY: 200,

          rectangleWidth: 120,
          rectangleHeight: 80,

          radiusT: 20,
          radiusB: 0,

          fillStyle: 'lightblue',
          method: 'fillAndDraw',
      });

      export const makeRectangle = function (items) {

          if (!items) return false;
          items.species = RECTANGLE;
          return new Rectangle(items);
      };

      constructors.Rectangle = Rectangle;