2017-08-26 1 views
1

Ich schreibe gerade eine Funktion, um den Schnittpunkt eines Rechtecks ​​mit einer Superellipse zu testen. Das Rechteck wird immer achsversetzt sein, während die Superellipse mit einem Drehwinkel alpha ausgerichtet sein kann.Algorithmus zum Erkennen von Überschneidungen zwischen einem achsenausgerichteten Rechteck und einer orientierten Superellipse

Im Falle eines achsausgerichteten Rechtecks, das eine achsenbündige Superellipse schneidet, habe ich diese zwei kurzen Funktionen geschrieben, die wunderbar funktionieren. Der Code ist prägnant, klar und effizient. Wenn möglich, möchte ich eine ähnliche Struktur für die neue allgemeinere Funktion beibehalten. Hier

ist, was ich habe zu erkennen, ob eine Achse ausgerichtetes Rechteck eine Achse ausgerichtete Superellipse schneidet:

double fclamp(double x, double min, double max) 
{ 
    if (x <= min) return min; 
    if (x >= max) return max; 
    return x; 
} 

bool rect_intersects_superellipse(const t_rect *rect, double cx, double cy, double rx, double ry, double exponent) 
{ 
    t_pt closest; 
    closest.x = fclamp(cx, rect->x, rect->x + rect->width); 
    closest.y = fclamp(cy, rect->y, rect->y + rect->height); 
    return point_inside_superellipse(&closest, cx, cy, rx, ry, exponent); 
} 

bool point_inside_superellipse(const t_pt *pt, double cx, double cy, double rx, double ry, double exponent) 
{ 
    double dx = fabs(pt->x - cx); 
    double dy = fabs(pt->y - cy); 

    double dxp = pow(dx, exponent); 
    double dyp = pow(dy, exponent); 

    double rxp = pow(rx, exponent); 
    double ryp = pow(ry, exponent); 

    return (dxp * ryp + dyp * rxp) <= (rxp * ryp); 
} 

Dies funktioniert korrekt, aber - wie gesagt - nur für eine Achse ausgerichtet Superellipse.

Jetzt möchte ich es zu einer orientierten Superellipse verallgemeinern, wobei ich die Algorithmusstruktur so nahe wie möglich an das obige halte. Die offensichtliche Erweiterung der bisherigen zwei Funktionen dann so etwas wie werden würde:

bool rect_intersects_oriented_superellipse(const t_rect *rect, double cx, double cy, double rx, double ry, double exponent, double radians) 
{ 
    t_pt closest; 
    closest.x = fclamp(cx, rect->x, rect->x + rect->width); 
    closest.y = fclamp(cy, rect->y, rect->y + rect->height); 
    return point_inside_oriented_superellipse(&closest, cx, cy, rx, ry, exponent, radians); 
} 

bool point_inside_oriented_superellipse(const t_pt *pt, double cx, double cy, double rx, double ry, double exponent, double radians) 
{ 
    double dx = pt->x - cx; 
    double dy = pt->y - cy; 

    if (radians) { 

     double c = cos(radians); 
     double s = sin(radians); 

     double new_x = dx * c - dy * s; 
     double new_y = dx * s + dy * c; 

     dx = new_x; 
     dy = new_y; 
    } 
    double dxp = pow(fabs(dx), exponent); 
    double dyp = pow(fabs(dy), exponent); 

    double rxp = pow(rx, exponent); 
    double ryp = pow(ry, exponent); 

    return (dxp * ryp + dyp * rxp) < (rxp * ryp); 
} 

Für eine orientierte Superellipse, wird die oben nicht korrekt funktionieren, obwohl point_inside_oriented_superellipse() von selbst wie erwartet funktioniert. Ich kann die obigen Funktionen nicht verwenden, um auf einen Schnittpunkt mit einem achsausgerichteten Rechteck zu testen. Ich habe seit ungefähr einer Woche online geforscht und ich habe einige Lösungen gefunden, die eine inverse Matrixtransformation erfordern, um die Superellipsenachsen auszugleichen und ihren Ursprung bei (0, 0) zu bringen. Der Nachteil ist, dass jetzt mein Rechteck kein Rechteck mehr ist und sicher nicht achsversetzt. Ich möchte es vermeiden, diesen Weg zu gehen. Meine Frage ist zu zeigen, wie man den obigen Algorithmus arbeiten lässt und seine Struktur mehr oder weniger unverändert behält. Wenn es nicht möglich ist, dieselbe algorithmische Struktur beizubehalten, zeigen Sie bitte den einfachsten und effizientesten Algorithmus, um den Schnittpunkt zwischen einem achsausgerichteten Rechteck und einer orientierten Superellipse zu testen. Ich muss nur wissen, ob der Schnittpunkt aufgetreten ist oder nicht (boolesches Ergebnis). Der Bereich des Exponentenparameters kann von 0,25 bis 100,0 variieren.

Vielen Dank für Ihre Hilfe.

+0

Sind Sie sicher, dass Ihre Methode funktioniert? Sie testen nur die Eckpunkte des Rechtecks, ja? Aber selbst für einen Kreis könnten Sie den Kreis und das Rechteck schneiden, ohne dass sich die Ecken im Kreis befinden. – dmuir

+0

Für eine achsenbündige Superellipse - ja - ich bin mir sicher, dass meine Methode so funktioniert, wie sie sollte. Ich habe es gründlich getestet und benutze es die ganze Zeit. Nein, ich teste nicht nur die Rechteckscheitelpunkte. Der Code ist dort, bitte versuchen Sie es selbst und sehen Sie ... –

Antwort

0

Werfen Sie einen Blick auf Punkt 2 in diesem source. In einfachen Worten müssen Sie die folgenden Tests durchführen:

1. Gibt es irgendwelche Rechteckscheitelpunkte in der Ellipse?

2. Ist eine Rechteckkante die Ellipse schneiden?

3. Ist das Zentrum der Ellipse innerhalb des Rechtecks?

Die Ellipse und das Rechteck schneiden sich-andere, wenn eine der oben genannten Fragen mit einem ja, so, Ihre Funktion zurückkehren sollte etwa wie folgt beantwortet werden:

return areVertexesInsideEllipse(/*params*/) || areRectangleEdgesIntersectingEllipse(/*params*/) || isEllipseCenterInsideRectangle(/*params*/); 

Der Doc hat sogar ein Beispiel für eine Implementierung, die Ihrer ziemlich nahe kommt.

Um zu überprüfen, ob sich einer der Eckpunkte innerhalb der Ellipse befindet, können Sie deren Koordinaten anhand der Ungleichheit der Ellipse berechnen. Um zu prüfen, ob eine Kante die Ellipse überlappt, müssen Sie prüfen, ob ihre Linie durch die Ellipse verläuft oder sie berührt. Wenn dies der Fall ist, müssen Sie überprüfen, ob das Segment, in dem die Linie durch die Ellipse verläuft oder es berührt, das durch die Kante definierte Segment schneidet. Um zu überprüfen, ob der Mittelpunkt der Ellipse innerhalb des Rechtecks ​​liegt, müssen Sie den Mittelpunkt gegen die Ungleichungen des Rechtecks ​​prüfen.

Beachten Sie, dass dies sehr allgemeine Begriffe sind, sie nehmen nicht einmal an, dass Ihr Rechteck achsenorientiert ist, und doch nur Ihre Ellipse.

+1

Beachten Sie, dass der verlinkte Artikel nur Kreuzungen mit Ellipsen, nicht Superellipsen diskutiert. Das Herausfinden, ob eine beliebige Linie eine Superellipse schneidet, ist ein wesentlich schwierigeres Problem. – Anton

+0

@ user3290797 true, die Antwort muss möglicherweise bearbeitet werden, aber wie es ist, ich denke, es ist bereits hilfreich, auch wenn es nicht abgeschlossen ist. Wenn die op bestimmte Probleme erwähnt, dann werde ich immer darüber nachdenken und die Antwort bearbeiten. Im Allgemeinen ist die Idee für Superellipsen die gleiche wie für Ellipsen. –

+0

Ich schätze es, dass Sie sich die Zeit nehmen zu antworten, aber Ihre Antwort ist nicht hilfreich - nicht einmal ein bisschen. Ich habe mich in meiner Frage ziemlich klar ausgedrückt und erwähne ein spezifisches Problem. Ich muss das Problem lösen, den Schnittpunkt eines achsausgerichteten Rechtecks ​​mit einer orientierten Superellipse zu erkennen. Eine Lösung für eine achsengerichtete Ellipse zu geben, sagt mir, was ich bereits weiß und beantwortet meine Frage nicht. –

0

Zuerst sollten Sie die offensichtlichen nicht schneidenden Fälle mit dem Trennachsensatz ausschließen - Die Superellipse hat möglicherweise zwei Bounding-Boxen (Fälle mit Exponent n> 1) und Groß-/Kleinschreibung n < = 1.

In der SAT werden alle Vertices in Bounding Box ABCD mit allen (gerichteten) Kanten in der BB (abcd) der Superellipse verglichen; dann umgekehrt. Wenn die vorzeichenbehafteten Abstände zur Trennachse alle positiv sind (d. H. Außerhalb), kollidieren die Objekte nicht.

  b 
     a 
    A------B 
    |  |  d 
    |  | c 
    C------D 

Der Exponent n == 1 teilt die Fälle weiter - n = 1 < macht die super-ellipsoid konkav ist, wobei in diesem Fall nur ABCD ABCD schneidet, wenn ein oder mehr Punkte innerhalb des super-Ellipsoids sind. Wenn n> 1 ist, muss man den Schnittpunkt des Liniensegments in AABB und des Super-Ellipsoids lösen, der eventuell durch Splines approximiert werden muss oder ein anderer Proxy muss gefunden werden. Schließlich ist der tatsächliche Schnittpunkt nicht von Interesse, aber die Gleichungen in Wolfram Alpha zu erstellen, führte zu keinen Ergebnissen in der Standardausführungszeit.

Verwandte Themen