views:

779

answers:

3

For the life of me I can't make sense why this code produces the following output...

I think there is a bug or something when using List and lambda if the type exposes a Rectangle property and you use the Contains method of the rectangle object...and explicit iteration proves true while the List Find method fails....

Oneway

Code

public GridSquare WorldToKeyPadSub(Point location)
    {
        location = _Map.WorldToClient(location);
        GridSquare gs = this.Find(x => x.Rectangle.Contains(location));
        GridSquare kp = gs.Find(x => x.Rectangle.Contains(location));
        List<GridSquare> list = kp.FindAll(x=>x.Rectangle.Contains(location));
        u.dp(list.Count);
        GridSquare sub = kp.Find(x => x.Rectangle.Contains(location));

        if (sub == null)
        {
            u.dp("Location to look for " + location);
            u.dp("Found Location in grid square " + gs.ToString());
            u.dp("grid square bounds " + gs.Rectangle.ToString());
            u.dp("Found Location in Keypad " + kp.ToString());
            u.dp("key pad bounds " + kp.Rectangle.ToString());
            u.dp("Sub Key Pads Print All sub keys in this grid.keypad");
            foreach (GridSquare t in kp)
            {
                u.dp(t.ToString() + "  " + t.Rectangle.ToString());                   

            }
            u.dp("Sub Key Pads Print Explicit Finds");
            foreach (GridSquare t in kp)
            {
                if (location.X >= t.Location.X
                    && location.Y >= t.Location.Y
                    && location.X <= t.Location.X + t.Rectangle.Width
                    && location.Y <= t.Location.Y + t.Rectangle.Height)
                {
                    u.dp(true);
                    u.dp(t.ToString() + "  " + t.Rectangle.ToString());
                }

            }
        }
        return sub;
    }

This produces the following output...

Notice how the explicit Rectangle (aka Manual method) finds the grid squares that contain the location....the in house GDI version fails....

Location to look for {X=1476,Y=1716}
Found Location in grid square GS: 14.3.0.0
grid square bounds {X=1398,Y=1650,Width=100,Height=100}
Found Location in Keypad GS: 14.3.6.0
key pad bounds {X=1465,Y=1683,Width=33,Height=34}
Sub Key Pads Print All sub keys in this grid.keypad
GS: 14.3.6.7  {X=1465,Y=1683,Width=11,Height=11}
GS: 14.3.6.8  {X=1476,Y=1683,Width=11,Height=11}
GS: 14.3.6.9  {X=1487,Y=1683,Width=11,Height=11}
GS: 14.3.6.4  {X=1465,Y=1694,Width=11,Height=11}
GS: 14.3.6.5  {X=1476,Y=1694,Width=11,Height=11}
GS: 14.3.6.6  {X=1487,Y=1694,Width=11,Height=11}
GS: 14.3.6.1  {X=1465,Y=1705,Width=11,Height=11}
GS: 14.3.6.2  {X=1476,Y=1705,Width=11,Height=11}
GS: 14.3.6.3  {X=1487,Y=1705,Width=11,Height=11}
Sub Key Pads Print Explicit Finds
True
GS: 14.3.6.1  {X=1465,Y=1705,Width=11,Height=11}
True
GS: 14.3.6.2  {X=1476,Y=1705,Width=11,Height=11}
A first chance exception of type 'System.NullReferenceException'
+4  A: 

Rectangle.Contains(Point) is exclusive (strictly lesser-than) on upper bounds of the rectangle.

For exemple, the equivalent check performed by Rectangle.Contains(Point) in your context would be :

foreach (GridSquare t in kp) 
{ 
    if (location.X >= t.Location.X 
        && location.Y >= t.Location.Y 
        && location.X < t.Location.X + t.Rectangle.Width   // < instead of <=
        && location.Y < t.Location.Y + t.Rectangle.Height) // < instead of <=
    { 
        u.dp(true); 
        u.dp(t.ToString() + "  " + t.Rectangle.ToString()); 
    } 

} 

As you can see, it verifies upper bounds as a strictly lesser than, instead of a lesser than or equal, the difference between your method and Rectangle.Contains(Point) lies there.

The location passed in your exemple is {X=1476,Y=1716}, which is when passed to the Contains of those rectangles:

GS: 14.3.6.1  {X=1465,Y=1705,Width=11,Height=11}  
GS: 14.3.6.2  {X=1476,Y=1705,Width=11,Height=11}  

will return false, when yours will return true.

That is why the kp.Find(x => x.Rectangle.Contains(location)); returns null, but your manual checks are returning true.

Dynami Le Savard
I would like to point out this is illegal... I suggest using the Mono source as reference.
Dykam
I suppose your point could be possible, however I doubt linking to Mono's Cairo will do any good to someone who doesn't use it.
Dynami Le Savard
A: 

I see two things you can check:

  1. Your explicit check is inclusive - less-than-and-equals, greater-than-and-equals. If Rectangle.Contains is exclusive, then both the points your explicit check found would be omitted.

  2. Are you sure that x.Location.X and .Y are always the same as x.Rectangle.X and .Y?

Bruce
A: 

This is what I found out...

Consider for a moment a rectangle that is defined 0,0,100,100...

One would assume that that point 100,100 is inside of that rectangle, but that is not the case at all...

Rectangle.Contains is exclusive of the bounds...in other words it would only return true for all points 0,0 to 99,99 in a rectangle defined as 0,0,100,100...

The trouble I was having is that when you use GDI to draw that rectangle...the pixels are drawn biased down and right...

The net effect is that Rectangle.Contains is inclusive for the top and left leg of a rectangle, and exclusive for the bottom and right leg of the rectangle...and from a graphic standpoint..and you could zoom in to the pixel level during a hit test with a mouse...

The cursor might appear to be just inside of the boundary of rectangle right and down...yet the hit test is return false because of exclusive nature of Rectangle.Contains for the right and bottom legs...

SW

Oneway