wpf 实现类似于InkCanvas的笔划绘制

jq6vz3qz  于 2023-06-24  发布在  其他
关注(0)|答案(1)|浏览(145)

我的问题有效地归结为精确的鼠标移动检测。
我需要创建自己的InkCanvas实现,并且在大部分情况下都取得了成功,除了准确地绘制笔划。

void OnMouseMove(object sneder, MouseEventArgs e)
{
    var position = e.GetPosition(this);
    
    if (!Rect.Contains(position))
        return;
    
    var ratio = new Point(Width / PixelDisplay.Size.X, Height / PixelDisplay.Size.Y);
    var intPosition = new IntVector(Math2.FloorToInt(position.X / ratio.X), Math2.FloorToInt(position.Y / ratio.Y));

    DrawBrush.Draw(intPosition, PixelDisplay);
    UpdateStroke(intPosition); // calls CaptureMouse
}

这工作。位图(PixelDisplay)被更新,一切都很好。但是,任何类型的快速鼠标移动都会导致图形中的较大跳跃。我已经将问题缩小到e.GetPosition(this),它阻止事件的时间足够长,以至于不准确。
有一个this问题,它的答案是不清楚的,或者根本没有明显的区别。经过更多的测试,上述解决方案和类似的想法失败,特别是因为e.GetPosition
我知道InkCanvas在浏览了源代码后使用了类似的方法;检测设备,如果是鼠标,获取其位置并捕获。我看不出同样的过程在这里不一样。

lvmkulzt

lvmkulzt1#

我最终能够部分解决这个问题。

position = getMousePosition()

if canvasRect does not contain position
    return

bitmapRatio = Vector2(elementWidth / bitmapSize.X, elementHeight / bitmapSize.Y);
bitmapPosition = Vector2(floor(position.X / bitmapRatio.X), floor(position.Y / bitmapRatio.Y))
// Calculate pixel coordinates based on the element size

lastDrawPoint = getLastDrawPoint() or null
// Uses System.Linq to grab the last stroke, if it exists

if lastDrawPoint is not null // Determine if we're in the middle of a stroke
    alphaDiff = 1 / Vector(bitmapPosition.X - lastDrawPoint.X, bitmapPosition.Y - lastDrawPoint.Y).Magnitude; 
// For some interpolation, calculate 1 / distance (magnitude) of the two points.
// Magnitude formula: Math.Sqrt(Math.Pow(X, 2) + Math.Pow(Y, 2));
    alpha = 0;

    xDiff = bitmapPosition.X - lastDrawPoint.X;
    yDiff = bitmapPosition.Y - lastDrawPoint.Y;

    while (alpha < 1)
        alpha += alphaDiff

        adjustedPosition = new Vector(
            floor((position.X + (xDiff * alpha)) / bitmapRatio.X),
            floor((position.Y + (yDiff * alpha)) / bitmapRatio.Y));
        
        drawCurrentBrush(adjustedPosition)

drawCurrentBrush(bitmapPosition); // Draw the original point
setLastDrawPoint(bitmapPosition);

此实现在最后一个点和当前点之间进行插值以填充任何间隙。例如,当使用非常小的画笔时,它并不完美,但仍然是一个解决方案。

备注

IntVector是一个由我延迟实现的Vector2,只是使用整数代替。Math2是一个helper类。FloorToInt(int)MathF.Round(...))的缩写

相关问题