Algorithmus zur Erkennung von Kreisliniensegmentkollisionen?
195
Ich habe eine Linie von A nach B und einen Kreis bei C mit dem Radius R.
Was ist ein guter Algorithmus, um zu überprüfen, ob die Linie den Kreis schneidet? Und an welcher Koordinate entlang der Kreiskante ist es aufgetreten?
Hmm. Eine Frage: Sprechen Sie über die unendliche Linie durch A und B oder das endliche Liniensegment von A nach B?
Jason S
2
In diesem Fall ist es das endliche Liniensegment. Wird "Linie" etwas anderes genannt, je nachdem, ob sie endlich oder unendlich ist?
Mizipzor
1
Gibt es eine Leistungsanforderung? Sollte es eine schnelle Methode sein?
chmike
An diesem Punkt, nein, alle Algorithmen hier, die ich versucht habe, verlangsamen die Anwendung nicht merklich.
Mizipzor
13
@ Mizipzor ja, sie heißen etwas anderes: Liniensegmente . Wenn Sie nur "Linie" sagen, ist dies eine unendliche.
MestreLion
Antworten:
200
Nehmen
E ist der Startpunkt des Strahls,
L ist der Endpunkt des Strahls,
C ist der Mittelpunkt der Kugel, gegen die Sie testen
r ist der Radius dieser Kugel
Berechnen Sie: d = L - E (Richtungsvektor des Strahls von Anfang bis Ende) f = E - C (Vektor von der Mittelkugel zum Strahlstart)
Dann wird der Schnittpunkt gefunden durch ..
Einstecken: P = E + t * d
Dies ist eine parametrische Gleichung:
P x = E x + td x
P y = E y + td y
in (x - h) 2 + (y - k) 2 = r 2
(h, k) = Kreismittelpunkt.
Hinweis: Wir haben das Problem hier auf 2D vereinfacht. Die Lösung gilt auch für 3D
bekommen:
Erweitern Sie
x 2 - 2xh + h 2 + y 2 - 2yk + k 2 - r 2 = 0
Stecker
x = e x + td x
y = e y + td y
(e x + td x ) 2 - 2 (e x + td x ) h + h 2 + (e y + td y ) 2 - 2 (e y + td y ) k + k 2 - r 2 = 0
Explodiere
e x 2 + 2e x td x + t 2 d x 2 - 2e x h - 2td x h + h 2 + e y 2 + 2e y td y + t 2 d y 2 - 2e y k - 2td y k + k 2 - r 2 = 0
Gruppe
t 2 (d x 2 + d y 2 ) + 2t (e x d x + e y d y - d x h - d y k) + e x 2 + e y 2 - 2e x h - 2e y k + h 2 + k 2 - r 2 = 0
Schließlich ist
t 2 (_d * _d) + 2t (_e * _d - _d * _c) + _e * _e - 2 (_e * _c) + _c * _c - r 2 = 0
* wobei _d der Vektor d ist und * ist das Punktprodukt. *
Und dann ist
t 2 (_d * _d) + 2t (_d * (_e - _c)) + (_e - _c) * (_e - _c) - r 2 = 0
Wir erhalten also: t 2 * (d DOT d) + 2t * (f DOT d) + (f DOT f - r 2 ) = 0
Lösen Sie also die quadratische Gleichung:
float a = d.Dot( d ) ;
float b = 2*f.Dot( d ) ;
float c = f.Dot( f ) - r*r ;
float discriminant = b*b-4*a*c;
if( discriminant < 0 )
{
// no intersection
}
else
{
// ray didn't totally miss sphere,
// so there is a solution to
// the equation.
discriminant = sqrt( discriminant );
// either solution may be on or off the ray so need to test both
// t1 is always the smaller value, because BOTH discriminant and
// a are nonnegative.
float t1 = (-b - discriminant)/(2*a);
float t2 = (-b + discriminant)/(2*a);
// 3x HIT cases:
// -o-> --|--> | | --|->
// Impale(t1 hit,t2 hit), Poke(t1 hit,t2>1), ExitWound(t1<0, t2 hit),
// 3x MISS cases:
// -> o o -> | -> |
// FallShort (t1>1,t2>1), Past (t1<0,t2<0), CompletelyInside(t1<0, t2>1)
if( t1 >= 0 && t1 <= 1 )
{
// t1 is the intersection, and it's closer than t2
// (since t1 uses -b - discriminant)
// Impale, Poke
return true ;
}
// here t1 didn't intersect so we are either started
// inside the sphere or completely past it
if( t2 >= 0 && t2 <= 1 )
{
// ExitWound
return true ;
}
// no intn: FallShort, Past, CompletelyInside
return false ;
}
Scheint zu funktionieren, wenn ich gerade kopiere und einfüge, aber ich versuche es zu verstehen. In (xh) ^ 2 + (yk) ^ 2 = r ^ 2 was ist h und k? Ist k konstant, um was die Linie / der Strahl auf y über x zunimmt? Und was ist t? Wenn Sie sich den Code ansehen, scheinen Sie seine 1 angenommen zu haben (also ist er nur "entfernt"). Haben diese Formeln einen Namen oder so? Vielleicht kann ich sie bei Wolfram im Detail nachschlagen.
Mizipzor
3
h und k sind der Mittelpunkt des Kreises, gegen den Sie sich schneiden. t ist der Parameter der Liniengleichung. Im Code sind t1 und t2 die Lösungen. t1 und t2 sagen dir "wie weit entlang des Strahls" die Kreuzung passiert ist.
Bobobobo
1
OK habe es. Das Punktprodukt wird einfach über die drei Elementvektoren (x, y, z) berechnet. Ich werde meinen Code auf diesen Algorithmus umstellen.
chmike
21
P = E + t * dWas ist t?
Derek 9 會 功夫
3
Ich weiß nicht warum, aber der Code scheint für den Impale-Fall nicht zu funktionieren. Es funktioniert, wenn ich addiere, wenn t1 <= 0 && t1> = -1 && t2 <= 0 && t2> = -1 als wahre Bedingung, aber dann gibt es auch ein falsches Positiv auf einer Seite der endlichen Linie, wenn der Kreis ist auf dem "unendlichen" Teil. Ich verstehe die Mathematik noch nicht, aber Kopie / Pasters, Vorsicht.
Nicolas Mommaerts
140
Niemand scheint über Projektion nachzudenken, bin ich hier völlig aus der Bahn geraten?
Projizieren Sie den Vektor ACauf AB. Der projizierte Vektor ADgibt den neuen Punkt an D.
Wenn der Abstand zwischen Dund Ckleiner als (oder gleich) ist R, haben wir einen Schnittpunkt.
Es sind viele Details zu berücksichtigen: Liegt D zwischen AB? Ist der senkrechte Abstand C zur Linie größer als der Radius? Alle diese beinhalten die Größe des Vektors, dh die Quadratwurzel.
ADB
15
Gute Idee, aber wie berechnet man dann die beiden Schnittpunkte?
Ben
4
@ Spider ist es egal. Da dies eine Variante des Kugel-Schnittpunkt-Problems ist, ist die Strategie von Mizipzor im Allgemeinen vollkommen gültig. CDist eine Projektion, sie ist per Definition senkrecht.
2
Es ist eine alte Frage, aber es gibt eine gute Ressource zu diesem und verwandten Algorithmen auf dieser Website: paulbourke.net/geometry/pointlineplane
Ich würde den Algorithmus verwenden, um den Abstand zwischen einem Punkt (Kreismittelpunkt) und einer Linie (Linie AB) zu berechnen. Dies kann dann verwendet werden, um die Schnittpunkte der Linie mit dem Kreis zu bestimmen.
Angenommen, wir haben die Punkte A, B, C. Ax und Ay sind die x- und y-Komponenten der A-Punkte. Gleiches gilt für B und C. Der Skalar R ist der Kreisradius.
Dieser Algorithmus erfordert, dass A, B und C unterschiedliche Punkte sind und dass R nicht 0 ist.
Hier ist der Algorithmus
// compute the euclidean distance between A and B
LAB = sqrt( (Bx-Ax)²+(By-Ay)² )
// compute the direction vector D from A to B
Dx = (Bx-Ax)/LAB
Dy = (By-Ay)/LAB
// the equation of the line AB is x = Dx*t + Ax, y = Dy*t + Ay with 0 <= t <= LAB.
// compute the distance between the points A and E, where
// E is the point of AB closest the circle center (Cx, Cy)
t = Dx*(Cx-Ax) + Dy*(Cy-Ay)
// compute the coordinates of the point E
Ex = t*Dx+Ax
Ey = t*Dy+Ay
// compute the euclidean distance between E and C
LEC = sqrt((Ex-Cx)²+(Ey-Cy)²)
// test if the line intersects the circle
if( LEC < R )
{
// compute distance from t to circle intersection point
dt = sqrt( R² - LEC²)
// compute first intersection point
Fx = (t-dt)*Dx + Ax
Fy = (t-dt)*Dy + Ay
// compute second intersection point
Gx = (t+dt)*Dx + Ax
Gy = (t+dt)*Dy + Ay
}
// else test if the line is tangent to circle
else if( LEC == R )
// tangent point to circle is E
else
// line doesn't touch circle
wenn sich eine Linie, die den Kreis nicht schneidet, und ihr Punkt p1 und p2 innerhalb des Kreises befinden. In diesem Fall, wie funktioniert Ihr Algorithmus?
Prashant
1
Sie müssen t-dt und t + dt testen. Wenn t-dt <0 ist, liegt p1 innerhalb des Kreises. Wenn t + dt> 1 ist, befindet sich p2 innerhalb des Kreises. Dies gilt natürlich, wenn LEC <R ist.
Chmike
Vielen Dank. Ich mochte diese pgm-Kommentare als Erklärung, weil die Wörter "Punktprodukt" nicht verwendet wurden, da meine Mathematik rostig ist. T und dt liegen jedoch nicht zwischen 0..1. Während ich dies in Python änderte, änderte ich t, um es durch LAB ** 2 zu teilen. Nach meinem Verständnis besteht die erste Division von LAB darin, den Mittelpunkt des Kreises auf die Linie AB zu projizieren, und die zweite Division von LAB besteht darin, ihn in den Bereich 0..1 zu normalisieren. Außerdem muss der dt durch LAB geteilt werden, damit er auch normalisiert wird. Somit existiert "wenn (t-dt> = 0,0)" erste Kreuzung "wenn (t + dt <= 1,0)" zweite Kreuzung existiert. Dies funktionierte beim Testen.
Lochkarte
2
Weil der Schnittpunkt mit dem Kreis in "Entfernung" t+dtund t-dtauf der Linie liegt. tist der Punkt auf der Linie, der dem Mittelpunkt des Kreises am nächsten liegt. Die Schnittpunkte mit dem Kreis befinden sich in einem symmetrischen Abstand von t. Die Schnittpunkte liegen bei "Abständen" t-dtund t+dt. Ich habe die Entfernung angegeben, weil es nicht die euklidische Entfernung ist. Um den euklidischen Abstand von Awo zu erhalten t=0, müssen Sie den Wert mit multiplizieren LAB.
Chmike
1
@Matt W Sie meinen "Wie kann festgestellt werden, ob der Schnittpunkt außerhalb der Endpunkte des Linienabschnitts AB liegt?" Stellen Sie sich t als Maß für die Entfernung entlang der Linie vor. Punkt A ist bei t=0. Punkt B bei t=LAB. Wenn beide Schnittpunkte ( t1=t-tdund t2=t+td) negative Werte haben, liegen die Schnittpunkte außerhalb des Abschnitts (hinter Punkt A von der Abschnittsseite des Punkts aus gesehen). Wenn t1 und t2 größer als LAB sind, sind sie auch draußen (diesmal hinter dem B-Punkt). Der Schnittpunkt t1 (oder t2) tritt zwischen A und B nur auf, wenn t1 (oder t2) zwischen 0 und LAB liegt.
Marconius
20
Okay, ich werde dir keinen Code geben, aber da du dies markiert hast AlgorithmusIch denke nicht, dass dir das etwas ausmachen wird. Zuerst müssen Sie einen Vektor senkrecht zur Linie erhalten.
Sie haben eine unbekannte Variable in y = ax + c(c wird unbekannt sein ).
Um dies zu lösen, berechnen Sie ihren Wert, wenn die Linie durch den Mittelpunkt des Kreises verläuft.
Das heißt,
Stecken Sie die Position des Kreismittelpunkts in die Liniengleichung und lösen Sie nach c.
Berechnen Sie dann den Schnittpunkt der ursprünglichen Linie und ihrer Normalen.
Dadurch erhalten Sie den Punkt auf der Linie, der dem Kreis am nächsten liegt.
Berechnen Sie den Abstand zwischen diesem Punkt und dem Kreismittelpunkt (unter Verwendung der Größe des Vektors).
Wenn dies kleiner als der Radius des Kreises ist - voila, haben wir einen Schnittpunkt!
Das war genau das, was ich wollte. Ich möchte die Theorie, eine Google-Suche nach Linien-Kreis-Kollisionsalgorithmus zeigt nur Code, soweit ich sehen kann.
Mizipzor
Ok, c ist in Ihrer Gleichung unbekannt, aber was ist "a"? Die anderen Antworten scheinen sich auf diese Variable als "alpha" und "t" zu beziehen. Obwohl dies nur eine lineare Funktion ist (y = kx + m), ziemlich einfache Mathematik, so fühle ich mich plötzlich ein bisschen verrostet. Ist k nicht auch unbekannt? Oder meinst du, wir können m = 0 annehmen und k lösen? Wäre dann nicht m (dh c) für unser gelöstes k immer Null?
Mizipzor
1
Oh, sorry - ich verwende die einfache Gleichung einer Linie mit einem Gradienten und einem Versatz (die kartesische Gleichung). Ich nahm an, dass Sie die Linie als solche Gleichung speichern - in diesem Fall verwenden Sie das Negativ des Gradienten für k. Wenn Sie die Zeile nicht so gespeichert haben, können Sie k als (y2-y1) / (x2-x1)
a_m0d
1
Wir nehmen nicht an, dass m Null ist; Wir berechnen zuerst den Gradienten (so dass die Gleichung der Linie dann als Beispiel wie folgt aussieht: y = 2x + m). Sobald wir den Gradienten haben, können wir nach m lösen, indem wir den Mittelpunkt des Kreises für y und x einstecken .
a_m0d
1
+1 Tolle Erklärung! Aber ich denke, dies setzt eine Linie voraus, kein Liniensegment. Wenn also der nächstgelegene Punkt auf dieser Linie zum Mittelpunkt des Kreises nicht zwischen den Punkten A und B liegt, wird er trotzdem gezählt.
Hassan
11
Eine andere Methode verwendet die Dreiecks-ABC-Flächenformel. Der Schnittpunkttest ist einfacher und effizienter als die Projektionsmethode, aber das Finden der Koordinaten des Schnittpunkts erfordert mehr Arbeit. Zumindest wird es bis zu dem Punkt verzögert, an dem es erforderlich ist.
Die Formel zur Berechnung der Dreiecksfläche lautet: area = bh / 2
Dabei ist b die Basislänge und h die Höhe. Wir haben das Segment AB als Basis gewählt, damit h der kürzeste Abstand von C, dem Kreismittelpunkt, zur Linie ist.
Da die Dreiecksfläche auch durch ein Vektorpunktprodukt berechnet werden kann, können wir h bestimmen.
// compute the triangle area times 2 (area = area2/2)
area2 = abs( (Bx-Ax)*(Cy-Ay) - (Cx-Ax)(By-Ay) )
// compute the AB segment length
LAB = sqrt( (Bx-Ax)² + (By-Ay)² )
// compute the triangle height
h = area2/LAB
// if the line intersects the circle
if( h < R )
{
...
}
UPDATE 1:
Sie könnten den Code mithilfe der schnellen Optimierung inverse Quadratwurzel - Berechnung beschrieben hier eine gute Näherung von 1 / LAB zu erhalten.
Die Berechnung des Schnittpunkts ist nicht so schwierig. Hier kommt's
// compute the line AB direction vector components
Dx = (Bx-Ax)/LAB
Dy = (By-Ay)/LAB
// compute the distance from A toward B of closest point to C
t = Dx*(Cx-Ax) + Dy*(Cy-Ay)
// t should be equal to sqrt( (Cx-Ax)² + (Cy-Ay)² - h² )
// compute the intersection point distance from t
dt = sqrt( R² - h² )
// compute first intersection point coordinate
Ex = Ax + (t-dt)*Dx
Ey = Ay + (t-dt)*Dy
// compute second intersection point coordinate
Fx = Ax + (t+dt)*Dx
Fy = Ay + (t+dt)*Dy
Wenn h = R ist, tangiert die Linie AB den Kreis und der Wert dt = 0 und E = F. Die Punktkoordinaten sind die von E und F.
Sie sollten überprüfen, ob A von B abweicht und die Segmentlänge nicht null ist, wenn dies in Ihrer Anwendung auftreten kann.
Ich mag die Einfachheit dieser Methode. Vielleicht könnte ich einen Teil des umgebenden Codes so anpassen, dass der eigentliche Kollisionspunkt selbst nicht benötigt wird. Ich werde sehen, was passiert, wenn ich A oder B anstelle des berechneten Punktes dazwischen verwende.
Mizipzor
1
t = Dx * (Cx-Axe) + Dy * (Cy-Axe) sollte t = Dx * (Cx-Axe) + Dy * (Cy-Ay)
Stonetip
Dies ist richtig. Vielen Dank für den Hinweis. Ich habe es in der Post korrigiert.
Chmike
gerade bearbeitet - Die erste Zeile berechnet die Dreiecksfläche mit einem Kreuzprodukt , nicht mit einem Punktprodukt. Hier mit Code verifiziert: stackoverflow.com/questions/2533011/…
ericsoco
4
Beachten Sie auch, dass in der ersten Hälfte dieser Antwort der Schnittpunkt mit einer Linie und nicht mit einem Liniensegment getestet wird (wie in der Frage gestellt).
Ericsoco
8
Ich schrieb ein kleines Skript, um die Schnittmenge zu testen, indem ich den Mittelpunkt des Kreises auf die Linie projizierte.
Diese Lösung, die ich gefunden habe, schien etwas einfacher zu befolgen als einige der anderen.
Nehmen:
p1 and p2 as the points for the line, and
c as the center point for the circle and r for the radius
Ich würde für die Gleichung der Linie in Steigungsschnittform lösen. Ich wollte mich jedoch nicht mit schwierigen Gleichungen cals Punkt befassen müssen , also habe ich das Koordinatensystem einfach so verschoben, dass der Kreis bei ist0,0
p3 = p1 - c
p4 = p2 - c
Übrigens, wenn ich Punkte voneinander subtrahiere x, subtrahiere ich die 's und subtrahiere dann die y' s und setze sie in einen neuen Punkt, nur für den Fall, dass jemand es nicht wusste.
Wie auch immer, ich löse jetzt nach der Gleichung der Linie mit p3und p4:
m = (p4_y - p3_y) / (p4_x - p3) (the underscore is an attempt at subscript)
y = mx + b
y - mx = b (just put in a point for x and y, and insert the m we found)
OK. Jetzt muss ich diese Gleichungen gleich setzen. Zuerst muss ich die Kreisgleichung für lösenx
Stecken Sie dann einfach das Ergebnis dieser beiden Gleichungen in das xIn mx + b. Aus Gründen der Übersichtlichkeit habe ich JavaScript-Code geschrieben, um zu zeigen, wie dies verwendet wird:
function interceptOnCircle(p1,p2,c,r){
//p1 is the first line point
//p2 is the second line point
//c is the circle's center
//r is the circle's radius
var p3 = {x:p1.x - c.x, y:p1.y - c.y} //shifted line points
var p4 = {x:p2.x - c.x, y:p2.y - c.y}
var m = (p4.y - p3.y) / (p4.x - p3.x); //slope of the line
var b = p3.y - m * p3.x; //y-intercept of line
var underRadical = Math.pow((Math.pow(r,2)*(Math.pow(m,2)+1)),2)-Math.pow(b,2)); //the value under the square root sign
if (underRadical < 0){
//line completely missed
return false;
} else {
var t1 = (-2*m*b+2*Math.sqrt(underRadical))/(2 * Math.pow(m,2) + 2); //one of the intercept x's
var t2 = (-2*m*b-2*Math.sqrt(underRadical))/(2 * Math.pow(m,2) + 2); //other intercept's x
var i1 = {x:t1,y:m*t1+b} //intercept point 1
var i2 = {x:t2,y:m*t2+b} //intercept point 2
return [i1,i2];
}
}
Ich hoffe das hilft!
PS Wenn jemand Fehler findet oder Vorschläge hat, kommentieren Sie diese bitte. Ich bin sehr neu und begrüße alle Hilfe / Vorschläge.
Wenn möglich, veröffentlichen Sie auch einige Beispielwerte, damit wir den Fluss schnell erfassen können.
Prabindh
Mit underRadicalextra ')'
Jeevan
4
Sie können einen Punkt auf einer unendlichen Linie finden, der dem Kreismittelpunkt am nächsten liegt, indem Sie den Vektor AC auf den Vektor AB projizieren. Berechnen Sie den Abstand zwischen diesem Punkt und dem Kreismittelpunkt. Wenn es größer als R ist, gibt es keinen Schnittpunkt. Wenn der Abstand gleich R ist, ist die Linie eine Tangente des Kreises und der Punkt, der dem Kreismittelpunkt am nächsten liegt, ist tatsächlich der Schnittpunkt. Wenn der Abstand kleiner als R ist, gibt es 2 Schnittpunkte. Sie liegen im gleichen Abstand von dem Punkt, der dem Kreismittelpunkt am nächsten liegt. Diese Entfernung kann leicht mit dem Satz von Pythagoras berechnet werden. Hier ist ein Algorithmus im Pseudocode:
{
dX = bX - aX;
dY = bY - aY;
if ((dX == 0) && (dY == 0))
{
// A and B are the same points, no way to calculate intersection
return;
}
dl = (dX * dX + dY * dY);
t = ((cX - aX) * dX + (cY - aY) * dY) / dl;
// point on a line nearest to circle center
nearestX = aX + t * dX;
nearestY = aY + t * dY;
dist = point_dist(nearestX, nearestY, cX, cY);
if (dist == R)
{
// line segment touches circle; one intersection point
iX = nearestX;
iY = nearestY;
if (t < 0 || t > 1)
{
// intersection point is not actually within line segment
}
}
else if (dist < R)
{
// two possible intersection points
dt = sqrt(R * R - dist * dist) / sqrt(dl);
// intersection point nearest to A
t1 = t - dt;
i1X = aX + t1 * dX;
i1Y = aY + t1 * dY;
if (t1 < 0 || t1 > 1)
{
// intersection point is not actually within line segment
}
// intersection point farthest from A
t2 = t + dt;
i2X = aX + t2 * dX;
i2Y = aY + t2 * dY;
if (t2 < 0 || t2 > 1)
{
// intersection point is not actually within line segment
}
}
else
{
// no intersection
}
}
BEARBEITEN: Code hinzugefügt, um zu überprüfen, ob gefundene Schnittpunkte tatsächlich innerhalb des Liniensegments liegen.
Sie haben einen Fall verpasst, da es sich um ein Liniensegment handelt: Wenn das Segment im Kreis endet.
ADB
@ADB Eigentlich funktioniert mein Algorithmus nur für unendliche Linien, nicht für Liniensegmente. Es gibt viele Fälle, die mit Liniensegmenten nicht behandelt werden.
Juozas Kontvainis
Die ursprüngliche Frage betraf Liniensegmente, nicht Kreis-Schnittpunkte, was ein viel einfacheres Problem ist.
msumme
4
Seltsamerweise kann ich antworten, aber nicht kommentieren ... Ich mochte Multitaskpros Ansatz, alles so zu verschieben, dass der Mittelpunkt des Kreises auf den Ursprung fällt. Leider gibt es zwei Probleme in seinem Code. Zuerst müssen Sie im Teil unter der Quadratwurzel die doppelte Kraft entfernen. So nicht:
var underRadical = Math.pow((Math.pow(r,2)*(Math.pow(m,2)+1)),2)-Math.pow(b,2));
aber:
var underRadical = Math.pow(r,2)*(Math.pow(m,2)+1)) - Math.pow(b,2);
In den endgültigen Koordinaten vergisst er, die Lösung zurückzuschieben. So nicht:
var i1 = {x:t1,y:m*t1+b}
aber:
var i1 = {x:t1+c.x, y:m*t1+b+c.y};
Die ganze Funktion wird dann:
function interceptOnCircle(p1, p2, c, r) {
//p1 is the first line point
//p2 is the second line point
//c is the circle's center
//r is the circle's radius
var p3 = {x:p1.x - c.x, y:p1.y - c.y}; //shifted line points
var p4 = {x:p2.x - c.x, y:p2.y - c.y};
var m = (p4.y - p3.y) / (p4.x - p3.x); //slope of the line
var b = p3.y - m * p3.x; //y-intercept of line
var underRadical = Math.pow(r,2)*Math.pow(m,2) + Math.pow(r,2) - Math.pow(b,2); //the value under the square root sign
if (underRadical < 0) {
//line completely missed
return false;
} else {
var t1 = (-m*b + Math.sqrt(underRadical))/(Math.pow(m,2) + 1); //one of the intercept x's
var t2 = (-m*b - Math.sqrt(underRadical))/(Math.pow(m,2) + 1); //other intercept's x
var i1 = {x:t1+c.x, y:m*t1+b+c.y}; //intercept point 1
var i2 = {x:t2+c.x, y:m*t2+b+c.y}; //intercept point 2
return [i1, i2];
}
}
Vorschläge: Lassen Sie es zunächst den Fall behandeln, in dem das Liniensegment vertikal ist (dh eine unendliche Steigung aufweist). Zweitens, lassen Sie es nur die Punkte zurückgeben, die tatsächlich in den Bereich des ursprünglichen Liniensegments fallen - ich glaube, es gibt glücklich alle Punkte zurück, die auf die unendliche Linie fallen, selbst wenn diese Punkte außerhalb des Liniensegments liegen.
Gino
Hinweis: Dies funktioniert gut für Linien, aber nicht für Liniensegmente.
Mike
3
Hier brauchen Sie etwas Mathe:
Angenommen, A = (Xa, Ya), B = (Xb, Yb) und C = (Xc, Yc). Jeder Punkt auf der Linie von A nach B hat Koordinaten (Alpha * Xa + (1-Alpha) Xb, Alpha Ya + (1-Alpha) * Yb) = P.
Wenn der Punkt P den Abstand R zu C hat, muss er sich auf dem Kreis befinden. Was Sie wollen, ist zu lösen
Wenn Sie die ABC-Formel auf diese Gleichung anwenden, um sie für Alpha zu lösen, und die Koordinaten von P unter Verwendung der Lösung (en) für Alpha berechnen, erhalten Sie die Schnittpunkte, falls vorhanden.
Wenn Sie den Abstand zwischen dem Mittelpunkt der Kugel (da es sich um 3D handelt, meine ich Kugel und nicht Kreis) und der Linie finden, prüfen Sie, ob dieser Abstand kleiner ist als der Radius, der den Trick ausführt.
Der Kollisionspunkt ist offensichtlich der nächstgelegene Punkt zwischen der Linie und der Kugel (der berechnet wird, wenn Sie den Abstand zwischen der Kugel und der Linie berechnen).
Es ist in 2D, nicht in 3D; Wie Sie sagen, ist das nicht wirklich wichtig
Martijn
Ich bin kein Mathematiker, also dachte ich, ich würde besser einen allgemeinen Ansatz skizzieren und es anderen überlassen, bestimmte Mathematik herauszufinden (obwohl ich ziemlich trivial aussehe)
Martin
2
+1 mit einer starken Gegenstimme. (obwohl ich auf eine andere Seite verlinkt hätte, sieht die pbourke-Seite verwirrend aus) Alle anderen Antworten sind bisher zu kompliziert. Obwohl Ihr Kommentar "Dieser Punkt ist auch der Schnittpunkt auf der Linie" falsch ist, wurde im Berechnungsprozess kein Punkt erstellt.
Ich erklärte etwas besser über den nächsten Punkt und verband mich mit mathworld anstelle von pbourke :)
Martin
3
Hier ist eine Implementierung in Javascript. Mein Ansatz ist es, zuerst das Liniensegment in eine unendliche Linie umzuwandeln und dann die Schnittpunkte zu finden. Von dort aus überprüfe ich, ob sich die gefundenen Punkte auf dem Liniensegment befinden. Der Code ist gut dokumentiert, Sie sollten in der Lage sein, mitzumachen.
// Small epsilon valuevar EPS =0.0000001;// point (x, y)functionPoint(x, y){this.x = x;this.y = y;}// Circle with center at (x,y) and radius rfunctionCircle(x, y, r){this.x = x;this.y = y;this.r = r;}// A line segment (x1, y1), (x2, y2)functionLineSegment(x1, y1, x2, y2){var d =Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));if(d < EPS)throw'A point is not a line segment';this.x1 = x1;this.y1 = y1;this.x2 = x2;this.y2 = y2;}// An infinite line defined as: ax + by = cfunctionLine(a, b, c){this.a = a;this.b = b;this.c = c;// Normalize line for good measureif(Math.abs(b)< EPS){
c /= a; a =1; b =0;}else{
a =(Math.abs(a)< EPS)?0: a / b;
c /= b; b =1;}}// Given a line in standard form: ax + by = c and a circle with // a center at (x,y) with radius r this method finds the intersection// of the line and the circle (if any). function circleLineIntersection(circle, line){var a = line.a, b = line.b, c = line.c;var x = circle.x, y = circle.y, r = circle.r;// Solve for the variable x with the formulas: ax + by = c (equation of line)// and (x-X)^2 + (y-Y)^2 = r^2 (equation of circle where X,Y are known) and expand to obtain quadratic:// (a^2 + b^2)x^2 + (2abY - 2ac + - 2b^2X)x + (b^2X^2 + b^2Y^2 - 2bcY + c^2 - b^2r^2) = 0// Then use quadratic formula X = (-b +- sqrt(a^2 - 4ac))/2a to find the // roots of the equation (if they exist) and this will tell us the intersection points// In general a quadratic is written as: Ax^2 + Bx + C = 0// (a^2 + b^2)x^2 + (2abY - 2ac + - 2b^2X)x + (b^2X^2 + b^2Y^2 - 2bcY + c^2 - b^2r^2) = 0var A = a*a + b*b;var B =2*a*b*y -2*a*c -2*b*b*x;var C = b*b*x*x + b*b*y*y -2*b*c*y + c*c - b*b*r*r;// Use quadratic formula x = (-b +- sqrt(a^2 - 4ac))/2a to find the // roots of the equation (if they exist).var D = B*B -4*A*C;var x1,y1,x2,y2;// Handle vertical line case with b = 0if(Math.abs(b)< EPS){// Line equation is ax + by = c, but b = 0, so x = c/a
x1 = c/a;// No intersectionif(Math.abs(x-x1)> r)return[];// Vertical line is tangent to circleif(Math.abs((x1-r)-x)< EPS ||Math.abs((x1+r)-x)< EPS)return[newPoint(x1, y)];var dx =Math.abs(x1 - x);var dy =Math.sqrt(r*r-dx*dx);// Vertical line cuts through circlereturn[newPoint(x1,y+dy),newPoint(x1,y-dy)];// Line is tangent to circle}elseif(Math.abs(D)< EPS){
x1 =-B/(2*A);
y1 =(c - a*x1)/b;return[newPoint(x1,y1)];// No intersection}elseif(D <0){return[];}else{
D =Math.sqrt(D);
x1 =(-B+D)/(2*A);
y1 =(c - a*x1)/b;
x2 =(-B-D)/(2*A);
y2 =(c - a*x2)/b;return[newPoint(x1, y1),newPoint(x2, y2)];}}// Converts a line segment to a line in general formfunction segmentToGeneralForm(x1,y1,x2,y2){var a = y1 - y2;var b = x2 - x1;var c = x2*y1 - x1*y2;returnnewLine(a,b,c);}// Checks if a point 'pt' is inside the rect defined by (x1,y1), (x2,y2)function pointInRectangle(pt,x1,y1,x2,y2){var x =Math.min(x1,x2), X =Math.max(x1,x2);var y =Math.min(y1,y2), Y =Math.max(y1,y2);return x - EPS <= pt.x && pt.x <= X + EPS &&
y - EPS <= pt.y && pt.y <= Y + EPS;}// Finds the intersection(s) of a line segment and a circlefunction lineSegmentCircleIntersection(segment, circle){var x1 = segment.x1, y1 = segment.y1, x2 = segment.x2, y2 = segment.y2;var line = segmentToGeneralForm(x1,y1,x2,y2);var pts = circleLineIntersection(circle, line);// No intersectionif(pts.length ===0)return[];var pt1 = pts[0];var includePt1 = pointInRectangle(pt1,x1,y1,x2,y2);// Check for unique intersectionif(pts.length ===1){if(includePt1)return[pt1];return[];}var pt2 = pts[1];var includePt2 = pointInRectangle(pt2,x1,y1,x2,y2);// Check for remaining intersectionsif(includePt1 && includePt2)return[pt1, pt2];if(includePt1)return[pt1];if(includePt2)return[pt2];return[];}
In diesem Beitrag wird die Kollision von Kreislinien überprüft, indem der Abstand zwischen Kreismittelpunkt und Punkt auf dem Liniensegment (Ipoint) überprüft wird, der den Schnittpunkt zwischen normalem N (Bild 2) vom Kreismittelpunkt zum Liniensegment darstellt.
Auf Bild 1 sind ein Kreis und eine Linie gezeigt, Vektor A Punkt zu Linie Startpunkt, Vektor B Punkt zu Linie Endpunkt, Vektor C Punkt zu Kreismittelpunkt. Nun müssen wir den Vektor E (vom Linienstartpunkt zum Kreismittelpunkt) und den Vektor D (vom Linienstartpunkt zum Linienendpunkt) finden. Diese Berechnung ist in Bild 1 dargestellt.
In Bild 2 können wir sehen, dass der Vektor E durch das "Punktprodukt" des Vektors E und des Einheitsvektors D auf den Vektor D projiziert wird. Das Ergebnis des Punktprodukts ist der Skalar Xp, der den Abstand zwischen dem Linienstartpunkt und dem Schnittpunkt (Ipoint) von darstellt Vektor N und Vektor D. Der nächste Vektor X wird durch Multiplizieren des Einheitsvektors D und des Skalars Xp gefunden.
Jetzt müssen wir den Vektor Z (Vektor zu Ipoint) finden, seine einfache Vektoraddition von Vektor A (Startpunkt auf Linie) und Vektor X. Als nächstes müssen wir uns mit Sonderfällen befassen, die wir überprüfen müssen, ob Ipoint auf Liniensegment ist, wenn Wir müssen nicht herausfinden, ob es links oder rechts davon ist. Wir werden den Vektor verwenden, der am nächsten ist, um zu bestimmen, welcher Punkt dem Kreis am nächsten liegt.
Wenn die Projektion Xp negativ ist, ist der Punkt links vom Liniensegment, der Vektor am nächsten ist gleich dem Vektor des Linienstartpunkts, wenn die Projektion Xp größer als die Größe des Vektors D ist, dann ist der Punkt rechts vom Liniensegment, dann ist der nächste Vektor gleich dem Vektor des Linienendes Punkt in jedem anderen Fall ist der nächste Vektor gleich dem Vektor Z.
Wenn wir nun den nächsten Vektor haben, müssen wir den Vektor vom Kreismittelpunkt zum Ipoint (dist-Vektor) finden. Es ist einfach, den nächsten Vektor vom Mittelvektor zu subtrahieren. Als nächstes prüfen Sie einfach, ob die Größe des Vektorabstands kleiner als der Kreisradius ist, wenn dies der Fall ist. Wenn sie nicht kollidieren, liegt keine Kollision vor.
Zum Schluss können wir einige Werte zum Auflösen der Kollision zurückgeben. Am einfachsten ist es, die Überlappung der Kollision (Subtrahieren des Radius von der Größe des Vektordistanzes) und die Kollisionsachse, ihren Vektor D, zurückzugeben. Der Schnittpunkt ist bei Bedarf auch der Vektor Z.
Wenn die Koordinaten der Linie Ax, Ay und Bx, By sind und die Kreismitte Cx, Cy ist, lauten die Linienformeln:
x = Ax * t + Bx * (1 - t)
y = Ay * t + By * (1 - t)
wobei 0 <= t <= 1
und der Kreis ist
(Cx - x) ^ 2 + (Cy - y) ^ 2 = R ^ 2
Wenn Sie die x- und y-Formeln der Linie in die Kreisformel einsetzen, erhalten Sie eine Gleichung zweiter Ordnung von t, und ihre Lösungen sind die Schnittpunkte (falls vorhanden). Wenn Sie einen Wert erhalten, der kleiner als 0 oder größer als 1 ist, ist dies keine Lösung, zeigt jedoch, dass die Linie in Richtung des Kreises zeigt.
Nur eine Ergänzung zu diesem Thread ... Unten ist eine Version des Codes von pahlevan, aber für C # / XNA und ein wenig aufgeräumt:
/// <summary>
/// Intersects a line and a circle.
/// </summary>
/// <param name="location">the location of the circle</param>
/// <param name="radius">the radius of the circle</param>
/// <param name="lineFrom">the starting point of the line</param>
/// <param name="lineTo">the ending point of the line</param>
/// <returns>true if the line and circle intersect each other</returns>
public static bool IntersectLineCircle(Vector2 location, float radius, Vector2 lineFrom, Vector2 lineTo)
{
float ab2, acab, h2;
Vector2 ac = location - lineFrom;
Vector2 ab = lineTo - lineFrom;
Vector2.Dot(ref ab, ref ab, out ab2);
Vector2.Dot(ref ac, ref ab, out acab);
float t = acab / ab2;
if (t < 0)
t = 0;
else if (t > 1)
t = 1;
Vector2 h = ((ab * t) + lineFrom) - location;
Vector2.Dot(ref h, ref h, out h2);
return (h2 <= (radius * radius));
}
' VB.NET - Code
Function CheckLineSegmentCircleIntersection(x1 As Double, y1 As Double, x2 As Double, y2 As Double, xc As Double, yc As Double, r As Double) As Boolean
Static xd As Double = 0.0F
Static yd As Double = 0.0F
Static t As Double = 0.0F
Static d As Double = 0.0F
Static dx_2_1 As Double = 0.0F
Static dy_2_1 As Double = 0.0F
dx_2_1 = x2 - x1
dy_2_1 = y2 - y1
t = ((yc - y1) * dy_2_1 + (xc - x1) * dx_2_1) / (dy_2_1 * dy_2_1 + dx_2_1 * dx_2_1)
If 0 <= t And t <= 1 Then
xd = x1 + t * dx_2_1
yd = y1 + t * dy_2_1
d = Math.Sqrt((xd - xc) * (xd - xc) + (yd - yc) * (yd - yc))
Return d <= r
Else
d = Math.Sqrt((xc - x1) * (xc - x1) + (yc - y1) * (yc - y1))
If d <= r Then
Return True
Else
d = Math.Sqrt((xc - x2) * (xc - x2) + (yc - y2) * (yc - y2))
If d <= r Then
Return True
Else
Return False
End If
End If
End If
End Function
Ich habe diese Funktion für iOS nach der Antwort von erstellt chmike
+ (NSArray *)intersectionPointsOfCircleWithCenter:(CGPoint)center withRadius:(float)radius toLinePoint1:(CGPoint)p1 andLinePoint2:(CGPoint)p2
{
NSMutableArray *intersectionPoints = [NSMutableArray array];
float Ax = p1.x;
float Ay = p1.y;
float Bx = p2.x;
float By = p2.y;
float Cx = center.x;
float Cy = center.y;
float R = radius;
// compute the euclidean distance between A and B
float LAB = sqrt( pow(Bx-Ax, 2)+pow(By-Ay, 2) );
// compute the direction vector D from A to B
float Dx = (Bx-Ax)/LAB;
float Dy = (By-Ay)/LAB;
// Now the line equation is x = Dx*t + Ax, y = Dy*t + Ay with 0 <= t <= 1.
// compute the value t of the closest point to the circle center (Cx, Cy)
float t = Dx*(Cx-Ax) + Dy*(Cy-Ay);
// This is the projection of C on the line from A to B.
// compute the coordinates of the point E on line and closest to C
float Ex = t*Dx+Ax;
float Ey = t*Dy+Ay;
// compute the euclidean distance from E to C
float LEC = sqrt( pow(Ex-Cx, 2)+ pow(Ey-Cy, 2) );
// test if the line intersects the circle
if( LEC < R )
{
// compute distance from t to circle intersection point
float dt = sqrt( pow(R, 2) - pow(LEC,2) );
// compute first intersection point
float Fx = (t-dt)*Dx + Ax;
float Fy = (t-dt)*Dy + Ay;
// compute second intersection point
float Gx = (t+dt)*Dx + Ax;
float Gy = (t+dt)*Dy + Ay;
[intersectionPoints addObject:[NSValue valueWithCGPoint:CGPointMake(Fx, Fy)]];
[intersectionPoints addObject:[NSValue valueWithCGPoint:CGPointMake(Gx, Gy)]];
}
// else test if the line is tangent to circle
else if( LEC == R ) {
// tangent point to circle is E
[intersectionPoints addObject:[NSValue valueWithCGPoint:CGPointMake(Ex, Ey)]];
}
else {
// line doesn't touch circle
}
return intersectionPoints;
}
Eine andere in c # (Teilkreisklasse). Getestet und wirkt wie ein Zauber.
public class Circle : IEquatable<Circle>
{
// ******************************************************************
// The center of a circle
private Point _center;
// The radius of a circle
private double _radius;
// ******************************************************************
/// <summary>
/// Find all intersections (0, 1, 2) of the circle with a line defined by its 2 points.
/// Using: http://math.stackexchange.com/questions/228841/how-do-i-calculate-the-intersections-of-a-straight-line-and-a-circle
/// Note: p is the Center.X and q is Center.Y
/// </summary>
/// <param name="linePoint1"></param>
/// <param name="linePoint2"></param>
/// <returns></returns>
public List<Point> GetIntersections(Point linePoint1, Point linePoint2)
{
List<Point> intersections = new List<Point>();
double dx = linePoint2.X - linePoint1.X;
if (dx.AboutEquals(0)) // Straight vertical line
{
if (linePoint1.X.AboutEquals(Center.X - Radius) || linePoint1.X.AboutEquals(Center.X + Radius))
{
Point pt = new Point(linePoint1.X, Center.Y);
intersections.Add(pt);
}
else if (linePoint1.X > Center.X - Radius && linePoint1.X < Center.X + Radius)
{
double x = linePoint1.X - Center.X;
Point pt = new Point(linePoint1.X, Center.Y + Math.Sqrt(Radius * Radius - (x * x)));
intersections.Add(pt);
pt = new Point(linePoint1.X, Center.Y - Math.Sqrt(Radius * Radius - (x * x)));
intersections.Add(pt);
}
return intersections;
}
// Line function (y = mx + b)
double dy = linePoint2.Y - linePoint1.Y;
double m = dy / dx;
double b = linePoint1.Y - m * linePoint1.X;
double A = m * m + 1;
double B = 2 * (m * b - m * _center.Y - Center.X);
double C = Center.X * Center.X + Center.Y * Center.Y - Radius * Radius - 2 * b * Center.Y + b * b;
double discriminant = B * B - 4 * A * C;
if (discriminant < 0)
{
return intersections; // there is no intersections
}
if (discriminant.AboutEquals(0)) // Tangeante (touch on 1 point only)
{
double x = -B / (2 * A);
double y = m * x + b;
intersections.Add(new Point(x, y));
}
else // Secant (touch on 2 points)
{
double x = (-B + Math.Sqrt(discriminant)) / (2 * A);
double y = m * x + b;
intersections.Add(new Point(x, y));
x = (-B - Math.Sqrt(discriminant)) / (2 * A);
y = m * x + b;
intersections.Add(new Point(x, y));
}
return intersections;
}
// ******************************************************************
// Get the center
[XmlElement("Center")]
public Point Center
{
get { return _center; }
set
{
_center = value;
}
}
// ******************************************************************
// Get the radius
[XmlElement]
public double Radius
{
get { return _radius; }
set { _radius = value; }
}
//// ******************************************************************
//[XmlArrayItemAttribute("DoublePoint")]
//public List<Point> Coordinates
//{
// get { return _coordinates; }
//}
// ******************************************************************
// Construct a circle without any specification
public Circle()
{
_center.X = 0;
_center.Y = 0;
_radius = 0;
}
// ******************************************************************
// Construct a circle without any specification
public Circle(double radius)
{
_center.X = 0;
_center.Y = 0;
_radius = radius;
}
// ******************************************************************
// Construct a circle with the specified circle
public Circle(Circle circle)
{
_center = circle._center;
_radius = circle._radius;
}
// ******************************************************************
// Construct a circle with the specified center and radius
public Circle(Point center, double radius)
{
_center = center;
_radius = radius;
}
// ******************************************************************
// Construct a circle based on one point
public Circle(Point center)
{
_center = center;
_radius = 0;
}
// ******************************************************************
// Construct a circle based on two points
public Circle(Point p1, Point p2)
{
Circle2Points(p1, p2);
}
Erforderlich:
using System;
namespace Mathematic
{
public static class DoubleExtension
{
// ******************************************************************
// Base on Hans Passant Answer on:
// http://stackoverflow.com/questions/2411392/double-epsilon-for-equality-greater-than-less-than-less-than-or-equal-to-gre
/// <summary>
/// Compare two double taking in account the double precision potential error.
/// Take care: truncation errors accumulate on calculation. More you do, more you should increase the epsilon.
public static bool AboutEquals(this double value1, double value2)
{
if (double.IsPositiveInfinity(value1))
return double.IsPositiveInfinity(value2);
if (double.IsNegativeInfinity(value1))
return double.IsNegativeInfinity(value2);
if (double.IsNaN(value1))
return double.IsNaN(value2);
double epsilon = Math.Max(Math.Abs(value1), Math.Abs(value2)) * 1E-15;
return Math.Abs(value1 - value2) <= epsilon;
}
// ******************************************************************
// Base on Hans Passant Answer on:
// http://stackoverflow.com/questions/2411392/double-epsilon-for-equality-greater-than-less-than-less-than-or-equal-to-gre
/// <summary>
/// Compare two double taking in account the double precision potential error.
/// Take care: truncation errors accumulate on calculation. More you do, more you should increase the epsilon.
/// You get really better performance when you can determine the contextual epsilon first.
/// </summary>
/// <param name="value1"></param>
/// <param name="value2"></param>
/// <param name="precalculatedContextualEpsilon"></param>
/// <returns></returns>
public static bool AboutEquals(this double value1, double value2, double precalculatedContextualEpsilon)
{
if (double.IsPositiveInfinity(value1))
return double.IsPositiveInfinity(value2);
if (double.IsNegativeInfinity(value1))
return double.IsNegativeInfinity(value2);
if (double.IsNaN(value1))
return double.IsNaN(value2);
return Math.Abs(value1 - value2) <= precalculatedContextualEpsilon;
}
// ******************************************************************
public static double GetContextualEpsilon(this double biggestPossibleContextualValue)
{
return biggestPossibleContextualValue * 1E-15;
}
// ******************************************************************
/// <summary>
/// Mathlab equivalent
/// </summary>
/// <param name="dividend"></param>
/// <param name="divisor"></param>
/// <returns></returns>
public static double Mod(this double dividend, double divisor)
{
return dividend - System.Math.Floor(dividend / divisor) * divisor;
}
// ******************************************************************
}
}
Kreis ist wirklich ein böser Kerl :) Ein guter Weg ist es also, echten Kreis zu vermeiden, wenn Sie können. Wenn Sie eine Kollisionsprüfung für Spiele durchführen, können Sie einige Vereinfachungen vornehmen und nur 3-Punkt-Produkte und einige Vergleiche durchführen.
Ich nenne das "fetten Punkt" oder "dünnen Kreis". seine Art einer Ellipse mit einem Radius von Null in einer Richtung parallel zu einem Segment. aber voller Radius in einer Richtung senkrecht zu einem Segment
Zunächst würde ich in Betracht ziehen, das Koordinatensystem umzubenennen und zu wechseln, um übermäßige Daten zu vermeiden:
s0s1 = B-A;
s0qp = C-A;
rSqr = r*r;
Zweitens muss der Index h in hvec2f als Vektor horizontale Operationen wie dot () / det () begünstigen. Das heißt, seine Komponenten müssen in einem separaten xmm-Register abgelegt werden, um ein Mischen / Hadding / Hsubing zu vermeiden. Und los geht's mit der performantesten Version der einfachsten Kollisionserkennung für 2D-Spiele:
bool fat_point_collides_segment(const hvec2f& s0qp, const hvec2f& s0s1, const float& rSqr) {
auto a = dot(s0s1, s0s1);
//if( a != 0 ) // if you haven't zero-length segments omit this, as it would save you 1 _mm_comineq_ss() instruction and 1 memory fetch
{
auto b = dot(s0s1, s0qp);
auto t = b / a; // length of projection of s0qp onto s0s1
//std::cout << "t = " << t << "\n";
if ((t >= 0) && (t <= 1)) //
{
auto c = dot(s0qp, s0qp);
auto r2 = c - a * t * t;
return (r2 <= rSqr); // true if collides
}
}
return false;
}
Ich bezweifle, dass Sie es weiter optimieren können. Ich verwende es für die Erkennung von Kollisionen bei Autorennen mit neuronalen Netzen, um Millionen von Millionen Iterationsschritten zu verarbeiten.
Wenn das Liniensegment den Kreis schneidet, aber nur geringfügig, damit es seinen Mittelpunkt nicht überschreitet, gibt diese Funktion dann nicht false zurück, wenn es true zurückgeben soll? Der t-Wert kann außerhalb des Bereichs 0..1 liegen.
Chris
1
Diese Java-Funktion gibt ein DVec2-Objekt zurück. Für den Mittelpunkt des Kreises, den Radius des Kreises und eine Linie wird ein DVec2 benötigt .
public static DVec2 CircLine(DVec2 C, double r, Line line)
{
DVec2 A = line.p1;
DVec2 B = line.p2;
DVec2 P;
DVec2 AC = new DVec2( C );
AC.sub(A);
DVec2 AB = new DVec2( B );
AB.sub(A);
double ab2 = AB.dot(AB);
double acab = AC.dot(AB);
double t = acab / ab2;
if (t < 0.0)
t = 0.0;
else if (t > 1.0)
t = 1.0;
//P = A + t * AB;
P = new DVec2( AB );
P.mul( t );
P.add( A );
DVec2 H = new DVec2( P );
H.sub( C );
double h2 = H.dot(H);
double r2 = r * r;
if(h2 > r2)
return null;
else
return P;
}
Hier ist meine Lösung in TypeScript, die der von @Mizipzor vorgeschlagenen Idee (unter Verwendung der Projektion) folgt:
/**
* Determines whether a line segment defined by a start and end point intersects with a sphere defined by a center point and a radius
* @param a the start point of the line segment
* @param b the end point of the line segment
* @param c the center point of the sphere
* @param r the radius of the sphere
*/exportfunction lineSphereIntersects(
a:IPoint,
b:IPoint,
c:IPoint,
r: number
): boolean {// find the three sides of the triangle formed by the three pointsconst ab: number = distance(a, b);const ac: number = distance(a, c);const bc: number = distance(b, c);// check to see if either ends of the line segment are inside of the sphereif(ac < r || bc < r){returntrue;}// find the angle between the line segment and the center of the sphereconst numerator: number =Math.pow(ac,2)+Math.pow(ab,2)-Math.pow(bc,2);const denominator: number =2* ac * ab;const cab: number =Math.acos(numerator / denominator);// find the distance from the center of the sphere and the line segmentconst cd: number =Math.sin(cab)* ac;// if the radius is at least as long as the distance between the center and the lineif(r >= cd){// find the distance between the line start and the point on the line closest to// the center of the sphereconst ad: number =Math.cos(cab)* ac;// intersection occurs when the point on the line closest to the sphere center is// no further away than the end of the linereturn ad <= ab;}returnfalse;}exportfunction distance(a:IPoint, b:IPoint): number {returnMath.sqrt(Math.pow(b.z - a.z,2)+Math.pow(b.y - a.y,2)+Math.pow(b.x - a.x,2));}exportinterfaceIPoint{
x: number;
y: number;
z: number;}
Ich weiß, es ist schon eine Weile her, seit dieser Thread offen war. Aus der Antwort von chmike und verbessert von Aqib Mumtaz. Sie geben eine gute Antwort, funktionieren aber nur für eine unendliche Linie, wie Aqib sagte. Also füge ich einige Vergleiche hinzu, um zu wissen, ob das Liniensegment den Kreis berührt. Ich schreibe es in Python.
def LineIntersectCircle(c, r, p1, p2):
#p1 is the first line point
#p2 is the second line point
#c is the circle's center
#r is the circle's radius
p3 = [p1[0]-c[0], p1[1]-c[1]]
p4 = [p2[0]-c[0], p2[1]-c[1]]
m = (p4[1] - p3[1]) / (p4[0] - p3[0])
b = p3[1] - m * p3[0]
underRadical = math.pow(r,2)*math.pow(m,2) + math.pow(r,2) - math.pow(b,2)
if (underRadical < 0):
print("NOT")
else:
t1 = (-2*m*b+2*math.sqrt(underRadical)) / (2 * math.pow(m,2) + 2)
t2 = (-2*m*b-2*math.sqrt(underRadical)) / (2 * math.pow(m,2) + 2)
i1 = [t1+c[0], m * t1 + b + c[1]]
i2 = [t2+c[0], m * t2 + b + c[1]]
if p1[0] > p2[0]: #Si el punto 1 es mayor al 2 en X
if (i1[0] < p1[0]) and (i1[0] > p2[0]): #Si el punto iX esta entre 2 y 1 en X
if p1[1] > p2[1]: #Si el punto 1 es mayor al 2 en Y
if (i1[1] < p1[1]) and (i1[1] > p2[1]): #Si el punto iy esta entre 2 y 1
print("Intersection")
if p1[1] < p2[1]: #Si el punto 2 es mayo al 2 en Y
if (i1[1] > p1[1]) and (i1[1] < p2[1]): #Si el punto iy esta entre 1 y 2
print("Intersection")
if p1[0] < p2[0]: #Si el punto 2 es mayor al 1 en X
if (i1[0] > p1[0]) and (i1[0] < p2[0]): #Si el punto iX esta entre 1 y 2 en X
if p1[1] > p2[1]: #Si el punto 1 es mayor al 2 en Y
if (i1[1] < p1[1]) and (i1[1] > p2[1]): #Si el punto iy esta entre 2 y 1
print("Intersection")
if p1[1] < p2[1]: #Si el punto 2 es mayo al 2 en Y
if (i1[1] > p1[1]) and (i1[1] < p2[1]): #Si el punto iy esta entre 1 y 2
print("Intersection")
if p1[0] > p2[0]: #Si el punto 1 es mayor al 2 en X
if (i2[0] < p1[0]) and (i2[0] > p2[0]): #Si el punto iX esta entre 2 y 1 en X
if p1[1] > p2[1]: #Si el punto 1 es mayor al 2 en Y
if (i2[1] < p1[1]) and (i2[1] > p2[1]): #Si el punto iy esta entre 2 y 1
print("Intersection")
if p1[1] < p2[1]: #Si el punto 2 es mayo al 2 en Y
if (i2[1] > p1[1]) and (i2[1] < p2[1]): #Si el punto iy esta entre 1 y 2
print("Intersection")
if p1[0] < p2[0]: #Si el punto 2 es mayor al 1 en X
if (i2[0] > p1[0]) and (i2[0] < p2[0]): #Si el punto iX esta entre 1 y 2 en X
if p1[1] > p2[1]: #Si el punto 1 es mayor al 2 en Y
if (i2[1] < p1[1]) and (i2[1] > p2[1]): #Si el punto iy esta entre 2 y 1
print("Intersection")
if p1[1] < p2[1]: #Si el punto 2 es mayo al 2 en Y
if (i2[1] > p1[1]) and (i2[1] < p2[1]): #Si el punto iy esta entre 1 y 2
print("Intersection")
Hier ist eine Lösung in Golang geschrieben. Die Methode ähnelt einigen anderen hier veröffentlichten Antworten, ist jedoch nicht ganz dieselbe. Es ist einfach zu implementieren und wurde getestet. Hier sind die Schritte:
Übersetzen Sie die Koordinaten so, dass sich der Kreis am Ursprung befindet.
Drücken Sie das Liniensegment als parametrisierte Funktionen von t für die x- und y-Koordinaten aus. Wenn t 0 ist, sind die Werte der Funktion ein Endpunkt des Segments, und wenn t 1 ist, sind die Werte der Funktion der andere Endpunkt.
Lösen Sie nach Möglichkeit die quadratische Gleichung, die sich aus den einschränkenden Werten von t ergibt, die x, y-Koordinaten mit Abständen vom Ursprung ergeben, die dem Radius des Kreises entsprechen.
Werfen Sie Lösungen aus, bei denen t <0 oder> 1 ist (<= 0 oder> = 1 für ein offenes Segment). Diese Punkte sind nicht im Segment enthalten.
Zurück in die ursprünglichen Koordinaten übersetzen.
Hier werden die Werte für A, B und C für das Quadrat abgeleitet, wobei (n-et) und (m-dt) die Gleichungen für die x- bzw. y-Koordinaten der Linie sind. r ist der Radius des Kreises.
(n-et)(n-et) + (m-dt)(m-dt) = rr
nn - 2etn + etet + mm - 2mdt + dtdt = rr
(ee+dd)tt - 2(en + dm)t + nn + mm - rr = 0
Daher ist A = ee + dd, B = - 2 (en + dm) und C = nn + mm - rr.
Hier ist der Golang-Code für die Funktion:
package geom
import (
"math"
)
// SegmentCircleIntersection return points of intersection between a circle and
// a line segment. The Boolean intersects returns true if one or
// more solutions exist. If only one solution exists,
// x1 == x2 and y1 == y2.
// s1x and s1y are coordinates for one end point of the segment, and
// s2x and s2y are coordinates for the other end of the segment.
// cx and cy are the coordinates of the center of the circle and
// r is the radius of the circle.
func SegmentCircleIntersection(s1x, s1y, s2x, s2y, cx, cy, r float64) (x1, y1, x2, y2 float64, intersects bool) {
// (n-et) and (m-dt) are expressions for the x and y coordinates
// of a parameterized line in coordinates whose origin is the
// center of the circle.
// When t = 0, (n-et) == s1x - cx and (m-dt) == s1y - cy
// When t = 1, (n-et) == s2x - cx and (m-dt) == s2y - cy.
n := s2x - cx
m := s2y - cy
e := s2x - s1x
d := s2y - s1y
// lineFunc checks if the t parameter is in the segment and if so
// calculates the line point in the unshifted coordinates (adds back
// cx and cy.
lineFunc := func(t float64) (x, y float64, inBounds bool) {
inBounds = t >= 0 && t <= 1 // Check bounds on closed segment
// To check bounds for an open segment use t > 0 && t < 1
if inBounds { // Calc coords for point in segment
x = n - e*t + cx
y = m - d*t + cy
}
return
}
// Since we want the points on the line distance r from the origin,
// (n-et)(n-et) + (m-dt)(m-dt) = rr.
// Expanding and collecting terms yeilds the following quadratic equation:
A, B, C := e*e+d*d, -2*(e*n+m*d), n*n+m*m-r*r
D := B*B - 4*A*C // discriminant of quadratic
if D < 0 {
return // No solution
}
D = math.Sqrt(D)
var p1In, p2In bool
x1, y1, p1In = lineFunc((-B + D) / (2 * A)) // First root
if D == 0.0 {
intersects = p1In
x2, y2 = x1, y1
return // Only possible solution, quadratic has one root.
}
x2, y2, p2In = lineFunc((-B - D) / (2 * A)) // Second root
intersects = p1In || p2In
if p1In == false { // Only x2, y2 may be valid solutions
x1, y1 = x2, y2
} else if p2In == false { // Only x1, y1 are valid solutions
x2, y2 = x1, y1
}
return
}
Ich habe es mit dieser Funktion getestet, die bestätigt, dass sich Lösungspunkte innerhalb des Liniensegments und auf dem Kreis befinden. Es erstellt ein Testsegment und fegt es um den angegebenen Kreis:
package geom_test
import (
"testing"
. "**put your package path here**"
)
func CheckEpsilon(t *testing.T, v, epsilon float64, message string) {
if v > epsilon || v < -epsilon {
t.Error(message, v, epsilon)
t.FailNow()
}
}
func TestSegmentCircleIntersection(t *testing.T) {
epsilon := 1e-10 // Something smallish
x1, y1 := 5.0, 2.0 // segment end point 1
x2, y2 := 50.0, 30.0 // segment end point 2
cx, cy := 100.0, 90.0 // center of circle
r := 80.0
segx, segy := x2-x1, y2-y1
testCntr, solutionCntr := 0, 0
for i := -100; i < 100; i++ {
for j := -100; j < 100; j++ {
testCntr++
s1x, s2x := x1+float64(i), x2+float64(i)
s1y, s2y := y1+float64(j), y2+float64(j)
sc1x, sc1y := s1x-cx, s1y-cy
seg1Inside := sc1x*sc1x+sc1y*sc1y < r*r
sc2x, sc2y := s2x-cx, s2y-cy
seg2Inside := sc2x*sc2x+sc2y*sc2y < r*r
p1x, p1y, p2x, p2y, intersects := SegmentCircleIntersection(s1x, s1y, s2x, s2y, cx, cy, r)
if intersects {
solutionCntr++
//Check if points are on circle
c1x, c1y := p1x-cx, p1y-cy
deltaLen1 := (c1x*c1x + c1y*c1y) - r*r
CheckEpsilon(t, deltaLen1, epsilon, "p1 not on circle")
c2x, c2y := p2x-cx, p2y-cy
deltaLen2 := (c2x*c2x + c2y*c2y) - r*r
CheckEpsilon(t, deltaLen2, epsilon, "p2 not on circle")
// Check if points are on the line through the line segment
// "cross product" of vector from a segment point to the point
// and the vector for the segment should be near zero
vp1x, vp1y := p1x-s1x, p1y-s1y
crossProd1 := vp1x*segy - vp1y*segx
CheckEpsilon(t, crossProd1, epsilon, "p1 not on line ")
vp2x, vp2y := p2x-s1x, p2y-s1y
crossProd2 := vp2x*segy - vp2y*segx
CheckEpsilon(t, crossProd2, epsilon, "p2 not on line ")
// Check if point is between points s1 and s2 on line
// This means the sign of the dot prod of the segment vector
// and point to segment end point vectors are opposite for
// either end.
wp1x, wp1y := p1x-s2x, p1y-s2y
dp1v := vp1x*segx + vp1y*segy
dp1w := wp1x*segx + wp1y*segy
if (dp1v < 0 && dp1w < 0) || (dp1v > 0 && dp1w > 0) {
t.Error("point not contained in segment ", dp1v, dp1w)
t.FailNow()
}
wp2x, wp2y := p2x-s2x, p2y-s2y
dp2v := vp2x*segx + vp2y*segy
dp2w := wp2x*segx + wp2y*segy
if (dp2v < 0 && dp2w < 0) || (dp2v > 0 && dp2w > 0) {
t.Error("point not contained in segment ", dp2v, dp2w)
t.FailNow()
}
if s1x == s2x && s2y == s1y { //Only one solution
// Test that one end of the segment is withing the radius of the circle
// and one is not
if seg1Inside && seg2Inside {
t.Error("Only one solution but both line segment ends inside")
t.FailNow()
}
if !seg1Inside && !seg2Inside {
t.Error("Only one solution but both line segment ends outside")
t.FailNow()
}
}
} else { // No intersection, check if both points outside or inside
if (seg1Inside && !seg2Inside) || (!seg1Inside && seg2Inside) {
t.Error("No solution but only one point in radius of circle")
t.FailNow()
}
}
}
}
t.Log("Tested ", testCntr, " examples and found ", solutionCntr, " solutions.")
}
Hier ist die Ausgabe des Tests:
=== RUN TestSegmentCircleIntersection
--- PASS: TestSegmentCircleIntersection (0.00s)
geom_test.go:105: Tested 40000 examples and found 7343 solutions.
Schließlich ist das Verfahren leicht auf den Fall eines Strahls erweiterbar, der an einem Punkt beginnt, durch den anderen geht und sich bis ins Unendliche erstreckt, indem nur getestet wird, ob t> 0 oder t <1, aber nicht beide.
Ich brauchte das nur, also kam ich auf diese Lösung. Die Sprache ist maxscript, sollte aber leicht in eine andere Sprache übersetzt werden können. sideA, sideB und CircleRadius sind Skalare, die restlichen Variablen sind Punkte wie [x, y, z]. Ich gehe davon aus, dass z = 0 ist, um auf der Ebene XY zu lösen
fn projectPoint p1 p2 p3 = --project p1 perpendicular to the line p2-p3
(
local v= normalize (p3-p2)
local p= (p1-p2)
p2+((dot v p)*v)
)
fn findIntersectionLineCircle CircleCenter CircleRadius LineP1 LineP2=
(
pp=projectPoint CircleCenter LineP1 LineP2
sideA=distance pp CircleCenter
--use pythagoras to solve the third side
sideB=sqrt(CircleRadius^2-sideA^2) -- this will return NaN if they don't intersect
IntersectV=normalize (pp-CircleCenter)
perpV=[IntersectV.y,-IntersectV.x,IntersectV.z]
--project the point to both sides to find the solutions
solution1=pp+(sideB*perpV)
solution2=pp-(sideB*perpV)
return #(solution1,solution2)
)
def check_line_segment_circle_intersection(line, point, radious):
""" Checks whether a point intersects with a line defined by two points.
A `point` is list with two values: [2, 3]
A `line` is list with two points: [point1, point2]
"""
line_distance = distance(line[0], line[1])
distance_start_to_point = distance(line[0], point)
distance_end_to_point = distance(line[1], point)
if (distance_start_to_point <= radious or distance_end_to_point <= radious):
return True
# angle between line and point with law of cosines
numerator = (math.pow(distance_start_to_point, 2)
+ math.pow(line_distance, 2)
- math.pow(distance_end_to_point, 2))
denominator = 2 * distance_start_to_point * line_distance
ratio = numerator / denominator
ratio = ratio if ratio <= 1 else 1 # To account for float errors
ratio = ratio if ratio >= -1 else -1 # To account for float errors
angle = math.acos(ratio)
# distance from the point to the line with sin projection
distance_line_to_point = math.sin(angle) * distance_start_to_point
if distance_line_to_point <= radious:
point_projection_in_line = math.cos(angle) * distance_start_to_point
# Intersection occurs whent the point projection in the line is less
# than the line distance and positive
return point_projection_in_line <= line_distance and point_projection_in_line >= 0
return False
def distance(point1, point2):
return math.sqrt(
math.pow(point1[1] - point2[1], 2) +
math.pow(point1[0] - point2[0], 2)
)
Function lineCircleCollision(p1,p2,c,r,precision){
Let dx = (p2.x-p1.x)/precision
Let dy = (p2.y-p1.y)/precision
Let collision=false
For(let i = 0;i<precision:i++){
If(Math.sqrt((p1.x+dx*i-c.x)**2+(p1.y+dy*i-c.y)**2).<r {
Collision=true
}
}
Sie können X gleichmäßig verteilte Punkte von der Linie nehmen, und wenn sich irgendwelche innerhalb des Kreises befinden, liegt eine Kollision vor
Antworten:
Nehmen
Berechnen Sie:
d = L - E (Richtungsvektor des Strahls von Anfang bis Ende)
f = E - C (Vektor von der Mittelkugel zum Strahlstart)
Dann wird der Schnittpunkt gefunden durch ..
Einstecken:
P = E + t * d
Dies ist eine parametrische Gleichung:
P x = E x + td x
P y = E y + td y
in
(x - h) 2 + (y - k) 2 = r 2
(h, k) = Kreismittelpunkt.
bekommen:
x 2 - 2xh + h 2 + y 2 - 2yk + k 2 - r 2 = 0
x = e x + td x
y = e y + td y
(e x + td x ) 2 - 2 (e x + td x ) h + h 2 + (e y + td y ) 2 - 2 (e y + td y ) k + k 2 - r 2 = 0
e x 2 + 2e x td x + t 2 d x 2 - 2e x h - 2td x h + h 2 + e y 2 + 2e y td y + t 2 d y 2 - 2e y k - 2td y k + k 2 - r 2 = 0
t 2 (d x 2 + d y 2 ) + 2t (e x d x + e y d y - d x h - d y k) + e x 2 + e y 2 - 2e x h - 2e y k + h 2 + k 2 - r 2 = 0
t 2 (_d * _d) + 2t (_e * _d - _d * _c) + _e * _e - 2 (_e * _c) + _c * _c - r 2 = 0
* wobei _d der Vektor d ist und * ist das Punktprodukt. *
t 2 (_d * _d) + 2t (_d * (_e - _c)) + (_e - _c) * (_e - _c) - r 2 = 0
t 2 (_d * _d) + 2t (_d * _f) + _f * _f - r 2 = 0 lassen
Wir erhalten also:
t 2 * (d DOT d) + 2t * (f DOT d) + (f DOT f - r 2 ) = 0
Lösen Sie also die quadratische Gleichung:
quelle
P = E + t * d
Was istt
?Niemand scheint über Projektion nachzudenken, bin ich hier völlig aus der Bahn geraten?
Projizieren Sie den Vektor
AC
aufAB
. Der projizierte VektorAD
gibt den neuen Punkt anD
.Wenn der Abstand zwischen
D
undC
kleiner als (oder gleich) istR
, haben wir einen Schnittpunkt.So was:
quelle
CD
ist eine Projektion, sie ist per Definition senkrecht.Ich würde den Algorithmus verwenden, um den Abstand zwischen einem Punkt (Kreismittelpunkt) und einer Linie (Linie AB) zu berechnen. Dies kann dann verwendet werden, um die Schnittpunkte der Linie mit dem Kreis zu bestimmen.
Angenommen, wir haben die Punkte A, B, C. Ax und Ay sind die x- und y-Komponenten der A-Punkte. Gleiches gilt für B und C. Der Skalar R ist der Kreisradius.
Dieser Algorithmus erfordert, dass A, B und C unterschiedliche Punkte sind und dass R nicht 0 ist.
Hier ist der Algorithmus
quelle
t+dt
undt-dt
auf der Linie liegt.t
ist der Punkt auf der Linie, der dem Mittelpunkt des Kreises am nächsten liegt. Die Schnittpunkte mit dem Kreis befinden sich in einem symmetrischen Abstand vont
. Die Schnittpunkte liegen bei "Abständen"t-dt
undt+dt
. Ich habe die Entfernung angegeben, weil es nicht die euklidische Entfernung ist. Um den euklidischen Abstand vonA
wo zu erhaltent=0
, müssen Sie den Wert mit multiplizierenLAB
.t=0
. Punkt B beit=LAB
. Wenn beide Schnittpunkte (t1=t-td
undt2=t+td
) negative Werte haben, liegen die Schnittpunkte außerhalb des Abschnitts (hinter Punkt A von der Abschnittsseite des Punkts aus gesehen). Wenn t1 und t2 größer als LAB sind, sind sie auch draußen (diesmal hinter dem B-Punkt). Der Schnittpunkt t1 (oder t2) tritt zwischen A und B nur auf, wenn t1 (oder t2) zwischen 0 und LAB liegt.Okay, ich werde dir keinen Code geben, aber da du dies markiert hast AlgorithmusIch denke nicht, dass dir das etwas ausmachen wird. Zuerst müssen Sie einen Vektor senkrecht zur Linie erhalten.
Sie haben eine unbekannte Variable in
y = ax + c
(c
wird unbekannt sein ).Um dies zu lösen, berechnen Sie ihren Wert, wenn die Linie durch den Mittelpunkt des Kreises verläuft.
Das heißt,
Stecken Sie die Position des Kreismittelpunkts in die Liniengleichung und lösen Sie nach
c
.Berechnen Sie dann den Schnittpunkt der ursprünglichen Linie und ihrer Normalen.
Dadurch erhalten Sie den Punkt auf der Linie, der dem Kreis am nächsten liegt.
Berechnen Sie den Abstand zwischen diesem Punkt und dem Kreismittelpunkt (unter Verwendung der Größe des Vektors).
Wenn dies kleiner als der Radius des Kreises ist - voila, haben wir einen Schnittpunkt!
quelle
Eine andere Methode verwendet die Dreiecks-ABC-Flächenformel. Der Schnittpunkttest ist einfacher und effizienter als die Projektionsmethode, aber das Finden der Koordinaten des Schnittpunkts erfordert mehr Arbeit. Zumindest wird es bis zu dem Punkt verzögert, an dem es erforderlich ist.
Die Formel zur Berechnung der Dreiecksfläche lautet: area = bh / 2
Dabei ist b die Basislänge und h die Höhe. Wir haben das Segment AB als Basis gewählt, damit h der kürzeste Abstand von C, dem Kreismittelpunkt, zur Linie ist.
Da die Dreiecksfläche auch durch ein Vektorpunktprodukt berechnet werden kann, können wir h bestimmen.
UPDATE 1:
Sie könnten den Code mithilfe der schnellen Optimierung inverse Quadratwurzel - Berechnung beschrieben hier eine gute Näherung von 1 / LAB zu erhalten.
Die Berechnung des Schnittpunkts ist nicht so schwierig. Hier kommt's
Wenn h = R ist, tangiert die Linie AB den Kreis und der Wert dt = 0 und E = F. Die Punktkoordinaten sind die von E und F.
Sie sollten überprüfen, ob A von B abweicht und die Segmentlänge nicht null ist, wenn dies in Ihrer Anwendung auftreten kann.
quelle
Ich schrieb ein kleines Skript, um die Schnittmenge zu testen, indem ich den Mittelpunkt des Kreises auf die Linie projizierte.
http://jsfiddle.net/ercang/ornh3594/1/
Wenn Sie die Kollision mit dem Segment überprüfen müssen, müssen Sie auch den Abstand des Kreismittelpunkts zu Start- und Endpunkten berücksichtigen.
https://jsfiddle.net/ercang/menp0991/
quelle
Diese Lösung, die ich gefunden habe, schien etwas einfacher zu befolgen als einige der anderen.
Nehmen:
Ich würde für die Gleichung der Linie in Steigungsschnittform lösen. Ich wollte mich jedoch nicht mit schwierigen Gleichungen
c
als Punkt befassen müssen , also habe ich das Koordinatensystem einfach so verschoben, dass der Kreis bei ist0,0
Übrigens, wenn ich Punkte voneinander subtrahiere
x
, subtrahiere ich die 's und subtrahiere dann diey
' s und setze sie in einen neuen Punkt, nur für den Fall, dass jemand es nicht wusste.Wie auch immer, ich löse jetzt nach der Gleichung der Linie mit
p3
undp4
:OK. Jetzt muss ich diese Gleichungen gleich setzen. Zuerst muss ich die Kreisgleichung für lösen
x
Dann setze ich sie gleich:
Und lösen Sie für die quadratische Gleichung (
0 = ax^2 + bx + c
):Jetzt habe ich meine
a
,b
undc
.Also habe ich das in die quadratische Formel eingefügt:
Und durch Werte ersetzen, dann so weit wie möglich vereinfachen:
Dies ist fast so weit wie es vereinfachen wird. Zum Schluss trennen Sie die Gleichungen mit ±:
Stecken Sie dann einfach das Ergebnis dieser beiden Gleichungen in das
x
Inmx + b
. Aus Gründen der Übersichtlichkeit habe ich JavaScript-Code geschrieben, um zu zeigen, wie dies verwendet wird:Ich hoffe das hilft!
PS Wenn jemand Fehler findet oder Vorschläge hat, kommentieren Sie diese bitte. Ich bin sehr neu und begrüße alle Hilfe / Vorschläge.
quelle
underRadical
extra ')'Sie können einen Punkt auf einer unendlichen Linie finden, der dem Kreismittelpunkt am nächsten liegt, indem Sie den Vektor AC auf den Vektor AB projizieren. Berechnen Sie den Abstand zwischen diesem Punkt und dem Kreismittelpunkt. Wenn es größer als R ist, gibt es keinen Schnittpunkt. Wenn der Abstand gleich R ist, ist die Linie eine Tangente des Kreises und der Punkt, der dem Kreismittelpunkt am nächsten liegt, ist tatsächlich der Schnittpunkt. Wenn der Abstand kleiner als R ist, gibt es 2 Schnittpunkte. Sie liegen im gleichen Abstand von dem Punkt, der dem Kreismittelpunkt am nächsten liegt. Diese Entfernung kann leicht mit dem Satz von Pythagoras berechnet werden. Hier ist ein Algorithmus im Pseudocode:
BEARBEITEN: Code hinzugefügt, um zu überprüfen, ob gefundene Schnittpunkte tatsächlich innerhalb des Liniensegments liegen.
quelle
Seltsamerweise kann ich antworten, aber nicht kommentieren ... Ich mochte Multitaskpros Ansatz, alles so zu verschieben, dass der Mittelpunkt des Kreises auf den Ursprung fällt. Leider gibt es zwei Probleme in seinem Code. Zuerst müssen Sie im Teil unter der Quadratwurzel die doppelte Kraft entfernen. So nicht:
var underRadical = Math.pow((Math.pow(r,2)*(Math.pow(m,2)+1)),2)-Math.pow(b,2));
aber:
var underRadical = Math.pow(r,2)*(Math.pow(m,2)+1)) - Math.pow(b,2);
In den endgültigen Koordinaten vergisst er, die Lösung zurückzuschieben. So nicht:
var i1 = {x:t1,y:m*t1+b}
aber:
var i1 = {x:t1+c.x, y:m*t1+b+c.y};
Die ganze Funktion wird dann:
quelle
Hier brauchen Sie etwas Mathe:
Angenommen, A = (Xa, Ya), B = (Xb, Yb) und C = (Xc, Yc). Jeder Punkt auf der Linie von A nach B hat Koordinaten (Alpha * Xa + (1-Alpha) Xb, Alpha Ya + (1-Alpha) * Yb) = P.
Wenn der Punkt P den Abstand R zu C hat, muss er sich auf dem Kreis befinden. Was Sie wollen, ist zu lösen
das ist
Wenn Sie die ABC-Formel auf diese Gleichung anwenden, um sie für Alpha zu lösen, und die Koordinaten von P unter Verwendung der Lösung (en) für Alpha berechnen, erhalten Sie die Schnittpunkte, falls vorhanden.
quelle
Wenn Sie den Abstand zwischen dem Mittelpunkt der Kugel (da es sich um 3D handelt, meine ich Kugel und nicht Kreis) und der Linie finden, prüfen Sie, ob dieser Abstand kleiner ist als der Radius, der den Trick ausführt.
Der Kollisionspunkt ist offensichtlich der nächstgelegene Punkt zwischen der Linie und der Kugel (der berechnet wird, wenn Sie den Abstand zwischen der Kugel und der Linie berechnen).
Abstand zwischen einem Punkt und einer Linie:
http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html
quelle
Hier ist eine Implementierung in Javascript. Mein Ansatz ist es, zuerst das Liniensegment in eine unendliche Linie umzuwandeln und dann die Schnittpunkte zu finden. Von dort aus überprüfe ich, ob sich die gefundenen Punkte auf dem Liniensegment befinden. Der Code ist gut dokumentiert, Sie sollten in der Lage sein, mitzumachen.
Sie können den Code hier in dieser Live-Demo ausprobieren . Der Code wurde aus meinem Algorithmus-Repo übernommen .
quelle
In diesem Beitrag wird die Kollision von Kreislinien überprüft, indem der Abstand zwischen Kreismittelpunkt und Punkt auf dem Liniensegment (Ipoint) überprüft wird, der den Schnittpunkt zwischen normalem N (Bild 2) vom Kreismittelpunkt zum Liniensegment darstellt.
( https://i.stack.imgur.com/3o6do.png )
Auf Bild 1 sind ein Kreis und eine Linie gezeigt, Vektor A Punkt zu Linie Startpunkt, Vektor B Punkt zu Linie Endpunkt, Vektor C Punkt zu Kreismittelpunkt. Nun müssen wir den Vektor E (vom Linienstartpunkt zum Kreismittelpunkt) und den Vektor D (vom Linienstartpunkt zum Linienendpunkt) finden. Diese Berechnung ist in Bild 1 dargestellt.
( https://i.stack.imgur.com/7098a.png )
In Bild 2 können wir sehen, dass der Vektor E durch das "Punktprodukt" des Vektors E und des Einheitsvektors D auf den Vektor D projiziert wird. Das Ergebnis des Punktprodukts ist der Skalar Xp, der den Abstand zwischen dem Linienstartpunkt und dem Schnittpunkt (Ipoint) von darstellt Vektor N und Vektor D. Der nächste Vektor X wird durch Multiplizieren des Einheitsvektors D und des Skalars Xp gefunden.
Jetzt müssen wir den Vektor Z (Vektor zu Ipoint) finden, seine einfache Vektoraddition von Vektor A (Startpunkt auf Linie) und Vektor X. Als nächstes müssen wir uns mit Sonderfällen befassen, die wir überprüfen müssen, ob Ipoint auf Liniensegment ist, wenn Wir müssen nicht herausfinden, ob es links oder rechts davon ist. Wir werden den Vektor verwenden, der am nächsten ist, um zu bestimmen, welcher Punkt dem Kreis am nächsten liegt.
( https://i.stack.imgur.com/p9WIr.png )
Wenn die Projektion Xp negativ ist, ist der Punkt links vom Liniensegment, der Vektor am nächsten ist gleich dem Vektor des Linienstartpunkts, wenn die Projektion Xp größer als die Größe des Vektors D ist, dann ist der Punkt rechts vom Liniensegment, dann ist der nächste Vektor gleich dem Vektor des Linienendes Punkt in jedem anderen Fall ist der nächste Vektor gleich dem Vektor Z.
Wenn wir nun den nächsten Vektor haben, müssen wir den Vektor vom Kreismittelpunkt zum Ipoint (dist-Vektor) finden. Es ist einfach, den nächsten Vektor vom Mittelvektor zu subtrahieren. Als nächstes prüfen Sie einfach, ob die Größe des Vektorabstands kleiner als der Kreisradius ist, wenn dies der Fall ist. Wenn sie nicht kollidieren, liegt keine Kollision vor.
( https://i.stack.imgur.com/QJ63q.png )
Zum Schluss können wir einige Werte zum Auflösen der Kollision zurückgeben. Am einfachsten ist es, die Überlappung der Kollision (Subtrahieren des Radius von der Größe des Vektordistanzes) und die Kollisionsachse, ihren Vektor D, zurückzugeben. Der Schnittpunkt ist bei Bedarf auch der Vektor Z.
quelle
Wenn die Koordinaten der Linie Ax, Ay und Bx, By sind und die Kreismitte Cx, Cy ist, lauten die Linienformeln:
x = Ax * t + Bx * (1 - t)
y = Ay * t + By * (1 - t)
wobei 0 <= t <= 1
und der Kreis ist
(Cx - x) ^ 2 + (Cy - y) ^ 2 = R ^ 2
Wenn Sie die x- und y-Formeln der Linie in die Kreisformel einsetzen, erhalten Sie eine Gleichung zweiter Ordnung von t, und ihre Lösungen sind die Schnittpunkte (falls vorhanden). Wenn Sie einen Wert erhalten, der kleiner als 0 oder größer als 1 ist, ist dies keine Lösung, zeigt jedoch, dass die Linie in Richtung des Kreises zeigt.
quelle
Nur eine Ergänzung zu diesem Thread ... Unten ist eine Version des Codes von pahlevan, aber für C # / XNA und ein wenig aufgeräumt:
quelle
Ray.Intersects(BoundingSphere)
quelle
Ich habe diese Funktion für iOS nach der Antwort von erstellt
chmike
quelle
Eine andere in c # (Teilkreisklasse). Getestet und wirkt wie ein Zauber.
Erforderlich:
quelle
Hier ist eine gute Lösung in JavaScript (mit allen erforderlichen Mathematik- und Live-Illustrationen) https://bl.ocks.org/milkbread/11000965
Die
is_on
Funktion in dieser Lösung muss jedoch geändert werden:quelle
Kreis ist wirklich ein böser Kerl :) Ein guter Weg ist es also, echten Kreis zu vermeiden, wenn Sie können. Wenn Sie eine Kollisionsprüfung für Spiele durchführen, können Sie einige Vereinfachungen vornehmen und nur 3-Punkt-Produkte und einige Vergleiche durchführen.
Ich nenne das "fetten Punkt" oder "dünnen Kreis". seine Art einer Ellipse mit einem Radius von Null in einer Richtung parallel zu einem Segment. aber voller Radius in einer Richtung senkrecht zu einem Segment
Zunächst würde ich in Betracht ziehen, das Koordinatensystem umzubenennen und zu wechseln, um übermäßige Daten zu vermeiden:
Zweitens muss der Index h in hvec2f als Vektor horizontale Operationen wie dot () / det () begünstigen. Das heißt, seine Komponenten müssen in einem separaten xmm-Register abgelegt werden, um ein Mischen / Hadding / Hsubing zu vermeiden. Und los geht's mit der performantesten Version der einfachsten Kollisionserkennung für 2D-Spiele:
Ich bezweifle, dass Sie es weiter optimieren können. Ich verwende es für die Erkennung von Kollisionen bei Autorennen mit neuronalen Netzen, um Millionen von Millionen Iterationsschritten zu verarbeiten.
quelle
Diese Java-Funktion gibt ein DVec2-Objekt zurück. Für den Mittelpunkt des Kreises, den Radius des Kreises und eine Linie wird ein DVec2 benötigt .
quelle
Hier ist meine Lösung in TypeScript, die der von @Mizipzor vorgeschlagenen Idee (unter Verwendung der Projektion) folgt:
quelle
Ich weiß, es ist schon eine Weile her, seit dieser Thread offen war. Aus der Antwort von chmike und verbessert von Aqib Mumtaz. Sie geben eine gute Antwort, funktionieren aber nur für eine unendliche Linie, wie Aqib sagte. Also füge ich einige Vergleiche hinzu, um zu wissen, ob das Liniensegment den Kreis berührt. Ich schreibe es in Python.
quelle
Hier ist eine Lösung in Golang geschrieben. Die Methode ähnelt einigen anderen hier veröffentlichten Antworten, ist jedoch nicht ganz dieselbe. Es ist einfach zu implementieren und wurde getestet. Hier sind die Schritte:
Hier werden die Werte für A, B und C für das Quadrat abgeleitet, wobei (n-et) und (m-dt) die Gleichungen für die x- bzw. y-Koordinaten der Linie sind. r ist der Radius des Kreises.
Daher ist A = ee + dd, B = - 2 (en + dm) und C = nn + mm - rr.
Hier ist der Golang-Code für die Funktion:
Ich habe es mit dieser Funktion getestet, die bestätigt, dass sich Lösungspunkte innerhalb des Liniensegments und auf dem Kreis befinden. Es erstellt ein Testsegment und fegt es um den angegebenen Kreis:
Hier ist die Ausgabe des Tests:
Schließlich ist das Verfahren leicht auf den Fall eines Strahls erweiterbar, der an einem Punkt beginnt, durch den anderen geht und sich bis ins Unendliche erstreckt, indem nur getestet wird, ob t> 0 oder t <1, aber nicht beide.
quelle
Ich brauchte das nur, also kam ich auf diese Lösung. Die Sprache ist maxscript, sollte aber leicht in eine andere Sprache übersetzt werden können. sideA, sideB und CircleRadius sind Skalare, die restlichen Variablen sind Punkte wie [x, y, z]. Ich gehe davon aus, dass z = 0 ist, um auf der Ebene XY zu lösen
quelle
Lösung in Python, basierend auf @Joe Skeen
quelle
Sie können X gleichmäßig verteilte Punkte von der Linie nehmen, und wenn sich irgendwelche innerhalb des Kreises befinden, liegt eine Kollision vor
quelle