Нахождение ближайшего объекта к CGPoint - PullRequest
9 голосов
/ 24 августа 2010

У меня есть четыре UIViews на UIScrollView (экран разделен на квартили)

В квартилях у меня есть несколько объектов (UIImageViews) в каждом квартиле.

Когда пользователь касается экрана, я хочу найти ближайший объект к данной CGPoint?

Есть идеи?

У меня есть CGPoint и рамка (CGRect) объектов в каждом квартиле.

UPDATE:

hRuler http://img.skitch.com/20100824-c333thq3rjcak9f5q5djjugjhw.preview.jpg
Красные значки - это UIImageViews.
    // UIScrollView
    NSLog(@" UIScrollView: %@", self);

    // Here's the tap on the Window in UIScrollView's coordinates
    NSLog(@"TapPoint: %3.2f, %3.2f", tapLocation.x, tapLocation.y);

    // Find Distance between tap and objects
    NSArray *arrayOfCGRrectObjects = [self subviews];
    NSEnumerator *enumerator = [arrayOfCGRrectObjects objectEnumerator];

    for (UIView *tilesOnScrollView in enumerator) {

        // each tile may have 0 or more images
        for ( UIView *subview in tilesOnScrollView.subviews ) {

            // Is this an UIImageView?
            if ( [NSStringFromClass([subview class]) isEqualToString:@"UIImageView"]) {

                // Yes, here are the UIImageView details (subView)
                NSLog(@"%@", subview);

                // Convert CGPoint of UIImageView to CGPoint of UIScrollView for comparison...
                // First, Convert CGPoint from UIScrollView to UIImageView's coordinate system for reference
                CGPoint found =  [subview convertPoint:tapLocation fromView:self];
                NSLog(@"Converted Point from ScrollView: %3.2f, %3.2f", found.x, found.y);

                // Second, Convert CGPoint from UIScrollView to Window's coordinate system for reference
                found =  [subview convertPoint:subview.frame.origin toView:nil];
                NSLog(@"Converted Point in Window: %3.2f, %3.2f", found.x, found.y);

                // Finally, use the object's CGPoint in UIScrollView's coordinates for comparison
                found =  [subview convertPoint:subview.frame.origin toView:self]; // self is UIScrollView (see above)
                NSLog(@"Converted Point: %3.2f, %3.2f", found.x, found.y);

                // Determine tap CGPoint in UIImageView's coordinate system
                CGPoint localPoint = [touch locationInView:subview];
                NSLog(@"LocateInView: %3.2f, %3.2f",localPoint.x, localPoint.y );

               //Kalle's code
                    CGRect newRect = CGRectMake(found.x, found.y, 32, 39);
                    NSLog(@"Kalle's Distance: %3.2f",[self distanceBetweenRect:newRect andPoint:tapLocation]);

            }

Консоль отладки

Вот проблема. Каждая плитка размером 256х256. CGPoint первого UIImageView преобразован в Система координат UIScrollView (53.25, 399.36) должна быть отключена с помощью tapPoint (30,331). Почему разница ?? Другая точка справа от повернутой точки вычисляется ближе (по расстоянию) ??

<CALayer: 0x706a690>>
[207] TapPoint: 30.00, 331.00
[207] <UIImageView: 0x7073db0; frame = (26.624 71.68; 32 39); opaque = NO; userInteractionEnabled = NO; tag = 55; layer = <CALayer: 0x70747d0>>
[207] Converted Point from ScrollView: 3.38, 3.32
[207] Converted Point in Window: 53.25, 463.36
[207] Converted Point: 53.25, 399.36 *** Looks way off!
[207] LocateInView: 3.38, 3.32
[207] Kalle's Distance: 72.20 **** THIS IS THE TAPPED POINT
[207] <UIImageView: 0x7074fb0; frame = (41.984 43.008; 32 39); opaque = NO; userInteractionEnabled = NO; tag = 55; layer = <CALayer: 0x7074fe0>>
[207] Converted Point from ScrollView: -11.98, 31.99
[207] Converted Point in Window: 83.97, 406.02
[207] Converted Point: 83.97, 342.02
[207] LocateInView: -11.98, 31.99
207] Kalle's Distance: 55.08 ***** BUT THIS ONE's CLOSER??????

Ответы [ 4 ]

20 голосов
/ 24 августа 2010

Следующий метод должен сработать.Если вы обнаружите в нем что-то странное, не стесняйтесь указывать на это.

- (CGFloat)distanceBetweenRect:(CGRect)rect andPoint:(CGPoint)point
{
    // first of all, we check if point is inside rect. If it is, distance is zero
    if (CGRectContainsPoint(rect, point)) return 0.f;

    // next we see which point in rect is closest to point
    CGPoint closest = rect.origin;
    if (rect.origin.x + rect.size.width < point.x)
        closest.x += rect.size.width; // point is far right of us
    else if (point.x > rect.origin.x) 
        closest.x = point.x; // point above or below us
    if (rect.origin.y + rect.size.height < point.y) 
        closest.y += rect.size.height; // point is far below us
    else if (point.y > rect.origin.y)
        closest.y = point.y; // point is straight left or right

    // we've got a closest point; now pythagorean theorem
    // distance^2 = [closest.x,y - closest.x,point.y]^2 + [closest.x,point.y - point.x,y]^2
    // i.e. [closest.y-point.y]^2 + [closest.x-point.x]^2
    CGFloat a = powf(closest.y-point.y, 2.f);
    CGFloat b = powf(closest.x-point.x, 2.f);
    return sqrtf(a + b);
}

Пример вывода:

CGPoint p = CGPointMake(12,12);

CGRect a = CGRectMake(5,5,10,10);
CGRect b = CGRectMake(13,11,10,10);
CGRect c = CGRectMake(50,1,10,10);
NSLog(@"distance p->a: %f", [self distanceBetweenRect:a andPoint:p]);
// 2010-08-24 13:36:39.506 app[4388:207] distance p->a: 0.000000
NSLog(@"distance p->b: %f", [self distanceBetweenRect:b andPoint:p]);
// 2010-08-24 13:38:03.149 app[4388:207] distance p->b: 1.000000
NSLog(@"distance p->c: %f", [self distanceBetweenRect:c andPoint:p]);
// 2010-08-24 13:39:52.148 app[4388:207] distance p->c: 38.013157

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

Следующий метод определяет расстояние между двумя CGPoints.

- (CGFloat)distanceBetweenPoint:(CGPoint)a andPoint:(CGPoint)b
{
    CGFloat a2 = powf(a.x-b.x, 2.f);
    CGFloat b2 = powf(a.y-b.y, 2.f);
    return sqrtf(a2 + b2)
}

Обновление: удалено fabsf ();-x ^ 2 - это то же самое, что и x ^ 2, поэтому в нем нет необходимости.

Обновление 2: для полноты добавлен также метод distanceBetweenPoint:andPoint:.

9 голосов
/ 26 июля 2015

Если вы используете Swift, вот как вы можете рассчитать расстояние между CGPoint и CGRect (например, рамкой UIView)

private func distanceToRect(rect: CGRect, fromPoint point: CGPoint) -> CGFloat {
    // if it's on the left then (rect.minX - point.x) > 0 and (point.x - rect.maxX) < 0
    // if it's on the right then (rect.minX - point.x) < 0 and (point.x - rect.maxX) > 0
    // if it's inside the rect then both of them < 0. 
    let dx = max(rect.minX - point.x, point.x - rect.maxX, 0)
    // same as dx
    let dy = max(rect.minY - point.y, point.y - rect.maxY, 0)
    // if one of them == 0 then the distance is the other one.
    if dx * dy == 0 {
        return max(dx, dy)
    } else {
        // both are > 0 then the distance is the hypotenuse
        return hypot(dx, dy)
    }
}
2 голосов
/ 21 апреля 2016

Спасибо @ cristian,

Вот версия вашего ответа Objective-C

- (CGFloat)distanceToRect:(CGRect)rect fromPoint:(CGPoint)point
{
    CGFloat dx = MAX(0, MAX(CGRectGetMinX(rect) - point.x, point.x - CGRectGetMaxX(rect)));
    CGFloat dy = MAX(0, MAX(CGRectGetMinY(rect) - point.y, point.y - CGRectGetMaxY(rect)));

    if (dx * dy == 0)
    {
        return MAX(dx, dy);
    }
    else
    {
        return hypot(dx, dy);
    }
}
0 голосов
/ 21 июля 2016

Короче @ кристиан ответ:

func distance(from rect: CGRect, to point: CGPoint) -> CGFloat {
  let dx = max(rect.minX - point.x, point.x - rect.maxX, 0)
  let dy = max(rect.minY - point.y, point.y - rect.maxY, 0)
  return dx * dy == 0 ? max(dx, dy) : hypot(dx, dy)
}

Лично я бы реализовал это как расширение CGPoint:

extension CGPoint {
    func distance(from rect: CGRect) -> CGFloat {
        let dx = max(rect.minX - x, x - rect.maxX, 0)
        let dy = max(rect.minY - y, y - rect.maxY, 0)
        return dx * dy == 0 ? max(dx, dy) : hypot(dx, dy)
    }
}

Кроме того, вы также можете реализовать его как расширение CGRect:

extension CGRect {
    func distance(from point: CGPoint) -> CGFloat {
        let dx = max(minX - point.x, point.x - maxX, 0)
        let dy = max(minY - point.y, point.y - maxY, 0)
        return dx * dy == 0 ? max(dx, dy) : hypot(dx, dy)
    }
}
...