0
votes
  • R 3.5.1
  • RStudio 1.1.463
  • rgl 0.99.16
  • extrafont 0.17
  • Windows 10 build 1809

When I plot a shape with quads3d() and then add a text3d() object, I find 3 problems (I think they are related, thus a single post here):

  1. The text3d() object produces artifacts that interfere with the drawing; they look like surfaces behind the text that intersect the drawn objects in strange ways.
  2. The plot zooms way out (and the extent/bbox changes) - I can make it not zoom using ignoreExtent=T, but if I do axes3d(), the bbox is seen to be much bigger than the one with no text. This seems to indicate that a comparatively very large piece of geometry was inserted. What about a few characters of text is so large dimensionally?
  3. The text looks crappy; pixellated, math symbols are weak, etc.

I have tried different font families (including the basic four), colors, and other text parameters. I use the extrafont package and have loaded my Windows fonts using font_import(). It doesn't matter if it's a fresh R session/environment. It happens with plotMath=T or F.

The geometry I'm working with is smallish, fits roughly into a unit cube, should that make any difference?

How can I get rid of the artifacts and get decent-looking text that doesn't change the plot dimensions? Thanks.

Here's example code:

# Draw a 3D Shape and Label it

library(rgl)
library(extrafont)

# Open a new device in which to display the diagram
open3d(windowRect=c(900,200,1700,800))

# Define vertices of the faces

A0 <- c(0, 0.1, -0.02)
B0 <- c(0, -0.1, -0.02)
C0 <- c(0, -0.1, 0.02)
D0 <- c(0, 0.1, 0.02)

Al <- c(1, 0.02, -0.1)
Bl <- c(1, -0.02, -0.1)
Cl <- c(1, -0.02, 0.1)
Dl <- c(1, 0.02, 0.1)

# Define the quadrangles to be visualized

Face0 <- c(A0, B0, C0, D0)
Facel <- c(Bl, Al, Dl, Cl)

Side1 <- c(A0, Al, Bl, B0)
Side2 <- c(B0, Bl, Cl, C0)
Side3 <- c(C0, Cl, Dl, D0)
Side4 <- c(D0, Dl, Al, A0)

# Draw faces and sides
TColor <- "steelblue"
TAlpha <- .25
F0 <- quads3d(matrix((Face0), nrow=4, byrow=T), col=TColor, alpha=TAlpha)
Fl <- quads3d(matrix((Facel), nrow=4, byrow=T), col=TColor, alpha=TAlpha)
S1 <- quads3d(matrix((Side1), nrow=4, byrow=T), col=TColor, alpha=TAlpha)
S2 <- quads3d(matrix((Side2), nrow=4, byrow=T), col=TColor, alpha=TAlpha)
S3 <- quads3d(matrix((Side3), nrow=4, byrow=T), col=TColor, alpha=TAlpha)
S4 <- quads3d(matrix((Side4), nrow=4, byrow=T), col=TColor, alpha=TAlpha)

Running this much results in a nice image of a 3d shape:

enter image description here

If I run the following code to add a text label,

# Label a point

Cx <- c(.6,-0.052,0.068)

Xcolor <- "#000000"

points3d(Cx[1], Cx[2], Cx[3], col=Xcolor, size=5)
points3d(matrix(Cx, nrow=1), col=Xcolor, size=5)

XVertexColor <- "darkseagreen4"
par3d(ignoreExtent=F)

labelCx <- text3d(x=Cx[1], y=Cx[2], z=Cx[3], adj=c(0,0), family="Calibri", cex=1, font=2, text=expression(bold(sqrt(1/C[3](x)))), usePlotmath=T, col=XVertexColor)

...it looks like this (with ignoreExtent=F): The same rgl device, the only change has been the point and the text3d() Zooming and rotating the image shows the text artifacts that interfere with the view of the geometry:

enter image description here

enter image description here

enter image description here

Note that the square root symbol is barely visible; this is true no matter what font family, and whether or not bold() is applied.

1
Since you're a new contributor, let me suggest that you mark my answer below as "accepted", or leave a comment on it explaining what is missing. This is how StackOverflow ends up with a collection of good answers. - user2554330
Thank you very much! All very useful! - Uncle Frank

1 Answers

2
votes

A known limitation of the way rgl draws transparent (i.e. alpha < 1) objects is that they don't always interact well. The problem is that transparent objects need to be drawn in order from furthest to closest in the current view, but if you have two transparent polygons that intersect, some parts need to be drawn in one order, and some parts need to be drawn in the opposite order. Since rgl doesn't split them into separate pieces, some part will be drawn incorrectly.

This affects text because text is drawn as a quad with the background drawn with alpha = 0 and the text drawn with alpha = 1. If the quad holding the text intersects a transparent polygon, some part of one of them will be drawn poorly.

You can reduce the pixellation of your text by increasing the initCex argument; see ?plotmath3d for a discussion. Unfortunately, this makes the square root symbol look even worse: I think it is drawn at a constant width regardless of size (by the base graphics functions, not by rgl). You can see this in base graphics using

plot(1,1, type="n")
text(1,1,expression(bold(sqrt(1/C[3](x)))), cex = 5)

Using a smaller initCex will give a better proportioned square root, but it will be blurry or pixellated (depending on the size). (NB: see the addition below.)

EDITED TO ADD:

Regarding the bounding box changes: that definitely looks like a bug, but again it appears to be a limitation of the design. As mentioned, text is drawn on a transparent quad. This quad is drawn by sprites3d, which means it doesn't rotate with the scene, it always faces towards the viewer. If you have ignoreExtent = FALSE, then rgl attempts to make sure that the quad fits within the scene regardless of orientation, i.e. it takes up the same space as a sphere around the quad.

Your scene is much bigger in the X direction than in Y or Z, so a sphere really distorts things.

The solution here is to use ignoreExtent = TRUE so that the bounding box ignores that sphere. Remember to restore it afterwards.

One other improvement is possible. Since you don't want resizeable text, you can improve the way it is drawn by setting cex and initCex to the same value, but drawing with different material properties. Before adding the text, set both texminfilter and texmagfilter to "nearest", and things will look a little pixellated, but better than what you were seeing.

Putting both changes together:

That is, change your final two lines of code to this:

saveIgnore <- par3d(ignoreExtent = TRUE)
saveFilter <- material3d(texminfilter = "nearest", texmagfilter = "nearest")
labelCx <- text3d(x=Cx[1], y=Cx[2], z=Cx[3], adj=c(0,0),
                  family="Calibri", cex = 1, initCex = 1, font=2,
                  text=expression(bold(sqrt(1/C[3](x)))), 
                  usePlotmath=TRUE, col=XVertexColor)
material3d(saveFilter)
par3d(saveIgnore)

2nd EDIT:

There are a few workarounds for your first problem. The simplest is moving the text away from anything that's transparent, or making the transparent things opaque. But if you really want to have text near transparent objects, setting the material property depth_mask = FALSE will mean the text's quad will never obscure anything behind it. This is probably a good default. Setting depth_test = "always" will mean nothing can obscure the text. This can lead to fairly weird looking displays so I wouldn't recommend it in general, but with your alpha = 0.25 surfaces it doesn't look too bad.