Registriert seit: 31. Mai 2009
1.198 Beiträge
Turbo Delphi für Win32
|
AW: Überprüfen ob Mausklick in bestimmten bereich war
23. Mär 2012, 17:03
Hier, eine etwas allgemeinere Lösung SAT (Separating Axis Theorem)
Kurze Erläuterung der Funktionsweise (soweit ich das richtig in Erinnerung habe):
Man berechnet zu allen Seiten beider Polygone schrittweise die Normalen, und projeziert alle Punkte auf diese Achse (Normale). Dann berechnet man sich die Min-Max Werte auf der Achse für Polygon 1 & 2. Schneiden sich nun diese beiden Bereiche nicht, so gibt es keine Kollision -> die Überprüfung kann vorzeitig beendet werden. Ansonsten kommt es höchstwahrscheinlich (nicht sicher!) zu einer Kollision.
Bild 1
Bild 2
(bei Bild 2 sieht man, dass der grüne und rote Bereich sich nicht schneiden -> keine Kollision)
Delphi-Quellcode:
type
TVector = record
X, Y: Single;
procedure Assign(const AX, AY: Single);
end;
TVectorArray = Array of TVector;
PPolygon = ^TPolygon;
TPolygon = TVectorArray;
procedure TVector.Assign(const AX, AY: Single);
begin
X := AX;
Y := AY;
end;
function vector(const AX, AY: Single): TVector;
begin
Result.Assign(AX, AY);
end;
function scalarProduct(const A, B: TVector): Single;
begin
Result := A.X*B.X + A.Y*B.Y;
end;
procedure normalize(var V: TVector);
var
size: Single;
begin
size := SQRT(scalarProduct(V, V));
if size <> 0 then V.Assign(V.X/size, V.Y/size);
end;
function normal(const V: TVector): TVector;
begin
Result.Assign(V.Y, -V.X);
end;
function BoundingBoxCollisionCheck(const A1, A2, B1, B2: TVector): Boolean; overload;
{ 1
+---------+
| |
| |
+---------+ 2
}
begin
Result := not((A1.X > B2.X) or (A2.X < B1.X) or
(A1.Y > B2.Y) or (A2.Y < B1.Y));
end;
function BoundingBoxCollisionCheck(const PolyA, PolyB: TPolygon): Boolean; overload;
var
boxA1, boxA2,
boxB1, boxB2: TVector;
procedure createBoundingBox(const P: TPolygon; var bbox1, bbox2: TVector);
var
i: Integer;
begin
for i := 0 to High(P) do
begin
if P[i].X < bbox1.X then
bbox1.X := P[i].X
else
if P[i].X > bbox2.X then
bbox2.X := P[i].X;
if P[i].Y < bbox1.Y then
bbox1.Y := P[i].Y
else
if P[i].Y > bbox2.Y then
bbox2.Y := P[i].Y
end;
end;
begin
with PolyA[0] do
begin
boxA1.Assign(X, Y);
boxA2.Assign(X, Y);
end;
with PolyB[0] do
begin
boxB1.Assign(X, Y);
boxB2.Assign(X, Y);
end;
createBoundingBox(PolyA, boxA1, boxA2);
createBoundingBox(PolyB, boxB1, boxB2);
Result := BoundingBoxCollisionCheck(boxA1, boxA2, boxB1, boxB2);
end;
function SATCollisionCheck(const PolyA, PolyB: TPolygon): Boolean;
var
P1, P2: PPolygon;
function collisionCheck: boolean;
var
len: Integer;
i: Integer;
n: TVector;
minMaxA, minMaxB: TVector;
procedure getMinMaxAxisMappedValues(const Polygon: PPolygon; var min, max: Single);
// axis = n
var
j: Integer;
scalar: Single;
begin
min := scalarProduct(Polygon^[0], n);
max := min;
for j := 1 to high(Polygon^) do
begin
scalar := scalarProduct(Polygon^[j], n);
if (scalar < min) then
min := scalar
else
if scalar > max then
max := scalar;
end;
end;
function collision1DCheck(const A, B: TVector): Boolean;
begin
Result := not((A.X > B.Y) or (A.Y < B.X));
end;
begin
Result := False;
// gehe alle Punkte des Polygons 1 durch, bilde die Gerade und berechne darauf die Normale
len := Length(P1^);
for i := 0 to len do
begin
with P1^[(i+1) mod len] do
n := normal(vector(X - P1^[i].X, Y - P1^[i].Y)); // n = normal(AB)
normalize(n);
// mappe nun alle punkte beider Polygone auf die Achse (punkt * n) und merke dir
// min max Werte
getMinMaxAxisMappedValues(P1, minMaxA.X, minMaxA.Y);
getMinMaxAxisMappedValues(P2, minMaxB.X, minMaxB.Y);
// wenn beide Bereiche sich NICHT schneiden, so kommts zu keienr Kollision und es kann beendet werden
if not collision1DCheck(minMaxA, minMaxB) then Exit;
end;
Result := True;
end;
begin
Result := False;
if (Length(PolyA) < 2) or (Length(PolyB) < 2) then Exit;
if not BoundingBoxCollisionCheck(PolyA, PolyB) then Exit;
P1 := @PolyA;
P2 := @PolyB;
if collisionCheck then
begin
P1 := @PolyB;
P2 := @PolyA;
Result := collisionCheck;
end;
end;
Das ganze ist noch mit Bounding Boxes optimiert.
Hf
das Erkennen beginnt, wenn der Erkennende vom zu Erkennenden Abstand nimmt
MfG
Geändert von Aphton (23. Mär 2012 um 17:12 Uhr)
|