@@ -49,7 +49,7 @@ extension Quaternion {
49
49
/// - If a quaternion is not finite, its `.length` is `infinity`.
50
50
///
51
51
/// See also `.magnitude`, `.lengthSquared`, `.polar` and
52
- /// `init(length:,phase:, axis:)`.
52
+ /// `init(length:argument: axis:)`.
53
53
@_transparent
54
54
public var length : RealType {
55
55
let naive = lengthSquared
@@ -77,124 +77,120 @@ extension Quaternion {
77
77
/// This property is more efficient to compute than `length`.
78
78
///
79
79
/// See also `.magnitude`, `.length`, `.polar` and
80
- /// `init(length:,phase:, axis:)`.
80
+ /// `init(length:argument: axis:)`.
81
81
@_transparent
82
82
public var lengthSquared : RealType {
83
83
( components * components) . sum ( )
84
84
}
85
85
86
+ /// The principle argument (half rotation angle) in radians within
87
+ /// *[0, π]* range.
88
+ ///
89
+ /// Edge cases:
90
+ /// - If the quaternion is zero or non-finite, argument is `nan`.
91
+ @inlinable
92
+ public var argument : RealType {
93
+ guard isFinite else { return . nan }
94
+ guard imaginary != . zero else {
95
+ // A zero quaternion does not encode transformation properties.
96
+ // If imaginary is zero, real must be non-zero or nan is returned.
97
+ return real. isZero ? . nan : . zero
98
+ }
99
+
100
+ // If lengthSquared computes without over/underflow, everything is fine
101
+ // and the result is correct. If not, we have to do the computation
102
+ // carefully and unscale the quaternion first.
103
+ let lenSq = imaginary. lengthSquared
104
+ guard lenSq. isNormal else { return divided ( by: magnitude) . argument }
105
+ return . atan2( y: . sqrt( lenSq) , x: real)
106
+ }
107
+
86
108
/// The [polar decomposition][wiki].
87
109
///
88
- /// Returns the length of this quaternion, phase in radians of range *[0, π]*
89
- /// and the rotation axis as SIMD3 vector of unit length.
110
+ /// Returns the length of this quaternion, argument in radians of range
111
+ /// *[0, π]* and the rotation axis as SIMD3 vector of unit length.
90
112
///
91
113
/// Edge cases:
92
- /// - If the quaternion is zero, length is `.zero` and angle and axis
114
+ /// - If the quaternion is zero, length is `.zero` and argument and axis
93
115
/// are `nan`.
94
- /// - If the quaternion is non-finite, length is `.infinity` and angle and
116
+ /// - If the quaternion is non-finite, length is `.infinity` and argument and
95
117
/// axis are `nan`.
96
- /// - For any length other than `.zero` or `.infinity`, if angle is zero, axis
97
- /// is `nan`.
118
+ /// - For any length other than `.zero` or `.infinity`, if argument is zero,
119
+ /// axis is `nan`.
98
120
///
99
121
/// See also `.magnitude`, `.length`, `.lengthSquared` and
100
- /// `init(length:,phase:, axis:)`.
122
+ /// `init(length:argument: axis:)`.
101
123
///
102
124
/// [wiki]: https://en.wikipedia.org/wiki/Polar_decomposition#Quaternion_polar_decomposition
103
- public var polar : ( length: RealType , phase: RealType , axis: SIMD3 < RealType > ) {
104
- ( length, halfAngle, axis)
125
+ public var polar : ( length: RealType , argument: RealType , axis: SIMD3 < RealType > ) {
126
+ ( length, argument, axis)
127
+ }
128
+
129
+ /// Creates a new quaternion from given half rotation angle about given
130
+ /// rotation axis.
131
+ ///
132
+ /// The angle-axis values are transformed using the following equation:
133
+ ///
134
+ /// Q = (cos(argument), unitAxis * sin(argument))
135
+ ///
136
+ /// - Parameters:
137
+ /// - argument: The half rotation angle
138
+ /// - unitAxis: The rotation axis of unit length
139
+ @usableFromInline @inline ( __always)
140
+ internal init ( argument: RealType , unitAxis: SIMD3 < RealType > ) {
141
+ self . init ( real: . cos( argument) , imaginary: unitAxis * . sin( argument) )
105
142
}
106
143
107
144
/// Creates a quaternion specified with [polar coordinates][wiki].
108
145
///
109
- /// This initializer reads given `length`, `phase ` and `axis` values and
146
+ /// This initializer reads given `length`, `argument ` and `axis` values and
110
147
/// creates a quaternion of equal rotation properties and specified *length*
111
148
/// using the following equation:
112
149
///
113
- /// Q = (cos(phase), axis * sin(phase)) * length
114
- ///
115
- /// - Note: `axis` must be of unit length, or an assertion failure occurs.
150
+ /// Q = (cos(argument), axis * sin(argument)) * length
116
151
///
117
152
/// Edge cases:
118
153
/// - Negative lengths are interpreted as reflecting the point through the
119
154
/// origin, i.e.:
120
155
/// ```
121
- /// Quaternion(length: -r, phase : θ, axis: axis) == -Quaternion(length: r, phase : θ, axis: axis)
156
+ /// Quaternion(length: -r, argument : θ, axis: axis) == -Quaternion(length: r, argument : θ, axis: axis)
122
157
/// ```
123
158
/// - For any `θ` and any `axis`, even `.infinity` or `.nan`:
124
159
/// ```
125
- /// Quaternion(length: .zero, phase : θ, axis: axis) == .zero
160
+ /// Quaternion(length: .zero, argument : θ, axis: axis) == .zero
126
161
/// ```
127
162
/// - For any `θ` and any `axis`, even `.infinity` or `.nan`:
128
163
/// ```
129
- /// Quaternion(length: .infinity, phase : θ, axis: axis) == .infinity
164
+ /// Quaternion(length: .infinity, argument : θ, axis: axis) == .infinity
130
165
/// ```
131
- /// - Otherwise, `θ` must be finite, or a precondition failure occurs.
166
+ /// - Otherwise, `θ` must be finite, or a precondition failure occurs and
167
+ /// `axis` must be of unit length, or an assertion failure occurs.
132
168
///
133
169
/// See also `.magnitude`, `.length`, `.lengthSquared` and `.polar`.
134
170
///
135
171
/// [wiki]: https://en.wikipedia.org/wiki/Polar_decomposition#Quaternion_polar_decomposition
136
172
@inlinable
137
- public init ( length: RealType , phase : RealType , axis: SIMD3 < RealType > ) {
173
+ public init ( length: RealType , argument : RealType , axis: SIMD3 < RealType > ) {
138
174
guard !length. isZero, length. isFinite else {
139
175
self = Quaternion ( length)
140
176
return
141
177
}
142
178
143
179
// Length is finite and non-zero, therefore
144
- // 1. `phase ` must be finite or a precondition failure needs to occur; as
180
+ // 1. `argument ` must be finite or a precondition failure needs to occur; as
145
181
// this is not representable.
146
182
// 2. `axis` must be of unit length or an assertion failure occurs; while
147
183
// "wrong" by definition, it is representable.
148
184
precondition (
149
- phase . isFinite,
150
- " Either phase must be finite, or length must be zero or infinite. "
185
+ argument . isFinite,
186
+ " Either argument must be finite, or length must be zero or infinite. "
151
187
)
152
188
assert (
153
189
// TODO: Replace with `approximateEquality()`
154
190
abs ( . sqrt( axis. lengthSquared) - 1 ) < max ( . sqrt( axis. lengthSquared) , 1 ) * RealType. ulpOfOne. squareRoot ( ) ,
155
191
" Given axis must be of unit length. "
156
192
)
157
193
158
- self = Quaternion ( halfAngle: phase, unitAxis: axis) . multiplied ( by: length)
159
- }
160
- }
161
-
162
- // MARK: - Operations for working with polar form
163
-
164
- extension Quaternion {
165
- /// The half rotation angle in radians within *[0, π]* range.
166
- ///
167
- /// Edge cases:
168
- /// - If the quaternion is zero or non-finite, halfAngle is `nan`.
169
- @usableFromInline @inline ( __always)
170
- internal var halfAngle : RealType {
171
- guard isFinite else { return . nan }
172
- guard imaginary != . zero else {
173
- // A zero quaternion does not encode transformation properties.
174
- // If imaginary is zero, real must be non-zero or nan is returned.
175
- return real. isZero ? . nan : . zero
176
- }
177
-
178
- // If lengthSquared computes without over/underflow, everything is fine
179
- // and the result is correct. If not, we have to do the computation
180
- // carefully and unscale the quaternion first.
181
- let lenSq = imaginary. lengthSquared
182
- guard lenSq. isNormal else { return divided ( by: magnitude) . halfAngle }
183
- return . atan2( y: . sqrt( lenSq) , x: real)
184
- }
185
-
186
- /// Creates a new quaternion from given half rotation angle about given
187
- /// rotation axis.
188
- ///
189
- /// The angle-axis values are transformed using the following equation:
190
- ///
191
- /// Q = (cos(halfAngle), unitAxis * sin(halfAngle))
192
- ///
193
- /// - Parameters:
194
- /// - halfAngle: The half rotation angle
195
- /// - unitAxis: The rotation axis of unit length
196
- @usableFromInline @inline ( __always)
197
- internal init ( halfAngle: RealType , unitAxis: SIMD3 < RealType > ) {
198
- self . init ( real: . cos( halfAngle) , imaginary: unitAxis * . sin( halfAngle) )
194
+ self = Quaternion ( argument: argument, unitAxis: axis) . multiplied ( by: length)
199
195
}
200
196
}
0 commit comments