How and why? 9.2
Mathematica Techniques
By Allan Hayes

Smooth graphics with sparse meshes;  contexts, Get, Needs, how packages work.

Examples

If you are using the electronic version of "Mathematica in Education and Research" and wish to evaluate the inputs below then  store the  package containing Smooth3D so that its context address is
	"haypacks`Graphics`Smooth3D`";

Let's draw a pear shape inside a loose net.

<<haypacks`Graphics`ParametricPlot3D`

SphericalPlot3D[{1-0.3/(1+(0.7+Tan[theta-Pi/2])^2),{SurfaceColor[Hue[1/6,.3,1],Hue[1/6,1,1],8]}},{theta,0,Pi},{phi,0,2Pi},Boxed->False,Axes->False,Compiled->False,AmbientLight->GrayLevel[.3],PlotPoints->8];

Too angular ! But if we increase PlotPoints to smooth it out then we get too many black lines.

SphericalPlot3D[{1-0.3/(1+(0.7+Tan[theta-Pi/2])^2),{SurfaceColor[Hue[1/6,.3,1],Hue[1/6,1,1],8]}},{theta,0,Pi},{phi,0,2Pi},Boxed->False,Axes->False,Compiled->False,AmbientLight->GrayLevel[.3],PlotPoints->30];

In response to this kind of problem Hartmut Wolf and I produced a function Smooth3D that allows independent control of the number of mesh lines and plot points. A package containing the function is available in the electronic version of this article, and I shall discuss how packages work and give the package code later.

<<haypacks`Graphics`Smooth3D`

We can now get

Smooth3D[SphericalPlot3D[
{1 - 0.3/(1 +(0.7 + Tan[theta - Pi/2])^2), 
{SurfaceColor[ Hue[1/6, .3, 1], Hue[1/6, 1, 1], 8]}}, {theta, 0, Pi}, {phi, 0, 2Pi},
Boxed -> False, Axes -> False, Compiled -> False,  AmbientLight -> GrayLevel[.3], PlotPoints -> 8
],  MeshLines->12, MeshStyle -> Hue[0.25, 1, 0.7], Smoothing -> 9
];

Here are some more examples.

Smooth3D[CylindricalPlot3D[
{Sin[5f], {Hue[f/(.4Pi), .4, 1]}}, 
{r, 2, 4}, {f, 0, 2 Pi}, 
Lighting -> False
], Smoothing -> {1, 5}, MeshLines -> {3, 0}, MeshStyle -> Hue[0]
];

Smooth3D[Plot3D[
{Sin[x + Cos[y]], Hue[x, .4, 1]}, 
{x, 1, 4}, {y, 1, 5}
], MeshStyle -> {{Thickness[.015], Hue[0.9]}, {Thickness[.01], Hue[.7]}}, MeshLines -> {3, 5}
];

Smooth3D[ParametricPlot3D[
{{x, y, Sin[x + Cos[y]], {Hue[(x + y)/12, .5, 1]}}, {x, y, Cos[x + Sin[y]], GrayLevel[.5]}}, 
{x, 1, 4}, {y, 1, 5}, 
Lighting -> False, Background -> Hue[0.15, 0.15, 1]
], MeshStyle -> {{Thickness[.015], Hue[0]}, {Hue[.7]}}, MeshLines -> 4
];

Packages

Packages are text files containing code that can entered by "loading" the package. For example,  <<Graphics`Arrow` finds the file "Arrow.m" and enters the code found in it. Besides being convenient they automatically keep package symbols distinct from user symbols and the same symbols in other packages and they avoid problems like

powers[x_]:= Table[x^i,{i,3,4}];

giving

powers[i]

instead of  {i^3, i^4}.

Please see at the end of the section "Loading package code", below, for how this is done.

Of course we could deal with this particular using Module.

powers2[x_]:= Module[{i}, Table[x^i,{i,3,4}]];

powers2[i]

In this section I will look in detail at what is happening in such situations.

Internal form: symbol contexts

Supporting the package behaviour is the system of contexts (rather like a directory system).

Internally, the only symbols that Mathematica uses are full names of the form c`s, where c and s are symbols, c` does not begin with the context sign ` and s does  not contain `. The string "c`"  is called the context of c`s and s is called its short name.
Names that are not full are said to be incomplete, they can begin with `. We normally input and see only short names: how this is managed is explained below.

As new full names, c`s, occur in evaluations their strings "c`s" are added to the list Names[]. We then say the s and c`s  exist in or have been created in the context "c`".

At any time Mathematica has a current context, say "cc`", given by $Context, and a current context path {"cp`"...} given by $ContextPath. The context "`System`" must be in the context path for Mathematica to work, it is usually the last entry. 
We call "cc`", "cp`"... the  active contexts.
Short names with no corresponding long name in an active context are said to be new.
The default context and context path when Mathematica  is loaded are "Global`" and {"Global`", "System`"} but the context path may contain more if some packages have been loaded automatically at start-up.
For convenience, long names are displayed as their short names if their context is active.

When an expression, expr, is entered, then before any evaluation is done, a copy of it is stored and converted to internal form by scanning its symbols and replacing incomplete names using the first applicable of the following rules 
	`a  ⟶ cc`a 
	   s  ⟶  c`s where "c`" is the first active context in which s occurs 
	   s  ⟶  cc`s	
As new long names, c`s,  are found or made, their strings "c`s" are added to the list $Names[], we call this creating  c and c`s in the context "c`" and say that they now exist in this context (name do not exist until they have been stored!).
Mathematica generates a warning of shadowing if a long name is created in active context when its short name already exists in other active contexts.

Loading packages: context addresses

The list $Packages is important for managing package loading:
It begins as {"Global`", "System`"}when the kernel is loaded, and from then on whenever the first line of a package code, BeginPackage["pkc`", "nc`",...], is evaluated the package context "pkc`" is added to $Packages (created).

Get is a general purpose function for loading (evaluating the code in) files.
Get["fp"], for a file path "fp",  looks for "fp" in the directories inside $Path and, if one is found, enters its lines one by one. 
Get can be used like this to load packages, but the file path then depends on the operating system. Instead, Get can use contexts as platform-independent context addresses. The Windows system for example gives

	 Get["c`g`"](or <<c`g`) ⟶ Get["c\g`"]

Notice that for a package to load in this way it must be in one of the directories inside $Path and the context address must supply Get with its relative address. A good place for extra packages is in AddOns\ ExtraPackages.

Needs["c`g`"] can be used instead of << c`g` and gives more control and protection (you may not wish to use Needs, but it should be understood since it is used automatically in the background when a package is loaded).
• Needs only accepts contexts;
• If  "c`g" is in $Packages then no loading is done - the package has already been loaded. This saves time, prevents infinite cycles of loadings, and avoids warning messages from packages that are not designed to be reloaded – I will show later how packages can be made reloadable. But it only does these things if the contexts of packages are the same as their context addresses; and unfortunately this entails changing the context if, for example, a package is moved. Needs warns about this:
• If the package context is not "c`g" then  Get["c`g`"] is still evaluated but a warning is generated that the context "c`g" has not been "generated". 

Get or Needs? I prefer <<... for interactive loading: it is shorter, and it lets me edit the package code and reload when the package is set up as outlined below.

Evaluating package code

Package text usually begins with standard information about the package in comments, (*..*)

(* :Title:title*)
(* :Context:"pkc`"*)
(* :Author: J.D. *)
....

Suppose that the context and context path are 
	 "cc`", {"cp`"...}

Outside packages and special investigations these will usually be of the form
	"Global`", {"Global`", ..., "System`"}

The code evaluation begins with an input line like

BeginPackage["pkc`", "nd`",...]

where "pkc`" is the package context and "nd`" ... are the context addresses of the needed packages whose functions are used in the subsequent code.
This does three things: it

1.  evaluates Needs["nd`"] ...  and adds the contexts of loaded packages (including those evaluated as needed packages of needed package and so on) to $Packages (cyclic evaluations are prevented since Needs will not reload packages — provided package contexts are the same as their context addresses). 

2. stores what will be the current context and context path after the package has been loaded:
	"cc`" and {"pkc`", the contexts just created  in 1.,"cc`","cp`"...}

3. sets the current context and context path to 
	"pkc`", {"pkc`","nd`"..., "System`"}
These will be use as explained above in creating internal forms;  in particular,  symbols in contexts other than these are now available only by their full names; however, to be able to include warnings of shadowings that might exist after the package has been loaded, Mathematica adds these contexts to those stored at 2. above when checking for shadowing.

Pitfall: if loading a needed package in 2. above created a symbol in a context "nnd`", then this symbol can be accessed inside the package code by its short name only if  "nnd`" is in "nd`"... (even if "nnd`" had been on the original context path, {"cp`"...},  and although it will be in the context path after the package has been loaded). So, take care to include all contexts that will be needed in "nd`"... .

[You may think that it unsatisfactory to have to search inside needed packages to find what packages they need. In fact we can avoid this  by loading each of the needed packages

<<nd`            (*A*)
..

This must be with Get not with Needs otherwise the loading may not be done. This works for the code inside the package but please note that we must still keep "nd`" ...  as needed packages since new contexts that are introduced by <<nd` ...  will not be on the context path after the package is loaded (this is why loadings like (*A*) are called hidden.)  ]

From now until Begin[] new short names will be created in the package context ,"pkc`".  Since "pkc`" will be on the context path , in the first place, after the package has been loaded such ss are said to be exported. 
But before creating the exported symbols I always arrange for reloading by using

Unprotect["`*"]; 
ClearAll["`*"];

Unprotect["`*"] is used because the exported symbols will be protected after End[] so as to prevent accidental changes outside the package. Without this unprotecting, reloading would generate warning messages and could not be used to redefine exported symbols.
ClearAll["`*"] gives a fresh start on reloading.

Exported symbols are usually created when defining their usage messages:

powers::usage= "powers[x] gives {x^3,x^4}";

The internal form of this is powers is pkc`powers.

The next stage is to define the behavior of the exported symbols. But now, not only do we want to avoid inadvertent use of names not in the active contexts, we also want any new short names to be created in a new context that will not be active after the package has been loaded. This is achieved by simply changing the current context

Begin["`Private`"]

The current context is now "haypacks`Graphics`Smooth3D`Private`"; the context path is unchanged. From now until End[](below) all new short names will be created in the context pkc`Private which will not be active after the package has been loaded. For example:

powers[x_]:= Table[x^i,{i,3,4}]

becomes the internal form

System`SetDelayed[pkc`powers[pkc`Private`x_],
  System`Table[pkc`Private`x^pkc`Private`i,
  	System`List[pkc`Private`i,3,4]
  ]
]

Having defined the behaviour of the exported symbols we go back to the package context.

EndPackage[]

The current context is now "pkc`"; the context path is unchanged.
Protect the exported symbols

Protect["`*"]

Finally, context and context path are set to the values stored at  2. above.

End[]

The context and context path are now
	"cc`" and {"pkc`", the contexts created  by 1.,"cc`","cp`"...}

What has been achieved? Well, the evaluation of powers[i] now proceeds as follows

powers[i] ⟶ 
pk`powers[cc`i] ⟶ 
Table[cc`i^pk`Private`i,{pk`Private`i,3,4}] ⟶
{cc`i^3, cc`i^4} ⟶
{i^3, i^4}

Of course we could make a mess of this by using powers[pk`Private`i].

Hidden Import

It was noted above that if in

BeginPackage["pkc`", "nd`" ...]

"nd`" itself needs a package  "nnd`". Then this may be evaluated but "nnd`" will not be active unless it is itself included amongst the needed packages:

BeginPackage["pkc`", "nd`", "nnd`" ...]

There is a problem here: we have to search through the needed packages of the package "nd`" and their needed packages and so on. To avoid this we could simply use

BeginPackage["pkc`"]
<<nd`                                  (*not Needs["nd`"]*)

After this the current context will be "pkc`" and the context path will be 
	{"pkc`", "nd`", "nnd`" ...}.

However, while this is fine for use inside the package,  it gives another problem: after the package is loaded the context "nnd`" is not on the context path. This is why this way of introducing required packages is called hidden import. A less serious problem is that "nd`" may be loaded a second time.

Loading Smooth3D`

In this section 
	package inpt looks like this and package coomments look like (*this*)or  (*this*);
	other input, used for illustration,  looks like this.

You can enter the inputs to simulate what happens when the package is loaded.
 But:
• quit the kernel before starting, to give a clean start;
• please be careful not to miss out any of the package code - quit the kernel and restart if you do. 
To avoid this problem you can simply select this section and evaluate it as a whole.

Let's set up a non-default initial situation.

<<Graphics`FilledPlot`

We now have

{$Context,$ContextPath}

$Packages

"Utilities`FilterOptions`" appears because it is a needed package for Graphics`FilledPlot`.

To illustrate shadowing create Smooth3D in the context "Global`".

Smooth3D;

I have put the package file "Smooth3D.m" in the directory C:\ProgramFiles\WolframResearch\Mathematica\4.0\AddOns\ExtraPackages\haypacks\Graphics\.
I could load it by entering  <<haypacks`Graphics`Smooth3D`, but here I am going to explicitly step through the code in this notebook.
I shall ignore the initial lines of information.

(*BEGIN PACKAGE CONTEXT*)
BeginPackage["haypacks`Graphics`Smooth3D`","Graphics`ParametricPlot3D`","Utilities`FilterOptions`"];

$Packages

{$Context, $ContextPath}

"Utilities`FilterOptions`" will not have been reevaluated.

Unprotect["`*"];
ClearAll["`*"];

(*USAGE MESSAGES*)

The new line symbols,"\n", in the messages are used to control the layout of the messages as seen by the user.

Smooth3D::usage="Smooth3D[plotfn[.,{x..},{y..}], MeshLines -> {xln ,yln}, MeshStyle -> {xstl, ystl}, Smoothing -> {sx,sy}] gives xln mesh lines with style xstl at equally spaced x values, and with the x-spacing between the plot points not more than 1/sx times that for plotfn[.,{x..},{y..}](similarly with x replaced by y). Where plotfn can be Plot3D, ParametricPlot3D, CylindricalPlot3D or SphericalPlot3D. The defaults for the options are 
	\n Smoothing -> 1 {equivalent to {1,1}} 
	\n MeshLines -> Automatic 
	\n \t(giving same numbers of mesh lines as the original) 
	\n MeshStyle -> Automatic
	\n\n Examples: please enter Smooth3DExamples.
	\n Input cells will be made below the entry cell.
	";

This tells us that Smooth3D already exists in the context "Global`". It will appear in the notebook that you are working in. The warning also shows that  "Global`" as well as the active contexts is being checked to find shadowing.
The package code for haypacks`Graphics`Smooth3D will be stored as we want - it is just that after loading we will need to remove the Global`Smooth3D , by using Remove[Smooth3D] to get at it from the short name Smooth3D.

The rest of the usage messages follow.

Smooth3DExamples::usage="Smooth3DExamples generates a sequence of Smooth3D examples in separate input cells immediately after the entry cell";

Smoothing::usage="Smoothing -> {sx, sy}, sx, sy positive Integers, 
	gives an x-spacing between the plot points not more than 1/sx times that in the original plot(similarly with x replaced by y). 
	\n Smoothing -> n is equivalent to Smoothing -> {n,n} 
	\n Default is Smoothing -> 1";

MeshLines::usage="MeshLines -> {xln ,yln}, where xln, yln are integers > 1, gives xln mesh lines at equally spaced x values, similarly for yln. 
	\n MeshLines -> {0 ,yln} gives no mesh lines at x values. 
	\n MeshLines -> {Automatic,yln} gives the same number of mesh lines at x values as in the original, 
	\n MeshLines-> mln (not a list) is equivalent to MeshLines -> {mln, mln} 
	\n Default is MeshLines -> Automatic, giving the same mesh lines as in the original.";

The final usage message code has to be more complicated because the option MeshStyle already exists in the context "Symbol`" which is on the current context path, so MeshStyle will be understood as System`MeshStyle.  
We want to append some text to its usage message explaining its role with Smooth3D; but we also need to arrange that repeated loadings of the package will not append further copies of this to the message. We must also allow for the unlikely event that the message is turned off.

If[!StringMatchQ[#,"*Smooth3D*"],
MeshStyle::usage =(a`a =(#<>
"\n\nWith Smooth3D, MeshStyle ->{xstl,ystl} gives the style xstl to the mesh	lines at constant x values, and the style ystl to the mesh lines at constant y values; 
			\n MeshStyle -> stl is equivalent to MeshStyle -> {stl, stl}. 
			\n Default is MeshStyle -> Automatic, which gives MeshStyle -> {{},{}} "
))]&[Cases[{MeshStyle::usage},_String, 2][[1]]];

(*BEGIN PRIVATE CONTEXT*)
Begin["`Private`"];

{$Context, $ContextPath}

To make a smoother plot of a function we need to know, amongst other things, what function is being plotted. To find this we must hold the expression being operated so that this information can be extracted before the expression is evaluated to a graphics object.

SetAttributes[Smooth3D,HoldFirst];

(*specify default options*)
Options[Smooth3D]={MeshStyle->Automatic,MeshLines->Automatic,Smoothing->1};

(*allow for a list of plotted functions*)Smooth3D[(plotfn_)[{f__List},xrng_,yrng_,opts___],lineopts___?OptionQ]:=Show[Block[{$DisplayFunction=Identity},Smooth3D[plotfn[#,xrng,yrng,opts],lineopts]&/@{f}],DisplayFunction->$DisplayFunction];

(*main code-for plotting a single function*)Smooth3D[(plotfn_)[fun_,xrng_,yrng_,opts___],lineopts___?OptionQ]:=Module[{stl,smth,lns,pp,lns2,mult,ppEnlarged,g,gNoMesh,gpoints,pointarray,xstl,ystl,xLines,yLines},
(*get option values*)
{stl,smth,lns}={MeshStyle,Smoothing,MeshLines}/.Flatten[{lineopts}]/.Options[Smooth3D];
pp=
PlotPoints/.Flatten[{opts,lineopts}]/.Options[plotfn]/.{Automatic->If[plotfn===Plot3D,15,20]};
(*standardize pp and lns to two numerical entry form*)
pp={1,1}pp;
lns=Replace[lns,{Automatic->pp,{Automatic,Automatic}->pp,{Automatic,yp_}->{pp[[1]],yp},{xp_,Automatic}->{xp,pp[[2]]},n_Integer->{n,n}}];
(*allow for no lines*)
lns2=lns/.{i_Integer/;i<2->2};
(* get minimum plotpoints, ppEnlarged, needed for the smoothing, smth*) 
mult=Ceiling[(pp-1)/(lns2-1) smth];
ppEnlarged=(lns2-1) mult+1;
(*make smooth graphics g: filtering options to those accepted by plotfn; suppress display with DisplayFunction->Identity *)
g=With[{plotopts=FilterOptions[plotfn,{opts,lineopts}]},plotfn[fun,xrng,yrng,DisplayFunction->Identity,PlotPoints->ppEnlarged,plotopts]];
(*if plotfn is Plot3D then g is a SurfaceGraphics object: transform it to a Graphics3D object so that the surface polygons are explicit*)
If[plotfn==Plot3D,g=Graphics3D[g]];
(*make the polygons in g edgeless - the required mesh will be added later*)
gNoMesh=g/.{{st___?(Head[#]=!=Polygon&),p_Polygon}->{st,EdgeForm[], \
p},p_Polygon->{EdgeForm[],p}};
(*make the styled mesh lines*)(*list of quadruples of points used in polygons*)gpoints=Cases[g,Polygon[pts_]->pts,Infinity];
(*if plotfn is Plot3D adjust gpoints*)
If[plotfn==Plot3D,gpoints=RotateLeft[gpoints,{0,1}]];
(*make gpoints into an array of dimensions ppEnlarged-1*)
pointarray=Map[First,MapAt[RotateRight[#,2]&,Transpose[Append[#,RotateRight[Last[#],{0,-1}]]&[Transpose[Append[#,RotateRight[Last[#],{0,1}]]&[Partition[gpoints,ppEnlarged[[1]]-1]]]]],{{-1,-1}}],{2}];
(*set the styles for the mesh lines*)
{xstl,ystl}=Replace[stl,{Automatic->{{},{}},{a_List,b_List}->{a,b},{a_List,b_}->{a,{b}},{a_,b_List}->{{a},b},{a___}->{{a},{a}},a_->{{a},{a}}}];
(*make the styled lines*)
xLines=Append[xstl,If[lns[[1]]==0,{},Line/@(Flatten[#,1]&/@Partition[Transpose[pointarray],1,mult[[1]]])]];
yLines=Append[ystl,If[lns[[2]]==0,{},Line/@(Flatten[#,1]&/@Partition[pointarray,1,mult[[2]]])]];
(*show combined lines and surface*)
Show[gNoMesh,Graphics3D[{xLines,yLines}],DisplayFunction->$DisplayFunction]];

(**EXAMPLES**)

Smooth3DExamples will generate a sequence of Smooth3D examples in separate input cells immediately after the entry cell

Smooth3DExamples:=CellPrint[Cell[#,"Input",GeneratedCell->False]&/@Reverse[{"Smooth3D[CylindricalPlot3D[\n{Sin[5f], {Hue[f/(.4Pi), .4, 1]}}, \n{r, 2, 4}, {f, 0, 2 Pi}, 
		\nLighting -> False
		\n], Smoothing -> {1, 5}, MeshLines -> {3, 0}, MeshStyle -> Hue[0]
		\n];",
"Smooth3D[Plot3D[\n{Sin[x + Cos[y]], Hue[x, .4, 1]}, 
		\n{x, 1, 4}, {y, 1, 5}
		\n], MeshStyle -> {{Thickness[.015], Hue[0.9]}, {Thickness[.01], Hue[.7]}}, MeshLines -> {3, 5}
		\n];",
"Smooth3D[ParametricPlot3D[
		\n{{x, y, Sin[x + Cos[y]], {Hue[(x + y)/12, .5, 1]}}, {x, y, Cos[x + Sin[y]], GrayLevel[.5]}}, 
		\n{x, 1, 4}, {y, 1, 5}, 
		\nLighting -> False, Background -> Hue[0.15, 0.15, 1]\n], MeshStyle -> {{Thickness[.015], Hue[0]}, {Hue[.7]}}, MeshLines -> 4
		\n]",
"Smooth3D[SphericalPlot3D[
		\n2, 
		\n{theta, 0, Pi}, {phi, 0, 2Pi}
		\n], MeshLines -> {5, 6}, LightSources -> {{{-2.7, 2., 2.5},GrayLevel[1]}}, MeshStyle -> {{Thickness[.01], Hue[0]}, {Thickness[.01], Hue[.7]}}
		\n];",
"Smooth3D[SphericalPlot3D[\n{1 - 0.3/(1 +(0.7 + Tan[theta - Pi/2])^2), 
		\n{SurfaceColor[ Hue[1/6, .3, 1], Hue[1/6, 1, 1], 8]}},
		{theta, 0, Pi}, {phi, 0, 2Pi},
		\nBoxed -> False, Axes -> False, Compiled -> False, 
		AmbientLight -> GrayLevel[.3], PlotPoints -> 15
		\n], 
		MeshStyle -> Hue[0.25, 1, 0.7], Smoothing -> 6
		\n];"}]];

(*END PRIVATE CONTEXT - revert to original*)
End[];

{$Context,$ContextPath}

(*Protect all symbols in the package context*)

Protect["`*"];

(*END PACKAGE CONTEXT*)
EndPackage[];

{$Context,$ContextPath}

Using the package

In view of the warning of shadowing, remove Global`Smooth3D

Remove[Smooth3D]

Get the examples from the package and evaluate them

Smooth3DExamples