# Converting xy coordinates to theta without discontinutities

This one is, or rather, should be, fairly simple. I have a list of tuples of XY positions, and am trying to pull out angles relative to the origin from it. However, unlike atan2 / friends, I want it to be continuous, in other words not wrapping. I know (and am assuming) that the differences between successive XY coordinates are small, and that there is a minimum radius. (In other words, it should pick the multiple of $2\pi$ that makes the difference between the previous angle and the current angle the smallest.)

Currently, this is the relevant snippet:

thetas = [math.atan2(coords, coords)]
prev = thetas
for index in range(1, len(coords)):
coord = coords[index]
theta = math.atan2(coord, coord) + (prev // (2*math.pi)) * (2 * math.pi)
while theta - prev > math.pi:
theta -= 2*math.pi
while prev - theta > math.pi:
theta += 2*math.pi
assert abs(theta - prev) < math.pi/2
thetas.append(theta)
prev = theta


However, it's slow, and I doubt it's Pythonic. It's also doing a lot of "magic", for lack of a better word. Any suggestions for improvement?

Line profiler results here.

• a simple question is why does it need to be continuous? – ratchet freak Feb 16 '15 at 16:28
• How can you assert that abs(theta - prev) < math.pi/2? Is there some guarantee that the XY coordinates are clustered in some quadrant? – 200_success Feb 16 '15 at 18:21
• Quote: "I know (and am assuming) that the differences between successive XY coordinates are small." I should have specified that I know that there is a minimum radius as well. – TLW Feb 16 '15 at 19:11
• @ratchetfreak, For plotting purposes, mainly. It's far easier to read a graph of something versus theta when theta doesn't wrap. – TLW Feb 16 '15 at 19:13

• for index in range() is almost always wrong. Consider

prev = coords
for coord in coords[1:]:
theta = ...
prev = coord

• Instead of computing theta directly, compute delta - this way you may actually take an advantage of small differences:

prev = (1, 0)    # Initial vector is along the X axis
theta = 0.0
for coord in coords[]:
delta = angle_between(coord, prev)
theta += delta
prev = coord


Computing delta is simply an application of a cross-product formula:

def angle_between(curr, prev):
return asin(cross_product(curr, prev) / (norm(curr) / norm(prev))

• Three problems with this. First off, that angle-between function doesn't make sense as written. (I'm working in 2D, for one thing, and for another arcsine isn't defined for vectors.) Secondly, that will end up with compounding rounding errors. Third, that method of indexing (coords[1:]) will make a temporary copy of the entire array. That's not exactly optimal for performance. Is there no good way to avoid that copy? – TLW Feb 17 '15 at 3:03