Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TRect.Intersect and TRect.IntersectsWith Inconsistencies

I am hoping that I am confused in some way. I am getting some inconsistent behavior with TRect.Intersect and TRect.IntersectsWith. Here is some code that demonstrates the problem.

program RectCheck;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Types,
  Vcl.Dialogs;

var
  rect1: TRect;
  rect2: TRect;
  combinedRect: TRect;
begin
  Rect1 := Rect(0,0,200,101);
  Rect2 := Rect(0,100,200,200);
  if Rect1.IntersectsWith(Rect2) then
  begin
    // We have interesected, get the combined rect
    combinedRect := TRect.Intersect(Rect1, Rect2);
    if not combinedRect.IsEmpty then
      ShowMessage(Format('Our new rect (%d, %d), (%d, %d)',
          [combinedRect.Left, combinedRect.Top, combinedRect.Right, combinedRect.Bottom]))
    else
      raise Exception.Create('They were supposed to intersect!');
  end;

  Rect1 := Rect(0,0,200,100);
  Rect2 := Rect(0,100,200,200);
  if Rect1.IntersectsWith(Rect2) then
  begin
    // We have interesected, get the combined rect
    combinedRect := TRect.Intersect(Rect1, Rect2);

    if not combinedRect.IsEmpty then
      ShowMessage(Format('Our new rect (%d, %d), (%d, %d)',
          [combinedRect.Left, combinedRect.Top, combinedRect.Right, combinedRect.Bottom]))
    else
      raise Exception.Create('They were supposed to intersect!');
  end;
end.

The second exception is raised. TRect.IntersectsWith indicates that the rects intersect but when I call TRect.Intersect to get the new intersected rect then it returns an empty rect.

The code in IntersectsWith (which isn't written very clearly) is returning true in the second case because Self.BottomRight.Y = R.TopLeft.Y (100).

function TRect.IntersectsWith(const R: TRect): Boolean;
begin
  Result := not ( (Self.BottomRight.X < R.TopLeft.X) or
                  (Self.BottomRight.Y < R.TopLeft.Y) or
                  (R.BottomRight.X < Self.TopLeft.X) or
                  (R.BottomRight.Y < Self.TopLeft.Y) );
end;

The problem is that IsRectEmpty which is called by Intersect checks to see if either the top and bottom of the rect or the left and right of the rect have the same values and when that passes Intersect sets the result to an empty rect.

function IsRectEmpty(const Rect: TRect): Boolean;
begin
  Result := (Rect.Right <= Rect.Left) or (Rect.Bottom <= Rect.Top);
end;

Is this the expected behavior and if not what should be changed. My understanding is that TRects exclude the bottom and right "edges" and if that's the case shouldn't TRect.IntersectsWith look something like this?

function TRect.IntersectsWith(const R: TRect): Boolean;
begin
  Result := not ( (Self.BottomRight.X <= R.TopLeft.X) or
                  (Self.BottomRight.Y <= R.TopLeft.Y) or
                  (R.BottomRight.X <= Self.TopLeft.X) or
                  (R.BottomRight.Y <= Self.TopLeft.Y) );
end;
like image 602
Graymatter Avatar asked Sep 19 '14 00:09

Graymatter


1 Answers

It's a bug; this cannot be the expected behavior. In the current implementation, RTL thinks two empty rects can intersect (e.g. (0,0,0,0), (0,0,0,0), or one non-empty rect with an empty one), which does not make any sense.

Assert(Rect(0, 0, 0, 0).IntersectsWith(Rect(0, 0, 0, 0)));

The above assertion does not fail.

Also, it does not work in accordance with the Windows api. The below assertion fails: winapi thinks (0,0,200,100) and (0,100,200,200) do not intersect.

Assert(winapi.windows.IntersectRect(OutRect, Rect(0,0,200,100), Rect(0,100,200,200)));

The overload of System.Types.IntersectRect() that returns a boolean is equally broken.

like image 95
Sertac Akyuz Avatar answered Nov 20 '22 06:11

Sertac Akyuz