Entwickler-Ecke

Algorithmen, Optimierung und Assembler - Punkt in Polygon (schnell)


Spaceguide - Mo 12.09.05 19:55
Titel: Punkt in Polygon (schnell)
Was ist die schnellste Methode nachzuprüfen, ob ein Punkt in einem (nicht unbedingt konvexen) Vieleck liegt? Ich habe hier noch diesen Code, den ich glaub irgendwann mal von C her übersetzt habe. Gibt es eine schnellere Methode, eventuell mit Zerlegung mittels Tiles/Quadtree?


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
function PtInPolyDouble (const Points: array of TPoint; Count: integer; X, Y: double): Boolean;

 var bInside : boolean;
     i,j : integer;
     rkU0,rkU1 : ^TPoint;
     fRHS, fLHS : double;
begin
    bInside := false;
    j:= Count-1;
    for i := 0 to Count-1 do
    begin
        rkU0 := @Points[i];
        rkU1 := @Points[j];

        if ( y < rkU1.y ) then  // U1 above ray
        begin
            if ( rkU0.y <= y ) then  // U0 on or below ray
            begin
                fLHS := (y-rkU0.y)*(rkU1.x-rkU0.x);
                fRHS := (x-rkU0.x)*(rkU1.y-rkU0.y);
                if ( fLHS > fRHS ) then
                bInside := not bInside;
            end;
        end
        else if ( y < rkU0.y ) then  // U1 on or below ray, U0 above ray
        begin
            fLHS := (y-rkU0.y)*(rkU1.x-rkU0.x);
            fRHS := (x-rkU0.x)*(rkU1.y-rkU0.y);
            if ( fLHS < fRHS ) then
                bInside := not bInside;
        end;

     j:=i;
    end;
  Result:=bInside;
end;


worm - Sa 17.09.05 20:08

Hallo!

Folgender Code stammt aus der guten alten GLScene-Library (aus der Geometry-Unit einer schon älteren Version, weiß nicht, ob in der aktuellen vielleicht sogar was schnelleres ist). Ich weiß auch nicht, ob er Deinen Anforderungen genügt (nicht-konvexe Vielecke). Außerdem erfordert er in der vorliegenden Version eine andere Form der Parameter (zwei Arrays statt TPoints).
GLScene ist unter der MPL veröffentlicht, siehe glscene.sf.net [http://glscene.sourceforge.net/faq.htm#240700-2].

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
function PointInPolygon(var xp, yp : array of Single; x, y: Single) : Boolean;
// The code below is from Wm. Randolph Franklin <wrf@ecse.rpi.edu>
// with some minor modifications for speed.  It returns 1 for strictly
// interior points, 0 for strictly exterior, and 0 or 1 for points on
// the boundary.
var
   I, J: Integer;
begin
   Result:=False;
   if High(XP)=High(YP) then begin
      J:=High(XP);
      for I:=0 to High(XP) do begin
         if ((((YP[I]<=Y) and (Y<YP[J])) or ((YP[J]<=Y) and (Y<YP[I])) )
             and (X<(XP[J]-XP[I])*(Y-YP[I])/(YP[J]-YP[I])+XP[I])) then
            Result:=not Result;
         J:=I;
      end;
   end;
end;


Spaceguide - So 18.09.05 17:31

Danke, muss ich mal "benchen". Ich sehe aber eine Division, das ist schonmal ein schlechtes Zeichen.

Edit: Deiner ist knapp 10% schneller, ist aber auch nur ein Tropfen auf den heissen Stein.


delfiphan - So 18.09.05 17:36

Wofür brauchst du das denn? Die Methode mittles Scanlines (PtInPolyDouble) scheint eigentlich schon recht optimal zu sein. Wie sind die Vielecke durchschnittlich?


Spaceguide - So 18.09.05 17:50

Durchschnittlich so vielleicht 100-200 Punkte. Würde sich eine Zerlegung in Dreiecke + Quadtree lohnen?


delfiphan - So 18.09.05 17:54

Die Division kannst du ja auch auf die andere Seite der Ungleichung nehmen. Das geht aber nur, wenn der Divisor nicht negativ ist... Eine Aufteilung bringt sicher was, geht aber nur, wenn man das vorberechnen kann (d.h. das Objekt darf sich nicht mehr ändern).
Aber nochmals: Wofür brauchst du es? Kollisionserkennung? Wie genau musst du das erkennen können?


Spaceguide - So 18.09.05 17:58

Ich brauche es zum "rendern" von Polygonen mit Antialiasing.