Skip to content
This repository was archived by the owner on Aug 2, 2024. It is now read-only.

Commit dffa709

Browse files
committed
Add optimized routines for manipulating directions (ideal points)
1 parent 9ac640c commit dffa709

File tree

9 files changed

+378
-7
lines changed

9 files changed

+378
-7
lines changed

docs/api/kln::direction.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,33 @@ struct kln::direction
55
: public kln::entity< 0b1000 >
66
```
77

8+
Directions in $\mathbf{P}(\mathbb{R}^3_{3, 0, 1})$ are represented using points at infinity (homogeneous coordinate 0). Having a homogeneous coordinate of zero ensures that directions are translation-invariant.
9+
810
### Summary
911

1012
Members | Descriptions
1113
--------------------------------|---------------------------------------------
1214
`public ` [`direction`](#structkln_1_1direction_1a6d22510e72f67516a65a2ee39a5591a3)`() = default` |
13-
`public ` [`direction`](#structkln_1_1direction_1a8fc796733d32a69230136b8376003fca)`(float x,float y,float z) noexcept` |
15+
`public ` [`direction`](#structkln_1_1direction_1a8fc796733d32a69230136b8376003fca)`(float x,float y,float z) noexcept` | Create a normalized direction.
1416
`public ` [`direction`](#structkln_1_1direction_1a81cd76b26d928f02e830d8c0175157d6)`(` [`entity`](/Klein/api/kln::entity#structkln_1_1entity)`< 0b1000 > const & e) noexcept` |
1517
`public constexpr float ` [`operator[]`](#structkln_1_1direction_1a8fa09342bff2583a7f29d04adf6dd3c2)`(size_t i) const noexcept` |
1618
`public constexpr float & ` [`operator[]`](#structkln_1_1direction_1a67a82ca405563ca75b7f8a84842e42c0)`(size_t i) noexcept` |
1719
`public float ` [`x`](#structkln_1_1direction_1a721087c2056a33cd780c678ae0ec39dd)`() const noexcept` |
20+
`public float & ` [`x`](#structkln_1_1direction_1a161c9821553a28babae050e37a0d1096)`() noexcept` |
1821
`public float ` [`y`](#structkln_1_1direction_1a40bc7052a639d57afde7a860c1631638)`() const noexcept` |
22+
`public float & ` [`y`](#structkln_1_1direction_1a582345212cc5165ac7f3b1c5483dd17c)`() noexcept` |
1923
`public float ` [`z`](#structkln_1_1direction_1a1e16d3ed0ff945d0d1cbf1e6458d334b)`() const noexcept` |
24+
`public float & ` [`z`](#structkln_1_1direction_1aff096710eaf0b26832f00b5313bdac75)`() noexcept` |
25+
`public void ` [`normalize`](#structkln_1_1direction_1a143cd2cfeb94860ce1d47b9735064802)`() noexcept` | Normalize this direction by dividing all components by the square magnitude
2026

2127
### Members
2228

2329
#### [direction](#structkln_1_1direction_1a6d22510e72f67516a65a2ee39a5591a3)() = default {#structkln_1_1direction_1a6d22510e72f67516a65a2ee39a5591a3}
2430

2531
#### [direction](#structkln_1_1direction_1a8fc796733d32a69230136b8376003fca)(float x,float y,float z) noexcept {#structkln_1_1direction_1a8fc796733d32a69230136b8376003fca}
2632

33+
Create a normalized direction.
34+
2735
#### [direction](#structkln_1_1direction_1a81cd76b26d928f02e830d8c0175157d6)( [entity](/Klein/api/kln::entity#structkln_1_1entity)< 0b1000 > const & e) noexcept {#structkln_1_1direction_1a81cd76b26d928f02e830d8c0175157d6}
2836

2937
#### float [operator[]](#structkln_1_1direction_1a8fa09342bff2583a7f29d04adf6dd3c2)(size_t i) const noexcept {#structkln_1_1direction_1a8fa09342bff2583a7f29d04adf6dd3c2}
@@ -32,7 +40,22 @@ struct kln::direction
3240

3341
#### float [x](#structkln_1_1direction_1a721087c2056a33cd780c678ae0ec39dd)() const noexcept {#structkln_1_1direction_1a721087c2056a33cd780c678ae0ec39dd}
3442

43+
#### float & [x](#structkln_1_1direction_1a161c9821553a28babae050e37a0d1096)() noexcept {#structkln_1_1direction_1a161c9821553a28babae050e37a0d1096}
44+
3545
#### float [y](#structkln_1_1direction_1a40bc7052a639d57afde7a860c1631638)() const noexcept {#structkln_1_1direction_1a40bc7052a639d57afde7a860c1631638}
3646

47+
#### float & [y](#structkln_1_1direction_1a582345212cc5165ac7f3b1c5483dd17c)() noexcept {#structkln_1_1direction_1a582345212cc5165ac7f3b1c5483dd17c}
48+
3749
#### float [z](#structkln_1_1direction_1a1e16d3ed0ff945d0d1cbf1e6458d334b)() const noexcept {#structkln_1_1direction_1a1e16d3ed0ff945d0d1cbf1e6458d334b}
3850

51+
#### float & [z](#structkln_1_1direction_1aff096710eaf0b26832f00b5313bdac75)() noexcept {#structkln_1_1direction_1aff096710eaf0b26832f00b5313bdac75}
52+
53+
#### void [normalize](#structkln_1_1direction_1a143cd2cfeb94860ce1d47b9735064802)() noexcept {#structkln_1_1direction_1a143cd2cfeb94860ce1d47b9735064802}
54+
55+
Normalize this direction by dividing all components by the square magnitude
56+
57+
!!! tip
58+
Point normalization divides the coordinates by the quantity
59+
a^2 + b^2 + c^2. This is done using the `rcpps` instruction with a
60+
maximum relative error of $1.5\times 2^{-12}$.
61+

docs/api/kln::motor.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,42 @@ struct kln::motor
77

88
A `motor` represents a kinematic motion in our algebra. From [Chasles' theorem](https://en.wikipedia.org/wiki/Chasles%27_theorem_(kinematics)), we know that any rigid body displacement can be produced by a translation along a line, followed or preceded by a rotation about an axis parallel to that line. The motor algebra is isomorphic to the dual quaternions but exists here in the same algebra as all the other geometric entities and actions at our disposal. Operations such as composing a motor with a rotor or translator are possible for example. The primary benefit to using a motor over its corresponding matrix operation is twofold. First, you get the benefit of numerical stability when composing multiple actions via the geometric product (`*` ). Second, because the motors constitute a continuous group, they are amenable to smooth interpolation and differentiation.
99

10+
!!! example
11+
```c++
12+
// Create a rotor representing a pi/2 rotation about the z-axis
13+
// Normalization is done automatically
14+
rotor r{M_PI * 0.5f, 0.f, 0.f, 1.f};
15+
16+
// Create a translator that represents a translation of 1 unit
17+
// in the yz-direction. Normalization is done automatically.
18+
translator t{1.f, 0.f, 1.f, 1.f};
19+
20+
// Create a motor that combines the action of the rotation and
21+
// translation above.
22+
motor m = r * t;
23+
24+
// Initialize a point at (1, 3, 2)
25+
kln::point p1{1.f, 3.f, 2.f};
26+
27+
// Translate p1 and rotate it to create a new point p2
28+
kln::point p2 = m(p1);
29+
```
30+
31+
32+
Motors can be multiplied to one another with the `*` operator to create a new motor equivalent to the application of each factor.
33+
34+
!!! example
35+
```c++
36+
// Suppose we have 3 motors m1, m2, and m3
37+
38+
// The motor m created here represents the combined action of m1,
39+
// m2, and m3.
40+
kln::motor m = m3 * m2 * m1;
41+
```
42+
43+
44+
The same `*` operator can be used to compose the motor's action with other translators and rotors.
45+
1046
A demonstration of using the exponential and logarithmic map to blend between two motors is provided in a test case [here](https://github.com/jeremyong/Klein/blob/master/test/test_exp_log.cpp#L48).
1147

1248
### Summary
@@ -27,6 +63,8 @@ A demonstration of using the exponential and logarithmic map to blend between tw
2763
`public ` [`point`](/Klein/api/kln::point#structkln_1_1point)` KLN_VEC_CALL ` [`operator()`](#structkln_1_1motor_1a7ae8d73c558d1f6581df3a4b50fb2a40)`(` [`point`](/Klein/api/kln::point#structkln_1_1point)` const & p) const noexcept` | Conjugates a point $p$ with this motor and returns the result $mp\widetilde{m}$.
2864
`public void KLN_VEC_CALL ` [`operator()`](#structkln_1_1motor_1ab14cf440bf8281b4a56cc338c568156b)`(` [`point`](/Klein/api/kln::point#structkln_1_1point)` * in,` [`point`](/Klein/api/kln::point#structkln_1_1point)` * out,size_t count) const noexcept` | Conjugates an array of points with this motor in the input array and stores the result in the output array. Aliasing is only permitted when `in == out` (in place motor application).
2965
`public ` [`point`](/Klein/api/kln::point#structkln_1_1point)` KLN_VEC_CALL ` [`operator()`](#structkln_1_1motor_1afa77e3a1d5a8f28bf6eee4de9e174489)`(` [`origin`](/Klein/api/kln::origin#structkln_1_1origin)`) const noexcept` | Conjugates the origin $O$ with this motor and returns the result $mO\widetilde{m}$.
66+
`public ` [`direction`](/Klein/api/kln::direction#structkln_1_1direction)` KLN_VEC_CALL ` [`operator()`](#structkln_1_1motor_1ac8debbfe23b80affa7bf9ef7e0dffc1f)`(` [`direction`](/Klein/api/kln::direction#structkln_1_1direction)` const & d) const noexcept` | Conjugates a direction $d$ with this motor and returns the result $md\widetilde{m}$.
67+
`public void KLN_VEC_CALL ` [`operator()`](#structkln_1_1motor_1aff385bad1df3b7ee29439b56bec43376)`(` [`direction`](/Klein/api/kln::direction#structkln_1_1direction)` * in,` [`direction`](/Klein/api/kln::direction#structkln_1_1direction)` * out,size_t count) const noexcept` | Conjugates an array of directions with this motor in the input array and stores the result in the output array. Aliasing is only permitted when `in == out` (in place motor application).
3068

3169
### Members
3270

@@ -97,3 +135,20 @@ Conjugates an array of points with this motor in the input array and stores the
97135

98136
Conjugates the origin $O$ with this motor and returns the result $mO\widetilde{m}$.
99137

138+
#### [direction](/Klein/api/kln::direction#structkln_1_1direction) KLN_VEC_CALL [operator()](#structkln_1_1motor_1ac8debbfe23b80affa7bf9ef7e0dffc1f)( [direction](/Klein/api/kln::direction#structkln_1_1direction) const & d) const noexcept {#structkln_1_1motor_1ac8debbfe23b80affa7bf9ef7e0dffc1f}
139+
140+
Conjugates a direction $d$ with this motor and returns the result $md\widetilde{m}$.
141+
142+
The cost of this operation is the same as the application of a rotor due to the translational invariance of directions (points at infinity).
143+
144+
#### void KLN_VEC_CALL [operator()](#structkln_1_1motor_1aff385bad1df3b7ee29439b56bec43376)( [direction](/Klein/api/kln::direction#structkln_1_1direction) * in, [direction](/Klein/api/kln::direction#structkln_1_1direction) * out,size_t count) const noexcept {#structkln_1_1motor_1aff385bad1df3b7ee29439b56bec43376}
145+
146+
Conjugates an array of directions with this motor in the input array and stores the result in the output array. Aliasing is only permitted when `in == out` (in place motor application).
147+
148+
The cost of this operation is the same as the application of a rotor due to the translational invariance of directions (points at infinity).
149+
150+
!!! tip
151+
When applying a motor to a list of tightly packed directions, this
152+
routine will be *significantly faster* than applying the motor to
153+
each direction individually.
154+

docs/api/kln::rotor.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,44 @@ struct kln::rotor
55
: public kln::entity< 0b10 >
66
```
77

8+
The rotor is an entity that represents a rigid rotation about an axis. To apply the rotor to a supported entity, the call operator is available.
9+
10+
!!! example
11+
```c++
12+
// Initialize a point at (1, 3, 2)
13+
kln::point p{1.f, 3.f, 2.f};
14+
15+
// Create a normalized rotor representing a pi/2 radian
16+
// rotation about the xz-axis.
17+
kln::rotor r{M_PI * 0.5f, 1.f, 0.f, 1.f};
18+
19+
// Rotate our point using the created rotor
20+
kln::point rotated = r(p);
21+
```
22+
We can rotate lines and planes as well using the rotor's call operator.
23+
24+
25+
Rotors can be multiplied to one another with the `*` operator to create a new rotor equivalent to the application of each factor.
26+
27+
!!! example
28+
```c++
29+
// Create a normalized rotor representing a $\frac{\pi}{2}$ radian
30+
// rotation about the xz-axis.
31+
kln::rotor r1{M_PI * 0.5f, 1.f, 0.f, 1.f};
32+
33+
// Create a second rotor representing a $\frac{\pi}{3}$ radian
34+
// rotation about the yz-axis.
35+
kln::rotor r2{M_PI / 3.f, 0.f, 1.f, 1.f};
36+
37+
// Use the geometric product to create a rotor equivalent to first
38+
// applying r1, then applying r2. Note that the order of the
39+
// operands here is significant.
40+
kln::rotor r3 = r2 * r1;
41+
```
42+
43+
44+
The same `*` operator can be used to compose the rotor's action with other translators and motors.
45+
846
### Summary
947

1048
Members | Descriptions
@@ -21,6 +59,8 @@ struct kln::rotor
2159
`public void KLN_VEC_CALL ` [`operator()`](#structkln_1_1rotor_1aad794881fa0c11fb05486986032a431a)`(` [`line`](/Klein/api/kln::line#structkln_1_1line)` * in,` [`line`](/Klein/api/kln::line#structkln_1_1line)` * out,size_t count) const noexcept` | Conjugates an array of lines with this rotor in the input array and stores the result in the output array. Aliasing is only permitted when `in == out` (in place rotor application).
2260
`public ` [`point`](/Klein/api/kln::point#structkln_1_1point)` KLN_VEC_CALL ` [`operator()`](#structkln_1_1rotor_1a5aabb4caa402fb5793807fe1d8cec199)`(` [`point`](/Klein/api/kln::point#structkln_1_1point)` const & p) const noexcept` | Conjugates a point $p$ with this rotor and returns the result $rp\widetilde{r}$.
2361
`public void KLN_VEC_CALL ` [`operator()`](#structkln_1_1rotor_1aa91e4024d7368fbd14a93ead29905aad)`(` [`point`](/Klein/api/kln::point#structkln_1_1point)` * in,` [`point`](/Klein/api/kln::point#structkln_1_1point)` * out,size_t count) const noexcept` | Conjugates an array of points with this rotor in the input array and stores the result in the output array. Aliasing is only permitted when `in == out` (in place rotor application).
62+
`public ` [`direction`](/Klein/api/kln::direction#structkln_1_1direction)` KLN_VEC_CALL ` [`operator()`](#structkln_1_1rotor_1a80935add9a99987a59879df1aa868b43)`(` [`direction`](/Klein/api/kln::direction#structkln_1_1direction)` const & d) const noexcept` | Conjugates a direction $d$ with this rotor and returns the result $rd\widetilde{r}$.
63+
`public void KLN_VEC_CALL ` [`operator()`](#structkln_1_1rotor_1a185ca5be93f00396e5e0de9a05561999)`(` [`direction`](/Klein/api/kln::direction#structkln_1_1direction)` * in,` [`direction`](/Klein/api/kln::direction#structkln_1_1direction)` * out,size_t count) const noexcept` | Conjugates an array of directions with this rotor in the input array and stores the result in the output array. Aliasing is only permitted when `in == out` (in place rotor application).
2464

2565
### Members
2666

@@ -89,3 +129,16 @@ Conjugates an array of points with this rotor in the input array and stores the
89129
routine will be *significantly faster* than applying the rotor to
90130
each point individually.
91131

132+
#### [direction](/Klein/api/kln::direction#structkln_1_1direction) KLN_VEC_CALL [operator()](#structkln_1_1rotor_1a80935add9a99987a59879df1aa868b43)( [direction](/Klein/api/kln::direction#structkln_1_1direction) const & d) const noexcept {#structkln_1_1rotor_1a80935add9a99987a59879df1aa868b43}
133+
134+
Conjugates a direction $d$ with this rotor and returns the result $rd\widetilde{r}$.
135+
136+
#### void KLN_VEC_CALL [operator()](#structkln_1_1rotor_1a185ca5be93f00396e5e0de9a05561999)( [direction](/Klein/api/kln::direction#structkln_1_1direction) * in, [direction](/Klein/api/kln::direction#structkln_1_1direction) * out,size_t count) const noexcept {#structkln_1_1rotor_1a185ca5be93f00396e5e0de9a05561999}
137+
138+
Conjugates an array of directions with this rotor in the input array and stores the result in the output array. Aliasing is only permitted when `in == out` (in place rotor application).
139+
140+
!!! tip
141+
When applying a rotor to a list of tightly packed directions, this
142+
routine will be *significantly faster* than applying the rotor to
143+
each direction individually.
144+

docs/api/kln::translator.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,38 @@ struct kln::translator
55
: public kln::entity< 0b110 >
66
```
77

8+
A translator represents a rigid-body displacement along a normalized axis. To apply the translator to a supported entity, the call operator is available.
9+
10+
!!! example
11+
```c++
12+
// Initialize a point at (1, 3, 2)
13+
kln::point p{1.f, 3.f, 2.f};
14+
15+
// Create a normalized translator representing a 4-unit
16+
// displacement along the xz-axis.
17+
kln::translator r{4.f, 1.f, 0.f, 1.f};
18+
19+
// Displace our point using the created translator
20+
kln::point translated = r(p);
21+
```
22+
We can translate lines and planes as well using the translator's call
23+
operator.
24+
25+
26+
Translators can be multiplied to one another with the `*` operator to create a new translator equivalent to the application of each factor.
27+
28+
!!! example
29+
```c++
30+
// Suppose we have 3 translators t1, t2, and t3
31+
32+
// The translator t created here represents the combined action of
33+
// t1, t2, and t3.
34+
kln::translator t = t3 * t2 * t1;
35+
```
36+
37+
38+
The same `*` operator can be used to compose the translator's action with other rotors and motors.
39+
840
### Summary
941

1042
Members | Descriptions

public/klein/direction.hpp

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@
44

55
namespace kln
66
{
7-
// Ideal points will have a 0 for the homogeneous coordinate
8-
// x*e_032 + y*e_013 + z*e_021
7+
/// Directions in $\mathbf{P}(\mathbb{R}^3_{3, 0, 1})$ are represented using
8+
/// points at infinity (homogeneous coordinate 0). Having a homogeneous
9+
/// coordinate of zero ensures that directions are translation-invariant.
910
struct direction final : public entity<0b1000>
1011
{
1112
direction() = default;
13+
14+
/// Create a normalized direction
1215
direction(float x, float y, float z) noexcept
1316
{
1417
parts[0].reg = _mm_set_ps(x, y, z, 0.f);
18+
normalize();
1519
}
1620

1721
// Provide conversion operator from parent class entity
@@ -39,14 +43,45 @@ struct direction final : public entity<0b1000>
3943
return parts[0].data[3];
4044
}
4145

46+
float& x() noexcept
47+
{
48+
return parts[0].data[3];
49+
}
50+
4251
float y() const noexcept
4352
{
4453
return parts[0].data[2];
4554
}
4655

56+
float& y() noexcept
57+
{
58+
return parts[0].data[2];
59+
}
60+
4761
float z() const noexcept
4862
{
4963
return parts[0].data[1];
5064
}
65+
66+
float& z() noexcept
67+
{
68+
return parts[0].data[1];
69+
}
70+
71+
/// Normalize this direction by dividing all components by the square
72+
/// magnitude
73+
///
74+
/// !!! tip
75+
///
76+
/// Direction normalization divides the coordinates by the quantity
77+
/// a^2 + b^2 + c^2. This is done using the `rcpps` instruction with a
78+
/// maximum relative error of $1.5\times 2^{-12}$.
79+
void normalize() noexcept
80+
{
81+
// Fast reciprocal operation to divide by a^2 + b^2 + c^2. The maximum
82+
// relative error for the rcp approximation is 1.5*2^-12 (~.00036621)
83+
__m128 tmp = _mm_rcp_ps(_mm_dp_ps(p3(), p3(), 0b11101110));
84+
parts[0].reg = _mm_mul_ps(parts[0].reg, tmp);
85+
}
5186
};
52-
}
87+
} // namespace kln

0 commit comments

Comments
 (0)