Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Bug in Rectangle.union?

Explorer ,
Apr 30, 2014 Apr 30, 2014

Is is just me, or is this wrong.  I'm guessing there's some sort of zero-area-based optimization that's causing rectangles to be improperly unioned.

import flash.geom.Rectangle;
 
var r1:Rectangle = new Rectangle( 0, 0, 40, 23 ); //non-zero area plus
var r2:Rectangle = new Rectangle( 0, 0, 100, 0 ); //zero-area but non-zero height
trace( r1.union( r2 ) ); //(x=0, y=0, w=40, h=23) WRONG!!! no-effect
 
 
var r3:Rectangle = new Rectangle( 0, 0, 40, 23 ); //non-zero area plus
var r4:Rectangle = new Rectangle( 0, 0, 0, 100 ); //zero-area but non-zero height
trace( r3.union( r4 ) ); //(x=0, y=0, w=40, h=23)  WRONG!!! no-effect
 
 
var r5:Rectangle = new Rectangle( 0, 0, 40, 0 ); //zero-area but non-zero height plus
var r6:Rectangle = new Rectangle( 0, 0, 0, 100 ); //zero-area but non-zero height
trace( r5.union( r6 ) ); //(x=0, y=0, w=0, h=100)  WHAT??? height is added?? and width is removed??

It sure doesn't fit, for example, this Java reference implementation:

/**
      * Computes the union of this <code>Rectangle</code> with the
      * specified <code>Rectangle</code>. Returns a new
      * <code>Rectangle</code> that
      * represents the union of the two rectangles
      * @Param r the specified <code>Rectangle</code>
      * @Return the smallest <code>Rectangle</code> containing both
      * the specified <code>Rectangle</code> and this
      * <code>Rectangle</code>.
      */

     public Rectangle union(Rectangle r) {

         int x1 = Math.min(x, r.x);

         int x2 = Math.max(x + width, r.x + r.width);

         int y1 = Math.min(y, r.y);

         int y2 = Math.max(y + height, r.y + r.height);

         return new Rectangle (x1, y1, x2 - x1, y2 - y1);

     }

That's how I would expect a union operation to work.   Add this to the previous chunk of AS3 to see the difference:

trace( union( r1, r2 ) ); //(x=0, y=0, w=100, h=23) //CORRECT
trace( union( r2, r3 ) ); //(x=0, y=0, w=100, h=23) //CORRECT
trace( union( r4, r5 ) ); //(x=0, y=0, w=40, h=100) //CORRECT
  
function union(r0:Rectangle, r:Rectangle ):Rectangle
{
   var x1 = Math.min(r0.x, r.x);
   var x2 = Math.max(r0.x + r0.width, r.x + r.width);
   var y1 = Math.min(r0.y, r.y);
   var y2 = Math.max(r0.y + r0.height, r.y + r.height);
   return new Rectangle (x1, y1, x2 - x1, y2 - y1);
}

This bug was completely messing up my Flex-like framework and took forever to track down.  I would have never expected such a core and basic math operation like Rectangle.union to be failing.  This was causing my autoHeight calculations for custom GUIControls to fail.

The union is supposed to be the smallest rectangle containing both rectangles.  If either of the rectangles has zero width and/or zero height, it is a point or a line, but the final rectangle must still contain it.  As you can see, Rectangle.union is not working correctly.

TOPICS
ActionScript
905
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Apr 30, 2014 Apr 30, 2014

Per the AS3 documentation:

Note: The union() method ignores rectangles with 0 as the height or width value, such as: var rect2:Rectangle = new Rectangle(300,300,50,0);

So it is probably a case where you will get unexpected results

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
May 01, 2014 May 01, 2014

It's not a rectangle union operation, plain and simple.  Adding a note to the documentation doesn't make it correctly functioning.

Note: The union() method ignores rectangles with 0 as the height or width value, such as: var rect2:Rectangle = new Rectangle(300,300,50,0);

Why the special case for zero?  Rectangles can have any x and y point, and any width or height, positive or negative.  They are ulimately defined by four points, regardless of whether those four points create a non-zero area, and a rectangle union operation is supposed to be the smallest rectangle containing all points on both unioned rectangles.

You also must not have noticed the inconconsistancy between the three examples I posted.  The last two show that it uses the first rectangle in one case, but the second rectangle in the other case.

var r3:Rectangle = new Rectangle( 0, 0, 40, 23 );
var r4:Rectangle = new Rectangle( 0, 0, 0, 100 ); //ADDING ZERO WIDTH RECTANGLE
trace( r3.union( r4 ) ); //(x=0, y=0, w=40, h=23)  //USES FIRST RECTANGLE


var r5:Rectangle = new Rectangle( 0, 0, 40, 0 );
var r6:Rectangle = new Rectangle( 0, 0, 0, 100 ); //ADDING ZERO WIDTH RECTANGLE (same as before)
trace( r5.union( r6 ) ); //(x=0, y=0, w=0, h=100)  //USES SECOND RECTANGLE (when first one has zero area?)


In the second case, it clearly is not ignoring one of the rectangles, despite both having zero area, because the instance upon which it's operating is having its value replaced by the parameter passed to it.  If it was simply ignoring the passed parameter, it would return the original rectangle (0,0,40,0), and if it was ignoring both, it would return an empty rectangle, but it's not.  So something is very wrong with this implementation.

The note appearing in the documentation is interesting, because there is no such note for other operations like Rectangle.intersection.

var r7:Rectangle = new Rectangle( 0, 0, 40, 40 ); //40x40

var r8:Rectangle = new Rectangle( 10, 10, 0, 1 ); //zero-area but non-zero height

trace("intersects: " + r7.intersects( r8 ) ); //intersects: false

trace("contains: " + r7.containsRect( r8 ) ); //contains: true


That is logically inconsistant for one rectangle to completely contain another rectangle but not intersect it, and this operation is not talking about edge intersection, but whether they overlap: "Similarly, you can use the intersects() method to find out whether the bounding rectangles of two display objects overlap."

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
May 01, 2014 May 01, 2014

I saw all of your cases which is why I said "So it is probably a case where you will get unexpected results"

The bottom line ius, the developers have indicated how the processing behaves.  If you have an issue with how they process a union then offer it as a suggestion to improve the program.

Adobe - Wishlist & Bug Report

-----------------------------

https://www.adobe.com/cfusion/mmform/index.cfm?name=wishform

(I do not know if that is still a valid submittal resource, though it does link to the expected page)

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
May 01, 2014 May 01, 2014

The bottom line is it's incorrectly functioning.  As if all that wasn't enough to prove it's incorrectly functioning, there is a loss of commutative consistancy.
Rect1.Union(Rect2) should be equivalent to Rect2.Union(Rect1), but that is not occuring in the AS3 rectatngle implementation:

var r5:Rectangle = new Rectangle( 0, 0, 40, 0 ); //zero-area but non-zero height plus

var r6:Rectangle = new Rectangle( 0, 0, 0, 100 ); //zero-area but non-zero height

trace( r5.union( r6 ) ); //(x=0, y=0, w=0, h=100)

trace( r6.union( r5 ) ); //(x=0, y=0, w=40, h=0) loss of commutative consistancy, r6.union(r5) returns different result than r5.union(r6).

That, if anything, is the nail-in-the-coffin proof that whatever these developers chose to do is wrong.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
May 01, 2014 May 01, 2014

It will probably be a waste of effort if you restrict your information to this posting.  I provided a link for you to use if you want to do more than complain to your fellow users.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
May 01, 2014 May 01, 2014

I'm not just complaining, and certainly not for the sake of complaining.  Firstly, I'm posting here to raise awareness of these behavior issues, so that if anyone else runs into this strange behavior, they'll be able to see a discussion about its effects and logical inconsistancies.  Secondly, I'm trying to have a discussion about the issue, to see if there could possibly be any reason why they chose to implement it this way, and what the impact of possibly changing the behavior would be.  That's why I started my original post with: "Is is just me, or is this wrong?"  Might there be someone who depends on this behavior?  Even if there were a benefit or usefulness to discarding zero-area intersections and unions, the loss of commutativity tells me there is probably something not quite right.

It's just not helpful when people try to downplay the issue by pointing out a note in the documentation or brushing it off like the issue doesn't exist or is ok just because.  Also, is the wishform the best place for this (I usually go there for Flash Professional issues)?  This seems like the kind of thing that would be better off in the Flash Player bug issue management system.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
May 01, 2014 May 01, 2014

It keeps getting worse.  The union is not even computing proper values.

import flash.geom.Rectangle;

var r0:Rectangle = new Rectangle( 0, 0, -300, 100 );

var r1:Rectangle = new Rectangle( 0, 0, 300, 100 );

trace( r0.union( r1 ) ); //(x=0, y=0, w=300, h=100) //WRONG!! should be (x=-300, y=0, w=600, h=100)

trace( unionRects( r0, r1 ) ); //(x=-300, y=0, w=600, h=100) //CORRECT

var r0:Rectangle = new Rectangle( 0, 0, -400, 100 );

var r1:Rectangle = new Rectangle( 0, 0, -300, 100 );

trace( r0.union( r1 ) ); //(x=0, y=0, w=-300, h=100) //WRONG!! should be (x=-400, y=0, w=400, h=100)

trace( r1.union( r0 ) ); //(x=0, y=0, w=-400, h=100) //WRONG and inconsistant with loss of commutativity!!  should be (x=-400, y=0, w=400, h=100)

trace( unionRects( r0, r1 ) ); //(x=-400, y=0, w=400, h=100) //CORRECT


//Here is a proper implementation

function unionRects( r0:Rectangle, r1:Rectangle ):Rectangle

{

    var x0:Number = Math.min( r0.left, r0.right, r1.left, r1.right ); //left or right of either rectangle could be minimum, depending on whether either rectangle's width is negative, since left=x and is not necessarily the "left edge"

    var x1:Number = Math.max( r0.left, r0.right, r1.left, r1.right ); //left or right of either rectangle could be maximum, depending on whether either rectangle's width is negative, since right=x+width and is not necessarily the "right edge"

    var y0:Number = Math.min( r0.top, r0.bottom, r1.top, r1.bottom ); //top or bottom of either rectangle couuld be minimum, depending on whether either rectangle's height is negative, since top=y and is not necessarily the "top edge"

    var y1:Number = Math.max( r0.top, r0.bottom, r1.top, r1.bottom ); //top or bottom of either rectangle couuld be maximum, depending on whether either rectangle's height is negative, since bottom=y+height and is not necessarily the "bottom edge"

    return new Rectangle( x0, y0, x1 - x0, y1 - y0 );

}

There are no notes in the documentation as of right now that say it doesn't handle negative widths and heights in addition to not handling zero widths and heights, so beware.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
May 01, 2014 May 01, 2014
LATEST

An abstract geometrical rectangle with negative dimensions is a total absurd. Nothing in reality has negative width or height. Objects either do have dimensions or they don't; and when they do this is just enough to use positive numbers to describe them while when there are no dimensions - zero is perfect. Except, maybe, objects derived from antimatter.

What is really a negative width? Negative relative to what?

Unlike x/y that can be presented in a relative coordinate space everyone can agree on - what would be the reference point at which object geometry enters this mysterious StarTrekkesque CERN collider realm of anti-dimensions?

As a matter of fact even when AS3 DisplayObjects dimensions are set to negative values, their width and height retain positive values because, well, they do have dimensions.

If I had time to complain about where sun rises, I would prefer this code:

var r0:Rectangle = new Rectangle( 0, 0, -400, 100 );

var r1:Rectangle = new Rectangle( 0, 0, -300, 100 );

trace(r0.union(r1));

to output this value:

(x=0, y=0, w=0, h=0)

Because this:

var r0:Rectangle = new Rectangle( 0, 0, -400, 100 );

var r1:Rectangle = new Rectangle( 0, 0, 100, 100 );

trace(r0.union(r1));

outputs what my conformist being expects:

(x=0, y=0, w=100, h=100) - all good.

Granted AS3 Rectangle class instance can be used as a descriptor of certain contexts where width/height properties negative values make sense (used not as actual dimensions). But, again, this is a convenience icing on the flexibility cake rather than something organic to the 2D space.

So, long live AS3 Rectangle.union() the way it is now!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines