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}