use core::fmt::{Display, Formatter};
use core::marker::PhantomData;
use crate::units::angle::Angle;
use crate::units::FromUnits;
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
pub enum RotationDirection {
#[default]
PositiveClockwise,
PositiveCounterClockwise,
}
impl FromUnits<Angle> for RotationDirection {
fn from(&self, value: Angle, units: Self) -> Angle {
value
* match self {
RotationDirection::PositiveClockwise => match units {
RotationDirection::PositiveClockwise => 1.0,
RotationDirection::PositiveCounterClockwise => -1.0,
},
RotationDirection::PositiveCounterClockwise => match units {
RotationDirection::PositiveClockwise => -1.0,
RotationDirection::PositiveCounterClockwise => 1.0,
},
}
}
}
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
pub enum CompassReference {
#[default]
TrueNorth,
MagneticNorth,
East,
}
impl FromUnits<Angle> for CompassReference {
fn from(&self, value: Angle, units: Self) -> Angle {
match self {
CompassReference::TrueNorth => match units {
CompassReference::TrueNorth => value,
_ => todo!(),
},
CompassReference::MagneticNorth => match units {
CompassReference::MagneticNorth => value,
_ => todo!(),
},
CompassReference::East => match units {
CompassReference::East => value,
_ => todo!(),
},
}
}
}
pub type Heading = Compass<HeadingType>;
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
pub struct HeadingType;
pub type Track = Compass<TrackType>;
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
pub struct TrackType;
pub type Bearing = Compass<BearingType>;
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
pub struct BearingType;
pub type Course = Compass<CourseType>;
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
pub struct CourseType;
pub type Azimuth = Compass<AzimuthType>;
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
pub struct AzimuthType;
#[derive(Debug, Copy, Clone, Default, PartialEq)]
pub struct Compass<T> {
angle: Angle,
direction: RotationDirection,
reference: CompassReference,
_ign: PhantomData<T>,
}
impl<T> Display for Compass<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(
f,
"{} {:?} {:?}",
self.angle, self.direction, self.reference
)
}
}
impl<T> Compass<T> {
#[must_use]
pub const fn new_heading(
angle: Angle,
direction: RotationDirection,
reference: CompassReference,
) -> Compass<HeadingType> {
Compass {
angle,
direction,
reference,
_ign: PhantomData,
}
}
#[must_use]
pub const fn new_track(
angle: Angle,
direction: RotationDirection,
reference: CompassReference,
) -> Compass<TrackType> {
Compass {
angle,
direction,
reference,
_ign: PhantomData,
}
}
#[must_use]
pub const fn new_bearing(
angle: Angle,
direction: RotationDirection,
reference: CompassReference,
) -> Compass<BearingType> {
Compass {
angle,
direction,
reference,
_ign: PhantomData,
}
}
#[must_use]
pub const fn new_course(
angle: Angle,
direction: RotationDirection,
reference: CompassReference,
) -> Compass<CourseType> {
Compass {
angle,
direction,
reference,
_ign: PhantomData,
}
}
#[must_use]
pub const fn new_azimuth(
angle: Angle,
direction: RotationDirection,
reference: CompassReference,
) -> Compass<AzimuthType> {
Compass {
angle,
direction,
reference,
_ign: PhantomData,
}
}
#[must_use]
pub const fn angle(&self) -> &Angle {
&self.angle
}
#[must_use]
pub const fn direction(&self) -> &RotationDirection {
&self.direction
}
#[must_use]
pub const fn reference(&self) -> &CompassReference {
&self.reference
}
#[must_use]
pub fn as_direction_reference(
&self,
direction: RotationDirection,
reference: CompassReference,
) -> Compass<T> {
let angle = direction.from(self.angle, self.direction);
let angle = reference.from(angle, self.reference);
Compass {
angle,
direction,
reference,
_ign: PhantomData,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CompassOffset<T, B> {
compass: Compass<T>,
offset: Angle,
direction: RotationDirection,
_ign: PhantomData<B>,
}
impl<T, B> CompassOffset<T, B> {
#[must_use]
pub fn compass(&self) -> &Compass<T> {
&self.compass
}
#[must_use]
pub fn offset(&self) -> &Angle {
&self.offset
}
#[must_use]
pub fn direction(&self) -> &RotationDirection {
&self.direction
}
}
pub type RelativeBearing = CompassOffset<HeadingType, BearingType>;
impl Compass<HeadingType> {
#[must_use]
pub fn relative_bearing(self, direction: RotationDirection, offset: Angle) -> RelativeBearing {
CompassOffset {
compass: self,
offset,
direction,
_ign: PhantomData,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum CompassDirection {
Heading(Heading),
Track(Track),
Bearing(Bearing),
Course(Course),
Azimuth(Azimuth),
}
impl Display for CompassDirection {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
CompassDirection::Heading(h) => {
write!(f, "Heading({h})")
}
CompassDirection::Track(t) => {
write!(f, "Track({t})")
}
CompassDirection::Bearing(b) => {
write!(f, "Bearing({b})")
}
CompassDirection::Course(c) => {
write!(f, "Course({c})")
}
CompassDirection::Azimuth(a) => {
write!(f, "Azimuth({a})")
}
}
}
}