package org.myrobotlab.leap; import java.util.ArrayList; import org.myrobotlab.kinematics.Point; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.service.LeapMotion; import org.myrobotlab.service.data.LeapData; import org.myrobotlab.service.data.LeapHand; import org.slf4j.Logger; import com.leapmotion.leap.Controller; import com.leapmotion.leap.Finger; import com.leapmotion.leap.Gesture; import com.leapmotion.leap.Hand; import com.leapmotion.leap.Listener; import com.leapmotion.leap.Vector; public class LeapMotionListener extends Listener { public final static Logger log = LoggerFactory.getLogger(LeapMotionListener.class); LeapMotion myService = null; int numFrames = 0; public LeapMotionListener(LeapMotion myService) { this.myService = myService; } private double computeAngleDegrees(Finger f, Vector palmNormal) { Vector fDir = f.direction(); // TODO: validate that this is what we actually want. // otherwise we can directly compute the angleTo in java. double angleInRadians = palmNormal.angleTo(fDir); // convert to degrees so it's easy to pass to servos double angle = Math.toDegrees(angleInRadians); return angle; } private LeapHand mapLeapHandData(Hand lh) { LeapHand mrlHand = new LeapHand(); // process the normal Vector palmNormal = lh.palmNormal(); mrlHand.palmNormalX = palmNormal.getX(); mrlHand.palmNormalY = palmNormal.getY(); mrlHand.palmNormalZ = palmNormal.getZ(); mrlHand.posX = lh.arm().center().getX(); mrlHand.posY = lh.arm().center().getY(); mrlHand.posZ = lh.arm().center().getZ(); // handle the fingers. for (Finger.Type t : Finger.Type.values()) { Finger f = lh.fingers().get(t.ordinal()); int angle = (int) computeAngleDegrees(f, palmNormal); if (t.equals(Finger.Type.TYPE_INDEX)) mrlHand.index = angle; else if (t.equals(Finger.Type.TYPE_MIDDLE)) mrlHand.middle = angle; else if (t.equals(Finger.Type.TYPE_RING)) mrlHand.ring = angle; else if (t.equals(Finger.Type.TYPE_PINKY)) mrlHand.pinky = angle; else if (t.equals(Finger.Type.TYPE_THUMB)) mrlHand.thumb = angle; else log.warn("Unknown finger! eek.."); } return mrlHand; } @Override public void onConnect(Controller controller) { log.info("Connected"); controller.enableGesture(Gesture.Type.TYPE_SWIPE); controller.enableGesture(Gesture.Type.TYPE_CIRCLE); controller.enableGesture(Gesture.Type.TYPE_SCREEN_TAP); controller.enableGesture(Gesture.Type.TYPE_KEY_TAP); log.info("Connected"); myService.invoke("publishConnect", controller); } @Override public void onDisconnect(Controller controller) { log.info("onDisconnect"); myService.invoke("publishDisconnect", controller); } @Override public void onExit(Controller controller) { log.info("onExit"); myService.invoke("publishExit", controller); } @Override public void onFrame(Controller controller) { LeapData data = new LeapData(); // The old publishFrame method for those who want it. data.frame = controller.frame(); myService.invoke("publishFrame", data.frame); // grab left/right hands Hand lh = controller.frame().hands().leftmost(); Hand rh = controller.frame().hands().rightmost(); // map the data to the MRL Hand pojo LeapHand mrlLHand = null; LeapHand mrlRHand = null; if (lh.isLeft()) { mrlLHand = mapLeapHandData(lh); } if (lh.isRight()) { mrlRHand = mapLeapHandData(rh); } // set them to the LeapData obj data.leftHand = mrlLHand; data.rightHand = mrlRHand; // Grab the current frame // Track the last valid data frame. // TODO: test and make sure this is worky? if (data.frame.isValid()) { numFrames++; myService.lastLeapData = data; // only publish valid frames ? myService.invoke("publishLeapData", data); ArrayList<Point> points = new ArrayList<Point>(); for (Hand h : data.frame.hands()) { // position infomration double x = h.arm().center().getX(); double y = h.arm().center().getY(); double z = h.arm().center().getZ(); // orientation information double roll = h.palmNormal().roll(); double pitch = h.palmNormal().pitch(); double yaw = h.palmNormal().yaw(); // create the point to publish Point palmPoint = new Point(x, y, z, roll, pitch, yaw); ; // add it to the list of points we publish points.add(palmPoint); } // publish the points. if (points.size() > 0) { // TODO: gotta down sample for ik3d to keep up. if (numFrames % 20 == 0) { myService.invoke("publishPoints", points); } } } } @Override public void onInit(Controller controller) { log.info("publishInit"); myService.invoke("onInit", controller); } }