Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to rotate, scale, and translate a matrix all at once in C#?

Okay, this is something that should be a simple matrix question, but my understanding of matrices is somewhat limited. Here's the scenario: I have a 1px by 1px sprite that I want to scale by some amount x and y (different amounts on each side), and then I want to rotate that sprite by some angle, and then I want to be able to precisely position the whole thing (from the top left or the center, makes no difference to me).

So far my code is vaguely close, but it tends to be off by some random amount depending on the angle I pass in.

I would think that this would do it:

        Point center = new Point( 50, 50 );
        float width = 60;
        float height = 100;
        float angle = 0.5;
        Vector3 axis = new Vector3( center.X, center.Y, 0 );
        axis.Normalize();
        Matrix m = Matrix.Scaling( width, height, 0 ) *
            Matrix.RotationAxis( axis, angle ) *
            Matrix.Translation( center.X, center.Y, 0 );

But it tends to shrink the scale of the rotated line way down, even though I think it's positioning it sort of right.

I've also tried this:

  Matrix m = Matrix.Transformation2D( new Vector2( center.X, center.Y ), 0f,
      new Vector2( width, height ), new Vector2( center.X, center.Y ),
      angle, Vector2.Zero );

The line looks exactly right, with the exact right size and shape, but I can't position it correctly at all. If I use the translation vector at the end of the call above, or if I set a position using Sprite.Draw, neither works right.

This is all in SlimDX. What am I doing wrong?

like image 832
Chris McElligott Park Avatar asked Mar 11 '09 19:03

Chris McElligott Park


2 Answers

I just went through the pain of learning matrix transformation, but using XNA.

I found these articles to be very helpful in understanding what happens. The method calls are very similar in XNA and the theory behind it all should apply to you, even with SlimDX.

From glancing at your code, I think you should be translating at the start, to the origin, and then translating again at the end, to the final position, though I'm still a little bit of a newbie at this as well.

The order I would do it in is:

  • Translate to origin
  • Scale
  • Rotate
  • Translate to desired location

The reason for translating to the origin first is that rotations are based from the origin. Therefore to rotate something about a certain point, place that point on the origin before rotating.

like image 117
Ben S Avatar answered Sep 21 '22 20:09

Ben S


Okay, this is now working. Here's my working code for this, in case someone else needs it:

    Point sourceLoc = new Point ( 50, 50 );
    float length = 60;
    float thickness = 2;
    float angle = 0.5;
    Matrix m = Matrix.Scaling( length, thickness, 0 ) *
            Matrix.RotationZ( angle ) *
            Matrix.Translation( sourceLoc.X, sourceLoc.Y, 0 );
    sprite.Transform = m;

    sprite.Draw( this.tx, Vector3.Zero, Vector3.Zero, Color.Red );

This will draw an angled line of your chosen length, with a thickness equal to your chosen thickness (presuming your texture is a 1x1 pixel white image). The source location is where the line will emit from, with whatever angle you specify (in radians). So if you start at zero and increment by something like 0.1 until you hit 2PI, and then reset to 0, you'll have a line that rotates around a center like a clock hand or radar sweep. This is what I was looking for -- thanks to all who contributed!

like image 34
Chris McElligott Park Avatar answered Sep 21 '22 20:09

Chris McElligott Park