views:

388

answers:

3

I'm writing iPhone code that fuzzily recognizes whether a swiped line is straight-ish. I get the bearing of the two end points and compare it to 0, 90, 180 and 270 degrees with a tolerance of 10 degrees plus or minus. Right now I do it with a bunch of if blocks, which seems super clunky.

How to write a function that, given the bearing 0..360, the tolerance percentage (say 20% = (-10° to +10°)) and a straight angle like 90 degrees, returns whether the bearing is within the tolerance?

Update: I am, perhaps, being too specific. I think a nice, general function that determines whether a number is within a percentage of another number has utility in many areas.

For instance: Is the number swipeLength within 10% of maxSwipe? That would be useful.

 BOOL isNumberWithinPercentOfNumber(float firstN, float percent, float secondN) {
      // dunno how to calculate
 }

 BOOL result;

 float swipeLength1 = 303; 
 float swipeLength2 = 310; 

 float tolerance = 10.0; // from -5% to 5%
 float maxSwipe = 320.0;

 result = isNumberWithinPercentOfNumber(swipeLength1, tolerance, maxSwipe); 
 // result = NO

 result = isNumberWithinPercentOfNumber(swipeLength2, tolerance, maxSwipe);
 // result = YES

Do you see what I'm getting at?

+4  A: 
int AngularDistance (int angle, int targetAngle) 
{

    int diff = 0;
    diff = abs(targetAngle - angle)

    if (diff > 180) diff = 360 - diff;

    return diff;
}

This should work for any two angles.

Andreas F
what question are you answering?
willc2
This is for the original question about comparing a bearing to a target bearing (0°, 90°, 180°, 270°). The function gives you the angle between both, which you could compare to your tolerance, lets say 10° As Jim Lewis pointed out, percentages make little sense for angles, and they are generally a little trickier (due to their circularity: e.g. 350° + 20° = 10°).
Andreas F
A: 

Answer to your refined/new question:

bool isNumberWithinPercentOfNumber (float n1, float percentage, float n2)
{

if (n2 == 0.0) //check for div by zero, may not be necessary for float
   return false; //default for a target value of zero is false
else
   return (percentage > abs(abs(n2 - n1)/n2)*100.0);
}

To explain, you take the absolute difference between your test and target value, and divide it by the target value (the two 'abs'olute calls make sure this works with negative target and test numbers too, but not with negative percentages/tolerances). This gives you the percentage of the difference expressed as decimal fraction, multiplies it with 100 to give the 'common' expression of percentage (10% = 0.10),

Andreas F
A: 

20% as a decimal is equal to 0.2. Just divide by 100.0 to get the decimal. Divide by 2.0 to get half of the acceptable range. (Combined into 200.0 divisor)

From there, add and subtract from 1.0 to get the 90%, and 110% values. If the first number is between the ranges, then there you have it.

BOOL isNumberWithinPercentOfNumber(float firstN, float percent, float secondN) {
      float decimalPercent = percent / 200.0;
      float highRange = secondN * (1.0 + decimalPercent);
      float lowRange = secondN * (1.0 - decimalPercent);
      return lowRange <= firstN && firstN <= highRange;
 }

Note: there's no error checking here for NaN or negative values. You'll want to add that for production code.

Update: to make percent to include both +/- range.

Matt Brunell
I didn't make clear that if tolerance is 20%, that means the range is from -10% to +10%. My apologies.
willc2
Okay, just divide by 200.0 instead of 100.0 then.
Matt Brunell