27

How could I make a pacman drink beer in Mathematica? This is what I have so far

pacman[xposFun_, angleFun_] := {Yellow, 
   Disk[{Dynamic@xposFun[Clock[{0, Infinity}]], 0`}, 1`, 
    Dynamic[{Pi/6 Abs@Sin[2 Pi Clock[]], 
       angleFun[Clock[{0, Infinity}]]} /. {val_, ang_} :> {val + ang, 
        2 Pi - val}]]};

burp := Quiet@EmitSound@ListPlay@Upsample[ConstantArray[1, 80], 200]

xpos[t_] := \[Piecewise] {
    {5 t, 0 < t < 3},
    {15, t >= 3},
    {0, True}
   };

angle[t_] := \[Piecewise] {
   {0, 0 < t < 3},
   {Pi/4 (t - 3), 3 < t < 4.8},
   {Pi/4 (4.8 - 3), t > 4.8}
  }

angleBeer[t_] := \[Piecewise] {
   {0, 0 < t < 3.5},
   {Pi/4 (t - 3.5), 3.5 < t < 5.3},
   {Pi/4 (5.3 - 3.5), t > 5.3}
  }

beer[filling_] := {{Opacity[0.2], EdgeForm[Directive[Thick, Gray]], 
    Polygon[{{0`, 0`}, {1`, 0`}, {1.2, 1.5}, {-0.2, 1.5}}]},
   Opacity[0.8], filling, EdgeForm[Directive[White, Thickness[0.01]]],
    Scale[{Polygon[{{0`, 0`}, {1`, 0`}, {1.2, 1.3}, {-0.2, 1.3}}],
     White, 
     Polygon[{{-0.2, 1.3}, {1.2, 1.3}, {1.3, 1.6}, {-0.3, 1.6}}]}, 
    0.9],
   {Opacity[0.], EdgeForm[Directive[Gray, Thickness[0.01]]], 
    Disk[{1.1`, 0.7`}, 0.3, {-Pi/2 - 0.2, Pi/2 - 0.2}]}};

Graphics[{pacman[xpos, angle], 
  Translate[
   Rotate[beer[
     Dynamic[If[Clock[{0, 5.4}, 5.4, 1] < 5.4, Darker@Yellow, 
       If[Clock[{0, 7.4, 1}, 7.4, 1] == 7.4, Refresh[burp, None]]; 
       Opacity[0.]]]], 
    Dynamic@angleBeer[Clock[{0, Infinity}]]], {16, -0.2}]}, 
 PlotRange -> {{0, 20}, {-1.2, 2}}, Background -> Black]

 Animator@Dynamic@anything

enter image description here

Now, this serves as an excuse to ask the real question. This is an issue I've also had with version 8 but that I didn't have a reproducible small code for it. I see the dynamic animation is choppy but it gets fixed while any Animator is running on display dynamically updating something, like the Animator@Dynamic@anything that the code above displays after the pacman. For testing, you can get the animation to restart, by copying and pasting the cell in the same place. Once the pacman is moving, try starting and stopping the animator to see the difference in choppiness.

Furthermore, changing the DynamicUpdateInterval in the SystemOptions can make this improve but it's always choppier without the animator, as if without it it always missed some frames... Do you see this behaviour too? How could I tell the front end "heyy, an animation is happening even if I'm not using an animator"?

Rojo
  • 42,601
  • 7
  • 96
  • 188
  • 1
    @EliLansey the "waca waca" sound would be nice :P. Any ideas how to synthesize it? – Rojo Dec 04 '12 at 19:02
  • Shouldn't the beer have bubbles :) – Mike Honeychurch Dec 04 '12 at 22:48
  • With 8.0.4 on Max 10.6.8 I didn't notice any choppiness but got an error from ListPlay. What is Upsample -- is a V9 function? – Mike Honeychurch Dec 05 '12 at 01:51
  • Yes @MikeHoneychurch. it puts zeros in between the elemts of the list. As many as the second argument says – Rojo Dec 05 '12 at 03:15
  • Change Disk[{Dynamic[bla], 0}, 1, Dynamic[bla]] in pacman definition to Dynamic[Disk[{bla, 0}, 4, bla]] seems to make it smoother on my MMA 9. Maybe when multiple Dynamics are used, they compete against each other? Or MMA must choose one Dynamic to update for any one frame? – Silvia Apr 22 '13 at 06:00
  • Rojo, please let me know if you wish me to specifically award the bounty. – Mr.Wizard Apr 27 '13 at 10:53
  • @Mr.Wizard award it to Michael for his attempts, he deserves it. It was a useful bounty, 2 interesting answers, thanks! – Rojo Apr 27 '13 at 13:56
  • I won't be accepting any answer for now, since I believe the real story is quite different to what's been posted. Perhaps after some more digging I'll self-answer, or perhaps some answer gets revamped, or a new one appears, who knows – Rojo Apr 27 '13 at 13:59

2 Answers2

12

[Edit 2: I've added further analysis and modified some of the explanations, esp. with respect to Refresh. Still far from complete, I fear.]

First of all, I find if I set SetSystemOptions["DynamicUpdateInterval" -> 0.005], then I cannot perceive a difference in performance with or without the Animator running. I see a very few hiccups in both scenarios, but I suspect the hiccups are due to system granularity (or too much beer).


On to some findings. I tested things by setting SetSystemOptions["DynamicUpdateInterval" -> 0.5], which allowed me better to perceive slow versus fast.

Hypothesis 1 - Animator overrides SystemOptions["DynamicUpdateInterval"]

This is obvious. But how?

Edit. Manipulator affects dynamic updating in a way similar to Animator. If the slider is moved or animated, updating is rapid.

Hypothesis 2 (modified) - Dynamic@Refresh[..., UpdateInterval -> t] can bypass "DynamicUpdateInterval"

Edit: Originally I reported Refresh sporadically overriding "DynamicUpdateInterval". After further playing with various permutations, Refresh appears not to override "DynamicUpdateInterval", at least not consistently. If I copy and paste two or more copies the output of

Dynamic@Refresh[Clock[], UpdateInterval -> 0.01]

I usually get rapidly updating clocks that switch between slow and rapid updating over long periods.

Hypothesis 3 - Clocks of the form Clock[{0, Infinity, dt}] behave differently than those of the form Clock[{0, Infinity}] or Clock[]

Compare the output of the original with the following modification, with the Animator running:

pacman[xposFun_, angleFun_] := {Yellow,
 Disk[{Dynamic@xposFun[Clock[{0, Infinity, 0.01}]], 0`}, 1`,
  Dynamic[{Pi/6 Abs@Sin[2 Pi Clock[{0, Infinity, 0.01}]],
   angleFun[Clock[{0, Infinity, 0.01}]]} /. {val_, ang_} :> {val+ang, 2 Pi-val}]]}

In both versions, the position is updated smoothly. But in the original code the mouth is updated every 0.5 sec., and in the above modification the mouth is updated smoothly.

Two or more clocks of the form Clock[{0, Infinity, 0.01}] randomly alternate between slow and rapid updating, while the other forms, such as Clock[], remain slow (unless wrapped in Refresh as in Hyp. 2 above):

Dynamic@Clock[]

Dynamic@Clock[{0, Infinity, 0.01}]

Dynamic@Clock[{0, Infinity, 0.01}]

Another difference is that Clock[{0, Infinity, 0.01}] calls Floor and Mod, while the other forms of Clock do not.

Hypothesis 4 - The system updates dynamic outputs ahead of schedule when convenient

I vaguely mean that for some reason updating the position of pacman is linked with updating the Animator, perhaps because of some connection to Clock.

@Rojo noticed that in the second case below, the animator does not cause Clock[{0, Infinity, 0.01}] to be updated continuously.

Module[{v}, Animator@Dynamic@v]

DynamicModule[{v}, Animator@Dynamic@v]

The distinction seems to be that if v is a kernel variable, then clocks are updated continuously, but not if v is a front end variable. However, this principle is not generally true, because both of the following cause the clock to be updated.

Module[{v = 0}, Dynamic@v++]

DynamicModule[{v = 0}, Dynamic@v++]

Neither of these expressions are limited by "DynamicUpdateInterval", even if the kernel is fed a long computation simultaneously, which seems at odds with the documentation

By default, dynamic outputs triggered by changes in variable values are updated no faster than twenty times per second (this rate can be changed with the SystemOption "DynamicUpdateInterval").

Hypothesis 5 - Animator is implemented in the front end

Following up the remark by @Rojo, I noticed the following. If you monitor CPU activity of the front end and kernel, one can see by comparing the preceding two animators that the second seems to be run entirely or almost entirely in the front end (paste several outputs to magnify the effect). In the Module case, kernel usage increases as more copies of the animator are pasted, whereas in the the DynamicModule case, kernel usage does not change. All forms of Clock, on the other hand, seem to use a small amount of kernel time.

Concluding conjectures

First, Animator is owned by the front end and bypasses "DynamicUpdateInterval", whose purpose is to limit interruptions of the kernel. Animator is represented by AnimatorBox in the output cell and it appears to run wholely in the front end; so it appears to be a basic element of the Dynamic system. For example, it is the basis for the various *Animate commands.

Second, Mathematica treats Clock[{t1, t2, dt}] in special way, triggering updates that are normally blocked. It is clear that it happens, but it is not so clear exactly how and under what conditions it happens. One question is whether the system is designed to do this. It seems unlikely, since the behavior seems sporadic or random. Another question is whether Animator is designed to cause clocks to update in the observed way and why.

Third, there is some connection between calls to the kernel and updates bypassing "DynamicUpdateInterval". This happens when the Animator variable is a kernel variables, sometimes with Clock, and even with Dynamic@v++ even when v is a DynamicModule variable perhaps because Increment is computed in the kernel. This connection is still mysterious.

Unresolved

After playing around for a while, with extra dynamic clocks added and deleted for testing, pacman would sometimes freeze (dropped down dead drunk, I suspect). The Animator would run, but pacman would stay at the start. The clocks would stop, too. Further evidence that Animator gets special treatment. Quitting the kernel and reevaluating fixed this. Also, Mathematica crashed on me a couple of times.

Michael E2
  • 235,386
  • 17
  • 334
  • 747
  • I'll look at this soon when I can. From far away it looks like the kind of answers I was hoping for :) Thanks – Rojo Apr 23 '13 at 18:31
  • +1, interesting, and it triggered ideas that I'm checking out :) – Rojo Apr 24 '13 at 14:02
  • About your Hypothesis 2, "sometimes the refreshed clock updates rapidly and sometimes only every 0.5 seconds. ". I can't reproduce it. Both of the two clock always refresh slowly (0.5sec per time). – Silvia Apr 24 '13 at 19:02
  • @Silvia It may be system dependent. I'm on a different machine now, and what I see is the second clock gets updated a few times quickly, but stops for a half second. The first clock sometimes jumps ~0.5 sec, and sometimes ~1.0 sec. I'll double-check on the other machine after I get home. I've got v9.0.1, old macbook pro; new macbook pro at home. There has always seemed a certain unpredictable randomness to it. Thanks for checking. – Michael E2 Apr 24 '13 at 19:27
  • You're welcome. I occasionally got the examples in my answer above totally "frozen". And regenerating the dynamic objects make them work again.. Frankly, I'm very confused.. – Silvia Apr 24 '13 at 19:32
  • btw I'm using 9.0.1 on win7 x64, with a Q6600 CPU, 8GB RAM and a nvidia 9800GTX+ graph card. – Silvia Apr 24 '13 at 19:42
  • @Silvia I've gotten the frozen situation, too, and I don't understand what's going on. – Michael E2 Apr 24 '13 at 23:46
  • AnimatorBox can be handled completely FrontEnd side, unless something complex is given inside. This can be confirmed with LinkSnooper as well as was confirmed by John Fultz. ProgressIndicator is implemented using AnimatorBoxes. Together with the fact that FE knows some arythmetic and renders graphics, makes it a powerful tool. – Kuba Nov 21 '16 at 13:17
9

I found two choices. One is to use the option UpdateInterval -> 0 to force the update being done as fast as possible. The second is to change Disk[{Dynamic[bla], 0}, 1, Dynamic[bla]] in pacman definition to Dynamic[Disk[{bla, 0}, 4, bla]] seems to make it smoother on my Mathematica 9. I guess, maybe when multiple Dynamics are used, they compete against each other. Or maybe Mathematica must choose only one Dynamic object to update for any one frame.

Please compare the behaviors of the following examples:

  1. Using Dynamic on separate objects, with the default option UpdateInterval -> ∞

    Function[{n, r},
      Graphics[
       Map[
        Dynamic[
          Disk[
           r {Cos[2 π ((# + Clock[])/n)], 
             Sin[2 π ((# + Clock[])/n)]}, 1]
          ] &,
        Range[n]],
       PlotRange -> (r + 1) {{-1, 1}, {-1, 1}}]
      ][3, 2/Sqrt[3]]
    
  2. Using Dynamic on separate objects, with the option UpdateInterval -> 0

    Function[{n, r},
      Graphics[
       Map[
        Dynamic[
          Disk[
           r {Cos[2 π ((# + Clock[])/n)], 
             Sin[2 π ((# + Clock[])/n)]}, 1],
          UpdateInterval -> 0] &,
        Range[n]],
       PlotRange -> (r + 1) {{-1, 1}, {-1, 1}}]
      ][3, 2/Sqrt[3]]
    
  3. Using one Dynamic on the whole group of objects, with the default option UpdateInterval -> ∞

    Function[{n, r},
      Dynamic[Graphics[
        Map[
         Disk[
           r {Cos[2 π ((# + Clock[])/n)], 
             Sin[2 π ((# + Clock[])/n)]}, 1] &,
         Range[n]],
        PlotRange -> (r + 1) {{-1, 1}, {-1, 1}}]
       ]
      ][3, 2/Sqrt[3]]
    
  4. Using one Dynamic on the whole group of objects, with the option UpdateInterval -> 0

    Function[{n, r},
      Dynamic[Graphics[
        Map[
         Disk[
           r {Cos[2 π ((# + Clock[])/n)], 
             Sin[2 π ((# + Clock[])/n)]}, 1] &,
         Range[n]],
        PlotRange -> (r + 1) {{-1, 1}, {-1, 1}}],
       UpdateInterval -> 0]
      ][3, 2/Sqrt[3]]
    

In my system, the last one outperforms all the others.

Silvia
  • 27,556
  • 3
  • 84
  • 164
  • +1, glad you answered :). This is definately interesting and helps. I'm still wondering about the Animator making a difference. It even improves your last example – Rojo Apr 22 '13 at 19:08
  • @Rojo Thanks :) About the mysterious Animator, I absolutely have no idea about why or how.. Let's wait for someone who knows something :) – Silvia Apr 22 '13 at 19:12
  • @Rojo hmm.. I didn't notice any obvious difference with/without the Animator for the last example.. What is your mma version? – Silvia Apr 22 '13 at 19:22
  • Right now I'm testing in MMA 9.0.0, in W7 – Rojo Apr 22 '13 at 19:33
  • @Rojo For your reference I recorded my screen with two avi size 2MB, one with Animator, the other without. – Silvia Apr 22 '13 at 20:06
  • Thanks, I'll watch it when I'm home in a few hours. Work's proxy doesn't like Dropbox, grunf – Rojo Apr 22 '13 at 20:20
  • @Rojo hmm.. guess you've got something like Great FireWall too? :D – Silvia Apr 22 '13 at 20:24
  • Haha. Just got home and saw the vids. I can't see any noticeable improvement in the animator version, nor I can see it testing it in my home computer. I suspect its because both versions work well enough and not because the issue was bypassed or something. You do get to see the difference in, for example, your first example (with vs without the animator)? – Rojo Apr 22 '13 at 22:26
  • @Rojo Yes I do see remarkable differences in both the 1st and 2nd examples. – Silvia Apr 23 '13 at 02:48