Files
esengine/source/src/Math/MathHelper.ts

599 lines
20 KiB
TypeScript
Raw Normal View History

2020-07-23 11:00:46 +08:00
module es {
export class MathHelper {
public static readonly Epsilon: number = 0.00001;
public static readonly Rad2Deg = 57.29578;
public static readonly Deg2Rad = 0.0174532924;
/**
* pi除以2的值(1.57079637)
*/
public static readonly PiOver2 = Math.PI / 2;
2020-06-16 00:04:28 +08:00
2020-07-23 11:00:46 +08:00
/**
*
* @param radians
*/
2020-07-28 16:25:20 +08:00
public static toDegrees(radians: number) {
2020-07-23 11:00:46 +08:00
return radians * 57.295779513082320876798154814105;
}
2020-06-08 11:49:45 +08:00
2020-07-23 11:00:46 +08:00
/**
*
* @param degrees
*/
2020-07-28 16:25:20 +08:00
public static toRadians(degrees: number) {
2020-07-23 11:00:46 +08:00
return degrees * 0.017453292519943295769236907684886;
}
2021-05-03 08:17:48 +08:00
/**
*
* @param value1
* @param value2
* @param value3
* @param amount1
* @param amount2
*/
public static barycentric(value1: number, value2: number, value3: number, amount1: number, amount2: number) {
return value1 + (value2 - value1) * amount1 + (value3 - value1) * amount2;
}
/**
* 使Catmull-Rom插值
* @param value1
* @param value2
* @param value3
* @param value4
* @param amount
*/
public static catmullRom(value1: number, value2: number, value3: number, value4: number, amount: number) {
// 使用来自http://www.mvps.org/directx/articles/catmull/的公式
let amountSquared = amount * amount;
let amountCubed = amountSquared * amount;
return (0.5 * (2 * value2 + (value3 - value1) * amount +
(2 * value1 - 5 * value2 + 4 * value3 - value4) * amountSquared +
(3 * value2 - value1 - 3 * value3 + value4) * amountCubed));
}
2020-07-23 11:00:46 +08:00
/**
* leftMin-leftMax范围内rightMin-rightMax范围内的值
2020-07-23 11:00:46 +08:00
* @param value
* @param leftMin
* @param leftMax
* @param rightMin
* @param rightMax
*/
2020-07-28 16:25:20 +08:00
public static map(value: number, leftMin: number, leftMax: number, rightMin: number, rightMax: number) {
2020-07-23 11:00:46 +08:00
return rightMin + (value - leftMin) * (rightMax - rightMin) / (leftMax - leftMin);
}
/**
* 01
* @param value
* @param min
* @param max
* @returns
*/
public static map01(value: number, min: number, max: number) {
return (value - min) * 1 / (max - min);
}
/**
* 10
* map01的取反
* @param value
* @param min
* @param max
* @returns
*/
public static map10(value: number, min: number, max: number) {
return 1 - this.map01(value, min, max);
}
2021-05-03 08:17:48 +08:00
/**
* 使
* @param value1
* @param value2
* @param amount
*/
public static smoothStep(value1: number, value2: number, amount: number) {
let result = this.clamp(amount, 0, 1);
result = MathHelper.hermite(value1, 0, value2, 0, result);
return result;
}
/**
* π-π
* @param angle
*/
public static wrapAngle(angle: number) {
if ((angle > -Math.PI) && (angle <= Math.PI))
return angle;
angle %= Math.PI * 2;
if (angle <= -Math.PI)
return angle + 2 * Math.PI;
if (angle > Math.PI)
return angle - 2 * Math.PI;
return angle;
}
/**
* 2
* @param value
* @returns
*/
public static isPowerOfTwo(value: number) {
return (value > 0) && ((value % (value - 1)) == 0);
}
2021-04-29 11:00:15 +08:00
public static lerp(from: number, to: number, t: number) {
return from + (to - from) * this.clamp01(t);
}
2021-04-30 15:10:11 +08:00
/**
* 使a和b之间
* 360
* @param a
* @param b
* @param t
* @returns
*/
public static lerpAngle(a: number, b: number, t: number) {
let num = this.repeat(b - a, 360);
if (num > 180)
num -= 360;
return a + num * this.clamp01(t);
}
/**
* 使a和b之间
* @param a
* @param b
* @param t
* @returns
*/
public static lerpAngleRadians(a: number, b: number, t: number) {
let num = this.repeat(b - a, Math.PI * 2);
if (num > Math.PI)
num -= Math.PI * 2;
return a + num * this.clamp01(t);
}
/**
* t使其不大于长度且不小于0
* @param t
* @param length
* @returns
*/
public static pingPong(t: number, length: number) {
t = this.repeat(t, length * 2);
return length - Math.abs(t - length);
}
/**
* value> = threshold返回其符号0
* @param value
* @param threshold
* @returns
*/
public static signThreshold(value: number, threshold: number) {
if (Math.abs(value) >= threshold)
return Math.sign(value);
else
return 0;
}
2021-04-29 11:00:15 +08:00
public static inverseLerp(from: number, to: number, t: number) {
if (from < to) {
if (t < from)
return 0;
2021-05-03 08:17:48 +08:00
else if (t > to)
2021-04-29 11:00:15 +08:00
return 1;
} else {
if (t < to)
return 1;
2021-05-03 08:17:48 +08:00
else if (t > from)
2021-04-29 11:00:15 +08:00
return 0;
}
return (t - from) / (to - from);
2020-07-23 11:00:46 +08:00
}
2020-06-15 08:46:38 +08:00
2021-05-03 08:17:48 +08:00
/**
* 线
* MathHelper.Lerp的效率较低
*/
public static lerpPrecise(value1: number, value2: number, amount: number) {
return ((1 - amount) * value1) + (value2 * amount);
}
2020-07-28 16:25:20 +08:00
public static clamp(value: number, min: number, max: number) {
2020-07-23 11:00:46 +08:00
if (value < min)
return min;
2020-07-23 11:00:46 +08:00
if (value > max)
return max;
2020-07-23 11:00:46 +08:00
return value;
}
2021-04-29 11:00:15 +08:00
public static snap(value: number, increment: number) {
return Math.round(value / increment) * increment;
}
/**
* 03
* @param circleCenter
* @param radius
* @param angleInDegrees
*/
2020-07-28 16:25:20 +08:00
public static pointOnCirlce(circleCenter: Vector2, radius: number, angleInDegrees: number) {
2020-07-23 11:00:46 +08:00
let radians = MathHelper.toRadians(angleInDegrees);
return new Vector2(Math.cos(radians) * radians + circleCenter.x,
Math.sin(radians) * radians + circleCenter.y);
2020-07-23 11:00:46 +08:00
}
2020-07-23 11:00:46 +08:00
/**
* true
* @param value
*/
2020-07-28 16:25:20 +08:00
public static isEven(value: number) {
2020-07-23 11:00:46 +08:00
return value % 2 == 0;
}
2020-07-09 16:16:04 +08:00
/**
* true
* @param value
* @returns
*/
public static isOdd(value: number) {
return value % 2 != 0;
}
/**
*
* @param value
* @param roundedAmount
* @returns
*/
public static roundWithRoundedAmount(value: number, roundedAmount: Ref<number>) {
let rounded = Math.round(value);
2021-05-03 08:17:48 +08:00
roundedAmount.value = value - (rounded * Math.round(value / rounded));
return rounded;
}
2020-07-23 11:00:46 +08:00
/**
* 0-1
* @param value
*/
2020-07-28 16:25:20 +08:00
public static clamp01(value: number) {
2020-07-23 11:00:46 +08:00
if (value < 0)
return 0;
2020-07-17 11:07:57 +08:00
2020-07-23 11:00:46 +08:00
if (value > 1)
return 1;
2020-07-17 11:07:57 +08:00
2020-07-23 11:00:46 +08:00
return value;
}
2020-07-17 11:07:57 +08:00
2020-07-28 16:25:20 +08:00
public static angleBetweenVectors(from: Vector2, to: Vector2) {
2020-07-23 11:00:46 +08:00
return Math.atan2(to.y - from.y, to.x - from.x);
}
2020-08-12 12:16:35 +08:00
2020-12-28 16:59:16 +08:00
public static angleToVector(angleRadians: number, length: number) {
return new Vector2(Math.cos(angleRadians) * length, Math.sin(angleRadians) * length);
}
2020-08-12 12:16:35 +08:00
/**
* t并确保它总是大于或等于0并且小于长度
* @param t
* @param length
*/
2020-12-28 16:59:16 +08:00
public static incrementWithWrap(t: number, length: number) {
t++;
2020-08-12 12:16:35 +08:00
if (t == length)
return 0;
return t;
}
/**
* t并确保其始终大于或等于0且小于长度
* @param t
* @param length
* @returns
*/
public static decrementWithWrap(t: number, length: number) {
2021-05-03 08:17:48 +08:00
t--;
if (t < 0)
return length - 1;
return t;
}
/**
* sqrtx * x + y * y
* @param x
* @param y
* @returns
*/
public static hypotenuse(x: number, y: number) {
return Math.sqrt(x * x + y * y);
}
public static closestPowerOfTwoGreaterThan(x: number) {
2021-05-03 08:17:48 +08:00
x--;
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
2021-05-03 08:17:48 +08:00
return (x + 1);
}
2020-12-28 16:59:16 +08:00
/**
* roundToNearest为步长1251275
* @param value
* @param roundToNearest
*/
public static roundToNearest(value: number, roundToNearest: number) {
return Math.round(value / roundToNearest) * roundToNearest;
}
/**
*
* @param value
* @param ep
*/
public static withinEpsilon(value: number, ep: number = this.Epsilon) {
return Math.abs(value) < ep;
}
/**
* start可以小于或大于end例如:开始是21046
* @param start
* @param end
* @param shift
*/
public static approach(start: number, end: number, shift: number): number {
if (start < end)
return Math.min(start + shift, end);
return Math.max(start - shift, end);
}
2021-04-30 15:10:11 +08:00
/**
*
* 1301002555
* 2340302553655
* @param start
* @param end
* @param shift
* @returns
*/
public static approachAngle(start: number, end: number, shift: number) {
let deltaAngle = this.deltaAngle(start, end);
if (-shift < deltaAngle && deltaAngle < shift)
return end;
return this.repeat(this.approach(start, start + deltaAngle, shift), 360);
}
/**
*
*
* 使2 * Pi代替360
* @param start
* @param end
* @param shift
* @returns
*/
public static approachAngleRadians(start: number, end: number, shift: number) {
let deltaAngleRadians = this.deltaAngleRadians(start, end);
if (-shift < deltaAngleRadians && deltaAngleRadians < shift)
return end;
return this.repeat(this.approach(start, start + deltaAngleRadians, shift), Math.PI * 2);
}
/**
* 使
* @param value1
* @param value2
* @param tolerance
* @returns
*/
public static approximately(value1: number, value2: number, tolerance: number = this.Epsilon) {
return Math.abs(value1 - value2) <= tolerance;
}
/**
*
* @param current
* @param target
*/
public static deltaAngle(current: number, target: number) {
let num = this.repeat(target - current, 360);
if (num > 180)
num -= 360;
return num;
}
/**
* //
* @param value
* @param min
* @param max
* @returns
*/
public static between(value: number, min: number, max: number) {
return value >= min && value <= max;
}
2021-04-30 15:10:11 +08:00
/**
*
* @param current
* @param target
* @returns
*/
public static deltaAngleRadians(current: number, target: number) {
let num = this.repeat(target - current, 2 * Math.PI);
if (num > Math.PI)
num -= 2 * Math.PI;
return num;
}
/**
* t使0
* @param t
* @param length
*/
public static repeat(t: number, length: number) {
return t - Math.floor(t / length) * length;
}
2021-04-29 11:00:15 +08:00
public static floorToInt(f: number) {
return Math.trunc(Math.floor(f));
}
2021-04-30 15:10:11 +08:00
/**
*
* @param position
* @param speed
* @returns
*/
public static rotateAround(position: Vector2, speed: number) {
let time = Time.totalTime * speed;
let x = Math.cos(time);
let y = Math.sin(time);
return new Vector2(position.x + x, position.y + y);
}
/**
*
* 90135使45135
* @param point
* @param center
* @param angleIndegrees
*/
public static rotateAround2(point: Vector2, center: Vector2, angleIndegrees: number) {
angleIndegrees = this.toRadians(angleIndegrees);
let cos = Math.cos(angleIndegrees);
let sin = Math.sin(angleIndegrees);
let rotatedX = cos * (point.x - center.x) - sin * (point.y - center.y) + center.x;
let rotatedY = sin * (point.x - center.x) + cos * (point.y - center.y) + center.y;
return new Vector2(rotatedX, rotatedY);
}
/**
* 03
* @param circleCenter
* @param radius
* @param angleInDegrees
*/
public static pointOnCircle(circleCenter: Vector2, radius: number, angleInDegrees: number) {
let radians = this.toRadians(angleInDegrees);
return new Vector2(Math.cos(radians) * radius + circleCenter.x, Math.sin(radians) * radius + circleCenter.y);
}
/**
* 03
* @param circleCenter
* @param radius
* @param angleInRadians
* @returns
*/
public static pointOnCircleRadians(circleCenter: Vector2, radius: number, angleInRadians: number) {
return new Vector2(Math.cos(angleInRadians) * radius + circleCenter.x, Math.sin(angleInRadians) * radius + circleCenter.y);
}
/**
* lissajou曲线
* @param xFrequency
* @param yFrequency
* @param xMagnitude
* @param yMagnitude
* @param phase
* @returns
*/
public static lissajou(xFrequency: number = 2, yFrequency: number = 3, xMagnitude: number = 1, yMagnitude: number = 1, phase: number = 0) {
let x = Math.sin(Time.totalTime * xFrequency + phase) * xMagnitude;
let y = Math.cos(Time.totalTime * yFrequency) * yMagnitude;
return new Vector2(x, y);
}
/**
* lissajou曲线的阻尼形式0
* 01
*
* @param xFrequency
* @param yFrequency
* @param xMagnitude
* @param yMagnitude
* @param phase
* @param damping
* @param oscillationInterval
* @returns
*/
2021-05-03 08:17:48 +08:00
public static lissajouDamped(xFrequency: number = 2, yFrequency: number = 3, xMagnitude: number = 1,
2021-04-30 15:10:11 +08:00
yMagnitude: number = 1, phase: number = 0.5, damping: number = 0,
oscillationInterval: number = 5) {
2021-05-03 08:17:48 +08:00
let wrappedTime = this.pingPong(Time.totalTime, oscillationInterval);
let damped = Math.pow(Math.E, -damping * wrappedTime);
let x = damped * Math.sin(Time.totalTime * xFrequency + phase) * xMagnitude;
let y = damped * Math.cos(Time.totalTime * yFrequency) * yMagnitude;
return new Vector2(x, y);
}
/**
* Hermite样条插值
* @param value1
* @param tangent1
* @param value2
* @param tangent2
* @param amount
* @returns
*/
public static hermite(value1: number, tangent1: number, value2: number, tangent2: number, amount: number) {
let v1 = value1, v2 = value2, t1 = tangent1, t2 = tangent2, s = amount, result;
let sCubed = s * s * s;
let sSquared = s * s;
2021-04-30 15:10:11 +08:00
2021-05-03 08:17:48 +08:00
if (amount == 0)
result = value1;
else if (amount == 1)
result = value2;
else
result = (2 * v1 - 2 * v2 + t2 + t1) * sCubed +
(3 * v2 - 3 * v1 - 2 * t1 - t2) * sSquared +
t1 * s +
v1;
return result;
}
/**
* NaN或无穷大
* @param x
* @returns
*/
public static isValid(x: number) {
if (Number.isNaN(x)) {
return false;
}
2021-04-30 15:10:11 +08:00
2021-05-03 08:17:48 +08:00
return !Number.isFinite(x);
2021-04-30 15:10:11 +08:00
}
2020-07-09 16:16:04 +08:00
}
2020-07-23 11:00:46 +08:00
}