// @program: canvasLab
// @brief: HTML5 canvas illustration & animation framework
// @author: Justin D. Byrne
// @email: justin@byrne-systems.com
// @version: 0.7.196
// @license: GPL-2.0
"use strict";
//// TYPEDEFS ////////////////////////////////////////
/**
* Canvas Lab objects, for Objects & Collections
* @typedef {Object} clObject
*/
/**
* Canvas Lab Template, for the creation of objects through a collection
* @typedef {Object} Template
* @property {Point} point X & Y coordinates
* @property {clObject} master Master Canvas Lab object
* @property {Function} init Initialization of Template
*/
/**
* Change, for animation changes through a transition
* @typedef {Object<Object>} clChange
*/
/**
* Transition, for animation transition instances
* @typedef {Object} Transition
* @param {clObject} transition.object CanvasLab Object
* @param {Function} transition.timing Timing function
* @param {number} transition.period Period of time
* @param {clChange} transition.change Changes to object
*/
//// COMPONENTS //////////////////////////////////////
/**
* Base module for shared accessors & mutators
* @namespace PROPERTY_BLOCKS
*/
const PROPERTY_BLOCKS =
{
/**
* Individual property accessors & mutators
* @var {Object} PROPERTY_BLOCKS.individual
*/
individual:
{
/** @function PROPERTY_BLOCKS.individual.alpha **/
alpha:
{
/**
* Set alpha value
* @public
* @function
* @param {number} value Alpha value; 0 - 1
*/
set ( value )
{
this._alpha = ( value <= 1 && value >= 0 ) ? value : this._alpha;
},
/**
* Set alpha value
* @public
* @function
* @return {number} Alpha value; 0 - 1
*/
get ( )
{
return this._alpha;
}
},
/** @function PROPERTY_BLOCKS.individual.area **/
/** @notes for <Rectangle>, <cImage> **/
area:
{
/**
* Get area of this object
* @readOnly
* @function
* @return {number} Area of this object
*/
get ( )
{
return ( this.width * this.height );
}
},
/** @function PROPERTY_BLOCKS.individual.canvas **/
canvas:
{
/**
* Set canvas value
* @public
* @function
* @param {string} value Canvas id
*/
set ( value )
{
this._canvas = ( value ) ? ( this._isInDom ( value ) )
? document.getElementById ( value ).getContext ( '2d' )
: console.warn ( `"${value}" is not a valid DOM element !` )
: ( window.canvaslab.canvas )
? document.getElementById ( window.canvaslab.canvas ).getContext ( '2d' )
: this._canvas;
},
/**
* Get canvas value
* @readOnly
* @function
* @return {string} Canvas id
*/
get ( )
{
return ( this._canvas != undefined ) ? this._canvas.canvas.id : undefined;
}
},
/** @function PROPERTY_BLOCKS.individual.center **/
/** @notes for <Rectangle>, <cImage> **/
center:
{
/**
* Get center of this object
* @readOnly
* @function
* @return {Point} Center point coordinates
*/
get ( )
{
let _x = this.x - ( this.x - this.anchor.x ) + ( this.width / 2 );
let _y = this.y - ( this.y - this.anchor.y ) + ( this.height / 2 );
return new Point ( _x, _y );
}
},
/** @function PROPERTY_BLOCKS.individual.master **/
master:
{
/**
* Set master object
* @public
* @function
* @param {clObject} value Canvas Lab object
*/
set ( value )
{
this._master = ( this._isCanvasLabObject ( value ) ) ? value : this._master;
},
/**
* Get master object
* @public
* @function
* @return {clObject} Master Canvas Lab object
*/
get ( )
{
return this._master;
}
},
/** @function PROPERTY_BLOCKS.individual.offset **/
offset:
{
/**
* Set offset
* @public
* @function
* @param {Point} value Shadow offset
*/
set ( value )
{
this._offset = ( this._isPoint ( value ) ) ? value : this._offset;
},
/**
* Get offset
* @public
* @function
* @return {Point} Shadow offset
*/
get ( )
{
return this._offset;
}
},
/** @function PROPERTY_BLOCKS.individual.offsetX **/
offsetX:
{
/**
* Set x-axis offset value
* @public
* @function
* @param {number} value X coordinate value
*/
set ( value )
{
this._offset.x = value;
},
/**
* Get x-axis offset value
* @readOnly
* @function
* @return {number} X coordinate value
*/
get ( )
{
return this._offset.x;
}
},
/** @function PROPERTY_BLOCKS.individual.offsetY **/
offsetY:
{
/**
* Set the y-axis offset value
* @public
* @function
* @param {number} value Y coordinate value
*/
set ( value )
{
this._offset.y = value;
},
/**
* Get y-axis offset value
* @readOnly
* @function
* @return {number} Y coordinate value
*/
get ( )
{
return this._offset.y;
}
},
/** @function PROPERTY_BLOCKS.individual.point **/
point:
{
/**
* Set point
* @public
* @function
* @param {Point} value X & Y coordinates
*/
set ( value )
{
this._point = ( this._isPoint ( value ) ) ? new Point ( value.x, value.y ) : this._point;
},
/**
* Get point
* @public
* @function
* @return {Point} X & Y coordinates
*/
get ( )
{
return this._point;
}
},
/** @function PROPERTY_BLOCKS.individual.pointX **/
pointX:
{
/**
* Set x-axis value
* @public
* @function
* @param {number} value X coordinate value
*/
set ( value )
{
this._point.x = value;
},
/**
* Get x-axis value
* @readOnly
* @function
* @return {number} X coordinate value
*/
get ( )
{
return this._point.x;
}
},
/** @function PROPERTY_BLOCKS.individual.pointY **/
pointY:
{
/**
* Set the y-axis value
* @public
* @function
* @param {number} value Y coordinate value
*/
set ( value )
{
this._point.y = value;
},
/**
* Get y-axis value
* @readOnly
* @function
* @return {number} Y coordinate value
*/
get ( )
{
return this._point.y;
}
},
/** @function PROPERTY_BLOCKS.individual.perimeter **/
perimeter:
{
/**
* Get perimeter
* @readOnly
* @function
* @return {number} Perimeter of rectangle
*/
get ( )
{
return ( this.area * 2 );
}
},
/** @function PROPERTY_BLOCKS.individual.radius **/
radius:
{
/**
* Set radius
* @public
* @function
* @param {number} value Radius of circle
*/
set ( value )
{
this._radius = ( typeof value === 'number' && value > 0 ) ? value : this._radius;
},
/**
* Get radius
* @readOnly
* @function
* @return {number} Radius of circle
*/
get ( )
{
return this._radius;
}
},
/** @function PROPERTY_BLOCKS.individual.template **/
template:
{
/**
* Set template
* @public
* @function
* @param {Template} value Template object
*/
set ( value )
{
this._template = ( this._isTemplate ( value ) ) ? value : this._template;
},
/**
* Get template
* @public
* @function
* @return {Template} Template object
*/
get ( )
{
return this._template;
}
},
/** @function PROPERTY_BLOCKS.individual.transitions **/
transitions:
{
/**
* Set transitions
* @public
* @function
* @param {Template} value Template object
*/
set ( value )
{
if ( this._isTransition ( value ) )
[ this._transitions, value.template ] = [ value, this ];
},
/**
* Get transitions
* @public
* @function
* @return {Template} Template object
*/
get ( )
{
return this._transitions;
}
},
},
/**
* Collection property accessors & mutators
* @var {Object} PROPERTY_BLOCKS.collection
*/
collection:
{
/** @function PROPERTY_BLOCKS.collection.anchor **/
anchor:
{
/**
* Set anchor type
* @public
* @function
* @param {string} value Anchor type
*/
set ( value )
{
this._anchor.type = ( this._isAnchor ( value ) ) ? value : this._anchor.type;
this._setAnchorPoint ( );
},
/**
* Get anchor
* @public
* @function
* @return {Anchor} Anchor properties
*/
get ( )
{
return this._anchor;
}
},
/** @function PROPERTY_BLOCKS.collection.area **/
area:
{
/**
* Get area of this object
* @readOnly
* @function
* @return {number} Area of rectangle
*/
get ( )
{
return ( this.width * this.height );
}
},
/** @function PROPERTY_BLOCKS.collection.aspect **/
aspect:
{
/**
* Get aspect properties
* @public
* @function
* @return {Aspect} Aspect properties
*/
get ( )
{
this._setAspect ( );
return this._aspect;
}
},
/** @function PROPERTY_BLOCKS.collection.aspectWidth **/
aspectWidth:
{
/**
* Get aspect with
* @readOnly
* @function
* @return {number} Width value
*/
get ( )
{
return this._aspect.width;
}
},
/** @function PROPERTY_BLOCKS.collection.aspectHeight **/
aspectHeight:
{
/**
* Get aspect height
* @readOnly
* @function
* @return {number} Height value
* @see {@link PROPERTY_BLOCKS.collection.aspectHeight}
*/
get ( )
{
return this._aspect.height;
}
},
/** @function PROPERTY_BLOCKS.collection.canvas **/
canvas:
{
/**
* Set canvas value
* @public
* @function
* @param {string} value Canvas id
*/
set ( value )
{
this._canvas = ( value ) ? ( this._isInDom ( value ) )
? document.getElementById ( value ).getContext ( '2d' )
: ( this._isCanvasLabObject ( value ) )
? null
: console.warn ( `"${value}" is not a valid DOM element !` )
: ( document.getElementById ( window.canvaslab.canvas ).getContext ( '2d' ) )
? document.getElementById ( window.canvaslab.canvas ).getContext ( '2d' )
: this._canvas;
if ( ( this.length > 0 ) && ( this._canvas instanceof CanvasRenderingContext2D ) )
for ( let _object of this )
if ( _object )
_object.canvas = this.canvas;
},
/**
* Get canvas value
* @readOnly
* @function
* @return {string} Canvas id
*/
get ( )
{
return ( this._canvas != undefined ) ? this._canvas.canvas.id : undefined;
}
},
/** @function PROPERTY_BLOCKS.collection.center **/
center:
{
/**
* Get center of this object
* @readOnly
* @function
* @return {Point} Center point coordinates
*/
get ( )
{
let [ _x, _y ] = [ this.width / 2, this.height / 2 ];
return new Point ( _x, _y );
}
},
/** @function PROPERTY_BLOCKS.collection.endPoint **/
endPoint:
{
/**
* Returns the last Point within this Array
* @public
* @function
* @return {Point} Last Array element's X & Y Coordinates
*/
get ( )
{
return this [ this.length - 1 ].point;
}
},
/** @function PROPERTY_BLOCKS.collection.perimeter **/
perimeter:
{
/**
* Get perimeter of this object
* @readOnly
* @function
* @return {number} Perimeter of rectangle
*/
get ( )
{
return ( this.area * 2 );
}
},
/** @function PROPERTY_BLOCKS.collection.template **/
template:
{
/**
* Set template
* @public
* @function
* @param {Object} value Template object
*/
set ( value )
{
if ( this._isTemplate ( value ) )
{
[ this._template, this._template._master ] = [ value, this ];
this._template.init ( );
this._setAllCanvases ( );
}
},
/**
* Get template
* @readOnly
* @function
* @return {Object} Template object
*/
get ( )
{
return this._template;
}
},
},
/**
* Animation property accessors & mutators
* @var {Object} PROPERTY_BLOCKS.animation
*/
animation:
{
/** @function PROPERTY_BLOCKS.animation.cache **/
cache:
{
/**
* Set cache
* @public
* @function
* @param {boolean} value True || False
*/
set ( value )
{
this._options.cache = ( typeof value == 'boolean' ) ? value : this._options.cache;
},
/**
* Get cache
* @readOnly
* @function
* @return {boolean} True || False
*/
get ( )
{
return this._options.cache;
}
},
/** @function PROPERTY_BLOCKS.animation.cancel **/
cancel:
{
/**
* Cancels animation
* @readOnly
* @function
*/
get ( )
{
this._options.active = false;
}
},
/** @function PROPERTY_BLOCKS.animation.period **/
period:
{
/**
* Set period of animation
* @public
* @function
* @param {number} value Period of animation-time
*/
set ( value )
{
this._period = ( this._isNumber ( value ) ) ? value : this._period;
},
/**
* Get period of animation
* @readOnly
* @function
* @return {number} Period of animation-time
*/
get ( )
{
return this._period;
}
},
/** @function PROPERTY_BLOCKS.animation.queue **/
queue:
{
/**
* Set queue
* @public
* @function
* @param {Queue} value Queue object
*/
set ( value )
{
this._queue = ( value instanceof Queue ) ? value : this._queue;
},
/**
* Get queue
* @readOnly
* @function
* @return {Queue} Queue object
*/
get ( )
{
return this._queue;
}
}
},
}
/**
* Shared utility functions
* @namespace UTILITIES
*/
const UTILITIES =
{
/**
* Utilities for collection functions
* @function UTILITIES.collection
*/
collection:
{
/**
* Draw function
* @public
* @function
* @param {string} canvas Canvas Id
*/
draw ( canvas )
{
if ( canvas != undefined ) this.canvas = canvas;
if ( this._canvas instanceof CanvasRenderingContext2D )
if ( this.length > 0 )
{
this._setAnchorPoint ( );
for ( let _object of this )
{
this._setPointOffset ( _object );
this._drawOptionsPre ( _object, this.options );
_object.draw ( );
}
this._drawOptionsPost ( );
}
else
console.warn ( `No ${this.constructor.name} exist to draw !` );
else
console.warn ( `'canvas' property is not set for ${this.constructor.name} !` );
},
/**
* Draws anchor point
* @public
* @function
*/
drawAnchor ( )
{
let _anchor = new Rectangle ( new Point ( this.x, this.y ), new Aspect ( 5, 5 ) );
_anchor.fill.color = new Rgb ( 255, 0, 0 );
_anchor.canvas = this.canvas;
_anchor.draw ( );
},
/**
* Draws associated options
* @public
* @function
*/
drawOptionsPost ( )
{
let _offset = 20;
let _aspect = new Aspect ( this.width + _offset, this.height + _offset );
////////////////////////////////////////////////////////////////////
if ( this.options.border ) this._drawBorder ( _aspect );
if ( this.options.axis ) this._drawAxis ( );
if ( this.options.anchor ) this._drawAnchor ( );
},
/**
* Get all or specific points throughout this collection
* @public
* @function
* @param {Array.<number>} indexes Indexes of points
* @param {Rgb} color Color to colorize objects selected points
*/
getPoints ( indexes, color = new Rgb ( 200, 20, 20, 1 ) )
{
let _results = new Array;
if ( Array.isArray ( indexes ) )
for ( let _index of indexes )
{
this [ _index ].stroke.color = color;
this [ _index ].stroke.width = 5;
this [ _index ].draw ( );
_results.push ( this [ _index ].point );
}
else
for ( let _index of this )
_results.push ( _index.point );
return _results;
},
/**
* Pushes child object(s) into this collection
* @public
* @function
*/
push ( )
{
for ( let _i = 0; _i < arguments.length; _i++ )
if ( arguments [ _i ] instanceof this.storageType )
Array.prototype.push.apply ( this, [ arguments [ _i ] ] );
else
if ( ! this._isPoint ( arguments [ _i ] ) )
console.error ( `[ERROR] Argument ${ ( _i + 1 ) }, of type "${ arguments [ _i ].constructor.name }", is not a valid type !` );
},
/**
* Sets anchor's point against this object's point location
* @public
* @function
*/
setAnchorPoint ( )
{
this._setAspect ( );
this._anchor = this.center;
switch ( this.anchor.type )
{
case 'center': this.anchor.x -= this.width / 2; this.anchor.y -= this.height / 2; break;
case 'top': this.anchor.x -= this.width / 2; /* ... do nothing */ break;
case 'topRight': this.anchor.x -= this.width; /* ... do nothing */ break;
case 'right': this.anchor.x -= this.width; this.anchor.y -= this.height / 2; break;
case 'bottomRight': this.anchor.x -= this.width; this.anchor.y -= this.height; break;
case 'bottom': this.anchor.x -= this.width / 2; this.anchor.y -= this.height; break;
case 'bottomLeft': /* ... do nothing */ this.anchor.y -= this.height; break;
case 'left': /* ... do nothing */ this.anchor.y -= this.height / 2; break;
case 'topLeft': /* ... do nothing */ /* ... do nothing */ break;
}
},
/**
* Sets offset of child object against this constructor's point
* @public
* @function
* @param {Object} Object CanvasLab Object
*/
setPointOffset ( Object )
{
Object.x += this.x;
Object.y += this.y;
},
},
/**
* Utilities for individual functions
* @function UTILITIES.individual
*/
individual:
{
/**
* Utility color functions
* @function UTILITIES.individual.color
*/
color:
{
/**
* Utility color cycling functions
*/
cycle:
{
/**
* Cycle colors for stroke
* @public
* @function
* @param {Rgb} start Starting RGB value
* @param {Rgb} end Ending RGB value
* @param {number} progress Progress time unit; 0.00 - 1.00
* @param {number} [max=1] Maximum increments
*/
stroke ( start, end, progress, max = 1 )
{
this._stroke._color._cycle ( start, end, progress, max );
},
/**
* Cycle colors for fill
* @public
* @function
* @param {Rgb} start Starting RGB value
* @param {Rgb} end Ending RGB value
* @param {number} progress Progress time unit between; 0.00 - 1.00
* @param {number} [max=1] Maximum increments
*/
fill ( start, end, progress, max = 1 )
{
this._fill.color._cycle ( start, end, progress, max );
},
/**
* Cycle colors for gradient
* @public
* @function
* @param {Rgb} start Starting RGB value
* @param {Rgb} end Ending RGB value
* @param {number} progress Progress time unit between; 0.00 - 1.00
* @param {number} stop Gradient color stop
* @param {number} [max=1] Maximum increments
*/
gradient ( start, end, progress, stop, max = 1 )
{
this._fill.gradient.stopColorCycle ( start, end, progress, stop, max );
},
/**
* Cycle colors for gradient stop(s)
* @public
* @function
* @param {Rgb} start Color model & values
* @param {Rgb} end Color model & values
* @param {number} progress Progress time unit; 0.00 - 1.00
* @param {number} stop Color stop to cycle
* @param {number} max Maximum number of steps between interpolation
*/
stop ( start, end, progress, stop, max )
{
this.stops [ stop ].color._cycle ( start, end, progress, max );
},
}
},
/**
* Utility draw functions
* @function UTILITIES.individual.draw
*/
draw:
{
/**
* Draws anchor point
* @public
* @function
*/
anchor ( color = new Rgb ( 255, 0, 0 ) )
{
let _anchor = new Rectangle ( new Point ( this.x, this.y ), new Aspect ( 5, 5 ) );
_anchor.fill.color = color;
_anchor.canvas = this.canvas;
_anchor.draw ( );
},
/**
* Draws an axis for the associated object
* @public
* @function
* @param {number} offset Offset of axis
* @param {Object} color Color model
* @param {number} stop Gradient color stop
*/
axis ( offset = 20, color = new Rgb ( 245, 80, 50 ) )
{
let _collections = [ 'Circles', 'Ellipses', 'Rectangles', 'RoundedRectangles', 'Texts' ];
let _lines = new Lines;
_lines.push ( new Line, new Line );
_lines.stroke.color = color;
_lines.point = this.center;
if ( _collections.includes ( this.constructor.name ) ) // Fix offset issue(s)
[ _lines.point.x, _lines.point.y ] = [ _lines.point.x + this.aspect.offset.x, _lines.point.y + this.aspect.offset.y ];
_lines.canvas = ( this instanceof Point ) ? this.options._master.canvas : this.canvas;
_lines [ 0 ].start = new Point ( - offset, 0 );
_lines [ 0 ].end = new Point ( offset, 0 );
_lines [ 1 ].start = new Point ( 0, - offset );
_lines [ 1 ].end = new Point ( 0, offset );
_lines.draw ( );
},
/**
* Draws an axis for the associated object
* @public
* @function
* @param {Aspect} aspect Aspect properties
* @param {Object} color Color model
*/
border ( aspect, color = new Rgb ( 245, 80, 50 ) )
{
let _collections = [ 'Circles', 'Ellipses', 'Rectangles', 'RoundedRectangles', 'Texts' ];
if ( this._isAspect ( aspect ) )
{
let _border = new Rectangle ( this.center, aspect );
if ( _collections.includes ( this.constructor.name ) ) // Fix offset issue(s)
[ _border.x, _border.y ] = [ _border.x + this.aspect.offset.x, _border.y + this.aspect.offset.y ]
_border.stroke.color = color;
_border.fill.color.alpha = 0;
_border.canvas = ( this instanceof Point ) ? this.options._master.canvas : this.canvas;
_border.draw ( );
}
else
console.warn ( `"${value}" is not a valid aspect !` );
},
},
/**
* Utility misc functions
* @function UTILITIES.individual.misc
*/
misc:
{
/**
* Push or pops the passed object
* @public
* @function
* @param {Object} object Object; Circle, Rectangle, Text
*/
pushPop ( object )
{
let _index = undefined;
if ( object instanceof this.storageType )
{
if ( this._canvas != undefined )
object.canvas = this.canvas;
if ( this.length != 0 )
{
for ( let _id in this )
if ( this [ _id ] instanceof this.storageType )
if ( this [ _id ].isThere ( object ) )
_index = _id;
else
break;
( _index != undefined )
? this.splice ( _index, 1 )
: this.push ( object );
}
else
this [ 0 ] = object;
}
else
console.warn ( `${this.constructor.name} only accepts '${this.storageType.name}' objects !`);
},
/**
* Move this object
* @public
* @function
* @param {number} degree Direction to move; in degrees
* @param {number} distance Distance to move
*/
move ( degree, distance )
{
let _point = this._rotatePoint ( { x: this.x, y: this.y }, degree, distance );
[ this.x, this.y ] = [ _point.x, _point.y ];
},
/**
* Rotate this object
* @public
* @function
* @param {number} degree Distance to rotate; in degrees
*/
rotate ( value )
{
if ( this._isDegree ( value ) )
{
let [ _x, _y ] = [ this.x, this.y ];
this._canvas.save ( );
this._canvas.translate ( _x, _y );
this._canvas.rotate ( value * Math.PI / 180 );
this._canvas.translate ( -_x, -_y );
}
},
/**
* Rotates the origin point by the degree & distance passed
* @public
* @function
* @param {Point} origin Origin point
* @param {number} degree Degree to rotate
* @param {number} distance Distance from origin
*/
rotatePoint ( origin = { x, y }, degree, distance )
{
let _point = new Point;
let _angle = ( degree % 360 );
_point.x = origin.x - Math.cos ( _angle * Math.PI / 180 ) * distance;
_point.y = origin.y - Math.sin ( _angle * Math.PI / 180 ) * distance;
return _point;
},
/**
* Shows coordinates of this object
* @public
* @function
* @param {number} [offset=10] Offset of coordinates y origin
* @param {number} [fontSize=16] Coordinates font size
*/
showCoordinates ( offset = 10, fontSize = 16 )
{
let _text = new Text ( this.point, `( ${this.x}, ${this.y} )` );
_text.canvas = this.canvas;
_text.size = fontSize;
_text.options.shadow = false;
_text.offset.y = ( offset / 2 );
_text.options.shadow = true;
_text.shadow.color = new Rgb ( 255, 255, 255 );
_text.shadow.blur = 1;
_text.shadow.x = _text.shadow.y = 1;
_text.draw ( );
},
},
/**
* Utility draw collection functions
* @function UTILITIES.individual.set
*/
set:
{
/**
* Sets all option values throughout a collection
* @public
* @function
* @param {string} property Option property
* @param {boolean} value True || False
*/
all ( property, value )
{
let _ancestor = this.constructor.name.replace ( 'Collection', '' ).toLowerCase ( );
this [ `_${property}` ] = value; // Set: parent element
if ( this._master.length > 0 )
for ( let _item of this._master )
_item [ _ancestor ] [ property ] = value; // Set: child elements
},
/**
* Sets anchor's point against this object's point location
* @public
* @function
* @notes for Rectangle & cImage only
*/
anchorPoint ( )
{
[ this._anchor.x, this._anchor.y ] = [ this.x, this.y ];
switch ( this.anchor.align )
{
case 'center': this.anchor.x -= this.width / 2; this.anchor.y -= this.height / 2; break;
case 'top': this.anchor.x -= this.width / 2; /* ... do nothing */ break;
case 'topRight': this.anchor.x -= this.width; /* ... do nothing */ break;
case 'right': this.anchor.x -= this.width; this.anchor.y -= this.height / 2; break;
case 'bottomRight': this.anchor.x -= this.width; this.anchor.y -= this.height; break;
case 'bottom': this.anchor.x -= this.width / 2; this.anchor.y -= this.height; break;
case 'bottomLeft': /* ... do nothing */ this.anchor.y -= this.height; break;
case 'left': /* ... do nothing */ this.anchor.y -= this.height / 2; break;
case 'topLeft': /* ... do nothing */ /* ... do nothing */ break;
}
},
/**
* Sets shadow properties
* @public
* @function
*/
shadow ( )
{
this._canvas.shadowBlur = this._shadow.blur;
this._canvas.shadowOffsetX = this._shadow.x;
this._canvas.shadowOffsetY = this._shadow.y;
this._canvas.shadowColor = this._shadow.color.toCss ( );
},
/**
* Sets fill type of the associated object
* @public
* @function
*/
fillType ( )
{
/**
* Sets stops for gradient fill types
* @public
* @function
* @param {Object} gradient [description]
* @param {Array.<Stops>} stops [description]
*/
function _setStops ( gradient, stops )
{
for ( let _stop of stops )
gradient.addColorStop ( _stop.offset, _stop.color.toCss ( ) );
return gradient;
}
switch ( this.fill.type )
{
case 'solid':
this._canvas.fillStyle = this.fill.color.toCss ( );
break;
case 'linear':
let _linear = this._canvas.createLinearGradient ( this.fill.gradient.start.x, this.fill.gradient.start.y, this.fill.gradient.end.x, this.fill.gradient.end.y );
this._canvas.fillStyle = _setStops ( _linear, this.fill.gradient.stops );
break;
case 'radial':
let _radial = this._canvas.createRadialGradient ( this.fill.gradient.start.x, this.fill.gradient.start.y, this.fill.gradient.startRadius, this.fill.gradient.end.x, this.fill.gradient.end.y, this.fill.gradient.endRadius );
this._canvas.fillStyle = _setStops ( _radial, this.fill.gradient.stops );
break;
case 'conic':
let _conic = this._canvas.createConicGradient ( this.fill.gradient.angle, this.fill.gradient.point.y, this.fill.gradient.point.x );
this._canvas.fillStyle = _setStops ( _conic, this.fill.gradient.stops );
break;
case 'pattern':
this.fill._pattern.onload = ( ) =>
{
this._canvas.fillStyle = this._canvas.createPattern ( this.fill.pattern, this.fill.repetition );
this._canvas.fill ( );
}
break;
}
},
},
},
/**
* Utilities for animation functions
* @function UTILITIES.animation
*/
animation:
{
/**
* End animation
* @private
* @function
*/
end ( )
{
let _frame = requestAnimationFrame ( this.animate );
cancelAnimationFrame ( _frame );
return;
},
/**
* Returns an inverted angle; of 360 degrees
* @private
* @function
* @param {number} angle Angle to convert
* @return {number} Inverted angle
*/
getInvertedAngle ( angle )
{
return ( angle + 180 ) % 360;
}
}
}
/**
* Shared validation functions
* @namespace VALIDATION
*/
const VALIDATION =
{
/**
* Returns whether the passed value is a 256 color value; 0 - 255
* @public
* @memberof VALIDATION
* @function
* @param {number} value 256 color value; 0 - 255
* @return {boolean} True || False
*/
is256 ( value )
{
return ( ( typeof value === 'number' ) && ( value >= 0 && value <= 255 ) );
},
/**
* Returns whether the passed value is an Anchor alignment
* @public
* @memberof VALIDATION
* @function
* @param {string} value Anchor alignment
* @return {boolean} True || False
*/
isAnchor ( value )
{
let _options = [ 'center', 'top', 'topRight', 'right', 'bottomRight', 'bottom', 'bottomLeft', 'left', 'topLeft' ];
for ( let _option of _options ) if ( value === _option ) return true;
return false;
},
/**
* Returns whether the passed value is an Angle or equivalent value
* @public
* @memberof VALIDATION
* @function
* @param {Object|number} value Angle object or number value
* @return {boolean} True || False
*/
isAngle ( value )
{
if ( value instanceof Angle ) return true;
return ( ( typeof value === 'number' ) && ( value <= 360 ) );
},
/**
* Returns whether the passed value is an Anchor alignment
* @public
* @memberof VALIDATION
* @function
* @param {string} value Anchor alignment
* @return {boolean} True || False
*/
isAlign ( value )
{
let _options = [ 'center', 'top', 'topRight', 'right', 'bottomRight', 'bottom', 'bottomLeft', 'left', 'topLeft', 'start', 'end' ];
for ( let _option of _options ) if ( value === _option ) return true;
return false;
},
/**
* Returns whether the passed value is an alpha value; 0.00 - 1
* @public
* @memberof VALIDATION
* @function
* @param {number} value Alpha value; 0.00 - 1
* @return {boolean} True || False
*/
isAlpha ( value )
{
return ( ( typeof value === 'number' ) && ( value >= 0 && value <= 1 ) );
},
/**
* Returns whether the passed value is an Aspect
* @public
* @memberof VALIDATION
* @function
* @param {Object} value Aspect or object equivalent
* @return {boolean} True || False
*/
isAspect ( value )
{
if ( value instanceof Aspect ) return true;
// // // // // // // // // // // // // // // // // // // // // // // //
let _length = ( Object.keys ( value ).length == 2 );
let _width = ( value.hasOwnProperty ( 'width' ) ) ? ( typeof value.width === 'number' ) : false;
let _height = ( value.hasOwnProperty ( 'height' ) ) ? ( typeof value.height === 'number' ) : false;
return ( _width && _height && _length );
},
/**
* Returns whether the passed value is a blur value
* @public
* @memberof VALIDATION
* @function
* @param {number} value Blur value
* @return {boolean} True || False
*/
isBlur ( value )
{
return ( ( typeof value === 'number' ) && ( value >= 0 ) );
},
/**
* Returns whether the passed value is a CanvasLab object; Line, Circle, Rectangle, Text
* @public
* @memberof VALIDATION
* @function
* @param {Object} value CanvasLab object; Line, Circle, Rectangle, Text
* @return {boolean} True || False
*/
isCanvasLabObject ( value )
{
let _clObjects = [ Group, Line, Lines, Circle, Circles, Ellipse, Ellipses, Rectangle, Rectangles, RoundedRectangle, Text, Texts, cImage ]
for ( let _object of _clObjects )
if ( value instanceof _object )
return true;
return false;
},
/**
* Returns whether the passed value is a CSS color name
* @public
* @memberof VALIDATION
* @function
* @param {string} value CSS color name
* @return {boolean} True || False
*/
isColorName ( value )
{
let _colors =
{
A:
[
'aliceblue',
'antiquewhite',
'aqua',
'aquamarine',
'azure'
],
B:
[
'beige',
'bisque',
'black',
'blanchedalmond',
'blue',
'blueviolet',
'brown',
'burlywood'
],
C:
[
'cadetblue',
'chartreuse',
'chocolate',
'coral',
'cornflowerblue',
'cornsilk',
'crimson',
'cyan'
],
D:
[
'darkblue',
'darkcyan',
'darkgoldenrod',
'darkgray',
'darkgreen',
'darkkhaki',
'darkmagenta',
'darkolivegreen',
'darkorange',
'darkorchid',
'darkred',
'darksalmon',
'darkseagreen',
'darkslateblue',
'darkslategray',
'darkturquoise',
'darkviolet',
'deeppink',
'deepskyblue',
'dimgray',
'dodgerblue'
],
E: [ ],
F:
[
'firebrick',
'floralwhite',
'forestgreen',
'fuchsia'
],
G:
[
'gainsboro',
'ghostwhite',
'gold',
'goldenrod',
'gray',
'green',
'greenyellow'
],
H:
[
'honeydew',
'hotpink'
],
I:
[
'indianred',
'indigo',
'ivory'
],
J: [ ],
K:
[
'khaki'
],
L:
[
'lavender',
'lavenderblush',
'lawngreen',
'lemonchiffon',
'lightblue',
'lightcoral',
'lightcyan',
'lightgoldenrodyellow',
'lightgreen',
'lightgrey',
'lightpink',
'lightsalmon',
'lightseagreen',
'lightskyblue',
'lightslategray',
'lightsteelblue',
'lightyellow',
'lime',
'limegreen',
'linen'
],
M:
[
'magenta',
'maroon',
'mediumaquamarine',
'mediumblue',
'mediumorchid',
'mediumpurple',
'mediumseagreen',
'mediumslateblue',
'mediumspringgreen',
'mediumturquoise',
'mediumvioletred',
'midnightblue',
'mintcream',
'mistyrose',
'moccasin'
],
N:
[
'navajowhite',
'navy',
'navyblue'
],
O:
[
'oldlace',
'olive',
'olivedrab',
'orange',
'orangered',
'orchid'
],
P:
[
'palegoldenrod',
'palegreen',
'paleturquoise',
'palevioletred',
'papayawhip',
'peachpuff',
'peru',
'pink',
'plum',
'powderblue',
'purple'
],
Q: [ ],
R:
[
'red',
'rosybrown',
'royalblue'
],
S:
[
'saddlebrown',
'salmon',
'sandybrown',
'seagreen',
'seashell',
'sienna',
'silver',
'skyblue',
'slateblue',
'slategray',
'snow',
'springgreen',
'steelblue'
],
T:
[
'tan',
'teal',
'thistle',
'tomato',
'turquoise'
],
U: [ ],
V:
[
'violet'
],
W:
[
'wheat',
'white',
'whitesmoke'
],
X: [ ],
Y:
[
'yellow',
'yellowgreen'
],
Z: [ ]
}
return _colors [ value [ 0 ].toUpperCase ( ) ].includes ( value );
},
/**
* Returns whether the passed value is a color model
* @public
* @memberof VALIDATION
* @function
* @param {Object} value Color model or object equivalent
* @return {boolean} True || False
*/
isColorModel ( value )
{
return ( value instanceof Rgb ) ? true : false;
},
/**
* Returns whether the passed value is an array of Control Point values
* @public
* @memberof VALIDATION
* @function
* @param {Array.<number>} value Array of Control Points
* @return {boolean} True || False
*/
isControlPoint ( value )
{
let _result = false;
if ( Array.isArray ( value ) )
if ( value.length === 4 )
{
for ( let _entry of value )
if ( typeof _entry != 'number' ) return _result;
_result = true;
}
return _result;
},
/**
* Returns whether the passed value is a decimal value; 0.00 - 1
* @public
* @memberof VALIDATION
* @function
* @param {number} value Decimal value; 0.00 - 1
* @return {boolean} True || False
*/
isDecimal ( value )
{
return ( ( typeof value === 'number' ) && ( value >= 0 && value <= 1 ) );
},
/**
* Returns whether the passed value is a degree
* @public
* @memberof VALIDATION
* @function
* @param {number} value Degree
* @return {boolean} True || False
*/
isDegree ( value )
{
return ( ( typeof value === 'number' ) && ( value <= 360 ) );
},
/**
* Returns whether the passed value is a fill type
* @public
* @memberof VALIDATION
* @function
* @param {string} value Fill type
* @return {boolean} True || False
*/
isFillType ( value )
{
return [ 'solid', 'linear', 'radial', 'conic', 'pattern' ].includes ( value );
},
/**
* Returns whether the passed value is a gradient object
* @public
* @memberof VALIDATION
* @function
* @param {Object} value Gradient object
* @return {boolean} True || False
*/
isGradient ( value )
{
if ( value instanceof Linear ) return true;
if ( value instanceof Radial ) return true;
if ( value instanceof Conic ) return true;
return false;
},
/**
* Returns whether the passed value is an element id within the DOM
* @public
* @memberof VALIDATION
* @function
* @param {string} value Element id
* @return {boolean} True || False
*/
isInDom ( value )
{
return ( document.getElementById ( value ) != null );
},
/**
* Returns whether the passed value is a Number value
* @public
* @memberof VALIDATION
* @function
* @param {number} value Number value
* @return {boolean} True || False
*/
isNumber ( value )
{
return ( ( typeof value === 'number') && !isNaN ( value ) );
},
/**
* Returns whether the passed value is a Point
* @public
* @memberof VALIDATION
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
*/
isPoint ( value )
{
if ( value instanceof Point ) return true;
// // // // // // // // // // // // // // // // // // // // // // // //
let _length = ( Object.keys ( value ).length == 2 );
let _x = ( value.hasOwnProperty ( 'x' ) ) ? ( typeof value.x === 'number' ) : false;
let _y = ( value.hasOwnProperty ( 'y' ) ) ? ( typeof value.y === 'number' ) : false;
return ( _length && _x && _y );
},
/**
* Returns whether the passed value is a Point & Aspect
* @public
* @memberof VALIDATION
* @function
* @param {Object} value Object
* @param {Point} value.point Point object
* @param {Aspect} value.aspect Aspect object
* @return {boolean} True || False
*/
isPointNAspect ( value )
{
let _point = undefined;
let _aspect = undefined;
if ( typeof value === 'object' )
if ( ( Object.keys ( value ).length == 2 ) )
{
_point = ( value.hasOwnProperty ( 'point' ) ) ? this._isPoint ( value.point )
: false;
_aspect = ( value.hasOwnProperty ( 'aspect' ) ) ? this._isAspect ( value.aspect )
: false;
}
return ( _point && _aspect );
},
/**
* Returns whether the passed value is a radian; 0 - 6.28...
* @public
* @memberof VALIDATION
* @function
* @param {number} value Radian value; 0 - 6.28...
* @return {boolean} True || False
*/
isRadian ( value )
{
return ( ( typeof value === 'number' ) && ( value >= 0 && value <= 6.283185307179586 ) );
},
/**
* Returns whether the passed value is a radius value
* @public
* @memberof VALIDATION
* @function
* @param {number} value Radius value
* @return {boolean} True || False
*/
isRadius ( value )
{
return ( ( typeof value === 'number' ) && ( value > 0 ) );
},
/**
* Returns whether the passed value is a repetition value
* @public
* @memberof VALIDATION
* @function
* @param {string} value Repetition value
* @return {boolean} True || False
*/
isRepetition ( value )
{
return [ 'repeat', 'repeat-x', 'repeat-y', 'no-repeat' ].includes ( value );
},
/**
* Returns whether the passed value is an Array of segment values
* @public
* @memberof VALIDATION
* @function
* @param {Array.<number>} value Array of segment values
* @return {boolean} True || False
*/
isSegments ( value )
{
function isArrayNumeric ( value )
{
let _result = undefined;
for ( let _element of value )
{
_result = ( typeof _element != 'number' ) ? false : true;
if ( _result == false ) break;
}
return _result;
}
return ( Array.isArray ( value ) ) ? isArrayNumeric ( value ) : false;
},
/**
* Returns whether the passed value is a Stop or object equivalent
* @public
* @memberof VALIDATION
* @function
* @param {Object} value Stop or object equivalent
* @return {boolean} True || False
*/
isStop ( value )
{
if ( value instanceof Stop ) return true;
// // // // // // // // // // // // // // // // // // // // // // // //
let _object = ( typeof value === 'object' && ! Array.isArray ( value ) );
let _offset = ( value.hasOwnProperty ( 'offset' ) ) ? VALIDATION.isNumber ( value.offset ) : false;
let _color = ( value.hasOwnProperty ( 'color' ) ) ? VALIDATION.isColorModel ( value.color ) : false;
return ( _object && _offset && _color );
},
/**
* Returns whether the passed value is a stroke type
* @public
* @memberof VALIDATION
* @function
* @param {string} value Stroke type
* @return {boolean} True || False
*/
isStrokeType ( value )
{
return ( ( typeof value === 'string' ) && [ 'solid', 'dashed' ].includes ( value ) );
},
/**
* Returns whether the passed value is a Template
* @public
* @memberof VALIDATION
* @function
* @param {Template} value Template object
* @return {boolean} True || False
*/
isTemplate ( value )
{
/**
* Returns classes functions
* @private
* @memberof VALIDATION
* @function
* @param {Object} object Template object
* @return {Array} Array of functions
*/
function _getClassFunctions ( object )
{
let _results = new Array;
let _object = object;
do
{
_results.push ( ... Object.getOwnPropertyNames ( _object ) );
}
while ( _object = Object.getPrototypeOf ( _object ) );
_results = _results.sort ( ).filter ( ( element, i, array ) =>
{
if ( element != array [ i + 1 ] && typeof object [ element ] == 'function' )
return true;
} );
return _results;
}
if ( value != undefined )
{
let _instance = eval ( `new ${value.constructor.name};` );
let _functions = _getClassFunctions ( value );
let _point = ( Object.hasOwn ( _instance, '_point' ) );
let _master = ( Object.hasOwn ( _instance, '_master' ) );
let _init = _functions.includes ( 'init' );
return ( _point && _master && _init );
}
else
return false;
},
/**
* Returns whether the passed value is a Transition
* @public
* @memberof VALIDATION
* @function
* @param {Transition} value Transition object
* @return {boolean} True || False
*/
isTransition ( value )
{
if ( value != undefined )
{
let _instance = eval ( `new ${value.constructor.name};` );
let _transitions = ( Object.hasOwn ( _instance, '_transitions' ) );
let _template = ( Object.hasOwn ( _instance, '_template' ) );
return ( _transitions && _template );
}
else
return false;
},
/**
* Returns whether the passed value is a width value
* @public
* @memberof VALIDATION
* @function
* @param {number} value Width value
* @return {boolean} True || False
*/
isWidth ( value )
{
return ( ( typeof value === 'number' ) && ( value >= 0 ) );
}
}
//// CANVASLAB ///////////////////////////////////////
/**
* @class {Object} canvasLab CanvasLab core application
* @property {Object} canvas Main canvas context
* @property {Array} canvases Array of all canvas contexts
* @property {string} font Main font type
*/
class canvasLab
{
_canvas = undefined;
_canvases = undefined;
_font = undefined;
_rotation = 0;
#application = new Application;
/**
* Create a canvasLab object
* @property {string} canvasId Canvas identifier
*/
constructor ( canvas )
{
//// COMPOSITION ////////////////////////////
this._isInDom = VALIDATION.isInDom;
this._init ( );
this.canvas = canvas;
}
//// [ CANVAS ] ////////////////////////////////////
/**
* Set canvas value
* @public
* @function
* @param {string} value Canvas identifier
*/
set canvas ( value )
{
this._canvas = ( this._isInDom ( value ) ) ? document.getElementById ( value ).getContext ( '2d' )
: this._canvas;
}
/**
* Get canvas value
* @readOnly
* @function
* @return {string} Canvas identifier
* @see {@link discrete.canvas}
*/
get canvas ( )
{
return this._canvas.canvas.id;
}
//// [ CANVASES ] ////////////////////////////////
/**
* Set canvas value
* @public
* @function
* @param {string} canvasId Canvas identifier
*/
set canvases ( canvasId )
{
let _canvas = ( this._isInDom ( canvasId ) ) ? document.getElementById ( canvasId ).getContext ( '2d' )
: undefined;
if ( this._canvases == undefined )
this._canvases = new Array;
if ( _canvas != undefined )
this._canvases.push ( _canvas );
}
/**
* Set canvas value
* @readOnly
* @function
* @return {Array} Array of canvas contexts
*/
get canvases ( )
{
return this._canvases;
}
//// [ FONT ] ////////////////////////////////////
/**
* Set main font type
* @public
* @function
* @param {string} font Main font type
*/
set font ( value )
{
this._font = ( typeof value === 'string' ) ? value : this._font;
}
/**
* Get main font type
* @readOnly
* @function
* @return {string} font Main font type
*/
get font ( )
{
return this._canvas.font;
}
//// [ ROTATION ] ////////////////////////////////
set rotation ( value )
{
this._rotation = value;
}
get rotation ( )
{
return this._rotation;
}
//// [ APPLICATION ] /////////////////////////////
/**
* Returns this application
* @readOnly
* @function
* @return {Application} Canvas Lab application
*/
get application ( )
{
return this.#application;
}
//// DOM /////////////////////////////////////////
/**
* Get dom details
* @readOnly
* @function
* @return {Object} DOM details
*/
get dom ( )
{
return this.#application.dom;
}
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is an element id within the DOM
* @private
* @function
* @param {string} value Element id
* @return {boolean} True || False
* @see {@link VALIDATION.isInDom}
*/
_isInDom ( ) { }
//// UTILITIES ////////////////////////////////////
/**
* Returns the center X & Y coordinates of the present canvas
* @public
* @function
* @return {Point} Center X & Y coordinates
*/
get center ( )
{
return new Point (
this._canvas.canvas.clientWidth / 2, // X coordinate
this._canvas.canvas.clientHeight / 2 // Y coordinate
);
}
/**
* Clears canvas
* @private
* @function
*/
clearCanvas ( )
{
let _canvas = document.getElementById ( this.canvas );
if ( _canvas ) // @TODO: identify why this check has to take place periodically !
this._canvas.clearRect ( 0, 0, _canvas.width, _canvas.height );
}
/**
* Animate the transition passed
* @property {Transition} transition Transition animation
*/
animate ( transition = { object, timing, period, change } )
{
if ( transition )
this.#application.animation = transition;
}
//// INITIALIZE ////////////////////////////////////
/**
* CanvasLab initializer
* @private
* @function
*/
_init ( )
{
let _canvases = document.getElementsByTagName ( 'canvas' );
if ( typeof _canvases === 'object' && this._canvases === undefined )
for ( let _id in _canvases )
if ( _id == 0 )
this.canvas = _canvases [ _id ].id;
else
this.canvases = _canvases [ _id ].id;
}
}
//// SUBJECTS ////////////////////////////////////////
/**
* @class {Object} Rgb RGB color model
* @property {number} [red=0] Red value; 0 - 255
* @property {number} [green=0] Green value; 0 - 255
* @property {number} [blue=0] Blue value; 0 - 255
* @property {number} [alpha=1] Alpha value; 0 - 1 (decimal)
*/
class Rgb
{
_red = 0;
_green = 0;
_blue = 0;
_alpha = 1;
/**
* Create an RGB color model
* @param {number} red Red value
* @param {number} green Green value
* @param {number} blue Blue value
* @param {number} alpha Alpha value
*/
constructor ( red, green, blue, alpha )
{
//// COMPOSITION /////////////////////////////
this._is256 = VALIDATION.is256;
Object.defineProperty ( this, 'alpha', PROPERTY_BLOCKS.individual.alpha );
this.red = red;
this.green = green;
this.blue = blue;
this.alpha = ( alpha === undefined ) ? 1 : alpha;
}
//// PROPERTIES //////////////////////////////////
//// [ RED ] ///////////////////////////
/**
* Sets the red value
* @public
* @function
* @param {number} red Red value; 0 - 255
*/
set red ( value )
{
this._red = this._is256 ( value ) ? Math.round ( value ) : this._red;
}
/**
* Gets the red value
* @readOnly
* @function
* @return {number} Red value; 0 - 255
*/
get red ( )
{
return this._red;
}
//// [ GREEN ] /////////////////////////
/**
* Sets the green value
* @public
* @function
* @param {number} green Green value; 0 - 255
*/
set green ( value )
{
this._green = this._is256 ( value ) ? Math.round ( value ) : this._green;
}
/**
* Gets the green value
* @readOnly
* @function
* @return {number} Green value; 0 - 255
*/
get green ( )
{
return this._green;
}
//// [ BLUE ] //////////////////////////
/**
* Sets the blue value
* @public
* @function
* @param {number} blue Blue value; 0 - 255
*/
set blue ( value )
{
this._blue = this._is256 ( value ) ? Math.round ( value ) : this._blue;
}
/**
* Gets the blue value
* @readOnly
* @function
* @return {number} Blue value; 0 - 255
*/
get blue ( )
{
return this._blue;
}
//// [ ALPHA ] /////////////////////////
/**
* Set alpha value
* @public
* @function
* @param {number} value Alpha value; 0 - 1
* @see {@link PROPERTY_BLOCKS.individual.alpha}
*/
set alpha ( value ) { }
/**
* Set alpha value
* @public
* @function
* @return {number} Alpha value; 0 - 1
* @see {@link PROPERTY_BLOCKS.individual.alpha}
*/
get alpha ( ) { }
//// VALIDATION //////////////////////////////////
/**
* Returns whether the passed value is a 256 color value; 0 - 255
* @private
* @function
* @param {number} value 256 color value; 0 - 255
* @return {boolean} True || False
* @see {@link VALIDATION.is256}
*/
_is256 ( ) { }
//// UTILITIES ///////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Linear interpolation color transitions
* @private
* @function
* @param {Object} start Color model & values
* @param {Object} end Color model & values
* @param {number} progress Progress time unit; 0.00 - 1.00
* @param {number} max Maximum number of steps between interpolation
*/
_lerp ( start, end, progress, max )
{
return Math.round ( start + ( end - start ) * progress / max );
}
/**
* Linear interpolation of Rgb values
* @private
* @function
* @param {Object} start Color model & values
* @param {Object} end Color model & values
* @param {number} progress Progress time unit; 0.00 - 1.00
* @param {number} max Maximum number of steps between interpolation
*/
_lerpRgb ( start, end, progress, max )
{
this._red = this._lerp ( start.red, end.red, progress, max );
this._green = this._lerp ( start.green, end.green, progress, max );
this._blue = this._lerp ( start.blue, end.blue, progress, max );
}
//// + PUBLIC //////////////////////
/**
* Color cycling
* @public
* @function
* @param {Rgb} start Color model & values
* @param {Rgb} end Color model & values
* @param {number} progress Progress time unit; 0.00 - 1.00
* @param {number} max Maximum number of steps between interpolation
*/
cycle ( start, end, progress, max )
{
this._lerpRgb ( start, end, progress, max );
}
/**
* Returns a CSS compatible <color> string value
* @public
* @function
* @return {string} CSS <color> string
*/
toCss ( )
{
return `rgb(${this.red} ${this.green} ${this.blue} / ${this.alpha * 100}%)`;
}
}
/**
* @class {Object} Options Options for collections
* @property {boolean} [anchor=false] Show anchor
* @property {boolean} [axis=false] Show axis
* @property {boolean} [border=false] Show border
* @property {boolean} [coordinates=false] Show coordinates
* @property {boolean} [controlPoints=false] Show control points
* @property {boolean} [points=false] Show points
* @property {boolean} [shadow=false] Show shadow
* @property {Object} master Master object
*/
class Options
{
_anchor = false;
_axis = false;
_border = false;
_coordinates = false;
_controlPoints = false;
_points = false;
_shadow = false;
_master = undefined;
/**
* Create an options object
* @param {boolean} anchor Show anchor
* @param {boolean} axis Show axis
* @param {boolean} border Show border
* @param {boolean} coordinates Show coordinates
* @param {boolean} controlPoints Show control points
* @param {boolean} points Show points
* @param {boolean} shadow Show shadow
*/
constructor ( anchor, axis, border, coordinates, controlPoints, points, shadow )
{
//// COMPOSITION ////////////////////////////
this._isCanvasLabObject = VALIDATION.isCanvasLabObject;
Object.defineProperty ( this, 'master', PROPERTY_BLOCKS.individual.master );
this.axis = axis;
this.anchor = anchor;
this.border = border;
this.coordinates = coordinates;
this.controlPoints = controlPoints;
this.points = points;
this.shadow = shadow;
}
//// PROPERTIES //////////////////////////////////
//// [ ANCHOR ] ////////////////////
/**
* Set anchor value
* @public
* @function
* @param {boolean} value Anchor; true | false
*/
set anchor ( value )
{
this._anchor = ( typeof value == 'boolean' ) ? value : this._anchor;
}
/**
* Get anchor value
* @readOnly
* @function
* @return {boolean} Anchor; true | false
*/
get anchor ( )
{
return this._anchor;
}
//// [ AXIS ] //////////////////////
/**
* Set axis value
* @public
* @function
* @param {boolean} value Axis; true | false
*/
set axis ( value )
{
this._axis = ( typeof value === 'boolean' ) ? value : this._axis;
}
/**
* Get axis value
* @readOnly
* @function
* @return {boolean} Axis; true | false
*/
get axis ( )
{
return this._axis;
}
//// [ BORDER ] ////////////////////
/**
* Set border value
* @public
* @function
* @param {boolean} value Border; true | false
*/
set border ( value )
{
this._border = ( typeof value === 'boolean' ) ? value : this._border;
}
/**
* Get border value
* @readOnly
* @function
* @return {boolean} Border; true | false
*/
get border ( )
{
return this._border;
}
//// [ COORDINATES ] //////////////
/**
* Set coordinates value
* @public
* @function
* @param {boolean} value Coordinates; true | false
*/
set coordinates ( value )
{
this._coordinates = ( typeof value === 'boolean' ) ? value : this._coordinates;
}
/**
* Get coordinates value
* @readOnly
* @function
* @return {boolean} Coordinates; true | false
*/
get coordinates ( )
{
return this._coordinates;
}
//// [ CONTROL POINTS ] ////////////
/**
* Set control points value
* @public
* @function
* @param {boolean} value Control points; true | false
*/
set controlPoints ( value )
{
this._controlPoints = ( typeof value === 'boolean' ) ? value : this._controlPoints;
}
/**
* Get control points value
* @readOnly
* @function
* @return {boolean} Control points; true | false
*/
get controlPoints ( )
{
return this._controlPoints;
}
//// [ SHADOW ] ////////////////////
/**
* Set shadow value
* @public
* @function
* @param {boolean} value Shadow; true | false
*/
set shadow ( value )
{
this._shadow = ( typeof value === 'boolean' ) ? value : this._shadow;
}
/**
* Get shadow value
* @public
* @function
* @return {boolean} Shadow; true | false
*/
get shadow ( )
{
return this._shadow;
}
//// [ MASTER ] ////////////////////
/**
* Set master object
* @public
* @function
* @param {clObject} value Canvas Lab object
* @see {@link PROPERTY_BLOCKS.individual.master}
*/
set master ( value ) { }
/**
* Get master object
* @public
* @function
* @return {clObject} Master Canvas Lab object
* @see {@link PROPERTY_BLOCKS.individual.master}
*/
get master ( ) { }
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is a CanvasLab object; Line, Circle, Rectangle, Text
* @private
* @function
* @param {Object} value CanvasLab object; Line, Circle, Rectangle, Text
* @return {boolean} True || False
* @see {@link VALIDATION.isCanvasLabObject}
*/
_isCanvasLabObject ( ) { }
}
/**
* @class {Object} Anchor Anchor object
* @property {Point} point X & Y axis coordinates
* @property {string} align Anchor alignment
*/
class Anchor
{
_point = new Point;
_align = 'center';
constructor ( )
{
//// COMPOSITION ////////////////////////////
this._isAlign = VALIDATION.isAlign;
Object.defineProperty ( this, 'point', PROPERTY_BLOCKS.individual.point );
Object.defineProperty ( this, 'x', PROPERTY_BLOCKS.individual.pointX );
Object.defineProperty ( this, 'y', PROPERTY_BLOCKS.individual.pointY );
}
//// PROPERTIES //////////////////////////////////
//// [ POINT ] ////////////////////
/**
* Set point
* @public
* @function
* @param {Point} point X & Y axis coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
set point ( value ) { }
/**
* Get point
* @public
* @function
* @return {Point} X & Y axis coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
get point ( ) { }
/**
* Set x-axis value
* @public
* @function
* @param {number} value X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
set x ( value ) { }
/**
* Get x-axis value
* @readOnly
* @function
* @return {number} X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
get x ( ) { }
/**
* Set y-axis value
* @public
* @function
* @param {number} value Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
set y ( value ) { }
/**
* Get y-axis value
* @readOnly
* @function
* @return {number} Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
get y ( ) { }
//// [ ALIGN ] /////////////////////
/**
* Set anchor alignment
* @public
* @function
* @param {string} value Anchor alignment
*/
set align ( value )
{
this._align = ( this._isAlign ( value ) ) ? value : this._align;
}
/**
* Get anchor alignment
* @readOnly
* @function
* @return {string} Anchor alignment
*/
get align ( )
{
return this._align;
}
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is an Anchor alignment
* @private
* @function
* @param {string} value Anchor alignment
* @return {boolean} True || False
* @see {@link VALIDATION.isAnchor}
*/
_isAnchor ( ) { }
}
/**
* @class {Object} Angle Angle properties of associated object
* @property {number} [start=0] The start of the angle, in radians; measured from the positive x-axis
* @property {number} [end=360] The end of the angle, in radians; measured from the positive x-axis
* @property {boolean} [clockwise=true] Path arc clockwise
*/
class Angle
{
_start = 0;
_end = 360;
_clockwise = true;
/**
* Create an angle
* @param {number} start The angle at which the arc starts in degrees, measured from the positive x-axis
* @param {number} end The angle at which the arc ends in degrees, measured from the positive x-axis
* @param {boolean} clockwise Draws the arc clockwise between the start and end angles
*/
constructor ( start, end, clockwise )
{
//// COMPOSITION ////////////////////////////
this._isDegree = VALIDATION.isDegree;
this._isRadian = VALIDATION.isRadian;
this.start = start;
this.end = end;
this.clockwise = clockwise;
}
//// PROPERTIES //////////////////////////////////
//// [ START ] /////////////////////
/**
* Set start angle
* @public
* @function
* @param {number} value Start angle; in degrees
*/
set start ( value )
{
this._start = ( this._isDegree ( value ) ) ? value : this._start;
}
/**
* Get start angle
* @readOnly
* @function
* @return {number} Start value; in degrees
*/
get start ( )
{
return this._start;
}
//// [ END ] ///////////////////////
/**
* Set end angle
* @public
* @function
* @param {number} value End angle; in degrees
*/
set end ( value )
{
this._end = ( this._isDegree ( value ) ) ? value : this._end;
}
/**
* Get end angle
* @readOnly
* @function
* @return {number} End angle; in degrees
*/
get end ( )
{
return this._end;
}
//// [ CLOCKWISE ] /////////////////
/**
* Set clockwise
* @public
* @function
* @param {boolean} value Clockwise; true | false
*/
set clockwise ( value )
{
this._clockwise = ( typeof value === 'boolean' ) ? value : this._clockwise;
}
/**
* Get clockwise
* @readOnly
* @function
* @return {boolean} Clockwise; true | false
*/
get clockwise ( )
{
return this._clockwise;
}
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is a degree
* @private
* @function
* @param {number} value Degree
* @return {boolean} True || False
* @see {@link VALIDATION.isDegree}
*/
_isDegree ( ) { }
/**
* Returns whether the passed value is a radian; 0 - 6.28...
* @private
* @function
* @param {number} value Radian value; 0 - 6.28...
* @return {boolean} True || False
* @see {@link VALIDATION.isRadian}
*/
_isRadian ( ) { }
//// UTILITIES ////////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Convert radian to degree
* @private
* @function
* @param {number} value Radian
* @return {number} Conversion in degrees
*/
_convert2Degree ( value )
{
return ( this._isRadian ) ? ( value * ( Math.PI / 180 ) ) : console.warn ( `${value} is not a radian value !` );
}
/**
* Convert degree to radian
* @private
* @function
* @param {number} value Degree
* @return {number} Conversion in radians
*/
_convert2Radian ( value )
{
return ( this._isDegree ) ? ( value * ( Math.PI / 180 ) ) : console.warn ( `${value} is not a degree value !` );
}
//// + PUBLIC //////////////////////
/**
* Get start angle in radians
* @readOnly
* @function
* @return {number} Start value; to radians
*/
get startInRadians ( )
{
return this._convert2Radian ( this.start );
}
/**
* Get end angle in radians
* @readOnly
* @function
* @return {number} End value; in radians
*/
get endInRadians ( )
{
return this._convert2Radian ( this.end );
}
}
/**
* @class {Object} Aspect Aspect dimensions of associated object
* @property {number} [width=0] Width
* @property {number} [height=0] Height
*/
class Aspect
{
_width = 0;
_height = 0;
#offset = new Point;
/**
* Create an aspect
* @param {number} width Width of aspect
* @param {number} height Height of aspect
*/
constructor ( width, height )
{
//// COMPOSITION ////////////////////////////
this._isPoint = VALIDATION.isPoint;
this.width = width;
this.height = height;
}
//// PROPERTIES //////////////////////////////////
//// [ WIDTH ] /////////////////////
/**
* Set width
* @public
* @function
* @param {number} value Width value
*/
set width ( value )
{
this._width = ( typeof value === 'number' && value > 0 ) ? value : this._width;
}
/**
* Get width
* @readOnly
* @function
* @return {number} Width value
*/
get width ( )
{
return this._width;
}
//// [ HEIGHT ] ////////////////////
/**
* Set height
* @public
* @function
* @param {number} value Height value
*/
set height ( value )
{
this._height = ( typeof value === 'number' && value > 0 ) ? value : this._height;
}
/**
* Get height
* @readOnly
* @function
* @return {number} Height value
*/
get height ( )
{
return this._height;
}
//// [ OFFSET ] ////////////////////
/**
* Get offset
* @readOnly
* @function
* @return {Point} Aspect offset
*/
get offset ( )
{
return this.#offset;
}
//// VALIDATION //////////////////////////////////
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
//// UTILITIES ///////////////////////////////////
/**
* Get center of aspect
* @readOnly
* @function
* @return {Point} Center point of this aspect
*/
get center ( )
{
let _point = new Point ( this.widthCenter, this.heightCenter );
return _point;
}
/**
* Get center of height
* @readOnly
* @function
* @return {number} Center of height
*/
get heightCenter ( )
{
return this.height / 2;
}
/**
* Get center of width
* @readOnly
* @function
* @return {number} Center of with
*/
get widthCenter ( )
{
return this.width / 2;
}
}
/**
* @class {Object} ControlPoints Defines the shape of a bezier curve
* @property {number} p0 Control point one
* @property {number} p1 Control point two
* @property {number} p2 Control point three
* @property {number} p3 Control point four
*/
class ControlPoints
{
_p0 = 0;
_p1 = 0;
_p2 = 0;
_p3 = 0;
/**
* Create control points
* @param {number} p0 Control point one
* @param {number} p1 Control point two
* @param {number} p2 Control point three
* @param {number} p3 Control point four
*/
constructor ( p0, p1, p2, p3 )
{
//// COMPOSITION ////////////////////////////
this._isNumber = VALIDATION.isNumber;
this._isControlPoint = VALIDATION.isControlPoint;
this.p0 = p0;
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
}
//// PROPERTIES //////////////////////////////////
//// [ ONE ] ///////////////////////
/**
* Set control point one
* @public
* @function
* @param {number} value Control point one
*/
set p0 ( value )
{
this._p0 = ( this._isNumber ( value ) ) ? value : this._p0;
}
/**
* Get control point one
* @readOnly
* @function
* @return {number} Control point one
*/
get p0 ( )
{
return this._p0;
}
//// [ TWO ] ///////////////////////
/**
* Set control point one
* @public
* @function
* @param {number} value Control point two
*/
set p1 ( value )
{
this._p1 = ( this._isNumber ( value ) ) ? value : this._p1;
}
/**
* Get control point one
* @readOnly
* @function
* @return {number} Control point two
*/
get p1 ( )
{
return this._p1;
}
//// [ THREE ] /////////////////////
/**
* Set control point one
* @public
* @function
* @param {number} value Control point three
*/
set p2 ( value )
{
this._p2 = ( this._isNumber ( value ) ) ? value : this._p2;
}
/**
* Get control point one
* @readOnly
* @function
* @return {number} Control point three
*/
get p2 ( )
{
return this._p2;
}
//// [ FOUR ] //////////////////////
/**
* Set control point one
* @public
* @function
* @param {number} value Control point four
*/
set p3 ( value )
{
this._p3 = ( this._isNumber ( value ) ) ? value : this._p3;
}
/**
* Get control point one
* @readOnly
* @function
* @return {number} Control point four
*/
get p3 ( )
{
return this._p3;
}
//// [ POINTS ] ////////////////////
/**
* Set points
* @public
* @function
* @param {number} value Points of object
*/
set points ( value )
{
if ( this._isControlPoint ( value ) )
[ this.p0, this.p1, this.p2, this.p3 ] = [ value [ 0 ], value [ 1 ], value [ 2 ], value [ 3 ] ];
}
/**
* Get points
* @public
* @function
* @return {number} Points of object
*/
get points ( )
{
return [ this.p0, this.p1, this.p2, this.p3 ];
}
//// VALIDATION //////////////////////////////////
/**
* Returns whether the passed value is an array of Control Point values
* @private
* @memberof VALIDATION
* @function
* @param {Array.<number>} value Array of Control Points
* @return {boolean} True || False
* @see {@link VALIDATION.isControlPoint}
*/
_isControlPoint ( ) { }
/**
* Returns whether the passed value is a Number value
* @private
* @function
* @param {number} value Number value
* @return {boolean} True || False
* @see {@link VALIDATION.isNumber}
*/
_isNumber ( ) { }
}
/**
* @class {Object} Font Font base class for text objects
* @property {string} type Font type or face; typography name
* @property {number} [size=24] Size of font; in pixels
* @property {string} [weight='normal'] Weight of font
* @property {number} maxWidth Font's maximum width
* @property {Point} offset Point offset coordinates
*/
class Font
{
_type = undefined;
_size = 24;
_weight = 'normal';
_maxWidth = undefined;
_offset = new Point;
#_options = { weight: [ 'normal', 'bold', 'italic' ] }
//// PROPERTIES //////////////////////////////////
//// [ TYPE ] //////////////////////
/**
* Set font type
* @public
* @function
* @param {string} value Type face; typography name
*/
set type ( value )
{
this._type = ( typeof value === 'string' ) ? value : this._type;
if ( value === undefined )
if ( window.canvaslab instanceof canvasLab )
{
let _regex = /(\w+(\s))?(?<size>\d+)px\s(?<type>\w.+)/;
let _font = canvaslab.font;
this._type = ( _regex.test ( _font ) )
? _regex.exec ( _font ).groups [ 'type' ]
: this._type;
}
else
console.warn ( 'canvaslab is not instantiated !' );
}
/**
* Get type
* @readOnly
* @function
* @return {string} Type face; typography name
*/
get type ( )
{
return this._type;
}
//// [ SIZE ] //////////////////////
/**
* Set font size
* @public
* @function
* @param {number} value Font size
*/
set size ( value )
{
this._size = ( typeof value === 'number' ) ? value : this._size;
}
/**
* Get font size
* @readOnly
* @function
* @return {number} Font size
*/
get size ( )
{
return this._size;
}
//// [ WEIGHT ] ////////////////////
/**
* Set font weight
* @public
* @function
* @param {number} value Font weight
*/
set weight ( value )
{
let _currentValue = this._weight;
for ( let _option of this.#_options.weight )
{
this._weight = ( value == _option ) ? value : this._weight;
if ( this._weight != _currentValue ) break;
}
}
/**
* Get font weight
* @readOnly
* @function
* @return {number} Font weight
*/
get weight ( )
{
return this._weight;
}
//// [ MAXWIDTH ] //////////////////
/**
* Set font's max width
* @public
* @function
* @param {number} value Max width
*/
set maxWidth ( value )
{
this._maxWidth = ( typeof value === 'number' ) ? value : this._maxWidth;
}
/**
* Get font's max width
* @readOnly
* @function
* @return {number} Max width
*/
get maxWidth ( )
{
return this._maxWidth;
}
//// [ OFFSET ] ////////////////////
/**
* Get font's offset
* @public
* @function
* @return {Point} Font's offset; ( x, y )
*/
get offset ( )
{
return this._offset;
}
//// UTILITIES ////////////////////////////////////
/**
* Set font
* @public
* @function
* @param {string} value CSS style font property syntax
*/
set font ( value )
{
this.font = ( /(\w+(-\w+?)?|[1-9][0][0]?)(\s?)\d{1,3}px\s\w.+/.test ( value ) ) ? value : this.font;
}
/**
* Get font
* @readOnly
* @function
* @return {string} CSS style font property syntax
*/
get font ( )
{
return `${this._weight} ${this._size}px ${this._type}`;
}
}
/**
* @class {Object} Point X & Y coordinates for an object
* @property {number} [x=0] X - x-axis coordinate
* @property {number} [y=0] Y - y-axis coordinate
* @property {Options} options Ancillary properties
*/
class Point
{
_x = 0;
_y = 0;
_canvas = undefined;
#_options = new Options;
/**
* Create a point
* @param {number} x X coordinate value
* @param {number} y Y coordinate value
*/
constructor ( x, y )
{
//// COMPOSITION ////////////////////////////
this._isAspect = VALIDATION.isAspect;
this._isInDom = VALIDATION.isInDom;
this._drawAxis = UTILITIES.individual.draw.axis;
this._drawBorder = UTILITIES.individual.draw.border;
this._rotatePoint = UTILITIES.individual.misc.rotatePoint;
Object.defineProperty ( this, 'canvas', PROPERTY_BLOCKS.individual.canvas );
delete this.#_options._shadow;
delete this.#_options._border;
this.x = x;
this.y = y;
}
//// PROPERTIES //////////////////////////////////
//// [ X ] /////////////////////////
/**
* Set x-axis value
* @public
* @function
* @param {number} value X coordinate value
*/
set x ( value )
{
this._x = ( ( typeof value === 'number' ) && !isNaN ( value ) ) ? value : this._x;
}
/**
* Get x-axis value
* @readOnly
* @function
* @return {number} X coordinate value
*/
get x ( )
{
return this._x;
}
//// [ Y ] /////////////////////////
/**
* Set the y-axis value
* @public
* @function
* @param {number} value Y coordinate value
*/
set y ( value )
{
this._y = ( ( typeof value === 'number' ) && !isNaN ( value ) ) ? value : this._y;
}
/**
* Get y-axis value
* @readOnly
* @function
* @return {number} Y coordinate value
*/
get y ( )
{
return this._y;
}
//// [ CANVAS ] ////////////////////
/**
* Set canvas value
* @public
* @function
* @param {string} value Canvas id
* @see {@link PROPERTY_BLOCKS.individual.canvas}
*/
set canvas ( value ) { }
/**
* Get canvas value
* @readOnly
* @function
* @return {string} Canvas id
* @see {@link PROPERTY_BLOCKS.individual.canvas}
*/
get canvas ( ) { }
//// [ OPTIONS ] ///////////////////
/**
* Get options
* @public
* @function
* @return {Options} Options object
*/
get options ( )
{
return this.#_options;
}
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is an Aspect
* @private
* @function
* @param {Object} value Aspect or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isAspect}
*/
_isAspect ( ) { }
/**
* Returns whether the passed value is an element id within the DOM
* @private
* @function
* @param {string} value Element id
* @return {boolean} True || False
* @see {@link VALIDATION.isInDom}
*/
_isInDom ( ) { }
//// UTILITIES ////////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Draws an axis for the associated object
* @private
* @function
* @param {number} offset Offset of axis
* @param {Object} color Color model
* @param {number} stop Gradient color stop
* @see {@link UTILITIES.individual.draw.axis}
*/
_drawAxis ( ) { }
/**
* Draws an axis for the associated object
* @private
* @function
* @param {Aspect} aspect Aspect properties
* @param {Object} color Color model
* @see {@link UTILITIES.individual.draw.border}
*/
_drawBorder ( ) { }
/**
* Rotates the origin point by the degree & distance passed
* @private
* @function
* @param {Point} origin Origin point
* @param {number} degree Degree to rotate
* @param {number} distance Distance from origin
* @see {@link UTILITIES.individual.misc.rotatePoint}
*/
_rotatePoint ( ) { }
//// + PUBLIC //////////////////////
/**
* Get center of this object
* @readOnly
* @function
* @return {Point} Center point coordinates
*/
get center ( )
{
return new Point ( this.x, this.y );
}
/**
* Draws associated options
* @public
* @function
* @param {number} offset Offset of drawable options
*/
drawOptions ( offset = 20 )
{
let _aspect = new Aspect ( offset, offset );
////////////////////////////////////////////////////////////////////
this._drawBorder ( _aspect );
this._drawAxis ( );
}
/**
* Invert x & y coordinate values
* @public
* @function
*/
invert ( )
{
let _y = this.y;
[ this.y, this.x ] = [ this.x, _y ];
}
/**
* Move this object
* @public
* @function
* @param {number} degree Direction to move; in degrees
* @param {number} distance Distance to move
*/
move ( degree, distance )
{
let _point = this._rotatePoint ( { x: this.x, y: this.y }, degree, distance );
[ this.x, this.y ] = [ _point.x, _point.y ];
}
}
/**
* @class {Object} Stop Color stop properties for associated array(s)
* @property {string} [color=<Rgb>] Color model & value
* @property {number} offset Representation of the color stop position; 0 = start, & 1 = end
*/
class Stop
{
_color = new Rgb;
_offset = undefined;
/**
* Create a color stop
* @property {string} color CSS color value
* @property {number} offset Representation of the color stop position
*/
constructor ( color, offset )
{
//// COMPOSITION ////////////////////////////
this._isColorModel = VALIDATION.isColorModel;
this._isDecimal = VALIDATION.isDecimal;
this.color = color;
this.offset = offset;
}
//// PROPERTIES //////////////////////////////////
//// [ COLOR ] ////////////////////
/**
* Set color value
* @public
* @function
* @param {Object} value Color model; Rgb, Hsl, Hwb
*/
set color ( value )
{
this._color = ( this._isColorModel ( value ) ) ? value : this._color;
}
/**
* Get color value
* @public
* @function
* @return {Object} Color model; Rgb, Hsl, Hwb
*/
get color ( )
{
return this._color;
}
//// [ OFFSET ] ////////////////////
/**
* Set offset value
* @public
* @function
* @param {number} value Offset value
*/
set offset ( value )
{
this._offset = ( this._isDecimal ( value ) ) ? value : this._offset;
}
/**
* Get offset value
* @readOnly
* @function
* @return {number} Offset value
*/
get offset ( )
{
return this._offset;
}
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is a color model
* @private
* @function
* @param {Object} value Color model or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isColorModel}
*/
_isColorModel ( ) { }
/**
* Returns whether the passed value is a decimal value; 0.00 - 1
* @private
* @function
* @param {number} value Decimal value; 0.00 - 1
* @return {boolean} True || False
* @see {@link VALIDATION.isDecimal}
*/
_isDecimal ( ) { }
}
/**
* @class {Object} Conic Conic gradient object type and properties
* @property {Point} point X & Y axis coordinates
* @property {number} angle Angle in radians
* @property {Array.<Stop>} stops Array of color stops
*/
class Conic
{
_point = new Point;
_angle = 0;
_stops = new Array;
/**
* Create a Conic gradient object type
* @property {number} angle Angle in radians
* @property {Point} point Starting point of linear gradient
* @property {Array.<Stop>} stops Array of color stops
*/
constructor ( angle, point, stops )
{
//// COMPOSITION ////////////////////////////
this._isPoint = VALIDATION.isPoint;
this._isRadian = VALIDATION.isRadian;
this._isStop = VALIDATION.isStop;
this._stopColorCycle = UTILITIES.individual.color.cycle.stop;
this.point = point;
this.angle = angle;
this.stops = stops;
}
//// PROPERTIES //////////////////////////////////
//// [ ANGLE ] /////////////////////
/**
* Set angle property
* @public
* @function
* @param {Angle} value Angle object
*/
set angle ( value )
{
this._angle = ( this._isRadian ( value ) ) ? value : this._angle;
}
/**
* Set angle property
* @readOnly
* @function
* @return {Angle} Angle object
*/
get angle ( )
{
return this._angle;
}
//// [ START ] /////////////////////
/**
* Set point
* @public
* @function
* @param {Point} value Point
*/
set point ( value )
{
this._point = ( this._isPoint ( value ) ) ? value : this._point;
}
/**
* Get point
* @readOnly
* @function
* @return {Point} Point
*/
get point ( )
{
return this._point;
}
//// [ STOPS ] /////////////////////
/**
* Set color stops
* @public
* @function
* @param {Array.<Stop>} values Color stops
*/
set stops ( value )
{
if ( Array.isArray ( value ) )
for ( let _stop of value )
if ( this._isStop ( _stop ) )
this._stops.push ( _stop );
else
console.warn ( '[ ERROR ]: value is not of type Array !' );
}
/**
* Get color stops
* @readOnly
* @function
* @return {Array.<Stop>} Color stops
*/
get stops ( )
{
return this._stops;
}
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
/**
* Returns whether the passed value is a radian; 0 - 6.28...
* @private
* @function
* @param {number} value Radian value; 0 - 6.28...
* @return {boolean} True || False
* @see {@link VALIDATION.isRadian}
*/
_isRadian ( ) { }
/**
* Returns whether the passed value is a Stop or object equivalent
* @private
* @function
* @param {Object} value Stop or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isStop}
*/
_isStop ( ) { }
//// UTILITIES ///////////////////////////////////
/**
* Cycle colors for gradient stop(s)
* @private
* @function
* @param {Object} start Color model & values
* @param {Object} end Color model & values
* @param {number} progress Progress time unit; 0.00 - 1.00
* @param {number} stop Color stop to cycle
* @param {number} max Maximum number of steps between interpolation
* @param {function} clear Clear callback from root object
* @param {function} draw Draw callback from root object
* @see {@link UTILITIES.individual.color.cycle.stop}
*/
_stopColorCycle ( ) { }
}
/**
* @class {Object} Linear Linear gradient object type and properties
* @property {Point} start Start X & Y axis coordinates
* @property {Point} end End X & Y axis coordinates
* @property {Array.<Stop>} stops Array of color stops
*/
class Linear
{
_start = new Point;
_end = new Point;
_stops = new Array;
/**
* Create a Linear gradient object type
* @property {Point} start Starting point of linear gradient
* @property {Point} end Ending point of linear gradient
* @property {Array.<Stop>} stops Array of color stops
*/
constructor ( start, end, stops )
{
//// COMPOSITION ////////////////////////////
this._isColorModel = VALIDATION.isColorModel;
this._isPoint = VALIDATION.isPoint;
this._isStop = VALIDATION.isStop;
this.__stopColorCycle = UTILITIES.individual.color.cycle.stop;
this.start = start;
this.end = end;
this.stops = stops;
}
//// PROPERTIES //////////////////////////////////
//// [ START ] /////////////////////
/**
* Set starting point
* @public
* @function
* @param {Point} value Starting point
*/
set start ( value )
{
this._start = ( this._isPoint ( value ) ) ? value : this._start;
}
/**
* Set starting point
* @readOnly
* @function
* @return {Point} Starting point
*/
get start ( )
{
return this._start;
}
//// [ END ] ///////////////////////
/**
* Set ending point
* @public
* @function
* @param {Point} value Ending point
*/
set end ( value )
{
this._end = ( this._isPoint ( value ) ) ? value : this._end;
}
/**
* Set ending point
* @readOnly
* @function
* @return {Point} Ending point
*/
get end ( )
{
return this._end;
}
//// [ STOPS ] /////////////////////
/**
* Set color stops
* @public
* @function
* @param {Array.<Stop>} values Color stops
*/
set stops ( value )
{
if ( Array.isArray ( value ) )
for ( let _stop of value )
if ( this._isStop ( _stop ) )
this._stops.push ( _stop );
else
console.warn ( '[ ERROR ]: value is not of type Array !' );
}
/**
* Get color stops
* @readOnly
* @function
* @return {Array.<Stop>} Color stops
*/
get stops ( )
{
return this._stops;
}
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is a color model
* @private
* @function
* @param {Object} value Color model or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isColorModel}
*/
_isColorModel ( ) { }
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
/**
* Returns whether the passed value is a Stop or object equivalent
* @private
* @function
* @param {Object} value Stop or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isStop}
*/
_isStop ( ) { }
//// UTILITIES ///////////////////////////////////
/**
* Cycle colors for gradient stop(s)
* @private
* @function
* @param {Object} start Color model & values
* @param {Object} end Color model & values
* @param {number} progress Progress time unit; 0.00 - 1.00
* @param {number} stop Color stop to cycle
* @param {number} max Maximum number of steps between interpolation
* @param {function} clear Clear callback from root object
* @param {function} draw Draw callback from root object
* @see {@link UTILITIES.individual.color.cycle.stop}
*/
_stopColorCycle ( ) { }
}
/**
* @class {Object} Radial Radial gradient object type and properties
* @property {Point} start Start X & Y axis coordinates
* @property {Number} startRadius Starting radius of linear gradient
* @property {Point} end End X & Y axis coordinates
* @property {Number} endRadius Ending radius of linear gradient gradient
* @property {Array.<Stop>} stops Array of color stops
*/
class Radial
{
_start = new Point;
_startRadius = 0;
_end = new Point;
_endRadius = 0;
_stops = new Array;
/**
* Create a Radial gradient object type and properties
* @property {Point} start Starting point of linear gradient
* @property {Number} startRadius Starting radius of linear gradient gradient
* @property {Point} end Ending point of linear gradient
* @property {Number} endRadius Ending radius of linear gradient gradient
* @property {Array.<Stop>} stops Array of color stops
*/
constructor ( start, startRadius, end, endRadius, stops )
{
//// COMPOSITION ////////////////////////////
this._isPoint = VALIDATION.isPoint;
this._isStop = VALIDATION.isStop;
this._isColorModel = VALIDATION.isColorModel;
this._isRadius = VALIDATION.isRadius;
this._stopColorCycle = UTILITIES.individual.color.cycle.stop;
this.start = start;
this.startRadius = startRadius;
this.end = end;
this.endRadius = endRadius;
this.stops = stops;
}
//// PROPERTIES //////////////////////////////////
//// [ START ] /////////////////////
/**
* Set starting point
* @public
* @function
* @param {Point} value Starting point
*/
set start ( value )
{
this._start = ( this._isPoint ( value ) ) ? value : this._start;
}
/**
* Set starting point
* @readOnly
* @function
* @return {Point} Starting point
*/
get start ( )
{
return this._start;
}
//// [ START RADIUS ] //////////////
/**
* Set starting radius
* @public
* @function
* @param {Number} value Starting radius
*/
set startRadius ( value )
{
this._startRadius = ( this._isRadius ( value ) ) ? value : this._startRadius;
}
/**
* Set starting radius
* @readOnly
* @function
* @return {Number} Starting radius
*/
get startRadius ( )
{
return this._startRadius;
}
//// [ END ] ///////////////////////
/**
* Set ending point
* @public
* @function
* @param {Point} value Ending point
*/
set end ( value )
{
this._end = ( this._isPoint ( value ) ) ? value : this._end;
}
/**
* Set ending point
* @readOnly
* @function
* @return {Point} Ending point
*/
get end ( )
{
return this._end;
}
//// [ END RADIUS ] ////////////////
/**
* Set ending radius
* @public
* @function
* @param {Number} value Ending radius
*/
set endRadius ( value )
{
this._endRadius = ( this._isRadius ( value ) ) ? value : this._endRadius;
}
/**
* Set ending radius
* @readOnly
* @function
* @return {Number} Ending radius
*/
get endRadius ( )
{
return this._endRadius;
}
//// [ STOPS ] /////////////////////
/**
* Set color stops
* @public
* @function
* @param {Array.<Stop>} value Color stops
*/
set stops ( value )
{
if ( Array.isArray ( value ) )
for ( let _stop of value )
if ( this._isStop ( _stop ) )
this._stops.push ( _stop );
else
console.warn ( '[ ERROR ]: value is not of type Array !' );
}
/**
* Get color stops
* @readOnly
* @function
* @return {Array.<Stop>} Color stops
*/
get stops ( )
{
return this._stops;
}
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is a color model
* @private
* @function
* @param {Object} value Color model or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isColorModel}
*/
_isColorModel ( ) { }
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
/**
* Returns whether the passed value is a radius value
* @private
* @function
* @param {number} value Radius value
* @return {boolean} True || False
* @see {@link VALIDATION.isRadius}
*/
_isRadius ( ) { }
/**
* Returns whether the passed value is a Stop or object equivalent
* @private
* @function
* @param {Object} value Stop or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isStop}
*/
_isStop ( ) { }
//// UTILITIES ///////////////////////////////////
/**
* Cycle colors for gradient stop(s)
* @private
* @function
* @param {Object} start Color model & values
* @param {Object} end Color model & values
* @param {number} progress Progress time unit; 0.00 - 1.00
* @param {number} stop Color stop to cycle
* @param {number} max Maximum number of steps between interpolation
* @param {function} clear Clear callback from root object
* @param {function} draw Draw callback from root object
* @see {@link UTILITIES.individual.color.cycle.stop}
*/
_stopColorCycle ( ) { }
}
/**
* @class {Object} Fill Fill container for various fill types
* @property {Object} [color=<Rgb>] Color model & value
* @property {string} [type='solid'] Fill type; solid | linear | radial | conic | pattern
* @property {Object} gradient Gradient object; Linear | Radial | Conic
* @property {Pattern} pattern Pattern fill object
*/
class Fill
{
_color = new Rgb ( 0, 0, 0, 0 );
_type = 'solid';
_gradient = undefined;
_pattern = undefined;
_repetition = 'repeat';
/**
* Create a fill type
* @param {Object} [color=<Rgb>] Color model & value
* @param {string} [type='solid'] Fill type
* @param {Object} gradient Gradient object
*/
constructor ( color, type, gradient )
{
//// COMPOSITION ////////////////////////////
this._isColorModel = VALIDATION.isColorModel;
this._isGradient = VALIDATION.isGradient;
this._isFillType = VALIDATION.isFillType;
this._isRepetition = VALIDATION.isRepetition;
this.color = color;
this.type = type;
this.gradient = gradient;
}
//// PROPERTIES //////////////////////////////////
//// [ COLOR ] /////////////////////
/**
* Set color type
* @public
* @function
* @param {Object} value Color model; Rgb
*/
set color ( value )
{
this._color = this._isColorModel ( value ) ? value : this._color;
}
/**
* Get color type
* @readOnly
* @function
* @return {Object} Color model; Rgb
*/
get color ( )
{
return this._color;
}
//// [ TYPE ] //////////////////////
/**
* Set type value
* @public
* @function
* @param {string} value Fill type value
*/
set type ( value )
{
this._type = ( this._isFillType ( value ) ) ? value : this._type;
}
/**
* Get type value
* @readOnly
* @function
* @return {string} Fill type value
*/
get type ( )
{
return this._type;
}
//// [ GRADIENT ] ///////////////////
/**
* Set gradient gradient properties
* @public
* @function
* @param {Object} value Gradient object & properties
*/
set gradient ( value )
{
this._gradient = ( this._isGradient ( value ) ) ? value : this._gradient;
this._type = ( this._isGradient ( value ) ) ? value.constructor.name.toLowerCase ( ) : this._type;
}
/**
* Get gradient gradient properties
* @readOnly
* @function
* @return {Object} Gradient object & properties
*/
get gradient ( )
{
return this._gradient;
}
//// [ PATTERN ] ////////////////////
/**
* Sets pattern property value
* @public
* @function
* @param {string} value Path of image to pattern
*/
set pattern ( value )
{
if ( typeof value === 'string' )
{
let _image = new Image;
_image.src = value;
this._pattern = _image;
this.type = 'pattern';
}
}
/**
* Gets pattern property value
* @readOnly
* @function
* @return {Pattern} Pattern fill object
*/
get pattern ( )
{
return this._pattern;
}
//// [ REPITION ] //////////////////
/**
* Sets repetition property value
* @public
* @function
* @param {string} value Repetition property value
*/
set repetition ( value )
{
this._repetition = ( this._isRepetition ( value ) ) ? value : this._repetition;
}
/**
* Gets repetition property value
* @readOnly
* @function
* @return {string} Repetition property value
*/
get repetition ( )
{
return this._repetition;
}
//// VALIDATION //////////////////////////////////
/**
* Returns whether the passed value is a color model
* @private
* @function
* @param {Object} value Color model or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isColorModel}
*/
_isColorModel ( ) { }
/**
* Returns whether the passed value is a gradient object
* @private
* @function
* @param {Object} value Gradient object
* @return {boolean} True || False
* @see {@link VALIDATION.isGradient}
*/
_isGradient ( ) { }
/**
* Returns whether the passed value is a fill type
* @private
* @function
* @param {string} value Fill type
* @return {boolean} True || False
* @see {@link VALIDATION.isFillType}
*/
_isFillType ( ) { }
/**
* Returns whether the passed value is a repetition value
* @public
* @memberof VALIDATION
* @function
* @param {string} value Repetition value
* @return {boolean} True || False
* @see {@link VALIDATION.isRepetition}
*/
_isRepetition ( ) { }
}
/**
* @class {Object} Shadow Shadow of associated object
* @property {Object} [color=<Rgb>] RGB color value; r, g, b
* @property {number} [blur=3] Blur strength
* @property {Point} offset Point offset coordinates
*/
class Shadow
{
_color = new Rgb;
_blur = 3;
_offset = new Point;
/**
* Create a shadow
* @param {Object} color RGB color value
* @param {number} blur Shadow blur value
* @param {Point} offset Shadow offset
*/
constructor ( color, blur, offset = { x: undefined, y: undefined } )
{
//// COMPOSITION ////////////////////////////
this._isColorModel = VALIDATION.isColorModel;
this._isBlur = VALIDATION.isBlur;
this._isPoint = VALIDATION.isPoint;
Object.defineProperty ( this, 'offset', PROPERTY_BLOCKS.individual.offset );
Object.defineProperty ( this, 'x', PROPERTY_BLOCKS.individual.offsetX );
Object.defineProperty ( this, 'y', PROPERTY_BLOCKS.individual.offsetY );
this.color = color;
this.blur = blur;
this.offset = offset;
}
//// PROPERTIES //////////////////////////////////
//// [ COLOR ] /////////////////////
/**
* Set color value
* @public
* @function
* @param {Object} value Color model; Rgb, Hsl, Hwb
*/
set color ( value )
{
this._color = ( this._isColorModel ( value ) ) ? value : this._color;
}
/**
* Get color value
* @public
* @function
* @return {Object} Color model; Rgb, Hsl, Hwb
*/
get color ( )
{
return this._color;
}
//// [ BLUR ] //////////////////////
/**
* Set blur value
* @public
* @function
* @param {number} blur Blur value
*/
set blur ( value )
{
this._blur = ( this._isBlur ( value ) ) ? value : this._blur;
}
/**
* Get blur value
* @readOnly
* @function
* @return {number} Blur value
*/
get blur ( )
{
return this._blur;
}
//// [ OFFSET.(X)(Y) ] /////////////
/**
* Set offset
* @public
* @function
* @param {Point} value Shadow offset
* @see {@link PROPERTY_BLOCKS.individual.offset}
*/
set offset ( value ) { }
/**
* Get offset
* @public
* @function
* @return {Point} Shadow offset
* @see {@link PROPERTY_BLOCKS.individual.offset}
*/
get offset ( ) { }
/**
* Set x-axis offset value
* @public
* @function
* @param {number} value X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.offsetX}
*/
set x ( value ) { }
/**
* Get x-axis offset value
* @readOnly
* @function
* @return {number} X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.offsetX}
*/
get x ( ) { }
/**
* Set the y-axis offset value
* @public
* @function
* @param {number} value Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.offsetY}
*/
set y ( value ) { }
/**
* Get y-axis offset value
* @readOnly
* @function
* @return {number} Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.offsetY}
*/
get y ( ) { }
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is a color model
* @private
* @function
* @param {Object} value Color model or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isColorModel}
*/
_isColorModel ( ) { }
/**
* Returns whether the passed value is a blur value
* @private
* @function
* @param {number} value Blur value
* @return {boolean} True || False
* @see {@link VALIDATION.isBlur}
*/
_isBlur ( ) { }
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
}
/**
* @class {Object} Stroke Stroke properties of associated object
* @property {Object} [color=<Rgb>] Color model & value
* @property {string} [type='solid'] Stroke type; 'solid' || 'dashed'
* @property {number[]} [segments=[5, 5]] Dashed line segment distance(s)
* @property {number} [width=2] Thickness of stroke
* @property {Shadow} shadow Shadow properties
*/
class Stroke
{
_color = new Rgb;
_type = 'solid';
_segments = [ 5, 5 ];
_width = 1;
/**
* Create a stroke
* @param {Object} color RGB color value
* @param {string} type Stroke type
* @param {number[]} segments Dashed line segment distance(s)
* @param {number} alpha Alpha value; number/decimal
* @param {number} width Thickness of stroke
*/
constructor ( color, type, segments, width )
{
//// COMPOSITION ////////////////////////////
this._isColorModel = VALIDATION.isColorModel;
this._isStrokeType = VALIDATION.isStrokeType;
this._isSegments = VALIDATION.isSegments;
this._isWidth = VALIDATION.isWidth;
this.color = color;
this.type = type;
this.segments = segments;
this.width = width;
}
//// PROPERTIES //////////////////////////////////
//// [ TYPE ] //////////////////////
/**
* Set type
* @public
* @function
* @param {string} value Stroke type: 'solid' || 'dashed'
*/
set type ( value )
{
this._type = ( this._isStrokeType ( value ) ) ? value : this._type;
}
/**
* Get type
* @readOnly
* @function
* @return {string} Stroke type: 'solid' || 'dashed'
*/
get type ( )
{
return this._type;
}
//// [ SEGMENTS ] //////////////////
/**
* Set segment value
* @public
* @function
* @param {Array.<number>} value Dashed line segment distance(s)
*/
set segments ( value )
{
this._segments = ( this._isSegments ( value ) ) ? value : this._segments;
}
/**
* Get segment value
* @readOnly
* @function
* @return {Array.<number>} Dashed line segment distance(s)
*/
get segments ( )
{
return this._segments;
}
//// [ COLOR ] /////////////////////
/**
* Set color value
* @public
* @function
* @param {Rgb} value Color model
*/
set color ( value )
{
this._color = ( this._isColorModel ( value ) ) ? value : this._color;
}
/**
* Get color value
* @public
* @function
* @return {Rgb} Color model
*/
get color ( )
{
return this._color;
}
//// [ WIDTH ] /////////////////////
/**
* Set width value
* @public
* @function
* @param {number} value Thickness of stroke
*/
set width ( value )
{
this._width = ( this._isWidth ( value ) ) ? value : this._width;
}
/**
* Get width value
* @readOnly
* @function
* @return {number} Thickness of stroke
*/
get width ( )
{
return this._width;
}
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is a color model
* @private
* @function
* @param {Object} value Color model or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isColorModel}
*/
_isColorModel ( ) { }
/**
* Returns whether the passed value is an Array of segment values
* @private
* @function
* @param {Array.<number>} value Array of segment values
* @return {boolean} True || False
* @see {@link VALIDATION.isSegments}
*/
_isSegments ( ) { }
/**
* Returns whether the passed value is a stroke type
* @private
* @function
* @param {string} value Stroke type
* @return {boolean} True || False
* @see {@link VALIDATION.isStrokeType}
*/
_isStrokeType ( ) { }
/**
* Returns whether the passed value is a width value
* @private
* @function
* @param {number} value Width value
* @return {boolean} True || False
* @see {@link VALIDATION.isWidth}
*/
_isWidth ( ) { }
}
/**
* @class {Object} OptionsCollection Options for shapes, lines, and groups
* @property {boolean} [shadow=false] Display shadow
* @property {boolean} [border=false] Display border
* @property {boolean} [axis=false] Display axis
* @property {boolean} [points=false] Display points
* @property {boolean} [coordinates=false] Display coordinates
* @property {boolean} [controlPoints=false] Display control points
* @property {boolean} [shadow=false] Display shadow
*/
class OptionsCollection
{
_shadow = false;
_border = false;
_axis = false;
_points = false;
_coordinates = false;
_controlPoints = false;
/**
* Create an options collection
* @param {boolean} shadow Show shadow
* @param {boolean} border Show border
* @param {boolean} axis Show axis
* @param {boolean} points Show points
* @param {boolean} coordinates Show coordinates
*/
constructor ( shadow, border, axis, points, coordinates )
{
//// COMPOSITION ////////////////////////////
this._setAll = UTILITIES.individual.set.all;
this.shadow = shadow;
this.border = border;
this.axis = axis;
this.points = points;
this.coordinates = coordinates;
}
//// PROPERTIES //////////////////////////////////
//// [ SHADOW ] ////////////////////
/**
* Set shadow value
* @public
* @function
* @param {boolean} value Shadow; true | false
*/
set shadow ( value )
{
if ( typeof value === 'boolean' ) this._setAll ( 'shadow', value );
}
/**
* Get shadow value
* @readOnly
* @function
* @return {boolean} Shadow; true | false
*/
get shadow ( )
{
return this._shadow;
}
//// [ BORDER ] ////////////////////
/**
* Set border value
* @public
* @function
* @param {boolean} value Border; true | false
*/
set border ( value )
{
if ( typeof value === 'boolean' ) this._setAll ( 'border', value );
}
/**
* Get border value
* @readOnly
* @function
* @return {boolean} Border; true | false
*/
get border ( )
{
return this._border;
}
//// [ AXIS ] //////////////////////
/**
* Set axis value
* @public
* @function
* @param {boolean} value Axis; true | false
*/
set axis ( value )
{
if ( typeof value === 'boolean' ) this._setAll ( 'axis', value );
}
/**
* Get axis value
* @readOnly
* @function
* @return {boolean} Axis; true | false
*/
get axis ( )
{
return this._axis;
}
//// [ POINTS ] ////////////////////
/**
* Set points value
* @public
* @function
* @param {boolean} value Points; true | false
*/
set points ( value )
{
if ( typeof value === 'boolean' ) this._setAll ( 'points', value );
}
/**
* Get points value
* @readOnly
* @function
* @return {boolean} Points; true | false
*/
get points ( )
{
return this._coordinates;
}
//// [ COORDINATES ] ///////////////
/**
* Set coordinates value
* @public
* @function
* @param {boolean} value Coordinates; true | false
*/
set coordinates ( value )
{
if ( typeof value === 'boolean' ) this._setAll ( 'coordinates', value );
}
/**
* Get coordinates value
* @readOnly
* @function
* @return {boolean} Coordinates; true | false
*/
get coordinates ( )
{
return this._coordinates;
}
//// [ CONTROL POINTS ] ////////////
/**
* Set control points value
* @public
* @function
* @param {boolean} value Control points; true | false
*/
set controlPoints ( value )
{
if ( typeof value === 'boolean' ) this._setAll ( 'controlPoints', value );
}
/**
* Get control points value
* @readOnly
* @function
* @return {boolean} Control points; true | false
*/
get controlPoints ( )
{
return this._controlPoints;
}
//// UTILITIES ////////////////////////////////////
/**
* Sets all option values throughout a collection
* @private
* @function
* @param {string} property Option property
* @param {boolean} value True || False
* @see {@link UTILITIES.individual.set.all}
*/
_setAll ( ) { }
}
/**
* @class {Object} PointCollection X & Y coordinates for an object
* @property {number} [x=0] X - x-axis coordinate
* @property {number} [y=0] Y - y-axis coordinate
* @property {OptionsCollection} options Ancillary properties
*/
class PointCollection
{
_x = 0;
_y = 0;
#_options = new OptionsCollection;
/**
* Create a point collection
*/
constructor ( )
{
//// COMPOSITION ////////////////////////////
this._setAll = UTILITIES.individual.set.all;
delete this.#_options._shadow;
delete this.#_options._border;
}
//// PROPERTIES //////////////////////////////////
//// [ X ] /////////////////////////
/**
* Set x-axis value
* @public
* @function
* @param {number} value X coordinate value
*/
set x ( value )
{
if ( typeof value === 'number' ) this._setAll ( 'x', value );
}
/**
* Get x-axis value
* @readOnly
* @function
* @return {number} X coordinate value
*/
get x ( )
{
return this._x;
}
//// [ Y ] /////////////////////////
/**
* Set the y-axis value
* @public
* @function
* @param {number} value Y coordinate value
*/
set y ( value )
{
if ( typeof value === 'number' ) this._setAll ( 'y', value );
}
/**
* Get y-axis value
* @readOnly
* @function
* @return {number} Y coordinate value
*/
get y ( )
{
return this._y;
}
//// [ OPTIONS ] ///////////////////
/**
* Get options
* @public
* @function
* @return {OptionsCollection} Options collection object
*/
get options ( )
{
return this.#_options;
}
//// UTILITIES ////////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Sets all option values throughout a collection
* @private
* @function
* @param {string} property Option property
* @param {boolean} value True || False
* @see {@link UTILITIES.individual.set.all}
*/
_setAll ( ) { }
//// + PUBLIC //////////////////////
/**
* Invert x & y coordinate values
* @public
* @function
*/
invert ( )
{
let _y = this.y;
[ this.y, this.x ] = [ this.x, _y ];
}
}
/**
* @class {Object} ShadowCollection Shadow of associated object
* @property {Object} [color=<Rgb>] Color model & value
* @property {number} blur Blur strength
* @property {Point} offset Point offset coordinates
*/
class ShadowCollection
{
_color = new Rgb;
_blur = 3;
_offset = new Point;
/**
* Create a shadow collection
*/
constructor ( )
{
//// COMPOSITION ////////////////////////////
this._isColorModel = VALIDATION.isColorModel;
this._isBlur = VALIDATION.isBlur;
this._isPoint = VALIDATION.isPoint;
this._setAll = UTILITIES.individual.set.all;
}
//// PROPERTIES //////////////////////////////////
//// [ COLOR ] //////////////////////////
/**
* Set color value
* @public
* @function
* @param {string} value RGB color value
*/
set color ( value )
{
if ( this._isColorModel ( value ) )
this._setAll ( 'color', value );
}
/**
* Get color value
* @public
* @function
* @return {string} RGB color value
*/
get color ( )
{
return this._color;
}
//// [ BLUR ] //////////////////////////
/**
* Set blur value
* @public
* @function
* @param {number} blur Blur value
*/
set blur ( value )
{
if ( this._isBlur ( value ) )
this._setAll ( 'blur', value );
}
/**
* Get blur value
* @readOnly
* @function
* @return {number} Blur value
*/
get blur ( )
{
return this._blur;
}
//// [ OFFSET.(X)(Y) ] /////////////////
/**
* Set offset
* @public
* @function
* @param {Point} value Shadow offset
*/
set offset ( value )
{
if ( this._isPoint ( value ) )
this._setAll ( 'offset', value );
}
/**
* Get offset
* @public
* @function
* @return {Point} Shadow offset
*/
get offset ( )
{
return this._offset;
}
/**
* Set x-axis offset value
* @public
* @function
* @param {number} value X coordinate value
*/
set x ( value )
{
this._offset.x = value;
}
/**
* Get x-axis offset value
* @readOnly
* @function
* @return {number} X coordinate value
*/
get x ( )
{
return this._offset.x;
}
/**
* Set the y-axis offset value
* @public
* @function
* @param {number} value Y coordinate value
*/
set y ( value )
{
this._offset.y = value;
}
/**
* Get y-axis offset value
* @readOnly
* @function
* @return {number} Y coordinate value
*/
get y ( )
{
return this._offset.y;
}
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is a blur value
* @private
* @function
* @param {number} value Blur value
* @return {boolean} True || False
* @see {@link VALIDATION.isBlur}
*/
_isBlur ( ) { }
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
//// UTILITIES ////////////////////////////////////
/**
* Sets all option values throughout a collection
* @private
* @function
* @param {string} property Option property
* @param {boolean} value True || False
* @see {@link UTILITIES.individual.set.all}
*/
_setAll ( ) { }
}
/**
* @class {Object} StrokeCollection Stroke properties of associated object
* @property {Object} [color=<Rgb>] Color model & value
* @property {string} [type='solid'] Stroke type; solid | dashed
* @property {number[]} [segments=[5, 5]] Dashed line segment distance(s)
* @property {number} [alpha=1] Alpha (transparency); number/decimal
* @property {number} [width=2] Thickness of stroke
* @property {Shadow} shadow Shadow properties
*/
class StrokeCollection
{
_color = new Rgb;
_type = 'solid';
_segments = [ 5, 5 ];
_width = 2;
_master = undefined;
/**
* Create a stroke collection
*/
constructor ( )
{
//// COMPOSITION ////////////////////////////
this._isCanvasLabObject = VALIDATION.isCanvasLabObject;
this._isColorModel = VALIDATION.isColorModel;
this._isStrokeType = VALIDATION.isStrokeType;
this._isSegments = VALIDATION.isSegments;
this._isWidth = VALIDATION.isWidth;
this._setAll = UTILITIES.individual.set.all;
Object.defineProperty ( this, 'master', PROPERTY_BLOCKS.individual.master );
}
//// PROPERTIES //////////////////////////////////
//// [ COLOR ] /////////////////////
/**
* Set color value
* @public
* @function
* @param {string} value RGB color value
*/
set color ( value )
{
if ( this._isColorModel ( value ) )
this._setAll ( 'color', value );
}
/**
* Get color value
* @public
* @function
* @return {string} RGB color value
*/
get color ( )
{
return this._color;
}
//// [ TYPE ] //////////////////////
/**
* Set type
* @public
* @function
* @param {number} value Type: (0) Solid or (1) Dashed
*/
set type ( value )
{
if ( this._isStrokeType ( value ) )
this._setAll ( 'type', value );
}
/**
* Get type
* @readOnly
* @function
* @return {number} Type: (0) Solid or (1) Dashed
*/
get type ( )
{
return this._type;
}
//// [ SEGMENTS ] //////////////////
/**
* Set segment value
* @public
* @function
* @param {Array} value Dashed line segment distance(s)
*/
set segments ( value )
{
if ( this._isSegments ( value ) )
this._setAll ( 'segments', value );
}
/**
* Get segment value
* @readOnly
* @function
* @return {Array} Dashed line segment distance(s)
*/
get segments ( )
{
return this._segments;
}
//// [ WIDTH ] /////////////////////
/**
* Set width value
* @public
* @function
* @param {number} value Thickness of stroke
*/
set width ( value )
{
if ( this._isWidth ( value ) )
this._setAll ( 'width', value );
}
/**
* Get width value
* @readOnly
* @function
* @return {number} Thickness of stroke
*/
get width ( )
{
return this._width;
}
//// [ MASTER ] ////////////////////
/**
* Set master object
* @public
* @function
* @param {clObject} value Canvas Lab object
* @see {@link PROPERTY_BLOCKS.individual.master}
*/
set master ( value ) { }
/**
* Get master object
* @public
* @function
* @return {clObject} Master Canvas Lab object
* @see {@link PROPERTY_BLOCKS.individual.master}
*/
get master ( ) { }
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is a color model
* @private
* @function
* @param {Object} value Color model or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isColorModel}
*/
_isColorModel ( ) { }
/**
* Returns whether the passed value is an Array of segment values
* @private
* @function
* @param {Array.<number>} value Array of segment values
* @return {boolean} True || False
* @see {@link VALIDATION.isSegments}
*/
_isSegments ( ) { }
/**
* Returns whether the passed value is a stroke type
* @private
* @function
* @param {string} value Stroke type
* @return {boolean} True || False
* @see {@link VALIDATION.isStrokeType}
*/
_isStrokeType ( ) { }
/**
* Returns whether the passed value is a width value
* @private
* @function
* @param {number} value Width value
* @return {boolean} True || False
* @see {@link VALIDATION.isWidth}
*/
_isWidth ( ) { }
//// UTILITIES ////////////////////////////////////
/**
* Sets all option values throughout a collection
* @private
* @function
* @param {string} property Option property
* @param {boolean} value True || False
* @see {@link UTILITIES.individual.set.all}
*/
_setAll ( ) { }
}
//// OBJECTS /////////////////////////////////////////
/**
* @class {Object} Position Position object
* @property {Point} origin Origin X & Y coordinates for an object's position
* @property {Point} start Origin start X & Y coordinates for a Line object's position
* @property {Point} end Origin end X & Y coordinates for a Line object's position
* @property {number} distance Distance from origin to destination
* @property {number} direction Direction to move towards destination; in degrees
* @property {number} rotation Amount object (including canvas) has been rotated
* @property {Aspect} aspect Original aspect value(s) for a rectangular object
* @property {ControlPoints} controlPoints Original control point values for a Line object
* @property {number} fontSize Original fontSize value for a Text object
* @property {clObject} master Master object
*/
class Position
{
_origin = undefined;
_start = undefined;
_end = undefined;
_distance = undefined;
_direction = undefined;
_rotation = 0;
_aspect = new Aspect;
_controlPoints = new ControlPoints;
_fontSize = 0;
_stroke = undefined;
_fill = undefined;
_master = undefined;
/**
* Create a Position object
*/
constructor ( )
{
//// COMPOSITION /////////////////////////////
this._isAspect = VALIDATION.isAspect;
this._isBlur = VALIDATION.isBlur;
this._isCanvasLabObject = VALIDATION.isCanvasLabObject;
this._isControlPoint = VALIDATION.isControlPoint;
this._isNumber = VALIDATION.isNumber;
this._isPoint = VALIDATION.isPoint;
this._isWidth = VALIDATION.isWidth
this._isHeight = VALIDATION.isHeight
Object.defineProperty ( this, 'master', PROPERTY_BLOCKS.individual.master );
}
//// PROPERTIES //////////////////////////////////
//// [ ORIGIN ] ////////////////////////
/**
* Set origin
* @public
* @function
* @param {Point} value X & Y coordinates
*/
set origin ( value )
{
this._origin = value;
}
/**
* Get origin
* @public
* @function
* @return {Point} X & Y coordinates
*/
get origin ( )
{
return this._origin;
}
//// [ DISTANCE ] //////////////////////
/**
* Set distance
* @public
* @function
* @param {number} value Distance from origin to destination
*/
set distance ( value )
{
if ( value != undefined && this._isPoint ( value ) )
this._distance = Math.sqrt (
( Math.pow ( value.x - this.master.x, 2 ) ) +
( Math.pow ( value.y - this.master.y, 2 ) )
);
}
/**
* Get distance
* @public
* @function
* @return {number} Distance from origin to destination
*/
get distance ( )
{
return this._distance;
}
//// [ DIRECTION ] /////////////////////
/**
* Set direction
* @public
* @function
* @param {number} value Direction in degrees
*/
set direction ( value )
{
if ( value != undefined && this._isPoint ( value ) )
this._direction = Math.atan2 ( value.y - this.master.y, value.x - this.master.x );
}
/**
* Get direction
* @public
* @function
* @return {number} Direction in degrees
*/
get direction ( )
{
return this._direction;
}
//// [ ROTATION ] //////////////////////
/**
* Set rotation
* @public
* @function
* @param {number} value Direction in degrees
*/
set rotation ( value )
{
this._rotation = value;
}
/**
* Get rotation
* @public
* @function
* @return {number} Direction in degrees
*/
get rotation ( )
{
return this._rotation;
}
//// [ ASPECT ] ////////////////////////
/**
* Set aspect
* @public
* @function
* @param {number} value Aspect of object
*/
set aspect ( value )
{
this._aspect = ( this._isAspect ( value ) ) ? value : this._aspect;
}
/**
* Get aspect
* @public
* @function
* @return {number} Aspect of object
*/
get aspect ( )
{
return this._aspect;
}
//// [ WIDTH ] /////////////////////////
/**
* Set width
* @public
* @function
* @param {number} value Width of object
*/
set width ( value )
{
this._aspect.width = value;
}
/**
* Get width
* @public
* @function
* @return {number} Width of object
*/
get width ( )
{
return this._aspect.width;
}
//// [ HEIGHT ] ////////////////////////
/**
* Set height
* @public
* @function
* @param {number} value Height of object
*/
set height ( value )
{
this._aspect.height = value;
}
/**
* Get height
* @public
* @function
* @return {number} Height of object
*/
get height ( )
{
return this._aspect.height;
}
//// [ CONTROLPOINTS ] /////////////////
/**
* Set controlPoints
* @public
* @function
* @param {Array.<number>} value ControlPoints of object
*/
set controlPoints ( value )
{
this._controlPoints = ( this._isControlPoint ( value ) ) ? value : this._controlPoints;
}
/**
* Get controlPoints
* @public
* @function
* @return {Array.<number>} ControlPoints of object
*/
get controlPoints ( )
{
return this._controlPoints;
}
//// [ FONTSIZE ] //////////////////////
/**
* Set fontSize
* @public
* @function
* @param {number} value FontSize of object
*/
set fontSize ( value )
{
this._fontSize = this._isNumber ( value ) ? value : this._fontSize;
}
/**
* Get fontSize
* @public
* @function
* @return {number} FontSize of object
*/
get fontSize ( )
{
return this._fontSize;
}
//// [ MASTER ] ////////////////////////
/**
* Set master object
* @public
* @function
* @param {clObject} value Canvas Lab object
* @see {@link PROPERTY_BLOCKS.individual.master}
*/
set master ( value ) { }
/**
* Get master object
* @public
* @function
* @return {clObject} Master Canvas Lab object
* @see {@link PROPERTY_BLOCKS.individual.master}
*/
get master ( ) { }
//// [ STROKE ] ////////////////////////
/**
* Set stroke
* @public
* @function
* @param {number} value Stroke of object
*/
set stroke ( value )
{
this._stroke = value;
}
/**
* Get stroke
* @public
* @function
* @return {number} Stroke of object
*/
get stroke ( )
{
return this._stroke;
}
//// [ FILL ] /////////////////////////
/**
* Set fill
* @public
* @function
* @param {number} value Fill of object
*/
set fill ( value )
{
this._fill = value;
}
/**
* Get fill
* @public
* @function
* @return {number} Fill of object
*/
get fill ( )
{
return this._fill;
}
//// VALIDATION //////////////////////////////////
/**
* Returns whether the passed value is an Aspect
* @private
* @memberof VALIDATION
* @function
* @param {Object} value Aspect or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isAspect}
*/
_isAspect ( ) { }
/**
* Returns whether the passed value is a blur value
* @private
* @function
* @param {number} value Blur value
* @return {boolean} True || False
* @see {@link VALIDATION.isBlur}
*/
_isBlur ( ) { }
/**
* Returns whether the passed value is a CanvasLab object; Line, Circle, Rectangle, Text
* @private
* @function
* @param {Object} value CanvasLab object; Line, Circle, Rectangle, Text
* @return {boolean} True || False
* @see {@link VALIDATION.isCanvasLabObject}
*/
_isCanvasLabObject ( ) { }
/**
* Returns whether the passed value is an array of Control Point values
* @private
* @memberof VALIDATION
* @function
* @param {Array.<number>} value Array of Control Points
* @return {boolean} True || False
* @see {@link VALIDATION.isControlPoint}
*/
_isControlPoint ( ) { }
/**
* Returns whether the passed value is a Number value
* @public
* @memberof VALIDATION
* @function
* @param {number} value Number value
* @return {boolean} True || False
* @see {@link VALIDATION.isNumber}
*/
_isNumber ( ) { }
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
/**
* Returns whether the passed value is a width value
* @private
* @memberof VALIDATION
* @function
* @param {number} value Width value
* @return {boolean} True || False
* @see {@link VALIDATION.isWidth}
*/
_isWidth ( ) { }
/**
* Returns whether the passed value is a height value
* @private
* @memberof VALIDATION
* @function
* @param {number} value Height value
* @return {boolean} True || False
* @see {@link VALIDATION.isHeight}
*/
_isHeight ( ) { }
}
/**
* @class {Object} Circle Circle object
* @property {Point} point X & Y axis coordinates
* @property {number|Point} [radius=25] Radius of circle
* @property {Angle} angle Angle properties
* @property {Stroke} stroke Stroke properties
* @property {Fill} fill Fill properties
* @property {Shadow} shadow Shadow properties
* @property {HTMLCanvasElement} canvas 2D canvas context
* @property {Anchor} anchor Anchor properties
* @property {Options} options Options for this object
* @property {Position} position Position properties
*/
class Circle
{
_point = new Point;
_radius = 25;
_angle = new Angle;
_stroke = new Stroke;
_fill = new Fill;
_shadow = new Shadow;
_canvas = undefined;
_anchor = new Anchor;
#options = new Options;
#position = new Position;
/**
* Create a Circle object
* @property {Point} point X & Y axis coordinates
* @property {number|Point} radius Radius of circle
* @property {Angle} angle Angle properties
* @property {Stroke} stroke Stroke properties
* @property {Fill} fill Fill properties
* @property {Shadow} shadow Shadow properties
* @property {HTMLCanvasElement} canvas Canvas Id
*/
constructor (
point = { x: undefined, y: undefined },
radius,
angle = { start: undefined, end: undefined, clockwise: undefined },
stroke = { color: undefined, type: undefined, segments: undefined, width: undefined },
fill = { color: undefined, type: undefined },
shadow = { color: undefined, blur: undefined, offset: { x: undefined, y: undefined } },
canvas = undefined
)
{
//// COMPOSITION ////////////////////////////
this._isAspect = VALIDATION.isAspect;
this._isAnchor = VALIDATION.isAnchor;
this._isDegree = VALIDATION.isDegree;
this._isInDom = VALIDATION.isInDom;
this._isNumber = VALIDATION.isNumber;
this._isPoint = VALIDATION.isPoint;
this._drawAnchor = UTILITIES.individual.draw.anchor;
this._drawAxis = UTILITIES.individual.draw.axis;
this._drawBorder = UTILITIES.individual.draw.border;
this._rotatePoint = UTILITIES.individual.misc.rotatePoint;
this._setFillType = UTILITIES.individual.set.fillType;
this._setShadow = UTILITIES.individual.set.shadow;
this.move = UTILITIES.individual.misc.move;
this.rotate = UTILITIES.individual.misc.rotate;
this.showCoordinates = UTILITIES.individual.misc.showCoordinates;
Object.defineProperty ( this, 'canvas', PROPERTY_BLOCKS.individual.canvas );
Object.defineProperty ( this, 'point', PROPERTY_BLOCKS.individual.point );
Object.defineProperty ( this, 'x', PROPERTY_BLOCKS.individual.pointX );
Object.defineProperty ( this, 'y', PROPERTY_BLOCKS.individual.pointY );
delete this.#options._controlPoints;
delete this.#options._points;
delete this.#options._master;
this.point = point;
this.radius = radius;
//// OBJECT INITIALIZER(S) ////////////////////
this._angle = new Angle ( angle.start, angle.end, angle.clockwise );
this._stroke = new Stroke ( stroke.color, stroke.type, stroke.segments, stroke.width );
this._fill = new Fill ( fill.color, fill.type );
this._shadow = new Shadow ( shadow.color, shadow.blur, { x: shadow.offset.x, y: shadow.offset.y } );
this.canvas = canvas;
//// ANCILLARY ////////////////////////////////
this.#options.shadow = ( shadow.offset.x != undefined && shadow.offset.y != undefined );
this.#position.master = this;
}
//// PROPERTIES //////////////////////////////////
//// [ POINT ] //////////////////////////
/**
* Set point
* @public
* @function
* @param {Point} value X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
set point ( value ) { }
/**
* Get point
* @public
* @function
* @return {Point} X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
get point ( ) { }
/**
* Set x-axis value
* @public
* @function
* @param {number} value X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
set x ( value ) { }
/**
* Get x-axis value
* @readOnly
* @function
* @return {number} X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
get x ( ) { }
/**
* Set y-axis value
* @public
* @function
* @param {number} value Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
set y ( value ) { }
/**
* Get y-axis value
* @readOnly
* @function
* @return {number} Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
get y ( ) { }
//// [ RADIUS ] ////////////////////////
/**
* Set radius value
* @public
* @function
* @param {number|Point} value Radius of circle
*/
set radius ( value )
{
if ( value )
{
this._radius = ( this._isNumber ( value ) ) ? value : this._radius;
this._radius = ( this._isPoint ( value ) ) ? value : this._radius;
}
}
/**
* Get radius value
* @readOnly
* @function
* @return {number|Point} Radius of circle
*/
get radius ( )
{
return this._radius;
}
//// [ ANGLE ] /////////////////////////
/**
* Get angle properties
* @public
* @function
* @return {Angle} Angle properties
*/
get angle ( )
{
return this._angle;
}
//// [ STROKE ] ////////////////////////
/**
* Get stroke properties
* @public
* @function
* @return {Stroke} Stroke properties
*/
get stroke ( )
{
return this._stroke;
}
//// [ FILL ] //////////////////////////
/**
* Get fill properties
* @public
* @function
* @return {Fill} Fill properties
*/
get fill ( )
{
return this._fill;
}
//// [ SHADOW ] ////////////////////////
/**
* Get shadow properties
* @public
* @function
* @return {Shadow} Shadow properties
*/
get shadow ( )
{
return this._shadow;
}
//// [ CANVAS ] ////////////////////////
/**
* Set canvas value
* @public
* @function
* @param {string} value Canvas id
* @see {@link individual.canvas}
*/
set canvas ( value ) { }
/**
* Get canvas value
* @readOnly
* @function
* @return {string} Canvas id
* @see {@link individual.canvas}
*/
get canvas ( ) { }
//// [ ANCHOR ] ////////////////////////
/**
* Get anchor
* @public
* @function
* @return {Anchor} Anchor properties
*/
get anchor ( )
{
return this._anchor;
}
//// [ OPTIONS ] ///////////////////////
/**
* Get options properties
* @public
* @function
* @return {Options} Options properties
*/
get options ( )
{
return this.#options;
}
//// [ POSITION ] //////////////////////
/**
* Get position properties
* @public
* @function
* @return {Position} Position properties
*/
get position ( )
{
return this.#position;
}
//// VALIDATION ////////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Returns whether the passed value is an Anchor alignment
* @private
* @function
* @param {string} value Anchor alignment
* @return {boolean} True || False
* @see {@link VALIDATION.isAnchor}
*/
_isAnchor ( ) { }
/**
* Returns whether the passed value is an Aspect
* @private
* @function
* @param {Object} value Aspect or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isAspect}
*/
_isAspect ( ) { }
/**
* Returns whether the passed value is a degree
* @private
* @function
* @param {number} value Degree
* @return {boolean} True || False
* @see {@link VALIDATION.isDegree}
*/
_isDegree ( ) { }
/**
* Returns whether the passed value is an element id within the DOM
* @private
* @function
* @param {string} value Element id
* @return {boolean} True || False
* @see {@link VALIDATION.isInDom}
*/
_isInDom ( ) { }
/**
* Returns whether the passed value is a Number value
* @private
* @function
* @param {number} value Number value
* @return {boolean} True || False
* @see {@link VALIDATION.isNumber}
*/
_isNumber ( ) { }
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
//// + PUBLIC //////////////////////
/**
* Check whether the passed object is already present
* @public
* @function
* @param {Circle} circle Object to validate
* @return {boolean} True || False
*/
isThere ( circle )
{
if ( circle instanceof this.constructor )
return (
( this.point.x == circle.point.x ) && // Point X
( this.point.y == circle.point.y ) && // Point Y
( this.radius == circle.radius ) // Radius
) ? true : false;
else
console.warn ( `"${circle.constructor.name}" is not of type ${this.constructor.name}` );
}
//// UTILITIES ////////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Draws anchor point
* @private
* @function
* @see {@link UTILITIES.individual.draw.anchor}
*/
_drawAnchor ( ) { }
/**
* Draws an axis for the associated object
* @private
* @function
* @param {number} offset Offset of axis
* @param {Object} color Color model
* @param {number} stop Gradient color stop
* @see {@link UTILITIES.individual.draw.axis}
*/
_drawAxis ( ) { }
/**
* Draws an axis for the associated object
* @private
* @function
* @param {Aspect} aspect Aspect properties
* @param {Object} color Color model
* @see {@link UTILITIES.individual.draw.border}
*/
_drawBorder ( ) { }
/**
* Draws associated options
* @private
* @function
*/
_drawOptions ( )
{
let _offset = 20;
let _aspect = ( this._isPoint ( this.radius ) ) ? new Aspect ( ( this.radius.x * 2 ) + _offset, ( this.radius.y * 2 ) + _offset )
: new Aspect ( ( this.radius * 2 ) + _offset, ( this.radius * 2 ) + _offset );
////////////////////////////////////////////////////////////////////
if ( this.#options.border ) this._drawBorder ( _aspect );
if ( this.#options.axis ) this._drawAxis ( _offset );
if ( this.#options.anchor ) this._drawAnchor ( );
if ( this.#options.coordinates ) this.showCoordinates ( );
}
/**
* Rotates the origin point by the degree & distance passed
* @private
* @function
* @param {Point} origin Origin point
* @param {number} degree Degree to rotate
* @param {number} distance Distance from origin
* @see {@link UTILITIES.individual.misc.rotatePoint}
*/
_rotatePoint ( ) { }
/**
* Sets anchor's point against this object's point position
* @private
* @function
*/
_setAnchorPoint ( )
{
[ this._anchor.x, this._anchor.y ] = [ this.x, this.y ];
if ( this._isPoint ( this.radius ) )
switch ( this.anchor.align )
{
case 'center': /* ... do nothing */ /* ... do nothing */ break;
case 'top': /* ... do nothing */ this.anchor.y += this.radius.y; break;
case 'topRight': this.anchor.x -= this.radius.x; this.anchor.y += this.radius.y; break;
case 'right': this.anchor.x -= this.radius.x; /* ... do nothing */ break;
case 'bottomRight': this.anchor.x -= this.radius.x; this.anchor.y -= this.radius.y; break;
case 'bottom': /* ... do nothing */ this.anchor.y -= this.radius.y; break;
case 'bottomLeft': this.anchor.x += this.radius.x; this.anchor.y -= this.radius.y; break;
case 'left': this.anchor.x += this.radius.x; /* ... do nothing */ break;
case 'topLeft': this.anchor.x += this.radius.x; this.anchor.y += this.radius.y; break;
}
else
switch ( this.anchor.align )
{
case 'center': /* ... do nothing */ /* ... do nothing */ break;
case 'top': /* ... do nothing */ this.anchor.y += this.radius; break;
case 'topRight': this.anchor.x -= this.radius; this.anchor.y += this.radius; break;
case 'right': this.anchor.x -= this.radius; /* ... do nothing */ break;
case 'bottomRight': this.anchor.x -= this.radius; this.anchor.y -= this.radius; break;
case 'bottom': /* ... do nothing */ this.anchor.y -= this.radius; break;
case 'bottomLeft': this.anchor.x += this.radius; this.anchor.y -= this.radius; break;
case 'left': this.anchor.x += this.radius; /* ... do nothing */ break;
case 'topLeft': this.anchor.x += this.radius; this.anchor.y += this.radius; break;
}
}
/**
* Sets fill type of the associated object
* @private
* @function
* @see {@link UTILITIES.individual.set.fillType}
*/
_setFillType ( ) { }
/**
* Sets shadow properties
* @private
* @function
* @see {@link UTILITIES.individual.set.shadow}
*/
_setShadow ( ) { }
//// + PUBLIC //////////////////////
/**
* Get area of this object
* @readOnly
* @function
* @return {number} Area of this object
*/
get area ( )
{
return ( Math.PI * Math.pow ( this.radius, 2 ) );
}
/**
* Get diameter of circle
* @readOnly
* @function
* @return {number} Diameter of circle
*/
get diameter ( )
{
return ( this.radius * 2 );
}
/**
* Get center of this object
* @readOnly
* @function
* @return {Point} Point coordinates
*/
get center ( )
{
this._setAnchorPoint ( );
let _x = this.x - ( this.x - this.anchor.x );
let _y = this.y - ( this.y - this.anchor.y );
return new Point ( _x, _y );
}
/**
* Get circumference of circle
* @readOnly
* @function
* @return {number} Circumference of circle
*/
get circumference ( )
{
return ( Math.PI * this.diameter ( ) );
}
/**
* Move this object
* @public
* @function
* @param {number} degree Direction to move; in degrees
* @param {number} distance Distance to move
* @see {@link UTILITIES.individual.misc.move}
*/
move ( ) { }
/**
* Rotate this object
* @public
* @function
* @param {number} degree Distance to rotate; in degrees
* @param {string} [anchor='center'] Anchoring point during rotation
* @param {number} [clear=true] Clear canvas during each rotation
* @see {@link UTILITIES.individual.misc.rotate}
*/
rotate ( ) { }
/**
* Shows coordinates of this object
* @public
* @function
* @param {number} [offset=10] Offset of coordinates y origin
* @param {number} [fontSize=16] Coordinates font size
* @see {@link UTILITIES.individual.misc.showCoordinates}
*/
showCoordinates ( ) { }
//// DRAW ////////////////////////////////////////
/**
* Draw this object
* @public
* @function
* @param {string} canvas Canvas Id
*/
draw ( canvas )
{
if ( canvas != undefined ) this.canvas = canvas;
if ( this._canvas instanceof CanvasRenderingContext2D )
{
this._setAnchorPoint ( );
if ( this.#options.shadow ) this._setShadow ( ); // Set: shadow
this._canvas.strokeStyle = this.stroke.color.toCss ( );
this._setFillType ( );
this._canvas.lineWidth = this.stroke.width;
////////////////////////////////////////////////////////////////
this._canvas.setLineDash ( ( this.stroke.type === 'solid' ) ? new Array : this.stroke.segments );
this._canvas.beginPath ( );
( this._isPoint ( this.radius ) )
? this._canvas.ellipse ( this.anchor.x, this.anchor.y, this.radius.x, this.radius.y, 0, this.angle.startInRadians, this.angle.endInRadians, ( this.angle.clockwise ) ? false : true )
: this._canvas.arc ( this.anchor.x, this.anchor.y, this.radius, this.angle.startInRadians, this.angle.endInRadians, ( this.angle.clockwise ) ? false : true );
this._canvas.stroke ( );
if ( this.fill.type != 'pattern' )
this._canvas.fill ( );
if ( this.#options.shadow ) this._canvas.shadowColor = new Rgb ( 0, 0, 0, 0 ).toCss ( ); // Reset: shadow
this._drawOptions ( );
}
else
console.warn ( `'canvas' property is not set for ${this.constructor.name} !` );
}
}
/**
* @class {Object} Ellipse Ellipse object
* @property {Point} point X & Y axis coordinates
* @property {Point} [radius=<Point<20, 30>] Radius of ellipse
* @property {Angle} angle Angle properties
* @property {Stroke} stroke Stroke properties
* @property {Fill} fill Fill properties
* @property {Shadow} shadow Shadow properties
* @property {HTMLCanvasElement} canvas 2D canvas context
* @property {Anchor} anchor Anchor properties
* @property {Options} options Options for this object
* @property {Position} position Position properties
* @extends {Circle}
*/
class Ellipse extends Circle
{
_radius = new Point ( 20, 30 );
_anchor = new Anchor;
//// [ RADIUS ] ////////////////////////////////////
/**
* Set radius value
* @public
* @function
* @param {Point} value Radius of circle
*/
set radius ( value )
{
this._radius = ( value && this._isPoint ( value ) ) ? value : this._radius;
}
/**
* Get radius value
* @readOnly
* @function
* @return {Point} Radius of circle
*/
get radius ( )
{
return this._radius;
}
}
/**
* @class {Object} Line Line object
* @property {Point} start Start X & Y axis coordinates
* @property {Point} end End X & Y axis coordinates
* @property {Stroke} stroke Stroke properties
* @property {Shadow} shadow Shadow properties
* @property {string} [lineCap='round'] Line cap's end points shape
* @property {HTMLCanvasElement} canvas 2D canvas context
* @property {ControlPoints} controlPoints Control point properties
* @property {Options} options Options for this object
* @property {Position} position Position properties
*/
class Line
{
_start = new Point;
_end = new Point;
_stroke = new Stroke;
_shadow = new Shadow;
_lineCap = 'round';
_canvas = undefined;
_anchor = new Anchor;
#options = new Options;
#position = new Position;
#controlPoints = new ControlPoints;
/**
* Create a Line object
* @property {Point} start Starting point of line
* @property {Point} end Ending point of line
* @property {Stroke} stroke Stroke properties
* @property {Shadow} shadow Shadow properties
* @property {string} lineCap Shape of end points
* @property {string} canvas Canvas Id
*/
constructor (
start = { x: undefined, y: undefined },
end = { x: undefined, y: undefined },
stroke = { color: undefined, type: undefined, segments: undefined, width: undefined },
shadow = { color: undefined, blur: undefined, offset: { x: undefined, y: undefined } },
lineCap = undefined,
canvas
)
{
//// COMPOSITION ////////////////////////////
this._isAspect = VALIDATION.isAspect;
this._isDegree = VALIDATION.isDegree;
this._isInDom = VALIDATION.isInDom;
this._isNumber = VALIDATION.isNumber;
this._isPoint = VALIDATION.isPoint;
this._drawAxis = UTILITIES.individual.draw.axis;
this._drawBorder = UTILITIES.individual.draw.border;
this._rotatePoint = UTILITIES.individual.misc.rotatePoint;
this._setShadow = UTILITIES.individual.set.shadow;
this.rotate = UTILITIES.individual.misc.rotate;
Object.defineProperty ( this, 'canvas', PROPERTY_BLOCKS.individual.canvas );
//// POINT OVERLOADING ////////////////////////
if ( start != undefined ) // Start
{
if ( this._isPoint ( start ) ) [ this.start.x, this.end.x ] = [ start.x, end.x ];
if ( this._isNumber ( start ) )
{
let _height = ( start / 2 );
this.start.y = end.y - _height;
this.end.y = end.y + _height;
this.start.x = this.end.x = end.x;
}
}
if ( end != undefined ) // End
{
if ( this._isPoint ( end ) ) [ this.start.y, this.end.y ] = [ start.y, end.y ];
if ( this._isNumber ( end ) )
{
let _width = ( end / 2 );
this.start.x = start.x - _width
this.end.x = start.x + _width;
this.start.y = this.end.y = start.y;
}
}
//// OBJECT INITIALIZER(S) ////////////////////
this._stroke = new Stroke ( stroke.color, stroke.type, stroke.segments, stroke.width );
this._shadow = new Shadow ( shadow.color, shadow.blur, { x: shadow.offset.x, y: shadow.offset.y } );
this.lineCap = lineCap;
this.canvas = canvas;
//// ANCILLARY ////////////////////////////////
this.#options.shadow = ( shadow.offset.x != undefined && shadow.offset.y != undefined );
this.#options.master = this;
this._start.options.master = this;
this._end.options.master = this;
Object.defineProperty ( this.#options, "points",
{
set ( value )
{
this._points = ( typeof value === 'boolean' ) ? value : this.#options.points;
this._master._start.options.points = value;
this._master._end.options.points = value;
},
get ( )
{
return this._points;
}
} );
this.#position.master = this;
}
//// PROPERTIES //////////////////////////////////
//// [ START ] /////////////////////
/**
* Set starting point
* @public
* @function
* @param {Point} value Starting point
*/
set start ( value )
{
this._start = ( this._isPoint ( value ) ) ? value : this._start;
}
/**
* Set starting point
* @public
* @function
* @return {Point} Starting point
*/
get start ( )
{
return this._start;
}
//// [ END ] ///////////////////////
/**
* Set ending point
* @public
* @function
* @param {Point} value Ending point
*/
set end ( value )
{
this._end = ( this._isPoint ( value ) ) ? value : this._end;
}
/**
* Set ending point
* @public
* @function
* @return {Point} Ending point
*/
get end ( )
{
return this._end;
}
//// [ STROKE ] ////////////////////
/**
* Get stroke properties
* @public
* @function
* @return {Stroke} Stroke properties
*/
get stroke ( )
{
return this._stroke;
}
//// [ SHADOW ] ////////////////////
/**
* Get shadow properties
* @public
* @function
* @return {Shadow} Shadow properties
*/
get shadow ( )
{
return this._shadow;
}
//// [ LINECAP ] ///////////////////
/**
* Set line cap
* @public
* @function
* @param {string} value Line cap
*/
set lineCap ( value )
{
this._lineCap = ( value === 'butt' || value === 'round' || value === 'square' ) ? value : this._lineCap;
}
/**
* Get line cap
* @readOnly
* @function
* @return {string} Line cap
*/
get lineCap ( )
{
return this._lineCap;
}
//// [ CANVAS ] ////////////////////
/**
* Set canvas value
* @public
* @function
* @param {string} value Canvas id
* @see {@link PROPERTY_BLOCKS.individual.canvas}
*/
set canvas ( value ) { }
/**
* Get canvas value
* @readOnly
* @function
* @return {string} Canvas id
* @see {@link PROPERTY_BLOCKS.individual.canvas}
*/
get canvas ( ) { }
//// [ ANCHOR ] ////////////////////
/**
* Get anchor
* @public
* @function
* @return {Anchor} Anchor properties
*/
get anchor ( )
{
return this._anchor;
}
//// [ OPTIONS ] ///////////////////
/**
* Get options properties
* @public
* @function
* @return {Options} Options properties
*/
get options ( )
{
return this.#options;
}
//// [ POSITION ] //////////////////
/**
* Get position properties
* @public
* @function
* @return {Position} Position properties
*/
get position ( )
{
return this.#position;
}
//// [ CONTROL POINTS ] ////////////
/**
* Get control point properties
* @public
* @function
* @return {ControlPoints} Control points properties
*/
get controlPoints ( )
{
return this.#controlPoints;
}
//// [ POINT ] ///////////////////// [ VIRTUAL ]
/**
* Set point
* @public
* @function
* @param {Point} value X & Y coordinates
*/
set point ( value )
{
let _xCheck = ( this.center.x > value.x );
let _yCheck = ( this.center.y > value.y );
let _x = ( _xCheck ) ? this.center.x - value.x : value.x - this.center.x;
let _y = ( _yCheck ) ? this.center.y - value.y : value.y - this.center.y;
( _xCheck ) ? [ this.start.x, this.end.x ] = [ this.start.x - _x, this.end.x - _x ]
: [ this.start.x, this.end.x ] = [ this.start.x + _x, this.end.x + _x ];
( _yCheck ) ? [ this.start.y, this.end.y ] = [ this.start.y - _y, this.end.y - _y ]
: [ this.start.y, this.end.y ] = [ this.start.y + _y, this.end.y + _y ];
}
/**
* Get point
* @public
* @function
* @return {Point} X & Y coordinates
*/
get point ( )
{
return this.center;
}
/**
* Set x-axis value
* @public
* @function
* @param {number} value X coordinate value
*/
set x ( value )
{
let _xCheck = ( this.center.x > value );
let _x = ( _xCheck ) ? this.center.x - value : value - this.center.x;
( _xCheck ) ? [ this.start.x, this.end.x ] = [ this.start.x - _x, this.end.x - _x ]
: [ this.start.x, this.end.x ] = [ this.start.x + _x, this.end.x + _x ];
}
/**
* Get x-axis value
* @readOnly
* @function
* @return {number} X coordinate value
*/
get x ( )
{
return this.center.x;
}
/**
* Set y-axis value
* @public
* @function
* @param {number} value Y coordinate value
*/
set y ( value )
{
let _yCheck = ( this.center.y > value );
let _y = ( _yCheck ) ? this.center.y - value : value - this.center.y;
( _yCheck ) ? [ this.start.y, this.end.y ] = [ this.start.y - _y, this.end.y - _y ]
: [ this.start.y, this.end.y ] = [ this.start.y + _y, this.end.y + _y ];
}
/**
* Get y-axis value
* @readOnly
* @function
* @return {number} Y coordinate value
*/
get y ( )
{
return this.center.y;
}
//// VALIDATION ////////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Returns whether the passed value is an Aspect
* @private
* @function
* @param {Object} value Aspect or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isAspect}
*/
_isAspect ( ) { }
/**
* Returns whether the passed value is a degree
* @private
* @function
* @param {number} value Degree
* @return {boolean} True || False
* @see {@link VALIDATION.isDegree}
*/
_isDegree ( ) { }
/**
* Returns whether the passed value is an element id within the DOM
* @private
* @function
* @param {string} value Element id
* @return {boolean} True || False
* @see {@link VALIDATION.isInDom}
*/
_isInDom ( ) { }
/**
* Returns whether the passed value is a Number value
* @private
* @function
* @param {number} value Number value
* @return {boolean} True || False
* @see {@link VALIDATION.isNumber}
*/
_isNumber ( ) { }
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
//// + PUBLIC //////////////////////
/**
* Check whether the passed object is already present
* @public
* @function
* @param {Line} line Object to validate
*/
isThere ( line )
{
if ( line instanceof this.constructor )
{
let _toString = ( valueA, valueB ) => `${valueA} ${valueB}`;
let _thisStart = _toString ( this.start.x, this.start.y ), _thisEnd = _toString ( this.end.x, this.end.y );
let _lineStart = _toString ( line.start.x, line.start.y ), _lineEnd = _toString ( line.end.x, line.end.y );
return ( ( _thisStart == _lineStart ) && ( _thisEnd == _lineEnd ) )
? true
: ( ( _thisStart == _lineEnd ) && ( _thisEnd == _lineStart ) );
}
else
console.warn ( `"${line.constructor.name}" is not of type ${this.constructor.name}` );
}
//// UTILITIES ////////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Draws anchor point
* @private
* @function
*/
_drawAnchor ( )
{
let _point = undefined;
let _aspect = new Aspect ( 5, 5 );
switch ( this.anchor.align )
{
case 'center': _point = new Point ( this.x, this.y ); break;
case 'start': _point = new Point ( this.start.x, this.start.y ); break;
case 'end': _point = new Point ( this.end.x, this.end.y ); break;
}
let _anchor = new Rectangle ( _point, _aspect );
_anchor.fill.color = new Rgb ( 255, 0, 0 );
_anchor.canvas = this.canvas;
_anchor.draw ( );
}
/**
* Draws an axis for the associated object
* @private
* @function
* @param {number} offset Offset of axis
* @param {Object} color Color model
* @param {number} stop Gradient color stop
* @see {@link UTILITIES.individual.draw.axis}
*/
_drawAxis ( ) { }
/**
* Draws an axis for the associated object
* @private
* @function
* @param {Aspect} aspect Aspect properties
* @param {Object} color Color model
* @see {@link UTILITIES.individual.draw.border}
*/
_drawBorder ( ) { }
/**
* Draws associated options
* @private
* @function
*/
_drawOptions ( )
{
let _offset = 20;
let _aspect = new Aspect ( ( this.end.x - this.start.x ) + ( _offset ),
( this.end.y - this.start.y ) + ( _offset ) );
////////////////////////////////////////////////////////////////////
if ( this.#options.border ) this._drawBorder ( _aspect );
if ( this.#options.axis ) this._drawAxis ( );
if ( this.#options.anchor ) this._drawAnchor ( );
if ( this.#options.points ) this.drawPoints ( );
if ( this.#options.coordinates ) this.showCoordinates ( );
if ( this.#options.controlPoints ) this.showControlPoints ( );
}
/**
* Rotates the origin point by the degree & distance passed
* @private
* @function
* @param {Point} origin Origin point
* @param {number} degree Degree to rotate
* @param {number} distance Distance from origin
* @see {@link UTILITIES.individual.misc.rotatePoint}
*/
_rotatePoint ( ) { }
/**
* Sets anchor's point
* @private
* @function
*/
_setAnchorPoint ( )
{
let _point = new Point ( );
switch ( this.anchor.align )
{
case 'start': [ _point.x, _point.y ] = [ this.start.x, this.start.y ]; break;
case 'end': [ _point.x, _point.y ] = [ this.end.x, this.end.y ]; break;
case 'center':
[ _point.x, _point.y ] = [ ( ( this.start.x + this.end.x ) * 0.5 ), ( ( this.start.y + this.end.y ) * 0.5 ) ];
break;
default:
console.warn ( `"${anchor}" is not a valid 'anchor' variable !` );
}
}
/**
* Set line's path
* @private
* @function
*/
_setPath ( )
{
if ( this.controlPoints.p0 != 0 || this.controlPoints.p1 != 0 || this.controlPoints.p2 != 0 || this.controlPoints.p3 != 0 )
this._canvas.bezierCurveTo ( this.controlPoints.p0 + this.start.x, this.controlPoints.p1 + this.start.y,
this.controlPoints.p2 + this.end.x, this.controlPoints.p3 + this.end.y,
this.end.x, this.end.y );
else
this._canvas.lineTo ( this.end.x, this.end.y );
}
/**
* Sets shadow properties
* @private
* @function
* @see {@link UTILITIES.individual.set.shadow}
*/
_setShadow ( ) { }
//// + PUBLIC //////////////////////
/**
* Get center of this object
* @readOnly
* @function
* @return {Point} Center point coordinates
*/
get center ( )
{
let _x = ( this.start.x > this.end.x ) ? this.end.x + ( ( this.start.x - this.end.x ) / 2 )
: this.start.x + ( ( this.end.x - this.start.x ) / 2 );
let _y = ( this.start.y > this.end.y ) ? this.end.y + ( ( this.start.y - this.end.y ) / 2 )
: this.start.y + ( ( this.end.y - this.start.y ) / 2 );
return new Point ( _x, _y );
}
/**
* Set control points for bezier curve
* @public
* @function
* @param {number} p0 Control point 0
* @param {number} p1 Control point 1
* @param {number} p2 Control point 2
* @param {number} p3 Control point 3
*/
curve ( p0, p1, p2, p3 )
{
this.controlPoints.p0 = p0;
this.controlPoints.p1 = p1;
this.controlPoints.p2 = p2;
this.controlPoints.p3 = p3;
}
/**
* Draws start & end points
* @public
* @function
*/
drawPoints ( )
{
this._start.drawOptions ( );
this._end.drawOptions ( );
}
/**
* Move this object
* @public
* @function
* @param {number} degree Direction to move; in degrees
* @param {number} distance Distance to move
*/
move ( degree, distance )
{
let _pointStart = this._rotatePoint ( { x: this.start.x, y: this.start.y }, degree, distance );
let _pointEnd = this._rotatePoint ( { x: this.end.x, y: this.end.y }, degree, distance );
[ this.start.x, this.start.y ] = [ _pointStart.x, _pointStart.y ];
[ this.end.x, this.end.y ] = [ _pointEnd.x, _pointEnd.y ];
}
/**
* Rotate this object
* @public
* @function
* @param {number} degree Distance to rotate; in degrees
* @param {string} [anchor='center'] Anchoring point during rotation
* @param {number} [clear=true] Clear canvas during each rotation
* @see {@link UTILITIES.individual.misc.rotate}
*/
rotate ( ) { }
/**
* Show control points for this object
* @public
* @function
* @param {number} [offset=10] Offset of control points y origin
* @param {number} [fontSize=16] Control points font size
*/
showControlPoints ( offset = 10, fontSize = 16 )
{
let _point0 = new Point ( this.start.x, this.start.y );
let _point1 = new Point ( this.controlPoints.p0 + this.start.x, this.controlPoints.p1 + this.start.y );
let _point2 = new Point ( this.controlPoints.p2 + this.end.x, this.controlPoints.p3 + this.end.y );
let _point3 = new Point ( this.end.x, this.end.y );
let _textStart = new Text ( _point1.x, _point1.y, `( ${this.#controlPoints.p0}, ${this.#controlPoints.p1} )` );
let _textEnd = new Text ( _point2.x, _point2.y, `( ${this.#controlPoints.p3}, ${this.#controlPoints.p4} )` );
_textStart.canvas = _textEnd.canvas = this.canvas;
_textStart.size = _textEnd.size = fontSize;
_textStart.offset.y = _textEnd.offset.y = - ( offset * 2 );
_textStart.stroke.color = _textEnd.stroke.color = new Rgb ( 255, 0, 0 );
_textStart.options.shadow = _textEnd.options.shadow = true;
_textStart.shadow.color = _textEnd.shadow.color = new Rgb ( 255, 255, 255 );
_textStart.shadow.blur = _textEnd.shadow.blur = 1;
_textStart.shadow.x = _textEnd.shadow.x = _textStart.shadow.y = _textEnd.shadow.y = 1;
_textStart.draw ( );
_textEnd.draw ( );
////////////////////////////////////////////////////////////////////////////////////////
let _red = new Rgb ( 255, 0, 0 );
let _blue = new Rgb ( 0, 0, 255 );
let _green = new Rgb ( 0, 255, 0 );
let _lineSegments = [ 2, 4 ];
let _lineA = new Line ( _point0, _point1 );
let _lineB = new Line ( _point2, _point3 );
let _lineC = new Line ( _point1, _point2 );
let _lines = new Lines ( _lineA, _lineB, _lineC );
_lineA.stroke.type = _lineB.stroke.type = _lineC.stroke.type = 1;
_lineA.stroke.segments = _lineB.stroke.segments = _lineC.stroke.segments = _lineSegments;
_lineA.canvas = _lineB.canvas = _lineC.canvas = this.canvas;
[ _lineA.stroke.color, _lineB.stroke.color, _lineC.stroke.color ] = [ _red, _blue, _green ];
_lineA.draw ( );
_lineB.draw ( );
_lineC.draw ( );
let _circleA = new Circle ( _point1 );
let _circleB = new Circle ( _point2 );
_circleA.radius = _circleB.radius = 3;
_circleA.stroke.color.alpha = _circleB.stroke.color.alpha = 0;
_circleA.canvas = _circleB.canvas = this.canvas;
[ _circleA.fill.color, _circleB.fill.color ] = [ _red, _blue ];
_circleA.draw ( );
_circleB.draw ( );
}
/**
* Shows coordinates of this object
* @public
* @function
* @param {number} [offset=10] Offset of coordinates y origin
* @param {number} [fontSize=16] Coordinates font size
*/
showCoordinates ( offset = 10, fontSize = 16 )
{
let _textStart = new Text ( this.start, `( ${this.start.x}, ${this.start.y} )` );
let _textEnd = new Text ( this.end, `( ${this.end.x}, ${this.end.y} )` );
_textStart.canvas = _textEnd.canvas = this.canvas;
_textStart.size = _textEnd.size = fontSize;
_textStart.options.shadow = _textEnd.options.shadow = false;
_textStart.offset.y = _textEnd.offset.y = - ( offset * 2 );
_textStart.options.shadow = _textEnd.options.shadow = true;
_textStart.shadow.color = _textEnd.shadow.color = new Rgb ( 255, 255, 255 );
_textStart.shadow.blur = _textEnd.shadow.blur = 1;
_textStart.shadow.x = _textEnd.shadow.x = _textStart.shadow.y = _textEnd.shadow.y = 1;
_textStart.draw ( );
_textEnd.draw ( );
}
//// DRAW ////////////////////////////////////////
/**
* Draw this object
* @public
* @function
* @param {string} canvas Canvas Id
*/
draw ( canvas )
{
if ( canvas != undefined ) this.canvas = canvas;
if ( this._canvas instanceof CanvasRenderingContext2D )
{
let _straddle = 0.5;
if ( this.#options.shadow ) this._setShadow ( ); // Set: shadow
this._canvas.strokeStyle = this.stroke.color.toCss ( );
this._canvas.lineCap = this.lineCap;
this._canvas.lineWidth = this.stroke.width;
////////////////////////////////////////////////////////////////
this._canvas.setLineDash ( ( this.stroke.type === 'solid' ) ? new Array : this.stroke.segments );
this._canvas.beginPath ( );
this._canvas.moveTo ( this.start.x + _straddle, this.start.y + _straddle );
this._setPath ( );
this._canvas.stroke ( );
if ( this.#options.shadow ) this._canvas.shadowColor = new Rgb ( 0, 0, 0, 0 ).toCss ( ); // Reset: shadow
this._drawOptions ( );
}
else
console.warn ( `'canvas' property is not set for ${this.constructor.name} !` );
}
}
/**
* @class {Object} Rectangle Rectangle object
* @property {Point} point X & Y axis coordinates
* @property {Aspect} aspect Aspect properties
* @property {Array} round Rounding properties
* @property {Stroke} stroke Stroke properties
* @property {Fill} fill Fill properties
* @property {Shadow} shadow Shadow properties
* @property {Anchor} anchor Anchor properties
* @property {HTMLCanvasElement} canvas 2D canvas context
* @property {Options} options Options for this object
* @property {Position} position Position properties
*/
class Rectangle
{
_point = new Point;
_aspect = new Aspect;
_round = new Array;
_stroke = new Stroke;
_fill = new Fill;
_shadow = new Shadow;
_anchor = new Anchor;
_canvas = undefined;
#options = new Options;
#position = new Position;
/**
* Create a Rectangle object
* @property {Point} point X & Y axis coordinates
* @property {Aspect} aspect Aspect properties
* @property {Array} round Rounding properties
* @property {Stroke} stroke Stroke properties
* @property {Fill} fill Fill properties
* @property {Shadow} shadow Shadow properties
* @property {string} canvas Canvas Id
*/
constructor (
point = { x: undefined, y: undefined },
aspect = { width: undefined, height: undefined },
round = [ 0, 0, 0, 0 ],
stroke = { color: undefined, type: undefined, segments: undefined, width: undefined },
fill = { color: undefined, type: undefined },
shadow = { color: undefined, blur: undefined, offset: { x: undefined, y: undefined } },
canvas
)
{
//// COMPOSITION ////////////////////////////
this._isAspect = VALIDATION.isAspect;
this._isDegree = VALIDATION.isDegree;
this._isInDom = VALIDATION.isInDom;
this._isPoint = VALIDATION.isPoint;
this._drawAnchor = UTILITIES.individual.draw.anchor;
this._drawAxis = UTILITIES.individual.draw.axis;
this._drawBorder = UTILITIES.individual.draw.border;
this._rotatePoint = UTILITIES.individual.misc.rotatePoint;
this._setAnchorPoint = UTILITIES.individual.set.anchorPoint;
this._setFillType = UTILITIES.individual.set.fillType;
this._setShadow = UTILITIES.individual.set.shadow;
this.move = UTILITIES.individual.misc.move;
this.rotate = UTILITIES.individual.misc.rotate;
this.showCoordinates = UTILITIES.individual.misc.showCoordinates;
Object.defineProperty ( this, 'area', PROPERTY_BLOCKS.individual.area );
Object.defineProperty ( this, 'canvas', PROPERTY_BLOCKS.individual.canvas );
Object.defineProperty ( this, 'center', PROPERTY_BLOCKS.individual.center );
Object.defineProperty ( this, 'perimeter', PROPERTY_BLOCKS.individual.perimeter );
Object.defineProperty ( this, 'point', PROPERTY_BLOCKS.individual.point );
Object.defineProperty ( this, 'x', PROPERTY_BLOCKS.individual.pointX );
Object.defineProperty ( this, 'y', PROPERTY_BLOCKS.individual.pointY );
delete this.#options._controlPoints;
delete this.#options._points;
delete this.#options._master;
this.point = point;
this.width = ( aspect.width != undefined ) ? aspect.width : 50;
this.height = ( aspect.height != undefined ) ? aspect.height : 50;
this.round = round;
//// OBJECT INITIALIZER(S) ////////////////////
this._stroke = new Stroke ( stroke.color, stroke.type, stroke.segments, stroke.width );
this._fill = new Fill ( fill.color, fill.type );
this._shadow = new Shadow ( shadow.color, shadow.blur, shadow.offset );
this.canvas = canvas;
//// ANCILLARY ////////////////////////////////
this.#options.shadow = ( shadow.offset.x != undefined && shadow.offset.y != undefined );
this.#position.master = this;
}
//// PROPERTIES //////////////////////////////////
//// [ POINT ] /////////////////////
/**
* Set point
* @public
* @function
* @param {Point} value X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
set point ( value ) { }
/**
* Get point
* @public
* @function
* @return {Point} X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
get point ( ) { }
/**
* Set x-axis value
* @public
* @function
* @param {number} value X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
set x ( value ) { }
/**
* Get x-axis value
* @readOnly
* @function
* @return {number} X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
get x ( ) { }
/**
* Set y-axis value
* @public
* @function
* @param {number} value Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
set y ( value ) { }
/**
* Get y-axis value
* @readOnly
* @function
* @return {number} Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
get y ( ) { }
//// [ ASPECT ] ////////////////////
/**
* Set aspect properties
* @public
* @function
* @param {Aspect} value Aspect properties
*/
set aspect ( value )
{
this._aspect = ( this._isAspect ( value ) ) ? value : this._aspect;
}
/**
* Get aspect properties
* @readOnly
* @function
* @return {Aspect} Aspect properties
*/
get aspect ( )
{
return this._aspect;
}
/**
* Set aspect width
* @public
* @function
* @param {number} value Width value
*/
set width ( value )
{
this._aspect.width = ( typeof value === 'number' && value > 0 ) ? value : this._aspect._width;
}
/**
* Get aspect with
* @readOnly
* @function
* @return {number} Width value
*/
get width ( )
{
return this._aspect.width;
}
/**
* Set aspect height
* @public
* @function
* @param {number} value Height value
*/
set height ( value )
{
this._aspect.height = ( typeof value === 'number' && value > 0 ) ? value : this._aspect._height;
}
/**
* Get aspect height
* @readOnly
* @function
* @return {number} Height value
*/
get height ( )
{
return this._aspect.height;
}
//// [ ROUND ] /////////////////////
/**
* Set round properties
* @public
* @function
* @param {Array} value Radii properties
*/
set round ( value )
{
this._round = Array.isArray ( value ) ? value : this._round;
}
/**
* Get round properties
* @readOnly
* @function
* @return {Array} Radii properties
*/
get round ( )
{
return this._round;
}
//// [ STROKE ] ////////////////////
/**
* Get stroke properties
* @public
* @function
* @return {Stroke} Stroke properties
*/
get stroke ( )
{
return this._stroke;
}
//// [ FILL ] //////////////////////
/**
* Get fill properties
* @public
* @function
* @return {Fill} Fill properties
*/
get fill ( )
{
return this._fill;
}
//// [ SHADOW ] ////////////////////
/**
* Get shadow properties
* @public
* @function
* @return {Shadow} Shadow properties
*/
get shadow ( )
{
return this._shadow;
}
//// [ CANVAS ] ////////////////////
/**
* Set canvas value
* @public
* @function
* @param {string} value Canvas id
* @see {@link PROPERTY_BLOCKS.individual.canvas}
*/
set canvas ( value ) { }
/**
* Get canvas value
* @readOnly
* @function
* @return {string} Canvas id
* @see {@link PROPERTY_BLOCKS.individual.canvas}
*/
get canvas ( ) { }
//// [ ANCHOR ] ////////////////////
/**
* Get anchor
* @public
* @function
* @return {Anchor} Anchor properties
*/
get anchor ( )
{
return this._anchor;
}
//// [ OPTIONS ] ///////////////////
/**
* Get options properties
* @public
* @function
* @return {Options} Options properties
*/
get options ( )
{
return this.#options;
}
//// [ POSITION ] //////////////////
get position ( )
{
return this.#position;
}
//// VALIDATION ////////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Returns whether the passed value is an Aspect
* @private
* @function
* @param {Object} value Aspect or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isAspect}
*/
_isAspect ( ) { }
/**
* Returns whether the passed value is a degree
* @private
* @function
* @param {number} value Degree
* @return {boolean} True || False
* @see {@link VALIDATION.isDegree}
*/
_isDegree ( ) { }
/**
* Returns whether the passed value is an element id within the DOM
* @private
* @function
* @param {string} value Element id
* @return {boolean} True || False
* @see {@link VALIDATION.isInDom}
*/
_isInDom ( ) { }
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
//// + PUBLIC //////////////////////
/**
* Check whether the passed object is already present
* @public
* @function
* @param {Rectangle} rectangle Object to validate
*/
isThere ( rectangle )
{
if ( rectangle instanceof this.constructor )
return (
( this.x == rectangle.x ) && // Point X
( this.y == rectangle.y ) && // Point Y
( this.width == rectangle.width ) && // Width
( this.height == rectangle.height ) // Height
) ? true : false;
else
console.warn ( `"${rectangle.constructor.name}" is not of type ${this.constructor.name}` );
}
//// UTILITIES ////////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Draws anchor point
* @private
* @function
* @see {@link UTILITIES.individual.draw.anchor}
*/
_drawAnchor ( ) { }
/**
* Draws an axis for the associated object
* @private
* @function
* @param {number} offset Offset of axis
* @param {Object} color Color model
* @param {number} stop Gradient color stop
* @see {@link UTILITIES.individual.draw.axis}
*/
_drawAxis ( ) { }
/**
* Draws an axis for the associated object
* @private
* @function
* @param {Aspect} aspect Aspect properties
* @param {Object} color Color model
* @see {@link UTILITIES.individual.draw.border}
*/
_drawBorder ( ) { }
/**
* Draws associated options
* @private
* @function
*/
_drawOptions ( )
{
let _offset = 20;
let _aspect = new Aspect ( this.width + _offset, this.height + _offset );
////////////////////////////////////////////////////////////////////
if ( this.#options.border ) this._drawBorder ( _aspect );
if ( this.#options.axis ) this._drawAxis ( _offset );
if ( this.#options.anchor ) this._drawAnchor ( );
if ( this.#options.coordinates ) this.showCoordinates ( );
}
/**
* Rotates the origin point by the degree & distance passed
* @private
* @function
* @param {Point} origin Origin point
* @param {number} degree Degree to rotate
* @param {number} distance Distance from origin
* @see {@link UTILITIES.individual.misc.rotatePoint}
*/
_rotatePoint ( ) { }
/**
* Sets anchor's point against this object's point position
* @private
* @function
* @see {@link UTILITIES.individual.set.anchorPoint}
*/
_setAnchorPoint ( ) { }
/**
* Sets fill type of the associated object
* @private
* @function
* @see {@link UTILITIES.individual.set.fillType}
*/
_setFillType ( ) { }
/**
* Sets shadow properties
* @private
* @function
* @see {@link UTILITIES.individual.set.shadow}
*/
_setShadow ( ) { }
//// + PUBLIC //////////////////////
/**
* Get area of this object
* @readOnly
* @function
* @return {number} Area of this object
* @see {@link PROPERTY_BLOCKS.individual.area}
*/
get area ( ) { }
/**
* Get center of this object
* @readOnly
* @function
* @return {Point} Center point coordinates
* @see {@link PROPERTY_BLOCKS.individual.center}
*/
get center ( ) { }
/**
* Cycle colors for fill
* @public
* @function
* @param {number} progress Progress time unit between; 0.00 - 1.00
* @param {Rgb} start Starting RGB value
* @param {Rgb} end Ending RGB value
* @param {number} [max=1] Maximum increments
* @see {@link UTILITIES.individual.color.cycle.fill}
*/
fillColorCycle ( ) { }
/**
* Cycle colors for gradient
* @public
* @function
* @param {number} progress Progress time unit between; 0.00 - 1.00
* @param {Rgb} start Starting RGB value
* @param {Rgb} end Ending RGB value
* @param {number} stop Gradient color stop
* @param {number} [max=1] Maximum increments
* @see {@link UTILITIES.individual.color.cycle.gradient}
*/
gradientColorCycle ( ) { }
/**
* Move this object
* @public
* @function
* @param {number} degree Direction to move; in degrees
* @param {number} distance Distance to move
* @param {boolean} [draw=false] Draw post movement
* @param {boolean} [clear=false] Clear canvas during each movement
* @see {@link UTILITIES.individual.misc.move}
*/
move ( ) { }
/**
* Get perimeter
* @readOnly
* @function
* @return {number} Perimeter of rectangle
* @see {@link PROPERTY_BLOCKS.individual.center}
*/
get perimeter ( ) { }
/**
* Rotate this object
* @public
* @function
* @param {number} degree Distance to rotate; in degrees
* @param {string} [anchor='center'] Anchoring point during rotation
* @param {number} [clear=true] Clear canvas during each rotation
* @see {@link UTILITIES.individual.misc.rotate}
*/
rotate ( ) { }
/**
* Shows coordinates of this object
* @public
* @function
* @param {number} [offset=10] Offset of coordinates y origin
* @param {number} [fontSize=16] Coordinates font size
* @see {@link UTILITIES.individual.misc.showCoordinates}
*/
showCoordinates ( ) { }
//// DRAW ////////////////////////////////////////
/**
* Draw this object
* @public
* @function
* @param {string} canvas Canvas Id
*/
draw ( canvas )
{
if ( canvas != undefined ) this.canvas = canvas;
if ( this._canvas instanceof CanvasRenderingContext2D )
{
this._setAnchorPoint ( );
if ( this.#options.shadow ) this._setShadow ( ); // Set: shadow
this._canvas.strokeStyle = this.stroke.color.toCss ( );
this._setFillType ( );
this._canvas.lineWidth = this.stroke.width;
////////////////////////////////////////////////////////////////
this._canvas.setLineDash ( ( this.stroke.type === 'solid' ) ? new Array : this.stroke.segments );
this._canvas.beginPath ( );
this._canvas.roundRect ( this.anchor.x, this.anchor.y, this.width, this.height, this.round );
this._canvas.stroke ( );
if ( this.fill.type != 'pattern' )
this._canvas.fill ( );
if ( this.#options.shadow ) this._canvas.shadowColor = new Rgb ( 0, 0, 0, 0 ).toCss ( ); // Reset: shadow
this._drawOptions ( );
}
else
console.warn ( `'canvas' property is not set for ${this.constructor.name} !` );
}
}
/**
* @class {Object} RoundingRectangle RoundingRectangle object
* @property {Point} point X & Y axis coordinates
* @property {Aspect} aspect Aspect properties
* @property {Array} [round=[5, 5, 5, 5]] Rounding properties
* @property {Stroke} stroke Stroke properties
* @property {Fill} fill Fill properties
* @property {Shadow} shadow Shadow properties
* @property {Anchor} anchor Anchor properties
* @property {HTMLCanvasElement} canvas 2D canvas context
* @property {Options} options Options for this object
* @property {Position} position Position properties
* @extends {Rectangle}
*/
class RoundedRectangle extends Rectangle
{
_round = [ 5, 5, 5, 5 ];
}
/**
* @class {Object} Text Text element to render within a canvas element
* @property {Point} point X & Y axis coordinates
* @property {string} text Text to display
* @property {Stroke} stroke Stroke properties
* @property {Fill} fill Fill properties
* @property {Shadow} shadow Shadow properties
* @property {HTMLCanvasElement} canvas 2D canvas context
* @property {Options} options Options for this object
* @property {Position} position Position properties
*/
class Text extends Font
{
_point = new Point;
_text = undefined;
_stroke = new Stroke;
_fill = new Fill;
_shadow = new Shadow;
_canvas = undefined;
#options = new Options;
#position = new Position;
/**
* Create a Text object
* @param {Point} point X & Y axis coordinates
* @param {string} text Text of text object
* @param {string} type Font type
* @param {number} size Font size
* @param {string} weight Font weight
* @param {number} maxWidth Font max width
* @param {Point} offset Text offset
* @param {Stroke} stroke Stroke properties
* @param {Fill} fill Fill Properties
* @param {Shadow} shadow Shadow properties
* @param {string} canvas Canvas Id
*/
constructor (
point = { x: undefined, y: undefined },
text, type, size, weight, maxWidth,
offset = { x: undefined, y: undefined },
stroke = { color: undefined, type: undefined, segments: undefined, width: undefined },
fill = { color: undefined, type: undefined },
shadow = { color: undefined, blur: undefined, offset: { x: undefined, y: undefined } },
canvas
)
{
super ( );
//// COMPOSITION ////////////////////////////
this._isDegree = VALIDATION.isDegree;
this._isInDom = VALIDATION.isInDom;
this._isPoint = VALIDATION.isPoint;
this._drawAnchor = UTILITIES.individual.draw.anchor;
this._rotatePoint = UTILITIES.individual.misc.rotatePoint;
this._setFillType = UTILITIES.individual.set.fillType;
this._setShadow = UTILITIES.individual.set.shadow;
this.move = UTILITIES.individual.misc.move;
this.rotate = UTILITIES.individual.misc.rotate;
this.showCoordinates = UTILITIES.individual.misc.showCoordinates;
Object.defineProperty ( this, 'canvas', PROPERTY_BLOCKS.individual.canvas );
Object.defineProperty ( this, 'offset', PROPERTY_BLOCKS.individual.offset );
Object.defineProperty ( this, 'point', PROPERTY_BLOCKS.individual.point );
Object.defineProperty ( this, 'x', PROPERTY_BLOCKS.individual.pointX );
Object.defineProperty ( this, 'y', PROPERTY_BLOCKS.individual.pointY );
stroke.width = ( stroke.width === undefined ) ? 0 : stroke.width;
fill.color = ( fill.color === undefined ) ? new Rgb ( 0, 0, 0 ) : fill.color; // Set: default fill property as 'Black'
this.point = point;
this.text = text;
//// SUPER CLASS PROPERTIES ////////////////////
super.type = type;
super.size = size;
super.weight = weight;
super.maxWidth = maxWidth;
super.offset.x = offset.x;
super.offset.y = offset.y;
//// OBJECT INITIALIZER(S) ////////////////////
this._stroke = new Stroke ( stroke.color, stroke.type, stroke.segments, stroke.width );
this._fill = new Fill ( fill.color, fill.type );
this._shadow = new Shadow ( shadow.color, shadow.blur, { x: shadow.offset.x, y: shadow.offset.y } );
this.canvas = canvas;
//// ANCILLARY ////////////////////////////////
this.#options.shadow = ( shadow.offset.x != undefined && shadow.offset.y != undefined );
this.#position.master = this;
}
//// PROPERTIES //////////////////////////////////
//// [ POINT ] //////////////////////
/**
* Set point
* @public
* @function
* @param {Point} value X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
set point ( value ) { }
/**
* Get point
* @public
* @function
* @return {Point} X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
get point ( ) { }
/**
* Set x-axis value
* @public
* @function
* @param {number} value X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
set x ( value ) { }
/**
* Get x-axis value
* @readOnly
* @function
* @return {number} X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
get x ( ) { }
/**
* Set the y-axis value
* @public
* @function
* @param {number} value Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
set y ( value ) { }
/**
* Get y-axis value
* @readOnly
* @function
* @return {number} Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
get y ( ) { }
//// [ TEXT ] //////////////////////
/**
* Set text
* @public
* @function
* @param {string} value Text of object
*/
set text ( value )
{
this._text = ( typeof value === 'string' ) ? value : undefined;
}
/**
* Get text
* @readOnly
* @function
* @return {string} Text of object
*/
get text ( )
{
return this._text;
}
//// [ STROKE ] //////////////////////
/**
* Get stroke properties
* @public
* @function
* @return {Stroke} Stroke properties
*/
get stroke ( )
{
return this._stroke;
}
//// [ FILL ] //////////////////////
/**
* Get fill properties
* @public
* @function
* @return {Fill} Fill properties
*/
get fill ( )
{
return this._fill;
}
//// [ SHADOW ] //////////////////////
/**
* Get shadow properties
* @public
* @function
* @return {Shadow} Shadow properties
*/
get shadow ( )
{
return this._shadow;
}
//// [ CANVAS ] //////////////////////
/**
* Set canvas value
* @public
* @function
* @param {string} value Canvas id
* @see {@link PROPERTY_BLOCKS.individual.canvas}
*/
set canvas ( value ) { }
/**
* Get canvas value
* @readOnly
* @function
* @return {string} Canvas id
* @see {@link PROPERTY_BLOCKS.individual.canvas}
*/
get canvas ( ) { }
//// [ OPTIONS ] //////////////////////
/**
* Get options properties
* @public
* @function
* @return {Options} Options properties
*/
get options ( )
{
return this.#options;
}
//// [ POSITION ] //////////////////
/**
* Get position properties
* @public
* @function
* @return {Position} Position properties
*/
get position ( )
{
return this.#position;
}
//// * SUPER * //////////////////////
//// [ type ] ////////
/**
* Set font's type
* @public
* @function
* @param {string} value Font's type
*/
set type ( value ) { super.type = value; }
/**
* Get font's type
* @readOnly
* @function
* @return {string} Font's type
*/
get type ( ) { return super.type; }
//// [ size ] ////////
/**
* Set font's size
* @public
* @function
* @param {number} value Font's size
*/
set size ( value ) { super.size = value; }
/**
* Get font's size
* @readOnly
* @function
* @return {number} Font's size
*/
get size ( ) { return super.size; }
//// [ weight ] //////
/**
* Set font's weight
* @public
* @function
* @param {string} value Font's weight
*/
set weight ( value ) { super.weight = value; }
/**
* Get font's weight
* @readOnly
* @function
* @return {string} Font's weight
*/
get weight ( ) { return super.weight; }
//// [ maxWidth ] ////
/**
* Set font's max width
* @public
* @function
* @param {number} value Font's max width
*/
set maxWidth ( value )
{
super.maxWidth = ( value == undefined )
? ( this._canvas != undefined )
? this._canvas.measureText ( value )
: value
: value;
}
/**
* Get font's max width
* @readOnly
* @function
* @return {number} Font's max width
*/
get maxWidth ( ) { return super.maxWidth; }
//// [ offset ] //////
/**
* Set offset
* @public
* @function
* @param {Point} value Shadow offset
* @see {@link PROPERTY_BLOCKS.individual.offset}
*/
set offset ( value ) { }
/**
* Get offset
* @readOnly
* @function
* @return {Point} Shadow offset
* @see {@link PROPERTY_BLOCKS.individual.offset}
*/
get offset ( ) { }
//// [ font ] ////////
/**
* Get font
* @public
* @function
* @return {string} CSS style font property syntax
*/
get font ( )
{
return super.font;
}
//// VALIDATION //////////////////////////////////
/**
* Returns whether the passed value is a degree
* @private
* @function
* @param {number} value Degree
* @return {boolean} True || False
* @see {@link VALIDATION.isDegree}
*/
_isDegree ( ) { }
/**
* Returns whether the passed value is an element id within the DOM
* @private
* @function
* @param {string} value Element id
* @return {boolean} True || False
* @see {@link VALIDATION.isInDom}
*/
_isInDom ( ) { }
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
//// UTILITIES ////////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Rotates the origin point by the degree & distance passed
* @private
* @function
* @param {Point} origin Origin point
* @param {number} degree Degree to rotate
* @param {number} distance Distance from origin
* @see {@link UTILITIES.individual.misc.rotatePoint}
*/
_rotatePoint ( ) { }
/**
* Sets fill type of the associated object
* @private
* @function
* @see {@link UTILITIES.individual.set.fillType}
*/
_setFillType ( ) { }
/**
* Sets shadow properties
* @private
* @function
* @see {@link UTILITIES.individual.set.shadow}
*/
_setShadow ( ) { }
/**
* Draws anchor point
* @private
* @function
* @see {@link UTILITIES.individual.draw.anchor}
*/
_drawAnchor ( ) { }
/**
* Draws axis through center of this object
* @private
* @function
* @param {number} [offset=10] Offset of axis's edges
*/
_drawAxis ( offset = 10 )
{
let _red = new Rgb ( 245, 80, 50, 1 );
let _size = super.size;
let _width = this._canvas.measureText ( this.text ).width;
let _height = _size - ( offset / 2 );
let _aspect = new Aspect ( _width + offset, _height );
let _xAxisStart = new Point ( this.x - ( _aspect.width / 2 ) - ( offset * 2 ), this.y - ( _aspect.height / 2.5 ) );
let _xAxisEnd = new Point ( this.x + ( _aspect.width / 2 ) + ( offset * 2 ), this.y - ( _aspect.height / 2.5 ) );
let _yAxisStart = new Point ( this.x, this.y - _aspect.height - offset );
let _yAxisEnd = new Point ( this.x, this.y + _aspect.height - ( _size / 2 ) );
let _xAxis = new Line ( _xAxisStart, _xAxisEnd, /* Point ( Start, End ) */
/* Stroke */ { color: _red, type: 'solid', segments: undefined, width: 1 },
/* Shadow */ undefined,
/* LineCap */ undefined,
/* Canvas */ this.canvas );
let _yAxis = new Line ( _yAxisStart, _yAxisEnd, /* Point ( Start, End ) */
/* Stroke */ { color: _red, type: 'solid', segments: undefined, width: 1 },
/* Shadow */ undefined,
/* LineCap */ undefined,
/* Canvas */ this.canvas );
_xAxis.draw ( );
_yAxis.draw ( );
}
/**
* Draws border around this object
* @private
* @function
* @param {number} [offset=10] Offset of border's perimeter
*/
_drawBorder ( offset = 10 )
{
let _red = new Rgb ( 245, 80, 50, 1 );
let _clear = new Rgb ( 0, 0, 0, 0 );
let _size = super.size;
let _width = this._canvas.measureText ( this.text ).width;
let _height = _size - ( offset / 2 );
let _aspect = new Aspect ( _width + offset, _height );
let _scaler = ( _size > 60 ) ? 3 : ( _size > 30 ) ? 3.5 : 4.5;
let _y = this.y - ( _height / _scaler ) - ( offset / 2 );
let _point = new Point ( this.x, _y );
let _border = new Rectangle (
/* Point */ _point,
/* Aspect */ _aspect,
/* Round */ undefined,
/* Stroke */ { color: _red, type: 'solid', segments: undefined, width: 1 },
/* Fill */ { color: _clear, type: 'solid' },
/* Shadow */ undefined,
/* Canvas */ this.canvas
);
_border.draw ( );
}
/**
* Draws associated options
* @private
* @function
*/
_drawOptions ( )
{
if ( this.#options.border ) this._drawBorder ( );
if ( this.#options.axis ) this._drawAxis ( );
if ( this.#options.anchor ) this._drawAnchor ( );
if ( this.#options.coordinates ) this.showCoordinates ( );
}
//// + PUBLIC //////////////////////
/**
* Move this object
* @public
* @function
* @param {number} degree Direction to move; in degrees
* @param {number} distance Distance to move
* @param {boolean} [draw=false] Draw post movement
* @param {boolean} [clear=false] Clear canvas during each movement
* @see {@link UTILITIES.individual.misc.move}
*/
move ( ) { }
/**
* Rotate this object
* @public
* @function
* @param {number} degree Distance to rotate; in degrees
* @param {string} [anchor='center'] Anchoring point during rotation
* @param {number} [clear=true] Clear canvas during each rotation
* @see {@link UTILITIES.individual.misc.rotate}
*/
rotate ( ) { }
/**
* Shows coordinates of this object
* @public
* @function
* @param {number} [offset=10] Offset of coordinates y origin
* @param {number} [fontSize=16] Coordinates font size
* @see {@link UTILITIES.individual.misc.showCoordinates}
*/
showCoordinates ( ) { }
//// DRAW ////////////////////////////////////////
/**
* Draw this object
* @public
* @function
* @param {string} canvas Canvas Id
*/
draw ( canvas, shadow = false )
{
if ( canvas != undefined ) this.canvas = canvas;
if ( this._canvas instanceof CanvasRenderingContext2D )
{
if ( this.#options.shadow ) this._setShadow ( ); // Set: shadow
[ this.x, this.y ] = [ ( this.x + this.offset.x ), ( this.y + this.offset.y ) ]; // Incorporate offset from super class
this._canvas.font = this.font;
this._canvas.textAlign = 'center';
this._setFillType ( );
this._canvas.fillText ( this.text, this.x, this.y, this.maxWidth ); // TODO: maxWidth is showing NaN !
if ( this.stroke.width > 0 )
{
let _width = this._canvas.lineWidth;
this._canvas.lineWidth = this.stroke.width;
this._canvas.strokeStyle = this.stroke.color.toCss ( );
this._canvas.strokeText ( this.text, this.x, this.y, this.maxWidth );
this._canvas.lineWidth = _width;
}
if ( this.#options.shadow ) this._canvas.shadowColor = new Rgb ( 0, 0, 0, 0 ).toCss ( ); // Reset: shadow
this._drawOptions ( );
}
else
console.warn ( `'canvas' property is not set for ${this.constructor.name} !` );
}
}
/**
* @class {Object} cImage cImage object
* @property {string} source Source path of image file
* @property {Object} primary Primary set of coordinates
* @property {Object} secondary Secondary set of coordinates
* @property {HTMLCanvasElement} canvas 2D canvas context
* @property {Anchor} anchor Anchor properties
* @property {Options} options Options for this object
* @property {Position} position Position properties
*/
class cImage
{
_source = new Image;
_primary =
{
point: new Point,
aspect: new Aspect
}
_secondary =
{
point: undefined,
aspect: undefined
}
_canvas = undefined;
_anchor = new Anchor;
#options = new Options;
#position = new Position;
/**
* Create a cImage object
* @class {Object} cImage cImage object
* @property {string} source Source path of image file
* @property {Object} primary Primary set of coordinates
* @property {Object} secondary Secondary set of coordinates
* @property {HTMLCanvasElement} canvas Canvas Id
*/
constructor (
source,
primary = { point: { x: undefined, y: undefined }, aspect: { width: undefined, height: undefined } },
secondary = { point: { x: undefined, y: undefined }, aspect: { width: undefined, height: undefined } },
canvas
)
{
//// COMPOSITION ////////////////////////////
this._isAspect = VALIDATION.isAspect;
this._isDegree = VALIDATION.isDegree;
this._isInDom = VALIDATION.isInDom;
this._isPoint = VALIDATION.isPoint;
this._isPointNAspect = VALIDATION.isPointNAspect;
this._drawAnchor = UTILITIES.individual.draw.anchor;
this._drawAxis = UTILITIES.individual.draw.axis;
this._drawBorder = UTILITIES.individual.draw.border;
this._rotatePoint = UTILITIES.individual.misc.rotatePoint
this._setAnchorPoint = UTILITIES.individual.set.anchorPoint;
this.move = UTILITIES.individual.misc.move;
this.rotate = UTILITIES.individual.misc.rotate;
Object.defineProperty ( this, 'area', PROPERTY_BLOCKS.individual.area );
Object.defineProperty ( this, 'canvas', PROPERTY_BLOCKS.individual.canvas );
Object.defineProperty ( this, 'center', PROPERTY_BLOCKS.individual.center );
Object.defineProperty ( this, 'perimeter', PROPERTY_BLOCKS.individual.perimeter );
delete this.#options._controlPoints;
delete this.#options._points;
delete this.#options._master;
this.source = source;
this.primary = primary;
this.secondary = secondary;
this.canvas = canvas;
//// ANCILLARY ////////////////////////////////
this.#position.master = this;
}
//// PROPERTIES //////////////////////////////////
//// [ SOURCE ] ////////////////////////
/**
* Sets source property value
* @public
* @function
* @param {string} value Path of image source
*/
set source ( value )
{
if ( typeof value === 'string' )
{
let _image = new Image;
_image.src = value;
this._source = _image;
this.type = 'source';
}
}
/**
* Gets source property value
* @readOnly
* @function
* @return {Image} Image source
*/
get source ( )
{
return this._source;
}
//// [ POINT ] //////////////////////////
/**
* Set point
* @public
* @function
* @param {Point} value X & Y coordinates
*/
set point ( value )
{
this._primary.point = ( this._isPoint ( value ) ) ? value : this._primary.point;
}
/**
* Get point
* @public
* @function
* @return {Point} X & Y coordinates
*/
get point ( )
{
return this._primary.point;
}
/**
* Set primary x-axis value
* @public
* @function
* @param {number} value X coordinate value
*/
set x ( value )
{
this._primary.point.x = value;
}
/**
* Get primary x-axis value
* @readOnly
* @function
* @return {number} X coordinate value
*/
get x ( )
{
return this._primary.point.x;
}
/**
* Set primary y-axis value
* @public
* @function
* @param {number} value Y coordinate value
*/
set y ( value )
{
this._primary.point.y = value;
}
/**
* Get primary y-axis value
* @readOnly
* @function
* @return {number} Y coordinate value
*/
get y ( )
{
return this._primary.point.y;
}
//// [ ASPECT ] //////////////////////////
/**
* Set aspect properties
* @public
* @function
* @param {Aspect} value Aspect properties
*/
set aspect ( value )
{
this._primary.aspect = ( this._isAspect ( value ) ) ? value : this._primary.aspect;
}
/**
* Get aspect properties
* @readOnly
* @function
* @return {Aspect} Aspect properties
*/
get aspect ( )
{
return this._primary.aspect;
}
/**
* Get aspect with
* @readOnly
* @function
* @return {number} Width value
*/
get width ( )
{
return this.source.width;
}
/**
* Get aspect height
* @readOnly
* @function
* @return {number} Height value
*/
get height ( )
{
return this.source.height;
}
//// [ PRIMARY ] ///////////////////////
/**
* Sets primary property value
* @public
* @function
* @param {string} value Path of image
*/
set primary ( value )
{
if ( this._isPointNAspect ( value ) )
[ this._primary.point, this._primary.aspect ] = [ value.point, value.aspect ];
}
/**
* Gets primary property value
* @readOnly
* @function
* @return {Image} Image object
*/
get primary ( )
{
return this._primary;
}
//// [ SECONDARY ] /////////////////////
/**
* Sets secondary property value
* @public
* @function
* @param {string} value Path of image
*/
set secondary ( value )
{
if ( this._isPointNAspect ( value ) )
[ this._secondary.point, this._secondary.aspect ] = [ value.point, value.aspect ];
}
/**
* Gets secondary property value
* @readOnly
* @function
* @return {Image} Image object
*/
get secondary ( )
{
return this._secondary;
}
//// [ CANVAS ] //////////////////////////
/**
* Set canvas value
* @public
* @function
* @param {string} value Canvas id
* @see {@link individual.canvas}
*/
set canvas ( value ) { }
/**
* Get canvas value
* @readOnly
* @function
* @return {string} Canvas id
* @see {@link individual.canvas}
*/
get canvas ( ) { }
//// [ ANCHOR ] //////////////////////////
/**
* Get anchor
* @public
* @function
* @return {Anchor} Anchor properties
*/
get anchor ( )
{
return this._anchor;
}
//// [ OPTIONS ] //////////////////////////
/**
* Get options properties
* @public
* @function
* @return {Options} Options properties
*/
get options ( )
{
return this.#options;
}
//// [ POSITION ] //////////////////////
/**
* Get position properties
* @public
* @function
* @return {Position} Position properties
*/
get position ( )
{
return this.#position;
}
//// VALIDATION //////////////////////////////////
/**
* Returns whether the passed value is an Aspect
* @private
* @function
* @param {Object} value Aspect or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isAspect}
*/
_isAspect ( ) { }
/**
* Returns whether the passed value is an element id within the DOM
* @private
* @function
* @param {string} value Element id
* @return {boolean} True || False
* @see {@link VALIDATION.isInDom}
*/
_isInDom ( ) { }
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
/**
* Returns whether the passed value is a Point & Aspect
* @private
* @memberof VALIDATION
* @function
* @param {Object} value Object of a Point & Aspect
* @param {Point} value.point Point object
* @param {Aspect} value.aspect Aspect object
* @return {boolean} True || False
* @see {@link VALIDATION.isPointNAspect}
*/
_isPointNAspect ( ) { }
//// UTILITIES ///////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Draws anchor point
* @private
* @function
*/
_drawAnchor ( ) { }
/**
* Draws an axis for the associated object
* @private
* @function
* @param {number} offset Offset of axis
* @param {Object} color Color model
* @param {number} stop Gradient color stop
* @see {@link UTILITIES.individual.draw.axis}
*/
_drawAxis ( ) { }
/**
* Draws an axis for the associated object
* @private
* @function
* @param {Aspect} aspect Aspect properties
* @param {Object} color Color model
* @see {@link UTILITIES.individual.draw.border}
*/
_drawBorder ( ) { }
/**
* Draws associated options
* @private
* @function
*/
_drawOptions ( )
{
let _offset = 20;
let _aspect = new Aspect ( this.source.width + _offset, this.source.height + _offset );
////////////////////////////////////////////////////////////////////
if ( this.#options.border ) this._drawBorder ( _aspect );
if ( this.#options.axis ) this._drawAxis ( );
if ( this.#options.anchor ) this._drawAnchor ( );
if ( this.#options.coordinates ) this.showCoordinates ( );
}
/**
* Rotates the origin point by the degree & distance passed
* @private
* @function
* @param {Point} origin Origin point
* @param {number} degree Degree to rotate
* @param {number} distance Distance from origin
* @see {@link UTILITIES.individual.misc.rotatePoint}
*/
_rotatePoint ( ) { }
/**
* Sets anchor's point against this object's point position
* @private
* @function
* @see {@link UTILITIES.individual.set.anchorPoint}
*/
_setAnchorPoint ( ) { }
//// + PUBLIC //////////////////////
/**
* Get area of this object
* @readOnly
* @function
* @return {number} Area of this object
* @see {@link PROPERTY_BLOCKS.individual.area}
*/
get area ( ) { }
/**
* Get center of this object
* @readOnly
* @function
* @return {Point} Center point coordinates
* @see {@link PROPERTY_BLOCKS.individual.center}
*/
get center ( ) { }
/**
* Get perimeter of this object
* @readOnly
* @function
* @return {number} Perimeter of rectangle
* @see {@link PROPERTY_BLOCKS.individual.center}
*/
get perimeter ( ) { }
/**
* Move this object
* @public
* @function
* @param {number} degree Direction to move; in degrees
* @param {number} distance Distance to move
* @param {boolean} [draw=false] Draw post movement
* @param {boolean} [clear=false] Clear canvas during each movement
* @see {@link UTILITIES.individual.misc.move}
*/
move ( ) { }
/**
* Rotate this object
* @public
* @function
* @param {number} degree Distance to rotate; in degrees
* @param {string} [anchor='center'] Anchoring point during rotation
* @param {number} [clear=true] Clear canvas during each rotation
* @see {@link UTILITIES.individual.misc.rotate}
*/
rotate ( ) { }
/**
* Shows coordinates of this object
* @public
* @function
* @param {number} [offset=10] Offset of coordinates y origin
* @param {number} [fontSize=16] Coordinates font size
* @see {@link UTILITIES.individual.misc.showCoordinates}
*/
showCoordinates ( ) { }
//// DRAW ////////////////////////////////////////
/**
* Draws image depending on primary & secondary property settings
* @private
* @function
*/
_drawImage ( )
{
if ( this.secondary.point )
this._canvas.drawImage ( this._source, this.secondary.point.x, this.secondary.point.y, this.secondary.aspect.width, this.secondary.aspect.height, this.anchor.x, this.anchor.y, this.primary.aspect.width, this.primary.aspect.height );
else
this._canvas.drawImage ( this._source, this.anchor.x, this.anchor.y, this._source.width, this._source.height );
}
/**
* Draw this object
* @public
* @function
* @param {string} canvas Canvas Id
*/
draw ( canvas )
{
if ( canvas != undefined ) this.canvas = canvas;
if ( this.source.onload === null && this._canvas instanceof CanvasRenderingContext2D )
this.source.onload = ( ) =>
{
this._setAnchorPoint ( );
this._drawImage ( );
this._drawOptions ( );
}
else
{
this._setAnchorPoint ( );
this._drawImage ( );
this._drawOptions ( );
}
}
}
/**
* @class {Array} Circles Collection of circle elements within an array
* @property {Point} point X & Y axis coordinates
* @property {StrokeCollection} stroke Stroke collection properties
* @property {ShadowCollection} shadow Shadow collection properties
* @property {Aspect} aspect Aspect properties
* @property {HTMLCanvasElement} canvas 2D canvas context
* @property {Template} template Canvas Lab Template object
* @property {Anchor} anchor Anchor properties
* @property {Options} options Options for this object
*/
class Circles extends Array
{
_point = new Point;
_stroke = new StrokeCollection;
_shadow = new ShadowCollection;
_aspect = new Aspect;
_canvas = undefined;
_template = undefined;
_anchor = new Anchor;
#options = new Options;
#storage = { type: Circle }
/**
* Create Circles object
* @property {Point} point X & Y axis coordinates
* @property {HTMLCanvasElement} canvas Canvas Id
* @property {Template} template Canvas Lab Template object
*/
constructor ( point = { x: undefined, y: undefined }, canvas, template )
{
super ( );
//// COMPOSITION ////////////////////////////
this._isAspect = VALIDATION.isAspect;
this._isCanvasLabObject = VALIDATION.isCanvasLabObject;
this._isDegree = VALIDATION.isDegree;
this._isInDom = VALIDATION.isInDom;
this._isPoint = VALIDATION.isPoint;
this._isTemplate = VALIDATION.isTemplate;
this._clearCanvas = UTILITIES.individual.misc.clearCanvas;
this._drawAnchor = UTILITIES.collection.drawAnchor;
this._drawAxis = UTILITIES.individual.draw.axis;
this._drawBorder = UTILITIES.individual.draw.border;
this._drawOptionsPost = UTILITIES.collection.drawOptionsPost;
this._setAnchorPoint = UTILITIES.collection.setAnchorPoint;
this._setPointOffset = UTILITIES.collection.setPointOffset;
this.draw = UTILITIES.collection.draw;
this.getPoints = UTILITIES.collection.getPoints;
this.push = UTILITIES.collection.push;
Object.defineProperty ( this, 'anchor', PROPERTY_BLOCKS.collection.anchor );
Object.defineProperty ( this, 'area', PROPERTY_BLOCKS.collection.area );
Object.defineProperty ( this, 'aspect', PROPERTY_BLOCKS.collection.aspect );
Object.defineProperty ( this, 'width', PROPERTY_BLOCKS.collection.aspectWidth );
Object.defineProperty ( this, 'height', PROPERTY_BLOCKS.collection.aspectHeight );
Object.defineProperty ( this, 'canvas', PROPERTY_BLOCKS.collection.canvas );
Object.defineProperty ( this, 'center', PROPERTY_BLOCKS.collection.center );
Object.defineProperty ( this, 'endPoint', PROPERTY_BLOCKS.collection.endPoint );
Object.defineProperty ( this, 'perimeter', PROPERTY_BLOCKS.collection.perimeter );
Object.defineProperty ( this, 'template', PROPERTY_BLOCKS.collection.template );
Object.defineProperty ( this, 'point', PROPERTY_BLOCKS.individual.point );
Object.defineProperty ( this, 'x', PROPERTY_BLOCKS.individual.pointX );
Object.defineProperty ( this, 'y', PROPERTY_BLOCKS.individual.pointY );
this.point = point;
this.canvas = canvas;
this.template = template;
}
//// PROPERTIES //////////////////////////////////
//// [ POINT ] //////////////////////
/**
* Set point
* @public
* @function
* @param {Point} value X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
set point ( value ) { }
/**
* Get point
* @public
* @function
* @return {Point} X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
get point ( ) { }
/**
* Set x-axis value
* @public
* @function
* @param {number} value X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
set x ( value ) { }
/**
* Get x-axis value
* @readOnly
* @function
* @return {number} X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
get x ( ) { }
/**
* Set the y-axis value
* @public
* @function
* @param {number} value Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
set y ( value ) { }
/**
* Get y-axis value
* @readOnly
* @function
* @return {number} Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
get y ( ) { }
//// [ STROKE ] //////////////////////
/**
* Get stroke properties
* @public
* @function
* @return {Stroke} Stroke properties
*/
get stroke ( )
{
return this._stroke;
}
//// [ SHADOW ] //////////////////////
/**
* Get shadow properties
* @public
* @function
* @return {Shadow} Shadow properties
*/
get shadow ( )
{
return this._shadow;
}
//// [ ASPECT ] //////////////////////
/**
* Get aspect properties
* @public
* @function
* @return {Aspect} Aspect properties
* @see {@link PROPERTY_BLOCKS.collection.aspect}
*/
get aspect ( ) { }
/**
* Get aspect with
* @readOnly
* @function
* @return {number} Width value
* @see {@link PROPERTY_BLOCKS.collection.aspectWidth}
*/
get width ( ) { }
/**
* Get aspect height
* @readOnly
* @function
* @return {number} Height value
* @see {@link PROPERTY_BLOCKS.collection.aspectHeight}
*/
get height ( ) { }
//// [ CANVAS ] //////////////////////
/**
* Set canvas value
* @public
* @function
* @param {string} value Canvas id
* @see {@link PROPERTY_BLOCKS.collection.canvas}
*/
set canvas ( value ) { }
/**
* Get canvas value
* @readOnly
* @function
* @return {string} Canvas id
* @see {@link PROPERTY_BLOCKS.collection.canvas}
*/
get canvas ( ) { }
//// [ TEMPLATE ] ////////////////////
/**
* Set template
* @public
* @function
* @param {Template} value Template object
* @see {@link PROPERTY_BLOCKS.collection.template}
*/
set template ( value ) { }
/**
* Get template
* @readOnly
* @function
* @return {Template} Template object
* @see {@link PROPERTY_BLOCKS.collection.template}
*/
get template ( ) { }
//// [ ANCHOR ] //////////////////////
/**
* Set anchor type
* @public
* @function
* @param {string} value Anchor type
* @see {@link PROPERTY_BLOCKS.collection.anchor}
*/
set anchor ( value ) { }
/**
* Get anchor
* @public
* @function
* @return {Anchor} Anchor properties
* @see {@link PROPERTY_BLOCKS.collection.anchor}
*/
get anchor ( ) { }
//// [ OPTIONS ] //////////////////////
/**
* Get options
* @public
* @function
* @return {Object} Options object
*/
get options ( )
{
return this.#options;
}
//// [ STORAGE TYPE ] ////////////////
/**
* Returns this collection's storage type
* @public
* @function
* @return {clObject} Canvas Lab object
*/
get storageType ( )
{
return this.#storage.type;
}
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is an Aspect
* @private
* @function
* @param {Object} value Aspect or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isAspect}
*/
_isAspect ( ) { }
/**
* Returns whether the passed value is a CanvasLab object; Line, Circle, Rectangle, Text
* @private
* @function
* @param {Object} value CanvasLab object; Line, Circle, Rectangle, Text
* @return {boolean} True || False
* @see {@link VALIDATION.isCanvasLabObject}
*/
_isCanvasLabObject ( ) { }
/**
* Returns whether the passed value is a degree
* @private
* @function
* @param {number} value Degree
* @return {boolean} True || False
* @see {@link VALIDATION.isDegree}
*/
_isDegree ( ) { }
/**
* Returns whether the passed value is an element id within the DOM
* @private
* @function
* @param {string} value Element id
* @return {boolean} True || False
* @see {@link VALIDATION.isInDom}
*/
_isInDom ( ) { }
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
/**
* Returns whether the passed value is a Template
* @private
* @memberof VALIDATION
* @function
* @param {Object} value Template object
* @return {boolean} True || False
* @see {@link VALIDATION.isTemplate}
*/
_isTemplate ( ) { }
//// UTILITIES ////////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Draws anchor point
* @private
* @function
* @see {@link UTILITIES.collection.drawAnchor}
*/
_drawAnchor ( ) { }
/**
* Draws an axis for the associated object
* @private
* @function
* @param {number} offset Offset of axis
* @param {Object} color Color model
* @param {number} stop Gradient color stop
* @see {@link UTILITIES.individual.draw.axis}
*/
_drawAxis ( ) { }
/**
* Draws an axis for the associated object
* @private
* @function
* @param {Aspect} aspect Aspect properties
* @param {Object} color Color model
* @see {@link UTILITIES.individual.draw.border}
*/
_drawBorder ( ) { }
/**
* Draws associated options
* @private
* @function
* @see {@link UTILITIES.collection.drawOptionsPost}
*/
_drawOptionsPost ( ) { }
/**
* Triggers associated pre-draw options
* @private
* @function
* @param {Object} object CanvasLab Object
* @param {Options} options Options for collections
*/
_drawOptionsPre ( object, options )
{
let _types = [ 'shadow', 'points', 'controlPoints', 'coordinates' ];
for ( let _type of _types )
if ( options [ _type ] )
object.options [ _type ] = true;
}
/**
* Sets anchor's point against this object's point location
* @private
* @function
* @see {@link UTILITIES.collection.setAnchorPoint}
*/
_setAnchorPoint ( ) { }
/**
* Sets aspect
* @private
* @function
*/
_setAspect ( )
{
let [ _width, _height ] = [ this._canvas.canvas.clientWidth * 2, this._canvas.canvas.clientHeight * 2 ]
let [ _left, _top ] = [ _width, _height ];
let [ _right, _bottom ] = [ - _width, - _height ];
if ( this.length > 0 )
if ( this.constructor.name === 'Ellipses' )
for ( let _object of this )
{
_left = ( _object.x - _object.radius.x < _left ) ? _object.x - _object.radius.x : _left;
_right = ( _object.x + _object.radius.x > _right ) ? _object.x + _object.radius.x : _right;
_top = ( _object.y - _object.radius.y < _top ) ? _object.y - _object.radius.y : _top;
_bottom = ( _object.y + _object.radius.y > _bottom ) ? _object.y + _object.radius.y : _bottom;
}
else
for ( let _object of this )
{
_left = ( _object.x - _object.radius < _left ) ? _object.x - _object.radius : _left;
_right = ( _object.x + _object.radius > _right ) ? _object.x + _object.radius : _right;
_top = ( _object.y - _object.radius < _top ) ? _object.y - _object.radius : _top;
_bottom = ( _object.y + _object.radius > _bottom ) ? _object.y + _object.radius : _bottom;
}
else
console.warn ( `No ${this.constructor.name} exist to draw !` );
[ this._aspect.width, this._aspect.height ] = [ _right - _left, _bottom - _top ];
[ this._aspect.offset.x, this._aspect.offset.y ] = [ _left, _top ];
}
/**
* Sets offset of child object against this constructor's point
* @private
* @function
* @param {Object} Object CanvasLab Object
* @see {@link UTILITIES.collection.setAnchorPoint}
*/
_setPointOffset ( ) { }
//// + PUBLIC //////////////////////
/**
* Get area of this object
* @readOnly
* @function
* @return {number} Area of rectangle
* @see {@link PROPERTY_BLOCKS.collection.area}
*/
get area ( ) { }
/**
* Get center of this object
* @readOnly
* @function
* @return {Point} Center point coordinates
* @see {@link PROPERTY_BLOCKS.collection.center}
*/
get center ( ) { }
/**
* Returns the last Point within this Array
* @public
* @function
* @return {Point} Last Array element's X & Y Coordinates
* @see {@link PROPERTY_BLOCKS.collection.endPoint}
*/
get endPoint ( ) { }
/**
* Get perimeter of this object
* @readOnly
* @function
* @return {number} Perimeter of rectangle
* @see {@link PROPERTY_BLOCKS.collection.perimeter}
*/
get perimeter ( ) { }
/**
* Get all or specific points throughout this collection
* @public
* @function
* @param {Array.<number>} indexes Indexes of points
* @param {Rgb} color Color to colorize objects selected points
* @see {@link UTILITIES.collection.getPoints}
*/
getPoints ( ) { }
/**
* Pushes child object(s) into this collection
* @public
* @function
* @see {@link UTILITIES.collection.push}
*/
push ( ) { }
//// DRAW ////////////////////////////////////////
/**
* Draw function for collections
* @public
* @function
* @param {string} canvas Canvas Id
* @see {@link UTILITIES.collection.draw}
*/
draw ( ) { }
}
/**
* @class {Array} Ellipses Collection of circle elements within an array
* @property {Point} point X & Y axis coordinates
* @property {StrokeCollection} stroke Stroke collection properties
* @property {ShadowCollection} shadow Shadow collection properties
* @property {Aspect} aspect Aspect properties
* @property {HTMLCanvasElement} canvas 2D canvas context
* @property {Template} template Canvas Lab Template object
* @property {Anchor} anchor Anchor properties
* @property {Options} options Options for this object
* @extends {Circles}
*/
class Ellipses extends Circles
{
#options = new Options;
//// [ OPTIONS ] ////////////////////////////////////
/**
* Get options
* @public
* @function
* @return {Object} Options object
*/
get options ( )
{
return this.#options;
}
}
/**
* @class {Array} Group Collection of Line, Circle, Rectangle & Text objects
* @property {Point} point X & Y axis coordinates
* @property {Array} lines Collection of Line objects
* @property {Array} circles Collection of Circle objects
* @property {Array} ellipses Collection of Ellipse objects
* @property {Array} rectangles Collection of Rectangle objects
* @property {Array} roundedRectangles Collection of Rounded Rectangle objects
* @property {Array} text Collection of Text objects
* @property {HTMLCanvasElement} canvas 2D canvas context
* @property {Template} template Canvas Lab Template object
*/
class Group extends Array
{
_point = new Point;
_lines = new Lines;
_circles = new Circles;
_ellipses = new Ellipses;
_rectangles = new Rectangles;
_roundedRectangles = new RoundedRectangles;
_texts = new Texts;
_canvas = undefined;
_template = undefined;
#storage = { types: [ 'lines', 'circles', 'ellipses', 'rectangles', 'roundedRectangles', 'texts' ] }
/**
* Create Group object
* @property {Point} point X & Y axis coordinates
* @property {HTMLCanvasElement} canvas Canvas Id
* @property {Template} template Canvas Lab Template object
*/
constructor ( point = { x: undefined, y: undefined }, canvas, template )
{
super ( );
//// COMPOSITION ////////////////////////////
this._isCanvasLabObject = VALIDATION.isCanvasLabObject;
this._isInDom = VALIDATION.isInDom;
this._isTemplate = VALIDATION.isTemplate;
this._isPoint = VALIDATION.isPoint;
Object.defineProperty ( this, 'template', PROPERTY_BLOCKS.collection.template );
Object.defineProperty ( this, 'point', PROPERTY_BLOCKS.individual.point );
Object.defineProperty ( this, 'x', PROPERTY_BLOCKS.individual.pointX );
Object.defineProperty ( this, 'y', PROPERTY_BLOCKS.individual.pointY );
this.point = point;
this.canvas = canvas;
this.template = template;
}
//// PROPERTIES //////////////////////////////////
//// [ POINT ] //////////////////////
/**
* Set point
* @public
* @function
* @param {Point} value X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
set point ( value ) { }
/**
* Get point
* @public
* @function
* @return {Point} X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
get point ( ) { }
/**
* Set x-axis value
* @public
* @function
* @param {number} value X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
set x ( value ) { }
/**
* Get x-axis value
* @readOnly
* @function
* @return {number} X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
get x ( ) { }
/**
* Set the y-axis value
* @public
* @function
* @param {number} value Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
set y ( value ) { }
/**
* Get y-axis value
* @readOnly
* @function
* @return {number} Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
get y ( ) { }
//// [ LINES ] /////////////////////
/**
* Get's lines
* @readOnly
* @function
* @return {Lines} Lines collection
*/
get lines ( )
{
return this._lines;
}
//// [ CIRCLES ] ///////////////////
/**
* Get's circles
* @readOnly
* @function
* @return {Circles} Circles collection
*/
get circles ( )
{
return this._circles;
}
//// [ ELLIPSES ] //////////////////
/**
* Get's ellipses
* @readOnly
* @function
* @return {Ellipses} Ellipses collection
*/
get ellipses ( )
{
return this._ellipses;
}
//// [ RECTANGLES ] ////////////////
/**
* Get's rectangles
* @readOnly
* @function
* @return {Rectangles} Rectangles collection
*/
get rectangles ( )
{
return this._rectangles;
}
//// [ ROUNDED RECTANGLES ] ////////
/**
* Get's rounded rectangles
* @readOnly
* @function
* @return {RoundedRectangles} Rectangles collection
*/
get roundedRectangles ( )
{
return this._roundedRectangles;
}
//// [ TEXTS ] /////////////////////
/**
* Get's texts
* @readOnly
* @function
* @return {Texts} Texts collection
*/
get texts ( )
{
return this._texts;
}
//// [ CANVAS ] //////////////////////
/**
* Set canvas value
* @public
* @function
* @param {string} value Canvas id
*/
set canvas ( value )
{
this._canvas = ( value ) ? ( this._isInDom ( value ) )
? document.getElementById ( value ).getContext ( '2d' )
: ( this._isCanvasLabObject ( value ) )
? null
: console.warn ( `"${value}" is not a valid DOM element !` )
: ( document.getElementById ( window.canvaslab.canvas ).getContext ( '2d' ) )
? document.getElementById ( window.canvaslab.canvas ).getContext ( '2d' )
: this._canvas;
for ( let _type of this.#storage.types )
if ( ( this [ _type ].length > 0 ) && ( this._canvas instanceof CanvasRenderingContext2D ) )
for ( let _object of this [ _type ] )
_object.canvas = value;
}
/**
* Get canvas value
* @readOnly
* @function
* @return {string} Canvas id
*/
get canvas ( )
{
return ( this._canvas != undefined ) ? this._canvas.canvas.id : undefined;
}
//// [ TEMPLATE ] ////////////////////
/**
* Set template
* @public
* @function
* @param {Template} value Template object
* @see {@link PROPERTY_BLOCKS.collection.template}
*/
set template ( value ) { }
/**
* Get template
* @readOnly
* @function
* @return {Template} Template object
* @see {@link PROPERTY_BLOCKS.collection.template}
*/
get template ( ) { }
//// VALIDATION //////////////////////////////////
/**
* Returns whether the passed value is a CanvasLab object; Line, Circle, Rectangle, Text
* @public
* @memberof VALIDATION
* @function
* @param {Object} value CanvasLab object; Line, Circle, Rectangle, Text
* @return {boolean} True || False
* @see {@link VALIDATION.isCanvasLabObject}
*/
_isCanvasLabObject ( ) { }
/**
* Returns whether the passed value is an element id within the DOM
* @private
* @function
* @param {string} value Element id
* @return {boolean} True || False
* @see {@link VALIDATION.isInDom}
*/
_isInDom ( ) { }
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
/**
* Returns whether the passed value is a Template
* @private
* @memberof VALIDATION
* @function
* @param {Object} value Template object
* @return {boolean} True || False
* @see {@link VALIDATION.isTemplate}
*/
_isTemplate ( ) { }
//// UTILITIES ///////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Sets all canvases throughout each internal collection of objects
* @private
* @function
*/
_setAllCanvases ( )
{
if ( this._canvas )
for ( let _type of this.#storage.types )
if ( this [ _type ].length )
this [ _type ].canvas = this.canvas;
}
/**
* Sets offset of child Rectangle against this constructor's point
* @private
* @function
* @param {Rectangle} Object Rectangle object
*/
_setPointOffset ( Object )
{
Object.x += this.x;
Object.y += this.y;
}
//// + PUBLIC //////////////////////
/**
* Pushes an object into this group
* @public
* @function
* @param {Object} object Object; Line(s), Circle(s), Rectangle(S)
*/
push ( object )
{
for ( let _value of arguments )
switch ( _value.constructor.name )
{
//// OBJECTS ////////////////////
case 'Line': this._lines.push ( _value ); break;
case 'Circle': this._circles.push ( _value ); break;
case 'Ellipse': this._ellipse.push ( _value ); break;
case 'Rectangle': this._rectangles.push ( _value ); break;
case 'RoundedRectangle': this._roundedRectangles.push ( _value ); break;
//// COLLECTIONS ////////////////
case 'Lines': for ( let _line of _value ) this._lines.push ( _line ); break;
case 'Circles': for ( let _circle of _value ) this._circles.push ( _circle ); break;
case 'Ellipses': for ( let _ellipse of _value ) this._ellipses.push ( _ellipse ); break;
case 'Rectangles': for ( let _rectangle of _value ) this._rectangles.push ( _rectangle ); break;
case 'RoundedRectangles': for ( let _roundedRectangle of _value ) this._roundedRectangles.push ( _roundedRectangle ); break;
}
}
/**
* Pops an object out of this group
* @public
* @function
* @param {Object} object Object; Line(s), Circle(s), Rectangle(S)
*/
pop ( object )
{
for ( let _value of arguments )
switch ( _value.constructor.name )
{
//// OBJECTS ////////////////////
case 'Line': this._lines.pop ( _value ); break;
case 'Circle': this._circles.pop ( _value ); break;
case 'Ellipse': this._ellipse.pop ( _value ); break;
case 'Rectangle': this._rectangles.pop ( _value ); break;
case 'RoundedRectangle': this._roundedRectangles.pop ( _value ); break;
//// COLLECTIONS ////////////////
case 'Lines': for ( let _line of _value ) this._lines.pop ( _line ); break;
case 'Circles': for ( let _circle of _value ) this._circles.pop ( _circle ); break;
case 'Ellipses': for ( let _ellipse of _value ) this._ellipses.pop ( _ellipse ); break;
case 'Rectangles': for ( let _rectangle of _value ) this._rectangles.pop ( _rectangle ); break;
case 'RoundedRectangles': for ( let _roundedRectangle of _value ) this._roundedRectangles.pop ( _roundedRectangle ); break;
default:
console.warn ( `"${_value}" is not a supported type to be popped into ${this.constructor.name}` );
break;
}
}
//// DRAW ////////////////////////////////////////
/**
* Draw this group
* @public
* @function
* @param {string} canvas Canvas Id
*/
draw ( canvas )
{
if ( canvas != undefined ) this.canvas = canvas;
for ( let _type of this.#storage.types )
if ( this [ _type ].length > 0 )
{
this._setPointOffset ( this [ _type ] );
this [ _type ].draw ( );
}
else
console.warn ( `No ${_type} exist to draw !` );
}
}
/**
* @class {Array} Lines Collection of Line objects
* @property {Point} point X & Y axis coordinates
* @property {StrokeCollection} stroke Stroke collection properties
* @property {ShadowCollection} shadow Shadow collection properties
* @property {string} lineCap Shape of end points
* @property {Aspect} aspect Aspect properties
* @property {Anchor} anchor Anchor properties
* @property {HTMLCanvasElement} canvas 2D canvas context
* @property {Template} template Canvas Lab Template object
*/
class Lines extends Array
{
_point = new Point;
_stroke = new StrokeCollection;
_shadow = new ShadowCollection;
_lineCap = 'round';
_aspect = new Aspect;
_anchor = new Anchor;
_canvas = undefined;
_template = undefined;
#options = new Options;
#storage = { type: Line }
/**
* Create a Lines object
* @property {Point} point X & Y axis coordinates
* @property {HTMLCanvasElement} canvas Canvas Id
* @property {Template} template Canvas Lab Template object
*/
constructor ( point = { x: undefined, y: undefined }, canvas, template )
{
super ( );
//// COMPOSITION ////////////////////////////
this._isAspect = VALIDATION.isAspect;
this._isCanvasLabObject = VALIDATION.isCanvasLabObject;
this._isInDom = VALIDATION.isInDom;
this._isPoint = VALIDATION.isPoint;
this._isTemplate = VALIDATION.isTemplate;
this._clearCanvas = UTILITIES.individual.misc.clearCanvas;
this._drawAnchor = UTILITIES.collection.drawAnchor;
this._drawAxis = UTILITIES.individual.draw.axis;
this._drawBorder = UTILITIES.individual.draw.border;
this._drawOptionsPost = UTILITIES.collection.drawOptionsPost;
this._setAnchorPoint = UTILITIES.collection.setAnchorPoint;
this.draw = UTILITIES.collection.draw;
this.getPoints = UTILITIES.collection.getPoints;
this.push = UTILITIES.collection.push;
Object.defineProperty ( this, 'anchor', PROPERTY_BLOCKS.collection.anchor );
Object.defineProperty ( this, 'area', PROPERTY_BLOCKS.collection.area );
Object.defineProperty ( this, 'aspect', PROPERTY_BLOCKS.collection.aspect );
Object.defineProperty ( this, 'width', PROPERTY_BLOCKS.collection.aspectWidth );
Object.defineProperty ( this, 'height', PROPERTY_BLOCKS.collection.aspectHeight );
Object.defineProperty ( this, 'canvas', PROPERTY_BLOCKS.collection.canvas );
Object.defineProperty ( this, 'endPoint', PROPERTY_BLOCKS.collection.endPoint );
Object.defineProperty ( this, 'perimeter', PROPERTY_BLOCKS.collection.perimeter );
Object.defineProperty ( this, 'template', PROPERTY_BLOCKS.collection.template );
Object.defineProperty ( this, 'point', PROPERTY_BLOCKS.individual.point );
Object.defineProperty ( this, 'x', PROPERTY_BLOCKS.individual.pointX );
Object.defineProperty ( this, 'y', PROPERTY_BLOCKS.individual.pointY );
this.point = point;
this.canvas = canvas;
this.template = template;
this.stroke.master = this;
//// POPULATE COLLECTION ////////////////////
if ( arguments.length > 0 )
this.push.apply ( this, arguments );
}
//// PROPERTIES //////////////////////////////////
//// [ POINT ] /////////////////////
/**
* Set point
* @public
* @function
* @param {Point} value X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
set point ( value ) { }
/**
* Get point
* @public
* @function
* @return {Point} X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
get point ( ) { }
/**
* Set x-axis value
* @public
* @function
* @param {number} value X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
set x ( value ) { }
/**
* Get x-axis value
* @readOnly
* @function
* @return {number} X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
get x ( ) { }
/**
* Set the y-axis value
* @public
* @function
* @param {number} value Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
set y ( value ) { }
/**
* Get y-axis value
* @readOnly
* @function
* @return {number} Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
get y ( ) { }
//// [ STROKE ] ////////////////////
/**
* Get stroke properties
* @public
* @function
* @return {Stroke} Stroke properties
*/
get stroke ( )
{
return this._stroke;
}
//// [ SHADOW ] ////////////////////
/**
* Get shadow properties
* @public
* @function
* @return {Shadow} Shadow properties
*/
get shadow ( )
{
return this._shadow;
}
//// [ LINECAP ] ///////////////////
/**
* Set line cap
* @public
* @function
* @param {string} value Line cap
*/
set lineCap ( value )
{
this._lineCap = ( value === 'butt' || value === 'round' || value === 'square' ) ? value : this._lineCap;
}
/**
* Get line cap
* @readOnly
* @function
* @return {string} Line cap
*/
get lineCap ( )
{
return this._lineCap;
}
//// [ ASPECT ] ////////////////////
/**
* Get aspect properties
* @public
* @function
* @return {Aspect} Aspect properties
* @see {@link PROPERTY_BLOCKS.collection.aspect}
*/
get aspect ( ) { }
/**
* Get aspect with
* @readOnly
* @function
* @return {number} Width value
* @see {@link PROPERTY_BLOCKS.collection.aspectWidth}
*/
get width ( ) { }
/**
* Get aspect height
* @readOnly
* @function
* @return {number} Height value
* @see {@link PROPERTY_BLOCKS.collection.aspectHeight}
*/
get height ( ) { }
//// [ ANCHOR ] ////////////////////
/**
* Set anchor type
* @public
* @function
* @param {string} value Anchor type
* @see {@link PROPERTY_BLOCKS.collection.anchor}
*/
set anchor ( value ) { }
/**
* Get anchor
* @public
* @function
* @return {Anchor} Anchor properties
* @see {@link PROPERTY_BLOCKS.collection.anchor}
*/
get anchor ( ) { }
//// [ CANVAS ] ////////////////////
/**
* Set canvas value
* @public
* @function
* @param {string} value Canvas id
* @see {@link PROPERTY_BLOCKS.collection.canvas}
*/
set canvas ( value ) { }
/**
* Get canvas value
* @readOnly
* @function
* @return {string} Canvas id
* @see {@link PROPERTY_BLOCKS.collection.canvas}
*/
get canvas ( ) { }
//// [ TEMPLATE ] //////////////////
/**
* Set template
* @public
* @function
* @param {Template} value Template object
* @see {@link PROPERTY_BLOCKS.collection.template}
*/
set template ( value ) { }
/**
* Get template
* @readOnly
* @function
* @return {Template} Template object
* @see {@link PROPERTY_BLOCKS.collection.template}
*/
get template ( ) { }
//// [ OPTIONS ] ///////////////////
/**
* Get options
* @public
* @function
* @return {Object} Options object
*/
get options ( )
{
return this.#options;
}
//// [ STORAGE TYPE ] //////////////
/**
* Returns this collection's storage type
* @public
* @function
* @return {clObject} Canvas Lab object
*/
get storageType ( )
{
return this.#storage.type;
}
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is an Aspect
* @private
* @function
* @param {Object} value Aspect or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isAspect}
*/
_isAspect ( ) { }
/**
* Returns whether the passed value is a CanvasLab object; Line, Circle, Rectangle, Text
* @private
* @function
* @param {Object} value CanvasLab object; Line, Circle, Rectangle, Text
* @return {boolean} True || False
* @see {@link VALIDATION.isCanvasLabObject}
*/
_isCanvasLabObject ( ) { }
/**
* Returns whether the passed value is an element id within the DOM
* @private
* @function
* @param {string} value Element id
* @return {boolean} True || False
* @see {@link VALIDATION.isInDom}
*/
_isInDom ( ) { }
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
/**
* Returns whether the passed value is a Template
* @private
* @memberof VALIDATION
* @function
* @param {Object} value Template object
* @return {boolean} True || False
* @see {@link VALIDATION.isTemplate}
*/
_isTemplate ( ) { }
//// UTILITIES ////////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Draws anchor point
* @private
* @function
* @see {@link UTILITIES.individual.collection.drawAnchor}
*/
_drawAnchor ( ) { }
/**
* Draws an axis for the associated object
* @private
* @function
* @param {number} offset Offset of axis
* @param {Object} color Color model
* @param {number} stop Gradient color stop
* @see {@link UTILITIES.individual.draw.axis}
*/
_drawAxis ( ) { }
/**
* Draws an axis for the associated object
* @private
* @function
* @param {Aspect} aspect Aspect properties
* @param {Object} color Color model
* @see {@link UTILITIES.individual.draw.border}
*/
_drawBorder ( ) { }
/**
* Draws associated options
* @private
* @function
* @see {@link UTILITIES.collection.drawOptionsPost}
*/
_drawOptionsPost ( ) { }
/**
* Triggers associated pre-draw options
* @private
* @function
* @param {Object} object CanvasLab Object
* @param {Options} options Options for collections
*/
_drawOptionsPre ( object, options )
{
let _types = [ 'shadow', 'points', 'controlPoints', 'coordinates' ];
for ( let _type of _types )
if ( options [ _type ] )
object.options [ _type ] = true;
}
/**
* Sets anchor's point against this object's point location
* @private
* @function
* @see {@link UTILITIES.collection.setAnchorPoint}
*/
_setAnchorPoint ( ) { }
/**
* Sets aspect
* @private
* @function
*/
_setAspect ( )
{
let [ _width, _height ] = [ this._canvas.canvas.clientWidth * 2, this._canvas.canvas.clientHeight * 2 ]
let [ _left, _top ] = [ _width, _height ];
let [ _right, _bottom ] = [ - _width, - _height ];
if ( this.length > 0 )
for ( let _object of this )
{
_left = ( _object.start.x < _left ) ? _object.start.x : _left;
_left = ( _object.end.x < _left ) ? _object.end.x : _left;
_right = ( _object.start.x > _right ) ? _object.start.x : _right;
_right = ( _object.end.x > _right ) ? _object.end.x : _right;
_top = ( _object.start.y < _top ) ? _object.start.y : _top;
_top = ( _object.end.y < _top ) ? _object.end.y : _top;
_bottom = ( _object.start.y > _bottom ) ? _object.start.y : _bottom;
_bottom = ( _object.end.y > _bottom ) ? _object.end.y : _bottom;
}
else
console.warn ( `No ${this.constructor.name} exist to draw !` );
[ this._aspect.width, this._aspect.height ] = [ _right - _left, _bottom - _top ];
}
/**
* Sets offset of child Line against this constructor's point
* @private
* @function
* @param {Line} Line Line object
*/
_setPointOffset ( Line )
{
Line.start.x += this.x;
Line.end.x += this.x;
Line.start.y += this.y;
Line.end.y += this.y;
}
//// + PUBLIC //////////////////////
/**
* Get area of this object
* @readOnly
* @function
* @return {number} Area of rectangle
* @see {@link PROPERTY_BLOCKS.collection.area}
*/
get area ( ) { }
/**
* Get center of this object
* @readOnly
* @function
* @return {Point} Center point coordinates
*/
get center ( )
{
const getMin = ( value, start, end ) => ( value === undefined ) ? start : ( start < end ) ? ( start < value ) ? start : value : ( end < value ) ? end : value;
let _x, _y = undefined;
for ( let _object of this )
{
_x = getMin ( _x, _object.start.x, _object.end.x );
_y = getMin ( _y, _object.start.y, _object.end.y );
}
[ _x, _y ] = [ _x + ( this.width / 2 ), _y + ( this.height / 2 ) ];
return new Point ( _x, _y );
}
/**
* Returns the last Point within this Array
* @public
* @function
* @return {Point} Last Array element's X & Y Coordinates
* @see {@link PROPERTY_BLOCKS.collection.endPoint}
*/
get endPoint ( ) { }
/**
* Get perimeter of this object
* @readOnly
* @function
* @return {number} Perimeter of rectangle
* @see {@link PROPERTY_BLOCKS.collection.perimeter}
*/
get perimeter ( ) { }
/**
* Get all or specific points throughout this collection
* @public
* @function
* @param {Array.<number>} indexes Indexes of points
* @param {Rgb} color Color to colorize objects selected points
* @see {@link UTILITIES.collection.getPoints}
*/
getPoints ( ) { }
/**
* Pushes child object(s) into this collection
* @public
* @function
* @see {@link UTILITIES.collection.push}
*/
push ( ) { }
//// DRAW ////////////////////////////////////////
/**
* Draw function for collections
* @public
* @function
* @param {string} canvas Canvas Id
* @see {@link UTILITIES.collection.draw}
*/
draw ( ) { }
}
/**
* @class {Array} Rectangles Collection of Rectangle objects
* @property {Point} point X & Y axis coordinates
* @property {StrokeCollection} stroke Stroke collection properties
* @property {ShadowCollection} shadow Shadow collection properties
* @property {Aspect} aspect Aspect properties
* @property {HTMLCanvasElement} canvas 2D canvas context
* @property {Template} template Canvas Lab Template object
* @property {Anchor} anchor Anchor properties
* @property {Options} options Options for this object
*/
class Rectangles extends Array
{
_point = new Point;
_stroke = new StrokeCollection;
_shadow = new ShadowCollection;
_aspect = new Aspect;
_canvas = undefined;
_template = undefined;
_anchor = new Anchor;
#options = new Options;
#storage = { type: Rectangle }
/**
* Create Rectangles object
* @property {Point} point X & Y axis coordinates
* @property {HTMLCanvasElement} canvas Canvas Id
* @property {Template} template Canvas Lab Template object
*/
constructor ( point = { x: undefined, y: undefined }, canvas, template )
{
super ( );
//// COMPOSITION ////////////////////////////
this._isAspect = VALIDATION.isAspect;
this._isCanvasLabObject = VALIDATION.isCanvasLabObject;
this._isInDom = VALIDATION.isInDom;
this._isPoint = VALIDATION.isPoint;
this._isTemplate = VALIDATION.isTemplate;
this._clearCanvas = UTILITIES.individual.misc.clearCanvas;
this._drawAnchor = UTILITIES.collection.drawAnchor;
this._drawAxis = UTILITIES.individual.draw.axis;
this._drawBorder = UTILITIES.individual.draw.border;
this._drawOptionsPost = UTILITIES.collection.drawOptionsPost;
this._setAnchorPoint = UTILITIES.collection.setAnchorPoint;
this._setPointOffset = UTILITIES.collection.setPointOffset;
this.draw = UTILITIES.collection.draw;
this.getPoints = UTILITIES.collection.getPoints;
this.push = UTILITIES.collection.push;
Object.defineProperty ( this, 'anchor', PROPERTY_BLOCKS.collection.anchor );
Object.defineProperty ( this, 'area', PROPERTY_BLOCKS.collection.area );
Object.defineProperty ( this, 'aspect', PROPERTY_BLOCKS.collection.aspect );
Object.defineProperty ( this, 'width', PROPERTY_BLOCKS.collection.aspectWidth );
Object.defineProperty ( this, 'height', PROPERTY_BLOCKS.collection.aspectHeight );
Object.defineProperty ( this, 'canvas', PROPERTY_BLOCKS.collection.canvas );
Object.defineProperty ( this, 'center', PROPERTY_BLOCKS.collection.center );
Object.defineProperty ( this, 'endPoint', PROPERTY_BLOCKS.collection.endPoint );
Object.defineProperty ( this, 'perimeter', PROPERTY_BLOCKS.collection.perimeter );
Object.defineProperty ( this, 'template', PROPERTY_BLOCKS.collection.template );
Object.defineProperty ( this, 'point', PROPERTY_BLOCKS.individual.point );
Object.defineProperty ( this, 'x', PROPERTY_BLOCKS.individual.pointX );
Object.defineProperty ( this, 'y', PROPERTY_BLOCKS.individual.pointY );
this.point = point;
this.canvas = canvas;
this.template = template;
}
//// PROPERTIES //////////////////////////////////
//// [ POINT ] //////////////////////
/**
* Set point
* @public
* @function
* @param {Point} value X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
set point ( value ) { }
/**
* Get point
* @public
* @function
* @return {Point} X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
get point ( ) { }
/**
* Set x-axis value
* @public
* @function
* @param {number} value X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
set x ( value ) { }
/**
* Get x-axis value
* @readOnly
* @function
* @return {number} X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
get x ( ) { }
/**
* Set the y-axis value
* @public
* @function
* @param {number} value Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
set y ( value ) { }
/**
* Get y-axis value
* @readOnly
* @function
* @return {number} Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
get y ( ) { }
//// [ STROKE ] //////////////////////
/**
* Get stroke properties
* @public
* @function
* @return {Stroke} Stroke properties
*/
get stroke ( )
{
return this._stroke;
}
//// [ SHADOW ] //////////////////////
/**
* Get shadow properties
* @public
* @function
* @return {Shadow} Shadow properties
*/
get shadow ( )
{
return this._shadow;
}
//// [ ASPECT ] //////////////////////
/**
* Get aspect properties
* @public
* @function
* @return {Aspect} Aspect properties
* @see {@link PROPERTY_BLOCKS.collection.aspect}
*/
get aspect ( ) { }
/**
* Get aspect with
* @readOnly
* @function
* @return {number} Width value
* @see {@link PROPERTY_BLOCKS.collection.aspectWidth}
*/
get width ( ) { }
/**
* Get aspect height
* @readOnly
* @function
* @return {number} Height value
* @see {@link PROPERTY_BLOCKS.collection.aspectHeight}
*/
get height ( ) { }
//// [ CANVAS ] //////////////////////
/**
* Set canvas value
* @public
* @function
* @param {string} value Canvas id
* @see {@link PROPERTY_BLOCKS.collection.canvas}
*/
set canvas ( value ) { }
/**
* Get canvas value
* @readOnly
* @function
* @return {string} Canvas id
* @see {@link PROPERTY_BLOCKS.collection.canvas}
*/
get canvas ( ) { }
//// [ TEMPLATE ] ////////////////////
/**
* Set template
* @public
* @function
* @param {Template} value Template object
* @see {@link PROPERTY_BLOCKS.collection.template}
*/
set template ( value ) { }
/**
* Get template
* @readOnly
* @function
* @return {Template} Template object
* @see {@link PROPERTY_BLOCKS.collection.template}
*/
get template ( ) { }
//// [ ANCHOR ] //////////////////////
/**
* Set anchor type
* @public
* @function
* @param {string} value Anchor type
* @see {@link PROPERTY_BLOCKS.collection.anchor}
*/
set anchor ( value ) { }
/**
* Get anchor
* @public
* @function
* @return {Anchor} Anchor properties
* @see {@link PROPERTY_BLOCKS.collection.anchor}
*/
get anchor ( ) { }
//// [ OPTIONS ] //////////////////////
/**
* Get options
* @public
* @function
* @return {Object} Options object
*/
get options ( )
{
return this.#options;
}
//// [ STORAGE TYPE ] ////////////////
/**
* Returns this collection's storage type
* @public
* @function
* @return {clObject} Canvas Lab object
*/
get storageType ( )
{
return this.#storage.type;
}
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is an Aspect
* @private
* @function
* @param {Object} value Aspect or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isAspect}
*/
_isAspect ( ) { }
/**
* Returns whether the passed value is a CanvasLab object; Line, Circle, Rectangle, Text
* @private
* @function
* @param {Object} value CanvasLab object; Line, Circle, Rectangle, Text
* @return {boolean} True || False
* @see {@link VALIDATION.isCanvasLabObject}
*/
_isCanvasLabObject ( ) { }
/**
* Returns whether the passed value is an element id within the DOM
* @private
* @function
* @param {string} value Element id
* @return {boolean} True || False
* @see {@link VALIDATION.isInDom}
*/
_isInDom ( ) { }
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
/**
* Returns whether the passed value is a Template
* @private
* @memberof VALIDATION
* @function
* @param {Object} value Template object
* @return {boolean} True || False
* @see {@link VALIDATION.isTemplate}
*/
_isTemplate ( ) { }
//// UTILITIES ////////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Draws anchor point
* @private
* @function
* @see {@link UTILITIES.collection.drawAnchor}
*/
_drawAnchor ( ) { }
/**
* Draws an axis for the associated object
* @private
* @function
* @param {number} offset Offset of axis
* @param {Object} color Color model
* @param {number} stop Gradient color stop
* @see {@link UTILITIES.individual.draw.axis}
*/
_drawAxis ( ) { }
/**
* Draws an axis for the associated object
* @private
* @function
* @param {Aspect} aspect Aspect properties
* @param {Object} color Color model
* @see {@link UTILITIES.individual.draw.border}
*/
_drawBorder ( ) { }
/**
* Draws associated options
* @private
* @function
* @see {@link UTILITIES.collection.drawOptionsPost}
*/
_drawOptionsPost ( ) { }
/**
* Triggers associated pre-draw options
* @private
* @function
* @param {Object} object CanvasLab Object
* @param {Options} options Options for collections
*/
_drawOptionsPre ( object, options )
{
let _types = [ 'shadow', 'points', 'controlPoints', 'coordinates' ];
for ( let _type of _types )
if ( options [ _type ] )
object.options [ _type ] = true;
}
/**
* Sets anchor's point against this object's point location
* @private
* @function
* @see {@link UTILITIES.collection.setAnchorPoint}
*/
_setAnchorPoint ( ) { }
/**
* Sets aspect
* @private
* @function
*/
_setAspect ( )
{
let [ _width, _height ] = [ this._canvas.canvas.clientWidth * 2, this._canvas.canvas.clientHeight * 2 ]
let [ _left, _top ] = [ _width, _height ];
let [ _right, _bottom ] = [ - _width, - _height ];
if ( this.length > 0 )
for ( let _object of this )
{
_left = ( _object.x < _left ) ? _object.x : _left;
_right = ( _object.x + _object.width > _right ) ? _object.x + _object.width : _right;
_top = ( _object.y < _top ) ? _object.y : _top;
_bottom = ( _object.y + _object.height > _bottom ) ? _object.y + _object.height : _bottom;
}
else
console.warn ( `No ${this.constructor.name} exist to draw !` );
[ this._aspect.width, this._aspect.height ] = [ _right - _left, _bottom - _top ];
[ this._aspect.offset.x, this._aspect.offset.y ] = [ _left - 25, _top - 25 ];
}
/**
* Sets offset of child object against this constructor's point
* @private
* @function
* @param {Object} Object CanvasLab Object
* @see {@link UTILITIES.collection.setAnchorPoint}
*/
_setPointOffset ( ) { }
//// + PUBLIC //////////////////////
/**
* Get area of this object
* @readOnly
* @function
* @return {number} Area of rectangle
* @see {@link PROPERTY_BLOCKS.collection.area}
*/
get area ( ) { }
/**
* Get center of this object
* @readOnly
* @function
* @return {Point} Center point coordinates
* @see {@link PROPERTY_BLOCKS.collection.center}
*/
get center ( ) { }
/**
* Returns the last Point within this Array
* @public
* @function
* @return {Point} Last Array element's X & Y Coordinates
* @see {@link PROPERTY_BLOCKS.collection.endPoint}
*/
get endPoint ( ) { }
/**
* Get perimeter of this object
* @readOnly
* @function
* @return {number} Perimeter of rectangle
* @see {@link PROPERTY_BLOCKS.collection.perimeter}
*/
get perimeter ( ) { }
/**
* Get all or specific points throughout this collection
* @public
* @function
* @param {Array.<number>} indexes Indexes of points
* @param {Rgb} color Color to colorize objects selected points
* @see {@link UTILITIES.collection.getPoints}
*/
getPoints ( ) { }
/**
* Pushes child object(s) into this collection
* @public
* @function
* @see {@link UTILITIES.collection.push}
*/
push ( ) { }
//// DRAW ////////////////////////////////////////
/**
* Draw function for collections
* @public
* @function
* @param {string} canvas Canvas Id
* @see {@link UTILITIES.collection.draw}
*/
draw ( ) { }
}
/**
* @class {Array} RoundedRectangles Collection of Rounded Rectangle objects
* @property {Point} point X & Y axis coordinates
* @property {StrokeCollection} stroke Stroke collection properties
* @property {ShadowCollection} shadow Shadow collection properties
* @property {Aspect} aspect Aspect properties
* @property {HTMLCanvasElement} canvas 2D canvas context
* @property {Template} template Canvas Lab Template object
* @property {Anchor} anchor Anchor properties
* @property {Options} options Options for this object
* @extends {Rectangles}
*/
class RoundedRectangles extends Rectangles
{
#options = new Options;
//// [ OPTIONS ] ////////////////////////////////////
/**
* Get options
* @public
* @function
* @return {Object} Options object
*/
get options ( )
{
return this.#options;
}
}
/**
* @class {Array} Texts Collection of Text objects
* @property {Point} point X & Y axis coordinates
* @property {StrokeCollection} stroke Stroke collection properties
* @property {ShadowCollection} shadow Shadow collection properties
* @property {Aspect} aspect Aspect properties
* @property {HTMLCanvasElement} canvas 2D canvas context
* @property {Template} template Canvas Lab Template object
* @property {Anchor} anchor Anchor properties
* @property {Options} options Options for this object
*/
class Texts extends Array
{
_point = new Point;
_stroke = new StrokeCollection;
_shadow = new ShadowCollection;
_aspect = new Aspect;
_canvas = undefined;
_template = undefined;
_anchor = new Anchor;
#options = new Options;
#storage = { type: Text }
/**
* Create Texts object
* @property {Point} point X & Y axis coordinates
* @property {HTMLCanvasElement} canvas Canvas Id
* @property {Template} template Canvas Lab Template object
*/
constructor ( point = { x: undefined, y: undefined }, canvas, template )
{
super ( );
//// COMPOSITION ////////////////////////////
this._isAspect = VALIDATION.isAspect;
this._isCanvasLabObject = VALIDATION.isCanvasLabObject;
this._isInDom = VALIDATION.isInDom;
this._isPoint = VALIDATION.isPoint;
this._isTemplate = VALIDATION.isTemplate;
this._clearCanvas = UTILITIES.individual.misc.clearCanvas;
this._drawAnchor = UTILITIES.collection.drawAnchor;
this._drawAxis = UTILITIES.individual.draw.axis;
this._drawBorder = UTILITIES.individual.draw.border;
this._drawOptionsPost = UTILITIES.collection.drawOptionsPost;
this._setAnchorPoint = UTILITIES.collection.setAnchorPoint;
this._setPointOffset = UTILITIES.collection.setPointOffset;
this.draw = UTILITIES.collection.draw;
this.getPoints = UTILITIES.collection.getPoints;
this.push = UTILITIES.collection.push;
Object.defineProperty ( this, 'anchor', PROPERTY_BLOCKS.collection.anchor );
Object.defineProperty ( this, 'area', PROPERTY_BLOCKS.collection.area );
Object.defineProperty ( this, 'aspect', PROPERTY_BLOCKS.collection.aspect );
Object.defineProperty ( this, 'width', PROPERTY_BLOCKS.collection.aspectWidth );
Object.defineProperty ( this, 'height', PROPERTY_BLOCKS.collection.aspectHeight );
Object.defineProperty ( this, 'canvas', PROPERTY_BLOCKS.collection.canvas );
Object.defineProperty ( this, 'center', PROPERTY_BLOCKS.collection.center );
Object.defineProperty ( this, 'endPoint', PROPERTY_BLOCKS.collection.endPoint );
Object.defineProperty ( this, 'perimeter', PROPERTY_BLOCKS.collection.perimeter );
Object.defineProperty ( this, 'template', PROPERTY_BLOCKS.collection.template );
Object.defineProperty ( this, 'point', PROPERTY_BLOCKS.individual.point );
Object.defineProperty ( this, 'x', PROPERTY_BLOCKS.individual.pointX );
Object.defineProperty ( this, 'y', PROPERTY_BLOCKS.individual.pointY );
this.point = point;
this.canvas = canvas;
this.template = template;
}
//// PROPERTIES //////////////////////////////////
//// [ POINT ] //////////////////////
/**
* Set point
* @public
* @function
* @param {Point} value X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
set point ( value ) { }
/**
* Get point
* @public
* @function
* @return {Point} X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
get point ( ) { }
/**
* Set x-axis value
* @public
* @function
* @param {number} value X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
set x ( value ) { }
/**
* Get x-axis value
* @readOnly
* @function
* @return {number} X coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointX}
*/
get x ( ) { }
/**
* Set the y-axis value
* @public
* @function
* @param {number} value Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
set y ( value ) { }
/**
* Get y-axis value
* @readOnly
* @function
* @return {number} Y coordinate value
* @see {@link PROPERTY_BLOCKS.individual.pointY}
*/
get y ( ) { }
//// [ STROKE ] //////////////////////
/**
* Get stroke properties
* @public
* @function
* @return {Stroke} Stroke properties
*/
get stroke ( )
{
return this._stroke;
}
//// [ SHADOW ] //////////////////////
/**
* Get shadow properties
* @public
* @function
* @return {Shadow} Shadow properties
*/
get shadow ( )
{
return this._shadow;
}
//// [ ASPECT ] //////////////////////
/**
* Get aspect properties
* @public
* @function
* @return {Aspect} Aspect properties
* @see {@link PROPERTY_BLOCKS.collection.aspect}
*/
get aspect ( ) { }
/**
* Get aspect with
* @readOnly
* @function
* @return {number} Width value
* @see {@link PROPERTY_BLOCKS.collection.aspectWidth}
*/
get width ( ) { }
/**
* Get aspect height
* @readOnly
* @function
* @return {number} Height value
* @see {@link PROPERTY_BLOCKS.collection.aspectHeight}
*/
get height ( ) { }
//// [ CANVAS ] //////////////////////
/**
* Set canvas value
* @public
* @function
* @param {string} value Canvas id
* @see {@link PROPERTY_BLOCKS.collection.canvas}
*/
set canvas ( value ) { }
/**
* Get canvas value
* @readOnly
* @function
* @return {string} Canvas id
* @see {@link PROPERTY_BLOCKS.collection.canvas}
*/
get canvas ( ) { }
//// [ TEMPLATE ] ////////////////////
/**
* Set template
* @public
* @function
* @param {Template} value Template object
* @see {@link PROPERTY_BLOCKS.collection.template}
*/
set template ( value ) { }
/**
* Get template
* @readOnly
* @function
* @return {Template} Template object
* @see {@link PROPERTY_BLOCKS.collection.template}
*/
get template ( ) { }
//// [ ANCHOR ] //////////////////////
/**
* Set anchor type
* @public
* @function
* @param {string} value Anchor type
* @see {@link PROPERTY_BLOCKS.collection.anchor}
*/
set anchor ( value ) { }
/**
* Get anchor
* @public
* @function
* @return {Anchor} Anchor properties
* @see {@link PROPERTY_BLOCKS.collection.anchor}
*/
get anchor ( ) { }
//// [ OPTIONS ] //////////////////////
/**
* Get options
* @public
* @function
* @return {Object} Options object
*/
get options ( )
{
return this.#options;
}
//// [ STORAGE TYPE ] ////////////////
/**
* Returns this collection's storage type
* @public
* @function
* @return {clObject} Canvas Lab object
*/
get storageType ( )
{
return this.#storage.type;
}
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is an Aspect
* @private
* @function
* @param {Object} value Aspect or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isAspect}
*/
_isAspect ( ) { }
/**
* Returns whether the passed value is a CanvasLab object; Line, Circle, Rectangle, Text
* @private
* @function
* @param {Object} value CanvasLab object; Line, Circle, Rectangle, Text
* @return {boolean} True || False
* @see {@link VALIDATION.isCanvasLabObject}
*/
_isCanvasLabObject ( ) { }
/**
* Returns whether the passed value is an element id within the DOM
* @private
* @function
* @param {string} value Element id
* @return {boolean} True || False
* @see {@link VALIDATION.isInDom}
*/
_isInDom ( ) { }
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
//// UTILITIES ////////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Draws anchor point
* @private
* @function
* @see {@link UTILITIES.collection.drawAnchor}
*/
_drawAnchor ( ) { }
/**
* Draws an axis for the associated object
* @private
* @function
* @param {number} offset Offset of axis
* @param {Object} color Color model
* @param {number} stop Gradient color stop
* @see {@link UTILITIES.individual.draw.axis}
*/
_drawAxis ( ) { }
/**
* Draws an axis for the associated object
* @private
* @function
* @param {Aspect} aspect Aspect properties
* @param {Object} color Color model
* @see {@link UTILITIES.individual.draw.border}
*/
_drawBorder ( ) { }
/**
* Draws associated options
* @private
* @function
* @see {@link UTILITIES.collection.drawOptionsPost}
*/
_drawOptionsPost ( ) { }
/**
* Triggers associated pre-draw options
* @private
* @function
* @param {Object} object CanvasLab Object
* @param {Options} options Options for collections
*/
_drawOptionsPre ( object, options )
{
let _types = [ 'shadow', 'points', 'controlPoints', 'coordinates' ];
for ( let _type of _types )
if ( options [ _type ] )
object.options [ _type ] = true;
}
/**
* Sets anchor's point against this object's point location
* @private
* @function
* @see {@link UTILITIES.collection.setAnchorPoint}
*/
_setAnchorPoint ( ) { }
/**
* Sets aspect
* @private
* @function
*/
_setAspect ( )
{
let [ _width, _height ] = [ this._canvas.canvas.clientWidth * 2, this._canvas.canvas.clientHeight * 2 ]
let [ _left, _top ] = [ _width, _height ];
let [ _right, _bottom ] = [ - _width, - _height ];
let _padding = { left: 14, top: 9 }
if ( this.length > 0 )
for ( let _object of this )
{
let _width = this._canvas.measureText ( this.text ).width;
let _height = this._canvas.fontBoundingBoxAscent + this._canvas.fontBoundingBoxDescent;
_left = ( _object.x < _left ) ? _object.x : _left;
_right = ( _object.x + _width > _right ) ? _object.x + _width : _right;
_top = ( _object.y < _top ) ? _object.y : _top;
_bottom = ( _object.y + _height > _bottom ) ? _object.y + _height : _bottom;
}
else
console.warn ( `No ${this.constructor.name} exist to draw !` );
[ this._aspect.width, this._aspect.height ] = [ _right - _left, _bottom - _top ];
[ this._aspect.offset.x, this._aspect.offset.y ] = [ _left - _padding.left, _top - _padding.top ];
}
/**
* Sets offset of child object against this constructor's point
* @private
* @function
* @param {Object} Object CanvasLab Object
* @see {@link UTILITIES.collection.setAnchorPoint}
*/
_setPointOffset ( ) { }
//// + PUBLIC //////////////////////
/**
* Get area of this object
* @readOnly
* @function
* @return {number} Area of rectangle
* @see {@link PROPERTY_BLOCKS.collection.area}
*/
get area ( ) { }
/**
* Get center of this object
* @readOnly
* @function
* @return {Point} Center point coordinates
* @see {@link PROPERTY_BLOCKS.collection.center}
*/
get center ( ) { }
/**
* Returns the last Point within this Array
* @public
* @function
* @return {Point} Last Array element's X & Y Coordinates
* @see {@link PROPERTY_BLOCKS.collection.endPoint}
*/
get endPoint ( ) { }
/**
* Get perimeter of this object
* @readOnly
* @function
* @return {number} Perimeter of rectangle
* @see {@link PROPERTY_BLOCKS.collection.perimeter}
*/
get perimeter ( ) { }
/**
* Get all or specific points throughout this collection
* @public
* @function
* @param {Array.<number>} indexes Indexes of points
* @param {Rgb} color Color to colorize objects selected points
* @see {@link UTILITIES.collection.getPoints}
*/
getPoints ( ) { }
/**
* Pushes child object(s) into this collection
* @public
* @function
* @see {@link UTILITIES.collection.push}
*/
push ( ) { }
//// DRAW ////////////////////////////////////////
/**
* Draw function for collections
* @public
* @function
* @param {string} canvas Canvas Id
* @see {@link UTILITIES.collection.draw}
*/
draw ( ) { }
}
//// DATA_STRUCTURES /////////////////////////////////
/**
* @class {Object} Queue Queue object
* @property {Array} entries Array of entries
* @property {number} [index=0] Current index
* @property {Object} entry Current entry
*/
class Queue
{
_entries = new Array;
_index = 0;
_entry = undefined;
#touched = false;
/**
* Create a Queue object
* @property {Array} entries Array of entries
*/
constructor ( entries )
{
if ( Array.isArray ( entries ) )
this.entries = entries;
else
for ( let _value of arguments )
this.entry = _value;
}
//// [ ENTRIES ] /////////////////////////////////
/**
* Set entries
* @public
* @function
* @param {Array} value Array of entries
*/
set entries ( value )
{
this._entries = Array.isArray ( value ) ? value : this._entries;
}
/**
* Get entries
* @public
* @function
* @return {Array} Array of entries
*/
get entries ( )
{
return this._entries;
}
//// [ INDEX ] ///////////////////////////////////
/**
* Get index
* @readOnly
* @function
* @return {number} Current index value
*/
get index ( )
{
return this._index;
}
//// [ ENTRY ] ///////////////////////////////////
/**
* Pushes in an entry
* @public
* @function
*/
set entry ( value )
{
if ( typeof value === 'object' || typeof value === 'function' )
this._entries.push ( value );
}
/**
* Get current entry
* @public
* @function
* @return {Object} Current entry
*/
get entry ( )
{
return this._entry;
}
//// VALIDATION //////////////////////////////////
/**
* Returns whether this queue is at its end
* @public
* @function
* @return {boolean} True || False
*/
get isEnd ( )
{
return ( this.#touched && this._index === 0 );
}
/**
* Returns whether this queue is on its last element
* @public
* @function
* @return {boolean} True || False
*/
get isLast ( )
{
return ( ( this._entries.length - 1 ) === this._index );
}
/**
* Returns whether this queue is set, or populated
* @public
* @function
* @return {boolean} True || False
*/
get isSet ( )
{
return ( this._entries.length > 0 );
}
//// UTILITIES ///////////////////////////////////
/**
* Returns next entry; begins with [ 0 ], or first entry
* @public
* @function
* @return {Object} Next entry
*/
get next ( )
{
this.#touched = true;
let _entry = this._entries [ this._index ];
if ( this._index === this._entries.length - 1 )
this._index = 0;
else
this._index = this._index + 1;
return _entry;
}
/**
* Resets index to 0
* @public
* @function
*/
get reset ( )
{
[ this._index, this.#touched ] = [ 0, false ];
}
}
//// HANDLERS ////////////////////////////////////////
/**
* @class {Object} Animation Animation handler; for drawing a single object in one instance
* @property {clObject} object CanvasLab object
* @property {string|Function} timing Timing function
* @property {number} period Period of time
* @property {Object} change Change to object
* @property {Object} options Options for this object
*/
class Animation
{
_object = undefined;
_timing = undefined;
_period = undefined;
_change = undefined;
#timings =
{
//// EASE-IN /////////////////////////////////////
'easeInSine': ( timeFraction ) => 1 - Math.cos ( ( timeFraction * Math.PI ) / 2 ),
'easeInCubic': ( timeFraction ) => timeFraction * timeFraction * timeFraction,
'easeInQuint': ( timeFraction ) => timeFraction * timeFraction * timeFraction * timeFraction * timeFraction,
'easeInCirc': ( timeFraction ) => 1 - Math.sqrt ( 1 - Math.pow ( timeFraction, 2 ) ),
'easeInElastic': ( timeFraction ) => ( timeFraction === 0 ) ? 0 : ( timeFraction === 1 ) ? 1 : - Math.pow ( 2, 10 * timeFraction - 10 ) * Math.sin ( ( timeFraction * 10 - 10.75 ) * ( ( 2 * Math.PI ) / 3 ) ),
'easeInQuad': ( timeFraction ) => timeFraction * timeFraction,
'easeInQuart': ( timeFraction ) => timeFraction * timeFraction * timeFraction * timeFraction,
'easeInExpo': ( timeFraction ) => ( timeFraction === 0 ) ? 0 : Math.pow ( 2, 10 * timeFraction - 10 ),
'easeInBack': ( timeFraction ) => ( 1.70158 + 1 ) * timeFraction * timeFraction * timeFraction - 1.70158 * timeFraction * timeFraction,
//// EASE-OUT ////////////////////////////////////
'easeOutSine': ( timeFraction ) => Math.sin ( ( timeFraction * Math.PI ) / 2 ),
'easeOutCubic': ( timeFraction ) => 1 - Math.pow ( 1 - timeFraction, 3 ),
'easeOutQuint': ( timeFraction ) => 1 - Math.pow ( 1 - timeFraction, 5 ),
'easeOutCirc': ( timeFraction ) => Math.sqrt ( 1 - Math.pow ( timeFraction - 1, 2 ) ),
'easeOutElastic': ( timeFraction ) => ( timeFraction === 0 ) ? 0 : ( timeFraction === 1 ) ? 1 : Math.pow ( 2, -10 * timeFraction ) * Math.sin ( ( timeFraction * 10 - 0.75 ) * ( ( 2 * Math.PI ) / 3 ) ) + 1,
'easeOutQuad': ( timeFraction ) => 1 - ( 1 - timeFraction ) * ( 1 - timeFraction ),
'easeOutQuart': ( timeFraction ) => 1 - Math.pow ( 1 - timeFraction, 4 ),
'easeOutExpo': ( timeFraction ) => ( timeFraction === 1 ) ? 1 : 1 - Math.pow ( 2, -10 * timeFraction ),
'easeOutBack': ( timeFraction ) => 1 + ( 1.70158 + 1 ) * Math.pow ( timeFraction - 1, 3 ) + 1.70158 * Math.pow ( timeFraction - 1, 2 ),
//// EASE-IN-OUT /////////////////////////////////
'easeInOutSine': ( timeFraction ) => - ( Math.cos ( Math.PI * timeFraction ) - 1 ) / 2,
'easeInOutCubic': ( timeFraction ) => ( timeFraction < 0.5 ) ? 4 * timeFraction * timeFraction * timeFraction : 1 - Math.pow ( -2 * timeFraction + 2, 3 ) / 2,
'easeInOutQuint': ( timeFraction ) => ( timeFraction < 0.5 ) ? 16 * timeFraction * timeFraction * timeFraction * timeFraction * timeFraction : 1 - Math.pow ( -2 * timeFraction + 2, 5 ) / 2,
'easeInOutCirc': ( timeFraction ) => ( timeFraction < 0.5 ) ? ( 1 - Math.sqrt ( 1 - Math.pow ( 2 * timeFraction, 2 ) ) ) / 2 : ( Math.sqrt ( 1 - Math.pow ( -2 * timeFraction + 2, 2 ) ) + 1 ) / 2,
'easeInOutElastic': ( timeFraction ) => ( timeFraction === 0 ) ? 0 : ( timeFraction === 1 ) ? 1 : ( timeFraction < 0.5 ) ? - ( Math.pow ( 2, 20 * timeFraction - 10 ) * Math.sin ( ( 20 * timeFraction - 11.125 ) * ( ( 2 * Math.PI ) / 4.5 ) ) ) / 2 : ( Math.pow ( 2, -20 * timeFraction + 10 ) * Math.sin ( ( 20 * timeFraction - 11.125 ) * ( 2 * Math.PI ) / 4.5 ) ) / 2 + 1,
'easeInOutQuad': ( timeFraction ) => ( timeFraction < 0.5 ) ? 2 * timeFraction * timeFraction : 1 - Math.pow ( -2 * timeFraction + 2, 2 ) / 2,
'easeInOutQuart': ( timeFraction ) => ( timeFraction < 0.5 ) ? 8 * timeFraction * timeFraction * timeFraction * timeFraction : 1 - Math.pow ( -2 * timeFraction + 2, 4 ) / 2,
'easeInOutExpo': ( timeFraction ) => ( timeFraction === 0 ) ? 0 : ( timeFraction === 1 ) ? 1 : ( timeFraction < 0.5 ) ? Math.pow ( 2, 20 * timeFraction - 10 ) / 2 : ( 2 - Math.pow ( 2, -20 * timeFraction + 10 ) ) / 2,
'easeInOutBack': ( timeFraction ) => ( timeFraction < 0.5 ) ? ( Math.pow ( 2 * timeFraction, 2 ) * ( ( ( 1.70158 * 1.525 ) + 1 ) * 2 * timeFraction - ( 1.70158 * 1.525 ) ) ) / 2 : ( Math.pow ( 2 * timeFraction - 2, 2 ) * ( ( ( 1.70158 * 1.525 ) + 1 ) * ( timeFraction * 2 - 2 ) + ( 1.70158 * 1.525 ) ) + 2 ) / 2
}
_options =
{
cache: false,
active: false,
normalize: false
}
_queue = new Queue;
#cache = new Array;
/**
* Creates an animation instance
* @param {clObject} object Canvas Lab object
* @param {string|Function} timing Timing function
* @param {number} period Period of time
* @param {Object} change Change to object
*/
constructor ( object, timing, period, change )
{
//// COMPOSITION ////////////////////////////
this._isNumber = VALIDATION.isNumber;
this._end = UTILITIES.animation.end;
this._getInvertedAngle = UTILITIES.animation.getInvertedAngle;
Object.defineProperty ( this, 'cache', PROPERTY_BLOCKS.animation.cache );
Object.defineProperty ( this, 'cancel', PROPERTY_BLOCKS.animation.cancel );
Object.defineProperty ( this, 'period', PROPERTY_BLOCKS.animation.period );
Object.defineProperty ( this, 'queue', PROPERTY_BLOCKS.animation.queue );
this.object = object;
this.timing = timing;
this.period = period;
this.change = change;
this._options.active = true;
}
//// PROPERTIES //////////////////////////////////
//// [ OBJECT ] ////////////////////
/**
* Set object to animate
* @public
* @function
* @param {clObject} value Canvas Lab object
*/
set object ( value )
{
this._object = value;
}
/**
* Get object
* @readOnly
* @function
* @return {clObject} Canvas Lab object
*/
get object ( )
{
return this._object;
}
//// [ TIMING ] ////////////////////
/**
* Set timing
* @public
* @function
* @param {string|Function} value Timing function
*/
set timing ( value )
{
if ( value != undefined )
switch ( typeof value )
{
case 'string':
this._timing = this.#timings [ value ];
break;
case 'function':
this._timing = value;
break;
default:
console.warn ( `"${value}" is not a valid timing type !` );
}
}
/**
* Get timing
* @readOnly
* @function
* @return {Function} Timing function
*/
get timing ( )
{
return this._timing;
}
//// [ PERIOD ] ////////////////////
/**
* Set period of animation
* @public
* @function
* @param {number} value Period of animation-time
* @see {@link PROPERTY_BLOCKS.animation.period}
*/
set period ( value ) { }
/**
* Get period of animation
* @readOnly
* @function
* @return {number} Period of animation-time
* @see {@link PROPERTY_BLOCKS.animation.period}
*/
get period ( ) { }
//// [ CHANGE ] ////////////////////
/**
* Set change
* @public
* @function
* @param {clChange} value Canvas Lab change object
*/
set change ( value )
{
this._change = value;
}
/**
* Get change
* @readOnly
* @function
* @return {clChange} Canvas Lab change object
*/
get change ( )
{
return this._change;
}
//// [ CACHE ] /////////////////////
/**
* Set cache
* @public
* @function
* @param {boolean} value True || False
* @see {@link PROPERTY_BLOCKS.animation.cache}
*/
set cache ( value ) { }
/**
* Get cache
* @readOnly
* @function
* @return {boolean} True || False
* @see {@link PROPERTY_BLOCKS.animation.cache}
*/
get cache ( ) { }
//// [ QUEUE ] /////////////////////
/**
* Set queue
* @public
* @function
* @param {Queue} value Queue object
* @see {@link PROPERTY_BLOCKS.animation.queue}
*/
set queue ( value ) { }
/**
* Get queue
* @readOnly
* @function
* @return {Queue} Queue object
* @see {@link PROPERTY_BLOCKS.animation.queue}
*/
get queue ( ) { }
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is a Number value
* @private
* @function
* @param {number} value Number value
* @return {boolean} True || False
* @see {@link VALIDATION.isNumber}
*/
_isNumber ( ) { }
//// UTILITIES ////////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Cache object
* @private
* @function
* @param {clObject} object Canvas Lab object
*/
_cacheObject ( )
{
let _object = undefined;
switch ( this.object.constructor.name )
{
case 'Circle':
_object = new Circle;
// point = {Point}
_object.point = new Point ( this.object.x, this.object.y );
// radius = {number}
_object.radius = this.object.radius;
// angle = start: {number}, end: {number}, clockwise: {boolean}
_object.angle.start = this.object.angle.start;
_object.angle.end = this.object.angle.end;
_object.angle.clockwise = this.object.angle.clockwise;
// stroke = color: {Rgb}, type: {string}, segments: {Array.<number>}, width: {number}
_object.stroke.color = new Rgb (
this.object.stroke.color.red,
this.object.stroke.color.green,
this.object.stroke.color.blue,
this.object.stroke.color.alpha
);
_object.stroke.type = this.object.stroke.type;
_object.stroke.segments = this.object.segments;
_object.stroke.width = this.object.stroke.width;
// fill = color: {Rgb}, type: {string}
_object.fill.color = new Rgb (
this.object.fill.color.red,
this.object.fill.color.green,
this.object.fill.color.blue,
this.object.fill.color.alpha
);
_object.fill.type = this.object.fill.type;
// shadow = color: {Rgb}, blur: {number}, offset: {Point}
_object.shadow.color = new Rgb (
this.object.shadow.color.red,
this.object.shadow.color.green,
this.object.shadow.color.blue,
this.object.shadow.color.alpha
);
_object.shadow.blur = this.object.shadow.blur;
_object.shadow.offset = new Point ( this.object.shadow.offset.x, this.object.shadow.offset.y );
// canvas = {string}
_object.canvas = this.object.canvas;
break;
case 'Ellipse':
_object = new Ellipse;
// point = {Point}
_object.point = new Point ( this.object.x, this.object.y );
// radius = {number}
_object.radius = this.object.radius;
// angle = start: {number}, end: {number}, clockwise: {boolean}
_object.angle.start = this.object.angle.start;
_object.angle.end = this.object.angle.end;
_object.angle.clockwise = this.object.angle.clockwise;
// stroke = color: {Rgb}, type: {string}, segments: {Array.<number>}, width: {number}
_object.stroke.color = new Rgb (
this.object.stroke.color.red,
this.object.stroke.color.green,
this.object.stroke.color.blue,
this.object.stroke.color.alpha
);
_object.stroke.type = this.object.stroke.type;
_object.stroke.segments = this.object.segments;
_object.stroke.width = this.object.stroke.width;
// fill = color: {Rgb}, type: {string}
_object.fill.color = new Rgb (
this.object.fill.color.red,
this.object.fill.color.green,
this.object.fill.color.blue,
this.object.fill.color.alpha
);
_object.fill.type = this.object.fill.type;
// shadow = color: {Rgb}, blur: {number}, offset: {Point}
_object.shadow.color = new Rgb (
this.object.shadow.color.red,
this.object.shadow.color.green,
this.object.shadow.color.blue,
this.object.shadow.color.alpha
);
_object.shadow.blur = this.object.shadow.blur;
_object.shadow.offset = new Point ( this.object.shadow.offset.x, this.object.shadow.offset.y );
// canvas = {string}
_object.canvas = this.object.canvas;
break;
case 'Rectangle':
_object = new Rectangle;
// point = {Point}
_object.point = new Point ( this.object.x, this.object.y );
// radius = {number}
_object.radius = this.object.radius;
// stroke = color: {Rgb}, type: {string}, segments: {Array.<number>}, width: {number}
_object.stroke.color = new Rgb (
this.object.stroke.color.red,
this.object.stroke.color.green,
this.object.stroke.color.blue,
this.object.stroke.color.alpha
);
_object.stroke.type = this.object.stroke.type;
_object.stroke.segments = this.object.segments;
_object.stroke.width = this.object.stroke.width;
// fill = color: {Rgb}, type: {string}
_object.fill.color = new Rgb (
this.object.fill.color.red,
this.object.fill.color.green,
this.object.fill.color.blue,
this.object.fill.color.alpha
);
_object.fill.type = this.object.fill.type;
// shadow = color: {Rgb}, blur: {number}, offset: {Point}
_object.shadow.color = new Rgb (
this.object.shadow.color.red,
this.object.shadow.color.green,
this.object.shadow.color.blue,
this.object.shadow.color.alpha
);
_object.shadow.blur = this.object.shadow.blur;
_object.shadow.offset = new Point ( this.object.shadow.offset.x, this.object.shadow.offset.y );
// canvas = {string}
_object.canvas = this.object.canvas;
break;
case 'RoundedRectangle':
_object = new RoundedRectangle;
// point = {Point}
_object.point = new Point ( this.object.x, this.object.y );
// radius = {number}
_object.radius = this.object.radius;
// stroke = color: {Rgb}, type: {string}, segments: {Array.<number>}, width: {number}
_object.stroke.color = new Rgb (
this.object.stroke.color.red,
this.object.stroke.color.green,
this.object.stroke.color.blue,
this.object.stroke.color.alpha
);
_object.stroke.type = this.object.stroke.type;
_object.stroke.segments = this.object.segments;
_object.stroke.width = this.object.stroke.width;
// fill = color: {Rgb}, type: {string}
_object.fill.color = new Rgb (
this.object.fill.color.red,
this.object.fill.color.green,
this.object.fill.color.blue,
this.object.fill.color.alpha
);
_object.fill.type = this.object.fill.type;
// shadow = color: {Rgb}, blur: {number}, offset: {Point}
_object.shadow.color = new Rgb (
this.object.shadow.color.red,
this.object.shadow.color.green,
this.object.shadow.color.blue,
this.object.shadow.color.alpha
);
_object.shadow.blur = this.object.shadow.blur;
_object.shadow.offset = new Point ( this.object.shadow.offset.x, this.object.shadow.offset.y );
// canvas = {string}
_object.canvas = this.object.canvas;
break;
case 'Text':
_object = new Text (
this.object.point,
this.object.text,
this.object.type,
this.object.size,
this.object.weight,
this.object.maxWidth,
this.object.offset,
this.object.stroke,
this.object.fill,
this.object.shadow,
this.object.canvas
);
break;
}
this.#cache.push ( _object );
}
/**
* Checks whether queue is set
* @private
* @function
*/
_checkQueue ( )
{
if ( this.queue.isSet )
{
let _animate = this.queue.next; // Get initial ( and subsequent ) queue entries
this.object = _animate.object;
this.timing = _animate.timing;
this.period = _animate.period;
this.change = _animate.change;
}
}
/**
* Clears canvas
* @private
* @function
* @param {clObject} object Canvas Lab object
*/
_clearCanvas ( object )
{
let _canvas = document.getElementById ( object.canvas );
if ( _canvas ) // @TODO: identify why this check has to take place periodically !
object._canvas.clearRect ( 0, 0, _canvas.width, _canvas.height );
}
/**
* Draw cached object(s)
* @private
* @function
*/
_drawCache ( )
{
if ( this.#cache.length > 0 )
for ( let _entry of this.#cache )
_entry.draw ( );
}
/**
* End animation
* @private
* @function
* @see {@link UTILITIES.animation.end}
*/
_end ( ) { }
/**
* Returns an inverted angle; of 360 degrees
* @private
* @function
* @param {number} angle Angle to convert
* @return {number} Inverted angle
* @see {@link UTILITIES.animation.getInvertedAngle}
*/
_getInvertedAngle ( angle ) { }
/**
* Returns a point based off of the direction & distance passed
* @private
* @function
* @param {number} direction Direction to point; in degrees
* @param {number} distance Distance to point
* @return {Point} X & Y coordinates
*/
_getPointByDegreeNDistance ( direction, distance )
{
let _point = new Point;
let _angle = ( direction % 360 );
_point.x = this.object.position.origin.x - Math.cos ( _angle * Math.PI / 180 ) * distance;
_point.y = this.object.position.origin.y - Math.sin ( _angle * Math.PI / 180 ) * distance;
return _point;
}
/**
* Resets the canvas transform; from rotational transforms
* @private
* @function
*/
_resetCanvasTransform ( )
{
if ( canvaslab.rotation > 0 )
{
this.object.rotate ( - ( canvaslab.rotation ) );
canvaslab.rotation = 0;
}
}
/**
* Caches current object
* @private
* @function
*/
_setCache ( )
{
if ( this.queue.isSet && ! this.queue.isEnd )
{
if ( this.change.cache )
this._cacheObject ( );
this.animate ( );
}
else
this._resetCanvasTransform ( );
console.info ( '. animation complete !' );
}
/**
* Set Position data
* @private
* @function
*/
_setPositionData ( )
{
for ( let _type in this.change )
{
let _amount = this.change [ _type ];
switch ( _type )
{
case 'point':
this.object.position.origin = this.object.point;
this.object.position.distance = _amount;
this.object.position.direction = _amount;
break;
case 'startPoint':
this.object.position.origin = this.object.start;
this.object.position.distance = _amount;
this.object.position.direction = _amount;
break;
case 'endPoint':
this.object.position.origin = this.object.end;
this.object.position.distance = _amount;
this.object.position.direction = _amount;
break;
case 'move':
this.object.position.origin = this.object.point;
_amount.degree = ( this.change.rotatePoint ) ? _amount.degree + this.change.rotatePoint
: _amount.degree;
let _point = this._getPointByDegreeNDistance ( _amount.degree, _amount.distance );
this.object.position.distance = _point;
this.object.position.direction = _point;
break;
case 'radius':
// code . . .
break;
case 'aspect':
this.object.position.aspect = this.object.aspect;
break;
case 'width':
this.object.position.aspect.width = this.object.width;
break;
case 'height':
this.object.position.aspect.height = this.object.height;
break;
case 'rotate':
// code . . .
break;
case 'strokeColor':
// code . . .
break;
case 'strokeAlpha':
// code . . .
break;
case 'fillColor':
// code . . .
break;
case 'fillAlpha':
// code . . .
break;
case 'fillLinear':
case 'fillRadial':
case 'fillConic':
// code . . .
break;
case 'curve':
case 'controlPoints':
this.object.position.controlPoints = this.object.controlPoints.points;
break;
case 'p0':
this.object.position.controlPoints.p0 = this.object.controlPoints.p0;
break;
case 'p1':
this.object.position.controlPoints.p1 = this.object.controlPoints.p1;
break;
case 'p2':
this.object.position.controlPoints.p2 = this.object.controlPoints.p2;
break;
case 'p3':
this.object.position.controlPoints.p3 = this.object.controlPoints.p3;
break;
case 'fontSize':
this.object.position.fontSize = this.object.size;
break;
case 'cache':
this.cache = _amount;
break;
}
}
}
/**
* Calculates an animation transition
* @private
* @async
* @function
* @param {clObject} object Canvas Lab object
* @param {number} progress Progress of transition; 0 - 1
*/
async _transition ( object, progress )
{
for ( let _type in this.change )
{
let _amount = this.change [ _type ];
switch ( _type )
{
case 'point':
case 'move':
object.point =
{
x: object.position.origin.x + ( object.position.distance * progress ) * Math.cos ( object.position.direction ),
y: object.position.origin.y + ( object.position.distance * progress ) * Math.sin ( object.position.direction )
}
break;
case 'startPoint':
object.start =
{
x: ( object.position.origin.x ) + ( object.position.distance * progress ) * Math.cos ( object.position.direction ),
y: ( object.position.origin.y ) + ( object.position.distance * progress ) * Math.sin ( object.position.direction )
}
break;
case 'endPoint':
object.end =
{
x: object.position.origin.x + ( object.position.distance * progress ) * Math.cos ( object.position.direction ),
y: object.position.origin.y + ( object.position.distance * progress ) * Math.sin ( object.position.direction )
}
break;
case 'radius':
let _progress = ( progress < 0 ) ? 0 : progress;
object.radius = _amount * _progress;
break;
case 'angleStart':
object.angle.start = _amount * progress;
break;
case 'angleEnd':
object.angle.end = _amount * progress;
break
case 'aspect':
object.position.aspect.width += ( _amount.width - object.position.aspect.width ) * progress;
object.aspect.width = object.position.aspect.width;
object.position.aspect.height += ( _amount.height - object.position.aspect.height ) * progress;
object.aspect.height = object.position.aspect.height;
break;
case 'width':
object.position.aspect.width += ( _amount - object.position.aspect.width ) * progress;
object.aspect.width = object.position.aspect.width;
break;
case 'height':
object.position.aspect.height += ( _amount - object.position.aspect.height ) * progress;
object.aspect.height = object.position.aspect.height;
break;
case 'rotate':
let _rotate = progress * ( _amount - object.position.rotation );
object.rotate ( _rotate );
object.position.rotation += _rotate;
canvaslab.rotation += _rotate;
break;
case 'strokeColor':
object.stroke.color.cycle ( object.stroke.color, _amount, progress, 1 );
break;
case 'strokeAlpha':
object.stroke.color.alpha = _amount * progress;
break;
case 'fillColor':
object.fill.color.cycle ( object.fill.color, _amount, progress, 1 );
break;
case 'fillAlpha':
object.fill.color.alpha = _amount * progress;
break;
case 'fillLinear':
case 'fillRadial':
case 'fillConic':
for ( let _entry in _amount )
{
let _start = object.fill.gradient.stops [ _entry ];
let _end = _amount [ _entry ];
object.fill.gradient.stops [ _entry ].color.cycle ( _start.color, _end.color, progress, 1 );
}
break;
case 'shadowColor':
object.shadow.color.cycle ( object.shadow.color, _amount, progress, 1 );
break;
case 'shadowAlpha':
object.shadow.color.alpha = _amount * progress;
break;
case 'shadowBlur':
object.shadow.blur = _amount * progress;
break;
case 'shadowOffset':
object.shadow.offset = new Point ( _amount.x * progress, _amount.y * progress );
break;
case 'curve':
case 'controlPoints':
for ( let _id in _amount )
{
let _oldControlPoint = object.position.controlPoints [ _id ];
let _newControlPoint = _amount [ _id ];
if ( _oldControlPoint != _newControlPoint )
{
let _change = ( _newControlPoint - _oldControlPoint ) * progress;
switch ( _id )
{
case '0': object.controlPoints.p0 = _change; break;
case '1': object.controlPoints.p1 = _change; break;
case '2': object.controlPoints.p2 = _change; break;
case '3': object.controlPoints.p3 = _change; break;
}
}
}
break;
case 'p0':
object.controlPoints.p0 = ( _amount - object.position.controlPoints.p0 ) * progress;
break;
case 'p1':
object.controlPoints.p1 = ( _amount - object.position.controlPoints.p1 ) * progress;
break;
case 'p2':
object.controlPoints.p2 = ( _amount - object.position.controlPoints.p2 ) * progress;
break;
case 'p3':
object.controlPoints.p3 = ( _amount - object.position.controlPoints.p3 ) * progress;
break;
case 'fontSize':
object.size = ( ( _amount - object.position.fontSize ) * progress ) + object.position.fontSize;
break;
}
}
}
//// + PUBLIC //////////////////////
/**
* Cancels animation
* @readOnly
* @function
* @see {@link PROPERTY_BLOCKS.animation.cancel}
*/
get cancel ( ) { }
//// ANIMATE /////////////////////////////////////
/**
* Initiates animation
* @public
* @function
*/
animate ( )
{
//// PREPARATORY /////////////////////////
this._checkQueue ( );
this._resetCanvasTransform ( );
this._setPositionData ( );
//// PROPERTIES //////////////////////////
let _object = this._object;
let _timing = this._timing;
let _period = this._period;
let _options = this._options;
//// FUNCTIONS ///////////////////////////
let _transition = ( object, progress ) => this._transition ( object, progress );
let _clearCanvas = ( object ) => this._clearCanvas ( object );
let _drawCache = ( ) => this._drawCache ( );
let _setCache = ( ) => this._setCache ( );
let _end = ( ) => this._end ( );
////////////////////////////////////////////////
//// ANIMATE /////////////////////////////
function _animate ( )
{
let _start = performance.now ( );
requestAnimationFrame (
function animate ( time )
{
_clearCanvas ( _object );
let _timeFraction = ( time - _start ) / _period; // timeFraction goes from 0 to 1
let _progress = _timing ( _timeFraction ); // calculate the current animation state
if ( _options.normalize )
_progress = ( _progress < 0 ) ? 0 : _progress;
_transition ( _object, _progress );
_drawCache ( );
_object.draw ( );
( _options.active ) ? ( _timeFraction < 1 ) // Resolve
? requestAnimationFrame ( animate )
: _setCache ( )
: _end ( );
}
);
}
////////////////////////////////////////////////
//// INITIALIZE //////////////////////////
if ( this._object && this._period )
_animate ( );
else
console.warn ( '[ Animation ] :: The "object" and/or "period" properties are invalid !' );
}
}
/**
* @class {Object} Animations Animations handler; for drawing multiple objects in one instance
* @property {Array.<clObject>} objects CanvasLab objects
* @property {Array.<string|Function>} timings Timing functions
* @property {number} periods Period of time
* @property {Array.<Object>} changes Changes an object
* @property {Object} options Options for this object
*/
class Animations
{
_objects = undefined;
_timings = undefined;
_periods = undefined;
_changes = undefined;
#timings =
{
//// EASE-IN /////////////////////////////////
'easeInSine': ( timeFraction ) => 1 - Math.cos ( ( timeFraction * Math.PI ) / 2 ),
'easeInCubic': ( timeFraction ) => timeFraction * timeFraction * timeFraction,
'easeInQuint': ( timeFraction ) => timeFraction * timeFraction * timeFraction * timeFraction * timeFraction,
'easeInCirc': ( timeFraction ) => 1 - Math.sqrt ( 1 - Math.pow ( timeFraction, 2 ) ),
'easeInElastic': ( timeFraction ) => ( timeFraction === 0 ) ? 0 : ( timeFraction === 1 ) ? 1 : - Math.pow ( 2, 10 * timeFraction - 10 ) * Math.sin ( ( timeFraction * 10 - 10.75 ) * ( ( 2 * Math.PI ) / 3 ) ),
'easeInQuad': ( timeFraction ) => timeFraction * timeFraction,
'easeInQuart': ( timeFraction ) => timeFraction * timeFraction * timeFraction * timeFraction,
'easeInExpo': ( timeFraction ) => ( timeFraction === 0 ) ? 0 : Math.pow ( 2, 10 * timeFraction - 10 ),
'easeInBack': ( timeFraction ) => ( 1.70158 + 1 ) * timeFraction * timeFraction * timeFraction - 1.70158 * timeFraction * timeFraction,
//// EASE-OUT ////////////////////////////////
'easeOutSine': ( timeFraction ) => Math.sin ( ( timeFraction * Math.PI ) / 2 ),
'easeOutCubic': ( timeFraction ) => 1 - Math.pow ( 1 - timeFraction, 3 ),
'easeOutQuint': ( timeFraction ) => 1 - Math.pow ( 1 - timeFraction, 5 ),
'easeOutCirc': ( timeFraction ) => Math.sqrt ( 1 - Math.pow ( timeFraction - 1, 2 ) ),
'easeOutElastic': ( timeFraction ) => ( timeFraction === 0 ) ? 0 : ( timeFraction === 1 ) ? 1 : Math.pow ( 2, -10 * timeFraction ) * Math.sin ( ( timeFraction * 10 - 0.75 ) * ( ( 2 * Math.PI ) / 3 ) ) + 1,
'easeOutQuad': ( timeFraction ) => 1 - ( 1 - timeFraction ) * ( 1 - timeFraction ),
'easeOutQuart': ( timeFraction ) => 1 - Math.pow ( 1 - timeFraction, 4 ),
'easeOutExpo': ( timeFraction ) => ( timeFraction === 1 ) ? 1 : 1 - Math.pow ( 2, -10 * timeFraction ),
'easeOutBack': ( timeFraction ) => 1 + ( 1.70158 + 1 ) * Math.pow ( timeFraction - 1, 3 ) + 1.70158 * Math.pow ( timeFraction - 1, 2 ),
//// EASE-IN-OUT /////////////////////////////
'easeInOutSine': ( timeFraction ) => - ( Math.cos ( Math.PI * timeFraction ) - 1 ) / 2,
'easeInOutCubic': ( timeFraction ) => ( timeFraction < 0.5 ) ? 4 * timeFraction * timeFraction * timeFraction : 1 - Math.pow ( -2 * timeFraction + 2, 3 ) / 2,
'easeInOutQuint': ( timeFraction ) => ( timeFraction < 0.5 ) ? 16 * timeFraction * timeFraction * timeFraction * timeFraction * timeFraction : 1 - Math.pow ( -2 * timeFraction + 2, 5 ) / 2,
'easeInOutCirc': ( timeFraction ) => ( timeFraction < 0.5 ) ? ( 1 - Math.sqrt ( 1 - Math.pow ( 2 * timeFraction, 2 ) ) ) / 2 : ( Math.sqrt ( 1 - Math.pow ( -2 * timeFraction + 2, 2 ) ) + 1 ) / 2,
'easeInOutElastic': ( timeFraction ) => ( timeFraction === 0 ) ? 0 : ( timeFraction === 1 ) ? 1 : ( timeFraction < 0.5 ) ? - ( Math.pow ( 2, 20 * timeFraction - 10 ) * Math.sin ( ( 20 * timeFraction - 11.125 ) * ( ( 2 * Math.PI ) / 4.5 ) ) ) / 2 : ( Math.pow ( 2, -20 * timeFraction + 10 ) * Math.sin ( ( 20 * timeFraction - 11.125 ) * ( 2 * Math.PI ) / 4.5 ) ) / 2 + 1,
'easeInOutQuad': ( timeFraction ) => ( timeFraction < 0.5 ) ? 2 * timeFraction * timeFraction : 1 - Math.pow ( -2 * timeFraction + 2, 2 ) / 2,
'easeInOutQuart': ( timeFraction ) => ( timeFraction < 0.5 ) ? 8 * timeFraction * timeFraction * timeFraction * timeFraction : 1 - Math.pow ( -2 * timeFraction + 2, 4 ) / 2,
'easeInOutExpo': ( timeFraction ) => ( timeFraction === 0 ) ? 0 : ( timeFraction === 1 ) ? 1 : ( timeFraction < 0.5 ) ? Math.pow ( 2, 20 * timeFraction - 10 ) / 2 : ( 2 - Math.pow ( 2, -20 * timeFraction + 10 ) ) / 2,
'easeInOutBack': ( timeFraction ) => ( timeFraction < 0.5 ) ? ( Math.pow ( 2 * timeFraction, 2 ) * ( ( ( 1.70158 * 1.525 ) + 1 ) * 2 * timeFraction - ( 1.70158 * 1.525 ) ) ) / 2 : ( Math.pow ( 2 * timeFraction - 2, 2 ) * ( ( ( 1.70158 * 1.525 ) + 1 ) * ( timeFraction * 2 - 2 ) + ( 1.70158 * 1.525 ) ) + 2 ) / 2
}
_options =
{
cache: false,
active: false,
normalize: false
}
_queue = new Queue;
#cache = new Array;
/**
* Creates an animation instance
* @property {Array.<clObject>} objects CanvasLab objects
* @property {Array.<string|Function>} timings Timing functions
* @property {number} period Period of time
* @property {Array.<Object>} changes Changes an objects
*/
constructor ( objects, timings, period, changes )
{
//// COMPOSITION ////////////////////////////
this._isNumber = VALIDATION.isNumber;
this._end = UTILITIES.animation.end;
this._getInvertedAngle = UTILITIES.animation.getInvertedAngle;
Object.defineProperty ( this, 'cache', PROPERTY_BLOCKS.animation.cache );
Object.defineProperty ( this, 'cancel', PROPERTY_BLOCKS.animation.cancel );
Object.defineProperty ( this, 'period', PROPERTY_BLOCKS.animation.period );
Object.defineProperty ( this, 'queue', PROPERTY_BLOCKS.animation.queue );
this.objects = objects;
this.timings = timings;
this.period = period;
this.changes = changes;
this._options.active = true;
}
//// PROPERTIES //////////////////////////////////
//// [ OBJECT ] ////////////////////
/**
* Set objects to animate
* @public
* @function
* @param {clObject} value Canvas Lab objects
*/
set objects ( value )
{
this._objects = value;
}
/**
* Get objects
* @readOnly
* @function
* @return {clObject} Canvas Lab objects
*/
get objects ( )
{
return this._objects;
}
//// [ TIMING ] ////////////////////
/**
* Set timings
* @public
* @function
* @param {string|Function} value Timing function
*/
set timings ( value )
{
if ( Array.isArray ( value ) )
{
let _results = new Array;
for ( let _entry of value )
( typeof _entry === 'string' )
? _results.push ( this.#timings [ _entry ] )
: _results.push ( _entry );
this._timings = _results;
}
else
this._timings = ( value != undefined )
? ( typeof value === 'string' )
? this.#timings [ value ]
: value
: this._timings;
}
/**
* Get timing
* @readOnly
* @function
* @return {Function} Timing function
*/
get timings ( )
{
return this._timings;
}
//// [ PERIOD ] ////////////////////
/**
* Set period of animation
* @public
* @function
* @param {number} value Period of animation-time
* @see {@link PROPERTY_BLOCKS.animation.period}
*/
set period ( value ) { }
/**
* Get period of animation
* @readOnly
* @function
* @return {number} Period of animation-time
* @see {@link PROPERTY_BLOCKS.animation.period}
*/
get period ( ) { }
//// [ CHANGE ] ////////////////////
/**
* Set changes
* @public
* @function
* @param {clChange} value Canvas Lab changes object
*/
set changes ( value )
{
this._changes = value;
}
/**
* Get changes
* @readOnly
* @function
* @return {clChange} Canvas Lab changes object
*/
get changes ( )
{
return this._changes;
}
//// [ CACHE ] /////////////////////
/**
* Set cache
* @public
* @function
* @param {boolean} value True || False
* @see {@link PROPERTY_BLOCKS.animation.cache}
*/
set cache ( value ) { }
/**
* Get cache
* @readOnly
* @function
* @return {boolean} True || False
* @see {@link PROPERTY_BLOCKS.animation.cache}
*/
get cache ( ) { }
//// [ QUEUE ] /////////////////////
/**
* Set queue
* @public
* @function
* @param {Queue} value Queue object
* @see {@link PROPERTY_BLOCKS.animation.queue}
*/
set queue ( value ) { }
/**
* Get queue
* @readOnly
* @function
* @return {Queue} Queue object
* @see {@link PROPERTY_BLOCKS.animation.queue}
*/
get queue ( ) { }
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is a Number value
* @private
* @function
* @param {number} value Number value
* @return {boolean} True || False
* @see {@link VALIDATION.isNumber}
*/
_isNumber ( ) { }
//// UTILITIES ////////////////////////////////////
//// - PRIVATE /////////////////////
/**
* Cache object
* @private
* @function
* @param {clObject} object Canvas Lab object
*/
_cacheObjects ( id )
{
let _object = this.objects [ id ];
let _point = new Point ( _object.x, _object.y );
let _stroke = new Stroke ( _object.stroke.color );
let _fill = new Fill ( _object.fill.color );
switch ( _object.constructor.name )
{
case 'Circle':
_object = new Circle ( _point, _object.radius, undefined, _stroke, _fill );
break;
case 'Ellipse':
_object = new Ellipse ( _point, undefined, undefined, _stroke, _fill );
break;
case 'Rectangle':
_object = new Rectangle ( _point, undefined, undefined, _stroke, _fill );
break;
case 'RoundedRectangle':
_object = new RoundedRectangle ( _point, undefined, undefined, _stroke, _fill );
break;
case 'Text':
_object = new Text (
_object.point,
_object.text,
_object.type,
_object.size,
_object.weight,
_object.maxWidth,
_object.offset,
_object.stroke,
_object.fill,
_object.shadow,
_object.canvas
);
break;
}
this.#cache.push ( _object );
}
/**
* Checks whether queue is set
* @private
* @function
*/
_checkQueue ( )
{
if ( this.queue.isSet )
{
let _transition = this.queue.next; // Get initial ( and subsequent ) queue entries
this.objects = this._getObjects ( _transition );
this.timings = this._getTimings ( _transition );
this.period = _transition.period;
this.changes = _transition.change;
}
}
/**
* Clears canvas
* @private
* @function
* @param {clObject} object Canvas Lab object
*/
_clearCanvas ( )
{
let _object = this.objects [ 0 ];
let _canvas = document.getElementById ( _object.canvas );
if ( _canvas ) // @TODO: identify why this check has to take place periodically !
_object._canvas.clearRect ( 0, 0, _canvas.width, _canvas.height );
}
/**
* Draw cached object(s)
* @private
* @function
*/
_drawCache ( )
{
if ( this.#cache.length > 0 )
for ( let _entry of this.#cache )
{
_entry.stroke.color.alpha = 0.35;
_entry.draw ( );
_entry.stroke.color.alpha = 1;
}
}
/**
* End animation
* @private
* @function
* @see {@link UTILITIES.animation.end}
*/
_end ( ) { }
/**
* Returns an inverted angle; of 360 degrees
* @private
* @function
* @param {number} angle Angle to convert
* @return {number} Inverted angle
* @see {@link UTILITIES.animation.getInvertedAngle}
*/
_getInvertedAngle ( angle ) { }
/**
* Returns an array of objects based on the transitions object type
* @private
* @function
* @param {Transition} transition Animation transition instance
* @return {Array} Array of objects
*/
_getObjects ( transition )
{
let _collections = [ 'Circles', 'Ellipses', 'Rectangles', 'RoundedRectangles', 'Texts' ]
let _array = new Array;
let _type = transition.object.constructor.name;
return ( _collections.includes ( _type ) ) ? _array.concat ( transition.object )
: transition.object;
}
/**
* Returns an array of timings based on the transition timing type
* @private
* @function
* @param {Transition} transition Animation transition instance
* @return {Array} Array of timings
*/
_getTimings ( transition )
{
let _results = undefined;
if ( Array.isArray ( transition.timing ) )
_results = transition.timing;
else
{
let _length = this.objects.length;
_results = new Array;
for ( let _i = 0; _i < _length; _i++ )
_results.push ( transition.timing );
}
return _results;
}
/**
* Returns a point based off of the direction & distance passed
* @private
* @function
* @param {number} direction Direction to point; in degrees
* @param {number} distance Distance to point
* @return {Point} X & Y coordinates
*/
_getPointByDegreeNDistance ( id, direction, distance )
{
let _point = new Point;
let _angle = ( direction % 360 );
_point.x = this.objects [ id ].position.origin.x - Math.cos ( _angle * Math.PI / 180 ) * distance;
_point.y = this.objects [ id ].position.origin.y - Math.sin ( _angle * Math.PI / 180 ) * distance;
return _point;
}
/**
* Caches current objects
* @private
* @function
*/
_setCache ( )
{
if ( this.queue.isSet && ! this.queue.isEnd )
{
for ( let _id in this.changes )
if ( this.changes [ _id ].cache )
this._cacheObjects ( _id );
this.animate ( );
}
else
console.info ( '. animations complete !' );
}
/**
* Set Position data
* @private
* @function
*/
_setPositionData ( )
{
for ( let _id in this.objects )
{
let _object = this.objects [ _id ];
let _change = this.changes [ _id ];
for ( let _type in _change )
{
let _difference = _change [ _type ];
switch ( _type )
{
case 'point':
_object.position.origin = _object.point;
_object.position.distance = _difference;
_object.position.direction = _difference;
break;
case 'pointFrom':
_object.position.origin = _difference;
_object.position.distance = _difference;
_object.position.direction = _difference;
break;
case 'move':
_object.position.origin = _object.point;
// Whether to invert degree
let _point = ( _difference.invert ) ? this._getPointByDegreeNDistance ( _id, this._getInvertedAngle ( _difference.degree ), _difference.distance )
: this._getPointByDegreeNDistance ( _id, _difference.degree, _difference.distance );
_object.position.distance = _point;
_object.position.direction = _point;
break;
case 'radius':
// code . . .
break;
case 'rotate':
// code . . .
break;
case 'strokeColor':
// code . . .
break;
case 'strokeAlpha':
// code . . .
break;
case 'fillColor':
// code . . .
break;
case 'fillAlpha':
// code . . .
break;
case 'fillLinear':
case 'fillRadial':
case 'fillConic':
// code . . .
break;
case 'cache':
this.cache = _difference;
break;
}
}
}
}
/**
* Calculates an animation transition
* @private
* @async
* @function
* @param {clObject} object Canvas Lab object
* @param {number} progress Progress of transition; 0 - 1
*/
async _transition ( id, progress )
{
let _object = this._objects [ id ];
let _change = this._changes [ id ];
for ( let _type in _change )
{
let _amount = _change [ _type ];
switch ( _type )
{
case 'point':
case 'move':
_object.point =
{
x: _object.position.origin.x + ( _object.position.distance * progress ) * Math.cos ( _object.position.direction ),
y: _object.position.origin.y + ( _object.position.distance * progress ) * Math.sin ( _object.position.direction )
}
break;
case 'pointFrom':
_object.point =
{
x: _amount.x + ( _object.position.distance * progress ) * Math.cos ( _object.position.direction ),
y: _amount.y + ( _object.position.distance * progress ) * Math.sin ( _object.position.direction )
}
break;
case 'radius':
let _progress = ( progress < 0 ) ? 0 : progress;
_object.radius = _amount * _progress;
break;
case 'rotate':
_object.rotate ( _amount );
break;
case 'strokeColor':
_object.stroke.color.cycle ( _object.stroke.color, _amount, progress, 1 );
break;
case 'strokeAlpha':
_object.stroke.color.alpha = _amount * progress;
break;
case 'fillColor':
_object.fill.color.cycle ( _object.fill.color, _amount, progress, 1 );
break;
case 'fillAlpha':
_object.fill.color.alpha = _amount * progress;
break;
case 'fillLinear':
case 'fillRadial':
case 'fillConic':
for ( let _entry in _amount )
{
let _start = _object.fill.gradient.stops [ _entry ];
let _end = _amount [ _entry ];
_object.fill.gradient.stops [ _entry ].color.cycle ( _start.color, _end.color, progress, 1 );
}
break;
case 'rotate':
// code . . .
break;
case 'lineTo':
let _lines = new Lines;
if ( Array.isArray ( _amount ) )
{
for ( let _entry of _amount )
{
let _line = new Line (
_object.point, /* Start */
_entry.point, /* End */
undefined, /* Stroke */
undefined, /* Shadow */
undefined, /* lineCap */
_object.canvas /* canvas */
);
_line.draw ( );
}
}
else
{
let _line = new Line (
_object.point, /* Start */
_amount.point, /* End */
undefined, /* Stroke */
undefined, /* Shadow */
undefined, /* lineCap */
_object.canvas /* canvas */
);
_line.draw ( );
}
break;
}
}
}
//// + PUBLIC //////////////////////
/**
* Cancels animation
* @readOnly
* @function
* @see {@link PROPERTY_BLOCKS.animation.cancel}
*/
get cancel ( ) { }
//// ANIMATE /////////////////////////////////////
/**
* Initiates animation
* @public
* @function
*/
animate ( )
{
//// PREPARATORY /////////////////////////
this._checkQueue ( );
this._setPositionData ( );
//// PROPERTIES //////////////////////////
let _objects = this._objects;
let _timings = this._timings;
let _period = this._period;
let _changes = this._changes;
let _options = this._options;
//// FUNCTIONS ///////////////////////////
let _transition = ( id, progress ) => this._transition ( id, progress );
let _clearCanvas = ( ) => this._clearCanvas ( );
let _drawCache = ( ) => this._drawCache ( );
let _setCache = ( ) => this._setCache ( );
let _end = ( ) => this._end ( );
////////////////////////////////////////////////
//// ANIMATE /////////////////////////////
let _timeFraction = undefined;
let _progress = undefined;
function _animate ( )
{
let _start = performance.now ( );
requestAnimationFrame (
function animate ( time )
{
_clearCanvas ( );
for ( let _id in _objects )
{
let _object = _objects [ _id ];
let _timing = _timings [ _id ];
let _change = _changes [ _id ];
_timeFraction = ( time - _start ) / _period; // timeFraction goes from 0 to 1
_progress = _timing ( _timeFraction ); // calculate the current animation state
if ( _options.normalize )
_progress = ( _progress < 0 ) ? 0 : _progress;
_transition ( _id, _progress );
_drawCache ( );
_object.draw ( );
}
( _options.active ) ? ( _timeFraction < 1 ) // Resolve
? requestAnimationFrame ( animate )
: _setCache ( )
: _end ( );
}
);
}
////////////////////////////////////////////////
//// INITIALIZE //////////////////////////
if ( this._objects && this._period )
_animate ( );
else
console.warn ( '[ Animations ] :: The "objects" and/or "period" properties are invalid !' );
}
}
/**
* @class {Object} Application Application handler
*/
class Application
{
_animation = undefined;
/**
* Application configurations & details
* @type {Object}
* @property {boolean} debug Whether to debug application
* @property {Object} about About properties
* @property {Object} about.Author Author of application
* @property {Object} about.Created Date originally created
* @property {Object} about.Library Library name
* @property {Object} about.Updated Date last updated
* @property {Object} about.Version Current versions
* @property {Object} about.Copyright Copyright
*/
#config =
{
debug: false,
about:
{
Author: 'Justin Don Byrne',
Created: 'October, 2 2023',
Library: 'Canvas Lab',
Updated: 'Oct, 24 2024',
Version: '0.7.196',
Copyright: 'Copyright (c) 2023 Justin Don Byrne'
}
}
/**
* Document object model data
* @type {Object}
* @property {Object} canvases List of canvases
* @property {Object} contexts List of canvas contexts
* @property {Object} window Window properties
* @property {number} window.width Window's width
* @property {number} window.height Window's height
* @property {Object} window.center Window's center X & Y coordinates
* @property {number} window.center.x X-axis coordinate
* @property {number} window.center.y Y-axis coordinate
* @property {Object} mouse Mouse properties
*/
#dom =
{
canvases: undefined,
contexts: { },
window:
{
width: window.innerWidth - 18,
height: window.innerHeight - 4,
center:
{
x: ( ( window.innerWidth - 18 ) / 4 ),
y: ( ( window.innerHeight - 4 ) / 2 )
}
},
mouse:
{
start: undefined,
end: undefined,
down: false,
extant: -1,
offset: { x: 0, y: 0 }
}
}
/**
* Creates an application handler
*/
constructor ( )
{
//// COMPOSITION ////////////////////////////
this._isInDom = VALIDATION.isInDom;
}
//// [ ANIMATION ] ///////////////////////////////
/**
* Get animation
* @readOnly
* @function
* @return {Animation} Animation object
*/
get animation ( )
{
return this._animation;
}
//// [ CANVAS ] ////////////////////////////////////
/**
* Set canvas element
* @public
* @function
* @param {string} value Element Id
*/
set canvas ( value )
{
let _canvases = document.getElementsByTagName ( 'canvas' );
if ( this.#dom.canvases === undefined )
{
this.#dom.canvases = new Object ( );
for ( let _canvas of _canvases )
{
this.#dom.canvases [ _canvas.id ] = document.getElementById ( _canvas.id );
this.#dom.contexts [ _canvas.id ] = document.getElementById ( _canvas.id ).getContext ( "2d" );
}
}
if ( this._isInDom ( value ) )
{
this.#dom.main.canvas = this.#dom.canvases [ value ];
this.#dom.main.context = this.#dom.contexts [ value ];
}
else
console.warn ( `"${value}" does not exist !` );
}
/**
* Get canvas element
* @public
* @function
* @return {HTMLCanvasElement} Canvas context
*/
get canvas ( )
{
return this.#dom.main.context;
}
//// [ ABOUT ] ////////////////////////////////////
/**
* Get application details
* @readOnly
* @function
* @return {Object} Application details
*/
get about ( )
{
return this.#config.about;
}
//// DOM /////////////////////////////////////////
/**
* Get dom details
* @readOnly
* @function
* @return {Object} DOM details
*/
get dom ( )
{
return this.#dom;
}
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is an element id within the DOM
* @private
* @function
* @param {string} value Element id
* @return {boolean} True || False
* @see {@link VALIDATION.isInDom}
*/
_isInDom ( ) { }
//// UTILITIES ////////////////////////////////////
/**
* Returns center ( x & y coordinates ) for the present window
* @private
* @function
* @return {Object} X & Y Coordinates
*/
get _center ( )
{
let _center =
{
x: ( ( window.innerWidth - 18 ) / 4 ),
y: ( ( window.innerHeight - 4 ) / 2 )
}
return _center;
}
/**
* Creates a new animation instance
* @param {Transition|Queue} transition Contains timing, draw, & duration values & functions
* @param {number} transition.object CanvasLab Object
* @param {Function} transition.timing Timing function
* @param {number} transition.period Period of time
* @param {clChange} transition.change Changes to object
*/
set animation ( transition = { object, timing, period, change } )
{
if ( Array.isArray ( transition ) ) // Transition(s)
if ( Array.isArray ( transition [ 0 ].object ) ) // Animation(s) #3
{
this._animation = new Animations;
this._animation.queue = new Queue ( transition );
this._animation.animate ( );
}
else // Animation #1
{
this._animation = new Animation;
this._animation.queue = new Queue ( transition );
this._animation.animate ( );
}
else // Transition
if ( Array.isArray ( transition.object ) ) // Animation(s) #2
{
this._animation = new Animations ( transition.object, transition.timing, transition.period, transition.change );
this._animation.animate ( );
}
else // Animation # One-Shot
{
this._animation = new Animation ( transition.object, transition.timing, transition.period, transition.change );
this._animation.animate ( );
}
}
}
//// INITIALIZATION ////////////////////////////////////
/**
* Initiates canvasLab
* @param {string} [canvas] Canvas identifier
*/
let initCanvasLab = ( canvas ) =>
{
if ( typeof canvasLab === 'function' && typeof window.canvaslab === 'undefined' )
window.canvaslab = new canvasLab ( canvas );
}
//// TEMPLATES ///////////////////////////////////////
class myTransitions
{
_transitions = undefined;
_template = undefined;
/**
* Create myTransitions object
*/
constructor ( )
{
//// COMPOSITION ////////////////////////////
this._isTemplate = VALIDATION.isTemplate;
Object.defineProperty ( this, 'template', PROPERTY_BLOCKS.individual.template );
}
//// PROPERTIES //////////////////////////////////
//// [ TRANSITIONS ] ///////////////
/**
* Get transitions
* @public
* @function
* @return {number} Transitions of object
*/
get transitions ( )
{
return this._transitions;
}
//// [ TEMPLATE ] //////////////////
/**
* Set template
* @public
* @function
* @param {Template} value Template object
* @see {@link PROPERTY_BLOCKS.individual.template}
*/
set template ( value ) { }
/**
* Get template
* @public
* @function
* @return {Template} Template object
* @see {@link PROPERTY_BLOCKS.individual.template}
*/
get template ( ) { }
//// VALIDATION //////////////////////////////////
/**
* Returns whether the passed value is a Template
* @private
* @memberof VALIDATION
* @function
* @param {Object} value Template object
* @return {boolean} True || False
* @see {@link VALIDATION.isTemplate}
*/
_isTemplate ( ) { }
//// UTILITIES ///////////////////////////////////
//// [ PRIVATE ] ///////////////////
/**
* Creates shape from array of numbers
* @public
* @function
* @param {Array} shape Array of collection indexes
* @param {ClCollection} collection Canvas Lab collection
* @param {string|Function} timing Timing function
* @param {number} period Period of timer
* @param {boolean} lineTos Draw lines connecting shapes
* @param {boolean} optimal Optimal line draws
*/
_shape ( shape, collection, timing, period, lineTos = true, optimal = false )
{
this._transitions = new Array;
let _objects = new Array;
let _changes = new Object;
let _matrix = new Array;
for ( let _i = 0; _i < shape.length; _i++ )
{
let _index = shape [ _i ];
let _object = collection [ _index ];
let _circle = new Circle (
this.template.point, /* Point */
_object.radius, /* Radius */
undefined, /* Angle */
new Stroke ( _object.stroke.color ), /* Stroke */
new Fill ( _object.fill.color ), /* Fill */
undefined, /* Shadow */
_object.canvas /* Canvas */
);
_objects.push ( _circle );
_changes [ _i ] = { point: _object.point, cache: true }
//// LINETOS /////////////////////////////////////
if ( lineTos )
{
_changes [ _i ].lineTo = new Array;
for ( let _j = 0; _j < shape.length; _j++ )
{
if ( _i === _j ) continue; // Skip the first _object's point, because it cannot draw a line to itself
if ( optimal ) // Skip every object's first, second, third to skip duplicate objects that have already had a line drawn to it.
if ( _j < _i ) continue;
_matrix.push ( { objectA: _i, objectB: _j } );
_changes [ _i ].lineTo.push ( collection [ shape [ _j ] ] )
}
}
}
let _result =
{
object: _objects,
timing: timing,
period: period,
change: _changes
}
this._transitions.push ( _result );
return this._transitions;
}
//// [ PUBLIC ] ////////////////////
/**
* Returns transitions for skip animation
* @public
* @function
* @param {clObject} object Canvas Lab object
* @param {ClCollection} collection Canvas Lab collection
* @param {string|Function} timing Timing function
* @param {number} period Period of timer
*/
skip ( object, collection, timing, period )
{
this._transitions = new Array;
for ( let _i = 0; _i < collection.length; _i++ )
{
let _result =
{
object: object,
timing: timing,
period: period,
change:
{
point: new Point ( collection [ _i ].point.x, collection [ _i ].point.y ),
cache: true,
fillColor: collection [ _i ].fill.color
}
}
this._transitions.push ( _result );
}
return this._transitions;
}
/**
* Returns transitions for hop animation
* @public
* @function
* @param {clObject} object Canvas Lab object
* @param {ClCollection} collection Canvas Lab collection
* @param {string|Function} timing Timing function
* @param {number} period Period of timer
*/
hop ( object, collection, timing, period )
{
this._transitions = new Array;
for ( let _i = 0; _i < collection.length; _i++ )
{
let _object = new Circle (
object.point,
collection [ _i ].radius,
collection [ _i ].angle,
new Stroke ( collection [ _i ].stroke.color ),
new Fill ( collection [ _i ].fill.color ),
undefined,
object.canvas
);
let _result =
{
object: _object,
timing: timing,
period: period,
change:
{
point: new Point ( collection [ _i ].point.x, collection [ _i ].point.y ),
cache: true
}
}
this._transitions.push ( _result );
}
return this._transitions;
}
/**
* Returns transitions for bloom animation
* @public
* @function
* @param {ClCollection} collection Canvas Lab collection
* @param {string|Function} timing Timing function
* @param {number} period Period of timer
* @param {boolean} out Whether to bloom out, or in
*/
bloom ( collection, timing, period, out = true )
{
this._transitions = new Array;
let _result =
{
object: collection,
timing: timing,
period: period,
change: new Array
}
if ( out )
for ( let _i = 0; _i < collection.length; _i++ )
_result.change.push ( { pointFrom: collection [ 0 ].point } );
else
for ( let _i = 0; _i < collection.length; _i++ )
_result.change.push ( { point: collection [ 0 ].point } );
this._transitions.push ( _result );
return this._transitions;
}
/**
* Creates shape from array of numbers
* @public
* @function
* @param {Array} shape Array of collection indexes
* @param {ClCollection} collection Canvas Lab collection
* @param {string|Function} timing Timing function
* @param {number} period Period of timer
* @param {boolean} lineTos Draw lines connecting shapes
* @param {boolean} optimal Optimal line draws
*/
shape ( shape, collection, timing, period, lineTos = true, optimal = false )
{
let _shapeArray = new Array;
if ( ! shape )
{
for ( let _i = 0; _i < collection.length; _i++ )
_shapeArray.push ( Array.from ( { length: _i }, ( _, i ) => i + 1 ) );
_shapeArray = _shapeArray [ collection.length - 1 ];
}
else
_shapeArray = shape;
return this._shape ( _shapeArray, collection, timing, period, lineTos, optimal );
}
}
/**
* @class {Object} SacredCircles SacredCircles template
* @property {Point} point X & Y axis coordinates
* @property {number} [radius=25] Radius of circle
* @property {number} iterations Amount of iterations
* @property {Rgb|Stroke|Queue} strokes Stroke colors
* @property {Rgb|Fill|Queue} fills Fill colors
* @property {Queue} degrees Degrees for generation
* @property {Object} master Master object
*/
class SacredCircles
{
_point = new Point;
_radius = 25;
_iterations = undefined;
_degrees = new Queue ( [ 90, 330, 270, 210, 150, 90, 30 ] );
_strokes = new Rgb ( 0, 0, 0, 1 );
_fills = new Rgb ( 255, 255, 255, 0 );
_transitions = undefined;
_master = undefined;
#numbers = undefined;
#tangents = undefined;
#counter = -1; /* Counter to define the gaps between each circle: @see this.create ( ) */
/**
* Create a SacredCircles template
* @property {Point} point X & Y axis coordinates
* @property {number} [radius=25] Radius of circle
* @property {number} iterations Amount of iterations
* @property {Rgb|Stroke|Queue} strokes Stroke colors
* @property {Rgb|Fill|Queue} fills Fill colors
* @property {Queue} degrees Degrees for generation
*/
constructor ( point = { x: undefined, y: undefined }, radius, iterations, strokes, fills, degrees, transitions )
{
//// COMPOSITION ////////////////////////////
this._isCanvasLabObject = VALIDATION.isCanvasLabObject;
this._isNumber = VALIDATION.isNumber;
this._isPoint = VALIDATION.isPoint;
this._isTransition = VALIDATION.isTransition;
Object.defineProperty ( this, 'master', PROPERTY_BLOCKS.individual.master );
Object.defineProperty ( this, 'point', PROPERTY_BLOCKS.individual.point );
Object.defineProperty ( this, 'radius', PROPERTY_BLOCKS.individual.radius );
Object.defineProperty ( this, 'transitions', PROPERTY_BLOCKS.individual.transitions );
this.point = point;
this.radius = radius;
this.iterations = iterations;
this.strokes = strokes;
this.fills = fills;
this.degrees = degrees;
this.transitions = transitions;
this._tangents = iterations;
}
//// PROPERTIES //////////////////////////////////
//// [ POINT ] /////////////////////
/**
* Set point
* @public
* @function
* @param {Point} value X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
set point ( value ) { }
/**
* Get point
* @public
* @function
* @return {Point} X & Y coordinates
* @see {@link PROPERTY_BLOCKS.individual.point}
*/
get point ( ) { }
//// [ RADIUS ] ////////////////////
/**
* Set radius
* @public
* @function
* @param {number} value Radius of circle
* @see {@link PROPERTY_BLOCKS.individual.radius}
*/
set radius ( value ) { }
/**
* Get radius
* @readOnly
* @function
* @return {number} Radius of circle
* @see {@link PROPERTY_BLOCKS.individual.radius}
*/
get radius ( ) { }
//// [ ITERATIONS ] ////////////////
/**
* Set iterations value
* @public
* @function
* @param {number} value Number of iterations
*/
set iterations ( value )
{
this._iterations = ( this._isNumber ( value ) ) ? value : this._iterations;
}
/**
* Get iterations value
* @readOnly
* @function
* @return {number} Number of iterations
*/
get iterations ( )
{
return this._iterations;
}
//// [ DEGREES ] ///////////////////
/**
* Set degrees value
* @public
* @function
* @param {Array} value Array of degrees
*/
set degrees ( value )
{
this._degrees = ( Array.isArray ( value ) ) ? new Queue ( value ) : this._degrees;
}
/**
* Get degrees value
* @readOnly
* @function
* @return {Queue} Queue of degrees
*/
get degrees ( )
{
return this._degrees;
}
//// [ STROKES ] ///////////////////
/**
* Set strokes value
* @public
* @function
* @param {Array} value Array of strokes
*/
set strokes ( value )
{
if ( value != undefined )
{
switch ( value.constructor.name )
{
case 'Rgb':
this._strokes = new Queue ( new Array ( new Stroke ( value ) ) );
break;
case 'Stroke':
this._strokes = new Queue ( new Array ( value ) );
case 'Queue':
this._strokes = value;
default:
Array.isArray ( value )
{
let _result = new Array;
for ( let _item of value )
_result.push ( new Stroke ( _item ) );
this._strokes = new Queue ( _result );
}
}
}
else
this._strokes = this._strokes;
}
/**
* Get strokes value
* @readOnly
* @function
* @return {Queue} Queue of strokes
*/
get strokes ( )
{
return this._strokes;
}
//// [ FILLS ] /////////////////////
/**
* Set fills value
* @public
* @function
* @param {Array} value Array of fills
*/
set fills ( value )
{
if ( value != undefined )
{
switch ( value.constructor.name )
{
case 'Rgb':
this._fills = new Queue ( new Array ( new Fill ( value ) ) );
break;
case 'Fill':
this._fills = new Queue ( new Array ( _array ) );
case 'Queue':
this._fills = value;
default:
Array.isArray ( value )
{
let _result = new Array;
for ( let _item of value )
_result.push ( new Fill ( _item ) );
this._fills = new Queue ( _result );
}
}
}
else
this._fills = this._fills;
}
/**
* Get fills value
* @readOnly
* @function
* @return {Queue} Queue of fills
*/
get fills ( )
{
return this._fills;
}
//// [ TRANSITIONS ] ///////////////
/**
* Set transitions
* @public
* @function
* @param {Transitions} value Transitions object
* @see {@link PROPERTY_BLOCKS.individual.transitions}
*/
set transitions ( value ) { }
/**
* Get transitions
* @public
* @function
* @return {Transitions} Transitions object
* @see {@link PROPERTY_BLOCKS.individual.transitions}
*/
get transitions ( ) { }
//// [ MASTER ] ////////////////////
/**
* Set master object
* @public
* @function
* @param {clObject} value Canvas Lab object
* @see {@link PROPERTY_BLOCKS.individual.master}
*/
set master ( value ) { }
/**
* Get master object
* @public
* @function
* @return {clObject} Master Canvas Lab object
* @see {@link PROPERTY_BLOCKS.individual.master}
*/
get master ( ) { }
//// [ NUMBERS ] ///////////////////
/**
* Set numbers value
* @private
* @function
* @param {Array} value Array of numbers
*/
set _numbers ( value )
{
this.#numbers = ( Array.isArray ( value ) ) ? new Queue ( value ) : this.#numbers;
}
//// [ TANGENTS ] //////////////////
/**
* Set tangents value
* @private
* @function
* @param {number} value Number of iterations
*/
set _tangents ( value )
{
this.#tangents = ( Number.isInteger ( value ) ) ? this.#getTangents ( value ) : this.#tangents;
}
//// VALIDATION ////////////////////////////////////
/**
* Returns whether the passed value is a CanvasLab object; Line, Circle, Rectangle, Text
* @private
* @function
* @param {Object} value CanvasLab object; Line, Circle, Rectangle, Text
* @return {boolean} True || False
* @see {@link VALIDATION.isCanvasLabObject}
*/
_isCanvasLabObject ( ) { }
/**
* Returns whether the passed value is a Number value
* @private
* @function
* @param {number} value Number value
* @return {boolean} True || False
* @see {@link VALIDATION.isNumber}
*/
_isNumber ( ) { }
/**
* Returns whether the passed value is a Point
* @private
* @function
* @param {Object} value Point or object equivalent
* @return {boolean} True || False
* @see {@link VALIDATION.isPoint}
*/
_isPoint ( ) { }
/**
* Returns whether the passed value is a Transition
* @private
* @function
* @param {Transition} value Transition object
* @return {boolean} True || False
* @see {@link VALIDATION.isTransition}
*/
isTransition ( ) { }
//// UTILITIES ///////////////////////////////////
//// # PROTECTED ///////////////////
/**
* Returns a Circle object
* @protected
* @function
* @param {Point} point X & Y Coordinates
* @param {Stroke} stroke Stroke properties
* @param {Fill} fill Fill properties
* @return {Circle} Circle object
*/
#getCircle = ( point, stroke, fill ) => new Circle ( point, this.radius, undefined, new Stroke ( stroke.color, stroke.type, stroke.segments, stroke.width ), new Fill ( fill.color, fill.type ), undefined, undefined );
/**
* Returns a Ellipse object
* @protected
* @function
* @param {Point} point X & Y Coordinates
* @param {Stroke} stroke Stroke properties
* @param {Fill} fill Fill properties
* @return {Ellipse} Ellipse object
*/
#getEllipse = ( point, stroke, fill ) => new Ellipse ( point, new Point ( this.radius, this.radius * 0.5 ), undefined, new Stroke ( stroke.color, stroke.type, stroke.segments, stroke.width ), new Fill ( fill.color, fill.type ), undefined, undefined );
/**
* Returns a Rectangle object
* @protected
* @function
* @param {Point} point X & Y Coordinates
* @param {Stroke} stroke Stroke properties
* @param {Fill} fill Fill properties
* @return {Rectangle} Rectangle object
*/
#getRectangle = ( point, stroke, fill ) => new Rectangle ( point, undefined, undefined, new Stroke ( stroke.color, stroke.type, stroke.segments, stroke.width ), new Fill ( fill.color, fill.type ), undefined, undefined );
/**
* Returns a RoundedRectangle object
* @protected
* @function
* @param {Point} point X & Y Coordinates
* @param {Stroke} stroke Stroke properties
* @param {Fill} fill Fill properties
* @return {RoundedRectangle} Rounded rectangle object
*/
#getRoundedRectangle = ( point, stroke, fill ) => new RoundedRectangle ( point, undefined, undefined, new Stroke ( stroke.color, stroke.type, stroke.segments, stroke.width ), new Fill ( fill.color, fill.type ), undefined, undefined );
/**
* Returns a Text object
* @protected
* @function
* @param {Point} point X & Y Coordinates
* @param {Stroke} stroke Stroke properties
* @param {Fill} fill Fill properties
* @return {Text} Text object
*/
#getText = ( point, stroke, fill ) => new Text ( point, this.#numbers.next, undefined, undefined, undefined, undefined, undefined, new Stroke ( stroke.color, stroke.type, stroke.segments, stroke.width ), new Fill ( fill.color, fill.type ), undefined );
/**
* Returns a Line object
* @protected
* @function
* @param {Point} startPoint Starting point of line
* @param {Point} endPoint Ending point of line
* @return {Line} Line object
*/
#getLine = ( startPoint, endPoint ) => new Line ( startPoint, endPoint, undefined, undefined, undefined, undefined );
/**
* Insert initial object
* @protected
* @function
* @param {Point} point X & Y Coordinate(s)
* @param {number} degree Degree of movement
* @param {Stroke} stroke Stroke properties
* @param {Fill} fill Fill properties
* @param {number} iterator Current iterator
*/
#insertInitialObject ( point, iterator, degree, stroke, fill )
{
let _object = this._getObjectPerCollectionType ( point, stroke, fill );
this._moveObject ( _object, degree, this.radius * iterator );
this._setObjectPerCollectionType ( _object );
}
/**
* Insert Ensuing objects
* @protected
* @function
* @param {number} degree Degree of movement
* @param {Stroke} stroke Stroke properties
* @param {Fill} fill Fill properties
*/
#insertEnsuingObjects ( degree, stroke, fill )
{
let _object = this._getObjectPerCollectionType ( this.master.circles.endPoint, stroke, fill );
this._moveObject ( _object, degree, this.radius );
this._setObjectPerCollectionType ( _object );
}
/**
* Returns an array of all tangents for each iteration
* @protected
* @function
* @return {Array} Tangents for each iteration
*/
#getTangents ( )
{
let _results = new Array;
let _count = 0;
for ( let _i = 1; _i <= this.iterations; _i++ )
{
_results.push ( _count * 6 );
_count = _i + _count;
}
if ( this.iterations > 1 )
_results.shift ( );
return _results;
}
//// - PRIVATE /////////////////////
/**
* Returns a clObject based on the current collection type
* @private
* @function
* @param {Point} point X & Y Coordinates
* @param {Stroke} stroke Stroke properties
* @param {Fill} fill Fill properties
*/
_getObjectPerCollectionType ( point, stroke, fill )
{
let _collectionType = this.master.constructor.name;
let _result = undefined;
switch ( _collectionType )
{
case 'Circles': _result = this.#getCircle ( point, stroke, fill ); break;
case 'Ellipses': _result = this.#getEllipse ( point, stroke, fill ); break;
case 'Rectangles': _result = this.#getRectangle ( point, stroke, fill ); break;
case 'RoundedRectangles': _result = this.#getRoundedRectangle ( point, stroke, fill ); break;
case 'Texts': _result = this.#getText ( point, stroke, fill ); break;
case 'Group':
let _objectA = this.#getCircle ( point, stroke, fill );
let _objectB = this.#getEllipse ( point, stroke, fill );
let _objectC = this.#getRectangle ( point, stroke, fill );
let _objectD = this.#getRoundedRectangle ( point, stroke, fill );
let _objectE = this.#getText ( point, stroke, fill );
_result = new Array ( _objectA, _objectB, _objectC, _objectD, _objectE );
}
return _result;
}
/**
* Moves the passed object in a specific degree & distance
* @private
* @function
* @param {clObject} object Canvas Lab object
* @param {number} degree Degree to move
* @param {number} distance Distance to move
*/
_moveObject ( object, degree, distance )
{
if ( Array.isArray ( object ) )
for ( let _entry of object )
_entry.move ( degree, distance );
else
object.move ( degree, distance );
}
/**
* Pushes the passed clObject into the current collection type
* @private
* @function
* @param {clObject} object Canvas Lab object
*/
_setObjectPerCollectionType ( object )
{
let _collectionType = this.master.constructor.name;
switch ( _collectionType )
{
case 'Group':
for ( let _entry of object )
{
let _type = `${_entry.constructor.name.toLowerCase ( )}s`;
_type = ( _type === 'roundedrectangles' ) ? 'roundedRectangles' : _type;
this.master [ _type ].push ( _entry );
}
default:
this.master.push ( object );
break;
}
}
//// + PUBLIC //////////////////////
// /**
// * Returns transitions for hop animation
// * @public
// * @function
// * @param {clObject} object Canvas Lab object
// * @param {ClCollection} collection Canvas Lab collection
// * @param {string|Function} timing Timing function
// * @param {number} period Period of timer
// */
// getHopTransitions ( object, collection, timing, period )
// {
// let _transitions = new Array;
// for ( let _i = 0; _i < collection.length; _i++ )
// {
// let _result =
// {
// object: object,
// timing: timing,
// period: period,
// change:
// {
// point: new Point ( collection [ _i ].point.x, collection [ _i ].point.y ),
// cache: true,
// fillColor: collection [ _i ].fill.color
// }
// }
// _transitions.push ( _result );
// }
// return _transitions;
// }
// /**
// * Creates shape from array of numbers
// * @public
// * @function
// * @param {Array} shape Array of collection indexes
// * @param {ClCollection} collection Canvas Lab collection
// * @param {string|Function} timing Timing function
// * @param {number} period Period of timer
// */
// getSkipTransition ( shape, collection, timing, period )
// {
// let _transitions = new Array;
// for ( let _i = 0; _i < collection.length; _i++ )
// {
// let _object = new Circle ( canvaslab.center );
// let _result =
// {
// object: _object,
// timing: timing,
// period: period,
// change:
// {
// point: new Point ( collection [ _i ].point.x, collection [ _i ].point.y ),
// cache: true
// }
// }
// _transitions.push ( _result );
// }
// return _transitions;
// }
// /**
// * Creates shape from array of numbers
// * @public
// * @function
// * @param {Array} shape Array of collection indexes
// * @param {ClCollection} collection Canvas Lab collection
// * @param {string|Function} timing Timing function
// * @param {number} period Period of timer
// */
// getShapeTransition ( shape, collection, timing, period )
// {
// // @TODO: Pass in Array index of collection
// let _transitions = new Array;
// for ( let _i = 0; _i < collection.length; _i++ )
// {
// let _object = new Circle ( canvaslab.center );
// let _result =
// {
// object: _object,
// timing: timing,
// period: period,
// change:
// {
// point: new Point ( collection [ _i ].point.x, collection [ _i ].point.y ),
// cache: true
// }
// }
// _transitions.push ( _result );
// }
// return _transitions;
// }
/**
* Get number of total objects
* @public
* @function
* @return {number} Number of total objects
*/
get totalObjects ( )
{
if ( this.#tangents === undefined )
this.#tangents = this.#getTangents ( );
return this.#tangents [ this.#tangents.length - 1 ];
}
//// INITIALIZER /////////////////////////////////
/**
* Sets this template
* @public
* @function
*/
init ( )
{
this._numbers = Array.from ( { length: this.totalObjects }, ( element, index ) => index.toString ( ) );
for ( let _i = 0; _i < this.iterations; _i++ )
{
this.degrees.reset;
//// FOUNDATION STONE ////////////////////////////////////
let [ _degree, _stroke, _fill ] = [ this.degrees.next, this.strokes.next, this.fills.next ];
for ( let _stone = 0; _stone < 1; _stone++ )
this.#insertInitialObject ( this.point, _i, _degree, _stroke, _fill );
//// FILLER STONE(S) /////////////////////////////////////
[ _degree, _stroke, _fill ] = [ this.degrees.next, this.strokes.next, this.fills.next ]; // Number: 01, Degree: 150
for ( let _stone = 0; _stone <= ( _i - 1 ); _stone++ )
this.#insertEnsuingObjects ( _degree, _stroke, _fill );
[ _degree, _stroke, _fill ] = [ this.degrees.next, this.strokes.next, this.fills.next ]; // Number: 02, Degree: 90
for ( let _stone = 0; _stone <= ( _i - 1 ); _stone++ )
this.#insertEnsuingObjects ( _degree, _stroke, _fill );
[ _degree, _stroke, _fill ] = [ this.degrees.next, this.strokes.next, this.fills.next ]; // Number: 03, Degree: 30
for ( let _stone = 0; _stone <= ( _i - 1 ); _stone++ )
this.#insertEnsuingObjects ( _degree, _stroke, _fill );
[ _degree, _stroke, _fill ] = [ this.degrees.next, this.strokes.next, this.fills.next ]; // Number: 04, Degree: 330
for ( let _stone = 0; _stone <= ( _i - 1 ); _stone++ )
this.#insertEnsuingObjects ( _degree, _stroke, _fill );
[ _degree, _stroke, _fill ] = [ this.degrees.next, this.strokes.next, this.fills.next ]; // Number: 05, Degree: 270
for ( let _stone = 0; _stone <= ( _i - 1 ); _stone++ )
this.#insertEnsuingObjects ( _degree, _stroke, _fill );
//// KEYSTONE ////////////////////////////////////////////
[ _degree, _stroke, _fill ] = [ this.degrees.next, this.strokes.next, this.fills.next ]; // Number: 06, Degree: 210
for ( let _stone = 0; _stone <= ( _i - 2 ); _stone++ )
this.#insertEnsuingObjects ( _degree, _stroke, _fill );
}
//// REVERSE COLLECTIONS /////////////////
// this.master.circles.reverse ( );
// this.master.ellipses.reverse ( );
// this.master.rectangles.reverse ( );
// this.master.roundedRectangles.reverse ( );
}
}