From c153dd32090354347282687b9dde09c71d9297a3 Mon Sep 17 00:00:00 2001
From: johnshaughnessy <johnfshaughnessy@gmail.com>
Date: Thu, 1 Nov 2018 09:47:05 -0700
Subject: [PATCH] Change priority resolution

---
 src/components/pitch-yaw-rotator.js           |   4 +-
 .../userinput/bindings/daydream-user.js       |   5 +-
 .../userinput/bindings/keyboard-debugging.js  |   5 +-
 .../userinput/bindings/keyboard-mouse-user.js |   6 +-
 .../userinput/bindings/oculus-go-user.js      |   5 +-
 .../userinput/bindings/oculus-touch-user.js   |   5 +-
 .../userinput/bindings/touchscreen-user.js    |   5 +-
 src/systems/userinput/bindings/utils.js       |  11 ++
 src/systems/userinput/bindings/vive-user.js   |   5 +-
 .../bindings/xbox-controller-user.js          |   5 +-
 src/systems/userinput/userinput.js            | 187 +++++++++++++++---
 11 files changed, 197 insertions(+), 46 deletions(-)
 create mode 100644 src/systems/userinput/bindings/utils.js

diff --git a/src/components/pitch-yaw-rotator.js b/src/components/pitch-yaw-rotator.js
index a2c2ebdea..d3a400905 100644
--- a/src/components/pitch-yaw-rotator.js
+++ b/src/components/pitch-yaw-rotator.js
@@ -5,8 +5,8 @@ const radToDeg = THREE.Math.radToDeg;
 
 AFRAME.registerComponent("pitch-yaw-rotator", {
   schema: {
-    minPitch: { default: -50 },
-    maxPitch: { default: 50 }
+    minPitch: { default: -65 },
+    maxPitch: { default: 65 }
   },
 
   init() {
diff --git a/src/systems/userinput/bindings/daydream-user.js b/src/systems/userinput/bindings/daydream-user.js
index e2cb60984..61526d9ee 100644
--- a/src/systems/userinput/bindings/daydream-user.js
+++ b/src/systems/userinput/bindings/daydream-user.js
@@ -1,6 +1,7 @@
 import { paths } from "../paths";
 import { sets } from "../sets";
 import { xforms } from "./xforms";
+import { addSetsToBindings } from "./utils";
 
 // vars
 const v = s => `/vars/daydream/${s}`;
@@ -53,7 +54,7 @@ const dropOnCenterOrSouth = [
   }
 ];
 
-export const daydreamUserBindings = {
+export const daydreamUserBindings = addSetsToBindings({
   [sets.global]: [
     {
       src: {
@@ -262,4 +263,4 @@ export const daydreamUserBindings = {
     },
     ...dropOnCenterOrSouth
   ]
-};
+});
diff --git a/src/systems/userinput/bindings/keyboard-debugging.js b/src/systems/userinput/bindings/keyboard-debugging.js
index d1142ee33..0abcefb21 100644
--- a/src/systems/userinput/bindings/keyboard-debugging.js
+++ b/src/systems/userinput/bindings/keyboard-debugging.js
@@ -1,8 +1,9 @@
 import { paths } from "../paths";
 import { sets } from "../sets";
 import { xforms } from "./xforms";
+import { addSetsToBindings } from "./utils";
 
-export const keyboardDebuggingBindings = {
+export const keyboardDebuggingBindings = addSetsToBindings({
   [sets.global]: [
     {
       src: {
@@ -14,4 +15,4 @@ export const keyboardDebuggingBindings = {
       xform: xforms.rising
     }
   ]
-};
+});
diff --git a/src/systems/userinput/bindings/keyboard-mouse-user.js b/src/systems/userinput/bindings/keyboard-mouse-user.js
index 12e3b3e4f..b42e5a165 100644
--- a/src/systems/userinput/bindings/keyboard-mouse-user.js
+++ b/src/systems/userinput/bindings/keyboard-mouse-user.js
@@ -1,6 +1,7 @@
 import { paths } from "../paths";
 import { sets } from "../sets";
 import { xforms } from "./xforms";
+import { addSetsToBindings } from "./utils";
 
 const wasd_vec2 = "/var/mouse-and-keyboard/wasd_vec2";
 const keyboardCharacterAcceleration = "/var/mouse-and-keyboard/keyboardCharacterAcceleration";
@@ -28,7 +29,7 @@ const dropWithRMBorEscBindings = [
   }
 ];
 
-export const keyboardMouseUserBindings = {
+export const keyboardMouseUserBindings = addSetsToBindings({
   [sets.global]: [
     {
       src: {
@@ -258,6 +259,7 @@ export const keyboardMouseUserBindings = {
     {
       src: { value: paths.device.mouse.buttonLeft },
       xform: xforms.noop,
+      dest: { value: paths.noop },
       priority: 200,
       root: "lmb"
     },
@@ -310,4 +312,4 @@ export const keyboardMouseUserBindings = {
       xform: xforms.rising
     }
   ]
-};
+});
diff --git a/src/systems/userinput/bindings/oculus-go-user.js b/src/systems/userinput/bindings/oculus-go-user.js
index c0ec9199a..15c4604fa 100644
--- a/src/systems/userinput/bindings/oculus-go-user.js
+++ b/src/systems/userinput/bindings/oculus-go-user.js
@@ -1,6 +1,7 @@
 import { paths } from "../paths";
 import { sets } from "../sets";
 import { xforms } from "./xforms";
+import { addSetsToBindings } from "./utils";
 
 const touchpad = "/vars/oculusgo/touchpad";
 const touchpadPressed = "/vars/oculusgo/touchpadPressed";
@@ -25,7 +26,7 @@ const grabBinding = {
   priority: 200
 };
 
-export const oculusGoUserBindings = {
+export const oculusGoUserBindings = addSetsToBindings({
   [sets.global]: [
     {
       src: {
@@ -241,4 +242,4 @@ export const oculusGoUserBindings = {
       xform: xforms.copyIfTrue
     }
   ]
-};
+});
diff --git a/src/systems/userinput/bindings/oculus-touch-user.js b/src/systems/userinput/bindings/oculus-touch-user.js
index 1d557129c..4688dac41 100644
--- a/src/systems/userinput/bindings/oculus-touch-user.js
+++ b/src/systems/userinput/bindings/oculus-touch-user.js
@@ -1,6 +1,7 @@
 import { paths } from "../paths";
 import { sets } from "../sets";
 import { xforms } from "./xforms";
+import { addSetsToBindings } from "./utils";
 
 const name = "/touch/var/";
 
@@ -58,7 +59,7 @@ const rightTouchSnapLeft = `${name}/right/snap-left`;
 const keyboardSnapRight = `${name}/keyboard/snap-right`;
 const keyboardSnapLeft = `${name}/keyboard/snap-left`;
 
-export const oculusTouchUserBindings = {
+export const oculusTouchUserBindings = addSetsToBindings({
   [sets.global]: [
     {
       src: {
@@ -665,4 +666,4 @@ export const oculusTouchUserBindings = {
   ],
 
   [sets.rightHandHoveringOnNothing]: []
-};
+});
diff --git a/src/systems/userinput/bindings/touchscreen-user.js b/src/systems/userinput/bindings/touchscreen-user.js
index 41503259f..02c8d833c 100644
--- a/src/systems/userinput/bindings/touchscreen-user.js
+++ b/src/systems/userinput/bindings/touchscreen-user.js
@@ -1,6 +1,7 @@
 import { paths } from "../paths";
 import { sets } from "../sets";
 import { xforms } from "./xforms";
+import { addSetsToBindings } from "./utils";
 
 const zero = "/vars/touchscreen/zero";
 const forward = "/vars/touchscreen/pinchDeltaForward";
@@ -13,7 +14,7 @@ const gyroCamDelta = "vars/gyro/gyroCameraDelta";
 const gyroCamDeltaXScaled = "vars/gyro/gyroCameraDelta/x/scaled";
 const gyroCamDeltaYScaled = "vars/gyro/gyroCameraDelta/y/scaled";
 
-export const touchscreenUserBindings = {
+export const touchscreenUserBindings = addSetsToBindings({
   [sets.global]: [
     {
       src: { value: paths.device.touchscreen.pinch.delta },
@@ -129,4 +130,4 @@ export const touchscreenUserBindings = {
       priority: 200
     }
   ]
-};
+});
diff --git a/src/systems/userinput/bindings/utils.js b/src/systems/userinput/bindings/utils.js
new file mode 100644
index 000000000..67559c1a6
--- /dev/null
+++ b/src/systems/userinput/bindings/utils.js
@@ -0,0 +1,11 @@
+export const addSetsToBindings = mapping => {
+  for (const setName in mapping) {
+    for (const binding of mapping[setName]) {
+      if (!binding.sets) {
+        binding.sets = new Set();
+      }
+      binding.sets.add(setName);
+    }
+  }
+  return mapping;
+};
diff --git a/src/systems/userinput/bindings/vive-user.js b/src/systems/userinput/bindings/vive-user.js
index 399544384..8a47985e8 100644
--- a/src/systems/userinput/bindings/vive-user.js
+++ b/src/systems/userinput/bindings/vive-user.js
@@ -1,6 +1,7 @@
 import { paths } from "../paths";
 import { sets } from "../sets";
 import { xforms } from "./xforms";
+import { addSetsToBindings } from "./utils";
 
 const v = name => {
   return `/vive-user/vive-var/${name}`;
@@ -119,7 +120,7 @@ const teleportRight = [
   }
 ];
 
-export const viveUserBindings = {
+export const viveUserBindings = addSetsToBindings({
   [sets.global]: [
     {
       src: {
@@ -806,4 +807,4 @@ export const viveUserBindings = {
       priority: 400
     }
   ]
-};
+});
diff --git a/src/systems/userinput/bindings/xbox-controller-user.js b/src/systems/userinput/bindings/xbox-controller-user.js
index b9d230a79..67699c539 100644
--- a/src/systems/userinput/bindings/xbox-controller-user.js
+++ b/src/systems/userinput/bindings/xbox-controller-user.js
@@ -1,6 +1,7 @@
 import { paths } from "../paths";
 import { sets } from "../sets";
 import { xforms } from "./xforms";
+import { addSetsToBindings } from "./utils";
 
 const xboxUnscaledCursorScalePenTip = "foobarbazbotbooch";
 
@@ -8,7 +9,7 @@ const button = paths.device.xbox.button;
 const axis = paths.device.xbox.axis;
 const rightTriggerFalling = "/vars/xbox/rightTriggerFalling";
 
-export const xboxControllerUserBindings = {
+export const xboxControllerUserBindings = addSetsToBindings({
   [sets.cursorHoldingInteractable]: [
     {
       src: { value: button("rightTrigger").pressed },
@@ -187,4 +188,4 @@ export const xboxControllerUserBindings = {
       priority: 100
     }
   ]
-};
+});
diff --git a/src/systems/userinput/userinput.js b/src/systems/userinput/userinput.js
index 5460b7769..1ac910e9e 100644
--- a/src/systems/userinput/userinput.js
+++ b/src/systems/userinput/userinput.js
@@ -26,32 +26,32 @@ import { resolveActionSets } from "./resolve-action-sets";
 import { GamepadDevice } from "./devices/gamepad";
 import { gamepadBindings } from "./bindings/generic-gamepad";
 
-const priorityMap = new Map();
-function prioritizeBindings(registeredMappings, activeSets) {
-  const activeBindings = new Set();
-  priorityMap.clear();
+function buildMap(registeredMappings) {
+  const map = new Map();
+  const add = (path, binding) => {
+    if (!map.has(path)) {
+      map.set(path, [binding]);
+    } else {
+      map.get(path).push(binding);
+    }
+  };
   for (const mapping of registeredMappings) {
     for (const setName in mapping) {
-      if (!activeSets.has(setName) || !mapping[setName]) continue;
       for (const binding of mapping[setName]) {
-        const { root, priority } = binding;
-        const prevBinding = priorityMap.get(root);
-        if (!root || !priority) {
-          activeBindings.add(binding);
-        } else if (!prevBinding) {
-          activeBindings.add(binding);
-          priorityMap.set(root, binding);
-        } else if (priority > prevBinding.priority) {
-          activeBindings.delete(priorityMap.get(root));
-          activeBindings.add(binding);
-          priorityMap.set(root, binding);
-        } else if (prevBinding.priority === priority) {
-          console.error("equal priorities on same root", binding, priorityMap.get(root));
+        if (Array.isArray(binding.src)) {
+          for (const path of binding.src) {
+            add(path, binding);
+          }
+        } else {
+          for (const srcKey in binding.src) {
+            const path = binding.src[srcKey];
+            add(path, binding);
+          }
         }
       }
     }
   }
-  return activeBindings;
+  return map;
 }
 
 AFRAME.registerSystem("userinput", {
@@ -71,6 +71,7 @@ AFRAME.registerSystem("userinput", {
     this.activeDevices = new Set([new MouseDevice(), new AppAwareMouseDevice(), new KeyboardDevice(), new HudDevice()]);
 
     this.registeredMappings = new Set([keyboardDebuggingBindings]);
+    this.map = buildMap(this.registeredMappings);
     this.xformStates = new Map();
 
     const appAwareTouchscreenDevice = new AppAwareTouchscreenDevice();
@@ -91,6 +92,7 @@ AFRAME.registerSystem("userinput", {
           this.registeredMappings.add(keyboardMouseUserBindings);
         }
       }
+      this.map = buildMap(this.registeredMappings);
     };
     this.el.sceneEl.addEventListener("enter-vr", updateBindingsForVRMode);
     this.el.sceneEl.addEventListener("exit-vr", updateBindingsForVRMode);
@@ -127,6 +129,7 @@ AFRAME.registerSystem("userinput", {
           this.registeredMappings.add(gamepadBindings);
         }
         this.activeDevices.add(gamepadDevice);
+        this.map = buildMap(this.registeredMappings);
       },
       false
     );
@@ -136,6 +139,7 @@ AFRAME.registerSystem("userinput", {
         for (const device of this.activeDevices) {
           if (device.gamepad === e.gamepad) {
             this.activeDevices.delete(device);
+            this.map = buildMap(this.registeredMappings);
             return;
           }
         }
@@ -150,16 +154,73 @@ AFRAME.registerSystem("userinput", {
     for (const { set, value } of this.pendingSetChanges) {
       this.activeSets[value ? "add" : "delete"](set);
     }
-    this.pendingSetChanges.length = 0;
+    let runners = this.pendingSetChanges.length ? [] : this.runners;
+    if (this.pendingSetChanges.length) {
+      this.pendingSetChanges.length = 0;
+      this.actives = [];
+      for (const mapping of this.registeredMappings) {
+        for (const setName in mapping) {
+          if (!this.activeSets.has(setName) || !mapping[setName]) continue;
+          for (const binding of mapping[setName]) {
+            let active = false;
+            for (const set of binding.sets) {
+              if (this.activeSets.has(set)) {
+                active = true;
+              }
+            }
+            this.actives.push(active);
+            runners.push(binding);
+          }
+        }
+      }
+
+      const maxAmongActive = (path, map) => {
+        let max = -1;
+        const bindings = map.get(path);
+        if (!bindings) {
+          return -1;
+        }
+        for (const binding of bindings) {
+          let active = false;
+          for (const set of binding.sets) {
+            if (this.activeSets.has(set)) {
+              active = true;
+            }
+          }
+          if (active && binding.priority && binding.priority > max) {
+            max = binding.priority;
+          }
+        }
+        return max;
+      };
+
+      for (const i in runners) {
+        if (!this.actives[i]) continue;
+        const binding = runners[i];
+        let active = true;
+        for (const p in binding.src) {
+          const path = binding.src[p];
+          let subpaths = String.split(path, "/");
+          while (binding.priority && subpaths.length) {
+            if (binding.priority < maxAmongActive(Array.join(subpaths, "/"), this.map, this.activeSets)) {
+              active = false;
+            }
+            subpaths.pop();
+          }
+          this.actives[i] = active;
+        }
+      }
+    }
 
     this.frame = {};
     for (const device of this.activeDevices) {
       device.write(this.frame);
     }
 
-    const activeBindings = prioritizeBindings(this.registeredMappings, this.activeSets);
-    for (const binding of activeBindings) {
-      const bindingExistedLastFrame = this.activeBindings && this.activeBindings.has(binding);
+    for (const i in runners) {
+      const binding = runners[i];
+      if (!this.actives[i]) continue;
+      const bindingExistedLastFrame = this.runners && this.runners.includes(binding);
       if (!bindingExistedLastFrame) {
         this.xformStates.delete(binding);
       }
@@ -171,14 +232,84 @@ AFRAME.registerSystem("userinput", {
       }
     }
 
-    this.activeBindings = activeBindings;
+    this.runners = runners;
 
     if (this.frame[paths.actions.logDebugFrame] || this.frame[paths.actions.log]) {
-      console.log("frame", this.frame);
-      console.log("sets", this.activeSets);
-      console.log("bindings", this.activeBindings);
-      console.log("devices", this.activeDevices);
+      const line = "__________________________________________________________________";
+      const bindingToString = b => {
+        let sb = [];
+        sb.push("  ");
+        sb.push("src: ");
+        sb.push("\n");
+        for (const s of Object.keys(b.src)) {
+          sb.push("  ");
+          sb.push("  ");
+          sb.push(s);
+          sb.push(" : ");
+          sb.push(b.src[s]);
+          sb.push("\n");
+        }
+        sb.push("  ");
+        sb.push("dest: ");
+        sb.push("\n");
+        for (const s of Object.keys(b.dest)) {
+          sb.push("  ");
+          sb.push("  ");
+          sb.push(s);
+          sb.push(" : ");
+          sb.push(b.dest[s]);
+          sb.push("\n");
+        }
+        sb.push("  ");
+        sb.push("priority");
+        sb.push(" : ");
+        sb.push(b.priority || 0);
+        for (const s of b.sets) {
+          sb.push("\n");
+          sb.push("  ");
+          sb.push("in set");
+          sb.push(" : ");
+          sb.push(s);
+          sb.push("\n");
+        }
+        sb.push(line);
+        sb.push("\n");
+        return sb.join("");
+      };
+      let sb = [];
+      sb.push("\n");
+      sb.push(line);
+      sb.push("\n");
+      sb.push("actives:");
+      sb.push("\n");
+      sb.push(line);
+      sb.push("\n");
+      for (let i = 0; i < this.runners.length; i++) {
+        if (this.actives[i]) {
+          sb.push(bindingToString(this.runners[i]));
+        }
+      }
+      sb.push("\n");
+      sb.push(line);
+      sb.push("\n");
+      sb.push("inactives:");
+      sb.push("\n");
+      sb.push(line);
+      sb.push("\n");
+      for (let i = 0; i < this.runners.length; i++) {
+        if (!this.actives[i]) {
+          sb.push(bindingToString(this.runners[i]));
+        }
+      }
+      console.log("active and inactive bindings");
+      console.log(sb.join(""));
+      console.log("runners", this.runners);
+      console.log("actives", this.actives);
       console.log("xformStates", this.xformStates);
+      console.log("devices", this.activeDevices);
+      console.log("map", this.map);
+      console.log("activeSets", this.activeSets);
+      console.log("frame", this.frame);
     }
   }
 });
-- 
GitLab