Scale Around a Point | Transform Multiple Objects | ActionScript 3 (AS3)

Scaling objects with ActionScript 3.0 (AS3) relative to a static point is commonly done with the the transform matrix, but I propose another solution after.

Solution 1:

var tx:Number = clip.x;
 var ty:Number = clip.y;
 var m:Matrix = clip.transform.matrix;
 m.translate( -tx, -ty );
 m.rotate(30*Math.PI/180);
 m.translate( tx, ty );
 m.scale( 1.5, 1.5);
clip.transform.matrix = m;

If you look at this and your response was "Huh?" I recommend reading on to solution two. The above example works well for scaling, rotating and translating objects when you know what the heck is going on, but when you start playing with iterative transformations and multiple objects you will start to have problems without a deep knowledge of the transform matrix. Here is what I suggest. If you want real control and understanding of what is going on you should do the transformations by hand. Here is solution 2 for scaling:

public static function scaleAroundPoint(objToScale:DisplayObject, regX:int, regY:int, scaleX:Number, scaleY:Number):void{
            if (!objToScale){
                return;
            }
            var transformedVector:Point = new Point( (regX-objToScale.x)*scaleX, (regY-objToScale.y)*scaleY );
            
            objToScale.x = regX-( transformedVector.x);
            objToScale.y = regY-( transformedVector.y);
            objToScale.scaleX =  objToScale.scaleX*(scaleX);
            objToScale.scaleY =  objToScale.scaleY*(scaleY);
}

So how does it work? The key is creating a vector that represents the location of our objToScale in relationship to our static point from which we want to scale.

Let us first look at my reason for doing this so as to have some sort of context for the solution.

Once upon a time, a little programmer was creating a drawing program. He created a user interface where one can select muliple objects on the screen which draws a bounding box around the selected objects. The next thing he did was to make it so the user can drag that bounding box to be larger or smaller. At this point what he wanted to do was to change the locations and scales of each of the selected objects so that they would fit into the new bounding box proportionaly to the way they originally fit in the orignal bounding box. First step was to find the static point (regX, regY) So when he created the new size for the bounding box he determined a point that was static. How he did this was by looking for a point that didn't move from befor to after the transformation. For example if you drag the top left corner of the box then the bottom right corner is the static point. If you drag the top right corner then the bottom left corner is the one that is static. If the scale from center option is set then that center point is the static point.

So again this point that does not move would be regX and regY in the above function in solution 2. Next each object is proccessed separately by the function using the same regX and regY. We then create a vector that runs from the registry point (aka the static point) to the x,y coordinates of our objToScale. This vector has an X component and a Y component which is the height and width of the vector if it was to be drawn on stage. Then we scale that vector by multiplying each component by our scaleX and scaleY values. From this new scaled vector we can determine the new location of the objToScale and relocate it by adding the X and Y component of the vector to the registry point. Then finally we can scale the movie clip its self and boom we are done.

I hope you understand this long and belabored explination. Please feel free to ask questions in the comments

 

 

Trackback(0)
Comments (17)add comment

Gregory Steimann said:

That's completely brilliant! Have you posted any of your code for the actual drawing of the shapes and applying the curves anywhere? Or would you be willing to? I'm kind of a novice AS3 programmer and honestly it would help me out sooo sooo much with a project I'm working on! I've been trying to figure this out for weeks now. smilies/sad.gif
 
report abuse
vote down
vote up
June 11, 2010
Votes: -1

Nicholas said:

Thanks! Check out the articles below for more things you might want to know about to build your own drawing program.

How to draw a bezier curve with two control points instead of the one.
http://www.actionscript-flash-guru.com/blog/31-cubic-bezier-using-beziersegment-actionscript-3-as3

Precision is the key to getting scaling right.
http://www.actionscript-flash-guru.com/blog/35-precision-problems-width-height-getbounds-actionscript-30-as3

Also in solution 2, regX and regY are integers. This is not necessary. In fact I suggest using numbers for everything since precision is key to getting this right as I mention in article 35 "precision problems with width height and getBounds".

A little bit on how I work with colors on the color selection panel:
http://www.actionscript-flash-guru.com/blog/36-uint-to-6-digit-rgb-hex-actionscript-30-as3

I am glad you like it. smilies/smiley.gif
 
report abuse
vote down
vote up
June 11, 2010 | url
Votes: +0

melodave said:

Thanks for this solution! I was thinking on it in the last few hours and it has just perfectly erased all my rubbish sketches. They were confusing anyway smilies/smiley.gif

Thanks for sharing!
Pisz smilies/smiley.gif
 
report abuse
vote down
vote up
June 16, 2010
Votes: +0

Nicholas said:

thanks melodave for the link to how you used this simple algorithm. others can check it out here http://extrarelish.co.uk/presentations/zooom/
 
report abuse
vote down
vote up
June 24, 2010 | url
Votes: +0

Greg Steimann said:

Thanks for the articles Nicholas! They helped some, but I'm still having a terrible time drawing out shapes. How are you accomplishing this? I've attempted methods like adding a circle on click so the users can see a point added, then immediately adding the point data it to a vector-array (like "shape:Vector.") then running a drawGraphicsData(shape) for each time a new point is added, but its really not working out the way I want.

The project I'm working on is a project for work, where people draw on a kite and the only thing I can't get to work is basically what you've figured out here, being able to draw/complete and move custom shapes.
 
report abuse
vote down
vote up
July 01, 2010
Votes: +0

Nicholas said:

Someone contacted me after checking out this post and provided me with a cool link that better describes how solution 1 works above. Here is the link for anyone trying to do skews http://www.touchspin.com/Actio...mation.php thanks greg! smilies/smiley.gif
 
report abuse
vote down
vote up
July 02, 2010 | url
Votes: +0

Chris said:

Great function thanks! Is it possible to use this for changing the registration point for rotation? I am creating a bitmap (for GPU caching) and I would like to use rotationY to flip it (I am creating an iPhone app and want to emulate the 3D flip) but when I use caurina to tween it, it rotates around 0,0, and not the desired centre point.
 
report abuse
vote down
vote up
September 15, 2010
Votes: +0

Nicholas said:

This will work to emulate the effect of the 3D rotation of a plane around the y access. You will have to displace the image after you do the transformation to move the new center point back to where it use to be. This of course is a hack way to do the 3D rotation and does not do the skew required. It is advised you use a real 3D polygon transform for this.
 
report abuse
vote down
vote up
September 18, 2010 | url
Votes: +0

MKey said:

thanks for the tutorial Nicholas...took me quite some time to find that particular transformation explained. However, as a brand new as3 user (previous html and java user) I find it very confusing to implement this transformation in a personal project that i m woking on. So, scaling from a static point is working ok but do you have an idea of how to connect the transfrmation with Dateclass....For some reason I find it very difficult to make a simple line 'grow' from a static point each second with 1px...
Any help with that would be much appresiated....spent almost 2 weeks trying to figure that out!
 
report abuse
vote down
vote up
November 13, 2010
Votes: +0

nicholas said:

not really sure what you mean by dateclass. I don't really understand what your problem is but this was a big issue for me http://www.actionscript-flash-...ipt-30-as3
 
report abuse
vote down
vote up
November 16, 2010 | url
Votes: +0

3dolab said:

Thanks for your extremely useful tips!
However, I couldn't manage things to work properly, probably because I'm getting in touch with AS3 again after some years spent in handling and JavaScript above all smilies/tongue.gif

Basically, I'm trying to dinamically control the Scale by mouse movements instead of assigning fixed values: at first sight, everything seems to behave as expected but some further testing shows that the static point is actually moving.

Let me explain with a concrete example: my goal is to obtain a shape (rectangle) that scales from its baseline and while it does keep the alignment to some extent, sometimes it moves by an half pixel or so. For instance, when scaling and then reverting back to the original size, the result is that the objToScale.y has moved. Greater scale changes actually involve greater (wrong) repositioning.

The difference is more appreciable after reaching the scale limits that I've set, but I bet the fault is not caused by the bounds.

Since I can't provide the code here in the comments, if possible take a look at the files
http://www.3dolab.net/mousescale.zip

Any suggestion?
 
report abuse
vote down
vote up
January 18, 2011 | url
Votes: +0

Nicholas said:

Interesting. Could this be the issue?

http://www.actionscript-flash-guru.com/blog/35-precision-problems-width-height-getbounds-actionscript-30-as3

is it because you are iteratively doing the scale which means that the above error would accumulate much more rapidly?
 
report abuse
vote down
vote up
February 07, 2011 | url
Votes: +0

3dolab said:

In fact I'd supposed something like that.
However if you take a look at the ActionScript code in the last layer or in
http://www.3dolab.net/mousescale_as.txt
you'll notice that I'm not using getRect or getBounds, only scale.

Now I'm going to try applying width and height multiples instead of scale iteration,
maybe I could use Math.round not only in value debug display but also into the functions.
I'll keep you informed, but meanwhile any further hint is welcome!
 
report abuse
vote down
vote up
February 07, 2011 | url
Votes: +0

George said:

The one thing I didn't understand is why this:

objToScale.scaleX = objToScale.scaleX*(scaleX);
objToScale.scaleY = objToScale.scaleY*(scaleY);

How do you go back to an smaller scale? If my current scale is 30, how do I go back to 20, or 1?

Thank you for the nice code!
 
report abuse
vote down
vote up
July 15, 2011
Votes: +0

Alessandro said:

Hi!
i want to use this technique with caurina tweener and tween classes....but i'm a beginner and i found some difficulties. i want to reproduce a linear scaling animation from right to left(right side of my moveclip stay fixed and the left side contract or expand). how can i change the code?
thank a lot!!!!
 
report abuse
vote down
vote up
October 10, 2011
Votes: +0

sals said:

function rotateAroundPoint(targetsmilies/cheesy.gifisplayObject, pointsmilies/tongue.gifoint, degrees:Number):void
{
var m:Matrix = target.transform.matrix;
m.translate( -point.x, -point.y );
m.rotate(degrees*Math.PI/180);
m.translate( point.x, point.y );
target.transform.matrix = m;
}

function scaleAroundPoint(targetsmilies/cheesy.gifisplayObject, pointsmilies/tongue.gifoint, degrees:Number):void
{
var m:Matrix = target.transform.matrix;
m.translate( -point.x, -point.y );
m.scale( 2, 2);
m.translate( point.x, point.y );
target.transform.matrix = m;
}
 
report abuse
vote down
vote up
December 07, 2011
Votes: +0

sals said:

sorr..

function rotateAroundPoint(targetsmilies/cheesy.gifisplayObject, pointsmilies/tongue.gifoint, degrees:Number):void
{
var m:Matrix = target.transform.matrix;
m.translate( -point.x, -point.y );
m.rotate(degrees*Math.PI/180);
m.translate( point.x, point.y );
target.transform.matrix = m;
}

function scaleAroundPoint(targetsmilies/cheesy.gifisplayObject, pointsmilies/tongue.gifoint, scaleFactor:Number):void
{
var m:Matrix = target.transform.matrix;
m.translate( -point.x, -point.y );
m.scale( scaleFactor:Number, scaleFactor:Number);
m.translate( point.x, point.y );
target.transform.matrix = m;
//works if only scaleX and scaleY are equal
}
 
report abuse
vote down
vote up
December 07, 2011
Votes: +0

Write comment
quote
bold
italicize
underline
strike
url
image
quote
quote
smile
wink
laugh
grin
angry
sad
shocked
cool
tongue
kiss
cry
smaller | bigger

security image
Write the displayed characters


busy