使用矩阵放大固定点

amu*_*cxg 4 c# matrix winforms

我正在尝试使用单个全局矩阵实现关于固定点的缩放。运行时,如果单击控件,它会缩放,但每次单击时测试矩形会进一步向下和向右移动。据我所知,每个转换(到原点、缩放和返回到原始位置)都单独工作正常,但是当我将所有 3 个组合在一起时,我没有得到正确的行为。

缩放代码

单击控件时,代码(应该)转换到原点,按比例放大,然后转换回原始位置。

protected override void OnMouseDown(MouseEventArgs e)
      {
         base.OnMouseDown(e);
         if (e.Button == System.Windows.Forms.MouseButtons.Left)
         {
            float xPos = e.Location.X - viewMatrix.OffsetX;
            float yPos = e.Location.Y - viewMatrix.OffsetY;

            Matrix translateOrigin = new Matrix(1, 0, 0, 1, -xPos, -yPos);
            Matrix translateBack = new Matrix(1, 0, 0, 1, xPos, yPos);
            Matrix scaleMatrix = new Matrix(1.5f, 0, 0, 1.5f, 0, 0);

            viewMatrix.Multiply(translateOrigin);
            viewMatrix.Multiply(scaleMatrix);
            viewMatrix.Multiply(translateBack);
         }
         else
         {
            viewMatrix = new Matrix();
         }
         Refresh();
      }
Run Code Online (Sandbox Code Playgroud)

绘图代码

这是我用来绘制的代码。这两个矩形仅供参考,第二个值new Pen(2)是为了确保我的线条保持 1 像素宽。

protected override void OnPaint(PaintEventArgs e)
      {
         base.OnPaint(e);

         GraphicsState gState = e.Graphics.Save();

         e.Graphics.MultiplyTransform(viewMatrix);
         e.Graphics.DrawRectangle(new Pen(Color.Pink, 1.0f / viewMatrix.Elements[3]), -5, -5, 10, 10);
         e.Graphics.DrawRectangle(new Pen(Color.Pink, 1.0f / viewMatrix.Elements[3]), 20, 20, 10, 10);

         e.Graphics.Restore(gState);
      }
Run Code Online (Sandbox Code Playgroud)

编辑

在休息了一天(或 2 天)后再次查看代码,我意识到我脑子里有一个错误的想法(这是我在一天结束时试图解决这个问题的结果)。我正在寻找的行为是视图将随着单击点保持在同一位置而缩放。例如,如果我单击其中一个矩形的右下角,视图将缩放它,将右下角保持在鼠标下方。

编辑 2

在@TaW 的大量帮助之后,我提出了以下代码,该代码将缩放并保持鼠标下的点固定。

protected override void OnMouseDown(MouseEventArgs e)
{
 base.OnMouseDown(e);

 if (e.Button == System.Windows.Forms.MouseButtons.Left)
 {

    //Get the inverse of the view matrix so that we can transform the mouse point into the view
    Matrix viewMatrixRev = viewMatrix.Clone();
    viewMatrixRev.Invert();

    //Translate the mouse point
    PointF mousePoint = e.Location;
    viewMatrixRev.TransformPoints(new PointF[] { mousePoint });

    //Transform the view
    viewMatrix.Translate(-mousePoint.X, -mousePoint.Y, MatrixOrder.Append);
    viewMatrix.Scale(zoom, zoom, MatrixOrder.Append);
    viewMatrix.Translate(mousePoint.X, mousePoint.Y, MatrixOrder.Append);
 }
 else
 {
    viewMatrix = new Matrix();
 }
 Refresh();
}
Run Code Online (Sandbox Code Playgroud)

Rip*_*ple 5

Matrix.Multiply 有一个论点

将此 Matrix 与矩阵参数中指定的矩阵相乘,方法是在指定的 Matrix前面加上。

因此,您的矩阵序列以相反的顺序应用。

试试这个:

viewMatrix.Multiply(translateOrigin, MatrixOrder.Append);
viewMatrix.Multiply(scaleMatrix, MatrixOrder.Append);
viewMatrix.Multiply(translateBack, MatrixOrder.Append);
Run Code Online (Sandbox Code Playgroud)

编辑:
这个想法很简单。

在此处输入图片说明

您需要做的就是以正确的顺序转换到原点、缩放并转换回枢轴(鼠标点)。您viewMatrix保留了先前的结果,因此应该之后应用新的变换矩阵,这将由MatrixOrder.Append.

现在的解决方案是:

float xPos = e.Location.X;
float yPos = e.Location.Y;

Matrix translateOrigin = new Matrix(1, 0, 0, 1, -xPos, -yPos);
Matrix translateBack = new Matrix(1, 0, 0, 1, xPos, yPos);
Matrix scaleMatrix = new Matrix(1.5f, 0, 0, 1.5f, 0, 0);

viewMatrix.Multiply(translateOrigin, MatrixOrder.Append);
viewMatrix.Multiply(scaleMatrix, MatrixOrder.Append);
viewMatrix.Multiply(translateBack, MatrixOrder.Append);
Run Code Online (Sandbox Code Playgroud)

此外,这可以更简单地完成。

float xPos = e.Location.X;
float yPos = e.Location.Y;

viewMatrix.Translate(-xPos, -yPos, MatrixOrder.Append);
viewMatrix.Scale(1.5f, 1.5f, MatrixOrder.Append);
viewMatrix.Translate(xPos, yPos, MatrixOrder.Append);
Run Code Online (Sandbox Code Playgroud)