Continuous object rotation around point - actionscript-3

I want to make a function similar to rotateAroundInternalPoint(). So far my solution is this:
import flash.events.Event;
import flash.geom.Matrix;
import flash.geom.Point;
addEventListener( Event.ENTER_FRAME, onFrame );
function onFrame(e:Event):void
{
var m:Matrix = item.transform.matrix.clone();
var point:Point = new Point( 50, 50 ); // The object's width and height are 100px, so 50 is the center
point = m.transformPoint( point );
m.translate( -point.x, -point.y );
m.rotate( 5 * ( Math.PI / 180 ) );
m.translate( point.x, point.y );
item.transform.matrix = m;
}
However there is a fundamental flaw in this code - it gets less and less precise with each iteration.
Could somebody point out what's causing this and what the solution would be?

I've solved the problem with introducing a reference matrix that would not change, so the mistake from the initial iteration would be non-existent.
Here's the implementation:
import flash.events.Event;
import flash.geom.Matrix;
import flash.geom.Point;
var referenceMatrix:Matrix = item.transform.matrix.clone();
addEventListener( Event.ENTER_FRAME, onFrame );
var i:Number = 0; // you'll need this because the referenceMatrix rotation will only go one step, so instead you need to increase the rotation
function onFrame(e:Event):void
{
var m:Matrix = referenceMatrix.clone();
var point:Point = new Point( 100, 100 ); // pivot point local to the object's coordinates
point = m.transformPoint( point );
m.translate( -point.x, -point.y );
m.rotate( i * ( Math.PI / 180 ) );
m.translate( point.x, point.y );
item.transform.matrix = m;
i += 1.2; // rotation step
}
Please note that this code is written in a frame and isn't well-optimized for real use, rather it illustrates the algorithm.

Related

Flash AS3: How Do I Change the Z-index of a Constructed Graphic?

I'm trying to make a program that pulses to the beat of a song, with a wave pattern on top, but the wave pattern seems to be behind everything. I know how to change the index of a child added to the stage, but I have no clue on how to set the index value of a graphic. Here's my code(if relevant):
import flash.display.Sprite;
import flash.display.Graphics;
import flash.events.Event;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundMixer;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.text.TextField;
var snd:Sound = new Sound();
var req:URLRequest = new URLRequest("SuperHexagonMusic.mp3");
var channel:SoundChannel;
stage.addEventListener(Event.ENTER_FRAME, everyFrame);
snd.load(req);
channel = snd.play();
channel.addEventListener(Event.SOUND_COMPLETE, onPlaybackComplete);
function everyFrame(event:Event):void
{
pulse.alpha = channel.leftPeak * 3;
var bytes:ByteArray = new ByteArray();
const PLOT_HEIGHT:int = 200;
const CHANNEL_LENGTH:int = 256;
SoundMixer.computeSpectrum(bytes, false, 0);
var g:Graphics = this.graphics;
g.clear();
g.lineStyle(0, 0x000000);
g.beginFill(0x000000);
g.moveTo(0, PLOT_HEIGHT);
var n:Number = 0;
for (var i:int = 0; i < CHANNEL_LENGTH; i++)
{
n = (bytes.readFloat() * PLOT_HEIGHT);
g.lineTo(i * 2.5, PLOT_HEIGHT - n);
}
g.lineTo(CHANNEL_LENGTH * 2.5, PLOT_HEIGHT);
g.endFill();
g.lineStyle(0, 0xFFFFFF);
g.beginFill(0xFFFFFF, 0.5);
g.moveTo(CHANNEL_LENGTH * 2.5, PLOT_HEIGHT);
for (i = CHANNEL_LENGTH; i > 0; i--)
{
n = (bytes.readFloat() * PLOT_HEIGHT);
g.lineTo(i * 2.5, PLOT_HEIGHT - n);
}
g.lineTo(0, PLOT_HEIGHT);
g.endFill();
}
function onPlaybackComplete(event:Event):void
{
stage.removeEventListener(Event.ENTER_FRAME, everyFrame);
}
What I'm hoping for is the constructed wave pattern to be on top of the pulse, but the current code produces this result:
In the image, you can see the end of the wave pattern poking out behind the pulse.
So in short: How might I bring that wave pattern to the front?
The parent's Graphics is always behind all parent's children. You should make yourself another Sprite or Shape object, place it on top of your display list, and draw on its graphics instead.
var spectrum:Shape=new Shape();
addChild(spectrum); // if last added, will be on top
function everyFrame(e:Event):void {
var g:Graphics=spectrum.graphics; // that's all you need to change!
// the rest of code follows
}

Applying blur filter to freeform areas

I have to build a flash application for simple wrinkle retouching. The user should be able to upload a portrait photo, select the wrinkled areas and then a blur filter will be applied to the selected parts. My question is - is it somehow possible to apply the filter to a freeform area? I know it would be easy to make the selection rectangular, but that would not be very useful to really mark the correct areas. Ideally the user should get some kind of round brush to use for marking the areas, press "OK" and then the filter will be applied.
Is there any way to do this? And do you maybe have some further recommendations on how to approach this task? I have very little experience with manipulating bitmap data with ActionScript.
Any help is appreciated! Thanks very much in advance :-)
The algorithm is as follows:
create a BitmapData of selection shape using BitmapData.draw() (let it be A)
cut a rectangular piece of original image that is covered by selection area, add some margins for blurred area using Bitmapdata.copyPixels() (let it be B).
remove all pixels from B, that are not covered by shape A with BitmapData.threshold() using A as source bitmap.
blur the resulting image
copy blurred pixels back to target image using Bitmapdata.copyPixels()
Here's a complete and working example.
I've written the code anyway while figuring out the solution.
Hope you find it usefull.
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Shape;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.filters.BlurFilter;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.net.URLRequest;
import flash.system.LoaderContext;
[SWF(width="800", height="600",backgroundColor="#FFFFFF")]
public class TestBlur extends Sprite
{
private const loader:Loader = new Loader();
public function TestBlur()
{
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
loader.load(new URLRequest("http://i.stack.imgur.com/u3iEv.png"), new LoaderContext(true));
}
protected function onLoadComplete(event:Event):void
{
trace(event);
var bmp:Bitmap = loader.content as Bitmap;
addChild(bmp);
// create some test selection area
var selection:Shape = new Shape();
selection.graphics.lineStyle(30, 0xFF0000, .5);
selection.graphics.curveTo(75, -50, 200, 10);
selection.x = 40;
selection.y = 60;
addChild(selection);
// create a duplicate of the original image
var target:BitmapData = bmp.bitmapData.clone();
var targetBmp:Bitmap = new Bitmap(target);
targetBmp.x = bmp.x + bmp.width;
addChild(targetBmp);
//
// *** main work starts here ***
// by now we have selection shape and a bitmap to blur
const destPoint:Point = new Point();
const drawMatrix:Matrix = new Matrix();
const blurMargin:uint = 10;
const blur:BlurFilter = new BlurFilter(2, 2, 3);
var rect:Rectangle;
// 0: prepare an image of selection area
// we'll need it at step 3
rect = selection.getBounds(selection);
rect.x -= blurMargin;
rect.y -= blurMargin;
rect.width += blurMargin*2;
rect.height += blurMargin*2;
var selectionImage:BitmapData = new BitmapData(rect.width, rect.height, true, 0);
drawMatrix.identity();
drawMatrix.translate(-rect.x, -rect.y);
selectionImage.draw(selection, drawMatrix);
// just some testing
var test0:Bitmap = new Bitmap(selectionImage.clone());
test0.y = bmp.y + bmp.height;
addChild(test0);
// 1: cut a rectangular piece of original image that is covered by selection area
rect = selection.getBounds(selection.parent);
rect.x -= blurMargin;
rect.y -= blurMargin;
rect.width += blurMargin*2;
rect.height += blurMargin*2;
var area:BitmapData = new BitmapData(rect.width, rect.height, true, 0);
area.copyPixels(bmp.bitmapData, rect, destPoint);
// just some testing
var test1:Bitmap = new Bitmap(area.clone());
test1.y = bmp.y + bmp.height;
test1.x = test0.x + test0.width;
addChild(test1);
// 2: remove all pixels that are not covered by selection
area.threshold(selectionImage, area.rect, destPoint, "==", 0, 0, 0xFF000000);
// just some testing
var test2:Bitmap = new Bitmap(area.clone());
test2.y = test0.y + test0.height;
test2.x = test0.x;
addChild(test2);
// 3: blur copied area
area.applyFilter(area, area.rect, destPoint, blur);
// just some testing
var test3:Bitmap = new Bitmap(area.clone());
test3.y = test0.y + test0.height;
test3.x = test2.x + test2.width;
addChild(test3);
// 4: copy blurred pixels back to target image
destPoint.x = rect.x;
destPoint.y = rect.y;
target.copyPixels(area, area.rect, destPoint);
}
}
}

AS3 - Check if BitmapData is completely obscured by other BitmapData

I'm looking to check if a BitmapData object is completely obscured by a BitmapData object. Is there something like the hitTest function but that makes sure every pixel is covered instead of any pixel?
Edit: It is important that transparent pixels are not included when checking if the object is obscured.
It's actually a pretty simple solution after all! Basically what you do is just capture the pixel values of the overlapping bitmap in the rectangle area that the overlapped bitmap occupies. You then iterate over that vector of values and as long as you don't have a 0 (completely transparent pixel), then you've completely covered the bitmap underneath.
Here are the two bitmaps I used in this test:
Overlapping bitmap:
Overlapped bitmap:
Code:
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.utils.ByteArray;
import flash.geom.Rectangle;
var coveredBitmapData:BitmapData = new CoveredBitmapData();
var coveringBitmapData:BitmapData = new CoveringBitmapData();
var coveringBitmap:Bitmap = new Bitmap(coveringBitmapData, "auto", true);
var coveredBitmap:Bitmap = new Bitmap(coveredBitmapData, "auto", true);
coveredBitmap.x = Math.random() * (stage.stageWidth - coveredBitmap.width);
coveredBitmap.y = Math.random() * (stage.stageHeight - coveredBitmap.height);
stage.addChild(coveredBitmap);
stage.addChild(coveringBitmap);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMovement);
function onMouseMovement(e:MouseEvent):void
{
coveringBitmap.x = mouseX - (coveringBitmap.width * .5);
coveringBitmap.y = mouseY - (coveringBitmap.height * .5);
checkIfCovering(coveringBitmap, coveredBitmap);
}
function checkIfCovering(bitmapA:Bitmap, bitmapB:Bitmap):Boolean
{
//bitmapA is the covering bitmap, bitmapB is the bitmap being overlapped
var overlappedBitmapOrigin:Point = new Point(bitmapB.x, bitmapB.y);
var localOverlappedBitmapOrigin:Point = bitmapA.globalToLocal(overlappedBitmapOrigin);
var overlappingPixels:Vector.<uint> = bitmapA.bitmapData.getVector(new Rectangle(localOverlappedBitmapOrigin.x, localOverlappedBitmapOrigin.y, bitmapB.width, bitmapB.height));
if(overlappingPixels.length == 0) {
//This means that there is no bitmap data in the rectangle we tried to capture. So we are not at all covering the underlying bitmap.
return false;
}
var i:uint = 0;
for(i; i < overlappingPixels.length; ++i) {
if(overlappingPixels[i] == 0) {
return false;
}
}
return true;
}
So you want to see if object2 completely covers object1 (both Bitmaps)?
var left:Boolean = object2.x <= object1.x;
var top:Boolean = object2.y <= object1.y;
var right:Boolean = object2.x + object2.width >= object1.x + object1.width;
var bottom:Boolean = object2.y + object2.height >= object1.y + object1.height;
if(left && right && top && bottom)
{
// Completely covered.
}

Drawing random circles using a timer within stage boundaries in ActionScript

I need to use a Timer for time controlled animation, time the drawing to occur every 500 milliseconds & Draw 20 circles in total. I also need to make sure the circles are completely drawn inside the limits of the stage. This is the code I've been playing around with and I can't figure it out.
import flash.events.TimerEvent;
import flash.utils.Timer;
// creates a new hundred-second Timer, ticks every 250 milliseconds
var faster_minuteTimer:Timer = new Timer(250, 6);
// designates listeners for the interval and completion events
faster_minuteTimer.addEventListener(TimerEvent.TIMER, onTick);
faster_minuteTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
// starts the timer ticking
faster_minuteTimer.start();
function onTick(event:TimerEvent):void
{
// displays the tick count so far
trace("Count... " + event.target.currentCount);
}
function onTimerComplete(event:TimerEvent):void
{
trace("Play Done.");
}
var xCoord, yCoord, radius, Width, Height:uint; // declare variables
// not using any variables for the first one
xCoord = (Math.random()* stage.stageWidth); // somewhere on the stage
yCoord = (Math.random() * stage.stageHeight);
radius = Math.max(Math.random() * 85, 20); // radius between two numbers
graphics.beginFill(Math.random() * 0xffffff); // random color
graphics.drawCircle(xCoord,yCoord,radius); // coordinates x & y, radius
graphics.endFill(); // end color fill
import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.utils.Timer;
var timer:Timer = new Timer(500, 20);
timer.addEventListener(TimerEvent.TIMER, timerTick);
timer.start();
function timerTick(e:TimerEvent):void {
var newCircle:Sprite = new Sprite();
var radius:Number = Math.max(Math.random() * 85, 20);
var safeX:Number = ((stage.stageWidth - radius) - radius) * Math.random() + radius;
var safeY:Number = ((stage.stageHeight - radius) - radius) * Math.random() + radius;
newCircle.graphics.beginFill(Math.random() * 0xFFFFFF, 1);
newCircle.graphics.drawCircle(0, 0, radius);
newCircle.graphics.endFill();
newCircle.x = safeX;
newCircle.y = safeY;
stage.addChild(newCircle);
}
Setup your timer
Create a circle sprite
Generate a random radius size per your question's parameters
Determine a safe value between max (stage.stageWidth - radius) and min (radius)
Draw a circle in the circle sprite such that its center point is on the Sprite's origin (0, 0)
Position the circle at its given, randomized safe coordinates
Add the circle to the stage
Hope this helps!
Tested and working :)
Edit: Here is a sample image of a distribution of 2000 circles: http://grab.by/6C1O
I don't have a compiler handy, but something like this should be close.
The global variables maxWidth, maxHeight, maxSize determine the largest circle that will fit and where it can be drawn.
The drawOne() function draws a circle of random radius. The center of the circle is randomly set to be at least the circle's radius away from each side of the stage.
Each tick of the timer calls drawOne().
import flash.events.TimerEvent;
import flash.utils.Timer;
// Timer ticks 20 times 500 msec apart
var circleTimer:Timer = new Timer(500, 20);
// designates listeners for the interval and completion events
circleTimer.addEventListener(TimerEvent.TIMER, onTick);
circleTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
// starts the timer ticking
circleTimer.start();
function onTick(event:TimerEvent):void
{
trace("Count... " + event.target.currentCount);
drawOne();
}
function onTimerComplete(event:TimerEvent):void
{
trace("Play Done.");
}
// globals for size of stage, circles
var maxWidth:uint = stage.stageWidth;
var maxHeight:uint = stage.stageHeight;
var maxSize:uint = Math.min(maxWidth, maxHeight);
var minSize:uint = Math.min(20, Math.floor(maxSize/2));
function drawOne():void
{
// to fit box, radius must be 1/2 shortest side or less
var radius:uint = Math.max(Math.floor(Math.random() * maxSize/2), minSize);
// center circle at least radius from any side
var xCoord:uint = Math.random()*(maxWidth - 2*radius) + radius;
var yCoord:uint = Math.random()*(maxHeight - 2*radius) + radius;
graphics.beginFill(Math.random() * 0xffffff); // random color
graphics.drawCircle(xCoord,yCoord,radius);
graphics.endFill();
}
Hope this helps...

mapping planes onto primitives

I've looped through the vertices and mapped a plane to each one. I'm having problems orientating the planes correctly. I can get it working with a sphere but when i make any alterations to the the primitive - positions are correct but they don't face/tilt the right way.
EDIT: Note - the alternation to the sphere was done before the sphere was created. I have updated the Sphere class to create an elongated sphere.
The code I'm using to place the planes are as follows:
pivotDO3D = new DisplayObject3D();
scene.addChild(pivotDO3D);
var bigSphere:Sphere = new Sphere(null, 500, 20, 20);
for each (var v:Vertex3D in bigSphere.geometry.vertices)
{
var __seatmaterial:ColorMaterial = new ColorMaterial(0x000000);
__seatmaterial.doubleSided = true;
var p:Plane = new Plane(__seatmaterial, 20, 20, 2, 2);
pivotDO3D.addChild(p);
p.position = v.toNumber3D();
p.lookAt(bigSphere);
}
The following demo shows how to minimize the problem. I changed the multiplication factor of 0.6 to 2.0 as well as the sphere size in order to exaggerate the effect so you can see it easily. Make sure to change 0.6 to 2.0 in your Sphere.as as well.
The key is in varying the z location of the target point with the z location of the point on the sphere.
To compare, run it as-is to see the "fixed" version, and change the lookAt target from pivotDO3D2 to bigSphere to see the old version.
package
{
import flash.display.Sprite;
import flash.events.Event;
import org.papervision3d.cameras.*;
import org.papervision3d.core.geom.renderables.*;
import org.papervision3d.materials.*;
import org.papervision3d.objects.*;
import org.papervision3d.objects.primitives.*;
import org.papervision3d.render.*;
import org.papervision3d.scenes.*;
import org.papervision3d.view.*;
[SWF(width='400', height='400', backgroundColor='0x000000', frameRate='30')]
public class PlaneOrientationDemo extends Sprite
{
private var scene:Scene3D;
private var camera:Camera3D;
private var renderer:BasicRenderEngine;
private var viewport:Viewport3D;
private var pivotDO3D:DisplayObject3D;
public function PlaneOrientationDemo()
{
viewport = new Viewport3D(0, 0, true, true);
addChild( viewport );
renderer = new BasicRenderEngine();
scene = new Scene3D( );
camera = new Camera3D();
camera.z = -700;
camera.zoom = 50;
pivotDO3D = new DisplayObject3D();
scene.addChild(pivotDO3D);
var pivotDO3D2:DisplayObject3D = new DisplayObject3D();
var bigSphere:Sphere = new Sphere(null, 150, 20, 20);
for each (var v:Vertex3D in bigSphere.geometry.vertices)
{
var __seatmaterial:ColorMaterial = new ColorMaterial(0x00FF00);
__seatmaterial.doubleSided = true;
var p:Plane = new Plane(__seatmaterial, 20, 20, 2, 2);
pivotDO3D.addChild(p);
p.position = v.toNumber3D();
// This number should match the fx multiplication factor in Sphere.as.
var xFactor:Number = 2.0;
pivotDO3D2.z = v.z / (Math.PI / xFactor);
p.lookAt(pivotDO3D2);
}
stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event: Event): void
{
pivotDO3D.rotationX += 1;
pivotDO3D.rotationY += 1;
renderer.renderScene(scene, camera, viewport);
}
}
}