There is way to style individual Contour
s:
{{f1, gr1},...}
contours drawn with graphics directives gri
and a way to have a function compute the contours for you:
func
a function to be applied to zmin
, zmax
to get the list of contours
Unfortunately they do not seem to cooperate -- that is, if func
returns a list of contour levels and graphics directives {{f1, gr1},...}
, all I get is a gray square for the plot. No error warnings, no contours. If this is by design, it seems that it leaves room for improvement.
However, one can take a two-step approach. Below is a function that does one contour plot to compute the list of styled contours and another to compute the image. To improve speed, by default, MaxRecursion
is set to 0
; this can be overridden. All we really need to know to compute the contours is the maximum and minimum of the plotted function over the domain. There are various ways to compute this. ContourPlot
is one. Sometimes human inspection is quick enough. If I can assume quality is more important than speed, then a double ContourPlot
is reasonable.
I made a some attempt to provide an interface with ContourPlot
, but the variety of forms of the options, especially Contours
and ContourStyle
, and my limited time led to a partial implementation.
The function cFn
yields a Function
that computes the styled contours. Each contour has to be styled with this form of the option Contours
. I apply the ContourStyle
cStyle
, which needs to be a single style, to the union of the user-styled and the automatically generated contours. The styles of the user-styled contours are then replaced by the styles the user specifies. The integer nContours
represents the "target" number of contours to be returned by FindDivisions
; it won't be the exact number of contours due to the workings of FindDivisions
.
If the max/min of the function are known, then contours = cFn[styledContours][min, max]
may be used to compute the value for the option Contours -> contours
directly.
To use cplot
, just pass a list of contours in the form {{f1, gr1},...}
, the same as for the option Contours
, followed by the arguments for ContourPlot
.
Clear[cFn];
cFn[styledContours_, nContours_: 10, cStyle_: {GrayLevel[0], Opacity[0.4]}] /;
ArrayDepth[styledContours] == 1 := cFn[{styledContours}, nContours];
cFn[styledContours_, nContours_: 10, cStyle_: {GrayLevel[0], Opacity[0.4]}] :=
Function[{min, max}, {#, cStyle} & /@
Union[First /@ N@styledContours, N @ FindDivisions[{min, max}, nContours]] /.
Thread[{#, cStyle} & /@ First /@ N @ styledContours -> styledContours]];
ClearAll[cplot];
SetAttributes[cplot, HoldAll];
cplot[styledContours_, fn_, dom1_, dom2_, opts : OptionsPattern[ContourPlot]] :=
Module[{contours},
ContourPlot[fn, dom1, dom2, PlotPoints -> OptionValue[PlotPoints],
MaxRecursion ->
If[OptionValue[MaxRecursion] === Automatic, 0, OptionValue[MaxRecursion]],
Contours -> ((contours = cFn[styledContours,
If[IntegerQ[OptionValue[Contours]], IntegerQ[OptionValue[Contours]], 10],
OptionValue[ContourStyle]][##]) &)];
ContourPlot[fn, dom1, dom2, Contours -> contours, opts]
]
Example
cplot[{0, {Thick, Red}}, (-1 + x)^2 (-1 + y) + x^2 y,
{x, 0, 1}, {y, 0, 1}, ContourLabels -> True]

I hope that helps. If Contours -> func
ever works the way I think it should, this would be a bit easier.
Contours -> (Table[i, {i, -.8, .8, .2}]~Join~{{0, Directive[Thick, Red]}})
$\endgroup$Contours -> (If[# == 0, {0, Directive[Thick, Dashed, Red]}, #] & /@ Range[-.8, .8, .2])
so it doesn't draw the 0-contour line twice. $\endgroup$