views:

584

answers:

3

This is unfortunately a quite complex issue to explain, so please don't get discouraged by the wall of text - it's there for a reason. ;)

I'm working on a transformation manager for flash, written with Actionscript 3. Users can place objects on the screen, for example a rectangle.

This rectangle can then be selected and transformed: move, scale or rotate.

Because flash by default rotates around the top left point of the object, and I want it to rotate around the center, I created a wrapper setup for each display object (eg. a rectangle).

This is how the wrappers are setup:

//the position wrapper makes sure that we do get the top left position when we access x and y
var positionWrapper:Sprite = new Sprite();
positionWrapper.x = renderObject.x;
positionWrapper.y = renderObject.y;

//set the render objects location to center at the rotation wrappers top left
renderObject.x = 0 - renderObject.width / 2;
renderObject.y = 0 - renderObject.height / 2;

//now create a rotation wrapper, at the center of the display object
var rotationWrapper:Sprite = new Sprite();
rotationWrapper.x = renderObject.width / 2;
rotationWrapper.y = renderObject.height / 2;

//put the rotation wrapper inside the position wrapper and the render object inside the rotation wrapper
positionWrapper.addChild(rotationWrapper);
rotationWrapper.addChild(renderObject);

Now, the x and y of the object can be accessed and set directly: mainWrapper.x or mainWrapper.y. The rotation can be set and accessed from the child of this main wrapper: mainWrapper.getChildAt(0).rotation. Finally, the width and height of the display object can be retreived and set by getting the child of the rotation wrapper and accessing the display object directly.

An example on how I access them:

//get wrappers and render object
var positionWrapper:Sprite = currentSelection["render"];
var rotationWrapper:Sprite = positionWrapper.getChildAt(0) as Sprite;
var renderObject:DisplayObject = rotationWrapper.getChildAt(0);

This works perfectly for all initial transformations: moving, scaling and rotating.

However, the problem arises when you first rotate an object (eg. 45 degrees) and then scale it. The scaled object is getting out of shape and doesn't scale as it should.

This for example happens when you scale to the left. Scaling left is basically adding n width to the object and then reduce the x coord of the position wrapper by n too:

renderObject.width -= diffX;
positionWrapper.x += diffX;

This works when the object is not rotated. However, when it is, the position wrapper won't be rotated as it is a parent of the rotation wrapper. This will make the position wrapper move left horizontally while the width of the object is increased diagonally.

I hope this makes any sense, if not, please tell me and I'll try to elaborate more.

Now, to the question: should I use a different kind of setup, system or structure? Should I maybe use matrixes, if so, how would you keep a static width/height after rotation? Or how do I fix my current wrapper system for scaling after rotation? Any help is appreciated.

+1  A: 

what about this:

positionWrapper.width -= diffX;
positionWrapper.x += diffX;

also, you can kick out rotationWrapper and perform rotations on positionWrapper directly.

apart from that, I suggest you have a look at senocular's tranformtool and sephiroth's modifications.

edit: what you're basically looking for is a scale transformation by an arbitrary vector with an arbitrary center. with that, the rest is trivial. for the first problem: create a matrix M that moves the center to 0.0 and rotates the vector onto the x axis. concat M with an x-scale by the vector's length and the inverse of M and the resulting matrix should do what you need. of course you can calculate the resulting matrix with pen and paper directly, but this should be good enough ;)

greetz

back2dos

back2dos
How can I kick out rotationWrapper when renderObject has to be rotated around the center? That's what the rotation wrapper is for, and then the position wrapper makes sure that the position is still at the top left and not the center.
Tom
@Tom: slight mistake on my side. but yes, you can kick it out. you can just as well center the renderObject within the positionWrapper and then rotate the positionWrapper itself. updated it in the post.
back2dos
Tried setting width to the positionWrapper, it doesn't work because the positionWrapper is not rotated and thus it will change the width horizonally even if the object has for example rotated 45 degrees.
Tom
Regardless of whether the wrapper has been rotated, you can't just move it n pixels back in x coords because the rotated object will not move that far. Maybe this requires some sinus calculations?
Tom
@Tom: you should maybe define the desired behaviour. what exactly does "scale to the left" mean, if not scaling horizontally? is "scale to the left" in renderObject-coordinates?also, did you check out the links I provided?
back2dos
I saw the links. Scale to left means that the object has to scale to the left, aka it should get a bigger width while the right side stays at the same position. This is the width and position of the rotated element, not of the wrapper. I was already known with those frameworks, however they seem to take a really different approach than I do and I'm not a fan of using code that is 1) not documented 2) not mine 3) not easily customizable
Tom
Update: if it is still not clear what I mean, please tell me again and I'll create some screenshots.
Tom
@Tom: well, that statement is true for more than 99% of the code out there. you'll just have to get used to it I guess.anyway, please see post update.
back2dos
Going to give matrix some more attention then, will accept if I manage to do it this time (already tried it before but the problem was that the matrix caused the width, height, x and y to change yet the image should remain stationary although rotated.
Tom
@Tom: the "trick" is to simply transform coordinates so that the scale transformation is trivial, which is what M does, then perform the transformation, and then transform coordinates back using the inverse of M. note that basically, you can do the same thing with renderObject: rotate it back to 0, perform the scaling (including moving), and restore rotation. this might however cost a little more performance, but since this happens on user input, it shouldn't really have an impact.
back2dos
+1  A: 

This looks like you should be using the transform matrix
have a look at this great post
http://gasi.ch/blog/zooming-in-flash-flex/

danjp
I tried using the matrix class. The problem is that the matrix class is changing the width, height, x and y of the object after rotation.
Tom
A: 

If you are having problems with the matrix class you should try doing it by hand. Check out this article for an alternative to the transform matrix class:

http://www.actionscript-flash-guru.com/blog/33-scale-around-a-point-transform-multiple-objects-actionscript-3-as3

Niko Limpika