cad_cs/libs/angle/math.rs
1// 📃 src/libs/angle/math.rs
2
3use std::f64::consts;
4
5use super::{
6 abstracts::{AbstractAngle, AngleExt},
7 model::{Angle, AngleFmt},
8};
9use crate::libs::frac;
10
11impl AbstractAngle for Angle {
12 /// 📚 【 POL】: Tworzy instancję Angle bezpośrednio z wartości w radianach (Konstruktor bazowy).
13 /// 📚 【 ENG】: Creates an Angle instance directly from a value in radians (Base constructor).
14 #[inline]
15 fn from_rad(r: f64) -> Self { Self(r) }
16
17 /// 📚 【 POL】: Tworzy instancję Angle z wartości w stopniach, dokonując konwersji na radiany.
18 /// 📚 【 ENG】: Creates an Angle instance from a value in degrees, converting it to radians.
19 #[inline]
20 fn from_deg(d: f64) -> Self { Self(d.to_radians()) }
21
22 /// 📚 【 POL】: Tworzy instancję Angle jako wielokrotność liczby PI.
23 /// 📚 【 ENG】: Creates an Angle instance as a multiple of PI.
24 #[inline]
25 fn from_pi_frac(fraction: f64) -> Self { Self(fraction * consts::PI) }
26
27 /// 📚 【 POL】: Dekoduje format DMS (Stopnie, Minuty, Sekundy) do reprezentacji radianowej.
28 /// 📚 【 ENG】: Decodes DMS (Degrees, Minutes, Seconds) format to radian representation.
29 ///
30 /// ⚠️ 【 POL】: Wykorzystanie i16 dla stopni uniemożliwia reprezentację "-0°" w formacie DMS.
31 /// ⚠️ 【 ENG】: Using i16 for degrees prevents representation of "-0°" in DMS format.
32 fn from_dms(d: i16, m: u8, s: f32) -> Self {
33 // Uwaga: prosty 'sign' z d < 0 nie obsłuży "-0 stopni".
34 // W GIS często używa się f64 z signum() dla stopnia, ale trzymając się i16:
35 let sign = if d < 0 { -1.0_f64 } else { 1.0_f64 };
36 let ddd = (d as f64) + sign * ((m as f64) / 60.0) + sign * ((s as f64) / 3600.0);
37 Self::from_deg(ddd)
38 }
39
40 /// 📚 【 POL】: Dekomponuje kąt z radianów do formatu DMS (Stopnie, Minuty, Sekundy).
41 /// 📚 【 ENG】: Decomposes angle from radians to DMS (Degrees, Minutes, Seconds) format.
42 fn to_dms(self) -> (i16, u8, f32) {
43 let ddd = self.deg();
44 let sign = ddd.signum();
45 let abs_ddd = ddd.abs();
46
47 let d = (abs_ddd.floor() * sign) as i16;
48 let m_float = (abs_ddd - abs_ddd.floor()) * 60.0;
49 let m = m_float.floor() as u8;
50 let s = ((m_float - m_float.floor()) * 60.0) as f32;
51
52 (d, m, s)
53 }
54
55 /// 📚 【 POL】: Zwraca wartość kąta w radianach (f64). Operacja bezkosztowa.
56 /// 📚 【 ENG】: Returns angle value in radians (f64). Zero-cost operation.
57 #[inline]
58 fn rad(self) -> f64 { self.0 }
59
60 /// 📚 【 POL】: Zwraca wartość kąta w stopniach (f64).
61 /// 📚 【 ENG】: Returns angle value in degrees (f64).
62 #[inline]
63 fn deg(self) -> f64 { self.0.to_degrees() }
64
65 /// 📚 【 POL】: Zwraca kąt jako ułamek liczby PI w postaci (licznik, mianownik).
66 /// 📚 【 ENG】: Returns angle as a fraction of PI in (numerator, denominator) form.
67 #[inline]
68 fn pi_frac(self) -> (f64, f64) { frac::as_frac_pi(self.0) }
69
70 /// 📚 【 POL】: Formatuje kąt jako ciąg znaków w radianach. Alokuje String.
71 /// 📚 【 ENG】: Formats angle as a string in radians. Allocates String.
72 fn print_rad(self) -> String { format!("{:.4} rad", self.rad()) }
73
74 /// 📚 【 POL】: Formatuje kąt jako ciąg znaków w stopniach. Alokuje String.
75 /// 📚 【 ENG】: Formats angle as a string in degrees. Allocates String.
76 fn print_deg(self) -> String { format!("{:.2}°", self.deg()) }
77
78 /// 📚 【 POL】: Formatuje kąt jako ułamek liczby PI. Alokuje String.
79 /// 📚 【 ENG】: Formats angle as a fraction of PI. Allocates String.
80 fn print_pi_frac(self) -> String {
81 let (num, den) = self.pi_frac();
82 if num == 0.0 {
83 return "0".to_string();
84 }
85 if den == 1.0 {
86 return format!("{} π", num);
87 }
88 format!("{} / {} π", num, den)
89 }
90}
91
92impl AngleExt for f64 {
93 #[rustfmt::skip] #[inline] fn deg(self) -> Angle { Angle::from_deg(self) }
94 #[rustfmt::skip] #[inline] fn rad(self) -> Angle { Angle::from_rad(self) }
95 #[rustfmt::skip] #[inline] fn pi_frac(self) -> Angle { Angle::from_pi_frac(self) }
96}
97
98impl AngleFmt {
99 /// 📚 【 POL】: Formatuje wartość f64 (interpretowaną jako radiany) zgodnie z wybranym formatem.
100 /// 📚 【 ENG】: Formats a f64 value (interpreted as radians) according to the selected format.
101 #[rustfmt::skip] #[inline]
102 pub fn format(&self, val: f64) -> String {
103 match self {
104 Self::Rad => Angle::from_rad(val).print_rad(),
105 Self::Deg => Angle::from_rad(val).print_deg(),
106 Self::PiFrac => Angle::from_rad(val).print_pi_frac(),
107 }
108 }
109}
110
111/// 📚 【 POL】: Funkcja pomocnicza (Top-level API) tworząca instancję Angle z wartości w radianach.
112/// 📚 【 ENG】: Helper function (Top-level API) creating an Angle instance from a value in radians.
113#[rustfmt::skip] #[inline] pub const fn rad(r: f64) -> Angle { Angle(r) }
114
115/// 📚 【 POL】: Funkcja pomocnicza (Top-level API) tworząca instancję Angle z wartości w stopniach.
116/// 📚 【 ENG】: Helper function (Top-level API) creating an Angle instance from a value in degrees.
117#[rustfmt::skip] #[inline] pub fn deg(d: f64) -> Angle { Angle::from_deg(d) }
118
119/// 📚 【 POL】: Funkcja pomocnicza (Top-level API) tworząca instancję Angle jako wielokrotność liczby PI.
120/// 📚 【 ENG】: Helper function (Top-level API) creating an Angle instance as a multiple of PI.
121#[rustfmt::skip] #[inline] pub fn pi_frac(fraction: f64) -> Angle { Angle::from_pi_frac(fraction) }