use std::ops;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Vec2D {
pub x: f64,
pub y: f64,
}
impl Vec2D {
#[must_use]
pub fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
#[must_use]
pub fn x_unit() -> Self {
Self { x: 1.0, y: 0.0 }
}
#[must_use]
pub fn y_unit() -> Self {
Self { x: 0.0, y: 1.0 }
}
#[must_use]
pub fn norm(&self) -> f64 {
self.norm2().powf(0.5)
}
#[must_use]
pub fn norm2(&self) -> f64 {
self.x.powf(2.0) + self.y.powf(2.0)
}
#[must_use]
pub fn dot(&self, other: &Self) -> f64 {
self.x * other.x + self.y * other.y
}
#[must_use]
pub fn linspace(a: &Self, b: &Self, npoints: u32) -> Vec<Self> {
match npoints {
0 => vec![],
1 => vec![a.clone()],
_ => (0..npoints)
.map(|u| f64::from(u) / (f64::from(npoints) - 1.0))
.map(|t| (1.0 - t) * a + t * b)
.collect(),
}
}
#[must_use]
pub fn linspread(a: &Self, b: &Self, npoints: u32) -> Vec<Self> {
(0..npoints)
.map(|u| (f64::from(u) / f64::from(npoints)) + 1.0 / (2.0 * f64::from(npoints)))
.map(|t| (1.0 - t) * a + t * b)
.collect()
}
#[must_use]
pub fn ortho(&self) -> Self {
Self {
x: -self.y,
y: self.x,
}
}
}
impl_op!(-|a: Vec2D| -> Vec2D { Vec2D::new(-a.x, -a.y) });
impl_op_ex_commutative!(/ |a:&Vec2D,b:&f64| -> Vec2D {
Vec2D {
x: a.x / b,
y: a.y / b,
}
});
impl_op_ex_commutative!(*|a: &Vec2D, b: &f64| -> Vec2D {
Vec2D {
x: a.x * b,
y: a.y * b,
}
});
impl_op_ex!(+ |a:&Vec2D,b:&Vec2D| -> Vec2D {
Vec2D {
x: a.x + b.x,
y: a.y + b.y,
}
});
impl_op_ex!(-|a: &Vec2D, b: &Vec2D| -> Vec2D {
Vec2D {
x: a.x - b.x,
y: a.y - b.y,
}
});
impl_op_ex_commutative!(+
|a:&Vec3D,b:&Vec2D| -> Vec3D
{
Vec3D {x:a.x+b.x,y:a.y+b.y,z:a.z}
}
);
impl_op_ex_commutative!(+
|a:&Line,b:&Vec2D| -> Line
{
Line::new(a.x0+b.x, a.xz, a.y0+b.y, a.yz)
}
);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Vec3D {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl Vec3D {
#[must_use]
pub fn new(x: f64, y: f64, z: f64) -> Self {
Self { x, y, z }
}
#[must_use]
pub fn origin() -> Self {
Self {
x: 0.0,
y: 0.0,
z: 0.0,
}
}
#[must_use]
pub fn distance_at_altitude(&self, line: &Line) -> f64 {
self.displacement_at_altitude(line).norm()
}
#[must_use]
pub fn displacement_at_altitude(&self, line: &Line) -> Vec2D {
let line_intersection = line.position_at_altitude(self.z);
Vec2D {
x: (line_intersection.x - self.x),
y: (line_intersection.y - self.y),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Line {
pub x0: f64,
pub xz: f64,
pub y0: f64,
pub yz: f64,
}
impl Line {
#[must_use]
pub fn new(x0: f64, xz: f64, y0: f64, yz: f64) -> Line {
Line { x0, xz, y0, yz }
}
#[must_use]
pub fn new_on_axis(x0: f64, y0: f64) -> Line {
Line::new(x0, 0.0, y0, 0.0)
}
#[must_use]
pub fn new_from_two_points(a: &Vec3D, b: &Vec3D) -> Line {
let xz = (b.x - a.x) / (b.z - a.z);
let yz = (b.y - a.y) / (b.z - a.z);
let x0 = a.x - (a.z) / (b.z - a.z) * (b.x - a.x);
let y0 = a.y - (a.z) / (b.z - a.z) * (b.y - a.y);
Line::new(x0, xz, y0, yz)
}
#[must_use]
pub fn position_at_altitude(&self, alt: f64) -> Vec2D {
Vec2D::new(alt * self.xz + self.x0, alt * self.yz + self.y0)
}
#[must_use]
pub fn distance_at_ground(&self, other: &Line) -> f64 {
Vec2D::new(self.x0 - other.x0, self.y0 - other.y0).norm()
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_abs_diff_eq;
#[test]
fn linspread() {
let a = Vec2D::new(0.0, 3.0);
let b = Vec2D::new(3.0, 0.0);
let ls = Vec2D::linspread(&a, &b, 3);
assert_abs_diff_eq!(ls[0].x, 0.5);
assert_abs_diff_eq!(ls[0].y, 2.5);
assert_abs_diff_eq!(ls[1].x, 1.5);
assert_abs_diff_eq!(ls[1].y, 1.5);
assert_abs_diff_eq!(ls[2].x, 2.5);
assert_abs_diff_eq!(ls[2].y, 0.5);
}
#[test]
fn linspace() {
let a = Vec2D::new(1.0, 2.0);
let b = Vec2D::new(4.0, 0.0);
let ls = Vec2D::linspace(&a, &b, 3);
assert_abs_diff_eq!(ls[1].x, 2.5);
assert_abs_diff_eq!(ls[1].y, 1.0);
assert_eq!(ls[0], a);
assert_eq!(ls[2], b);
}
#[test]
fn adding() {
let a = Vec2D::new(1.0, 2.0);
let b = Vec3D::new(10.0, 20.0, 30.0);
let c = a + b;
assert_abs_diff_eq!(c.x, 11.0);
assert_abs_diff_eq!(c.y, 22.0);
assert_abs_diff_eq!(c.z, 30.0);
}
}