I suggest a different user interface, for fun. The user clicks down the mouse and drags. An arrow will be drawn that indicates the magnitude and direction of a "flick" of the spinner. When the mouse is released ("MouseUp"
), a random destination is computed and the index of the spinner spins to it, decelerating as if under constant deceleration. The number of revolutions depends on the magnitude of the flick. It is not exactly a physical model, since the index will stop at the random destination independent of the magnitude of the flick.
I wrote three helper functions, one for the background and two for the index (pointer). The background is composed of sectors instead of a PieChart
because the elements of a PieChart
respond to mouse clicks. One of the index functions draws the index at a specified angle and the other dynamically moves the index from a specified angle0
to another angle1
. When the motion is finished, the variable spinningQ
is set to False
, which ultimately stops the dynamic updating. The motion is regulated by Clock[]
, so that the motion will be accurate in time and not depend on system delays; however, system speed will determine the jumps in the movements (the intervals between renderings) and the apparent continuity of motion. There are embedded constant parameters that control the performance, which are pointed out in comments in the code.
spinnerBG[labels_] :=(* background *)
With[{nSectors = Length@labels},
Table[{ColorData["DarkRainbow"][Rescale[i, {1, nSectors}]],
Disk[{0, 0}, 1, π - 2 π (i - 1)/nSectors + 2 π/nSectors {-1, 0}], Black,
Text[Style[labels[[i]], 24], 0.7 {Cos[#], Sin[#]} &[π - 2 π (i - 0.5)/nSectors]]},
{i, nSectors}]];
stillIDX[angle_] := (* non-moving index *)
{{PointSize[0.05], Point[{0, 0}]}, {Thick, Arrow[{{0, 0}, 0.7 {Cos[angle], Sin[angle]}}]}};
spinningIDX[angle0_, angle1_, time0_] := (* animated spinning index *)
DynamicModule[{scaledTime, v0, a0, dt},
dt = 0.5 Sqrt[Abs[angle1 - angle0]];(* alter 0.5 to adjust speed *)
v0 = a0 = 2 (angle1 - angle0);
Dynamic[If[(scaledTime = Rescale[Clock[Infinity] - time0, {0, dt}]) >= 1,
spinningQ = False;(* stop spinning *)
scaledTime = 1];
stillIDX[-0.5 a0 scaledTime^2 + v0 scaledTime + angle0]]
];
Here is my version of the Manipulate
.
spinningQ = False;
Manipulate[EventHandler[
Dynamic@Graphics[{
spinnerBG[{2, 4, 6, 8, 10}],
If[spinningQ,
Dynamic@spinningIDX[angle0, angle1, Refresh[Clock[Infinity], None]],
stillIDX[angle1]],
Red, Thick, Dynamic@If[ptDown != ptUp, Arrow[{ptUp, ptDown}], {}]
}, PlotRange -> 1.2, ImageSize -> 600],
{"MouseDown" :> (ptDown = ptUp = MousePosition["Graphics"];
angle1 = ArcTan @@ ptDown; spinningQ = False),
"MouseDragged" :> (ptUp = MousePosition["Graphics"]),
"MouseUp" :> (ptUp = MousePosition["Graphics"];
If[ptDown != ptUp,
angle0 = ArcTan @@ ptDown;
angle1 = angle0 + RandomReal[{-π, π}] +
2 π Round[Sign[#] 0.5 (* to make sure the rotation is not in the reverse \
direction of the arrow *)
+ # &[4 (ptDown - ptUp).{-Sin[angle1], Cos[angle1]}]];
(* the factor of 4 affects how many times it goes around before stopping *)
spinningQ = True; (* start spinning *)
ptDown = ptUp; (* reset: makes arrow disappear *)
])
}
],
{{ptDown, {1, 0}}, None}, {{ptUp, {1, 0}}, None},
{{angle1, 0}, None}, {{angle0, 0}, None},
TrackedSymbols :> {angle1, spinningQ}]

Edit
In case you want a button:
Manipulate[
Dynamic@Graphics[{
spinnerBG[{2, 4, 6, 8, 10}],
If[spinningQ,
Dynamic@spinningIDX[angle0, angle1, Refresh[Clock[Infinity], None]],
stillIDX[angle1]],
Red, Thick, Dynamic@If[ptDown != ptUp, Arrow[{ptUp, ptDown}], {}]
}, PlotRange -> 1.2, ImageSize -> 300],
{{ptDown, {1, 0}}, None}, {{ptUp, {1, 0}}, None},
{{angle1, 0}, None}, {{angle0, 0}, None},
Button["Spin", angle0 = angle1;
angle1 += RandomReal[{0, 2 π}] + 2 π RandomInteger[{1, 4}];
spinningQ = True; ptUp = {1, 0}; ptDown = {1, 0}],
TrackedSymbols :> {angle1, spinningQ},
Initialization :> (spinningQ = False)
]
The code for the Button
can be added to the first Manipulate
, if you want both interfaces.