I'm trying to implement scaling about a fixed point using a single global matrix. When run when, if the control is clicked it scaled but the test rectangles move further down and to right with each click. As far as I can tell each transformation (to the origin, scale, and back to the original location) is working fine individually but when I combine all 3 together I don't get the correct behavior.
Scaling Code
When the control is clicked the code (should) translate to the origin, scale up by a factor, then translate back to the original position.
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();
}
Drawing Code
This is the code that I'm using to draw. The two rectangles are just for reference and the second value on the new Pen(2)
is to make sure my lines stay 1 pixel wide.
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);
}
Edit
Looking at the code again after a good day (or 2) of rest I realized I had the wrong idea stuck in my head (this is what I get for trying to figure this out at the end of the day). The behavior that I'm looking for is that the view will scale with the clicked point staying in the same spot. Example if I clicked the lower right hand corner of one of the rectangles the view would zoom it keeping the lower right under the mouse.
Edit 2
After a lot of help from @TaW I came out with the following code that will zoom and keep the point under the mouse fixed.
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();
}