numerics – Signed vector angle over an array


I am calculating signed vector angles (limited to {-Pi,Pi}), but the twist is that these calculations are being done in the context of a ‘landscape’ array. I have a point on a 2D landscape and a direction expressed as a vector. Imagine an animal standing one the landscape and looking along that vector. What I need to calculate is the signed vector angle between that ‘looking angle’ and every location on the array, so the end result is an array of signed angles. And of course I need to do it as fast as possible. It is simple enough to generate an {x,y} coordinate for each element of the array and subtract the location of the animal to turn these into vectors. Functionally then, the task is to map a ‘signed angle’ function over that array of vectors. But to make it fast, we want to be able to use Listable functions and vectorize calcuations as much as possible. And maybe compile it as well! Here is my current code:

signedVectorAnglesC = 
 Compile({{vec1Array, _Real, 3}, {vec2, _Real, 1}}, 
  Module({a2, a1Array, aArray, piArray, piNegArray, piPosArray},
   a2 = ArcTan(vec2((1)), vec2((2)));
   a1Array = 
    ArcTan(vec1Array((All, All, 1)), vec1Array((All, All, 2)));
   aArray = a1Array - a2;
   piArray = IntegerPart(aArray/Pi);
   piNegArray = N(-Clip(piArray, {-1, -1}, {0, 0}));
   piPosArray = N(Clip(piArray, {1, 1}, {0, 0}));
   aArray + piNegArray*2*Pi - piPosArray*2*Pi
   ))

Here is an example for input

landscapeDimensions = {800, 500};
arrayCoordinates = 
  Map(Reverse, 
   Reverse(Table({i, j}, {i, 1, landscapeDimensions((2))}, {j, 1, 
       landscapeDimensions((1))}) - 0.5), {2});
animalLoc = {327.2, 100.7};
animalVec = {1.1, 0.4};
arrayVectors = Map(# - animalLoc &, arrayCoordinates, {2});

It works, and is reasonably fast

In(1306):= Timing(
 arrayAngles = signedVectorAnglesC(arrayVectors, animalVec);)

Out(1306)= {0.060664, Null}

But I want it to be as fast as possible, because I need to do it repeatedly in the context of a simulation. I found the challenge to be the correction factor to bring the ArcTan differences into the range {-Pi,Pi}: the last four lines of the function do that in a way that is hacky but quite fast in the array context. But perhaps I have missed something simpler? Anyone up for a speed challenge?