Draw and move polygons snapping them to a grid in C#

example

This example lets the user draw and move polygons snapping them to a grid. See the example Let the user draw polygons, move them, and add points to them in C# for information on how the program lets the user draw, move, and add new points to polygons. See the example Draw and move line segments snapping to a grid in C# for information about snapping points to a grid.

The program uses the following SnapToGrid method to snap a point to the nearest grid location.

// The grid spacing.
private const int GridGap = 8;

// Snap to the nearest grid point.
private Point SnapToGrid(Point point)
{
    int x = GridGap * (int)Math.Round((float)point.X / GridGap);
    int y = GridGap * (int)Math.Round((float)point.Y / GridGap);
    return new Point(x, y);
}

This method takes a Point‘s X and Y coordinates, rounds them to the nearest multiple of GridGap, and returns a new Point with the rounded coordinates.

The program calls the SnapToGrid method whenever it deals with a point. For example, the following code shows the MouseDown event handler that executes when the mouse moves and no drawing or moving operation is in progress.

// See if we're over a polygon or corner point.
private void picCanvas_MouseMove_NotDrawing(
    object sender, MouseEventArgs e)
{
    Cursor new_cursor = Cursors.Cross;

    // See what we're over.
    Point mouse_pt = SnapToGrid(e.Location);
    List<Point> hit_polygon;
    int hit_point, hit_point2;
    Point closest_point;

    if (MouseIsOverCornerPoint(mouse_pt,
        out hit_polygon, out hit_point))
    {
        new_cursor = Cursors.Arrow;
    }
    else if (MouseIsOverEdge(mouse_pt, out hit_polygon,
        out hit_point, out hit_point2, out closest_point))
    {
        new_cursor = AddPointCursor;
    }
    else if (MouseIsOverPolygon(mouse_pt, out hit_polygon))
    {
        new_cursor = Cursors.Hand;
    }

    // Set the new cursor.
    if (picCanvas.Cursor != new_cursor)
    {
        picCanvas.Cursor = new_cursor;
    }
}

First this code passes the mouse’s current location to the SnapToGrid method. It then uses the MouseIdOverCornerPoint, MouseIsOverEdge, and MouseIsOverPolygon methods to see if the mouse is over part of a polygon. It then displays the appropriate cursor: Arrow if the mouse is over a polygon’s corner, the “add point” cursor if the mouse is over an edge, and Hand if the mouse is over the body of a polygon.

The example’s other methods similarly snap mouse locations to grid locations. For example, when you press the mouse down over a polygon, the program stores the offset from the mouse’s position (not snapped to the grid) and the polygon’s origin. Then the following code executes when you move the mouse to drag the polygon.

// Move the selected polygon.
private void picCanvas_MouseMove_MovingPolygon(
    object sender, MouseEventArgs e)
{
    // See how far the first point will move.
    int new_x1 = e.X + OffsetX;
    int new_y1 = e.Y + OffsetY;

    int dx = new_x1 - MovingPolygon[0].X;
    int dy = new_y1 - MovingPolygon[0].Y;

    // Snap the movement to a multiple of the grid distance.
    dx = GridGap * (int)(Math.Round((float)dx / GridGap));
    dy = GridGap * (int)(Math.Round((float)dy / GridGap));

    if (dx == 0 && dy == 0) return;

    // Move the polygon.
    for (int i = 0; i < MovingPolygon.Count; i++)
    {
        MovingPolygon[i] = new Point(
            MovingPolygon[i].X + dx,
            MovingPolygon[i].Y + dy);
    }

    // Redraw.
    picCanvas.Invalidate();
}

This code calculates the distances dx and dy that the polygon should be moved. It then rounds those values to the nearest multiple of GridGap, and moves the polygon’s points by the rounded distances. It finishes by invalidating the PictureBox to redraw the polygon in its new position.

Download the example to see the rest of the code.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in algorithms, drawing, geometry, graphics, mathematics and tagged , , , , , , , , , , , , , , , , , , , , . Bookmark the permalink.

6 Responses to Draw and move polygons snapping them to a grid in C#

  1. Javier Encinas says:

    I cannot open the solution with VS 2013. It says that the files are read-only and cannot be migrated. It seems that it was created with VS Express 2008. Could you please save the project with a newer version of VS? Thank you.

    • RodStephens says:

      I do build most projects in VS 2008 because Visual Studio will upgrade to newer versions but will not downgrade to older versions. (And VS 2008 is MUCH faster than more recent versions.)

      The problem is probably that you are trying to open the project while it is inside a zip file. Be sure to unzip it first. Windows Explorer can drill into a zip file so it looks like it’s part of the normal file system, but Visual Studio cannot open files there because it needs to create temporary files and it cannot do that inside a zip file.

      • Javier Encinas says:

        Yes, it worked. That was the problem. Thank you.

        I need to add rectangles instead of polygons, to model a wall elevation with doors and windows of different sizes. What do I need to change in the code? For example, the four corners will now be defined by the coordinates of the two opposite corners. Moving one corner will actually move three corners of the rectangle. Please advise.

        • RodStephens says:

          Here are two examples that let the user select rectangles:

          Draw and move polygons snapping them to a grid in C#
          Let the user select rectangular areas in an image in C#

          After the user makes a rectangle and then drags a corner, adjust the three corners that need to be modified.

          • Javier Encinas says:

            I managed to use your code to draw, resize, and move rectangles. The problem now is how to prevent overlapping of the rectangles. I have tried to develop an algorithm to do this, but it seems very complicated due to the different possible scenarios. Do you have any example, or idea on how to accomplish this? If you want I can send you my code. Thank you.

          • RodStephens says:

            Sorry but I think that will be hard. You’ll need to figure out exactly what you want the program to do when the user moves a rectangle over another one.

            You could allow the user to put rectangles only in certain places. In that case, you could just let the user click those places to pick the locations that should have rectangles. Or you could move a rectangle back to its original position if the user drops it on another rectangle.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.