This vignette illustrates how linear transformations can be
represented in 3D using the rgl
package. It
is explained more fully in this StackExchange discussion 3D
geometric interpretations of matrix inverse. It also illustrates the
determinant, \(\det(\mathbf{A})\), as
the volume of the transformed cube, and the relationship between \(\mathbf{A}\) and \(\mathbf{A}^{-1}\).
Start with a unit cube, which represents the identity matrix \(\mathbf{I}_{3 \times 3}\) =
diag(3)
in 3 dimensions. In rgl
this is given
by cube3d()
which returns a "mesh3d"
object
containing the vertices from -1 to 1 on each axis. I translate and scale
this to make each vertex go from 0 to 1. These are represented in
homogeneous coordinates to handle perspective and transformations. See
help("identityMatrix")
for details.
library(rgl)
library(matlib)
# cube, with each face colored differently
colors <- rep(2:7, each=4)
c3d <- cube3d()
# make it a unit cube at the origin
c3d <- scale3d(translate3d(c3d, 1, 1, 1),
.5, .5, .5)
str(c3d)
## List of 6
## $ vb : num [1:4, 1:8] 0 0 0 1 1 0 0 1 0 1 ...
## $ material : list()
## $ normals : NULL
## $ texcoords: NULL
## $ meshColor: chr "vertices"
## $ ib : num [1:4, 1:6] 1 3 4 2 3 7 8 4 2 4 ...
## - attr(*, "class")= chr [1:2] "mesh3d" "shape3d"
The vertices are the 8 columns of the vb
component of
the object.
c3d$vb
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,] 0 1 0 1 0 1 0 1
## [2,] 0 0 1 1 0 0 1 1
## [3,] 0 0 0 0 1 1 1 1
## [4,] 1 1 1 1 1 1 1 1
I want to show the transformation of \(\mathbf{I}\) by a matrix \(\mathbf{A}\) as the corresponding transformation of the cube.
# matrix A:
A <- matrix(c( 1, 0, 1,
0, 2, 0,
1, 0, 2), 3, 3) |> print()
## [,1] [,2] [,3]
## [1,] 1 0 1
## [2,] 0 2 0
## [3,] 1 0 2
det(A)
## [1] 2
# A can be produced by elementary row operations on the identity matrix
I <- diag( 3 )
AA <- I |>
rowadd(3, 1, 1) |> # add 1 x row 3 to row 1
rowadd(1, 3, 1) |> # add 1 x row 1 to row 3
rowmult(2, 2) # multiply row 2 by 2
all(AA==A)
## [1] TRUE
I want to be able to display 3D mesh objects, with colored points,
lines and faces. This is a little tricky, because in rgl colors are
applied to vertices, not faces. The faces are colored by interpolating
the colors at the vertices. See the StackOverflow discussion drawing
a cube with colored faces, vertex points and lines. The function
draw3d()
handles this for "mesh3d"
objects.
Another function, vlabels()
allows for labeling
vertices.
# draw a mesh3d object with vertex points and lines
draw3d <- function(object, col=rep(rainbow(6), each=4),
alpha=0.6,
vertices=TRUE,
lines=TRUE,
reverse = FALSE,
...) {
if(reverse) col <- rev(col)
shade3d(object, col=col, alpha=alpha, ...)
vert <- t(object$vb)
indices <- object$ib
if (vertices) points3d(vert, size=5)
if (lines) {
for (i in 1:ncol(indices))
lines3d(vert[indices[,i],])
}
}
# label vertex points
vlabels <- function(object, vertices, labels=vertices, ...) {
text3d( t(object$vb[1:3, vertices] * 1.05), texts=labels, ...)
}
We can show the cube representing the identity matrix \(\mathbf{I}\) using draw3d()
.
Transforming this by \(\mathbf{A}\) is
accomplished using transform3d()
.
The inverse of A
is found using `solve()
The determinant of the matrix A
here is 2. The
determinant of \(\mathbf{A}^{-1}\) is
the reciprocal, \(1/
\det(\mathbf{A})\). Geometrically, this means that the larger
\(\mathbf{A}^{-1}\) is the smaller is
\(\mathbf{A}^{-1}\).
You can see the relation between them by graphing both together in the same plot. It is clear that \(\mathbf{A}^{-1}\) is small in the directions \(\mathbf{A}\) is large, and vice versa.