Quelle est la formule pour obtenir un vecteur tridimensionnel B situé sur le plan perpendiculaire à un vecteur A?
C’est-à-dire, étant donné un vecteur A, quelle est la formule f (angle, module) qui donne un vecteur perpendiculaire à A, avec ledit module et tourné d’un angle?
Si les deux vecteurs sont perpendiculaires, leur produit scalaire est égal à zéro.
Donc: v1(x1, y1, z1), v2(x2, y2, z2)
.
=> x1 * x2 + y1 * y2 + z1 * z2 = 0
Vous connaissez (x1, y1, z1)
. Mettez les x2
ety2
arbitraires et vous recevrez le z2
correspondant:
z1 * z2 = -x1 * x2 - y1 * y2
=> z2 = (-x1 * x2 - y1 * y2) / z1
Soyez conscient si z1
est 0
. Ensuite, vous êtes dans l'avion.
Calculez le produit croiséAxC
avec un autre vecteur C
qui n'est pas colinéaire avec A
.
Il existe de nombreuses directions possibles dans le plan perpendiculaire à A
. Si vous ne vous en souciez pas vraiment, choisissez un vecteur arbitraire C
non colinéaire avec A
:
if (A2 != 0 || A3 != 0)
C = (1, 0, 0);
else
C = (0, 1, 0);
B = A x C;
function (a,b,c)
{
return (-b,a,0)
}
Mais cette réponse n'est pas stable numériquement lorsque a, b sont proches de 0.
Pour éviter ce cas, utilisez:
function (a,b,c)
{
return c<a ? (b,-a,0) : (0,-c,b)
}
La réponse ci-dessus est numériquement stable, car dans le cas où c < a
puis max(a,b) = max(a,b,c)
, puis vector(b,-a,0).length() > max(a,b) = max(a,b,c)
, et puisque max(a,b,c)
ne devrait pas être proche de zéro, le vecteur l'est également. Le cas c > a
est similaire.
q4W56 est presque là pour une solution robuste. Problèmes: 1) Ne prend pas en compte la mise à l'échelle. 2) Ne compare pas la magnitude entre deux variables quand il le devrait.
scale = |x| + |y| + |z|
if scale == 0:
return (0,0,0)
x = x/scale
y = y/scale
z = z/scale
if |x| > |y|:
return (z, 0,-x)
else:
return (0, z,-y)
La mise à l'échelle est importante lorsqu'il s'agit de très grands ou de très petits nombres. De plus, en général, il est préférable d’effectuer des opérations en virgule flottante sur des valeurs comprises entre 0 et 1.
Une solution consisterait à trouver une transformation de rotation de l'axe z positif (ou de tout autre axe) en un vecteur donné. Puis transformez <modulus * cos(angle), modulus * sin(angle), 0>
en utilisant cette transformation.
def getPerpendicular(v1,modulus,angle):
v2 = vector(0,0,1)
v1_len = v2.length()
axis = v1.cross_product(v2)
sinAngle = axis.length() / v1_len # |u x v| = |u| * |v| * sin(angle)
cosAngle = v1.dot_product(v2) / v1_len # u . v = |u| * |v| * cos(angle)
axis = axis.normalize()
# atan2(sin(a), cos(a)) = a, -pi < a < pi
angle = math.atan2(sinAngle, cosAngle)
rotationMatrix = fromAxisAngle(axis, angle)
# perpendicular to v2
v3 = vector(modulus*cos(angle),modulus*sin(angle),0)
return rotationMatrix.multiply(v3);
Pour calculer la matrice de rotation, voir cet article: WP: matrice de rotation d'axe et d'angle
Une autre méthode consisterait à utiliser quaternion rotation . C'est un peu plus compliqué, mais moins de chiffres à garder en tête.
Je pense que cela devrait produire un vecteur arbitraire perpendiculaire au vecteur donné vec
tout en restant numériquement stable quel que soit l'angle de vec
(en supposant que la valeur de vec
ne soit pas proche de zéro). Supposons que Vec3D est un vecteur tridimensionnel de type numérique arbitraire.
Vec3D arbitrary_orthogonal(Vec3D vec)
{
bool b0 = (vec[0] < vec[1]) && (vec[0] < vec[2]);
bool b1 = (vec[1] <= vec[0]) && (vec[1] < vec[2]);
bool b2 = (vec[2] <= vec[0]) && (vec[2] <= vec[1]);
return cross(vec, Vec3D(int(b0), int(b1), int(b2)));
}