///|
/// 三维向量。几何内核只做静态求值,所有分量用 `Double`:
/// 这里的正确性标准是「与解析公式逐点一致」,不是跨后端逐位一致
/// (后者是 moonroll 的定点数要解决的问题)。
pub(all) struct Vec3 {
x : Double
y : Double
z : Double
}
///|
pub fn Vec3::new(x : Double, y : Double, z : Double) -> Vec3 {
{ x, y, z, }
}
///|
pub fn Vec3::zero() -> Vec3 {
{ x: 0.0, y: 0.0, z: 0.0, }
}
///|
pub fn Vec3::splat(v : Double) -> Vec3 {
{ x: v, y: v, z: v, }
}
///|
pub fn Vec3::add(self : Vec3, o : Vec3) -> Vec3 {
{ x: self.x + o.x, y: self.y + o.y, z: self.z + o.z, }
}
///|
pub fn Vec3::sub(self : Vec3, o : Vec3) -> Vec3 {
{ x: self.x - o.x, y: self.y - o.y, z: self.z - o.z, }
}
///|
pub fn Vec3::neg(self : Vec3) -> Vec3 {
{ x: -self.x, y: -self.y, z: -self.z, }
}
///|
pub fn Vec3::mul(self : Vec3, s : Double) -> Vec3 {
{ x: self.x * s, y: self.y * s, z: self.z * s, }
}
///|
/// 逐分量乘。
pub fn Vec3::mul_vec(self : Vec3, o : Vec3) -> Vec3 {
{ x: self.x * o.x, y: self.y * o.y, z: self.z * o.z, }
}
///|
pub fn Vec3::div(self : Vec3, s : Double) -> Vec3 {
{ x: self.x / s, y: self.y / s, z: self.z / s, }
}
///|
pub fn Vec3::dot(self : Vec3, o : Vec3) -> Double {
self.x * o.x + self.y * o.y + self.z * o.z
}
///|
pub fn Vec3::cross(self : Vec3, o : Vec3) -> Vec3 {
{
x: self.y * o.z - self.z * o.y,
y: self.z * o.x - self.x * o.z,
z: self.x * o.y - self.y * o.x,
}
}
///|
pub fn Vec3::length_sq(self : Vec3) -> Double {
self.dot(self)
}
///|
pub fn Vec3::length(self : Vec3) -> Double {
self.length_sq().sqrt()
}
///|
/// 零向量归一化返回零向量:调用方不该靠 panic 处理退化输入。
pub fn Vec3::normalize(self : Vec3) -> Vec3 {
let len = self.length()
if len == 0.0 {
self
} else {
self.div(len)
}
}
///|
pub fn Vec3::abs(self : Vec3) -> Vec3 {
{
x: if self.x < 0.0 {
-self.x
} else {
self.x
},
y: if self.y < 0.0 {
-self.y
} else {
self.y
},
z: if self.z < 0.0 {
-self.z
} else {
self.z
},
}
}
///|
/// 逐分量 `max(v, 0)`,SDF 盒公式里反复用到。
pub fn Vec3::max_zero(self : Vec3) -> Vec3 {
{
x: if self.x > 0.0 {
self.x
} else {
0.0
},
y: if self.y > 0.0 {
self.y
} else {
0.0
},
z: if self.z > 0.0 {
self.z
} else {
0.0
},
}
}
///|
pub fn Vec3::min(self : Vec3, o : Vec3) -> Vec3 {
{
x: if self.x < o.x {
self.x
} else {
o.x
},
y: if self.y < o.y {
self.y
} else {
o.y
},
z: if self.z < o.z {
self.z
} else {
o.z
},
}
}
///|
pub fn Vec3::max(self : Vec3, o : Vec3) -> Vec3 {
{
x: if self.x > o.x {
self.x
} else {
o.x
},
y: if self.y > o.y {
self.y
} else {
o.y
},
z: if self.z > o.z {
self.z
} else {
o.z
},
}
}
///|
pub fn Vec3::lerp(self : Vec3, o : Vec3, t : Double) -> Vec3 {
self.add(o.sub(self).mul(t))
}
///|
/// 3×3 矩阵,按列存:`c0`/`c1`/`c2` 是三个基向量。
///
/// 按列存是为了 `mul_vec` 写成一次线性组合——这也是后面做旋转
/// 与梯度变换时唯一需要的方向。
pub(all) struct Mat3 {
c0 : Vec3
c1 : Vec3
c2 : Vec3
}
///|
pub fn Mat3::identity() -> Mat3 {
{
c0: Vec3::new(1.0, 0.0, 0.0),
c1: Vec3::new(0.0, 1.0, 0.0),
c2: Vec3::new(0.0, 0.0, 1.0),
}
}
///|
pub fn Mat3::from_columns(c0 : Vec3, c1 : Vec3, c2 : Vec3) -> Mat3 {
{ c0, c1, c2, }
}
///|
pub fn Mat3::mul_vec(self : Mat3, v : Vec3) -> Vec3 {
self.c0.mul(v.x).add(self.c1.mul(v.y)).add(self.c2.mul(v.z))
}
///|
/// 转置:旋转的逆。SDF 变换里要用它把世界坐标拉回局部坐标。
pub fn Mat3::transpose(self : Mat3) -> Mat3 {
{
c0: Vec3::new(self.c0.x, self.c1.x, self.c2.x),
c1: Vec3::new(self.c0.y, self.c1.y, self.c2.y),
c2: Vec3::new(self.c0.z, self.c1.z, self.c2.z),
}
}
///|
/// 轴对齐包围盒。`min` 逐分量小于等于 `max` 才是有效盒。
pub(all) struct Aabb {
min : Vec3
max : Vec3
}
///|
pub fn Aabb::new(min : Vec3, max : Vec3) -> Aabb {
{ min, max, }
}
///|
pub fn Aabb::from_center_half(center : Vec3, half : Vec3) -> Aabb {
{ min: center.sub(half), max: center.add(half), }
}
///|
pub fn Aabb::center(self : Aabb) -> Vec3 {
self.min.add(self.max).mul(0.5)
}
///|
pub fn Aabb::size(self : Aabb) -> Vec3 {
self.max.sub(self.min)
}
///|
/// 空盒用「min > max」表示,不额外加标志位。
pub fn Aabb::is_empty(self : Aabb) -> Bool {
self.min.x > self.max.x || self.min.y > self.max.y || self.min.z > self.max.z
}
///|
pub fn Aabb::union(self : Aabb, o : Aabb) -> Aabb {
{ min: self.min.min(o.min), max: self.max.max(o.max), }
}
///|
pub fn Aabb::intersect(self : Aabb, o : Aabb) -> Aabb {
{ min: self.min.max(o.min), max: self.max.min(o.max), }
}
///|
/// 各方向等比外扩 `r`,网格化时用来给采样留一圈边界。
pub fn Aabb::expand(self : Aabb, r : Double) -> Aabb {
{ min: self.min.sub(Vec3::splat(r)), max: self.max.add(Vec3::splat(r)), }
}
///|
pub fn Aabb::contains(self : Aabb, p : Vec3) -> Bool {
p.x >= self.min.x &&
p.y >= self.min.y &&
p.z >= self.min.z &&
p.x <= self.max.x &&
p.y <= self.max.y &&
p.z <= self.max.z
}
///|
pub fn Aabb::diagonal(self : Aabb) -> Double {
self.size().length()
}