API Docs for: 0.1.5
Show:

File: src\hypotrochoid.js

/**
 * A float threshold.
 *
 * @property EPSILON
 * @type Number
 * @private
 * @static
 * @final
 */

var EPSILON = 0.0001;

/**
 * Checks if two float values are (almost) equal.
 *
 * @method equal
 * @private
 * @static
 * @param {Number} a - Value a.
 * @param {Number} b - Value b.
 * @return {Boolean} Whether the values are equal or not.
 */

function equal(a, b) {

	return Math.abs(a - b) <= EPSILON;

}

/**
 * Hypotrochoid.
 * http://en.wikipedia.org/wiki/Hypotrochoid
 *
 * @class Hypotrochoid
 * @constructor
 * @param {Object} [options] - The settings.
 * @param {Number} [options.R] - Radius of the outer circle.
 * @param {Number} [options.r] - Radius of the inner circle.
 * @param {Number} [options.d=0.0] - Distance from the center to the inner circle.
 * @param {Object} [options.origin={x: 0.0, y: 0.0}] - Object with x and y components, representing the origin coordinates.
 * @param {Number} [options.rotation=0.0] - Sets the rotational direction and speed. (Negative for left rotation, 0.0 for no rotation.)
 * @param {Number} [options.iterations=64] - Limits the processing of very detailed hypotrochoids.
 * @param {Number} [options.opacity=0.75] - The opacity.
 * @param {Number} [options.lineWidth=0.5] - The line width.
 * @param {Boolean} [options.colourRoll=true] - Whether the colour should change continuously.
 * @param {Number} [options.hue=0.0] - The hue in degree.
 * @param {Number} [options.saturation=100.0] - The saturation in percent.
 * @param {Number} [options.luminance=50.0] - The luminance in percent.
 */

export default function Hypotrochoid(options) {

	/**
	 * Pi * 2.
	 *
	 * @property TWO_PI
	 * @type Number
	 * @private
	 * @final
	 */

	this.TWO_PI = Math.PI * 2;

	/**
	 * Current rotation.
	 *
	 * @property rotation
	 * @type Number
	 * @private
	 */

	this.rotation = 0.0;

	/**
	 * Incremented by step, used to draw the hypotrochoid.
	 *
	 * @property theta
	 * @type Number
	 * @private
	 */

	this.theta = 0.0;

	/**
	 * Theta is incremented by step.
	 *
	 * @property step
	 * @type Number
	 * @private
	 */

	this.step = 360.0 * Math.PI / 180.0;

	/**
	 * The first 2D-point in the draw process.
	 *
	 * @property firstCoord
	 * @type Object
	 * @private
	 */

	this.firstCoord = {x: 0.0, y: 0.0};

	/**
	 * The previous 2D-point in the draw process.
	 *
	 * @property firstCoord
	 * @type Object
	 * @private
	 */

	this.prevCoord = {x: 0.0, y: 0.0};

	/**
	 * r.
	 *
	 * @property r
	 * @type Number
	 */

	this.r = Math.random() * 2.0 + EPSILON;

	/**
	 * R.
	 *
	 * @property R
	 * @type Number
	 */

	this.R = this.r + Math.random() * 2.0 + EPSILON;

	/**
	 * d.
	 *
	 * @property d
	 * @type Number
	 */

	this.d = 0.0;

	/**
	 * Maximum iteration count.
	 *
	 * @property iterations
	 * @type Number
	 */

	this.iterations = (Math.random() * 300 + 3) | 0;

	/**
	 * Rotation speed and direction.
	 *
	 * @property rotationSpeed
	 * @type Number
	 */

	this.rotationSpeed = Math.random() * 0.05 - Math.random() * 0.05;

	/**
	 * The origin of the hypotrochoid.
	 *
	 * @property origin
	 * @type Object
	 */

	this.origin = {x: 0.0, y: 0.0};

	/**
	 * the current opacity.
	 *
	 * @property opacity
	 * @type Object
	 */

	this.opacity = 0.75;

	/**
	 * The line width.
	 *
	 * @property lineWidth
	 * @type Number
	 */

	this.lineWidth = 0.5;

	/**
	 * Colour roll flag.
	 *
	 * @property colourRoll
	 * @type Boolean
	 */

	this.colourRoll = true;

	/**
	 * The current hue.
	 *
	 * @property hue
	 * @type Number
	 */

	this.hue = 0.0;

	/**
	 * The current saturation.
	 *
	 * @property saturation
	 * @type Number
	 */

	this.saturation = 100.0;

	/**
	 * The current limunance.
	 *
	 * @property luminance
	 * @type Number
	 */

	this.luminance = 50.0;

	// Overwrite the defaults.
	this.setting = options;

	/**
	 * The inverted r value.
	 *
	 * @property rInv
	 * @type Number
	 * @private
	 */

	this.rInv = 1.0 / this.r;

}

/**
 * The hypotrochoid's settings: {
 *		r,
 *		R,
 *		d,
 *		iterations,
 *		rotation,
 *		origin,
 *		opacity,
 *		lineWidth,
 *		colourRoll,
 *		saturation,
 *		luminance,
 *		hue
 * }
 *
 * @property settings
 * @type Object
 */

Object.defineProperty(Hypotrochoid.prototype, "settings", {

	get: function() {

		return {
			r: this.r,
			R: this.R,
			d: this.d,
			iterations: this.iterations,
			rotation: this.rotationSpeed,
			origin: this.origin,
			opacity: this.opacity,
			lineWidth: this.lineWidth,
			colourRoll: this.colourRoll,
			saturation: this.saturation,
			luminance: this.luminance,
			hue: this.hue
		};

	},

	set: function(options) {

		if(options !== undefined) {

			if(options.r !== undefined) { this.r = options.r; }
			if(options.R !== undefined) { this.R = options.R; }
			if(options.d !== undefined) { this.d = options.d; }
			if(options.iterations !== undefined) { this.iterations = options.iterations; }
			if(options.rotation !== undefined) { this.rotationSpeed = options.rotation; }
			if(options.origin !== undefined) { this.origin = options.origin; }
			if(options.opacity !== undefined) { this.opacity = options.opacity; }
			if(options.lineWidth !== undefined) { this.lineWidth = options.lineWidth; }
			if(options.colourRoll !== undefined) { this.colourRoll = options.colourRoll; }
			if(options.saturation !== undefined) { this.saturation = options.saturation; }
			if(options.luminance !== undefined) { this.luminance = options.luminance; }
			if(options.hue !== undefined) { this.hue = options.hue; }

		}

	}

});

/**
 * Updates hue and rotation.
 *
 * @method update
 */

Hypotrochoid.prototype.update = function() {

	if(this.colourRoll) {

		this.hue -= 0.5;
		if(this.hue <= -360.0) { this.hue += 360.0; }

	}

	if(!equal(this.rotationSpeed, 0.0)) {

		this.rotation -= this.rotationSpeed;
		if(Math.abs(this.rotation) >= this.TWO_PI) { this.rotation -= this.TWO_PI; }

	}

};

/**
 * Draws the hypotrochoid onto the given 2D-context.
 * This function does not clear the canvas.
 *
 * @method draw
 * @param {CanvasRenderingContext2D} ctx - The surface to draw on.
 */

Hypotrochoid.prototype.draw = function(ctx) {

	var i, q, x = 0.0, y = 0.0, bypass = true;

	ctx.save();

	ctx.lineWidth = this.lineWidth;
	ctx.lineCap = "round";
	ctx.globalCompositeOperation = "source-over";

	this.theta = 0.0;
	this.prevCoord.x = 0.0;
	i = this.iterations;

	ctx.beginPath();

	while(i >= 0 && (bypass || !equal(this.firstCoord.x, x) || !equal(this.firstCoord.y, y))) {

		if(bypass) { bypass = false; }
		this.theta += this.step;

		q = (this.r / this.R - 1.0) * this.theta; 
		x = (this.r - this.R) * Math.cos(this.theta) + this.d * Math.cos(q + this.rotation) + this.origin.x + (this.R - this.r);
		y = (this.r - this.R) * Math.sin(this.theta) - this.d * Math.sin(q + this.rotation) + this.origin.y;

		if(this.prevCoord.x) {

			ctx.moveTo(this.prevCoord.x, this.prevCoord.y);
			ctx.lineTo(x, y);

		} else {

			this.firstCoord.x = x;
			this.firstCoord.y = y;
			bypass = true;

		}

		this.prevCoord.x = x;
		this.prevCoord.y = y;
		--i;

	}

	ctx.strokeStyle = "hsla(" + this.hue + ", " + this.saturation + "%, " + this.luminance + "%, " + this.opacity + ")";
	ctx.stroke();

	ctx.restore();

};