0
# 3D Rotation
1
2
Apply quaternion rotations to 3D vectors and convert between different rotation representations including rotation matrices, Euler angles, and axis-angle pairs.
3
4
## Capabilities
5
6
### Vector Rotation
7
8
Rotate 3D vectors using quaternion rotations.
9
10
```python { .api }
11
def rotate(self, vector):
12
"""
13
Rotate a 3D vector by the rotation stored in the quaternion.
14
15
Parameters:
16
vector: 3-element sequence (list, tuple, numpy array) or Quaternion
17
For sequences: [x, y, z] coordinates
18
For Quaternion: uses vector part as 3D vector
19
20
Returns:
21
Same type as input: Rotated vector
22
- list input → list output
23
- tuple input → tuple output
24
- numpy array → numpy array
25
- Quaternion → Quaternion (rotated vector part)
26
27
Raises:
28
TypeError: If vector elements cannot be converted to real numbers
29
ValueError: If vector cannot be interpreted as 3-vector
30
"""
31
```
32
33
**Usage Examples:**
34
35
```python
36
from pyquaternion import Quaternion
37
import numpy as np
38
39
# 90-degree rotation about Y-axis
40
q = Quaternion(axis=[0, 1, 0], degrees=90)
41
42
# Rotate different vector types
43
vector_list = [1, 0, 0]
44
rotated_list = q.rotate(vector_list) # [0.0, 0.0, -1.0]
45
46
vector_tuple = (1, 0, 0)
47
rotated_tuple = q.rotate(vector_tuple) # (0.0, 0.0, -1.0)
48
49
vector_array = np.array([1, 0, 0])
50
rotated_array = q.rotate(vector_array) # array([0., 0., -1.])
51
52
# Rotate quaternion's vector part
53
q_vector = Quaternion(vector=[1, 0, 0])
54
rotated_q = q.rotate(q_vector) # Quaternion with rotated vector
55
```
56
57
### Axis Extraction
58
59
Get the rotation axis with handling for edge cases.
60
61
```python { .api }
62
def get_axis(self, undefined=np.zeros(3)):
63
"""
64
Get the rotation axis with fallback for undefined cases.
65
66
For unit quaternions representing rotations, returns the axis of rotation.
67
For edge cases (identity quaternion, pure real quaternions), returns
68
the provided fallback value.
69
70
Parameters:
71
undefined: array-like, default [0, 0, 0]
72
Fallback value when axis is undefined
73
74
Returns:
75
numpy.ndarray: Unit vector representing rotation axis,
76
or undefined value for edge cases
77
78
Note:
79
The axis property may return [0, 0, 0] for identity rotations,
80
while this method allows custom handling of such cases.
81
"""
82
```
83
84
**Usage Examples:**
85
86
```python
87
from pyquaternion import Quaternion
88
import numpy as np
89
90
# Regular rotation - has well-defined axis
91
q = Quaternion(axis=[1, 1, 0], degrees=45)
92
axis = q.get_axis()
93
print(f"Rotation axis: {axis}") # Normalized [1, 1, 0]
94
95
# Identity quaternion - no rotation, undefined axis
96
q_identity = Quaternion()
97
axis_default = q_identity.get_axis() # [0, 0, 0]
98
axis_custom = q_identity.get_axis(undefined=[0, 0, 1]) # [0, 0, 1]
99
100
print(f"Identity axis (default): {axis_default}")
101
print(f"Identity axis (custom): {axis_custom}")
102
```
103
104
### Matrix Representations
105
106
Convert quaternions to rotation and transformation matrices.
107
108
```python { .api }
109
@property
110
def rotation_matrix(self):
111
"""
112
Get 3x3 rotation matrix equivalent of the quaternion rotation.
113
114
The quaternion is automatically normalized to unit length.
115
116
Returns:
117
numpy.ndarray: 3x3 orthogonal rotation matrix
118
119
Note:
120
Only meaningful for unit quaternions. Non-unit quaternions
121
are automatically normalized before conversion.
122
"""
123
124
@property
125
def transformation_matrix(self):
126
"""
127
Get 4x4 homogeneous transformation matrix.
128
129
Creates a 4x4 matrix with the rotation in the upper-left 3x3 block,
130
zero translation, and [0, 0, 0, 1] in the bottom row.
131
132
Returns:
133
numpy.ndarray: 4x4 homogeneous transformation matrix
134
"""
135
```
136
137
**Usage Examples:**
138
139
```python
140
# Create rotation quaternion
141
q = Quaternion(axis=[0, 0, 1], degrees=45) # 45° about Z-axis
142
143
# Get rotation matrix
144
rot_matrix = q.rotation_matrix
145
print(f"Rotation matrix shape: {rot_matrix.shape}") # (3, 3)
146
print(f"Determinant: {np.linalg.det(rot_matrix)}") # ~1.0 (orthogonal)
147
148
# Get transformation matrix
149
transform_matrix = q.transformation_matrix
150
print(f"Transform matrix shape: {transform_matrix.shape}") # (4, 4)
151
152
# Apply to multiple points
153
points = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]).T # 3x3
154
rotated_points = rot_matrix @ points
155
```
156
157
### Euler Angles
158
159
Convert quaternions to Euler angle representations.
160
161
```python { .api }
162
@property
163
def yaw_pitch_roll(self):
164
"""
165
Get equivalent yaw-pitch-roll angles (intrinsic Tait-Bryan z-y'-x'').
166
167
The quaternion is automatically normalized to unit length.
168
169
Returns:
170
tuple: (yaw, pitch, roll) angles in radians
171
yaw: rotation about z-axis, range [-π, π]
172
pitch: rotation about y'-axis, range [-π/2, π/2]
173
roll: rotation about x''-axis, range [-π, π]
174
175
Note:
176
Rotation matrix equivalent: R = R_x(roll) * R_y(pitch) * R_z(yaw)
177
"""
178
```
179
180
**Usage Example:**
181
182
```python
183
# Create quaternion from Euler angles equivalent
184
q = Quaternion(axis=[0, 0, 1], degrees=30) # Yaw rotation
185
186
# Extract Euler angles
187
yaw, pitch, roll = q.yaw_pitch_roll
188
print(f"Yaw: {np.degrees(yaw):.1f}°") # ~30.0°
189
print(f"Pitch: {np.degrees(pitch):.1f}°") # ~0.0°
190
print(f"Roll: {np.degrees(roll):.1f}°") # ~0.0°
191
```
192
193
### Axis-Angle Representation
194
195
Extract rotation axis and angle from quaternions.
196
197
```python { .api }
198
@property
199
def axis(self):
200
"""
201
Get the unit vector axis of rotation.
202
203
Returns:
204
numpy.ndarray: 3-element unit vector [x, y, z]
205
Returns [0, 0, 0] for null rotations
206
"""
207
208
def get_axis(self, undefined=np.zeros(3)):
209
"""
210
Get rotation axis with custom fallback for null rotations.
211
212
Parameters:
213
undefined: 3-element sequence to return for null rotations
214
Default: [0, 0, 0]
215
216
Returns:
217
numpy.ndarray: 3-element unit vector or undefined value
218
"""
219
220
@property
221
def angle(self):
222
"""
223
Get rotation angle in radians.
224
225
Returns:
226
float: Angle in range (-π, π), sign indicates direction
227
Magnitude represents rotation amount
228
229
Note:
230
For 180° rotations, the axis/angle representation may
231
jump discontinuously between equivalent representations.
232
"""
233
234
@property
235
def degrees(self):
236
"""
237
Get rotation angle in degrees.
238
239
Returns:
240
float: Angle in degrees, converted from radians
241
"""
242
243
@property
244
def radians(self):
245
"""Alias for angle property."""
246
```
247
248
**Usage Examples:**
249
250
```python
251
# Create quaternion with known axis and angle
252
original_axis = [1, 1, 0] # Will be normalized
253
original_angle = 60 # degrees
254
q = Quaternion(axis=original_axis, degrees=original_angle)
255
256
# Extract axis and angle
257
extracted_axis = q.axis
258
extracted_angle_deg = q.degrees
259
extracted_angle_rad = q.radians
260
261
print(f"Original axis: {np.array(original_axis) / np.linalg.norm(original_axis)}")
262
print(f"Extracted axis: {extracted_axis}")
263
print(f"Original angle: {original_angle}°")
264
print(f"Extracted angle: {extracted_angle_deg:.1f}°")
265
266
# Handle null rotation case
267
identity_q = Quaternion() # Identity quaternion
268
null_axis = identity_q.axis # [0, 0, 0]
269
null_angle = identity_q.degrees # 0.0
270
271
# Custom undefined axis for null rotations
272
custom_axis = identity_q.get_axis(undefined=[1, 0, 0])
273
```
274
275
### Polar Decomposition
276
277
Advanced decomposition of quaternions into unit vector and angle components.
278
279
```python { .api }
280
@property
281
def polar_unit_vector(self):
282
"""
283
Get the unit vector component of polar decomposition.
284
285
Returns:
286
numpy.ndarray: Unit vector direction [x, y, z]
287
288
Raises:
289
ZeroDivisionError: If quaternion is pure real (no unique unit vector)
290
"""
291
292
@property
293
def polar_angle(self):
294
"""
295
Get the angle component of polar decomposition.
296
297
Returns:
298
float: Polar angle in radians
299
"""
300
301
@property
302
def polar_decomposition(self):
303
"""
304
Get complete polar decomposition.
305
306
Decomposes quaternion as: q = |q| * exp(unit_vector * angle)
307
308
Returns:
309
tuple: (unit_vector, angle)
310
unit_vector: numpy array [x, y, z]
311
angle: float in radians
312
313
Raises:
314
ZeroDivisionError: For pure real quaternions
315
"""
316
```
317
318
**Usage Example:**
319
320
```python
321
# Create non-trivial quaternion
322
q = Quaternion(axis=[1, 1, 1], degrees=120)
323
324
try:
325
# Get polar decomposition
326
unit_vec, polar_angle = q.polar_decomposition
327
print(f"Unit vector: {unit_vec}")
328
print(f"Polar angle: {np.degrees(polar_angle):.1f}°")
329
330
# Reconstruct quaternion (approximately)
331
reconstructed = q.norm * Quaternion.exp(
332
Quaternion(vector=unit_vec * polar_angle)
333
)
334
print(f"Original: {q}")
335
print(f"Reconstructed: {reconstructed}")
336
337
except ZeroDivisionError:
338
print("Cannot decompose pure real quaternion")
339
```
340
341
### Internal Rotation Methods
342
343
Internal methods used by the rotation system.
344
345
```python { .api }
346
def _rotate_quaternion(self, q):
347
"""
348
Rotate a quaternion vector using stored rotation.
349
350
Parameters:
351
q: Quaternion with vector part to rotate (scalar part ignored)
352
353
Returns:
354
Quaternion: Rotated quaternion using formula: self * q * self.conjugate
355
"""
356
357
def _wrap_angle(self, theta):
358
"""
359
Wrap angle to range (-π, π].
360
361
Parameters:
362
theta: Angle in radians
363
364
Returns:
365
float: Wrapped angle, with odd multiples of π mapped to +π
366
"""
367
```