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);
}
}