From 182371e3eb052f4ab61d0e9473047ebf8742257a Mon Sep 17 00:00:00 2001 From: ApsisRS contributors Date: Fri, 2 Oct 2026 01:45:27 +0800 Subject: [PATCH] docs: bilingual module docs for the foundation layer --- crates/apsis-rs/src/lib.rs | 20 ++++++++++++++ crates/foundation/apsis-core/src/lib.rs | 18 ++++++++++++ crates/foundation/apsis-core/src/quantity.rs | 9 ++++++ crates/foundation/apsis-macros/src/lib.rs | 26 ++++++++++++++++++ crates/foundation/apsis-math/src/lib.rs | 15 ++++++++++ crates/foundation/apsis-math/src/mat3.rs | 2 ++ crates/foundation/apsis-math/src/parallel.rs | 17 ++++++++++++ crates/foundation/apsis-math/src/quat.rs | 2 ++ crates/foundation/apsis-math/src/vec3.rs | 2 ++ .../check-bilingual.cpython-314.pyc | Bin 7018 -> 14100 bytes 10 files changed, 111 insertions(+) diff --git a/crates/apsis-rs/src/lib.rs b/crates/apsis-rs/src/lib.rs index ada361b..8de7fc2 100644 --- a/crates/apsis-rs/src/lib.rs +++ b/crates/apsis-rs/src/lib.rs @@ -21,6 +21,26 @@ //! aggregates all other layers and is only needed by simulation workloads, //! projects that do not run simulations can enable `ops` instead, which //! enables every layer except `sim`. +//! +//! ApsisRS:面向商业航天与卫星互联网系统的计算内核。 +//! +//! 工作区按依赖排序的各层组织: +//! +//! ```text +//! foundation -> physics -> orbit -> rf -> network -> ops -> sim +//! ``` +//! +//! 依赖只朝一个方向流动。一层可以使用其左侧的各层,但绝不使用其右侧的 +//! 层。 +//! +//! 本 crate 是名为 `apsis-rs` 的公共门面。下游项目直接依赖它,并启用与其 +//! 所需层相匹配的特性。每个特性重导出某一层的各 crate,并引入其下方各层 +//! 的特性。例如,`orbit` 特性启用 `physics`,并重导出 [`apsis_orbit`]、 +//! [`apsis_tle`]、[`apsis_sgp4`]、[`apsis_kepler`] 与 [`apsis_ephemeris`]。 +//! +//! `full` 特性启用每一层,包括 `sim`。因为 `sim` 聚合了其余所有层且只为 +//! 仿真工作负载所需,不运行仿真的项目可以改而启用 `ops`,它启用除 `sim` +//! 之外的每一层。 #![forbid(unsafe_code)] #![deny(missing_docs)] diff --git a/crates/foundation/apsis-core/src/lib.rs b/crates/foundation/apsis-core/src/lib.rs index ffc4fc9..df7c49b 100644 --- a/crates/foundation/apsis-core/src/lib.rs +++ b/crates/foundation/apsis-core/src/lib.rs @@ -20,6 +20,24 @@ //! SI units. //! - [`Quantity`] marks newtypes that carry a physical unit, so generic //! code can accept any quantity and still recover its unit type. +//! +//! ApsisRS 工作区共享的基础类型:一个通用错误类型、结果别名、物理常量,以及 +//! 单位标记 trait。 +//! +//! 本 crate 位于依赖图的底部。它不依赖任何其他 ApsisRS crate,故其余每个 +//! crate 都可以使用它。 +//! +//! 这里有三件事: +//! +//! - [`Error`] 与 [`Result`] 是携带粗糙 [`ErrorKind`] 的通用错误。它们存在, +//! 是为了让失败没有有趣结构的函数无需发明一个类型即可报告它,也是为了让 +//! 工作区有一个可退守的形状。它们有意地*不是*各层的错误类型:具有领域 +//! 特定失败的一层会定义自己的 `Error` 与 `ErrorKind`,其变体指名调用方 +//! 想要匹配的具体情形。原因以及各领域错误遵循的约定见 +//! `docs/design/errors.md`。 +//! - [`constant`] 保存以 SI 单位表达的物理与天文常量。 +//! - [`Quantity`] 标记携带有物理单位的 newtype,使泛型代码可以接受任何量 +//! 并仍能恢复其单位类型。 #![forbid(unsafe_code)] #![deny(missing_docs)] diff --git a/crates/foundation/apsis-core/src/quantity.rs b/crates/foundation/apsis-core/src/quantity.rs index 16f3366..bef173f 100644 --- a/crates/foundation/apsis-core/src/quantity.rs +++ b/crates/foundation/apsis-core/src/quantity.rs @@ -9,6 +9,15 @@ //! of its kind are in the corresponding base unit, which allows conversion //! between units of the same dimension. The base unit of a dimension always //! has a factor of one. +//! +//! 携带有物理单位的量所用的标记 trait。 +//! +//! 量的 newtype 把数值与其单位一同存在类型中。[`Quantity`] trait 通过关联 +//! 类型公开该单位,故可对任何量编写泛型代码并仍能恢复所给的是哪个单位。 +//! +//! 单位本身是任何实现 [`Unit`] 的类型。单位知道其同类在相应基本单位中有 +//! 多少个,这允许同一量纲的各单位之间换算。一个量纲的基本单位其因子总是 +//! 一。 // //! 单位本身是任意实现 [`Unit`] 的类型。单位知道自己相当于多少个对应的基本单位, //! 从而可以在同一量纲的单位之间换算。某量纲的基本单位其因子恒为一。 diff --git a/crates/foundation/apsis-macros/src/lib.rs b/crates/foundation/apsis-macros/src/lib.rs index b869e92..bd4cd1d 100644 --- a/crates/foundation/apsis-macros/src/lib.rs +++ b/crates/foundation/apsis-macros/src/lib.rs @@ -33,6 +33,32 @@ //! If the workspace later needs macros that generate items, such as deriving //! a set of conversions from one definition, that is the point at which this //! crate should gain its first macro. +//! +//! 支持 ApsisRS 工作区的宏。 +//! +//! 本 crate 当前不定义任何宏。它的存在是为了让工作区在需要时有存放宏定义的 +//! 单一位置,也是为了日后再加一个宏时无需新建 crate。 +//! +//! 让它保持为空的决定源于工作区实际所需。Rust 内建的 `macro_rules!` 已能完成 +//! 基础层所需的一切: +//! +//! - 替换一个类型与一个字面量以构建量,如 `quantity!(Meters, 1.5)`。 +//! - 依据尾随记号分派以接受单位后缀,如 `to_meters!(2.5 km)`。 +//! +//! 两种形式都由 `tests/probe.rs` 中的测试演示。它们可以直接写在用到它们的 +//! crate 中,故无需共享的宏 crate。 +//! +//! 有两点限制值得记下,因为它们解释了为何不提供基于宏的单位系统: +//! +//! - `macro_rules!` 宏不能对不是标识符的单位后缀泛型化。`1.5 m` 与 +//! `1.5 km` 由不同的规则接受,故该宏必须枚举它所支持的每个后缀。 +//! `apsis-units` 中的单位 newtype 已在没有该列表的情况下给出编译期单位 +//! 安全,故该宏只会增加表面积而不增加检查。 +//! - 声明式宏无法检视其参数的值,故它无法在常量求值已做的之外于编译期折叠 +//! 单位换算。诸如 `2.5 * 1000.0` 这样的字面量反正会被编译器折叠。 +//! +//! 若工作区日后需要生成项(例如从一个定义派生一组换算)的宏,那便是本 +//! crate 应获得其第一个宏之时。 #![forbid(unsafe_code)] #![deny(missing_docs)] diff --git a/crates/foundation/apsis-math/src/lib.rs b/crates/foundation/apsis-math/src/lib.rs index 1422afc..79a38e5 100644 --- a/crates/foundation/apsis-math/src/lib.rs +++ b/crates/foundation/apsis-math/src/lib.rs @@ -24,6 +24,21 @@ //! let z = x.cross(y); //! assert_eq!(z, Vec3::new(0.0, 0.0, 1.0)); //! ``` +//! +//! ApsisRS 工作区无依赖的线性代数。 +//! +//! 本 crate 提供三种类型: +//! +//! - [`Vec3`],基于 `f64` 的三分量列矢量 +//! - [`Quat`],用于转动的单位四元数 +//! - [`Mat3`],按列主序存储的三阶矩阵 +//! +//! 这些类型是纯数据,除各方法上所述外没有内部不变量,故其字段是公开的, +//! 值可由结构体字面量表达式构建。对某些输入在数学上未定义的操作(如把零 +//! 矢量归一化)返回 [`Option`],而不 panic 或产生非有限结果。 +//! +//! 所有转动都绕给定轴采用右手定则,所有角度都以弧度计。输入时角度不进行 +//! 环绕;要求取规范区间的方法会说明该要求。 #![forbid(unsafe_code)] #![deny(missing_docs)] diff --git a/crates/foundation/apsis-math/src/mat3.rs b/crates/foundation/apsis-math/src/mat3.rs index 9270d33..ea50af4 100644 --- a/crates/foundation/apsis-math/src/mat3.rs +++ b/crates/foundation/apsis-math/src/mat3.rs @@ -1,4 +1,6 @@ //! Three by three matrices over `f64`. +//! +//! 基于 `f64` 的三阶矩阵。 use crate::quat::Quat; use crate::vec3::Vec3; diff --git a/crates/foundation/apsis-math/src/parallel.rs b/crates/foundation/apsis-math/src/parallel.rs index 195bd86..7d6b77e 100644 --- a/crates/foundation/apsis-math/src/parallel.rs +++ b/crates/foundation/apsis-math/src/parallel.rs @@ -27,6 +27,23 @@ //! let output = map_index(input.len(), |index| input[index] * input[index]); //! assert_eq!(output[10], 100.0); //! ``` +//! +//! 仅建立在标准库之上的作用域并行。 +//! +//! 工作区禁止第三方 crate,故线程池须在此编写。但它并未如此:本工作区中 +//! 受益于多核的操作,全都是对独立项做可并行化的循环,而标准库的作用域线程 +//! 直接表达这些。`std::thread::scope` 借用其数据,故调用方无需原子引用计数 +//! 即可共享切片或读取器,并在返回前 join 每个线程,故没有任何结果会在仍被 +//! 写入时逸出该作用域。 +//! +//! 入口是 [`for_each_index`],它把区间按线程切成若干块,并对每个元素运行 +//! 一个闭包。该闭包必须可从另一线程调用,故它所捕获的任何东西都必须是 +//! `Sync` 的;实践中这意味着读取共享输入并写入输出的互不相交槽位,这正是 +//! 此处每个并行循环所具有的形状。 +//! +//! 因为没有池,一次并行调用的成本包含启动线程。只有当每个线程都有实打实的 +//! 工作时才值得付出,故 [`thread_count`] 报告机器所提供的数量,本工作区中 +//! 的调用方用它来对小的输入跳过并行路径。 use core::hint; diff --git a/crates/foundation/apsis-math/src/quat.rs b/crates/foundation/apsis-math/src/quat.rs index 22f9a6b..e09df23 100644 --- a/crates/foundation/apsis-math/src/quat.rs +++ b/crates/foundation/apsis-math/src/quat.rs @@ -1,4 +1,6 @@ //! Unit quaternions used for rotations. +//! +//! 用于转动的单位四元数。 use crate::vec3::Vec3; use core::fmt; diff --git a/crates/foundation/apsis-math/src/vec3.rs b/crates/foundation/apsis-math/src/vec3.rs index 24ee924..d54aafb 100644 --- a/crates/foundation/apsis-math/src/vec3.rs +++ b/crates/foundation/apsis-math/src/vec3.rs @@ -1,4 +1,6 @@ //! Three component vectors over `f64`. +//! +//! 基于 `f64` 的三分量矢量。 use core::fmt; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; diff --git a/scripts/__pycache__/check-bilingual.cpython-314.pyc b/scripts/__pycache__/check-bilingual.cpython-314.pyc index c7aa947a1dcf12609b92a37766ab7e3d65b78cd6..88922fa15c4963996a2b9ab47944514b04e55361 100644 GIT binary patch literal 14100 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 XX+CO!C(5HX=%Gszd9ZymOAY)t;