Rotate image around center point of it's container - actionscript-3

I have Image component inside some container with clipAndEnableScrolling property set to true. I need a static method which gets this Image, rotation angle and rotates Image around center point of container without loosing any previous transformations. The best method I've created adds error after few rotations.
I thing it must work like this
public static function rotateImageAroundCenterOfViewPort(image:Image, value:int):void
{
// Calculate rotation and shifts
var bounds:Rectangle = image.getBounds(image.parent);
var angle:Number = value - image.rotation;
var radians:Number = angle * (Math.PI / 180.0);
var shiftByX:Number = image.parent.width / 2 - bounds.x;
var shiftByY:Number = image.parent.height / 2 - bounds.y;
// Perform rotation
var matrix:Matrix = new Matrix();
matrix.translate(-shiftByX, -shiftByY);
matrix.rotate(radians);
matrix.translate(+shiftByX, +shiftByY);
matrix.concat(image.transform.matrix);
image.transform.matrix = matrix;
}
but it doesn't. Looks like I can't understand how transformation works(

If you are trying to rotate the object around it's center, I think you'll want some more like this:
var matrix:Matrix = image.transform.matrix;
var rect:Rectangle = image.getBounds( insertParentObject );
//translate matrix to center
matrix.translate(- ( rect.left + ( rect.width/2 ) ), - ( rect.top + ( rect.height/2 ) ) );
matrix.rotate(radians);
//translate back
matrix.translate(rect.left + ( rect.width/2 ), rect.top + ( rect.height/2 ) );
image.transform.matrix = matrix;
Also here is a link to the same SO question with varying answers including the one I provided:
Flex/ActionScript - rotate Sprite around its center
As discussed in the comments if you are looking to rotate an object around a point (that is the center of your container), here's a function that I think would work:
//pass rotateAmount as the angle you want to rotate in degrees
private function rotateAround( rotateAmount:Number, obj:DisplayObject, origin:Point, distance:Number = 100 ):void {
var radians:Number = rotateAmount * Math.PI / 180;
obj.x = origin.x + distance * Math.cos( radians );
obj.y = origin.y + distance * Math.sin( radians );
}
Then you just call it:
rotateAround( rotateAmount, image, new Point( container.width/2, container.height/2 ) );
The last parameter distance you can pass whatever you like, so for example if I wanted a distance of the image vector length:
var dx:Number = spr.x - stage.stageWidth/2;
var dy:Number = spr.y - stage.stageHeight/2;
var dist:Number = Math.sqrt(dx * dx + dy * dy);
rotateAround( rotateAmount, image, new Point( container.width/2, container.height/2 ), dist );

Here's the solution I've found:
public static function rotateImageAroundCenterOfViewPort(image:Image, value:int):void
{
// Calculate rotation and shifts
var center:Point = new Point(image.parent.width / 2, image.parent.height / 2);
center = image.parent.localToGlobal(center);
center = image.globalToLocal(center);
var angle:Number = value - image.rotation;
var radians:Number = angle * (Math.PI / 180.0);
var shiftByX:Number = center.x;
var shiftByY:Number = center.y;
// Perform rotation
var matrix:Matrix = new Matrix();
matrix.translate(-shiftByX, -shiftByY);
matrix.rotate(radians);
matrix.translate(+shiftByX, +shiftByY);
matrix.concat(image.transform.matrix);
image.transform.matrix = matrix;
image.rotation = Math.round(image.rotation);
}
Teste only with the angles like 90, 180 etc. (I don't need any else).

Related

How to find correct offset to adjust sprite to the position of box2D body after rotation

I am trying to implement phsyics with the as3 box2d port. I currently have a b2body for each of some certain sprites in my game and I am able to update the sprite's positions correctly from the positions of the bodies. This is shown in the picture below (debugDraw shows the positions of the b2bodies overlaid on their corresponding spirtes. The green rectangles are the walls and floor)
However, I also want to have the sprite's rotations reflect the rotations of the b2bodies. But, after I rotate the sprites, the offset I use to center them correctly with the b2body positions is no longer accurate.
My code for updating the sprites positions is as follows:
private function Update(update_event:TimerEvent):void
{
//step physics simulation forward
world.Step(0.025,10,10);
//update all objects in world
for each (var obj:HouseItemPhysicsObject in physicsObjects)
{
//update object's position from gravity if it is not being dragged
if(!obj.isHeld)
{
/*adjust rotation of sprite along with body -> yourMC.rotation = (yourMCbody.GetAngle() * 180 / Math.PI) % 360; */
obj.object.rotation = (obj.pBody.GetAngle() * 180/Math.PI) % 360;
if(obj.object.rotation >=5)
// set object's x position but adjust for offset between the cooridinate systems
obj.x = (obj.pBody.GetPosition().x* scaleFactor)-(obj.object.width/2);
//keep in horizontal bounds of screen
if(obj.x > GeneralConstants.GAME_WIDTH)
{
obj.x =GeneralConstants.GAME_WIDTH;
}
else if(obj.x < 0)
{
obj.x = 0;
}
// set object's x position but adjust for offset between the cooridinate systems in Flash and box2d
obj.y = (obj.pBody.GetPosition().y * scaleFactor)-(obj.object.height/2);
//keep in vertical bounds of the screen
if(obj.y > GeneralConstants.GAME_HEIGHT)
{
obj.y =GeneralConstants.GAME_HEIGHT;
}
else if(obj.x < 0)
{
obj.x = 0;
}
/*Draw shapes to see for debug*/
//obj.DrawDebug();
//trace("OBJECT's X is :" + obj.x + " Y is :" +obj.y);
trace("Object's rotation is:" + obj.object.rotation);
}
}
//move debug draw to front of display list
m_sprite.parent.setChildIndex(m_sprite, m_sprite.parent.numChildren - 5);
world.DrawDebugData();
}
How can I find the correct X and Y offset between the coordinate systems (Flash and Box2d) after rotating the sprite according to the b2Body? Thanks for the help.
EDIT:
For clarity, the object is a class that extends the Sprite class, and it's data member _object is a an instance of MovieClip.
Box2D objects have their anchor point in the center by default, while for Flash objects, it's in the top left. To position them properly, you need to take this into account
Easy way
Wrap your Bitmaps/whatever in a Sprite and center them:
// create the image, center it, and add it to a holder Sprite
var image:Bitmap = new Bitmap( objGraphicsBitmapData );
image.x = -image.width * 0.5;
image.y = -image.height * 0.5;
var holder:Sprite = new Sprite;
holder.addChild( image );
Now just set the position and rotation of holder as you do currently, and it should be fine
Hard way
You need to manually adjust the position offset based on the object's rotation. A simple rotation function:
public function rotate( p:Point, radians:Number, out:Point = null ):Point
{
// formula is:
// x1 = x * cos( r ) - y * sin( r )
// y1 = x * sin( r ) + y * cos( r )
var sin:Number = Math.sin( radians );
var cos:Number = Math.cos( radians );
var ox:Number = p.x * cos - p.y * sin;
var oy:Number = p.x * sin + p.y * cos;
// we use ox and oy in case out is one of our points
if ( out == null )
out = new Point;
out.x = ox;
out.y = oy;
return out;
}
First we need to store the object's offset - this is normally new Point( -obj.width * 0.5, -obj.height * 0.5 ). You need to stock this while it's rotation is 0, and rotating the object will change its width and height properties, so the following won't work properly.
obj.offset = new Point( -obj.width * 0.5, -obj.height * 0.5 );
When you're updating the position, simply rotate the offset by the rotation and add it:
// get our object's position and rotation
// NOTE: you'll probably need to adjust the position based on your pixels per meter value
var pos:Point = new Point( obj.pBody.GetPosition().x, obj.pBody.GetPosition().y ); // pos in screen coords
var rotR:Number = obj.pBody.GetAngle(); // rotation in radians
var rotD:Number = radiansToDegrees( rotR ); // rotation in degrees
// rotate our offset by our rotation
var offset:Point = rotate( obj.offset, rotR );
// set our position and rotation
obj.x = pos.x + offset.x;
obj.y = pos.y + offset.y;
obj.rotation = rotD;
Other useful functions:
public function degreesToRadians( deg:Number ):Number
{
return deg * ( Math.PI / 180.0 );
}
public function radiansToDegrees( rad:Number ):Number
{
return rad * ( 180.0 / Math.PI );
}
If you do it to give your sprites properties of physical objects, it can be easier to use physInjector for box2D:
http://www.emanueleferonato.com/2013/03/27/add-box2d-physics-to-your-projects-in-a-snap-with-physinjector/
It is free can do it in a couple of lines.

rotate swf along center

i have one swf which looks like below image
OBJECTIVE:-want to rotate the red colored line swf along center as per given image
i have conditions like
x-axis,y axis,x=1,x=y,x=-y
my first two conditions are shown in the images.
for the next three conditions the images are
You can do this either with a matrix translation and rotation, or by finding the objects center, rotating the object, and then translating it back (which is basically the same thing as the matrix technique).
The matrix way:
var mat:Matrix = spr.transform.matrix;
var bounds:Rectangle = spr.getBounds( spr.parent );
mat.translate( -(bounds.left + bounds.width/2), -(bounds.top + bounds.height/2 ) );
mat.rotate( degree * Math.PI / 180 ); //rotate amount
mat.translate( bounds.left + bounds.width/2, bounds.top + bounds.height/2 );
spr.transform.matrix = mat;
Without matrix:
var bounds:Rectangle = spr.getBounds( spr.parent );
var center:Point = new Point( bounds.x + bounds.width/2, bounds.y + bounds.height/2 );
spr.rotation = degree; //rotate amount
bounds = spr.getBounds( spr.parent );
var newCenter:Point = new Point( bounds.x + bounds.width/2, bounds.y + bounds.height/2 );
spr.x += center.x - newCenter.x;
spr.y += center.y - newCenter.y;

AS3 shooting in multiple directions

i am try trying to make space shooter game in which ship is able to rotate and shoot in all directions, now, i do know basics of trigonometry, but i stuck at this point and my brain seems to be frozen so i seek your help.
I have Ship.as and Turret.as , every ship contains some number of turrets, and this is loop that is responsible to create bullet for each turret on players input and its located in Ship.as.
for (var i:int = 0; i < turrets.length; i++)
{
var _pcos:Number = Math.cos(angle / 180 * Math.PI);
var _psin:Number = Math.sin(angle / 180 * Math.PI);
var bulletX:Number = center.x + turrets[i].distance * _pcos;
var bulletY:Number = center.y + turrets[i].distance * _psin;
var bullet:BulletBase = new bulletClass(bulletX, bulletY, angle);
layerBullets.add(bullet);
bullets.push(bullet);
}
variable center is point positioned in exact center of ships sprite, angle is ships rotation towards mouse, turret.distance is distance from center to turret
This is whats happening in Turret.as
public class Turret extends Point
{
private var ship:Ship;
public var distance:Number;
public var angle:Number;
/**
*
* #param x position with angle 0
* #param y position with angle 0
* #param distance distance from center of ship to turret
*/
public function Turret(x:Number = 0, y:Number = 0, ship:Ship = null)
{
super(x, y);
this.ship = ship;
this.x = x;
this.y = y;
var dx:Number = ship.center.x - x;
var dy:Number = ship.center.y - y;
angle = Math.atan2(dy, dx);
distance = Math.sqrt(dx * dx + dy * dy);
}
Now, what is happening with this code is that bullets seems like they are fired from same direction, only one bullet is behind.
I am not math expert and if anyone knows the answer i would really appreciate it.
var _pcos:Number = Math.cos((turret.angle + angle) / 180 * Math.PI);
var _psin:Number = Math.sin((turret.angle + angle) / 180 * Math.PI);
angle is angle of ship

Drawing a rectangle with one side skewing based on degress/radians

I'm trying to take one side of a rectangle and skew the side based on degree/angle.
I whipped up some code for you
Any questions just ask.
import flash.geom.Matrix;
var temp_matrix = new Matrix();
var square:Sprite = new Sprite();
addChild(square);
square.graphics.lineStyle(3,0x000000);
square.graphics.drawRect(0,0,200,100);
square.graphics.endFill();
var angle:Number = -10; // the angle of degrees
temp_matrix.b = Math.PI * 2 * angle / 360;// y skew
//temp_matrix.c = Math.PI * 2 * angle / 360;// x skew
var sourceMatrix:Matrix = square.transform.matrix;// get existing matrix
sourceMatrix.concat(temp_matrix); // apply skew to existing matrix
square.transform.matrix = temp_matrix;// assign the new skew
square.x = 100
square.y = 100
[SECOND ROUND]
var trapezium:Sprite = new Sprite();
addChild(trapezium);
trapezium.x = 100;
trapezium.y = 100;
var dir:Boolean = true;
var side:Boolean = true;
var angle:Number = 0; // the angle of degrees
var w:Number = 300;
var h:Number = 80;
var timer:Timer = new Timer(16);
timer.addEventListener( TimerEvent.TIMER, tick );
timer.start();
function tick(e:TimerEvent):void{
var radians:Number = Math.PI/180*angle;
trapezium.graphics.clear();
trapezium.graphics.beginFill(0x000000)
if( side){
// long side is right side
trapezium.graphics.lineTo(w,0);
trapezium.graphics.lineTo(w,radians*w+h);
trapezium.graphics.lineTo(0,h);
trapezium.graphics.lineTo(0,0);
}else{
trapezium.graphics.lineTo(w,0);
trapezium.graphics.lineTo(w,h);
trapezium.graphics.lineTo(0,radians*w+h);
trapezium.graphics.lineTo(0,0);
}
trapezium.graphics.endFill();
if(angle>=10){
dir = false;
}
if(angle<=0){
dir = true;
}
if(dir){
angle = angle+.2;
}else{
angle = angle-.2;
}
if( Math.floor(angle*10) <= 0 ){
side = !side;
}
}
Take the tangent of the angle and multiply by the width of the rectangle to get the delta y for the bottom axis so you would have
[x1,y1] as the origin of the rectangle (which never changes)
[x1+length, y1+deltaY] as the right bottom corner
Don't know AS, but after editing this looks like filled polygon with vertices:
P0 =(X0, Y0)
P1 = (X1, Y0)
if Angle >= 0 then
P2 = (X1, Y1)
P3 = (X0, Y1 + (X1-X0) * Tan(Angle))
else
P2 = (X1, Y1 - (X1-X0) * Tan(Angle))
P3 = (X0, Y1)

How to calculate third point on line using atan2?

I'm trying to animate some bitmaps out in relation to a center point. They don't all start at that center point, but I want them to fly out as though a force from that center point slammed into them and pushed them outwards radially, such that they fly completely off the stage.
So: I know the center point, and the x and y position of each bitmap arranged around it. For each one I can draw a line from the center to that x,y point. I should then be able to get the angle formed by that line to the horizontal, and then set a destination point farther out on that line. The bitmap will be tweened out to that point. I believe that that is what Math.atan2 is for.
Here's what I've got as I iterate through the array of bitmaps (i is an object):
var angle:Number = Math.atan2(i.bitmap.y - centerY, i.bitmap.x - centerX) * 180 / Math.PI;
var dist:Number = 200; //arbitrary number, just to test
destX = centerX + dist * Math.cos(angle); //destination x
destY = centerY + dist * Math.sin(angle); //destination y
Instead of these things gliding out radially, they're jumping around.
I'm having trouble understanding atan2 and exactly what I'm doing wrong.
Thanks,
David
You can achieve the same effect without trigonometric functions using just vector operations:
var dist:Number = 200; //arbitrary number, just to test
var dx:Number = i.bitmap.x - centerX;
var dy:Number = i.bitmap.y - centerY;
var length:Number = Math.sqrt( dx*dx + dy*dy );
var normalizeddx:Number = dx / length;
var normalizeddy:Number = dy / length;
destX = centerX + dist * normalizeddx; //destination x
destY = centerY + dist * normalizeddy; //destination y
This should be much faster, than using trigonometric functions. I don't know the language specifics of actionscript, so probably this can be optimized more.
Try removing the *180/PI to keep the angle in radians.
var angle:Number = Math.atan2(i.bitmap.y-centerY, i.bitmap.x - centerX);
Then change destX and destY to
destX = i.bitmap.x + dist * Math.cos(angle);
destY = i.bitmap.y + dist * Math.sin(angle);
atan2 could work in this situation I suppose but I would just use atan:
var angle:Number = Math.atan((i.bitmap.y - centerY) / (i.bitmap.x - centerX));
ADDITION:
Code I just saw on another forum that appears to do what you want (there's only a slight difference from what you wrote in the first place)
var angle:Number = Math.atan2(mouseX,mouseY-180)-Math.PI/2;
var xNew:Number = 20*Math.cos(angle);
var yNew:Number = -20*Math.sin(angle);
You have to get rid of the *180/Math.PI part. The angle has to be in radians. So the first line would look like
var angle:Number = Math.atan2(i.bitmap.y - centerY, i.bitmap.x - centerX);
The rest should be fine.