/**
* Class that allows drawing a path using PIXI.Graphics, which can then be followed and used as a tween
*
* @class
* @memberof PIXI.tween
*/
export default class TweenPath extends PIXI.Graphics {
/**
*
*/
constructor() {
super();
/** @member {PIXI.Polygon} - PIXI object to use as the path */
this.polygon = new PIXI.Polygon();
this.polygon.closeStroke = false;
/**
* @member {boolean}
* Used to detect if the graphics object has changed.
* If this is set to true then the graphics object will be recalculated.
*/
this.dirty = true;
// assign a brightly coloured line style to use for debugging purposes.
// just add the tween path as a child of a container, and it will be visible.
const brightColor = Math.floor((0xffffff / 2)) + (Math.random() * 0xffffff / 2);
this.lineStyle(4, brightColor, 1, 0.5, false);
this._tmpPoint = new PIXI.Point();
this._tmpPoint2 = new PIXI.Point();
this._tmpDistance = [];
}
/**
* The number of points along the path
*
* @member {number}
* @readonly
*/
get length() {
return (this.polygon.points.length) ? (this.polygon.points.length / 2) + ((this.polygon.closeStroke) ? 1 : 0) : 0;
}
/**
* Clear the path
*
* @returns {PIXI.tween.TweenPath} - This instance of TweenPath
*/
clear() {
super.clear();
this.polygon.points.length = 0;
this.polygon.closeStroke = false;
this.dirty = false;
return this;
}
/**
* Finds the distance between two points
*
* @param {number} num1 - First point
* @param {number} num2 - Second point
* @returns {number} - Distance in pixels
*/
distanceBetween(num1, num2) {
this.parsePoints();
const { x: p1X, y: p1Y } = this.getPoint(num1);
const { x: p2X, y: p2Y } = this.getPoint(num2);
const dx = p2X - p1X;
const dy = p2Y - p1Y;
return Math.sqrt((dx * dx) + (dy * dy));
}
/**
* Finds the nth point along the path
*
* @param {number} num - Point
* @returns {PIXI.Point} - Point co-ordinates
*/
getPoint(num) {
this.parsePoints();
const len = (this.polygon.closeStroke && num >= this.length - 1) ? 0 : num * 2;
this._tmpPoint.set(this.polygon.points[len], this.polygon.points[len + 1]);
return this._tmpPoint;
}
/**
* Finds the nth point along the path
*
* @param {number} num - Point
* @returns {PIXI.Point} - Point co-ordinates
*/
getPointAt(num) {
this.parsePoints();
if (num > this.length) {
return this.getPoint(this.length - 1);
}
if (num % 1 === 0) {
return this.getPoint(num);
}
this._tmpPoint2.set(0, 0);
const diff = num % 1;
const { x: ceilX, y: ceilY } = this.getPoint(Math.ceil(num));
const { x: floorX, y: floorY } = this.getPoint(Math.floor(num));
const xx = -((floorX - ceilX) * diff);
const yy = -((floorY - ceilY) * diff);
this._tmpPoint2.set(floorX + xx, floorY + yy);
return this._tmpPoint2;
}
/**
* Finds the nearest point for the distance to be travelled
*
* @param {number} distance - how far to travel
* @returns {PIXI.Point} - Point co-ordinates
*/
getPointAtDistance(distance) {
this.parsePoints();
if (!this._tmpDistance) {
this.totalDistance();
}
const len = this._tmpDistance.length;
let n = 0;
const totalDistance = this._tmpDistance[this._tmpDistance.length - 1];
if (distance < 0) {
distance = totalDistance + distance;
} else if (distance > totalDistance) {
distance = distance - totalDistance;
}
for (let i = 0; i < len; ++i) {
if (distance >= this._tmpDistance[i]) {
n = i;
}
if (distance < this._tmpDistance[i]) {break;}
}
if (n === this.length - 1) {
return this.getPointAt(n);
}
const diff1 = distance - this._tmpDistance[n];
const diff2 = this._tmpDistance[n + 1] - this._tmpDistance[n];
return this.getPointAt(n + (diff1 / diff2));
}
/**
* Parse the list of points from the path
*
* @returns {PIXI.tween.TweenPath} - This instance of TweenPath
*/
parsePoints() {
if (!this.dirty) {
return this;
}
this.dirty = false;
this.polygon.points.length = 0;
this.finishPoly();
for (let i = 0; i < this.geometry.graphicsData.length; ++i) {
const shape = this.geometry.graphicsData[i].shape;
if (shape && shape.points) {
this.polygon.points = this.polygon.points.concat(shape.points);
}
}
return this;
}
/**
* Calculates the total distance along the entire path
*
* @returns {number} - Distance in pixels
*/
totalDistance() {
this.parsePoints();
this._tmpDistance.length = 0;
this._tmpDistance.push(0);
const len = this.length;
let distance = 0;
for (let i = 0; i < len - 1; ++i) {
distance += this.distanceBetween(i, i + 1);
this._tmpDistance.push(distance);
}
return distance;
}
/**
* The arc method creates an arc/curve (used to create circles, or parts of circles).
*
* @param {number} cx - The x-coordinate of the center of the circle
* @param {number} cy - The y-coordinate of the center of the circle
* @param {number} radius - The radius of the circle
* @param {number} startAngle - The starting angle, in radians (0 is at the 3 o'clock position
* of the arc's circle)
* @param {number} endAngle - The ending angle, in radians
* @param {boolean} [anticlockwise=false] - Specifies whether the drawing should be
* counter-clockwise or clockwise. False is default, and indicates clockwise, while true
* indicates counter-clockwise.
* @returns {PIXI.tween.TweenPath} - This instance of TweenPath
*/
arc(cx, cy, radius, startAngle, endAngle, anticlockwise) {
super.arc(cx, cy, radius, startAngle, endAngle, anticlockwise);
this.dirty = true;
return this;
}
/**
* The arcTo() method creates an arc/curve between two tangents on the canvas.
*
* "borrowed" from https://code.google.com/p/fxcanvas/ - thanks google!
*
* @param {number} x1 - The x-coordinate of the beginning of the arc
* @param {number} y1 - The y-coordinate of the beginning of the arc
* @param {number} x2 - The x-coordinate of the end of the arc
* @param {number} y2 - The y-coordinate of the end of the arc
* @param {number} radius - The radius of the arc
* @returns {PIXI.tween.TweenPath} - This instance of TweenPath
*/
arcTo(x1, y1, x2, y2, radius) {
super.arcTo(x1, y1, x2, y2, radius);
this.dirty = true;
return this;
}
/**
* Calculate the points for a bezier curve and then draws it.
*
* @param {number} cpX - Control point x
* @param {number} cpY - Control point y
* @param {number} cpX2 - Second Control point x
* @param {number} cpY2 - Second Control point y
* @param {number} toX - Destination point x
* @param {number} toY - Destination point y
* @returns {PIXI.tween.TweenPath} - This instance of TweenPath
*/
bezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY) {
super.bezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY);
this.dirty = true;
return this;
}
/**
* Draws a circle.
*
* @param {number} x - The X coordinate of the center of the circle
* @param {number} y - The Y coordinate of the center of the circle
* @param {number} radius - The radius of the circle
* @returns {PIXI.tween.TweenPath} - This instance of TweenPath
*/
drawCircle(x, y, radius) {
super.drawCircle(x, y, radius);
this.dirty = true;
return this;
}
/**
* Draws an ellipse.
*
* @param {number} x - The X coordinate of the center of the ellipse
* @param {number} y - The Y coordinate of the center of the ellipse
* @param {number} width - The half width of the ellipse
* @param {number} height - The half height of the ellipse
* @returns {PIXI.tween.TweenPath} - This instance of TweenPath
*/
drawEllipse(x, y, width, height) {
super.drawEllipse(x, y, width, height);
this.dirty = true;
return this;
}
/**
* Draws a rectangle shape.
*
* @param {number} x - The X coord of the top-left of the rectangle
* @param {number} y - The Y coord of the top-left of the rectangle
* @param {number} width - The width of the rectangle
* @param {number} height - The height of the rectangle
* @returns {PIXI.tween.TweenPath} - This instance of TweenPath
*/
drawRect(x, y, width, height) {
super.drawRect(x, y, width, height);
this.dirty = true;
return this;
}
/**
* Draw a rectangle shape with rounded/beveled corners.
*
* @param {number} x - The X coord of the top-left of the rectangle
* @param {number} y - The Y coord of the top-left of the rectangle
* @param {number} width - The width of the rectangle
* @param {number} height - The height of the rectangle
* @param {number} radius - Radius of the rectangle corners
* @returns {PIXI.tween.TweenPath} - This instance of TweenPath
*/
drawRoundedRect(x, y, width, height, radius) {
super.drawRoundedRect(x, y, width, height, radius);
this.dirty = true;
return this;
}
/**
* Draws a polygon using the given path.
*
* @param {number[]|PIXI.Point[]|PIXI.Polygon} path - The path data used to construct the polygon.
* @returns {PIXI.tween.TweenPath} - This instance of TweenPath
*/
drawPolygon(path) {
super.drawPolygon(path);
this.dirty = true;
return this;
}
/**
* Draws the given shape to this TweenPath object. Can be any of Circle, Rectangle, Ellipse, Line or Polygon.
*
* @param {PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.Rectangle|PIXI.RoundedRectangle} shape - Shape to draw
* @returns {PIXI.tween.TweenPath} - This instance of TweenPath
*/
drawShape(shape) {
super.drawShape(shape);
this.dirty = true;
return this;
}
/**
* Draw a star shape with an arbitrary number of points.
*
* @param {number} x - Center X position of the star
* @param {number} y - Center Y position of the star
* @param {number} points - The number of points of the star, must be > 1
* @param {number} radius - The outer radius of the star
* @param {number} [innerRadius] - The inner radius between points, default half `radius`
* @param {number} [rotation=0] - The rotation of the star in radians, where 0 is vertical
* @returns {PIXI.tween.TweenPath} - This instance of TweenPath
*/
drawStar(x, y, points, radius, innerRadius, rotation = 0) {
super.drawStar(x, y, points, radius, innerRadius, rotation);
this.dirty = true;
return this;
}
/**
* Draws a line using the current line style from the current drawing position to (x, y);
* The current drawing position is then set to (x, y).
*
* @param {number} x - the X coordinate to draw to
* @param {number} y - the Y coordinate to draw to
* @returns {PIXI.tween.TweenPath} - This instance of TweenPath
*/
lineTo(x, y) {
super.lineTo(x, y);
this.dirty = true;
return this;
}
/**
* Moves the current drawing position to x, y.
*
* @param {number} x - the X coordinate to move to
* @param {number} y - the Y coordinate to move to
* @returns {PIXI.tween.TweenPath} - This instance of TweenPath
*/
moveTo(x, y) {
super.moveTo(x, y);
this.dirty = true;
return this;
}
/**
* Calculate the points for a quadratic bezier curve and then draws it.
* Based on: https://stackoverflow.com/questions/785097/how-do-i-implement-a-bezier-curve-in-c
*
* @param {number} cpX - Control point x
* @param {number} cpY - Control point y
* @param {number} toX - Destination point x
* @param {number} toY - Destination point y
* @returns {PIXI.tween.TweenPath} - This instance of TweenPath
*/
quadraticCurveTo(cpX, cpY, toX, toY) {
super.quadraticCurveTo(cpX, cpY, toX, toY);
this.dirty = true;
return this;
}
}