Как я могу определить ближайшую точку мыши на определенной фигуре? - PullRequest
4 голосов
/ 22 ноября 2011

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

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

  1. Линия проводится между предварительно определенной статической точкой внутри области и новой позицией мыши.
  2. Найдена точка, где эта линия пересекает край разрешенной области.
  3. Мышь перемещается в эту точку.

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

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

(я делаю это в Objective C / Cocoa на OS X 10.7, однако, псевдокод подойдет, если вы не хотите набирать код или не знаете Objective C / C)

Спасибо!

Вот мой текущий алгоритм:

#import <Cocoa/Cocoa.h>
#import "stuff.h"
#import <CoreMedia/CoreMedia.h>




bool
is_in_area(NSInteger x, NSInteger y, NSBitmapImageRep *mouse_mask){

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSUInteger pixel[4];
    [mouse_mask getPixel:pixel atX:x y:y];

    if(pixel[0]!= 0){
        [pool release];
        return false;
    }
    [pool release];
    return true;
}

CGEventRef 
mouse_filter(CGEventTapProxy proxy, CGEventType type, CGEventRef event, NSBitmapImageRep *mouse_mask) {


    CGPoint point = CGEventGetLocation(event);


    float tX = point.x;
    float tY = point.y;

    if( is_in_area(tX,tY, mouse_mask)){

        // target is inside O.K. area, do nothing
    }else{

    CGPoint target; 

    //point inside restricted region:
    float iX = 600; // inside x
    float iY = 500; // inside y


    // delta to midpoint between iX,iY and tX,tY
    float dX;
    float dY;

    float accuracy = .5; //accuracy to loop until reached

    do {
        dX = (tX-iX)/2;
        dY = (tY-iY)/2;

        if(is_in_area((tX-dX),(tY-dY),mouse_mask)){

            iX += dX;
            iY += dY;
        } else {

            tX -= dX;
            tY -= dY;
        }

    } while (abs(dX)>accuracy || abs(dY)>accuracy);

        target = CGPointMake(roundf(tX), roundf(tY));
        CGDisplayMoveCursorToPoint(CGMainDisplayID(),target);

    }


    return event;
}




int
main(int argc, char *argv[]) {


    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    stuff *stuff_doer = [[stuff alloc] init];

    NSBitmapImageRep *mouse_mask= [stuff_doer get_mouse_mask]; 


    CFRunLoopSourceRef runLoopSource;
    CGEventMask event_mask;
    event_mask = CGEventMaskBit(kCGEventMouseMoved) | CGEventMaskBit(kCGEventLeftMouseDragged) | CGEventMaskBit(kCGEventRightMouseDragged) | CGEventMaskBit(kCGEventOtherMouseDragged);

        CGSetLocalEventsSuppressionInterval(0);

    CFMachPortRef eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, 0, event_mask, mouse_filter, mouse_mask);

    if (!eventTap) {
        NSLog(@"Couldn't create event tap!");
        exit(1);
    }

    runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);

    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);

    CGEventTapEnable(eventTap, true);

    CFRunLoopRun();

    CFRelease(eventTap);
    CFRelease(runLoopSource);
    [pool release];

    exit(0);
}

example region that might be used (black is the allowed area) Это пример растрового изображения области, которое можно использовать, черный цвет - это разрешенная область. Это показывает, почему преобразование в многоугольник не было бы удобным или даже правдоподобным.

Ответы [ 3 ]

3 голосов
/ 22 ноября 2011

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

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

  1. , если бы вы могли изменить способ определения действительной области мыши с растрового изображения на многоугольник (список 2D точек, отмечающихуглы местности) тогда задача становится намного проще.Просто найдите сегмент, который находится ближе всего к положению мыши.В рамках этого вычисления вы можете получить ближайшую точку в пределах этого сегмента, и именно здесь вы хотите переместить указатель мыши.

  2. также должно работать решение методом грубой силы.Начните с текущей позиции мыши и двигайтесь наружу.Сначала проверьте восемь пикселей вокруг него.Затем 16 вокруг 8 и так далее.Как только вы найдете точку, которая находится в допустимой области, запишите ее расстояние до текущей позиции мыши.Продолжайте, продолжая искать пиксели, которые находятся ближе, или пока все пиксели на текущем внешнем уровне не будут иметь расстояние, превышающее ваше наименьшее записанное расстояние.

Надеюсь, это поможет.

1 голос
/ 22 ноября 2011

Некоторые идеи:

  • Довольно стандартный подход к задачам вида: «дан набор двумерных точек S (в вашем случае набор ребер) иТочка запроса P (в вашем случае, положение мыши), найти ближайшую точку к S в P ", это использовать квадродерево.Их можно рассматривать как обобщение бинарного поиска в 2D.Quadtree популярны для обнаружения столкновений в видеоиграх, поэтому вы можете найти множество учебников в Google.

  • Меняется ли форма или она статическая?Во втором случае, если память не является проблемой, я просто предварительно вычисляю ближайшую границу для каждого пикселя и помещаю ее в таблицу поиска.(На практике я бы просто использовал два массива, один для координаты x, а другой для координаты y).Избыточные вычисления могут быть устранены путем использования чего-либо в соответствии с алгоритмом Флойда-Варшалла, который в этом случае имеет довольно простую форму.

0 голосов
/ 22 ноября 2011
...