Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Di Mai 14, 2024 00:04

Foren-Übersicht » Programmierung » Mathematik-Forum
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 4 Beiträge ] 
Autor Nachricht
 Betreff des Beitrags: Kollision [Kreis - Linie]
BeitragVerfasst: Mo Jan 19, 2004 17:42 
Offline
DGL Member

Registriert: Mi Okt 16, 2002 15:06
Beiträge: 1012
Hi,

ich wollt mal fragen wie man prüft ob ein Kreis eine Linie schneidet

ich hab schon einige src gefunden, allerdings bisher in c, was aber nich wirklich ein problem ist das zu portieren, nur funzt das nie so dann wie es soll.

Wenn jemand ne schöne Line-Circle Kollisions Funktion posten könnte wäre ich sehr dankbar und könnte endlich mein fast gestorbenes projekt Crackout wieder zum leben erwecken ;)

Hier mal ne routine die ich gefunden habe, und portiert habe, die aber ums verrecken nicht funzt:

Achja, das ganze ist 2D, obs 2D oder 3D ist mir relativ schnuppe.
3D wäre nicer, muss aber nich sein.

:::

Delphi:

Code:
  1.  
  2.  
  3. function LineHitsCircle(const _LineStart, _LineEnd : TVector2; const _CircleCenter : TVector2; const _Radius : Single) : Boolean;
  4. var
  5.   RadiusSq : Single;
  6.   PMinusM  : TVector2;
  7.   LineDir  : TVector2;
  8.   UDotPMinusM : Single;
  9.   LineDirSq   : Single;
  10.   d, s        : Single;
  11. begin
  12.   // r² vorberechnen
  13.   RadiusSq := _Radius * _Radius;
  14.  
  15.   // (p - m) vorberechnen
  16.   PMinusM := Vector2Sub(_LineStart, _CircleCenter);
  17.  
  18.   // Wenn der Startpunkt der Linie schon im Kreis liegt,
  19.   // dann brechen wir sofort ab.
  20.   if (PMinusM.Length <= RadiusSq) then
  21.   begin
  22.     // Als Schnittpunkt nehmen wir einfach
  23.     // den Startpunkt der Linie.
  24.  
  25.     //if(pOut) *pOut = LineStart;
  26.     Result := True;
  27.     Exit;
  28.   end;
  29.  
  30.   // Richtungsvektor der Linie berechnen (u)
  31.   LineDir := Vector2Sub(_LineEnd, _LineStart);
  32.  
  33.   // u * (p - m) vorberechnen
  34.   UDotPMinusM := Vector2DotProduct(LineDir, PMinusM);
  35.  
  36.   // u² vorberechnen
  37.   LineDirSq  := LineDir.Length;
  38.  
  39.   // Die Diskriminante berechnen:
  40.   //   (u * (p - m))²
  41.   // - (u² * ((p - m)² - r²))
  42.   d := UDotPMinusM * UDotPMinusM - LineDirSq * (PMinusM.Length - RadiusSq);
  43.  
  44.   // Ist die Diskriminante negativ, null oder positiv?
  45.   if (d < 0.0) then
  46.   begin
  47.     Result := False;
  48.     Exit;
  49.   end
  50.   else
  51.   begin
  52.     if (d < 0.0001) then
  53.     begin
  54.       // Die Diskriminante ist (ungefähr) null.
  55.       // Die gesamte Wurzel entfällt und die Lösung ist:
  56.       // (-u * (p - m)) / u².
  57.       // Wir müssen nur noch prüfen, ob der Wert
  58.       // im korrekten Bereich [0; 1] liegt.
  59.       s := -UDotPMinusM / LineDirSq;
  60.       if (s < 0.0) or (s > 1.0) then
  61.       begin
  62.         result := False;
  63.         Exit;
  64.       end
  65.       else
  66.       begin
  67.         // Berührpunkt!
  68.         //if(pOut) *pOut = LineStart + s * LineDir;
  69.         result := True;
  70.         Exit;
  71.       end;
  72.     end
  73.     else
  74.     begin
  75.       // Die Gerade schneidet den Kreis zweimal.
  76.       // Uns interessiert nur die kleinere Lösung für s,
  77.       // denn das ist die Stelle, wo die Linie den Kreis
  78.       // "zum ersten Mal" schneidet.
  79.       // Diese Lösung berechnen wir nun.
  80.       s := (-UDotPMinusM - sqrt(d)) / LineDirSq;
  81.       if (s < 0.0) or (s > 1.0) then
  82.       begin
  83.         result := False;
  84.         exit;
  85.       end
  86.       else
  87.       begin
  88.         //if(pOut) *pOut = LineStart + s * LineDir;
  89.         result := True;
  90.         Exit;
  91.       end;
  92.  
  93.     end;
  94.   end;
  95. end;
  96.  
  97.  


C:

Code:
  1.  
  2. // Schneidet eine Linie einen Kreis?
  3. bool LineHitsCircle(const Vector2D& LineStart,
  4.                     const Vector2D& LineEnd,
  5.                     const Vector2D& CircleCenter,
  6.                     const float Radius,
  7.                     Vector2D* const pOut = 0)
  8. {
  9.     // r² vorberechnen
  10.     const float RadiusSq = Radius * Radius;
  11.  
  12.     // (p - m) vorberechnen
  13.     const Vector2D PMinusM(LineStart - CircleCenter);
  14.  
  15.     // Wenn der Startpunkt der Linie schon im Kreis liegt,
  16.     // dann brechen wir sofort ab.
  17.     if(PMinusM.LengthSq() <= RadiusSq)
  18.     {
  19.         // Als Schnittpunkt nehmen wir einfach
  20.         // den Startpunkt der Linie.
  21.         if(pOut) *pOut = LineStart;
  22.         return true;
  23.     }
  24.  
  25.     // Richtungsvektor der Linie berechnen (u)
  26.     const Vector2D LineDir(LineEnd - LineStart);
  27.  
  28.     // u * (p - m) vorberechnen
  29.     const float UDotPMinusM = Vector2D_Dot(LineDir, PMinusM);
  30.  
  31.     // u² vorberechnen
  32.     const float LineDirSq = LineDir.LengthSq();
  33.  
  34.     // Die Diskriminante berechnen:
  35.     //   (u * (p - m))²
  36.     // - (u² * ((p - m)² - r²))
  37.     const float d =   UDotPMinusM * UDotPMinusM
  38.                     - LineDirSq * (PMinusM.LengthSq() - RadiusSq);
  39.  
  40.     // Ist die Diskriminante negativ, null oder positiv?
  41.     if(d < 0.0f) return false;
  42.     else if(d < 0.0001f)
  43.     {
  44.         // Die Diskriminante ist (ungefähr) null.
  45.         // Die gesamte Wurzel entfällt und die Lösung ist:
  46.         // (-u * (p - m)) / u².
  47.         // Wir müssen nur noch prüfen, ob der Wert
  48.         // im korrekten Bereich [0; 1] liegt.
  49.         const float s = -UDotPMinusM / LineDirSq;
  50.         if(s < 0.0f || s > 1.0f) return false;
  51.         else
  52.         {
  53.             // Berührpunkt!
  54.             if(pOut) *pOut = LineStart + s * LineDir;
  55.             return true;
  56.         }
  57.     }
  58.     else
  59.     {
  60.         // Die Gerade schneidet den Kreis zweimal.
  61.         // Uns interessiert nur die kleinere Lösung für s,
  62.         // denn das ist die Stelle, wo die Linie den Kreis
  63.         // "zum ersten Mal" schneidet.
  64.         // Diese Lösung berechnen wir nun.
  65.         const float s = (-UDotPMinusM - sqrtf(d)) / LineDirSq;
  66.         if(s < 0.0f || s > 1.0f) return false;
  67.         else
  68.         {
  69.             if(pOut) *pOut = LineStart + s * LineDir;
  70.             return true;
  71.         }
  72.     }
  73. }
  74.  


daher hab ich die routine:
http://www.scherfgen-software.net/index ... lision2d_2


thx im voraus,
Final


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mi Jan 21, 2004 09:42 
Offline
DGL Member
Benutzeravatar

Registriert: Mo Nov 17, 2003 09:07
Beiträge: 24
Wohnort: Regensburg
Ich kann zwar im Moment leider nicht nachprüfen ob mein Gerechne jetzt so stimmt (ich hock gerade an einen FH-CIP-Pool Rechner ohne installierter Progragrammiersprache und leider hab ich auch keinen Taschenrechner oder Formelsammlung zur Hand), aber ich versuch´s mal:

Also erstmal den Kreis: (Methode ala Pythagoras)


(X+Xo)^2 + (Y+Yo)^2 = R^2 => X = sqrt ( R^2 - (Y-Yo)^2 ) -Xo

Xo,Yo = Kreismittelpunkt

Eine Gerade beschreiben... sagen wir mal mit:

Y=mX+t (Jede andere Geradenformel würde es vermutlich auch tun,
Tipp: 2-Punkt-Geradenformel - hab´s nur gerade leider nicht im Kopf)

m = Steigung
t = Y-Achsenabschnitt

eingesetzt wäre dann :

m*sqrt ( R^2 - (Y-Yo)^2) - Xo +t - Y = 0

Also wenn dieser Term da oben Null ist (if-Abfrage), dann trifft deine Gerade den Kreis(hoffe ich zumindest, bitte rechne es nochmal durch :roll: ...
jetzt mußt du nur noch in deinen Linienzeichnen-Algorithmus diese
Formel mitreinwurschteln und die Gerade Punkt für Punkt abfragen...
(Möglicherweise mit den Bresenham-Algorithmus ?!?)

_________________
Jetzt reicht´s - her mir der Flex !


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mi Jan 21, 2004 14:12 
Offline
DGL Member
Benutzeravatar

Registriert: Mo Mai 06, 2002 20:27
Beiträge: 479
Wohnort: Bremen
Die Gerade Punkt für Punkt abfragen? Das halte ich für ziemlich illusorisch... in welcher Auflösung willst du denn die For-Schleife entlang laufen ohne das du Gefahr läufst einen Schnittpunkt zu übersehen? Diese Auflösung müsst ja rein mathematisch unendlich klein sein.
Und dazu kommt, dass es ziemlich schwierig wird den Bereich der Gerade zu definieren auf den man die For-Schleife ansetzt, zumindest wenn man flexibel bleiben will. In jedem Fall musst du hunderte Rechnungen durchführen nur um bei 99% festzustellen, dass da kein Schnittpunkt war. Ziemliche Rechenzeitverschwendung.

Ich denke mal man kann die beiden Schnittpunkte auch einfach ausrechnen.
Folgende Überlegung:

1.) wir haben eine Gerade g(x) = A + x * B
B ist ein Richtungsvektor und A ein Punkt auf der Gerade.

2.) wir haben außerdem eine Kugel definiet über einen Mittelpunkt M und einen Radius r.

wir suchen nun ein Skalar s so das g(s) Schnittpunkt mit der Kugel ist. Das heißt der Abstand zwischen g(s) und M ist genau r.
d. h. |g(s) - M| = r
<-> |A + s * B - M| = r

Durch Auflösen des Betrags, Quadrierung und Umstellen lässt sich folgende Gleichung erhalten:

c1 * s² + 2 * c2 * s + c3 = r²
wobei gilt:
c1 = |B|²
c2 = (M[0]-A[0])*B[0] + (M[1]-A[1])*B[1] + (M[2]-A[2])*B[2]
c3 = |M-A|²
ERKLÄRUNG zu c2: B[2] bedeutet 3. Komponente des Vektors b bzw. die Z-Komponente. Was da im Prinzip steht ist das Punktprodukt von (M-A) und B.

Durch einsetzen obiger Gleichung in die Quadratische Lösungsformel erhalten wir:

s1 = - (c2/c1) + sqrt[ (c2/c1)² - (c3 - r²)/c1 ]
s2 = - (c2/c1) - sqrt[ (c2/c1)² - (c3 - r²)/c1 ]

Die gesuchten Schnittpunkte sind also S1 = g(s1) und S2 = g(s2)
Tangiert die Gerade die Kugel gibt der Wurzelausdruck 0. Ist der die Zahl unter der Wurzel gar negativ bedeutet das, dass die Gerade die Kugel nicht schneidet.

Wenn ich mich nicht verrechnet/verdacht habe, müsst man so relativ schnell und sicher auf die Schnittpunkte kommen so sie den existieren. Implementierbar in wenigen Zeilen Code. ;)

Grüße,
Lith

_________________
Selber Denken macht klug!


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Do Jan 22, 2004 19:17 
Offline
DGL Member
Benutzeravatar

Registriert: Mi Aug 20, 2003 22:26
Beiträge: 38
Wohnort: Dresden (noch)
Wenn es nur 2D sein soll, du die Kollisionspunkte nicht brauchst und auch nur prüfen willst, ob die Linie von der Richtung her den Kreis trifft (zielen etc), dann gibt es noch zwei einfache geometrische Ansätze:
Einen über das Lot und einen über Winkel.

- Zum ersteren hab ich vor langer Zeit mal eine Funktion geschrieben, die allerdings nicht getestet ist. Sie bilded die Senkrechte vom Kreismittelpunkt auf die Linie und prüft dann, ob der Schnittpunkt zwischen Senkrechte und Linie innerhalb des Kreises liegt.

Code:
  1.  
  2. TLineStruct  = record P1,P2:TVector2f; m,b: Single; Vertical:Boolean; end;
  3.  
  4. Function LineSectCircle(const Line:TLineStruct; const Centre:TVector2f; Radius:Single):Boolean;
  5. Var NormSectX,Normm,Normb:Single;
  6. begin
  7.  If Line.Vertical then begin Result:=Abs(Line.P1[0]-Centre[0]) < Radius; exit; end;
  8.  If Abs(Line.m) < cVeryLittle then begin Result:=Abs(Line.P1[1]-Centre[1]) < Radius; exit; end;
  9.  Normm:=-1/Line.m;
  10.  Normb:=Centre[1]-Normm*Centre[0];
  11.  NormSectX:=(Normb-Line.b)/(Line.m-Normm);
  12.  Result:=(sqr(NormSectX-Centre[0])+sqr((Line.m*NormSectX+Normb)-Centre[1]) < sqr(Radius));
  13. end;
  14.  


- Für den Ansatz über die Winkel brauchst du zwei Vektoren, den Richtungvektor der Line und einen zwischen Linienanfang und Kreismittelpunkt. Ist der Winkel den diese Vorktoren einschließen kleiner als der Arcustangent vom Betrag des zweiten Vektors und dem Radius des Kreises, so schneidet die Linie den Kreis.
Allsklar? ... Naja wenn man es sich aufzeichnet ist es sehr einleuchtend


Wie auch immer das sind natürlich geometrische ansätze also für Computerspiele so nicht unbedingt geeignet, weil zu speziell und langsam, aber ich dachte es interessiert dich vielleicht.


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 4 Beiträge ] 
Foren-Übersicht » Programmierung » Mathematik-Forum


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 4 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.027s | 17 Queries | GZIP : On ]