Skip to content

Commit 2eeae49

Browse files
authored
REFACTOR: Refactored quaternion implementation (#6151)
1 parent e5917db commit 2eeae49

12 files changed

Lines changed: 2627 additions & 542 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Refactored quaternion implementation

doc/source/API/generic.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,35 @@ The following methods allows to read and parse files.
4141
read_configuration_file
4242
write_configuration_file
4343
compute_fft
44+
45+
46+
Quaternion
47+
~~~~~~~~~~
48+
49+
PyAEDT contains an implementation of fundamental quaternion operations.
50+
51+
Quaternions are only used to represent rotations in 3D space. They are not used to represent translations or other transformations.
52+
Only methods related to rotations are implemented.
53+
54+
.. currentmodule:: ansys.aedt.core.generic.quaternion
55+
56+
.. autosummary::
57+
:toctree: _autosummary
58+
:nosignatures:
59+
60+
Quaternion
61+
62+
63+
Math utils
64+
~~~~~~~~~~
65+
66+
MathUtils is a class that provides mathematical utility methods like numerical comparisons and checks.
67+
68+
.. currentmodule:: ansys.aedt.core.generic.math_utils
69+
70+
.. autosummary::
71+
:toctree: _autosummary
72+
:nosignatures:
73+
74+
MathUtils
75+
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# Copyright (C) 2021 - 2025 ANSYS, Inc. and/or its affiliates.
4+
# SPDX-License-Identifier: MIT
5+
#
6+
#
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy
8+
# of this software and associated documentation files (the "Software"), to deal
9+
# in the Software without restriction, including without limitation the rights
10+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
# copies of the Software, and to permit persons to whom the Software is
12+
# furnished to do so, subject to the following conditions:
13+
#
14+
# The above copyright notice and this permission notice shall be included in all
15+
# copies or substantial portions of the Software.
16+
#
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
# SOFTWARE.
24+
25+
import math
26+
from sys import float_info
27+
28+
from ansys.aedt.core.generic.general_methods import pyaedt_function_handler
29+
30+
31+
class MathUtils:
32+
"""MathUtils is a utility class that provides methods for numerical comparisons and checks."""
33+
34+
EPSILON = float_info.epsilon * 10.0
35+
36+
@staticmethod
37+
@pyaedt_function_handler()
38+
def is_zero(x: float, eps: float = EPSILON) -> bool:
39+
"""Check if a number is close to zero within a small epsilon tolerance.
40+
41+
Parameters:
42+
x: float
43+
Number to check.
44+
eps : float
45+
Tolerance for the comparison. Default is ``EPSILON``.
46+
47+
Returns:
48+
bool
49+
``True`` if the number is numerically zero, ``False`` otherwise.
50+
51+
"""
52+
return abs(x) < eps
53+
54+
@staticmethod
55+
@pyaedt_function_handler()
56+
def is_close(a: float, b: float, relative_tolerance: float = 1e-9, absolute_tolerance: float = 0.0) -> bool:
57+
"""Whether two numbers are close to each other given relative and absolute tolerances.
58+
59+
Parameters
60+
----------
61+
a : float, int
62+
First number to compare.
63+
b : float, int
64+
Second number to compare.
65+
relative_tolerance : float
66+
Relative tolerance. The default value is ``1e-9``.
67+
absolute_tolerance : float
68+
Absolute tolerance. The default value is ``0.0``.
69+
70+
Returns
71+
-------
72+
bool
73+
``True`` if the two numbers are closed, ``False`` otherwise.
74+
"""
75+
return abs(a - b) <= max(relative_tolerance * max(abs(a), abs(b)), absolute_tolerance)
76+
77+
@staticmethod
78+
@pyaedt_function_handler()
79+
def is_equal(a: float, b: float, eps: float = EPSILON) -> bool:
80+
"""
81+
Return True if numbers a and b are equal within a small epsilon tolerance.
82+
83+
Parameters:
84+
a: float
85+
First number.
86+
b: float
87+
Second number.
88+
eps : float
89+
Tolerance for the comparison. Default is ``EPSILON``.
90+
91+
Returns:
92+
bool
93+
``True`` if the absolute difference between a and b is less than epsilon, ``False`` otherwise.
94+
"""
95+
return abs(a - b) < eps
96+
97+
@staticmethod
98+
@pyaedt_function_handler()
99+
def atan2(y: float, x: float) -> float:
100+
"""Implementation of atan2 that does not suffer from the following issues:
101+
math.atan2(0.0, 0.0) = 0.0
102+
math.atan2(-0.0, 0.0) = -0.0
103+
math.atan2(0.0, -0.0) = 3.141592653589793
104+
math.atan2(-0.0, -0.0) = -3.141592653589793
105+
and returns always 0.0.
106+
107+
Parameters
108+
----------
109+
y : float
110+
Y-axis value for atan2.
111+
112+
x : float
113+
X-axis value for atan2.
114+
115+
Returns
116+
-------
117+
float
118+
119+
"""
120+
if abs(y) < MathUtils.EPSILON:
121+
y = 0.0
122+
if abs(x) < MathUtils.EPSILON:
123+
x = 0.0
124+
return math.atan2(y, x)
125+
126+
@staticmethod
127+
@pyaedt_function_handler()
128+
def is_scalar_number(x):
129+
"""Check if a value is a scalar number (int or float).
130+
131+
Parameters
132+
----------
133+
x : object
134+
Value to check.
135+
136+
Returns
137+
-------
138+
bool
139+
``True`` if x is a scalar number, ``False`` otherwise.
140+
"""
141+
return isinstance(x, (int, float))
142+
143+
@staticmethod
144+
@pyaedt_function_handler()
145+
def fix_negative_zero(value):
146+
"""Fix the negative zero.
147+
It supports lists (and nested lists).
148+
149+
Parameters
150+
----------
151+
value : float, List
152+
Value to be fixed.
153+
154+
Returns
155+
-------
156+
float, List
157+
Fixed value.
158+
159+
"""
160+
if isinstance(value, list):
161+
return [MathUtils.fix_negative_zero(item) for item in value]
162+
return 0.0 if value == 0.0 and math.copysign(1.0, value) == -1.0 else value

0 commit comments

Comments
 (0)