11
$\begingroup$

I want to figure out how to combine several graphics in manipulate such that only the ones that change are recomputed.

I looked into the tutorials on Advanced Manipulate, but couldn't figure out how to get this to work. In the toy example below, I combine two graphics with Show. When I move the control for the point p, the ContourPlot also gets recomputed, which leads to a low-res drawing (in my actual example, the graphs are a lot more complex, so re-evaluating them, even when the relevant parameters don't change isn't an option). I tried using Dynamic, but couldn't find a solution that works. Any help highly appreciated!

Manipulate[
Show[ContourPlot[Cos[a x] + Cos[ a y], {x, 0, 1}, {y, 0, 1}],
  Graphics[Disk[p, 0.02]]],
 {{a, 10}, 0, 20},
 {p, {0, 0}, {1, 1}}
 ]
$\endgroup$
8
$\begingroup$

TL;DR;

 Graphics[{
        Dynamic @ First @ plottingprocedure,
        Dynamic @ First @ anotherplottingprocedure,
        ...
      }, few options needed as we lost them above
    ]

Explanation

Independent interactive elements should not be gathered inside the same Dynamic.

And if they already are, due to an outer gui structure or, like here, simply because Manipulate's body acts like big Dynamic we can nest Dynamic. You can read more in:

How can we apply those rules to two plots that are supposed to be combined?

Every *Plot*/Graph* function evaluates to Graphics[primitives, options] which further typesets(?) to GraphicsBox[primitivesBoxes, options] and later FrontEnd takes care of rendering points/lines etc. Because it is done by the FE, primitives (options too) can contain Dynamic elements as long as they wrap something that makes sense. That is primitives/directives/coordinates. So something like this will work:

Graphics @ Dynamic @ Disk[] 

There is one but, Graphics can't contain Graphics:

Graphics @ Dynamic @ Graphics @ Disk[] (*pink box*)

so if we want to combine separate Graphics we need to extract their primitives (and options). Just by First @ Graphics..., as easy as that.

This is what you need:

Manipulate[
 Graphics[
  {Dynamic @ First @ ContourPlot[Cos[a x] + Cos[a y], {x, 0, 1}, {y, 0, 1}], 
   Dynamic @ First @ Graphics[Disk[p, 0.02]]
   }, PlotRange -> {{0, 1}, {0, 1}}], 
 {{a, 10}, 0, 20}, 
 {p, {0, 0}, {1, 1}}
]

Graphics[Disk[p, 0.02]] could be whatever plotting function but in such simple case just Disk[Dynamic @ p, 0.02] will work.

More examples:

Also, notice that performance of locator drops as the ContourPlot gets more complicated, it is related to performance of graphics renderer (as opposed to recalculation issue we had before), there are some tricks to improve it too. Though it is not a major issue here:

$\endgroup$
  • $\begingroup$ OK, you've convinced me. I am happy to learn something new about dynamic evaluation of graphics. $\endgroup$ – m_goldberg Jun 15 '17 at 10:34
  • $\begingroup$ @m_goldberg I'm glad I did, this is really something fundamental which should be included in docs as part of basic guidelines in dynamics. Unfortunately it is not. $\endgroup$ – Kuba Jun 15 '17 at 10:38
  • $\begingroup$ BTW, you can get away with just Dynamic @ Disk[p, 0.02] $\endgroup$ – m_goldberg Jun 15 '17 at 11:02
  • $\begingroup$ @m_goldberg correct, even Disk[Dynamic@p, 0.02] will do. Will change this later, with comments. Wanted to show a general way meanwhile. $\endgroup$ – Kuba Jun 15 '17 at 11:09
  • $\begingroup$ Thanks, this works nicely, but I noticed that in Kuba's solution the axis disappear in the contour plot. I couldn't figure out how to avoid this. The simpler solution of just using Dynamic in front of p doesn't apply for the more complex example I'm trying to do. $\endgroup$ – Beginner Jun 16 '17 at 11:43
2
$\begingroup$

Another approach which I use, is the tick method. The idea is to do the computation related to each control right there, using the second argument of dynamics. In this method, there is only one variable to track, which is the tick itself. I've described this method before in number of places. It is very flexible.

But with this method, one needs to initialize all the other variables for the initial display of Manipulate. I never found this an issue myself.

enter image description here

Manipulate[
 tick;
 Show[g1, g2],
 Grid[{
   {"a", Manipulator[Dynamic[a, {a = #;
        Block[{$PerformanceGoal = "Quality"},
         g1 = ContourPlot[Cos[a x] + Cos[a y], {x, 0, 1}, {y, 0, 1}]
         ]; tick = Not[tick]} &], {0, 20, .1}, ImageSize -> Small], 
    Spacer[3], Dynamic[a]}
   ,
   {"p", Slider2D[
     Dynamic[p, {p = #; g2 = Graphics[Disk[p, 0.02]]; 
        tick = Not[tick]} &], {{0, 0}, {1, 1}}, ImageSize -> Small]}
   }],

 (*Initialization*)
 {{a, 10}, None},
 {{p, {0, 0}}, None},
 {{g1, ContourPlot[Cos[10 x] + Cos[10 y], {x, 0, 1}, {y, 0, 1}]}, 
  None},
 {{g2, Graphics[Disk[{0, 0}, 0.02]]}, None},

 (*the tick is all what is needed to track*)
 {{tick, True}, None},
 TrackedSymbols :> {tick}
 ]
$\endgroup$

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.