@esengine/nova-ecs-math
Version:
Comprehensive fixed-point mathematics library for deterministic calculations in games and simulations
1,692 lines (1,685 loc) • 70.4 kB
JavaScript
'use strict';
var novaEcs = require('@esengine/nova-ecs');
/**
* Fixed-point number implementation for deterministic arithmetic
* 用于确定性算术的定点数实现
*/
class Fixed {
/**
* Create a new Fixed-point number
* 创建新的定点数
*
* @param value - The value to convert to fixed-point
*/
constructor(value) {
if (value instanceof Fixed) {
this._value = value._value;
}
else if (typeof value === 'string') {
this._value = Math.round(parseFloat(value) * Fixed.SCALE);
}
else {
this._value = Math.round(value * Fixed.SCALE);
}
}
/**
* Create a Fixed number from raw internal value
* 从原始内部值创建定点数
*/
static fromRaw(rawValue) {
const fixed = new Fixed(0);
fixed._value = rawValue;
return fixed;
}
/**
* Create a cached Fixed number for commonly used values
* 为常用值创建缓存的定点数
*/
static cached(value) {
const rawValue = Math.round(value * Fixed.SCALE);
if (Fixed._cache.has(rawValue)) {
return Fixed._cache.get(rawValue);
}
if (Fixed._cache.size >= Fixed.MAX_CACHE_SIZE) {
// Clear cache when it gets too large
Fixed._cache.clear();
}
const fixed = Fixed.fromRaw(rawValue);
Fixed._cache.set(rawValue, fixed);
return fixed;
}
/**
* Add two fixed-point numbers
* 两个定点数相加
*/
add(other) {
return Fixed.fromRaw(this._value + other._value);
}
/**
* Add in place (modifies this instance for better performance)
* 就地相加(修改当前实例以提高性能)
*/
addInPlace(other) {
this._value += other._value;
return this;
}
/**
* Subtract two fixed-point numbers
* 两个定点数相减
*/
subtract(other) {
return Fixed.fromRaw(this._value - other._value);
}
/**
* Subtract in place (modifies this instance for better performance)
* 就地相减(修改当前实例以提高性能)
*/
subtractInPlace(other) {
this._value -= other._value;
return this;
}
/**
* Multiply two fixed-point numbers
* 两个定点数相乘
*/
multiply(other) {
return Fixed.fromRaw(Math.round((this._value * other._value) / Fixed.SCALE));
}
/**
* Multiply in place (modifies this instance for better performance)
* 就地相乘(修改当前实例以提高性能)
*/
multiplyInPlace(other) {
this._value = Math.round((this._value * other._value) / Fixed.SCALE);
return this;
}
/**
* Divide two fixed-point numbers
* 两个定点数相除
*/
divide(other) {
if (other._value === 0) {
throw new Error('Division by zero');
}
return Fixed.fromRaw(Math.round((this._value * Fixed.SCALE) / other._value));
}
/**
* Divide in place (modifies this instance for better performance)
* 就地相除(修改当前实例以提高性能)
*/
divideInPlace(other) {
if (other._value === 0) {
throw new Error('Division by zero');
}
this._value = Math.round((this._value * Fixed.SCALE) / other._value);
return this;
}
/**
* Get the absolute value
* 获取绝对值
*/
abs() {
return Fixed.fromRaw(Math.abs(this._value));
}
/**
* Negate the value
* 取负值
*/
negate() {
return Fixed.fromRaw(-this._value);
}
/**
* Calculate square root using Newton's method for deterministic results
* 使用牛顿迭代法计算平方根,确保确定性结果
*/
sqrt() {
if (this.lessThan(Fixed.ZERO)) {
throw new Error('Square root of negative number');
}
if (this.equals(Fixed.ZERO)) {
return Fixed.ZERO;
}
if (this.equals(Fixed.ONE)) {
return Fixed.ONE;
}
// Newton's method: x_{n+1} = (x_n + a/x_n) / 2
// Start with a reasonable initial guess
let x = this.greaterThan(Fixed.ONE) ? this.divide(Fixed.TWO) : Fixed.ONE;
let prev;
// Iterate until convergence (max 20 iterations for safety)
for (let i = 0; i < 20; i++) {
prev = x;
// x = (x + this/x) / 2
x = x.add(this.divide(x)).divide(Fixed.TWO);
// Check for convergence (difference less than 1/SCALE)
if (x.subtract(prev).abs().rawValue <= 1) {
break;
}
}
return x;
}
/**
* Static square root method
* 静态平方根方法
*/
static sqrt(value) {
return value.sqrt();
}
/**
* Normalize angle to [-π, π] range for trigonometric functions
* 将角度标准化到[-π, π]范围用于三角函数
*/
static normalizeAngle(angle) {
let normalized = new Fixed(angle.toNumber());
// Reduce to [-π, π] range
while (normalized.greaterThan(Fixed.PI)) {
normalized = normalized.subtract(Fixed.TWO_PI);
}
while (normalized.lessThanOrEqual(Fixed.PI.negate())) {
normalized = normalized.add(Fixed.TWO_PI);
}
return normalized;
}
/**
* Calculate sine using Taylor series for deterministic results
* 使用泰勒级数计算正弦值,确保确定性结果
*/
sin() {
return Fixed.sin(this);
}
/**
* Static sine function using Taylor series
* 使用泰勒级数的静态正弦函数
*/
static sin(angle) {
// 1. 角度标准化到[-π, π]
const normalizedAngle = Fixed.normalizeAngle(angle);
// Check lookup table first
if (Fixed.SIN_TABLE.has(normalizedAngle.rawValue)) {
return Fixed.SIN_TABLE.get(normalizedAngle.rawValue);
}
// 2. 利用对称性优化
let x = normalizedAngle;
let sign = Fixed.ONE;
// sin(-x) = -sin(x)
if (x.lessThan(Fixed.ZERO)) {
x = x.negate();
sign = sign.negate();
}
// sin(π - x) = sin(x) for x in [π/2, π]
if (x.greaterThan(Fixed.PI_2)) {
x = Fixed.PI.subtract(x);
}
// 3. 泰勒级数展开(使用预计算的阶乘)
// sin(x) = x - x³/3! + x⁵/5! - x⁷/7! + x⁹/9! - x¹¹/11! + ...
let result = Fixed.ZERO;
let term = x;
const x_squared = x.multiply(x);
let termSign = Fixed.ONE;
for (let i = 1; i <= 11; i += 2) {
const factorial = Fixed.FACTORIALS[i] || 1;
result = result.add(term.multiply(termSign).divide(new Fixed(factorial)));
// Next term: multiply by x²
term = term.multiply(x_squared);
termSign = termSign.negate();
}
return result.multiply(sign);
}
/**
* Calculate cosine using the identity cos(x) = sin(x + π/2)
* 使用恒等式 cos(x) = sin(x + π/2) 计算余弦值
*/
cos() {
return Fixed.cos(this);
}
/**
* Static cosine function using cos(x) = sin(x + π/2)
* 使用 cos(x) = sin(x + π/2) 的静态余弦函数
*/
static cos(angle) {
return Fixed.sin(angle.add(Fixed.PI_HALF));
}
/**
* Calculate tangent
* 计算正切值
*/
tan() {
return Fixed.tan(this);
}
/**
* Static tangent function
* 静态正切函数
*/
static tan(angle) {
const cosValue = Fixed.cos(angle);
// Use a small threshold instead of exact zero check for numerical stability
if (cosValue.abs().lessThan(new Fixed(0.01))) {
throw new Error('Tangent undefined');
}
return Fixed.sin(angle).divide(cosValue);
}
/**
* Calculate arcsine using Newton's method
* 使用牛顿法计算反正弦值
*/
asin() {
if (this.abs().greaterThan(Fixed.ONE)) {
throw new Error('Arcsine domain error: input must be in [-1, 1]');
}
if (this.equals(Fixed.ZERO))
return Fixed.ZERO;
if (this.equals(Fixed.ONE))
return Fixed.PI_2;
if (this.equals(new Fixed(-1)))
return Fixed.PI_2.negate();
// Use Newton's method to solve sin(y) = x for y
let y = new Fixed(this.toNumber()); // Initial guess
for (let i = 0; i < 10; i++) {
const sinY = y.sin();
const cosY = y.cos();
if (cosY.equals(Fixed.ZERO))
break;
const delta = sinY.subtract(this).divide(cosY);
y = y.subtract(delta);
if (delta.abs().rawValue <= 1)
break; // Converged
}
return y;
}
/**
* Calculate arccosine
* 计算反余弦值
*/
acos() {
if (this.abs().greaterThan(Fixed.ONE)) {
throw new Error('Arccosine domain error: input must be in [-1, 1]');
}
// acos(x) = π/2 - asin(x)
return Fixed.PI_2.subtract(this.asin());
}
/**
* Calculate arctangent using CORDIC algorithm
* 使用CORDIC算法计算反正切值
*/
atan() {
// Use the identity: atan(x) = asin(x/√(1+x²))
const x_squared = this.multiply(this);
const denominator = Fixed.ONE.add(x_squared).sqrt();
return this.divide(denominator).asin();
}
/**
* Calculate atan2 for vector angles
* 计算atan2用于向量角度
*/
static atan2(y, x) {
if (x.equals(Fixed.ZERO) && y.equals(Fixed.ZERO)) {
throw new Error('atan2 undefined for (0, 0)');
}
if (x.greaterThan(Fixed.ZERO)) {
return y.divide(x).atan();
}
else if (x.lessThan(Fixed.ZERO)) {
if (y.greaterThanOrEqual(Fixed.ZERO)) {
return y.divide(x).atan().add(Fixed.PI);
}
else {
return y.divide(x).atan().subtract(Fixed.PI);
}
}
else { // x == 0
if (y.greaterThan(Fixed.ZERO)) {
return Fixed.PI_2;
}
else {
return Fixed.PI_2.negate();
}
}
}
/**
* Convert to regular number
* 转换为普通数字
*/
toNumber() {
return this._value / Fixed.SCALE;
}
/**
* Convert to string representation
* 转换为字符串表示
*/
toString() {
return this.toNumber().toString();
}
/**
* Check equality with another Fixed number
* 检查与另一个定点数是否相等
*/
equals(other) {
return this._value === other._value;
}
/**
* Check if this number is less than another
* 检查是否小于另一个数
*/
lessThan(other) {
return this._value < other._value;
}
/**
* Check if this number is less than or equal to another
* 检查是否小于或等于另一个数
*/
lessThanOrEqual(other) {
return this._value <= other._value;
}
/**
* Check if this number is greater than another
* 检查是否大于另一个数
*/
greaterThan(other) {
return this._value > other._value;
}
/**
* Check if this number is greater than or equal to another
* 检查是否大于或等于另一个数
*/
greaterThanOrEqual(other) {
return this._value >= other._value;
}
/**
* Get the raw internal value (for advanced usage)
* 获取原始内部值(高级用法)
*/
get rawValue() {
return this._value;
}
/**
* Get the scale factor used for fixed-point arithmetic
* 获取用于定点算术的比例因子
*/
static get scale() {
return Fixed.SCALE;
}
/**
* Floor function - largest integer less than or equal to this value
* 向下取整函数
*/
floor() {
return Fixed.fromRaw(Math.floor(this._value / Fixed.SCALE) * Fixed.SCALE);
}
/**
* Ceiling function - smallest integer greater than or equal to this value
* 向上取整函数
*/
ceil() {
return Fixed.fromRaw(Math.ceil(this._value / Fixed.SCALE) * Fixed.SCALE);
}
/**
* Round to nearest integer
* 四舍五入到最近整数
*/
round() {
return Fixed.fromRaw(Math.round(this._value / Fixed.SCALE) * Fixed.SCALE);
}
/**
* Get fractional part
* 获取小数部分
*/
frac() {
return this.subtract(this.floor());
}
/**
* Power function using exponentiation by squaring
* 使用平方求幂的幂函数
*/
pow(exponent) {
if (exponent.equals(Fixed.ZERO)) {
return Fixed.ONE;
}
if (exponent.equals(Fixed.ONE)) {
return new Fixed(this.toNumber());
}
// For integer exponents, use exponentiation by squaring
if (exponent.frac().equals(Fixed.ZERO)) {
const exp = Math.abs(exponent.toNumber());
let result = Fixed.ONE;
let base = new Fixed(this.toNumber());
let n = exp;
while (n > 0) {
if (n % 2 === 1) {
result = result.multiply(base);
}
base = base.multiply(base);
n = Math.floor(n / 2);
}
return exponent.lessThan(Fixed.ZERO) ? Fixed.ONE.divide(result) : result;
}
// For fractional exponents, use exp(ln(x) * y)
return this.ln().multiply(exponent).exp();
}
/**
* Natural logarithm using Taylor series
* 使用泰勒级数计算自然对数
*/
ln() {
if (this.lessThanOrEqual(Fixed.ZERO)) {
throw new Error('Logarithm domain error: input must be positive');
}
if (this.equals(Fixed.ONE)) {
return Fixed.ZERO;
}
// Use ln(x) = 2 * ((x-1)/(x+1) + (x-1)³/(3(x+1)³) + ...)
const x = this;
const numerator = x.subtract(Fixed.ONE);
const denominator = x.add(Fixed.ONE);
const ratio = numerator.divide(denominator);
const ratio_squared = ratio.multiply(ratio);
let result = Fixed.ZERO;
let term = ratio;
for (let i = 1; i <= 20; i += 2) {
result = result.add(term.divide(new Fixed(i)));
term = term.multiply(ratio_squared);
}
return result.multiply(Fixed.TWO);
}
/**
* Exponential function using Taylor series
* 使用泰勒级数计算指数函数
*/
exp() {
// Use Taylor series: e^x = 1 + x + x²/2! + x³/3! + ...
let result = Fixed.ONE;
let term = Fixed.ONE;
for (let i = 1; i <= 20; i++) {
term = term.multiply(this).divide(new Fixed(i));
result = result.add(term);
}
return result;
}
/**
* Minimum of two values
* 两个值的最小值
*/
static min(a, b) {
return a.lessThan(b) ? a : b;
}
/**
* Maximum of two values
* 两个值的最大值
*/
static max(a, b) {
return a.greaterThan(b) ? a : b;
}
/**
* Clamp value between min and max
* 将值限制在最小值和最大值之间
*/
clamp(min, max) {
if (this.lessThan(min))
return min;
if (this.greaterThan(max))
return max;
return new Fixed(this.toNumber());
}
/**
* Modulo operation - returns the remainder after division
* 模运算 - 返回除法后的余数
*/
mod(divisor) {
if (divisor.equals(Fixed.ZERO)) {
throw new Error('Modulo by zero');
}
// Use the formula: a mod b = a - b * floor(a/b)
const quotient = this.divide(divisor).floor();
return this.subtract(divisor.multiply(quotient));
}
/**
* Linear interpolation between two values
* 两个值之间的线性插值
*/
static lerp(a, b, t) {
return a.add(b.subtract(a).multiply(t));
}
}
Fixed.SCALE = 1000000;
// Cache for commonly used values to reduce object creation
// 缓存常用值以减少对象创建
Fixed._cache = new Map();
Fixed.MAX_CACHE_SIZE = 1000;
// Static constants
Fixed.ZERO = new Fixed(0);
Fixed.ONE = new Fixed(1);
Fixed.PI = new Fixed(3.141592653589793);
Fixed.E = new Fixed(2.718281828459045);
Fixed.HALF = new Fixed(0.5);
Fixed.TWO = new Fixed(2);
Fixed.PI_2 = new Fixed(1.5707963267948966); // PI/2
Fixed.PI_HALF = new Fixed(1.5707963267948966); // PI/2 (alias)
Fixed.PI_4 = new Fixed(0.7853981633974483); // PI/4
Fixed.PI_QUARTER = new Fixed(0.7853981633974483); // PI/4 (alias)
Fixed.TWO_PI = new Fixed(6.283185307179586); // 2*PI
Fixed.PI_TWO = new Fixed(6.283185307179586); // 2*PI (alias)
Fixed.DEG_TO_RAD = new Fixed(0.017453292519943295); // PI/180
Fixed.RAD_TO_DEG = new Fixed(57.29577951308232); // 180/PI
// Precomputed factorial values for Taylor series
// 预计算的阶乘值用于泰勒级数
Fixed.FACTORIALS = [
1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600
];
// Lookup table for common angles (in radians)
// 常用角度的查找表(弧度)
Fixed.SIN_TABLE = new Map([
[0, Fixed.ZERO],
[Fixed.PI_4.rawValue, new Fixed(0.7071067811865476)], // sin(π/4) = √2/2
[Fixed.PI_2.rawValue, Fixed.ONE], // sin(π/2) = 1
[Fixed.PI.rawValue, Fixed.ZERO], // sin(π) = 0
]);
/**
* 2D Vector using fixed-point arithmetic for deterministic calculations
* 使用定点算术的2D向量,用于确定性计算
*/
class FixedVector2 {
/**
* Create a new 2D fixed-point vector
* 创建新的2D定点向量
*
* @param x - X component (can be Fixed or number)
* @param y - Y component (can be Fixed or number)
*/
constructor(x = 0, y = 0) {
this.x = x instanceof Fixed ? x : new Fixed(x);
this.y = y instanceof Fixed ? y : new Fixed(y);
}
/**
* Add two vectors
* 两个向量相加
*/
add(other) {
return new FixedVector2(this.x.add(other.x), this.y.add(other.y));
}
/**
* Add in place (modifies this instance for better performance)
* 就地相加(修改当前实例以提高性能)
*/
addInPlace(other) {
this.x.addInPlace(other.x);
this.y.addInPlace(other.y);
return this;
}
/**
* Subtract two vectors
* 两个向量相减
*/
subtract(other) {
return new FixedVector2(this.x.subtract(other.x), this.y.subtract(other.y));
}
/**
* Subtract in place (modifies this instance for better performance)
* 就地相减(修改当前实例以提高性能)
*/
subtractInPlace(other) {
this.x.subtractInPlace(other.x);
this.y.subtractInPlace(other.y);
return this;
}
/**
* Multiply vector by a scalar
* 向量乘以标量
*/
multiply(scalar) {
const scalarFixed = scalar instanceof Fixed ? scalar : new Fixed(scalar);
return new FixedVector2(this.x.multiply(scalarFixed), this.y.multiply(scalarFixed));
}
/**
* Multiply in place (modifies this instance for better performance)
* 就地相乘(修改当前实例以提高性能)
*/
multiplyInPlace(scalar) {
const scalarFixed = scalar instanceof Fixed ? scalar : new Fixed(scalar);
this.x.multiplyInPlace(scalarFixed);
this.y.multiplyInPlace(scalarFixed);
return this;
}
/**
* Divide vector by a scalar
* 向量除以标量
*/
divide(scalar) {
const scalarFixed = scalar instanceof Fixed ? scalar : new Fixed(scalar);
if (scalarFixed.equals(Fixed.ZERO)) {
throw new Error('Division by zero vector');
}
return new FixedVector2(this.x.divide(scalarFixed), this.y.divide(scalarFixed));
}
/**
* Divide in place (modifies this instance for better performance)
* 就地相除(修改当前实例以提高性能)
*/
divideInPlace(scalar) {
const scalarFixed = scalar instanceof Fixed ? scalar : new Fixed(scalar);
if (scalarFixed.equals(Fixed.ZERO)) {
throw new Error('Division by zero vector');
}
this.x.divideInPlace(scalarFixed);
this.y.divideInPlace(scalarFixed);
return this;
}
/**
* Calculate the magnitude (length) of the vector
* 计算向量的大小(长度)
*/
magnitude() {
const sqrMagnitude = this.x.multiply(this.x).add(this.y.multiply(this.y));
return sqrMagnitude.sqrt();
}
/**
* Calculate the squared magnitude (more efficient than magnitude)
* 计算平方大小(比magnitude更高效)
*/
sqrMagnitude() {
return this.x.multiply(this.x).add(this.y.multiply(this.y));
}
/**
* Normalize the vector (make it unit length)
* 归一化向量(使其长度为1)
*/
normalize() {
const mag = this.magnitude();
if (mag.equals(Fixed.ZERO)) {
return new FixedVector2(Fixed.ZERO, Fixed.ZERO);
}
return new FixedVector2(this.x.divide(mag), this.y.divide(mag));
}
/**
* Calculate dot product with another vector
* 计算与另一个向量的点积
*/
dot(other) {
return this.x.multiply(other.x).add(this.y.multiply(other.y));
}
/**
* Calculate cross product with another vector (returns scalar for 2D)
* 计算与另一个向量的叉积(2D返回标量)
*/
cross(other) {
return this.x.multiply(other.y).subtract(this.y.multiply(other.x));
}
/**
* Calculate distance to another vector
* 计算到另一个向量的距离
*/
distance(other) {
return this.subtract(other).magnitude();
}
/**
* Calculate squared distance to another vector (more efficient)
* 计算到另一个向量的平方距离(更高效)
*/
sqrDistance(other) {
return this.subtract(other).sqrMagnitude();
}
/**
* Negate the vector
* 取向量的负值
*/
negate() {
return new FixedVector2(this.x.negate(), this.y.negate());
}
/**
* Get the absolute values of components
* 获取分量的绝对值
*/
abs() {
return new FixedVector2(this.x.abs(), this.y.abs());
}
/**
* Check equality with another vector
* 检查与另一个向量是否相等
*/
equals(other) {
return this.x.equals(other.x) && this.y.equals(other.y);
}
/**
* Convert to string representation
* 转换为字符串表示
*/
toString() {
return `(${this.x.toString()}, ${this.y.toString()})`;
}
/**
* Convert to array [x, y]
* 转换为数组 [x, y]
*/
toArray() {
return [this.x.toNumber(), this.y.toNumber()];
}
/**
* Create a copy of this vector
* 创建此向量的副本
*/
clone() {
return new FixedVector2(this.x, this.y);
}
/**
* Set this vector's components from another vector (for reusing objects)
* 从另一个向量设置此向量的分量(用于重用对象)
*/
setFrom(other) {
this.x = other.x;
this.y = other.y;
return this;
}
/**
* Set this vector's components from coordinates (for reusing objects)
* 从坐标设置此向量的分量(用于重用对象)
*/
set(x, y) {
this.x = x instanceof Fixed ? x : new Fixed(x);
this.y = y instanceof Fixed ? y : new Fixed(y);
return this;
}
/**
* Reset this vector to zero (for object pooling)
* 将此向量重置为零(用于对象池)
*/
reset() {
this.x = Fixed.ZERO;
this.y = Fixed.ZERO;
return this;
}
/**
* Linear interpolation between two vectors
* 两个向量之间的线性插值
*/
static lerp(a, b, t) {
const tFixed = t instanceof Fixed ? t : new Fixed(t);
return new FixedVector2(Fixed.lerp(a.x, b.x, tFixed), Fixed.lerp(a.y, b.y, tFixed));
}
/**
* Calculate angle between two vectors in radians using deterministic math
* 使用确定性数学计算两个向量之间的角度(弧度)
*/
static angle(a, b) {
const dot = a.dot(b);
const magnitudes = a.magnitude().multiply(b.magnitude());
if (magnitudes.equals(Fixed.ZERO)) {
return Fixed.ZERO;
}
const cosAngle = dot.divide(magnitudes);
// Clamp to [-1, 1] to avoid domain errors
const clampedCos = cosAngle.clamp(new Fixed(-1), Fixed.ONE);
return clampedCos.acos();
}
/**
* Get the angle of this vector from the positive X axis
* 获取此向量相对于正X轴的角度
*/
angle() {
return Fixed.atan2(this.y, this.x);
}
/**
* Reflect this vector across a normal vector
* 沿法向量反射此向量
*
* @param normal - The normal vector to reflect across (should be normalized)
* @returns The reflected vector
*/
reflect(normal) {
// Formula: v - 2 * (v · n) * n
const dotProduct = this.dot(normal);
const reflection = normal.multiply(dotProduct.multiply(new Fixed(2)));
return this.subtract(reflection);
}
/**
* Project this vector onto another vector
* 将此向量投影到另一个向量上
*
* @param onto - The vector to project onto
* @returns The projected vector
*/
project(onto) {
// Formula: (v · u) / (u · u) * u
const dotProduct = this.dot(onto);
const ontoLengthSquared = onto.dot(onto);
if (ontoLengthSquared.equals(Fixed.ZERO)) {
return FixedVector2.ZERO;
}
const scalar = dotProduct.divide(ontoLengthSquared);
return onto.multiply(scalar);
}
/**
* Get the component of this vector perpendicular to another vector
* 获取此向量垂直于另一个向量的分量
*
* @param onto - The vector to get the perpendicular component relative to
* @returns The perpendicular component
*/
reject(onto) {
// Formula: v - project(v, onto)
return this.subtract(this.project(onto));
}
/**
* Rotate this vector by the given angle in radians
* 将此向量按给定角度(弧度)旋转
*
* @param angle - Angle in radians
* @returns The rotated vector
*/
rotate(angle) {
const a = angle instanceof Fixed ? angle : new Fixed(angle);
const cos = a.cos();
const sin = a.sin();
const newX = this.x.multiply(cos).subtract(this.y.multiply(sin));
const newY = this.x.multiply(sin).add(this.y.multiply(cos));
return new FixedVector2(newX, newY);
}
/**
* Get a vector perpendicular to this one (rotated 90 degrees counter-clockwise)
* 获取垂直于此向量的向量(逆时针旋转90度)
*/
perpendicular() {
return new FixedVector2(this.y.negate(), this.x);
}
/**
* Get the right perpendicular vector (rotated 90 degrees clockwise)
* 获取右垂直向量(顺时针旋转90度)
*/
perpendicularRight() {
return new FixedVector2(this.y, this.x.negate());
}
/**
* Create a vector from angle and magnitude
* 从角度和大小创建向量
*/
static fromAngle(angle, magnitude = Fixed.ONE) {
return new FixedVector2(angle.cos().multiply(magnitude), angle.sin().multiply(magnitude));
}
}
// Static constants
FixedVector2.ZERO = new FixedVector2(0, 0);
FixedVector2.ONE = new FixedVector2(1, 1);
FixedVector2.UP = new FixedVector2(0, 1);
FixedVector2.DOWN = new FixedVector2(0, -1);
FixedVector2.LEFT = new FixedVector2(-1, 0);
FixedVector2.RIGHT = new FixedVector2(1, 0);
/**
* 2x2 Matrix using fixed-point arithmetic for deterministic calculations
* 使用定点算术的2x2矩阵,用于确定性计算
*
* Matrix layout:
* | m00 m01 |
* | m10 m11 |
*/
class FixedMatrix2x2 {
/**
* Create a new 2x2 fixed-point matrix
* 创建新的2x2定点矩阵
*
* @param m00 - Element at row 0, column 0
* @param m01 - Element at row 0, column 1
* @param m10 - Element at row 1, column 0
* @param m11 - Element at row 1, column 1
*/
constructor(m00 = 1, m01 = 0, m10 = 0, m11 = 1) {
this.m00 = m00 instanceof Fixed ? m00 : new Fixed(m00);
this.m01 = m01 instanceof Fixed ? m01 : new Fixed(m01);
this.m10 = m10 instanceof Fixed ? m10 : new Fixed(m10);
this.m11 = m11 instanceof Fixed ? m11 : new Fixed(m11);
}
/**
* Create an identity matrix
* 创建单位矩阵
*/
static identity() {
return new FixedMatrix2x2(1, 0, 0, 1);
}
/**
* Create a zero matrix
* 创建零矩阵
*/
static zero() {
return new FixedMatrix2x2(0, 0, 0, 0);
}
/**
* Create a rotation matrix
* 创建旋转矩阵
*
* @param angle - Rotation angle in radians
*/
static rotation(angle) {
const cos = angle.cos();
const sin = angle.sin();
return new FixedMatrix2x2(cos, sin.negate(), sin, cos);
}
/**
* Create a scaling matrix
* 创建缩放矩阵
*
* @param scaleX - Scale factor for X axis
* @param scaleY - Scale factor for Y axis (defaults to scaleX for uniform scaling)
*/
static scaling(scaleX, scaleY) {
const sy = scaleY || scaleX;
return new FixedMatrix2x2(scaleX, Fixed.ZERO, Fixed.ZERO, sy);
}
/**
* Create a shear matrix
* 创建剪切矩阵
*
* @param shearX - Shear factor for X axis
* @param shearY - Shear factor for Y axis
*/
static shear(shearX, shearY) {
return new FixedMatrix2x2(Fixed.ONE, shearX, shearY, Fixed.ONE);
}
/**
* Create a matrix from an array [m00, m01, m10, m11]
* 从数组创建矩阵 [m00, m01, m10, m11]
*/
static fromArray(array) {
if (array.length !== 4) {
throw new Error('Array must have exactly 4 elements');
}
return new FixedMatrix2x2(array[0], array[1], array[2], array[3]);
}
/**
* Add two matrices
* 两个矩阵相加
*/
add(other) {
return new FixedMatrix2x2(this.m00.add(other.m00), this.m01.add(other.m01), this.m10.add(other.m10), this.m11.add(other.m11));
}
/**
* Add in place (modifies this instance for better performance)
* 就地相加(修改当前实例以提高性能)
*/
addInPlace(other) {
this.m00.addInPlace(other.m00);
this.m01.addInPlace(other.m01);
this.m10.addInPlace(other.m10);
this.m11.addInPlace(other.m11);
return this;
}
/**
* Subtract two matrices
* 两个矩阵相减
*/
subtract(other) {
return new FixedMatrix2x2(this.m00.subtract(other.m00), this.m01.subtract(other.m01), this.m10.subtract(other.m10), this.m11.subtract(other.m11));
}
/**
* Subtract in place (modifies this instance for better performance)
* 就地相减(修改当前实例以提高性能)
*/
subtractInPlace(other) {
this.m00.subtractInPlace(other.m00);
this.m01.subtractInPlace(other.m01);
this.m10.subtractInPlace(other.m10);
this.m11.subtractInPlace(other.m11);
return this;
}
/**
* Multiply two matrices
* 两个矩阵相乘
*/
multiply(other) {
return new FixedMatrix2x2(this.m00.multiply(other.m00).add(this.m01.multiply(other.m10)), this.m00.multiply(other.m01).add(this.m01.multiply(other.m11)), this.m10.multiply(other.m00).add(this.m11.multiply(other.m10)), this.m10.multiply(other.m01).add(this.m11.multiply(other.m11)));
}
/**
* Multiply matrix by a scalar
* 矩阵乘以标量
*/
multiplyScalar(scalar) {
return new FixedMatrix2x2(this.m00.multiply(scalar), this.m01.multiply(scalar), this.m10.multiply(scalar), this.m11.multiply(scalar));
}
/**
* Multiply matrix by a scalar in place
* 就地矩阵乘以标量
*/
multiplyScalarInPlace(scalar) {
this.m00.multiplyInPlace(scalar);
this.m01.multiplyInPlace(scalar);
this.m10.multiplyInPlace(scalar);
this.m11.multiplyInPlace(scalar);
return this;
}
/**
* Transform a vector by this matrix
* 使用此矩阵变换向量
*/
transformVector(vector) {
return new FixedVector2(this.m00.multiply(vector.x).add(this.m01.multiply(vector.y)), this.m10.multiply(vector.x).add(this.m11.multiply(vector.y)));
}
/**
* Calculate the determinant of this matrix
* 计算此矩阵的行列式
*/
determinant() {
return this.m00.multiply(this.m11).subtract(this.m01.multiply(this.m10));
}
/**
* Calculate the inverse of this matrix
* 计算此矩阵的逆矩阵
*/
inverse() {
const det = this.determinant();
if (det.equals(Fixed.ZERO)) {
throw new Error('Matrix is not invertible (determinant is zero)');
}
const invDet = Fixed.ONE.divide(det);
return new FixedMatrix2x2(this.m11.multiply(invDet), this.m01.negate().multiply(invDet), this.m10.negate().multiply(invDet), this.m00.multiply(invDet));
}
/**
* Transpose this matrix
* 转置此矩阵
*/
transpose() {
return new FixedMatrix2x2(this.m00, this.m10, this.m01, this.m11);
}
/**
* Calculate the trace (sum of diagonal elements) of this matrix
* 计算此矩阵的迹(对角线元素之和)
*/
trace() {
return this.m00.add(this.m11);
}
/**
* Check if this matrix equals another matrix
* 检查此矩阵是否等于另一个矩阵
*/
equals(other) {
return this.m00.equals(other.m00) &&
this.m01.equals(other.m01) &&
this.m10.equals(other.m10) &&
this.m11.equals(other.m11);
}
/**
* Check if this matrix is the identity matrix
* 检查此矩阵是否为单位矩阵
*/
isIdentity() {
return this.m00.equals(Fixed.ONE) &&
this.m01.equals(Fixed.ZERO) &&
this.m10.equals(Fixed.ZERO) &&
this.m11.equals(Fixed.ONE);
}
/**
* Create a copy of this matrix
* 创建此矩阵的副本
*/
clone() {
return new FixedMatrix2x2(this.m00, this.m01, this.m10, this.m11);
}
/**
* Convert to a regular number array [m00, m01, m10, m11]
* 转换为普通数字数组 [m00, m01, m10, m11]
*/
toArray() {
return [
this.m00.toNumber(),
this.m01.toNumber(),
this.m10.toNumber(),
this.m11.toNumber()
];
}
/**
* Convert to string representation
* 转换为字符串表示
*/
toString() {
return `[${this.m00.toString()}, ${this.m01.toString()}]\n[${this.m10.toString()}, ${this.m11.toString()}]`;
}
}
// Static constants
FixedMatrix2x2.IDENTITY = FixedMatrix2x2.identity();
FixedMatrix2x2.ZERO = FixedMatrix2x2.zero();
/**
* Rectangle using fixed-point arithmetic for deterministic calculations
* 使用定点算术的矩形,用于确定性计算
*/
class FixedRect {
/**
* Create a new fixed-point rectangle
* 创建新的定点矩形
*
* @param x - X coordinate of the top-left corner
* @param y - Y coordinate of the top-left corner
* @param width - Width of the rectangle
* @param height - Height of the rectangle
*/
constructor(x = 0, y = 0, width = 0, height = 0) {
this.x = x instanceof Fixed ? x : new Fixed(x);
this.y = y instanceof Fixed ? y : new Fixed(y);
this.width = width instanceof Fixed ? width : new Fixed(width);
this.height = height instanceof Fixed ? height : new Fixed(height);
}
/**
* Get the left edge X coordinate
* 获取左边缘X坐标
*/
get left() {
return this.x;
}
/**
* Get the right edge X coordinate
* 获取右边缘X坐标
*/
get right() {
return this.x.add(this.width);
}
/**
* Get the top edge Y coordinate
* 获取顶边Y坐标
*/
get top() {
return this.y;
}
/**
* Get the bottom edge Y coordinate
* 获取底边Y坐标
*/
get bottom() {
return this.y.add(this.height);
}
/**
* Get the center point of the rectangle
* 获取矩形的中心点
*/
get center() {
return new FixedVector2(this.x.add(this.width.divide(new Fixed(2))), this.y.add(this.height.divide(new Fixed(2))));
}
/**
* Get the top-left corner as a vector
* 获取左上角作为向量
*/
get topLeft() {
return new FixedVector2(this.x, this.y);
}
/**
* Get the top-right corner as a vector
* 获取右上角作为向量
*/
get topRight() {
return new FixedVector2(this.right, this.y);
}
/**
* Get the bottom-left corner as a vector
* 获取左下角作为向量
*/
get bottomLeft() {
return new FixedVector2(this.x, this.bottom);
}
/**
* Get the bottom-right corner as a vector
* 获取右下角作为向量
*/
get bottomRight() {
return new FixedVector2(this.right, this.bottom);
}
/**
* Check if a point is inside this rectangle
* 检查点是否在此矩形内
*/
contains(point) {
return point.x.greaterThanOrEqual(this.x) &&
point.x.lessThan(this.right) &&
point.y.greaterThanOrEqual(this.y) &&
point.y.lessThan(this.bottom);
}
/**
* Check if a point is inside this rectangle (inclusive of edges)
* 检查点是否在此矩形内(包含边缘)
*/
containsInclusive(point) {
return point.x.greaterThanOrEqual(this.x) &&
point.x.lessThanOrEqual(this.right) &&
point.y.greaterThanOrEqual(this.y) &&
point.y.lessThanOrEqual(this.bottom);
}
/**
* Check if this rectangle intersects with another rectangle
* 检查此矩形是否与另一个矩形相交
*/
intersects(other) {
return this.left.lessThan(other.right) &&
this.right.greaterThan(other.left) &&
this.top.lessThan(other.bottom) &&
this.bottom.greaterThan(other.top);
}
/**
* Get the intersection rectangle with another rectangle
* 获取与另一个矩形的交集矩形
*/
intersection(other) {
if (!this.intersects(other)) {
return null;
}
const left = Fixed.max(this.left, other.left);
const top = Fixed.max(this.top, other.top);
const right = Fixed.min(this.right, other.right);
const bottom = Fixed.min(this.bottom, other.bottom);
return new FixedRect(left, top, right.subtract(left), bottom.subtract(top));
}
/**
* Get the union rectangle with another rectangle
* 获取与另一个矩形的并集矩形
*/
union(other) {
const left = Fixed.min(this.left, other.left);
const top = Fixed.min(this.top, other.top);
const right = Fixed.max(this.right, other.right);
const bottom = Fixed.max(this.bottom, other.bottom);
return new FixedRect(left, top, right.subtract(left), bottom.subtract(top));
}
/**
* Expand the rectangle by the given amount in all directions
* 在所有方向上按给定量扩展矩形
*/
expand(amount) {
const amt = amount instanceof Fixed ? amount : new Fixed(amount);
return new FixedRect(this.x.subtract(amt), this.y.subtract(amt), this.width.add(amt.multiply(new Fixed(2))), this.height.add(amt.multiply(new Fixed(2))));
}
/**
* Shrink the rectangle by the given amount in all directions
* 在所有方向上按给定量收缩矩形
*/
shrink(amount) {
const amt = amount instanceof Fixed ? amount : new Fixed(amount);
return this.expand(amt.negate());
}
/**
* Move the rectangle by the given offset
* 按给定偏移量移动矩形
*/
translate(offset) {
return new FixedRect(this.x.add(offset.x), this.y.add(offset.y), this.width, this.height);
}
/**
* Create a copy of this rectangle
* 创建此矩形的副本
*/
clone() {
return new FixedRect(this.x, this.y, this.width, this.height);
}
/**
* Check if this rectangle equals another rectangle
* 检查此矩形是否等于另一个矩形
*/
equals(other) {
return this.x.equals(other.x) &&
this.y.equals(other.y) &&
this.width.equals(other.width) &&
this.height.equals(other.height);
}
/**
* Get the area of the rectangle
* 获取矩形的面积
*/
area() {
return this.width.multiply(this.height);
}
/**
* Get the perimeter of the rectangle
* 获取矩形的周长
*/
perimeter() {
return this.width.add(this.height).multiply(new Fixed(2));
}
/**
* Check if the rectangle is empty (zero or negative area)
* 检查矩形是否为空(零或负面积)
*/
isEmpty() {
return this.width.lessThanOrEqual(Fixed.ZERO) ||
this.height.lessThanOrEqual(Fixed.ZERO);
}
/**
* Convert to string representation
* 转换为字符串表示
*/
toString() {
return `FixedRect(${this.x.toString()}, ${this.y.toString()}, ${this.width.toString()}, ${this.height.toString()})`;
}
/**
* Convert to a regular number array [x, y, width, height]
* 转换为普通数字数组 [x, y, width, height]
*/
toArray() {
return [
this.x.toNumber(),
this.y.toNumber(),
this.width.toNumber(),
this.height.toNumber()
];
}
/**
* Create a rectangle from an array [x, y, width, height]
* 从数组创建矩形 [x, y, width, height]
*/
static fromArray(array) {
if (array.length !== 4) {
throw new Error('Array must have exactly 4 elements');
}
return new FixedRect(array[0], array[1], array[2], array[3]);
}
/**
* Create a rectangle from two corner points
* 从两个角点创建矩形
*/
static fromCorners(corner1, corner2) {
const left = Fixed.min(corner1.x, corner2.x);
const top = Fixed.min(corner1.y, corner2.y);
const right = Fixed.max(corner1.x, corner2.x);
const bottom = Fixed.max(corner1.y, corner2.y);
return new FixedRect(left, top, right.subtract(left), bottom.subtract(top));
}
/**
* Create a rectangle centered at the given point
* 创建以给定点为中心的矩形
*/
static centered(center, width, height) {
const w = width instanceof Fixed ? width : new Fixed(width);
const h = height instanceof Fixed ? height : new Fixed(height);
const halfW = w.divide(new Fixed(2));
const halfH = h.divide(new Fixed(2));
return new FixedRect(center.x.subtract(halfW), center.y.subtract(halfH), w, h);
}
}
// Static constants
FixedRect.ZERO = new FixedRect(0, 0, 0, 0);
FixedRect.UNIT = new FixedRect(0, 0, 1, 1);
/**
* Circle using fixed-point arithmetic for deterministic calculations
* 使用定点算术的圆形,用于确定性计算
*/
class FixedCircle {
constructor(centerOrX, radiusOrY, radius) {
if (centerOrX instanceof FixedVector2) {
this.center = centerOrX;
this.radius = radiusOrY instanceof Fixed ? radiusOrY : new Fixed(radiusOrY);
}
else {
const x = centerOrX instanceof Fixed ? centerOrX : new Fixed(centerOrX);
const y = radiusOrY instanceof Fixed ? radiusOrY : new Fixed(radiusOrY);
this.center = new FixedVector2(x, y);
this.radius = radius instanceof Fixed ? radius : new Fixed(radius);
}
}
/**
* Get the X coordinate of the center
* 获取中心点的X坐标
*/
get x() {
return this.center.x;
}
/**
* Set the X coordinate of the center
* 设置中心点的X坐标
*/
set x(value) {
this.center.x = value instanceof Fixed ? value : new Fixed(value);
}
/**
* Get the Y coordinate of the center
* 获取中心点的Y坐标
*/
get y() {
return this.center.y;
}
/**
* Set the Y coordinate of the center
* 设置中心点的Y坐标
*/
set y(value) {
this.center.y = value instanceof Fixed ? value : new Fixed(value);
}
/**
* Get the diameter of the circle
* 获取圆的直径
*/
get diameter() {
return this.radius.multiply(new Fixed(2));
}
/**
* Set the diameter of the circle (updates radius)
* 设置圆的直径(更新半径)
*/
set diameter(value) {
const d = value instanceof Fixed ? value : new Fixed(value);
this.radius = d.divide(new Fixed(2));
}
/**
* Check if a point is inside this circle
* 检查点是否在此圆内
*/
contains(point) {
const distance = this.center.distance(point);
return distance.lessThan(this.radius);
}
/**
* Check if a point is inside this circle (inclusive of edge)
* 检查点是否在此圆内(包含边缘)
*/
containsInclusive(point) {
const distance = this.center.distance(point);
return distance.lessThanOrEqual(this.radius);
}
/**
* Check if this circle intersects with another circle
* 检查此圆是否与另一个圆相交
*/
intersects(other) {
const distance = this.center.distance(other.center);
const radiusSum = this.radius.add(other.radius);
return distance.lessThan(radiusSum);
}
/**
* Check if this circle intersects with a rectangle
* 检查此圆是否与矩形相交
*/
intersectsRect(rect) {
// Find the closest point on the rectangle to the circle center
const closestX = this.center.x.clamp(rect.left, rect.right);
const closestY = this.center.y.clamp(rect.top, rect.bottom);
const closest = new FixedVector2(closestX, closestY);
// Check if the distance to the closest point is less than the radius
const distance = this.center.distance(closest);
return distance.lessThanOrEqual(this.radius);
}
/**
* Check if this circle completely contains another circle
* 检查此圆是否完全包含另一个圆
*/
containsCircle(other) {
const distance = this.center.distance(other.center);
const radiusDiff = this.radius.subtract(other.radius);
return distance.lessThanOrEqual(radiusDiff) && radiusDiff.greaterThanOrEqual(Fixed.ZERO);
}
/**
* Check if this circle is completely contained within another circle
* 检查此圆是否完全被另一个圆包含
*/
isContainedBy(other) {
return other.containsCircle(this);
}
/**
* Get the area of the circle
* 获取圆的面积
*/
area() {
// Area = π * r²
return Fixed.PI.multiply(this.radius.multiply(this.radius));
}
/**
* Get the circumference of the circle
* 获取圆的周长
*/
circumference() {
// Circumference = 2 * π * r
return new Fixed(2).multiply(Fixed.PI).multiply(this.radius);
}
/**
* Get the bounding rectangle of the circle
* 获取圆的边界矩形
*/
getBounds() {
return new FixedRect(this.center.x.subtract(this.radius), this.center.y.subtract(this.radius), this.diameter, this.diameter);
}
/**
* Move the circle by the given offset
* 按给定偏移量移动圆
*/
translate(offset) {
return new FixedCircle(this.center.add(offset), this.radius);
}
/**
* Scale the circle by the given factor
* 按给定因子缩放圆
*/
scale(factor) {
const f = factor instanceof Fixed ? factor : new Fixed(factor);
return new FixedCircle(this.center, this.radius.multiply(f));
}
/**
* Get the closest point on the circle to the given point
* 获取圆上距离给定点最近的点
*/
closestPointTo(point) {
const direction = point.subtract(this.center).normalize();
return this.center.add(direction.multiply(this.radius));
}
/**
* Get the distance from the circle edge to the given point
* 获取从圆边缘到给定点的距离
*/
distanceToPoint(point) {
const centerDistance = this.center.distance(point);
return centerDistance.subtract(this.radius);
}
/**
* Get a point on the circle at the given angle
* 获取圆上给定角度的点
*
* @param angle - Angle in radians (0 = right, π/2 = up)
*/
pointAtAngle(angle) {
const a = angle instanceof Fixed ? angle : new Fixed(angle);
const x = this.center.x.add(this.radius.multiply(a.cos()));
const y = this.center.y.add(this.radius.multiply(a.sin()));
return new FixedVector2(x, y);
}
/**
* Get the angle from the center to the given point
* 获取从中心到给定点的角度
*/
angleToPoint(point) {
const direction = point.subtract(this.center);
return direction.angle();
}
/**
* Create a copy of this circle
* 创建此圆的副本
*/
clone() {
return new FixedCircle(this.center.clone(), this.radius);
}
/**
* Check if this circle equals another circle
* 检查此圆是否等于另一个圆
*/
equals(other) {
return this.center.equals(other.center) && this.radius.equals(other.radius);
}
/**
* Convert to string representation
* 转换为字符串表示
*/
toString() {
return `FixedCircle(center: ${this.center.toString()}, radius: ${this.radius.toString()})`;
}
/**
* Convert to a regular number array [x, y, radius]
* 转换为普通数字数组 [x, y, radius]
*/
toArray() {
return [this.center.x.toNumber(), this.center.y.toNumber(), this.radius.toNumber()];
}
/**
* Create a circle from an array [x, y, radius]
* 从数组创建圆 [x, y, radius]
*/
static fromArray(array) {
if (array.length !== 3) {
throw new Error('Array must have exactly 3 elements');
}
return new FixedCircle(array[0], array[1], array[2]);
}
/**
* Create a circle that encompasses the given points
* 创建包含给定点的圆
*/
static fromPoints(points) {
if (points.length === 0) {
return new FixedCircle(FixedVector2.ZERO, Fixed.ZERO);
}
if (points.length === 1) {
return new FixedCircle(points[0], Fixed.ZERO);
}
// Simple implementation: find center as average of points, radius as max distance
let centerX = Fixed.ZERO;
let centerY = Fixed.ZERO;
for (const point of points) {
centerX = centerX.add(point.x);
centerY = centerY.add(point.y);
}
const count = new Fixed(points.length);
const center = new FixedVector2(centerX.divide(count), centerY.divide(count));
let maxDistance = Fixed.ZERO;
for (const point of points) {
const distance = center.distance(point);
if (distance.greaterThan(maxDistance)) {
maxDistance = distance;
}
}
return new FixedCircle(center, maxDistance);
}
/**
* Create a circle from a bounding rectangle (inscribed circle)
* 从边界矩形创建圆(内接圆)
*/
static inscribedInRect(rect) {
const center = rect.center;
const radius = Fixed.min(rect.width, rect.height).divide(new Fixed(2));
return new FixedCircle(center, radius);
}
/**
* Create a circle from a bounding rectangle (circumscribed circle)
* 从边界矩形创建圆(外接圆)
*/
static circumscribedAroundRect(rect) {
const center = rect.center;
const radius = rect.width.multiply(rect.width)
.add(rect.height.multiply(rect.height))
.sqrt()
.divide(new Fixed(2));
return new FixedCircle(center, radius);
}
}
// Static constants
FixedCircle.ZERO = new FixedCircle(FixedVector2.ZERO, Fixed.ZERO);
FixedCircle.UNIT = new FixedCircle(FixedVector2.ZERO, Fixed.ONE);
/**
* Utility functions for geometric calculations using fixed-point arithmetic
* 使用定点算术进行几何计算的实用函数
*/
class GeometryUtils {
/**
* Calculate the distance between two points
* 计算两点之间的距离
*/
static distance(point1, point2) {
return point1.distance(point2);
}
/**
* Calculate the squared distance between two points (faster than distance)
* 计算两点之间的距离平方(比距离计算更快)
*/
static distanceSquared(point1, point2)