Inspection
Let's take a look at what is being returned from the plot:
plot = DensityPlot[Exp[-((x-5)^2 + (y-5)^2)/5^2], {x,0,10}, {y,0,10}, PlotRange -> {0,1}]
The output mainly consists of a big GraphicsComplex:
In[2]:= Head[Plot[[1]]]
Out[2]= GraphicsComplex
with color specified by VertexColors:
In[3]:= Short[Options[plot[[1]]]]
Out[3]= {VertexColors->{<<1>>}}
So in theory, by replacing VertexColors with other color values, you can change the colors without reevaluation.
Good News
By default, DensityPlot
uses color functions on z values and it is scaled (ColorFunctionScaling
->True
), which makes our task a bit easier since scaled z-values between 0 and 1 will be applied to color functions.
Bad News
We need z-values because we need to apply color functions on them. But, the color function is already evaluated, and only RGB values are stored in VertexColors
. Thankfully, the default color function is 1-1 function, but there is no such guarantee in general. Also, the inverse function of the color functions are not trivial (not that hard, just will take some time to compute--beating the purpose of not re-evaluating).
Solution
A bit of cheating. But let's set up the graphics a bit so that it actually contains useful information. Instead of the default color function, let's use GrayLevel
.
plot = DensityPlot[Exp[-((x-5)^2 + (y-5)^2)/5^2], {x,0,10}, {y,0,10},
PlotRange -> {0,1}, ColorFunction -> GrayLevel]
If you take a look at the vertex colors again, now it is in fact the same value as the scaled z-values of the function (and nicely packed too).
In[5]:= Short[Options[plot[[1]]], 4]
Out[5]= {VertexColors->{0.,0.0475548,0.0989165,0.150418,0.197553,0.23559,<<238>>,0.0113745,0.0113745,0.0113745,0.0113745,0.0113745}}
From here, now it is just a matter of applying a color function to the list of vertex colors--which is indeed scaled z-values--and replace it with the new values.
The position of VertexColors
within output can be inspected by the following:
In[6]:= Position[Plot, HoldPattern[VertexColors -> _]]
Out[6] = {{1, 3}}
Usually, the positions in plot outputs are pretty stable unless you change options that modifies geometry (such as Exclusions
), and there is a slight worry that Position
may cause unpacking of nicely packed plot outputs (read this), so let's stick to this value for a while (of course, actual replace should be {1, 3, 2}
, the second argument of the rule). A simple test:
ReplacePart[plot, {1,3,2} -> (ColorData["Rainbow"] /@ plot[[1, 3, 2]])]
It works nicely. Now with Manipulate
:
Manipulate[
ReplacePart[plot, {1,3,2} -> (ColorData[gradient] /@ plot[[1, 3, 2]])],
{gradient, ColorData["Gradients"]}]
Problems
If the goal is to completely avoid Kernel evaluation, there is no easy solution--if not impossible, since ColorData
will rely on the Kernel evaluation anyway.
Calling ColorData
(and any other color functions, such as Blend
or Hue
for that matter) manually like this is not efficient in two ways;
First, internal plot functions do a lot of optimization for calling color data. Essentially it is getting a treatment like CompliedFunction
, but more specialized. By manually calling them, you are not getting much speed up.
Secondly, the output form is a list of RGBColor[...]
which makes the whole result unpacked. Not efficient memory-wise.
There is a way to achieve maximum efficiency by turning ColorData
into a linear interpolation function of triples and using CompiledFunction
or InterpolatingFunction
, but I frankly don't think that it is worth...
ColorFunction
for a select one or two of them. $\endgroup$plot
is a graphics object, it does not retain information of the underlying mathematical function used to construct it (i.e. you cannot access the "z" values). You can replace the VertexColors in the graphics object, but you'd need to map rgb values between colormaps. $\endgroup$