use nalgebra::{Point3, Vector3};
use crate::{Classification, Plane3D, PlaneSide};
#[derive(Debug, Clone, PartialEq)]
pub struct Rectangle {
origin: Point3<f32>,
u: Vector3<f32>,
v: Vector3<f32>,
}
impl Rectangle {
pub fn new(origin: Point3<f32>, u: Vector3<f32>, v: Vector3<f32>) -> Self {
Self { origin, u, v }
}
pub fn from_corners(
a: Point3<f32>,
b: Point3<f32>,
c: Point3<f32>,
d: Point3<f32>,
) -> Self {
debug_assert!(
{
let plane = Plane3D::from_three_points(a, b, d);
plane.classify_point(c) == PlaneSide::OnPlane
},
"Rectangle corners must be coplanar"
);
let u = b - a;
let v = d - a;
Self { origin: a, u, v }
}
#[inline]
pub fn origin(&self) -> Point3<f32> {
self.origin
}
#[inline]
pub fn u(&self) -> Vector3<f32> {
self.u
}
#[inline]
pub fn v(&self) -> Vector3<f32> {
self.v
}
pub fn vertices(&self) -> [Point3<f32>; 4] {
[
self.origin,
self.origin + self.u,
self.origin + self.u + self.v,
self.origin + self.v,
]
}
pub fn normal(&self) -> Vector3<f32> {
self.u.cross(&self.v)
}
pub fn unit_normal(&self) -> Option<Vector3<f32>> {
let n = self.normal();
let len = n.norm();
if len > f32::EPSILON {
Some(n / len)
} else {
None
}
}
pub fn plane(&self) -> Plane3D {
Plane3D::from_point_and_normal(self.origin, self.normal())
}
pub fn centroid(&self) -> Point3<f32> {
self.origin + (self.u + self.v) * 0.5
}
pub fn area(&self) -> f32 {
self.normal().norm()
}
pub fn classify(&self, plane: &Plane3D) -> Classification {
let mut front = 0;
let mut back = 0;
let mut on_plane = 0;
for vertex in self.vertices() {
match plane.classify_point(vertex) {
PlaneSide::Front => front += 1,
PlaneSide::Back => back += 1,
PlaneSide::OnPlane => on_plane += 1,
}
}
if on_plane == 4 {
Classification::Coplanar
} else if back == 0 {
Classification::Front
} else if front == 0 {
Classification::Back
} else {
Classification::Spanning
}
}
}
impl From<Rectangle> for Plane3D {
fn from(rectangle: Rectangle) -> Self {
rectangle.plane()
}
}
impl From<&Rectangle> for Plane3D {
fn from(rectangle: &Rectangle) -> Self {
rectangle.plane()
}
}