Home > 3D > Calculating the Projected Bounds using Utils3D.projectVector()

Calculating the Projected Bounds using Utils3D.projectVector()

December 15th, 2009 Leave a comment Go to comments

projectedboundsIf you are playing with objects in 3D and want their projected bounds, there’s a neat flash functionality for that – Utils3D.projectVector(). However it may not be that apparent how to use it. Here I’m showing a small helper function that does the trick.
The projectVector() method actually takes a Matrix3D for a parameter. Note that here you should pass in the Matrix3D of the object in question combined with the perspective projection of its parent. This is not as trivial as it seems, as you’d need to also translate by the projectionCenter and the focalLength:

16
17
18
19
20
// Setup the matrix
 var centerX:Number = projection.projectionCenter.x;
 var centerY:Number = projection.projectionCenter.y;
 matrix.appendTranslation(-centerX, -centerY, projection.focalLength);
 matrix.append(projection.toMatrix3D());

After that just pass in the matrix and the point to be projected to Utils3D.projectVector(). Then translate the result by centerX and centerY.
Here’s the helper function source, it takes in bounds, the matrix of the object and the perspective projection and returns the projected 2D bounds:

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public static function projectBounds(bounds:Rectangle,
 matrix:Matrix3D,
 projection:PerspectiveProjection):Rectangle
 {
 // Setup the matrix
 var centerX:Number = projection.projectionCenter.x;
 var centerY:Number = projection.projectionCenter.y;
 matrix.appendTranslation(-centerX, -centerY, projection.focalLength);
 matrix.append(projection.toMatrix3D());
 
// Project the corner points
 var pt1:Vector3D = new Vector3D(bounds.left, bounds.top, 0);
 var pt2:Vector3D = new Vector3D(bounds.right, bounds.top, 0)
 var pt3:Vector3D = new Vector3D(bounds.left, bounds.bottom, 0);
 var pt4:Vector3D = new Vector3D(bounds.right, bounds.bottom, 0);
 pt1 = Utils3D.projectVector(matrix, pt1);
 pt2 = Utils3D.projectVector(matrix, pt2);
 pt3 = Utils3D.projectVector(matrix, pt3);
 pt4 = Utils3D.projectVector(matrix, pt4);
 
// Find the bounding box in 2D
 var maxX:Number = Math.max(Math.max(pt1.x, pt2.x), Math.max(pt3.x, pt4.x));
 var minX:Number = Math.min(Math.min(pt1.x, pt2.x), Math.min(pt3.x, pt4.x));
 var maxY:Number = Math.max(Math.max(pt1.y, pt2.y), Math.max(pt3.y, pt4.y));
 var minY:Number = Math.min(Math.min(pt1.y, pt2.y), Math.min(pt3.y, pt4.y));
 
// Add back the projection center
 bounds.x = minX + centerX;
 bounds.y = minY + centerY;
 bounds.width = maxX - minX;
 bounds.height = maxY - minY;
 return bounds;
 }

The full source is here projectedbounds.mxml.

  1. November 24th, 2010 at 04:21 | #1

    Hi Evtim,

    I am trying to use this in order to measure the width of my layout as I’m transforming it’s elements in 3D.

    If you look at http://www.pixelbox.net/demos/customLayout3/CustomLayout3.html you can see the width is slightly off (view source enabled). The only thing I can think of is that the perspective is wrong in measure().

    Would you be able to have a look and see if it’s something obvious that I am doing wrong?

    Thanks

    Rob

    • Evtim
      November 27th, 2010 at 19:43 | #2

      Hi Rob,
      I took a quick look – looking good! I think the issue you pointed out is due to the way you initialize the projection. If your application is using the default stage projection (not setting any custom projection or using the maintainProjectionCenter property), then you’d need to use that in your calculations. I think you’d also need to convert the bounds to global coordinates (because you’re projecting relative to the stage) and once you get the projected bounds, convert them back to your container’s coordinates.

      On the other hand, if you plan to use the “maintainProjectinoCenter” property on your container, then things will be more tricky – now Flex will keep the projection centered to the container, which means it will re-calculate and update the container’s projection at updateDisplayList time by setting up the center to be the container’s center. In measure(), you’d need to calculate width and height such that when used as center of the projection the bounds of the projected elements end up being the same width and height… I can’t think of anything smart here off the top of my head, so probably I’ll do some iterations and see if things converge. Hope this helps.

  2. November 28th, 2010 at 03:45 | #3

    Thanks Evtim!

    I assumed it was something to do with that. To be honest this is my first venture into 3D and perspectives so I’m not totally sure of what I am doing, but understand what your saying about the center changing. I guess setting it to something global should work, so will have a look at that next.

    It would be great to get your feedback on my posts about layout, incase I have got anything wrong :)

    http://www.pixelbox.net/?p=440

    Thanks again

    Rob

  3. shenshouer
    December 15th, 2010 at 22:00 | #4

    how could i get this example code?
    when i view the application and right click to view the source code

    report 404?
    can you send me the code to my gmail? thanks

  4. shenshouer
    December 15th, 2010 at 22:01 | #7

    gmail:shenshouer51@gmail.com

  5. Bart
    May 11th, 2011 at 09:22 | #8

    Thanks, you saved the day.

    Note for others: this works by ‘mimicking’ Flash native 3D on DisplayObjects (so you have a actionscript Vector3D that matches with the way Flash transforms DisplayObjects. I took me a bit to strip your function to just single vectors, so here a stripped-down snippet:

    //the projection (grab from your perspective container)
    var pp:PerspectiveProjection;

    //a 3D coordinate in world space (= same space as the perspective container)
    var world:Vector3D;

    //the magic bit
    var mat:Matrix3D = new Matrix3D();
    mat.appendTranslation(-pp.projectionCenter.x, -pp.projectionCenter.y, pp.focalLength);
    mat.append(pp.toMatrix3D());

    //project your world vectors so screen has the 2D coordinate in its x/y
    var screen:Vector3D = Utils3D.projectVector(mat, world);

  1. No trackbacks yet.