## rgl: drawing a cube with colored faces, vertex points and lines

Question

To demonstrate the effect of linear transformations in 3D, `x -> A x`, I want to draw a cube and show its transformation under `A`. For this, I need to color each face separately, and also show the vertex points and the lines that outline each face.

I can't figure out how to use distinct colors for the faces, and how to make this more general so I don't have to repeat all the steps for the result under the transformation.

what I tried:

``````library(rgl)
c3d <- cube3d(color=rainbow(6), alpha=0.5)
open3d()
points3d(t(c3d\$vb), size=5)
for (i in 1:6)
lines3d(t(c3d\$vb)[c3d\$ib[,i],])
``````

This gives the image below. But I don't understand how the faces are colored. And, I seem to have to use `points3d` and `lines3d` on the components of the `c3d` shape, and don't have a single object I can transform.

A particular transformation is given by the matrix `A` below, and here is how I add that to the scene,

``````A <- matrix(c( 1, 0, 1, 0, 2, 0,  1, 0, 2), 3, 3)
c3d_trans <- transform3d(c3d, A)
points3d(t(c3d_trans\$vb), size=5)
``````

This gives:

Is there some way to simplify this and make it more generally useful?

Show source

## Answers to rgl: drawing a cube with colored faces, vertex points and lines ( 3 )

1. In `rgl`, when drawing primitive shapes, you apply colours to vertices, not faces. The faces are coloured by interpolating the colors at the vertices.

However, `cube3d()` is not a primitive shape, it's a "mesh". It is drawn as 6 separate quadrilaterals. Each vertex is used 3 times.

It's not really documented, but the order the colours are used is that the first 4 are used for one face, then the next 4 for the next face, etc. If you want your colours to be `rainbow(6)`, you need to replicate each colour 4 times:

``````library(rgl)
c3d <- cube3d(color=rep(rainbow(6), each = 4), alpha = 0.5)
open3d()
points3d(t(c3d\$vb), size = 5)
for (i in 1:6)
lines3d(t(c3d\$vb)[c3d\$ib[,i],])
``````

I'd recommend a higher `alpha` value; I find the transparency a little confusing at `alpha = 0.5`.

By the way, for the same purpose, I generally use a shape that looks more spherical as the baseline; I think it gives better intuition about the transformation. Here's code I have used:

``````sphere <- subdivision3d(cube3d(color=rep(rainbow(6),rep(4*4^4,6)), alpha=0.9),
depth=4)
sphere\$vb[4,] <- apply(sphere\$vb[1:3,], 2, function(x) sqrt(sum(x^2)))
open3d()
``````

and this gives this shape:

which transforms to this:

``````A <- matrix(c( 1, 0, 1, 0, 2, 0,  1, 0, 2), 3, 3)
trans <- transform3d(sphere, A)
open3d()
``````

Of course, it all looks better if you can rotate it.

2. The second part of my question-- how to generalize this a bit -- was unanswered. Here is a simple function I'm now using to make the steps reusable.

``````# draw a mesh3d object with vertex points and lines
draw3d <- function(object, ...) {
vertices <- t(object\$vb)
indices <- object\$ib
points3d(vertices, size=5)
for (i in 1:ncol(indices))
lines3d(vertices[indices[,i],])
}
``````
3. (Note: I use `rgl version 0.96.0`. If my memory is correct, `wire3d()` and `dot3d()` rules have been changed)

It isn't a good idea to give a color informatin when you make a class `mesh3d` object, because `shade3d()`, `wire3d()` and `dot3d()` use it in the different ways. It would be better to give plot-function a color information when you draw a object.

for example;
``````A <- matrix(c( 1, 0, 1, 0, 2, 0,  1, 0, 2), 3, 3)
c3d2 <- cube3d()
c3d_trans2 <- cube3d(A)
colv <- rep(2:7, each=4)

shade3d(c3d2, col = colv, alpha = 0.8)
wire3d(c3d2); dot3d(c3d2, size = 5)
dot3d(c3d_trans2, size = 5)
``````

[ the details related to colors and mesh3d.obj ]

Most important rule is that a vertex consumes a color whenever it is used (but it is a bit complex, please see below exampe). I call below matrix ib and `mesh3d.obj\$vb`'s col number index .

``````cube3d()\$ib
#      [,1] [,2] [,3] [,4] [,5] [,6]
# [1,]    1    3    2    1    1    5
# [2,]    3    7    4    5    2    6
# [3,]    4    8    8    7    6    8
# [4,]    2    4    6    3    5    7
``````
``````plot3d(cube3d(scaleMatrix(1.2,1.2,1.2)), alpha=0)
text3d(t(cube3d()\$vb[1:3,]*1.05), texts=1:8)  # indices
shade3d(cube3d(), col=c(rep(2,4), rep(3,4), rep(4,4), rep(5,4), rep(6,4), rep(7,4)), alpha=0.8)
#  ib[1:4, 1]  [1:4, 2]  [1:4, 3]  [1:4, 4]  [1:4, 5]  [1:4, 6]
# index 1,3,4,2  3,7,8,4   2,4,8,6, ...
``````

wire3d rule (complex and terrible..; edited)
``````text3d(t(cube3d()\$vb[1:3,]*1.05), texts=1:8, font=2)  # indices
wire3d(cube3d(), col=c(rep(2,6), rep(3,6), rep(4,6), rep(5,6), rep(6,6), rep(7,6)))
# I gave each color 6 times.

index  1       3       4       2       1
ib\$[1,1] - [2,1] - [3,1] - [4,1] - [1,1] - NA
col   2       2       2       2       2    2  # Why NA uses a color!!??

index  3       7      8    4 (skipped) 3       # the line already has been drawn, skipped.
ib\$[1,2] - [2,2] - [3,2] - [4,2] - [1,2] - NA;
col   3       3      3    3 (skipped) 3    3

index 2 (sk)   4 (sk)  8       6      2
ib\$[1,3] - [2,3] - [3,3] - [4,3] - [1,3] - NA;
col 4  (sk)   4 (sk)  4       4       4    4,   and so on.
``````

In one `ib`'s `col`, one vertex have only one color (e.g., at `ib[,1]`, Index3's color information is used both 1-3 and 3-4. When the line already has been drawn, it is skipped. It is too difficult (some patterns is impossible) to draw lines in different colors using `wire3d()`. If you want to do, it would be better to use `lines3d()` or `segments3d()`

dot3d rule
``````plot3d(cube3d(scaleMatrix(1.2,1.2,1.2)), alpha=0)
text3d(t(cube3d()\$vb[1:3,]*1.05), texts=1:8)  # indices
dot3d(cube3d(), col=1:8, size=8)

unique(c(cube3d()\$ib))
# [1] 1 3 4 2 7 8 6 5
``````

if you give `col=1:8`, NOT index2 but index3 becomes `col = 2` (`red`).
So, `col = c("col1", "col2", ..., "col8")[unique(c(object\$ib))]` means index1 is `col1`, index2 is `col2`.