0

I tried to calculate the angle ABC between points A, B and C. I know the math are pretty basic but I don't understand why my function give the wrong result. First, here is the code (a contains a list [x, y, z])

    def angle(a, b, c):

        # Create vectors from points
        ba = [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
        bc = [c[0] - b[0], c[1] - b[1], c[2] - b[2]]

        # Normalize vector
        nba = sqrt(ba[0]**2 + ba[1]**2 + ba[2]**2)
        ba = [ba[0]/nba, ba[1]/nba, ba[2]/nba]

        nbc = sqrt(bc[0]**2 + bc[1]**2 + bc[2]**2)
        bc = [bc[0]/nbc, bc[1]/nbc, bc[2]/nbc]

        # Calculate scalar from normalized vectors
        scal = ba[0]*bc[0] + ba[1]*bc[1] + ba[2]*bc[2]

        # calculate the angle in radian
        angle = acos(scal)

This function gives wrong result. In fact, it gives the good result if I change the second vector from bc to cb:

    cb = [b[0]-c[0], b[1]-c[1], b[2]-c[2]]

I don't understand why, as if I follow math, my first solution should work well and give the good result...

Will Vousden
  • 32,488
  • 9
  • 84
  • 95
user3703297
  • 75
  • 2
  • 6
  • 1
    I don't see any problem with this code – what's the input and the expected output? The change you described that fixes it gives `pi-theta` (if `theta` is the angle you want). – Will Vousden Sep 13 '15 at 16:21
  • Ok, I think the problem is that i'm looking for the angle between vectors ba and bc but that's not how it work. I have to get the angle between ab and bc to get ABC. – user3703297 Sep 13 '15 at 16:22

1 Answers1

3

Firstly, your code is very non-pythonic. Here is a suggestion:

from math import sqrt, acos
def angle(a, b, c):
    
    # Create vectors from points
    ba = [ aa-bb for aa,bb in zip(a,b) ]
    bc = [ cc-bb for cc,bb in zip(c,b) ]
    
    # Normalize vector
    nba = sqrt ( sum ( (x**2.0 for x in ba) ) )
    ba = [ x/nba for x in ba ]
    
    nbc = sqrt ( sum ( (x**2.0 for x in bc) ) )
    bc = [ x/nbc for x in bc ]
    
    # Calculate scalar from normalized vectors
    scalar = sum ( (aa*bb for aa,bb in zip(ba,bc)) )
    
    # calculate the angle in radian
    angle = acos(scalar)
    return angle

Secondly, your code is probably returning the correct angle, but maybe not the angle you expected.

Assuming this scenario:

A-----C
|    /
|   /
|  /
| /
|/
B

The angle you're calculating is the bottom angle at B, not the top-left angle at A which is usually what people want when they pass three vectors (a,b,c) into a function that returns angles.

iAdjunct
  • 2,739
  • 1
  • 18
  • 27
  • I think you're missing some `sqrt` calls. – Mark Dickinson Sep 13 '15 at 16:35
  • you forgot to `sqrt` the `sum` of squares to normalize. Anyway, I’d go further and define a normalize function to avoid typing the logic twice and call it using `scal = sum(aa*bb for aa,bb in zip(normalize(b,a), normalize(b,c)))` – 301_Moved_Permanently Sep 13 '15 at 16:40
  • Oops. Yup, forgot that. And I was trying not to deviate too far from his original code, so I didn't separate them in functions. In all reality, he should just use numpy. – iAdjunct Sep 15 '15 at 13:51