Thursday, June 14, 2007

Rotating Around a Point

In the fl.motion package there is a class called MatrixTransformer. It provides a bunch of methods that will come in handy on occasion. For instance, have you ever needed to rotate a movieclip around some point? I have, and I usually spend 30 minutes figuring it out each time I need to use it.

Well MatrixTransformer provides an easy way for you to do this with 1 line of code! In the fewest lines possible, here is how you can rotate a movie clips 45 degrees around the mouse position:



import flash.geom.Matrix;
import fl.motion.*;
var mat:Matrix = clip.transform.matrix;
MatrixTransformer.rotateAroundExternalPoint(mat,mouseX,mouseY,45);



The code above gets the clip's current matrix, modifies it, and reapplies it back to the clip. Pretty slick! The MatrixTransformer class provides a few other useful methods. The only thing that I find a bit odd is that it takes degrees instead of radians.

Here is a more practical example use of this code. (Practical from a game programmer's point of view.) Just click and hold on an object. It will hold that point it place as the object simulates a realistic rotation.




26 comments:

Biby Cletus said...

Cool blog, i just randomly surfed in, but it sure was worth my time, will be back

Deep Regards from the other side of the Moon

Biby Cletus

Anonymous said...

Cool to know. 30 minutes? I can recall more like 3-5 hours once but to be fair I was trying to fit it into an existing architecture and, it was was Flash 8. (If this was in Flash 8/AS2 I think I'd be embarrassed right about now.)

Thanks, Phillip

Adrian Parr said...

Hi Jobe,

Where is the fl.motion package? Is this something you have written that is available?

Cheers,

Adrian

Jobe Makar said...

Hi,

I did not create those classes. They come installed with Flash where ever Flash installs the classes (differs per OS).

vini said...

Great Work.. love it

mark said...

Hey, that looks really good and is helping along with what I'm doing.

A couple of questions though: Firstly, I can't find fl.motion, it seems to throw up an error for no reason. It's listed in Flash CS3 on the left hand side, but no clue as to the whereabouts ...

Secondly - I'm trying to pre-render however many rotations are required for a particular image, and making sure that they are always rotated around the center for obvious reasons. What would be the best way to achieve this? I'm struggling to find a high-quality and glitch-free way to get this working!

Good stuff - thanks! :-)

Jobe Makar said...

Hi,

If you wanted to precache rotations then you can use the snapshot function I talke about in this post:
http://jobemakar.blogspot.com/2007/05/taking-movieclip-snapshot.html

If you cache 1 shot per degree, then you'd have an array with 360 elements per image. You can precache this in the beginning of your applicaiton. Or you can check to see if it has been cached when it is time to render that rotation. If no, then cache it. That would mean your app starts off a little slow and speeds up the more it is used.

mark said...

Hi Jobe,

Thanks for your help - I was planning on doing something just like that, and will certainly look at that link in detail - cheers!

What about the lack of being able to get fl.motion.* imported, or anything like that for that matter? (As in, I've tried without ".*", tried .MatrixTransform, etc etc...)

Jobe Makar said...

It is an ActionScript 3 package. If you're trying to do this in AS2, then that is your problem. If you're doing it in AS3 then it sounds like for some reason you might be missing that package, or have a typo.

mark said...

Hey,

It is AS3, using the Flex compiler (not compiling with Flash CS3 or anything as it's only a trial version and don't want to get into that habbit!). Also not a typo, as I've tried variations, and have copied directly from you.

So, how could I be missing that package? Does it not all come with the normal install? If not, I'll go forth and try and find out where to obtain said package!

Cheers!

mark said...

Okay so I've figured out the problem - I think...

Flex does not have fl.motion - only Flash does. (!?!). So, now the important question - is there any way to actually get fl.motion working on Flex? I've read somewhere about bringing it across using XML but from what I can tell this is just a bridging mechanism - or, is there any alternative, given that rotation is my biggest concern here?

Thanks - appreciate any help! :-)

Martin Munoz said...

Hey Jobe,

I've been following your blog (silently) and I have a situation that relates to this post.

I'm making an interface for a website that will be similar to www.leoburnett.com . I first did it by placing the entire site within a container and then offsetting the site within the container to in effect change the registration point.

I do not like this method.

I would prefer to have the site complete controlled mathematically. It seems less prone to glitches and hacks, and all around more flexible and dynamic ( ie. no hacks required for clicking on a link, while another one is already zooming in ).

So here's the problem: the method you posted is only for relative angles. It is great for an exponential (or elastic in your specific example) implementation.

ie. rotation += (goal - rotation)/10;

Now instead of adding the rotation directly, just replace the deltaAngle parameter of the rotateAroundExternalPoint function with "(goal - rotation)/10" or something of the sort.

The problem with this is that its hard to control the timing and finishing. It's great for something like you showed: a physics simulation which is constantly updating and exact timing is not important, but it is not so great for something that has a definitive start and end (namely a website's navigation animation).

So my proposed solution is to create a tween for a "temporary" rotation variable using the fl.transitions package and finding the difference in the value of the tweened property after each Tick passing that deltaTempRotation to rotateAroundExternalPoint.

This seems like it would not function "perfectly" though. I'm worried that the final value may not be exactly the final value that you passed to the tween's constructor. This is very important. If it is not exact pixel fonts may not align properly. It has to be as accurate as Flash allows!

So what do you think of this method? Can you lead me in a better direction?

Thanks,
Martin Munoz
munozm@mcmaster.ca

Robert Penner said...

Hi Jobe, I wrote the fl.motion package; it's cool to see you found MatrixTransformer.

Martin, if you want absolute rotation around a point, you can easily edit the rotateAroundExternalPoint() method. The original code is fairly simple:

public static function rotateAroundExternalPoint(m:Matrix, x:Number, y:Number, angleDegrees:Number):void
{
m.tx -= x;
m.ty -= y;
m.rotate(angleDegrees*(Math.PI/180));
m.tx += x;
m.ty += y;
}

The logic is simply "shift to the origin, rotate, then shift back".
You can change the middle line of code to:

setRotation(m, angleDegrees);

If you prefer radians, you can do this instead:

setRotationRadians(m, angleRadians);

Here's the code for setRotationRadians:

public static function setRotationRadians(m:Matrix, rotation:Number):void
{
var oldRotation:Number = getRotationRadians(m);
var oldSkewX:Number = getSkewXRadians(m);
setSkewXRadians(m, oldSkewX + rotation-oldRotation);
setSkewYRadians(m, rotation);
}

Oh boy, now you need the code for the skew as well:

public static function setSkewXRadians(m:Matrix, skewX:Number):void
{
var scaleY:Number = getScaleY(m);
m.c = -scaleY * Math.sin(skewX);
m.d = scaleY * Math.cos(skewX);
}

public static function setSkewYRadians(m:Matrix, skewY:Number):void
{
var scaleX:Number = getScaleX(m);
m.a = scaleX * Math.cos(skewY);
m.b = scaleX * Math.sin(skewY);
}


The docs for MatrixTransformer are
here.

Robert Penner said...

Oh right, the skew methods call the scale methods. Here they are:

public static function getScaleX(m:Matrix):Number
{
return Math.sqrt(m.a*m.a + m.b*m.b);
}

public static function getScaleY(m:Matrix):Number
{
return Math.sqrt(m.c*m.c + m.d*m.d);
}

Jobe Makar said...

Hey penner, what you are you doing here?! :)

Anonymous said...

Does anyone know if the entire MatrixTransformer class exist anywhere for AS2?
Thanks!

Rodislav said...

thanks a lot for this post! is verry helpfull!! thanks!

Piterwilson.com said...

Great post thank you!

but i have a question. How do you get the bitmapData to stay smooth?

When i do it, the bitmap rotates but it looks jagged and not anti-aliased.

Can you please post that part of the code?

thank you!

Jobe Makar said...

Hi,

Find the bitmap in the library, double-click it, select 'allow smoothing'.

Anonymous said...
This post has been removed by a blog administrator.
Tof said...

Hi, Jobe!
Your sample doesn't seem to include the gravitational physics code. For example if you click the lower-right side of an object it swings down from the upper left and oscillates until stopping.

If that code is proprietary then could you at least point us in the direction of something like it, thanks!

Anonymous said...

Hi,
Thanks for the example... is it available for download so I can see how you get the realistic movement?

Whenever I apply the code it just seems to jump from one position (eg.zero rotation) to the specified rotation (eg.200 degrees). it doesn't do the tweening in between.

ThankyouThankyouThankyou

Anonymous said...

Great idea, but I am having a problem using it. This demo also illustrates the problem.

Click a corner pixel and let it swing. The corner pixel wanders away from it's original position. It seems to be due to the gradual summation of rounding errors. How can I fix this?

John McCormack

Anonymous said...

This was perfect, thank you SO much. I had a 3d engine I'd stopped working on which this tip has revived. Thanks again!

Anonymous said...

Thanks a lot. I was googling this issue and came across many unnecessarily complicated solutions.

Tarwin said...

Thanks! I was doing it manually the whole time by moving the movieClip by a distance I had to work out with a Cos and Sin call - I'm hoping this is less expensive!