使用矩阵放大固定点

时间:2015-01-10 00:59:04

标签: c# winforms matrix

我正在尝试使用单个全局矩阵实现关于固定点的缩放。当运行时,如果单击控件,则会缩放,但测试矩形会在每次单击时向下和向右移动。 据我所知,每个转换(原点,比例和回到原始位置)单独工作正常,但当我将所有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();
      }

绘图代码

这是我用来绘制的代码。这两个矩形仅供参考,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);
      }

修改

在休息了好一天(或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();
}

2 个答案:

答案 0 :(得分:3)

带有一个参数的

Matrix.Multiply

  

将此矩阵乘以矩阵参数中指定的矩阵,预先指定的矩阵。

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

请改为尝试:

viewMatrix.Multiply(translateOrigin, MatrixOrder.Append);
viewMatrix.Multiply(scaleMatrix, MatrixOrder.Append);
viewMatrix.Multiply(translateBack, MatrixOrder.Append);

修改
这个想法很简单。

enter image description here

您需要做的就是转换为原点,缩放,并按正确顺序转换回数据(鼠标点)。 您的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);

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

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);

答案 1 :(得分:1)

你的代码很好用imo。但是当然你需要清楚自己想要什么!

这是您用来放大的要点:

float xPos = e.Location.X - viewMatrix.OffsetX;
float yPos = e.Location.Y - viewMatrix.OffsetY;

那就是发生了什么。

如果您希望第一个矩形保持其位置(以原点为中心),您只需将其更改为

float xPos = - viewMatrix.OffsetX;
float yPos = - viewMatrix.OffsetY;    

从而忽略了鼠标点击的位置。

当您只放大一个点时可以实际上保持在相同的位置!

更新:如果您希望该点为鼠标点击位置,您只需要一个使其成为新来源的翻译:

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

现在,当您在一个正方形的中间点击时,该矩形将保持固定并将在鼠标光标周围生长..

注意:减号可以弥补您编写代码的方式。从概念上讲,您首先移动Origin(正面),然后将其移回(负面)。

更新2

只有在您不改变放大的点时,上述代码更改才有效。为了使其适用于任何地方的一系列点击,它会更加复杂。

问题是,第一次缩放后有两个不同的视图:

  • 点击控件报告鼠标位置的未缩放和未翻译点。
  • ..但用户会看到并点击已缩放和翻译的图片。

我们下一次翻译需要的是 点击原始版本的要点。 为了获得这些点,我们可以使用反向转换来保持另一个矩阵,以使鼠标位置从感知返回到原始坐标:< / p>

// make the zoom factor accessible
float zoom = 1.5f;
// the graphics transformation
Matrix viewMatrix = new Matrix();
// the reverse transformation for the mouse point
Matrix viewMatrixRev = new Matrix();

private void panel1_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button == System.Windows.Forms.MouseButtons.Left)
    {
        // first we reverse translate the point
        // we need an array!
        PointF[] tv = new PointF[] { e.Location };
        viewMatrixRev.TransformPoints(tv);
        // after the reversal we can use the coordinates
        float xPos = tv[0].X;
        float yPos = tv[0].Y;

        // revers translation for the point
        Matrix scaleMatrixRev = new Matrix(1f / zoom, 0, 0, 1f / zoom, 0, 0);
        // the other transformations
        Matrix scaleMatrix = new Matrix(zoom, 0, 0, zoom, 0, 0);
        Matrix translateOrigin = new Matrix(1, 0, 0, 1, xPos, yPos);
        Matrix translateBack = new Matrix(1, 0, 0, 1, -xPos, -yPos);

        // we need two different orders, not sure yet why(?)
        MatrixOrder moP = MatrixOrder.Prepend;
        MatrixOrder moA = MatrixOrder.Append;

        // graphics transfomation
        viewMatrix.Multiply(translateOrigin, moP );
        viewMatrix.Multiply(scaleMatrix, moP ); 
        viewMatrix.Multiply(translateBack, moP ); 

        // store the next point reversal:
        viewMatrixRev.Multiply(translateBack, moA); 
        viewMatrixRev.Multiply(scaleMatrixRev, moA); 
        viewMatrixRev.Multiply(translateOrigin, moA); 

    }
    else
    {
        // reset
        viewMatrix = new Matrix();
        viewMatrixRev = new Matrix();
    }
    panel1.Invalidate();
}

现在我可以点击任意位置,它会放大鼠标。

但是,只要我们没有改变这一点,为什么缩放工作才会在任何一点上进行?因为我们点击的点没有被移动所以它保持不变并且有效&#39;一直......

顺便说一句,我认为你不需要在Graphics事件中保存Paint状态。无论如何,它会在每次通话中重置。 - 我的代码正在处理一个简单的Panel;您可以根据OnMouseDown ..

进行调整