142 lines
3.3 KiB
Rust

use crate::{
m3x3::M3x3,
quats::{Quat, RadianQuat},
v2::V2,
};
use std::ops::{Add, Div, Mul, Neg, Sub};
#[derive(Clone, Copy, Debug)]
pub struct V3(pub f64, pub f64, pub f64);
impl V3 {
pub fn init(v: f64) -> Self {
Self(v, v, v)
}
pub fn map<F: Fn(f64) -> f64>(&self, func: F) -> Self {
Self(func(self.0), func(self.1), func(self.2))
}
pub fn zip<F: Fn(f64, f64) -> f64>(&self, rhs: V3, func: F) -> Self {
Self(
func(self.0, rhs.0),
func(self.1, rhs.1),
func(self.2, rhs.2),
)
}
pub fn iter(&self) -> impl Iterator<Item = f64> {
[self.0, self.1, self.2].into_iter()
}
pub fn reduce<F: Fn(f64, f64) -> f64>(&self, initial: f64, func: F) -> f64 {
let mut acc = initial;
for v in self.iter() {
acc = func(acc, v);
}
acc
}
pub fn cross(&self, rhs: V3) -> Self {
let V3(ax, ay, az) = self;
let V3(bx, by, bz) = rhs;
V3(ay * bz - az * by, az * bx - ax * bz, ax * by - ay * bx)
}
pub fn rotate_by_v3(&self, rot: V3) -> Self {
M3x3::new_rotate_z(rot.2)
* (M3x3::new_rotate_y(rot.1) * (M3x3::new_rotate_x(rot.0) * *self))
}
pub fn rotate_by_quat(&self, rot: RadianQuat) -> Self {
let u = rot.vector_quat();
let theta = rot.rad;
let q_real = theta.cos();
let q_vector = u * theta.sin();
let p = *self;
let r = p
+ (u.cross(p)) * 2.0 * theta.cos() * theta.sin()
+ (u * 2.0 * theta.sin().powi(2)).cross(u.cross(p));
r
}
pub fn rotate_by_m3x3(&self, rot: M3x3) -> Self {
rot * *self
}
/// See https://en.wikipedia.org/wiki/3D_projection#Mathematical_formula
/// for details on the implementation.
pub fn project_2d(&self, camera_pos: V3, camera_rot: M3x3, screen_rel_pos: V3) -> V2 {
let a = *self;
let c = camera_pos;
let d = (a - c).rotate_by_m3x3(camera_rot);
let e = screen_rel_pos - c;
V2(e.2 / d.2 * d.0 + e.0, e.2 / d.2 * d.1 + e.1)
}
pub fn angle(&self, rhs: Self) -> f64 {
(self.dot(rhs) / (self.len() * rhs.len())).acos()
}
pub fn dot(&self, rhs: Self) -> f64 {
self.zip(rhs, |a, b| a * b).reduce(0.0, |acc, v| acc + v)
}
pub fn translate(&self, offset: Self) -> Self {
*self + offset
}
pub fn scale(&self, scale: Self) -> Self {
self.zip(scale, |a, b| a * b)
}
pub fn len(&self) -> f64 {
self.map(|v| v.powi(2)).reduce(0.0, |acc, v| acc + v).sqrt()
}
pub fn unit(&self) -> V3 {
*self / self.len()
}
}
impl Add for V3 {
type Output = V3;
fn add(self, rhs: V3) -> Self::Output {
self.zip(rhs, |a, b| a + b)
}
}
impl Sub for V3 {
type Output = V3;
fn sub(self, rhs: V3) -> Self::Output {
self.zip(rhs, |a, b| a - b)
}
}
impl Mul<f64> for V3 {
type Output = V3;
fn mul(self, rhs: f64) -> Self::Output {
self.map(|v| v * rhs)
}
}
impl Div<f64> for V3 {
type Output = V3;
fn div(self, rhs: f64) -> Self::Output {
self.map(|v| v / rhs)
}
}
impl Neg for V3 {
type Output = V3;
fn neg(self) -> Self::Output {
V3::init(0.0) - self
}
}