rs_math/graphical/
linear_equation.rs

1use std::f64::consts::PI;
2use std::f64::EPSILON;
3use std::fmt::Debug;
4use crate::graphical::circle::Circle;
5use crate::graphical::point_2d::Point2D;
6
7/// 代表二维平面上的直线方程。
8#[derive(Debug)]
9pub struct LinearEquation {
10    /// 直线方程的 x 系数。
11    pub a: f64,
12    /// 直线方程的 y 系数。
13    pub b: f64,
14    /// 直线方程的常数项。
15    pub c: f64,
16}
17
18/// 表示点与直线之间的关系。
19#[derive(Debug, PartialEq, Clone)]
20pub enum PointLineRelationship {
21    /// 点在直线上。
22    OnLine,
23    /// 点在直线上方。
24    AboveLine,
25    /// 点在直线下方。
26    BelowLine,
27}
28#[allow(dead_code)]
29impl LinearEquation {
30    /// 将直线方程转换为字符串表示。
31    pub fn to_string(&self) -> String {
32        format!("{}x + {}y + {} = 0", self.a, self.b, self.c)
33    }
34    /// 通过两点计算切线方程的一般式表示。
35    ///
36    /// # 参数
37    ///
38    /// * `x1` - 第一个点的 x 坐标。
39    /// * `y1` - 第一个点的 y 坐标。
40    /// * `x2` - 第二个点的 x 坐标。
41    /// * `y2` - 第二个点的 y 坐标。
42    ///
43    /// # 返回值
44    ///
45    /// 返回切线方程的一般式表示。
46    ///
47    /// # 示例
48    ///
49    /// ```
50    /// use rs_math::graphical::linear_equation::LinearEquation;
51    /// let equation = LinearEquation::from_points(1.0, 2.0, 3.0, 4.0);
52    /// ```
53    pub fn from_points(x1: f64, y1: f64, x2: f64, y2: f64) -> LinearEquation {
54        let a = y2 - y1;
55        let b = x1 - x2;
56        let c = x2 * y1 - x1 * y2;
57
58        LinearEquation {
59            a,
60            b,
61            c,
62        }
63    }
64    /// 从点斜式参数创建一般式方程。
65    ///
66    /// # 参数
67    ///
68    /// * `x1` - 直线上的一点的 x 坐标。
69    /// * `y1` - 直线上的一点的 y 坐标。
70    /// * `slope` - 直线的斜率。
71    ///
72    /// # 返回值
73    ///
74    /// 返回一般式方程的实例。
75    ///
76    /// # 示例
77    ///
78    /// ```
79    /// use rs_math::graphical::linear_equation::LinearEquation;
80    /// let equation = LinearEquation::from_point_slope(1.0, 2.0, 3.0);
81    /// ```
82    pub fn from_point_slope(x1: f64, y1: f64, slope: f64) -> Self {
83        // 一般式方程的 A 系数为 -slope
84        let a = -slope;
85        // 一般式方程的 B 系数为 1
86        let b = 1.0;
87        // 一般式方程的 C 系数为 y1 - slope * x1
88        let c = y1 - slope * x1;
89
90        // 创建并返回一般式方程的实例
91        LinearEquation { a, b, c }
92    }
93    /// 检查两条直线是否平行。
94    ///
95    /// # 参数
96    ///
97    /// * `other` - 与当前直线比较的另一条直线。
98    ///
99    /// # 返回值
100    ///
101    /// 如果两条直线平行,返回 `true`,否则返回 `false`。
102    ///
103    /// # 示例
104    ///
105    /// ```
106    /// use rs_math::graphical::linear_equation::LinearEquation;
107    /// let line1 = LinearEquation { a: 2.0, b: 3.0, c: 4.0 };
108    /// let line2 = LinearEquation { a: 2.0, b: 3.0, c: 7.0 };
109    /// assert!(line1.is_parallel_to(&line2));
110    /// ```
111    pub fn is_parallel_to(&self, other: &LinearEquation) -> bool {
112        // 两条直线平行的条件是它们的斜率相等
113        if let (Some(slope1), Some(slope2)) = (self.slope(), other.slope()) {
114            slope1 == slope2
115        } else {
116            false // 如果其中一条直线的斜率无法计算,则无法判断是否平行
117        }
118    }
119
120    /// 从截距式方程创建一般式方程。
121    ///
122    /// # 参数
123    ///
124    /// * `m` - 直线的斜率。
125    /// * `b` - y 轴截距。
126    ///
127    /// # 返回值
128    ///
129    /// 返回包含一般式方程的 `LinearEquation` 结构体实例。
130    ///
131    /// # 示例
132    ///
133    /// ```
134    /// use rs_math::graphical::linear_equation::LinearEquation;
135    /// let line = LinearEquation::from_slope_intercept(2.0, 3.0);
136    /// assert_eq!(line.to_string(), "-2x + y - 3 = 0");
137    /// ```
138    pub fn from_slope_intercept(m: f64, b: f64) -> Self {
139        // 一般式方程的 A 系数为 -m
140        let a = -m;
141        // 一般式方程的 B 系数为 1
142        // 一般式方程的 C 系数为 -b
143        let c = -b;
144
145        // 创建并返回一般式方程的实例
146        LinearEquation { a, b: 1.0, c }
147    }
148
149    /// 通过圆弧的两个端点计算切线方程的一般式表示。
150    ///
151    /// # 参数
152    ///
153    /// * `radius` - 圆弧的半径。
154    /// * `x1` - 第一个端点的 x 坐标。
155    /// * `y1` - 第一个端点的 y 坐标。
156    /// * `x2` - 第二个端点的 x 坐标。
157    /// * `y2` - 第二个端点的 y 坐标。
158    ///
159    /// # 返回值
160    ///
161    /// 返回包含切线方程的一般式表示的 `LinearEquation` 结构体实例。
162    ///
163    /// # 示例
164    ///
165    /// ```
166    /// use rs_math::graphical::linear_equation::LinearEquation;
167    /// let line = LinearEquation::from_arc(1.0, 0.0, 0.0, 1.0, 0.0);
168    /// assert_eq!(line.to_string(), "0x + y - 1 = 0");
169    /// ```
170    /// todo: 是否需要增加两点+半径求圆弧,再求切线方程
171    pub fn from_arc(_radius: f64, x1: f64, y1: f64, x2: f64, y2: f64) -> LinearEquation {
172        // 计算圆心坐标
173        let center_x = (x1 + x2) / 2.0;
174        let center_y = (y1 + y2) / 2.0;
175
176        // 计算切线斜率
177        let m = (y2 - y1) / (x2 - x1);
178
179        // 计算切线方程的斜率和截距
180        let a = -m;
181        let b = 1.0;
182        let c = -a * center_x - b * center_y;
183
184        LinearEquation { a: a, b: b, c: c }
185    }
186
187    /// 将直线沿 x 轴平移指定单位,返回新的直线方程。
188    ///
189    /// # 参数
190    ///
191    /// * `h` - 沿 x 轴的平移距离。
192    ///
193    /// # 返回值
194    ///
195    /// 返回包含平移后直线方程的一般式表示的 `LinearEquation` 结构体实例。
196    ///
197    /// # 示例
198    ///
199    /// ```
200    /// use rs_math::graphical::linear_equation::LinearEquation;
201    /// let line = LinearEquation { a: 1.0, b: 2.0, c: 3.0 };
202    /// let translated_line = line.translate_along_x(2.0);
203    /// assert_eq!(translated_line.to_string(), "1x + 2y - 1 = 0");
204    /// ```
205    pub fn translate_along_x(&self, h: f64) -> LinearEquation {
206        LinearEquation {
207            a: self.a,
208            b: self.b,
209            c: self.c + h,
210        }
211    }
212
213    /// 将直线绕原点逆时针旋转指定弧度,返回新的直线方程。
214    ///
215    /// # 参数
216    ///
217    /// * `theta` - 逆时针旋转的弧度。
218    ///
219    /// # 返回值
220    ///
221    /// 返回包含旋转后直线方程的一般式表示的 `LinearEquation` 结构体实例。
222    ///
223    /// # 示例
224    ///
225    /// ```
226    /// use rs_math::graphical::linear_equation::LinearEquation;
227    /// let line = LinearEquation { a: 1.0, b: 2.0, c: 3.0 };
228    /// let rotated_line = line.rotate_around_origin(1.0);
229    /// // 根据旋转后的具体结果进行验证
230    /// ```
231    pub fn rotate_around_origin(&self, theta: f64) -> LinearEquation {
232        let cos_theta = theta.cos();
233        let sin_theta = theta.sin();
234
235        // 定义旋转矩阵
236        let rotation_matrix = [
237            [cos_theta, -sin_theta],
238            [sin_theta, cos_theta],
239        ];
240
241        // 计算新的系数
242        let new_a = self.a * rotation_matrix[0][0] + self.b * rotation_matrix[0][1];
243        let new_b = self.a * rotation_matrix[1][0] + self.b * rotation_matrix[1][1];
244        let new_c = self.c;
245
246        // 返回新的直线方程
247        LinearEquation {
248            a: new_a,
249            b: new_b,
250            c: new_c,
251        }
252    }
253
254    /// 将直线绕任意点逆时针旋转指定弧度,返回新的直线方程。
255    ///
256    /// # 参数
257    ///
258    /// * `theta` - 逆时针旋转的弧度。
259    /// * `center` - 旋转中心的坐标。
260    ///
261    /// # 返回值
262    ///
263    /// 返回包含旋转后直线方程的一般式表示的 `LinearEquation` 结构体实例。
264    ///
265    /// # 示例
266    ///
267    /// ```
268    /// use rs_math::graphical::linear_equation::LinearEquation;
269    /// let line = LinearEquation { a: 1.0, b: 2.0, c: 3.0 };
270    /// let rotated_line = line.rotate_around_point(1.0, (0.0, 0.0));
271    /// // 根据旋转后的具体结果进行验证
272    /// ```
273    pub fn rotate_around_point(&self, theta: f64, center: (f64, f64)) -> LinearEquation {
274        // 计算旋转矩阵
275        let cos_theta = theta.cos();
276        let sin_theta = theta.sin();
277
278        // 将直线平移到旋转中心
279        let mut translated_line = self.translate(-center.0, -center.1);
280
281        // 应用旋转矩阵
282        let new_a = self.a * cos_theta - self.b * sin_theta;
283        let new_b = self.a * sin_theta + self.b * cos_theta;
284
285        // 更新新的系数
286        translated_line.a = new_a;
287        translated_line.b = new_b;
288
289        // 将直线还原到原来的位置
290        translated_line.translate(center.0, center.1)
291    }
292
293    /// 将直线沿 x 轴平移指定距离,沿 y 轴平移指定距离。
294    ///
295    /// # 参数
296    ///
297    /// * `h` - 沿 x 轴的平移距离。
298    /// * `k` - 沿 y 轴的平移距离。
299    ///
300    /// # 返回值
301    ///
302    /// 返回包含平移后直线方程的一般式表示的 `LinearEquation` 结构体实例。
303    ///
304    /// # 示例
305    ///
306    /// ```
307    /// use rs_math::graphical::linear_equation::LinearEquation;
308    /// let line = LinearEquation { a: 1.0, b: 2.0, c: 3.0 };
309    /// let translated_line = line.translate(2.0, 3.0);
310    /// ```
311    pub fn translate(&self, h: f64, k: f64) -> LinearEquation {
312        LinearEquation {
313            a: self.a,
314            b: self.b,
315            c: self.c + self.a * h + self.b * k,
316        }
317    }
318    /// 计算直线与 X 轴和 Y 轴的夹角(弧度)。
319    ///
320    /// # 返回值
321    ///
322    /// 返回包含直线与 X 轴和 Y 轴夹角的元组 `(angle_with_x_axis, angle_with_y_axis)`。
323    ///
324    /// # 示例
325    ///
326    /// ```
327    /// use rs_math::graphical::linear_equation::LinearEquation;
328    /// let line = LinearEquation { a: 1.0, b: 2.0, c: 3.0 };
329    /// let (angle_with_x_axis, angle_with_y_axis) = line.angles_with_axes();
330    /// 
331    /// ```
332    pub fn angles_with_axes(&self) -> (f64, f64) {
333        // 计算斜率
334        let slope = -self.a / self.b;
335
336        // 计算与 X 轴的夹角
337        let angle_with_x_axis = slope.atan();
338
339        // 计算与 Y 轴的夹角
340        let angle_with_y_axis = PI / 2.0 - angle_with_x_axis;
341
342        (angle_with_x_axis, angle_with_y_axis)
343    }
344    /// 判断点与直线的位置关系。
345    ///
346    /// # 参数
347    ///
348    /// * `point` - 要判断的点的坐标。
349    ///
350    /// # 返回值
351    ///
352    /// 返回 [`PointLineRelationship`] 枚举,表示点与直线的位置关系。
353    ///
354    /// # 示例
355    ///
356    /// ```
357    /// use rs_math::graphical::linear_equation::{LinearEquation, PointLineRelationship};
358    /// use rs_math::graphical::point_2d::Point2D;
359    ///
360    /// let line = LinearEquation { a: 1.0, b: -1.0, c: 0.0 };
361    /// let point = Point2D { x: 2.0, y: 2.0 };
362    /// let relationship = line.point_line_relationship(&point);
363    /// 
364    /// ```
365    pub fn point_line_relationship(&self, point: &Point2D) -> PointLineRelationship {
366        let result = self.a * point.x + self.b * point.y + self.c;
367
368        if result == 0.0 {
369            PointLineRelationship::OnLine
370        } else if result > 0.0 {
371            PointLineRelationship::AboveLine
372        } else {
373            PointLineRelationship::BelowLine
374        }
375    }
376
377    /// 判断直线与圆是否相切。
378    ///
379    /// # 参数
380    ///
381    /// * `circle` - 圆的信息。
382    ///
383    /// # 返回值
384    ///
385    /// 如果直线与圆相切,返回 `true`;否则,返回 `false`。
386    ///
387    /// # 注意
388    ///
389    /// 在比较距离时,考虑到浮点数误差,使用了 EPSILON 常量。
390    ///
391    /// # 示例
392    ///
393    /// ```
394    /// use rs_math::graphical::linear_equation::LinearEquation;
395    /// use rs_math::graphical::circle::Circle;
396    ///
397    /// let line = LinearEquation { a: 1.0, b: -1.0, c: 0.0 };
398    /// let circle = Circle::new(0.0, 0.0, 1.0);
399    /// let is_tangent = line.is_tangent_to_circle(&circle);
400    /// 
401    /// ```
402    pub fn is_tangent_to_circle(&self, circle: &Circle) -> bool {
403        // 计算直线到圆心的距离
404        let distance_to_center = (self.a * circle.x + self.b * circle.y + self.c).abs()
405            / f64::sqrt(self.a.powi(2) + self.b.powi(2));
406
407        // 判断是否相切(距离差小于 EPSILON,考虑浮点数误差)
408        (distance_to_center - circle.radius).abs() < EPSILON
409    }
410    /// 判断直线是否垂直于 X 轴。
411    ///
412    /// # 返回值
413    ///
414    /// 如果直线垂直于 X 轴,返回 `true`;否则,返回 `false`。
415    ///
416    /// # 示例
417    ///
418    /// ```
419    /// use rs_math::graphical::linear_equation::LinearEquation;
420    ///
421    /// let line = LinearEquation { a: 2.0, b: 0.0, c: 3.0 };
422    /// let is_vertical = line.is_vertical_to_x_axis();
423    /// 
424    /// ```
425    pub fn is_vertical_to_x_axis(&self) -> bool {
426        self.b == 0.0
427    }
428
429    /// 判断直线是否垂直于 Y 轴。
430    ///
431    /// # 返回值
432    ///
433    /// 如果直线垂直于 Y 轴,返回 `true`;否则,返回 `false`。
434    ///
435    /// # 示例
436    ///
437    /// ```
438    /// use rs_math::graphical::linear_equation::LinearEquation;
439    ///
440    /// let line = LinearEquation { a: 0.0, b: 3.0, c: 5.0 };
441    /// let is_vertical = line.is_vertical_to_y_axis();
442    /// 
443    /// ```
444    pub fn is_vertical_to_y_axis(&self) -> bool {
445        self.a == 0.0
446    }
447
448    /// 判断两条直线是否相交。
449    ///
450    /// # 返回值
451    ///
452    /// 如果两条直线相交,返回 `true`;否则,返回 `false`。
453    ///
454    /// # 示例
455    ///
456    /// ```
457    /// use rs_math::graphical::linear_equation::LinearEquation;
458    ///
459    /// let line1 = LinearEquation { a: 2.0, b: -3.0, c: 5.0 };
460    /// let line2 = LinearEquation { a: -4.0, b: 6.0, c: 8.0 };
461    /// let do_intersect = line1.are_intersecting(&line2);
462    /// 
463    /// ```
464    pub fn are_intersecting(&self, other: &LinearEquation) -> bool {
465        !(self.is_parallel_to(other) || self.is_equal_to(other))
466    }
467
468    /// 判断两条直线是否平行。
469    ///
470    /// # 返回值
471    ///
472    /// 如果两条直线平行,返回 `true`;否则,返回 `false`。
473    ///
474    /// # 示例
475    ///
476    /// ```
477    /// use rs_math::graphical::linear_equation::LinearEquation;
478    ///
479    /// let line1 = LinearEquation { a: 2.0, b: -3.0, c: 5.0 };
480    /// let line2 = LinearEquation { a: 4.0, b: -6.0, c: 10.0 };
481    /// let are_parallel = line1.are_parallel(&line2);
482    /// 
483    /// ```
484    pub fn are_parallel(&self, other: &LinearEquation) -> bool {
485        self.a * other.b == self.b * other.a
486    }
487
488    /// 判断两条直线是否垂直。
489    ///
490    /// # 返回值
491    ///
492    /// 如果两条直线垂直,返回 `true`;否则,返回 `false`。
493    ///
494    /// # 示例
495    ///
496    /// ```
497    /// use rs_math::graphical::linear_equation::LinearEquation;
498    ///
499    /// let line1 = LinearEquation { a: 2.0, b: -3.0, c: 5.0 };
500    /// let line2 = LinearEquation { a: 3.0, b: 2.0, c: -4.0 };
501    /// let are_perpendicular = line1.are_perpendicular(&line2);
502    ///
503    /// ```
504    pub fn are_perpendicular(&self, other: &LinearEquation) -> bool {
505        self.a * other.a + self.b * other.b == 0.0
506    }
507
508    /// 判断两条直线是否相等。
509    ///
510    /// # 返回值
511    ///
512    /// 如果两条直线相等,返回 `true`;否则,返回 `false`。
513    ///
514    /// # 示例
515    ///
516    /// ```
517    /// use rs_math::graphical::linear_equation::LinearEquation;
518    ///
519    /// let line1 = LinearEquation { a: 2.0, b: -3.0, c: 5.0 };
520    /// let line2 = LinearEquation { a: 2.0, b: -3.0, c: 5.0 };
521    /// let are_equal = line1.is_equal_to(&line2);
522    ///
523    /// ```
524    pub fn is_equal_to(&self, other: &LinearEquation) -> bool {
525        self.a == other.a && self.b == other.b && self.c == other.c
526    }
527
528    /// 获取直线的斜率。
529    ///
530    /// # 返回值
531    ///
532    /// 如果直线与 X 轴垂直,返回 `None`;否则,返回直线的斜率 `Some(slope)`。
533    ///
534    /// # 示例
535    ///
536    /// ```
537    /// use rs_math::graphical::linear_equation::LinearEquation;
538    ///
539    /// let line = LinearEquation { a: 2.0, b: -3.0, c: 5.0 };
540    /// let slope = line.slope();
541    ///
542    /// ```
543    pub fn slope(&self) -> Option<f64> {
544        if self.is_vertical_to_x_axis() {
545            None // 斜率不存在
546        } else {
547            Some(-self.a / self.b)
548        }
549    }
550    /// 将一般式直线方程转换为斜率截距形式。
551    ///
552    /// # 返回值
553    ///
554    /// 如果直线方程的 B 不为零,则返回 `Some((slope, intercept))`,
555    /// 其中 `slope` 是斜率,`intercept` 是截距。
556    ///
557    /// 如果直线方程的 B 为零(垂直于 X 轴),则返回 `None`,因为斜率不存在。
558    ///
559    /// # 示例
560    ///
561    /// ```
562    /// use rs_math::graphical::linear_equation::LinearEquation;
563    ///
564    /// let line = LinearEquation { a: 2.0, b: -3.0, c: 5.0 };
565    /// let result = line.to_slope_intercept_form();
566    ///
567    /// ```
568    pub fn to_slope_intercept_form(&self) -> Option<(f64, f64)> {
569        if self.b != 0.0 {
570            let slope = -self.a / self.b;
571            let intercept = -self.c / self.b;
572            Some((slope, intercept))
573        } else {
574            None // 如果 B 为零,斜率不存在
575        }
576    }
577
578    /// 将一般式直线方程转换为点斜式。
579    ///
580    /// # 返回值
581    ///
582    /// 如果直线方程的 B 不为零,则返回 `Some((slope, point))`,
583    /// 其中 `slope` 是斜率,`point` 是直线上的一点 (x, y)。
584    ///
585    /// 如果直线方程的 B 为零(垂直于 X 轴),则返回 `None`,因为斜率不存在。
586    ///
587    /// # 示例
588    ///
589    /// ```
590    /// use rs_math::graphical::linear_equation::LinearEquation;
591    ///
592    /// let line = LinearEquation { a: 2.0, b: -3.0, c: 5.0 };
593    /// let result = line.to_point_slope_form();
594    ///
595    /// ```
596    pub fn to_point_slope_form(&self) -> Option<(f64, (f64, f64))> {
597        if self.b != 0.0 {
598            let slope = -self.a / self.b;
599            let point = (0.0, -self.c / self.b);
600            Some((slope, point))
601        } else {
602            None // 如果 B 为零,斜率不存在
603        }
604    }
605}