# Adding z-value to mesh lines in Plot3D

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]
]


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.

• This is closely related if not duplicate to [mathematica.stackexchange.com/questions/61850/…. The difference this is based on contours in 3D, the question linked to is general 3d text inserts at points. Aug 3, 2020 at 9:59

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]


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]] &)]


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]] &)]


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]] &)]


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]}]]


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]


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]


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]


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.