use std::fmt::{Display, Error, Formatter};
use crate::core::Axis;
use super::{
Circle, Intersect, Line1, Orientation, PointIntersection, Rect, Shape, Triangle, Vec2,
};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Line2 {
pub a: Vec2,
pub b: Vec2,
}
impl Line2 {
pub fn new(a: Vec2, b: Vec2) -> Self {
Self { a, b }
}
pub fn reverse(&self) -> Self {
Self {
a: self.b,
b: self.a,
}
}
pub fn intersects_rect(&self, other: &Rect) -> bool {
let l1 = Line2 {
a: Vec2::new(other.min.x, other.min.y),
b: Vec2::new(other.max.x, other.min.y),
};
let l2 = Line2 {
a: Vec2::new(other.min.x, other.max.y),
b: Vec2::new(other.max.x, other.max.y),
};
let l3 = Line2 {
a: Vec2::new(other.min.x, other.min.y),
b: Vec2::new(other.min.x, other.max.y),
};
let l4 = Line2 {
a: Vec2::new(other.max.x, other.min.y),
b: Vec2::new(other.max.x, other.max.y),
};
self.intersects(&l1) || self.intersects(&l2) || self.intersects(&l3) || self.intersects(&l4)
}
pub fn length(&self) -> f32 {
(self.a - self.b).length()
}
pub fn length_y(&self) -> f32 {
(self.a.y - self.b.y).abs()
}
pub fn length_x(&self) -> f32 {
(self.a.x - self.b.x).abs()
}
pub fn axis_aligned(&self) -> Option<Axis> {
if self.length_x() == 0.0 {
return Some(Axis::Vertical);
}
if self.length_y() == 0.0 {
return Some(Axis::Horizontal);
}
return None;
}
pub fn subtract(self, other: Self) -> Vec<Self> {
if self == other || self == other.reverse() {
return Vec::new();
}
let my_aa = self.axis_aligned();
let other_aa = other.axis_aligned();
if my_aa.is_some() && other_aa.is_some() {
let my_axis = my_aa.unwrap();
let other_axis = other_aa.unwrap();
if my_axis == other_axis {
match my_axis {
Axis::Vertical => {
if self.a.x == other.a.x {
let my_line1 = Line1::from_line2(self.clone(), Axis::Vertical);
let other_line1 = Line1::from_line2(other.clone(), Axis::Vertical);
let subs = my_line1.subtract(other_line1);
return subs
.iter()
.map(|l| l.into_line2(Axis::Vertical, self.a.x))
.collect();
}
}
Axis::Horizontal => {
if self.a.y == other.a.y {
let my_line1 = Line1::from_line2(self.clone(), Axis::Horizontal);
let other_line1 = Line1::from_line2(other.clone(), Axis::Horizontal);
let subs = my_line1.subtract(other_line1);
return subs
.iter()
.map(|l| l.into_line2(Axis::Horizontal, self.a.y))
.collect();
}
}
_ => panic!("What in blazes are these lines up to then eh?"),
}
}
} else {
}
return vec![self];
}
pub fn subtract_collection(lines: Vec<Self>, other: Self) -> Vec<Self> {
let mut result = Vec::new();
for line in lines {
result.extend(line.subtract(other));
}
return result;
}
}
impl Shape for Line2 {
fn bounds(&self) -> Rect {
Rect::new(self.a, self.b).validate()
}
fn x_range(&self) -> Line1 {
Line1::from_line2(self.clone(), Axis::Horizontal)
}
fn y_range(&self) -> Line1 {
Line1::from_line2(self.clone(), Axis::Vertical)
}
fn contains_point(&self, point: Vec2) -> bool {
let dist_a = self.a.distance(point);
let dist_b = self.b.distance(point);
return dist_a + dist_b == self.length();
}
fn center(&self) -> Vec2 {
(self.a + self.b) / 2.0
}
}
impl Intersect<Self, Vec2> for Line2 {
fn intersects(&self, other: &Self) -> bool {
let o1 = Triangle::new(self.a, self.b, other.a).orientation();
let o2 = Triangle::new(self.a, self.b, other.b).orientation();
let o3 = Triangle::new(other.a, other.b, self.a).orientation();
let o4 = Triangle::new(other.a, other.b, self.b).orientation();
if o1 != o2 && o3 != o4 {
return true;
}
if o1 == Orientation::Linear && on_segment(self.a, other.a, self.b) {
return true;
}
if o2 == Orientation::Linear && on_segment(self.a, other.b, self.b) {
return true;
}
if o3 == Orientation::Linear && on_segment(other.a, self.a, other.b) {
return true;
}
if o4 == Orientation::Linear && on_segment(other.a, self.b, other.b) {
return true;
}
return false; }
fn intersection(&self, other: &Self) -> Option<Vec2> {
let a = self.a;
let c = other.a;
let r = self.b - a;
let s = other.b - c;
let denom = cross(r, s);
if denom == 0.0 {
return None;
}
let numer_a = cross(c - a, s);
let numer_c = cross(c - a, r);
let t = numer_a / denom;
let u = numer_c / denom;
if t < 0.0 || t > 1.0 || u < 0.0 || u > 1.0 {
return None;
}
return Some(a + r * t);
}
}
impl Intersect<Rect, PointIntersection> for Line2 {
fn intersects(&self, other: &Rect) -> bool {
for line in other.edges() {
if self.intersects(&line) {
return true;
}
}
false
}
fn intersection(&self, other: &Rect) -> Option<PointIntersection> {
let mut result = None;
for line in other.edges() {
if let Some(intersection) = self.intersection(&line) {
if result.is_none() {
result = Some(PointIntersection::One(intersection));
} else {
result = Some(result.unwrap().add(intersection));
}
}
}
return result;
}
}
impl Intersect<Circle, PointIntersection> for Line2 {
fn intersects(&self, circle: &Circle) -> bool {
if circle.contains_point(self.a) || circle.contains_point(self.b) {
return true;
}
let dot = (((circle.center.x - self.a.x) * (self.b.x - self.a.x))
+ ((circle.center.y - self.a.y) * (self.b.y - self.a.y)))
/ self.length().powi(2);
let closest_x = self.a.x + (dot * (self.b.x - self.a.x));
let closest_y = self.a.y + (dot * (self.b.y - self.a.y));
let closest = Vec2::new(closest_x, closest_y);
return self.contains_point(closest) && circle.contains_point(closest)
}
fn intersection(&self, _other: &Circle) -> Option<PointIntersection> {
todo!();
}
}
impl Display for Line2 {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "Line2({}, {})", self.a, self.b)
}
}
fn on_segment(p: Vec2, q: Vec2, r: Vec2) -> bool {
let x_max = if p.x > r.x { p.x } else { r.x };
let y_max = if p.y > r.y { p.y } else { r.y };
return q.x <= x_max && q.x >= x_max && q.y <= y_max && q.y >= y_max;
}
fn cross(a: Vec2, b: Vec2) -> super::Float {
a.x * b.y - a.y * b.x
}