use std::f64::consts::PI;
use std::f64::EPSILON;
use std::fmt::Debug;
use crate::graphical::circle::Circle;
use crate::graphical::point_2d::Point2D;
#[derive(Debug)]
pub struct LinearEquation {
pub a: f64,
pub b: f64,
pub c: f64,
}
#[derive(Debug, PartialEq, Clone)]
pub enum PointLineRelationship {
OnLine,
AboveLine,
BelowLine,
}
#[allow(dead_code)]
impl LinearEquation {
pub fn to_string(&self) -> String {
format!("{}x + {}y + {} = 0", self.a, self.b, self.c)
}
pub fn from_points(x1: f64, y1: f64, x2: f64, y2: f64) -> LinearEquation {
let a = y2 - y1;
let b = x1 - x2;
let c = x2 * y1 - x1 * y2;
LinearEquation {
a,
b,
c,
}
}
pub fn from_point_slope(x1: f64, y1: f64, slope: f64) -> Self {
let a = -slope;
let b = 1.0;
let c = y1 - slope * x1;
LinearEquation { a, b, c }
}
pub fn is_parallel_to(&self, other: &LinearEquation) -> bool {
if let (Some(slope1), Some(slope2)) = (self.slope(), other.slope()) {
slope1 == slope2
} else {
false }
}
pub fn from_slope_intercept(m: f64, b: f64) -> Self {
let a = -m;
let c = -b;
LinearEquation { a, b: 1.0, c }
}
pub fn from_arc(_radius: f64, x1: f64, y1: f64, x2: f64, y2: f64) -> LinearEquation {
let center_x = (x1 + x2) / 2.0;
let center_y = (y1 + y2) / 2.0;
let m = (y2 - y1) / (x2 - x1);
let a = -m;
let b = 1.0;
let c = -a * center_x - b * center_y;
LinearEquation { a: a, b: b, c: c }
}
pub fn translate_along_x(&self, h: f64) -> LinearEquation {
LinearEquation {
a: self.a,
b: self.b,
c: self.c + h,
}
}
pub fn rotate_around_origin(&self, theta: f64) -> LinearEquation {
let cos_theta = theta.cos();
let sin_theta = theta.sin();
let rotation_matrix = [
[cos_theta, -sin_theta],
[sin_theta, cos_theta],
];
let new_a = self.a * rotation_matrix[0][0] + self.b * rotation_matrix[0][1];
let new_b = self.a * rotation_matrix[1][0] + self.b * rotation_matrix[1][1];
let new_c = self.c;
LinearEquation {
a: new_a,
b: new_b,
c: new_c,
}
}
pub fn rotate_around_point(&self, theta: f64, center: (f64, f64)) -> LinearEquation {
let cos_theta = theta.cos();
let sin_theta = theta.sin();
let mut translated_line = self.translate(-center.0, -center.1);
let new_a = self.a * cos_theta - self.b * sin_theta;
let new_b = self.a * sin_theta + self.b * cos_theta;
translated_line.a = new_a;
translated_line.b = new_b;
translated_line.translate(center.0, center.1)
}
pub fn translate(&self, h: f64, k: f64) -> LinearEquation {
LinearEquation {
a: self.a,
b: self.b,
c: self.c + self.a * h + self.b * k,
}
}
pub fn angles_with_axes(&self) -> (f64, f64) {
let slope = -self.a / self.b;
let angle_with_x_axis = slope.atan();
let angle_with_y_axis = PI / 2.0 - angle_with_x_axis;
(angle_with_x_axis, angle_with_y_axis)
}
pub fn point_line_relationship(&self, point: &Point2D) -> PointLineRelationship {
let result = self.a * point.x + self.b * point.y + self.c;
if result == 0.0 {
PointLineRelationship::OnLine
} else if result > 0.0 {
PointLineRelationship::AboveLine
} else {
PointLineRelationship::BelowLine
}
}
pub fn is_tangent_to_circle(&self, circle: &Circle) -> bool {
let distance_to_center = (self.a * circle.x + self.b * circle.y + self.c).abs()
/ f64::sqrt(self.a.powi(2) + self.b.powi(2));
(distance_to_center - circle.radius).abs() < EPSILON
}
pub fn is_vertical_to_x_axis(&self) -> bool {
self.b == 0.0
}
pub fn is_vertical_to_y_axis(&self) -> bool {
self.a == 0.0
}
pub fn are_intersecting(&self, other: &LinearEquation) -> bool {
!(self.is_parallel_to(other) || self.is_equal_to(other))
}
pub fn are_parallel(&self, other: &LinearEquation) -> bool {
self.a * other.b == self.b * other.a
}
pub fn are_perpendicular(&self, other: &LinearEquation) -> bool {
self.a * other.a + self.b * other.b == 0.0
}
pub fn is_equal_to(&self, other: &LinearEquation) -> bool {
self.a == other.a && self.b == other.b && self.c == other.c
}
pub fn slope(&self) -> Option<f64> {
if self.is_vertical_to_x_axis() {
None } else {
Some(-self.a / self.b)
}
}
pub fn to_slope_intercept_form(&self) -> Option<(f64, f64)> {
if self.b != 0.0 {
let slope = -self.a / self.b;
let intercept = -self.c / self.b;
Some((slope, intercept))
} else {
None }
}
pub fn to_point_slope_form(&self) -> Option<(f64, (f64, f64))> {
if self.b != 0.0 {
let slope = -self.a / self.b;
let point = (0.0, -self.c / self.b);
Some((slope, point))
} else {
None }
}
}