use super::*;
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub struct Line2<T> {
pub start: Point2<T>,
pub end: Point2<T>,
}
#[allow(non_snake_case)]
#[inline]
pub const fn Line2<T>(start: Point2<T>, end: Point2<T>) -> Line2<T> {
Line2 { start, end }
}
specialized_type!(Line2, Line2f, f32, start: Point2f, end: Point2f);
specialized_type!(Line2, Line2d, f64, start: Point2d, end: Point2d);
specialized_type!(Line2, Line2i, i32, start: Point2i, end: Point2i);
#[cfg(feature = "dataview")]
unsafe impl<T: dataview::Pod> dataview::Pod for Line2<T> {}
impl<T> Line2<T> {
#[inline]
pub const fn new(start: Point2<T>, end: Point2<T>) -> Line2<T> {
Line2 { start, end }
}
#[inline]
pub const fn pinch(self, pt: Point2<T>) -> (Line2<T>, Line2<T>) where T: Copy {
let Line2 { start, end } = self;
(Line2::new(start, pt), Line2::new(pt, end))
}
#[inline]
pub fn delta(self) -> Vec2<T> where T: ops::Sub<Output = T> {
self.end - self.start
}
}
impl<T: Scalar> Line2<T> {
#[inline]
pub fn bounds(&self) -> Bounds2<T> {
let (mins, maxs) = self.start.min_max(self.end);
Bounds2 { mins, maxs }
}
}
impl<T: Float> Line2<T> {
#[inline]
pub fn project(self, pt: Point2<T>) -> Point2<T> {
self.start + (pt - self.start).project_sat(self.end - self.start)
}
#[inline]
pub fn distance(self, pt: Point2<T>) -> T {
self.project(pt).distance(pt)
}
#[inline]
pub fn segment_x(self, rhs: Line2<T>) -> Option<T> {
let r = self.end - self.start;
let s = rhs.end - rhs.start;
let denom = r.cross(s);
if denom == T::ZERO {
return None;
}
let u = (rhs.start - self.start).cross(r) / denom;
Some(u)
}
#[inline]
pub fn intersect(self, rhs: Line2<T>) -> Option<Point2<T>> {
let denom = self.delta().cross(rhs.delta());
if denom == T::ZERO {
return None;
}
let p = rhs.delta() * self.start.cross(self.start + self.delta()) - self.delta() * rhs.start.cross(rhs.start + rhs.delta());
Some(p / denom)
}
#[inline]
pub fn lerp(self, target: Line2<T>, t: T) -> Line2<T> {
Line2 {
start: self.start.lerp(target.start, t),
end: self.end.lerp(target.end, t),
}
}
}
#[cfg(feature = "urandom")]
impl<T> urandom::Distribution<Line2<T>> for urandom::distr::StandardUniform where
urandom::distr::StandardUniform: urandom::Distribution<Point2<T>>,
{
#[inline]
fn sample<R: urandom::Rng + ?Sized>(&self, rand: &mut urandom::Random<R>) -> Line2<T> {
let distr = urandom::distr::StandardUniform;
let start = distr.sample(rand);
let end = distr.sample(rand);
Line2 { start, end }
}
}
#[cfg(feature = "urandom")]
impl<T: urandom::distr::SampleUniform> urandom::distr::SampleUniform for Line2<T> {
type Sampler = Line2<urandom::distr::Uniform<T>>;
}
#[cfg(feature = "urandom")]
impl<T: urandom::distr::SampleUniform> urandom::distr::UniformSampler<Line2<T>> for Line2<urandom::distr::Uniform<T>> where Point2<T>: urandom::distr::SampleUniform {
#[inline]
fn try_new(low: Line2<T>, high: Line2<T>) -> Result<Self, urandom::distr::UniformError> {
let start = Vec2::try_new(low.start, high.start)?;
let end = Vec2::try_new(low.end, high.end)?;
Ok(Line2 { start, end })
}
#[inline]
fn try_new_inclusive(low: Line2<T>, high: Line2<T>) -> Result<Self, urandom::distr::UniformError> where Self: Sized {
let start = Vec2::try_new_inclusive(low.start, high.start)?;
let end = Vec2::try_new_inclusive(low.end, high.end)?;
Ok(Line2 { start, end })
}
}
#[cfg(feature = "urandom")]
impl<T: urandom::distr::SampleUniform> urandom::Distribution<Line2<T>> for Line2<urandom::distr::Uniform<T>> {
#[inline]
fn sample<R: urandom::Rng + ?Sized>(&self, rand: &mut urandom::Random<R>) -> Line2<T> {
let start = self.start.sample(rand);
let end = self.end.sample(rand);
Line2 { start, end }
}
}
impl<T: Float> Trace2<T> for Line2<T> {
#[inline]
fn inside(&self, _pt: Point2<T>) -> bool {
false
}
fn trace(&self, ray: &Ray2<T>) -> Option<Hit2<T>> {
let delta = self.end - self.start;
let denom = ray.direction.cross(delta);
if denom == T::ZERO {
return None;
}
let qp = self.start - ray.origin;
let distance = qp.cross(delta) / denom;
let u = qp.cross(ray.direction) / denom;
if !(distance > ray.distance.min && distance <= ray.distance.max && u >= T::ZERO && u <= T::ONE) {
return None;
}
let point = ray.at(distance);
let normal = if denom < T::ZERO { delta.ccw() } else { delta.cw() }.norm();
let side = HitSide::Entry;
Some(Hit2 { point, distance, normal, index: 0, side })
}
}