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 }