Получить угол и квадрант из трех точек - PullRequest
3 голосов
/ 01 сентября 2011

Дано:

  • точка щелчка колеса центр
  • точка startPoint (первое касание)
  • точка (фактическое касание)

Я хочу определить угол между этими тремя точками и узнать, в каком квадранте было найдено последнее касание.
Расчет угла работает хорошо, в то время как квадрант всегда 1 или 2, а не 3 или 4.

Где я не прав?

CGFloat DistanceBetweenTwoPoints(CGPoint point1,CGPoint point2)
{
    CGFloat dx = point2.x - point1.x;
    CGFloat dy = point2.y - point1.y;
    return sqrt(dx*dx + dy*dy );
};

NSInteger GetQuadrant(double angle)
{
    double sinAngle = sin(angle);
    double cosAngle = cos(angle);
    double tanAngle = tan(angle);
    double cotAngle = 1.0/tanAngle;

    NSLog(@"%f %f %f %f", sinAngle, cosAngle, tanAngle, cosAngle);

    if(sinAngle > 0 && cosAngle > 0 && tanAngle > 0 && cotAngle > 0) return 1;
    if(sinAngle > 0 && cosAngle < 0 && tanAngle < 0 && cotAngle < 0) return 2;
    if(sinAngle < 0 && cosAngle < 0 && tanAngle > 0 && cotAngle > 0) return 3;
    if(sinAngle < 0 && cosAngle > 0 && tanAngle < 0 && cotAngle < 0) return 4;
    return 0;
}

double AngleBetweenThreePoints(CGPoint point1,CGPoint point2, CGPoint point3)
{
    CGPoint point_a = point1;
    CGPoint point_b = point2;
    CGPoint point_c = point3;
    CGFloat a, b, c;

    a = DistanceBetweenTwoPoints(point_b, point_c);
    b = DistanceBetweenTwoPoints(point_a, point_c);
    c = DistanceBetweenTwoPoints(point_a, point_b);
    double result = acos((b*b+c*c-a*a)/(2*b*c));

    NSLog(@"%d", GetQuadrant(result));

    return result/M_PI * 180.0;
}


-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    startPoint = [[touches anyObject] locationInView:self];
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    CGPoint point = [[touches anyObject] locationInView:self];
    double angle = AngleBetweenThreePoints(self.clickWheelCenter, startPoint, point);
    NSLog(@"%f", angle);    
}

Ответы [ 5 ]

4 голосов
/ 02 сентября 2011

Все это можно сделать намного проще.

Если {x1, y1} = первая точка - центральная точка, а {x2, y2} = третья точка - центральная точка, задается угол со знаком (-pi, + pi)по:

atan2(x2*y1 - x1*y2,x1*x2 + y1*y2)

В зависимости от вашего определения знака угла, вы можете или не хотите добавлять минус впереди.

Вы можете легко получить квадрант из знака и абсолютногоразмер.

  1. размер <= pi / 2, знак + </li>
  2. размер> pi / 2, знак +
  3. размер> pi / 2, знак -
  4. размер <= pi / 2, знак - </li>
3 голосов
/ 01 сентября 2011

Как уже упоминали другие, то, что вы делаете, получает внутренний угол, который всегда меньше 180 градусов.Я предполагаю, что вы хотите, чтобы квадрант измерялся углом против часовой стрелки от одного вектора к другому.

Простой способ получить этот квадрант - это создать точечный продукт и точечный продукт и проверить знаки каждого результата..

Это работает, потому что скалярное произведение равно | A | * | B | * cos (theta), где ||означает величину, а тета - угол между А и В. Поскольку величина всегда положительна, проверяя знак результата, вы фактически проверяете знак cos (тета).

На самом деле произведение точки Perp | A |* | B | * sin (theta), поэтому, проверяя знак точки perp, вы фактически проверяете знак sin (theta).

Допустим, мы измеряем тета как угол против часовой стрелки от A до BКвадрант также измеряется тэтой.Мы можем вычислить точку и perpdot следующим образом:

Сначала вам нужно будет получить векторы A и B. A и B будут векторами от clickWheelCenter до начальной точки и от clickWheelCenter до того, что вы назвали 'point'

точка = Ax * Bx + Ay * По

perpdot = Ax * By - Ay * Bx

, затем проверьте знаки, отметив таблицу ниже.

<b>quadrant  dot         perpdot</b>
1         positive    positive
2         negative    positive
3         positive    negative
4         negative    negative

Опять же, проверяя знак скалярного произведения, вы фактически проверяете знак cos (тэта) и проверяя perpdot, вы проверяете знак греха (тэта).Все, что вам нужно, это набор операторов if для сравнения, если они> 0 или <0. </p>

(примечание: я быстро написал выше и не все хорошо проверил на точность, но общая идея верна)

BBitmaster

1 голос
/ 21 февраля 2014

Вы рассчитываете внутренний угол, который всегда меньше 180 градусов.Вот быстрый и правильный способ вычисления правильного значения угла:

double AngleBetweenThreePoints(CGPoint pointA, CGPoint pointB, CGPoint pointC)
{
    CGFloat a = pointB.x - pointA.x;
    CGFloat b = pointB.y - pointA.y;
    CGFloat c = pointB.x - pointC.x;
    CGFloat d = pointB.y - pointC.y;

    CGFloat atanA = atan2(a, b);
    CGFloat atanB = atan2(c, d);

    return atanB - atanA;
} 
1 голос
/ 01 сентября 2011

Похоже, ваша функция AngleBetweenThreePoints только когда-либо возвращает угол между 0 и Пи, который затем передается.

Ваша проблема в том, что acos - это не функция "один к одному".Для любого заданного входа есть несколько значений, поэтому без каких-либо дополнительных знаний он просто выберет одно из заданного диапазона.Простым примером является то, что 270 градусов и 90 градусов оба имеют cos, равные 0. Так что, если вы acos (0), то он может явно вернуть только один из двух.

Мне немного неясно, что вы подразумеваете подв каком квадранте это все же.Обычно вы можете просто определить это, взглянув на координаты x и y, и использовать их, чтобы выяснить, каков ваш квадрант.Глядя на угол между двумя строками, вы получите странное значение для квадранта (квадранта относительно первой строки) - это то, что вы хотите?

1 голос
/ 01 сентября 2011

Потому что закон косинуса вернет внутренний угол 180 градусов или меньше. Я не совсем уверен, что вы пытаетесь сделать здесь, но этот подход не имеет смысла с точки зрения квадрантов. Кроме того, вам нужно проверить ошибки, если это код производственного уровня. Закон косинусов взорвется, когда ваш треугольник выродится.

...