281 lines
9.3 KiB
Python
281 lines
9.3 KiB
Python
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0.If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at https ://mozilla.org/MPL/2.0/.
|
|
import sys
|
|
import os
|
|
|
|
# Make the parent directory (root of the package) discoverable
|
|
package_directory = os.path.dirname(os.path.abspath(__file__))
|
|
sys.path.insert(0, package_directory)
|
|
|
|
import math
|
|
import importlib
|
|
#from Float import *
|
|
importlib.import_module("Float")
|
|
|
|
# This is in fact AngleSingle
|
|
class Angle:
|
|
# The angle is internally limited to (-180..180] degrees or (-PI...PI]
|
|
# radians. When an angle exceeds this range, it is normalized to a value
|
|
# within the range.
|
|
Rad2Deg = 360 / (math.pi * 2)
|
|
Deg2Rad = (math.pi * 2) / 360
|
|
|
|
def __init__(self, degrees = 0):
|
|
self.value: float = degrees
|
|
Angle.Normalize(self)
|
|
|
|
@staticmethod
|
|
def Degrees(degrees):
|
|
angle = Angle(degrees)
|
|
return angle
|
|
|
|
@staticmethod
|
|
def Radians(radians):
|
|
angle = Angle(radians * Angle.Rad2Deg)
|
|
return angle;
|
|
|
|
def InDegrees(self):
|
|
return self.value;
|
|
def InRadians(self) -> float:
|
|
return self.value * Angle.Deg2Rad;
|
|
|
|
def __eq__(self, angle):
|
|
"""! Tests whether this angle is equal to the given angle
|
|
@param angle The angle to compare to
|
|
@return True when the angles are equal, False otherwise
|
|
@note The equality is determine within the limits of precision of the raw
|
|
type T
|
|
"""
|
|
return self.value == angle.value
|
|
def __gt__(self, angle):
|
|
"""! Tests if this angle is greater than the given angle
|
|
@param angle The given angle
|
|
@return True when this angle is greater than the given angle, False
|
|
otherwise
|
|
"""
|
|
return self.value > angle.value
|
|
def __gte__(self, angle):
|
|
"""! Tests if this angle is greater than or equal to the given angle
|
|
@param angle The given angle
|
|
@return True when this angle is greater than or equal to the given angle.
|
|
False otherwise.
|
|
"""
|
|
return self.value >= angle.value
|
|
def __lt__(self, angle):
|
|
"""! Tests if this angle is less than the given angle
|
|
@param angle The given angle
|
|
@return True when this angle is less than the given angle, False
|
|
otherwise
|
|
"""
|
|
return self.value < angle.value
|
|
def __lte__(self, angle):
|
|
"""! Tests if this angle is less than or equal to the given angle
|
|
@param angle The given angle
|
|
@return True when this angle is less than or equal to the given angle.
|
|
False otherwise.
|
|
"""
|
|
return self.value <= angle.value
|
|
|
|
def Sign(self):
|
|
"""! Returns the sign of the angle
|
|
@param angle The angle
|
|
@return -1 when the angle is negative, 1 when it is positive and 0
|
|
otherwise.
|
|
"""
|
|
if self.value < 0:
|
|
return -1
|
|
if self.value > 0:
|
|
return 1
|
|
return 0
|
|
def Abs(self):
|
|
"""! Returns the magnitude of the angle
|
|
@param angle The angle
|
|
@return The positive magitude of the angle.
|
|
Negative values are negated to get a positive result
|
|
"""
|
|
if self.value < 0:
|
|
return -self
|
|
return self
|
|
|
|
def __neg__(self):
|
|
"""! Negate the angle
|
|
@return The negated angle
|
|
"""
|
|
return Angle(-self.value)
|
|
def Inverse(self):
|
|
"""! Invert the angle: rotate by 180 degrees
|
|
"""
|
|
return self + Angle.Degrees(180)
|
|
|
|
def __sub__(self, other):
|
|
"""! Substract another angle from this angle
|
|
@param angle The angle to subtract from this angle
|
|
@return The result of the subtraction
|
|
"""
|
|
return Angle(self.value - other.value)
|
|
def __add__(self, other):
|
|
"""! Add another angle from this angle
|
|
@param angle The angle to add to this angle
|
|
@return The result of the addition
|
|
"""
|
|
return Angle(self.value + other.value)
|
|
def __mul__(self, factor):
|
|
"""! Multiplies the angle by a factor
|
|
@param angle The angle to multiply
|
|
@param factor The factor by which the angle is multiplied
|
|
@return The multiplied angle
|
|
"""
|
|
return Angle(self.value * factor)
|
|
def __truediv__(self, factor):
|
|
"""! Divides the angle by a factor
|
|
@param angle The angle to devide
|
|
@param factor The factor by which the angle is devided
|
|
@return The devided angle
|
|
"""
|
|
return Angle(self.value / factor)
|
|
|
|
@staticmethod
|
|
def Normalize(angle):
|
|
"""! Normalizes the angle to (-180..180] or (-PI..PI]
|
|
@note Should not be needed but available in case it is.
|
|
"""
|
|
while angle.value < -180:
|
|
angle.value += 360
|
|
while angle.value >= 180:
|
|
angle.value -= 360
|
|
return angle
|
|
|
|
@staticmethod
|
|
def Clamp(angle, min, max):
|
|
"""! Clamps the angle value between the two given angles
|
|
@param angle The angle to clamp
|
|
@param min The minimum angle
|
|
@param max The maximum angle
|
|
@return The clamped value
|
|
@remark When the min value is greater than the max value, angle is
|
|
returned unclamped.
|
|
"""
|
|
degrees = Float.Clamp(angle.InDegrees(), min.InDegrees(), max.InDegrees());
|
|
return Angle.Degrees(degrees)
|
|
|
|
@staticmethod
|
|
def MoveTowards(from_angle, to_angle, max_angle):
|
|
"""! Rotates an angle towards another angle with a max distance
|
|
@param from_angle The angle to start from
|
|
@param to_angle The angle to rotate towards
|
|
@param max_angle The maximum angle to rotate
|
|
@return The rotated angle
|
|
"""
|
|
max_degrees = max(0, max_angle) # filter out negative distances
|
|
delta_angle = Angle.Abs(to_angle - from_angle)
|
|
delta_degrees = delta_angle.InDegrees()
|
|
delta_degrees = Float.Clamp(delta_degrees, 0, max_degrees)
|
|
if delta_degrees < 0:
|
|
delta_degrees = -delta_degrees
|
|
return from_angle + Angle.Degrees(delta_degrees)
|
|
|
|
@staticmethod
|
|
def Cos(angle):
|
|
"""! Calculates the cosine of an angle
|
|
@param angle The given angle
|
|
@return The cosine of the angle
|
|
"""
|
|
return math.cos(angle.InRadians())
|
|
@staticmethod
|
|
def Sin(angle):
|
|
"""! Calculates the sine of an angle
|
|
@param angle The given angle
|
|
@return The sine of the angle
|
|
"""
|
|
return math.sin(angle.InRadians())
|
|
@staticmethod
|
|
def Tan(angle):
|
|
"""! Calculates the tangent of an angle
|
|
@param angle The given angle
|
|
@return The tangent of the angle
|
|
"""
|
|
return math.tan(angle.InRadians())
|
|
|
|
@staticmethod
|
|
def Acos(f):
|
|
"""! Calculates the arc cosine angle
|
|
@param f The value
|
|
@return The arc cosine for the given value
|
|
"""
|
|
return Angle.Radians(math.acos(f))
|
|
@staticmethod
|
|
def Asin(f):
|
|
"""! Calculates the arc sine angle
|
|
@param f The value
|
|
@return The arc sine for the given value
|
|
"""
|
|
return Angle.Radians(math.asin(f))
|
|
@staticmethod
|
|
def Atan(f):
|
|
"""! Calculates the arc tangent angle
|
|
@param f The value
|
|
@return The arc tangent for the given value
|
|
"""
|
|
return Angle.Radians(math.atan(f))
|
|
def Atan2(y, x):
|
|
"""! Calculates the tangent for the given values
|
|
@param y The vertical value
|
|
@param x The horizontal value
|
|
@return The tanget for the given values
|
|
Uses the y and x signs to compute the quadrant
|
|
"""
|
|
return Angle.Radians(math.atan2(y, x))
|
|
|
|
@staticmethod
|
|
def CosineRuleSide(a, b, gamma):
|
|
"""! Computes the length of a side of a triangle using the rule of cosines
|
|
@param a The length of side A
|
|
@param b The length of side B
|
|
@param gamma The angle of the corner opposing side C
|
|
@return The length of side C
|
|
"""
|
|
a2: float = a * a
|
|
b2: float = b * b
|
|
d: float = a2 + b2 - 2 * a * b * Angle.Cos(gamma)
|
|
# Catch edge cases where float inaccuracies lead tot NaNs
|
|
if d < 0:
|
|
return 0
|
|
|
|
c: float = math.sqrt(d)
|
|
return c
|
|
@staticmethod
|
|
def CosineRuleAngle(a, b, c):
|
|
"""! Computes the angle of a corner of a triangle using the rule of cosines
|
|
@param a The length of side A
|
|
@param b The length of side B
|
|
@param c The length of side C
|
|
@return The angle of the corner opposing side C
|
|
"""
|
|
a2: float = a * a
|
|
b2: float = b * b
|
|
c2: float = c * c
|
|
d: float = (a2 + b2 - c2) / (2 * a * b);
|
|
# Catch edge cases where float inaccuracies lead tot NaNs
|
|
if d >= 1:
|
|
return Angle()
|
|
if d <= -1:
|
|
return Angle.Degrees(180)
|
|
gamma: Angle = Angle.Acos(d)
|
|
return gamma;
|
|
|
|
@staticmethod
|
|
def SineRuleAngle(a, beta, c):
|
|
"""! Computes the angle of a triangle corner using the rule of sines
|
|
@param a The length of side A
|
|
@param beta the angle of the corner opposing side B
|
|
@param c The length of side C
|
|
@return The angle of the corner opposing side A
|
|
"""
|
|
alpha:Angle = Angle.Asin(a * Angle.Sin(beta) / c);
|
|
return alpha;
|
|
|
|
|
|
Angle.zero = Angle(0)
|
|
## An zero value angle
|