RotateX a square around its center - actionscript-3

I have 3 squares (50 px x 50 px) in a Sprite, one next to each other. Pivot of each is at 0, 0.
1st square X, Y: 0, 0
2nd square X, Y: 50, 0
3rd square X, Y: 100, 0
I would like to rotateX each square around its center-line. I cannot seem to figure out how to set the vanishing point so that all the squares rotate around their individual point and not all of them around the same point.
Any help greatly appreciated!

You can make the rotation of child movieclips by rotating and translating them to newer position. Required the pivot of the child movieclip coincides with it's registration point
in the following code, the holder is your sprite and content are the squares. ( Required that squares are having the pivot, coinciding with the registration point)
PS: Holder must have a width and height bigger than the content's. It's assumed that holder here is some kind of big container ( say stage).
var holder_Mc:MovieClip = holder_Mc
var content_Mc:MovieClip = holder_Mc.content_Mc ;
var rotation_val_num:Number = 50// in degrees
var bnd = holder_Mc.getBounds(MovieClip(holder_Mc.parent)) ;
var cw:Number = bnd.width ;
var ch:Number = bnd.height;
var cx:Number = bnd.x ;
var cy:Number = bnd.y ;
content_Mc.rotation = rotation_val_num ;
var bnd2 = holder_Mc.getBounds(MovieClip(holder_Mc.parent)) ;
var cw2 = bnd2.width ;
var ch2 = bnd2.height;
var cx2 = bnd2.x ;
var cy2 = bnd2.y ;
var dx = Math.abs( holder_Mc.x - cx2 ) ;
var dy = Math.abs( holder_Mc.y - cy2) ;
holder_Mc.x = cx + cw/2 + dx - cw2/2
holder_Mc.y = cy + ch/2 + dy - ch2/2

Basically, you need to move the box while it is rotating to get that effect. Since you also know the width/height of the box and the pivot points positions the calculations aren't that hard.
However, why calculate it yourself? With TweenLite, You could use the TransformAroundCenterPlugin, which handles the transformation for you. I would recommend to use it. If you don't want to tween it, then set the tween-duration (second parameter) to 0.
// Activate plugin (should be called once)
TweenPlugin.activate([TransformAroundPointPlugin]);
// Transform your boxes around it's center, does not use the pivot point.
TweenLite.to(this.mcBox1, 1, new TweenLiteVars().transformAroundCenter({rotationX: 50}));
TweenLite.to(this.mcBox2, 1, new TweenLiteVars().transformAroundCenter({rotationX: 190}));
TweenLite.to(this.mcBox3, 0, new TweenLiteVars().transformAroundCenter({rotationX: 5}));

Related

Position one object based on another that's rotated

I have a set of blocks which could be rotated at any angle. My task is to create an extra block to one side of the set, adjacent to the last block. I'm using Flex/AS3 and openscales. An illustration will be good to explain what I want to achieve. I apologise in advance for the crudeness of my pictures, I only have MS Paint to hand!
My current formula for the picture 2 is simply:
block5XPos += block4XPos - block3XPos;
block5YPos += block4YPos - block3YPos;
** Edit **
I cannot work with Display Objects in the usual way i.e. rotate, addChild. I'm restricted to utilising x and y values and having to re-calculate these values.
If your solution allows, you could put all your blocks in a parent movieclip, rotate that movieclip. And then just modify the x parameter when you add new blocks to the parent movieclip. This would translate the block along the "local" x axis of the rotated parent.
Pseudocode:
var parent:MovieClip = new MovieClip()
parent.rotation = 45
addChild(parent)
var block1 = new BlockClip()
block1.x = 10
parent.addChild(block1)
var block2 = new BlockClip()
block2.x = 20
parent.addChild(block2)
I figured it out. I leveraged Math.cos and Math.sin to calculate the position of the new block based on the angle and distance from the last block.
private function calculateNewSlotPosition(slot:SlotDO):void {
var blockToRadians:Number = blockDegrees * Math.PI / 180;
var cos:Number = Math.cos(blockToRadians);
var sin:Number = Math.sin(blockToRadians);
for(var k:uint = 0; k < block.polygon.length; k++) {
block.polygon[k].x += ((block.width + workBlock.gap) * cos);
block.polygon[k].y -= ((block.width + workBlock.gap) * sin);
}
}

AS3 drawing lines, making shorter after that

i have a little math/coding problem witch i don`t have any idea how could i do it work in a simple way, so the problem is is need to make a line shorter, with 15
in my program i have :
http://gyazo.com/aff5ff61fb9ad3ecedde3118d9c0895e
the line takes the center coordinates of both circles and draws from one to another, but i need it to be from the circumference of the circles, so it wont get inside
the code im using is :
var line:Shape = new Shape();
line.graphics.lineStyle(3,0xFF0000,2);
line.graphics.moveTo(sx,sy);
line.graphics.lineTo(fx,fy);
this.addChild(line);
arrow2(sx,sy,fx,fy);
var rline:Shape = new Shape();
rline.graphics.lineStyle(3,0xFF0000,2);
rline.graphics.moveTo(fx,fy);
rline.graphics.lineTo(xa,ya);
this.addChild(rline);
var rline2:Shape = new Shape();
rline2.graphics.lineStyle(3,0xFF0000,2);
rline2.graphics.moveTo(fx,fy);
rline2.graphics.lineTo(xb,yb);
this.addChild(rline2);
the rline and rline2 function is for the arrow lines, now my question is how do i make it shorter not depending on it direction so it will not overlap the circle
You can use vectors to solve your problem; they're pretty easy to get the hang of, and pretty much indispensable for things like game dev or what you're trying to do. You can get an overview here: http://www.mathsisfun.com/algebra/vectors.html or by searching "vector math" in google
So first step is to get a vector from one circle to another (pretty much what you've done):
var vector:Point = new Point( circle2.x - circle1.x, circle2.y - circle1.y );
var length:Number = vector.length; // store the length of the vector for later
This is the equivalent of saying "if you start at circle1 and move along vector, you'll arrive at circle2"
Next thing we're going to do is normalise it; all this does is set the length to 1 - the direction is unchanged - this makes it easier to work with for what you're looking to do. A vector with length 1.0 is called a unit vector:
vector.normalize( 1.0 ); // you can pass any length you like, but for this example, we'll stick with 1.0
Now, to draw a line from one circle to another, but starting from the outside, we simply find the start and the end points. The starting point is simple the position of circle1 plus vector (normalised to unit length) multiplied by the radius of circle1:
var sx:Number = circle1.x + vector.x * circle1.radius; // or circle1.width * 0.5 if you don't store the radius
var sy:Number = circle1.y + vector.y * circle1.radius;
The ending point can be found by starting at our start point, and continuing along our vector for a distance equal to the distance between the two circles (minus their radii). The length value that we created earlier is the distance between your two circles, from one center point to another, so we can use that to get the distance minus the radii:
var dist:Number = length - ( circle1.radius + circle2.radius ); // or circle1.width * 0.5 etc
And so the end point:
var ex:Number = sx + vector.x * dist;
var ey:Number = sy + vector.y * dist;
And to draw the line between them:
var line:Shape = new Shape;
line.graphics.lineStyle( 1.0, 0x000000 );
line.graphics.moveTo( sx, sy );
line.graphics.lineTo( ex, ey );
this.addChild( line )

HTML5: Inverse text-color on canvas

I want to draw text on a canvas in the inverse color of the background (to make sure the text is readible no matter the background color). I believe in oldskool bitblt-ing, this was an XOR operation.
How to do this?
Update: most of the newer browsers now support the blending mode "difference" which can achieve the same result.
context.globalCompositeOperation = "difference";
Updated demo.
Old answer:
One should think that the XOR mode for composition would do this, but unfortunately canvas' XOR only XORs the alpha bits.
By applying the following code we can however receive a result such as this:
You can make an extension to the canvas like this:
CanvasRenderingContext2D.prototype.fillInversedText =
function(txt, x, y) {
//code - see below
}
Now you can call it on the context as the normal fillText, but with a slight change:
ctx.fillInversedText(txt, x, y);
For this to work we do the following first - measure text. Currently we can only calculate width of text and then assume the height. This may or may not work well as fonts can be very tall and so forth. Luckily this will change in the future, but for now:
var tw = this.measureText(txt).width;
var th = parseInt(ctx.font, '10');
th = (th === 0) ? 10 : th; //assume default if no font and size is set
Next thing we need to do is to setup an off-screen canvas to draw the text we want ot invert:
var co = document.createElement('canvas');
co.width = tw;
co.height = th;
Then draw the actual text. Color does not matter as we are only interested in the alpha channel for this canvas:
var octx = co.getContext('2d');
octx.font = this.font;
octx.textBaseline = 'top';
octx.fillText(txt, 0, 0);
Then we extract the pixel buffers for the area we want to draw the inverted text as well as all the pixels for the off-screen canvas which now contains our text:
var ddata = this.getImageData(x, y, tw, th);
var sdata = octx.getImageData(0, 0, tw, th);
var dd = ddata.data; //cache for increased speed
var ds = sdata.data;
var len = ds.length;
And then we invert each pixel where alpha channel for pixel is greater than 0.
for (var i = 0; i < len; i += 4) {
if (ds[i + 3] > 0) {
dd[i] = 255 - dd[i];
dd[i + 1] = 255 - dd[i + 1];
dd[i + 2] = 255 - dd[i + 2];
}
}
Finally put back the inverted image:
this.putImageData(ddata, x, y);
This may seem as a lot of operations, but it goes pretty fast.
Demo (warning if you are sensitive to flicker)
(the psychedelic background is just to have some variations as fiddle needs external images and most are prevented by CORS when we use pixel manipulation).
I've removed my old answer, as it did not solve the question. As of recently, there are new globalCompositeOperations that do all kinds of great things. I've created an example that shows how to obtain inverted text. In case that link breaks, the method is essentially this:
ctx.globalCompositeOperation = "difference";
ctx.fillStyle = "white";
//draw inverted things here
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation

AS3 - Finding the Y position of a rotated object if X is known

I am trying to find out the Y position of a rotated object on stage, when only the X position is known. I am not extremely formiliar with how I'd go about doing this, but I know it'll be related to the Rotation of the border object.
What I need to do is know that based on the below X position that is worked out, what the exact maximum Y position can be before it hits the black border that is onscreen. I know that the original position is 280, but I am not sure at all how I then work out what the Y position is further down the line.
I have attached the code to find the X (all be it, it doesn't take into account any rotation as on this level it isn't needed), as well as a screenshot so you can understand clearly.
Thank you for your time.
private function init(e:Event = null):void{
var maxX:int = stage.width
var freeSpace:int = 300
var startX:int = Math.ceil(Math.random() * (maxX - (freeSpace+this.width))) + freeSpace;
this.x = startX
}
I'm not entirely sure on your question but hopefully these suggestions will help:
You can use the localToGlobal() function on a display object to return a rotated, translated, and scaled point within that display container to the stage. Example, $p:Point = myMovieClip.localToGlobal(new Point(10, 10));
A Matrix is also a nice and easy way to rotate a point. Example, var $mtx:Matrix = new Matrix(); $mtx.tx = 10; $mtx.ty = 10; $mtx.rotate(); and now $mtx.tx and $mtx.ty have the rotated result
Those probably won't answer your question, but I figured I'd mention them just in case and before I get into something more complex. Like wvxvw said you can't really solve the equation you're trying to do without some other variables. I wrote some code that shows how to find Y when comparing X to a point in a line segment:
import flash.display.Shape;
import flash.geom.Point;
import flash.display.Graphics;
import flash.events.MouseEvent;
var $s:Shape = new Shape();
addChild($s);
var borderStart:Point = new Point(stage.stageWidth/2, stage.stageHeight/2);
var borderRotation:Number = 45;
var borderLength:Number = 800;
var borderRad:Number = borderRotation * (Math.PI/180);
var borderEnd:Point = new Point(borderStart.x + Math.cos(borderRad) * borderLength, borderStart.y + Math.sin(borderRad) * borderLength);
stage.addEventListener(MouseEvent.MOUSE_MOVE, update);
function update(e:MouseEvent):void{
var $g:Graphics = $s.graphics;
$g.clear();
//Drawing the rotated border
$g.lineStyle(3, 0xff0000, .5);
$g.moveTo(borderStart.x, borderStart.y);
$g.lineTo(borderEnd.x, borderEnd.y);
//Finding if and where mouseX collides with our border
if (stage.mouseX >= Math.min(borderStart.x, borderEnd.x) && stage.mouseX <= Math.max(borderStart.x, borderEnd.x)){
var $x:Number = stage.mouseX;
//SOLVING HERE : Solve collision with X
var $percent:Number = ($x - borderStart.x)/(borderLength * Math.cos(borderRad));
var $y:Number = borderStart.y + Math.sin(borderRad) * borderLength * $percent;
//Drawing to our collision
$g.lineStyle(1, 0xffff00, .6);
$g.moveTo($x, 0);
$g.lineTo($x, $y);
$g.lineStyle(2, 0xffff00, 1);
$g.drawCircle($x, $y, 3);
trace("----\nCollision #\t" + "x: " + $x + "\ty:" + Math.round($y));
}
}
Hopefully this will give some insight on how to solve your particular issue.
I'm not sure if I'm answering the right question, because as you worded it, it's impossible to solve, or rather you would have to accept that Y can be just anything... (In order to be able to find a point in a vector space over R^2 you need a basis of two vectors of a form (x,y), but you only have a vector in R^1).
But it looks like you want to find an intersection of the "black line on the screen" - i.e. an arbitrary line and a vertical line through the lowest point of the "shape" which you want to fit. It's hard to tell from the question, what shape are you trying to fit, but if it is a rectangle, which is not rotated, then it would be either its bottom right or bottom left corner. You can then find which point to choose by comparing the angle between a horizontal line and the "black line" and the horizontal line and the bottom of the rectangle.
Next, you would need to find an intersection between these two lines, the formula can be found here: http://en.wikipedia.org/wiki/Line_intersection

Hit testing pseudo 3d space

So I am writing a little mini pseudo 3d engine for the canvas element in html5. In the code below I am drawing a bunch of squares with varying positions and rotations (rotation around the z axis, so no deformation)
Now I want to be able to tell which square the user clicks on. In the objects array the items are supported by the z position starting with the squares the furthest away from the camera (so that they draw properly). So given a 3d point relative to the top left of the corner of the canvas how can I tell which square was clicked?
//Draw objects
for (var i = 0; i < objects.length; i++) {
var object = objects[i];
var cz = object.position.z - camera.position.z;
if (cz > 0) {
cz = 1 / ((cz - 1) * 0.75 + 1);
context.save();
context.translate(halfWidth, halfHeight); //viewport transform
context.scale(1, -1); //invert y axis
context.scale(cz, cz); //perspective
context.translate(-camera.position.x, -camera.position.y); //camera transform
context.translate(object.position.x, object.position.y); //world transform
context.rotate(object.rotation);
context.fillStyle = object.color;
context.fillRect(-40, -40, 80, 80);
context.restore();
}
}
P.S. If I am doing anything weird or backwards and you know of a way to improve, I would love to hear suggestions
I would suggest that you draw the objects with the same transformations to a hidden canvas of the same size, but give each square a unique color (maybe derived from the index i).
You would do that like this:
var col = index.toString(16); // convert to hex
while (col.length < 6) col = "0"+col; // pad leading 0s
ctx.fillStyle = "#"+col;
ctx.fillRect(-40,-40,80,80);
Then when you get a mouseclick event on the visible canvas, look at that location in your hidden one to get the color (index) of the selected object:
var colData = ctx.getImageData(clickX, clickY, 1, 1).data;
var index = (colData[2]<<16) | (colData[1]<<8) | colData[0];
This will work for up to 16M objects and is fairly simple.