J'ai deux segments: X1, Y1, Z1 - X2, Y2, Z2 et X3, Y3, Z3 - X4, Y4, Z4
J'essaie de trouver la distance la plus courte entre les deux segments.
Je cherche une solution depuis des heures, mais ils semblent tous fonctionner avec des lignes plutôt que des segments.
Des idées sur la façon de s'y prendre, ou des sources de furmula?
Une approche de base équivaut à calculer la distance la plus courte entre 2 lignes, à une exception près.
Si vous examinez la plupart des algorithmes pour trouver la distance la plus courte entre 2 lignes, vous constaterez qu'il trouve les points les plus proches sur chaque ligne, puis calcule la distance à partir de ceux-ci.
L'astuce pour étendre cela aux segments (ou rayons) est de voir si ce point est au-delà de l'un des points d'extrémité de la ligne, et si c'est le cas, utilisez le point final au lieu du point le plus proche réel sur la ligne infinie.
Pour un exemple concret, voir:
http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm
Plus précisement:
http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm#dist3D_Segment_to_Segment ()
Je vais répondre à cela en termes de matlab, mais d'autres environnements de programmation peuvent être utilisés. J'ajouterai que cette solution est valide pour résoudre le problème dans un nombre quelconque de dimensions (> = 3).
Supposons que nous ayons deux segments de ligne dans l'espace, PQ et RS. Voici quelques ensembles de points aléatoires.
> P = randn(1,3)
P =
-0.43256 -1.6656 0.12533
> Q = randn(1,3)
Q =
0.28768 -1.1465 1.1909
> R = randn(1,3)
R =
1.1892 -0.037633 0.32729
> S = randn(1,3)
S =
0.17464 -0.18671 0.72579
La ligne infinie PQ (t) est facilement définie comme
PQ(u) = P + u*(Q-P)
De même, nous avons
RS(v) = R + v*(S-R)
Voir que pour chaque ligne, lorsque le paramètre est à 0 ou 1, nous obtenons l'un des points de terminaison d'origine sur la ligne retournée. Ainsi, nous savons que PQ (0) == P, PQ (1) == Q, RS (0) == R et RS (1) == S.
Cette façon de définir une ligne paramétrée est très utile dans de nombreux contextes.
Ensuite, imaginez que nous regardions le long de la ligne PQ. Pouvons-nous trouver le point de la plus petite distance du segment de ligne RS à la ligne infinie PQ? Cela se fait le plus facilement par une projection dans l'espace nul de la ligne PQ.
> N = null(P-Q)
N =
-0.37428 -0.76828
0.9078 -0.18927
-0.18927 0.61149
Ainsi, null (P-Q) est une paire de vecteurs de base qui couvrent le sous-espace bidimensionnel orthogonal à la ligne PQ.
> r = (R-P)*N
r =
0.83265 -1.4306
> s = (S-P)*N
s =
1.0016 -0.37923
Essentiellement, nous avons projeté le vecteur RS dans le sous-espace à 2 dimensions (plan) orthogonal à la ligne PQ. En soustrayant P (un point de la ligne PQ) pour obtenir r et s, nous nous assurons que la ligne infinie passe à travers l'origine dans ce plan de projection.
Donc, vraiment, nous avons réduit ceci à la recherche de la distance minimale entre la ligne rs (v) et l’origine (0,0) dans le plan de projection. Rappelez-vous que la ligne rs (v) est définie par le paramètre v comme suit:
rs(v) = r + v*(s-r)
Le vecteur normal à la ligne rs (v) nous donnera ce dont nous avons besoin. Puisque nous avons réduit cela à 2 dimensions parce que l'espace d'origine était en 3-D, nous pouvons le faire simplement. Sinon, j'aurais juste utilisé null à nouveau. Ce petit truc fonctionne en 2D:
> n = (s - r)*[0 -1;1 0];
> n = n/norm(n);
n est maintenant un vecteur avec une longueur unitaire. La distance entre la ligne infinie rs (v) et l’origine est simple.
> d = dot(n,r)
d =
1.0491
Voir que j'aurais aussi pu utiliser s, pour avoir la même distance. La distance réelle est abs (d), mais il s'est avéré que d était positif ici de toute façon.
> d = dot(n,s)
d =
1.0491
Peut-on déterminer v à partir de cela? Oui. Rappelez-vous que l'origine est une distance de d unités de la ligne qui relie les points r et s. Par conséquent, nous pouvons écrire d n = r + v (s-r), pour une certaine valeur du scalaire v. Former le produit scalaire de chaque côté de cette équation avec le vecteur (s-r) et résoudre pour v.
> v = dot(s-r,d*n-r)/dot(s-r,s-r)
v =
1.2024
Cela nous indique que l'approche du segment de ligne la plus proche de l'origine a eu lieu en dehors des extrémités du segment de ligne. Donc, vraiment le point le plus proche de rs de l’origine était le point rs (1) = s.
En partant de la projection, cela nous indique que le point le plus proche du segment RS de la ligne infinie PQ était le point S.
Il reste encore une étape dans l'analyse à effectuer. Quel est le point le plus proche du segment de droite PQ? Ce point se situe-t-il dans le segment de ligne ou tombe-t-il aussi en dehors des points d'extrémité?
Nous projetons le point S sur la ligne PQ. (Cette expression pour u dérive assez facilement de la même logique que précédemment. Notez que j'ai utilisé\pour faire le travail.)
> u = (Q-P)'\((S - (S*N)*N') - P)'
u =
0.95903
Voir que u se situe dans l'intervalle [0,1]. Nous avons résolu le problème. Le point sur la ligne PQ est
> P + u*(Q-P)
ans =
0.25817 -1.1677 1.1473
Et, la distance entre les points les plus proches sur les deux segments de ligne était
> norm(P + u*(Q-P) - S)
ans =
1.071
Bien entendu, tout cela peut être compressé en quelques lignes de code. Mais cela aide à tout développer pour mieux comprendre comment cela fonctionne.
Je voudrais paramétrer les deux segments de ligne pour utiliser un paramètre chacun, liés entre 0 et 1 inclus. Trouvez ensuite la différence entre les deux fonctions de ligne et utilisez-la comme fonction d'objectif dans un problème d'optimisation linéaire avec les paramètres comme variables.
Donc, disons que vous avez une ligne de (0,0,0) à (1,0,0) et une autre de (0,1,0) à (0,0,0) (oui, j'utilise des lignes faciles) . Les lignes peuvent être paramétrées comme (1 * t, 0 * t, 0 * t) où t est situé dans [0,1] et (0 * s, 1 * s, 0 * s) où s est situé dans [0,1 ], indépendant de t.
Ensuite, vous devez minimiser || (1 * t, 1 * s, 0) || où t, s sont situés dans [0,1]. C'est un problème assez simple à résoudre.
Que diriez-vous d'étendre les segments de ligne en lignes infinies et trouver la distance la plus courte entre les deux lignes. Recherchez ensuite sur chaque ligne les points qui sont les extrémités du segment de ligne de distance le plus court.
Si le point de chaque ligne se trouve sur le segment de ligne d'origine, vous avez la réponse. Si un point pour chaque ligne ne se trouve pas sur le segment d'origine, il est l'un des points d'extrémité des segments de ligne d'origine.
Trouver la distance entre deux lignes finies basée sur la recherche entre deux lignes infinies, puis relier les lignes infinies aux lignes finies ne fonctionne pas toujours. par exemple, essayez ces points
Q=[5 2 0]
P=[2 2 0]
S=[3 3.25 0]
R=[0 3 0]
Sur la base d'une approche infinie, l'algorithme sélectionne R et P pour le calcul de la distance (distance = 2,2361), mais quelque part au milieu de R et S se trouve à une distance plus proche du point P. Apparemment, la sélection de P et de [2 3.166] de R à S a une distance inférieure de 1.1666. Même cette réponse pourrait s’améliorer grâce à un calcul précis et à la recherche d’une ligne orthogonale de P à R ligne.
Tout d’abord, recherchez le segment de ligne d’approche le plus proche reliant leurs lignes prolongées. Appelons ce LineSeg BR.
Si BR.endPt1 tombe sur LS1 et BR.endPt2 sur LS2, vous avez terminé ... calculez simplement la longueur de BR.
Si le pont BR coupe LS1 mais pas LS2, utilisez la plus courte de ces deux distances: plus petitOf (dist (BR.endPt1, LS2.endPt1), dist (BR.endPt1, LS2.endPt2))
Si le pont BR coupe LS2 mais pas LS1, utilisez la plus courte de ces deux distances: plus petitOf (dist (BR.endPt2, LS1.endPt1), dist (BR.endPt2, LS1.endPt2))
Si aucune de ces conditions ne se vérifie, la distance la plus proche est l'appariement le plus proche des points d'extrémité sur les segments de ligne opposés.