Documentation
¶
Overview ¶
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.
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) } }
Output: 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) }
Output: (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) }
Output: (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) }
Output: (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) }
Output: (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
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
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 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 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 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)