-1

I'm working on a personal project linked to the ant colony problem. I successfully developed everything for a console project and now I want to transpose the code in a more graphic layout with windows forms.

Using the code below the form shows correctly one moving pixel, but I'm not able to find a way to show the left trail of the movement. I tried to remove the pictureBox1.Invalidate(), but this brings me to a static pixel in the starting point.

I would like not to reparse the map matrix and draw the pheromone trail, but only leave the drawn positions as if I'm drawing something with a pen on a piece of paper.

    public void RenderAnts(object sender, PaintEventArgs e)
    {
        pictureBox1.Invalidate();
        foreach (Ant a in ants)
        {
            Brush c;
            c = Brushes.DarkBlue;
            if (a.role == AntRole.Scout)
            {
                a.Move(i);
                c = Brushes.Red;
            }
            e.Graphics.FillRectangle(c, a.position.x, a.position.y, 1, 1);
        }
        pictureBox1.Show();
    }
  • Use RectangleF instead of Rectangle. The last two parameters (1,1) are the width and height which are floating point numbers. Make smaller so you don't over write old points. – jdweng Jun 25 '16 at 09:00
  • 2
    Create a bitmap and draw onto that instead of directly into the picturebox during a paint event. The Paint procedure doesn't keep pixels around through invalidation cycles. – theB Jun 25 '16 at 09:47
  • Do not call pictureBox1.Invalidate() from the Paint event or an event that is called form the Paint event ! So after removing the Invalidate and the Show you should see all your ants, provided their positions are actually correct.. – TaW Jun 25 '16 at 10:40

2 Answers2

0

Add List<Point> form field, where Point is type of ant Position field. On each Move add current ants positions to this list. I emulate moving by mouse click on PictureBox. Paint event handler just draws positions.

See sample

public partial class Form1 : Form
{
    public Form1()
    {
        //InitializeComponent();
        pictureBox1 = new PictureBox { Parent = this, Dock = DockStyle.Fill };

        ants.Add(new Ant { Role = AntRole.Scout, Position = new Point(50, 50) });
        ants.Add(new Ant { Role = AntRole.Scout, Position = new Point(150, 150) });
        ants.Add(new Ant { Role = AntRole.Scout, Position = new Point(100, 100) });

        pictureBox1.Paint += RenderAnts;

        pictureBox1.Click += (s, e) =>
        {
            for (int i = 0; i < 100; i++)
                foreach (Ant a in ants)
                {
                    if (a.Role == AntRole.Scout)
                    {
                        a.Move();
                        antTrails.Add(a.Position);
                    }
                }
            pictureBox1.Refresh();
        };
    }
    PictureBox pictureBox1;
    List<Ant> ants = new List<Ant>();
    List<Point> antTrails = new List<Point>();

    public void RenderAnts(object sender, PaintEventArgs e)
    {
        foreach (var point in antTrails)
            e.Graphics.FillRectangle(Brushes.Red, point.X, point.Y, 1, 1);
    }
}

public class Ant
{
    public AntRole Role;
    public Point Position;
    static Random rnd = new Random();

    public void Move()
    {
        int r = rnd.Next(4);
        if (r == 0)
            Position.X--;
        else if (r == 1)
            Position.X++;
        else if (r == 2)
            Position.Y--;
        else
            Position.Y++;
    }
}

public enum AntRole
{
    Normal,
    Scout
}

Run and try clicking the mouse.

Alexander Petrov
  • 13,457
  • 2
  • 20
  • 49
  • If I have applied correctly your hypothesis of solution, it draws trails, but all at once. Is it the same for you? The behaviour I'm looking for is a redrawing every single step. – sipronunciaaigor Jun 25 '16 at 14:00
  • @sipronunciaaigor - In the `Click` event I applied a `for` loop to illustrate one hundred trails. Remove this loop. – Alexander Petrov Jun 25 '16 at 14:07
  • ok, I see how it works and now I have been able to run it properly. Is there some sort of universal handler to avoid the click, or the mouse movement? I was thinking to something that simply looped until exit condition with a Thread.Sleep(amount) to slow down the simulation – sipronunciaaigor Jun 25 '16 at 14:44
  • Ok, given the timer, may I ask you for another handler? – sipronunciaaigor Jun 25 '16 at 15:00
0

Here is a solution that combines drawing the trail into a bitmap and the new ants onto the surface:

Prepare the bitmap:

Bitmap bmp = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
pictureBox1.Image = bmp;

Now the modified paint event:

void RenderAnts(object sender, PaintEventArgs e)
{
    //pictureBox1.Invalidate();   // don't do this here!

    using (Graphics G = Graphics.FromImage(pictureBox1.Image))
    foreach (ant a in ants)
    {
        c = Brushes.DarkBlue;
        if (a.role == AntRole.Scout)
        {
            a.Move(i);
            c = Brushes.Red;
        }
        // this draws onto the surface:
        e.Graphics.FillRectangle(c, a.position.x, a.position.y, 1, 1);
        // and this into the Image bitmap below:
        G.FillRectangle(Brushes.Gray, a.position.x, a.position.y, 1, 1);
    }

}

Note that the surface sit above the Image.

I'm not sure about the details of your code; espcially the varaible i is unclear.. And you indeed may want to consider floats for the positions..

Do not call pictureBox1.Invalidate() from the Paint event as this usually will trigger the Paint event again.. Instead this should probably move to the animation timer tick or a button click etc..

Also note the using clause that makes sure the Graphics object I create is disposed.

The example assumes that you can already draw the ants themselves correctly and simply adds the trails..

TaW
  • 53,122
  • 8
  • 69
  • 111
  • Sorry, but I wasn't able to make it run and I started focusing on @alexander reply. The "i" is a typo from the complete source code. – sipronunciaaigor Jun 25 '16 at 14:51
  • After making alexander's code work fine I'm after your code but it shows up the first iteration and I don't understand how to make it properly run. Sorry, but this is my first experience with graphics in winforms – sipronunciaaigor Jun 26 '16 at 05:05
  • Do you run the 1st two lines once? And how do you trigger the RenderAnts? From a Timer.Tick? – TaW Jun 26 '16 at 06:13
  • They run once, but I'm not triggering anything. I thought that the "Application.Run(new ShowAnts());" would have looped the main method all the time during the form lifetime. – sipronunciaaigor Jun 26 '16 at 12:05
  • Um, no. Nothing is happening during idle time. If you don't do anything, how do your ants move??? The normal way is to have a Timer with a Tick event that contains an `pictureBox1.Invalidate();`.. The Timer can have an Interval of maybe 200ms which would triggger the Tick 5 times per second.. – TaW Jun 26 '16 at 13:33
  • I can show you how to write such an animation but you should update the question. If Move(i) is a typo, please correct it..! – TaW Jun 26 '16 at 13:47
  • As requested, I asked a new question trying to be more exaustive from the code point of view [link](http://stackoverflow.com/questions/38044137/dynamic-drawing-ants-in-winforms-during-execution-of-ant-colony). – sipronunciaaigor Jun 26 '16 at 23:34