use crate::{CoreError, StructureHandle};
use molgfx_math::Vec3;
#[cfg(test)]
#[path = "planar_tests.rs"]
mod tests;
const EPSILON: f32 = 1.0e-6;
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct PlanarRegion {
pub owner: StructureHandle,
pub center: Vec3,
pub normal: Vec3,
pub tangent: Vec3,
pub bitangent: Vec3,
pub size: [f32; 2],
}
impl PlanarRegion {
pub fn new(
owner: StructureHandle,
center: Vec3,
normal: Vec3,
tangent: Vec3,
size: [f32; 2],
) -> Result<Self, CoreError> {
if !center.is_finite()
|| !normal.is_finite()
|| !tangent.is_finite()
|| !size
.iter()
.all(|value| value.is_finite() && *value > EPSILON)
|| normal.length_squared() <= EPSILON
{
return Err(invalid(
"planar region pose and size must be finite and non-degenerate",
));
}
let normal = normal.normalize();
let tangent = tangent - normal * normal.dot(tangent);
if tangent.length_squared() <= EPSILON {
return Err(invalid(
"planar region tangent must not be parallel to its normal",
));
}
let tangent = tangent.normalize();
Ok(Self {
owner,
center,
normal,
tangent,
bitangent: normal.cross(tangent).normalize(),
size,
})
}
#[must_use]
pub fn corners(self) -> [Vec3; 4] {
let half_tangent = self.tangent * (self.size[0] * 0.5);
let half_bitangent = self.bitangent * (self.size[1] * 0.5);
[
self.center - half_tangent - half_bitangent,
self.center + half_tangent - half_bitangent,
self.center + half_tangent + half_bitangent,
self.center - half_tangent + half_bitangent,
]
}
#[must_use]
pub fn edges(self) -> [(Vec3, Vec3); 4] {
let corners = self.corners();
[
(corners[0], corners[1]),
(corners[1], corners[2]),
(corners[2], corners[3]),
(corners[3], corners[0]),
]
}
}
const fn invalid(reason: &'static str) -> CoreError {
CoreError::InvalidPrimitive { reason }
}