Source code for units.units_inner.angle.angle_delta
"""Module for the angle difference class."""
import math
from .unit import (
Unit,
get_abbreviation,
get_name,
get_unit_delta_per_radian,
)
def _map_to_unit_circle(value: float, unit: Unit) -> float:
"""Map an angle to the range [-pi, pi) radians."""
unit_delta_per_radian = get_unit_delta_per_radian(unit)
tmp_value = value % (2 * math.pi * unit_delta_per_radian)
if tmp_value < -math.pi * unit_delta_per_radian:
return tmp_value + 2 * math.pi * unit_delta_per_radian
if tmp_value >= math.pi * unit_delta_per_radian:
return tmp_value - 2 * math.pi * unit_delta_per_radian
return tmp_value
[docs]
class AngleDelta:
"""The difference between two angles.
Angle difference always in range [-pi, pi) radians.
"""
[docs]
def __init__(self, value: float, unit: Unit) -> None:
"""Initialise a new angle difference.
Values outside the range [-pi, pi) radians will be mapped into it.
+--------+-----------------------------------+-----------------------------------+
| Range | INITIALIZATION | EQUIVALENT TO |
+========+===================================+===================================+
| Within | AngleDelta(-40, Unit.DEGREE) | AngleDelta(-40, Unit.DEGREE) |
+--------+-----------------------------------+-----------------------------------+
| Below | AngleDelta(-3*pi, Unit.RADIAN) | AngleDelta(pi, Unit.RADIAN) |
+--------+-----------------------------------+-----------------------------------+
| Above | AngleDelta(3.75, Unit.REVOLUTION) | AngleDelta(-0.25, Unit.REVOLUTION)|
+--------+-----------------------------------+-----------------------------------+
""" # noqa: E501
self._value = _map_to_unit_circle(value, unit)
self._unit = unit
[docs]
def as_unit(self, unit: Unit) -> float:
"""Return the angle difference, expressed as the unit."""
internal_unit_delta_per_radian = get_unit_delta_per_radian(self._unit)
value_as_radian = self._value / internal_unit_delta_per_radian
external_unit_delta_per_radian = get_unit_delta_per_radian(unit)
return external_unit_delta_per_radian * value_as_radian
[docs]
def __add__(self, other: "AngleDelta") -> "AngleDelta":
"""Return the sum of the angle differences."""
# This NotImplemented block is here because the case of
# a AngleDelta + an Angle is handled in the __radd__ method in the
# Angle class, otherwise it runs into problems with circular imports
if not isinstance(other, AngleDelta): # type: ignore[reportUnnecessaryIsInstance]
return NotImplemented
value_as_radian = self.as_unit(Unit.RADIAN)
delta_value_as_radian = other.as_unit(Unit.RADIAN)
added_value_as_radian = value_as_radian + delta_value_as_radian
return AngleDelta(added_value_as_radian, Unit.RADIAN)
[docs]
def __sub__(self, delta: "AngleDelta") -> "AngleDelta":
"""Return the difference between the angle differences."""
return self + (-delta)
[docs]
def __neg__(self) -> "AngleDelta":
"""Return the inverse of the angle difference."""
inverted_value = -self._value
return AngleDelta(inverted_value, self._unit)
[docs]
def __eq__(self, other: object) -> bool:
"""Return whether the objects are equal angle differences."""
if not isinstance(other, AngleDelta):
return NotImplemented
return self.as_unit(Unit.RADIAN) == other.as_unit(Unit.RADIAN)
[docs]
def __hash__(self) -> int:
"""Return the hash of the angle difference."""
return hash(self.as_unit(Unit.RADIAN))
def __str__(self) -> str:
"""Return a string representation of the angle difference."""
return f"{self._value} {get_abbreviation(self._unit)}"
def __repr__(self) -> str:
"""Return a string representation of the angle difference for devs."""
return f"{__class__.__name__}({self._value}, {get_name(self._unit)})"