How and why?
Mathematica Techniques
By Allan Hayes

For this issue,  I look at the use of conditions in assignments,  custom meshes for 3D graphics, a convenient way of representing polynomials  (a + b x + c (xy^5) ⟶ {{1, a}, {x, b},{(xy^5),  c}}), and removing vertical lines from plots of functions like tan(x).

Conditions in assignments and rules

In

f[x_ /; x > 0]  = 3

the part x_/;x>0 is called a condition, it is the special form of Condition[x_, x>0].

Where can conditions be put and are some places better than others? Are there any special features of Condition that need to be remembered? — let's see.

1. Most positions work and give the same answer

\(\(f1[x_ /; x > 0\ ] = \ 3; 
f2[x_] /; x > 0\  = \ 3; 

    f3[x_ /; x > 0\ ] := \ 3; 
f4[x_] /; x > 0\  := \ 3; 

    f5[x_]\  := \ 3 /; x > 0; \ \)\)

\({{f1[2], f2[2], f3[2], f4[2], f5[2]}, 

      	{f1[0], f2[0], f3[0], f4[0], f5[0]}}\)

The only exception is

f6[x_]  =  3 /; x > 0;

f6[2]

And even this can be simulated by

f7[x_] := Evaluate[3/;x>0];

f7[2]

2.  Condition must receive the data on which it is to decide

This is fine

g[x_, y_] /; x <= y  :=  y - x

but not this

g[x_, y_ /; x <= y]  :=  y - x

3

3. For efficiency the condition should be checked as early in the evaluation as possible; this fits in with keeping eveything together.

So the the earlier ones below are preferable

\(\(f3[x_ /; x > 0\ ] := \ 3; 
f4[x_] /; x > 0\  := \ 3; 

    f5[x_]\  := \ 3 /; x > 0; \ \)\)

4. Condition holds its second entry, but not its first entry

a = 3;

Condition[a + x_, x < a]

5. Condition always evaluates,  even at completely held positions:

SetAttributes[h,  HoldAllComplete];

h[x_ /; x > 100] := big;

h[101]

Rules

Analogous remarks hold for rules. For example the following does not work

2 /. x_ -> 3 /; x > 0

Custom mesh in 3D plots

The question of how to remove or change the mesh on 3D plots often comes up in mathgroup: a recent postig asked how to use different styles for the x- and y- meshes. Here are some experiments with Plot3D and ParametricPlot3D provoked by this query.  In addition to getting different mesh styles, we explore how to do this conveniently without needing to do any replotting.

Plot3D

First a simple plot with a thin mesh shown in black.

p1 = 
Plot3D[Sin[x y],{x,0,Pi},{y,0,Pi},PlotPoints -> 3];

The option Mesh -> False gives a plot without edges

p2 = Show[p1,  Mesh  ->  False];

The option MeshStyle will give a uniform style for the mesh

p3 = Show[p1,  MeshStyle -> {Hue[1/6],  Thickness[.03]}];

To do more we need to get at the parts of the mesh. How can this be done? The mesh is actually made up of the edges of the polygons that make up the surface: so we need to get at these polygons. However, SurfaceGraphics stores only the heights and only makes the polygons for display, so the first step is to convert it to Graphics3D, which does store the polygons..

gp1  =  Graphics3D[%];

From each polygon we can make edges as required. For example, with the rule

pe = 
Polygon[{a_, b_, c_, d_}] :> 
    Sequence[
    	Thickness[0.01], {Hue[0], 
    	Line[{a, b}], Line[{c, d}]}, 
    	{Hue[2/3], Dashing[{0.01, 0.02}], 
     	Line[{b, c}], Line[{d, a}]}
    ];

we get

msh1 = gp1 /. pe;

Show[msh1];

Both meshes can be shown together

Show[p3,  msh];

To use only the msh for the mesh we can either replace p3 with p2 or, more flexibly, and avoiding replotting, we can modify the option Mesh in p3 before displaying with Show:

edgeOff  = (Mesh -> _)  -> (Mesh  -> False);

Show[p3 /. edgeOff,  msh];

ParametricPlot3D

It should be easier with ParametricPlot3D since it gives Graphics3D objects with polygons already in them. However, that  Mesh -> False is not an option for ParametricPlot3D, we control the mesh  by using the directive EdgeForm[] in the optional fourth entry place in the input list.

pp1 = 
ParametricPlot3D[{x,y,Sin[x y]},{x,0,Pi},{y,0,Pi},
	PlotPoints -> 3, BoxRatios -> {1,1,.4}
];

Remove the mesh, using EdgeForm[] as the fourth entry in the input list:

pp2 = 
ParametricPlot3D[{x, y, Sin[x y], EdgeForm[]}, 
   {x, 0, Pi}, {y, 0, Pi}, 
   PlotPoints -> 3, BoxRatios -> {1, 1, 0.4}
];

Style the mesh, again using MeshForm.

pp3 = 
ParametricPlot3D[
	{	x,y,Sin[x*y],
		EdgeForm[Hue[1/6], Thickness[0.03]]
	}, 
    {x, 0, Pi}, {y, 0, Pi}, 
    PlotPoints -> 3, BoxRatios -> {1, 1, 0.4}
 ];

Create the more complicated custom mesh we used for Plot3D.

msh2  =  pp3 /. pe;

Show[msh2];

Surprise! the polygon vertices are listed in a different order from what we got from Plot3D.
I'll look at this further in the note at the end of this section. For now let's just carry on.

Show both meshes

Show[pp3,  msh2];

To get just msh2 we can use

Show[pp2,  msh2];

But, are there ways of changing the mesh in without re-plotting? We could do this with Plot3D because mesh was controlled by options. Here there is no suitable option and we have to operate on the polygons

edgeOff2  = p_Polygon  :>  Sequence[EdgeForm[],  p];

Show[pp1 /. edgeOff2] ;

Similarly, if we want to style the mesh without replotting, we do not have the option MeshStyle, so again we operate on the polygons:

edgeStyle[st___]  =  p_Polygon  :>  Sequence[EdgeForm[st],  p];

Show[pp1 /. edgeStyle[Hue[1/6],  Thickness[0.03]] ];

Examples

ex1 = ParametricPlot3D[
	{Cos[t]Sin[s], Sin[t]Sin[s], Cos[s]},
	{s, 0,Pi}, {t, 0,2Pi},
  	DisplayFunction -> Identity
];

Show[ex1 /. edgeOff2,  ex1 /. pe, 
      
DisplayFunction  ->  $DisplayFunction, 
      
Shading  ->  False, 
      
Boxed  -> False, 
      
Axes  -> False
];

Using similar methods we can get a wire mesh version. For example

Show[Graphics3D[
	Cases[ex1, p_Polygon :> (p /. pe), Infinity],
	Boxed->False
]];

But note that with Plot3D we can easily get wire mesh plots as follows

\(\(Plot3D[Sin[x\ y], {x, 0, Pi}, {y, 0, Pi}, 
	
      MeshStyle\  -> \ {Hue[0], Thickness[.01]}, 
	
      HiddenSurface\  -> \ False]; \)\)

Note: order of vertices

pn = Plot3D[x + 3  y, {x, 0, 2}, {y, 0, 2}, PlotPoints  ->  3];

Here is the matrix of heights  that is stored. Since the heights are all different it is easier to follow what is going on.

pn[[1]] // MatrixForm

>From this Graphics3D makes a list of polygons - I indicate the points by their heights.

(Graphics3D[pn][[1]] /. {_, _, h_} -> h) // ColumnForm

ppn = 
      ParametricPlot3D[{x, y, x + 3  y}, {x, 0, 2}, {y, 0, 2}, 
        PlotPoints  ->  3,  BoxRatios -> {1, 1, .4}];

(ppn[[1]] /. {_, _, h_} -> h) // ColumnForm

We see that by rotating each polygon's lists of points to the right we get the first list.

Coefficients

MatchedCoefficients behaves as follows

MatchedCoefficients[
e + a x + b y + c x y + f y^2 + h x^4 y^7, 
{x, y}]

The original polynomial is easily recovered:

Dot@@Transpose[%]

It's a good idea to read the full description of built in functions from time to time: the following simple code (stimulated by code posted by FredSimons to mathgroup) depends heavily on the optional third entry for Coefficient. Note also the use of Replace, which acts only on the whole expression –not on any proper subparts –  and of  u_. , to allow u_. * e to match  e (regarding it as 1*e)

MatchedCoefficients[expr_, vars_] := 
  Module[{h}, 
  	Replace[
  		Expand[Collect[expr, vars, h]], 
     	{e_Plus :> List @@ e, e_ :> {e}}
    ] /.{(u_.)*h[y_] -> {u, y}}
 ]

a =.

MatchedCoefficients[a  + b x  +  c x y^5,  {x, y}]

Avoiding vertical lines at discontinuites

We often wish to avoid the sort of spurious vertical features shown in

plt = Plot[Tan[x],  {x,  -2 Pi,  Pi}];

Using Split we can write a function that operates on Graphics objects  and splits lines at points where the absolute slope gets big,

\(\(\ \(DeleteVerticals[gr_Graphics, \ slopeMax_] := 
	
      gr /. Line[lst_]\  :> \ 
          
Map[Line, 
				\ \ \ 
            Split[lst, Abs[Divide@@\ Reverse[#1\  - #2]] < slopeMax\ &], 
            
1]; \)\)\)

The pure function for absolute slope of the join of successive points on the line is based on

Absolute[Divide@@Reverse[{x1, y1} - {x2, y2}]]

Test this on the previous plot.

Show[DeleteVerticals[plt,  5000]];

This can be refined in many ways:  
1. for example to act more automatically by getting a values for slopeMax based on the perceived slope rather than underlying mathematical slope  — for this we would need to find the plot range and aspect ratio actually used, this can be done with FullOptions.
2. One-point lines like, Line[{{0,0}}], may be made by DeleteVerticals. These are displayed as points. They could be deleted with the help of DeleteCases - maybe used in as option to DeleteVerticals.