gonum.org/v1/gonum@v0.14.0/num/dualquat/dual_example_test.go (about)

     1  // Copyright ©2018 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package dualquat_test
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  
    11  	"gonum.org/v1/gonum/floats/scalar"
    12  	"gonum.org/v1/gonum/num/dualquat"
    13  	"gonum.org/v1/gonum/num/quat"
    14  )
    15  
    16  // point is a 3-dimensional point/vector.
    17  type point struct {
    18  	x, y, z float64
    19  }
    20  
    21  // raise raises the dimensionality of a point to a quaternion.
    22  func raise(p point) quat.Number {
    23  	return quat.Number{Imag: p.x, Jmag: p.y, Kmag: p.z}
    24  }
    25  
    26  // raiseDual raises the dimensionality of a point to a dual quaternion.
    27  func raiseDual(p point) dualquat.Number {
    28  	return dualquat.Number{
    29  		Real: quat.Number{Real: 1},
    30  		Dual: raise(p),
    31  	}
    32  }
    33  
    34  // transform performs the transformation of p by the given dual quaternions.
    35  // The transformations are normalized to unit vectors.
    36  func transform(p point, by ...dualquat.Number) point {
    37  	if len(by) == 0 {
    38  		return p
    39  	}
    40  
    41  	// Ensure the modulus of by is correctly scaled.
    42  	for i := range by {
    43  		if len := quat.Abs(by[i].Real); len != 1 {
    44  			by[i].Real = quat.Scale(1/len, by[i].Real)
    45  		}
    46  	}
    47  
    48  	// Perform the transformations.
    49  	q := by[0]
    50  	for _, o := range by[1:] {
    51  		q = dualquat.Mul(o, q)
    52  	}
    53  	pp := dualquat.Mul(dualquat.Mul(q, raiseDual(p)), dualquat.Conj(q))
    54  
    55  	// Extract the point.
    56  	return point{x: pp.Dual.Imag, y: pp.Dual.Jmag, z: pp.Dual.Kmag}
    57  }
    58  
    59  func Example() {
    60  	// Translate a 1×1×1 cube by [3, 4, 5] and rotate it 120° around the
    61  	// diagonal vector [1, 1, 1].
    62  	fmt.Println("cube:")
    63  
    64  	// Construct a displacement.
    65  	displace := dualquat.Number{
    66  		Real: quat.Number{Real: 1},
    67  		Dual: quat.Scale(0.5, raise(point{3, 4, 5})),
    68  	}
    69  
    70  	// Construct a rotations.
    71  	alpha := 2 * math.Pi / 3
    72  	axis := raise(point{1, 1, 1})
    73  	rotate := dualquat.Number{Real: axis}
    74  	rotate.Real = quat.Scale(math.Sin(alpha/2)/quat.Abs(rotate.Real), rotate.Real)
    75  	rotate.Real.Real += math.Cos(alpha / 2)
    76  
    77  	for i, p := range []point{
    78  		{x: 0, y: 0, z: 0},
    79  		{x: 0, y: 0, z: 1},
    80  		{x: 0, y: 1, z: 0},
    81  		{x: 0, y: 1, z: 1},
    82  		{x: 1, y: 0, z: 0},
    83  		{x: 1, y: 0, z: 1},
    84  		{x: 1, y: 1, z: 0},
    85  		{x: 1, y: 1, z: 1},
    86  	} {
    87  		pp := transform(p,
    88  			displace, rotate,
    89  		)
    90  
    91  		// Clean up floating point error for clarity.
    92  		pp.x = scalar.Round(pp.x, 2)
    93  		pp.y = scalar.Round(pp.y, 2)
    94  		pp.z = scalar.Round(pp.z, 2)
    95  
    96  		fmt.Printf(" %d %+v -> %+v\n", i, p, pp)
    97  	}
    98  
    99  	// Rotate a line segment from {[2, 1, 1], [2, 1, 2]} 120° around
   100  	// the diagonal vector [1, 1, 1] at its lower end.
   101  	fmt.Println("\nline segment:")
   102  
   103  	// Construct an displacement to the origin from the lower end...
   104  	origin := dualquat.Number{
   105  		Real: quat.Number{Real: 1},
   106  		Dual: quat.Scale(0.5, raise(point{-2, -1, -1})),
   107  	}
   108  	// ... and back from the origin to the lower end.
   109  	replace := dualquat.Number{
   110  		Real: quat.Number{Real: 1},
   111  		Dual: quat.Scale(-1, origin.Dual),
   112  	}
   113  
   114  	for i, p := range []point{
   115  		{x: 2, y: 1, z: 1},
   116  		{x: 2, y: 1, z: 2},
   117  	} {
   118  		pp := transform(p,
   119  			origin,  // Displace to origin.
   120  			rotate,  // Rotate around axis.
   121  			replace, // Displace back to original location.
   122  		)
   123  
   124  		// Clean up floating point error for clarity.
   125  		pp.x = scalar.Round(pp.x, 2)
   126  		pp.y = scalar.Round(pp.y, 2)
   127  		pp.z = scalar.Round(pp.z, 2)
   128  
   129  		fmt.Printf(" %d %+v -> %+v\n", i, p, pp)
   130  	}
   131  
   132  	// Output:
   133  	//
   134  	// cube:
   135  	//  0 {x:0 y:0 z:0} -> {x:5 y:3 z:4}
   136  	//  1 {x:0 y:0 z:1} -> {x:6 y:3 z:4}
   137  	//  2 {x:0 y:1 z:0} -> {x:5 y:3 z:5}
   138  	//  3 {x:0 y:1 z:1} -> {x:6 y:3 z:5}
   139  	//  4 {x:1 y:0 z:0} -> {x:5 y:4 z:4}
   140  	//  5 {x:1 y:0 z:1} -> {x:6 y:4 z:4}
   141  	//  6 {x:1 y:1 z:0} -> {x:5 y:4 z:5}
   142  	//  7 {x:1 y:1 z:1} -> {x:6 y:4 z:5}
   143  	//
   144  	// line segment:
   145  	//  0 {x:2 y:1 z:1} -> {x:2 y:1 z:1}
   146  	//  1 {x:2 y:1 z:2} -> {x:3 y:1 z:1}
   147  }