Найти CGPoint в прямоугольнике UIView, пересекаемом прямой линией под заданным углом от центральной точки - PullRequest
10 голосов
/ 02 марта 2012

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

Скажите, что я знаю центральную точку, размер прямоугольника и угол (начиная с 0 градусов для Востока и заканчивая против часовой стрелки через 90 для Севера и 180 для Западного и от 270 для Южного до 360 градусов для Восточного снова). Мне нужно знать координаты точки пересечения.

Несколько запутанный (для меня) математический, но, по-видимому, точный ответ на Поиск точек на прямоугольнике под заданным углом побудил меня попробовать следующий код, но он не работает должным образом. Этот вопрос похож на этот, но я ищу исправленный метод Objective-C / iOS, а не общий математический ответ.

Я думаю, что часть проблемы кода связана с использованием единственного входа от 0 до 360 градусов (в радианах без возможности отрицательного числа), но могут быть и другие проблемы. В приведенном ниже коде в основном используются обозначения, определенные в ответе от belisarius , включая мою попытку вычислить точки пересечения для каждой из четырех указанных здесь областей.

Этот код находится в моем подклассе UIImageView:

- (CGPoint) startingPointGivenAngleInDegrees:(double)angle {
    double angleInRads = angle/180.0*M_PI;
    float height = self.frame.size.height;
    float width = self.frame.size.width;
    float x0 = self.center.x;
    float y0 = self.center.y;
    // region 1 
    if (angleInRads >= -atan2(height, width) && angleInRads <= atan2(height, width)) {
        return CGPointMake(x0 + width/2, y0 + width/2 * tan(angleInRads));
    }
    // region 2
    if (angleInRads >= atan2(height, width) && angleInRads <= M_PI - atan2(height, width)) {
        return CGPointMake(x0 + height / (2*tan(angleInRads)),y0+height/2);
    }
    // region 3
    if (angleInRads >= M_PI - atan2(height, width) && angleInRads <= M_PI + atan2(height, width)) {
        return CGPointMake(x0 - width/2, y0 + width/2 * tan(angleInRads));
    }
    // region 4
    return CGPointMake(x0 + height / (2*tan(angleInRads)),y0-height/2);    
}

Ответы [ 2 ]

21 голосов
/ 02 марта 2012

Вместо того, чтобы отлаживать ваш код, я просто объясню, как мне это сделать.

Сначала несколько терминов.Давайте определим xRadius как половину ширины рамки, а yRadius как половину высоты рамки.

Теперь рассмотрим четыре края рамки и вытяните их как бесконечные линии.Поверх этих четырех линий положите линию, которая проходит через центр рамки под указанным вами углом:

diagram of rectangle with overlaid line

Скажем, рамка находится по центру в начале координат - в центрекадра в координатах (0,0).Мы можем легко вычислить, где диагональная линия пересекает правый край рамки: координаты (xRadius, xRadius * tan(angle)).И мы можем легко вычислить, где диагональная линия пересекает верхний край фрейма: координаты (-yRadius / tan(angle), -yRadius).

(Почему мы отрицаем координаты для пересечения верхнего края?Потому что система координат UIView перевернута из обычной математической системы координат. В математике координаты y увеличиваются к верхней части страницы. В UIView координаты y увеличиваются к нижней части вида.)

Таким образом, мы можем просто вычислить пересечение линии с правым краем кадра.Если это пересечение находится за пределами рамки, то мы знаем, что линия должна пересекать верхний край , прежде чем пересекает правый край.Как мы узнаем, что пересечение правого края выходит за границы?Если его координата y (xRadius * tan(angle)) больше, чем yRadius (или меньше, чем -yRadius), это выходит за пределы.

Итак, чтобы сложить все вместе в методе, мы начнем с вычисления xRadius и yRadius:

- (CGPoint)radialIntersectionWithConstrainedRadians:(CGFloat)radians {
    // This method requires 0 <= radians < 2 * π.

    CGRect frame = self.frame;
    CGFloat xRadius = frame.size.width / 2;
    CGFloat yRadius = frame.size.height / 2;

Затем мы вычисляем координату y пересечения с правым краем:

    CGPoint pointRelativeToCenter;
    CGFloat tangent = tanf(radians);
    CGFloat y = xRadius * tangent;

Проверяем, находится ли пересечение в кадре:

    if (fabsf(y) <= yRadius) {

Как только мы узнаем, что это в кадре, мы должны выяснить, хотим ли мы пересечение с правым или левым краем.Если угол меньше π / 2 (90 °) или больше 3π / 2 (270 °), нам нужен правый край.В противном случае нам нужен левый край.

        if (radians < (CGFloat)M_PI_2 || radians > (CGFloat)(M_PI + M_PI_2)) {
            pointRelativeToCenter = CGPointMake(xRadius, y);
        } else {
            pointRelativeToCenter = CGPointMake(-xRadius, -y);
        }

Если координата y пересечения правого края • была * вне пределов, мы вычисляем координату x пересечения с нижним краем.

    } else {
        CGFloat x = yRadius / tangent;

Затем мы выясним, хотим ли мы верхний край или нижний край.Если угол меньше π (180 °), нам нужен нижний край.В противном случае нам нужен верхний край.

        if (radians < (CGFloat)M_PI) {
            pointRelativeToCenter = CGPointMake(x, yRadius);
        } else {
            pointRelativeToCenter = CGPointMake(-x, -yRadius);
        }
    }

Наконец, мы смещаем вычисленную точку на фактический центр кадра и возвращаем ее.

    return CGPointMake(pointRelativeToCenter.x + CGRectGetMidX(frame),
        pointRelativeToCenter.y + CGRectGetMidY(frame));
}

Тестовый проект здесь: https://github.com/mayoff/stackoverflow-radial-intersection

выглядит так:

edgepoint screen shot

4 голосов
/ 23 июня 2018

вот ответ Роба в виде быстрой копии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...