Skip to content

Commit 7b46b41

Browse files
committed
Replace argument with halfAngle
1 parent 8244763 commit 7b46b41

File tree

4 files changed

+56
-50
lines changed

4 files changed

+56
-50
lines changed

Sources/QuaternionModule/Polar.swift

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -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:argument:axis:)`.
52+
/// `init(length:halfAngle:axis:)`.
5353
@_transparent
5454
public var length: RealType {
5555
let naive = lengthSquared
@@ -70,26 +70,25 @@ extension Quaternion {
7070
/// The squared length `(r*r + x*x + y*y + z*z)`.
7171
///
7272
/// This value is highly prone to overflow or underflow.
73-
///
73+
///
7474
/// For many cases, `.magnitude` can be used instead, which is similarly
7575
/// cheap to compute and always returns a representable value.
7676
///
7777
/// This property is more efficient to compute than `length`.
7878
///
7979
/// See also `.magnitude`, `.length`, `.polar` and
80-
/// `init(length:argument:axis:)`.
80+
/// `init(length:halfAngle: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.
86+
/// The half rotation angle in radians within *[0, π]* range.
8887
///
8988
/// Edge cases:
90-
/// - If the quaternion is zero or non-finite, argument is `nan`.
89+
/// - If the quaternion is zero or non-finite, halfAngle is `nan`.
9190
@inlinable
92-
public var argument: RealType {
91+
public var halfAngle: RealType {
9392
guard isFinite else { return .nan }
9493
// A zero quaternion does not encode transformation properties.
9594
// If imaginary is zero, real must be non-zero or nan is returned.
@@ -104,61 +103,65 @@ extension Quaternion {
104103

105104
/// The [polar decomposition][wiki].
106105
///
107-
/// Returns the length of this quaternion, argument in radians of range
106+
/// Returns the length of this quaternion, halfAngle in radians of range
108107
/// *[0, π]* and the rotation axis as SIMD3 vector of unit length.
109108
///
110109
/// Edge cases:
111-
/// - If the quaternion is zero, length is `.zero` and argument and axis
110+
/// - If the quaternion is zero, length is `.zero` and halfAngle and axis
112111
/// are `nan`.
113-
/// - If the quaternion is non-finite, length is `.infinity` and argument and
112+
/// - If the quaternion is non-finite, length is `.infinity` and halfAngle and
114113
/// axis are `nan`.
115-
/// - For any length other than `.zero` or `.infinity`, if argument is zero,
114+
/// - For any length other than `.zero` or `.infinity`, if halfAngle is zero,
116115
/// axis is `nan`.
117116
///
118117
/// See also `.magnitude`, `.length`, `.lengthSquared` and
119-
/// `init(length:argument:axis:)`.
118+
/// `init(length:halfAngle:axis:)`.
120119
///
121120
/// [wiki]: https://en.wikipedia.org/wiki/Polar_decomposition#Quaternion_polar_decomposition
122-
public var polar: (length: RealType, argument: RealType, axis: SIMD3<RealType>) {
123-
(length, argument, axis)
121+
public var polar: (
122+
length: RealType,
123+
halfAngle: RealType,
124+
axis: SIMD3<RealType>
125+
) {
126+
(length, halfAngle, axis)
124127
}
125128

126129
/// Creates a new quaternion from given half rotation angle about given
127130
/// rotation axis.
128131
///
129132
/// The angle-axis values are transformed using the following equation:
130133
///
131-
/// Q = (cos(argument), unitAxis * sin(argument))
134+
/// Q = (cos(halfAngle), unitAxis * sin(halfAngle))
132135
///
133136
/// - Parameters:
134-
/// - argument: The half rotation angle
137+
/// - halfAngle: The half rotation angle
135138
/// - unitAxis: The rotation axis of unit length
136139
@usableFromInline @inline(__always)
137-
internal init(argument: RealType, unitAxis: SIMD3<RealType>) {
138-
self.init(real: .cos(argument), imaginary: unitAxis * .sin(argument))
140+
internal init(halfAngle: RealType, unitAxis: SIMD3<RealType>) {
141+
self.init(real: .cos(halfAngle), imaginary: unitAxis * .sin(halfAngle))
139142
}
140143

141144
/// Creates a quaternion specified with [polar coordinates][wiki].
142145
///
143-
/// This initializer reads given `length`, `argument` and `axis` values and
146+
/// This initializer reads given `length`, `halfAngle` and `axis` values and
144147
/// creates a quaternion of equal rotation properties and specified *length*
145148
/// using the following equation:
146149
///
147-
/// Q = (cos(argument), axis * sin(argument)) * length
150+
/// Q = (cos(halfAngle), axis * sin(halfAngle)) * length
148151
///
149152
/// Edge cases:
150153
/// - Negative lengths are interpreted as reflecting the point through the
151154
/// origin, i.e.:
152155
/// ```
153-
/// Quaternion(length: -r, argument: θ, axis: axis) == -Quaternion(length: r, argument: θ, axis: axis)
156+
/// Quaternion(length: -r, halfAngle: θ, axis: axis) == -Quaternion(length: r, halfAngle: θ, axis: axis)
154157
/// ```
155158
/// - For any `θ` and any `axis`, even `.infinity` or `.nan`:
156159
/// ```
157-
/// Quaternion(length: .zero, argument: θ, axis: axis) == .zero
160+
/// Quaternion(length: .zero, halfAngle: θ, axis: axis) == .zero
158161
/// ```
159162
/// - For any `θ` and any `axis`, even `.infinity` or `.nan`:
160163
/// ```
161-
/// Quaternion(length: .infinity, argument: θ, axis: axis) == .infinity
164+
/// Quaternion(length: .infinity, halfAngle: θ, axis: axis) == .infinity
162165
/// ```
163166
/// - Otherwise, `θ` must be finite, or a precondition failure occurs and
164167
/// `axis` must be of unit length, or an assertion failure occurs.
@@ -167,27 +170,30 @@ extension Quaternion {
167170
///
168171
/// [wiki]: https://en.wikipedia.org/wiki/Polar_decomposition#Quaternion_polar_decomposition
169172
@inlinable
170-
public init(length: RealType, argument: RealType, axis: SIMD3<RealType>) {
173+
public init(length: RealType, halfAngle: RealType, axis: SIMD3<RealType>) {
171174
guard !length.isZero, length.isFinite else {
172175
self = Quaternion(length)
173176
return
174177
}
175178

176179
// Length is finite and non-zero, therefore
177-
// 1. `argument` must be finite or a precondition failure needs to occur; as
178-
// this is not representable.
180+
// 1. `halfAngle` must be finite or a precondition failure needs to occur;
181+
// as this is not representable.
179182
// 2. `axis` must be of unit length or an assertion failure occurs; while
180183
// "wrong" by definition, it is representable.
181184
precondition(
182-
argument.isFinite,
183-
"Either argument must be finite, or length must be zero or infinite."
185+
halfAngle.isFinite,
186+
"Either halfAngle must be finite, or length must be zero or infinite."
184187
)
185188
assert(
186189
// TODO: Replace with `approximateEquality()`
187190
abs(.sqrt(axis.lengthSquared)-1) < max(.sqrt(axis.lengthSquared), 1)*RealType.ulpOfOne.squareRoot(),
188191
"Given axis must be of unit length."
189192
)
190193

191-
self = Quaternion(argument: argument, unitAxis: axis).multiplied(by: length)
194+
self = Quaternion(
195+
halfAngle: halfAngle,
196+
unitAxis: axis
197+
).multiplied(by: length)
192198
}
193199
}

Sources/QuaternionModule/Quaternion+ElementaryFunctions.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ extension Quaternion/*: ElementaryFunctions */ {
4545
// `exp(r) cos(||v||)` would not be.
4646
guard q.isFinite else { return q }
4747
let (â, θ) = q.imaginary.unitAxisAndLength
48-
let rotation = Quaternion(argument: θ, unitAxis: â)
48+
let rotation = Quaternion(halfAngle: θ, unitAxis: â)
4949
// If real < log(greatestFiniteMagnitude), then exp(real) does not overflow.
5050
// To protect ourselves against sketchy log or exp implementations in
5151
// an unknown host library, or slight rounding disagreements between
@@ -90,7 +90,7 @@ extension Quaternion/*: ElementaryFunctions */ {
9090
// so the -1 term is _always_ negligable).
9191
guard q.real < RealType.log(.greatestFiniteMagnitude) - 1 else {
9292
let halfScale = RealType.exp(q.real/2)
93-
let rotation = Quaternion(argument: θ, unitAxis: â)
93+
let rotation = Quaternion(halfAngle: θ, unitAxis: â)
9494
return rotation.multiplied(by: halfScale).multiplied(by: halfScale)
9595
}
9696
return Quaternion(
@@ -123,7 +123,7 @@ extension Quaternion/*: ElementaryFunctions */ {
123123
guard q.isFinite else { return q }
124124
let (â, θ) = q.imaginary.unitAxisAndLength
125125
guard q.real.magnitude < -RealType.log(.ulpOfOne) else {
126-
let rotation = Quaternion(argument: θ, unitAxis: â)
126+
let rotation = Quaternion(halfAngle: θ, unitAxis: â)
127127
let firstScale = RealType.exp(q.real.magnitude/2)
128128
return rotation.multiplied(by: firstScale).multiplied(by: firstScale/2)
129129
}
@@ -145,7 +145,7 @@ extension Quaternion/*: ElementaryFunctions */ {
145145
guard q.isFinite else { return q }
146146
let (â, θ) = q.imaginary.unitAxisAndLength
147147
guard q.real.magnitude < -RealType.log(.ulpOfOne) else {
148-
let rotation = Quaternion(argument: θ, unitAxis: â)
148+
let rotation = Quaternion(halfAngle: θ, unitAxis: â)
149149
let firstScale = RealType.exp(q.real.magnitude/2)
150150
let secondScale = RealType(signOf: q.real, magnitudeOf: firstScale/2)
151151
return rotation.multiplied(by: firstScale).multiplied(by: secondScale)

Sources/QuaternionModule/Transformation.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ extension Quaternion {
2424
/// [wiki]: https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Recovering_the_axis-angle_representation
2525
@inlinable
2626
public var angle: RealType {
27-
2 * argument
27+
2 * halfAngle
2828
}
2929

3030
/// The [rotation axis][wiki] of the Angle-Axis representation.
@@ -155,7 +155,7 @@ extension Quaternion {
155155
"Given axis must be of unit length."
156156
)
157157

158-
self = Quaternion(argument: angle/2, unitAxis: axis).multiplied(by: length)
158+
self = Quaternion(halfAngle: angle/2, unitAxis: axis).multiplied(by: length)
159159
}
160160

161161
/// Creates a unit quaternion specified with given [rotation vector][wiki].
@@ -196,7 +196,7 @@ extension Quaternion {
196196
public init(rotation vector: SIMD3<RealType>) {
197197
let angle: RealType = .sqrt(vector.lengthSquared)
198198
if !angle.isZero, angle.isFinite {
199-
self = Quaternion(argument: angle/2, unitAxis: vector/angle)
199+
self = Quaternion(halfAngle: angle/2, unitAxis: vector/angle)
200200
} else {
201201
self = Quaternion(angle)
202202
}

Tests/QuaternionTests/TransformationTests.swift

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,12 @@ final class TransformationTests: XCTestCase {
181181
func testPolarDecomposition<T: Real & SIMDScalar>(_ type: T.Type) {
182182
let axis = SIMD3<T>(0,-1,0)
183183

184-
let q = Quaternion<T>(length: 5, argument: .pi, axis: axis)
184+
let q = Quaternion<T>(length: 5, halfAngle: .pi, axis: axis)
185185
XCTAssertEqual(q.axis, axis)
186186
XCTAssertEqual(q.angle, .pi * 2)
187187

188188
XCTAssertEqual(q.polar.length, 5)
189-
XCTAssertEqual(q.polar.argument, .pi)
189+
XCTAssertEqual(q.polar.halfAngle, .pi)
190190
XCTAssertEqual(q.polar.axis, axis)
191191
}
192192

@@ -196,18 +196,18 @@ final class TransformationTests: XCTestCase {
196196
}
197197

198198
func testPolarDecompositionEdgeCases<T: Real & SIMDScalar>(_ type: T.Type) {
199-
XCTAssertEqual(Quaternion<T>(length: .zero, argument: .zero, axis: .zero), .zero)
200-
XCTAssertEqual(Quaternion<T>(length: .zero, argument: .infinity, axis: .infinity), .zero)
201-
XCTAssertEqual(Quaternion<T>(length: .zero, argument:-.infinity, axis:-.infinity), .zero)
202-
XCTAssertEqual(Quaternion<T>(length: .zero, argument: .nan, axis: .nan), .zero)
203-
XCTAssertEqual(Quaternion<T>(length: .infinity, argument: .zero, axis: .zero), .infinity)
204-
XCTAssertEqual(Quaternion<T>(length: .infinity, argument: .infinity, axis: .infinity), .infinity)
205-
XCTAssertEqual(Quaternion<T>(length: .infinity, argument:-.infinity, axis:-.infinity), .infinity)
206-
XCTAssertEqual(Quaternion<T>(length: .infinity, argument: .nan, axis: .infinity), .infinity)
207-
XCTAssertEqual(Quaternion<T>(length:-.infinity, argument: .zero, axis: .zero), .infinity)
208-
XCTAssertEqual(Quaternion<T>(length:-.infinity, argument: .infinity, axis: .infinity), .infinity)
209-
XCTAssertEqual(Quaternion<T>(length:-.infinity, argument:-.infinity, axis:-.infinity), .infinity)
210-
XCTAssertEqual(Quaternion<T>(length:-.infinity, argument: .nan, axis: .infinity), .infinity)
199+
XCTAssertEqual(Quaternion<T>(length: .zero, halfAngle: .zero, axis: .zero), .zero)
200+
XCTAssertEqual(Quaternion<T>(length: .zero, halfAngle: .infinity, axis: .infinity), .zero)
201+
XCTAssertEqual(Quaternion<T>(length: .zero, halfAngle:-.infinity, axis:-.infinity), .zero)
202+
XCTAssertEqual(Quaternion<T>(length: .zero, halfAngle: .nan, axis: .nan), .zero)
203+
XCTAssertEqual(Quaternion<T>(length: .infinity, halfAngle: .zero, axis: .zero), .infinity)
204+
XCTAssertEqual(Quaternion<T>(length: .infinity, halfAngle: .infinity, axis: .infinity), .infinity)
205+
XCTAssertEqual(Quaternion<T>(length: .infinity, halfAngle:-.infinity, axis:-.infinity), .infinity)
206+
XCTAssertEqual(Quaternion<T>(length: .infinity, halfAngle: .nan, axis: .infinity), .infinity)
207+
XCTAssertEqual(Quaternion<T>(length:-.infinity, halfAngle: .zero, axis: .zero), .infinity)
208+
XCTAssertEqual(Quaternion<T>(length:-.infinity, halfAngle: .infinity, axis: .infinity), .infinity)
209+
XCTAssertEqual(Quaternion<T>(length:-.infinity, halfAngle:-.infinity, axis:-.infinity), .infinity)
210+
XCTAssertEqual(Quaternion<T>(length:-.infinity, halfAngle: .nan, axis: .infinity), .infinity)
211211
}
212212

213213
func testPolarDecompositionEdgeCases() {

0 commit comments

Comments
 (0)