gonum: gonum.org/v1/gonum/num/dualquat Index | Examples | Files

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:

package main

import (
    "fmt"
    "math"

    "gonum.org/v1/gonum/floats"
    "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 = floats.Round(pp.x, 2)
        pp.y = floats.Round(pp.y, 2)
        pp.z = floats.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 = floats.Round(pp.x, 2)
        pp.y = floats.Round(pp.y, 2)
        pp.z = floats.Round(pp.z, 2)

        fmt.Printf(" %d %+v -> %+v\n", i, p, pp)
    }

}

Code:

// 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)

Code:

// 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)

Code:

// 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)

Code:

// 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

Examples

Package Files

doc.go dual.go dual_fike.go

func Abs Uses

func Abs(d Number) dual.Number

Abs returns the absolute value of d.

type Number Uses

type Number struct {
    Real, Dual quat.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 Uses

func Add(x, y Number) Number

Add returns the sum of x and y.

func Conj Uses

func Conj(d Number) Number

Conj returns the dual quaternion conjugate of d₁+d₂ϵ, d̅₁-d̅₂ϵ.

func ConjDual Uses

func ConjDual(d Number) Number

ConjDual returns the dual conjugate of d₁+d₂ϵ, d₁-d₂ϵ.

func ConjQuat Uses

func ConjQuat(d Number) Number

ConjQuat returns the quaternion conjugate of d₁+d₂ϵ, d̅₁+d̅₂ϵ.

func Exp Uses

func Exp(d Number) Number

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 Uses

func Inv(d Number) Number

Inv returns the dual inverse of d.

func Log Uses

func Log(d Number) Number

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 Uses

func Mul(x, y Number) Number

Mul returns the dual product of x and y.

func Pow Uses

func Pow(d, p Number) Number

Pow return d**p, the base-d exponential of p.

func PowReal Uses

func PowReal(d Number, p float64) Number

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 Uses

func Scale(f float64, d Number) Number

Scale returns d scaled by f.

func Sqrt Uses

func Sqrt(d Number) Number

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 Uses

func Sub(x, y Number) Number

Sub returns the difference of x and y, x-y.

func (Number) Format Uses

func (d Number) Format(fs fmt.State, c rune)

Format implements fmt.Formatter.

Package dualquat imports 5 packages (graph) and is imported by 1 packages. Updated 2019-06-06. Refresh now. Tools for package owners.