5
$\begingroup$

In the following code I'm plotting the contour $f(x,y)=0$ as well as the family of lines $y=mx$:

Manipulate[Show[ContourPlot[f==0,{x,-5,5},{y,-5,5}],Plot[m*x,{x,-5,5}]],{{f,x^2+y^2-1}},{m,-5,5}]

The problem is: while changing the slope $m$ I don't want to re-render the contour, since it makes it look jagged for a short while. Is there a way to make some parameters affect only one of the plots?

Thanks!

$\endgroup$
  • 2
    $\begingroup$ For this specific case you can put PerformanceGoal -> "Quality" in the ContourPlot. This will prevent slider movement from affecting the ContourPlot. 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$ – Szabolcs Jul 2 '15 at 15:23
  • $\begingroup$ @Szabolcs your comment resolves the issue, thanks! $\endgroup$ – user1337 Jul 2 '15 at 15:29
  • $\begingroup$ @Szabolcs actually, could you please elaborate on how to get different controls to affect different plots? $\endgroup$ – user1337 Jul 2 '15 at 16:11
  • $\begingroup$ I'll take a look tomorrow. I don't know that off-hand. I have to search the docs. The keyword is ControlActive. I'm not even 100% sure it's possible using documented functionality (though it likely is). $\endgroup$ – Szabolcs Jul 2 '15 at 17:34
4
$\begingroup$

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.

$\endgroup$
  • $\begingroup$ Thanks Michael. There is still an issue that bothers me: If I modify your code to be more computationally heavy, such as Manipulate[ With[{cplot = ContourPlot[f, {x, -5, 5}, {y, -5, 5}, PlotPoints -> 50, MaxRecursion -> 4]}, Dynamic@Show[cplot, Plot[m*x, {x, -5, 5}]]], {{f, x^2 + y^2 - 1}}, {m, -5, 5}] then the reaction time for the slider is slower than in the simpler code. It seems that the cost of changing $m$ still depends slightly on the contour plot. Any insights on this? $\endgroup$ – user1337 Jul 8 '15 at 7:26
  • $\begingroup$ Without f it's impossible to be sure. It could be the tooltips (turn off with ContourLabels -> None); see mathematica.stackexchange.com/a/58949. It takes time just to display cplot even if it is not recomputed. If cplot is a large amount of data (e.g. b/c of PlotPoints/MaxRecursion), then the time it takes could be noticeable. (Add foo = cplot inside the Manipulate and after it's computed once, execute ByteCount[foo] outside the Manipulate to check the size.) Maybe something about f causes cplot to be recomputed (you'd notice the jaggedness, though). $\endgroup$ – Michael E2 Jul 8 '15 at 14:11
  • $\begingroup$ To be clear, I'm talking about the default f, namely $x^2+y^2-1$. $\endgroup$ – user1337 Jul 9 '15 at 4:50
  • $\begingroup$ @user1337 Then it's probably the nearly 70,000 polygons in cplot. I can see by monitoring the CPUs that the computational time is taken up by the front end, which does the rendering. $\endgroup$ – Michael E2 Jul 9 '15 at 5:26
  • $\begingroup$ @user1337 I remembered a couple of other approaches, which I've added. Each has advantages and drawbacks. $\endgroup$ – Michael E2 Jul 9 '15 at 15:52
3
$\begingroup$

Apart from szabolcs' dirty trick, a generic way to resolve this would be to precalculate one plot

g=ContourPlot[f==0,{x,-5,5},{y,-5,5}];

And then use manipulate with the precalculated plot

Manipulate[Show[g,Plot[m*x,{x,-5,5}]],{m,-5,5}]
$\endgroup$
  • 1
    $\begingroup$ But what if I want to manipulate on the function $f$ as well? $\endgroup$ – user1337 Jul 2 '15 at 16:11
  • $\begingroup$ That's a good question $\endgroup$ – yohbs Jul 2 '15 at 18:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.