Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I've got an expression involving x and y that I want to use as a MeshFunction:

mymesh = x + y
Plot3D[x^2 + y^2, {x, -1, 1}, {y, -1, 1}, MeshFunctions -> {Function[{x, y, z}, mymesh]}]

This works just fine. But now I want to create a plotting function that takes the mesh function as an argument, like so:

myplot[mesh_] := Plot3D[x^2 + y^2, {x, -1, 1}, {y, -1, 1},
                        MeshFunctions -> {Function[{x, y, z}, mesh]}]
myplot[mymesh]

This produces the following error:

MeshFunctions::invmeshf:
  "MeshFunctions->Function[{x$,y$,z$},x+y] must be a pure function or a list of pure functions"

It seems like it's making local variables x$, y$, z$ and then keeping them distinct from the x,y in mymesh.

Any suggestions how to make this work? I'd like to keep mymesh in the symbolic form, because the plot I'm actually interested in doing is much more complicated, and uses the mymesh function a second time as part of the function to plot.

I'm going to add some more to this question, because I have a few answers, all of which suggested not passing mymesh in it's current form to the function. But I explicitly want to pass mymesh as it is.

Here's the actual code I'm using it for. It plots the solutions to c==0 on the unit sphere, where c is a homogeneous polynomial in x,y,z.

elliptic = z y^2 - x^3 + z^2 x
curveSpherePlot[c_] := ContourPlot3D[
  {x^2 + y^2 + z^2 == 1, c == 0},
  {x, -1, 1}, {y, -1, 1}, {z, -1, 1},
  MeshFunctions -> {Function[{x, y, z}, x^2 + y^2 + z^2 - 1 - c]},
  MeshStyle -> {{Thick, Black}},
  Mesh -> {{0}},
  ContourStyle -> {Opacity[0.9], None},
  BoundaryStyle -> None
]
curveSpherePlot[elliptic]

Notice that the argument c appears in the equation as well as in the mesh function. Not only that, but I manipulate the curve symbolically with other functions in the package, so I really don't want to switch it to a different type of object if I can help it.

share|improve this question
    
How about , MeshFunctions -> mesh and myplot[Function[{x, y, z}, mymesh]]. –  Chenminqi Jun 25 at 16:39

4 Answers 4

up vote 1 down vote accepted

The failure you observe is caused by automatic renaming within nested scoping constructs:

You can use any of the methods described therein to correct the problem. For the first example using the form MeshFunctions -> Function @@ {{x, y}, mymesh} will work. For the second you could use MeshFunctions -> Function @@ {{x, y, z}, x^2 + y^2 + z^2 - 1 - c}:

curveSpherePlot[c_] :=
 ContourPlot3D[{x^2 + y^2 + z^2 == 1, c == 0}, {x, -1, 1}, {y, -1, 1}, {z, -1, 1},
  MeshFunctions -> Function @@ {{x, y, z}, x^2 + y^2 + z^2 - 1 - c},
  MeshStyle -> {{Thick, Black}}, Mesh -> {{0}},
  ContourStyle -> {Opacity[0.9], None},
  BoundaryStyle -> None
 ]

curveSpherePlot[elliptic]

enter image description here

These do not protect against e.g. x or y having a global value assigned and breaking the operation, but then neither does your basic construct. We can manually Block those Symbols and hold elliptic using SetDelayed to prevent unwanted evaluation:

x = y = z = "Fail!";

elliptic := z y^2 - x^3 + z^2 x

SetAttributes[curveSpherePlot, HoldFirst]

curveSpherePlot[c_] := Block[{x, y, z},
  ContourPlot3D[{x^2 + y^2 + z^2 == 1, c == 0}, {x, -1, 1}, {y, -1, 1}, {z, -1, 1}, 
   MeshFunctions -> Function @@ {{x, y, z}, x^2 + y^2 + z^2 - 1 - c}, 
   MeshStyle -> {{Thick, Black}}, Mesh -> {{0}},
   ContourStyle -> {Opacity[0.9], None}, 
   BoundaryStyle -> None]
  ]

curveSpherePlot[elliptic]

enter image description here

There is a variation leveraging the internal evaluation behavior of plotting functions that allows us to eliminate Block. It relies on the fact that elliptic will not be evaluated until after x, y, and z are localized by ContourPlot3D, and the time at which the function is evaluated. This may be viewed as a hack as it relies on undisclosed internal behavior that may change between versions.

x = y = z = "Fail!";

elliptic := z y^2 - x^3 + z^2 x

SetAttributes[curveSpherePlot, HoldFirst]

curveSpherePlot[c_] := 
 ContourPlot3D[{x^2 + y^2 + z^2 == 1, c == 0}, {x, -1, 1}, {y, -1, 1}, {z, -1, 1}, 
  MeshFunctions -> Function @@ Hold[{x, y, z}, x^2 + y^2 + z^2 - 1 - c], 
  MeshStyle -> {{Thick, Black}}, Mesh -> {{0}},
  ContourStyle -> {Opacity[0.9], None}, 
  BoundaryStyle -> None
 ]

curveSpherePlot[elliptic]

enter image description here

share|improve this answer
    
This answer is very satisfying, and explains the issue deeply. I switched my official 'answer' tag to this one even though Wouter's answer did solve the problem. I hope that's accepted etiquette. –  Bryan Clair Jun 27 at 2:25
    
@Bryan I am glad this answer is satisfying, and thank you for the Accept. Yes, it is normal practice to change the Accept as you see fit. Some people choose the first answer that works, some choose the one they end up using, some choose the one they feel is most correct, and some I frankly have no idea. :^) While I will sometimes advocate the Accept for a specific answer (mine or not) one should never fear anger or retribution for Accepting whatever answer pleases most. –  Mr.Wizard Jun 27 at 2:36

Your original was ok, apart from the incorporation of the Function[{x,y,z}, body]inside the Plot function. You should pass it as an argument instead:

mymesh=Function[{x,y,z},x y];
myplot[mesh_]:=Plot3D[x^2+y^2,{x,-1,1},{y,-1,1},MeshFunctions->mesh];

and then myplot[{mymesh}] or even myplot[mymesh].

Taking into account your ammendment:

 elliptic=z y^2-x^3+z^2 x;
curveSpherePlot[c_]:=Block[{temp=x^2+y^2+z^2-1-c},ContourPlot3D[{x^2+y^2+z^2==1,c==0},{x,-1,1},{y,-1,1},{z,-1,1},MeshFunctions:>{Function[{x,y,z},temp]},MeshStyle->{{Thick,Black}},Mesh->{{0}},ContourStyle->{Opacity[0.9],None},BoundaryStyle->None]];
curveSpherePlot[elliptic]
share|improve this answer
    
This would work, but for external reasons I can't change the definition of mymesh. I've posted a less simplified example to make the challenge a bit more clear. –  Bryan Clair Jun 25 at 18:06
    
Beautiful. I had a feeling there was something magical along the lines of :> that would help. –  Bryan Clair Jun 25 at 21:06
mymesh[x_, y_] := x*y

myplot[mesh_] := Plot3D[x^2 + y^2, {x, -1, 1}, {y, -1, 1}, MeshFunctions -> mesh]

myplot[mymesh[#1, #2] &]

enter image description here

ADDENDUM

f1 = x^2 + y^2 + z^2 - 1;

f2 = z y^2 - x^3 + z^2 x;

curveSpherePlot[c_] := ContourPlot3D[
  x^2 + y^2 + z^2 == 1.0001, {x, -1, 1}, {y, -1, 1}, {z, -1, 1}, 
  MeshFunctions -> c, PlotPoints -> 50]

curveSpherePlot@Function[{x, y, z}, f1 - f2]

enter image description here

The problem is that I haven't found a way to combine two pure functions in the way you suggested:

Function[{x, y, z}, x^2 + y^2 + z^2 - 1 - c]

So let's hope a better solution comes forth :)

share|improve this answer
    
Thanks for the suggestion. This would work, but essentially changes the question by redefining mymesh. I've posted a less simplified example to make the challenge a bit more clear. –  Bryan Clair Jun 25 at 18:12
mymesh = x$ + y$;
myplot[mesh_] := 
 Plot3D[x^2 + y^2, {x, -1, 1}, {y, -1, 1}, 
  MeshFunctions -> {Function[{x, y, z}, mesh]}]
myplot[mymesh]

enter image description here

share|improve this answer
1  
I strongly advise against this kind of code. You use the specific knowledge about certain implementation details, which makes this error-prone. –  Leonid Shifrin Jun 25 at 17:40
    
@Leonid Shifrin thanks for the advice. do you think there will be error for this particular problem? –  Algohi Jun 25 at 17:49
    
I don't know. Perhaps one can come up with some example, where the renaming will work differently. But I am opposed to this approach in principle. It's just not a robust code. If the renaming mechanism is, for example, changed in later versions, even slightly, this will stop working. –  Leonid Shifrin Jun 25 at 20:21

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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