根据当前鼠标位置缩放图形

Nat*_*ema 6 c# graphics zooming winforms

我正在尝试根据鼠标的当前位置缩放绘图。现在我的 onMouseWheel 方法看起来像这样(基于这个 StackOverflow 答案):

    private void onMouseWheel(object sender, MouseEventArgs e)
    {
        if (e.Delta > 0)
        {
            _scale *= 1.25f;
            _translateY = e.Y - 1.25f * (e.Y - _translateY);
            _translateX = e.X - 1.25f * (e.X - _translateX);
        }
        else
        {
            _scale /= 1.25f;
            _translateY = e.Y - 0.8f * (e.Y - _translateY);
            _translateX = e.X - 0.8f * (e.X - _translateX);
        }
        this.Invalidate();
    }
Run Code Online (Sandbox Code Playgroud)

_scale, _translateX, 和_translateY是成员变量。

我正在缩放图形,翻译它,然后像这样绘制线条:

    protected override void OnPaint(PaintEventArgs e)
    {
        g.ScaleTransform(_scale, _scale);
        g.TranslateTransform(_translateX, _translateY);
        //draw lines here
    }
Run Code Online (Sandbox Code Playgroud)

该视频显示了当我尝试放大和缩小某个点时会发生什么。我究竟做错了什么?

这是用于测试目的的示例面板类中的代码:

class Display : Panel
{
    public Display()
    {
        this.MouseWheel += new MouseEventHandler(this.onMouseWheel);
    }

    private void onMouseWheel(object sender, MouseEventArgs e)
    {
        if (e.Delta > 0)
        {
            _scale *= 1.25f;
            _translateY = e.Y - 1.25f * (e.Y - _translateY);
            _translateX = e.X - 1.25f * (e.X - _translateX);
        }
        else
        {
            _scale /= 1.25f;
            _translateY = e.Y - 0.8f * (e.Y - _translateY);
            _translateX = e.X - 0.8f * (e.X - _translateX);
        }
        this.Invalidate();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        g.ScaleTransform(_scale, _scale);
        g.TranslateTransform(_translateX, _translateY);

        Pen pen = new Pen(Color.Red);
        g.FillEllipse(pen.Brush, 50, 50, 10, 10);
    }
}
Run Code Online (Sandbox Code Playgroud)

Spe*_*tre 3

太懒了,无法使方程式正确(并且很可能会犯与您类似的错误......我不知道这是否只是我,但正是这种简单的东西我无法处理并让我发疯)。相反,我按如下方式处理此类任务(这样更安全,不会出错):

  1. 创建屏幕坐标和世界坐标之间的变换函数

    因此,您的鼠标位置位于屏幕坐标中,而渲染的内容位于世界坐标中。因为这只是2D,所以很容易。make函数在这两者之间进行转换。你的世界到屏幕的变换(如果我没有忽略某些事情)是这样的:

    g.ScaleTransform(_scale, _scale);
    g.TranslateTransform(_translateX, _translateY);
    
    Run Code Online (Sandbox Code Playgroud)

    所以:

    screen_x=(world_x*_scale)+_translateX;
    screen_y=(world_y*_scale)+_translateY;
    
    Run Code Online (Sandbox Code Playgroud)

    所以反过来:

    world_x=(screen_x-_translateX)/_scale;
    world_y=(screen_y-_translateY)/_scale;
    
    Run Code Online (Sandbox Code Playgroud)
  2. 改变缩放/比例

    这个想法是,在缩放/缩放更改后,鼠标位置在世界坐标中应与以前保持相同。所以在改变之前记住鼠标的世界坐标。然后根据它计算更改后的屏幕位置并将差异放入翻译中。

这是简单的 C++ 示例:

g.ScaleTransform(_scale, _scale);
g.TranslateTransform(_translateX, _translateY);
Run Code Online (Sandbox Code Playgroud)

mx,my屏幕坐标中的实际鼠标位置, 是x0,y0平移,zoom是比例。

这里捕获了这个的GIF动画:

例子

[edit1] 看起来你的 gfx 对象使用了转播矩阵

这意味着转换的顺序颠倒了,因此方程发生了一些变化...这里是C++中的案例:

screen_x=(world_x*_scale)+_translateX;
screen_y=(world_y*_scale)+_translateY;
Run Code Online (Sandbox Code Playgroud)