AS3 image rotate within / scale to fit sprite - actionscript-3

We're making bitmap data from a sprite where we want to take an image and rotate within / scale to fit. This is our code, which includes a rotation.
_rotation defines how much the user has input.
The problem is, we're getting an output file that is 100% white.
We think that the image is rotating about 0x0y therefore rotating the image outside the bounds of the sprite.
Furthermore, the image is not scaling to the child, instead is sort of "cropping" as it inherits.
What is the best way of doing this? Basically we want to take an image and rotate within / scale to fit
var sprite1:Sprite = new Sprite();
addChild(sprite1);
var photoBitmap:Bitmap = new Bitmap(_bitmapData);
sprite1.addChild(photoBitmap);
sprite1.rotation = _rotation;
var sprite2:Sprite = new Sprite();
addChild(sprite2);
sprite2.addChild(sprite1);
var bitmapData:BitmapData = new BitmapData(sprite2.width,sprite2.height,false,0xFFFFFF);
bitmapData.draw(sprite2);

The simple way to draw sprite with scaling/rotating is using Matrix in method bitmapData.draw().
Example:
var sourceImage:SomeSprite = new SomeSprite();
var matrix:Matrix = new Matrix();
matrix.scale(0.5, 0.5);
matrix.rotate(0.5 * Math.PI);
var newImage:BitmapData = new BitmapData(sourceImage.width/2, sourceImage.height/2);
newImage.draw(sourceImage, matrix);

Your issue is likely a cause of rotating around the top left corner (which can make the entire object left of or above the registration point (0 x and 0 y) and not get drawn.
An easy way you can account for this, is to move sprite1 after the rotation to account for the new size and position caused by rotating:
...
sprite2.addChild(sprite1);
var actualPosition:Rectangle = sprite2.getBounds(sprite2); //this gets the new position/dimensions of the object
sprite1.x = -actualPosition.x;
sprite1.y = -actualPosition.y;
var bitmapData:BitmapData = new BitmapData(sprite2.width,sprite2.height,false,0xFFFFFF);
...

Related

AS3 Having trouble understanding Rectangle Intersection

I'm trying to get Rectangle.intersection to provide me with the rectangle of the intersection area of 2 overlapping shapes but not having much success.
The code below is simply 2 shapes the same size. The top most shape is draggable.
When the drag is stopped I perform a bottomRect.intersection(topRect) call but this always returns the full size of the rect, not the intersection size.
(the code can be copied and pasted into a new ActionScript file on the first frame and run.)
Does anyone have an idea where I'm going wrong?
Thanks
import flash.geom.Rectangle;
import flash.display.Sprite;
var bottomSprite:Sprite = new Sprite();
addChild(bottomSprite);
var bottomRect:Shape = new Shape;
bottomRect.graphics.beginFill(0xFF0000);
bottomRect.graphics.drawRect(0, 0, 320,480);
bottomRect.graphics.endFill();
bottomSprite.addChild(bottomRect);
var topSprite:Sprite = new Sprite();
addChild(topSprite);
var topRect:Shape = new Shape;
topRect.graphics.beginFill(0x000033);
topRect.graphics.drawRect(0, 0, 320,480);
topRect.graphics.endFill();
topSprite.addChild(topRect);
var bottomBoundsRect:Rectangle = stage.getBounds(bottomSprite);
trace("START: bottomBoundsRect ", bottomBoundsRect);
var topBoundsRect:Rectangle = stage.getBounds(topSprite);
trace("START: topBoundsRect ", topBoundsRect);
topSprite.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
topSprite.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
function mouseDownHandler(evt:MouseEvent):void{
topSprite.startDrag();
}
function mouseUpHandler(evt:MouseEvent):void{
topSprite.stopDrag();
topBoundsRect = stage.getBounds(topSprite);
trace("INTERSECTION RECT", bottomBoundsRect.intersection(topBoundsRect));
}
The problem is because of wrong toIntersect property that you pass:
topBoundsRect = stage.getBounds(topSprite);
trace("INTERSECTION RECT", bottomBoundsRect.intersection(topBoundsRect));
What you do is that you get the bounds of the topSprite agains the Stage. If you trace it, it will give you something like this:
(x=-62, y=-41, w=382, h=521)
So you have a bounds that start at 0,0 and have bigger width/height, because you move the topSprite - here I've moved it 62 pixels to the right (382 - 320 [width]), and 41 pixels down (521 - 480 [height]).
The actual intersection of this rectangle against the bottom one, is exactly the size of the bottom one.
What you should do is something similar to this:
// somehow get the rectangle of the bottom sprite
var br:Rectangle = new Rectangle(bottomSprite.x, bottomSprite.y, bottomSprite.width, bottomSprite.height);
// somehow get the rectangle of the top sprite
var tr:Rectangle = new Rectangle(topSprite.x, topSprite.y, topSprite.width, topSprite.height);
trace (br.intersection(tr)); // intersect them
There are few ways to get the bounds, but this is also working and shows the idea.
Hope that helps! :)

Loop an image in Flash

I want to have a scene where an image which is 5000 pixels high is moving up 5 pixels each frame-refresh. When the image is all up, I'd like to see the top of the image connected to the bottom of the image. This should be done untill the level is 'done'. How can I 'loop' such an Image?
You can create a copy of that image which you keep hidden/above and the trick is to update the position and loop accordingly so when one image goes bellow the screen it goes back on top and repeats.
Here's a basic snippet to illustrate the idea using the DisplayObject class and the scrollRect property:
//ignore this, you have your content already
var dummyContent:BitmapData = new BitmapData(100,100,false);
dummyContent.perlinNoise(10,10,8,12,true,true);
//important stuff starts here
var container:Sprite = addChild(new Sprite()) as Sprite;//make a container
container.scrollRect = new Rectangle(0,0,dummyContent.width,dummyContent.height);//set a scrollRect/'mask'
var part1:DisplayObject = container.addChild(new Bitmap(dummyContent));//add two copies
var part2:DisplayObject = container.addChild(new Bitmap(dummyContent));//of the same content
part2.y -= part2.height;//set the 2nd at the top of the 1st
addEventListener(Event.ENTER_FRAME,update);
function update(e:Event):void{
//move both
part1.y += 5;
part2.y += 5;
//check if any reach the bottom so they can be moved back up
if(part1.y >= part1.height) part1.y = -part1.height;
if(part2.y >= part2.height) part2.y = -part2.height;
//the above can also be nicely placed in a loop if you plan on using more seamless looping clips/images
}
Obviously you will have different content, but the principle is the same.
If you're working with images, you can simply use BitmapData's copyPixels method:
var s:int = 5;//scroll speed
//make some content
var w:int = 100;
var h:int = 100;
var dummyContent:BitmapData = new BitmapData(w,h,false);
dummyContent.perlinNoise(10,10,8,12,true,true);
//prepare for stiching
var renderPos:Point = new Point();//position to render the current image to
var prenderPos:Point = new Point();//position to render the previous image (the 'hidden' copy above)
var render:BitmapData = new BitmapData(w,h,false);//create a bitmap data instance to render updated pixels int
addChild(new Bitmap(render));//and add it to the stage
addEventListener(Event.ENTER_FRAME,update);
function update(e:Event):void{
renderPos.y = (renderPos.y+s)%h;//update the scroll position for the 1st part, % is used to loop back to 0 when the position gets to the content height
prenderPos.y = renderPos.y - h;//update the scroll position for the 2nd part (above)
render.lock();//freeze pixel updates
render.copyPixels(dummyContent,dummyContent.rect,renderPos);//copy pixels from the scroll position to the bottom
render.copyPixels(dummyContent,dummyContent.rect,prenderPos);//copy pixels from the top to the scroll position
render.unlock();//unfreeze/update ALL THE PIXELS
}
You can try to use a Rectangle object which changes height (height-scrollPosition) so you potentially access less pixels each time or you can manually work out single for loops using BitmapData's getVector method, but that's something to look into if performance is actually an issue for such a simple task and it's worth checking what's faster ( copy full bitmap rect vs copy partial bitmap rect vs manually copy values using vector )
Be warned, Flash cannot load an image greater than 16,769,025 pixels (or 4095x4095). The height of 5000 pixels will work as long as the width is not greater than 3353.
That said, I'd loop the image by keeping two copies of the image onstage, move both at the same time with a parent object, and reset to origin once your loop point is met.
Consider the following stage setup:
Stage ¬
0: MainTimeline:MovieClip ¬
0: Container:MovieClip ¬
0: img1:Bitmap
1: img2:Bitmap
Now moving container up, you'd just need to check that the looping second image reaches the origin point of the first image.
function onEnterFrame(e:Event):void {
Container.y = Container.y - 5;
if (Container.y < -5000) {
Container.y = -5;
}
}

make visual clone of displayObject that's nested within other displayObjects, and add the clone to the stage layer in the same location, rotation, etc

I want to be able to grab a copy of a DisplayObject that is nested within other transformed DisplayObjects (rotated, scaled, stretched objects), and be able to stamp it back into the same visual location, but on the stage layer. Essentially, being able to make a clone of a nested DisplayObject, but be able to add the clone to the stage layer, yet have it perfectly align (visually) with the original (same position, scale, rotation)
I have been working with something along the lines of:
// draw the pixels of a displayobject into a new bitmap object
var bitmapData:BitmapData = new BitmapData(nestedSprite.width, nestedSprite.height, true, 0xFFFFFF);
var bitmap:Bitmap = new Bitmap(bitmapData);
bitmapData.draw(nestedSprite);
// put the copy on the top most layer
stage.addChild(bitmap);
// position the copy to perfectly overlay the original, but on the top stage layer
var point:Point = nestedSprite.localToGlobal(new Point(0, 0));
bitmap.x = point.x;
bitmap.y = point.y;
But this only works well for displayObjects whose parents are not transformed; and for displayObjetcs that are perectly at the (0,0) origin. It falls apart for centered aligned objects or scaled parents, etc.
I am aware that I can add a matrix param to the .draw() method, as well as a clipping rectngle, and scale my bitmap afterwards, or setting the transform of one object to another, or use .transform.concatenatedMatrix, or use nestedObject.getBounds(null), or nestedSprite.getBounds(nestedSprite), etc. But I have unfortunately fallen into doing trial and error programming on this one, and with some many variables, this is never a good way to solve a programming problem.
I believe this function should work, the only extra step was offsetting the concatenated matrix so that the target would draw with its top left at (0, 0) on the Bitmap even if its origin was somewhere else. Hopefully the rest is self explanatory, but I can add more comments if anything doesn't make sense.
function createBitmapClone(target:DisplayObject):Bitmap {
var targetTransform:Matrix = target.transform.concatenatedMatrix;
var targetGlobalBounds:Rectangle = target.getBounds(target.stage);
var targetGlobalPos:Point = target.localToGlobal(new Point());
// Calculate difference between target origin and top left.
var targetOriginOffset:Point = new Point(targetGlobalPos.x - targetGlobalBounds.left, targetGlobalPos.y - targetGlobalBounds.top);
// Move transform matrix so that top left of target will be at (0, 0).
targetTransform.tx = targetOriginOffset.x;
targetTransform.ty = targetOriginOffset.y;
var cloneData:BitmapData = new BitmapData(targetGlobalBounds.width, targetGlobalBounds.height, true, 0x00000000);
cloneData.draw(target, targetTransform);
var clone:Bitmap = new Bitmap(cloneData);
// Move clone to target's global position, minus the origin offset.
clone.x = targetGlobalPos.x - targetOriginOffset.x;
clone.y = targetGlobalPos.y - targetOriginOffset.y;
return clone;
}
Unfortunately, pixelBounds seems to return an origin of (0, 0) if there are any filters on the DisplayObjects, which obviously breaks things.
Edit: Replaced target.transform.pixelBounds with target.getBounds(target.stage) as a slight improvement. This keeps the position correct if there are filters, but filters on parent DisplayObjects still won't be included, and filters on the target can overlap the edges of the Bitmap. I'm not sure if there's a simple way to work around that.
Update: Jimmi Heiserman spotted that this function is broken if the swf is scaled. Without stage.scaleMode = StageScaleMode.NO_SCALE; though, the stageWidth and stageHeight parameters seem to stay unchanged, so the only (rather hacky) workaround I've found is to add an "unscaled" test Sprite and use its concatenatedMatrix to adjust the clone's position and scale:
function createScaledBitmapClone(target:DisplayObject):Bitmap {
var targetTransform:Matrix = target.transform.concatenatedMatrix;
var targetGlobalBounds:Rectangle = target.getBounds(target.stage);
var targetGlobalPos:Point = target.localToGlobal(new Point());
// Calculate difference between target origin and top left.
var targetOriginOffset:Point = new Point(targetGlobalPos.x - targetGlobalBounds.left, targetGlobalPos.y - targetGlobalBounds.top);
// Create a test Sprite to check if the stage is scaled.
var testSprite:Sprite = new Sprite();
target.stage.addChild(testSprite);
var testMatrix:Matrix = testSprite.transform.concatenatedMatrix;
target.stage.removeChild(testSprite);
// Move transform matrix so that top left of target will be at (0, 0).
targetTransform.tx = targetOriginOffset.x * testMatrix.a;
targetTransform.ty = targetOriginOffset.y * testMatrix.d;
var cloneData:BitmapData = new BitmapData(targetGlobalBounds.width * testMatrix.a, targetGlobalBounds.height * testMatrix.d, true, 0x00000000);
cloneData.draw(target, targetTransform);
var clone:Bitmap = new Bitmap(cloneData);
// Move clone to target's global position, minus the origin offset, and cancel out stage scaling.
clone.x = targetGlobalPos.x - targetOriginOffset.x;
clone.y = targetGlobalPos.y - targetOriginOffset.y;
clone.scaleX = 1 / testMatrix.a;
clone.scaleY = 1 / testMatrix.d;
return clone;
}
Have you tried passing the parents transform into draw? draw takes a transform matrix as the second param.
If you have a handle on the parent you can use something like this
bitmapData.draw(nestedSprite, parent.transform.matrix);

How to properly create a Point relative to an Image using FigPanZoom?

I have an Image that's 5000 pixels by 5000 pixels. It's scaled up and down regularly to fit different parts of the image into the window.
I'm making a function that focuses in on different places on the image (like a map) and then zooms into a certain scale that I specify.
I pass my point into this function (new Point(2000,2500) for example)) however this always breaks because it's not relative to the image specifically.
How do I make the Point relative to the image at the image's given scale at any given time?
Additional Info: I'm using the guide here http://www.adobe.com/devnet/flex/samples/fig_panzoom.html for panning and zooming functionality.
Solved:
One of my pitfalls was that I was using a possible bitmapScaleFactor > 1 which would mess up the scaling. This was the final function that worked for my usage.
protected function testFocus(p:Point):void
{
var content:ContentRectangle = boardViewer._contentRectangle;
var panToPoint:PanToPointCommand = new PanToPointCommand(boardViewer, content);
var scale:Number = boardViewer.bitmapScaleFactor;
var location = new Point((p.x*scale)+content.x, (p.y*scale)+content.y);
var center = new Point(boardViewer.width/2, boardViewer.height/2);
//Move the point to the center
panToPoint.fromPoint = location;
panToPoint.toPoint = center;
panToPoint.execute();
}
I've not tested this but how about using the scale factor that is being applied to the image to alter the point coordinates.
e.g.
var scaler:Number = 0.5;
image.scaleX = image.scaleY = scaler;
new Point(2000*scaler,2500*scaler);
Are you looking for DisplayObject.mouseX/mouseY? These do not take any rotation into account, however.
To take rotation into account, you could do something like (untested code):
var mat:Matrix = image.transform.concatenatedMatrix;
mat.invert();
// now use mat to transform point from 'stage space' to 'image space'
var pImage:Point = mat.transform(new Point(stage.mouseX, stage.mouseY));

actionscript child sprite coordinate

I have 3 square. smallest square s3 is inside s2. s2 is in side s1. each is 10px larger
var s1:Sprite = new Sprite();
var s2:Sprite = new Sprite();
var s3:Sprite = new Sprite();
s2.addChild(s3);
s1.addChild(s2);
how can I find the coordinate of child in its parent's coordinate system?
Inside s3
trace(x) //10
trace(parent.childXof(s3)) //20 how do I get this?
What you are looking for is the localToGlobal and globalToLocal methods of DisplayObjects
Links to documentation:
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/display/DisplayObject.html#globalToLocal%28%29
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/display/DisplayObject.html#localToGlobal%28%29
They allow you to transform a point from one coordinate space to the other, which is what you are looking to do.
Hope it helps!