Comment calculer à la fois l'angle positif et l'angle négatif entre deux lignes?

il y a un ensemble très pratique d'Utilités de géométrie 2d ici.

l'angleBetweenLines a cependant un problème. Le résultat est toujours positif. Je dois détecter les angles positifs et négatifs, donc si une ligne est de 15 degrés "au-dessus" ou "au-dessous" de l'autre ligne, la forme semble évidemment différente.

la configuration que j'ai est qu'une ligne reste stationnaire, tandis que l'autre tourne, et j'ai besoin de comprendre dans quelle direction elle tourne., en le comparant avec la ligne fixe.

EDIT: en réponse à swestrup de commentaire ci-dessous, la situation est en fait que j'ai une seule ligne, et j'enregistre sa position de départ. La ligne tourne alors de sa position de départ, et je dois calculer l'angle de sa position de départ à la position actuelle. E. g si elle a tourné dans le sens horaire, il est positif de rotation; si dans le sens antihoraire, puis négative. (Ou vice-versa.)

Comment améliorer l'algorithme afin qu'il renvoie l'angle à la fois positif ou négatif selon la position des lignes?

13
demandé sur Jaanus 2010-04-18 23:16:14

6 réponses

la réponse de@duffymo est correcte, mais si vous ne voulez pas mettre en œuvre le produit croisé, vous pouvez utiliser le atan2 fonction. Ceci renvoie à un angle compris entre -π et π, et vous pouvez l'utiliser sur chacune des lignes (ou plus précisément les vecteurs représentant les lignes).

si vous obtenez un angle θ Pour la première (ligne stationnaire), vous devrez normaliser l'angle φ pour que la deuxième ligne soit entre θ-π Et θ+π (en ajoutant ±2π). L'angle entre les deux lignes sera alors φ-θ.

8
répondu brainjam 2010-04-18 19:47:07

Voici la mise en œuvre de la suggestion de brainjam's. (Cela fonctionne avec mes contraintes que la différence entre les lignes est garantie d'être suffisamment petite qu'il n'y a pas besoin de normaliser quoi que ce soit.)

CGFloat angleBetweenLinesInRad(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) {
    CGFloat a = line1End.x - line1Start.x;
    CGFloat b = line1End.y - line1Start.y;
    CGFloat c = line2End.x - line2Start.x;
    CGFloat d = line2End.y - line2Start.y;

    CGFloat atanA = atan2(a, b);
    CGFloat atanB = atan2(c, d);

    return atanA - atanB;
}

j'aime que ce soit concis. La version vectorielle serait-elle plus concise?

19
répondu Jaanus 2018-10-04 07:59:17

C'est un problème facile impliquant des vecteurs 2D. Le sinus de l'angle entre deux vecteurs est relié au produit croisé entre les deux vecteurs. Et "au-dessus" ou "au-dessous" est déterminé par le signe du vecteur qui est produit par le produit croisé: si vous croisez deux vecteurs A et B, et le produit croisé produit est positif, alors A est "au-dessous" B; si c'est négatif, A est "au-dessus" B. voir Mathworld pour plus de détails.

Voici comment je pourrais le coder Java:

package cruft;

import java.text.DecimalFormat;
import java.text.NumberFormat;

/**
 * VectorUtils
 * User: Michael
 * Date: Apr 18, 2010
 * Time: 4:12:45 PM
 */
public class VectorUtils
{
    private static final int DEFAULT_DIMENSIONS = 3;
    private static final NumberFormat DEFAULT_FORMAT = new DecimalFormat("0.###");

    public static void main(String[] args)
    {
        double [] a = { 1.0, 0.0, 0.0 };
        double [] b = { 0.0, 1.0, 0.0 };

        double [] c = VectorUtils.crossProduct(a, b);

        System.out.println(VectorUtils.toString(c));
    }

    public static double [] crossProduct(double [] a, double [] b)
    {
        assert ((a != null) && (a.length >= DEFAULT_DIMENSIONS ) && (b != null) && (b.length >= DEFAULT_DIMENSIONS));

        double [] c = new double[DEFAULT_DIMENSIONS];

        c[0] = +a[1]*b[2] - a[2]*b[1];
        c[1] = +a[2]*b[0] - a[0]*b[2];
        c[2] = +a[0]*b[1] - a[1]*b[0];

        return c;
    }

    public static String toString(double [] a)
    {
        StringBuilder builder = new StringBuilder(128);

        builder.append("{ ");

        for (double c : a)
        {
            builder.append(DEFAULT_FORMAT.format(c)).append(' ');
        }

        builder.append("}");

        return builder.toString();
    }
}

Vérifier le signe du 3ème composant. S'il est positif, A est "inférieur" b; s'il est négatif, A est "supérieur" b - aussi longtemps que les deux vecteurs sont dans les deux quadrants à la droite de l'axe des Y. Évidemment, s'ils sont tous les deux dans les deux quadrants à gauche de l'axe des y, l'inverse est vrai.

vous devez penser à vos notions intuitives de "Au-dessus"et" au-dessous". Que faire si Un est dans le premier quadrant (0 <= θ <= 90) et B est dans le deuxième quadrant (90 <= θ <= 180)? "Au-dessus" et "dessous" perdent leur sens.

La ligne tourne alors de son position de départ, et j'ai besoin de calculer l'angle de son départ la position à la position actuelle. E. g si il a tourné dans le sens horaire, il est rotation positive; si dans le sens antihoraire, puis négative. (Ou inversement.)

C'est exactement à cela que sert le produit croisé. Le signe du 3ème composant est positif pour le sens contraire des aiguilles d'une montre et négatif pour des aiguilles d'une montre (en regardant dans le plan de rotation).

7
répondu duffymo 2018-10-04 07:59:46

une méthode "rapide et sale" que vous pouvez utiliser est d'introduire une troisième ligne de référence R. donc, compte tenu de deux lignes A et B, calculer les angles entre A et R et ensuite B et R, et les soustraire.

Cela fait environ deux fois plus de calcul que nécessaire, mais est facile à expliquer et à déboguer.

1
répondu swestrup 2010-04-18 19:35:40

Cette fonction travaille en RADS

Il y a 2pi RADS dans un tour complet (360 degrés)

ainsi je crois que l'answear que vous recherchez est tout simplement la valeur retournée - 2pi

Si vous demandez à avoir qu'une fonction renvoie les deux valeurs en même temps, puis vous demandez à briser la langue, une fonction ne peut retourner qu'une seule valeur. Vous pourriez passer deux pointeurs qu'il peut utiliser pour définir la valeur de sorte que le changement peut persister après l' la fonction se termine et votre programme peut continuer à fonctionner. Mais pas vraiment un bon moyen de résoudre ce problème.

Modifier

je viens de remarquer que la fonction convertit en fait les Rads en degrés quand elle renvoie la valeur. Mais le même principe fonctionnera.

0
répondu thecoshman 2010-04-18 19:23:39
// Considering two vectors CA and BA
// Computing angle from CA to BA
// Thanks to code shared by Jaanus, but atan2(y,x) is used wrongly.

float getAngleBetweenVectorsWithSignInDeg(Point2f C, Point2f A, Point2f B)
{      
    float a = A.x - C.x;
    float b = A.y - C.y;
    float c = B.x - C.x;
    float d = B.y - C.y;

    float angleA = atan2(b, a);
    float angleB = atan2(d, c);
    cout << "angleA: " << angleA << "rad, " << angleA * 180 / M_PI << " deg" << endl;
    cout << "angleB: " << angleB << "rad, " << angleB * 180 / M_PI << " deg" << endl;
    float rotationAngleRad = angleB - angleA;
    float thetaDeg = rotationAngleRad * 180.0f / M_PI;
    return thetaDeg;
}
0
répondu marikhu 2018-10-04 08:09:24