如果你觉得文档很有用,你还可以去我的淘宝购买此文档最新的docx、pdf、html版本。
Leap Motion翻译系列文章http://brightguo.com/leap-motion-official-doc-translation
屏幕定位 Screen Location
一个用户可以使用Leap Motion设置对话框,来注册显示器屏幕的位置和朝向。当屏幕位置已知时,你可以使用Leap Motion API的屏幕类(Screen)来得到Leap Motion坐标系统下的屏幕物理位置和大小,并且可以找到手指或工具所指向屏幕的那一点。
使用屏幕探测器设施来注册屏幕位置,对于Leap Motion系统正常的运行的函数大多不需要。只有当你的应用使用Leap::Screen类时,才需要注册。
注意:屏幕探测器工具和屏幕定位API,目前在Linux下都不支持。
概述
屏幕类提供了一个在参照Leap Motion帧中显示屏的坐标和朝向。函数Screen::intersect()计算手指或工具指向方向射线与屏幕平面的交点的位置。此外,Screen::project()函数计算某点投影到屏幕平面的位置。
在使用Screen类之前,考虑到注册屏幕位置会需要你花一些精力,并且只有当你移动屏幕或者Leap才是有效的。你的应用对于API的使用必须是充分值得的,这样用户才会觉得他们的努力没有白费。我们建议你,让你的用户清楚了解为什么你需要他们去注册他们屏幕的位置,并且为用户提供一个快速的测试以验证屏幕位置是否依旧准确。
使用屏幕定位功能的说明可以参考Screen location.
屏幕坐标
对于每个已知屏幕,屏幕类提供了一个原点和向量以表示屏幕范围水平和垂直方向。一个正交的向量也会有提供。
上图:原点和已定位屏幕的方向向量
屏幕的原点坐落在下方,屏幕的左手角落。坐标向量与屏幕一边平行。向量的长度表示以毫米为单位的屏幕长度。正交向量是一个单位朝向向量与屏幕表面正交。
交点和投影点
屏幕类提供了一个intersect()方法来计算手指或者工具指向屏幕的交点。这个函数从尖端物体发射一个射线,然后判断射线与屏幕平面在哪里相交。只要尖端物体指向屏幕所处位置,就会返回一个有效的数值。如果交点是平行的或者指向方向不在屏幕上,在交叉向量中的坐标则都为NaN(not-a-number)。
屏幕类还提供了一个近似的intersect()函数,接受位置和朝向向量,并且计算它与屏幕交点,这样就不需要一个尖端物体了。
上图:三个指向以及与之对应的交叉点
@指针A与屏幕平面相交于屏幕外侧。
@指针B与屏幕直接相交。
@指针C没有与屏幕相交。[是平行的]
另外,屏幕类提供了一个project()函数来计算点在屏幕上的投影位置。与intersect()函数不同的是,project()函数永远返回一个有效值,因为点永远可以投影到平面上。
上图:2个点与相对于的投影点
@点A直接投影到屏幕上。
@点B投影到了屏幕外侧。
你可以依据Leap Motion坐标系统不断调用得到交点和投影点。返回的坐标是三维向量,以Leap Motion为原点毫米为单位衡量的点坐标。
依次的,你可以请求二维归一化的坐标,它们把交点和投影点定义成相对于屏幕低端左下角并且在屏幕平面内的坐标。
当使用归一化坐标时,原点是(0,0,0),相反的右上角坐标为(1,1,0)。这些点在屏幕平面以内,并且它们的x和y数值在0到1之间,表示点距离屏幕两边的比率(当使用归一化坐标时,z坐标的值一直是0)。例如,在屏幕中心的点被归一为(0.5,0.5,0)。对于交点和投影点,归一化的坐标在屏幕边界外时可能小于0也可能大于1。
默认时,intersect()和project()函数将坐标归一化到(clamp夹在这个范围里)坐标范围(0~1,0~1,0)。这意味着,任何在屏幕边框外的交点和投影点实际上被移动到边框上。但调用intersect()或者project()函数时,你可以通过设置clapRatio参数,来修改默认的“夹板区域”。默认的夹板率是1.0。使用一个更小的数可以减小夹板区域,而使用一个大的数值可以增大区域。例如将clampRatio设置为0.5会将交互区域缩小为以屏幕为中心50%区域,并且把说有点的坐标压缩在0.25到0.75范围里。同样的,如果把clampRatio设置为2.0会将交互区域扩大1倍,点的坐标扩展到-0.5到1.5之间[这二个功能很实用啊,这样可以限定交互区域范围,减少开发难度]。无论怎么设置,交互区域永远是以屏幕为中心的[一般都是盯着屏幕操作的(主要是能直接看到操作反馈结果),他们这样设计理所当然]。
找到最近的屏幕
你可以通过控制器实例对象的注册位置得到一张所有屏幕的列表:
1 2 |
Controller controller; ScreenList screens = controller.calibratedScreens(); |
注意,屏幕列表永远至少包含一项,即使用户从未注册过屏幕的位置。这项代表用户默认的屏幕。
你可以获取从手指或工具引出的射线会与屏幕列表对象相交的,最近的屏幕[如果有好多屏幕也只会得到最近的那个]:
1 2 3 4 5 6 7 |
Controller controller; Frame frame = controller.frame(); if (frame.pointables().count() > 0) { Pointable pointable = frame.pointables()[0]; ScreenList screens = controller.calibratedScreens(); Screen screen = screens.closestScreenHit(pointable); } |
同样地,你还可以在屏幕列表对象中,通过一个点(诸如指尖或者工具的末端位置)来得到与之最靠近的屏幕。[注意就最后一行不一样,函数和传入参数都不一样]
1 2 3 4 5 6 7 |
Controller controller; Frame frame = controller.frame(); if (frame.pointables().count() > 0) { Pointable pointable = frame.pointables()[0]; ScreenList screens = controller.calibratedScreens(); Screen screen = screens.closestScreen(pointable.tipPosition()); } |
找到交点的像素坐标
1 2 3 4 5 6 7 8 9 10 11 |
Controller controller; Frame frame = controller.frame(); if (frame.pointables().count() > 0) { Pointable pointable = frame.pointables()[0]; ScreenList screens = controller.calibratedScreens(); Screen screen = screens.closestScreenHit(pointable); Vector normalizedCoordinates = screen.intersect(pointable, true); int xPixel = (int)(normalizedCoordinates.x * screen.widthPixels()); int yPixel = screen.heightPixels() - (int)(normalizedCoordinates.y * screen.heightPixels()); } |
找到在Leap Motion坐标系下的交点坐标
你可以使用intersect()函数得到归一化的坐标,以帮你计算在屏幕上的某一个交点的像素点坐标[这个很必要!]。
1 2 3 4 5 6 7 8 9 10 11 |
Controller controller; Frame frame = controller.frame(); if (frame.pointables().count() > 0) { Pointable pointable = frame.pointables()[0]; ScreenList screens = controller.calibratedScreens(); Screen screen = screens.closestScreenHit(pointable); Vector normalizedCoordinates = screen.intersect(pointable, true); int xPixel = (int)(normalizedCoordinates.x * screen.widthPixels()); int yPixel = screen.heightPixels() - (int)(normalizedCoordinates.y * screen.heightPixels()); } |
设置交互区域的大小
当使用归一化坐标时,通过调用inersect()函数来设置calmpRatio参数,你可以将用户说指向的交互区域扩大或缩小。默认情况下,clampRatio参数为1.0,与屏幕边缘相匹配[自适应很好很强大]。返回任何在屏幕边界外交点的坐标时,它们都会被归一到边界上。(在交互区域内的点不会受到归一影响)
为了将交互区域二等分,在你调用intersect()函数时,可以将clampRatio参数设置为0.5。
1 |
Vector normalizedCoordinates = screen.intersect(pointable, true, 0.5); |
如果需要把区域扩展一倍,可以将clampRatio也乘以2。
1 |
Vector normalizedCoordinates = screen.intersect(pointable, true, 2.0); |
找到指向某处的手指或工具与屏幕的距离
为了取得手指或者工具的指向与屏幕交点的坐标,可以简单地将2个表示这些点的向量相减再取模就可以得到结果[中学几何知识]。当调用函数intersect()来获取交点,可以将归一化参数设置为false。
1 2 3 4 5 6 7 8 9 10 11 |
Controller controller; Frame frame = controller.frame(); if (frame.pointables().count() > 0) { Pointable pointable = frame.pointables()[0]; ScreenList screens = controller.calibratedScreens(); Screen screen = screens.closestScreenHit(pointable); Vector intersection = screen.intersect(pointable, false); Vector tipToScreen = intersection - pointable.tipPosition(); float pointingDistance = tipToScreen.magnitude(); } |
为了得到手指、工具与屏幕平面最近的距离,也就是从尖端末梢的点做垂线到屏幕的那个距离,可以使用distanceToPoint()函数传入尖端坐标。
1 2 3 4 5 6 7 8 9 |
Controller controller; Frame frame = controller.frame(); if (frame.pointables().count() > 0) { Pointable pointable = frame.pointables()[0]; ScreenList screens = controller.calibratedScreens(); Screen screen = screens.closestScreenHit(pointable); float perpendicularDistance = screen.distanceToPoint(pointable.tipPosition()); } |