use std::f64::consts::{ PI, TAU };
use std::ops::{ Add, Sub, Mul, MulAssign, Div, DivAssign, Neg };
use std::fmt::{ Display, Result as DRes };
use super::{ cartesian::Cartesian, spherical::Spherical };
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Cylindrical {
pub r: f64,
pub theta: f64,
pub z: f64
}
impl Display for Cylindrical {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> DRes {
write!(f, "r={} :: theta={}° :: z={}", self.r, self.theta.to_degrees(), self.z)?;
Ok(())
}
}
impl Cylindrical {
pub const fn new() -> Self {
Self {
r: 0.0,
theta: 0.0,
z: 0.0
}
}
pub fn from<T, U, V>(r: T, theta: U, z: V) -> Self
where T: Into<f64>, U: Into<f64>, V: Into<f64> {
Self {
r: r.into(),
theta: theta.into() % TAU,
z: z.into()
}
}
pub fn from_degree<T, U, V>(r: T, theta: U, z: V) -> Self
where T: Into<f64>, U: Into<f64>, V: Into<f64> {
let td: f64 = theta.into();
Self {
r: r.into(),
theta: td.to_radians() % TAU,
z: z.into()
}
}
pub fn from_coord<T>(c: T) -> Self
where T: Into<Self> {
c.into()
}
pub fn distance(&self, other: Self) -> f64 {
let t1: f64 = self.r.powi(2) + other.r.powi(2);
let t2: f64 = (self.theta - other.theta).cos() * 2.0 * self.r * other.r;
let t3: f64 = (self.z - other.z).powi(2);
(t1 - t2 + t3).sqrt()
}
}
impl Into<Cartesian> for Cylindrical {
fn into(self) -> Cartesian {
Cartesian {
x: self.r * self.theta.cos(),
y: self.r * self.theta.sin(),
z: self.z
}
}
}
impl Into<Spherical> for Cylindrical {
fn into(self) -> Spherical {
let rho: f64 = (self.r.powi(2) + self.z.powi(2)).sqrt();
let mut np: f64 = (self.r / self.z).atan();
if self.z.is_sign_negative() {
np += PI;
}
Spherical {
r: rho,
theta: self.theta,
phi: np
}
}
}
impl<T: Into<Cartesian>> Add<T> for Cylindrical {
type Output = Self;
fn add(self, rhs: T) -> Self::Output {
let s: Cartesian = self.into();
let r: Cartesian = rhs.into();
(s + r).into()
}
}
impl<T: Into<Cartesian>> Sub<T> for Cylindrical {
type Output = Self;
fn sub(self, rhs: T) -> Self::Output {
let s: Cartesian = self.into();
let r: Cartesian = rhs.into();
(s - r).into()
}
}
impl<T: Into<f64>> Mul<T> for Cylindrical {
type Output = Self;
fn mul(self, rhs: T) -> Self::Output {
let f: f64 = rhs.into();
let res: Self = Self {
r: self.r * f.abs(),
theta: self.theta,
z: self.z * f.abs()
};
if f.is_sign_negative() {
-res
} else {
res
}
}
}
impl<T: Into<f64>> MulAssign<T> for Cylindrical {
fn mul_assign(&mut self, rhs: T) {
let f: f64 = rhs.into();
self.r *= f.abs();
self.z *= f;
if f.is_sign_negative() {
self.theta = (self.theta + PI) % TAU;
}
}
}
impl<T: Into<f64>> Div<T> for Cylindrical {
type Output = Self;
fn div(self, rhs: T) -> Self::Output {
let f: f64 = rhs.into();
let res: Self = Self {
r: self.r / f.abs(),
theta: self.theta,
z: self.z / f.abs()
};
if f.is_sign_negative() {
-res
} else {
res
}
}
}
impl<T: Into<f64>> DivAssign<T> for Cylindrical {
fn div_assign(&mut self, rhs: T) {
let f: f64 = rhs.into();
self.r /= f.abs();
self.z /= f;
if f.is_sign_negative() {
self.theta = (self.theta + PI) % TAU;
}
}
}
impl Neg for Cylindrical {
type Output = Self;
fn neg(self) -> Self::Output {
Self {
r: self.r,
theta: (self.theta + PI) % TAU,
z: -self.z
}
}
}