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}