8
$\begingroup$

I am writing a Manipulate with a set of bookmark animations. Below is a simple toy example. However, rather than having to select the "+" icon and select the "Animate bookmarks" to run the animation, I would prefer to add a button in the control list to duplicate exactly the function of `Animate bookmarks'. As you can see by running the example below, Animate bookmarks animates a set of controls in a continuous fashion through their range of definition as an animation. Is this possible and if so could someone help me set this up or perhaps direct me to info about doing this?

Manipulate[
 Plot[a + b x + c x^2 + d x^3, {x, -1, 1}, PlotRange -> 2],
 {{a, 1}, 0, 1}, {{b, 0}, 0, 1}, {{c, 1}, 0, 1}, {{d, 0}, 0, 1},
 Bookmarks -> {"frame1" :> (a = 1; b = 0; c = 1; d = 0), 
   "frame2" :> (a = 0; b = 0; c = 1; d = 0), 
   "frame3" :> (a = 0; b = 1; c = 1; d = 0), 
   "frame4" :> (a = 0; b = 1; c = 0; d = 0)
   }
 ]
$\endgroup$

2 Answers 2

4
+100
$\begingroup$

Here's a way to get the "Animate Bookmarks" control into your manipulate:

Manipulate[
 Plot[a + b x + c x^2 + d x^3, {x, -1, 1}, PlotRange -> 2], {{a, 1}, 
  0, 1}, {{b, 0}, 0, 1}, {{c, 1}, 0, 1}, {{d, 0}, 0, 1}, Dynamic[
  dummy = Manipulate`BookmarksAnimator[
     Hold@Typeset`specs,
     Typeset`bookmarkList,
     Dynamic@Typeset`animvar,
     Automatic,
     {1.5, True}
     ] /.
    Animator[var_, rng_, opts___] :> 
     Animator[var, rng, AnimationRunning -> False, opts]
  ],
 {reset,
  With[
    {res = Manipulate`Dump`reset},
    Button["Reset", res; Typeset`animvar = 1]
    ] &},
 Bookmarks -> {
   "frame1" :> (a = 1; b = 0; c = 1; d = 0),
   "frame2" :> (a = 0; b = 0; c = 1; d = 0),
   "frame3" :> (a = 0; b = 1; c = 1; d = 0),
   "frame4" :> (a = 0; b = 1; c = 0; d = 0)
   }
 ]

enter image description here

This works by directly invoking the relevant functionality from Manipulate itself. As such, it will behave exactly the same, and can automatically process normal bookmark specifications.

Some notes:

  • The entire Dynamic@... expression is treated as a custom control for Manipulate. As such, you can change where it appears in the list of controls, etc.
  • The Automatic argument controls the InterpolationOrder setting of the resulting interpolation that moves through the bookmark states.
  • The 1.5 controls the speed of the animation: It gives the transition duration between bookmarks in seconds.
  • The True controls whether the values of the variables should be clipped to their allowed ranges (the interpolation might overshoot depending on the interpolation order)
  • After constructing the Animator control responsible for the animation, I apply a replacement rule to insert custom options. This allows us e.g. to set the initial state to not running (as done above).
  • The reset variable construct is a trick to get the value of Manipulate`Dump`reset, which contains all the initial states of the manipulate variables. After we capture it, we construct a button that evaluates that variable, and also set Typeset`animvar to 1 (this corresponds to the first bookmark, 2 would be the second etc.). Alternatively, you can also just create a "normal" button and set the variables to whatever you want, and then just include Typeset`animvar = ...
  • The Typeset`* variables are "magic" variables in the sense that they access the variables created by Manipulate for the bookmark animation functionality. They will be replaced by the relevant DynamicModule variables when displaying the Manipulate
  • The dummy = ... assignment is required. It doesn't matter what the variable is called, as long as there is one. This is needed to work around a "bug" within the Manipulate`BookmarksAnimator code that stores a variable in a Module variable instead of a DynamicModule variable. Consequently, the variable is usually deleted after the Module (thanks to the Temporary attribute). Assigning the result of the entire thing to a variable prevents this cleanup.
$\endgroup$
5
  • $\begingroup$ Thank you for your solution and your explanation of the constructs. I noticed the animation starts immediately when the Manipulate is initiated. Would it be too much trouble to disable this and only start it when Play button is selected? Also, I added a Button to reset all the controls to the initial settings but I can't get it to reset the animator back to its initial setting as well.. Is it possible to reset the animator to the start as well with the same button? $\endgroup$
    – josh
    Sep 12 at 14:14
  • $\begingroup$ @josh Please see the updated answer $\endgroup$
    – Lukas Lang
    Sep 12 at 15:14
  • $\begingroup$ Thanks Lukas. That works nicely. However there is a minor issue with the Reset button you designed: the reset variable appears to the left of the labeled button as "reset, Button[Reset]" which is a bit redundant. This is a demo I'm designing and I would like to make it really nice looking. I tried to remove the redundant "reset" using just "" but didn't work or even using just R is a bit unnecessary. Is there anyway to just show the labeled Reset button without the variable appearing to the left of the button? $\endgroup$
    – josh
    Sep 12 at 18:11
  • $\begingroup$ @josh The usual method to specify control labels for Manipulate should work, so something like {{reset, 0, ""}, With[...]&} (i.e. the {{var, init, label}, func} syntax) $\endgroup$
    – Lukas Lang
    Sep 12 at 19:10
  • $\begingroup$ Thanks a bunch for your help. That did it. I'll post a bounty on this tomorrow. $\endgroup$
    – josh
    Sep 12 at 20:44
8
$\begingroup$

I would prefer to add a button in the control list to do this.

You can just not use Bookmarks in first place, and just add buttons to run the specific frames.

If you have many frames, this can be easily changed to have a pull up menu used to first select the frame of interest, then have just one button to run the selected frame.

enter image description here

Manipulate[
 Plot[a + b x + c x^2 + d x^3, {x, -1, 1}, PlotRange -> 2],
 {{a, 1}, 0, 1},
 {{b, 0}, 0, 1},
 {{c, 1}, 0, 1},
 {{d, 0}, 0, 1},
 Grid[{
   {Button[Text@Style["frame 1", 10], {a = 1; b = 0; c = 1; d = 0}, 
     ImageSize -> {60, 30}],
    Button[Text@Style["frame 2", 10], {a = 0; b = 0; c = 1; d = 0}, 
     ImageSize -> {60, 30}],
    Button[Text@Style["frame 3", 10], {a = 0; b = 1; c = 1; d = 0}, 
     ImageSize -> {60, 30}],
    Button[Text@Style["frame 4", 10], {a = 0; b = 1; c = 0; d = 0}, 
     ImageSize -> {60, 30}]
    }}, Spacings -> {.2, 0}, FrameStyle -> Gray]
 ,
 TrackedSymbols :> {a, b, c, d}
 ]

Update

I thought OP wanted just the frames shown.

This runs animation using a button. Speed of animation can be controlled by another buttons.

enter image description here

Manipulate[
   Which[state == "init", a = 1; b = 0; c = 1; d = 0; state = "running",
          state == "running",
              Which[a > incr, a -= incr,
                 b < (1 - incr), a = 0; b += incr,
                 c > incr, b = 1; c -= incr,
                 d < (1 - incr), c = 0; d += incr,
                 True, d = 1; state = "stop"
           ]
    ];
   Plot[a + b x + c x^2 + d x^3, {x, -1, 1}, PlotRange -> 2]
   ,
   {{a, 1}, 0, 1}, 
   {{b, 0}, 0, 1},
   {{c, 1}, 0, 1},
   {{d, 0}, 0, 1},
   Grid[{{Button[Text@Style["Animate", 10], {state = "init"}, 
     ImageSize -> {60, 30}],
            
    Button[Text@Style["Reset", 10], {state = "stop", a = 1, b = 0, 
      c = 1, d = 0, incr = 0.01}, ImageSize -> {60, 30}],
            
    Button[Text@Style["Slower", 10], 
     incr = If[incr > 0.001, incr - 0.001, incr], 
     ImageSize -> {60, 30}],
            
    Button[Text@Style["Faster", 10], incr += 0.001, 
     ImageSize -> {60, 30}]
       }}, Spacings -> {.2, 0}, FrameStyle -> Gray]
    ,
    {{state, "stop"}, None},
    {{incr, 0.01}, None},
     TrackedSymbols :> {a, b, c, d, state}
 ]

ps. I do not know how to call/invoke the built-in Animate bookmarks using a button from inside Manipulate itself. May be there is an undocumented way to do that.

$\endgroup$
3
  • $\begingroup$ That only shows the frames at the start and end points but doesn't animate them in continuous motion for example as a goes from 0 to 1 as the Animate Bookmarks function does. Basically wish, if possible, to have one button which does exactly what Animate Bookmarks does. I've updated my post above to be more clear about this. Thanks anyway. $\endgroup$
    – josh
    Sep 12 at 10:23
  • $\begingroup$ @josh fyi, updated. hopefully this is what you meant. $\endgroup$
    – Nasser
    Sep 12 at 12:19
  • $\begingroup$ Wow! Sure looks like it. Didn't think it was possible. Need to spend time understanding it and then integrating it into my demo to see how it works later today. Will get back with you. Thanks for responding promptly. $\endgroup$
    – josh
    Sep 12 at 12:49

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.