3

我有一个用 Objective C 编写的 iPhone 应用程序,我在其中收集用户在屏幕上绘制的触摸点以创建路径。

我希望能够精简这些数据。我打算通过检查一个点的角度是否超过某个阈值来实现此目的的方法之一。例如,如果我取直线上的任意三个相邻点,称为 a、b、c,如果角度 ABC 在 180 的 5 度范围内,那么我可以删除点 b 而不会对直线造成太大影响。

我试图通过将三角形 ABC 分成 2 个直角三角形来实现这一点。然后我使用 acosf() 来找到 a (BAC) 处的角度和 c (BCA) 处的角度。然后我可以从 180 中减去这两个角度来找到 ABC 的角度。

我的问题是 acosf() 经常返回 NaN。当它返回一个数字时,我已经在计算器上检查了它并且该值是正确的。我已经尝试过返回 NaN 的值,但它们也不适用于计算器。我知道这是因为它们超出了 acosf() 的范围。我怎样才能避免这种情况?

这是我正在使用的代码:

float prevToNextDist = ccpDistance(prevPoint.location, nextPoint.location);
    //work out the next point angle
    float pointToNextDistance = ccpDistance(point.location, nextPoint.location);
    float nextPointAngle = acosf((prevToNextDist/2)/pointToNextDistance);
    nextPointAngle = CC_RADIANS_TO_DEGREES(nextPointAngle);

    //work out the previous point angle
    float prevToPointDistance = ccpDistance(prevPoint.location, point.location);
    float prevPointAngle = acosf((prevToNextDist/2)/prevToPointDistance);
    prevPointAngle = CC_RADIANS_TO_DEGREES(prevPointAngle);

    //work out the point angle
    float pointAngle = 180 - nextPointAngle - prevPointAngle;

有任何想法吗?

4

3 回答 3

7

acos(x)一个经典的问题acosf(x)是确保论点在范围内。

有时,由于各种 FP 问题,计算结果x刚好超出 -1 <= x <= 1 的范围。数学上可能在范围内,但舍入/精度有时会出现类似. 有了这个值(到8位小数),那么是 NaN?x1.0000001xacosf(x)

另一种方法是测试是否x在安排之外并将其带入。

if (x > 1.0) {
  x = 1.0;
}
else if (x < -1.0) {
  x = -1.0;
}
angle = acos(x);

进一步:acosf()仅提供180度[0到pi]的范围。对于您的任务,您可能希望使用atan2f(). 它提供了一个完整的答案“圈子”。

于 2013-10-17T21:15:06.970 回答
1

1/ float 不是默认的浮点类型,它是双精度的。float 可能足以存储,即使那样您也应该使用 double 进行计算。

2/没有cppDistance的代码,很难确定,但我认为它不会像你想象的那样计算三角形的边(我认为没有3个点作为参数是不可能的) .

3/ 如果您的点是 (xa, ya), (0, 0) 和 (xb, yb) 并且您正在寻找 (0, 0) 处的角度(如果不是这种情况,只需更改坐标就是这样),你正在寻找的角度是

acos((xa*xb+ya*yb)/sqrt((xa*xa+ya*ya)*(xb*xb+yb*yb)))
于 2013-10-17T18:45:13.140 回答
0

好的,我已经解决了这个问题,所以我将我的解决方案发布给任何可能偶然发现这个步骤并发现它有用的人。虽然,我使用的是我自己的解决方案,但 chux 和 AProgrammers 的建议都是合理的,并且帮助我想出了解决方案,所以我对他们都投了赞成票。

正如 chux 所建议的,atan2() 是比 acosf() 更好的函数。这是基于角度对点数据进行细化的代码:

#define kAngleTolerance 3

    double prevPointAngle = atan2(point.location.y - prevPoint.location.y, point.location.x - prevPoint.location.x) * 180.0f / M_PI;
    prevPointAngle = fabs(prevPointAngle);

    double nextPointAngle = atan2(nextPoint.location.y - point.location.y, nextPoint.location.x - point.location.x) * 180.0f / M_PI;
    nextPointAngle = fabs(nextPointAngle);

    double pointAngle = 180 - fabs(nextPointAngle - prevPointAngle);

    if (pointAngle < 180 + kAngleTolerance && pointAngle > 180 - kAngleTolerance ) {
        point.remove = YES;
        [pointsToRemove addObject:point];
    }
于 2013-10-19T13:48:18.227 回答