@@ -49,7 +49,7 @@ extension Quaternion {
4949 /// - If a quaternion is not finite, its `.length` is `infinity`.
5050 ///
5151 /// See also `.magnitude`, `.lengthSquared`, `.polar` and
52- /// `init(length:,phase:, axis:)`.
52+ /// `init(length:argument: axis:)`.
5353 @_transparent
5454 public var length : RealType {
5555 let naive = lengthSquared
@@ -77,124 +77,120 @@ extension Quaternion {
7777 /// This property is more efficient to compute than `length`.
7878 ///
7979 /// See also `.magnitude`, `.length`, `.polar` and
80- /// `init(length:,phase:, axis:)`.
80+ /// `init(length:argument: axis:)`.
8181 @_transparent
8282 public var lengthSquared : RealType {
8383 ( components * components) . sum ( )
8484 }
8585
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+
86108 /// The [polar decomposition][wiki].
87109 ///
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.
90112 ///
91113 /// 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
93115 /// 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
95117 /// 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`.
98120 ///
99121 /// See also `.magnitude`, `.length`, `.lengthSquared` and
100- /// `init(length:,phase:, axis:)`.
122+ /// `init(length:argument: axis:)`.
101123 ///
102124 /// [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) )
105142 }
106143
107144 /// Creates a quaternion specified with [polar coordinates][wiki].
108145 ///
109- /// This initializer reads given `length`, `phase ` and `axis` values and
146+ /// This initializer reads given `length`, `argument ` and `axis` values and
110147 /// creates a quaternion of equal rotation properties and specified *length*
111148 /// using the following equation:
112149 ///
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
116151 ///
117152 /// Edge cases:
118153 /// - Negative lengths are interpreted as reflecting the point through the
119154 /// origin, i.e.:
120155 /// ```
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)
122157 /// ```
123158 /// - For any `θ` and any `axis`, even `.infinity` or `.nan`:
124159 /// ```
125- /// Quaternion(length: .zero, phase : θ, axis: axis) == .zero
160+ /// Quaternion(length: .zero, argument : θ, axis: axis) == .zero
126161 /// ```
127162 /// - For any `θ` and any `axis`, even `.infinity` or `.nan`:
128163 /// ```
129- /// Quaternion(length: .infinity, phase : θ, axis: axis) == .infinity
164+ /// Quaternion(length: .infinity, argument : θ, axis: axis) == .infinity
130165 /// ```
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.
132168 ///
133169 /// See also `.magnitude`, `.length`, `.lengthSquared` and `.polar`.
134170 ///
135171 /// [wiki]: https://en.wikipedia.org/wiki/Polar_decomposition#Quaternion_polar_decomposition
136172 @inlinable
137- public init ( length: RealType , phase : RealType , axis: SIMD3 < RealType > ) {
173+ public init ( length: RealType , argument : RealType , axis: SIMD3 < RealType > ) {
138174 guard !length. isZero, length. isFinite else {
139175 self = Quaternion ( length)
140176 return
141177 }
142178
143179 // 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
145181 // this is not representable.
146182 // 2. `axis` must be of unit length or an assertion failure occurs; while
147183 // "wrong" by definition, it is representable.
148184 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. "
151187 )
152188 assert (
153189 // TODO: Replace with `approximateEquality()`
154190 abs ( . sqrt( axis. lengthSquared) - 1 ) < max ( . sqrt( axis. lengthSquared) , 1 ) * RealType. ulpOfOne. squareRoot ( ) ,
155191 " Given axis must be of unit length. "
156192 )
157193
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)
199195 }
200196}
0 commit comments