Skip to main content

zmatrix/physics/basic/
mass.rs

1use std::any::Any;
2use std::ops::{Add, Div, Mul, Neg, Sub};
3use crate::physics::basic::{Coef, Mass, MassType, Momentum, PhysicalQuantity, Velocity};
4
5impl Default for Mass {
6    fn default() -> Self {
7        Self::from_kg(0.0)
8    }
9}
10
11
12impl PhysicalQuantity for Mass {
13    fn as_any(&self) -> &dyn Any {
14        self
15    }
16
17    fn is_zero(&self) -> bool {
18        self.v == 0.0
19    }
20
21    fn default_unit_value(&self) -> f64 {
22        self.as_kg()
23    }
24
25    fn set_value(&mut self, value: f64) {
26        self.v = value;
27    }
28    fn unit_display_name(&self) -> String {
29        "Mass (kg)".to_string()
30    }
31}
32
33impl Mass {
34    pub fn from_kg(kg: f64) -> Self {
35        Self {
36            default_type: MassType::Kg,
37            v: kg,
38        }
39    }
40
41    pub fn from_g(g: f64) -> Self {
42        Self {
43            default_type: MassType::g,
44            v: g,
45        }
46    }
47
48    pub fn as_kg(&self) -> f64 {
49        match self.default_type {
50            MassType::Kg => self.v,
51            MassType::g => self.v / 1000.0,
52        }
53    }
54
55    pub fn as_g(&self) -> f64 {
56        match self.default_type {
57            MassType::Kg => self.v * 1000.0,
58            MassType::g => self.v,
59        }
60    }
61}
62
63
64impl Add for Mass {
65    type Output = Self;
66    fn add(self, rhs: Self) -> Self::Output {
67        let v = self.as_kg() + rhs.as_kg();
68        Self::from_kg(v)
69    }
70}
71
72// 引用-引用 与 混合引用:Mass 加法
73impl<'a, 'b> Add<&'b Mass> for &'a Mass {
74    type Output = Mass;
75    fn add(self, rhs: &'b Mass) -> Self::Output { Mass::from_kg(self.as_kg() + rhs.as_kg()) }
76}
77impl<'a> Add<&'a Mass> for Mass {
78    type Output = Mass;
79    fn add(self, rhs: &'a Mass) -> Self::Output { Mass::from_kg(self.as_kg() + rhs.as_kg()) }
80}
81impl<'a> Add<Mass> for &'a Mass {
82    type Output = Mass;
83    fn add(self, rhs: Mass) -> Self::Output { Mass::from_kg(self.as_kg() + rhs.as_kg()) }
84}
85
86// Mass + 裸 f64:按**当前存储单位**直接相加,不做单位换算 —— 有意设计。
87// 裸 f64 不带单位,库无从判断它代表哪种单位;擅自换算等于替调用方做假设。
88// 需要单位安全时改用两个物理量运算,如 `x + Mass::from_kg(1.0)`。
89// 详见 `physics::basic` 模块文档「物理量与裸 f64 的运算」。
90impl Add<f64> for Mass {
91    type Output = Self;
92    fn add(self, rhs: f64) -> Self::Output {
93        let v = self.v + rhs;
94        Mass {
95            v,
96            default_type: self.default_type,
97        }
98    }
99}
100
101impl Sub for Mass {
102    type Output = Self;
103    fn sub(self, rhs: Self) -> Self::Output {
104        let v = self.as_kg() - rhs.as_kg();
105        Self::from_kg(v)
106    }
107}
108
109// 引用-引用 与 混合引用:Mass 减法
110impl<'a, 'b> Sub<&'b Mass> for &'a Mass {
111    type Output = Mass;
112    fn sub(self, rhs: &'b Mass) -> Self::Output { Mass::from_kg(self.as_kg() - rhs.as_kg()) }
113}
114impl<'a> Sub<&'a Mass> for Mass {
115    type Output = Mass;
116    fn sub(self, rhs: &'a Mass) -> Self::Output { Mass::from_kg(self.as_kg() - rhs.as_kg()) }
117}
118impl<'a> Sub<Mass> for &'a Mass {
119    type Output = Mass;
120    fn sub(self, rhs: Mass) -> Self::Output { Mass::from_kg(self.as_kg() - rhs.as_kg()) }
121}
122
123impl Sub<f64> for Mass {
124    type Output = Self;
125    fn sub(self, rhs: f64) -> Self::Output {
126        let v = self.v - rhs;
127        Mass {
128            v,
129            default_type: self.default_type,
130        }
131    }
132}
133
134impl Mul<f64> for Mass {
135    type Output = Self;
136    fn mul(self, rhs: f64) -> Self::Output {
137        let v = self.as_kg() * rhs;
138        Self::from_kg(v)
139    }
140}
141
142impl Div<f64> for Mass {
143    type Output = Self;
144    fn div(self, rhs: f64) -> Self::Output {
145        let v = self.as_kg() / rhs;
146        Self::from_kg(v)
147    }
148}
149
150impl Mul<Mass> for f64 {
151    type Output = Mass;
152    fn mul(self, rhs: Mass) -> Self::Output {
153        rhs * self
154    }
155}
156
157impl Div<Mass> for f64 {
158    type Output = Mass;
159    fn div(self, rhs: Mass) -> Self::Output {
160        let v = self / rhs.v;
161        Mass {
162            default_type: rhs.default_type,
163            v: v,
164        }
165    }
166}
167
168impl Mul<Coef> for Mass {
169    type Output = Self;
170    fn mul(self, rhs: Coef) -> Self::Output {
171        let v = self.as_kg() * rhs.get_value();
172        Self::from_kg(v)
173    }
174}
175
176impl Div<Coef> for Mass {
177    type Output = Self;
178    fn div(self, rhs: Coef) -> Self::Output {
179        let v = self.as_kg() / rhs.get_value();
180        Self::from_kg(v)
181    }
182}
183
184impl Neg for Mass {
185    type Output = Self;
186    fn neg(self) -> Self::Output {
187        let v = -self.as_kg();
188        Self::from_kg(v)
189    }
190}
191
192
193impl Mul<Velocity> for Mass {
194    type Output = Momentum;
195    fn mul(self, rhs: Velocity) -> Self::Output {
196        rhs * self
197    }
198}
199
200#[cfg(test)]
201mod tests {
202    use super::*;
203    use approx::assert_relative_eq;
204    #[test]
205    fn test_mass() {
206        let m1 = Mass::from_kg(1000.0);
207        assert_eq!(m1.v, 1000.0);
208        assert_eq!(m1.default_type, MassType::Kg);
209
210        let m2 = Mass::from_g(1000.0);
211        assert_eq!(m2.v, 1000.0);
212        assert_eq!(m2.default_type, MassType::g);
213
214        let m2 = Mass::default();
215        assert_eq!(m2.v, 0.0);
216        assert_eq!(m2.default_type, MassType::Kg);
217    }
218
219    #[test]
220    fn test_mass_as() {
221        let m1 = Mass::from_kg(1000.0);
222        let _m2 = m1.as_g();
223        assert_eq!(m1.as_g(), 1000.0 * 1000.0);
224        assert_eq!(m1.as_kg(), 1000.0);
225
226
227        let m2 = Mass::from_g(1000.0);
228        assert_eq!(m2.as_g(), 1000.0);
229        assert_eq!(m2.as_kg(), 1000.0 / 1000.0);
230    }
231
232    #[test]
233    fn test_mass_add() {
234        let m1 = Mass::from_kg(1000.0);
235        let m2 = Mass::from_kg(1000.0);
236        let m3 = m1 + m2;
237        assert_eq!(m3.as_kg(), 2000.0);
238
239        let m1 = Mass::from_g(1000.0);
240        let m2 = Mass::from_g(1000.0);
241        let m3 = m1 + m2;
242        assert_eq!(m3.as_g(), 2.0 * 1000.0);
243
244        let m1 = Mass::from_kg(1000.0);
245        let m2 = Mass::from_g(1000.0);
246        let m3 = m1 + m2;
247        assert_eq!(m3.as_kg(), 1001.0);
248
249        let m1 = Mass::from_g(1000.0);
250        let m2 = Mass::from_kg(1.0);
251        let m3 = m1 + m2;
252        assert_eq!(m3.as_kg(), 2.0);
253
254        let m1 = Mass::from_g(1000.0);
255        let m2 = m1 + 100.0;
256        assert_eq!(m2.as_kg(), 1.1);
257
258        let m1 = Mass::from_kg(1200.0);
259        let m2 = m1 + 10.2;
260        assert_eq!(m2.as_kg(), 1210.2);
261    }
262
263    #[test]
264    fn test_mass_sub() {
265        let m1 = Mass::from_kg(2000.0);
266        let m2 = Mass::from_kg(1000.0);
267        let m3 = m1 - m2;
268        assert_eq!(m3.as_kg(), 1000.0);
269
270        let m1 = Mass::from_g(3000.0);
271        let m2 = Mass::from_g(1000.0);
272        let m3 = m1 - m2;
273        assert_eq!(m3.as_g(), 2.0 * 1000.0);
274
275        let m1 = Mass::from_kg(1000.0);
276        let m2 = Mass::from_g(1000.0);
277        let m3 = m1 - m2;
278        assert_eq!(m3.as_kg(), 999.0);
279
280        let m1 = Mass::from_g(1000.0);
281        let m2 = Mass::from_kg(1.0);
282        let m3 = m1 - m2;
283        assert_eq!(m3.as_kg(), 0.0);
284
285        let m1 = Mass::from_g(1000.0);
286        let m2 = m1 - 100.0;
287        assert_eq!(m2.as_kg(), 0.9);
288
289        let m1 = Mass::from_kg(1200.0);
290        let m2 = m1 - 10.0;
291        assert_eq!(m2.as_kg(), 1190.0);
292    }
293
294    #[test]
295    fn test_mass_mul() {
296        let m1 = Mass::from_kg(2000.0);
297        let m2 = m1 * 2.0;
298        assert_eq!(m2.as_kg(), 4000.0);
299        let m1 = Mass::from_g(3000.0);
300        let m2 = m1 * 2.0;
301        assert_eq!(m2.as_g(), 6000.0);
302
303        let m1 = Mass::from_g(1000.0);
304        let m2 = m1 * Coef::new(2.0);
305        assert_eq!(m2.as_kg(), 2.0);
306
307        let m1 = Mass::from_g(1000.0);
308        let m2 = m1 * Coef::new(2.0);
309        assert_eq!(m2.as_kg(), 2.0);
310    }
311
312    #[test]
313    fn test_mass_div() {
314        let m1 = Mass::from_kg(2000.0);
315        let m2 = m1 / 2.0;
316        assert_eq!(m2.as_kg(), 1000.0);
317        let m1 = Mass::from_g(3000.0);
318        let m2 = m1 / 2.0;
319        assert_eq!(m2.as_g(), 1500.0);
320        let m1 = Mass::from_g(1000.0);
321        let m2 = m1 / Coef::new(2.0);
322        assert_eq!(m2.as_kg(), 0.5);
323    }
324
325    #[test]
326    fn test_is_zero() {
327        let m1 = Mass::from_kg(0.0);
328        assert!(m1.is_zero());
329        let m1 = Mass::from_kg(1.0);
330        assert!(!m1.is_zero());
331    }
332
333    #[test]
334    fn test_default_unit_value() {
335        let m1 = Mass::from_kg(1.0);
336        assert_eq!(m1.default_unit_value(), 1.0);
337        let m1 = Mass::from_g(1000.0);
338        assert_eq!(m1.default_unit_value(), 1.0);
339    }
340
341    #[test]
342    fn test_set_value() {
343        let mut m1 = Mass::from_kg(1.0);
344        m1.set_value(1000.0);
345        assert_eq!(m1.as_kg(), 1000.0);
346    }
347
348    #[test]
349    fn test_mass_mul_velocity() {
350        let m1 = Mass::from_kg(1000.0);
351        let v1 = Velocity::from_m_per_sec(1000.0);
352        let m2 = m1 * v1;
353        assert_eq!(m2.as_kg_m_s(), 1000.0 * 1000.0);
354    }
355
356    #[test]
357    fn test_f64_mul_mass() {
358        // f64 * Mass 测试
359        let m = Mass::from_kg(2.0);
360        let result = 3.0 * m;
361        assert_relative_eq!(result.as_kg(), 6.0);
362
363        let m = Mass::from_g(1000.0);
364        let result = 2.0 * m;
365        assert_relative_eq!(result.as_g(), 2000.0);
366    }
367
368    #[test]
369    fn test_f64_div_mass() {
370        // f64 / Mass 测试
371        let m = Mass::from_kg(2.0);
372        let result = 6.0 / m;
373        assert_relative_eq!(result.as_kg(), 3.0);
374
375        let m = Mass::from_g(1000.0);
376        let result = 2000.0 / m;
377        assert_relative_eq!(result.as_g(), 2.0);
378    }
379
380    #[test]
381    fn test_mass_ref_ops() {
382        let m1 = Mass::from_kg(2.0);
383        let m2 = Mass::from_g(1000.0); // 1 kg
384        let s = &m1 + &m2;
385        assert_relative_eq!(s.as_kg(), 3.0);
386
387        let d = &m1 - &m2;
388        assert_relative_eq!(d.as_kg(), 1.0);
389
390        // 混合引用:加/减
391        let s2 = m1 + &m2;
392        assert_relative_eq!(s2.as_kg(), 3.0);
393        let m1b = Mass::from_kg(2.0);
394        let s3 = &m1b + m2;
395        assert_relative_eq!(s3.as_kg(), 3.0);
396
397        let m1c = Mass::from_kg(2.0);
398        let m2c = Mass::from_g(1000.0);
399        let d2 = m1c - &m2c;
400        assert_relative_eq!(d2.as_kg(), 1.0);
401        let m1d = Mass::from_kg(2.0);
402        let m2d = Mass::from_g(1000.0);
403        let d3 = &m1d - m2d;
404        assert_relative_eq!(d3.as_kg(), 1.0);
405    }
406
407    #[test]
408    fn test_mass_neg() {
409        // 测试正值的负号
410        let m1 = Mass::from_kg(5.0);
411        let neg_m1 = -m1;
412        assert_relative_eq!(neg_m1.as_kg(), -5.0);
413
414        // 测试负值的负号
415        let m2 = Mass::from_g(-1000.0);
416        let neg_m2 = -m2;
417        assert_relative_eq!(neg_m2.as_g(), 1000.0);
418
419        // 测试零值
420        let m3 = Mass::from_kg(0.0);
421        let neg_m3 = -m3;
422        assert_relative_eq!(neg_m3.as_kg(), 0.0);
423
424        // 测试不同单位的负号操作
425        let m4 = Mass::from_g(1000.0);
426        let neg_m4 = -m4;
427        assert_relative_eq!(neg_m4.as_kg(), -1.0);
428
429        // 测试大数值
430        let m5 = Mass::from_kg(2.0);
431        let neg_m5 = -m5;
432        assert_relative_eq!(neg_m5.as_g(), -2000.0);
433    }
434}