SkiaSharp/Skia - Rotating shapes around their own axis

Sal Datoccio 96 Reputation points
2021-03-22T09:02:51.88+00:00

I am learning SkiaSharp and I'd like to have N shapes rotating around their own vertical axis, but I can't find a way to do it.

This is the result of my OnDraw method (rotationView is updated every N milliseconds):

80047-cho7x.png

 protected override void OnDraw(SKCanvas canvas, int width, int height)  
    {  
        int i = 0;  
        int step = 0;  
        List<SKRect> rects = new List<SKRect>();  
  
        // get the 2D equivalent of the 3D matrix  
        var rotationMatrix = rotationView.Matrix;  
  
        // get the properties of the rectangle  
        var length = Math.Min(width / 6, height / 6);  
  
        canvas.Clear(EffectMedia.Colors.XamarinLightBlue);  
  
        foreach (var n in numbers)  
        {  
            var rect = new SKRect(0 + step, 0, 100 + step, 100);  
            rects.Add(rect);  
            step += 120;  
        }  
  
        //var sideHoriz = rotationMatrix.MapPoint(new SKPoint(0, 1)).Y > 0;  
        var sideVert = rotationMatrix.MapPoint(new SKPoint(1, 0)).X > 0;  
  
        var paint = new SKPaint  
        {  
            Color = sideVert ? EffectMedia.Colors.XamarinPurple : EffectMedia.Colors.XamarinGreen,  
            Style = SKPaintStyle.Fill,  
            IsAntialias = true  
        };  
  
        // first do 2D translation to the center of the screen  
        canvas.Translate((width - (120 * numbers.Count)) / 2, height / 2);  
  
        // The following line is disabled because it makes the whole canvas rotate!  
        // canvas.Concat(ref rotationMatrix);  
  
        foreach (var n in numbers)  
        {              
            canvas.RotateDegrees((float)-3);  
            canvas.DrawRoundRect(rects[i], 30, 30, paint);  
  
            var shadow = SKShader.CreateLinearGradient(  
                    new SKPoint(0, 0), new SKPoint(0, length * 2),  
                    new[] { paint.Color.WithAlpha(127), paint.Color.WithAlpha(0) },  
                    null,  
                    SKShaderTileMode.Clamp);  
  
            var paintShadow = new SKPaint  
            {  
                Shader = shadow,  
                Style = SKPaintStyle.Fill,  
                IsAntialias = true,  
                BlendMode = SKBlendMode.SoftLight  
            };  
  
            foreach (var r in rects)  
            {  
                r.Offset(0, 105);  
                canvas.DrawRoundRect(r, 30, 30, paintShadow);  
            }  
  
            i++;  
        }  
    }  

I tried using SKPath + Transform, saving&restoring the rotationMatrix and/or the canvas but I can't find a way to have 6 rotating boxes around their own axis.

Do you have any hint on how that can be achieved? Do I need N rotation matrices?

Thanks!

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
4,818 questions
Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,185 questions
{count} votes

2 answers

Sort by: Most helpful
  1. Sal Datoccio 96 Reputation points
    2021-03-22T10:55:55.01+00:00

    My goal would be to have N shapes rotating around their axes, something like:

    80080-untitled.png

    Would could be a viable approach in SkiaSharp (I'd just need some directions)?

    Thanks :)

    No comments

  2. JarvanZhang 23,846 Reputation points
    2021-03-25T10:07:30.197+00:00

    Hi, Sal-Datoccio. According to my search, it doesn't provide a way to get the drawing sharp. A workaround is using SKCanvasView to draw each sharp one separately, and rotate the skCanvasViews.

    Here is the related code, you could refer to it.

    public partial class TestPage : ContentPage
    {
        public TestPage()
        {
            Title = "Testing for Animated Rotation";
    
            canvasView1 = new SKCanvasView() { AutomationId = "1" };
            canvasView2 = new SKCanvasView() { AutomationId = "2" };
            canvasView3 = new SKCanvasView() { AutomationId = "3" };
            canvasView4 = new SKCanvasView() { AutomationId = "4" };
            canvasView5 = new SKCanvasView() { AutomationId = "5" };
            canvasView6 = new SKCanvasView() { AutomationId = "6" };
    
            canvasView1.PaintSurface += OnCanvasViewPaintSurface;
            canvasView2.PaintSurface += OnCanvasViewPaintSurface;
            canvasView3.PaintSurface += OnCanvasViewPaintSurface;
            canvasView4.PaintSurface += OnCanvasViewPaintSurface;
            canvasView5.PaintSurface += OnCanvasViewPaintSurface;
            canvasView6.PaintSurface += OnCanvasViewPaintSurface;
            Content = new StackLayout()
            {
                HorizontalOptions = LayoutOptions.Center,
                Orientation = StackOrientation.Horizontal,
                Children = { canvasView1, canvasView2, canvasView3, canvasView4, canvasView5, canvasView6 }
            };
    
            // Measure the text
            textPaint.MeasureText(text1, ref textBounds);
        }
    
        protected override void OnAppearing()
        {
            base.OnAppearing();
    
            new Animation((value) =>
            {
                yRotationDegrees = 360 * (float)value;
                canvasView1.InvalidateSurface();
                canvasView2.InvalidateSurface();
                canvasView3.InvalidateSurface();
                canvasView4.InvalidateSurface();
                canvasView5.InvalidateSurface();
                canvasView6.InvalidateSurface();
            }).Commit(this, "yRotationAnimation", length: 1000, repeat: () => true);
        }
        protected override void OnDisappearing()
        {
            base.OnDisappearing();
            //this.AbortAnimation("xRotationAnimation");
            this.AbortAnimation("yRotationAnimation");
            //this.AbortAnimation("zRotationAnimation");
        }
        void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
        {
            SKImageInfo info = args.Info;
            SKSurface surface = args.Surface;
            SKCanvas canvas = surface.Canvas;
    
            canvas.Clear();
    
            // Find center of canvas
            float xCenter = info.Width / 2;
            float yCenter = info.Height / 2;
    
            // Translate center to origin
            SKMatrix matrix = SKMatrix.MakeTranslation(-xCenter, -yCenter);
    
            float scale = 1;
            SKMatrix.PostConcat(ref matrix, SKMatrix.MakeScale(scale, scale));
    
            // Calculate composite 3D transforms
            float depth = 0.75f * scale * textBounds.Width;
    
            SKMatrix44 matrix44 = SKMatrix44.CreateIdentity();
            //matrix44.PostConcat(SKMatrix44.CreateRotationDegrees(1, 0, 0, xRotationDegrees));
            matrix44.PostConcat(SKMatrix44.CreateRotationDegrees(0, 1, 0, yRotationDegrees));
            //matrix44.PostConcat(SKMatrix44.CreateRotationDegrees(0, 0, 1, zRotationDegrees));
    
            SKMatrix44 perspectiveMatrix = SKMatrix44.CreateIdentity();
            perspectiveMatrix[3, 2] = -1 / depth;
            matrix44.PostConcat(perspectiveMatrix);
    
            // Concatenate with 2D matrix
            SKMatrix.PostConcat(ref matrix, matrix44.Matrix);
    
            // Translate back to center
            SKMatrix.PostConcat(ref matrix,
                SKMatrix.MakeTranslation(xCenter, yCenter));
    
            // Set the matrix and display the text
            canvas.SetMatrix(matrix);
            float xText = xCenter - textBounds.MidX;
            float yText = yCenter - textBounds.MidY;
    
            SKCanvasView view = sender as SKCanvasView;
            switch (view.AutomationId)
            {
                case "1":
                    canvas.DrawText(text1, xText, yText, textPaint);
                    break;
                case "2":
                    canvas.DrawText(text2, xText, yText, textPaint);
                    break;
                case "3":
                    canvas.DrawText(text3, xText, yText, textPaint);
                    break;
                case "4":
                    canvas.DrawText(text4, xText, yText, textPaint);
                    break;
                case "5":
                    canvas.DrawText(text5, xText, yText, textPaint);
                    break;
                case "6":
                    canvas.DrawText(text6, xText, yText, textPaint);
                    break;
            }
        }
    }