2025-03-31 12:20:52 +02:00

114 lines
4.2 KiB
Python

import math
from .Angle import Angle
from .Vector import Vector3
class Direction:
"""! A direction using angles
* The horizontal angle ranging from -180 (inclusive) to 180 (exclusive)
degrees which is a rotation in the horizontal plane
* A vertical angle ranging from -90 (inclusive) to 90 (exclusive) degrees
which is the rotation in the up/down direction applied after the horizontal
rotation has been applied.
The angles are automatically normalized to stay within the abovenmentioned
ranges.
"""
def __init__(self, horizontal=Angle(), vertical=Angle()):
"""! Create a new direction
@param horizontal The horizontal angle
@param vertical The vertical angle.
"""
## horizontal angle, range in degrees = (-180..180]
self.horizontal: Angle = horizontal
## vertical angle, range in degrees = (-90..90]
self.vertical: Angle = vertical
self.Normalize()
@staticmethod
def Degrees(horizontal: float, vertical: float):
"""! Create a direction using angle values in degrees
@param horizontal The horizontal angle in degrees
@param vertical The vertical angle in degrees
@return The direction
"""
direction = Direction(Angle.Degrees(horizontal), Angle.Degrees(vertical))
return direction
@staticmethod
def Radians(horizontal: float, vertical: float):
"""! Create a direction using angle values in radians
@param horizontal The horizontal angle in radians
@param vertical The vertical angle in radians
@return The direction
"""
direction = Direction(Angle.Radians(horizontal), Angle.Radians(vertical))
return direction
def FromVector3(v: Vector3):
d = Direction(
horizontal = Angle.Atan2(v.right, v.forward),
vertical = Angle.Degrees(-90) - Angle.Acos(v.up)
)
return d;
def ToVector3(self) -> Vector3:
"""! Convert the direction to a Vector3 coordinate
@return The vector coordinate
"""
verticalRad = (math.pi / 2) - self.vertical.InRadians()
horizontalRad = self.horizontal.InRadians()
cosVertical = math.cos(verticalRad)
sinVertical = math.sin(verticalRad)
cosHorizontal = math.cos(horizontalRad)
sinHorizontal = math.sin(horizontalRad)
right = sinVertical * sinHorizontal
up = cosVertical
forward = sinVertical * cosHorizontal
return Vector3(right, up, forward)
def __eq__(self, direction):
"""! Test whether this direction is equal to another direction
@param direction The direction to compare to
@return True when the direction angles are equal, false otherwise.
"""
return (self.horizontal == direction.horizontal and
self.vertical == direction.vertical)
def isclose(self, other, rel_tol=1e-9, abs_tol=1e-8):
return (
Angle.isclose(self.horizontal, other.horizontal, rel_tol=rel_tol, abs_tol=abs_tol) and
Angle.isclose(self.vertical, other.vertical, rel_tol=rel_tol, abs_tol=abs_tol)
)
def __neg__(self):
"""! Negate/reverse the direction
@return The reversed direction.
"""
h: Angle = self.horizontal + Angle.Degrees(-180)
v: Angle = -self.vertical
return Direction(h, v)
def Inverse(self):
"""! This is a synonym for negation
"""
return -self
def Normalize(self):
"""! Normalize this vector to the specified ranges
@note Should not be needed but available in case it is.
"""
v = self.vertical.InDegrees()
deg180 = Angle.Degrees(-180)
if v > 90 or v < -90:
self.horizontal += deg180
self.vertical = deg180 - self.vertical
def __repr__(self):
return f"Direction(x={self.horizontal}, y={self.vertical})"
Direction.zero = Direction.Degrees(0, 0)
Direction.forward = Direction.Degrees(0, 0)
Direction.backward = Direction.Degrees(-180, 0)
Direction.up = Direction.Degrees(0, 90)
Direction.down = Direction.Degrees(0, -90)
Direction.left = Direction.Degrees(-90, 0)
Direction.right = Direction.Degrees(90, 0)