Skip to content

Instantly share code, notes, and snippets.

@jeriley
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeriley/f6d3def08ad544b2ef5f to your computer and use it in GitHub Desktop.
Save jeriley/f6d3def08ad544b2ef5f to your computer and use it in GitHub Desktop.
example plugins for the leapmotion, also note you'll need underscorejs
Leap.plugin('book', function(options) {
options || (options = {});
options.frameDuration || (options.frameDuration = 20);
var toDegrees = 180/Math.PI;
var sendEvent = _.debounce(function(eventName, controller){
console.log(eventName);
$.jGrowl(eventName);
controller.emit(eventName);
}, 250);
return {
frame: function(frame) {
var history = this.frame(options.frameDuration);
if (frame.valid == false || history.valid == false)
return;
if (frame.hands.length != 2){
return;
}
if (history.hands.length != 2){
return;
}
var oneHandPrevious = history.hands[0].roll() * toDegrees;
var otherHandPrevious = history.hands[1].roll() * toDegrees;
var oneHandNow = frame.hands[0].roll() * toDegrees;
var otherHandNow = frame.hands[1].roll() * toDegrees;
oneHandMoved = Math.round(oneHandPrevious - oneHandNow);
otherHandMoved = Math.round(otherHandPrevious - otherHandNow);
var handDistanceNow = Math.round(Math.abs(frame.hands[0].palmPosition[0] - frame.hands[1].palmPosition[0]));
var handDistanceBefore = Math.round(Math.abs(history.hands[0].palmPosition[0] - history.hands[1].palmPosition[0]));
var handDistanceDifference = handDistanceNow - handDistanceBefore;
/*console.log({
oneHandMoved: oneHandMoved,
otherHandMoved: otherHandMoved,
handDistanceNow: handDistanceNow,
handDistanceBefore: handDistanceBefore,
handDistanceDifference: handDistanceDifference
});*/
//still need to determine which hand and which direction
if (Math.abs(oneHandMoved) > 60 && Math.abs(otherHandMoved) > 60){ //we got enough rotation
if (handDistanceNow > 75) {//hands were opened
sendEvent('bookOpen', this);
return;
} else if (handDistanceNow < 75) { //hands were closed
sendEvent('bookClosed', this);
return;
}
} else if (Math.abs(handDistanceDifference) > 200){ // show them dem details!
if (handDistanceDifference > 0) {
sendEvent('expand', this);
return;
} else if (handDistanceDifference < 0) {
sendEvent('collapse', this);
return;
}
}
}
}
});
Leap.plugin('dial', function(options) {
options || (options = {});
options.rotationNeeded || (options.rotationNeeded = 30);
options.frameDuration || (options.frameDuration = 20);
var toDegrees = 180/Math.PI;
var largestJump = 0;
var jumpOver = _.throttle(function(controller, direction){
controller.emit(direction, largestJump);
largestJump = 0; //reset;
}, 100);
return {
frame: function(frame){
//what if this could be a method on the frame itself?
//ie if frame.IsADial(), do stuff via method set below?
var history = this.frame(options.frameDuration);
if (frame.hands.length != 1 || history.hands.length != 1){
return;
}
if (frame.hands[0].fingers.length < 3 || history.hands[0].length < 3){
return;
}
var hand = frame.hands[0];
var handBefore = history.hands[0];
var handNow = Math.round(hand.roll() * toDegrees);
var handRotationBefore = Math.round(handBefore.roll() * toDegrees);
/*var diag = {
handNow: handNow,
handBefore: handRotationBefore,
degree: handNow - handRotationBefore
};*/
var degree = handNow - handRotationBefore;
if (Math.abs(degree) <= 30){ //if it doesn't move 30' we don't care so reset
frameCount = 1;
handRotationBefore = hand.roll() * toDegrees;
return;
}
var numberToJump = Math.round((Math.abs(degree) / 30));
if (largestJump < numberToJump)
largestJump = numberToJump;
var direction = '';
if (degree > 0){
console.log('dial to the left');
direction = 'dialLeft';
} else if (degree < 0) {
console.log('dial to the right');
direction = 'dialRight';
}
jumpOver(this, direction, largestJump);
}
}
});
Leap.plugin('flipDown', function(options) {
options || (options = {});
options.rotationNeeded || (options.rotationNeeded = 40);
var toDegrees = 180/Math.PI;
return {
hand: function(hand) {
var rotation = Math.abs(hand.roll() * toDegrees);
if (hand.frame.hands.length > 1)
return;
if (rotation < options.rotationNeeded){
this.emit('flipDown', hand);
}
}
};
});
Leap.plugin('flipUp', function(options) {
options || (options = {});
options.rotationNeeded || (options.rotationNeeded = 120);
var toDegrees = 180/Math.PI;
return {
hand: function(hand) {
var rotation = Math.abs(hand.roll() * toDegrees);
if (hand.frame.hands.length > 1)
return;
if (rotation > options.rotationNeeded){
this.emit('flipUp', hand);
}
}
};
});
Leap.plugin('handClosed', function(options) {
options || (options = {});
options.fingersNeeded || (options.fingersNeeded = 2);
var toDegrees = 180/Math.PI;
return {
hand: function(hand) {
if (hand.frame.hands.length > 1)
return;
var palmDirection = Math.abs(hand.roll() * toDegrees);
var palmIsUpOrDown = palmDirection < 20 || palmDirection > 140;
if (hand.fingers.length <= options.fingersNeeded && palmIsUpOrDown){
this.emit('handClosed', hand);
}
}
};
});
Leap.plugin('handOpen', function(options) {
options || (options = {});
options.fingersNeeded || (options.fingersNeeded = 5);
options.startOffOpened || (options.startOffOpened = true);
var toDegrees = 180/Math.PI;
return {
hand: function(hand) {
if (hand.frame.hands.length > 1)
return;
var palmDirection = Math.abs(hand.roll() * toDegrees);
var palmIsUpOrDown = palmDirection < 20 || palmDirection > 140;
if (hand.fingers.length >= options.fingersNeeded && palmIsUpOrDown){
this.emit('handOpen', hand);
}
}
};
});
Leap.plugin('handsLeave', function(options) {
options || (options = {});
options.timeToWait || (options.timeToWait = 10);
var leaving = _.debounce(function(controller){
controller.emit('handsLeave');
}, 250);
return {
frame: function(frame) {
var history = this.frame(options.timeToWait);
if (history.hands.length > 0 && frame.hands.length == 0){
leaving(this);
}
}
};
});
<script src="scripts/underscore.js"></script>
<script src="//js.leapmotion.com/leap-0.5.0.min.js"></script>
<script src="scripts/leap/handOpen.js"></script>
<script src="scripts/leap/handClosed.js"></script>
<script src="scripts/leap/handsLeave.js"></script>
<script src="scripts/leap/flipUp.js"></script>
<script src="scripts/leap/flipDown.js"></script>
<script src="scripts/leap/dial.js"></script>
<script src="scripts/leap/book.js"></script>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jquery-jgrowl/1.2.12/jquery.jgrowl.min.css" />
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-jgrowl/1.2.12/jquery.jgrowl.min.js"></script>
<script type="text/javascript">
$(function() {
var toDegrees = 180/Math.PI;
var liveAction = {
handIsOpen: false,
palmIsUp: false,
isBusy: false,
};
var controller = new Leap.Controller({enableGestures: true});
controller.use('handOpen', {})
.use('handClosed', {})
.use('handsLeave', {})
.use('flipUp', {})
.use('flipDown', {})
.use('dial', {})
.use('book', {});
var slowItDown = _.throttle(function(callback, eventName){
console.log('live action ? ' + liveAction.isBusy);
if (liveAction.isBusy)
return;
console.log('slowin it down');
liveAction.isBusy = true; //pause gestures
$.jGrowl(eventName)
callback();
liveAction.isBusy = false;
}, 300);
controller.on('handOpen', function(hand){
if (liveAction.handIsOpen)
return;
slowItDown(function(){
liveAction.handIsOpen = true;
}, 'Hand Open');
});
controller.on('handClosed', function(hand){
if (!liveAction.handIsOpen)
return;
slowItDown(function(){
liveAction.handIsOpen = false;
$('div.inline-asset-detail .cancel').click();
}, 'Hand Closed');
});
controller.on('handsLeave', function(hand){
slowItDown(function(){
var applyButton = $('div button[name=apply]').first();
if (applyButton.is(':enabled'))
applyButton.click();
}, 'Hands Leave');
});
controller.on('flipUp', function(hand){
if (liveAction.palmIsUp)
return;
slowItDown(function(){
liveAction.palmIsUp = true;
showDetails();
}, 'Flip Up');
});
controller.on('flipDown', function(hand){
if (!liveAction.palmIsUp)
return;
slowItDown(function(){
liveAction.palmIsUp = false;
$('div.inline-asset-detail .cancel').click();
}, 'Flip Down');
});
controller.on('dialLeft', function(number){
var numberToJump = number;
slowItDown(function(){
var above = $('.bucket.active-scope').prevAll();
var zeroIndex = numberToJump - 1;
if (above.length < numberToJump)
above.last().find('.header').click();
else
above.eq(zeroIndex).find('.header').click();
}, 'Dial Left');
});
controller.on('dialRight', function(number){
var numberToJump = number;
slowItDown(function(){
var below = $('.bucket.active-scope').nextAll();
var zeroIndex = numberToJump - 1;
if (below.length < numberToJump)
below.last().find('.header').click();
else
below.eq(zeroIndex).find('.header').click();
}, 'Dial Right');
});
controller.on('bookOpen', function(){
slowItDown(function(){
console.log('WHAT?');
openItem();
}, 'Book Open');
});
controller.on('bookClosed', function(){
slowItDown(function(){
closeItem();
}, 'Book Closed');
});
controller.on('expand', function(){
slowItDown(function(){
expandSummary();
}, 'Expand');
});
controller.on('collapse', function(){
slowItDown(function(){
collapseSummary();
}, 'Collapse');
});
var circle = controller.gesture('circle');
circle.stop(function(end){
if(liveAction.isBusy)
return;
if (end.lastFrame.hands.length > 1)
return;
console.log('circle');
liveAction.isBusy = true;
var last = end.lastGesture;
var clockwise = false;
if (last.normal[2] <= 0)
clockwise = true;
var wise = clockwise ? "Clockwise" : "Counter-Clockwise";
var pointValue = Math.round(last.radius * 6 / 60);
if (clockwise) increaseEstimate(pointValue);
else decreaseEstimate(pointValue);
$.jGrowl("circle!!!! -- " + last.radius + "in a " + wise);
liveAction.isBusy = false;
});
var screenTap = controller.gesture('screenTap');
screenTap.complete(function(done){
showDetails();
$.jGrowl('show details');
})
var swipe = controller.gesture('swipe');
swipe.stop(function(end){
if (liveAction.isBusy)
return;
if (end.lastFrame.hands.length > 1)
return;
console.log('swipe');
var last = end.lastGesture;
var toOrAwayFromMe = Math.abs(last.direction[2] > 0.5);
if (toOrAwayFromMe)
return;
var likelyUpDown = Math.abs(last.direction[1]) > 0.15;
var likelyLeftRight = Math.abs(last.direction[0]) > 0.15;
if (likelyUpDown && last.direction[1] < 0){
selectNextRow();
$.jGrowl("swipin DOWN");
}
else if (likelyUpDown && last.direction[1] > 0){
selectPreviousRow();
$.jGrowl("swipe UP");
}
else if (likelyLeftRight && last.direction[0] > 0){
transferToScope();
$.jGrowl("kicked out");
}
else if (likelyLeftRight && last.direction[0] < 0)
$.jGrowl("swipe left");
liveAction.isBusy = false;
});
controller.on('connect', function() {
$.jGrowl("Leap Motion connected!", {position: "bottom-right"});
});
/*controller.on('gesture', function(gesture){
return; //inert, new way to do this.
case 'screenTap':
showDetails();
break;
case 'keyTap':
$('div.inline-asset-detail .cancel').click();
$.jGrowl("close that");
break;
liveAction.isBusy = false;
});*/
controller.connect();
function showDetails(){
var table = $('table.gridtable');
var selected = table.find('.selected');
if (selected.length == 1)
table.find('.selected a.open-by-name').click()
if (selected.length == 0)
table.find('tr.gg').first().addClass('selected');
table.find('.selected a.open-by-name').click()
}
function expandSummary(){
$('div.active-scope').find('.expander').click();
}
function collapseSummary(){
$('div.active-scope').find('.expander.open').click()
}
function transferToScope(){
var row = $('table.gridtable .selected');
if (row.length == 0)
return;
if (row.find('a.disabledname').length == 1)
return; //cant move a closed item
var activeScope = $('div.current-scope').attr('_v1_asset');
var oid = row.attr('_v1_asset');
var scopeOid = activeScope;
$.post($("link[rel=move-to-current-scope-link]").attr('href'), { epic: oid, scope: scopeOid }, function(success) {
$.get($("link[rel=summary-link]").attr('href'), { scope: scopeOid }, function(html) {
$('div.bucket[_v1_asset="' + scopeOid + '"] .scope-summary').replaceWith(html);
});
V1.Topics.Publish("GadgetSync", {obj : ""});
});
}
function increaseEstimate (pointValue) {
var input = $('table.gridtable .selected .estimate');
input.val(function(i, val){
return +val + pointValue;
});
input.trigger('change');
}
function decreaseEstimate (pointValue) {
var input = $('table.gridtable .selected .estimate');
input.val(function(i, val){
if (+val <= pointValue)
return "";
console.log('decrease by ' + Math.round((+val - pointValue) * 10) / 10)
return +val - pointValue;
});
input.trigger('change');
}
function closeItem(){
if ($('table.gridtable .selected a.disabledname.asset-name-link').length == 1){
$.jGrowl('selected row is already closed');
return;
}
var selectedRow = $('table.gridtable .selected');
if (selectedRow.length == 0)
selectNextRow();
selectedRow = $('table.gridtable .selected');
assetType = selectedRow.attr('_v1_type');
oid = selectedRow.attr('_v1_asset');
data = { ExecuteOperation: { "Name": assetType + ".QuickClose", "Oid": oid} };
$.post('ui.v1?norender=1&showErrors=0', JSON.stringify(data), function(success){
V1.Topics.Publish("GadgetSync", {obj : ""});
$.jGrowl("closed workitem");
$("table.gridtable[_v1_asset='" + oid + '"]').addClass('selected');
});
}
function openItem(){
var closedRow = $('table.gridtable .selected');
if (closedRow.find('a.disabledname.asset-name-link').length == 0){
$.jGrowl("selected row is open");
return;
}
assetType = closedRow.attr('_v1_type');
oid = closedRow.attr('_v1_asset');
data = { ExecuteOperation: { "Name": assetType + ".Reactivate", "Oid": oid} };
$.post('ui.v1?norender=1&showErrors=0', JSON.stringify(data), function(success){
V1.Topics.Publish("GadgetSync", {obj : ""});
$.jGrowl("reopened workitem");
$("table.gridtable[_v1_asset='" + oid + '"]').addClass('selected');
});
}
function selectNextRow(){
var table = $('table.gridtable');
var selected = table.find('.selected');
if (selected.length === 1){
var next = selected.next();
if (next.length === 0) //last one
return;
selected.removeClass('selected');
next.addClass('selected');
} else if (selected.length === 0)
table.find('tr.gg').first().addClass('selected');
}
function selectPreviousRow(){
var table = $('table.gridtable');
var selected = table.find('.selected');
if (selected.length === 1) {
var next = selected.prev();
if (next.length === 0)
return;
selected.removeClass('selected');
next.addClass('selected');
} else if (selected.length === 0)
table.find('tr.gg').last().addClass('selected');
}
)};
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment