Comment puis-je déterminer si deux lignes se croisent ou non, et si c'est le cas, à quel point x, y?
geometry
line-intersection
KingNestor
la source
la source
Réponses:
Il existe une belle approche à ce problème qui utilise des produits croisés vectoriels. Définissez le produit vectoriel vectoriel bidimensionnel v × w comme étant v x w y - v y w x .
Supposons que les deux segments de ligne s'étendent de p à p + r et de q à q + s . Alors tout point sur la première ligne est représentable comme p + t r (pour un paramètre scalaire t ) et tout point sur la deuxième ligne comme q + u s (pour un paramètre scalaire u ).
Les deux droites se croisent si nous pouvons trouver t et u tels que:
Traversez les deux côtés avec s ,
Et puisque s × s = 0, cela signifie
Et donc, résoudre pour t :
De la même manière, nous pouvons résoudre pour u :
Pour réduire le nombre d'étapes de calcul, il est pratique de réécrire ceci comme suit (en se souvenant que s × r = - r × s ):
Maintenant, il y a quatre cas:
Si r × s = 0 et ( q - p ) × r = 0, alors les deux lignes sont colinéaires.
Dans ce cas, exprimez les points d'extrémité du deuxième segment ( q et q + s ) en termes d'équation du premier segment de ligne ( p + t r ):
Si l'intervalle entre t 0 et t 1 croise l'intervalle [0, 1] alors les segments de ligne sont colinéaires et se chevauchent; sinon, ils sont colinéaires et disjoints.
Notez que si s et r pointent dans des directions opposées, alors s · r <0 et donc l'intervalle à vérifier est [ t 1 , t 0 ] plutôt que [ t 0 , t 1 ].
Si r × s = 0 et ( q - p ) × r ≠ 0, alors les deux droites sont parallèles et sans intersection.
Si r × s ≠ 0 et 0 ≤ t ≤ 1 et 0 ≤ u ≤ 1, les deux segments de droite se rencontrent au point p + t r = q + u s .
Sinon, les deux segments de ligne ne sont pas parallèles mais ne se coupent pas.
Crédit: cette méthode est la spécialisation bidimensionnelle de l'algorithme d'intersection de lignes 3D de l'article "Intersection of two lines in three-space" de Ronald Goldman, publié dans Graphics Gems , page 304. En trois dimensions, le cas habituel est que les lignes sont asymétriques (ni parallèles ni entrecroisées) auquel cas la méthode donne les points d'approche les plus proches des deux lignes.
la source
/ (r × s)
, mais(r × s)
c'est un vecteur, non? Un vecteur(0, 0, rx * sy - ry * sx)
. Et le côté gauche est également un vecteur parallèle à l'axe z. Alors ... est-ce que je divise simplement le composant z par l'autre composant z? La formule de t est-elle réellement|(q − p) × s| / |(r × s)|
?FWIW, la fonction suivante (en C) détecte à la fois les intersections de lignes et détermine le point d'intersection. Il est basé sur un algorithme des " Astuces des gourous de la programmation des jeux Windows " d'André LeMothe . Ce n'est pas différent de certains algorithmes dans d'autres réponses (par exemple, Gareth). LeMothe utilise ensuite la règle de Cramer (ne me demandez pas) pour résoudre les équations elles-mêmes.
Je peux attester que cela fonctionne dans mon clone d'astéroïdes faibles et semble traiter correctement les cas marginaux décrits dans d'autres réponses par Elemental, Dan et Wodzu. Il est également probablement plus rapide que le code publié par KingNestor, car tout est multiplication et division, pas de racines carrées!
Je suppose qu'il y a un potentiel de division par zéro là-dedans, même si cela n'a pas été un problème dans mon cas. Assez facile à modifier pour éviter le crash de toute façon.
BTW, je dois dire que dans le livre de LeMothe, bien qu'il ait apparemment le bon algorithme, l'exemple concret qu'il montre des prises avec de mauvais nombres et fait des calculs erronés. Par exemple:
Cela m'a dérouté pendant des heures . :(
la source
s
ett
directement, tester la relation entre les deux numérateurs et le dénominateur. Ce n'est que s'il est confirmé que les lignes se croisent que vous devez réellement calculer la valeur det
(mais pass
).Le problème se résume à cette question: deux lignes de A à B et de C à D se coupent-elles? Ensuite, vous pouvez le demander quatre fois (entre la ligne et chacun des quatre côtés du rectangle).
Voici les mathématiques vectorielles pour le faire. Je suppose que la ligne de A à B est la ligne en question et la ligne de C à D est l'une des lignes rectangulaires. Ma notation est
Ax
la "coordonnée x de A" etCy
la "coordonnée y de C." Et "*
" signifie produit scalaire, par exempleA*B = Ax*Bx + Ay*By
.Ce
h
numéro est la clé. Sih
est entre0
et1
, les lignes se coupent, sinon elles ne le font pas. SiF*P
est nul, vous ne pouvez bien sûr pas faire le calcul, mais dans ce cas les lignes sont parallèles et ne se coupent donc que dans les cas évidents.Le point d'intersection exact est
C + F*h
.Plus amusant:
Si
h
est exactement0
ou1
les lignes se touchent à un point final. Vous pouvez considérer cela comme une "intersection" ou non comme bon vous semble.Plus précisément,
h
c'est combien vous devez multiplier la longueur de la ligne pour toucher exactement l'autre ligne.Par conséquent, Si
h<0
, cela signifie que la ligne rectangle est "derrière" la ligne donnée (avec "direction" étant "de A à B"), et sih>1
la ligne rectangle est "en face" de la ligne donnée.Dérivation:
A et C sont des vecteurs qui pointent vers le début de la ligne; E et F sont les vecteurs des extrémités de A et C qui forment la ligne.
Pour deux lignes non parallèles dans le plan, il doit y avoir exactement une paire de scalaires
g
eth
telle que cette équation soit vraie:Pourquoi? Parce que deux lignes non parallèles doivent se croiser, ce qui signifie que vous pouvez mettre à l'échelle les deux lignes d'une certaine quantité et vous toucher.
( Au début, cela ressemble à une seule équation avec deux inconnues! Mais ce n'est pas quand vous considérez qu'il s'agit d'une équation vectorielle 2D, ce qui signifie qu'il s'agit vraiment d'une paire d'équations dans
x
ety
.)Nous devons éliminer l'une de ces variables. Un moyen simple est de rendre le
E
terme zéro. Pour ce faire, prenez le produit scalaire des deux côtés de l'équation en utilisant un vecteur qui pointera à zéro avec E. Ce vecteur que j'ai appeléP
ci-dessus, et j'ai fait la transformation évidente de E.Vous avez maintenant:
la source
A + E*g = C + F*h
Les deux lignes se croisent si et seulement si la solution de cette équation (en supposant qu'elles ne sont pas parallèles) a les deux,g
eth
entre 0 et 1 (en ou exclusif, selon que vous comptez toucher en un point final).J'ai essayé d'implémenter l'algorithme si élégamment décrit par Jason ci-dessus; malheureusement, tout en travaillant bien que les mathématiques dans le débogage, j'ai trouvé de nombreux cas pour lesquels cela ne fonctionne pas.
Par exemple, considérons les points A (10,10) B (20,20) C (10,1) D (1,10) donne h = 0,5 et pourtant il est clair en examinant que ces segments ne sont nulle part près de chacun autre.
La représentation graphique de ceci montre clairement que les critères 0 <h <1 indiquent seulement que le point d'interception se situerait sur CD s'il existait, mais ne dit rien de si ce point se trouve sur AB. Pour vous assurer qu'il existe un point de croisement, vous devez effectuer le calcul symétrique pour la variable g et l'exigence d'interception est: 0 <g <1 ET 0 <h <1
la source
Voici une amélioration de la réponse de Gavin. La solution de Marcp est également similaire, mais aucune ne retarde la division.
Cela s'avère en fait être une application pratique de la réponse de Gareth Rees également, car l'équivalent du produit croisé en 2D est le produit perp-dot, ce dont ce code utilise trois. Passer à la 3D et utiliser le produit croisé, interpoler à la fois s et t à la fin, entraîne les deux points les plus proches entre les lignes en 3D. Quoi qu'il en soit, la solution 2D:
Fondamentalement, il reporte la division jusqu'au dernier moment et déplace la plupart des tests jusqu'à ce que certains calculs soient effectués, ajoutant ainsi des sorties anticipées. Enfin, il évite également la division par zéro casse qui se produit lorsque les lignes sont parallèles.
Vous pouvez également envisager d'utiliser un test epsilon plutôt qu'une comparaison avec zéro. Les lignes extrêmement proches de la parallèle peuvent produire des résultats légèrement décalés. Ce n'est pas un bug, c'est une limitation avec les mathématiques à virgule flottante.
la source
s32_y
lieu de quelque chose qui décrit ce que c'estpoint2YDifference
?Question C: Comment détectez-vous si deux segments de ligne se croisent ou non?
J'ai cherché le même sujet et je n'étais pas satisfait des réponses. J'ai donc écrit un article qui explique très en détail comment vérifier si deux segments de ligne se croisent avec beaucoup d'images. Il existe un code Java complet (et testé).
Voici l'article, rogné aux parties les plus importantes:
L'algorithme, qui vérifie si le segment de ligne a croise le segment de ligne b, ressemble à ceci:
Que sont les boîtes englobantes? Voici deux boîtes englobantes de deux segments de ligne:
Si les deux boîtes englobantes ont une intersection, vous déplacez le segment de ligne a de sorte qu'un point soit à (0 | 0). Vous avez maintenant une ligne passant par l'origine définie par a. Maintenant, déplacez le segment de ligne b de la même manière et vérifiez si les nouveaux points du segment de ligne b se trouvent de différents côtés de la ligne a. Si tel est le cas, vérifiez-le dans l'autre sens. Si c'est également le cas, les segments de ligne se coupent. Sinon, ils ne se croisent pas.
Question A: Où se croisent deux segments de ligne?
Vous savez que deux segments de droite a et b se coupent. Si vous ne le savez pas, vérifiez-le avec les outils que je vous ai fournis dans la "Question C".
Vous pouvez maintenant passer en revue certains cas et obtenir la solution avec des mathématiques de 7e année (voir code et exemple interactif ).
Question B: Comment détectez-vous si deux lignes se croisent ou non?
Disons que votre point
A = (x1, y1)
, le pointB = (x2, y2)
,C = (x_3, y_3)
,D = (x_4, y_4)
. Votre première ligne est définie par AB (avec A! = B), et votre seconde par CD (avec C! = D).Question D: Où se croisent deux lignes?
Vérifiez avec la question B si elles se recoupent.
Les lignes a et b sont définies par deux points pour chaque ligne. Vous pouvez essentiellement appliquer la même logique que celle utilisée à la question A.
la source
La réponse une fois acceptée ici est incorrecte (elle a depuis été refusée, alors hourra!). Il n'élimine pas correctement toutes les non-intersections. Trivialement, il peut sembler fonctionner, mais il peut échouer, en particulier dans le cas où 0 et 1 sont considérés comme valides pour h.
Considérez le cas suivant:
Lignes à (4,1) - (5,1) et (0,0) - (0,2)
Ce sont des lignes perpendiculaires qui ne se chevauchent clairement pas.
A = (4,1)
B = (5,1)
C = (0,0)
D = (0,2)
E = (5,1) - (4,1) = (- 1,0)
F = (0,2) - (0,0) = (0, -2)
P = (0,1)
h = ((4,1) - (0,0)) point (0,1) / ((0 , -2) point (0,1)) = 0
Selon la réponse ci-dessus, ces deux segments de ligne se rencontrent à un point final (valeurs de 0 et 1). Ce critère serait:
(0,0) + (0, -2) * 0 = (0,0)
Donc, apparemment, les deux segments de ligne se rencontrent en (0,0), qui est sur la ligne CD, mais pas sur la ligne AB. Donc, qu'est-ce qui ne va pas? La réponse est que les valeurs de 0 et 1 ne sont pas valides et seulement parfois HAPPEN pour prédire correctement l'intersection de point final. Lorsque l'extension d'une ligne (mais pas de l'autre) rencontrerait le segment de ligne, l'algorithme prédit une intersection de segments de ligne, mais ce n'est pas correct. J'imagine qu'en testant en commençant par AB vs CD puis en testant également avec CD vs AB, ce problème serait éliminé. Ce n'est que si les deux se situent entre 0 et 1 inclusivement qu'on peut dire qu'ils se coupent.
Je recommande d'utiliser la méthode des produits croisés vectoriels si vous devez prédire les points finaux.
-Dan
la source
Version Python de la réponse d'iMalc:
la source
denom = float(...)
Trouver l'intersection correcte de deux segments de ligne est une tâche non triviale avec beaucoup de cas de bord. Voici une solution bien documentée, fonctionnelle et testée en Java.
Essentiellement, trois choses peuvent se produire lors de la recherche de l'intersection de deux segments de ligne:
Les segments ne se coupent pas
Il y a un point d'intersection unique
L'intersection est un autre segment
REMARQUE : dans le code, je suppose qu'un segment de ligne (x1, y1), (x2, y2) avec x1 = x2 et y1 = y2 est un segment de ligne valide. Mathématiquement parlant, un segment de ligne se compose de points distincts, mais je permets aux segments d'être des points dans cette implémentation pour être complet.
Le code est tiré de mon dépôt github
Voici un exemple d'utilisation simple:
la source
Je voulais juste mentionner qu'une bonne explication et une solution explicite peuvent être trouvées dans la série Recettes numériques. J'ai la 3e édition et la réponse se trouve à la page 1117, section 21.4. Une autre solution avec une nomenclature différente peut être trouvée dans un article de Marina Gavrilova Reliable Line Section Intersection Testing . Sa solution est, à mon avis, un peu plus simple.
Mon implémentation est ci-dessous:
la source
De nombreuses solutions sont disponibles ci-dessus, mais je pense que la solution ci-dessous est assez simple et facile à comprendre.
Deux segments Vector AB et Vector CD se croisent si et seulement si
Plus précisément, a et b sont sur le côté opposé du segment CD si et seulement si exactement l'un des deux triplets a, c, d et b, c, d est dans le sens antihoraire.
Ici, CCW représente dans le sens antihoraire qui renvoie vrai / faux en fonction de l'orientation des points.
Source: http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf Page 2
la source
CCW
test est-il défini? Avec le signe du produit extérieur?C et Objectif-C
D'après la réponse de Gareth Rees
Beaucoup de fonctions et de structures sont privées, mais vous devriez assez facilement pouvoir savoir ce qui se passe. Ceci est public sur ce dépôt https://github.com/hfossli/AGGeometryKit/
la source
Cela fonctionne bien pour moi. Pris d' ici .
la source
J'ai essayé certaines de ces réponses, mais elles n'ont pas fonctionné pour moi (désolé les gars); après quelques recherches sur Internet, j'ai trouvé cela .
Avec une petite modification de son code, j'ai maintenant cette fonction qui retournera le point d'intersection ou si aucune intersection n'est trouvée, elle retournera -1, -1.
la source
Il semble y avoir un certain intérêt dans la réponse de Gavin pour laquelle cortijon a proposé une version javascript dans les commentaires et iMalc a fourni une version avec un peu moins de calculs . Certains ont signalé des lacunes dans diverses propositions de code et d'autres ont commenté l'efficacité de certaines propositions de code.
L'algorithme fourni par iMalc via la réponse de Gavin est celui que j'utilise actuellement dans un projet javascript et je voulais juste fournir une version nettoyée ici si cela peut aider quelqu'un.
la source
t = dx2 * dy3 - dx3 * dy2;
...t/d
crossProduct = (line1XDifference * line2YDifference) - (line2XDifference * line1YDifference)
etscaledResult = crossProduct / dotProduct
?p1x, p1y
etc. sont destinés à décrire les points par leurs valeurs x et y, doncp1x
c'est une abréviation pourpoint1x
, de mêmed1x
, dans mon esprit, c'est une abréviation pour la lettre grecquedeltaX
ou vous pourriez diredifferenceInX
. (plus)Je pense qu'il existe une solution beaucoup plus simple à ce problème. J'ai eu une autre idée aujourd'hui et elle semble fonctionner très bien (au moins en 2D pour l'instant). Tout ce que vous avez à faire est de calculer l'intersection entre deux lignes, puis de vérifier si le point d'intersection calculé se trouve dans les zones de délimitation des deux segments de ligne. Si tel est le cas, les segments de ligne se coupent. C'est ça.
ÉDITER:
Voici comment je calcule l'intersection (je ne sais plus où j'ai trouvé cet extrait de code)
vient de
et voici ma classe BoundingBox (simplifiée aux fins de la réponse):
la source
Cette solution peut aider
la source
J'ai porté la réponse ci-dessus de Kris sur JavaScript. Après avoir essayé de nombreuses réponses différentes, la sienne a fourni les bons points. Je pensais devenir fou que je n'obtenais pas les points dont j'avais besoin.
la source
J'ai essayé de nombreuses façons et j'ai décidé d'écrire la mienne. Voici donc:
Basé sur ces deux formules: (je les ai simplifiées à partir de l'équation des lignes et d'autres formules)
la source
Ceci basé sur la réponse de Gareth Ree. Il renvoie également le chevauchement des segments de ligne s'ils le font. Codé en C ++, V est une classe vectorielle simple. Où le produit croisé de deux vecteurs en 2D renvoie un seul scalaire. Il a été testé et approuvé par le système de test automatique de mon école.
la source
Voici une implémentation de base d'un segment de ligne en C #, avec le code de détection d'intersection correspondant. Il nécessite une structure vectorielle / point 2D appelée
Vector2f
, bien que vous puissiez la remplacer par tout autre type ayant des propriétés X / Y. Vous pouvez également remplacerfloat
pardouble
si cela convient mieux à vos besoins.Ce code est utilisé dans ma bibliothèque de physique .NET, Boing .
la source
Un programme C ++ pour vérifier si deux segments de ligne donnés se croisent
la source
Basé sur la réponse de @Gareth Rees, version pour Python:
la source
Si chaque côté du rectangle est un segment de ligne et que la partie dessinée par l'utilisateur est un segment de ligne, vous devez simplement vérifier le segment dessiné par l'utilisateur pour l'intersection avec les quatre segments de ligne latéraux. Cela devrait être un exercice assez simple étant donné les points de début et de fin de chaque segment.
la source
Basé sur la réponse de t3chb0t:
la source
J'ai lu ces algorithmes du livre "géométrie à vues multiples"
texte suivant utilisant
'comme signe de transposition
* comme produit scalaire
x comme produit croisé, en cas d'utilisation comme opérateur
1. définition de la ligne
un point x_vec = (x, y) 'se trouve sur la ligne ax + de + c = 0
nous notons L = (a, b, c) ', le point comme (x, y, 1)' comme coordonnées homogènes
l'équation de la ligne peut s'écrire
(x, y, 1) (a, b, c) '= 0 ou x' * L = 0
2. intersection de lignes
nous avons deux lignes L1 = (a1, b1, c1) ', L2 = (a2, b2, c2)'
supposons que x est un point, un vecteur et x = L1 x L2 (produit croisé L1 L2).
attention, x est toujours un point 2D, veuillez lire les coordonnées homogènes si vous êtes confus (L1xL2) est un vecteur à trois éléments, et x est une coordonnée 2D.
selon le triple produit, nous savons que
L1 * (L1 x L2) = 0, et L2 * (L1 x L2) = 0, en raison du co-plan L1, L2
nous substituons (L1xL2) par le vecteur x, puis nous avons L1 * x = 0, L2 * x = 0, ce qui signifie que x se trouve à la fois sur L1 et L2, x est le point d'intersection.
attention, ici x est des coordonnées homogènes, si le dernier élément de x est nul, cela signifie que L1 et L2 sont parallèles.
la source
De nombreuses réponses ont regroupé tous les calculs dans une seule fonction. Si vous devez calculer les pentes de ligne, les intersections y ou les intersections x pour les utiliser ailleurs dans votre code, vous effectuerez ces calculs de manière redondante. J'ai séparé les fonctions respectives, utilisé des noms de variables évidents et commenté mon code pour le rendre plus facile à suivre. J'avais besoin de savoir si les lignes se croisent infiniment au-delà de leurs points de terminaison, donc en JavaScript:
http://jsfiddle.net/skibulk/evmqq00u/
la source