from typing import Generic
from ground.base import (Context,
Kind,
Orientation)
from ground.hints import (Point,
Scalar)
from reprit.base import generate_repr
Kind = Kind
Orientation = Orientation
class Angle(Generic[Scalar]):
[docs] @classmethod
def from_sides(cls,
vertex: Point[Scalar],
first_ray_point: Point[Scalar],
second_ray_point: Point[Scalar]) -> 'Angle[Scalar]':
"""
Constructs angle from sides.
Time complexity:
``O(1)``
Memory complexity:
``O(1)``
>>> from gon.base import Angle, Point
>>> angle = Angle.from_sides(Point(0, 0), Point(1, 0), Point(0, 1))
>>> angle == Angle(0, 1)
True
"""
context = cls._context
squared_lengths_product = (
context.points_squared_distance(vertex, first_ray_point)
* context.points_squared_distance(vertex, second_ray_point)
)
if not squared_lengths_product:
return cls(1, 0)
lengths_product_inverted = 1 / context.sqrt(squared_lengths_product)
return cls(context.dot_product(vertex, first_ray_point, vertex,
second_ray_point)
* lengths_product_inverted,
context.cross_product(vertex, first_ray_point, vertex,
second_ray_point)
* lengths_product_inverted)
@property
def cosine(self) -> Scalar:
"""
Returns cosine of the angle.
Time complexity:
``O(1)``
Memory complexity:
``O(1)``
>>> from gon.base import Angle
>>> angle = Angle(0, 1)
>>> angle.cosine == 0
True
"""
return self._cosine
@property
def kind(self) -> Kind:
"""
Returns kind of the angle.
Time complexity:
``O(1)``
Memory complexity:
``O(1)``
>>> from gon.base import Angle, Kind
>>> angle = Angle(0, 1)
>>> angle.kind is Kind.RIGHT
True
"""
return Kind(to_sign(self.cosine))
@property
def orientation(self) -> Orientation:
"""
Returns orientation of the angle.
Time complexity:
``O(1)``
Memory complexity:
``O(1)``
>>> from gon.base import Angle, Orientation
>>> angle = Angle(0, 1)
>>> angle.orientation is Orientation.COUNTERCLOCKWISE
True
"""
return Orientation(to_sign(self.sine))
@property
def sine(self) -> Scalar:
"""
Returns sine of the angle.
Time complexity:
``O(1)``
Memory complexity:
``O(1)``
>>> from gon.base import Angle
>>> angle = Angle(0, 1)
>>> angle.sine == 1
True
"""
return self._sine
__slots__ = '_cosine', '_sine'
[docs] def __init__(self, cosine: Scalar, sine: Scalar) -> None:
"""
Initializes angle.
Time complexity:
``O(1)``
Memory complexity:
``O(1)``
"""
self._cosine, self._sine = cosine, sine
__repr__ = generate_repr(__init__)
[docs] def __add__(self, other: 'Angle[Scalar]') -> 'Angle[Scalar]':
"""
Returns sum of the angle with the other.
Time complexity:
``O(1)``
Memory complexity:
``O(1)``
>>> from gon.base import Angle
>>> angle = Angle(0, 1)
>>> angle + Angle(1, 0) == angle
True
"""
return (type(self)(self.cosine * other.cosine - self.sine * other.sine,
self.cosine * other.sine + self.sine * other.cosine)
if isinstance(other, Angle)
else NotImplemented)
[docs] def __bool__(self) -> bool:
"""
Checks that the angle is non-zero.
Time complexity:
``O(1)``
Memory complexity:
``O(1)``
>>> from gon.base import Angle
>>> angle = Angle(0, 1)
>>> bool(angle)
True
"""
return bool(self.sine)
[docs] def __eq__(self, other: 'Angle') -> bool:
"""
Checks if the angle is equal to the other.
Time complexity:
``O(1)``
Memory complexity:
``O(1)``
>>> from gon.base import Angle
>>> angle = Angle(0, 1)
>>> angle == angle
True
"""
return (self.cosine == other.cosine and self.sine == other.sine
if isinstance(other, Angle)
else NotImplemented)
[docs] def __hash__(self) -> int:
"""
Returns hash value of the angle.
Time complexity:
``O(1)``
Memory complexity:
``O(1)``
>>> from gon.base import Angle
>>> angle = Angle(0, 1)
>>> hash(angle) == hash(angle)
True
"""
return hash((self.cosine, self.sine))
[docs] def __neg__(self) -> 'Angle[Scalar]':
"""
Returns the angle negated.
Time complexity:
``O(1)``
Memory complexity:
``O(1)``
>>> from gon.base import Angle
>>> angle = Angle(0, 1)
>>> -angle == Angle(0, -1)
True
"""
return type(self)(self.cosine, -self.sine)
[docs] def __pos__(self) -> 'Angle[Scalar]':
"""
Returns the angle positive.
Time complexity:
``O(1)``
Memory complexity:
``O(1)``
>>> from gon.base import Angle
>>> angle = Angle(0, 1)
>>> +angle == angle
True
"""
return self
[docs] def __sub__(self, other: 'Angle[Scalar]') -> 'Angle[Scalar]':
"""
Returns difference of the angle with the other.
Time complexity:
``O(1)``
Memory complexity:
``O(1)``
>>> from gon.base import Angle
>>> angle = Angle(0, 1)
>>> angle - Angle(1, 0) == angle
True
"""
return (type(self)(self.cosine * other.cosine + self.sine * other.sine,
self.sine * other.cosine - self.cosine * other.sine)
if isinstance(other, Angle)
else NotImplemented)
[docs] def validate(self) -> None:
"""
Checks if the angle is valid.
Time complexity:
``O(1)``
Memory complexity:
``O(1)``
>>> from gon.base import Angle
>>> angle = Angle(0, 1)
>>> angle.validate()
"""
if square(self.cosine) + square(self.sine) != 1:
raise ValueError('Pythagorean trigonometric identity is unmet.')
_context = ... # type: Context
def square(value: Scalar) -> Scalar:
return value * value
def to_sign(value: Scalar) -> int:
return 1 if value > 0 else (-1 if value else 0)