it-swarm.com.de

Kollisionserkennungsalgorithmus für Kreisliniensegmente?

Ich habe eine Linie von A nach B und einen Kreis mit dem Radius R bei C. 

Image

Was ist ein guter Algorithmus, um zu überprüfen, ob die Linie den Kreis schneidet? Und an welcher Koordinate entlang des Kreises Edge trat es auf?

180
Mizipzor

Nehmen 

  1. Eist der Startpunkt des Strahls, 
  2. List der Endpunkt des Strahls, 
  3. Cist das Zentrum der Kugel, gegen das Sie testen 
  4. r ist der Radius dieser Kugel

Berechnen:
d = L - E (Richtungsvektor des Strahls vom Anfang bis zum Ende)
f = E - C (Vektor von der mittleren Kugel zum Strahlenanfang) 

Dann wird die Kreuzung durch .. gefunden.
Einstecken:
P = E + t * d
Dies ist eine parametrische Gleichung:
Px = Ex + tdx
Py = Ey + tdy
in
(x - h)2 + (y - k)2 = r2
(h, k) = Mittelpunkt des Kreises. 

Hinweis: Wir haben das Problem auf 2D vereinfacht, die Lösung, die wir erhalten, gilt auch für 3D

bekommen:

  1. Erweitern
    x2 - 2xh + h2 + y2 - 2yk + k2 - r2 = 0 
  2. Stecker
    x = ex + tdx
    y = ey + tdy
    (ex + tdx )2 - 2 (ex + tdx ) h + h2 + (ey + tdy )2 - 2 (ey + tdy ) k + k2 - r2 = 0
  3. Explodieren
    ex2 + 2extdx + t2dx2 - 2exh - 2tdxh + h2 + ey2 + 2eytdy + t2dy2 - 2eyk - 2tdyk + k2 - r2 = 0
  4. Gruppe
    t2(dx2 + dy2 ) + 2t (exdx + eydy - dxh - dyk) + ex2 + ey2 - 2exh - 2eyk + h2 + k2 - r2 = 0
  5. Endlich,
    t2(_d * _d) + 2t (_e * _d - _d * _c) + _e * _e - 2 (_e * _c) + _c * _c - r2 = 0
    * Wobei _d der Vektor d und * das Punktprodukt ist. *
  6. Und dann,
    t2(_d * _d) + 2t (_d * (_e - _c)) + (_e - _c) * (_e - _c) - r2 = 0 
  7. Vermiete _f = _e - _c
    t2(_d * _d) + 2t (_d * _f) + _f * _f - r2 = 0

Also bekommen wir:
t2 * (d DOT d) + 2t * (f DOT d) + (f DOT f - r2 ) = 0
.__ Die quadratische Gleichung lösen:

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 ;
}
185
bobobobo

Niemand scheint die Projektion in Betracht zu ziehen, bin ich hier völlig falsch?

Projizieren Sie den Vektor AC auf AB. Der projizierte Vektor AD gibt den neuen Punkt D an.
Wenn der Abstand zwischen D und C kleiner als oder gleich R ist, haben wir eine Schnittmenge.

So was:
Image by SchoolBoy

122
Mizipzor

Ich würde den Algorithmus verwenden, um den Abstand zwischen einem Punkt (Kreismittelpunkt) und einer Linie (Linie AB) zu berechnen. Damit können dann die Schnittpunkte der Linie mit dem Kreis bestimmt werden.

Nehmen wir an, wir haben die Punkte A, B, C. Ax und Ay sind die x- und y-Komponenten der A-Punkte. Gleiches für B und C. Der Skalar R ist der Kreisradius.

Dieser Algorithmus erfordert, dass A, B und C verschiedene 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
45
chmike

Okay, ich gebe Ihnen keinen Code, aber da Sie diesen Algorithmus markiert haben, glaube ich nicht, dass das für Sie eine Rolle spielt. Zuerst müssen Sie einen Vektor senkrecht zur Linie bekommen. 

Sie haben eine unbekannte Variable in y = ax + c(c ist unbekannt )
Um dies zu lösen, berechnen Sie den Wert, wenn die Linie durch den Mittelpunkt des Kreises verläuft. 

Das ist,
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 nächstgelegenen Punkt auf der Linie dem Kreis.
Berechnen Sie den Abstand zwischen diesem Punkt und dem Kreismittelpunkt (anhand der Größe des Vektors).
Wenn dies kleiner als der Radius des Kreises ist - voila, haben wir einen Schnittpunkt!

17
a_m0d

Eine andere Methode verwendet die Dreieck-ABC-Bereichsformel. Der Schnittpunkttest ist einfacher und effizienter als die Projektionsmethode, das Auffinden der Koordinaten des Schnittpunkts erfordert jedoch mehr Arbeit. Zumindest wird es bis zu dem Punkt verzögert, an dem es erforderlich ist.

Die Formel zur Berechnung des Dreiecksbereichs lautet: area = bh/2 

wobei b die Basislänge und h die Höhe ist. Wir haben das Segment AB als Basis gewählt, so dass h der kürzeste Abstand von C, dem Kreismittelpunkt, zur Linie ist. 

Da der Dreieckbereich 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önnen den Code mithilfe der in hier beschriebenen schnellen inversen Quadratwurzelberechnung optimieren, um 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, dann tangiert die Linie AB den Kreis und der Wert dt = 0 und E = F. Die Punktkoordinaten sind die von E und F.

Sie sollten prüfen, ob A sich von B unterscheidet und die Segmentlänge nicht Null ist, wenn dies in Ihrer Anwendung passieren kann. 

9
chmike

Ich habe ein kleines Skript geschrieben, um den Schnittpunkt zu testen, indem der Mittelpunkt des Kreises auf die Linie projiziert wird. 

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 die Entfernung des Kreismittelpunkts zu den 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);
}

https://jsfiddle.net/ercang/menp0991/

7
ercang

Diese Lösung, die ich fand, schien ein wenig leichter zu folgen als andere. 

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 nach der Gleichung der Linie in Steigungsschnittpunktform auflösen. Ich wollte mich jedoch nicht mit schwierigen Gleichungen mit c befassen, deshalb habe ich das Koordinatensystem so verschoben, dass sich der Kreis auf 0,0 befindet.

p3 = p1 - c
p4 = p2 - c

Übrigens subtrahiere ich immer, wenn ich Punkte voneinander entferne, die x-Werte und dann die y-Werte, 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 Zeile 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 Gleichung des Kreises für x lösen.

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öse nach der quadratischen 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 füge ich das in die quadratische Formel ein:

(-b ± sqrt(b^2 - 4ac)) / 2a

Und durch Werte ersetzen, dann vereinfachen Sie so viel wie möglich:

(-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 vereinfacht wird. Trennen Sie sich schließlich zu Gleichungen mit dem ±:

(-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 x in mx + b. Zur Verdeutlichung habe ich etwas JavaScript-Code geschrieben, um zu zeigen, wie man dies verwendet:

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!

P.S. Wenn jemand Fehler findet oder Vorschläge hat, bitte kommentieren. Ich bin sehr neu und freue mich über jede Hilfe/Anregung.

5
multitaskPro

Merkwürdigerweise 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 Wurzelbereich 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 Endkoordinaten 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];
    }
}
4
Duq

Sie können einen Punkt auf einer unendlichen Linie finden, der dem Kreismittelpunkt am nächsten ist, 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 keine Kreuzung. Wenn der Abstand gleich R ist, ist die Linie ein Tangens des Kreises und der Punkt, der dem Kreismittelpunkt am nächsten ist, ist tatsächlich der Schnittpunkt. Wenn der Abstand kleiner als R ist, gibt es zwei Schnittpunkte. Sie liegen in der gleichen Entfernung von dem Punkt, der dem Kreismittelpunkt am nächsten liegt. Diese Entfernung kann leicht mit dem Satz des 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 sich gefundene Schnittpunkte tatsächlich innerhalb des Liniensegments befinden.

4

In diesem Nachkreis wird die Linienkollision überprüft, indem der Abstand zwischen Kreismittelpunkt und Punkt auf Liniensegment (Ipoint) überprüft wird, der den Schnittpunkt zwischen der Normalen N (Bild 2) vom Kreismittelpunkt zum Liniensegment darstellt. 

( https://i.stack.imgur.com/3o6do.png )Image 1. Finding vectors E and D

In Bild 1 werden ein Kreis und eine Linie gezeigt, Vektor A Punkt an Linienanfang, Vektor B Punkt an Linienendpunkt, Vektor C Punkt Kreismittelpunkt. Nun müssen wir den Vektor E (vom Linienanfangspunkt zum Kreismittelpunkt) und den Vektor D (vom Linienanfangspunkt bis zum Linienendpunkt) finden. Diese Berechnung ist in Bild 1 dargestellt.

( https://i.stack.imgur.com/7098a.png )Image 2. Finding vector X

In Bild 2 ist zu sehen, dass der Vektor E durch "Punktprodukt" des Vektors E und des Einheitsvektors D auf den Vektor D projiziert wird. Das Ergebnis des Punktprodukts ist der Skalar Xp, der die Entfernung zwischen dem Linienanfang 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.

Nun müssen wir den Vektor Z (Vektor zu Ipoint) finden, seine einfache Vektoraddition des Vektors A (Startpunkt auf der Linie) und des Vektors X ist leicht. Als Nächstes müssen wir uns mit Sonderfällen befassen, die überprüft werden müssen: Ipoint auf Liniensegment, wenn Wir müssen nicht herausfinden, ob es links davon oder rechts davon ist. Wir werden den nächstgelegenen Vektor verwenden, um zu bestimmen, welcher Punkt dem Kreis am nächsten liegt. 

( https://i.stack.imgur.com/p9WIr.png )Image 3. Finding closest point

Wenn die Projektion Xp negativ ist. Ipoint ist links vom Liniensegment, der Vektor am nächsten ist gleich dem Vektor des Zeilenanfangspunkts. Wenn die Projektion Xp größer ist als der Betrag des Vektors D, dann ist Ipoint rechts vom Liniensegment, dann ist der nächste Vektor dem Vektor des Zeilenendes Punkt in jedem anderen Fall ist der nächstgelegene Vektor gleich Vektor Z.

Wenn wir nun den nächstgelegenen Vektor haben, müssen wir den Vektor vom Kreismittelpunkt zum I-Punkt (dist-Vektor) finden. Es ist einfach, den nächstgelegenen Vektor vom Mittelvektor abzuziehen. Als nächstes prüfen Sie einfach, ob der Vektor dist distitude kleiner als der Kreisradius ist. Wenn dies der Fall ist, kollidieren sie, wenn nicht, gibt es keine Kollision.

( https://i.stack.imgur.com/QJ63q.png )Image 4. Checking for collision

Zum Ende können wir einige Werte für das Auflösen der Kollision zurückgeben. Die einfachste Methode besteht darin, die Überlappung der Kollision (den Radius von der Vektordistanzgröße abzuziehen) und die Kollisionsachse zurückzuschalten, wobei der Vektor D ebenfalls der Schnittpunkt ist.

3
FunDeveloper89

Hier ist eine Implementierung in Javascript. Mein Ansatz ist, zuerst das Liniensegment in eine unendliche Linie umzuwandeln und dann den Schnittpunkt bzw. die Schnittpunkte zu finden. Von dort aus überprüfe ich, ob die gefundenen Punkte auf dem Liniensegment liegen. Der Code ist gut dokumentiert, Sie sollten folgen können. 

Sie können den Code hier auf dieser Live-Demo ..__ ausprobieren. Der Code wurde aus meinem Algorithmus-Repo übernommen.

 enter image description here

// 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 [];

}
3
will.fiset

Wenn Sie den Abstand zwischen dem Mittelpunkt der Kugel (da es sich um 3D handelt, nehme ich an, dass Sie Kugel und nicht Kreis bedeuten) und der Linie finden, überprüfen Sie, ob der Abstand kleiner ist als der Radius, der den Trick ausführen wird.

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

3
Martin

Sie benötigen hier etwas Mathematik:

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, sofern vorhanden.

3
Martijn

Nur ein Zusatz zu diesem Thread ... Unten ist eine Version des Codes, die von pahlevan gepostet wurde, aber für C #/XNA etwas aufgeräumt wurde:

    /// <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));
    }
2
Rob

Wenn die Linienkoordinaten A.x, A.y und B.x, B.y sind und das Kreisezentrum C.x, C.y ist, lauten die Linienformeln: 

x = A.x * t + B.x * (1 - t)

y = A.y * t + B.y * (1 - t)

wobei 0 <= t <= 1

und der Kreis ist 

(C.x - x) ^ 2 + (C.y - 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 (sofern vorhanden). Wenn Sie ein t erhalten, das kleiner als 0 oder größer als 1 ist, ist dies keine Lösung, sondern es zeigt, dass die Linie auf die Richtung des Kreises zeigt.

2
Gábor Hargitai

enter image description here

' 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
2
A.J.Bauer

Ich habe diese Funktion für iOS nach der Antwort von chmike erstellt.

+ (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;
}
2
Aqib Mumtaz

Eine andere in c # (partielle Circle-Klasse) . Getestet und funktioniert 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;
        }

        // ******************************************************************
    }
}
1
Eric Ouellet

Hier ist eine gute Lösung in JavaScript (mit allen erforderlichen Mathematik und Live-Illustrationen) https://bl.ocks.org/milkbread/11000965

Obwohl die is_on-Funktion in dieser Lösung geändert werden muss:

function is_on(a, b, c) {
    return Math.abs(distance(a,c) + distance(c,b) - distance(a,b))<0.000001;
}

1
nazar kuliyev

Hier ist meine Lösung in TypeScript, basierend auf der Idee, die @Mizipzor vorgeschlagen hat (mit Projektion):

/**
 * 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;
}
1
Joe Skeen

Circle ist wirklich ein böser Kerl :) Ein guter Weg ist, den richtigen Circle zu vermeiden, wenn Sie können. Wenn Sie eine Kollisionsprüfung für Spiele durchführen, können Sie einige Vereinfachungen vornehmen und haben nur 3-Punkt-Produkte und einige Vergleiche.

Ich nenne das "Fat Point" oder "dünnen Kreis". eine Art Ellipse mit einem Radius von Null in einer Richtung parallel zu einem Segment. aber voller Radius in einer Richtung senkrecht zu einem Segment

Zuerst würde ich das Umbenennen und Wechseln des Koordinatensystems in Betracht ziehen, um übermäßige Daten zu vermeiden:

s0s1 = B-A;
s0qp = C-A;
rSqr = r*r;

Zweitens: Der Index h in hvec2f bedeutet, dass vector horizontale Operationen wie dot ()/det () bevorzugen muss. Dies bedeutet, dass seine Komponenten in separaten xmm-Registern abgelegt werden müssen, um ein Umsortieren/Verschieben von Daten 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 noch weiter optimieren können. Ich verwende es für die Kollisionserkennung im neuronalen Netzwerk, um Millionen von Iterationsschritten zu verarbeiten.

1
xakepp35

Diese Java-Funktion gibt ein DVec2-Objekt zurück. Es braucht a DVec2 für den Kreismittelpunkt, den Radius des Kreises und eine Linie.

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;
}
1
pahlevan

Hier ist eine in Golang geschriebene Lösung. Die Methode ähnelt einigen anderen hier veröffentlichten Antworten, ist jedoch nicht ganz die gleiche. Es ist einfach zu implementieren und wurde getestet. Hier sind die Schritte:

  1. Übersetzen Sie die Koordinaten so, dass sich der Kreis im Ursprung befindet.
  2. Das Liniensegment wird als parametrisierte Funktion von t sowohl für die x- als auch für die y-Koordinate ausgedrückt. 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.
  3. Lösen Sie, falls möglich, die quadratische Gleichung, die sich aus der Beschränkung der Werte von t ergibt, die x, y-Koordinaten mit Entfernungen vom Ursprung erzeugen, die dem Radius des Kreises entsprechen.
  4. Lösungen werfen, bei denen t <0 oder> 1 ist (<= 0 oder> = 1 für ein offenes Segment). Diese Punkte sind nicht im Segment enthalten.
  5. Übersetzen Sie zurück zu den ursprünglichen Koordinaten.

Die Werte für A, B und C für das Quadratische werden hier abgeleitet, wobei (n-et) und (m-dt) die Gleichungen für die x- und 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 gilt 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 herum:

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 bis unendlich geht, indem nur geprüft wird, ob t> 0 oder t <1 ist, aber nicht beides.

0
Steller

Ich brauchte das nur, also kam ich zu dieser Lösung. Die Sprache ist maxscript, sollte aber leicht in jede andere Sprache übersetzt werden können. SeiteA, SeiteB und CircleRadius sind Skalare, die restlichen Variablen sind Punkte als [x, y, z]. Ich gehe davon aus, dass z = 0 auf der Ebene XY zu lösen ist

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)
)
0
user18965