The methods described when talking about coordinate system transformations can be used to apply affine transformations to objects on the Graphics surface. However, sometimes you may also want to apply some sort of arbitrary transformation to a GraphicsPath or to a Graphics object. You can do so by using their Transform method, with the overload that takes a Func<Point, Point> delegate.
The GraphicsPath.Transform method
The GraphicsPath.Transform method takes a single parameter, which is a Func<Point, Point> delegate. This should be a method that, given a single Point as an argument, returns a new Point corresponding to the transformed point. When this method is used, arc segments are transformed into Bézier segments, and then all the end points of the segments, as well as all control points, are transformed using the supplied transformation method.
Since the path is not linearised before applying the transformation, you should keep in mind the following:
If you are sure that the transformation is an affine transformation, you can apply it without any pre-processing.
If instead the transformation is a projective transformation, you should linearise the path before applying it.
If the transformation is not a projective transformation, in addition to linearising the path, you should make sure that the line segments that compose the path are not too long (this is because a non-projective transformation transforms lines into curves, thus having line segments that are too long might produce an inaccurate result).
The following example shows how to apply a non-projective transformation.
The overloads of the Graphics.Transform method can be used to do the same thing as the Transform method of the GraphicsPath class. The Graphics.Transform method has three overloads:
public void Transform(double a, double b, double c, double d, double e, double f, string tag = null): here, the six double parameters describe an affine transformation matrix, which has already been discussed in the context of coordinate system transformations. This method transforms the coordinate system of the Graphics object, and only affects drawing calls performed after it has been invoked.
public Graphics Transform(Func<Point, Point> transformationFunction, double linearisationResolution): the first parameter of this overload is a Func<Point, Point> like the GraphicsPath.Transform method, while the second represents the linearisation resolution. When this overload is used, the Graphics is linearised using the specified linearisation resolution, and then the transformation function is applied. This overload returns a new Graphics object where the transformation has been applied to the elements that had already been drawn.
public Graphics Transform(Func<Point, Point> transformationFunction, double linearisationResolution, double maxSegmentLength): this overload has the same parameters as overload 2, with an additional parameter representing the maximum length of any line segment in the plot. When this overload is used, in addition to linearising all paths in the plot, line segments that are longer than the specified length are also divided in smaller segments, using a similar approach as the one used in the GraphicsPath example above. Like overload 2, this overload returns a new Graphics object where the transformation has been applied to the elements that had already been drawn.
As discussed above:
If the transformation you want to apply is an affine transformation, you should use overload 1 before drawing the plot elements on the Graphics object.
If the transformation is projective, you should use overload 2 after drawing the plot elements.
If the transformation is not projective (or if you are not sure), you should use overload 3 after drawing the plot elements.
The following example shows how to apply a non-projective transformation.