Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

DaiFei

Frischling

  • »DaiFei« ist der Autor dieses Themas

Beiträge: 28

Wohnort: München

  • Private Nachricht senden

1

10.05.2014, 13:55

Problem mit der Berechnung des Winkels einer Strecke (acos() liefert -1.#IND)

Hallo zusammen!

Ich stehe hier gerade nicht unbedingt vor einem formeltechnischen/mathematischen,
dafür aber vielleicht vor einem Verständnisproblem was die Präzision von float
und double anbelangt.

Ich versuche den Winkel zwischen 2 Punkten respektive einer
Strecke im 2-dimensionalen zu berechnen. Die Richtung (0,-1) stellt dabei 0
Grad dar (oben/Norden), im Uhrzeigersinn geht es dann in positive Richtung.
Siehe hierzu erst das Bild und danach folgenden Code:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    // Winkel der Strecke CB; 0° in negativer y-Richtung (Ausrichtung nach oben/Norden);
    // vorwärts im Uhrzeigersinn
    float WinkelDerLinie(const Vector2D& C, const Vector2D& B)
    {
        // Punkt A := (C.x, C.y - 1)
        // -> Liegt auf der Y-Achse 1 über Punkt C => |AC| = b = 1

        // Formel: c² = a² + b² - 2ab * cos(gamma)

        const Vector2D CB = B - C;
        const float a_square = CB.x * CB.x + CB.y * CB.y;

        const Vector2D AB = B - Vector2D(C.x, C.y - 1);
        const float c_square = AB.x * AB.x + AB.y * AB.y;

        // Ausdruck für den ArcCos
        float tmpVal = (a_square + 1 - c_square) / (2 * sqrtf(a_square));

        /** EHER UNELEGANTE LÖSUNG: **/

        // Verhinderung von Werten > 1 und < -1
        if (tmpVal > 1)
            tmpVal = 1.0f;
        else if (tmpVal < -1)
            tmpVal = -1.0f;


        // Wenn Punkt B links von C liegt (entlang der x-Achse),
        // dann Ergebnis von 2 * pi abziehen

        if (B.x < C.x)
            return CONST_2PI - acosf(tmpVal);
        else
            return acosf(tmpVal);
    }


Nun habe ich das Problem, dass ich bei 180° bzw. pi ein -1.#IND
bekomme. Inzwischen bin ich soweit, dass ich weiß, dass die Funktion acos()
hier einen Wert entweder >1 oder <-1 bekommt, was zu obigem return-Wert
führt. Dieses Problem habe ich bisher (auf wahrscheinlich unelegante Weise) mit
if-Abfragen gelöst. Möchte Euch daher einmal fragen, ob Euch dazu etwas besseres,
wie zum Beispiel eine bessere Formel oder ein besserer Umgang mit float oder
double einfällt. Vielen Dank!
»DaiFei« hat folgendes Bild angehängt:
  • Problemstellung.jpg

2

10.05.2014, 15:02

  1. Es gibt keine "Winkel von Strecken". Es gibt Winkel zwischen zwei Strecken bzw. Vektoren. Einer deiner Vektoren ist der Richtungsvektor einer Geraden durch zwei Punkte B und C, der zweite Vektor ist der Richtungsvektor der y-Achse.
  2. Ich empfehle dir, sinnvolle Variablennamen zu nutzen. "tmpVal" sagt garnichts darüber aus, was die Variable speichert.
  3. Du bekommst ein falsches Ergebnis, weil du deine Vektoren nicht normalisiert hast. Probiere doch mal sowas:

    C-/C++-Quelltext

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    // Calculates the angle between a ray through two given points and the Up-Vector
    // "Up" means the up-vector, which is assumed to be (0, 1)
    float RayUpAngle(const Vector2D& p1, const Vector2D& p2)
    {
        // first, we calculate the dot product of the normalized vectors
        Vector2D rayVector(p2 - p1); // from p1 to p2
        rayVector.Normalize();
        double dRayDotUp = (double)rayVector.y; // short form of: rayVector.x * 0 + rayVector.y * 1        
        
        // dot product returns the cosine of the angle. now we want to get the actual angle of this cosine
        double dAngle = acos(dRayDotUp);
        
        // finally we want the clock-wise-angle. We assume that p1 is the position vector
        if (p2.x < p1.x)
            dAngle = 2*PI - dAngle;
    
        return (float)dAngle;
    }
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »iSmokiieZz« (10.05.2014, 15:09)