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 __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)