Let the user draw rotated skewed polygons in C#

[skewed polygons]

The example Let the user draw rotated polygons with right angles in C# draws polygons with edges that are parallel or perpendicular to a baseline. This example is very similar except its polygons have edges that are parallel to one of two baselines. The result is a rotated and skewed polygon.

The two programs are extremely similar. The only difference is in the GetTransform method. Before I describe the new version of that method, let me recap how the previous example works.

The Previous Example

The previous example uses a GetTransform method to make a transformation matrix that rotates polygon points so the polygon’s edges are parallel to the X and Y axes. It then transforms the new polygon point and compares it to the polygon’s previous point. If the transformed points differ by less in the X direction than in the Y direction, the program gives the new point same X coordinate as the old point. Conversely if the points differ by less in the Y direction than in the X direction, the program gives the new point same Y coordinate as the old point.

After it has adjusted the new point’s transformed version, it inverts the transformation matrix and uses it to move the updated point back into the original frame of reference. The result is the final adjusted point.

Here’s a summary of the steps.

  1. Transform the new point and the previous one into a rotated coordinate system where the polygon’s edges are parallel to the X and Y axes.
  2. Determine whether the transformed new point and the transformed previous point are closer in the X or Y direction and update the transformed new point accordingly.
  3. Invert the previous transformation and use it to move the updated point back into the original coordinate system.

This is a fairly straightforward technique, but it’s not an intuitive way to think so it may be hard to understand. If you don’t see how it works, you may want to revisit the previous example.

GetTransform

The current example uses the same technique. The only difference is in the transformation that maps points from the original skewed coordinate system into a coordinate system that’s easier to work with.

Unfortunately there’s no direct way to make a transformation that maps from a skewed coordinate system to a rectangular one. Fortunately there is an easy way to map in the other direction. One of the Matrix class’s constructors takes as parameters a source rectangle and an array of three points that indicate where the upper left, upper right, and lower left corners of the rectangle should be mapped. The fourth corner’s position is implied by the positions of the other three corners.

[skewed polygons]

The picture on the right shows how the corners of the rectangle on the left map to corners on a parallelogram on the right.

But we want to do the opposite. We want to map from a skewed coordinate system to a rectangular one. To do that, we first make a transformation matrix that maps from a rectangular system to the system defined by the program’s baselines. We then invert that transformation matrix to get the transformation that we need.

The following code shows the new GetTransform method.

private Matrix GetTransform()
{
    float xdx = XAxisEnd.X - XAxisStart.X;
    float xdy = XAxisEnd.Y - XAxisStart.Y;
    float xlen = (float)Math.Sqrt(xdx * xdx + xdy * xdy);

    float ydx = YAxisEnd.X - YAxisStart.X;
    float ydy = YAxisEnd.Y - YAxisStart.Y;
    float ylen = (float)Math.Sqrt(ydx * ydx + ydy * ydy);

    RectangleF rect = new RectangleF(0, 0, xlen, ylen);
    PointF[] dest_points =
    {
        XAxisStart,
        XAxisEnd,
        new PointF(XAxisStart.X + ydx, XAxisStart.Y + ydy),
    };
    Matrix matrix = new Matrix(rect, dest_points);

    // Invert the matrix.
    matrix.Invert();
    return matrix;
}

The program stores the end points of the two baseline axes in variables XAxisStart, XAxisEnd, YAxisStart, and YAxisEnd. The GetTransform method first calculates the lengths of those axes. It then makes a rectangle with width equal to the length of the X baseline and height equal to the length of the Y baseline. Think of this rectangle as living in normal rectangular coordinates.

Next the code creates an array indicating where the rectangle’s upper left, upper right, and lower left corners should be mapped. It maps the upper left and upper right corners to the end points of the X baseline. It maps the lower left corner to the point you get when you add the Y baseline to the start of the X baseline. If the two baselines start at the same point, as they do in the picture at the top of this post, then that point is at the end of the Y baseline.

The method then uses the rectangle and points to create a transformation matrix to make the rectangle to the skewed coordinates. Finally it inverts that matrix and returns the result.

The rest of the program is the same as the previous example. It uses the GetTransform method to map from the drawing coordinate system to a rectangular system and then adjusts the new point in the easier-to-use coordinate system.

Conclusion

This example and the previous one demonstrate a powerful technique. They both map from an inconvenient coordinate system to one that is easier to use. Although it’s a powerful technique, it’s probably one that you won’t need to use very often.

Download the new example to see additional details and to experiment with it. For example, see what happens if you draw a polygon, change the X and Y baselines, and then draw a new polygon.


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, graphics, mathematics, transformations and tagged , , , , , , , , , , , , . Bookmark the permalink.

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.