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;
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With