Disons que nous avons deux rectangles, définis avec leurs coins inférieur gauche et supérieur droit. Par exemple: rect1 (x1, y1) (x2, y2) et rect2 (x3, y3) (x4, y4). J'essaie de trouver les coordonnées (en bas à gauche et en haut à droite) du rectangle intersecté.
Toute idée, algorithme, pseudo-code, serait grandement appréciée.
p.s. J'ai trouvé des questions similaires, mais elles ne vérifient que si 2 rectangles se croisent.
Si les rectangles d'entrée sont normalisés, c'est-à-dire que vous savez déjà que x1 < x2
, y1 < y2
(et de même pour le deuxième rectangle), alors tout ce que vous avez à faire est de calculer
int x5 = max(x1, x3);
int y5 = max(y1, y3);
int x6 = min(x2, x4);
int y6 = min(y2, y4);
et il vous donnera votre intersection sous forme de rectangle (x5, y5)-(x6, y6)
. Si les rectangles d'origine ne se coupent pas, le résultat sera un rectangle "dégénéré" (avec x5 >= x6
et/ou y5 >= y6
), que vous pouvez facilement vérifier.
P.S. Comme d'habitude, les petits détails dépendront si vous devez considérer les rectangles toucher comme se croisant.
Pour rechercher une intersection, vous devrez faire une simple comparaison des points:
Donc, comme nous pouvons le voir sur l'image si x3, y3 est supérieur ou égal à x1, y1 et inférieur ou égal à x2, y2 alors il est à l'intérieur du premier rectangle, de même vous devrez vérifier si x4, y4 tombe à l'intérieur du gamme de x1, y1 à x2, y2 ainsi.
si les deux conditions s'avèrent vraies, vous pouvez être sûr que le deuxième rectangle est totalement englobé par le premier.
Vous devrez également vérifier l'inverse, si vous trouvez ce qui est à l'intérieur de ce qui est important pour vous.
Vous devez également avoir les rectangles alignés sur l'axe, sinon cela ne fonctionnera pas de manière fiable.
Faites-moi savoir si vous avez besoin de plus de détails, bien que je pense qu'une recherche rapide sur Google vous permettra de découvrir beaucoup plus de détails très facilement, mais faites-le moi savoir et je peux faire un tutoriel sur les collisions rectangulaires si vous le souhaitez.
Plus en détail:
Pour savoir si les rectangles ont des intersections, vous pouvez vérifier les coordonnées de leurs points de définition, pour nos besoins, nous utiliserons les coordonnées des coins supérieur gauche et inférieur droit. Nous pouvons utiliser une classe pour nous faciliter la tâche et pour maximiser l'utilisation du code, nous pouvons utiliser un vecteur 2D et un point 2D: 2dVectorPoint.h
#include <cmath>
class Vector2D
{
public:
float x;
float y;
Vector2D() {}
Vector2D(float inX, float inY)
{
x = inX;
y = inY;
}
Vector2D& Set(float inX, float inY)
{
x = inX;
y = inY;
return (*this);
}
float& operator [](long k) { return ((&x)[k]); }
const float& operator [](long k) const { return ((&x)[k]); }
Vector2D& operator +=(const Vector2D& v)
{
x += v.x;
y += v.y;
return (*this);
}
Vector2D& operator -=(const Vector2D& v)
{
x -= v.x;
y -= v.y;
return (*this);
}
Vector2D& operator *=(float t)
{
x *= t;
y *= t;
return (*this);
}
Vector2D& operator /=(float t)
{
float f = 1.0F / t;
x *= f;
y *= f;
return (*this);
}
Vector2D& operator &=(const Vector2D& v)
{
x *= v.x;
y *= v.y;
return (*this);
}
Vector2D operator -(void) const { return (Vector2D(-x, -y)); }
Vector2D operator +(const Vector2D& v) const { return (Vector2D(x + v.x, y + v.y)); }
Vector2D operator -(const Vector2D& v) const { return (Vector2D(x - v.x, y - v.y)); }
Vector2D operator *(float t) const { return (Vector2D(x * t, y * t)); }
Vector2D operator /(float t) const { float f = 1.0F / t; return (Vector2D(x * , y * f)); }
float operator *(const Vector2D& v) const { return (x * v.x + y * v.y); }
Vector2D operator &(const Vector2D& v) const { return (Vector2D(x * v.x, y * v.y)); }
bool operator ==(const Vector2D& v) const { return ((x == v.x) && (y == v.y)); }
bool operator !=(const Vector2D& v) const { return ((x != v.x) || (y != v.y)); }
Vector2D& Normalize(void) { return (*this /= sqrtf(x * x + y * y)); }
Vector2D& Rotate(float angle);
};
class Point2D : public Vector2D
{
public:
Point2D() {}
Point2D(float r, float s) : Vector2D(r, s) {}
Point2D& operator =(const Vector2D& v)
{
x = v.x;
y = v.y;
return (*this);
}
Point2D& operator *=(float t)
{
x *= t;
y *= t;
return (*this);
}
Point2D& operator /=(float t)
{
float f = 1.0F / t;
x *= f;
y *= f;
return (*this);
}
Point2D operator -(void) const{ return (Point2D(-x, -y)); }
Point2D operator +(const Vector2D& v) const { return (Point2D(x + v.x, y + v.y)); }
Point2D operator -(const Vector2D& v) const { return (Point2D(x - v.x, y - v.y)); }
Vector2D operator -(const Point2D& p) const { return (Vector2D(x - p.x, y - p.y)); }
Point2D operator *(float t) const { return (Point2D(x * t, y * t)); }
Point2D operator /(float t) const
{
float f = 1.0F / t;
return (Point2D(x * f, y * f));
}
};
inline Vector2D operator *(float t, const Vector2D& v){ return (Vector2D(t * v.x, t * v.y));}
inline Point2D operator *(float t, const Point2D& p){ return (Point2D(t * p.x, t * p.y));}
inline float Dot(const Vector2D& v1, const Vector2D& v2){ return (v1 * v2);}
inline float Magnitude(const Vector2D& v){ return (sqrtf(v.x * v.x + v.y * v.y));}
inline float InverseMag(const Vector2D& v){ return (1.0F / sqrtf(v.x * v.x + v.y * v.y));}
inline float SquaredMag(const Vector2D& v){ return (v.x * v.x + v.y * v.y);}
struct Origin2D_
{
const Point2D& operator +(const Vector2D& v) { return (static_cast<const Point2D&>(v)); }
Point2D operator -(const Vector2D& v) { return (Point2D(-v.x, -v.y)); }
};
2dVectorPoint.cpp
#include "2dVectorPoint.h"
Origin2D_ Origin2D;
Vector2D& Vector2D::Rotate(float angle)
{
float s = sinf(angle);
float c = cosf(angle);
float nx = c * x - s * y;
float ny = s * x + c * y;
x = nx;
y = ny;
return (*this);
}
extern Origin2D_ Origin2D;
Le code utilisé est adapté de ici pour sauver mes doigts.
Ensuite, nous pouvons utiliser ceci pour comparer facilement: nous pouvons définir le rectangle 1 comme ayant P1 et P2 comme limites et le rectangle 2 comme ayant P3 et P4 comme limites, nous donnant la comparaison suivante:
if ( P2.y <= P3.y && P1.y >= P4.y && P2.x>= P3.x && P1.x <= P4.x )
{
return true;
}
Cela renverra une valeur vraie pour toute instance d'intersection ou pour le rectangle 1 englobant totalement le rectangle 2.
Pour vérifier uniquement les intersections, supprimez simplement le contrôle d'égalité (prenez toutes les =
de l'équation ci-dessus), et vous ne vérifierez que les intersections. Si vous avez une intersection, vous pouvez alors utiliser l'algèbre linéaire pour évaluer les coordonnées exactes.
Disons qu'une boîte a un rayon X et un rayon Y (je sais que ce n'est pas le cas mais ce terme est utile ici).
Tu vas avoir:
rect1_x_radius = (x2-x1)/2
rect1_y_radius = (y2-y1)/2
et
rect2_x_radius = (x4-x3)/2
rect2_y_radius = (y4-y3)/2
Maintenant, si les points médians droits sont plus éloignés que la somme de leurs rayons dans la direction appropriée - ils ne se heurtent pas. Sinon, c'est le cas - cet indice devrait suffire.
Vous devriez maintenant pouvoir terminer votre mission.
MISE À JOUR:
OK - résolvons-le pour 1D - plus tard, vous le résoudrez pour 2D. Regardez cette œuvre d'art ... ;-)
Vous voyez 2 segments - maintenant quelques calculs:
rA = (maxA-minA) / 2
rB = (maxB-minB) / 2
midA = minA + rA
midB = minB + rB
mid_dist = |midA - midB|
Maintenant, comment vérifier si une collision se produit? Comme je l'ai dit, si la somme des "rayons" est inférieure à la distance des segments - il n'y a pas de collision:
if ( mid_dist > fabs(rA+rB) )
{
// no intersection
}
else
{
// segments intersect
}
Il vous appartient maintenant de calculer l'intersection/la partie commune en 1D et 2D. C'est à vous maintenant (ou vous pouvez lire la réponse d'Andrey).
Voici la même situation mais en 2D - deux situations 1D:
Vous pouvez traiter la direction x
et y
séparément.
Suppose que x1 <= x3
(la première case est au moins aussi à gauche que la seconde). Ensuite, il y a chevauchement si et seulement si x1 <= x3 <= x2
.
De même, supposons que y1 <= y3
(la première case est au moins aussi bas que la seconde). Ensuite, il y a chevauchement si et seulement si y1 <= y3 <= y2
.
S'il y a chevauchement dans les deux directions, il y a chevauchement de rectangle. Vous pouvez trouver les coordonnées en triant les coordonnées x
et y
et en sélectionnant les deux du milieu.
En pseudocode:
if (((x1 <= x3 && x3 <= x2) || (x3 <= x1 && x1 <= x4)) // x-overlap
&&
((y1 <= y3 && y3 <= y2) || (y3 <= y1 && y1 <= y4)) // y-overlap
) {
int[] xs = {x1, x2, x3, x4};
int[] ys = {y1, y2, y3, y4};
sort(xs);
sort(ys);
// bottom-left: xs[1], ys[1]
// top-right: xs[2], ys[2]
}
Juste au cas où une solution C # simple conviendrait à n'importe qui:
public struct Rectangle
{
public double Left { get; }
public double Top { get; }
public double Width { get; }
public double Height { get; }
public double Right => Left + Width;
public double Bottom => Top + Height;
public static Rectangle Empty { get; } = new Rectangle(0, 0, 0, 0);
public Rectangle(double left, double top, double width, double height)
{
Left = left;
Top = top;
Width = width;
Height = height;
}
public static bool RectanglesIntersect(Rectangle rectangle1, Rectangle rectangle2)
{
rectangle1 = rectangle1.Normalize();
rectangle2 = rectangle2.Normalize();
if (rectangle2.Left >= rectangle1.Right)
return false;
if (rectangle2.Right <= rectangle1.Left)
return false;
if (rectangle2.Top >= rectangle1.Bottom)
return false;
if (rectangle2.Bottom <= rectangle1.Top)
return false;
return true;
}
public static Rectangle GetIntersection(Rectangle rectangle1, Rectangle rectangle2)
{
rectangle1 = rectangle1.Normalize();
rectangle2 = rectangle2.Normalize();
if (rectangle1.IntersectsWith(rectangle2))
{
double left = Math.Max(rectangle1.Left, rectangle2.Left);
double width = Math.Min(rectangle1.Right, rectangle2.Right) - left;
double top = Math.Max(rectangle1.Top, rectangle2.Top);
double height = Math.Min(rectangle1.Bottom, rectangle2.Bottom) - top;
return new Rectangle(left, top, width, height);
}
return Empty;
}
public Rectangle GetIntersection(Rectangle rectangle)
{
return GetIntersection(this, rectangle);
}
public bool IntersectsWith(Rectangle rectangle)
{
return RectanglesIntersect(this, rectangle);
}
public Rectangle NormalizeWidth()
{
if (Width >= 0)
return this;
Rectangle result = new Rectangle(Left + Width, Top, -Width, Height);
return result;
}
public Rectangle NormalizeHeight()
{
if (Height >= 0)
return this;
Rectangle result = new Rectangle(Left, Top + Height, Width, -Height);
return result;
}
public Rectangle Normalize()
{
Rectangle result = NormalizeWidth().NormalizeHeight();
return result;
}
}