# Asymptote 3D label background

I'd like to create a 3D label in Asymptote that has a "background," effectively giving the label the look as though it were printed on poster board. I don't care if this background conforms to the Billboard effect when the 3D image is rotated; my only need here is for a static picture. I need the label to stand out against the surface behind it.

I know I could fake it by placing a white plane behind my label, but I don't know how to form this plane properly - how to correctly find the normal of the plane so that it looks rectangular from the viewpoint.

Here's a MWE, wherein the text of the label gets a little "lost" in the lines of the surface behind it.

``````import graph3;
size(200,200,IgnoreAspect);
currentprojection=orthographic(5,5,1);
defaultrender.merge=true;

triple f(pair t) {
return (t.y*cos(t.x),2*t.y*sin(t.x),t.y);//
}
surface s=surface(f,(0,0),(2*pi,2),32,32,Spline);
draw(s,emissive(white),meshpen=black+.2mm);

label("plot of \$x\$",(2,2,1.5));
``````

I use TikZ for some of my graphics, and there it is a simple manner to have the node drawn with a solid background color making the text easy to read. I am basically looking for the same effect here.

• Please provide `MWE` of the scene with labels for which you'd like to add a background. – g.kov May 13 '15 at 22:22
• I once spent some hours trying to figure out how to do this. I'm sure it's possible, but in the end I just positioned the white plane by trial and error. In so doing, I discovered that the effect was not nearly as nice as I had hoped--for some reason, this effect just does not look good in 3d. – Charles Staats May 14 '15 at 5:35
• That having been said, it would still be good to have a "right" solution, even if the effect is much less useful than one might assume. – Charles Staats May 14 '15 at 5:37
• @g.kov I have added a MWE for you. – GregH May 14 '15 at 10:36
• @CharlesStaats If you have tried it, and were not so successful, I probably won't spend much more time on it. Manually adjusting corners of a box in 3D seems like a big pain... if I really need the effect, I might go that route. – GregH May 14 '15 at 10:41

The following solution is slightly hacky, but much less draconian than drawing on top of the picture. It creates two labels: one with the text, and one, positioned directly behind the text (relative to the camera), with a solid box the same size as the text. This solution will only work if the projection is orthographic.

``````settings.outformat="png";
settings.render=4;
import graph3;
usepackage("color");
size(200,200,IgnoreAspect);
currentprojection=orthographic(5,5,1);

// Decreasing the tubegranularity makes the gridlines look nicer.
defaultrender.tubegranularity = 1e-3;

triple forward = -unit(currentprojection.camera);
// Take the component of currentprojection.up orthogonal to forward.
triple upvec = unit(currentprojection.up - dot(currentprojection.up,forward) * forward);
triple rightvec = cross(forward, upvec);

triple f(pair t) {
return (t.y*cos(t.x),2*t.y*sin(t.x),t.y);
}
surface s=surface(f,(0,0),(2*pi,2),32,32,Spline);
draw(s,emissive(white),meshpen=black+.2mm);

label("plot of \$x\$", (2,2,1.5));
label("\colorbox{black}{plot of \$x\$}", (2,2,1.5) + .01 forward, white+opacity(0.9));
`````` • Nice. I was able to make something near this, but didn't know that I could add "white" to the end of the label to turn the black box from the `\colorbox{}{}` white. I'm continually amazed by the number of options/settings that exist; I knew nothing of `tube.granularity`. Finally, just to be clear, you might remove `upvec` and `rightvec` from your answer as you don't actually use them. – GregH May 20 '15 at 10:19
• I only just found out about `tube.granularity` myself--see sourceforge.net/p/asymptote/bugs/191/#e4f7 – Charles Staats May 20 '15 at 16:20
• Concerning `upvec` and `rightvec`: They will come in handy if you want to move anything in the left/right or up/down directions. But you are correct that I did not end up using them here. – Charles Staats May 20 '15 at 16:21
• Nice thinking ahead ... – GregH May 21 '15 at 10:20

Following the answer from asymptote discussion,

``````//
// bglabel3d.asy
//
settings.tex="pdflatex";
settings.prc=false;
settings.render=0;

import graph3;
size(200,200,IgnoreAspect);
currentprojection=orthographic(5,5,1);
defaultrender.merge=true;

triple f(pair t) { return (t.y*cos(t.x),2*t.y*sin(t.x),t.y); }

surface s=surface(f,(0,0),(2*pi,2),32,32,Spline);
draw(s,emissive(white),meshpen=deepblue+.2mm);

label(Label("plot of \$x\$",Fill(orange+opacity(0.8))),(2,2,1.5));
`````` Edit:

As it is noted in the linked answer, this solution works only with `render=0`.

For raster images we can draw the labels on the image:

First, make a label-free `img.png`:

``````//
// scene3d.asy
//
settings.outformat="png";
settings.prc=false;
settings.render=4;
import graph3;
size(200,200,IgnoreAspect);
currentprojection=orthographic(5,5,1);
defaultrender.merge=true;

triple f(pair t) { return (t.y*cos(t.x),2*t.y*sin(t.x),t.y); }

surface s=surface(f,(0,0),(2*pi,2),32,32,Spline);

pen bpen=rgb(0.75, 0.7, 0.1);
material m=material(diffusepen=0.6bpen
,ambientpen=bpen
,emissivepen=0.4*bpen
,specularpen=0.8white
,shininess=1.0);
draw(s,m);
shipout("img");
``````

next, draw the labels on the raster image:

``````settings.tex="pdflatex";
import graph;
import math; // for grid() function

defaultpen(fontsize(10pt));

real sc=1/4;
unitsize(sc*1bp);

real wd=796*sc;
real ht=808*sc;

label(
shift(wd/2,ht/2)*
graphic("img.png"
,"width="+string(wd)+"bp"
+",height="+string(ht)+"bp"
+",scale="+string(sc)
),(0,0)
);
layer();

draw(((0,0)--(wd,ht)/sc),blue+2pt);

int ngrid=100;
int n=(int)(wd/(ngrid-1)/sc);
int m=(int)(ht/(ngrid-1)/sc);

xaxis( 0,wd/sc,RightTicks(Step=ngrid));
yaxis(0,ht/sc,LeftTicks(Step=ngrid));

label(Label("plot of \$y\$"
,Fill(1mm,orange+opacity(0.8))),(400,400));
`````` • Nice. I didn't know about nesting `label` and `Label` commands. It is interesting that adding extra space to the text of the label doesn't seem to affect the size of the colored box. Adding `\phantom{xx}`, for instance, does nothing. – GregH May 14 '15 at 17:10
• @GregH: Yes, the label is trimmed to fit the actual ink, but we can use a trick with microscopic dot: `\rule{1sp}{1sp}\phantom{xx}plot of \$x\$`. Also, the first parameter to `Fill` can be used to enlarge the bounding box, for example, `Fill(5mm,orange+opacity(0.8)))`. – g.kov May 14 '15 at 18:47
• This is great, but it does not seem to work when `settings.render > 0`. – Charles Staats May 15 '15 at 2:10
• I realized that after marking the solution as "the" answer. While I like the vector based graphics of `settings.render=0`, it doesn't seem to handle multiple objects well. In the cone above, it works fine. For most of my pictures it doesn't. So I still want to see a solution that works for any render setting. – GregH May 15 '15 at 10:38