Animation with the GDI library for Netduino.

  • RSS
  • Add To My MSN
  • Add To Windows Live
  • Add To My Yahoo
  • Add To Google
Posted by Mario Vernari Thursday, February 14, 2013 5:57:57 AM
Rate this Content 2 Votes

As promised, here is a tutorial on how create a basic animation with the GDI library for Netduino and the Sure Electronics HT1632 led-matrix module.

There's nothing special behind it: the process is exactly the same as drawing a static composition, but the sequence of frames is performed many times in a second. The deal is managing the drawing composition on every frame in the quickest way, that require a pretty good knowledge of the library and the framework itself.

 

Frame rendering: choosing an approach.

There are two approaches on how manage the animation.

The first way is running all the rendering in a tight endless-loop, so that the frame rendering sequence will be as fast as possible. The actual frame rate is not known a priori, but it varies upon the amount of work to be done for the graphics composition. Thus, when a frame is relatively simple, the frame rate will be high. Instead, when the composition is rather complex, you'll experience a poor, jerky animation.

Another way is constraining the frame rate at a certain value (e.g. 20 frames per second). This should ensure the same animation quality in any context, but requires a much more careful development of the composition. In fact, you must be sure that, in any case, no composition will take longer than the maximum allowed (in the example of 20 fps, no longer than 50ms).

I'd prefer the latter, but there are pros and cons in both of them. 

Although you may choose the best approach you'll like, you have to deal with short timings of composition in any case. This is the only real task to deal with.

To measure the framing time, I used a simple trick. An OutputPort is taken high on the very beginning of the composition, then pulled low at the end. The high-level duration is measured with the scope (or any logic analyzer), and gives an approximate time of the task.

 

The bouncing-balls example.

This tutorial will cover an example on how to animate one or more "balls", each one actually drawn as a framed rectangle, and bouncing against the virtual bounds of the display viewport.

The program is structured to animate an array of "Ball" instances, thus you can experience how long takes animating just one or a series of them. A similar project is also interesting for inspecting what and where can be optimized.

Let's start back from a generic application, as seen in this article.

This time we have to enrich our app by adding something "clocks" the framing sequence: a simple timer. The following snippet is the very minimum for a constant-period animation application.

 

    public class Program
    {
 
        private static ICompositionRenderer _renderer;
        private static CompositionTargetBase _composition;
 
        private static Timer _clock;
 
        //just for timing measurements
        private static OutputPort _qtest = new OutputPort(Pins.GPIO_PIN_D2, false);
 
 
        public static void Main()
        {
            //create a led-matrix driver instance
            _renderer = new SureHT1632(
                cspin: Pins.GPIO_PIN_D10,
                clkpin: Pins.GPIO_PIN_D9);
 
            //creates the target composition instance
            _composition = _renderer.CreateTarget();
 
            //start the timer for the demo loop
            const int LoopPeriod = 50; //ms
 
            _clock = new Timer(
                ClockTick,
                null,
                LoopPeriod,
                LoopPeriod);
 
            //keep alive
            Thread.Sleep(Timeout.Infinite);
        }
 
 
        private static void ClockTick(object state)
        {
            _qtest.Write(true);
 
            //wipes out the composition target
            _composition.Clear();
 
 
            // ... frame rendering ...
 
 
            //dumps the composition to the physical device
            _renderer.Dump(_composition);
 
            _qtest.Write(false);
        }
 
    }

If you deal sometime with some kind of PC animation, you'll realize that here is the same thing. On every frame the composition cache is flushed, and a complete reconstruction must take place. At the end, the whole composition is dumped to the physical device (i.e. graphics driver).

NOTE: Bear in mind that there's no any strict rule on how to manage the cache and the composition, thus you may manage many caches at same time, as in many contexts you would do with the double-buffering technique.

 

Prepare for the declarative programming approach.

For this example, I want to use the GDI library in a declarative way, instead of the most "intuitive" imperative as used in my previous article. If you deal with WPF or any modern UI language, the declarative approach is hugely better than the imperative one, yielding much more elegancy and robustness in term of software maintenance. However, the imperative approach is still often used whereas the resources are limited and/or the users don't have enough practice with the declarative programming.

To do that, I consider a "Ball" class, which implements a IRenderable interface. This contract ensures that every object will provide its own rendering implementation, arising from the actual context of the frame. Let's say that a "Ball" should be aware to draw itself, just itself, and don't meaning anything where will be positioned in the final composition.

Don't you want a ball to be drawn in the composition? Remove it from the IRenderable objects!

At this point, we may refine our main app with the declarative-programming pattern useful for the demo.

 

    public class Program
    {
        private const int BallCount = 15;
 
        private static readonly IRenderable[] _renderables = new IRenderable[BallCount];
        private static ICompositionRenderer _renderer;
        private static CompositionTargetBase _composition;
 
        private static Timer _clock;
 
        //just for timing measurements
        private static OutputPort _qtest = new OutputPort(Pins.GPIO_PIN_D2, false);
 
 
        public static void Main()
        {
            //create a led-matrix driver instance
            _renderer = new SureHT1632(
                cspin: Pins.GPIO_PIN_D10,
                clkpin: Pins.GPIO_PIN_D9);
 
            //creates the target composition instance
            _composition = _renderer.CreateTarget();
 
            //creates a series of bouncing-ball instances
            var rnd = new Random();
 
            for (int i = 0; i < BallCount; i++)
            {
                _renderables[i] = new Ball(rnd.Next());
            }
 
            //start the timer for the demo loop
            const int LoopPeriod = 50; //ms
 
            _clock = new Timer(
                ClockTick,
                null,
                LoopPeriod,
                LoopPeriod);
 
            //keep alive
            Thread.Sleep(Timeout.Infinite);
        }
 
 
        private static void ClockTick(object state)
        {
            _qtest.Write(true);
 
            //wipes out the composition target
            _composition.Clear();
 
            //renders the objects against the target
            for (int i = 0; i < BallCount; i++)
            {
                _renderables[i].OnRender(_composition);
            }
 
            //dumps the composition to the physical device
            _renderer.Dump(_composition);
 
            _qtest.Write(false);
        }
 
    }

The interesting thing is that our app does not know anything about the shapes of the "Ball"s. Instead, it does know about how many Ball's instances are involved, and take care to render them at the predefined frame rate.

 

Defining the Ball class and its look.

So far, so well.

It's time to depict the initial form of the Ball class, which might look like this:

 

    public class Ball
        : IRenderable
    {
        private const int EdgeLength = 3;
 
 
        public Ball(int hash)
        {
 
            //create both brush and pen for blending
            this._pen = new Pen(Colors.Lime);
            this._brush = new SolidBrush(Colors.Red);
        }
 
 
        private Rectangle _rect;
 
        private Pen _pen;
        private Brush _brush;
 
 
        public void OnRender(CompositionTargetBase target)
        {
            //render the ball as a square at the current position
            this._rect.X = (int)(this._xpos + 0.5f);
            this._rect.Y = (int)(this._ypos + 0.5f);
 
            target.FillRectangle(
                this._brush,
                this._rect);
 
            target.DrawRectangle(
                this._pen,
                this._rect);
        }
 
    }

However, the above snippet won't work and does not have sense as real code, other than structuring what we want the ball do and appear. Also, we want a ball shaped as a framed square, having an edge sized 3 units (4 pixels). Also we define the colors for both the border and the body of the ball.

We have to add the behavior of the ball, that is what the ball does frame by frame. And we'd expect it moves around the screen, then bounces when any edge is hit. Moreover, when our app will have more than a single ball moving, it would be nice having several positions and speeds different, otherwise the result will be pretty boring.

 

Implementing the ball's behavior.

To implement the behavior, we leverage the OnRender method, which is called by the host application on every frame rendering.

First off, we should provide some variables to hold position and speed of the moving ball. Also we'd like to choose a random starting position, as well as a random speed.

Please, bear in mind that the position is geometrically a "Point", whereas the speed is a "Vector" instead.

 

    public class Ball
        : IRenderable
    {
        private const int EdgeLength = 3;
 
 
        public Ball(int hash)
        {
            //pick a random position as well as a random speed
            this._xpos = 8 + hash & 15;
            this._ypos = 4 + (hash >> 4) & 7;
            this._xspeed = ((hash >> 8) & 3) - 1.5f;
            this._yspeed = ((hash >> 10) & 3) - 1.5f;
 
            //define the ball rectangle
            this._rect = new Rectangle(
                (int)(this._xpos + 0.5f),
                (int)(this._ypos + 0.5f),
                EdgeLength,
                EdgeLength);
 
            //create both brush and pen for blending
            this._pen = new Pen(Colors.Lime);
            this._brush = new SolidBrush(Colors.Red);
        }
 
 
        private float _xpos;
        private float _ypos;
        private float _xspeed;
        private float _yspeed;
 
        private Rectangle _rect;
 
        private Pen _pen;
        private Brush _brush;
 
 
        public void OnRender(CompositionTargetBase target)
        {
            //render the ball as a square at the current position
            this._rect.X = (int)(this._xpos + 0.5f);
            this._rect.Y = (int)(this._ypos + 0.5f);
 
            target.FillRectangle(
                this._brush,
                this._rect);
 
            target.DrawRectangle(
                this._pen,
                this._rect);
 
            //move the ball according to its speed
            this._xpos += this._xspeed;
            this._ypos += this._yspeed;
        }
 
    }

Secondly, each ball instance should be aware to the viewport bounds, and consider to "bounce" when any of the four edge will be hit.

As said, the speed is a vector: it means that it has a direction and a modulo. If you like, it has an angle and a length. What actually happens when a moving object hit against an hard wall? The speed change its angle, while the modulo keeps the same. That's not what happens in the real world, because there are several factors to take in account, but we'd like to idealize our "pool" as it were a perfect elastic model, where no energy will be lost.

So, the final code for the Ball class is the following:

 

    public class Ball
        : IRenderable
    {
        private const int EdgeLength = 3;
 
 
        public Ball(int hash)
        {
            //pick a random position as well as a random speed
            this._xpos = 8 + hash & 15;
            this._ypos = 4 + (hash >> 4) & 7;
            this._xspeed = ((hash >> 8) & 3) - 1.5f;
            this._yspeed = ((hash >> 10) & 3) - 1.5f;
 
            //define the ball rectangle
            this._rect = new Rectangle(
                (int)(this._xpos + 0.5f),
                (int)(this._ypos + 0.5f),
                EdgeLength,
                EdgeLength);
 
            //create both brush and pen for blending
            this._pen = new Pen(Colors.Lime);
            this._brush = new SolidBrush(Colors.Red);
        }
 
 
        private float _xpos;
        private float _ypos;
        private float _xspeed;
        private float _yspeed;
 
        private Rectangle _rect;
 
        private int _rightBound;
        private int _bottomBound;
 
        private Pen _pen;
        private Brush _brush;
 
 
        public void OnRender(CompositionTargetBase target)
        {
            //render the ball as a square at the current position
            this._rect.X = (int)(this._xpos + 0.5f);
            this._rect.Y = (int)(this._ypos + 0.5f);
 
            target.FillRectangle(
                this._brush,
                this._rect);
 
            target.DrawRectangle(
                this._pen,
                this._rect);
 
            //move the ball according to its speed
            this._xpos += this._xspeed;
            this._ypos += this._yspeed;
 
            //checks the viewport edges and makes the ball bouncing
            if (this._rightBound == 0)
            {
                Size vp = target.ViewportSize;
                this._rightBound = vp.Width - EdgeLength;
                this._bottomBound = vp.Height - EdgeLength;
            }
 
            if (this._xpos < 0)
            {
                this._xspeed = -this._xspeed;
                this._xpos += this._xspeed;
            }
            else if (this._xpos >= this._rightBound)
            {
                this._xspeed = -this._xspeed;
                this._xpos += this._xspeed;
            }
 
            if (this._ypos < 0)
            {
                this._yspeed = -this._yspeed;
                this._ypos += this._yspeed;
            }
            else if (this._ypos >= this._bottomBound)
            {
                this._yspeed = -this._yspeed;
                this._ypos += this._yspeed;
            }
        }
 
    }

 

The running demo.

If everything gone well, then you'll be able to run your demo app without any problem. The resulting animation should be something like the below video.

 

This is just a starting point for creating your own animation, game, and whatever else you find funny or even useful with a led-matrix.

Enjoy!

Comments are closed on this post.