@@ -25,8 +25,8 @@ extension Quaternion {
25
25
/// - `.angleAxis`
26
26
/// - `.polar`
27
27
/// - `.rotationVector`
28
- /// - `init(angle:axis:)`
29
- /// - `init(length:angle :axis)`
28
+ /// - `init(length: angle:axis:)`
29
+ /// - `init(length:phase :axis)`
30
30
/// - `init(rotation:)`
31
31
///
32
32
/// [wiki]: https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Recovering_the_axis-angle_representation
@@ -51,23 +51,23 @@ extension Quaternion {
51
51
/// - `.angleAxis`
52
52
/// - `.polar`
53
53
/// - `.rotationVector`
54
- /// - `init(angle:axis:)`
55
- /// - `init(length:angle :axis)`
54
+ /// - `init(length: angle:axis:)`
55
+ /// - `init(length:phase :axis)`
56
56
/// - `init(rotation:)`
57
57
///
58
58
/// [wiki]: https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Recovering_the_axis-angle_representation
59
59
@inlinable
60
60
public var axis : SIMD3 < RealType > {
61
- guard isFinite && imaginary != . zero && !real . isZero else {
61
+ guard isFinite, imaginary != . zero, real != . zero else {
62
62
return SIMD3 ( repeating: . nan)
63
63
}
64
64
return imaginary / . sqrt( imaginary. lengthSquared)
65
65
}
66
66
67
67
/// The [Angle-Axis][wiki] representation.
68
68
///
69
- /// Returns the rotation angle in radians within *[0, 2π]* and the rotation
70
- /// axis as SIMD3 vector of unit length.
69
+ /// Returns the length of the quaternion, the rotation angle in radians
70
+ /// within *[0, 2π]* and the rotation axis as SIMD3 vector of unit length.
71
71
///
72
72
/// Edge cases:
73
73
/// -
@@ -80,13 +80,13 @@ extension Quaternion {
80
80
/// - `.axis`
81
81
/// - `.polar`
82
82
/// - `.rotationVector`
83
- /// - `init(angle:axis:)`
84
- /// - `init(length:angle :axis)`
83
+ /// - `init(length: angle:axis:)`
84
+ /// - `init(length:phase :axis)`
85
85
/// - `init(rotation:)`
86
86
///
87
87
/// [wiki]: https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Recovering_the_axis-angle_representation
88
- public var angleAxis : ( angle: RealType , axis: SIMD3 < RealType > ) {
89
- ( angle, axis)
88
+ public var angleAxis : ( length : RealType , angle: RealType , axis: SIMD3 < RealType > ) {
89
+ ( length , angle, axis)
90
90
}
91
91
92
92
/// The [rotation vector][rotvector].
@@ -109,8 +109,8 @@ extension Quaternion {
109
109
/// - `.angle`
110
110
/// - `.axis`
111
111
/// - `.angleAxis`
112
- /// - `init(angle:axis:)`
113
- /// - `init(length:angle :axis)`
112
+ /// - `init(length: angle:axis:)`
113
+ /// - `init(length:phase :axis)`
114
114
/// - `init(rotation:)`
115
115
///
116
116
/// [rotvector]: https://en.wikipedia.org/wiki/Axis–angle_representation#Rotation_vector
@@ -139,8 +139,8 @@ extension Quaternion {
139
139
/// - `.axis`
140
140
/// - `.angleAxis`
141
141
/// - `.rotationVector`
142
- /// - `init(angle:axis:)`
143
- /// - `init(length:angle :axis)`
142
+ /// - `init(length: angle:axis:)`
143
+ /// - `init(length:phase :axis)`
144
144
/// - `init(rotation:)`
145
145
///
146
146
/// [wiki]: https://en.wikipedia.org/wiki/Polar_decomposition#Quaternion_polar_decomposition
@@ -150,28 +150,31 @@ extension Quaternion {
150
150
151
151
/// Creates a unit quaternion specified with [Angle-Axis][wiki] values.
152
152
///
153
- /// Angle-Axis is a representation of a 3D rotation using two different
154
- /// quantities: an angle describing the magnitude of rotation, and a vector
155
- /// of unit length indicating the axis direction to rotate along.
153
+ /// Angle-Axis is a representation of a three-dimensional rotation using two
154
+ /// different quantities: an angle describing the magnitude of rotation, and
155
+ /// a vector of unit length indicating the axis direction to rotate along.
156
+ /// The optional length parameter scales the quaternion after the conversion.
156
157
///
157
- /// This initializer reads given `angle` and `axis` values and creates a
158
- /// quaternion of equal rotation properties using the following equation:
158
+ /// This initializer reads given `length`, `angle` and `axis` values and
159
+ /// creates a quaternion of equal rotation properties and of specified length
160
+ /// using the following equation:
159
161
///
160
- /// Q = (cos(angle/2), axis * sin(angle/2))
162
+ /// Q = (cos(angle/2), axis * sin(angle/2)) * length
161
163
///
162
- /// Given `axis` gets normalized if it is not of unit length.
164
+ /// If `length` is not specified, it defaults to *1*; and the final
165
+ /// quaternion is of unit length.
163
166
///
164
- /// The final quaternion is of unit length.
167
+ /// - Note: `axis` must be of unit length, or an assertion failure occurs .
165
168
///
166
169
/// Edge cases:
167
170
/// -
168
171
/// - For any `θ`, even `.infinity` or `.nan`:
169
172
/// ```
170
- /// Quaternion(angle: θ, axis: .zero ) == .zero
173
+ /// Quaternion(length: .zero, angle: θ, axis: axis ) == .zero
171
174
/// ```
172
175
/// - For any `θ`, even `.infinity` or `.nan`:
173
176
/// ```
174
- /// Quaternion(angle: θ, axis: .infinity ) == .ininfity
177
+ /// Quaternion(length: .infinity, angle: θ, axis: axis ) == .ininfity
175
178
/// ```
176
179
/// - Otherwise, `θ` must be finite, or a precondition failure occurs.
177
180
///
@@ -183,24 +186,36 @@ extension Quaternion {
183
186
/// - `.rotationVector`
184
187
/// - `.polar`
185
188
/// - `init(rotation:)`
186
- /// - `init(length:angle :axis)`
189
+ /// - `init(length:phase :axis)`
187
190
///
188
- /// - Parameter angle: The rotation angle about the rotation axis in radians
189
- /// - Parameter axis: The rotation axis
191
+ /// - Parameter length: The length of the quaternion. Defaults to `1`.
192
+ /// - Parameter angle: The rotation angle about the rotation axis in radians.
193
+ /// - Parameter axis: The rotation axis. Must be of unit length.
190
194
///
191
195
/// [wiki]: https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Recovering_the_axis-angle_representation
192
196
@inlinable
193
- public init ( angle: RealType , axis: SIMD3 < RealType > ) {
194
- let length : RealType = . sqrt( axis. lengthSquared)
195
- if angle. isFinite && length. isNormal {
196
- self = Quaternion ( halfAngle: angle/ 2 , unitAxis: axis/ length)
197
- } else {
198
- precondition (
199
- length. isZero || length. isInfinite,
200
- " Either angle must be finite, or axis length must be zero or infinite. "
201
- )
197
+ public init ( length: RealType = 1 , angle: RealType , axis: SIMD3 < RealType > ) {
198
+ guard !length. isZero, length. isFinite else {
202
199
self = Quaternion ( length)
200
+ return
203
201
}
202
+
203
+ // Length is finite and non-zero, therefore
204
+ // 1. `angle` must be finite or a precondition failure needs to occur; as
205
+ // this is not representable.
206
+ // 2. `axis` must be of unit length or an assertion failure occurs; while
207
+ // "wrong" by definition, it is representable.
208
+ precondition (
209
+ angle. isFinite,
210
+ " Either angle must be finite, or length must be zero or infinite. "
211
+ )
212
+ assert (
213
+ // TODO: Replace with `approximateEquality()`
214
+ abs ( . sqrt( axis. lengthSquared) - 1 ) < max ( . sqrt( axis. lengthSquared) , 1 ) * RealType. ulpOfOne. squareRoot ( ) ,
215
+ " Given axis must be of unit length. "
216
+ )
217
+
218
+ self = Quaternion ( halfAngle: angle/ 2 , unitAxis: axis) . multiplied ( by: length)
204
219
}
205
220
206
221
/// Creates a unit quaternion specified with given [rotation vector][wiki].
@@ -239,8 +254,8 @@ extension Quaternion {
239
254
/// - `.angleAxis`
240
255
/// - `.polar`
241
256
/// - `.rotationVector`
242
- /// - `init(angle:axis:)`
243
- /// - `init(length:angle :axis)`
257
+ /// - `init(length: angle:axis:)`
258
+ /// - `init(length:phase :axis)`
244
259
///
245
260
/// - Parameter vector: The rotation vector.
246
261
///
0 commit comments