AS3 - Check if BitmapData is completely obscured by other BitmapData - actionscript-3

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

Related

Relative coordinate issue in AS3

I've edited the following code in order to let those green rectangles to follow my cursor which is customized by a small rectangle. But I've encountered several problems:
Although I haven't defined any coordinate in the separate class, but the size is abviously wrong in the stage when publish with only half size for the cursor coordinate.
The reset button cannot be activated, although I've tested well in the other code.
Here is the work I've published: http://neowudesign.com/hwu_ex04.html
The code on timeline
//hw//Creating a new cursor
newcursor.startDrag ("true");
Mouse.hide();
//hw//Creating a holder to hold the butterfly objects
var mothHolder = new Sprite();
addChild(mothHolder);
//hw//Creating seven moths at the beginning
makeMoths(7);
//hw//creating a function which can generate limited numbers of moths.
function makeMoths(MothsNumber:Number)
{
for (var i = 0; i < MothsNumber; i++)
{
newMoth = new Moth();
mothHolder.addChild(newMoth);
}
}
//hw//Set the reset button at the top for clicking, but it's failed to work;
//hw//Set the cursor back to the default one, and remove the custom one when hovering;
mothHolder.setChildIndex(reset,mothHolder.numChildren);
reset.addEventListener(MouseEvent.MOUSE_OVER, cursorchange);
function cursorchange(event:MouseEvent):void
{
Mouse.show();
newcursor.visible = false;
trace("alert!!");
}
//hw//creating a function of reset
reset.addEventListener(MouseEvent.CLICK, resetClick, false, 0, true);
function resetClick(evt:MouseEvent):void
{
removeChild(mothHolder);
mothHolder = new MovieClip();
addChild(mothHolder);
var numMoths:Number = Math.round(Math.random() * 6) + 1;
trace("Moths Numeber: "+ numMoths);
makeButterflies(numButterflies);
}
//hw//when the cursor leave the reset region, it turns back to the customized one
reset.addEventListener(MouseEvent.MOUSE_OUT, fl_MouseOutHandler);
function fl_MouseOutHandler(event:MouseEvent):void
{
removeEventListener(MouseEvent.MOUSE_OVER, cursorchange);
Mouse.hide();
newcursor.visible = true;
}
And the code for class "Moth" separately named "angle.as"
package {
import flash.display.MovieClip;
import flash.events.Event;
import flash.geom.Point;
public class angle extends MovieClip {
var speed:Number = 8;
function angle() {
//letting every moth follow the moving of the cursor
addEventListener(Event.ENTER_FRAME,mothMove);
function mothMove(myEvent:Event) {
trace(mouseX);
trace(mouseY);
var angle:Number = Math.atan2(mouseY - y, mouseX - x);
x += Math.cos( angle ) * speed;
y += Math.sin( angle ) * speed;
}
}
}
}

AS3 Wrapped-Around BitmapData Scrolling?

Does anyone have a good solution for creating a BitmapData-based scroller in AS3 that can wrap around an image while scrolling to the left and to the right (and also supports arbitrary speed, not just one pixel per loop)? I'm trying to write a fast and lightweight scroller that may only use BitmapData.copyPixels() and BitmapData.scroll().
So far I came up with this code:
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.KeyboardEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.ui.Keyboard;
[SWF(width="800", height="480", frameRate="60", backgroundColor="#000000")]
public class Main extends Sprite
{
private var _source:BitmapData;
private var _sourceWidth:int;
private var _buffer:BitmapData;
private var _canvas:BitmapData;
private var _rect:Rectangle = new Rectangle();
private var _point:Point = new Point();
private var _xOffset:int = 0;
public function Main()
{
_source = new Picture();
_sourceWidth = _source.width;
_rect.width = _source.width;
_rect.height = _source.height;
_canvas = new BitmapData(_source.width, _source.height, false, 0x000000);
_canvas.copyPixels(_source, _rect, _point);
_buffer = _canvas.clone();
var b:Bitmap = new Bitmap(_canvas);
b.x = stage.stageWidth / 2 - _canvas.width / 2;
b.y = 10;
addChild(b);
stage.addEventListener(KeyboardEvent.KEY_DOWN, function(e:KeyboardEvent):void
{
if (e.keyCode == Keyboard.RIGHT) scroll(10);
else if (e.keyCode == Keyboard.LEFT) scroll(-10);
});
}
private function scroll(speed:int):void
{
/* Update offset. */
_xOffset -= speed;
/* Reset rect & point. */
_rect.width = _sourceWidth;
_rect.x = 0;
_point.x = 0;
/* Reached the end of the source image width. Copy full source onto buffer. */
if (_xOffset == (speed > -1 ? -(_sourceWidth + speed) : _sourceWidth - speed))
{
_xOffset = -speed;
_buffer.copyPixels(_source, _rect, _point);
}
/* Scroll the buffer by <speed> pixels. */
_buffer.scroll(-speed, 0);
/* Draw the scroll buffer onto the canvas. */
_canvas.copyPixels(_buffer, _rect, _point);
/* Update rect and point for copying scroll-in part. */
_rect.width = Math.abs(_xOffset);
/* Scrolls to left. */
if (speed > -1)
{
_rect.x = 0;
_point.x = _sourceWidth + _xOffset;
}
/* Scrolls to right. */
else
{
_rect.x = _sourceWidth - _xOffset;
_point.x = 0;
}
trace("rect.x: " + _rect.x + " point.x: " + _point.x);
/* Copy the scrolling-in part from source to the canvas. */
_canvas.copyPixels(_source, _rect, _point);
}
}
}
You can scroll left/right with cursor keys. The scrolling works fine only if the image wraps around once. If deciding to scroll in the opposite direction somewhere in between it will mess up the coordinates to copy the region from the scroll buffer. Basically this block here needs work:
/* Update rect and point for copying scroll-in part. */
_rect.width = Math.abs(_xOffset);
/* Scrolls to left. */
if (speed > -1)
{
_rect.x = 0;
_point.x = _sourceWidth + _xOffset;
}
/* Scrolls to right. */
else
{
_rect.x = _sourceWidth - _xOffset;
_point.x = 0;
}
... The rect.x and point.x need to be set differently here depending on the scroll direction but I don't know how without completely over-complicating the whole loop. Any hints or ideas for a better implementation would be welcome!
This question was asked a while ago, but since it has remained unanswered I'll add my two cents.
I just had to figure this out for a project I'm working on and came up with the following solution. The general idea is to draw the bitmap into the graphics of a display object and to use a matrix transform to control the x and y coords of a viewport over the bitmap data.
public class Example extends Sprite
{
private var vx:Number;
private var vy:Number;
private var coords:Point;
private var shape:Shape;
private var bitmap:Bitmap;
private var bmd:BitmapData;
public function Example()
{
vx = 1.5;
vy = -3.0;
coords = new Point();
shape = new Shape();
bitmap = new Image(); // image, in my case is an embedded image
bmd = bitmap.bitmapData;
addChild(shape);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event:Event):void
{
coords.x += vx;
coords.y += vy;
// this is important!
// without it, bitmap size constraints are reached and scrolling stops
coords.x %= bitmap.width;
coords.y %= bitmap.height;
shape.graphics.clear();
shape.graphics.beginBitmapFill(bmd, new Matrix(1, 0, 0, 1, coords.x, coords.y), true, true);
shape.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
shape.graphics.endFill();
}
}
Now you can just update the x & y values of the Point object to scroll around the bitmap. You can easily add velocity, acceleration and friction to the equation to make the scrolling more dynamic.

Shaking effect - Flash CS6 ActionScript3.0

This question is related to ActionScript 3.0 and Flash CS6
I am trying to make an object shake a bit in a certain for some seconds. I made it a "movieclip" and made this code:
import flash.events.TimerEvent;
var Machine_mc:Array = new Array();
var fl_machineshaking:Timer = new Timer(1000, 10);
fl_machineshaking.addEventListener (TimerEvent.TIMER, fl_shakemachine);
fl_machineshaking.start ();
function fl_shakemachine (event:TimerEvent):void {
for (var i = 0; i < 20; i++) {
Machine.x += Math.random() * 6 - 4;
Machine.y += Math.random() * 6 - 4;
}
}
When testing the movie I get multiple errors looking exactly like this one:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at Historieoppgave_fla::MainTimeline/fl_shakemachine()
at flash.utils::Timer/_timerDispatch()
at flash.utils::Timer/tick()
Also, the object doesnt shake, but it moves steadily upwards to the left a bit every tick.
To the point:
I wish to know how I stop the script after the object is not in the stage/scene anymore and also how to make it shake around, as I do not see what is wrong with my script, please help, thank you ^_^
AStupidNube brought up a great point about the original position. So adding that to shaking that should be a back and forth motion, so don't rely on random values that may or may not get you what you want. Shaking also has a dampening effect over time, so try something like this:
Link to working code
• http://wonderfl.net/c/eB1E - Event.ENTER_FRAME based
• http://wonderfl.net/c/hJJl - Timer Based
• http://wonderfl.net/c/chYC - Event.ENTER_FRAME based with extra randomness
**1 to 20 shaking items Timer Based code - see link above for ENTER_FRAME code••
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.geom.Point;
import flash.text.TextField;
import flash.utils.Timer;
public class testing extends Sprite {
private var shakeButton:Sprite;
private var graphic:Sprite;
private var shakerPos:Array;
private var shakers:Array;
private var numShakers:int = 20;
private var dir:int = 1;
private var displacement:Number = 10;
private var shakeTimer:Timer;
public function testing() {
this.shakers = new Array();
this.shakerPos = new Array();
this.addEventListener(Event.ADDED_TO_STAGE, this.init);
}
private function init(e:Event):void {
this.stage.frameRate = 30;
this.shakeTimer = new Timer(33, 20);
this.shakeTimer.addEventListener(TimerEvent.TIMER, this.shake);
this.graphics.beginFill(0x333333);
this.graphics.drawRect(0,0,this.stage.stageWidth, this.stage.stageHeight);
this.graphics.endFill();
this.createShakers();
this.shakeButton = this.createSpriteButton("Shake ");
this.addChild(this.shakeButton);
this.shakeButton.x = 10;
this.shakeButton.y = 10;
this.shakeButton.addEventListener(MouseEvent.CLICK, this.shakeCallback);
}
private function createSpriteButton(btnName:String):Sprite {
var sBtn:Sprite = new Sprite();
sBtn.name = btnName;
sBtn.graphics.beginFill(0xFFFFFF);
sBtn.graphics.drawRoundRect(0,0,80,20,5);
var sBtnTF:TextField = new TextField();
sBtn.addChild(sBtnTF);
sBtnTF.text = btnName;
sBtnTF.x = 5;
sBtnTF.y = 3;
sBtnTF.selectable = false;
sBtn.alpha = .5;
sBtn.addEventListener(MouseEvent.MOUSE_OVER, function(e:Event):void { sBtn.alpha = 1 });
sBtn.addEventListener(MouseEvent.MOUSE_OUT, function(e:Event):void { sBtn.alpha = .5 });
return sBtn;
}
private function createShakers():void {
var graphic:Sprite;
for(var i:int = 0;i < this.numShakers;i++) {
graphic = new Sprite();
this.addChild(graphic);
graphic.graphics.beginFill(0xFFFFFF);
graphic.graphics.drawRect(0,0,10,10);
graphic.graphics.endFill();
// add a 30 pixel margin for the graphic
graphic.x = (this.stage.stageWidth-60)*Math.random()+30;
graphic.y = (this.stage.stageWidth-60)*Math.random()+30;
this.shakers[i] = graphic;
this.shakerPos[i] = new Point(graphic.x, graphic.y);
}
}
private function shakeCallback(e:Event):void {
this.shakeTimer.reset();
this.shakeTimer.start();
}
private function shake(e:TimerEvent):void {
this.dir *= -1;
var dampening:Number = (20 - e.target.currentCount)/20;
for(var i:int = 0;i < this.numShakers;i++) {
this.shakers[i].x = this.shakerPos[i].x + Math.random()*10*dir*dampening;
this.shakers[i].y = this.shakerPos[i].y + Math.random()*10*dir*dampening;
}
}
}
}
Now this is a linear dampening, you can adjust as you see fit by squaring or cubing the values.
You have to remember the original start position and calculate the shake effect from that point. This is my shake effect for MovieClips. It dynamically adds 3 variables (startPosition, shakeTime, maxShakeAmount) to it. If you use classes, you would add them to your clips.
import flash.display.MovieClip;
import flash.geom.Point;
function shake(mc:MovieClip, frames:int = 10, maxShakeAmount:int = 30) : void
{
if (!mc._shakeTime || mc._shakeTime <= 0)
{
mc.startPosition = new Point(mc.x, mc.y);
mc._shakeTime = frames;
mc._maxShakeAmount = maxShakeAmount;
mc.addEventListener(Event.ENTER_FRAME, handleShakeEnterFrame);
}
else
{
mc.startPosition = new Point(mc.x, mc.y);
mc._shakeTime += frames;
mc._maxShakeAmount = maxShakeAmount;
}
}
function handleShakeEnterFrame(event:Event):void
{
var mc:MovieClip = MovieClip(event.currentTarget);
var shakeAmount:Number = Math.min(mc._maxShakeAmount, mc._shakeTime);
mc.x = mc.startPosition.x + (-shakeAmount / 2 + Math.random() * shakeAmount);
mc.y = mc.startPosition.y + (-shakeAmount / 2 + Math.random() * shakeAmount);
mc._shakeTime--;
if (mc._shakeTime <= 0)
{
mc._shakeTime = 0;
mc.removeEventListener(Event.ENTER_FRAME, handleShakeEnterFrame);
}
}
You can use it like this:
// shake for 100 frames, with max distance of 15px
this.shake(myMc, 100, 15);
BTW: In Flash, you should enable 'permit debugging' in your 'publish settings' to have more detailed errors. This also gives back the line numbers where your code is breaking.
update:
Code now with time / maximum distance separated.
Here is a forked version of the chosen answer, but is a bit more flexible in that it allows you to set the frequency as well. It's also based on time as opposed to frames so you can think in terms of time(ms) as opposed to frames when setting the duration and interval.
The usage is similar to the chosen answer :
shake (clipToShake, durationInMilliseconds, frequencyInMilliseconds, maxShakeRange);
This is just an example of what I meant by using a TimerEvent as opposed to a ENTER_FRAME. It also doesn't require adding dynamic variables to the MovieClips you are shaking to track time, shakeAmount, and starting position.
public function shake(shakeClip:MovieClip, duration:Number = 3000, frequency:Number = 30, distance:Number = 30):void
{
var shakes:int = duration / frequency;
var shakeTimer:Timer = new Timer(frequency, shakes);
var startX:Number = shakeClip.x;
var startY:Number = shakeClip.y;
var shakeUpdate:Function = function(e:TimerEvent):void
{
shakeClip.x = startX + ( -distance / 2 + Math.random() * distance);
shakeClip.y = startY + ( -distance / 2 + Math.random() * distance);
}
var shakeComplete:Function = function(e:TimerEvent):void
{
shakeClip.x = startX;
shakeClip.y = startY;
e.target.removeEventListener(TimerEvent.TIMER, shakeUpdate);
e.target.removeEventListener(TimerEvent.TIMER_COMPLETE, shakeComplete);
}
shakeTimer.addEventListener(TimerEvent.TIMER, shakeUpdate);
shakeTimer.addEventListener(TimerEvent.TIMER_COMPLETE, shakeComplete);
shakeTimer.start();
}
-4 <= Math.random() * 6 - 4 < 2
You add this offset to Machine.x 20 times, so chances for moving to the left is greater, than to the right.
It seems that you looking for something like this:
for each (var currentMachine:MovieClip in Machine_mc)
{
currentMachine.x += Math.random() * 6 - 3;
currentMachine.y += Math.random() * 6 - 3;
}

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);
}
}
}

Papervision3D; rotate child objects within camera view

It's my first time with Papervision3D and I have created a slide show of images that is skewed on the y-axis. Smallest photos on the left, and they increase in size going to the right. So they zoom from left to right, smallest to biggest.
I have a tooltip that pops up when you hovers over the photo, but the tooltip also gets skewed proportionate to the camera view (slanted). I want the tooltip's angle to be independent of the entire camera view.
Any idea how to rotate objects independent of the parent's camera angle?
Thanks!
my_obj = new DisplayObject3D();
my_plane = my_obj.addChild(new Plane(bla bla));
my_obj.lookAt(camera);
The 'lookAt' bit is what you need.
Why not draw the tooltips in 2d? You can get the on-screen position of the images and then just draw a regular Sprite like so:
package
{
import flash.display.Sprite;
import flash.text.TextField;
import org.papervision3d.events.InteractiveScene3DEvent;
import org.papervision3d.materials.ColorMaterial;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.objects.primitives.Plane;
import org.papervision3d.typography.Text3D;
import org.papervision3d.view.BasicView;
public class PVTest extends Sprite
{
private var world:BasicView;
private var text:Text3D;
private var text2d:TextField;
public function PVTest()
{
world = new BasicView(stage.width, stage.height, true, true);
var colorMat:ColorMaterial = new ColorMaterial();
colorMat.interactive = true;
var planeContainer:DisplayObject3D = new DisplayObject3D();
var plane:Plane;
for(var i:int = 0; i < 11; i++)
{
plane= new Plane(
colorMat,
100, 100,
10);
plane.x = (i * 105) - 500;
plane.addEventListener(InteractiveScene3DEvent.OBJECT_OVER, handleMouseOver);
plane.addEventListener(InteractiveScene3DEvent.OBJECT_OUT, handleMouseOut);
planeContainer.addChild(plane);
}
planeContainer.rotationY = 10;
world.scene.addChild(planeContainer);
world.camera.z = -500;
addChild(world);
world.startRendering();
}
private function handleMouseOver(event:InteractiveScene3DEvent):void
{
var plane:Plane = Plane(event.displayObject3D);
plane.calculateScreenCoords(world.camera);
const OFFSET_X:int = -20;
const OFFSET_Y:int = 30;
text2d = new TextField();
text2d.text = "toolTip";
text2d.x = plane.screen.x + (stage.width/2) + OFFSET_X;
text2d.y = plane.screen.y + (stage.height/2) + OFFSET_Y;
addChild(text2d);
}
private function handleMouseOut(event:InteractiveScene3DEvent):void
{
removeChild(text2d);
}
}
}
Even for this example you'd have to offset the y position of the tooltip based on the objects scale but it may be easier than working out the rotations and is the best way to get a consistent looking result.