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:
parent
a828b0b9e6
commit
4579ed064a
|
|
@ -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],
|
||||||
|
|
|
||||||
|
|
@ -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| {
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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.
|
|
@ -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
|
||||||
Loading…
Reference in New Issue