13
$\begingroup$

Background: I have a geometric transformation composed of a RotationTransform, ScalingTransform and ShearingTransform, I can use dynamic constructs with rotations and scalings but this does not (seem to) work with the ShearingTransform.

Consider the following snippet:

  DynamicModule[{shr, deg = 30},
   shr0 = ShearingTransform[deg Degree, {1, 0}, {0, 1}];
   shr1 = Dynamic[ShearingTransform[deg Degree, {1, 0}, {0, 1}]];
   shr2 = ShearingTransform[Dynamic[deg Degree], {1, 0}, {0, 1}];
  Column[{
     Slider[Dynamic[deg], {0, 90, 1}],
     GeometricTransformation[{Blue,
       Rectangle[{0, 0}, {3, 1}]},
      shr0] // Graphics}
   ]]

Note that the snippet does not work (as expected) : moving the slider does not change the angle of the shear. See shr1 and shr2 for alternatives I have already tried.

Question: How can I make the angle in a ShearingTransform dynamic, such that it works in the context of the snippet above?

$\endgroup$
8
  • $\begingroup$ Fixing it is one thing, but understanding why exactly it doesn't work is more interesting. Here's another example which I don't understand completely right now: DynamicModule[{a, b}, b := f[a]; {Slider[Dynamic[a], {0, 1}], Dynamic[a], Dynamic[b, TrackedSymbols -> {a}]} ] (f is an inert head here) $\endgroup$
    – Szabolcs
    Commented May 31, 2012 at 19:43
  • $\begingroup$ And here's another one, analogous to Leonid's example, which does appear to work at first: DynamicModule[{a, b}, b := f[a]; {Slider[Dynamic[a], {0, 1}], Dynamic[a], Dynamic[b, TrackedSymbols -> {a}]}, Initialization :> (a = 0.4444) ]. But now try this for both versions: select the output and press Ctrl-Shift-I to convert to InputForm and see what's in the DynamicModule. Now convert back to StandardForm (Ctrl-Shift-N), and see again what happens. Leonid's version got "broken" while the first version picked up a value for b but b still won't update. $\endgroup$
    – Szabolcs
    Commented May 31, 2012 at 19:49
  • $\begingroup$ It probably has to do something with how DynamicModule variables are owned by the Front End and not the kernel. Also note that when using Module (making the variables be owned by the kernel), the example seems to work. $\endgroup$
    – Szabolcs
    Commented May 31, 2012 at 19:50
  • $\begingroup$ @Szabolcs The effects you describe are likely due to caching. Try this, for instance: DynamicModule[{a, b}, b := f[a]; {Slider[Dynamic[a, (a = #; Update[Unevaluated[b]]) &], {0, 1}], Dynamic[a], Dynamic[b]}] $\endgroup$ Commented May 31, 2012 at 20:01
  • $\begingroup$ @Leonid It seems just mentioning b changes the behaviour: you could use (a = #; b) & as the second arg in Dynamic---it's enough to "fix" it. $\endgroup$
    – Szabolcs
    Commented May 31, 2012 at 20:11

2 Answers 2

20
$\begingroup$

There are several things to understand here, but all of them center around a common theme. Exactly what is evaluating, and exactly when? It looks like you're just trying to guess what's going on, but guessing is bad... you really should understand the evaluation model.

A: Problems with the code in the question

  1. The method in the question would not have worked under normal evaluation without Dynamic or DynamicModule because it used Set (=) rather than SetDelayed (:=). i.e.,

    a=1;
    b=f[a];
    a=2;
    b
    
    (* f[1] *)
    

    So, your example fails immediately because what it assigned to shr0 was not ShearingTransform[deg Degree, {1, 0}, {0, 1}], but ShearingTransform[30 Degree, {1, 0}, {0, 1}]. Changing deg will never again help you.

  2. You can't just put Dynamic anywhere and expect it to delay evaluation. For example, the following:

    a=2; 2 + Dynamic[a]
    

    will not give you four, and putting a Slider on the value of a would not allow you to see the result of 2+a. I've written on this subject ad nauseum before...a bit in this forum and to a much greater extent here.

    Dynamic works in exactly two places... where it directly translates into output you wish to see visibly, or where it is the value of a control. Every time you use Dynamic, you need to think about that... am I seeing the result? Am I setting the value from a control? If not, you are misusing Dynamic and will probably not get the effect you intended.

B: Misconceptions in the comments and other answers

  1. An issue which is causing confusion among the commenters is that DynamicModule, sadly, does not support SetDelayed on its member variables. SetDelayed is silently translated to Set.

    ToBoxes[DynamicModule[{a = 5, b}, b := a; Dynamic[b]], StandardForm]
    
    (* DynamicModuleBox[{a$$ = 5, b$$ = 5}, 
      DynamicBox[ToBoxes[b$$, StandardForm]], DynamicModuleValues :> {}] *)
    

    Note the output shows that what was assigned to b was 5, not a. And the assignment is immediate, not delayed.

  2. Leonid's answer very subtly "works around" the issue by flipping evaluation orders around, but ultimately fails because it can't keep the evaluation order flipped forever. To boil down the essential point of Leonid's example, we have:

    DynamicModule[{a, b}, b := f[a]; {Slider[Dynamic[a]], Dynamic[b]},
        Initialization :> (a = 0)]
    

    As long as Dynamic[b] always sends f[a] to the kernel, things will work. But if, at any point, the Dynamic instead sends f[0] (or any other value of a), the jig is up, and Dynamic[b] will stay frozen forever. Both the kernel and the FE have copies of a and b lying around, and for reasons which are outside of the scope of this question, this happens to cause the FE to think b=f[a] while the kernel thinks b=f[0]. But if they ever have to sync up values, the jig is definitely up. And the FE always syncs up values when determining what to save to disk or when making a clipboard copy of a DynamicModule. So closing/reopening the file or pasting a copy of the resulting cell will show that the association is broken in both my toy example and Leonid's full example.

C: The right approach

So what would I suggest? First, you have to expand the scope of the Dynamic to fully cover the output. Leonid's example does that correctly, as will mine. And Leonid's example would work perfectly if you simply removed shr0 and just used the full rhs value directly in the Dynamic, but I'll assume that you have good reason to want to modularize this. The trick, then, is to use DynamicWrapper to do the updating we might have preferred to do via SetDelayed. First, let's see how this would apply to the simple example above:

DynamicModule[{a, b}, {Slider[Dynamic[a]], DynamicWrapper[Dynamic[b], b = f[a]]}]

DynamicWrapper, like Dynamic, evaluates its contents when they appear onscreen. By wrapping Dynamic[b] with DynamicWrapper, I guarantee that the evaluation b=f[a] will always happen at the right time for Dynamic[b] to display the proper result.

So, let's see the final result in action:

DynamicModule[{shr, deg = 30},
 Column[{Slider[Dynamic[deg], {0, 90, 1}], 
   DynamicWrapper[
    Dynamic[GeometricTransformation[{Blue, Rectangle[{0, 0}, {3, 1}]},
        shr] // Graphics], 
    shr = ShearingTransform[deg Degree, {1, 0}, {0, 1}]]}]]

This code, as all good DynamicModule code should, will survive restarts of the kernel and the FE, copy/paste, and closing/reopening of the file.

$\endgroup$
7
  • $\begingroup$ +1 For the advice where dynamic works. Learned that from the tutorials, but it took me much longer. $\endgroup$
    – Ajasja
    Commented Jun 1, 2012 at 8:11
  • 1
    $\begingroup$ I should point out one thing that's outdated in my linked content...when I wrote that, the "invisible Dynamic" trick was the only option for getting things to evaluate you didn't want to see. Now DynamicWrapper is really the preferred way, as I outlined in this answer. $\endgroup$
    – John Fultz
    Commented Jun 1, 2012 at 8:46
  • $\begingroup$ Thanks a lot, this clears things up. $\endgroup$ Commented Jun 1, 2012 at 8:50
  • $\begingroup$ @JohnFultz I was just about to ask a 'related question' because I have the same problems again. As you wrote in your answer this was more or less to be expected. I hope I can fix the issue with the help of the clarification in this answer. $\endgroup$ Commented Jun 1, 2012 at 10:51
  • 2
    $\begingroup$ Welcome back! Glad you got the time to get us to stop guessing $\endgroup$
    – Rojo
    Commented Jun 1, 2012 at 13:08
5
$\begingroup$

NOTE: This answer fails subtly when closing and reopening the notebook. Please see Section B.2 in John Fultz's answer for an explanation of why it fails and Section C in his answer for the correct approach.

I've retained my original answer below to aid the discussion in John's answer and for people to learn from mistakes arising from what appears to be correct code at first glance, but with subtle points of failure.


This is what you need:

DynamicModule[{deg, shr0},
   shr0 = ShearingTransform[deg Degree, {1, 0}, {0, 1}];
   Column[{
     Slider[Dynamic[deg], {0, 90, 1}],
     Dynamic[
       Graphics[
         GeometricTransformation[{Blue, Rectangle[{0, 0}, {3, 1}]}, shr0]
       ], 
       Initialization :> (deg = 30)]
    }]]
$\endgroup$
19
  • $\begingroup$ Could you take a look at my comments on the main post, and the two code snippets? I don't really understand what is happening here, why your version seems to work at first while removing the Initialization seems to break it. I'm going to look at the cell expressions now instead of converting to InputForm. $\endgroup$
    – Szabolcs
    Commented May 31, 2012 at 19:50
  • $\begingroup$ Just copying the cell expression to a new cell (and breaking the "hidden" front end variables) has the same effect on the two version as converting to InputForm and back. The fact that these variables are owned by the front end are key to understanding what is happening, but right now it just became even more mysterious to me. I really wonder if your solution is robust enough to survive closing and re-opening the notebook (or being put in a CDF, which is the same). $\endgroup$
    – Szabolcs
    Commented May 31, 2012 at 19:55
  • 1
    $\begingroup$ @Szabolcs I can't offer any explanation. This behavior seems to violate the documentation for DynamicModule. But, you know what, I always use kernel vars in my UI-s, and I don't care if that is or is not "politically correct". If I can't make it work with the kernels vars, I try to find a way which would. I already said many times that I much prefer the open DOM models for UI programming, such as Java Swing or Javascript DOM. My personal experience is that the pain/gain ratio for "proper" UI coding in M is just too high for me. Perhaps it's just me, and I don't encourage this viewpoint. $\endgroup$ Commented May 31, 2012 at 20:27
  • 1
    $\begingroup$ @LeonidShifrin - I agree ( in sofar ) that I think that there is a tipping point after which Mma becomes too tiresome for developing GUIs. I suppose that point differs for all of us. $\endgroup$ Commented May 31, 2012 at 20:36
  • 1
    $\begingroup$ To the downvoter: I only keep this answer because it is referenced by the better one by John Fultz, and was an integral part of the discussion. One more downvote, and I am deleting it (which will make the discussion less complete for lower-rep users). $\endgroup$ Commented Jun 2, 2012 at 13:26

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.