use std::f64::consts::PI;
use crate::graphical::point_2d::Point2D;
use crate::graphical::rectangle::Rectangle;
#[derive(Debug, PartialEq)]
pub struct Circle {
pub x: f64,
pub y: f64,
pub radius: f64,
}
#[allow(dead_code)]
impl Circle {
pub fn from_points_and_radius(point1: &Point2D, point2: &Point2D, radius: f64) -> Option<Circle> {
let center_x = (point1.x + point2.x) / 2.0;
let center_y = (point1.y + point2.y) / 2.0;
let distance = ((point2.x - point1.x).powi(2) + (point2.y - point1.y).powi(2)).sqrt();
if (radius - distance / 2.0).abs() < std::f64::EPSILON {
Some(Circle {
x: center_x,
y: center_y,
radius,
})
} else {
None
}
}
pub fn from_points(p1: &Point2D, p2: &Point2D, p3: &Point2D) -> Option<Circle> {
let h = (p1.x + p2.x) / 2.0;
let k = (p1.y + p2.y) / 2.0;
let r = ((p1.x - h).powi(2) + (p1.y - k).powi(2)).sqrt();
let distance_to_center_squared = (p3.x - h).powi(2) + (p3.y - k).powi(2);
let epsilon = 1e-6;
if (distance_to_center_squared - r.powi(2)).abs() < epsilon {
Some(Circle { x: h, y: k, radius: r })
} else {
None
}
}
pub fn new(x: f64, y: f64, radius: f64) -> Circle {
Circle { x, y, radius }
}
pub fn area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
pub fn is_point_inside(&self, point_x: f64, point_y: f64) -> bool {
let distance_squared = (point_x - self.x).powi(2) + (point_y - self.y).powi(2);
distance_squared <= self.radius.powi(2)
}
pub fn generate_points(&self, num_points: usize) -> Vec<Point2D> {
generate_points_on_circle(self.x, self.y, self.radius, num_points)
}
pub fn is_point_on_arc(&self, start_angle: f64, end_angle: f64, point: &Point2D) -> bool {
let distance_squared = (point.x - self.x).powi(2) + (point.y - self.y).powi(2);
let distance = distance_squared.sqrt();
distance == self.radius && self.is_angle_in_range(start_angle, end_angle, point)
}
pub fn is_angle_in_range(&self, start_angle: f64, end_angle: f64, point: &Point2D) -> bool {
let angle = (point.y - self.x).atan2(point.x - self.y);
let positive_angle = if angle < 0.0 {
2.0 * std::f64::consts::PI + angle
} else {
angle
};
positive_angle >= start_angle && positive_angle <= end_angle
}
pub fn is_point_on_circle_boundary(&self, point: &Point2D) -> bool {
let distance = distance_between_points(point.x, point.y, self.x, self.y);
distance == self.radius
}
pub fn find_line_intersection(&self, p1: &Point2D, p2: &Point2D) -> Vec<Point2D> {
let dx = p2.x - p1.x;
let dy = p2.y - p1.y;
let a = dx * dx + dy * dy;
let b = 2.0 * (dx * (p1.x - self.x) + dy * (p1.y - self.y));
let c = (p1.x - self.x) * (p1.x - self.x) + (p1.y - self.y) * (p1.y - self.y) - self.radius * self.radius;
let discriminant = b * b - 4.0 * a * c;
if discriminant < 0.0 {
return Vec::new();
}
let t1 = (-b + discriminant.sqrt()) / (2.0 * a);
let t2 = (-b - discriminant.sqrt()) / (2.0 * a);
let mut intersections = Vec::new();
if t1 >= 0.0 && t1 <= 1.0 {
intersections.push(Point2D {
x: p1.x + t1 * dx,
y: p1.y + t1 * dy,
});
}
if t2 >= 0.0 && t2 <= 1.0 {
intersections.push(Point2D {
x: p1.x + t2 * dx,
y: p1.y + t2 * dy,
});
}
intersections
}
pub fn circles_intersect(&self, other: &Circle) -> bool {
let distance_between_centers = ((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt();
let sum_of_radii = self.radius + other.radius;
distance_between_centers < sum_of_radii
}
pub fn circles_touch(&self, other: &Circle) -> bool {
let distance_between_centers = ((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt();
let sum_of_radii = self.radius + other.radius;
(distance_between_centers - sum_of_radii).abs() < f64::EPSILON
}
pub fn circle_contains(&self, other: &Circle) -> bool {
let distance_between_centers = ((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt();
distance_between_centers + other.radius <= self.radius
}
pub fn circle_inside_rectangle(&self, rect: &Rectangle) -> bool {
self.x >= rect.x1 && self.x <= rect.x2 && self.y >= rect.y1 && self.y <= rect.y2
}
pub fn circle_on_rectangle_edge(&self, rect: &Rectangle) -> bool {
(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)
}
pub fn circle_on_rectangle_corner(&self, rect: &Rectangle) -> bool {
(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)
}
pub fn bounding_box(&self) -> Rectangle {
Rectangle {
x1: self.x - self.radius,
y1: self.y - self.radius,
x2: self.x + self.radius,
y2: self.y + self.radius,
}
}
}
pub fn distance_between_points(x1: f64, y1: f64, x2: f64, y2: f64) -> f64 {
((x2 - x1).powi(2) + (y2 - y1).powi(2)).sqrt()
}
pub fn generate_points_on_circle(center_x: f64, center_y: f64, radius: f64, num_points: usize) -> Vec<Point2D> {
let mut points = Vec::with_capacity(num_points);
let angle_step = 2.0 * PI / num_points as f64;
for i in 0..num_points {
let angle = i as f64 * angle_step;
let x = center_x + radius * angle.cos();
let y = center_y + radius * angle.sin();
points.push(Point2D { x, y });
}
points
}