rs_math/graphical/
circle.rs

1use std::f64::consts::PI;
2use crate::graphical::point_2d::Point2D;
3use crate::graphical::rectangle::Rectangle;
4
5/// 表示一个圆的结构体。
6#[derive(Debug, PartialEq)]
7pub struct Circle {
8    /// 圆的中心坐标
9    pub x: f64,
10    pub y: f64,
11    /// 圆的半径
12    pub radius: f64,
13}
14
15#[allow(dead_code)]
16impl Circle {
17    /// 通过两点和半径创建圆。
18    ///
19    /// # 参数
20    ///
21    /// * `point1` - 圆上的第一个点。
22    /// * `point2` - 圆上的第二个点。
23    /// * `radius` - 圆的半径。
24    ///
25    /// # 返回值
26    ///
27    /// 如果给定半径有效,返回一个包含圆心和半径的 `Circle` 结构体实例;否则返回 `None`。
28    ///
29    /// # 示例
30    ///
31    /// ```
32    /// use rs_math::graphical::point_2d::Point2D;
33    /// use rs_math::graphical::circle::Circle;
34    ///
35    /// let point1 = Point2D { x: 0.0, y: 0.0 };
36    /// let point2 = Point2D { x: 1.0, y: 0.0 };
37    /// let radius = 0.5;
38    /// let circle = Circle::from_points_and_radius(&point1, &point2, radius);
39    /// ```
40    pub fn from_points_and_radius(point1: &Point2D, point2: &Point2D, radius: f64) -> Option<Circle> {
41        // 计算圆心的中点坐标
42        let center_x = (point1.x + point2.x) / 2.0;
43        let center_y = (point1.y + point2.y) / 2.0;
44
45        // 计算两点间的距离
46        let distance = ((point2.x - point1.x).powi(2) + (point2.y - point1.y).powi(2)).sqrt();
47
48        // 验证半径是否有效
49        if (radius - distance / 2.0).abs() < std::f64::EPSILON {
50            Some(Circle {
51                x: center_x,
52                y: center_y,
53                radius,
54            })
55        } else {
56            None
57        }
58    }
59    /// 通过三个点创建圆。
60    ///
61    /// # 参数
62    ///
63    /// * `p1` - 圆上的第一个点。
64    /// * `p2` - 圆上的第二个点。
65    /// * `p3` - 圆上的第三个点。
66    ///
67    /// # 返回值
68    ///
69    /// 如果给定三个点共线,返回 `None`;否则返回一个包含圆心和半径的 `Circle` 结构体实例。
70    ///
71    /// # 示例
72    ///
73    /// ```
74    /// use rs_math::graphical::point_2d::Point2D;
75    /// use rs_math::graphical::circle::Circle;
76    ///
77    /// let p1 = Point2D { x: 0.0, y: 0.0 };
78    /// let p2 = Point2D { x: 1.0, y: 0.0 };
79    /// let p3 = Point2D { x: 0.0, y: 1.0 };
80    /// let circle = Circle::from_points(&p1, &p2, &p3);
81    /// ```
82    pub fn from_points(p1: &Point2D, p2: &Point2D, p3: &Point2D) -> Option<Circle> {
83        // 计算圆心坐标 (h, k)
84        let h = (p1.x + p2.x) / 2.0;
85        let k = (p1.y + p2.y) / 2.0;
86
87        // 计算半径 r
88        let r = ((p1.x - h).powi(2) + (p1.y - k).powi(2)).sqrt();
89
90        // 检查第三个点是否在圆上
91        let distance_to_center_squared = (p3.x - h).powi(2) + (p3.y - k).powi(2);
92        let epsilon = 1e-6; // 设置一个小的误差范围
93
94        // 如果给定三个点共线,返回 None;否则返回包含圆心和半径的 Circle 结构体实例。
95        if (distance_to_center_squared - r.powi(2)).abs() < epsilon {
96            Some(Circle { x: h, y: k, radius: r })
97        } else {
98            None
99        }
100    }
101    /// 创建一个新的圆实例。
102    ///
103    /// # 参数
104    ///
105    /// * `x` - 圆的中心横坐标。
106    /// * `y` - 圆的中心纵坐标。
107    /// * `radius` - 圆的半径。
108    ///
109    /// # 返回值
110    ///
111    /// 返回一个包含给定圆心和半径的 `Circle` 结构体实例。
112    ///
113    /// # 示例
114    ///
115    /// ```
116    /// use rs_math::graphical::circle::Circle;
117    ///
118    /// let circle = Circle::new(0.0, 0.0, 1.0);
119    /// ```
120    pub fn new(x: f64, y: f64, radius: f64) -> Circle {
121        Circle { x, y, radius }
122    }
123
124    /// 计算圆的面积。
125    ///
126    /// # 返回值
127    ///
128    /// 返回圆的面积,使用标准的 π(pi) 值。
129    ///
130    /// # 示例
131    ///
132    /// ```
133    /// use rs_math::graphical::circle::Circle;
134    ///
135    /// let circle = Circle::new(0.0, 0.0, 1.0);
136    /// let area = circle.area();
137    /// ```
138    pub fn area(&self) -> f64 {
139        std::f64::consts::PI * self.radius * self.radius
140    }
141
142    /// 判断给定点是否在圆内。
143    ///
144    /// # 参数
145    ///
146    /// * `point_x` - 待判断点的横坐标。
147    /// * `point_y` - 待判断点的纵坐标。
148    ///
149    /// # 返回值
150    ///
151    /// 如果给定点在圆内或在圆上,返回 `true`;否则返回 `false`。
152    ///
153    /// # 示例
154    ///
155    /// ```
156    /// use rs_math::graphical::circle::Circle;
157    ///
158    /// let circle = Circle::new(0.0, 0.0, 1.0);
159    /// let is_inside = circle.is_point_inside(0.5, 0.5);
160    /// ```
161    pub fn is_point_inside(&self, point_x: f64, point_y: f64) -> bool {
162        let distance_squared = (point_x - self.x).powi(2) + (point_y - self.y).powi(2);
163        distance_squared <= self.radius.powi(2)
164    }
165    /// 生成圆上的点。
166    ///
167    /// # 参数
168    ///
169    /// * `num_points` - 要生成的点的数量。
170    ///
171    /// # 返回值
172    ///
173    /// 返回一个包含圆上生成点的 `Vec<Point2D>`。
174    ///
175    /// # 示例
176    ///
177    /// ```
178    /// use rs_math::graphical::circle::Circle;
179    /// use rs_math::graphical::point_2d::Point2D;
180    ///
181    /// let circle = Circle::new(0.0, 0.0, 1.0);
182    /// let points = circle.generate_points(10);
183    /// ```
184    pub fn generate_points(&self, num_points: usize) -> Vec<Point2D> {
185        generate_points_on_circle(self.x, self.y, self.radius, num_points)
186    }
187    /// 判断点是否在圆弧上。
188    ///
189    /// # 参数
190    ///
191    /// * `start_angle` - 圆弧的起始角度。
192    /// * `end_angle` - 圆弧的结束角度。
193    /// * `point` - 待判断的点。
194    ///
195    /// # 返回值
196    ///
197    /// 如果给定点在圆弧上,返回 `true`;否则返回 `false`。
198    ///
199    /// # 示例
200    ///
201    /// ```
202    /// use rs_math::graphical::circle::Circle;
203    /// use rs_math::graphical::point_2d::Point2D;
204    ///
205    /// let circle = Circle::new(0.0, 0.0, 1.0);
206    /// let start_angle = 0.0;
207    /// let end_angle = std::f64::consts::PI;
208    /// let point = Point2D { x: 1.0, y: 0.0 };
209    /// let is_on_arc = circle.is_point_on_arc(start_angle, end_angle, &point);
210    /// ```
211    pub fn is_point_on_arc(&self, start_angle: f64, end_angle: f64, point: &Point2D) -> bool {
212        let distance_squared = (point.x - self.x).powi(2) + (point.y - self.y).powi(2);
213        let distance = distance_squared.sqrt();
214
215        distance == self.radius && self.is_angle_in_range(start_angle, end_angle, point)
216    }
217
218    /// 判断夹角是否在指定范围内的辅助函数。
219    ///
220    /// # 参数
221    ///
222    /// * `start_angle` - 范围的起始角度。
223    /// * `end_angle` - 范围的结束角度。
224    /// * `point` - 待判断的点。
225    ///
226    /// # 返回值
227    ///
228    /// 如果给定点的夹角在指定范围内,返回 `true`;否则返回 `false`。
229    ///
230    /// # 示例
231    ///
232    /// ```
233    /// use rs_math::graphical::circle::Circle;
234    /// use rs_math::graphical::point_2d::Point2D;
235    ///
236    /// let circle = Circle::new(0.0, 0.0, 1.0);
237    /// let start_angle = 0.0;
238    /// let end_angle = std::f64::consts::PI;
239    /// let point = Point2D { x: 1.0, y: 0.0 };
240    /// let is_in_range = circle.is_angle_in_range(start_angle, end_angle, &point);
241    /// ```
242    pub fn is_angle_in_range(&self, start_angle: f64, end_angle: f64, point: &Point2D) -> bool {
243        let angle = (point.y - self.x).atan2(point.x - self.y);
244        let positive_angle = if angle < 0.0 {
245            2.0 * std::f64::consts::PI + angle
246        } else {
247            angle
248        };
249        positive_angle >= start_angle && positive_angle <= end_angle
250    }
251
252    /// 判断点是否在圆边界上。
253    ///
254    /// # 参数
255    ///
256    /// * `point` - 待判断的点。
257    ///
258    /// # 返回值
259    ///
260    /// 如果给定点在圆边界上,返回 `true`;否则返回 `false`。
261    ///
262    /// # 示例
263    ///
264    /// ```
265    /// use rs_math::graphical::circle::Circle;
266    /// use rs_math::graphical::point_2d::Point2D;
267    ///
268    /// let circle = Circle::new(0.0, 0.0, 1.0);
269    /// let point = Point2D { x: 1.0, y: 0.0 };
270    /// let is_on_boundary = circle.is_point_on_circle_boundary(&point);
271    /// ```
272    pub fn is_point_on_circle_boundary(&self, point: &Point2D) -> bool {
273        let distance = distance_between_points(point.x, point.y, self.x, self.y);
274        distance == self.radius
275    }
276
277    /// 寻找与直线的交点。
278    ///
279    /// # 参数
280    ///
281    /// * `p1` - 直线上的第一个点。
282    /// * `p2` - 直线上的第二个点。
283    ///
284    /// # 返回值
285    ///
286    /// 返回一个包含交点的 `Vec<Point2D>`。
287    ///
288    /// # 示例
289    ///
290    /// ```
291    /// use rs_math::graphical::circle::Circle;
292    /// use rs_math::graphical::point_2d::Point2D;
293    ///
294    /// let circle = Circle::new(0.0, 0.0, 1.0);
295    /// let point1 = Point2D { x: -2.0, y: 0.0 };
296    /// let point2 = Point2D { x: 2.0, y: 0.0 };
297    /// let intersections = circle.find_line_intersection(&point1, &point2);
298    /// ```
299    pub fn find_line_intersection(&self, p1: &Point2D, p2: &Point2D) -> Vec<Point2D> {
300        let dx = p2.x - p1.x;
301        let dy = p2.y - p1.y;
302
303        let a = dx * dx + dy * dy;
304        let b = 2.0 * (dx * (p1.x - self.x) + dy * (p1.y - self.y));
305        let c = (p1.x - self.x) * (p1.x - self.x) + (p1.y - self.y) * (p1.y - self.y) - self.radius * self.radius;
306
307        let discriminant = b * b - 4.0 * a * c;
308
309        if discriminant < 0.0 {
310            // 无实数解(无交点)
311            return Vec::new();
312        }
313
314        let t1 = (-b + discriminant.sqrt()) / (2.0 * a);
315        let t2 = (-b - discriminant.sqrt()) / (2.0 * a);
316
317        let mut intersections = Vec::new();
318
319        if t1 >= 0.0 && t1 <= 1.0 {
320            intersections.push(Point2D {
321                x: p1.x + t1 * dx,
322                y: p1.y + t1 * dy,
323            });
324        }
325
326        if t2 >= 0.0 && t2 <= 1.0 {
327            intersections.push(Point2D {
328                x: p1.x + t2 * dx,
329                y: p1.y + t2 * dy,
330            });
331        }
332
333        intersections
334    }
335    /// 判断两个圆是否相交。
336    ///
337    /// # 参数
338    ///
339    /// * `other` - 另一个圆的实例。
340    ///
341    /// # 返回值
342    ///
343    /// 如果两个圆相交,返回 `true`;否则返回 `false`。
344    ///
345    /// # 示例
346    ///
347    /// ```
348    /// use rs_math::graphical::circle::Circle;
349    ///
350    /// let circle1 = Circle::new(0.0, 0.0, 1.0);
351    /// let circle2 = Circle::new(2.0, 0.0, 1.0);
352    /// let do_intersect = circle1.circles_intersect(&circle2);
353    /// ```
354    pub fn circles_intersect(&self, other: &Circle) -> bool {
355        let distance_between_centers = ((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt();
356        let sum_of_radii = self.radius + other.radius;
357
358        distance_between_centers < sum_of_radii
359    }
360
361    /// 判断两个圆是否相切。
362    ///
363    /// # 参数
364    ///
365    /// * `other` - 另一个圆的实例。
366    ///
367    /// # 返回值
368    ///
369    /// 如果两个圆相切,返回 `true`;否则返回 `false`。
370    ///
371    /// # 示例
372    ///
373    /// ```
374    /// use rs_math::graphical::circle::Circle;
375    ///
376    /// let circle1 = Circle::new(0.0, 0.0, 1.0);
377    /// let circle2 = Circle::new(2.0, 0.0, 1.0);
378    /// let do_touch = circle1.circles_touch(&circle2);
379    /// ```
380    pub fn circles_touch(&self, other: &Circle) -> bool {
381        let distance_between_centers = ((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt();
382        let sum_of_radii = self.radius + other.radius;
383
384        (distance_between_centers - sum_of_radii).abs() < f64::EPSILON
385    }
386
387    /// 判断一个圆是否完全包含另一个圆。
388    ///
389    /// # 参数
390    ///
391    /// * `other` - 另一个圆的实例。
392    ///
393    /// # 返回值
394    ///
395    /// 如果一个圆完全包含另一个圆,返回 `true`;否则返回 `false`。
396    ///
397    /// # 示例
398    ///
399    /// ```
400    /// use rs_math::graphical::circle::Circle;
401    ///
402    /// let large_circle = Circle::new(0.0, 0.0, 2.0);
403    /// let small_circle = Circle::new(0.0, 0.0, 1.0);
404    /// let does_contain = large_circle.circle_contains(&small_circle);
405    /// ```
406    pub fn circle_contains(&self, other: &Circle) -> bool {
407        let distance_between_centers = ((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt();
408        distance_between_centers + other.radius <= self.radius
409    }
410
411    /// 判断圆心是否在矩形内。
412    ///
413    /// 公式:`circle_x >= rect.x1 && circle_x <= rect.x2 && circle_y >= rect.y1 && circle_y <= rect.y2`
414    ///
415    /// 圆心的 x 坐标在矩形的 x 范围内,且圆心的 y 坐标在矩形的 y 范围内。
416    ///
417    /// # 参数
418    ///
419    /// * `rect` - 包含矩形的实例。
420    ///
421    /// # 返回值
422    ///
423    /// 如果圆心在矩形内,返回 `true`;否则返回 `false`。
424    ///
425    /// # 示例
426    ///
427    /// ```
428    /// use rs_math::graphical::circle::Circle;
429    /// use rs_math::graphical::rectangle::Rectangle;
430    ///
431    /// let circle = Circle::new(1.0, 1.0, 2.0);
432    /// let rectangle = Rectangle::new(0.0, 0.0, 3.0, 3.0);
433    /// let is_inside = circle.circle_inside_rectangle(&rectangle);
434    /// ```
435    pub fn circle_inside_rectangle(&self, rect: &Rectangle) -> bool {
436        self.x >= rect.x1 && self.x <= rect.x2 && self.y >= rect.y1 && self.y <= rect.y2
437    }
438
439    /// 判断圆心是否在矩形的某个边上。
440    ///
441    /// 公式:
442    /// - `(circle_x == rect.x1 || circle_x == rect.x2) && circle_y >= rect.y1 && circle_y <= rect.y2`
443    /// 或
444    /// - `circle_x >= rect.x1 && circle_x <= rect.x2 && (circle_y == rect.y1 || circle_y == rect.y2)`
445    ///
446    /// 圆心在矩形的 x 或 y 范围的一个边界上,但不在矩形内部。
447    ///
448    /// # 参数
449    ///
450    /// * `rect` - 包含矩形的实例。
451    ///
452    /// # 返回值
453    ///
454    /// 如果圆心在矩形的边上,返回 `true`;否则返回 `false`。
455    ///
456    /// # 示例
457    ///
458    /// ```
459    /// use rs_math::graphical::circle::Circle;
460    /// use rs_math::graphical::rectangle::Rectangle;
461    ///
462    /// let circle = Circle::new(2.0, 2.0, 1.0);
463    /// let rectangle = Rectangle::new(1.0, 1.0, 3.0, 3.0);
464    /// let on_edge = circle.circle_on_rectangle_edge(&rectangle);
465    /// ```
466    pub fn circle_on_rectangle_edge(&self, rect: &Rectangle) -> bool {
467        (self.x == rect.x1 || self.x == rect.x2) && self.y >= rect.y1 && self.y <= rect.y2 || self.x >= rect.x1 && self.x <= rect.x2 && (self.y == rect.y1 || self.y == rect.y2)
468    }
469
470    /// 判断圆心是否在矩形的角上。
471    ///
472    /// 公式:
473    /// - `(circle_x == rect.x1 && circle_y == rect.y1)`
474    /// 或
475    /// - `(circle_x == rect.x1 && circle_y == rect.y2)`
476    /// 或
477    /// - `(circle_x == rect.x2 && circle_y == rect.y1)`
478    /// 或
479    /// - `(circle_x == rect.x2 && circle_y == rect.y2)`
480    ///
481    /// 圆心在矩形的 x 或 y 范围的一个边界上,并且与另一个边界相交。
482    ///
483    /// # 参数
484    ///
485    /// * `rect` - 包含矩形的实例。
486    ///
487    /// # 返回值
488    ///
489    /// 如果圆心在矩形的角上,返回 `true`;否则返回 `false`。
490    ///
491    /// # 示例
492    ///
493    /// ```
494    /// use rs_math::graphical::circle::Circle;
495    /// use rs_math::graphical::rectangle::Rectangle;
496    ///
497    /// let circle = Circle::new(1.0, 1.0, 0.5);
498    /// let rectangle = Rectangle::new(0.0, 0.0, 2.0, 2.0);
499    /// let on_corner = circle.circle_on_rectangle_corner(&rectangle);
500    /// ```
501    pub fn circle_on_rectangle_corner(&self, rect: &Rectangle) -> bool {
502        (self.x == rect.x1 && self.y == rect.y1) || (self.x == rect.x1 && self.y == rect.y2) || (self.x == rect.x2 && self.y == rect.y1) || (self.x == rect.x2 && self.y == rect.y2)
503    }
504
505    /// 获取圆的外接矩形。
506    ///
507    /// 外接矩形的左上角坐标为 `(circle_x - radius, circle_y - radius)`,
508    /// 右下角坐标为 `(circle_x + radius, circle_y + radius)`。
509    ///
510    /// # 返回值
511    ///
512    /// 返回一个包含外接矩形坐标的 `Rectangle` 结构体实例。
513    ///
514    /// # 示例
515    ///
516    /// ```
517    /// use rs_math::graphical::circle::Circle;
518    /// use rs_math::graphical::rectangle::Rectangle;
519    ///
520    /// let circle = Circle::new(3.0, 4.0, 2.0);
521    /// let bounding_box = circle.bounding_box();
522    /// ```
523
524    pub fn bounding_box(&self) -> Rectangle {
525        Rectangle {
526            x1: self.x - self.radius,
527            y1: self.y - self.radius,
528            x2: self.x + self.radius,
529            y2: self.y + self.radius,
530        }
531    }
532}
533/// 计算两点之间的欧几里德距离。
534///
535/// 公式:sqrt((x2 - x1)^2 + (y2 - y1)^2)
536///
537/// # 参数
538///
539/// * `x1` - 第一个点的 x 坐标。
540/// * `y1` - 第一个点的 y 坐标。
541/// * `x2` - 第二个点的 x 坐标。
542/// * `y2` - 第二个点的 y 坐标。
543///
544/// # 返回值
545///
546/// 返回两点之间的欧几里德距离。
547///
548/// # 示例
549///
550/// ```
551/// use rs_math::graphical::circle::distance_between_points;
552///
553/// let distance = distance_between_points(1.0, 2.0, 4.0, 6.0);
554/// ```
555pub fn distance_between_points(x1: f64, y1: f64, x2: f64, y2: f64) -> f64 {
556    ((x2 - x1).powi(2) + (y2 - y1).powi(2)).sqrt()
557}
558
559/// 生成圆上的均匀分布的点集合。
560///
561/// # 参数
562///
563/// * `center_x` - 圆心的 x 坐标。
564/// * `center_y` - 圆心的 y 坐标。
565/// * `radius` - 圆的半径。
566/// * `num_points` - 生成的点的数量。
567///
568/// # 返回值
569///
570/// 返回一个包含生成的点坐标的 `Vec<Point2D>` 实例。
571///
572/// # 示例
573///
574/// ```
575/// use rs_math::graphical::circle::generate_points_on_circle;
576///
577/// let points = generate_points_on_circle(0.0, 0.0, 1.0, 8);
578/// ```
579
580pub fn generate_points_on_circle(center_x: f64, center_y: f64, radius: f64, num_points: usize) -> Vec<Point2D> {
581    // 存储生成的点的容器
582    let mut points = Vec::with_capacity(num_points);
583
584    // 计算角度步长,确保点在圆上均匀分布
585    let angle_step = 2.0 * PI / num_points as f64;
586
587    // 生成点的坐标
588    for i in 0..num_points {
589        let angle = i as f64 * angle_step;
590        let x = center_x + radius * angle.cos();
591        let y = center_y + radius * angle.sin();
592        points.push(Point2D { x, y });
593    }
594
595    points
596}