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()
shade3d(c3d)
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.

enter image description here

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) 
shade3d( c3d_trans )
points3d(t(c3d_trans$vb), size=5)

This gives:

enter image description here

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


Show source
| R   | linear-algebra   | rgl   2016-09-27 19:09 3 Answers

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

  1. 2016-09-27 23:09

    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()
    shade3d(c3d)
    points3d(t(c3d$vb), size = 5)
    for (i in 1:6)
        lines3d(t(c3d$vb)[c3d$ib[,i],])
    

    Screenshot of rendered cube

    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()
    shade3d(sphere)
    

    and this gives this shape:

    sphere

    which transforms to this:

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

    transformed sphere

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

  2. 2016-09-28 15:09

    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, ...) {
        shade3d(object, ...)
        vertices <- t(object$vb)
        indices <- object$ib
        points3d(vertices, size=5)
        for (i in 1:ncol(indices))
            lines3d(vertices[indices[,i],])
    }
    
  3. 2016-09-28 16:09

    (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)
    shade3d(c3d_trans2, col = colv, alpha=0.5)
    dot3d(c3d_trans2, size = 5)
    

    enter image description here


    [ 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
    
    shade3d() rule (it's easy)
    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, ...
    

    enter image description here

    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()

    enter image description here

    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.

    enter image description here

Leave a reply to - rgl: drawing a cube with colored faces, vertex points and lines

◀ Go back