如何确定某个形状上鼠标的最近点?

时间:2011-11-22 06:24:47

标签: objective-c c xcode macos cocoa

我创建了一个程序,根据黑/白位图将鼠标限制在某个区域。该程序按原样100%运行,但是当它偏离区域外时,使用不准确但尽管快速的算法来重新定位鼠标。

目前,当鼠标移动到该区域之外时,基本上会发生以下情况:

  1. 在区域内预定义的静态点和鼠标的新位置之间绘制一条线。
  2. 找到该线与允许区域边缘相交的点。
  3. 鼠标移动到那一点。
  4. 这是有效的,但只有完美才能在正确的圆心中设置预定义的点。不幸的是,情况永远不会如此。该应用将与各种矩形和不规则的无定形形状一起使用。在这样的形状上,绘制的线与边缘相交的点通常不是鼠标形状上的最近点。

    我需要创建一个新算法,找到最接近指向鼠标在允许区域边缘的新位置。我怎样才能做到这一点?优选地,该方法应该能够足够快地执行以在将鼠标拖动到该区域的边缘时给予平滑的鼠标移动。

    (我在OS X 10.7上的Objective C / Cocoa中这样做,但是,如果您不想输入代码或者不了解目标C / C,伪代码就可以了。) / p>

    谢谢!

    这是我目前的算法:

    #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 个答案:

答案 0 :(得分:3)

如果用户正在移动鼠标指针并且需要限制在某个区域,那么我认为最好的解决办法是找到该区域内最近的点。相反,对用户来说似乎更直观的是将鼠标移回到有效区域中退出它。只要您能够以足够快的速度监视鼠标位置,这就更容易实现。

现在,你可能有理由以你想要的方式去做,我尊重这一点。在这种情况下,我可以提出以下建议:

  1. 如果您可以更改有效鼠标区域从位图到多边形(标记区域角落的2D点列表)的定义方式,则任务变得更加简单。只需找到最接近鼠标位置的段。作为该计算的一部分,您可以获得该段中最近的点,这是您要重新定位鼠标指针的位置。

  2. 蛮力解决方案也应该有效。从当前鼠标位置开始向外工作。首先检查它周围的八个像素。然后是16周围的8,依此类推。找到有效区域内的点后,记录其到当前鼠标位置的距离。继续前进,仍然寻找更接近的像素,或直到当前外层的所有像素的距离都大于您记录的最小距离。

  3. 我希望这会有所帮助。

答案 1 :(得分:1)

一些想法:

  • 形式问题的一种非常标准的方法:“给定一组2D点S(在您的情况下,边缘点集合)和查询点P(在您的情况下,鼠标位置),找到P中最接近S的点,就是使用四叉树。它们可以看作二维搜索到2D的概括。 Quadtrees在视频游戏中很容易用于碰撞检测,所以你可以在谷歌找到很多教程。

  • 形状是变化还是静止?在第二种情况下,如果内存不是问题,我只需预先计算每个像素的最近边缘点并将其放入查找表中。 (实际上,我只使用两个数组,一个用于x坐标,另一个用于y坐标)。通过使用Floyd-Warshall算法中的某些东西可以消除冗余计算,在这种情况下,这种算法的形式非常简单。

答案 2 :(得分:0)

我认为这个问题不是很简单(至少如果你想要效率和精确度)。据传Qt Graphics View在这方面做得很好。也许使用它,或浏览它的源代码(它是免费软件)应该有用吗?