如何在 SkiaSharp 中将图表坐标转换为设备像素?

Posted

技术标签:

【中文标题】如何在 SkiaSharp 中将图表坐标转换为设备像素?【英文标题】:How do you translate chart coordinates to device pixels in SkiaSharp? 【发布时间】:2020-01-29 20:50:28 【问题描述】:

我正在尝试编写矩阵变换以将图表点转换为 SkiaSharp 中的设备像素。只要我使用 0,0 作为我的最小图表坐标,我就可以使用它,但是如果我需要从负数逐步增加,它会导致绘图向左和向下移动。即X轴左移出窗口,Y轴下移出窗口。

这是一个典型的折线图(最小图表点在左下角,而最小设备点在左上角)。我已经在转换中考虑了这一点。

在单步执行代码时,我可以看到从 Matrix 返回的坐标不是我所期望的,所以我认为问题出在我的变换上,但我无法查明它。

更新:经过进一步检查,我相信我弄错了,它没有移动,只是没有正确缩放到屏幕的最大端。图表顶部和右侧的边距比应有的要大,但底部和左侧很好。我一直无法确定为什么缩放没有填满画布。

以下是我的矩阵方法:

private SKMatrix ChartToDeviceMatrix, DeviceToChartMatrix;
private void ConfigureTransforms(SKPoint ChartMin, 
    SKPoint ChartMax, SKPoint DeviceMin, SKPoint DeviceMax)

    this.ChartToDeviceMatrix = SKMatrix.MakeIdentity();
    float xScale = (DeviceMax.X - DeviceMin.X) / (ChartMax.X - ChartMin.X);
    float yScale = (DeviceMin.Y - DeviceMax.Y) / (ChartMax.Y - ChartMin.Y);

    this.ChartToDeviceMatrix.SetScaleTranslate(xScale, yScale, DeviceMin.X, DeviceMax.Y);
    this.ChartToDeviceMatrix.TryInvert(out this.DeviceToChartMatrix);


// Transform a point from chart to device coordinates.
private SKPoint ChartToDevice(SKPoint point)

    return this.ChartToDeviceMatrix.MapPoint(point);

调用它的代码是:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)

    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    float strokeWidth = 1;
    float margin = 10;

    // SKPaint definitions omitted for brevity.

    var ChartMin = new SKPoint(-10, -1); // Works fine if I change this to 0,0
    var ChartMax = new SKPoint(110, 11);
    var DeviceMin = new SKPoint(margin, margin);
    var DeviceMax = new SKPoint(info.Width - margin, info.Height - margin);
    const float stepX = 10;
    const float stepY = 1;
    const float tickX = 0.5;
    const float tickY = 0.075F;

    // Prepare the transformation matrices.
    this.ConfigureTransforms(ChartMin, ChartMax, DeviceMin, DeviceMax);

    // Draw the X axis.
    var lineStart = new SKPoint(ChartMin.X, 0);
    var lineEnd = new SKPoint(ChartMax.X, 0);
    canvas.DrawLine(this.ChartToDevice(lineStart), this.ChartToDevice(lineEnd), axisPaint);

    // X Axis Tick Marks
    for (float x = stepX; x <= ChartMax.X - stepX; x += stepX)
    
        var tickMin = new SKPoint(x, -tickY);
        var tickMax = new SKPoint(x, tickY);
        canvas.DrawLine(this.ChartToDevice(tickMin), this.ChartToDevice(tickMax), axisPaint);
    

    // Draw the Y axis.
    // The inversion of above, basically the same.

【问题讨论】:

【参考方案1】:

我能够用足够的时间发现自己的问题。我没有计算正确的偏移量。 this.ChartToDeviceMatrix.SetScaleTranslate(xScale, yScale, DeviceMin.X, DeviceMax.X);

应该是:

this.ChartToDeviceMatrix.SetScaleTranslate(xScale, yScale, -ChartMin.X * xScale + DeviceMin.Y, -ChartMin.Y * yScale + DeviceMax.Y);

最终矩阵方法是:

private SKMatrix ChartToDeviceMatrix, DeviceToChartMatrix;
private void ConfigureTransforms(SKPoint ChartMin, SKPoint ChartMax, SKPoint DeviceMin, SKPoint DeviceMax)

    this.ChartToDeviceMatrix = SKMatrix.MakeIdentity();
    float xScale = (DeviceMax.X - DeviceMin.X) / (ChartMax.X - ChartMin.X);
    float yScale = (DeviceMin.Y - DeviceMax.Y) / (ChartMax.Y - ChartMin.Y);
    float xOffset = -ChartMin.X * xScale + DeviceMin.X;
    float yOffset = -ChartMin.Y * yScale + DeviceMax.Y;

    this.ChartToDeviceMatrix.SetScaleTranslate(xScale, yScale, xOffset, yOffset);
    this.ChartToDeviceMatrix.TryInvert(out this.DeviceToChartMatrix);

【讨论】:

以上是关于如何在 SkiaSharp 中将图表坐标转换为设备像素?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 OpenLayers 中将“正常”坐标转换为 OSM 坐标

如何将 SkiaSharp.SkBitmap 转换为 Android.Graphics.Bitmap?

在 CUDA 中,如何在内核函数中将屏幕空间坐标转换为世界空间坐标

使用 Projection 在 Three.js 中将世界坐标转换为屏幕坐标

如何在 R 中将 Highcharter 图表保存为 gif?

有没有一种简单的方法来处理(转换)SkiaSharp 中的一组对象?