Files
OneCAD/crates/geom/src/vec.rs
T

160 lines
4.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign};
use serde::{Deserialize, Serialize};
/// 2D vector / point (sketch space).
#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(from = "[f64; 2]", into = "[f64; 2]")]
pub struct Vec2 {
pub x: f64,
pub y: f64,
}
/// 3D vector / point (model space, Z up).
#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(from = "[f64; 3]", into = "[f64; 3]")]
pub struct Vec3 {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl From<[f64; 2]> for Vec2 {
fn from(a: [f64; 2]) -> Self {
Vec2::new(a[0], a[1])
}
}
impl From<Vec2> for [f64; 2] {
fn from(v: Vec2) -> Self {
[v.x, v.y]
}
}
impl From<[f64; 3]> for Vec3 {
fn from(a: [f64; 3]) -> Self {
Vec3::new(a[0], a[1], a[2])
}
}
impl From<Vec3> for [f64; 3] {
fn from(v: Vec3) -> Self {
[v.x, v.y, v.z]
}
}
impl Vec2 {
pub const ZERO: Vec2 = Vec2 { x: 0.0, y: 0.0 };
pub const X: Vec2 = Vec2 { x: 1.0, y: 0.0 };
pub const Y: Vec2 = Vec2 { x: 0.0, y: 1.0 };
pub const fn new(x: f64, y: f64) -> Self {
Vec2 { x, y }
}
pub fn dot(self, o: Vec2) -> f64 {
self.x * o.x + self.y * o.y
}
/// z component of the 3D cross product.
pub fn cross(self, o: Vec2) -> f64 {
self.x * o.y - self.y * o.x
}
pub fn len(self) -> f64 {
self.x.hypot(self.y)
}
pub fn len2(self) -> f64 {
self.dot(self)
}
pub fn dist(self, o: Vec2) -> f64 {
(self - o).len()
}
pub fn normalized(self) -> Option<Vec2> {
let l = self.len();
(l > crate::EPS && l.is_finite()).then(|| self / l)
}
/// Rotated +90°.
pub fn perp(self) -> Vec2 {
Vec2::new(-self.y, self.x)
}
pub fn angle(self) -> f64 {
self.y.atan2(self.x)
}
pub fn from_angle(a: f64) -> Vec2 {
Vec2::new(a.cos(), a.sin())
}
pub fn lerp(self, o: Vec2, t: f64) -> Vec2 {
self + (o - self) * t
}
pub fn is_finite(self) -> bool {
self.x.is_finite() && self.y.is_finite()
}
pub fn extend(self, z: f64) -> Vec3 {
Vec3::new(self.x, self.y, z)
}
}
impl Vec3 {
pub const ZERO: Vec3 = Vec3 { x: 0.0, y: 0.0, z: 0.0 };
pub const X: Vec3 = Vec3 { x: 1.0, y: 0.0, z: 0.0 };
pub const Y: Vec3 = Vec3 { x: 0.0, y: 1.0, z: 0.0 };
pub const Z: Vec3 = Vec3 { x: 0.0, y: 0.0, z: 1.0 };
pub const fn new(x: f64, y: f64, z: f64) -> Self {
Vec3 { x, y, z }
}
pub fn dot(self, o: Vec3) -> f64 {
self.x * o.x + self.y * o.y + self.z * o.z
}
pub fn cross(self, o: Vec3) -> Vec3 {
Vec3::new(self.y * o.z - self.z * o.y, self.z * o.x - self.x * o.z, self.x * o.y - self.y * o.x)
}
pub fn len(self) -> f64 {
self.len2().sqrt()
}
pub fn len2(self) -> f64 {
self.dot(self)
}
pub fn dist(self, o: Vec3) -> f64 {
(self - o).len()
}
pub fn normalized(self) -> Option<Vec3> {
let l = self.len();
(l > crate::EPS && l.is_finite()).then(|| self / l)
}
pub fn lerp(self, o: Vec3, t: f64) -> Vec3 {
self + (o - self) * t
}
pub fn is_finite(self) -> bool {
self.x.is_finite() && self.y.is_finite() && self.z.is_finite()
}
pub fn min(self, o: Vec3) -> Vec3 {
Vec3::new(self.x.min(o.x), self.y.min(o.y), self.z.min(o.z))
}
pub fn max(self, o: Vec3) -> Vec3 {
Vec3::new(self.x.max(o.x), self.y.max(o.y), self.z.max(o.z))
}
/// Any unit vector perpendicular to `self` (which should be non-zero).
pub fn any_perp(self) -> Vec3 {
let a = if self.x.abs() < 0.9 { Vec3::X } else { Vec3::Y };
self.cross(a).normalized().unwrap_or(Vec3::Z)
}
pub fn to_f32(self) -> [f32; 3] {
[self.x as f32, self.y as f32, self.z as f32]
}
/// Distance from this point to the segment `a`–`b`.
pub fn dist_to_segment(self, a: Vec3, b: Vec3) -> f64 {
let ab = b - a;
let l2 = ab.len2();
let t = if l2 > 0.0 { ((self - a).dot(ab) / l2).clamp(0.0, 1.0) } else { 0.0 };
self.dist(a + ab * t)
}
}
macro_rules! ops {
($t:ident, $($f:ident),+) => {
impl Add for $t { type Output = $t; fn add(self, o: $t) -> $t { $t { $($f: self.$f + o.$f),+ } } }
impl Sub for $t { type Output = $t; fn sub(self, o: $t) -> $t { $t { $($f: self.$f - o.$f),+ } } }
impl Mul<f64> for $t { type Output = $t; fn mul(self, k: f64) -> $t { $t { $($f: self.$f * k),+ } } }
impl Div<f64> for $t { type Output = $t; fn div(self, k: f64) -> $t { $t { $($f: self.$f / k),+ } } }
impl Neg for $t { type Output = $t; fn neg(self) -> $t { $t { $($f: -self.$f),+ } } }
impl AddAssign for $t { fn add_assign(&mut self, o: $t) { $(self.$f += o.$f;)+ } }
impl SubAssign for $t { fn sub_assign(&mut self, o: $t) { $(self.$f -= o.$f;)+ } }
};
}
ops!(Vec2, x, y);
ops!(Vec3, x, y, z);