package dualquat
import "gonum.org/v1/gonum/num/dualquat"
Package dualquat provides the dual quaternion numeric type and functions.
Dual quaternions provide a system for rigid transformation with interpolation
and blending in ℝ³. See https://www.cs.utah.edu/~ladislav/kavan06dual/kavan06dual.pdf and
https://en.wikipedia.org/wiki/Dual_quaternion for more details.
Code:play
Output: Code:play
Output: Code:play
Output: Code:play
Output: Code:play
Output:Example¶
package main
import (
"fmt"
"math"
"gonum.org/v1/gonum/floats/scalar"
"gonum.org/v1/gonum/num/dualquat"
"gonum.org/v1/gonum/num/quat"
)
// point is a 3-dimensional point/vector.
type point struct {
x, y, z float64
}
// raise raises the dimensionality of a point to a quaternion.
func raise(p point) quat.Number {
return quat.Number{Imag: p.x, Jmag: p.y, Kmag: p.z}
}
// raiseDual raises the dimensionality of a point to a dual quaternion.
func raiseDual(p point) dualquat.Number {
return dualquat.Number{
Real: quat.Number{Real: 1},
Dual: raise(p),
}
}
// transform performs the transformation of p by the given dual quaternions.
// The transformations are normalized to unit vectors.
func transform(p point, by ...dualquat.Number) point {
if len(by) == 0 {
return p
}
// Ensure the modulus of by is correctly scaled.
for i := range by {
if len := quat.Abs(by[i].Real); len != 1 {
by[i].Real = quat.Scale(1/len, by[i].Real)
}
}
// Perform the transformations.
q := by[0]
for _, o := range by[1:] {
q = dualquat.Mul(o, q)
}
pp := dualquat.Mul(dualquat.Mul(q, raiseDual(p)), dualquat.Conj(q))
// Extract the point.
return point{x: pp.Dual.Imag, y: pp.Dual.Jmag, z: pp.Dual.Kmag}
}
func main() {
// Translate a 1×1×1 cube by [3, 4, 5] and rotate it 120° around the
// diagonal vector [1, 1, 1].
fmt.Println("cube:")
// Construct a displacement.
displace := dualquat.Number{
Real: quat.Number{Real: 1},
Dual: quat.Scale(0.5, raise(point{3, 4, 5})),
}
// Construct a rotations.
alpha := 2 * math.Pi / 3
axis := raise(point{1, 1, 1})
rotate := dualquat.Number{Real: axis}
rotate.Real = quat.Scale(math.Sin(alpha/2)/quat.Abs(rotate.Real), rotate.Real)
rotate.Real.Real += math.Cos(alpha / 2)
for i, p := range []point{
{x: 0, y: 0, z: 0},
{x: 0, y: 0, z: 1},
{x: 0, y: 1, z: 0},
{x: 0, y: 1, z: 1},
{x: 1, y: 0, z: 0},
{x: 1, y: 0, z: 1},
{x: 1, y: 1, z: 0},
{x: 1, y: 1, z: 1},
} {
pp := transform(p,
displace, rotate,
)
// Clean up floating point error for clarity.
pp.x = scalar.Round(pp.x, 2)
pp.y = scalar.Round(pp.y, 2)
pp.z = scalar.Round(pp.z, 2)
fmt.Printf(" %d %+v -> %+v\n", i, p, pp)
}
// Rotate a line segment from {[2, 1, 1], [2, 1, 2]} 120° around
// the diagonal vector [1, 1, 1] at its lower end.
fmt.Println("\nline segment:")
// Construct an displacement to the origin from the lower end...
origin := dualquat.Number{
Real: quat.Number{Real: 1},
Dual: quat.Scale(0.5, raise(point{-2, -1, -1})),
}
// ... and back from the origin to the lower end.
replace := dualquat.Number{
Real: quat.Number{Real: 1},
Dual: quat.Scale(-1, origin.Dual),
}
for i, p := range []point{
{x: 2, y: 1, z: 1},
{x: 2, y: 1, z: 2},
} {
pp := transform(p,
origin, // Displace to origin.
rotate, // Rotate around axis.
replace, // Displace back to original location.
)
// Clean up floating point error for clarity.
pp.x = scalar.Round(pp.x, 2)
pp.y = scalar.Round(pp.y, 2)
pp.z = scalar.Round(pp.z, 2)
fmt.Printf(" %d %+v -> %+v\n", i, p, pp)
}
}
cube:
0 {x:0 y:0 z:0} -> {x:5 y:3 z:4}
1 {x:0 y:0 z:1} -> {x:6 y:3 z:4}
2 {x:0 y:1 z:0} -> {x:5 y:3 z:5}
3 {x:0 y:1 z:1} -> {x:6 y:3 z:5}
4 {x:1 y:0 z:0} -> {x:5 y:4 z:4}
5 {x:1 y:0 z:1} -> {x:6 y:4 z:4}
6 {x:1 y:1 z:0} -> {x:5 y:4 z:5}
7 {x:1 y:1 z:1} -> {x:6 y:4 z:5}
line segment:
0 {x:2 y:1 z:1} -> {x:2 y:1 z:1}
1 {x:2 y:1 z:2} -> {x:3 y:1 z:1}
Example (Displace)¶
package main
import (
"fmt"
"gonum.org/v1/gonum/num/dualquat"
"gonum.org/v1/gonum/num/quat"
)
func main() {
// Displace a point [3, 4, 5] by [4, 2, 6].
// See http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/other/dualQuaternion/index.htm
// Point to be transformed in the dual imaginary vector.
p := dualquat.Number{Real: quat.Number{Real: 1}, Dual: quat.Number{Imag: 3, Jmag: 4, Kmag: 5}}
// Displacement vector, half [4, 2, 6], in the dual imaginary vector.
d := dualquat.Number{Real: quat.Number{Real: 1}, Dual: quat.Number{Imag: 2, Jmag: 1, Kmag: 3}}
fmt.Println(dualquat.Mul(dualquat.Mul(d, p), dualquat.Conj(d)).Dual)
}
(0+7i+6j+11k)
Example (DisplaceAndRotate)¶
package main
import (
"fmt"
"gonum.org/v1/gonum/num/dualquat"
"gonum.org/v1/gonum/num/quat"
)
func main() {
// Displace a point [3, 4, 5] by [4, 2, 6] and then rotate
// by 180° around the x axis.
// See http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/other/dualQuaternion/index.htm
// Point to be transformed in the dual imaginary vector.
p := dualquat.Number{Real: quat.Number{Real: 1}, Dual: quat.Number{Imag: 3, Jmag: 4, Kmag: 5}}
// Displacement vector, half [4, 2, 6], in the dual imaginary vector.
d := dualquat.Number{Real: quat.Number{Real: 1}, Dual: quat.Number{Imag: 2, Jmag: 1, Kmag: 3}}
// Rotation in the real quaternion.
r := dualquat.Number{Real: quat.Number{Real: 0, Imag: 1}}
// Combine the rotation and displacement so
// the displacement is performed first.
q := dualquat.Mul(r, d)
fmt.Println(dualquat.Mul(dualquat.Mul(q, p), dualquat.Conj(q)).Dual)
}
(0+7i-6j-11k)
Example (Rotate)¶
package main
import (
"fmt"
"gonum.org/v1/gonum/num/dualquat"
"gonum.org/v1/gonum/num/quat"
)
func main() {
// Rotate a point [3, 4, 5] by 180° around the x axis.
// See http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/other/dualQuaternion/index.htm
// Point to be transformed in the dual imaginary vector.
p := dualquat.Number{Real: quat.Number{Real: 1}, Dual: quat.Number{Imag: 3, Jmag: 4, Kmag: 5}}
// Rotation in the real quaternion.
r := dualquat.Number{Real: quat.Number{Real: 0, Imag: 1}}
fmt.Println(dualquat.Mul(dualquat.Mul(r, p), dualquat.Conj(r)).Dual)
}
(0+3i-4j-5k)
Example (RotateAndDisplace)¶
package main
import (
"fmt"
"gonum.org/v1/gonum/num/dualquat"
"gonum.org/v1/gonum/num/quat"
)
func main() {
// Rotate a point [3, 4, 5] by 180° around the x axis and then
// displace by [4, 2, 6]
// Point to be transformed in the dual imaginary vector.
p := dualquat.Number{Real: quat.Number{Real: 1}, Dual: quat.Number{Imag: 3, Jmag: 4, Kmag: 5}}
// Displacement vector, half [4, 2, 6], in the dual imaginary vector.
d := dualquat.Number{Real: quat.Number{Real: 1}, Dual: quat.Number{Imag: 2, Jmag: 1, Kmag: 3}}
// Rotation in the real quaternion.
r := dualquat.Number{Real: quat.Number{Real: 0, Imag: 1}}
// Combine the rotation and displacement so
// the rotations is performed first.
q := dualquat.Mul(d, r)
fmt.Println(dualquat.Mul(dualquat.Mul(q, p), dualquat.Conj(q)).Dual)
}
(0+7i-2j+1k)
Index ¶
- func Abs(d Number) dual.Number
- type Number
- func Add(x, y Number) Number
- func Conj(d Number) Number
- func ConjDual(d Number) Number
- func ConjQuat(d Number) Number
- func Exp(d Number) Number
- func Inv(d Number) Number
- func Log(d Number) Number
- func Mul(x, y Number) Number
- func Pow(d, p Number) Number
- func PowReal(d Number, p float64) Number
- func Scale(f float64, d Number) Number
- func Sqrt(d Number) Number
- func Sub(x, y Number) Number
- func (d Number) Format(fs fmt.State, c rune)
Examples ¶
Functions ¶
func Abs ¶
Abs returns the absolute value of d.
Types ¶
type Number ¶
Number is a float64 precision dual quaternion. A dual quaternion is a hypercomplex number composed of two quaternions, q₀+q₂ϵ, where ϵ²=0, but ϵ≠0. Here, q₀ is termed the real and q₂ the dual.
func Add ¶
Add returns the sum of x and y.
func Conj ¶
Conj returns the dual quaternion conjugate of d₁+d₂ϵ, d̅₁-d̅₂ϵ.
func ConjDual ¶
ConjDual returns the dual conjugate of d₁+d₂ϵ, d₁-d₂ϵ.
func ConjQuat ¶
ConjQuat returns the quaternion conjugate of d₁+d₂ϵ, d̅₁+d̅₂ϵ.
func Exp ¶
Exp returns e**d, the base-e exponential of d.
Special cases are:
Exp(+Inf) = +Inf Exp(NaN) = NaN
Very large values overflow to 0 or +Inf. Very small values underflow to 1.
func Inv ¶
Inv returns the dual inverse of d.
func Log ¶
Log returns the natural logarithm of d.
Special cases are:
Log(+Inf) = (+Inf+0ϵ) Log(0) = (-Inf±Infϵ) Log(x < 0) = NaN Log(NaN) = NaN
func Mul ¶
Mul returns the dual product of x and y.
func Pow ¶
Pow return d**p, the base-d exponential of p.
func PowReal ¶
PowReal returns d**p, the base-d exponential of p.
Special cases are (in order):
PowReal(NaN+xϵ, ±0) = 1+NaNϵ for any x PowReal(x, ±0) = 1 for any x PowReal(1+xϵ, y) = 1+xyϵ for any y PowReal(x, 1) = x for any x PowReal(NaN+xϵ, y) = NaN+NaNϵ PowReal(x, NaN) = NaN+NaNϵ PowReal(±0, y) = ±Inf for y an odd integer < 0 PowReal(±0, -Inf) = +Inf PowReal(±0, +Inf) = +0 PowReal(±0, y) = +Inf for finite y < 0 and not an odd integer PowReal(±0, y) = ±0 for y an odd integer > 0 PowReal(±0, y) = +0 for finite y > 0 and not an odd integer PowReal(-1, ±Inf) = 1 PowReal(x+0ϵ, +Inf) = +Inf+NaNϵ for |x| > 1 PowReal(x+yϵ, +Inf) = +Inf for |x| > 1 PowReal(x, -Inf) = +0+NaNϵ for |x| > 1 PowReal(x, +Inf) = +0+NaNϵ for |x| < 1 PowReal(x+0ϵ, -Inf) = +Inf+NaNϵ for |x| < 1 PowReal(x, -Inf) = +Inf-Infϵ for |x| < 1 PowReal(+Inf, y) = +Inf for y > 0 PowReal(+Inf, y) = +0 for y < 0 PowReal(-Inf, y) = Pow(-0, -y)
func Scale ¶
Scale returns d scaled by f.
func Sqrt ¶
Sqrt returns the square root of d
Special cases are:
Sqrt(+Inf) = +Inf Sqrt(±0) = (±0+Infϵ) Sqrt(x < 0) = NaN Sqrt(NaN) = NaN
func Sub ¶
Sub returns the difference of x and y, x-y.
func (Number) Format ¶
Format implements fmt.Formatter.
Source Files ¶
doc.go dual.go dual_fike.go
- Version
- v0.15.1 (latest)
- Published
- Aug 16, 2024
- Platform
- linux/amd64
- Imports
- 5 packages
- Last checked
- 2 days ago –
Tools for package owners.