Graph mosquito populations in C#


[mosquito]

The example Simulate mosquito populations in C# simulates a mosquito population. This example draws a graph showing the population during each generation.

When it calculates the populations for a new generation, it stores them in the following lists.

// Data for the graph.
private List<Point> FemalePoints,
    RegularMalePoints, ModifiedMalePoints;

The following code shows how the program saves the new values.

// Save points representing the new values.
FemalePoints.Add(
    new Point(GenerationNumber, NumFemales));
RegularMalePoints.Add(
    new Point(GenerationNumber, NumRegularMales));
ModifiedMalePoints.Add(
    new Point(GenerationNumber, NumModifiedMales));

After calculating each generation, the program refreshes its PictureBox to make the following Paint event handler graph the population data. (This is the fun part.)

// Graph the data.
private void picGraph_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.Clear(picGraph.BackColor);
    if (FemalePoints == null) return;
    if (FemalePoints.Count < 2) return;

    // Use the next multiple of 10 generations
    int num_generations =
        10 * (1 + (int)((GenerationNumber - 1) / 10.0));

    // Transform the graph.
    Rectangle source_rect = new Rectangle(
        0, 0, num_generations, (int)PopulationLimit);
    Point[] dest_points =
    {
        new Point(0, picGraph.ClientSize.Height),
        new Point(picGraph.ClientSize.Width,
            picGraph.ClientSize.Height),
        new Point(0, 0),
    };
    e.Graphics.Transform = new Matrix(
        source_rect, dest_points);

    using (Pen thin_pen = new Pen(Color.Gray, 0))
    {
        // Draw lines to show the generations.
        for (int i = 1; i < num_generations; i++)
        {
            e.Graphics.DrawLine(thin_pen,
                i, 0, i, (int)PopulationLimit);
        }

        // Draw the population data.
        thin_pen.Color = Color.Red;
        e.Graphics.DrawLines(thin_pen,
            FemalePoints.ToArray());
        thin_pen.Color = Color.Green;
        e.Graphics.DrawLines(thin_pen,
            RegularMalePoints.ToArray());
        thin_pen.Color = Color.Blue;
        e.Graphics.DrawLines(thin_pen,
            ModifiedMalePoints.ToArray());
    }
}

This code starts by clearing the PictureBox. If the data doesn’t contain at least two generations of values, the event handler exits.

The graph draws multiples of 10 generations at a time. In other words, if there are between 0 and 10 generations, the program makes room on the graph for 10 generations. (Except it doesn’t draw anything if there are fewer than 2 generations.) If there are between 11 and 20 generations of data, it makes room for 20 generations. If there are between 21 and 30 generations of data, it makes room for 30 generations, and so on. This approach means the program doesn’t need to resize the graph with every generation, which would be pretty distracting.

To determine how much room it needs, the program calculates 10 * (1 + (int)((GenerationNumber – 1) / 10.0)) to determine the smallest multiple of 10 greater than or equal to the number of generations of data. This calculation subtracts 1 and divides by 10, truncates to an integer, adds 1, and multiplies by 10.

For example, suppose GenerationNumber is 21. Then 21 – 1 = 20. Dividing by 10 and truncating gives 2. Adding 1 gives 3. Finally multiplying by 10 gives 30. This is correct because 30 is the smallest multiple of 10 that is at least 21.

For another example, suppose GenerationNumber is 30. Then 30 – 1 = 29. Dividing by 10 and truncating gives 2. Adding the 1 gives 3. Finally multiplying by 10 gives 30. This is also correct because 30 is the smallest multiple of 10 that is at least 30.

After it has calculated the number of generations it should display, the program creates a transformation to map a convenient coordinate system onto the PictureBox. First it creates a Rectangle to represent the graphing coordinate system. The Rectangle covers the area 0 ≤ X < num_generations (where num_generations is the multiple of 10 just calculated) and 0 ≤ Y < PopulationLimit (the largest allowed population).

The program then creates an array of Point to map the corners of the Rectangle onto the PictureBox. The three Points indicate where on the PictureBox the Rectangle‘s upper left, upper right, and lower left corners should go.

Next the code uses the Rectangle and Points to make a matrix operation to map the coordinates, and sets the Graphics object’s Transform property to that Matrix.

Finally the code can start to draw. It first makes a Pen with 0 thickness. The Graphics object draws a Pen that has 0 thickness as thin as it can be drawn (1 pixel wide) even if a transformation would otherwise change its width.

The code uses a loop to draw vertical lines showing the visible generations. It then simply draws the points stored in the FemalePoints, RegularMalePoints, and ModifiedMalePoints lists.

See the code for additional details.


Download Example   Follow me on Twitter   RSS feed   Donate




This entry was posted in algorithms, animation, drawing, graphics, mathematics and tagged , , , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *