27
$\begingroup$

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"?

$\endgroup$
8
  • 1
    $\begingroup$ @EliLansey the "waca waca" sound would be nice :P. Any ideas how to synthesize it? $\endgroup$
    – Rojo
    Commented Dec 4, 2012 at 19:02
  • $\begingroup$ Shouldn't the beer have bubbles :) $\endgroup$ Commented Dec 4, 2012 at 22:48
  • $\begingroup$ 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? $\endgroup$ Commented Dec 5, 2012 at 1:51
  • $\begingroup$ Yes @MikeHoneychurch. it puts zeros in between the elemts of the list. As many as the second argument says $\endgroup$
    – Rojo
    Commented Dec 5, 2012 at 3:15
  • $\begingroup$ 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? $\endgroup$
    – Silvia
    Commented Apr 22, 2013 at 6:00

2 Answers 2

12
+100
$\begingroup$

[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.

$\endgroup$
8
  • $\begingroup$ I'll look at this soon when I can. From far away it looks like the kind of answers I was hoping for :) Thanks $\endgroup$
    – Rojo
    Commented Apr 23, 2013 at 18:31
  • $\begingroup$ +1, interesting, and it triggered ideas that I'm checking out :) $\endgroup$
    – Rojo
    Commented Apr 24, 2013 at 14:02
  • $\begingroup$ 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). $\endgroup$
    – Silvia
    Commented Apr 24, 2013 at 19:02
  • $\begingroup$ @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. $\endgroup$
    – Michael E2
    Commented Apr 24, 2013 at 19:27
  • $\begingroup$ 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.. $\endgroup$
    – Silvia
    Commented Apr 24, 2013 at 19:32
9
$\begingroup$

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.

$\endgroup$
9
  • $\begingroup$ +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 $\endgroup$
    – Rojo
    Commented Apr 22, 2013 at 19:08
  • $\begingroup$ @Rojo Thanks :) About the mysterious Animator, I absolutely have no idea about why or how.. Let's wait for someone who knows something :) $\endgroup$
    – Silvia
    Commented Apr 22, 2013 at 19:12
  • $\begingroup$ @Rojo hmm.. I didn't notice any obvious difference with/without the Animator for the last example.. What is your mma version? $\endgroup$
    – Silvia
    Commented Apr 22, 2013 at 19:22
  • $\begingroup$ Right now I'm testing in MMA 9.0.0, in W7 $\endgroup$
    – Rojo
    Commented Apr 22, 2013 at 19:33
  • $\begingroup$ @Rojo For your reference I recorded my screen with two avi size 2MB, one with Animator, the other without. $\endgroup$
    – Silvia
    Commented Apr 22, 2013 at 20:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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