web-dev-qa-db-fra.com

Comment vérifier si un segment de ligne coupe un rectangle?

Si vous avez 2 points, (x1, y1) et (x2, y2), qui représentent deux coins opposés d'un rectangle, et 2 autres points, (x3, y3) et (x4, y4), qui représentent deux extrémités d'un segment de ligne, comment vérifier si le segment de ligne coupe le rectangle?

(Le segment de ligne est simplement le segment contenu entre les extrémités données. Il ne s'agit pas d'une ligne de longueur infinie définie par ces deux points.)

26
omega

Une option très simple consisterait à utiliser un algorithme standard pour vérifier si deux segments de ligne se croisent pour vérifier si les segments de ligne intersectent l’un des quatre segments de ligne qui constituent les coins du cadre. Il est très efficace du point de vue du calcul de vérifier si deux segments de ligne se croisent. Je pense donc que cela pourrait fonctionner très rapidement.

J'espère que cela t'aides!

25
templatetypedef

Pour comprendre comment calculer la formule permettant de vérifier si un segment de ligne coupe un rectangle, il est important de garder en mémoire les propriétés du produit point vectoriel .

Représente le segment de ligne sous la forme d'un vecteur unitaire et d'une distance entre le point de départ du segment et l'origine. Voici un code C # pour le calculer à partir des variables PointFa_ptStart et a_ptEnd, en utilisant un Vector :

Vector vecLine = new Vector(a_ptEnd.X - a_ptStart.X, a_ptEnd.Y - a_ptStart.Y);
double dLengthLine = vecLine.Length;
vecLine /= dLengthLine;
double dDistLine = Vector.Multiply(vecLine, new Vector(a_ptStart.X, a_ptStart.Y));

Vous devrez également calculer le vecteur perpendiculaire et sa distance par rapport à l'origine pour le segment de ligne. Faire pivoter un vecteur unitaire de 90 ° est facile .

Vector vecPerpLine = new Vector(-vecLine.Y, vecLine.X);
double dDistPerpLine = Vector.Multiply(vecPerpLine, new Vector(a_ptStart.X, a_ptStart.Y));

En supposant que les quatre coins du rectangle sont dans des variables Vector appelées vecRect1, vecRect2, vecRect3 et vecRect4, calculez la distance entre le segment de ligne et les quatre coins du rectangle de délimitation de la cible:

double dPerpLineDist1 = Vector.Multiply(vecPerpLine, vecRect1) - dDistPerpLine;
double dPerpLineDist2 = Vector.Multiply(vecPerpLine, vecRect2) - dDistPerpLine;
double dPerpLineDist3 = Vector.Multiply(vecPerpLine, vecRect3) - dDistPerpLine;
double dPerpLineDist4 = Vector.Multiply(vecPerpLine, vecRect4) - dDistPerpLine;
double dMinPerpLineDist = Math.Min(dPerpLineDist1, Math.Min(dPerpLineDist2,
    Math.Min(dPerpLineDist3, dPerpLineDist4)));
double dMaxPerpLineDist = Math.Max(dPerpLineDist1, Math.Max(dPerpLineDist2,
    Math.Max(dPerpLineDist3, dPerpLineDist4)));

Si toutes les distances sont positives ou si toutes les distances sont négatives, le rectangle est situé d'un côté ou de l'autre de la ligne, il n'y a donc pas d'intersection. (Les rectangles à extension nulle sont considérés comme ne intersectant aucun segment de droite.)

if (dMinPerpLineDist <= 0.0 && dMaxPerpLineDist <= 0.0
        || dMinPerpLineDist >= 0.0 && dMaxPerpLineDist >= 0.0)
    /* no intersection */;

Ensuite, projetez les quatre coins du rectangle de délimitation de la cible sur le segment. Cela nous donne la distance entre l'origine de la ligne et la projection du coin rectangle sur cette ligne.

double dDistLine1 = Vector.Multiply(vecLine, vecRect1) - dDistLine;
double dDistLine2 = Vector.Multiply(vecLine, vecRect2) - dDistLine;
double dDistLine3 = Vector.Multiply(vecLine, vecRect3) - dDistLine;
double dDistLine4 = Vector.Multiply(vecLine, vecRect4) - dDistLine;
double dMinLineDist = Math.Min(dDistLine1, Math.Min(dDistLine2,
    Math.Min(dDistLine3, dDistLine4)));
double dMaxLineDist = Math.Max(dDistLine1, Math.Max(dDistLine2,
    Math.Max(dDistLine3, dDistLine4)));

Si les points du rectangle ne correspondent pas à l'étendue du segment, il n'y a pas d'intersection.

if (dMaxLineDist <= 0.0 || dMinLineDist >= dLengthLine)
    /* no intersection */;

Je crois que c'est suffisant.

1
ulatekh

Obtenez le produit scalaire de tous les 4 sommets (les coins du rectangle) avec le vecteur de direction du segment de ligne. Si tous les 4 ont des valeurs du même signe, tous les sommets sont situés du même côté de la ligne (pas le segment de ligne, mais la ligne infinie) et la ligne ne coupe donc pas le rectangle. Cette approche n'est viable que pour la détection d'intersection 2D. Ceci peut être utilisé pour filtrer rapidement la plupart d’entre eux (en utilisant uniquement des multiplications et des ajouts). Vous devrez faire des vérifications supplémentaires pour les segments de ligne au lieu de lignes.

0
Srinivasan