Suppose Show
contains multiple graphics objects that differ markedly in the amount of computation needed for their display. Hence, when dynamically updating only those individual graphics changed by any underlying variable change should be redrawn. For example (and with apologies to Dutch patriots), in the following dynamic Show
, "pulling the red down" (by dragging the l.h.s locator) should take longer than "pulling the blue up" (by dragging the r.h.s. locator) since it is a more computationally intensive graphic (as simulated here by [email protected]
). Dragging the r.h.s. locator however, is equally sluggish due to the overarching Dynamic
wrapper causing both graphics to update whenever either locator is dragged. The logical solution would seem to be to wrap each inner Plot
graphic in a Dynamic
but Show
doesn't like this apparently expecting a pure graphic for each of its shown components.
DynamicModule[{top = {.02, 0.15}, bottom = {.08, 0.08}},
Dynamic@
Show[
Plot[([email protected]; top[[2]]), {x, 0, .1}, Filling -> Top,FillingStyle -> Red],
Plot[bottom[[2]], {x, 0, .1}, Filling -> Axis,FillingStyle -> Blue],
Graphics@Locator[Dynamic[top, (top[[2]] = #[[2]]) &]],
Graphics@Locator[Dynamic[bottom, (bottom[[2]] = #[[2]]) &]],
PlotRange -> {0, .2}, Axes -> False]]
Overlay
, on the other hand, has no such qualms as indicated by the following graphic being much more responsive to the r.h.s. VerticalSlider
"pulling the blue up".
DynamicModule[{top = {.02, 0.15},bottom = {.08, 0.08}},
{VerticalSlider[Dynamic[top[[2]]], {0, 0.15}],
Overlay[{
Dynamic@Plot[([email protected]; top[[2]]), {x, 0, .1}, Filling -> Top,FillingStyle -> Red, Axes -> False,PlotRange -> {0, .2}],
Dynamic@Plot[bottom[[2]], {x, 0, .1}, Filling -> Axis,FillingStyle -> Blue, Axes -> False, PlotRange -> {0, .2}]}],
VerticalSlider[Dynamic[bottom[[2]]], {0, 0.15}]}]
This is not a permanent solution as Overlay
doesn't enjoy Show
's more natural way of combining plots (more intuitive formatting, locator usage, ability to set common graphics options etc) so the question is; how can Overlay
's selective updating be replicated in Show
?
Answer Update:
The following answer is a hybrid taking from both Jens and Mr Wizard's responses which I think help to nicely illustrate some more general points about dynamic interface design. First, the combined answer:
SetAttributes[{DynamicShow, DynamicPlotRed, DynamicPlotBlue}, HoldAll];
DynamicShow[p_Plot] := Graphics[Dynamic@p[[1]], p[[2]]];
DynamicPlotRed[top_] := DynamicShow@Plot[([email protected]; top[[2]]), {x, 0, .1}, Filling -> Top, FillingStyle -> Red]
DynamicPlotBlue[bottom_] := DynamicShow@Plot[bottom[[2]], {x, 0, .1}, Filling -> Axis, FillingStyle -> Blue]
DynamicModule[{top = {.02, 0.15},bottom = {.08, 0.08}},
Show[
DynamicPlotRed@top,
DynamicPlotBlue@bottom,
Graphics@Locator[Dynamic[top, (top[[2]] = #[[2]]) &]],
Graphics@Locator[Dynamic[bottom, (bottom[[2]] = #[[2]]) &]],
PlotRange -> {0, .2}, Axes -> False]]
This includes the desired, lower level (indirect) use of Dynamic
to maintain the usefulness of its automatic, targeting updating as captured in Mr Wizard's answer. This is important as it allows a more general, more numerous adding of interface components (note the relevant Dynamic
sits in DynamicShow
to which I'll return). The approach in Jens' answer - having each Locator
directly update the corresponding plot, while working perfectly for the quoted two-element example, doesn't generalize in the sense that all plot updating now needs to be done "by hand" or via Dynamic
's second argument without harnessing Dynamic
's automatic updating (in full generality "Plot" can be an arbitrarily complex interface component with multiple, not-immediately-obvious dependencies).
There is a potential issue in that this solution relies on a consistent FullForm
being generated by Plot
but this can be addressed by adding a helper function. For example, using the PlotLegend
option changes the FullForm
structure but this can be handled as follows:
SetAttributes[{DynamicShow, DynamicShowLegend, DynamicPlotRed, DynamicPlotBlue}, HoldAll];
DynamicShow[p_Plot] := Graphics[Dynamic@p[[1]], p[[2]]];
DynamicShowLegend[p_] /; (! FreeQ[Hold@p, PlotLegends]) := Legended[Graphics[Dynamic@p[[1, 1]]], p[[2]]];
DynamicPlotRed[top_] := DynamicShow@Plot[([email protected]; top[[2]]), {x, 0, .1}, Filling -> Top, FillingStyle -> Red]
DynamicPlotBlue[bottom_] := DynamicShowLegend@Plot[{x, bottom[[2]]}, {x, 0, .1}, Filling -> {1 ->{Axis, Green}, 2 -> {Axis, Blue}}, PlotLegends -> True]
DynamicModule[{top = {.02, 0.15}, bottom = {.08, 0.08}},
Show[
DynamicPlotRed@top,
DynamicPlotBlue@bottom,
Graphics@Locator[Dynamic[top, (top[[2]] = #[[2]]) &]],
Graphics@Locator[Dynamic[bottom, (bottom[[2]] = #[[2]]) &]],
PlotRange -> {0, .2}, Axes -> False]]
The functions introduced in Jens' answer also illustrate a fundamental principle for building more complex dynamic interfaces. While introduced here to make Dynamic
's second argument more readable, this improved readability assumes much greater proportions as interfaces' complexify (essentially implementing modularization with all its attended advantages, as, for example, enumerated in Tom Wickham-Jones white paper). There is a wrinkle in performing such modularization when it comes to dynamic interfaces however, which can be illustrated by seeing how Mr Wizard's solution modularizes (in fact generalizes).
Essentially his insight was that Show
apparently (mostly) expects Graphics
heads in its arguments (with exceptions noted as above) so that wrapping one of these in a Dynamic
upsets this obstinance (which I regard as a design oversight). This can now however, at least be accommodated by giving Show
its cherished Graphics
head and instead wrapping Dynamic
around Graphic
's first argument via Graphics[Plot[...][[1]], AspectRatio->1/GoldenRatio]
. The AspectRatio
option corresponds to that generated automatically in this plot but in fact any generated option value can be included by defining
DynamicShow[p_Plot]:=Graphics[Dynamic@p[[1]],p[[2]]]
(n.b. The FullForm
of a Plot
output is Graphics[comps, opts]
)
The wrinkle is that in order to apply DynamicShow
in this way, its argument, p
, needs to avoid evaluation prior to being wrapped in Dynamic
to maintain its own "lexical scoping" (a form of code generation/injection). Consequently, this dynamic variable needs to be held right through to its final placement nestled inside a Dynamic
thereby requiring a chain of HoldAll
attributions.
In a fuller generality, the passed-through variable need not just be a local DynamicModule
variable holding a pair of numbers like top
and bottom
but instead can potentially hold an arbitrary large dataset. This suggests an updated, less-emulative, idiom for answering an earlier question about realizing such modularization in dynamic interfaces; that is, there appears to now be a sufficient level of mutability in the new associations/datasets for expressions like top[[2]]
to be fruitfully extended to expressions like interfaceData[[ ...]]
.
Dynamic
insideLocator
, just so you know. ("Cannot assign to raw object 0.08`" etc.) $\endgroup$Locator
containing a constant and not a variable. I posted a version that fixes this and also addresses theShow
issue. $\endgroup$Dynamic
's second argument up one's sleeve and answered the question as posed. Mr Wizard's answer, on the other hand, captured the preservation ofDynamic
's automatic tracking though lower-level placement of dynamic the actual sought-after idiom whose hinting lay in the accompanyingOverlay
example. On the other, other hand, Jen's modularization provided the opportunity to put this in a broader ... $\endgroup$