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?
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?
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.
Hinweis: Wir haben das Problem hier auf 2D vereinfacht. Die Lösung gilt auch für 3D
bekommen:
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 ;
}
P = E + t * d
Was ist t
?
Niemand scheint über Projektion nachzudenken, bin ich hier völlig aus der Bahn geraten?
Projizieren Sie den Vektor AC
auf AB
. Der projizierte Vektor AD
gibt den neuen Punkt an D
.
Wenn der Abstand zwischen D
und C
kleiner als (oder gleich) ist R
, haben wir einen Schnittpunkt.
So was:
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
// 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
t+dt
und t-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 von t
. Die Schnittpunkte liegen bei "Abständen" t-dt
und t+dt
. Ich habe die Entfernung angegeben, weil es nicht die euklidische Entfernung ist. Um den euklidischen Abstand von A
wo zu erhalten t=0
, müssen Sie den Wert mit multiplizieren LAB
.
t=0
. Punkt B bei t=LAB
. Wenn beide Schnittpunkte ( t1=t-td
und 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.
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!
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 schrieb ein kleines Skript, um die Schnittmenge zu testen, indem ich den Mittelpunkt des Kreises auf die Linie projizierte.
vector distVector = centerPoint - projectedPoint;
if(distVector.length() < circle.radius)
{
double distance = circle.radius - distVector.length();
vector moveVector = distVector.normalize() * distance;
circle.move(moveVector);
}
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.
vector distVector = centerPoint - startPoint;
if(distVector.length() < circle.radius)
{
double distance = circle.radius - distVector.length();
vector moveVector = distVector.normalize() * distance;
circle.move(moveVector);
}
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 c
als 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 p3
und 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
x^2 + y^2 = r^2
y^2 = r^2 - x^2
y = sqrt(r^2 - x^2)
Dann setze ich sie gleich:
mx + b = sqrt(r^2 - x^2)
Und lösen Sie für die quadratische Gleichung ( 0 = ax^2 + bx + c
):
(mx + b)^2 = r^2 - x^2
(mx)^2 + 2mbx + b^2 = r^2 - x^2
0 = m^2 * x^2 + x^2 + 2mbx + b^2 - r^2
0 = (m^2 + 1) * x^2 + 2mbx + b^2 - r^2
Jetzt habe ich meine a
, b
und c
.
a = m^2 + 1
b = 2mb
c = b^2 - r^2
Also habe ich das in die quadratische Formel eingefügt:
(-b ± sqrt(b^2 - 4ac)) / 2a
Und durch Werte ersetzen, dann so weit wie möglich vereinfachen:
(-2mb ± sqrt(b^2 - 4ac)) / 2a
(-2mb ± sqrt((-2mb)^2 - 4(m^2 + 1)(b^2 - r^2))) / 2(m^2 + 1)
(-2mb ± sqrt(4m^2 * b^2 - 4(m^2 * b^2 - m^2 * r^2 + b^2 - r^2))) / 2m^2 + 2
(-2mb ± sqrt(4 * (m^2 * b^2 - (m^2 * b^2 - m^2 * r^2 + b^2 - r^2))))/ 2m^2 + 2
(-2mb ± sqrt(4 * (m^2 * b^2 - m^2 * b^2 + m^2 * r^2 - b^2 + r^2)))/ 2m^2 + 2
(-2mb ± sqrt(4 * (m^2 * r^2 - b^2 + r^2)))/ 2m^2 + 2
(-2mb ± sqrt(4) * sqrt(m^2 * r^2 - b^2 + r^2))/ 2m^2 + 2
(-2mb ± 2 * sqrt(m^2 * r^2 - b^2 + r^2))/ 2m^2 + 2
(-2mb ± 2 * sqrt(m^2 * r^2 + r^2 - b^2))/ 2m^2 + 2
(-2mb ± 2 * sqrt(r^2 * (m^2 + 1) - b^2))/ 2m^2 + 2
Dies ist fast so weit wie es vereinfachen wird. Zum Schluss trennen Sie die Gleichungen mit ±:
(-2mb + 2 * sqrt(r^2 * (m^2 + 1) - b^2))/ 2m^2 + 2 or
(-2mb - 2 * sqrt(r^2 * (m^2 + 1) - b^2))/ 2m^2 + 2
Stecken Sie dann einfach das Ergebnis dieser beiden Gleichungen in das x
In 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.
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:
{
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.
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];
}
}
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
distance(P, C) = R
das ist
(alpha*Xa + (1-alpha)*Xb)^2 + (alpha*Ya + (1-alpha)*Yb)^2 = R^2
alpha^2*Xa^2 + alpha^2*Xb^2 - 2*alpha*Xb^2 + Xb^2 + alpha^2*Ya^2 + alpha^2*Yb^2 - 2*alpha*Yb^2 + Yb^2=R^2
(Xa^2 + Xb^2 + Ya^2 + Yb^2)*alpha^2 - 2*(Xb^2 + Yb^2)*alpha + (Xb^2 + Yb^2 - R^2) = 0
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).
Abstand zwischen einem Punkt und einer Linie:
http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html
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 .
// Small epsilon value
var EPS = 0.0000001;
// point (x, y)
function Point(x, y) {
this.x = x;
this.y = y;
}
// Circle with center at (x,y) and radius r
function Circle(x, y, r) {
this.x = x;
this.y = y;
this.r = r;
}
// A line segment (x1, y1), (x2, y2)
function LineSegment(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 = c
function Line(a, b, c) {
this.a = a; this.b = b; this.c = c;
// Normalize line for good measure
if (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) = 0
var 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 = 0
if (Math.abs(b) < EPS) {
// Line equation is ax + by = c, but b = 0, so x = c/a
x1 = c/a;
// No intersection
if (Math.abs(x-x1) > r) return [];
// Vertical line is tangent to circle
if (Math.abs((x1-r)-x) < EPS || Math.abs((x1+r)-x) < EPS)
return [new Point(x1, y)];
var dx = Math.abs(x1 - x);
var dy = Math.sqrt(r*r-dx*dx);
// Vertical line cuts through circle
return [
new Point(x1,y+dy),
new Point(x1,y-dy)
];
// Line is tangent to circle
} else if (Math.abs(D) < EPS) {
x1 = -B/(2*A);
y1 = (c - a*x1)/b;
return [new Point(x1,y1)];
// No intersection
} else if (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 [
new Point(x1, y1),
new Point(x2, y2)
];
}
}
// Converts a line segment to a line in general form
function segmentToGeneralForm(x1,y1,x2,y2) {
var a = y1 - y2;
var b = x2 - x1;
var c = x2*y1 - x1*y2;
return new Line(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 circle
function 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 intersection
if (pts.length === 0) return [];
var pt1 = pts[0];
var includePt1 = pointInRectangle(pt1,x1,y1,x2,y2);
// Check for unique intersection
if (pts.length === 1) {
if (includePt1) return [pt1];
return [];
}
var pt2 = pts[1];
var includePt2 = pointInRectangle(pt2,x1,y1,x2,y2);
// Check for remaining intersections
if (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.
( 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.
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));
}
Ray.Intersects(BoundingSphere)
' 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;
}
// ******************************************************************
}
}
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:
function is_on(a, b, c) {
return Math.abs(distance(a,c) + distance(c,b) - distance(a,b))<0.000001;
}
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.
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
*/
export function lineSphereIntersects(
a: IPoint,
b: IPoint,
c: IPoint,
r: number
): boolean {
// find the three sides of the triangle formed by the three points
const 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 sphere
if (ac < r || bc < r) {
return true;
}
// find the angle between the line segment and the center of the sphere
const 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 segment
const cd: number = Math.sin(cab) * ac;
// if the radius is at least as long as the distance between the center and the line
if (r >= cd) {
// find the distance between the line start and the point on the line closest to
// the center of the sphere
const 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 line
return ad <= ab;
}
return false;
}
export function distance(a: IPoint, b: IPoint): number {
return Math.sqrt(
Math.pow(b.z - a.z, 2) + Math.pow(b.y - a.y, 2) + Math.pow(b.x - a.x, 2)
);
}
export interface IPoint {
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:
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)
)
Lösung in Python, basierend auf @Joe Skeen
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