Skip to content

Commit 44ccbd0

Browse files
markuswntrstephentyrone
authored andcommitted
Update polar initializer to assume a unit axis parameter
1 parent 231d08a commit 44ccbd0

File tree

1 file changed

+33
-21
lines changed

1 file changed

+33
-21
lines changed

Sources/QuaternionModule/Transformation.swift

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,17 @@ extension Quaternion {
168168
///
169169
/// Edge cases:
170170
/// -
171-
/// - For any `θ`, even `.infinity` or `.nan`:
171+
/// - Negative lengths are interpreted as reflecting the point through the origin, i.e.:
172+
/// ```
173+
/// Quaternion(length: -r, angle: θ, axis: axis) == -Quaternion(length: r, angle: θ, axis: axis)
174+
/// ```
175+
/// - For any `θ` and any `axis`, even `.infinity` or `.nan`:
172176
/// ```
173177
/// Quaternion(length: .zero, angle: θ, axis: axis) == .zero
174178
/// ```
175-
/// - For any `θ`, even `.infinity` or `.nan`:
179+
/// - For any `θ` and any `axis`, even `.infinity` or `.nan`:
176180
/// ```
177-
/// Quaternion(length: .infinity, angle: θ, axis: axis) == .ininfity
181+
/// Quaternion(length: .infinity, angle: θ, axis: axis) == .infinity
178182
/// ```
179183
/// - Otherwise, `θ` must be finite, or a precondition failure occurs.
180184
///
@@ -215,7 +219,7 @@ extension Quaternion {
215219
"Given axis must be of unit length."
216220
)
217221

218-
self = Quaternion(halfAngle: angle/2,unitAxis: axis).multiplied(by: length)
222+
self = Quaternion(halfAngle: angle/2, unitAxis: axis).multiplied(by: length)
219223
}
220224

221225
/// Creates a unit quaternion specified with given [rotation vector][wiki].
@@ -278,23 +282,23 @@ extension Quaternion {
278282
///
279283
/// Q = (cos(phase), axis * sin(phase)) * length
280284
///
281-
/// Given `axis` gets normalized if it is not of unit length.
285+
/// - Note: `axis` must be of unit length, or an assertion failure occurs.
282286
///
283287
/// Edge cases:
284288
/// -
285289
/// - Negative lengths are interpreted as reflecting the point through the origin, i.e.:
286290
/// ```
287-
/// Quaternion(length: -r, angle: θ, axis: axis) == Quaternion(length: -r, angle: θ, axis: axis)
291+
/// Quaternion(length: -r, phase: θ, axis: axis) == -Quaternion(length: r, phase: θ, axis: axis)
288292
/// ```
289293
/// - For any `θ` and any `axis`, even `.infinity` or `.nan`:
290294
/// ```
291-
/// Quaternion(length: .zero, angle: θ, axis: axis) == .zero
295+
/// Quaternion(length: .zero, phase: θ, axis: axis) == .zero
292296
/// ```
293297
/// - For any `θ` and any `axis`, even `.infinity` or `.nan`:
294298
/// ```
295-
/// Quaternion(length: .infinity, angle: θ, axis: axis) == .infinity
299+
/// Quaternion(length: .infinity, phase: θ, axis: axis) == .infinity
296300
/// ```
297-
/// - Otherwise, `θ` and `axis` must be finite, or a precondition failure occurs.
301+
/// - Otherwise, `θ` must be finite, or a precondition failure occurs.
298302
///
299303
/// See also:
300304
/// -
@@ -303,25 +307,33 @@ extension Quaternion {
303307
/// - `.angleAxis`
304308
/// - `.rotationVector`
305309
/// - `.polar`
306-
/// - `init(angle:axis)`
310+
/// - `init(length:angle:axis:)`
307311
/// - `init(rotation:)`
308312
///
309313
/// [wiki]: https://en.wikipedia.org/wiki/Polar_decomposition#Quaternion_polar_decomposition
310314
@inlinable
311315
public init(length: RealType, phase: RealType, axis: SIMD3<RealType>) {
312-
let axisLength: RealType = .sqrt(axis.lengthSquared)
313-
if phase.isFinite && axisLength.isNormal {
314-
self = Quaternion(
315-
halfAngle: phase,
316-
unitAxis: axis/axisLength
317-
).multiplied(by: length)
318-
} else {
319-
precondition(
320-
length.isZero || length.isInfinite,
321-
"Either angle must be finite, or length must be zero or infinite."
322-
)
316+
guard !length.isZero, length.isFinite else {
323317
self = Quaternion(length)
318+
return
324319
}
320+
321+
// Length is finite and non-zero, therefore
322+
// 1. `phase` must be finite or a precondition failure needs to occur; as
323+
// this is not representable.
324+
// 2. `axis` must be of unit length or an assertion failure occurs; while
325+
// "wrong" by definition, it is representable.
326+
precondition(
327+
phase.isFinite,
328+
"Either phase must be finite, or length must be zero or infinite."
329+
)
330+
assert(
331+
// TODO: Replace with `approximateEquality()`
332+
abs(.sqrt(axis.lengthSquared)-1) < max(.sqrt(axis.lengthSquared), 1)*RealType.ulpOfOne.squareRoot(),
333+
"Given axis must be of unit length."
334+
)
335+
336+
self = Quaternion(halfAngle: phase, unitAxis: axis).multiplied(by: length)
325337
}
326338

327339
/// Transforms a vector by this quaternion.

0 commit comments

Comments
 (0)