Shortest distance between two degree marks on a circle?
I had been looking for a microcontroller solution like this for gearbox seeking for an animatronic puppet and I didn't have the grunt to calculate trig properly.
@ruakh's answer was a good basis but I founded that the sign was incorrectly flipped in certain conditions.
Here's the solution that worked for me. This solution works for degree marks in a circle but changing MAX_VALUE
allows this to work for an arbitrary max range that's useful when measuring gear encoder pulses.
Tested on Arduino.
#define MAX_VALUE 360
float shortestSignedDistanceBetweenCircularValues(float origin, float target){
float signedDiff = 0.0;
float raw_diff = origin > target ? origin - target : target - origin;
float mod_diff = fmod(raw_diff, MAX_VALUE); //equates rollover values. E.g 0 == 360 degrees in circle
if(mod_diff > (MAX_VALUE/2) ){
//There is a shorter path in opposite direction
signedDiff = (MAX_VALUE - mod_diff);
if(target>origin) signedDiff = signedDiff * -1;
} else {
signedDiff = mod_diff;
if(origin>target) signedDiff = signedDiff * -1;
}
return signedDiff;
}
You could also use vector math and trigonometry; angles would be in radians here.
float angle(float angle1, float angle2)
{
float x1=cos(angle1);
float y1=sin(angle1);
float x2=cos(angle2);
float y2=sin(angle2);
float dot_product = x1*x2 + y1*y2;
return acos(dot_product);
}
Step 1: Get the "raw" difference. For example, given
-528.2
and740.0
, this is1268.2
.- one way:
raw_diff = first > second ? first - second : second - first
- another way:
raw_diff = std::fabs(first - second)
- one way:
Step 2: Subtract a multiple of
360.0
to get a value between0.0
(inclusive) and360.0
(exclusive).mod_diff = std::fmod(raw_diff, 360.0)
Step 3: If this value is greater than
180.0
, subtract it from360.0
.- one way:
dist = mod_diff > 180.0 ? 360.0 - mod_diff : mod_diff
- another way:
dist = 180.0 - std::fabs(mod_diff - 180.0)
- one way:
It's probably most readable as a series of statements:
double raw_diff = first > second ? first - second : second - first;
double mod_diff = std::fmod(raw_diff, 360.0);
double dist = mod_diff > 180.0 ? 360.0 - mod_diff : mod_diff;
But if desired, it's not hard to put it all into a single expression:
180.0 - std::fabs(std::fmod(std::fabs(first - second), 360.0) - 180.0)