向量数学库

基本信息

属性 值
包 com.github.lunatrius.core.util.vector
类数 9(2/3/4 维 × i 整数 / f 浮点 / d 双精度)
总行数 1367
内存占用 0 —— 全部成员为 public static 方法或常量,无实例字段

这是本 mod 最大的 API 面(9/28 个 Java 文件)。EntityHelper 是本 mod 内唯一使用它的类,返回 Vector3f 与 Vector3i。

类继承关系

类 继承 坐标字段
Vector2d 无(基类) public double x, public double y
Vector2f 无(基类) public float x, public float y
Vector2i 无(基类) public int x, public int y
Vector3d extends Vector2d 继承 x y,新增 public double z
Vector3f extends Vector2f 继承 x y,新增 public float z
Vector3i extends Vector2i 继承 x y,新增 public int z
Vector4d extends Vector3d 继承 x y z,新增 public double w
Vector4f extends Vector3f 继承 x y z,新增 public float w
Vector4i extends Vector3i 继承 x y z,新增 public int w

[!IMPORTANT] 三个 Vector3* 继承对应的 Vector2*,三个 Vector4* 继承对应的 Vector3*——形成 3 条并行的继承链。坐标字段是 public 可直接读写,与 getX/setX 访问器并存。

构造函数

每个类 4 个重载,模式完全一致(以 Vector3f 为例):

签名 行为
public Vector3f() 各分量取 0
public Vector3f(Vector3f vec) 复制构造
public Vector3f(float num) 所有分量取同一值 num
public Vector3f(float x, float y, float z) 分量分别赋值

通用方法

以下方法在每一族(2 维 / 3 维 / 4 维)内的签名完全一致,只是分量数与类型不同。

访问器

签名 说明
getX() / getY() 读 x / y(所有类都有)
getZ() / getW() 读 z / w(仅 Vector3* / Vector4* 有)
setX(...) / setY(...) 写 x / y(所有类都有)
setZ(...) / setW() 写 z / w(仅 Vector3* / Vector4* 有)
set(VectorXX vec) 用另一向量覆盖全部分量,返回 this
set(分量 1, 分量 2, …) 逐分量覆盖,返回 this

[!NOTE] setX / setY / setZ / setW 与 getX 等都是 final;set 两个重载不是 final(子类要重载 set 时依赖这一点)。

长度与距离

签名 返回类型(以 2 维 d 为例)
length() double = Math.sqrt(lengthSquared()),标 final
lengthSquared() double = x*x + y*y(3/4 维加上 z、w)
lengthTo(VectorXX vec) double = Math.sqrt(lengthSquaredTo(vec)),标 final
lengthSquaredTo(VectorXX vec) double = pow2(差x) + pow2(差y) + …
protected final X pow2(X num) return num * num;(内部辅助)

[!IMPORTANT] 返回类型随分量类型变化,浮点与整数不一致:

类 length() lengthSquared() lengthTo() lengthSquaredTo() dot()
Vector2d / Vector3d / Vector4d double double double double double
Vector2f / Vector3f / Vector4f double float double float float
Vector2i / Vector3i / Vector4i double int double int double

即 length() 与 lengthTo() 在 9 个类中全部返回 double(内部用 Math.sqrt),而平方长度与点积跟随分量类型。

运算

签名 行为
public final VectorXX normalize() double len = length(); if (len != 0.0) return scale(1.0 / len); return this;
public VectorXX negate() 各分量取负,返回 this
public <T> dot(VectorXX vec) 各分量相乘求和
public VectorXX scale(double scale) 各分量 *= scale(参数恒为 double,即使 Vector2i 的分量是 int)
public VectorXX add(VectorXX vec) 逐分量相加,返回 this
public VectorXX add(分量…) 逐分量加上独立参数,返回 this
public VectorXX sub(VectorXX vec) 逐分量相减,返回 this
public VectorXX sub(分量…) 逐分量减去独立参数,返回 this

[!WARNING] 除 length() / lengthTo() / lengthSquaredTo() / pow2 外,几乎所有运算方法都原地修改 this 并返回它(链式写法),不做不可变语义。dot() 与 lengthSquared() 是纯查询。误以为它们返回新对象会直接破坏原向量。

[!NOTE] normalize() 对 Vector*i 同样可用:它调用 scale(1.0 / len),而 scale 的参数是 double,所以整数向量的分量会被隐式回落到 int 截断(如 Vector2i(3, 4).normalize() → (int)(0.6) / (int)(0.8) → (0, 0))。同理 dot() 在 Vector*i 上返回 double,但内部按 int 相乘后相加再隐式转回 double。

克隆与相等

签名 行为
public VectorXX clone() 标注 @Override,返回 new VectorXX(this)
public boolean equals(Object obj) 标注 @Override,obj instanceof VectorXX && equals((VectorXX) obj)
public boolean equals(VectorXX vec) 浮点:转调 equals(vec, EPSILON);整数:逐分量 ==
public boolean equals(VectorXX vec, double epsilon) / (…, float epsilon) 仅浮点类有:Math.abs(this.x - vec.x) < epsilon && …
public String toString() 标注 @Override,String.format("[%s, %s]", …),逗号分隔(3/4 维继续追加)

[!IMPORTANT] 浮点类 equals(Object) 不使用 epsilon 之外的精度,判定规则是严格小于:

  • Vector2d.equals(vec) → equals(vec, DOUBLE_EPSILON)
  • Vector2f.equals(vec) → equals(vec, FLOAT_EPSILON)
  • Vector2i.equals(vec) → 无重载,直接 this.x == vec.x && this.y == vec.y

精度常量

类 常量 源码值 实际数值
Vector2d DOUBLE_EPSILON 10e-6f ≈ 1.0 × 10⁻⁵
Vector2f FLOAT_EPSILON 10e-6f ≈ 1.0 × 10⁻⁵

[!WARNING] 常量名暗示 1 × 10⁻⁶,但字面量写的是 10e-6f(10 × 10⁻⁶ = 10⁻⁵),且 Vector2d.DOUBLE_EPSILON 声明为 double 却用浮点字面量 f 初始化。实际容差是 0.00001 而不是 0.000001,比命名意图宽 10 倍。这两个常量分别定义在 Vector2d / Vector2f 上,被 3 维、4 维的同族子类继承复用。

类型转换

每个类提供 4 个转换方法,分为「无参新建」与「写入传入向量」两两成对:

签名模式 行为
public VectorYY toVectorYY() new VectorYY(转换后的各分量) — 分配新对象
public VectorYY toVectorYY(VectorYY vec) vec.set(转换后的各分量) — 写入调用方提供的对象,返回该对象

转换矩阵

源类 可转目标(无参 / 带参各一)
Vector2d Vector2i、Vector2f
Vector2f Vector2i、Vector2d
Vector2i Vector2f、Vector2d
Vector3d Vector3i、Vector3f
Vector3f Vector3i、Vector3d
Vector3i Vector3f、Vector3d
Vector4d Vector4i、Vector4f
Vector4f Vector4i、Vector4d
Vector4i Vector4f、Vector4d

[!IMPORTANT] 只能向同维数转换,且必须转成另一种分量类型(不转回自身类型)。 因此:

  • 没有降维转换:4 个 Vector4* 类全部没有 toVector3x() 方法,无法从 4 维降到 3 维
  • 3 个 Vector3* 也没有 toVector2x()
  • 跨族只能靠字段继承间接访问(Vector3f 是 Vector2f 的子类,其 x / y 可直接读)
  • Vector4d 自身类型无法再转成 Vector4d(无意义),要复制请用 clone()

转换取整规则

转换 取整方式 示例
d → i (int) Math.floor(...) Vector2d(1.7, -0.5).toVector2i() → Vector2i(1, -1)
d → f (float) Math.floor(...) Vector2d(1.7, -0.5).toVector2f() → 1.7f 变 1.0f、-0.5f 变 -1.0f
f → i (int) Math.floor(...) Vector2f(1.7f, -0.5f).toVector2i() → Vector2i(1, -1)
f → d 直接隐式提升 Vector2f(1.7f, …).toVector2d() → 1.7000000476837158
i → f / i → d 直接隐式提升 Vector2i(3, -4).toVector2f() → 3.0f

[!WARNING] 向浮点类型转换时也会 Math.floor,即 Vector2d(1.7, 2.3).toVector2f() 得到的是 1.0f 而不是 1.7f。这会丢失小数部分,与「向浮点类型转换应保留精度」的直觉相反。另外 f → d 因 IEEE 754 单精度无法精确表示某些小数,往返转换可能出现 1.7000000476837158 这类尾数。

完整方法清单(以 Vector4d 为例)

分类 方法
字段 public double x, y, z, w
常量 继承 Vector2d.DOUBLE_EPSILON
构造函数 Vector4d() / Vector4d(Vector4d) / Vector4d(double num) / Vector4d(double x, double y, double z, double w)
访问器 getX getY getZ getW setX setY setZ setW set(Vector4d) set(double×4)
长度 length() lengthSquared() lengthTo(Vector4d) lengthSquaredTo(Vector4d) protected pow2(double)
运算 normalize() negate() dot(Vector4d) scale(double) add(Vector4d) add(double×4) sub(Vector4d) sub(double×4)
转换 toVector4i() toVector4i(Vector4i) toVector4f() toVector4f(Vector4f)
对象契约 clone() equals(Object) equals(Vector4d) equals(Vector4d, double) toString()

[!NOTE] Vector3* / Vector4* 不重复声明 length()、normalize()、getX / setX / pow2 等——它们直接从父类继承并使用 this.x / this.y,父类的运算方法则被子类按分量数重载覆盖(add、sub、dot、negate、scale、lengthSquared 等)。

使用示例

// 新建 + 链式运算(注意全部原地修改)
Vector3f dir = new Vector3f(3.0f, 0.0f, 4.0f);   // 长度 5
dir.normalize();                                  // → (0.6, 0.0, 0.8)

// 复用向量,零分配
Vector3i block = new Vector3i();
block.set(entity.posX, entity.posY, entity.posZ);  // 由调用方完成 floor

// 整数向量求方向
Vector3i step = new Vector3i(1, 0, 0);
step.normalize();                                  // → (0, 0, 0)(1.0 截断为 0)

相关条目