3

In my application, a user taps 3 times and an angle will be created by the 3 points that were tapped. It draws the angle perfectly. I am trying to calculate the angle at the second tap, but I think I am doing it wrong (probably a math error). I haven't covered this in my calculus class yet, so I am going off of a formula on wikipedia.

http://en.wikipedia.org/wiki/Law_of_cosines

Here is what I am trying:

Note: First, Second, and Third are CGPoints created at the user's tap.

        CGFloat xDistA = (second.x - third.x);
        CGFloat yDistA = (second.y - third.y);
        CGFloat a = sqrt((xDistA * xDistA) + (yDistA * yDistA));

        CGFloat xDistB = (first.x - third.x);
        CGFloat yDistB = (first.y - third.y);
        CGFloat b = sqrt((xDistB * xDistB) + (yDistB * yDistB));

        CGFloat xDistC = (second.x - first.x);
        CGFloat yDistC = (second.y - first.y);
        CGFloat c = sqrt((xDistC * xDistC) + (yDistC * yDistC));

        CGFloat angle = acos(((a*a)+(b*b)-(c*c))/((2*(a)*(b))));

        NSLog(@"FULL ANGLE IS: %f, ANGLE IS: %.2f",angle, angle);

Sometimes, it gives the angle as 1 which doesn't make sense to me. Can anyone explain why this is, or how to fix it please?

Josue Espinosa
  • 5,009
  • 16
  • 47
  • 81

5 Answers5

14

Not sure if this is the main problem but it is a problem

Your answer gives the angle at the wrong point:

enter image description here

To get the angle in green (which is probably angle you want based on your variable names "first", "second" and "third), use:

CGFloat angle = acos(((a*a)+(c*c)-(b*b))/((2*(a)*(c))));

Tyler
  • 17,669
  • 10
  • 51
  • 89
  • 1
    Thank you! Very useful graphic! I don't know what the world would do without people who know how to do math. – Josue Espinosa Nov 26 '13 at 20:27
  • I added this to avoid zero division: `CGFloat angle = 0.0; CGFloat numerator = ((a*a)+(c*c)-(b*b)) ; if (numerator != 0 ) { angle = acos( numerator / ((2*(a)*(c))) ); }` – Pedro Trujillo Sep 30 '19 at 16:39
5

Here's a way that circumvents the law of cosines and instead calculates the angles of the two vectors. The difference between the angles is the searched value:

CGVector vec1 = { first.x - second.x, first.y - second.y };
CGVector vec2 = { third.x - second.x, third.y - second.y };

CGFloat theta1 = atan2f(vec1.dy, vec1.dx);
CGFloat theta2 = atan2f(vec2.dy, vec2.dx);

CGFloat angle = theta1 - theta2;
NSLog(@"angle: %.1f°, ", angle / M_PI * 180);

Note the atan2 function that takes the x and y components as separate arguments and thus avoids the 0/90/180/270° ambiguity.

Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
  • It is probably a matter of taste, but I always use CGPoint (which has x and y "components"). – Martin R Nov 26 '13 at 20:22
  • @MartinR It's a longstanding Cocoa convention to use CGSize for distances. It will probably take me a while to find a reference, though. I'm sure I've seen it in Apple code. – Nikolai Ruhe Nov 26 '13 at 20:25
  • Thanks Nikolai, this answer is very helpful! This works, but it returns a negative number every time and gets larger than 180 (After 180 it needs to become smaller) so I will stick with the answer I chose but with a little tweaking this works too! – Josue Espinosa Nov 26 '13 at 20:28
  • It's more correct to use `CGVector` I believe. And congrats! I was very slow in writing my answer, so you've beat me :) – ilya n. Nov 26 '13 at 20:33
  • @Josue, you could normalize it with `if (angle < 0) angle += 360`. – ilya n. Nov 26 '13 at 20:35
  • @ilyan. There's no `CGVector` in Foundation or any Apple framework I'm aware of. – Nikolai Ruhe Nov 26 '13 at 20:35
  • I wasn't aware of CGVector until now, but it *is* defined in CGGeometry.h. – Martin R Nov 26 '13 at 20:36
  • 2
    @NikolaiRuhe; indeed, it's only from iOS 7: https://developer.apple.com/library/ios/documentation/graphicsimaging/reference/CGGeometry/Reference/reference.html – ilya n. Nov 26 '13 at 20:37
  • Oh my, you know you're getting old when you notice that you stopped reading the API diffs three major versions before :) Thanks, ilya! `CGVector` really seems suitable. – Nikolai Ruhe Nov 26 '13 at 20:41
1

The cosine formula implementation looks right; did you take into account that acos() returns the angle in radians, not in degrees? In order to convert into degrees, multiply the angle by 180 and divide by Pi (3.14159...).

Seva Alekseyev
  • 59,826
  • 25
  • 160
  • 281
1

The way I have done it is to calculate the two angles separately using atan2(y,x) then using this function.

static inline double
AngleDiff(const double Angle1, const double Angle2)
{
    double diff = 0;
    diff = fabs(Angle1 - Angle2);
    if (diff > <Pi>) {
        diff = (<2Pi>) - diff;
    }
    return diff;
}

The function deals in radians, but you can change <Pi> to 180 and <2Pi> to 360

David Skrundz
  • 13,067
  • 6
  • 42
  • 66
0

Using this answer to compute angle of the vector:

CGFloat angleForVector(CGFloat dx, CGFloat dy) {
    return atan2(dx, -dy) * 180.0/M_PI;
}

// Compute angle at point Corner, that is between AC and BC:

CGFloat angle = angleForVector(A.x - Corner.x, A.y - Corner.y)
              - angleForVector(B.x - Corner.x, B.y - Corner.y);

NSLog(@"FULL ANGLE IS: %f, ANGLE IS: %.2f",angle, angle);
Community
  • 1
  • 1
ilya n.
  • 18,398
  • 15
  • 71
  • 89