Message Boards Message Boards

2
|
7442 Views
|
6 Replies
|
8 Total Likes
View groups...
Share
Share this post:

How can I make the graphics change dynamically?

Posted 11 years ago
Hi, I am trying to make a digital version of a toy that I had as a kid, which I hope will help teach my 6-year old daughter about place values in arithmetic.

I am pretty new to Mathematica, although I used it some 10 years ago, so I hope that someone here can easily spot the flaw in my program.
 
The toy I am trying to re-create was basically a stripped down version of a pascaline, Pascal's calculator. The one I had was very simple, with 4 digits and 4 buttons. To make the number 43, you could press the 10's button 4 times and the 1's button 3 times (or you could press the 1's button 43 times, etc). Then to add, say 59, you could press the 10's button 5 times and the 1's button 9 times. The machine did the carrying for you. It didn't subtract.

I am able to get something similar to work, but I can't figure out how to animate the numbers rolling, which I think is kind of crucial...seeing the 9's roll over into 10's. I have been able to roll the digits of a simple counter, but not once I put in the buttons to "add 10", etc.

I have an image of the digits i want to use here:
horizTallyDigits=
htdigits = Flatten@ImagePartition[horizTallyDigits, {17, 26}];

tallydigits[n_] := ImageAssemble[List /@ RotateLeft[htdigits, n]];

(* and write functions to display any number, eg. 4.5 shows the wheel \

half way between 4 and 5 *)

window[x_] := (n = IntegerPart[x]; f = Round[26 (x - n)];
  ImageTake[
   tallydigits[Mod[n, 10]], {f, If[f > 0, 28, 29] + f}, {1, 17}]);

This works fine, and I can see the numbers roll as I would like (eg with a Trigger). But I want the numbers to roll in response to button presses. So I create buttons and variables that keep track of where the wheels are. I have the buttons generate an Animation List, but it seems to wait for that to finish before updating the image.

I've tried putting the Dynamic in lots of different places...some places make the counters roll very fast and never stop, others don't update at all. I haven't figured out a way to make it update the way I would like. I'm clearly missing something.
 
  (* I want a device where when i click the +10 button, i see the 10's \
 
 digit moving up slowly. if it's a 9, then it will also cause the \
 100's digit to move with it *)
 
 pascalineTotal = 0; pascalineButtons = {0, 0, 0, 0};
 Clear[pascaline]; Clear[animlist]; Clear[newCounter];
 newCounter[xvals_] := ImageAssemble[{window /@ xvals}];
 animlist[lis_, change_, nsteps_] :=
  (chg = change;
   Do[If[(chg[[i]] > 0) || (chg[[i + 1]] > 0 && (lis[[i + 1]] == 9)),
     chg[[i]] = 1], {i, Length[lis] - 1}];
   fparts = Array[Mod[#1/nsteps, 10] &, nsteps];
   lis + chg # & /@ fparts);

pascaline := (
  al = animlist[IntegerDigits[pascalineTotal, 10, 4],
    pascalineButtons, 20];
  a = ListAnimate[newCounter[#] & /@ al, 5,
    AnimationRepetitions -> 1];
  pascalineTotal += FromDigits[pascalineButtons];
  pascalineButtons = {0, 0, 0, 0} ; a)

(* This almost works, but it doesn't show the digits moving. The animate list is ignored until it is finished. I don't know why, related to Dynamic? *)

Row[{Button["+1000", (pascalineButtons = IntegerDigits[1000])],
  Button["+100", pascalineButtons = IntegerDigits[100, 10, 4]],
  Button["+10", pascalineButtons = {0, 0, 1, 0}],
  Button["+1", pascalineButtons = {0, 0, 0, 1}],
  Button["Reset", (pascalineTotal = 0;
    pascalineButtons = {0, 0, 0, 0})]}]


Dynamic[pascaline]



Thanks,
6 Replies
Posted 11 years ago
Here's a demo of a way to do the transitions, but I didn't put in the carrying mechanism. It shouldn't be too hard, though. I'll add another update when I have some time.

Update: I added carrying. In this example, adding past 9,999 works but gives you the result mod 10,000.




 digits = First@ImagePartition[horizTallyDigits, {17, 26}];
 
 stack = ImageAssemble[List /@ Prepend[Reverse@digits, First@digits]];
 
 fullCycle =
   Table[
    ImageTake[stack, {r, r + 25}],
    {r, ImageDimensions[stack][[2]] - 25, 2, -1}
    ];

frameIndices = ConstantArray[1, 4];

increment[places__] :=
Do[
  frameIndices[[{places}]] =
   Mod[frameIndices[[{places}]], Length@fullCycle] + 1;
  Pause[.02],
  {26}
  ]

withAnyCarries[place_] :=
Block[{values, numCarries},
  values = Floor[frameIndices/26];
  numCarries =
   LengthWhile[
    Reverse[values[[;; place]]],
    # === 9 &
    ];
  Range[place, Max[1, place - numCarries], -1]
  ]

reset[] :=
With[{resetIndices = ConstantArray[1, 4]},
  While[frameIndices =!= resetIndices,
   frameIndices =
    Mod[frameIndices, Length@fullCycle] + 1 /. (2 -> 1);
   Pause[.01]
   ]
  ]

Column@
{Row@
   {Button["+1000", increment @@ withAnyCarries[1],
     Method -> "Queued"],
    Button["+100", increment @@ withAnyCarries[2], Method -> "Queued"],
    Button["+10", increment @@ withAnyCarries[3],
     Method -> "Queued"],
    Button["+1", increment @@ withAnyCarries[4], Method -> "Queued"],
    Button["Reset", reset[], Method -> "Queued"]},
  Row@
   Table[
    With[{i = i},
     Dynamic[fullCycle[[frameIndices[[i]]]]]
     ],
    {i, Length@frameIndices}
    ]}
POSTED BY: William Rummler
Thank you! That looks like it solves my problem. 
@William, your solution is great!

@Stephen, the idea is wonderful - you should consider publishing it at Demonstrations project.

I just wanted to add a small note on digits. It is not necessary to seek an process an image. You could build one from any available font - and some are funny - good for kids:
digits = ImagePad[ImageCrop[ColorConvert[
      Rasterize[Style[ToString[#], 50, FontFamily -> "Curlz MT"],
       ImageSize -> 60], "Grayscale"]], 10, White] & /@ Range[0, 9]




And another idea is to make a realistic device-like machinery, something like a wheel you can rotate, this may make it funner for kids too. Then instead of shifting array you would just need to apply function Rotate to the wheel.
Graphics3D[Table[{Texture[digits[[k]]],
   Rotate[Polygon[{{1, -1, -1}, {1, 1, -1}, {-1, 1, -1}, {-1, -1, -1}},
     VertexTextureCoordinates -> {{0, 0}, {1, 0}, {1, 1}, {0, 1}}], k 2 Pi/10, {0, 1, 0}, {-0, -1, -4}]}, {k, 1, 10}],
Lighting -> "Neutral", Boxed -> False]

POSTED BY: Sam Carrettie
Thanks, Sam and William. It works exactly as i had hoped, except the way you did the reset was better than what i had in mind. 

Sam, thanks for both suggestions, and for showing me how to do them. I was originally going to do the numbers on a wheel, but i hadn't figured out how to do it. I'll play with this because it would definitely be nice to see the wheels turning. Speaking of which, here's a youtube video of a working lego version of a pascaline, if you are interested.  http://www.youtube.com/watch?v=olfNFXJEZOA. It's so much easier in Mathematica, especially with your help.
Nice LEGO machine ;) BTW an easier way to make a wheel:
strip = Rasterize[Style[Column[Range[0, 9], Spacings -> 0], 50, FontFamily -> "Curlz MT"], ImageSize -> 100];

ParametricPlot3D[{Cos[\[Theta]], Sin[\[Theta]], z}, {\[Theta], 0, 2 \[Pi]}, {z, -.2, .2},
TextureCoordinateFunction -> ({#3, -#4} &), Axes -> False, Mesh -> None, ImageSize -> Small, Boxed -> False,
PlotStyle -> Directive[Texture[strip]]]

POSTED BY: Sam Carrettie
That is nice. Thanks. I'm sure that this is more fun for me than it will be for my daughter. 
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract