Source code for units.units_inner.angle.angle
"""Module for the angle class."""
import math
from typing import overload
from .angle_delta import AngleDelta
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 [0, 2*pi) radians."""
unit_delta_per_radian = get_unit_delta_per_radian(unit)
tmp_value = value % (2 * math.pi * unit_delta_per_radian)
return (
tmp_value if tmp_value >= 0 else tmp_value + 2 * math.pi * unit_delta_per_radian
)
[docs]
class Angle:
"""The opening between two lines in the same plane that meet at a point.
Angle always in range [0, 2*pi) radians.
"""
[docs]
def __init__(self, value: float, unit: Unit) -> None:
"""Initialise a new angle.
Values outside of the range range [0, 2*pi) radians will be mapped into it.
+--------+--------------------------------+-----------------------------+
| Range | INITIALIZATION | EQUIVALENT TO |
+========+================================+=============================+
| Within | Angle(185, Unit.DEGREE) | Angle(185, Unit.DEGREE) |
+--------+--------------------------------+-----------------------------+
| Below | Angle(-3*math.pi, Unit.RADIAN) | Angle(math.pi, Unit.RADIAN) |
+--------+--------------------------------+-----------------------------+
| Above | Angle(4, Unit.REVOLUTION) | Angle(0, Unit.REVOLUTION) |
+--------+--------------------------------+-----------------------------+
"""
self._value = _map_to_unit_circle(value, unit)
self._unit = unit
[docs]
def as_unit(self, unit: Unit) -> float:
"""Return the angle, 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, delta: AngleDelta) -> "Angle":
"""Return the sum of the angle and the difference."""
value_as_radian = self.as_unit(Unit.RADIAN)
delta_value_as_radian = delta.as_unit(Unit.RADIAN)
value_sum_as_radian = value_as_radian + delta_value_as_radian
return Angle(value_sum_as_radian, Unit.RADIAN)
[docs]
def __radd__(self, delta: AngleDelta) -> "Angle":
"""Return the sum of the angle and the difference."""
return self + delta
@overload
def __sub__(self, other: "Angle") -> AngleDelta: ...
@overload
def __sub__(self, other: AngleDelta) -> "Angle": ...
[docs]
def __sub__(self, other: "Angle | AngleDelta") -> "AngleDelta | Angle":
"""Return the delta between angles or the angle less the delta.
The behaviour depends upon the type of the argument.
- If the argument is an angle, return the difference between the two angles.
- If the argument is an angle delta, return the angle less the difference.
"""
value_as_radian = self.as_unit(Unit.RADIAN)
other_value_as_radian = other.as_unit(Unit.RADIAN)
value_difference_as_radian = value_as_radian - other_value_as_radian
return (
AngleDelta(value_difference_as_radian, Unit.RADIAN)
if isinstance(other, Angle)
else Angle(value_difference_as_radian, Unit.RADIAN)
)
[docs]
def __eq__(self, other: object) -> bool:
"""Return whether the objects are equal angles."""
if not isinstance(other, Angle):
return NotImplemented
return self.as_unit(Unit.RADIAN) == other.as_unit(Unit.RADIAN)
[docs]
def __hash__(self) -> int:
"""Return the hash of the length."""
return hash(self.as_unit(Unit.RADIAN))
def __str__(self) -> str:
"""Return a string representation of the length."""
return f"{self._value} {get_abbreviation(self._unit)}"
def __repr__(self) -> str:
"""Return a string representation of the length for developers."""
return f"{__class__.__name__}({self._value}, {get_name(self._unit)})"