In this post, I’m going to explain object rotation from the mathematical point of view and demonstrate different methods of how to draw rotated objects in SkiaSharp.
Rotation and Math
So, what is rotation? Usually, when someone says that he wants to rotate a figure, intuitively I think that the figure will stay at the same location and will be rotated around its center. But in fact, in computer graphics, that’s not quite correct. A rotation in computer graphics always happens around the origin, which is the point (0, 0).
Take a look at Figure 1. Point A (x, y) is rotated at angle θ around (0,0). The result is A'(x’,y’). Note that the length r does not change. With a little help of trigonometry, we can find that:
(1) x = r cos (α)
(2) y = r sin (α)
Similar is true for Point A’:
(3) x’ = r cos (α + θ)
(4) y’ = r sin (α + θ)
Using Ptolemy’s identities we can get:
(5) x’ = r cos (α) cos (θ) − r sin (α) sin (θ)
(6) y’ = r cos (α) sin (θ) + r sin (α) cos (θ)
And finally, we can substitute (1) and (2) into (5) and (6):
x’ = x cos (θ) – y sin (θ)
y’ = x sin (θ) + y cos (θ)
Knowing how to rotate a point, we can rotate any figure by applying the same transformation to all points of the figure.
And finally to rotate a figure about an arbitrary point C (cx, cy), we should do three steps:
- Move (translate) the figure so the point C becomes a new origin. That can be achieved by subtracting cx, cy from all coordinates;
- Rotate the figure;
- Translate the figure back to the original position.
At Figure 3 a rectangle is rotated at 90 degrees around point A (2,1).
Rotation and SkiaSharp
There are multiple ways to do a rotation in SkiaSharp. Let me demonstrate three of them. In the demo project, I’m going to rotate a square using
- Canvas methods;
- Manual approach;
- Transformation Matrices.
The point (0,0) is in the upper left corner of the screen and the y-axis grows downward, that’s why the objects rotate clockwise.
Canvas methods
This is the easiest way to do a rotation.
void DrawRotatedUsingCanvas(SKCanvas canvas, SKPath path, SKPaint paint, float degrees, float cx, float cy) { canvas.Save(); canvas.RotateDegrees(degrees, cx, cy); canvas.DrawPath(path, paint); canvas.Restore(); }
We begin with saving the current canvas state and applying a rotation about a certain point. Anything drawn afterward will be rotated. Finally, we restore the initial canvas state.
Manual approach
We already reviewed the formulas to calculate new coordinates to rotate a point. Let’s use them to rotate an SKPath object that represents a square:
void DrawRotatedManually(SKCanvas canvas, SKPath path, SKPaint paint, float degrees, int cx, int cy) { float rad = ToRadians(degrees); using (SKPath pathRotated = new SKPath()) { pathRotated.MoveTo(Rotate(path[0], cx, cy, rad)); pathRotated.LineTo(Rotate(path[1], cx, cy, rad)); pathRotated.LineTo(Rotate(path[2], cx, cy, rad)); pathRotated.LineTo(Rotate(path[3], cx, cy, rad)); pathRotated.LineTo(Rotate(path[4], cx, cy, rad)); canvas.DrawPath(pathRotated, paint); } } SKPoint Rotate(SKPoint point, int cx, int cy, float rad) { var sin = (float)Math.Sin(rad); var cos = (float)Math.Cos(rad); var translatedX = point.X - cx; var translatedY = point.Y - cy; var rotatedX = translatedX * cos - translatedY * sin; var rotatedY = translatedX * sin + translatedY * cos; return new SKPoint(rotatedX + cx, rotatedY + cy); } static float ToRadians(float degrees) { return (float)(Math.PI * degrees / 180.0); }
The code here is self-explanatory. It’s good to understand the basics but probably there is no point to use it for production.
Transformation Matrices
Matrix Transform is a powerful technique that allows combining (concatenating) multiple transformations into a single one to significantly reduce complexity and number of calculations required to get new coordinates.
void DrawRotatedWithMatrices(SKCanvas canvas, SKPath path, SKPaint paint, float degrees, int cx, int cy) { var result = SKMatrix.MakeIdentity(); var translate = SKMatrix.MakeTranslation(-cx, -cy); var rotate = SKMatrix.MakeRotationDegrees(degrees); var translate2 = SKMatrix.MakeTranslation(cx, cy); SKMatrix.PostConcat(ref result, translate); SKMatrix.PostConcat(ref result, rotate); SKMatrix.PostConcat(ref result, translate2); path.Transform(result); canvas.DrawPath(path, paint); }
The code above creates 4 matrices – one to hold the result and one for each transformation (translate, rotate, translate back), concatenates them and applies the transformation to the given path.
There is an overload of the SKMatrix.MakeRotationDegrees method that takes an arbitrary rotation point. We can get the desired rotation matrix with just one line of code, but for the sake of demonstration I’m not using it.
Conclusion
Here I have shown three different ways to draw a rotated object using SkiaSharp. For production, use canvas methods when you simply need to draw an object and use Transformation Matrices when you need a complex transformation or you want to know coordinates before drawing an object. Computer graphics is fun, happy coding everyone!
P.S.
I used a super-awesome free geometry tool to create images for this post: https://www.math10.com/en/geometry/geogebra/geogebra.html
The code of the demo project can be found here: https://github.com/vecalion/SkiaSharpRotationDemo