use super::*;
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub struct Triangle2<T> {
pub p: Point2<T>,
pub u: Vec2<T>,
pub v: Vec2<T>,
}
#[allow(non_snake_case)]
#[inline]
pub const fn Triangle2<T>(p: Point2<T>, u: Vec2<T>, v: Vec2<T>) -> Triangle2<T> {
Triangle2 { p, u, v }
}
impl<T> Triangle2<T> {
#[inline]
pub const fn new(p: Point2<T>, u: Vec2<T>, v: Vec2<T>) -> Triangle2<T> {
Triangle2 { p, u, v }
}
}
impl<T: Copy> Triangle2<T> {
#[inline]
pub fn points(p: Point2<T>, p2: Point2<T>, p3: Point2<T>) -> Triangle2<T> where T: Scalar {
let u = p2 - p;
let v = p3 - p;
Triangle2 { p, u, v }.norm()
}
#[inline]
pub fn norm(self) -> Triangle2<T> where T: Scalar {
if self.u.cross(self.v) >= T::ZERO { self } else { -self }
}
#[inline]
pub fn p1(&self) -> Point2<T> {
self.p
}
#[inline]
pub fn p2(&self) -> Point2<T> where T: ops::Add<T, Output = T> {
self.p + self.u
}
#[inline]
pub fn p3(&self) -> Point2<T> where T: ops::Add<T, Output = T> {
self.p + self.v
}
#[inline]
pub fn centroid(&self) -> Point2<T> where T: Scalar {
let p1 = self.p;
let p2 = self.p + self.u;
let p3 = self.p + self.v;
let three = T::ONE + T::ONE + T::ONE;
(p1 + p2 + p3) / three
}
#[inline]
pub fn area(&self) -> T where T: Scalar {
self.u.cross(self.v) / (T::ONE + T::ONE)
}
}
impl<T> ops::Neg for Triangle2<T> {
type Output = Triangle2<T>;
#[inline]
fn neg(self) -> Triangle2<T> {
Triangle2 { p: self.p, u: self.v, v: self.u }
}
}
impl<T: Scalar> Triangle2<T> {
#[inline]
pub fn bounds(&self) -> Bounds2<T> {
let mins = self.p.min(self.p + self.u).min(self.p + self.v);
let maxs = self.p.max(self.p + self.u).max(self.p + self.v);
Bounds2 { mins, maxs }
}
}
impl<T: Float> Triangle2<T> {
#[inline]
pub fn decompose(&self, q: Point2<T>) -> Vec2<T> {
let w = q - self.p;
let area_inv = T::ONE / (self.u.cross(self.v));
let x = w.cross(self.v) * area_inv;
let y = self.u.cross(w) * area_inv;
Vec2(x, y)
}
#[inline]
pub fn barycentric(&self, q: Point2<T>) -> Vec3<T> {
let Vec2 { x: beta, y: gamma } = self.decompose(q);
let alpha = T::ONE - beta - gamma;
Vec3(alpha, beta, gamma)
}
}
#[cfg(feature = "urandom")]
impl<T> urandom::Distribution<Triangle2<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>) -> Triangle2<T> {
let distr = urandom::distr::StandardUniform;
let p = distr.sample(rand);
let u = distr.sample(rand);
let v = distr.sample(rand);
Triangle2 { p, u, v }
}
}
#[cfg(feature = "urandom")]
impl<T: urandom::distr::SampleUniform> urandom::distr::SampleUniform for Triangle2<T> {
type Sampler = Triangle2<urandom::distr::Uniform<T>>;
}
#[cfg(feature = "urandom")]
impl<T: urandom::distr::SampleUniform> urandom::distr::UniformSampler<Triangle2<T>> for Triangle2<urandom::distr::Uniform<T>> where Point2<T>: urandom::distr::SampleUniform {
#[inline]
fn try_new(low: Triangle2<T>, high: Triangle2<T>) -> Result<Self, urandom::distr::UniformError> {
let p = Vec2::try_new(low.p, high.p)?;
let u = Vec2::try_new(low.u, high.u)?;
let v = Vec2::try_new(low.v, high.v)?;
Ok(Triangle2 { p, u, v })
}
#[inline]
fn try_new_inclusive(low: Triangle2<T>, high: Triangle2<T>) -> Result<Self, urandom::distr::UniformError> where Self: Sized {
let p = Vec2::try_new_inclusive(low.p, high.p)?;
let u = Vec2::try_new_inclusive(low.u, high.u)?;
let v = Vec2::try_new_inclusive(low.v, high.v)?;
Ok(Triangle2 { p, u, v })
}
}
#[cfg(feature = "urandom")]
impl<T: urandom::distr::SampleUniform> urandom::Distribution<Triangle2<T>> for Triangle2<urandom::distr::Uniform<T>> {
#[inline]
fn sample<R: urandom::Rng + ?Sized>(&self, rand: &mut urandom::Random<R>) -> Triangle2<T> {
let p = self.p.sample(rand);
let u = self.u.sample(rand);
let v = self.v.sample(rand);
Triangle2 { p, u, v }
}
}
impl<T: Float> Trace2<T> for Triangle2<T> {
fn inside(&self, pt: Point2<T>) -> bool {
let d = pt - self.p;
let a = self.u.cross(d);
let b = (self.v - self.u).cross(d - self.u);
let c = (-self.v).cross(d - self.v);
a >= T::ZERO && b >= T::ZERO && c >= T::ZERO
}
fn trace(&self, ray: &Ray2<T>) -> Option<Hit2<T>> {
let edges = [
(self.p, self.u),
(self.p + self.u, self.v - self.u),
(self.p + self.v, -self.v),
];
let mut min_hit: Option<Hit2<T>> = None;
for &(q, edge) in edges.as_slice() {
let p = ray.origin;
let d = ray.direction;
let denom = d.cross(edge);
if denom == T::ZERO {
continue; }
let pq = q - p;
let distance = pq.cross(edge) / denom;
let s = pq.cross(d) / denom;
if !(s >= T::ZERO && s <= T::ONE && distance > ray.distance.min && distance <= ray.distance.max) {
continue; }
if min_hit.is_none() || distance < min_hit.as_ref().unwrap().distance {
let point = ray.at(distance);
let normal = edge.ccw().norm();
let (normal, side) = if denom < T::ZERO { (normal, HitSide::Entry) } else { (-normal, HitSide::Exit) };
min_hit = Some(Hit2 { point, distance, normal, index: 0, side });
}
}
min_hit
}
}