Version: v0.12.0 Latest Latest This package is not in the latest version of its module.

Go to latest
Published: Sep 2, 2022 License: BSD-3-Clause

## Documentation ¶

### Overview ¶

Package r3 provides 3D vectors and boxes and operations on them.

### Constants ¶

This section is empty.

### Variables ¶

This section is empty.

### Functions ¶

#### func Cos ¶ added in v0.8.0

`func Cos(p, q Vec) float64`

Cos returns the cosine of the opening angle between p and q.

#### func Divergence ¶ added in v0.12.0

`func Divergence(p, step Vec, field func(Vec) Vec) float64`

Divergence returns the divergence of the vector field at the point p, approximated using finite differences with the given step sizes.

#### func Dot ¶ added in v0.9.0

`func Dot(p, q Vec) float64`

Dot returns the dot product p·q.

#### func Norm ¶ added in v0.8.0

`func Norm(p Vec) float64`

Norm returns the Euclidean norm of p

```|p| = sqrt(p_x^2 + p_y^2 + p_z^2).
```

#### func Norm2 ¶ added in v0.8.0

`func Norm2(p Vec) float64`

Norm2 returns the Euclidean squared norm of p

```|p|^2 = p_x^2 + p_y^2 + p_z^2.
```

### Types ¶

#### type Box ¶

```type Box struct {
Min, Max Vec
}```

Box is a 3D bounding box. Well formed Boxes Min components are smaller than Max components.

#### func NewBox ¶ added in v0.12.0

`func NewBox(x0, y0, z0, x1, y1, z1 float64) Box`

NewBox is shorthand for Box{Min:Vec{x0,y0,z0}, Max:Vec{x1,y1,z1}}. The sides are swapped so that the resulting Box is well formed.

#### func (Box) Add ¶ added in v0.12.0

`func (a Box) Add(v Vec) Box`

Add adds v to the bounding box components. It is the equivalent of translating the Box by v.

#### func (Box) Canon ¶ added in v0.12.0

`func (a Box) Canon() Box`

Canon returns the canonical version of a. The returned Box has minimum and maximum coordinates swapped if necessary so that it is well-formed.

#### func (Box) Center ¶ added in v0.12.0

`func (a Box) Center() Vec`

Center returns the center of the Box.

#### func (Box) Contains ¶ added in v0.12.0

`func (a Box) Contains(v Vec) bool`

Contains returns true if v is contained within the bounds of the Box.

#### func (Box) Empty ¶ added in v0.12.0

`func (a Box) Empty() bool`

IsEmpty returns true if a Box's volume is zero or if a Min component is greater than its Max component.

#### func (Box) Scale ¶ added in v0.12.0

`func (a Box) Scale(scale Vec) Box`

Scale returns a new Box scaled by a size vector around its center. The scaling is done element wise which is to say the Box's X dimension is scaled by scale.X. Negative elements of scale are interpreted as zero.

#### func (Box) Size ¶ added in v0.12.0

`func (a Box) Size() Vec`

Size returns the size of the Box.

#### func (Box) Union ¶ added in v0.12.0

`func (a Box) Union(b Box) Box`

Union returns a box enclosing both the receiver and argument Boxes.

#### func (Box) Vertices ¶ added in v0.12.0

`func (a Box) Vertices() []Vec`

Vertices returns a slice of the 8 vertices corresponding to each of the Box's corners.

Ordering of vertices 0-3 is CCW in the XY plane starting at box minimum. Ordering of vertices 4-7 is CCW in the XY plane starting at box minimum for X and Y values and maximum Z value.

Edges for the box can be constructed with the following indices:

```edges := int{
{0, 1}, {1, 2}, {2, 3}, {3, 0},
{4, 5}, {5, 6}, {6, 7}, {7, 4},
{0, 4}, {1, 5}, {2, 6}, {3, 7},
}
```

#### type Mat ¶ added in v0.11.0

```type Mat struct {
// contains filtered or unexported fields
}```

Mat represents a 3×3 matrix. Useful for rotation matrices and such. The zero value is usable as the 3×3 zero matrix.

#### func Eye ¶ added in v0.11.0

`func Eye() *Mat`

Eye returns the 3×3 Identity matrix

#### func NewMat ¶ added in v0.11.0

`func NewMat(val []float64) *Mat`

NewMat returns a new 3×3 matrix Mat type and populates its elements with values passed as argument in row-major form. If val argument is nil then NewMat returns a matrix filled with zeros.

#### func Skew ¶ added in v0.11.0

`func Skew(v Vec) (M *Mat)`

Skew returns the 3×3 skew symmetric matrix (right hand system) of v.

```                ⎡ 0 -z  y⎤
Skew({x,y,z}) = ⎢ z  0 -x⎥
⎣-y  x  0⎦
```

DEPRECATED: use Mat.Skew()

#### func (*Mat) Add ¶ added in v0.11.0

`func (m *Mat) Add(a, b mat.Matrix)`

Add adds a and b element-wise, placing the result in the receiver. Add will panic if the two matrices do not have the same shape.

#### func (*Mat) At ¶ added in v0.11.0

`func (m *Mat) At(i, j int) float64`

At returns the value of a matrix element at row i, column j. At expects indices in the range [0,2]. It will panic if i or j are out of bounds for the matrix.

#### func (*Mat) CloneFrom ¶ added in v0.11.0

`func (m *Mat) CloneFrom(a mat.Matrix)`

CloneFrom makes a copy of a into the receiver m. Mat expects a 3×3 input matrix.

#### func (*Mat) Det ¶ added in v0.11.0

`func (m *Mat) Det() float64`

Det calculates the determinant of the receiver using the following formula

```    ⎡a b c⎤
m = ⎢d e f⎥
⎣g h i⎦
det(m) = a(ei − fh) − b(di − fg) + c(dh − eg)
```

#### func (*Mat) Dims ¶ added in v0.11.0

`func (m *Mat) Dims() (r, c int)`

Dims returns the number of rows and columns of this matrix. This method will always return 3×3 for a Mat.

#### func (*Mat) Hessian ¶ added in v0.12.0

`func (m *Mat) Hessian(p, step Vec, field func(Vec) float64)`

Hessian sets the receiver to the Hessian matrix of the scalar field at the point p, approximated using finite differences with the given step sizes. The field is evaluated at points in the area surrounding p by adding at most 2 components of step to p. Hessian expects the field's second partial derivatives are all continuous for correct results.

#### func (*Mat) Jacobian ¶ added in v0.12.0

`func (m *Mat) Jacobian(p, step Vec, field func(Vec) Vec)`

Jacobian sets the receiver to the Jacobian matrix of the vector field at the point p, approximated using finite differences with the given step sizes.

The Jacobian matrix J is the matrix of all first-order partial derivatives of f. If f maps an 3-dimensional vector x to an 3-dimensional vector y = f(x), J is a 3×3 matrix whose elements are given as

```J_{i,j} = ∂f_i/∂x_j,
```

or expanded out

```    [ ∂f_1/∂x_1 ∂f_1/∂x_2 ∂f_1/∂x_3 ]
J = [ ∂f_2/∂x_1 ∂f_2/∂x_2 ∂f_2/∂x_3 ]
[ ∂f_3/∂x_1 ∂f_3/∂x_2 ∂f_3/∂x_3 ]
```

Jacobian expects the field's first order partial derivatives are all continuous for correct results.

#### func (*Mat) Mul ¶ added in v0.11.0

`func (m *Mat) Mul(a, b mat.Matrix)`

Mul takes the matrix product of a and b, placing the result in the receiver. If the number of columns in a does not equal 3, Mul will panic.

#### func (*Mat) MulVec ¶ added in v0.11.0

`func (m *Mat) MulVec(v Vec) Vec`

MulVec returns the matrix-vector product M⋅v.

#### func (*Mat) MulVecTrans ¶ added in v0.11.0

`func (m *Mat) MulVecTrans(v Vec) Vec`

MulVecTrans returns the matrix-vector product Mᵀ⋅v.

#### func (*Mat) Outer ¶ added in v0.11.0

`func (m *Mat) Outer(alpha float64, x, y Vec)`

Outer calculates the outer product of the vectors x and y, where x and y are treated as column vectors, and stores the result in the receiver.

```m = alpha * x * yᵀ
```

#### func (*Mat) RawMatrix ¶ added in v0.11.0

`func (m *Mat) RawMatrix() blas64.General`

RawMatrix returns the blas representation of the matrix with the backing data of this matrix. Changes to the returned matrix will be reflected in the receiver.

#### func (*Mat) Scale ¶ added in v0.11.0

`func (m *Mat) Scale(f float64, a mat.Matrix)`

Scale multiplies the elements of a by f, placing the result in the receiver.

See the mat.Scaler interface for more information.

#### func (*Mat) Set ¶ added in v0.11.0

`func (m *Mat) Set(i, j int, v float64)`

Set sets the element at row i, column j to the value v.

#### func (*Mat) Skew ¶ added in v0.12.0

`func (m *Mat) Skew(v Vec)`

Skew sets the receiver to the 3×3 skew symmetric matrix (right hand system) of v.

```                ⎡ 0 -z  y⎤
Skew({x,y,z}) = ⎢ z  0 -x⎥
⎣-y  x  0⎦
```

#### func (*Mat) Sub ¶ added in v0.11.0

`func (m *Mat) Sub(a, b mat.Matrix)`

Sub subtracts the matrix b from a, placing the result in the receiver. Sub will panic if the two matrices do not have the same shape.

#### func (*Mat) T ¶ added in v0.11.0

`func (m *Mat) T() mat.Matrix`

T returns the transpose of Mat. Changes in the receiver will be reflected in the returned matrix.

#### func (*Mat) VecCol ¶ added in v0.11.0

`func (m *Mat) VecCol(j int) Vec`

VecCol returns the elements in the jth column of the receiver.

#### func (*Mat) VecRow ¶ added in v0.11.0

`func (m *Mat) VecRow(i int) Vec`

VecRow returns the elements in the ith row of the receiver.

#### type Rotation ¶ added in v0.9.0

`type Rotation quat.Number`

Rotation describes a rotation in space.

Example (EulerAngles)
```package main

import (
"fmt"
"math"

"gonum.org/v1/gonum/num/quat"
"gonum.org/v1/gonum/spatial/r3"
)

// euler returns an r3.Rotation that corresponds to the Euler
// angles alpha, beta and gamma which are rotations around the x,
// y and z axes respectively. The order of rotations is x, y, z;
// there are many conventions for this ordering.
func euler(alpha, beta, gamma float64) r3.Rotation {
// Note that this function can be algebraically simplified
// to reduce floating point operations, but is left in this
// form for clarity.
var rot1, rot2, rot3 quat.Number
rot1.Imag, rot1.Real = math.Sincos(alpha / 2) // x-axis rotation
rot2.Jmag, rot2.Real = math.Sincos(beta / 2)  // y-axis rotation
rot3.Kmag, rot3.Real = math.Sincos(gamma / 2) // z-axis rotation

return r3.Rotation(quat.Mul(rot3, quat.Mul(rot2, rot1))) // order of rotations
}

func main() {
// It is possible to interconvert between the quaternion representation
// of a rotation and Euler angles, but this leads to problems.
//
// The first of these is that there are a variety of conventions for
// application of the rotations.
//
// The more serious consequence of using Euler angles is that it is
// possible to put the rotation system into a singularity which results
// in loss of degrees of freedom and so causes gimbal lock. This happens
// when the second axis to be rotated around is rotated to 𝝿/2.
//
// See https://en.wikipedia.org/wiki/Euler_angles for more details.

pt := r3.Vec{1, 0, 0}

// For the Euler conversion function in this example, the second rotation
// is around the y-axis.
const singularY = math.Pi / 2

arb := math.Pi / 4

fmt.Printf("rotate around x-axis: %.2f\n", euler(arb, 0, 0).Rotate(pt))
fmt.Printf("rotate around y-axis: %.2f\n", euler(0, arb, 0).Rotate(pt))
fmt.Printf("rotate around z-axis: %.2f\n", euler(0, 0, arb).Rotate(pt))
fmt.Printf("rotate around x+y-axes: %.2f\n", euler(arb, arb, 0).Rotate(pt))
fmt.Printf("rotate around x+z-axes: %.2f\n", euler(arb, 0, arb).Rotate(pt))
fmt.Printf("rotate around y+z-axes: %.2f\n", euler(0, arb, arb).Rotate(pt))

fmt.Printf("rotate around y-axis to singularity: %.2f\n", euler(0, singularY, 0).Rotate(pt))
fmt.Printf("rotate around x+y-axes with singularity → gimbal lock: %.2f\n", euler(arb, singularY, 0).Rotate(pt))
fmt.Printf("rotate around z+y-axes with singularity → gimbal lock: %.2f\n", euler(0, singularY, arb).Rotate(pt))
fmt.Printf("rotate around all-axes with singularity → gimbal lock: %.2f\n", euler(arb, singularY, arb).Rotate(pt))

}
```
```Output:

rotate around x-axis: {1.00 0.00 0.00}
rotate around y-axis: {0.71 0.00 -0.71}
rotate around z-axis: {0.71 0.71 0.00}
rotate around x+y-axes: {0.71 0.00 -0.71}
rotate around x+z-axes: {0.71 0.71 0.00}
rotate around y+z-axes: {0.50 0.50 -0.71}
rotate around y-axis to singularity: {0.00 0.00 -1.00}
rotate around x+y-axes with singularity → gimbal lock: {0.00 0.00 -1.00}
rotate around z+y-axes with singularity → gimbal lock: {0.00 0.00 -1.00}
rotate around all-axes with singularity → gimbal lock: {0.00 0.00 -1.00}
```

#### func NewRotation ¶ added in v0.9.0

`func NewRotation(alpha float64, axis Vec) Rotation`

NewRotation creates a rotation by alpha, around axis.

#### func (Rotation) Mat ¶ added in v0.11.0

`func (r Rotation) Mat() *Mat`

Mat returns a 3×3 rotation matrix corresponding to the receiver. It may be used to perform rotations on a 3-vector or to apply the rotation to a 3×n matrix of column vectors. If the receiver is not a unit quaternion, the returned matrix will not be a pure rotation.

#### func (Rotation) Rotate ¶ added in v0.9.0

`func (r Rotation) Rotate(p Vec) Vec`

Rotate returns p rotated according to the parameters used to construct the receiver.

#### type Triangle ¶ added in v0.12.0

`type Triangle Vec`

Triangle represents a triangle in 3D space and is composed by 3 vectors corresponding to the position of each of the vertices. Ordering of these vertices decides the "normal" direction. Inverting ordering of two vertices inverts the resulting direction.

Example (Icosphere)
```package main

import (
"fmt"

"gonum.org/v1/gonum/spatial/r3"
)

func main() {
// This example generates a 3D icosphere from
// a starting icosahedron by subdividing surfaces.
// See https://schneide.blog/2016/07/15/generating-an-icosphere-in-c/.
const subdivisions = 5
// vertices is a slice of r3.Vec
// triangles is a slice of int indices
// referencing the vertices.
vertices, triangles := icosahedron()
for i := 0; i < subdivisions; i++ {
vertices, triangles = subdivide(vertices, triangles)
}
var faces []r3.Triangle
for _, t := range triangles {
var face r3.Triangle
for i := 0; i < 3; i++ {
face[i] = vertices[t[i]]
}
faces = append(faces, face)
}
fmt.Println(faces)
// The 3D rendering of the icosphere is left as an exercise to the reader.
}

// edgeIdx represents an edge of the icosahedron
type edgeIdx int

func subdivide(vertices []r3.Vec, triangles []int) ([]r3.Vec, []int) {
// We generate a lookup table of all newly generated vertices so as to not
// duplicate new vertices. edgeIdx has lower index first.
lookup := make(map[edgeIdx]int)
var result []int
for _, triangle := range triangles {
var mid int
for edge := 0; edge < 3; edge++ {
lookup, mid[edge], vertices = subdivideEdge(lookup, vertices, triangle[edge], triangle[(edge+1)%3])
}
newTriangles := []int{
{triangle, mid, mid},
{triangle, mid, mid},
{triangle, mid, mid},
{mid, mid, mid},
}
result = append(result, newTriangles...)
}
return vertices, result
}

// subdivideEdge takes the vertices list and indices first and second which
// refer to the edge that will be subdivided.
// lookup is a table of all newly generated vertices from
// previous calls to subdivideEdge so as to not duplicate vertices.
func subdivideEdge(lookup map[edgeIdx]int, vertices []r3.Vec, first, second int) (map[edgeIdx]int, int, []r3.Vec) {
key := edgeIdx{first, second}
if first > second {
// Swap to ensure edgeIdx always has lower index first.
key, key = key, key
}
vertIdx, vertExists := lookup[key]
if !vertExists {
// If edge not already subdivided add
// new dividing vertex to lookup table.
edge0 := vertices[first]
edge1 := vertices[second]
point := r3.Unit(r3.Add(edge0, edge1)) // vertex at a normalized position.
vertices = append(vertices, point)
vertIdx = len(vertices) - 1
lookup[key] = vertIdx
}
return lookup, vertIdx, vertices
}

// icosahedron returns an icosahedron mesh.
func icosahedron() (vertices []r3.Vec, triangles []int) {
const (
radiusSqrt = 1.0 // Example designed for unit sphere generation.
X          = radiusSqrt * .525731112119133606
Z          = radiusSqrt * .850650808352039932
N          = 0.0
)
return []r3.Vec{
{-X, N, Z}, {X, N, Z}, {-X, N, -Z}, {X, N, -Z},
{N, Z, X}, {N, Z, -X}, {N, -Z, X}, {N, -Z, -X},
{Z, X, N}, {-Z, X, N}, {Z, -X, N}, {-Z, -X, N},
}, []int{
{0, 1, 4}, {0, 4, 9}, {9, 4, 5}, {4, 8, 5},
{4, 1, 8}, {8, 1, 10}, {8, 10, 3}, {5, 8, 3},
{5, 3, 2}, {2, 3, 7}, {7, 3, 10}, {7, 10, 6},
{7, 6, 11}, {11, 6, 0}, {0, 6, 1}, {6, 10, 1},
{9, 11, 0}, {9, 2, 11}, {9, 5, 2}, {7, 11, 2},
}
}
```
```Output:

```

#### func (Triangle) Area ¶ added in v0.12.0

`func (t Triangle) Area() float64`

Area returns the surface area of the triangle.

#### func (Triangle) Centroid ¶ added in v0.12.0

`func (t Triangle) Centroid() Vec`

Centroid returns the intersection of the three medians of the triangle as a point in space.

#### func (Triangle) IsDegenerate ¶ added in v0.12.0

`func (t Triangle) IsDegenerate(tol float64) bool`

IsDegenerate returns true if all of triangle's vertices are within tol distance of its longest side.

#### func (Triangle) Normal ¶ added in v0.12.0

`func (t Triangle) Normal() Vec`

Normal returns the vector with direction perpendicular to the Triangle's face and magnitude twice that of the Triangle's area. The ordering of the triangle vertices decides the normal's resulting direction. The returned vector is not normalized.

#### type Vec ¶

```type Vec struct {
X, Y, Z float64
}```

Vec is a 3D vector.

#### func Add ¶ added in v0.9.0

`func Add(p, q Vec) Vec`

Add returns the vector sum of p and q.

#### func Cross ¶ added in v0.9.0

`func Cross(p, q Vec) Vec`

Cross returns the cross product p×q.

#### func Gradient ¶ added in v0.12.0

`func Gradient(p, step Vec, field func(Vec) float64) Vec`

Gradient returns the gradient of the scalar field at the point p, approximated using finite differences with the given step sizes.

#### func Rotate ¶ added in v0.9.0

`func Rotate(p Vec, alpha float64, axis Vec) Vec`

Rotate returns a new vector, rotated by alpha around the provided axis.

#### func Scale ¶ added in v0.9.0

`func Scale(f float64, p Vec) Vec`

Scale returns the vector p scaled by f.

#### func Sub ¶ added in v0.9.0

`func Sub(p, q Vec) Vec`

Sub returns the vector sum of p and -q.

#### func Unit ¶ added in v0.8.0

`func Unit(p Vec) Vec`

Unit returns the unit vector colinear to p. Unit returns {NaN,NaN,NaN} for the zero vector.