@@ -25,8 +25,8 @@ extension Quaternion {
2525 /// - `.angleAxis`
2626 /// - `.polar`
2727 /// - `.rotationVector`
28- /// - `init(angle:axis:)`
29- /// - `init(length:angle :axis)`
28+ /// - `init(length: angle:axis:)`
29+ /// - `init(length:phase :axis)`
3030 /// - `init(rotation:)`
3131 ///
3232 /// [wiki]: https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Recovering_the_axis-angle_representation
@@ -51,23 +51,23 @@ extension Quaternion {
5151 /// - `.angleAxis`
5252 /// - `.polar`
5353 /// - `.rotationVector`
54- /// - `init(angle:axis:)`
55- /// - `init(length:angle :axis)`
54+ /// - `init(length: angle:axis:)`
55+ /// - `init(length:phase :axis)`
5656 /// - `init(rotation:)`
5757 ///
5858 /// [wiki]: https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Recovering_the_axis-angle_representation
5959 @inlinable
6060 public var axis : SIMD3 < RealType > {
61- guard isFinite && imaginary != . zero && !real . isZero else {
61+ guard isFinite, imaginary != . zero, real != . zero else {
6262 return SIMD3 ( repeating: . nan)
6363 }
6464 return imaginary / . sqrt( imaginary. lengthSquared)
6565 }
6666
6767 /// The [Angle-Axis][wiki] representation.
6868 ///
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.
7171 ///
7272 /// Edge cases:
7373 /// -
@@ -80,13 +80,13 @@ extension Quaternion {
8080 /// - `.axis`
8181 /// - `.polar`
8282 /// - `.rotationVector`
83- /// - `init(angle:axis:)`
84- /// - `init(length:angle :axis)`
83+ /// - `init(length: angle:axis:)`
84+ /// - `init(length:phase :axis)`
8585 /// - `init(rotation:)`
8686 ///
8787 /// [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)
9090 }
9191
9292 /// The [rotation vector][rotvector].
@@ -109,8 +109,8 @@ extension Quaternion {
109109 /// - `.angle`
110110 /// - `.axis`
111111 /// - `.angleAxis`
112- /// - `init(angle:axis:)`
113- /// - `init(length:angle :axis)`
112+ /// - `init(length: angle:axis:)`
113+ /// - `init(length:phase :axis)`
114114 /// - `init(rotation:)`
115115 ///
116116 /// [rotvector]: https://en.wikipedia.org/wiki/Axis–angle_representation#Rotation_vector
@@ -139,8 +139,8 @@ extension Quaternion {
139139 /// - `.axis`
140140 /// - `.angleAxis`
141141 /// - `.rotationVector`
142- /// - `init(angle:axis:)`
143- /// - `init(length:angle :axis)`
142+ /// - `init(length: angle:axis:)`
143+ /// - `init(length:phase :axis)`
144144 /// - `init(rotation:)`
145145 ///
146146 /// [wiki]: https://en.wikipedia.org/wiki/Polar_decomposition#Quaternion_polar_decomposition
@@ -150,28 +150,31 @@ extension Quaternion {
150150
151151 /// Creates a unit quaternion specified with [Angle-Axis][wiki] values.
152152 ///
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.
156157 ///
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:
159161 ///
160- /// Q = (cos(angle/2), axis * sin(angle/2))
162+ /// Q = (cos(angle/2), axis * sin(angle/2)) * length
161163 ///
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.
163166 ///
164- /// The final quaternion is of unit length.
167+ /// - Note: `axis` must be of unit length, or an assertion failure occurs .
165168 ///
166169 /// Edge cases:
167170 /// -
168171 /// - For any `θ`, even `.infinity` or `.nan`:
169172 /// ```
170- /// Quaternion(angle: θ, axis: .zero ) == .zero
173+ /// Quaternion(length: .zero, angle: θ, axis: axis ) == .zero
171174 /// ```
172175 /// - For any `θ`, even `.infinity` or `.nan`:
173176 /// ```
174- /// Quaternion(angle: θ, axis: .infinity ) == .ininfity
177+ /// Quaternion(length: .infinity, angle: θ, axis: axis ) == .ininfity
175178 /// ```
176179 /// - Otherwise, `θ` must be finite, or a precondition failure occurs.
177180 ///
@@ -183,24 +186,36 @@ extension Quaternion {
183186 /// - `.rotationVector`
184187 /// - `.polar`
185188 /// - `init(rotation:)`
186- /// - `init(length:angle :axis)`
189+ /// - `init(length:phase :axis)`
187190 ///
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.
190194 ///
191195 /// [wiki]: https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Recovering_the_axis-angle_representation
192196 @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 {
202199 self = Quaternion ( length)
200+ return
203201 }
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)
204219 }
205220
206221 /// Creates a unit quaternion specified with given [rotation vector][wiki].
@@ -239,8 +254,8 @@ extension Quaternion {
239254 /// - `.angleAxis`
240255 /// - `.polar`
241256 /// - `.rotationVector`
242- /// - `init(angle:axis:)`
243- /// - `init(length:angle :axis)`
257+ /// - `init(length: angle:axis:)`
258+ /// - `init(length:phase :axis)`
244259 ///
245260 /// - Parameter vector: The rotation vector.
246261 ///
0 commit comments