3
$\begingroup$

I'd like to label the level curves of a scalar field according to their $z$-value. The below code (taken mostly from this answer to a very similar question, but which uses tool-tips instead of traditional labels) should illustrate what I'm trying to achieve:

f[x_, y_] = (x^3 + y^3)/(x^2 + y^2);
Show[
    Plot3D[f[x, y], {x, -1, 1}, {y, -1, 1}, MeshFunctions -> {#3 &}],
    Normal[Plot3D[f[x, y], {x, -1, 1}, {y, -1, 1}, 
        MeshFunctions -> {#3 &}, PlotStyle -> None]
    ] /. Line[pp_] :> Text[pp[[1, 3]], pp/1.5]
 ]

mwe

The problem with this example is that I have no clue how to position the labels (e.g. how could I position them all in the middle of each line, see this other question) or how I could only label selected lines. I also tried labeling curves on a 2D contour plot and then using that as the texture for the main plot, but of course the labels and line widths got distorted.

$\endgroup$
1

2 Answers 2

6
$\begingroup$

Maybe you could use ContourPlot to obtain some good automatic spacing for the contour labels.

Clear[f, a, b, cp, plt, crd, txts]
f[x_, y_] = (x^3 + y^3)/(x^2 + y^2);
cp = ContourPlot[f[x, y], {x, -1, 1}, {y, -1, 1}, Contours -> 20, 
  ContourLabels -> All]
plt = Plot3D[f[x, y], {x, -1, 1}, {y, -1, 1}, MeshFunctions -> {#3 &},
   Mesh -> 19]
crd = Cases[cp, GraphicsComplex[a_, ___] :> a, Infinity][[1]];
txts = Graphics3D@
   Cases[cp, 
    Text[a_, b_] :> 
     Text[a, {crd[[b]][[1]], crd[[b]][[2]], f @@ crd[[b]]}], Infinity];
Show[plt, txts]

enter image description here

$\endgroup$
2
$\begingroup$

Better practice is

ContourPlot[x y, {x, -2, 2}, {y, -2, 2}, ContourShading -> False, 
 Contours -> 10, 
 ContourLabels -> (Rotate[
     Text[" " <> ToString[#3] <> " ", {#1, #2}, 
      Background -> White], -ArcTan[#2/#1]] &)]

ContourPlot with labels along the contours

This reaches the level of for example Matlab contour plots that are very popular.

This is how it looks for x/y:

ContourPlot[x/y, {x, -2, 2}, {y, -2, 2}, ContourShading -> False, 
 Contours -> 10, 
 ContourLabels -> (Rotate[
     Text[" " <> ToString[#3] <> " ", {#1, #2}, 
      Background -> White], -ArcTan[#2/#1]] &)]

ContourPlot with labels along the contours

For arctangens:

ContourPlot[ArcTan[x, y], {x, -2, 2}, {y, -2, 2}, 
 ContourShading -> False, Contours -> 10, 
 ContourLabels -> (Rotate[
     Text[" " <> ToString[#3] <> " ", {#1, #2}, 
      Background -> White], -ArcTan[#2/#1]] &)]

ContourPlot with labels along the contours

So this already is critical.

The labels may be not so nicely visible so this will be comfortable:

Clear[circle]
circle[n_Integer /; 1 <= n <= 10] := 
 Style[FromCharacterCode[9311 + n], FontFamily -> "Arial Unicode MS"]
circle[n_Integer /; 1 <= n <= 10, size_Integer?(# > 0 &)] := 
 Style[FromCharacterCode[9311 + n], size, 
  FontFamily -> "Arial Unicode MS"]

Show[Plot3D[f[x, y], {x, 0, 2}, {y, 0, 3}, ViewAngle -> 0.50, 
  ViewPoint -> {2, -2.5, 0.7}], 
 Graphics3D[{Black, Text[circle[1, 24], {1.5, 0, 0}], 
   Text[circle[2, 24], {2, 1.5, 0}], Lighter@Gray, 
   Text[circle[3, 24], {1, 3, 0}], Gray, 
   Text[circle[4, 24], {0, 1.5, 0}], 
   Style[Text[circle[5, 24], {1, 1.5, -1}], FontSlant -> Italic]}]]

3D text labels customized

This shows what happens if the labels get to close together:

data = {{258, 1028, 0}, {217, 747, 0}, {212, 754, 0}, {210, 748, 
    0}, {191, 654, 0}, {157, 638, 0}};

Show[Graphics3D[{Blue, PointSize[0.04], Point[data]}], 
 Graphics3D[Text[#[[1]], 1.04 #] & /@ data], Axes -> True, 
 BoxRatios -> 1]

3d text labels by built-ins to close together

In general, the methodology suggested by @tim-laska avoids these problems as much as possible but just in two dimensions.

There this is a nice alternative Callout:

ListPointPlot3D[Callout[#, #[[1]]] & /@ data]

Callout built-in

Put it all together:

Clear[f, a, b, cp, plt, crd, txts]
f[x_, y_] = (x^3 + y^3)/(x^2 + y^2);
cp = ContourPlot[f[x, y], {x, -1, 1}, {y, -1, 1}, Contours -> 20, 
   ContourLabels -> All];
ContourPlot[f[x, y], {x, -1, 1}, {y, -1, 1}, ContourShading -> False, 
 Contours -> 10, 
 ContourLabels -> (Rotate[
     Text[" " <> ToString[#3] <> " ", {#1, #2}, 
      Background -> White], -ArcTan[#2/#1]] &)]
plt = Plot3D[f[x, y], {x, -1, 1}, {y, -1, 1}, MeshFunctions -> {#3 &},
   Mesh -> 19]
crd = Cases[cp, GraphicsComplex[a_, ___] :> a, Infinity][[1]];
Cases[cp, 
 Callout[a_, b_] :> 
  Callout[{crd[[b]][[1]], crd[[b]][[2]], f @@ crd[[b]]}], Infinity]
txts = ListPointPlot3D[
  Callout[#[[2]], #[[1]], Above] & /@ 
   Cases[cp, 
    Text[a_, 
      b_] :> {a, {crd[[b]][[1]], crd[[b]][[2]], f @@ crd[[b]]}}, 
    Infinity]]
Show[plt, txts]

ContourPlot with aligned labels

Plot3D of the quested function

Label set in Callout with Above

Plot3D with a set of Callout

The advantage of using Callout is possible that Callout offers more options for customization.

The link to the level or contour is direct and the Plot3D is better evaluable than with Text only. The contrast with the background for the label is better above the contours and at most if they get closer together. Still, the layout work is done by ContourPlot and taken completely from there. The labels stay aligned if the Plot3D is rotated.

In the example, a point on the contour is selected and a dot is placed upon. That is not necessary but enhances the readout.

The Cases input is generalized version independent of Text or Callout.

I expect that Text in 3D wraps Callout for simpler applications.

The discours of the answer shall shed some light on the complexity of contour representations of functions in 2D and 3D.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.