views:

526

answers:

2

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?

+1  A: 

You have to be aware that the order of the transformations is important. Therefore, just removing the scale may change the "meaning" of the following translations (offset for instance).

Maybe just keep a record of the scale applied so far (let's call this currentscale) and add a scale of 1/currentscale to your tranformations.

Lucero
Accepted tentatively. It's what I've suspected all along. Isn't there a way to retrieve the scaling component from the transformation matrix? I tried but it affects the rotation.
Vulcan Eager
No, not that I know of, at least not in a generic way. If only a scale was performed, this should be easy, but if the matrix performs more complex operations, things are no longer as easy.
Lucero
+1  A: 

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.

Chris Ostler
Good idea. But seems expensive.
Vulcan Eager
Accepted as the only possible answer.
Vulcan Eager