mirror of
https://github.com/photonstorm/phaser
synced 2024-12-25 04:23:30 +00:00
5911b94d35
This reverts commit 854589ddbe
.
woops
614 lines
17 KiB
JavaScript
614 lines
17 KiB
JavaScript
/**
|
|
* @author Richard Davey <rich@photonstorm.com>
|
|
* @copyright 2020 Photon Storm Ltd.
|
|
* @license {@link https://opensource.org/licenses/MIT|MIT License}
|
|
*/
|
|
|
|
var Class = require('../utils/Class');
|
|
var FromPoints = require('../geom/rectangle/FromPoints');
|
|
var Rectangle = require('../geom/rectangle/Rectangle');
|
|
var Vector2 = require('../math/Vector2');
|
|
|
|
/**
|
|
* @classdesc
|
|
* A Base Curve class, which all other curve types extend.
|
|
*
|
|
* Based on the three.js Curve classes created by [zz85](http://www.lab4games.net/zz85/blog)
|
|
*
|
|
* @class Curve
|
|
* @memberof Phaser.Curves
|
|
* @constructor
|
|
* @since 3.0.0
|
|
*
|
|
* @param {string} type - The curve type.
|
|
*/
|
|
var Curve = new Class({
|
|
|
|
initialize:
|
|
|
|
function Curve (type)
|
|
{
|
|
/**
|
|
* String based identifier for the type of curve.
|
|
*
|
|
* @name Phaser.Curves.Curve#type
|
|
* @type {string}
|
|
* @since 3.0.0
|
|
*/
|
|
this.type = type;
|
|
|
|
/**
|
|
* The default number of divisions within the curve.
|
|
*
|
|
* @name Phaser.Curves.Curve#defaultDivisions
|
|
* @type {integer}
|
|
* @default 5
|
|
* @since 3.0.0
|
|
*/
|
|
this.defaultDivisions = 5;
|
|
|
|
/**
|
|
* The quantity of arc length divisions within the curve.
|
|
*
|
|
* @name Phaser.Curves.Curve#arcLengthDivisions
|
|
* @type {integer}
|
|
* @default 100
|
|
* @since 3.0.0
|
|
*/
|
|
this.arcLengthDivisions = 100;
|
|
|
|
/**
|
|
* An array of cached arc length values.
|
|
*
|
|
* @name Phaser.Curves.Curve#cacheArcLengths
|
|
* @type {number[]}
|
|
* @default []
|
|
* @since 3.0.0
|
|
*/
|
|
this.cacheArcLengths = [];
|
|
|
|
/**
|
|
* Does the data of this curve need updating?
|
|
*
|
|
* @name Phaser.Curves.Curve#needsUpdate
|
|
* @type {boolean}
|
|
* @default true
|
|
* @since 3.0.0
|
|
*/
|
|
this.needsUpdate = true;
|
|
|
|
/**
|
|
* For a curve on a Path, `false` means the Path will ignore this curve.
|
|
*
|
|
* @name Phaser.Curves.Curve#active
|
|
* @type {boolean}
|
|
* @default true
|
|
* @since 3.0.0
|
|
*/
|
|
this.active = true;
|
|
|
|
/**
|
|
* A temporary calculation Vector.
|
|
*
|
|
* @name Phaser.Curves.Curve#_tmpVec2A
|
|
* @type {Phaser.Math.Vector2}
|
|
* @private
|
|
* @since 3.0.0
|
|
*/
|
|
this._tmpVec2A = new Vector2();
|
|
|
|
/**
|
|
* A temporary calculation Vector.
|
|
*
|
|
* @name Phaser.Curves.Curve#_tmpVec2B
|
|
* @type {Phaser.Math.Vector2}
|
|
* @private
|
|
* @since 3.0.0
|
|
*/
|
|
this._tmpVec2B = new Vector2();
|
|
},
|
|
|
|
/**
|
|
* Draws this curve on the given Graphics object.
|
|
*
|
|
* The curve is drawn using `Graphics.strokePoints` so will be drawn at whatever the present Graphics stroke color is.
|
|
* The Graphics object is not cleared before the draw, so the curve will appear on-top of anything else already rendered to it.
|
|
*
|
|
* @method Phaser.Curves.Curve#draw
|
|
* @since 3.0.0
|
|
*
|
|
* @generic {Phaser.GameObjects.Graphics} G - [graphics,$return]
|
|
*
|
|
* @param {Phaser.GameObjects.Graphics} graphics - The Graphics instance onto which this curve will be drawn.
|
|
* @param {integer} [pointsTotal=32] - The resolution of the curve. The higher the value the smoother it will render, at the cost of rendering performance.
|
|
*
|
|
* @return {Phaser.GameObjects.Graphics} The Graphics object to which the curve was drawn.
|
|
*/
|
|
draw: function (graphics, pointsTotal)
|
|
{
|
|
if (pointsTotal === undefined) { pointsTotal = 32; }
|
|
|
|
// So you can chain graphics calls
|
|
return graphics.strokePoints(this.getPoints(pointsTotal));
|
|
},
|
|
|
|
/**
|
|
* Returns a Rectangle where the position and dimensions match the bounds of this Curve.
|
|
*
|
|
* You can control the accuracy of the bounds. The value given is used to work out how many points
|
|
* to plot across the curve. Higher values are more accurate at the cost of calculation speed.
|
|
*
|
|
* @method Phaser.Curves.Curve#getBounds
|
|
* @since 3.0.0
|
|
*
|
|
* @param {Phaser.Geom.Rectangle} [out] - The Rectangle to store the bounds in. If falsey a new object will be created.
|
|
* @param {integer} [accuracy=16] - The accuracy of the bounds calculations.
|
|
*
|
|
* @return {Phaser.Geom.Rectangle} A Rectangle object holding the bounds of this curve. If `out` was given it will be this object.
|
|
*/
|
|
getBounds: function (out, accuracy)
|
|
{
|
|
if (!out) { out = new Rectangle(); }
|
|
if (accuracy === undefined) { accuracy = 16; }
|
|
|
|
var len = this.getLength();
|
|
|
|
if (accuracy > len)
|
|
{
|
|
accuracy = len / 2;
|
|
}
|
|
|
|
// The length of the curve in pixels
|
|
// So we'll have 1 spaced point per 'accuracy' pixels
|
|
|
|
var spaced = Math.max(1, Math.round(len / accuracy));
|
|
|
|
return FromPoints(this.getSpacedPoints(spaced), out);
|
|
},
|
|
|
|
/**
|
|
* Returns an array of points, spaced out X distance pixels apart.
|
|
* The smaller the distance, the larger the array will be.
|
|
*
|
|
* @method Phaser.Curves.Curve#getDistancePoints
|
|
* @since 3.0.0
|
|
*
|
|
* @param {integer} distance - The distance, in pixels, between each point along the curve.
|
|
*
|
|
* @return {Phaser.Geom.Point[]} An Array of Point objects.
|
|
*/
|
|
getDistancePoints: function (distance)
|
|
{
|
|
var len = this.getLength();
|
|
|
|
var spaced = Math.max(1, len / distance);
|
|
|
|
return this.getSpacedPoints(spaced);
|
|
},
|
|
|
|
/**
|
|
* Get a point at the end of the curve.
|
|
*
|
|
* @method Phaser.Curves.Curve#getEndPoint
|
|
* @since 3.0.0
|
|
*
|
|
* @param {Phaser.Math.Vector2} [out] - Optional Vector object to store the result in.
|
|
*
|
|
* @return {Phaser.Math.Vector2} Vector2 containing the coordinates of the curves end point.
|
|
*/
|
|
getEndPoint: function (out)
|
|
{
|
|
if (out === undefined) { out = new Vector2(); }
|
|
|
|
return this.getPointAt(1, out);
|
|
},
|
|
|
|
/**
|
|
* Get total curve arc length
|
|
*
|
|
* @method Phaser.Curves.Curve#getLength
|
|
* @since 3.0.0
|
|
*
|
|
* @return {number} The total length of the curve.
|
|
*/
|
|
getLength: function ()
|
|
{
|
|
var lengths = this.getLengths();
|
|
|
|
return lengths[lengths.length - 1];
|
|
},
|
|
|
|
|
|
/**
|
|
* Get a list of cumulative segment lengths.
|
|
*
|
|
* These lengths are
|
|
*
|
|
* - [0] 0
|
|
* - [1] The first segment
|
|
* - [2] The first and second segment
|
|
* - ...
|
|
* - [divisions] All segments
|
|
*
|
|
* @method Phaser.Curves.Curve#getLengths
|
|
* @since 3.0.0
|
|
*
|
|
* @param {integer} [divisions] - The number of divisions or segments.
|
|
*
|
|
* @return {number[]} An array of cumulative lengths.
|
|
*/
|
|
getLengths: function (divisions)
|
|
{
|
|
if (divisions === undefined) { divisions = this.arcLengthDivisions; }
|
|
|
|
if ((this.cacheArcLengths.length === divisions + 1) && !this.needsUpdate)
|
|
{
|
|
return this.cacheArcLengths;
|
|
}
|
|
|
|
this.needsUpdate = false;
|
|
|
|
var cache = [];
|
|
var current;
|
|
var last = this.getPoint(0, this._tmpVec2A);
|
|
var sum = 0;
|
|
|
|
cache.push(0);
|
|
|
|
for (var p = 1; p <= divisions; p++)
|
|
{
|
|
current = this.getPoint(p / divisions, this._tmpVec2B);
|
|
|
|
sum += current.distance(last);
|
|
|
|
cache.push(sum);
|
|
|
|
last.copy(current);
|
|
}
|
|
|
|
this.cacheArcLengths = cache;
|
|
|
|
return cache; // { sums: cache, sum:sum }; Sum is in the last element.
|
|
},
|
|
|
|
// Get point at relative position in curve according to arc length
|
|
|
|
// - u [0 .. 1]
|
|
|
|
/**
|
|
* Get a point at a relative position on the curve, by arc length.
|
|
*
|
|
* @method Phaser.Curves.Curve#getPointAt
|
|
* @since 3.0.0
|
|
*
|
|
* @generic {Phaser.Math.Vector2} O - [out,$return]
|
|
*
|
|
* @param {number} u - The relative position, [0..1].
|
|
* @param {Phaser.Math.Vector2} [out] - A point to store the result in.
|
|
*
|
|
* @return {Phaser.Math.Vector2} The point.
|
|
*/
|
|
getPointAt: function (u, out)
|
|
{
|
|
var t = this.getUtoTmapping(u);
|
|
|
|
return this.getPoint(t, out);
|
|
},
|
|
|
|
// Get sequence of points using getPoint( t )
|
|
|
|
/**
|
|
* Get a sequence of evenly spaced points from the curve.
|
|
*
|
|
* You can pass `divisions`, `stepRate`, or neither.
|
|
*
|
|
* The number of divisions will be
|
|
*
|
|
* 1. `divisions`, if `divisions` > 0; or
|
|
* 2. `this.getLength / stepRate`, if `stepRate` > 0; or
|
|
* 3. `this.defaultDivisions`
|
|
*
|
|
* `1 + divisions` points will be returned.
|
|
*
|
|
* @method Phaser.Curves.Curve#getPoints
|
|
* @since 3.0.0
|
|
*
|
|
* @generic {Phaser.Math.Vector2[]} O - [out,$return]
|
|
*
|
|
* @param {integer} [divisions] - The number of divisions to make.
|
|
* @param {number} [stepRate] - The curve distance between points, implying `divisions`.
|
|
* @param {(array|Phaser.Math.Vector2[])} [out] - An optional array to store the points in.
|
|
*
|
|
* @return {(array|Phaser.Math.Vector2[])} An array of Points from the curve.
|
|
*/
|
|
getPoints: function (divisions, stepRate, out)
|
|
{
|
|
if (out === undefined) { out = []; }
|
|
|
|
// If divisions is a falsey value (false, null, 0, undefined, etc) then we calculate it based on the stepRate instead.
|
|
if (!divisions)
|
|
{
|
|
if (!stepRate)
|
|
{
|
|
divisions = this.defaultDivisions;
|
|
}
|
|
else
|
|
{
|
|
divisions = this.getLength() / stepRate;
|
|
}
|
|
}
|
|
|
|
for (var d = 0; d <= divisions; d++)
|
|
{
|
|
out.push(this.getPoint(d / divisions));
|
|
}
|
|
|
|
return out;
|
|
},
|
|
|
|
/**
|
|
* Get a random point from the curve.
|
|
*
|
|
* @method Phaser.Curves.Curve#getRandomPoint
|
|
* @since 3.0.0
|
|
*
|
|
* @generic {Phaser.Math.Vector2} O - [out,$return]
|
|
*
|
|
* @param {Phaser.Math.Vector2} [out] - A point object to store the result in.
|
|
*
|
|
* @return {Phaser.Math.Vector2} The point.
|
|
*/
|
|
getRandomPoint: function (out)
|
|
{
|
|
if (out === undefined) { out = new Vector2(); }
|
|
|
|
return this.getPoint(Math.random(), out);
|
|
},
|
|
|
|
// Get sequence of points using getPointAt( u )
|
|
|
|
/**
|
|
* Get a sequence of equally spaced points (by arc distance) from the curve.
|
|
*
|
|
* `1 + divisions` points will be returned.
|
|
*
|
|
* @method Phaser.Curves.Curve#getSpacedPoints
|
|
* @since 3.0.0
|
|
*
|
|
* @param {integer} [divisions=this.defaultDivisions] - The number of divisions to make.
|
|
* @param {number} [stepRate] - Step between points. Used to calculate the number of points to return when divisions is falsy. Ignored if divisions is positive.
|
|
* @param {(array|Phaser.Math.Vector2[])} [out] - An optional array to store the points in.
|
|
*
|
|
* @return {Phaser.Math.Vector2[]} An array of points.
|
|
*/
|
|
getSpacedPoints: function (divisions, stepRate, out)
|
|
{
|
|
if (out === undefined) { out = []; }
|
|
|
|
// If divisions is a falsey value (false, null, 0, undefined, etc) then we calculate it based on the stepRate instead.
|
|
if (!divisions)
|
|
{
|
|
if (!stepRate)
|
|
{
|
|
divisions = this.defaultDivisions;
|
|
}
|
|
else
|
|
{
|
|
divisions = this.getLength() / stepRate;
|
|
}
|
|
}
|
|
|
|
for (var d = 0; d <= divisions; d++)
|
|
{
|
|
var t = this.getUtoTmapping(d / divisions, null, divisions);
|
|
|
|
out.push(this.getPoint(t));
|
|
}
|
|
|
|
return out;
|
|
},
|
|
|
|
/**
|
|
* Get a point at the start of the curve.
|
|
*
|
|
* @method Phaser.Curves.Curve#getStartPoint
|
|
* @since 3.0.0
|
|
*
|
|
* @generic {Phaser.Math.Vector2} O - [out,$return]
|
|
*
|
|
* @param {Phaser.Math.Vector2} [out] - A point to store the result in.
|
|
*
|
|
* @return {Phaser.Math.Vector2} The point.
|
|
*/
|
|
getStartPoint: function (out)
|
|
{
|
|
if (out === undefined) { out = new Vector2(); }
|
|
|
|
return this.getPointAt(0, out);
|
|
},
|
|
|
|
/**
|
|
* Get a unit vector tangent at a relative position on the curve.
|
|
* In case any sub curve does not implement its tangent derivation,
|
|
* 2 points a small delta apart will be used to find its gradient
|
|
* which seems to give a reasonable approximation
|
|
*
|
|
* @method Phaser.Curves.Curve#getTangent
|
|
* @since 3.0.0
|
|
*
|
|
* @generic {Phaser.Math.Vector2} O - [out,$return]
|
|
*
|
|
* @param {number} t - The relative position on the curve, [0..1].
|
|
* @param {Phaser.Math.Vector2} [out] - A vector to store the result in.
|
|
*
|
|
* @return {Phaser.Math.Vector2} Vector approximating the tangent line at the point t (delta +/- 0.0001)
|
|
*/
|
|
getTangent: function (t, out)
|
|
{
|
|
if (out === undefined) { out = new Vector2(); }
|
|
|
|
var delta = 0.0001;
|
|
var t1 = t - delta;
|
|
var t2 = t + delta;
|
|
|
|
// Capping in case of danger
|
|
|
|
if (t1 < 0)
|
|
{
|
|
t1 = 0;
|
|
}
|
|
|
|
if (t2 > 1)
|
|
{
|
|
t2 = 1;
|
|
}
|
|
|
|
this.getPoint(t1, this._tmpVec2A);
|
|
this.getPoint(t2, out);
|
|
|
|
return out.subtract(this._tmpVec2A).normalize();
|
|
},
|
|
|
|
/**
|
|
* Get a unit vector tangent at a relative position on the curve, by arc length.
|
|
*
|
|
* @method Phaser.Curves.Curve#getTangentAt
|
|
* @since 3.0.0
|
|
*
|
|
* @generic {Phaser.Math.Vector2} O - [out,$return]
|
|
*
|
|
* @param {number} u - The relative position on the curve, [0..1].
|
|
* @param {Phaser.Math.Vector2} [out] - A vector to store the result in.
|
|
*
|
|
* @return {Phaser.Math.Vector2} The tangent vector.
|
|
*/
|
|
getTangentAt: function (u, out)
|
|
{
|
|
var t = this.getUtoTmapping(u);
|
|
|
|
return this.getTangent(t, out);
|
|
},
|
|
|
|
// Given a distance in pixels, get a t to find p.
|
|
/**
|
|
* [description]
|
|
*
|
|
* @method Phaser.Curves.Curve#getTFromDistance
|
|
* @since 3.0.0
|
|
*
|
|
* @param {integer} distance - [description]
|
|
* @param {integer} [divisions] - [description]
|
|
*
|
|
* @return {number} [description]
|
|
*/
|
|
getTFromDistance: function (distance, divisions)
|
|
{
|
|
if (distance <= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return this.getUtoTmapping(0, distance, divisions);
|
|
},
|
|
|
|
// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant
|
|
|
|
/**
|
|
* [description]
|
|
*
|
|
* @method Phaser.Curves.Curve#getUtoTmapping
|
|
* @since 3.0.0
|
|
*
|
|
* @param {number} u - [description]
|
|
* @param {integer} distance - [description]
|
|
* @param {integer} [divisions] - [description]
|
|
*
|
|
* @return {number} [description]
|
|
*/
|
|
getUtoTmapping: function (u, distance, divisions)
|
|
{
|
|
var arcLengths = this.getLengths(divisions);
|
|
|
|
var i = 0;
|
|
var il = arcLengths.length;
|
|
|
|
var targetArcLength; // The targeted u distance value to get
|
|
|
|
if (distance)
|
|
{
|
|
// Cannot overshoot the curve
|
|
targetArcLength = Math.min(distance, arcLengths[il - 1]);
|
|
}
|
|
else
|
|
{
|
|
targetArcLength = u * arcLengths[il - 1];
|
|
}
|
|
|
|
// binary search for the index with largest value smaller than target u distance
|
|
|
|
var low = 0;
|
|
var high = il - 1;
|
|
var comparison;
|
|
|
|
while (low <= high)
|
|
{
|
|
i = Math.floor(low + (high - low) / 2); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
|
|
|
|
comparison = arcLengths[i] - targetArcLength;
|
|
|
|
if (comparison < 0)
|
|
{
|
|
low = i + 1;
|
|
}
|
|
else if (comparison > 0)
|
|
{
|
|
high = i - 1;
|
|
}
|
|
else
|
|
{
|
|
high = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
i = high;
|
|
|
|
if (arcLengths[i] === targetArcLength)
|
|
{
|
|
return i / (il - 1);
|
|
}
|
|
|
|
// we could get finer grain at lengths, or use simple interpolation between two points
|
|
|
|
var lengthBefore = arcLengths[i];
|
|
var lengthAfter = arcLengths[i + 1];
|
|
|
|
var segmentLength = lengthAfter - lengthBefore;
|
|
|
|
// determine where we are between the 'before' and 'after' points
|
|
|
|
var segmentFraction = (targetArcLength - lengthBefore) / segmentLength;
|
|
|
|
// add that fractional amount to t
|
|
|
|
return (i + segmentFraction) / (il - 1);
|
|
},
|
|
|
|
/**
|
|
* Calculate and cache the arc lengths.
|
|
*
|
|
* @method Phaser.Curves.Curve#updateArcLengths
|
|
* @since 3.0.0
|
|
*
|
|
* @see Phaser.Curves.Curve#getLengths()
|
|
*/
|
|
updateArcLengths: function ()
|
|
{
|
|
this.needsUpdate = true;
|
|
|
|
this.getLengths();
|
|
}
|
|
|
|
});
|
|
|
|
module.exports = Curve;
|