views:

353

answers:

1

The following is entirely a math question.

As we know, PerspectiveProjection delivers perspective transformations in 3D represented by the interdependent values of fieldOfView and focalLength according to the following formula:

focalLength = stageWidth/2 * (cos(fieldOfView/2) / sin(fieldOfView/2)

Illustration by Bjørn Gunnar Staal

Q: How to get the visible on-screen size of the DisplayObject (Cube on the above-linked image) to which PerspectiveProjection has been applied?

A more thorough description and illustrative code on the issue in ActionScript 3 lacks functionality for visible bounds of DisplayObject.

+2  A: 

I think I'm missing the point.

I did a simple test in the ide, put 3 clips together like so: 3d size

The width and height report the size of the visible 3d area, as you can see in the property inspector as well. You can access just as simple as

trace(cube.width + ' by ' + cube.height);

at runtime or with a bit of jsfl:

var doc = fl.getDocumentDOM();
fl.outputPanel.clear();
fl.trace(doc.selection[0].width + ' by ' + doc.selection[0].height);

for one selected clip at authortime, within the IDE.

I did a similar test with a bit of actionscript:

//draw faces
var front:Sprite = drawFace();
var back:Sprite = drawFace();
var left:Sprite = drawFace();
var right:Sprite = drawFace();
var top:Sprite = drawFace();
var bottom:Sprite = drawFace();
//transform faces
front.z  = -50;
back.z   =  50;
left.x   = -50;
right.x  =  50;
top.y    = -50;
bottom.y =  50;
left.rotationY = right.rotationY = 90;
top.rotationX = bottom.rotationX = 90;
//putem them all together
var cube:Sprite = new Sprite();
cube.addChild(front);
cube.addChild(back);
cube.addChild(left);
cube.addChild(right);
cube.addChild(top);
cube.addChild(bottom);
addChild(cube);
cube.x = stage.stageWidth * .5;
cube.y = stage.stageHeight* .5;
cube.z = -100;
cube.rotationX = cube.rotationY = 30;

trace(cube.width + ' by ' + cube.height);
trace(cube.getBounds(this));
trace(cube.transform.pixelBounds);
this.addEventListener(Event.ENTER_FRAME, loop);
function loop(event:Event):void {
    cube.rotationY += 3;
    var b:Rectangle = cube.getBounds(this);
    graphics.clear();
    graphics.lineStyle(1,0x009900);
    graphics.drawRect(b.x,b.y,b.width,b.height);
}

function drawFace():Sprite{
    var s:Sprite = new Sprite();
    s.graphics.beginFill(0x000099,.5);
    s.graphics.drawRect(-50,-50,100,100);
    s.graphics.endFill();
    return s;
}

Here is a modified sample from Programming Actionscript 3.0

package {  
    import flash.display.*  
    import flash.events.*;  
    import flash.utils.getTimer;  
    import flash.geom.*;

    public class Triangles extends Sprite {  
        var x1:Number = -100,y1:Number = -100,z1:Number = 0,t1:Number = 0;  
        var x2:Number = 100,y2:Number = -100,z2:Number = 0,t2:Number = 0;  
        var x3:Number = 100,y3:Number = 100,z3:Number = 0,t3:Number = 0;  
        var x4:Number = -100,y4:Number = 100,z4:Number = 0,t4:Number = 0;  
        var v1 = new Vector3D(-100,-100,0,0);
        var v2 = new Vector3D(100,-100,0,0);
        var v3 = new Vector3D(100,100,0,0);
        var v4 = new Vector3D(-100,100,0,0);
        var focalLength:Number = 200;   
        var indices:Vector.<int>;  

        var container:Sprite;  

        var bitmapData:BitmapData; // texture  
        var imageLoader:ImageLoader;  

        public function Triangles():void {  
            indices =  new Vector.<int>();  
            indices.push(0,1,3, 1,2,3);  

            container = new Sprite(); // container to draw triangles in  
            container.x = 200;  
            container.y = 200;  
            addChild(container);  

            imageLoader = new ImageLoader("head.jpg");  
            imageLoader.addEventListener(Event.COMPLETE, onImageLoaded);  
        }  

        function onImageLoaded(event:Event):void {  
            bitmapData = imageLoader.bitmap.bitmapData; 
            addEventListener(Event.ENTER_FRAME, rotatePlane);  
        }  
        function rotatePlane(event:Event):void {  
            var ticker = getTimer()/1600;  
            z2 = z3 = -(z1 = z4 = 100*Math.sin(ticker));  
            x2 = x3 = -(x1 = x4 = 100*Math.cos(ticker));  

            v2.z = v3.z = -(v1.z = v4.z = 100*Math.sin(ticker));
            v2.x = v3.x = -(v1.x = v4.x = 100*Math.cos(ticker));


            t1 = focalLength/(focalLength + z1);  
            t2 = focalLength/(focalLength + z2);  
            t3 = focalLength/(focalLength + z3);  
            t4 = focalLength/(focalLength + z4);  

            v1.w = focalLength/(focalLength + v1.z);  
            v2.w = focalLength/(focalLength + v2.z);  
            v3.w = focalLength/(focalLength + v3.z);  
            v4.w = focalLength/(focalLength + v4.z);  

            // determine triangle vertices based on t values  
            var vertices:Vector.<Number> = new Vector.<Number>();  
            //vertices.push(x1*t1,y1*t1, x2*t2,y2*t2, x3*t3,y3*t3, x4*t4,y4*t4);  

            vertices.push(v1.x*v1.w,v1.y*v1.w, v2.x*v2.w,v2.y*v2.w, v3.x*v3.w,v3.y*v3.w, v4.x*v4.w,v4.y*v4.w);  
            var uvtData:Vector.<Number> = new Vector.<Number>();  
            uvtData.push(0,0,v1.w, 1,0,v2.w, 1,1,v3.w, 0,1,v4.w);  

            // draw  
            container.graphics.clear();  
            container.graphics.beginBitmapFill(bitmapData);  
            container.graphics.drawTriangles(vertices, indices, uvtData);  
            var b:Rectangle = container.transform.pixelBounds;
            graphics.clear();
            graphics.lineStyle(1,0x009900);
            graphics.drawRect(b.x,b.y,b.width,b.height);
        }  
    }  
} 

pixelBounds work well here, but it's a simple plane. pixelBounds wouldn't work in the previous example, but getBounds() seems to work fine.

George Profenza
The question was about perspective projection and math related esp. in regard to visible size of the elements. Both getBounds and pixelBounds methods return a value that doesn't consider transformations.
Ain
@Ain I've updated some code, getBounds() seems to work fine on a cube, pixelBounds reports the untranformed bounds of a clip. There is a simple test using drawTriangles() and it seems to work fine. Do you have a simple example or can you recreate minimal version of your issue ?
George Profenza
@George see http://tekkie.flashbit.net/flash/as/actionscript-3-lacks-functionality-for-visible-bounds-of-displayobject for a codepiece. I couldn't past it here so I produced a detailed write-up.
Ain
@Ain You're numbers are different to mine. I'm not talking about the stage size, but about the evident difference between the two bounding boxes. I've drawn visibleBounds in red and bounds in green here: http://i984.photobucket.com/albums/ae330/orgicus/Picture8.png Both look close. Is the stage size I'm using, or something else ? What Flash Player version do you use ?
George Profenza
Perspective Projection is calculated given the stage size, therefore the visible bounds in the above-mentioned code vary.Visible bounds are what you see on screen meaning that they are what's required in many occasions, e.g. layout calculations and if you measure by hand, they are correct. The concern is, Adobe should introduce a method to retrieve these bounds without doing any extra math or manipulations.
Ain