如何在 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 中将世界坐标转换为屏幕坐标