scrawl-canvas
Version:
Responsive, interactive and more accessible HTML5 canvas elements. Scrawl-canvas is a JavaScript library designed to make using the HTML5 canvas element easier, and more fun
246 lines (168 loc) • 5.99 kB
JavaScript
// # Star factory
// A factory for generating star shape-based entitys
// #### Imports
import { constructors } from '../core/library.js';
import { addStrings, doCreate, mergeOver, Ωempty } from '../helper/utilities.js';
import { releaseVector, requestVector } from '../untracked-factory/vector.js';
import baseMix from '../mixin/base.js';
import shapeMix from '../mixin/shape-basic.js';
// Shared constants
import { ENTITY, ZERO_PATH, ZERO_STR } from '../helper/shared-vars.js';
// Local constants
const STAR = 'star',
T_STAR = 'Star';
// #### Star constructor
const Star = function (items = Ωempty) {
this.shapeInit(items);
return this;
};
// #### Star prototype
const P = Star.prototype = doCreate();
P.type = T_STAR;
P.lib = ENTITY;
P.isArtefact = true;
P.isAsset = false;
// #### Mixins
baseMix(P);
shapeMix(P);
// #### Star attributes
const defaultAttributes = {
radius1: 0,
radius2: 0,
points: 0,
twist: 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;
// __radius1__, __radius2__
S.radius1 = function (item) {
this.radius1 = item;
this.updateDirty();
};
D.radius1 = function (item) {
this.radius1 = addStrings(this.radius1, item);
this.updateDirty();
};
S.radius2 = function (item) {
this.radius2 = item;
this.updateDirty();
};
D.radius2 = function (item) {
this.radius2 = addStrings(this.radius2, item);
this.updateDirty();
};
// __points__
S.points = function (item) {
this.points = item;
this.updateDirty();
};
D.points = function (item) {
this.points += item;
this.updateDirty();
};
// __twist__
S.twist = function (item) {
this.twist = item;
this.updateDirty();
};
D.twist = function (item) {
this.twist += item;
this.updateDirty();
};
// #### Prototype functions
// `cleanSpecies` - internal helper function - called by `prepareStamp`
P.cleanSpecies = function () {
this.dirtySpecies = false;
this.pathDefinition = this.makeStarPath();
};
// `makeStarPath` - internal helper function - called by `cleanSpecies`
P.makeStarPath = function () {
const points = this.points,
twist = this.twist,
turn = 360 / points;
let radius1 = this.radius1,
radius2 = this.radius2;
let currentX, currentY, x, y,
myPath = ZERO_STR;
if (radius1.substring || radius2.substring) {
const host = this.getHost();
if (host) {
const hW = host.currentDimensions[0];
radius1 = (radius1.substring) ? (parseFloat(radius1) / 100) * hW : radius1;
radius2 = (radius2.substring) ? (parseFloat(radius2) / 100) * hW : radius2;
}
}
const v1 = requestVector({x: 0, y: -radius1}),
v2 = requestVector({x: 0, y: -radius2});
currentX = v1.x;
currentY = v1.y;
v2.rotate(-turn/2);
v2.rotate(twist);
for (let i = 0; i < points; i++) {
v2.rotate(turn);
x = parseFloat((v2.x - currentX).toFixed(1));
currentX += x;
y = parseFloat((v2.y - currentY).toFixed(1));
currentY += y;
myPath += `${x},${y} `;
v1.rotate(turn);
x = parseFloat((v1.x - currentX).toFixed(1));
currentX += x;
y = parseFloat((v1.y - currentY).toFixed(1));
currentY += y;
myPath += `${x},${y} `;
}
releaseVector(v1, v2);
return `${ZERO_PATH}l${myPath}z`;
};
P.calculateLocalPathAdditionalActions = function () {
let scale = this.scale;
if (scale < 0.001) scale = 0.001;
const [x, y] = this.localBox;
this.pathDefinition = this.pathDefinition.replace(ZERO_PATH, `m${-x / scale},${-y / scale}`);
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
// ##### makeStar
// Accepts argument with attributes:
// + __radius1__ (required) - the _outer_ radius representing the distance between the center of the Shape and the tips of its (acute angle) points.
// + __radius2__ (required) - the _inner_ radius representing the distance between the center of the Shape and the obtuse angle at the valley between the tips of its (acute angle) points.
// + ... where these radius values are supplied as %Strings, they are calculated as relative to the canvas/cell ___width___ value.
// + __points__ (required) - a positive integer Number representing the number of points the star will have.
// + __twist__ - a float Number representing the degrees by which the star's second radius will be rotated out of line from its first radius; the default value `0` will produce a star with all of its sides of equal length and the star's valleys falling midway between its connecting points.
// + Note that the use of _inner_ and _outer_ above is purely descriptive: `radius2` can be larger than `radius1`
//
// ```
// scrawl.makeStar({
//
// name: '5star',
//
// startX: 20,
// startY: 100,
//
// radius1: 80,
// radius2: 50,
//
// points: 5,
//
// fillStyle: 'linen',
// method: 'fillAndDraw',
// });
// ```
export const makeStar = function (items) {
if (!items) return false;
items.species = STAR;
return new Star(items);
};
constructors.Star = Star;