Skip to main content

squid_n_core/geom/
vec3.rs

1//! 3 次元ベクトルの基本演算。
2//!
3//! 節点座標・部材軸・面法線の扱いはクレートを横断して現れるため、内積・外積・
4//! ノルム・単位化といった最小限の演算をここに集約する(各クレートが同じ数式を
5//! 私的なヘルパとして持ち直すのを防ぎ、零ベクトル判定の閾値も 1 か所に保つ)。
6//!
7//! 座標系は常に mm。ベクトルは `[f64; 3]` の値型で受け渡す(要素数 3 の
8//! 固定長配列はコピーが安く、参照を持ち回るより呼び出し側が読みやすい)。
9
10/// 数値的に零ベクトルとみなすノルムの上限 [mm]。
11///
12/// 座標が mm であるため、これを下回る長さの部材軸・面法線は縮退している
13/// (方向を定義できない)とみなす。
14pub const ZERO_TOL: f64 = 1e-9;
15
16/// 差 `a − b`。
17pub fn sub(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
18    [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
19}
20
21/// 和 `a + b`。
22pub fn add(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
23    [a[0] + b[0], a[1] + b[1], a[2] + b[2]]
24}
25
26/// スカラ倍 `s・a`。
27pub fn scale(a: [f64; 3], s: f64) -> [f64; 3] {
28    [a[0] * s, a[1] * s, a[2] * s]
29}
30
31/// 2 点の中点。
32pub fn midpoint(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
33    [
34        0.5 * (a[0] + b[0]),
35        0.5 * (a[1] + b[1]),
36        0.5 * (a[2] + b[2]),
37    ]
38}
39
40/// 内積。
41pub fn dot(a: [f64; 3], b: [f64; 3]) -> f64 {
42    a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
43}
44
45/// 外積。
46pub fn cross(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
47    [
48        a[1] * b[2] - a[2] * b[1],
49        a[2] * b[0] - a[0] * b[2],
50        a[0] * b[1] - a[1] * b[0],
51    ]
52}
53
54/// ノルム(長さ)。
55pub fn norm(a: [f64; 3]) -> f64 {
56    dot(a, a).sqrt()
57}
58
59/// 2 点間の距離。
60pub fn dist(a: [f64; 3], b: [f64; 3]) -> f64 {
61    norm(sub(a, b))
62}
63
64/// 単位ベクトル。長さが [`ZERO_TOL`] 以下(縮退)なら `None`。
65pub fn unit(a: [f64; 3]) -> Option<[f64; 3]> {
66    let l = norm(a);
67    (l > ZERO_TOL).then(|| [a[0] / l, a[1] / l, a[2] / l])
68}
69
70/// `a` から `b` へ向かう単位ベクトル。2 点が一致(縮退)なら `None`。
71pub fn unit_from(a: [f64; 3], b: [f64; 3]) -> Option<[f64; 3]> {
72    unit(sub(b, a))
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn dot_cross_norm_の基本則() {
81        let a = [1.0, 0.0, 0.0];
82        let b = [0.0, 2.0, 0.0];
83        assert_eq!(dot(a, b), 0.0);
84        assert_eq!(cross(a, b), [0.0, 0.0, 2.0]);
85        assert_eq!(norm(b), 2.0);
86        // 外積は両ベクトルに直交する。
87        let c = cross(a, b);
88        assert_eq!(dot(c, a), 0.0);
89        assert_eq!(dot(c, b), 0.0);
90    }
91
92    #[test]
93    fn unit_は縮退ベクトルで_none_を返す() {
94        assert_eq!(unit([0.0, 0.0, 0.0]), None);
95        assert_eq!(unit([ZERO_TOL, 0.0, 0.0]), None);
96        assert_eq!(unit([0.0, 3.0, 4.0]), Some([0.0, 0.6, 0.8]));
97    }
98
99    #[test]
100    fn dist_と_midpoint() {
101        let a = [0.0, 0.0, 0.0];
102        let b = [3.0, 4.0, 0.0];
103        assert_eq!(dist(a, b), 5.0);
104        assert_eq!(midpoint(a, b), [1.5, 2.0, 0.0]);
105        assert_eq!(unit_from(a, b), Some([0.6, 0.8, 0.0]));
106        assert_eq!(unit_from(a, a), None);
107    }
108}