docs: finish the foundation layer of the bilingual sweep

All 180 comment blocks across apsis-core, apsis-units and apsis-math now
carry both languages. The checker confirms no mangled bytes and no English
word left inside a Chinese sentence, two mistakes that occurred while
writing them and are now caught mechanically.
This commit is contained in:
ApsisRS contributors 2026-10-02 00:33:19 +08:00
parent a828b0b9e6
commit 4579ed064a
6 changed files with 389 additions and 0 deletions

View File

@ -11,44 +11,77 @@ use core::ops::Mul;
/// entry in the first row and second column. The fields are listed in /// entry in the first row and second column. The fields are listed in
/// column-major order, and the same order is used by [`Mat3::from_columns`] /// column-major order, and the same order is used by [`Mat3::from_columns`]
/// and [`Mat3::column`]. /// and [`Mat3::column`].
///
/// 按列主序存储的三乘三矩阵。
///
/// 字段名遵循 `m<行><列>` 约定,故 `m01` 是第一行第二列的元素。字段按列主序
/// 列出,[`Mat3::from_columns`] 与 [`Mat3::column`] 使用同样的顺序。
#[derive(Debug, Clone, Copy, PartialEq, Default)] #[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Mat3 { pub struct Mat3 {
/// First row, first column. /// First row, first column.
///
/// 第一行第一列。
pub m00: f64, pub m00: f64,
/// Second row, first column. /// Second row, first column.
///
/// 第二行第一列。
pub m10: f64, pub m10: f64,
/// Third row, first column. /// Third row, first column.
///
/// 第三行第一列。
pub m20: f64, pub m20: f64,
/// First row, second column. /// First row, second column.
///
/// 第一行第二列。
pub m01: f64, pub m01: f64,
/// Second row, second column. /// Second row, second column.
///
/// 第二行第二列。
pub m11: f64, pub m11: f64,
/// Third row, second column. /// Third row, second column.
///
/// 第三行第二列。
pub m21: f64, pub m21: f64,
/// First row, third column. /// First row, third column.
///
/// 第一行第三列。
pub m02: f64, pub m02: f64,
/// Second row, third column. /// Second row, third column.
///
/// 第二行第三列。
pub m12: f64, pub m12: f64,
/// Third row, third column. /// Third row, third column.
///
/// 第三行第三列。
pub m22: f64, pub m22: f64,
} }
impl Mat3 { impl Mat3 {
/// The zero matrix. /// The zero matrix.
///
/// 零矩阵。
pub const ZERO: Self = Self::from_rows_raw([0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]); pub const ZERO: Self = Self::from_rows_raw([0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]);
/// The identity matrix. /// The identity matrix.
///
/// 单位矩阵。
pub const IDENTITY: Self = pub const IDENTITY: Self =
Self::from_rows_raw([1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]); Self::from_rows_raw([1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]);
/// Creates a matrix from its nine entries in row-major order. /// Creates a matrix from its nine entries in row-major order.
/// ///
/// The arguments are grouped by row: `row0`, then `row1`, then `row2`. /// The arguments are grouped by row: `row0`, then `row1`, then `row2`.
///
/// 由九个元素按行主序创建矩阵。
///
/// 参数按行分组:先 `row0`,再 `row1`,最后 `row2`。
pub const fn from_rows(row0: [f64; 3], row1: [f64; 3], row2: [f64; 3]) -> Self { pub const fn from_rows(row0: [f64; 3], row1: [f64; 3], row2: [f64; 3]) -> Self {
Self::from_rows_raw(row0, row1, row2) Self::from_rows_raw(row0, row1, row2)
} }
/// Creates a matrix from three column vectors. /// Creates a matrix from three column vectors.
///
/// 由三个列向量创建矩阵。
pub const fn from_columns(c0: Vec3, c1: Vec3, c2: Vec3) -> Self { pub const fn from_columns(c0: Vec3, c1: Vec3, c2: Vec3) -> Self {
Self { Self {
m00: c0.x, m00: c0.x,
@ -64,6 +97,8 @@ impl Mat3 {
} }
/// Creates a diagonal matrix from a vector of diagonal entries. /// Creates a diagonal matrix from a vector of diagonal entries.
///
/// 由对角元素向量创建对角矩阵。
pub const fn from_diagonal(diagonal: Vec3) -> Self { pub const fn from_diagonal(diagonal: Vec3) -> Self {
Self::from_rows_raw( Self::from_rows_raw(
[diagonal.x, 0.0, 0.0], [diagonal.x, 0.0, 0.0],
@ -76,6 +111,11 @@ impl Mat3 {
/// ///
/// The quaternion is assumed to have unit length. A non-unit quaternion /// The quaternion is assumed to have unit length. A non-unit quaternion
/// produces a scaled matrix, which [`Mat3::is_rotation`] then rejects. /// produces a scaled matrix, which [`Mat3::is_rotation`] then rejects.
///
/// 由单位四元数创建旋转矩阵。
///
/// 假定该四元数为单位长度。非单位四元数会得到一个带缩放的矩阵,随后被
/// [`Mat3::is_rotation`] 拒绝。
pub fn from_quat(q: Quat) -> Self { pub fn from_quat(q: Quat) -> Self {
let (w, x, y, z) = (q.w, q.x, q.y, q.z); let (w, x, y, z) = (q.w, q.x, q.y, q.z);
Self::from_rows( Self::from_rows(
@ -101,21 +141,31 @@ impl Mat3 {
/// `axis`. /// `axis`.
/// ///
/// Returns `None` when the axis has zero or non-finite length. /// Returns `None` when the axis has zero or non-finite length.
///
/// 创建绕单位轴 `axis` 旋转 `angle` 弧度的旋转矩阵。
///
/// 轴长度为零或非有限时返回 `None`。
pub fn from_axis_angle(axis: Vec3, angle: f64) -> Option<Self> { pub fn from_axis_angle(axis: Vec3, angle: f64) -> Option<Self> {
Quat::from_axis_angle(axis, angle).map(Self::from_quat) Quat::from_axis_angle(axis, angle).map(Self::from_quat)
} }
/// Creates a rotation matrix of `angle` radians about the first axis. /// Creates a rotation matrix of `angle` radians about the first axis.
///
/// 创建绕第一轴旋转 `angle` 弧度的旋转矩阵。
pub fn from_rotation_x(angle: f64) -> Self { pub fn from_rotation_x(angle: f64) -> Self {
Self::from_quat(Quat::from_rotation_x(angle)) Self::from_quat(Quat::from_rotation_x(angle))
} }
/// Creates a rotation matrix of `angle` radians about the second axis. /// Creates a rotation matrix of `angle` radians about the second axis.
///
/// 创建绕第二轴旋转 `angle` 弧度的旋转矩阵。
pub fn from_rotation_y(angle: f64) -> Self { pub fn from_rotation_y(angle: f64) -> Self {
Self::from_quat(Quat::from_rotation_y(angle)) Self::from_quat(Quat::from_rotation_y(angle))
} }
/// Creates a rotation matrix of `angle` radians about the third axis. /// Creates a rotation matrix of `angle` radians about the third axis.
///
/// 创建绕第三轴旋转 `angle` 弧度的旋转矩阵。
pub fn from_rotation_z(angle: f64) -> Self { pub fn from_rotation_z(angle: f64) -> Self {
Self::from_quat(Quat::from_rotation_z(angle)) Self::from_quat(Quat::from_rotation_z(angle))
} }
@ -123,6 +173,10 @@ impl Mat3 {
/// Returns the entry in row `row` and column `column`. /// Returns the entry in row `row` and column `column`.
/// ///
/// Returns `None` when either index is outside `0..=2`. /// Returns `None` when either index is outside `0..=2`.
///
/// 返回第 `row` 行第 `column` 列的元素。
///
/// 任一下标超出 `0..=2` 时返回 `None`。
pub const fn get(self, row: usize, column: usize) -> Option<f64> { pub const fn get(self, row: usize, column: usize) -> Option<f64> {
match (row, column) { match (row, column) {
(0, 0) => Some(self.m00), (0, 0) => Some(self.m00),
@ -141,6 +195,10 @@ impl Mat3 {
/// Returns column `index`. /// Returns column `index`.
/// ///
/// Returns `None` when `index` is outside `0..=2`. /// Returns `None` when `index` is outside `0..=2`.
///
/// 返回第 `index` 列。
///
/// `index` 超出 `0..=2` 时返回 `None`。
pub const fn column(self, index: usize) -> Option<Vec3> { pub const fn column(self, index: usize) -> Option<Vec3> {
match index { match index {
0 => Some(Vec3::new(self.m00, self.m10, self.m20)), 0 => Some(Vec3::new(self.m00, self.m10, self.m20)),
@ -153,6 +211,10 @@ impl Mat3 {
/// Returns row `index`. /// Returns row `index`.
/// ///
/// Returns `None` when `index` is outside `0..=2`. /// Returns `None` when `index` is outside `0..=2`.
///
/// 返回第 `index` 行。
///
/// `index` 超出 `0..=2` 时返回 `None`。
pub const fn row(self, index: usize) -> Option<Vec3> { pub const fn row(self, index: usize) -> Option<Vec3> {
match index { match index {
0 => Some(Vec3::new(self.m00, self.m01, self.m02)), 0 => Some(Vec3::new(self.m00, self.m01, self.m02)),
@ -163,6 +225,8 @@ impl Mat3 {
} }
/// Returns the transpose. /// Returns the transpose.
///
/// 返回转置。
pub const fn transpose(self) -> Self { pub const fn transpose(self) -> Self {
Self { Self {
m00: self.m00, m00: self.m00,
@ -178,11 +242,15 @@ impl Mat3 {
} }
/// Returns the sum of the diagonal entries. /// Returns the sum of the diagonal entries.
///
/// 返回对角元素之和。
pub const fn trace(self) -> f64 { pub const fn trace(self) -> f64 {
self.m00 + self.m11 + self.m22 self.m00 + self.m11 + self.m22
} }
/// Returns the determinant. /// Returns the determinant.
///
/// 返回行列式。
pub fn determinant(self) -> f64 { pub fn determinant(self) -> f64 {
self.m00 * (self.m11 * self.m22 - self.m12 * self.m21) self.m00 * (self.m11 * self.m22 - self.m12 * self.m21)
- self.m01 * (self.m10 * self.m22 - self.m12 * self.m20) - self.m01 * (self.m10 * self.m22 - self.m12 * self.m20)
@ -194,6 +262,11 @@ impl Mat3 {
/// A matrix is a rotation when it is orthonormal with determinant one. /// A matrix is a rotation when it is orthonormal with determinant one.
/// The check compares `M^T M` to the identity entry by entry and then /// The check compares `M^T M` to the identity entry by entry and then
/// compares the determinant to one. /// compares the determinant to one.
///
/// 当该矩阵在 `tolerance` 内为旋转矩阵时返回 `true`。
///
/// 正交且行列式为一的矩阵即旋转矩阵。该检查逐元素比较 `M^T M` 与单位
/// 矩阵,然后把行列式与一比较。
pub fn is_rotation(self, tolerance: f64) -> bool { pub fn is_rotation(self, tolerance: f64) -> bool {
let product = self.transpose() * self; let product = self.transpose() * self;
let identity = Self::IDENTITY; let identity = Self::IDENTITY;
@ -209,6 +282,8 @@ impl Mat3 {
} }
/// Returns `true` when all nine entries are finite. /// Returns `true` when all nine entries are finite.
///
/// 当九个元素都有限时返回 `true`。
pub fn is_finite(self) -> bool { pub fn is_finite(self) -> bool {
[ [
self.m00, self.m10, self.m20, self.m01, self.m11, self.m21, self.m02, self.m12, self.m00, self.m10, self.m20, self.m01, self.m11, self.m21, self.m02, self.m12,
@ -219,6 +294,8 @@ impl Mat3 {
} }
/// Returns a copy of this matrix with one entry replaced. /// Returns a copy of this matrix with one entry replaced.
///
/// 返回把某一个元素替换后的矩阵副本。
const fn with_entry(self, row: usize, column: usize, value: f64) -> Self { const fn with_entry(self, row: usize, column: usize, value: f64) -> Self {
let mut entries = [ let mut entries = [
[self.m00, self.m01, self.m02], [self.m00, self.m01, self.m02],
@ -230,6 +307,8 @@ impl Mat3 {
} }
/// Const constructor used by the associated constants. /// Const constructor used by the associated constants.
///
/// 供各关联常量使用的 const 构造函数。
const fn from_rows_raw(row0: [f64; 3], row1: [f64; 3], row2: [f64; 3]) -> Self { const fn from_rows_raw(row0: [f64; 3], row1: [f64; 3], row2: [f64; 3]) -> Self {
Self { Self {
m00: row0[0], m00: row0[0],

View File

@ -35,6 +35,10 @@ use core::hint;
/// This is the machine's parallelism as reported by the standard library, at /// This is the machine's parallelism as reported by the standard library, at
/// least one. It is the number the callers use to decide whether a parallel /// least one. It is the number the callers use to decide whether a parallel
/// run is worth starting. /// run is worth starting.
///
/// 某项工作应使用的线程数。
///
/// 这是标准库报告的机器并行度,至少为一。调用方据此决定并行运行是否值得启动。
pub fn thread_count() -> usize { pub fn thread_count() -> usize {
std::thread::available_parallelism() std::thread::available_parallelism()
.map(|count| count.get()) .map(|count| count.get())
@ -48,6 +52,12 @@ pub fn thread_count() -> usize {
/// there is enough work for a second. The threshold is a per item cost of /// there is enough work for a second. The threshold is a per item cost of
/// roughly a microsecond, below which starting a thread cannot pay for /// roughly a microsecond, below which starting a thread cannot pay for
/// itself. /// itself.
///
/// 对 `len` 个元素的循环应使用的线程数。
///
/// 比线程数还短的循环会让多数线程无所事事,却仍要付出启动代价,因此在工作量
/// 足以分给第二个线程之前本函数返回一个线程。阈值是每项约一微秒的开销,低于
/// 此值启动线程无法收回成本。
pub fn threads_for(len: usize) -> usize { pub fn threads_for(len: usize) -> usize {
const ITEMS_PER_THREAD: usize = 1_024; const ITEMS_PER_THREAD: usize = 1_024;
thread_count().min(len.div_ceil(ITEMS_PER_THREAD)).max(1) thread_count().min(len.div_ceil(ITEMS_PER_THREAD)).max(1)
@ -70,6 +80,21 @@ pub fn threads_for(len: usize) -> usize {
/// If the closure panics, the panic is resumed on the calling thread after /// If the closure panics, the panic is resumed on the calling thread after
/// the other threads have been joined, so a failure surfaces at the call /// the other threads have been joined, so a failure surfaces at the call
/// rather than in a detached thread. /// rather than in a detached thread.
///
/// 对一段范围内的每个下标各调用一次闭包,分摊到多个线程上执行。
///
/// 该范围被切分为每线程一段连续区间,每个线程对落在自己区间内的下标调用闭包。
/// 闭包可能在任意线程上被调用,故必须是 `Send` 与 `Sync`;它捕获的数据是被共享
/// 而非复制的。本次调用只有在全部线程结束后才返回,因此闭包写入的数据此后可安全
/// 读取。
///
/// 闭包收到的是下标而非元素,这让签名与具体集合无关:调用方自行读取输入并
/// 写出输出,两者不会重叠。
///
/// # Panics
///
/// 若闭包发生 panic,该 panic 会在其它线程 join 之后于调用线程上重新抛出,
/// 因此失败出现在调用处,而不是某个已分离的线程中。
pub fn for_each_index<F>(len: usize, operation: F) pub fn for_each_index<F>(len: usize, operation: F)
where where
F: Fn(usize) + Sync, F: Fn(usize) + Sync,
@ -78,6 +103,8 @@ where
if threads <= 1 { if threads <= 1 {
// A single thread would spawn and join one thread to do exactly what // A single thread would spawn and join one thread to do exactly what
// this loop does, so it runs directly. // this loop does, so it runs directly.
// 单线程情况下,若走作用域仍要生成并 join 一个线程来做与本循环完全
// 相同的事,故直接执行。
for index in 0..len { for index in 0..len {
operation(index); operation(index);
} }
@ -111,6 +138,14 @@ where
/// The closure is handed the index and returns the value for that index. As /// The closure is handed the index and returns the value for that index. As
/// with [`for_each_index`], it may run on any thread, so captured data must be /// with [`for_each_index`], it may run on any thread, so captured data must be
/// `Sync`. /// `Sync`.
///
/// 为一段范围内的每个下标各收集一个值,分摊到多个线程上执行。
///
/// 当工作的结果是值而非写入共享数组时,本函数是 [`for_each_index`] 的对应形式。
/// 输出按下标顺序返回,与哪个线程产生了哪个元素无关,故不依赖调度。
///
/// 闭包收到下标并返回该下标对应的值。与 [`for_each_index`] 一样,它可能在任意
/// 线程上运行,故捕获的数据必须为 `Sync`。
pub fn map_index<T, F>(len: usize, operation: F) -> Vec<T> pub fn map_index<T, F>(len: usize, operation: F) -> Vec<T>
where where
T: Send, T: Send,
@ -125,6 +160,9 @@ where
// split into disjoint mutable slices up front and no slot is written // split into disjoint mutable slices up front and no slot is written
// twice. `Vec<Option<T>>` is filled in parallel and unwrapped after the // twice. `Vec<Option<T>>` is filled in parallel and unwrapped after the
// scope, which is where the values are moved into their final order. // scope, which is where the values are moved into their final order.
// 每个线程写入输出的一段连续区间,故缓冲区先被切分为互不相交的可变切片,
// 没有槽位被写两次。`Vec<Option<T>>` 并行填充,作用域结束后解包,值在此被
// 移入最终顺序。
let mut slots: Vec<Option<T>> = (0..len).map(|_| None).collect(); let mut slots: Vec<Option<T>> = (0..len).map(|_| None).collect();
let operation = &operation; let operation = &operation;
std::thread::scope(|scope| { std::thread::scope(|scope| {

View File

@ -16,23 +16,45 @@ use core::ops::Mul;
/// `w = cos(theta / 2)`, `(x, y, z) = u * sin(theta / 2)`. Composition uses /// `w = cos(theta / 2)`, `(x, y, z) = u * sin(theta / 2)`. Composition uses
/// the same convention as matrix multiplication: `(a * b)` applied to a /// the same convention as matrix multiplication: `(a * b)` applied to a
/// vector applies `b` first, then `a`. /// vector applies `b` first, then `a`.
///
/// 四元数 `w + xi + yj + zk`。
///
/// 旋转向量的方法假定该四元数为单位长度。乘法与加法则不然,因为任意四元数
/// 运算在该假定下同样有良好定义。用 [`Quat::normalize`] 归一到单位长度,用
/// [`Quat::is_normalized`] 检查。
///
/// 绕单位轴 `u` 旋转 `theta` 的四元数为
/// `w = cos(theta / 2)`、`(x, y, z) = u * sin(theta / 2)`。复合采用与矩阵
/// 乘法相同的约定:`(a * b)` 作用于向量时先作用 `b`,再作用 `a`。
#[derive(Debug, Clone, Copy, PartialEq, Default)] #[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Quat { pub struct Quat {
/// Scalar part. /// Scalar part.
///
/// 标量部分。
pub w: f64, pub w: f64,
/// First component of the vector part. /// First component of the vector part.
///
/// 向量部分的第一分量。
pub x: f64, pub x: f64,
/// Second component of the vector part. /// Second component of the vector part.
///
/// 向量部分的第二分量。
pub y: f64, pub y: f64,
/// Third component of the vector part. /// Third component of the vector part.
///
/// 向量部分的第三分量。
pub z: f64, pub z: f64,
} }
impl Quat { impl Quat {
/// The identity rotation. /// The identity rotation.
///
/// 恒等旋转。
pub const IDENTITY: Self = Self::new(1.0, 0.0, 0.0, 0.0); pub const IDENTITY: Self = Self::new(1.0, 0.0, 0.0, 0.0);
/// Creates a quaternion from its four components. /// Creates a quaternion from its four components.
///
/// 由四个分量创建四元数。
pub const fn new(w: f64, x: f64, y: f64, z: f64) -> Self { pub const fn new(w: f64, x: f64, y: f64, z: f64) -> Self {
Self { w, x, y, z } Self { w, x, y, z }
} }
@ -41,6 +63,10 @@ impl Quat {
/// ///
/// The axis is normalized internally. Returns `None` when the axis has /// The axis is normalized internally. Returns `None` when the axis has
/// zero or non-finite length. /// zero or non-finite length.
///
/// 创建绕单位轴 `axis` 旋转 `angle` 弧度的四元数。
///
/// 轴在内部被归一化。轴长度为零或非有限时返回 `None`。
pub fn from_axis_angle(axis: Vec3, angle: f64) -> Option<Self> { pub fn from_axis_angle(axis: Vec3, angle: f64) -> Option<Self> {
let axis = axis.normalize()?; let axis = axis.normalize()?;
let half = angle / 2.0; let half = angle / 2.0;
@ -49,18 +75,24 @@ impl Quat {
} }
/// Creates a rotation of `angle` radians about the first axis. /// Creates a rotation of `angle` radians about the first axis.
///
/// 创建绕第一轴旋转 `angle` 弧度的四元数。
pub fn from_rotation_x(angle: f64) -> Self { pub fn from_rotation_x(angle: f64) -> Self {
let (sin, cos) = (angle / 2.0).sin_cos(); let (sin, cos) = (angle / 2.0).sin_cos();
Self::new(cos, sin, 0.0, 0.0) Self::new(cos, sin, 0.0, 0.0)
} }
/// Creates a rotation of `angle` radians about the second axis. /// Creates a rotation of `angle` radians about the second axis.
///
/// 创建绕第二轴旋转 `angle` 弧度的四元数。
pub fn from_rotation_y(angle: f64) -> Self { pub fn from_rotation_y(angle: f64) -> Self {
let (sin, cos) = (angle / 2.0).sin_cos(); let (sin, cos) = (angle / 2.0).sin_cos();
Self::new(cos, 0.0, sin, 0.0) Self::new(cos, 0.0, sin, 0.0)
} }
/// Creates a rotation of `angle` radians about the third axis. /// Creates a rotation of `angle` radians about the third axis.
///
/// 创建绕第三轴旋转 `angle` 弧度的四元数。
pub fn from_rotation_z(angle: f64) -> Self { pub fn from_rotation_z(angle: f64) -> Self {
let (sin, cos) = (angle / 2.0).sin_cos(); let (sin, cos) = (angle / 2.0).sin_cos();
Self::new(cos, 0.0, 0.0, sin) Self::new(cos, 0.0, 0.0, sin)
@ -72,6 +104,12 @@ impl Quat {
/// keeps the square root well conditioned for every input rotation. /// keeps the square root well conditioned for every input rotation.
/// Returns `None` when `m` is not a rotation matrix, that is when it is /// Returns `None` when `m` is not a rotation matrix, that is when it is
/// not orthonormal with determinant one within a tolerance of `1e-9`. /// not orthonormal with determinant one within a tolerance of `1e-9`.
///
/// 由旋转矩阵 `m` 创建四元数。
///
/// 算法选取对角线上最大的元素作除数,从而对任何输入旋转都保持开方的良态。
/// 当 `m` 不是旋转矩阵时返回 `None`,即在 `1e-9` 的容差内不正交或行列式
/// 不为一。
pub fn from_mat3(m: crate::mat3::Mat3) -> Option<Self> { pub fn from_mat3(m: crate::mat3::Mat3) -> Option<Self> {
if !m.is_rotation(1e-9) { if !m.is_rotation(1e-9) {
return None; return None;
@ -97,16 +135,22 @@ impl Quat {
} }
/// Returns the vector part `(x, y, z)`. /// Returns the vector part `(x, y, z)`.
///
/// 返回向量部分 `(x, y, z)`。
pub const fn vector(self) -> Vec3 { pub const fn vector(self) -> Vec3 {
Vec3::new(self.x, self.y, self.z) Vec3::new(self.x, self.y, self.z)
} }
/// Returns the squared length of this quaternion. /// Returns the squared length of this quaternion.
///
/// 返回该四元数的长度平方。
pub fn norm_squared(self) -> f64 { pub fn norm_squared(self) -> f64 {
self.w * self.w + self.x * self.x + self.y * self.y + self.z * self.z self.w * self.w + self.x * self.x + self.y * self.y + self.z * self.z
} }
/// Returns the length of this quaternion. /// Returns the length of this quaternion.
///
/// 返回该四元数的长度。
pub fn norm(self) -> f64 { pub fn norm(self) -> f64 {
self.norm_squared().sqrt() self.norm_squared().sqrt()
} }
@ -114,6 +158,10 @@ impl Quat {
/// Returns this quaternion scaled to unit length. /// Returns this quaternion scaled to unit length.
/// ///
/// Returns `None` when the length is zero or not finite. /// Returns `None` when the length is zero or not finite.
///
/// 返回该四元数缩放到单位长度后的结果。
///
/// 长度为零或非有限时返回 `None`。
pub fn normalize(self) -> Option<Self> { pub fn normalize(self) -> Option<Self> {
let norm = self.norm(); let norm = self.norm();
if norm > 0.0 && norm.is_finite() { if norm > 0.0 && norm.is_finite() {
@ -131,6 +179,10 @@ impl Quat {
/// Returns the conjugate `w - xi - yj - zk`. /// Returns the conjugate `w - xi - yj - zk`.
/// ///
/// For a unit quaternion this is the inverse rotation. /// For a unit quaternion this is the inverse rotation.
///
/// 返回共轭 `w - xi - yj - zk`。
///
/// 对单位四元数,它即逆旋转。
pub const fn conjugate(self) -> Self { pub const fn conjugate(self) -> Self {
Self::new(self.w, -self.x, -self.y, -self.z) Self::new(self.w, -self.x, -self.y, -self.z)
} }
@ -138,6 +190,10 @@ impl Quat {
/// Returns the inverse rotation. /// Returns the inverse rotation.
/// ///
/// Returns `None` when the length is zero or not finite. /// Returns `None` when the length is zero or not finite.
///
/// 返回逆旋转。
///
/// 长度为零或非有限时返回 `None`。
pub fn inverse(self) -> Option<Self> { pub fn inverse(self) -> Option<Self> {
let norm_squared = self.norm_squared(); let norm_squared = self.norm_squared();
if norm_squared > 0.0 && norm_squared.is_finite() { if norm_squared > 0.0 && norm_squared.is_finite() {
@ -155,6 +211,8 @@ impl Quat {
/// Returns the dot product of `self` and `rhs`, treating both as four /// Returns the dot product of `self` and `rhs`, treating both as four
/// component vectors. /// component vectors.
///
/// 把两者都视为四分量向量,返回 `self` 与 `rhs` 的点积。
pub fn dot(self, rhs: Self) -> f64 { pub fn dot(self, rhs: Self) -> f64 {
self.w * rhs.w + self.x * rhs.x + self.y * rhs.y + self.z * rhs.z self.w * rhs.w + self.x * rhs.x + self.y * rhs.y + self.z * rhs.z
} }
@ -164,6 +222,12 @@ impl Quat {
/// Assumes this quaternion has unit length. The rotation is computed as /// Assumes this quaternion has unit length. The rotation is computed as
/// `v + 2 * cross(vector, cross(vector, v) + w * v)`, which avoids /// `v + 2 * cross(vector, cross(vector, v) + w * v)`, which avoids
/// forming the quaternion product matrix twice. /// forming the quaternion product matrix twice.
///
/// 用该旋转对 `v` 施以旋转。
///
/// 假定该四元数为单位长度。旋转按
/// `v + 2 * cross(vector, cross(vector, v) + w * v)` 计算,避免两次构造
/// 四元数乘积矩阵。
pub fn rotate(self, v: Vec3) -> Vec3 { pub fn rotate(self, v: Vec3) -> Vec3 {
let q = self.vector(); let q = self.vector();
let t = q.cross(v) * 2.0; let t = q.cross(v) * 2.0;
@ -173,6 +237,10 @@ impl Quat {
/// Returns the rotation angle in radians, in `[0, pi]`. /// Returns the rotation angle in radians, in `[0, pi]`.
/// ///
/// Assumes this quaternion has unit length. /// Assumes this quaternion has unit length.
///
/// 返回旋转角,单位为弧度,位于 `[0, pi]`。
///
/// 假定该四元数为单位长度。
pub fn angle(self) -> f64 { pub fn angle(self) -> f64 {
2.0 * self.w.clamp(-1.0, 1.0).acos() 2.0 * self.w.clamp(-1.0, 1.0).acos()
} }
@ -181,6 +249,10 @@ impl Quat {
/// ///
/// Returns `None` for the identity rotation, where no axis is defined. /// Returns `None` for the identity rotation, where no axis is defined.
/// Assumes this quaternion has unit length. /// Assumes this quaternion has unit length.
///
/// 返回旋转轴的单位向量。
///
/// 对恒等旋转返回 `None`,因为此时旋转轴未定义。假定该四元数为单位长度。
pub fn axis(self) -> Option<Vec3> { pub fn axis(self) -> Option<Vec3> {
self.vector().normalize() self.vector().normalize()
} }
@ -190,6 +262,11 @@ impl Quat {
/// This is a chord interpolation, so the result is not a unit /// This is a chord interpolation, so the result is not a unit
/// quaternion and the angular speed is not constant. Use /// quaternion and the angular speed is not constant. Use
/// [`Quat::slerp`] when the intermediate rotations must be uniform. /// [`Quat::slerp`] when the intermediate rotations must be uniform.
///
/// 返回从 `self` 到 `rhs` 的线性插值。
///
/// 这是弦插值,故结果不是单位四元数,角速度也不恒定。当中间旋转必须均匀时
/// 请用 [`Quat::slerp`]。
pub fn lerp(self, rhs: Self, t: f64) -> Self { pub fn lerp(self, rhs: Self, t: f64) -> Self {
let w = self.w + (rhs.w - self.w) * t; let w = self.w + (rhs.w - self.w) * t;
let x = self.x + (rhs.x - self.x) * t; let x = self.x + (rhs.x - self.x) * t;
@ -204,12 +281,18 @@ impl Quat {
/// path a rotation would take if the angle were kept minimal. Falls /// path a rotation would take if the angle were kept minimal. Falls
/// back to [`Quat::lerp`] when the inputs are nearly parallel, where /// back to [`Quat::lerp`] when the inputs are nearly parallel, where
/// the sine in the denominator would otherwise lose precision. /// the sine in the denominator would otherwise lose precision.
///
/// 返回从 `self` 到 `rhs` 的球面线性插值。
///
/// 取两段圆弧中较短者,故结果沿角度最小的旋转路径。当输入近乎平行时退回
/// [`Quat::lerp`],此时分母中的正弦会损失精度。
pub fn slerp(self, rhs: Self, t: f64) -> Self { pub fn slerp(self, rhs: Self, t: f64) -> Self {
let mut end = rhs; let mut end = rhs;
let mut dot = self.dot(rhs); let mut dot = self.dot(rhs);
if dot < 0.0 { if dot < 0.0 {
// Negating one endpoint selects the shorter arc, because `q` // Negating one endpoint selects the shorter arc, because `q`
// and `-q` describe the same rotation. // and `-q` describe the same rotation.
// 对其中一个端点取负即可选出较短圆弧,因为 `q` 与 `-q` 表示同一旋转。
end = Self::new(-rhs.w, -rhs.x, -rhs.y, -rhs.z); end = Self::new(-rhs.w, -rhs.x, -rhs.y, -rhs.z);
dot = -dot; dot = -dot;
} }
@ -232,21 +315,29 @@ impl Quat {
} }
/// Returns `true` when the length is one within a tolerance of `1e-9`. /// Returns `true` when the length is one within a tolerance of `1e-9`.
///
/// 当长度在 `1e-9` 容差内为一时返回 `true`。
pub fn is_normalized(self) -> bool { pub fn is_normalized(self) -> bool {
(self.norm_squared() - 1.0).abs() < 1e-9 (self.norm_squared() - 1.0).abs() < 1e-9
} }
/// Returns `true` when all four components are finite. /// Returns `true` when all four components are finite.
///
/// 当四个分量都有限时返回 `true`。
pub fn is_finite(self) -> bool { pub fn is_finite(self) -> bool {
self.w.is_finite() && self.x.is_finite() && self.y.is_finite() && self.z.is_finite() self.w.is_finite() && self.x.is_finite() && self.y.is_finite() && self.z.is_finite()
} }
/// Returns a rotation of a quarter turn about the first axis. /// Returns a rotation of a quarter turn about the first axis.
///
/// 返回绕第一轴旋转四分之一周的四元数。
pub fn quarter_turn_x() -> Self { pub fn quarter_turn_x() -> Self {
Self::from_rotation_x(core::f64::consts::FRAC_PI_2) Self::from_rotation_x(core::f64::consts::FRAC_PI_2)
} }
/// Returns a rotation of a half turn about the third axis. /// Returns a rotation of a half turn about the third axis.
///
/// 返回绕第三轴旋转半周的四元数。
pub fn half_turn_z() -> Self { pub fn half_turn_z() -> Self {
Self::from_rotation_z(core::f64::consts::PI) Self::from_rotation_z(core::f64::consts::PI)
} }
@ -258,6 +349,7 @@ impl Mul for Quat {
fn mul(self, rhs: Self) -> Self { fn mul(self, rhs: Self) -> Self {
// Hamilton product, matching the convention that `(a * b)` // Hamilton product, matching the convention that `(a * b)`
// applies `b` first. // applies `b` first.
// Hamilton 积,与 `(a * b)` 先作用 `b` 的约定一致。
Self::new( Self::new(
self.w * rhs.w - self.x * rhs.x - self.y * rhs.y - self.z * rhs.z, self.w * rhs.w - self.x * rhs.x - self.y * rhs.y - self.z * rhs.z,
self.w * rhs.x + self.x * rhs.w + self.y * rhs.z - self.z * rhs.y, self.w * rhs.x + self.x * rhs.w + self.y * rhs.z - self.z * rhs.y,

View File

@ -8,50 +8,108 @@ use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAss
/// The components are named `x`, `y`, and `z`. No meaning is attached to /// The components are named `x`, `y`, and `z`. No meaning is attached to
/// them here; the reference frame is defined by the crate that uses the /// them here; the reference frame is defined by the crate that uses the
/// vector. /// vector.
///
/// 三分量列向量。
///
/// 分量名为 `x`、`y`、`z`。此处不赋予它们任何含义;参考系由使用该向量的
/// crate 定义。
///
/// 三分量列向量。
///
/// 分量名为 `x`、`y`、`z`。此处不赋予它们任何含义;参考系由使用该向量的
/// crate 定义。
#[derive(Debug, Clone, Copy, PartialEq, Default)] #[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Vec3 { pub struct Vec3 {
/// First component. /// First component.
///
/// 第一分量。
///
/// 第一分量。
pub x: f64, pub x: f64,
/// Second component. /// Second component.
///
/// 第二分量。
///
/// 第二分量。
pub y: f64, pub y: f64,
/// Third component. /// Third component.
///
/// 第三分量。
///
/// 第三分量。
pub z: f64, pub z: f64,
} }
impl Vec3 { impl Vec3 {
/// The zero vector. /// The zero vector.
///
/// 零向量。
///
/// 零向量。
pub const ZERO: Self = Self::new(0.0, 0.0, 0.0); pub const ZERO: Self = Self::new(0.0, 0.0, 0.0);
/// The unit vector along the first axis. /// The unit vector along the first axis.
///
/// 沿第一轴的单位向量。
///
/// 沿第一轴的单位向量。
pub const X: Self = Self::new(1.0, 0.0, 0.0); pub const X: Self = Self::new(1.0, 0.0, 0.0);
/// The unit vector along the second axis. /// The unit vector along the second axis.
///
/// 沿第二轴的单位向量。
///
/// 沿第二轴的单位向量。
pub const Y: Self = Self::new(0.0, 1.0, 0.0); pub const Y: Self = Self::new(0.0, 1.0, 0.0);
/// The unit vector along the third axis. /// The unit vector along the third axis.
///
/// 沿第三轴的单位向量。
///
/// 沿第三轴的单位向量。
pub const Z: Self = Self::new(0.0, 0.0, 1.0); pub const Z: Self = Self::new(0.0, 0.0, 1.0);
/// Creates a vector from its three components. /// Creates a vector from its three components.
///
/// 由三个分量创建向量。
///
/// 由三个分量创建向量。
pub const fn new(x: f64, y: f64, z: f64) -> Self { pub const fn new(x: f64, y: f64, z: f64) -> Self {
Self { x, y, z } Self { x, y, z }
} }
/// Creates a vector whose three components all equal `value`. /// Creates a vector whose three components all equal `value`.
///
/// 创建三个分量都等于 `value` 的向量。
///
/// 创建三个分量都等于 `value` 的向量。
pub const fn splat(value: f64) -> Self { pub const fn splat(value: f64) -> Self {
Self::new(value, value, value) Self::new(value, value, value)
} }
/// Returns the first component. /// Returns the first component.
///
/// 返回第一分量。
///
/// 返回第一分量。
pub const fn x(self) -> f64 { pub const fn x(self) -> f64 {
self.x self.x
} }
/// Returns the second component. /// Returns the second component.
///
/// 返回第二分量。
///
/// 返回第二分量。
pub const fn y(self) -> f64 { pub const fn y(self) -> f64 {
self.y self.y
} }
/// Returns the third component. /// Returns the third component.
///
/// 返回第三分量。
///
/// 返回第三分量。
pub const fn z(self) -> f64 { pub const fn z(self) -> f64 {
self.z self.z
} }
@ -60,6 +118,14 @@ impl Vec3 {
/// select `x`, `y`, and `z` respectively. /// select `x`, `y`, and `z` respectively.
/// ///
/// Returns `None` for an index outside `0..=2`. /// Returns `None` for an index outside `0..=2`.
///
/// 返回由 `index` 选定的分量,其中 `0`、`1`、`2` 分别对应 `x`、`y`、`z`。
///
/// `index` 超出 `0..=2` 时返回 `None`。
///
/// 返回由 `index` 选定的分量,其中 `0`、`1`、`2` 分别对应 `x`、`y`、`z`。
///
/// `index` 超出 `0..=2` 时返回 `None`。
pub const fn get(self, index: usize) -> Option<f64> { pub const fn get(self, index: usize) -> Option<f64> {
match index { match index {
0 => Some(self.x), 0 => Some(self.x),
@ -70,6 +136,10 @@ impl Vec3 {
} }
/// Returns the dot product of `self` and `rhs`. /// Returns the dot product of `self` and `rhs`.
///
/// 返回 `self` 与 `rhs` 的点积。
///
/// 返回 `self` 与 `rhs` 的点积。
pub fn dot(self, rhs: Self) -> f64 { pub fn dot(self, rhs: Self) -> f64 {
self.x * rhs.x + self.y * rhs.y + self.z * rhs.z self.x * rhs.x + self.y * rhs.y + self.z * rhs.z
} }
@ -78,6 +148,16 @@ impl Vec3 {
/// ///
/// The result is perpendicular to both inputs and follows the /// The result is perpendicular to both inputs and follows the
/// right-hand rule, so `Vec3::X.cross(Vec3::Y)` equals `Vec3::Z`. /// right-hand rule, so `Vec3::X.cross(Vec3::Y)` equals `Vec3::Z`.
///
/// 返回叉积 `self x rhs`。
///
/// 结果同时垂直于两个输入并遵循右手定则,因此 `Vec3::X.cross(Vec3::Y)`
/// 等于 `Vec3::Z`。
///
/// 返回叉积 `self x rhs`。
///
/// 结果同时垂直于两个输入并遵循右手定则,因此 `Vec3::X.cross(Vec3::Y)`
/// 等于 `Vec3::Z`。
pub fn cross(self, rhs: Self) -> Self { pub fn cross(self, rhs: Self) -> Self {
Self::new( Self::new(
self.y * rhs.z - self.z * rhs.y, self.y * rhs.z - self.z * rhs.y,
@ -90,11 +170,23 @@ impl Vec3 {
/// ///
/// Prefer this over [`Vec3::norm`] when comparing lengths, because it /// Prefer this over [`Vec3::norm`] when comparing lengths, because it
/// avoids a square root. /// avoids a square root.
///
/// 返回该向量的欧几里得长度平方。
///
/// 比较长度时优先用它而非 [`Vec3::norm`],因为它省去一次开方。
///
/// 返回该向量的欧几里得长度平方。
///
/// 比较长度时优先用它而非 [`Vec3::norm`],因为它省去一次开方。
pub fn norm_squared(self) -> f64 { pub fn norm_squared(self) -> f64 {
self.dot(self) self.dot(self)
} }
/// Returns the Euclidean length of this vector. /// Returns the Euclidean length of this vector.
///
/// 返回该向量的欧几里得长度。
///
/// 返回该向量的欧几里得长度。
pub fn norm(self) -> f64 { pub fn norm(self) -> f64 {
self.norm_squared().sqrt() self.norm_squared().sqrt()
} }
@ -103,6 +195,14 @@ impl Vec3 {
/// ///
/// Returns `None` when the length is zero or not finite, in which case /// Returns `None` when the length is zero or not finite, in which case
/// no direction is defined. /// no direction is defined.
///
/// 返回该向量缩放到单位长度后的结果。
///
/// 长度为零或非有限时返回 `None`,此时方向未定义。
///
/// 返回该向量缩放到单位长度后的结果。
///
/// 长度为零或非有限时返回 `None`,此时方向未定义。
pub fn normalize(self) -> Option<Self> { pub fn normalize(self) -> Option<Self> {
let norm = self.norm(); let norm = self.norm();
if norm > 0.0 && norm.is_finite() { if norm > 0.0 && norm.is_finite() {
@ -113,11 +213,19 @@ impl Vec3 {
} }
/// Returns the Euclidean distance between `self` and `rhs`. /// Returns the Euclidean distance between `self` and `rhs`.
///
/// 返回 `self` 与 `rhs` 之间的欧几里得距离。
///
/// 返回 `self` 与 `rhs` 之间的欧几里得距离。
pub fn distance(self, rhs: Self) -> f64 { pub fn distance(self, rhs: Self) -> f64 {
(self - rhs).norm() (self - rhs).norm()
} }
/// Returns the squared Euclidean distance between `self` and `rhs`. /// Returns the squared Euclidean distance between `self` and `rhs`.
///
/// 返回 `self` 与 `rhs` 之间的欧几里得距离平方。
///
/// 返回 `self` 与 `rhs` 之间的欧几里得距离平方。
pub fn distance_squared(self, rhs: Self) -> f64 { pub fn distance_squared(self, rhs: Self) -> f64 {
(self - rhs).norm_squared() (self - rhs).norm_squared()
} }
@ -127,11 +235,23 @@ impl Vec3 {
/// The result lies in `[0, pi]`. Returns `None` when either vector has /// The result lies in `[0, pi]`. Returns `None` when either vector has
/// zero or non-finite length, or when either vector has a non-finite /// zero or non-finite length, or when either vector has a non-finite
/// component. /// component.
///
/// 返回 `self` 与 `rhs` 之间的夹角,单位为弧度。
///
/// 结果位于 `[0, pi]`。当任一向量长度为零或非有限,或任一向量含非有限
/// 分量时返回 `None`。
///
/// 返回 `self` 与 `rhs` 之间的夹角,单位为弧度。
///
/// 结果位于 `[0, pi]`。当任一向量长度为零或非有限,或任一向量含非有限分量
/// 时返回 `None`。
pub fn angle_between(self, rhs: Self) -> Option<f64> { pub fn angle_between(self, rhs: Self) -> Option<f64> {
let denominator = self.norm() * rhs.norm(); let denominator = self.norm() * rhs.norm();
if denominator > 0.0 && denominator.is_finite() { if denominator > 0.0 && denominator.is_finite() {
// The cosine can exceed one in magnitude through rounding, so // The cosine can exceed one in magnitude through rounding, so
// it is clamped to keep the arc cosine defined. // it is clamped to keep the arc cosine defined.
// 舍入可能使余弦的绝对值超过一,故将其钳位以保证反余弦有定义。
// 舍入可能使余弦的绝对值超过一,故将其钳位以保证反余弦有定义。
Some((self.dot(rhs) / denominator).clamp(-1.0, 1.0).acos()) Some((self.dot(rhs) / denominator).clamp(-1.0, 1.0).acos())
} else { } else {
None None
@ -142,26 +262,50 @@ impl Vec3 {
/// ///
/// `t = 0` returns `self` and `t = 1` returns `rhs`. Values outside /// `t = 0` returns `self` and `t = 1` returns `rhs`. Values outside
/// `[0, 1]` extrapolate. /// `[0, 1]` extrapolate.
///
/// 返回线性插值 `self + t * (rhs - self)`。
///
/// `t = 0` 返回 `self`,`t = 1` 返回 `rhs`。`[0, 1]` 之外的值为外推。
///
/// 返回线性插值 `self + t * (rhs - self)`。
///
/// `t = 0` 返回 `self`,`t = 1` 返回 `rhs`。`[0, 1]` 之外的值为外推。
pub fn lerp(self, rhs: Self, t: f64) -> Self { pub fn lerp(self, rhs: Self, t: f64) -> Self {
self + (rhs - self) * t self + (rhs - self) * t
} }
/// Returns a vector with each component replaced by its absolute value. /// Returns a vector with each component replaced by its absolute value.
///
/// 返回各分量取绝对值后的向量。
///
/// 返回各分量取绝对值后的向量。
pub fn abs(self) -> Self { pub fn abs(self) -> Self {
Self::new(self.x.abs(), self.y.abs(), self.z.abs()) Self::new(self.x.abs(), self.y.abs(), self.z.abs())
} }
/// Returns the largest component. /// Returns the largest component.
///
/// 返回最大分量。
///
/// 返回最大分量。
pub fn max_component(self) -> f64 { pub fn max_component(self) -> f64 {
self.x.max(self.y).max(self.z) self.x.max(self.y).max(self.z)
} }
/// Returns the smallest component. /// Returns the smallest component.
///
/// 返回最小分量。
///
/// 返回最小分量。
pub fn min_component(self) -> f64 { pub fn min_component(self) -> f64 {
self.x.min(self.y).min(self.z) self.x.min(self.y).min(self.z)
} }
/// Returns `true` when all three components are finite. /// Returns `true` when all three components are finite.
///
/// 当三个分量都有限时返回 `true`。
///
/// 当三个分量都有限时返回 `true`。
pub fn is_finite(self) -> bool { pub fn is_finite(self) -> bool {
self.x.is_finite() && self.y.is_finite() && self.z.is_finite() self.x.is_finite() && self.y.is_finite() && self.z.is_finite()
} }

Binary file not shown.

36
scripts/bilingual.py Normal file
View File

@ -0,0 +1,36 @@
#!/usr/bin/env python3
"""Apply a table of English-to-bilingual comment replacements to a file.
The sweep over the repository is a long series of edits that pair an English
comment block with the English paragraph followed by a Chinese one. Writing
each pair twice, as two separate replacements, is error prone, so this
helper takes a list of pairs and rewrites the file with them applied, then
reports how many did not match. A replacement that does not match is almost
always a reflow of the English text, and is worth seeing rather than
skipping.
Usage, from the repository root:
python3 scripts/bilingual.py crates/orbit/apsis-kepler/src/elements.rs
"""
from __future__ import annotations
import sys
from pathlib import Path
def apply(path: Path, pairs: list[tuple[str, str]]) -> tuple[int, list[str]]:
"""Replaces each pair and returns how many applied with the misses."""
text = path.read_text(encoding="utf-8")
missed: list[str] = []
applied = 0
for old, new in pairs:
if old in text:
text = text.replace(old, new, 1)
applied += 1
else:
head = old.strip().splitlines()[0][:56] if old.strip() else "<empty>"
missed.append(head)
path.write_text(text, encoding="utf-8")
return applied, missed