# Why does Dynamic for PlotRange in ListLinePlot prevent the proper range from being used?

I am using Manipulate to simulate Brownian motion. I want to be able to zoom in. Thus, I want to use Dynamic for the PlotRange option so that this can be done quickly without recomputing the motion. It works properly without Dynamic (except for speed), but using Dynamic changes its operation so that the range is no longer what I ask for once I zoom in sufficiently. See this by changing the "horizontal zoom". I also have the same PlotRange in Show. It seems I need both. No doubt I am not doing other things optimally, but this issue is a separate problem. Here is my code, first without the first Dynamic and second with it. At the end is a smaller example. It behaves differently, but again the behavior depends on whether PlotRange has Dynamic or not. This last example contains both types of code in it. It comes in two forms, one using Show and one not.

Manipulate[
Module[{bmPath, iterate}, SeedRandom[seed];
iterate[l_List] :=
Module[{i = 1, len = Length[l], new = l},
While[i < Length[new],
new = Insert[
new, (new[[i]] + new[[i + 1]])/2 +
RandomVariate[NormalDistribution[0, .5/Sqrt[(len - 1)]]],
i + 1]; i = i + 2]; new];
bmPath = {0, RandomVariate[NormalDistribution[0, 1]]};
Do[bmPath = iterate[bmPath], {n}];
Show[ListLinePlot[bmPath, AxesLabel -> {"Time", "Location"},
DataRange -> {0, 1},
PlotRange -> {Clip[{hcenter - 2^(-zoom), hcenter + 2^(-zoom)}, {0,
1}], vcenter + {-2, 2}/2^(scale/2)}],
Plot[{Sqrt[x], -Sqrt[x]}, {x, 0, 1},
PlotStyle -> If[bound, Dotted, Opacity[0]]],
PlotRange ->
Dynamic[{Clip[{hcenter - 2^(-zoom), hcenter + 2^(-zoom)}, {0, 1}],
vcenter + {-2, 2}/2^(scale/2)}],
AxesOrigin -> Dynamic[{hcenter, vcenter}],
ImageSize -> {475, 360}]],
{{n, 10, "detail"}, 1, 40, 1, Appearance -> {"Labeled", "Open"},
AnimationRate -> rate}, {{rate, 1, "rate"}, 1, 3, 1,
Appearance -> "Labeled"}, {{bound, False,
"show sqrt bounds"}, {True, False}}, Delimiter, {{hcenter, .5,
"horizontal center"}, 0, 1, .01/2^(zoom - 1),
Appearance -> {"Labeled", "Open"},
AnimationRate -> .2/2^(zoom - 1)}, {{vcenter, 0,
"vertical center"}, -3, 3, .01/2^(scale - 1),
Appearance -> {"Labeled", "Open"},
AnimationRate -> .1/2^(scale - 1)}, Delimiter, {{zoom, 1,
"horizontal zoom"}, 1, 20, 1,
Appearance -> {"Labeled", "Open"}}, {{scale, 1,
"vertical scale"}, -2, 20, 1,
Appearance -> {"Labeled", "Open"}}, Delimiter, {{seed, 77777,
"new random case"}, 10000, 999999, 1, Appearance -> "Open",
AnimationRate -> newrate}, {{newrate, 1, "rate of new case"}, 1, 10,
1, Appearance -> "Labeled"},
ControlPlacement -> Right, SynchronousUpdating -> False]

Manipulate[
Module[{bmPath, iterate}, SeedRandom[seed];
iterate[l_List] :=
Module[{i = 1, len = Length[l], new = l},
While[i < Length[new],
new = Insert[
new, (new[[i]] + new[[i + 1]])/2 +
RandomVariate[NormalDistribution[0, .5/Sqrt[(len - 1)]]],
i + 1]; i = i + 2]; new];
bmPath = {0, RandomVariate[NormalDistribution[0, 1]]};
Do[bmPath = iterate[bmPath], {n}];
Show[ListLinePlot[bmPath, AxesLabel -> {"Time", "Location"},
DataRange -> {0, 1},
PlotRange -> Dynamic[{Clip[{hcenter - 2^(-zoom), hcenter + 2^(-zoom)}, {0,
1}], vcenter + {-2, 2}/2^(scale/2)}]],
Plot[{Sqrt[x], -Sqrt[x]}, {x, 0, 1},
PlotStyle -> If[bound, Dotted, Opacity[0]]],
PlotRange ->
Dynamic[{Clip[{hcenter - 2^(-zoom), hcenter + 2^(-zoom)}, {0, 1}],
vcenter + {-2, 2}/2^(scale/2)}],
AxesOrigin -> Dynamic[{hcenter, vcenter}],
ImageSize -> {475, 360}]],
{{n, 10, "detail"}, 1, 40, 1, Appearance -> {"Labeled", "Open"},
AnimationRate -> rate}, {{rate, 1, "rate"}, 1, 3, 1,
Appearance -> "Labeled"}, {{bound, False,
"show sqrt bounds"}, {True, False}}, Delimiter, {{hcenter, .5,
"horizontal center"}, 0, 1, .01/2^(zoom - 1),
Appearance -> {"Labeled", "Open"},
AnimationRate -> .2/2^(zoom - 1)}, {{vcenter, 0,
"vertical center"}, -3, 3, .01/2^(scale - 1),
Appearance -> {"Labeled", "Open"},
AnimationRate -> .1/2^(scale - 1)}, Delimiter, {{zoom, 1,
"horizontal zoom"}, 1, 20, 1,
Appearance -> {"Labeled", "Open"}}, {{scale, 1,
"vertical scale"}, -2, 20, 1,
Appearance -> {"Labeled", "Open"}}, Delimiter, {{seed, 77777,
"new random case"}, 10000, 999999, 1, Appearance -> "Open",
AnimationRate -> newrate}, {{newrate, 1, "rate of new case"}, 1, 10,
1, Appearance -> "Labeled"},
ControlPlacement -> Right, SynchronousUpdating -> False]

Manipulate[
Module[{bmPath}, bmPath = {0, 2, 1};
Row[{ListLinePlot[bmPath, AxesLabel -> {"Time", "Location"},
DataRange -> {0, 1},
PlotRange ->
Dynamic[{Clip[{.5 - 2^(-zoom), .5 + 2^(-zoom)}, {0, 1}], {-2,
2}}], ImageSize -> {200, 100}],
ListLinePlot[bmPath, AxesLabel -> {"Time", "Location"},
DataRange -> {0, 1},
PlotRange -> {Clip[{.5 - 2^(-zoom), .5 + 2^(-zoom)}, {0,
1}], {-2, 2}}, ImageSize -> {200, 100}]}]],
{{zoom, 1, "horizontal zoom"}, 1, 20, 1,
Appearance -> {"Labeled", "Open"}},
ControlPlacement -> Right, SynchronousUpdating -> True]

Manipulate[
Module[{bmPath}, bmPath = {0, 2, 1};
Row[{ListLinePlot[bmPath, AxesLabel -> {"Time", "Location"},
DataRange -> {0, 1},
PlotRange ->
Dynamic[{Clip[{.5 - 2^(-zoom), .5 + 2^(-zoom)}, {0, 1}], {-2,
2}}], ImageSize -> {200, 100}],
Show[ListLinePlot[bmPath, AxesLabel -> {"Time", "Location"},
DataRange -> {0, 1}],
PlotRange ->
Dynamic[{Clip[{.5 - 2^(-zoom), .5 + 2^(-zoom)}, {0, 1}], {-2,
2}}], ImageSize -> {200, 100}]}]],
{{zoom, 1, "horizontal zoom"}, 1, 20, 1,
Appearance -> {"Labeled", "Open"}},
ControlPlacement -> Right, SynchronousUpdating -> True]

-
Is this a minimum working example? – Michael E2 May 3 '14 at 23:02
Of course some of the sliders could be eliminated, but they are not very complicated. Because I am not sure what is causing the problem and I need to have all those things there, it is hard to find the minimum example. I have already tried many variations. Sorry about that. However, if you execute this code, I think it will be clear what is going on. I'm happy to clarify anything. – Russ Lyons May 3 '14 at 23:10
I just added a minimal example. Sorry for not doing so from the start. – Russ Lyons May 4 '14 at 0:13
It's because Dynamic[{Clip[{.5 - 2^(-zoom), .5 + 2^(-zoom)}, {0, 1}], {-2, 2}}] is a not a proper form for the option PlotRange. Further the Dynamic wrapper isn't needed. – m_goldberg May 4 '14 at 0:38
Would you mind giving a fuller explanation or pointing to more? As you can see from the first code, Dynamic is needed there when it modifies PlotRange for Show. The whole point of using Dynamic is to speed things up when zooming (or panning, etc.). – Russ Lyons May 4 '14 at 1:19

It appears that ListLinePlot needs the PlotRange to be an actual list of numbers or another valid setting; something of the form PlotRange -> Dynamic[expr] generates an error. Here's an example:

Module[{bmPath}, bmPath = {0, 2, 1};
Show[
ListLinePlot[bmPath, AxesLabel -> {"Time", "Location"},
DataRange -> {0, 1}, ImageSize -> {200, 100},
PlotRange ->
Dynamic@{Clip[{.5 - 2^(-2), .5 + 2^(-2)}, {0, 1}], {-2, 2}}]
]]


ListLinePlot::prng: Value of option PlotRange -> {{0.25,0.75},{-2,2}} is not All, Full, Automatic, a positive machine number, or an appropriate list of range specifications. >>

ListLinePlot seems to use PlotRange to compute things like the axes, and must have an actual numerical setting or one like Automatic. The way around is to take the graphics returned by ListLinePlot and reset the PlotRange with Show. Graphics may have a dynamic PlotRange. ListLinePlot also explicitly sets the AxesOrigin, so that needs to be reset to Automatic with Show as well.

Manipulate[
Module[{bmPath},
bmPath = Accumulate@RandomReal[{-0.1, 0.1}, 10000];
Row[{
Show[
ListLinePlot[bmPath, AxesLabel -> {"Time", "Location"},
DataRange -> {0, 1}, ImageSize -> {200, 100}],
PlotRange -> Dynamic@{Clip[{.5 - 2^(-zoom), .5 + 2^(-zoom)}, {0, 1}], Automatic},
AxesOrigin -> Automatic
]}]
],
{{zoom, 1, "horizontal zoom"}, 1, 20, 1, Appearance -> {"Labeled", "Open"}},
ControlPlacement -> Right, SynchronousUpdating -> True]


-
It seems to me that your example does not work: Try increasing the zoom. it is supposed to zoom by a factor of 2 for each increase by 1, but it stops doing that around the value of zoom = 4 and is especially evident around zoom = 8. – Russ Lyons May 4 '14 at 2:45
@RussLyons PlotRange is working. It's PlotRangePadding that you're observing. Set it as you desire. Above I modified it to be 0. ListLinePlot does not follow the standard Plot option defaults it seems; this may be version dependent. – Michael E2 May 4 '14 at 12:22
Thank you! That does it. – Russ Lyons May 4 '14 at 14:34
@RussLyons You're welcome. – Michael E2 May 4 '14 at 14:48
@MichaelE2, this problem also appears in other array plotting functions (Array/Matrix Plot)- it has been annoying/puzzling me for the past hour. Thanks for clarifying (+1) – gpap Dec 5 '14 at 13:29

Let me quote something:

Kuba said:

Why some Options works with Dynamic and other doesn't?

John Fultz said:

The difference is that some option values get translated directly into FE option values. Others affect kernel computation, but don't have a direct translation into the FE.

And, as I've lectured many times before, Dynamic is useless unless it's displaying, directly, in the FE.

For example, look at the Plot function. Something like ImageSize goes directly to the FE. So Dynamic translates well there. But PlotPoints affects the underlying mechanisms of Plot itself, not anything the FE is actually doing.

If you want to change PlotPoints based upon a Dynamic setting, then you need to recompute the entire Plot. So Dynamic needs to wrap the entire Plot.

*FE -> FrontEnd

-
I don't think it will work to wrap the whole plot in Dynamic because then I cannot use SynchronousUpdating -> False, which is the whole point of speeding up the display by not recomputing the data (at least, as far as I understand it). – Russ Lyons May 4 '14 at 14:44
@RussLyons This is not the anaswer that says what you have to do but it says why PlotRange with Dynamic is not working. Wrapping everything in Dynamic is quick solution but not necessairly good idea as you can tell. You may ofcourse try specific solutions. P.s. everything in Manipulate body is effectively wrapped in Dynamic. – Kuba May 4 '14 at 14:49

The problem is that the PlotRangePadding of your ListLinePLot is absolute and doesn't use Scaled expressions.

Compare the behavior of these two examples:

center = 2;
width = 2;
llp = ListLinePlot[{1, 2, 3}];
p = Plot[x, {x, 1, 3}];

Manipulate[
Show[
llp,
PlotRange -> Dynamic@{center + width {-1, 1} 2^-zoom, {1, 3}}
],
{zoom, 1, 20, 1}
]

Manipulate[
Show[
p,
PlotRange -> Dynamic@{center + width {-1, 1} 2^-zoom, {1, 3}}
],
{zoom, 1, 20, 1}
]


Like your example, the first fails to zoom any further after a certain point. The second works fine.

Take a look at the option settings for each of those plots:

In[90]:= Options[llp, PlotRangePadding]

Out[90]= {PlotRangePadding -> {{0.04, 0.04}, {0.04, 0.04}}}

Out[91]= {PlotRangePadding -> {Scaled[0.02], Scaled[0.02]}}


PlotRangePadding is an option for graphics functions that specifies how much further axes etc. should extend beyond the range of coordinates specified by PlotRange.

So the behavior here is that your PlotRangePadding setting is keeping your range at a minimum of just over .08 coordinate units despite the PlotRange setting. You'll want to scale that padding as you zoom, thus Scaled is called for.

You might try setting PlotRangePadding in the ListLinePlot expression itself. For some reason, that doesn't seem to work. In 9.0.1 on Linux, I get back the exact same absolute setting.

It seems like you have to set it with Show, side-by-side with PlotRange. E.g.:

Manipulate[
Show[
llp,
PlotRangePadding -> {Scaled@.02, Scaled@.02},
PlotRange -> Dynamic@{center + width {-1, 1} 2^-zoom, {1, 3}}
],
{zoom, 1, 20, 1}
]


I made the same adjustment to your original example. It worked.

-
Thank you for the detailed explanation! – Russ Lyons May 4 '14 at 14:35
@RussLyons, no problem at all, very glad to help. I don't mean to be overly concerned with rep etc., but what was missing from this answer that made you decide not to mark it as the accepted one? The answer you've currently marked as accepted didn't mention the actual issue at all until an edit and comment about 5 hours ago, which was about 8 hours after I posted this answer. – billisphere May 4 '14 at 18:16
It was hard to decide which to accept. I didn't see either of them until this morning, so the order of posting was not a factor. I decided that having 0 padding was what I really wanted, so I accepted that. Actually, while your explanation was very good and helpful, it did not completely answer my question in the sense that it did not explain why Dynamic made a change. Without testing it, I suspect that the reason is that Dynamic prevents the range from being seen at all; having a PlotRange that is seen overrides the default padding. That's why it was so confusing to me in my original code. – Russ Lyons May 4 '14 at 20:39
@RussLyons, thanks for this feedback, very fair. Sorry I overlooked that aspect. Even after some more experimentation and lookup right now, I'm not sure why ListLinePlot doesn't complain at all about the Dynamic wrapper around its PlotRange setting. Plot certainly complains in the same setting. E.g. Manipulate[Plot[x, {x, 1, 3}, PlotRange -> Dynamic@s], {s, 1, 20}] vs. Manipulate[ListLinePlot[Range@3, PlotRange -> Dynamic@s], {s, 1, 20}]. I filed a report with WRI in case it's bad behavior. Anyway, thanks for your kind and thoughtful words. – billisphere May 5 '14 at 1:46
BTW, at some point when I have more time, it is possible I will decide that in the end, I will want scaled padding as you suggested. But for now I use 0. – Russ Lyons May 5 '14 at 12:32