检测分两步进行,首先是一个收敛测试,圆的起点和终点一定要非常接近,近到几乎相连。不过这也需要有适当的余地,因为在不提供直接的视觉反馈的情况下,
用户可能画不圆或画过头。这里适用的像素距离是60个像素,大约是视图大小的1/3。
第二个测试检查中心点周围的移动,它合计移动的弧度,在这个正圆中应该等于360度,容许45度范围内的移动。
通过这两个测试之后,算法生成一个无框矩形,并根据原手势上的各点的几何平均值确定矩形中心。该结果被赋给圆形实例变量。
它不是一个完美的检测系统,不过这足以为许多iPhone应用程序提供相当好的圆形检查
代码:
#define POINT(X) [[self.points objectAtIndex:X] CGPointValue]
//检测圆形
- (void) touchesEnded:(NSSet *) touches withEvent:(UIEvent *) event
{
if (!self.points) return;
if (self.points.count < 3) return;
// Test 1: The start and end points must be between 60 pixels of each other
CGRect tcircle;
if (distance(POINT(0), POINT(self.points.count - 1)) < 60.0f)
tcircle = [self centeredRectangle];
// Test 2: Count the distance traveled in degrees. Must fall within 45 degrees of 2 PI
CGPoint center = CGPointMake(CGRectGetMidX(tcircle), CGRectGetMidY(tcircle));
float distance = ABS(acos(dotproduct(centerPoint(POINT(0), center), centerPoint(POINT(1), center))));
for (int i = 1; i < (self.points.count - 1); i++)
distance += ABS(acos(dotproduct(centerPoint(POINT(i), center), centerPoint(POINT(i+1), center))));
if ((ABS(distance - 2 * M_PI) < (M_PI / 4.0f))) circle = tcircle;
[self setNeedsDisplay];
}
// Return distance between two points
float distance (CGPoint p1, CGPoint p2)
{
float dx = p2.x - p1.x;
float dy = p2.y - p1.y;
return sqrt(dx*dx + dy*dy);
}
// Calculate and return least bounding rectangle
- (CGRect) centeredRectangle
{
float x = 0.0f;
float y = 0.0f;
for (NSValue *pt in self.points)
{
x += [pt CGPointValue].x;
y += [pt CGPointValue].y;
}
// Calculate weighted center
x /= self.points.count;
y /= self.points.count;
float minx = 9999.0f;
float maxx = -9999.0f;
float miny = 9999.0f;
float maxy = -9999.0f;
for (NSValue *pt in self.points)
{
minx = MIN(minx, [pt CGPointValue].x);
miny = MIN(miny, [pt CGPointValue].y);
maxx = MAX(maxx, [pt CGPointValue].x);
maxy = MAX(maxy, [pt CGPointValue].y);
}
return CGRectMake(minx, miny, (maxx - minx), (maxy - miny));
}
CGPoint centerPoint(CGPoint pt, CGPoint origin)
{
return CGPointMake(pt.x - origin.x, pt.y - origin.y);
}
float dotproduct (CGPoint v1, CGPoint v2)
{
float dot = (v1.x * v2.x) + (v1.y * v2.y);
float a = ABS(sqrt(v1.x * v1.x + v1.y * v1.y));
float b = ABS(sqrt(v2.x * v2.x + v2.y * v2.y));
dot /= (a * b);
return dot;
}