rotate swf along center - actionscript-3

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;

Related

Centering on a canvas object within an HTML5 canvas

I have an Html5 canvas which i am drawing squares to.
The canvas itself is roughly the size of the window.
When i detect a click on a square i would like to translate the canvas so that the square is roughly in the center of the window. Any insights, hints, or straight-forward replies are welcome.
Here is what i tried so far:
If a square is at point (1000, 1000) I would simply translate the canvas (-1000, -1000). I know i need to add an offset so that it is centered in the window. However, the canvas always ends up off of the visible window (too far in the upper-left corner somewhere).
A more complex scenario:
Ultimately i would like to be able to center on a clicked object on a canvas that is transformed (rotated & skewed). I'm going for an isometric effect which seems to work really well. I'm wondering if this transformation affects the centering logic/math at all?
Transforming from screen to world and back
When working with non standard axis (or projections) such as isometrix it is always best to use a transformation matrix. It will cover every possible 2D projection with the same simple functions.
The coordinates of the iso world are called world coordinates. All you objects are stored as world coordinates. When you render them you project those coordinates to the screen coordinates using a transformation matrix.
The matrix, not a movie.
The matrix represents the direction and size in screen coordinates of the world
x and y axis and the screen location of the world origin (0,0)
For iso that is
x axis across 1 down 0.5
y axis across -1 down 0.5
z axis up 1 (-1 as up is the reverse of down) but this example does not use z
So the matrix as an array
const isoMat = [1,0.5,-1,0.5,0,0]; // ISO (pixel art) dimorphic projection
The first two are the x axis, the next two the y axis and the last two values are the screen coordinates of the origin.
Use the matrix to transform points
You apply a matrix to a point, this transforms the point from one coordinate system to another. You can also convert back via a inverse transform.
World to screen
You will need to convert from world coordinates to screen coordinates.
function worldToScreen(pos,retPos){
retPos.x = pos.x * isoMat[0] + pos.y * isoMat[2] + isoMat[4];
retPos.y = pos.x * isoMat[1] + pos.y * isoMat[3] + isoMat[5];
}
In the demo I ignore the origin as I set that at the center of the canvas at all times. Thus remove the origin from that function
function worldToScreen(pos,retPos){
retPos.x = pos.x * isoMat[0] + pos.y * isoMat[2];
retPos.y = pos.x * isoMat[1] + pos.y * isoMat[3];
}
Screen to world.
You will also need to convert from the screen coordinates to the world. For this you need to use the inverse transform. It's a bit like the inverse of multiply a * 2 = b is the inverse of b / 2 = a
There is a standard method for calculating the inverse matrix as follows
const invMatrix = []; // inverse matrix
// I call the next line cross, most call it the determinant which I
// think is stupid as it is effectively a cross product and is used
// like you would use a cross product. Anyways I digress
const cross = isoMat[0] * isoMat[3] - isoMat[1] * isoMat[2];
invMatrix[0] = isoMat[3] / cross;
invMatrix[1] = -isoMat[1] / cross;
invMatrix[2] = -isoMat[2] / cross;
invMatrix[3] = isoMat[0] / cross;
Then we have a function that converts from the screen x,y to the world position
function screenToWorld(pos,retPos){
const x = pos.x - isoMat[4];
const y = pos.y - isoMat[5];
retPos.x = x * invMatrix[0] + y * invMatrix[2];
retPos.y = x * invMatrix[1] + y * invMatrix[3];
}
So you get the mouse coords as screen pixels, use the above function to convert to world coords. Then you can use the world coords to find the object you are looking for.
To move a world object to the screen center you convert its coords to screen coords, add the position on the screen (the canvas center) and set the transform matrix origin to that location.
The demo
The demo creates a set of boxes in world coordinates. It sets the 2D context transform to the isoMat (isometric projection) via ctx.setTransform(
Every frame I convert the mouse screen coords to world coords then use that to check which box the mouse is over.
If the mouse button is down I then convert that box from world coords to screen and add the screen center. To smooth the step the new screen center is chased (smoothed)..
Well you should be able to work it out in the code, any problems ask in the comments.
const ctx = canvas.getContext("2d");
const moveSpeed = 0.4;
const boxMin = 20;
const boxMax = 50;
const boxCount = 100;
const boxArea = 2000;
// some canvas vals
var w = canvas.width;
var h = canvas.height;
var cw = w / 2; // center
var ch = h / 2;
var globalTime;
const U = undefined;
// Helper function
const doFor = (count, cb) => { var i = 0; while (i < count && cb(i++) !== true); };
const eachOf = (array, cb) => { var i = 0; const len = array.length; while (i < len && cb(array[i], i++, len) !== true ); };
const setOf = (count, cb) => {var a = [],i = 0; while (i < count) { a.push(cb(i ++)) } return a };
const randI = (min, max = min + (min = 0)) => (Math.random() * (max - min) + min) | 0;
const rand = (min, max = min + (min = 0)) => Math.random() * (max - min) + min;
// mouse function and object
const mouse = {x : 0, y : 0, button : false, world : {x : 0, y : 0}}
function mouseEvents(e){
mouse.x = e.pageX;
mouse.y = e.pageY;
mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
}
["down","up","move"].forEach(name => document.addEventListener("mouse"+name,mouseEvents));
// boxes in world coordinates.
const boxes = [];
function draw(){
if(this.dead){
ctx.fillStyle = "rgba(0,0,0,0.5)";
ctx.fillRect(this.x,this.y,this.w,this.h);
}
ctx.strokeStyle = this.col;
ctx.globalAlpha = 1;
ctx.strokeRect(this.x,this.y,this.w,this.h);
// the rest is just overkill
if(this.col === "red"){
this.mr = 10;
}else{
this.mr = 1;
}
this.mc += (this.mr-this.m) * 0.45;
this.mc *= 0.05;
this.m += this.mc;
for(var i = 0; i < this.m; i ++){
const m = this.m * (i + 1);
ctx.globalAlpha = 1-(m / 100);
ctx.strokeRect(this.x-m,this.y-m,this.w,this.h);
}
}
// make random boxes.
function createBoxes(){
boxes.length = 0;
boxes.push(...setOf(boxCount,()=>{
return {
x : randI(cw- boxArea/ 2, cw + boxArea/2),
y : randI(ch- boxArea/ 2, ch + boxArea/2),
w : randI(boxMin,boxMax),
h : randI(boxMin,boxMax),
m : 5,
mc : 0,
mr : 5,
col : "black",
dead : false,
draw : draw,
isOver : isOver,
}
}));
}
// use mouse world coordinates to find box under mouse
function isOver(x,y){
return x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h;
}
var overBox;
function findBox(x,y){
if(overBox){
overBox.col = "black";
}
overBox = undefined;
eachOf(boxes,box=>{
if(box.isOver(x,y)){
overBox = box;
box.col = "red";
return true;
}
})
}
function drawBoxes(){
boxes.forEach(box=>box.draw());
}
// next 3 values control the movement of the origin
// rather than move instantly the currentPos chases the new pos.
const currentPos = {x :0, y : 0};
const newPos = {x :0, y : 0};
const chasePos = {x :0, y : 0};
// this function does the chasing
function updatePos(){
chasePos.x += (newPos.x - currentPos.x) * moveSpeed;
chasePos.y += (newPos.y - currentPos.y) * moveSpeed;
chasePos.x *= moveSpeed;
chasePos.y *= moveSpeed;
currentPos.x += chasePos.x;
currentPos.y += chasePos.y;
}
// ISO matrix and inverse matrix plus 2world and 2 screen
const isoMat = [1,0.5,-1,0.5,0,0];
const invMatrix = [];
const cross = isoMat[0] * isoMat[3] - isoMat[1] * isoMat[2];
invMatrix[0] = isoMat[3] / cross;
invMatrix[1] = -isoMat[1] / cross;
invMatrix[2] = -isoMat[2] / cross;
invMatrix[3] = isoMat[0] / cross;
function screenToWorld(pos,retPos){
const x = pos.x - isoMat[4];
const y = pos.y - isoMat[5];
retPos.x = x * invMatrix[0] + y * invMatrix[2];
retPos.y = x * invMatrix[1] + y * invMatrix[3];
}
function worldToScreen(pos,retPos){
retPos.x = pos.x * isoMat[0] + pos.y * isoMat[2];// + isoMat[4];
retPos.y = pos.x * isoMat[1] + pos.y * isoMat[3];// + isoMat[5];
}
// main update function
function update(timer){
// standard frame setup
globalTime = timer;
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.globalAlpha = 1; // reset alpha
if(w !== innerWidth || h !== innerHeight){
cw = (w = canvas.width = innerWidth) / 2;
ch = (h = canvas.height = innerHeight) / 2;
createBoxes();
}else{
ctx.clearRect(0,0,w,h);
}
ctx.fillStyle = "black";
ctx.font = "28px arial";
ctx.textAlign = "center";
ctx.fillText("Click on a box to center it.",cw,28);
// update position
updatePos();
isoMat[4] = currentPos.x;
isoMat[5] = currentPos.y;
// set the screen transform to the iso matrix
// all drawing can now be done in world coordinates.
ctx.setTransform(isoMat[0], isoMat[1], isoMat[2], isoMat[3], isoMat[4], isoMat[5]);
// convert the mouse to world coordinates
screenToWorld(mouse,mouse.world);
// find box under mouse
findBox(mouse.world.x, mouse.world.y);
// if mouse down and over a box
if(mouse.button && overBox){
mouse.button = false;
overBox.dead = true; // make it gray
// get the screen coordinates of the box
worldToScreen({
x:-(overBox.x + overBox.w/2),
y:-(overBox.y + overBox.h/2),
},newPos
);
// move it to the screen center
newPos.x += cw;
newPos.y += ch;
}
// forget what the following function does, think it does something like draw boxes, but I am guessing.. :P
drawBoxes();
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas { position : absolute; top : 0px; left : 0px; }
<canvas id="canvas"></canvas>

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 image around center point of it's container

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).

how to move projectile in an arc to mouse x,y with AS3?

I've read a number of similar questions to this on here, but unfortunately none of them seem to give the exact answer I'm after, or they might but the maths is beyond me!
I'm creating a game where you have a cannon at the left edge of the screen. I want to be able to fire a cannonball from the cannon in an arc so that it intersects where the mouse pointer is on the screen.
I've seen a few examples that move a projectile in an arc from point a to point b, but what I need is for the cannonball to first move along the axis of the cannon itself, it's no good if the ball leaves the end of the cannon at a different angle to which the cannon is pointing.
The only force acting on the ball will be gravity and it's starting velocity.
Also to complicate matters, I need the cannons angle to change according to how far away the mouse pointer is from the end of the cannon, so if the pointer is far away than the cannon will point upwards say at an angle of 45 degrees, but if the pointer is very close to the end of the cannon then the cannon will point directly at the pointer, this I've more or less already got working by just getting the distance between them and then dividing it by a number and subtracting it from the rotation value of the cannon, but it's a bit of a rough way of doing it.
EDIT
Using the code below I've managed to the line in the screen shot below. But as you can see it's not the trajectory I need, I need something more like the red line I've put in.
And here's how I've implemented the code (probably wrongly)
public class GameTurretLine2
{
var rt:Object = null;
var lineMc:MovieClip = new MovieClip();
var myTurret:GameMainGun = null;
var pta:Point = new Point(0,0);
var ptb:Point = new Point(0,0);
var ptc:Point = new Point(0,0);
var ptd:Point = new Point(0,0);
public function GameTurretLine2(rt2,turret)
{
rt = rt2;
myTurret = turret;
lineMc.graphics.lineStyle(2, 0x55aa00);
mainLoop();
rt.rt.GameLayers.turretLineMc.addChild(lineMc);
}
function mainLoop()
{
lineMc.graphics.clear();
//get points
var turretEnd:Object = myTurret.rt.Useful.localToGlobalXY(myTurret.mC.turret.firePoint);
var turretStart:Object = myTurret.rt.Useful.localToGlobalXY(myTurret.mC.turret);
var mousePos:Point = new Point(myTurret.rt.rt.mouseX,myTurret.rt.rt.mouseY);
var inbetween:Point = new Point(0,0);
//start
pta.x = turretStart.newX;
pta.y = turretStart.newY;
//mouse end
ptd.x = mousePos.x;
ptd.y = mousePos.y;
// The cannon's angle:
// make the cannon's angle some inverse factor
// of the distance between the mouse and cannon tip
var dist:Number = myTurret.rt.Useful.getDistance(turretEnd.newX, turretEnd.newY, mousePos.x, mousePos.y);
var cAng:Number = dist * (180/Math.PI);
var ptbc:Point = new Point((ptd.x - pta.x) *.5,0);
ptbc.y = Math.tan(cAng) * ptbc.x;
//ptb = new Point(ptbc.x - ptbc.x * .15, ptbc.y);
ptb = new Point(turretEnd.newX, turretEnd.newY);
ptc = new Point(ptbc.x + ptbc.x * .5, ptbc.y);
// create the Bezier:
var bz:BezierSegment = new BezierSegment(pta,ptb,ptc,ptd);
trace(bz);
// define the distance between points that you want to draw
// has to be between 0 and 1.
var stepVal:Number = .1;
var curPt:Point = pta;
//draw circles
lineMc.graphics.drawCircle(pta.x, pta.y, 4);
lineMc.graphics.drawCircle(ptb.x, ptb.y, 4);
lineMc.graphics.drawCircle(ptc.x, ptc.y, 4);
lineMc.graphics.drawCircle(ptd.x, ptd.y, 4);
lineMc.graphics.lineStyle(2, 0x0000ff);
//step along the curve to draw it
for(var t:Number = 0;t < 1;t+=stepVal){
lineMc.graphics.moveTo(curPt.x, curPt.y);
curPt = bz.getValue(t);
trace("curPt = " + curPt.x + "," + curPt.y);
lineMc.graphics.lineTo(curPt.x, curPt.y);
}
trace("pta = " + pta.x + "," + pta.y);
trace("ptb = " + ptb.x + "," + ptb.y);
trace("ptc = " + ptc.x + "," + ptc.y);
trace("ptd = " + ptd.x + "," + ptd.y);
}
}
Also for some strange reason, the line created by the code flips, from how it is in the screen shot to an indented code (y flipped) just by moving the mouse a tiny amount, so as you move the mouse the line jumps everywhere.
One method is to create a Bezier curve.
This sounds like a workable solution because you essentially want the curve to always fit under some triangle. If this triangle defines the control points for a Bezier curve, you can make that match pretty closely the arc of a cannonball under gravity (it's not a perfect representation of gravity). One side-effect of this method is that the (inversed) height can define the force of the cannonball.
You can use the fl.motion.BezierSegment to create a curve and step along it. Paste this code into an FLA:
import fl.motion.BezierSegment;
var mySprite:Sprite = new Sprite();
addChild(mySprite);
mySprite.graphics.lineStyle(2, 0x55aa00);
// End point of the cannon:
var pta:Point = new Point(0, 100);
mySprite.graphics.drawCircle(pta.x, pta.y, 4);
trace("pta = " + pta.x + "," + pta.y);
// mouse point
// var ptd:Point = new Point(mouseX, mouseY);
// for testing:
var ptd:Point = new Point(200,100);
mySprite.graphics.drawCircle(ptd.x, ptd.y, 4);
trace("ptd = " + ptd.x + "," + ptd.y);
// The cannon's angle:
// make the cannon's angle some inverse factor
// of the distance between the mouse and cannon tip
// var dx:Number = ptd.x-pta.x;
// var dy:Number = ptd.y-pta.y;
// var dist:Number = Math.sqrt(dx * dx + dy * dy);
var cAng:Number = 30 * /(180/Math.PI);
// point the cannon in the correct direction here, however you are intending to do that.
// triangulate the cannon pt and mouse pt assuming the cannon's angle for both:
// *** NOTE: for simplicity, this assumes a straight line on the x-plane. ***
var ptbc:Point = new Point((ptd.x - pta.x) *.5,0);
ptbc.y = Math.tan(cAng) * ptbc.x;
trace("ptbc = " + ptbc.x + "," + ptbc.y);
// to adjust the curve:
var ptb:Point = new Point(ptbc.x - ptbc.x * .15, ptbc.y);
var ptc:Point = new Point(ptbc.x + ptbc.x * .5, ptbc.y);
mySprite.graphics.drawCircle(ptb.x, ptb.y, 4);
mySprite.graphics.drawCircle(ptc.x, ptc.y, 4);
// create the Bezier:
var bz:BezierSegment = new BezierSegment(pta,ptb,ptc,ptd);
trace(bz);
// define the distance between points that you want to draw
// has to be between 0 and 1.
var stepVal:Number = .1;
var curPt:Point = pta;
mySprite.graphics.lineStyle(2, 0x0000ff);
//step along the curve to draw it
for(var t:Number = 0;t < 1;t+=stepVal){
mySprite.graphics.moveTo(curPt.x, curPt.y);
curPt = bz.getValue(t);
trace("curPt = " + curPt.x + "," + curPt.y);
mySprite.graphics.lineTo(curPt.x, curPt.y);
}
mySprite.x = stage.stageWidth/2-mySprite.width/2;
mySprite.y = stage.stageHeight/2-mySprite.height/2;
As is, this code is not attached directly to the mouse, so you will have to use your own MouseEvent and AdjustCannonEvent to run this code. (Also, make sure to see the note in the code.)

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.