Managing Manipulate
and other Dynamic
functionality is tricky. It takes some time reading the tutorials and experimenting to sort it all out. (There are four tutorials linked on the Manipulate
page that introduce you to the complicated issues of Dynamic
and Manipulate
that anyone interested in solving such issues needs to read.) Even then you might still get surprised now and then.
The trick is to separate the code segments that need updating using Dynamic
, Refresh
, and sometimes DynamicWrapper
; further, one needs to control which symbols in each segment are being "tracked." Refresh
and the option TrackedSymbols
are sometimes needed for that. When a symbol that is being tracked for a code segment changes, the code segment is re-executed (provided that the segment actually displays something in the front end). Now, that's all very complicated, but in the OP's case, the solution turns out to be quite simple:
Manipulate[
With[{cplot = ContourPlot[f == 0, {x, -5, 5}, {y, -5, 5}]},
Dynamic@Show[
cplot,
Plot[m*x, {x, -5, 5}]]
],
{{f, x^2 + y^2 - 1}}, {m, -5, 5}]
The whole body of a Manipulate
is always wrapped in Dynamic
. The Dynamic@Show...
inside the With
statement marks that segment as code that can be updated independently of the code outside. When inside code is updated, Mathematica just uses the stored plot in cplot
without recomputing ContourPlot
.
When does the code get updated? The inside Dynamic
depends on m
, but outside it, the ContourPlot
does not. Mathematica figures out that when m
changes, only the inside code segment needs updating. So when m
changes, the line is replotted but the ContourPlot
is not recomputed. On the other hand, the ContourPlot
depends on f
, so the whole With
statement will be recomputed whenever f
changes.
As the OP noticed, when a slider is actively being adjusted, the ContourPlot
gets jagged. This is because by default ContourPlot
and other plotting functions use ControlActive
to control the appearance of the plot when controls are "actively" being manipulated. Basically, they reduce the number of points that need to be computed to speed the computation; this makes the app respond more quickly to changes in the control. After manipulation of the control ceases, a new plot is computed with higher quality. In the code above, when m
is changed, the higher quality contour plot is already stored in cplot
; it is this higher quality plot that is displayed while m
is changed. Not having to recompute the contour plot keeps the app responsive and gives you a good-looking plot.
Alternative solutions
High, low resolution plots. From the comments, the OP seems interested in having a high-resolution contour plot. The problem is that if the plot is too big (too many points and polygons), the front end becomes less responsive. Can you make the demo responsive without reverting to the unsatisfactory default of PerformanceGoal
switching between "Quality"
and "Speed"
?
One way is to precompute both a high and low resolution plot as we did with cplot
above. One still has to balance quality and speed, and not every problem will be solvable. But I've used this approach before.
Manipulate[
With[{
cplotHigh = ContourPlot[f, {x, -5, 5}, {y, -5, 5}, PlotPoints -> 50, MaxRecursion -> 4],
cplotLow = ContourPlot[f, {x, -5, 5}, {y, -5, 5}, PlotPoints -> 25, MaxRecursion -> 1]},
Dynamic@ Show[ControlActive[cplotLow, cplotHigh], Plot[m*x, {x, -5, 5}]]],
{{f, x^2 + y^2 - 1}},
{m, -5, 5}]
Rasterization.
One can rasterize the contour plot, which will improve responsiveness. It will look fine at screen resolution, but not if the magnification is changed or the graphic resized. You can use Deploy
to prevent resizing. I suppose one could use CurrentValue
to get the magnification (perhaps CurrentValue[EvaluationCell[], Magnification]
).
Manipulate[
With[{
cplot0 = ContourPlot[f, {x, -5, 5}, {y, -5, 5}, PlotPoints -> 50, MaxRecursion -> 4]},
With[{cplot = Rasterize[cplot0]},
Dynamic@ Deploy@Plot[m*x, {x, -5, 5},
Prolog -> {Inset[cplot]}, Axes -> False, Evaluate@Options[cplot0]]]],
{{f, x^2 + y^2 - 1}}, {m, -5, 5}]
Note in both alternatives, the time to recompute when f
is changed is increased.
PerformanceGoal -> "Quality"
in theContourPlot
. This will prevent slider movement from affecting theContourPlot
. If you have two sliders and two plots, and you wanted the first slider to affect the quality of the first plot, but not the second, and you wanted the second slider to affect the quality of the second plot but not the first, then it would likely be quite a bit more complicated ... $\endgroup$ControlActive
. I'm not even 100% sure it's possible using documented functionality (though it likely is). $\endgroup$