使用矩阵放大固定点

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

编辑

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

Matrix.Multiply有一个参数

将矩阵乘以矩阵参数中指定的矩阵,方法是预先指定Matrix。

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

试试这个:

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

编辑:
这个想法很简单。

在此处输入图像描述

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

你的代码工作正常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

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

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

  • 您单击的控件将报告鼠标位置的未缩放和未翻译点。
  • ..) 用户查看并单击缩放和翻译的图形。

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

 // 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(); } 

现在我可以点击任何地方,它会放大鼠标。

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

顺便说一下,我认为你不需要在Paint事件中保存Graphics状态。 无论如何,它会在每次通话中重置。 – 我的代码正在使用一个简单的Panel ; 你可以适应你的OnMouseDown ..