rs_math/graphical/
ellipse.rs

1use std::f64::consts::PI;
2
3/// 表示椭圆的结构体。
4///
5/// # 字段
6///
7/// * `h` - 椭圆中心的 x 坐标。
8/// * `k` - 椭圆中心的 y 坐标。
9/// * `a` - 长轴的长度。
10/// * `b` - 短轴的长度。
11/// * `phi` - 旋转角度(可能为零)。
12///
13/// 椭圆是一个二维图形,定义了平面上所有满足特定数学条件的点的集合。
14/// 椭圆可以通过其中心坐标、长轴和短轴的长度以及旋转角度来描述。
15/// 长轴是通过椭圆中心的两个焦点的距离,短轴是垂直于长轴的轴的长度。
16/// 旋转角度表示椭圆相对于坐标轴的旋转程度。
17#[derive(Debug, PartialEq, Clone, Copy)]
18pub struct Ellipse {
19    h: f64,
20    // 椭圆中心的 x 坐标。
21    k: f64,
22    // 椭圆中心的 y 坐标。
23    a: f64,
24    // 长轴的长度。
25    b: f64,
26    // 短轴的长度。
27    phi: f64,  // 旋转角度(可能为零)。
28}
29
30/// 表示点相对于椭圆的位置的枚举。
31///
32/// # 变体
33///
34/// * `OnEllipse` - 点在椭圆上。
35/// * `InsideEllipse` - 点在椭圆内部。
36/// * `OutsideEllipse` - 点在椭圆外部。
37pub enum PointPosition {
38    OnEllipse,
39    InsideEllipse,
40    OutsideEllipse,
41}
42
43#[allow(dead_code)]
44impl Ellipse {
45    /// 构造函数,用于创建新的椭圆实例。
46    ///
47    /// # 参数
48    ///
49    /// * `a` - 长轴的长度。
50    /// * `b` - 短轴的长度。
51    /// * `h` - 椭圆中心的 x 坐标。
52    /// * `k` - 椭圆中心的 y 坐标。
53    /// * `phi` - 旋转角度(可能为零)。
54    ///
55    /// # 返回值
56    ///
57    /// 返回一个包含给定参数的椭圆实例。
58    ///
59    /// # 示例
60    ///
61    /// ```
62    /// use rs_math::graphical::ellipse::Ellipse;
63    /// let ellipse = Ellipse::new(5.0, 3.0, 0.0, 0.0, 45.0);
64    /// ```
65    pub fn new(a: f64, b: f64, h: f64, k: f64, phi: f64) -> Ellipse {
66        Ellipse { a, b, h, k, phi }
67    }
68
69    /// 计算椭圆离心率的方法。
70    ///
71    /// # 返回值
72    ///
73    /// 返回椭圆的离心率,计算公式为 sqrt(1 - (b^2 / a^2))。
74    ///
75    /// # 示例
76    ///
77    /// ```
78    /// use rs_math::graphical::ellipse::Ellipse;
79    /// let ellipse = Ellipse::new(5.0, 3.0, 0.0, 0.0, 45.0);
80    /// let eccentricity = ellipse.eccentricity();
81    /// ```
82    pub fn eccentricity(&self) -> f64 {
83        (1.0 - self.b.powi(2) / self.a.powi(2)).sqrt()
84    }
85    /// 输出椭圆的方程。
86    ///
87    /// 打印椭圆的标准方程,其中 `(x - h)^2 / a^2 + (y - k)^2 / b^2 = 1`。
88    ///
89    /// # 示例
90    ///
91    /// ```
92    /// use rs_math::graphical::ellipse::Ellipse;
93    /// let ellipse = Ellipse::new(5.0, 3.0, 0.0, 0.0, 45.0);
94    /// ellipse.print_equation();
95    /// ```
96    pub fn print_equation(&self) {
97        println!("椭圆方程:(x - {})^2 / {}^2 + (y - {})^2 / {}^2 = 1", self.h, self.a, self.k, self.b);
98    }
99
100    /// 计算椭圆的面积。
101    ///
102    /// 使用椭圆的长轴和短轴计算椭圆的面积,公式为 πab,其中 a 为长轴长度,b 为短轴长度。
103    ///
104    /// # 示例
105    ///
106    /// ```
107    /// use rs_math::graphical::ellipse::Ellipse;
108    /// let ellipse = Ellipse::new(5.0, 3.0, 0.0, 0.0, 0.0);
109    /// let area = ellipse.area();
110    /// println!("椭圆面积:{}", area);
111    /// ```
112    pub fn area(&self) -> f64 {
113        PI * self.a * self.b
114    }
115    /// 估算椭圆的周长。
116    ///
117    /// 使用椭圆的长轴和短轴估算椭圆的周长。这是一个近似值,使用 Ramanujan 的公式:
118    /// π * [3(a + b) - sqrt((3a + b)(a + 3b))]
119    ///
120    /// # 示例
121    ///
122    /// ```
123    /// use rs_math::graphical::ellipse::Ellipse;
124    /// let ellipse = Ellipse::new(5.0, 3.0, 0.0, 0.0, 0.0);
125    /// let circumference = ellipse.estimate_circumference();
126    /// println!("椭圆周长的估算值:{}", circumference);
127    /// ```
128    pub fn estimate_circumference(&self) -> f64 {
129        PI * (3.0 * (self.a + self.b) - ((3.0 * self.a + self.b) * (self.a + 3.0 * self.b)).sqrt())
130    }
131    /// 判断点和椭圆的关系并返回枚举值。
132    ///
133    /// # 参数
134    ///
135    /// * `x` - 点的 x 坐标。
136    /// * `y` - 点的 y 坐标。
137    ///
138    /// # 返回值
139    ///
140    /// 返回 `PointPosition` 枚举值,表示点和椭圆的关系。
141    ///
142    /// # 示例
143    ///
144    /// ```
145    /// use rs_math::graphical::ellipse::{Ellipse, PointPosition};
146    /// let ellipse = Ellipse::new(5.0, 3.0, 0.0, 0.0, 0.0);
147    /// let position = ellipse.point_position(2.0, 1.0);
148    /// match position {
149    ///     PointPosition::OnEllipse => println!("点在椭圆上"),
150    ///     PointPosition::InsideEllipse => println!("点在椭圆内部"),
151    ///     PointPosition::OutsideEllipse => println!("点在椭圆外部"),
152    /// }
153    /// ```
154    pub fn point_position(&self, x: f64, y: f64) -> PointPosition {
155        let distance_squared = ((x - self.h) / self.a).powi(2) + ((y - self.k) / self.b).powi(2);
156
157        if distance_squared == 1.0 {
158            PointPosition::OnEllipse
159        } else if distance_squared < 1.0 {
160            PointPosition::InsideEllipse
161        } else {
162            PointPosition::OutsideEllipse
163        }
164    }
165    /// 计算椭圆的焦点坐标。
166    ///
167    /// # 返回值
168    ///
169    /// 返回包含两个焦点坐标的元组。
170    ///
171    /// # 示例
172    ///
173    /// ```
174    /// use rs_math::graphical::ellipse::Ellipse;
175    /// let ellipse = Ellipse::new(5.0, 3.0, 0.0, 0.0, 0.0);
176    /// let foci_coordinates = ellipse.calculate_foci();
177    /// println!("焦点1坐标: {:?}", foci_coordinates.0);
178    /// println!("焦点2坐标: {:?}", foci_coordinates.1);
179    /// ```
180    pub fn calculate_foci(&self) -> ((f64, f64), (f64, f64)) {
181        let c = (self.a.powi(2) - self.b.powi(2)).sqrt();
182        let f1 = (self.h + c, self.k);
183        let f2 = (self.h - c, self.k);
184
185        (f1, f2)
186    }
187    /// 通过拟合给定点集合来估算椭圆。
188    ///
189    /// # 参数
190    ///
191    /// * `points` - 包含待拟合椭圆的点集合。
192    ///
193    /// # 返回值
194    ///
195    /// 如果拟合成功,返回一个包含椭圆参数的 `Option<Ellipse>`;否则返回 None。
196    ///
197    /// # 注意
198    ///
199    /// 至少需要 5 个点进行椭圆拟合。
200    ///
201    /// # 示例
202    ///
203    /// ```
204    /// use rs_math::graphical::ellipse::Ellipse;
205    ///
206    /// let points = vec![(1.0, 0.0), (0.0, 1.0), (-1.0, 0.0), (0.0, -1.0), (0.0, 0.0)];
207    /// let fitted_ellipse = Ellipse::fit(&points);
208    ///
209    /// match fitted_ellipse {
210    ///     Some(ellipse) => {
211    ///         println!("拟合椭圆成功!");
212    ///         ellipse.print_equation();
213    ///     }
214    ///     None => println!("拟合椭圆失败,至少需要 5 个点。"),
215    /// }
216    /// ```
217    pub fn fit(points: &[(f64, f64)]) -> Option<Ellipse> {
218        let n = points.len();
219        if n < 5 {
220            return None; // 至少需要 5 个点
221        }
222
223        // 归一化数据
224        let (mean_x, mean_y) = points.iter().fold((0.0, 0.0), |acc, &(x, y)| (acc.0 + x, acc.1 + y));
225        let mean_x = mean_x / n as f64;
226        let mean_y = mean_y / n as f64;
227
228        let normalized_points: Vec<(f64, f64)> = points
229            .iter()
230            .map(|&(x, y)| (x - mean_x, y - mean_y))
231            .collect();
232
233        // 构建设计矩阵
234        let mut d_matrix: Vec<Vec<f64>> = Vec::with_capacity(n);
235        for i in 0..n {
236            let (x, y) = normalized_points[i];
237            d_matrix.push(vec![x * x, x * y, y * y, x, y]);
238        }
239
240        // 构建响应矩阵
241        let r_matrix: Vec<Vec<f64>> = vec![vec![1.0; 1]; n];
242
243        // 解线性方程组
244        let result = gauss_elimination(&d_matrix, &r_matrix);
245
246        match result {
247            Some(parameters) => {
248                // 重建椭圆方程
249                let a = parameters[0];
250                let b = parameters[1];
251                let c = parameters[2];
252                let d = parameters[3];
253                let e = parameters[4];
254
255                // 反归一化
256                let h = mean_x - (2.0 * c * d + b * e) / (4.0 * a * c - b.powi(2));
257                let k = mean_y - (b * d + 2.0 * a * e) / (4.0 * a * c - b.powi(2));
258
259                let phi = 0.5 * (c - a).atan2(b);
260
261                Some(Ellipse::new(1.0 / a, 1.0 / c, h, k, phi))
262            }
263            None => None,
264        }
265    }
266}
267
268/// 使用高斯消元法解线性方程组。
269///
270/// # 参数
271///
272/// * `a` - 系数矩阵。
273/// * `b` - 右侧常数矩阵。
274///
275/// # 返回值
276///
277/// 如果方程组有唯一解,返回解的向量;否则返回 None。
278///
279/// # 注意
280///
281/// - `a` 和 `b` 的行数应相同。
282/// - 该函数会修改输入的矩阵。
283///
284/// # 示例
285///
286/// ```
287/// let a = vec![vec![2.0, -1.0, 1.0], vec![-3.0, 2.0, -1.0], vec![-2.0, 1.0, 2.0]];
288/// let b = vec![vec![8.0], vec![-11.0], vec![-3.0]];
289///
290/// match gauss_elimination(&a, &b) {
291///     Some(result) => println!("解向量:{:?}", result),
292///     None => println!("方程组无解或有无穷多解。"),
293/// }
294/// ```
295pub fn gauss_elimination(a: &Vec<Vec<f64>>, b: &Vec<Vec<f64>>) -> Option<Vec<f64>> {
296    let n = a.len();
297    let m = a[0].len();
298
299    // 构建增广矩阵
300    let mut augmented_matrix = vec![vec![0.0; m + 1]; n];
301    for i in 0..n {
302        for j in 0..m {
303            augmented_matrix[i][j] = a[i][j];
304        }
305        augmented_matrix[i][m] = b[i][0];
306    }
307
308    // 高斯消元
309    for i in 0..n {
310        for k in i + 1..n {
311            let factor = augmented_matrix[k][i] / augmented_matrix[i][i];
312            for j in i..m + 1 {
313                augmented_matrix[k][j] -= factor * augmented_matrix[i][j];
314            }
315        }
316    }
317
318    // 回代
319    let mut result = vec![0.0; n];
320    for i in (0..n).rev() {
321        result[i] = augmented_matrix[i][m];
322        for j in i + 1..n {
323            result[i] -= augmented_matrix[i][j] * result[j];
324        }
325        result[i] /= augmented_matrix[i][i];
326    }
327
328    Some(result)
329}