2014-09-19 8 views
9

Mam nadzieję, że jestem zdezorientowany w jakiś sposób. Otrzymuję niespójne zachowanie z TRect.Intersect i TRect.IntersectsWith. Oto kod, który demonstruje problem.TRekt.Intersect and TRect.IntersectsWith Inconsistencies

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. 

Drugi wyjątek został zgłoszony. TRect.IntersectsWith wskazuje, że rects przecinają się, ale kiedy zadzwonię pod numer TRect.Intersect, aby uzyskać nowy przecięty prostokąt, to zwraca on pusty rect.

Kod w PrzecięcieWith (który nie jest napisany bardzo wyraźnie) zwraca true w drugim przypadku, ponieważ 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; 

Problemem jest to, że IsRectEmpty który jest nazywany przez Intersect sprawdza, czy zarówno górna i dolna część rect lub w lewo i na prawo od rect mają te same wartości, a gdy to przechodzi Intersect ustawia wynik pusty rect.

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

Czy jest to oczekiwane zachowanie, a jeśli nie to, co należy zmienić. Rozumiem, że TRects wyklucza dolną i prawą "krawędź", a jeśli tak, to czy nie powinien wyglądać tak?

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; 

Odpowiedz

6

To błąd; to nie może być oczekiwane zachowanie. W obecnej implementacji RTL uważa, że ​​dwa puste rectory mogą się przecinać (na przykład (0,0,0,0), (0,0,0,0) lub jeden niepusty rect z pustym), co nie ma żadnego sensu.

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

Powyższe twierdzenie nie zawiedzie.

Ponadto nie działa zgodnie z interfejsem systemu Windows. Poniższa asercja kończy się niepowodzeniem: winapi uważa, że ​​(0,0,200,100) i (0,100,200,200) nie przecinają się.

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

Przeciążenie System.Types.IntersectRect(), która zwraca wartość logiczną jest również przerwany.

+0

Dzięki, dodałem QC z niektórymi z twoich informacji (http://qc.embarcadero.com/wc/qcmain.aspx?d=127696) – Graymatter

+0

Hmmm ... Dwa puste reks są identyczne, więc oczywiście przecinają się, IMO. Przecięcie to oczywiście pusty rect. Ale 'Rect (0, 0, 100, 200)' i 'Rect (0, 100, 200, 200)' nie powinny się przecinać, IMO, nawet jeśli mają nominalnie wspólny punkt '(0, 100)' (ale nie w rzeczywistości, ponieważ ten punkt w systemie Windows nie jest częścią pierwszego recta). –

+0

@Rudy - Pusty rect to rect, który nie jest. Nie może przecinać się z niczym, nie ma nawet wnętrza/obszaru. –