///<summary>Causes a rotating rectangle that grows and fades in then shrinks and fades out to be shown.</summary>
        public static Lifetime AnimateSpinningRectangleExplosion(this Game game,
                                                                 PerishableCollection <UIElement> controls,
                                                                 Func <double, Point> position,
                                                                 Color color,
                                                                 TimeSpan duration,
                                                                 double rotationsPerSecond,
                                                                 double finalRadius)
        {
            var rect = new Rectangle {
                RenderTransformOrigin = new Point(0.5, 0.5)
            };
            var life = game.AnimateWith(
                duration,
                (step, portion, ellapsed) => {
                var nearHalfPortion  = (portion - 0.5).Abs() * 2;
                var radius           = 0.LerpTo(finalRadius, 1 - nearHalfPortion);
                var pos              = position(portion);
                rect.Width           = rect.Height = radius * 2;
                rect.RenderTransform = new TransformGroup {
                    Children = new TransformCollection {
                        new RotateTransform(rotationsPerSecond * ellapsed.TotalSeconds * 360),
                        new TranslateTransform(pos.X - radius, pos.Y - radius)
                    }
                };
                rect.Fill = new SolidColorBrush(color.LerpToTransparent(nearHalfPortion));
            });

            controls.Add(rect, life);
            return(life);
        }
        ///<summary>Handles making ellipse controls for each ball in the game, as while as showing their death animations.</summary>
        public static void SetupDrawBallsAsControls(this Game game, PerishableCollection <UIElement> controls, TimeSpan deathFadeOutDuration, double deathFinalRadiusFactor)
        {
            game.Balls.CurrentAndFutureItems().Subscribe(
                e => {
                // create an ellipse control to visually represent the ball
                var ball           = e.Value;
                var color          = ball.Hue.HueToApproximateColor(period: 3);
                var ellipseControl = new Ellipse {
                    Width               = ball.Radius * 2,
                    Height              = ball.Radius * 2,
                    VerticalAlignment   = VerticalAlignment.Top,
                    HorizontalAlignment = HorizontalAlignment.Left,
                    Fill = new SolidColorBrush(color)
                };

                // 'root' balls have a black border
                if (ball.Generation == 1)
                {
                    ellipseControl.StrokeThickness = 3;
                    ellipseControl.Stroke          = new SolidColorBrush(Colors.Black);
                }

                // reposition ellipse control during each game loop, until ball is dead
                game.LoopActions.Add(
                    step => ellipseControl.Reposition(ball.Pos, ball.Radius),
                    e.Lifetime);

                // once ball is dead, expand and fade out the ellipse
                var controlLifetime = e.Lifetime.ThenResurrect(() =>
                                                               game.AnimateWith(
                                                                   deathFadeOutDuration,
                                                                   (step, portion, dt) => {
                    // fade out
                    ellipseControl.Fill = new SolidColorBrush(color.LerpToTransparent(portion));
                    // expand
                    var radius = ball.Radius * 1.LerpTo(deathFinalRadiusFactor, portion);
                    ellipseControl.Reposition(ball.Pos, radius);
                }));

                controls.Add(ellipseControl, controlLifetime);
            },
                game.Life);
        }
        ///<summary>Handles making line controls for each connector in the game, as while as showing their death animations.</summary>
        public static void SetupDrawConnectorsAsControls(this Game game,
                                                         PerishableCollection <UIElement> controls,
                                                         TimeSpan deathFadeDuration,
                                                         double deathFinalThicknessFactor,
                                                         Color cutBangColor,
                                                         TimeSpan cutBangDuration,
                                                         double cutBangMaxRadius,
                                                         Color propagateBangColor,
                                                         TimeSpan propagateBangDuration,
                                                         double propagateBangRotationsPerSecond,
                                                         double propagateBangMaxRadius)
        {
            game.Connectors.CurrentAndFutureItems().Subscribe(
                e => {
                // create a line control to visually represent the line
                var con         = e.Value;
                var thickness   = con.Child.Radius * 0.1;
                var lineControl = new Line {
                    Stroke          = new SolidColorBrush(Colors.Black),
                    StrokeThickness = thickness,
                };

                // reposition line control during each game loop, until the connector is dead
                game.LoopActions.Add(
                    step => lineControl.Reposition(con.Line),
                    e.Lifetime);

                // show a bang if the connector is cut
                e.Lifetime.WhenDead(() => {
                    if (con.CutPoint == null || con.CutDir == null)
                    {
                        return;
                    }
                    var cutLineControl = new Line {
                        Stroke          = new SolidColorBrush(cutBangColor),
                        StrokeThickness = thickness,
                    };
                    var p    = con.CutPoint.Value;
                    var d    = con.CutDir.Value * cutBangMaxRadius;
                    var life = game.AnimateWith(
                        cutBangDuration,
                        (step, prop, elapsed) => {
                        var c = 1 - (prop - 0.5).Abs() * 2;
                        cutLineControl.Reposition((p - d * c).To(p + d * c));
                        cutLineControl.StrokeThickness = prop * cutBangMaxRadius / 2;
                        cutLineControl.Stroke          = new SolidColorBrush(cutBangColor.LerpToTransparent(1 - c));
                    });
                    controls.Add(cutLineControl, life);
                });

                // show a bang travelling along the connector when it dies
                e.Lifetime.WhenDead(() =>
                                    game.AnimateSpinningRectangleExplosion(
                                        controls,
                                        p => (con.CutPoint == null ? con.Line.Start : con.CutPoint.Value.ClosestPointOn(con.Line)).To(e.Value.Line.End).LerpAcross(p),
                                        propagateBangColor,
                                        propagateBangDuration,
                                        propagateBangRotationsPerSecond,
                                        propagateBangMaxRadius));

                // expand and fade out the line control after the connector dies
                var controlLife = e.Lifetime.ThenResurrect(() => game.AnimateWith(
                                                               deathFadeDuration,
                                                               (step, portion, dt) => {
                    lineControl.StrokeThickness = thickness * 1.LerpTo(deathFinalThicknessFactor, portion);
                    lineControl.Stroke          = new SolidColorBrush(Colors.Black.LerpToTransparent(portion));
                    //lineControl.Reposition(e.Value.Line);
                }));

                controls.Add(lineControl, controlLife);
            },
                game.Life);
        }