use std::f64::{INFINITY, NEG_INFINITY};
use approx::ulps_eq;
use bounding_box::BoundingBox;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{
DEFAULT_EPSILON, DEFAULT_MAX_ULPS, Rotation2, Transformation,
primitive::{Primitive, PrimitiveIntersections},
};
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct Line {
pub a: f64,
pub b: f64,
pub c: f64,
}
impl Line {
pub fn new(a: f64, b: f64, c: f64) -> Self {
return Self { a, b, c };
}
pub fn from_point_angle(pt: [f64; 2], angle: f64) -> Self {
let (sin, cos) = angle.sin_cos();
let a = -sin;
let b = cos;
let c = -(a * pt[0] + b * pt[1]);
Self { a, b, c }
}
pub fn from_two_points(
pt1: [f64; 2],
pt2: [f64; 2],
epsilon: f64,
max_ulps: u32,
) -> Result<Self, crate::error::Error> {
Ok(crate::segment::LineSegment::new(pt1, pt2, epsilon, max_ulps)?.into())
}
pub fn parallel(&self, other: &Self, epsilon: f64, max_ulps: u32) -> bool {
let zn = det(self.a, self.b, other.a, other.b);
return ulps_eq!(zn, 0.0, epsilon = epsilon, max_ulps = max_ulps);
}
pub fn identical(&self, other: &Self, epsilon: f64, max_ulps: u32) -> bool {
return ulps_eq!(
det(self.a, self.b, other.a, other.b),
0.0,
epsilon = epsilon,
max_ulps = max_ulps
) && ulps_eq!(
det(self.a, self.c, other.a, other.c),
0.0,
epsilon = epsilon,
max_ulps = max_ulps
) && ulps_eq!(
det(self.b, self.c, other.b, other.c),
0.0,
epsilon = epsilon,
max_ulps = max_ulps
);
}
}
impl PartialEq for Line {
fn eq(&self, other: &Self) -> bool {
return self.identical(other, DEFAULT_EPSILON, DEFAULT_MAX_ULPS);
}
}
impl From<crate::segment::LineSegment> for Line {
fn from(l: crate::segment::LineSegment) -> Self {
return Line::from(&l);
}
}
impl From<[[f64; 2]; 2]> for Line {
fn from(pts: [[f64; 2]; 2]) -> Self {
let [pt1, pt2] = pts;
let a = pt2[1] - pt1[1];
let b = pt1[0] - pt2[0];
let c = pt2[0] * pt1[1] - pt1[0] * pt2[1];
return Self { a, b, c };
}
}
impl From<&crate::segment::LineSegment> for Line {
fn from(l: &crate::segment::LineSegment) -> Self {
return [l.start(), l.stop()].into();
}
}
impl From<&'_ Line> for BoundingBox {
fn from(value: &'_ Line) -> Self {
if value.a == 0.0 {
let y = value.c / value.b;
return BoundingBox::new(NEG_INFINITY, INFINITY, y, y);
} else if value.b == 0.0 {
let x = value.c / value.a;
return BoundingBox::new(x, x, NEG_INFINITY, INFINITY);
} else {
return BoundingBox::new(NEG_INFINITY, INFINITY, NEG_INFINITY, INFINITY);
}
}
}
fn det(a: f64, b: f64, c: f64, d: f64) -> f64 {
return a * d - b * c;
}
impl Transformation for Line {
fn translate(&mut self, shift: [f64; 2]) {
self.c = self.c - (self.a * shift[0] + self.b * shift[1]);
}
fn rotate(&mut self, center: [f64; 2], angle: f64) {
let t = Rotation2::new(angle);
let ab_r = t * [self.a, self.b];
self.c = self.c + self.a * center[0] + self.b * center[1]
- ab_r[0] * center[0]
- ab_r[1] * center[1];
self.a = ab_r[0];
self.b = ab_r[1];
}
fn scale(&mut self, factor: f64) {
self.a /= factor;
self.b /= factor;
}
fn line_reflection(&mut self, start: [f64; 2], stop: [f64; 2]) -> () {
let [x1, y1] = start;
let [x2, y2] = stop;
let dx = x2 - x1;
let dy = y2 - y1;
let mut mx = dy;
let mut my = -dx;
let len = (mx * mx + my * my).sqrt();
mx /= len;
my /= len;
let (a, b, c) = (self.a, self.b, self.c);
let c0 = c + a * x1 + b * y1;
let dot = a * mx + b * my;
let a2 = a - 2.0 * dot * mx;
let b2 = b - 2.0 * dot * my;
let c2 = c0 - (a2 * x1 + b2 * y1);
self.a = a2;
self.b = b2;
self.c = c2;
}
}
impl crate::primitive::private::Sealed for Line {}
impl Primitive for Line {
fn contains_point(&self, point: [f64; 2], epsilon: f64, max_ulps: u32) -> bool {
return ulps_eq!(
(self.a * point[0] + self.b * point[1] + self.c).abs()
/ (self.a.powi(2) + self.b.powi(2)).sqrt(),
0.0,
epsilon = epsilon,
max_ulps = max_ulps
);
}
fn intersections_line(
&self,
line: &Line,
epsilon: f64,
max_ulps: u32,
) -> PrimitiveIntersections {
fn det(a: f64, b: f64, c: f64, d: f64) -> f64 {
return a * d - b * c;
}
let zn = det(self.a, self.b, line.a, line.b);
if approx::ulps_eq!(zn, 0.0, epsilon = epsilon, max_ulps = max_ulps) {
return PrimitiveIntersections::Zero;
} else {
let x = -det(self.c, self.b, line.c, line.b) / zn;
let y = -det(self.a, self.c, line.a, line.c) / zn;
return PrimitiveIntersections::One([x, y]);
}
}
fn intersections_line_segment(
&self,
line_segment: &crate::segment::LineSegment,
epsilon: f64,
max_ulps: u32,
) -> PrimitiveIntersections {
let other_line = Line::from(line_segment);
match self.intersections_line(&other_line, epsilon, max_ulps) {
PrimitiveIntersections::Zero => PrimitiveIntersections::Zero,
PrimitiveIntersections::One(pt) => {
return line_segment.intersections_point(pt, epsilon, max_ulps);
}
PrimitiveIntersections::Two(_) => {
unreachable!("line-line can have either zero or one intersection")
}
}
}
fn intersections_arc_segment(
&self,
arc_segment: &crate::segment::ArcSegment,
epsilon: f64,
max_ulps: u32,
) -> PrimitiveIntersections {
arc_segment.intersections_line_circle(self.a, self.b, self.c, epsilon, max_ulps)
}
fn intersections_primitive<T: Primitive>(
&self,
other: &T,
epsilon: f64,
max_ulps: u32,
) -> PrimitiveIntersections {
return other.intersections_line(self, epsilon, max_ulps);
}
}