1

After this question (Show trail of moving pixel in C# WinForm project) for my personal ant colony project in c#, I'm trying to apply the solution second suggested solution: the one that combines drawing the trail into a bitmap and the new ants onto the surface.

[...]Application.Run(new ShowAnts());[...]

public partial class ShowAnts : Form
{
    Bitmap bmp;
    int j = 0;
    public ShowAnts()
    {
        InitializeAntProgram();
        InitializeComponent();
        bmp = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
        pictureBox1.Image = bmp;
    }

    public void RenderAnts(object sender, PaintEventArgs e)
    {
        using (Graphics G = Graphics.FromImage(pictureBox1.Image))
        {
            while (j < 1000)
            {
                Map.EvaporatesPheromones();
                foreach (Vector2D food in foodSrcs)
                {
                    Map.SetMapPoint(food, 500);
                }
                foreach (Ant a in ants)
                {
                    Brush c;
                    c = Brushes.DarkBlue;
                    if (a.role == AntRole.Scout)
                    {
                        a.Move(j);
                        c = Brushes.Red;
                    }
                    e.Graphics.FillRectangle(Brushes.DarkBlue, a.position.x, a.position.y, 1, 1);
                    G.FillRectangle(Brushes.Gray, a.position.x, a.position.y, 1, 1);
                }
                j++;
            }
        }
    }
}

The code above shows the graphic attempt to draw the ant movement into a winform. It works perfectly, but it shows only the final result. I would like to show the step by step evolution keeping the graphical trail information without reparsing my map info.

Please consider that a working console project on which I' developing this "graphic interface" already exists so:

  • some variables are set elsewhere (i.e.: food) in the project;
  • the `a.Move(j);` refers to the ant logic itself (analysis, decision, new cell movement referring to the map array);
  • the `j` counter is used to count steps and to set an arbitrary stop, but has no real use;
  • I'm already storing into map array and some other variables all informations concerning pheromone, movement, positions etc.
Community
  • 1
  • 1
  • You could separate each step inside j loop as its own method, and only use Graphics inside that method. Then add a timer, on each tick call the method. – Martheen Jun 27 '16 at 03:02

1 Answers1

0

Looking at your code and also the comments of the previous question, it seems that you are missing the part that would animate the movement. Instead you are looping inside what seems to be the Paint event.

Here is a quick fix for that. It adds a Timer that triggers the RenderAnts event, which seems to be hooked up to the pictureBox1.Paint handler..:

A few class level variables:

 int counter = 0;
 int limit = 1000;
 Timer antTimer = new Timer(); 

Start code:

 antTimer.Interval = 50;   // <-- pick your speed !!
 antTimer.Tick += (ss, ee) =>
 { pictureBox1.Invalidate(); counter++; if (counter > limit) antTimer.Stop(); };
 antTimer.Start();

The speed is 50ms, which means 20 Ticks per second.

The Tick event is inlined with a tiny Lambda epression and has only one statement plus the loop logic. By Invalidating the pictureBox1 control its Paint event and thereby the RenderAnts event is triggered.

Also note that I called it a 'quick fix'. Usually you would discern between the rendering and the moving code of an animation; but in this case this fine difference doesn't matter much.

Now we change the RenderAnts method, taking out the loop:

public void RenderAnts(object sender, PaintEventArgs e)
{
    using (Graphics G = Graphics.FromImage(pictureBox1.Image))
    {
        Map.EvaporatesPheromones();
        foreach (Vector2D food in foodSrcs)
        {
           Map.SetMapPoint(food, 500);
        }
        foreach (Ant a in ants)
        {
           Brush c = Brushes.DarkBlue;
           if (a.role == AntRole.Scout)
           {
              a.Move(j);
              c = Brushes.Red;
           }
           e.Graphics.FillRectangle(c, a.position.x, a.position.y, 1, 1);
           G.FillRectangle(Brushes.Gray, a.position.x, a.position.y, 1, 1);
        }
    }
}

You also may want to add a Start/Stop Button. Also a TrackBar to change the speed..

Now you should be able to watch the progress of your ants at 20Hz, leaving grey trails.

TaW
  • 53,122
  • 8
  • 69
  • 111
  • I have been able to set the code properly up and it works. Thank you! But I have some lacks in comprehension of one concept:"By Invalidating the pictureBox1 control its Paint event [...] is triggered." Does this mean that every time I call `Invalidate` a new `Paint` event is fired? If so, it is enough to call `Invalidate` every time I need to refresh the image? (The last question doesn't concern necessarily the main question, it is a more theoretical one) – sipronunciaaigor Jun 27 '16 at 03:32
  • Yes, that is the way `Invalidate` works. (You need to understand the full picture here: Besides your code there are other events (beyond your control) that will trigger the `Paint` event. Most common: The minimize/maximize sequence of a form will also make it repaint itself and all its controls. Which is why usually the painting and the movement are separated. Not an issue for your case, but when one wants to move step by step in maybe a button click this wouldn't work right as outside event triggering the `Paint` would interfere with the movement. Not an issue with an ongoing animation) – TaW Jun 27 '16 at 04:51
  • Thank you again for all the help you have given me! Clear as light. – sipronunciaaigor Jun 27 '16 at 06:39