Move and resize polygons in WPF and C#

[resize polygons]

The example Move and resize multiple shapes in WPF and C# shows how to resize some shapes in WPF, but it doesn’t let you resize polygons. You determine the position and size of most shapes by giving their left, top, width, and height values. In contrast, you specify a polygon by giving coordinates for all of its points.

The following section describes the ResizeShape method that does most of the work. The sections after that describe methods that handle polygons specifically.

ResizeShape

The following ResizeShape method updates a shape’s size and position when you drag one of its corners or sides with the mouse.

private void ResizeShape()
{
    // See how much the mouse has moved.
    Point point = Mouse.GetPosition(canvas1);
    double offset_x = point.X - LastPoint.X;
    double offset_y = point.Y - LastPoint.Y;

    // Get the shape's current position.
    double left, right, top, bottom;
    GetLRTB(HitShape, out left, out right, out top, out bottom);

    double new_x = left;
    double new_y = top;
    double new_width = right - left;
    double new_height = bottom - top;

    // Update the shape.
    switch (MouseHitType)
    {
        case HitType.Body:
            new_x += offset_x;
            new_y += offset_y;
            break;
        case HitType.UL:
            new_x += offset_x;
            new_y += offset_y;
            new_width -= offset_x;
            new_height -= offset_y;
            break;
        case HitType.UR:
            new_y += offset_y;
            new_width += offset_x;
            new_height -= offset_y;
            break;
        case HitType.LR:
            new_width += offset_x;
            new_height += offset_y;
            break;
        case HitType.LL:
            new_x += offset_x;
            new_width -= offset_x;
            new_height += offset_y;
            break;
        case HitType.L:
            new_x += offset_x;
            new_width -= offset_x;
            break;
        case HitType.R:
            new_width += offset_x;
            break;
        case HitType.B:
            new_height += offset_y;
            break;
        case HitType.T:
            new_y += offset_y;
            new_height -= offset_y;
            break;
    }

    // If the new width or height is not positive, do nothing.
    if ((new_width <= 0) || (new_height <= 0)) return;

    // Update the shape.
    if (HitShape is Polygon)
    {
        // Update a polygon.
        UpdatePolygon(left, right, top, bottom,
            new_x, new_y, new_width, new_height);
    }
    else
    {
        // Update a non-polygon.
        Canvas.SetLeft(HitShape, new_x);
        Canvas.SetTop(HitShape, new_y);
        HitShape.Width = new_width;
        HitShape.Height = new_height;
    }

    // Save the mouse's new location.
    LastPoint = point;
}

The method first gets the current mouse position and determines how far the mouse has moved since the last time the program updated the shape. It then calls the GetLRTB method described in the earlier post to get the shape’s left, right, top, and bottom coordinates. This example uses a new version of that method to handle polygons. I’ll describe the new version shortly.

Next the program adjusts the size and position values depending on where you clicked on the shape. For example, if you’re dragging the shape’s bottom edge, then the code updates the bottom value. If you’re dragging the top edge, the code updates the shape’s top and height values.

After it has calculated the new size and position values, the code updates the shape. If the shape is not a polygon, the method simply sets its new left, top, width, and height values. If the shape is a polygon, the code calls the UpdatePoylgon method to update the shape.

The following sections describe two methods that deal explicitly with polygons.

GetLRTB

The GetLRTB method gets a shape’s left, right, top, and bottom coordinates.

// Return the shape's left, right, top, and bottom.
private void GetLRTB(Shape shape,
    out double left, out double right,
    out double top, out double bottom)
{
    if (!(shape is Polygon))
    {
        left = Canvas.GetLeft(shape);
        top = Canvas.GetTop(shape);
        right = left + shape.ActualWidth;
        bottom = top + shape.ActualHeight;
        return;
    }

    // Handle polygons separately.
    Polygon polygon = shape as Polygon;
    left = polygon.Points[0].X;
    right = left;
    top = polygon.Points[0].Y;
    bottom = top;
    foreach (Point point in polygon.Points)
    {
        if (left > point.X) left = point.X;
        if (right < point.X) right = point.X;
        if (top > point.Y) top = point.Y;
        if (bottom < point.Y) bottom = point.Y;
    }
}

This method first checks whether the shape is a polygon. If the shape is not a polygon, the code simply gets its left and right coordinates and adds its width and height to get the right and bottom values.

If the shape is a polygon, the code loops through the polygon’s points to find their largest and smallest X and Y coordinates.

UpdatePolygon

To give the polygon its new size and position, the program performs the same steps you use to scale and translate any object. For a point A in the polygon, you subtract A’s X and Y coordinates from the coordinates of the polygon’s current upper left corner to get an offset <dx, dy>. You then multiply those offsets by a scale factor to resize the polygon. Finally you add the scaled offsets to the polygon’s new upper left coordinates to get the point’s final position.

The following UpdatePolygon method uses those steps to moves the polygon’s points.

// Update the polygon's size and position.
private void UpdatePolygon(
    double left, double right, double top, double bottom,
    double new_x, double new_y, double new_width, double new_height)
{
    // Get scale factors to give the polygon its new size.
    double x_scale = new_width / (right - left);
    double y_scale = new_height / (bottom - top);
    
    // Loop through the points and adjust them.
    Polygon polygon = HitShape as Polygon;
    List<Point> new_points = new List<Point>();
    foreach(Point point in polygon.Points)
    {
        double x = new_x + x_scale * (point.X - left);
        double y = new_y + y_scale * (point.Y - top);
        new_points.Add(new Point(x, y));
    }
    polygon.Points = new PointCollection(new_points);
}

The code first calculates the horizontal and vertical scale factors that it needs to use to change the polygon’s width and height from their current values to the desired new ones.

It then loops through the polygon’s points. The code calculates each point’s horizontal and vertical offsets, multiplies them by the appropriate scale factor, and adds them to the coordinates of the desired new upper left corner.

After it has calculated the points’ new positions, the method sets the polygon’s Points property so it uses the new points.

Conclusion

The rest of the program is similar to the previous version. View the previous post to see additional details.

This example isn’t really intended to build a full-featured shape editor and it still several drawbacks. For example, to resize an ellipse or polygon you need to click and drag the shape’s corners even though those corners may not lie above the shape. In a real shape editor, you would also probably want to be able to resize objects while preserving their aspect ratios. You might also want to be able to move a polygon’s individual points. This example also does not provide a way to create new shapes, delete existing shapes, or change a shape’s colors. You can download the example and add some of those features if you like.


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 controls, graphics, wpf, XAML and tagged , , , , , , , , , , , . Bookmark the permalink.

2 Responses to Move and resize polygons in WPF and C#

  1. annon says:

    howto_whatever.zip?

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.