Reset Scale in a GDI+ Transformation Matrix

gdi+netscalingtransformwinforms

I'm writing a function to draw UI handles (rotate, resize and so on) in my client area in a Windows Forms application. The function is called when an object is selected.

The caller sends me a Graphics object properly rotated, scaled and translated to fit into the larger scheme of things (the selected object has its own rotation/translation/scale and the UI handle has a relative translation and rotation w.r.t the selected object). Now, I want my UI handles to be the same size regardless of the scale of the parent (selected object).

How do I eliminate/reset the scale factor in the transformation matrix? How do I reset to a scale of 1 while retaining the precious translation and rotation values?

Best Answer

Anti-Grain Geometry uses a basic method for determining the scaling of a transformation (implementation found in agg_trans_affine.cpp). It does so by:

  1. Calculating the rotation of the transformation
  2. Duplicating the transformation and applying the opposite rotation
  3. Transforming two known points and calculating the scale from the result

Translated to C#, this looks like:

Matrix transform = (Matrix)graphics.Transform.Clone();

PointF[] rotationPoints = new PointF[] { new PointF(0, 0), new PointF(1, 0) };
transform.TransformPoints(rotationPoints);

double rotationRadians = Math.Atan2(rotationPoints[1].Y - rotationPoints[0].Y, rotationPoints[1].X - rotationPoints[0].X);
transform.Rotate((float)(-rotationRadians * (180.0 / Math.PI)));

PointF[] scalePoints = new PointF[] { new PointF(0, 0), new PointF(1, 1) };
transform.TransformPoints(scalePoints);

float xScale = scalePoints[1].X - scalePoints[0].X;
float yScale = scalePoints[1].Y - scalePoints[0].Y;

The AGG code also contains a warning that there are degenerate cases where this will not work correctly, but it may be useful for your situation.

Related Topic