use crate::graphical::circle::Circle;
use crate::graphical::point_2d::Point2D;
#[derive(Debug, PartialEq)]
pub struct Triangle {
pub vertex_a: Point2D,
pub vertex_b: Point2D,
pub vertex_c: Point2D,
}
#[derive(Debug, PartialEq)]
pub enum TriangleType {
Acute,
Right,
Obtuse,
}
#[allow(dead_code)]
impl Triangle {
pub fn new(vertex_a: Point2D, vertex_b: Point2D, vertex_c: Point2D) -> Self {
Triangle {
vertex_a,
vertex_b,
vertex_c,
}
}
pub fn side_length(&self, start: &Point2D, end: &Point2D) -> f64 {
((end.x - start.x).powi(2) + (end.y - start.y).powi(2)).sqrt()
}
pub fn perimeter(&self) -> f64 {
let side_ab = self.side_length(&self.vertex_a, &self.vertex_b);
let side_bc = self.side_length(&self.vertex_b, &self.vertex_c);
let side_ca = self.side_length(&self.vertex_c, &self.vertex_a);
side_ab + side_bc + side_ca
}
pub fn area(&self) -> f64 {
let side_ab = self.side_length(&self.vertex_a, &self.vertex_b);
let side_bc = self.side_length(&self.vertex_b, &self.vertex_c);
let side_ca = self.side_length(&self.vertex_c, &self.vertex_a);
let s = self.perimeter() / 2.0; (s * (s - side_ab) * (s - side_bc) * (s - side_ca)).sqrt()
}
pub fn semi_perimeter(&self) -> f64 {
let side_ab = self.side_length(&self.vertex_a, &self.vertex_b);
let side_bc = self.side_length(&self.vertex_b, &self.vertex_c);
let side_ca = self.side_length(&self.vertex_c, &self.vertex_a);
(side_ab + side_bc + side_ca) / 2.0
}
pub fn in_circle(&self) -> Circle {
let s = self.semi_perimeter();
let side_ab = self.side_length(&self.vertex_a, &self.vertex_b);
let side_bc = self.side_length(&self.vertex_b, &self.vertex_c);
let side_ca = self.side_length(&self.vertex_c, &self.vertex_a);
let radius = self.area() / s;
let center_x = (side_bc * self.vertex_a.x + side_ca * self.vertex_b.x + side_ab * self.vertex_c.x) / (side_ab + side_bc + side_ca);
let center_y = (side_bc * self.vertex_a.y + side_ca * self.vertex_b.y + side_ab * self.vertex_c.y) / (side_ab + side_bc + side_ca);
Circle {
x: center_x,
y: center_y,
radius,
}
}
pub fn rotate_around_point(&self, center: &Point2D, angle_degrees: f64) -> Triangle {
let angle_radians = angle_degrees.to_radians();
let rotation_matrix = [
[angle_radians.cos(), -angle_radians.sin()],
[angle_radians.sin(), angle_radians.cos()],
];
let new_vertex_a = rotate_point(&self.vertex_a, center, rotation_matrix);
let new_vertex_b = rotate_point(&self.vertex_b, center, rotation_matrix);
let new_vertex_c = rotate_point(&self.vertex_c, center, rotation_matrix);
Triangle {
vertex_a: new_vertex_a,
vertex_b: new_vertex_b,
vertex_c: new_vertex_c,
}
}
pub fn point_inside_triangle(&self, p: &Point2D) -> bool {
let barycentric_coords = self.barycentric_coordinates(p);
barycentric_coords.iter().all(|&coord| coord >= 0.0 && coord <= 1.0)
}
pub fn barycentric_coordinates(&self, p: &Point2D) -> [f64; 3] {
let area_triangle = self.area();
let area_sub_triangle_a = Triangle::new(p.clone(), self.vertex_b.clone(), self.vertex_c.clone()).area();
let area_sub_triangle_b = Triangle::new(self.vertex_a.clone(), p.clone(), self.vertex_c.clone()).area();
let area_sub_triangle_c = Triangle::new(self.vertex_a.clone(), self.vertex_b.clone(), p.clone()).area();
let p1 = area_sub_triangle_a / area_triangle;
let p2 = area_sub_triangle_b / area_triangle;
let p3 = area_sub_triangle_c / area_triangle;
[p1, p2, p3]
}
pub fn orthocenter(&self) -> Point2D {
let x_h = self.vertex_a.x;
let y_h = self.vertex_b.y;
Point2D { x: x_h, y: y_h }
}
pub fn centroid(&self) -> Point2D {
let x_g = (self.vertex_a.x + self.vertex_b.x + self.vertex_c.x) / 3.0;
let y_g = (self.vertex_a.y + self.vertex_b.y + self.vertex_c.y) / 3.0;
Point2D { x: x_g, y: y_g }
}
pub fn incenter(&self) -> Point2D {
let a = self.vertex_b.distance_to(&self.vertex_c);
let b = self.vertex_c.distance_to(&self.vertex_a);
let c = self.vertex_a.distance_to(&self.vertex_b);
let x_i = (a * self.vertex_a.x + b * self.vertex_b.x + c * self.vertex_c.x) / (a + b + c);
let y_i = (a * self.vertex_a.y + b * self.vertex_b.y + c * self.vertex_c.y) / (a + b + c);
Point2D { x: x_i, y: y_i }
}
pub fn circumcenter(&self) -> Point2D {
let m_ab = Point2D {
x: (self.vertex_a.x + self.vertex_b.x) / 2.0,
y: (self.vertex_a.y + self.vertex_b.y) / 2.0,
};
let m_bc = Point2D {
x: (self.vertex_b.x + self.vertex_c.x) / 2.0,
y: (self.vertex_b.y + self.vertex_c.y) / 2.0,
};
let m_ca = Point2D {
x: (self.vertex_c.x + self.vertex_a.x) / 2.0,
y: (self.vertex_c.y + self.vertex_a.y) / 2.0,
};
let m_ab_slope = -(self.vertex_b.x - self.vertex_a.x) / (self.vertex_b.y - self.vertex_a.y);
let m_bc_slope = -(self.vertex_c.x - self.vertex_b.x) / (self.vertex_c.y - self.vertex_b.y);
let m_ca_slope = -(self.vertex_a.x - self.vertex_c.x) / (self.vertex_a.y - self.vertex_c.y);
let x_o = (m_ab.y - m_ab_slope * m_ab.x + m_bc.y - m_bc_slope * m_bc.x + m_ca.y - m_ca_slope * m_ca.x)
/ (m_ab_slope + m_bc_slope + m_ca_slope);
let y_o = m_ab_slope * (x_o - m_ab.x) + m_ab.y;
Point2D { x: x_o, y: y_o }
}
pub fn classify_triangle(&self) -> TriangleType {
let (angle_a, angle_b, angle_c) = self.classify_angles();
if angle_a < 90.0 && angle_b < 90.0 && angle_c < 90.0 {
TriangleType::Acute
} else if angle_a == 90.0 || angle_b == 90.0 || angle_c == 90.0 {
TriangleType::Right
} else {
TriangleType::Obtuse
}
}
pub fn classify_angles(&self) -> (f64, f64, f64) {
let angle_a = self.angle_between(&self.vertex_b, &self.vertex_c, &self.vertex_a);
let angle_b = self.angle_between(&self.vertex_a, &self.vertex_c, &self.vertex_b);
let angle_c = self.angle_between(&self.vertex_a, &self.vertex_b, &self.vertex_c);
(angle_a, angle_b, angle_c)
}
fn angle_between(&self, p1: &Point2D, p2: &Point2D, p3: &Point2D) -> f64 {
let a = p1.distance_to(p2);
let b = p2.distance_to(p3);
let c = p3.distance_to(p1);
let cos_angle = (a.powi(2) + b.powi(2) - c.powi(2)) / (2.0 * a * b);
let angle_rad = cos_angle.acos();
let angle_deg = angle_rad.to_degrees();
angle_deg
}
}
fn rotate_point( point: &Point2D, center: &Point2D, matrix: [[f64; 2]; 2]) -> Point2D {
let translated_x = point.x - center.x;
let translated_y = point.y - center.y;
let rotated_x = matrix[0][0] * translated_x + matrix[0][1] * translated_y;
let rotated_y = matrix[1][0] * translated_x + matrix[1][1] * translated_y;
Point2D {
x: rotated_x + center.x,
y: rotated_y + center.y,
}
}