From 9c66a25d837268615ce7d6cbf670377f438452eb Mon Sep 17 00:00:00 2001 From: druiz17 Date: Tue, 19 Sep 2017 17:03:53 -0700 Subject: [PATCH] fixing and cleanup tablet inputs --- .../controllers/controllerDispatcher.js | 6 +- .../controllerModules/overlayLaserInput.js | 344 ++++-------------- .../controllerModules/tabletStylusInput.js | 268 ++------------ .../libraries/controllerDispatcherUtils.js | 4 +- scripts/system/libraries/touchEventUtils.js | 270 ++++++++++++++ 5 files changed, 375 insertions(+), 517 deletions(-) create mode 100644 scripts/system/libraries/touchEventUtils.js diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 63657e9b6f..22987245a4 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -140,10 +140,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }; this.setIgnoreTablet = function() { - if (HMD.tabletID !== _this.tabletID) { - RayPick.setIgnoreOverlays(_this.leftControllerRayPick, [HMD.tabletID]); - RayPick.setIgnoreOverlays(_this.rightControllerRayPick, [HMD.tabletID]); - } + RayPick.setIgnoreOverlays(_this.leftControllerRayPick, [HMD.tabletID]); + RayPick.setIgnoreOverlays(_this.rightControllerRayPick, [HMD.tabletID]); }; this.update = function () { diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index 218122e876..9bf7ed23da 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -16,6 +16,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { + var touchEvent = Script.require("/~/system/libraries/touchEventUtils.js"); var halfPath = { type: "line3d", color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, @@ -88,186 +89,6 @@ Script.include("/~/system/libraries/controllers.js"); var HAPTIC_STYLUS_STRENGTH = 1.0; var HAPTIC_STYLUS_DURATION = 20.0; - function laserTargetHasKeyboardFocus(laserTarget) { - if (laserTarget && laserTarget !== NULL_UUID) { - return Overlays.keyboardFocusOverlay === laserTarget; - } - } - - function setKeyboardFocusOnLaserTarget(laserTarget) { - if (laserTarget && laserTarget !== NULL_UUID) { - Overlays.keyboardFocusOverlay = laserTarget; - Entities.keyboardFocusEntity = NULL_UUID; - } - } - - function sendHoverEnterEventToLaserTarget(hand, laserTarget) { - if (!laserTarget) { - return; - } - var pointerEvent = { - type: "Move", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: laserTarget.position2D, - pos3D: laserTarget.position, - normal: laserTarget.normal, - direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), - button: "None" - }; - - if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { - Overlays.sendHoverEnterOverlay(laserTarget.overlayID, pointerEvent); - } - } - - function sendHoverOverEventToLaserTarget(hand, laserTarget) { - - if (!laserTarget) { - return; - } - var pointerEvent = { - type: "Move", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: laserTarget.position2D, - pos3D: laserTarget.position, - normal: laserTarget.normal, - direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), - button: "None" - }; - - if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { - Overlays.sendMouseMoveOnOverlay(laserTarget.overlayID, pointerEvent); - Overlays.sendHoverOverOverlay(laserTarget.overlayID, pointerEvent); - } - } - - function sendTouchStartEventToLaserTarget(hand, laserTarget) { - if (!laserTarget) { - return; - } - - var pointerEvent = { - type: "Press", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: laserTarget.position2D, - pos3D: laserTarget.position, - normal: laserTarget.normal, - direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), - button: "Primary", - isPrimaryHeld: true - }; - - if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { - Overlays.sendMousePressOnOverlay(laserTarget.overlayID, pointerEvent); - } - } - - function sendTouchEndEventToLaserTarget(hand, laserTarget) { - if (!laserTarget) { - return; - } - var pointerEvent = { - type: "Release", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: laserTarget.position2D, - pos3D: laserTarget.position, - normal: laserTarget.normal, - direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), - button: "Primary" - }; - - if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { - Overlays.sendMouseReleaseOnOverlay(laserTarget.overlayID, pointerEvent); - Overlays.sendMouseReleaseOnOverlay(laserTarget.overlayID, pointerEvent); - } - } - - function sendTouchMoveEventToLaserTarget(hand, laserTarget) { - if (!laserTarget) { - return; - } - var pointerEvent = { - type: "Move", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: laserTarget.position2D, - pos3D: laserTarget.position, - normal: laserTarget.normal, - direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), - button: "Primary", - isPrimaryHeld: true - }; - - if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { - Overlays.sendMouseReleaseOnOverlay(laserTarget.overlayID, pointerEvent); - } - } - - // will return undefined if overlayID does not exist. - function calculateLaserTargetFromOverlay(worldPos, overlayID) { - var overlayPosition = Overlays.getProperty(overlayID, "position"); - if (overlayPosition === undefined) { - return null; - } - - // project stylusTip onto overlay plane. - var overlayRotation = Overlays.getProperty(overlayID, "rotation"); - if (overlayRotation === undefined) { - return null; - } - var normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1}); - var distance = Vec3.dot(Vec3.subtract(worldPos, overlayPosition), normal); - - // calclulate normalized position - var invRot = Quat.inverse(overlayRotation); - var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(worldPos, overlayPosition)); - var dpi = Overlays.getProperty(overlayID, "dpi"); - - var dimensions; - if (dpi) { - // Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property - // is used as a scale. - var resolution = Overlays.getProperty(overlayID, "resolution"); - if (resolution === undefined) { - return null; - } - resolution.z = 1;// Circumvent divide-by-zero. - var scale = Overlays.getProperty(overlayID, "dimensions"); - if (scale === undefined) { - return null; - } - scale.z = 0.01;// overlay dimensions are 2D, not 3D. - dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); - } else { - dimensions = Overlays.getProperty(overlayID, "dimensions"); - if (dimensions === undefined) { - return null; - } - if (!dimensions.z) { - dimensions.z = 0.01;// sometimes overlay dimensions are 2D, not 3D. - } - } - var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z }; - var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT); - - // 2D position on overlay plane in meters, relative to the bounding box upper-left hand corner. - var position2D = { - x: normalizedPosition.x * dimensions.x, - y: (1 - normalizedPosition.y) * dimensions.y // flip y-axis - }; - - return { - entityID: null, - overlayID: overlayID, - distance: distance, - position: worldPos, - position2D: position2D, - normal: normal, - normalizedPosition: normalizedPosition, - dimensions: dimensions, - valid: true - }; - } - function distance2D(a, b) { var dx = (a.x - b.x); var dy = (a.y - b.y); @@ -277,16 +98,11 @@ Script.include("/~/system/libraries/controllers.js"); function OverlayLaserInput(hand) { this.hand = hand; this.active = false; - this.previousLaserClikcedTarget = false; + this.previousLaserClickedTarget = false; this.laserPressingTarget = false; - this.tabletScreenID = null; this.mode = "none"; - this.laserTargetID = null; this.laserTarget = null; this.pressEnterLaserTarget = null; - this.hover = false; - this.target = null; - this.lastValidTargetID = this.tabletTargetID; this.parameters = makeDispatcherModuleParameters( @@ -307,23 +123,35 @@ Script.include("/~/system/libraries/controllers.js"); return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; }; - this.stealTouchFocus = function(laserTarget) { - this.requestTouchFocus(laserTarget); + this.hasTouchFocus = function(laserTarget) { + return (laserTarget.overlayID === this.hoverOverlay); }; this.requestTouchFocus = function(laserTarget) { - if (laserTarget !== null || laserTarget !== undefined) { - sendHoverEnterEventToLaserTarget(this.hand, this.laserTarget); - this.lastValidTargetID = laserTarget; + if (laserTarget.overlayID && + laserTarget.overlayID !== this.hoverOverlay) { + this.hoverOverlay = laserTarget.overlayID; + touchEvent.sendHoverEnterEventToTouchTarget(this.hand, laserTarget); } }; this.relinquishTouchFocus = function() { // send hover leave event. - var pointerEvent = { type: "Move", id: this.hand + 1 }; - Overlays.sendMouseMoveOnOverlay(this.lastValidTargetID, pointerEvent); - Overlays.sendHoverOverOverlay(this.lastValidTargetID, pointerEvent); - Overlays.sendHoverLeaveOverlay(this.lastValidID, pointerEvent); + if (this.hoverOverlay) { + var pointerEvent = { type: "Move", id: this.hand + 1 }; + Overlays.sendMouseMoveOnOverlay(this.hoverOverlay, pointerEvent); + Overlays.sendHoverOverOverlay(this.hoverOverlay, pointerEvent); + Overlays.sendHoverLeaveOverlay(this.hoverOverlay, pointerEvent); + this.hoverOverlay = null; + } + }; + + this.stealTouchFocus = function(laserTarget) { + if (laserTarget.overlayID === this.getOtherModule().hoverOverlay) { + this.getOtherModule().relinquishTouchFocus(); + } + + this.requestTouchFocus(laserTarget); }; this.updateLaserPointer = function(controllerData) { @@ -345,38 +173,23 @@ Script.include("/~/system/libraries/controllers.js"); this.processControllerTriggers = function(controllerData) { if (controllerData.triggerClicks[this.hand]) { this.mode = "full"; - this.laserPressingTarget = true; - this.hover = false; } else if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { this.mode = "half"; - this.laserPressingTarget = false; - this.hover = true; - this.requestTouchFocus(this.laserTargetID); } else { this.mode = "none"; - this.laserPressingTarget = false; - this.hover = false; - this.relinquishTouchFocus(); - } }; - this.hovering = function() { - if (!laserTargetHasKeyboardFocus(this.laserTagetID)) { - setKeyboardFocusOnLaserTarget(this.laserTargetID); - } - sendHoverOverEventToLaserTarget(this.hand, this.laserTarget); - }; - this.laserPressEnter = function () { - sendTouchStartEventToLaserTarget(this.hand, this.laserTarget); + this.stealTouchFocus(this.laserTarget); + touchEvent.sendTouchStartEventToTouchTarget(this.hand, this.laserTarget); Controller.triggerHapticPulse(HAPTIC_STYLUS_STRENGTH, HAPTIC_STYLUS_DURATION, this.hand); this.touchingEnterTimer = 0; this.pressEnterLaserTarget = this.laserTarget; this.deadspotExpired = false; - var LASER_PRESS_TO_MOVE_DEADSPOT = 0.026; + var LASER_PRESS_TO_MOVE_DEADSPOT = 0.094; this.deadspotRadius = Math.tan(LASER_PRESS_TO_MOVE_DEADSPOT) * this.laserTarget.distance; }; @@ -386,15 +199,15 @@ Script.include("/~/system/libraries/controllers.js"); } // special case to handle home button. - if (this.laserTargetID === HMD.homeButtonID) { - Messages.sendLocalMessage("home", this.laserTargetID); + if (this.laserTarget.overlayID === HMD.homeButtonID) { + Messages.sendLocalMessage("home", this.laserTarget.overlayID); } // send press event if (this.deadspotExpired) { - sendTouchEndEventToLaserTarget(this.hand, this.laserTarget); + touchEvent.sendTouchEndEventToTouchTarget(this.hand, this.laserTarget); } else { - sendTouchEndEventToLaserTarget(this.hand, this.pressEnterLaserTarget); + touchEvent.sendTouchEndEventToTouchTarget(this.hand, this.pressEnterLaserTarget); } }; @@ -402,27 +215,49 @@ Script.include("/~/system/libraries/controllers.js"); this.touchingEnterTimer += dt; if (this.laserTarget) { - var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds - if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || - distance2D( this.laserTarget.position2D, - this.pressEnterLaserTarget.position2D) > this.deadspotRadius) { - sendTouchMoveEventToLaserTarget(this.hand, this.laserTarget); - this.deadspotExpired = true; + if (controllerData.triggerClicks[this.hand]) { + var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds + if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || + distance2D(this.laserTarget.position2D, + this.pressEnterLaserTarget.position2D) > this.deadspotRadius) { + touchEvent.sendTouchMoveEventToTouchTarget(this.hand, this.laserTarget); + this.deadspotExpired = true; + } + } else { + this.laserPressingTarget = false; } } else { this.laserPressingTarget = false; } }; - this.releaseTouchEvent = function() { - sendTouchEndEventToLaserTarget(this.hand, this.pressEnterLaserTarget); - }; - - - this.updateLaserTargets = function(controllerData) { + this.processLaser = function(controllerData) { + if (this.shouldExit(controllerData) || this.getOtherModule().active) { + this.exitModule(); + return false; + } var intersection = controllerData.rayPicks[this.hand]; - this.laserTargetID = intersection.objectID; - this.laserTarget = calculateLaserTargetFromOverlay(intersection.intersection, intersection.objectID); + var laserTarget = touchEvent.composeTouchTargetFromIntersection(intersection); + + if (controllerData.triggerClicks[this.hand]) { + this.laserTarget = laserTarget; + this.laserPressingTarget = true; + } else { + this.requestTouchFocus(laserTarget); + + if (!touchEvent.touchTargetHasKeyboardFocus(laserTarget)) { + touchEvent.setKeyboardFocusOnTouchTarget(laserTarget); + } + + if (this.hasTouchFocus(laserTarget) && !this.laserPressingTarget) { + touchEvent.sendHoverOverEventToTouchTarget(this.hand, laserTarget); + } + } + + this.processControllerTriggers(controllerData); + this.updateLaserPointer(controllerData); + this.active = true; + return true; }; this.shouldExit = function(controllerData) { @@ -436,7 +271,12 @@ Script.include("/~/system/libraries/controllers.js"); }; this.exitModule = function() { - this.releaseTouchEvent(); + if (this.laserPressingTarget) { + this.deadspotExpired = true; + this.laserPressExit(); + this.laserPressingTarget = false; + } + this.deleteContextOverlay(); this.relinquishTouchFocus(); this.reset(); this.updateLaserPointer(); @@ -444,12 +284,6 @@ Script.include("/~/system/libraries/controllers.js"); }; this.reset = function() { - this.hover = false; - this.pressEnterLaserTarget = null; - this.laserTarget = null; - this.laserTargetID = null; - this.laserPressingTarget = false; - this.previousLaserClickedTarget = null; this.mode = "none"; this.active = false; }; @@ -467,35 +301,13 @@ Script.include("/~/system/libraries/controllers.js"); }; this.isReady = function (controllerData) { - this.target = null; - var intersection = controllerData.rayPicks[this.hand]; - if (intersection.type === RayPick.INTERSECTED_OVERLAY) { - if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE && !this.getOtherModule().active) { - this.target = intersection.objectID; - this.active = true; - return makeRunningValues(true, [], []); - } else { - this.deleteContextOverlay(); - } + if (this.processLaser(controllerData)) { + return makeRunningValues(true, [], []); } - this.reset(); return makeRunningValues(false, [], []); }; this.run = function (controllerData, deltaTime) { - if (this.shouldExit(controllerData)) { - this.exitModule(); - return makeRunningValues(false, [], []); - } - - if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { - this.deleteContextOverlay(); - } - - this.updateLaserTargets(controllerData); - this.processControllerTriggers(controllerData); - this.updateLaserPointer(controllerData); - if (!this.previousLaserClickedTarget && this.laserPressingTarget) { this.laserPressEnter(); } @@ -508,11 +320,11 @@ Script.include("/~/system/libraries/controllers.js"); this.laserPressing(controllerData, deltaTime); } - if (this.hover) { - this.hovering(); + if (this.processLaser(controllerData)) { + return makeRunningValues(true, [], []); + } else { + return makeRunningValues(false, [], []); } - - return makeRunningValues(true, [], []); }; this.cleanup = function () { diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js index 230038adb5..52706fc6d7 100644 --- a/scripts/system/controllers/controllerModules/tabletStylusInput.js +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -16,7 +16,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { - + var touchEvent = Script.require("/~/system/libraries/touchEventUtils.js"); // triggered when stylus presses a web overlay/entity var HAPTIC_STYLUS_STRENGTH = 1.0; var HAPTIC_STYLUS_DURATION = 20.0; @@ -25,230 +25,6 @@ Script.include("/~/system/libraries/controllers.js"); var WEB_STYLUS_LENGTH = 0.2; var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative number) to slide stylus in hand - - function stylusTargetHasKeyboardFocus(stylusTarget) { - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - return Entities.keyboardFocusEntity === stylusTarget.entityID; - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - return Overlays.keyboardFocusOverlay === stylusTarget.overlayID; - } - } - - function setKeyboardFocusOnStylusTarget(stylusTarget) { - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID && - Entities.wantsHandControllerPointerEvents(stylusTarget.entityID)) { - Overlays.keyboardFocusOverlay = NULL_UUID; - Entities.keyboardFocusEntity = stylusTarget.entityID; - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.keyboardFocusOverlay = stylusTarget.overlayID; - Entities.keyboardFocusEntity = NULL_UUID; - } - } - - function sendHoverEnterEventToStylusTarget(hand, stylusTarget) { - var pointerEvent = { - type: "Move", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: stylusTarget.position2D, - pos3D: stylusTarget.position, - normal: stylusTarget.normal, - direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), - button: "None" - }; - - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - Entities.sendHoverEnterEntity(stylusTarget.entityID, pointerEvent); - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.sendHoverEnterOverlay(stylusTarget.overlayID, pointerEvent); - } - } - - function sendHoverOverEventToStylusTarget(hand, stylusTarget) { - var pointerEvent = { - type: "Move", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: stylusTarget.position2D, - pos3D: stylusTarget.position, - normal: stylusTarget.normal, - direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), - button: "None" - }; - - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - Entities.sendMouseMoveOnEntity(stylusTarget.entityID, pointerEvent); - Entities.sendHoverOverEntity(stylusTarget.entityID, pointerEvent); - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.sendMouseMoveOnOverlay(stylusTarget.overlayID, pointerEvent); - Overlays.sendHoverOverOverlay(stylusTarget.overlayID, pointerEvent); - } - } - - function sendTouchStartEventToStylusTarget(hand, stylusTarget) { - var pointerEvent = { - type: "Press", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: stylusTarget.position2D, - pos3D: stylusTarget.position, - normal: stylusTarget.normal, - direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), - button: "Primary", - isPrimaryHeld: true - }; - - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - Entities.sendMousePressOnEntity(stylusTarget.entityID, pointerEvent); - Entities.sendClickDownOnEntity(stylusTarget.entityID, pointerEvent); - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.sendMousePressOnOverlay(stylusTarget.overlayID, pointerEvent); - } - } - - function sendTouchEndEventToStylusTarget(hand, stylusTarget) { - var pointerEvent = { - type: "Release", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: stylusTarget.position2D, - pos3D: stylusTarget.position, - normal: stylusTarget.normal, - direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), - button: "Primary" - }; - - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - Entities.sendMouseReleaseOnEntity(stylusTarget.entityID, pointerEvent); - Entities.sendClickReleaseOnEntity(stylusTarget.entityID, pointerEvent); - Entities.sendHoverLeaveEntity(stylusTarget.entityID, pointerEvent); - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.sendMouseReleaseOnOverlay(stylusTarget.overlayID, pointerEvent); - } - } - - function sendTouchMoveEventToStylusTarget(hand, stylusTarget) { - var pointerEvent = { - type: "Move", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: stylusTarget.position2D, - pos3D: stylusTarget.position, - normal: stylusTarget.normal, - direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), - button: "Primary", - isPrimaryHeld: true - }; - - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - Entities.sendMouseMoveOnEntity(stylusTarget.entityID, pointerEvent); - Entities.sendHoldingClickOnEntity(stylusTarget.entityID, pointerEvent); - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.sendMouseMoveOnOverlay(stylusTarget.overlayID, pointerEvent); - } - } - - // will return undefined if overlayID does not exist. - function calculateStylusTargetFromOverlay(stylusTip, overlayID) { - var overlayPosition = Overlays.getProperty(overlayID, "position"); - if (overlayPosition === undefined) { - return; - } - - // project stylusTip onto overlay plane. - var overlayRotation = Overlays.getProperty(overlayID, "rotation"); - if (overlayRotation === undefined) { - return; - } - var normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1}); - var distance = Vec3.dot(Vec3.subtract(stylusTip.position, overlayPosition), normal); - var position = Vec3.subtract(stylusTip.position, Vec3.multiply(normal, distance)); - - // calclulate normalized position - var invRot = Quat.inverse(overlayRotation); - var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, overlayPosition)); - var dpi = Overlays.getProperty(overlayID, "dpi"); - - var dimensions; - if (dpi) { - // Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property - // is used as a scale. - var resolution = Overlays.getProperty(overlayID, "resolution"); - if (resolution === undefined) { - return; - } - resolution.z = 1; // Circumvent divide-by-zero. - var scale = Overlays.getProperty(overlayID, "dimensions"); - if (scale === undefined) { - return; - } - scale.z = 0.01; // overlay dimensions are 2D, not 3D. - dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); - } else { - dimensions = Overlays.getProperty(overlayID, "dimensions"); - if (dimensions === undefined) { - return; - } - if (!dimensions.z) { - dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D. - } - } - var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z }; - var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT); - - // 2D position on overlay plane in meters, relative to the bounding box upper-left hand corner. - var position2D = { - x: normalizedPosition.x * dimensions.x, - y: (1 - normalizedPosition.y) * dimensions.y // flip y-axis - }; - - return { - entityID: null, - overlayID: overlayID, - distance: distance, - position: position, - position2D: position2D, - normal: normal, - normalizedPosition: normalizedPosition, - dimensions: dimensions, - valid: true - }; - } - - // will return undefined if entity does not exist. - function calculateStylusTargetFromEntity(stylusTip, props) { - if (props.rotation === undefined) { - // if rotation is missing from props object, then this entity has probably been deleted. - return; - } - - // project stylus tip onto entity plane. - var normal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1}); - Vec3.multiplyQbyV(props.rotation, {x: 0, y: 1, z: 0}); - var distance = Vec3.dot(Vec3.subtract(stylusTip.position, props.position), normal); - var position = Vec3.subtract(stylusTip.position, Vec3.multiply(normal, distance)); - - // generate normalized coordinates - var invRot = Quat.inverse(props.rotation); - var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, props.position)); - var invDimensions = { x: 1 / props.dimensions.x, y: 1 / props.dimensions.y, z: 1 / props.dimensions.z }; - var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint); - - // 2D position on entity plane in meters, relative to the bounding box upper-left hand corner. - var position2D = { - x: normalizedPosition.x * props.dimensions.x, - y: (1 - normalizedPosition.y) * props.dimensions.y // flip y-axis - }; - - return { - entityID: props.id, - entityProps: props, - overlayID: null, - distance: distance, - position: position, - position2D: position2D, - normal: normal, - normalizedPosition: normalizedPosition, - dimensions: props.dimensions, - valid: true - }; - } - function isNearStylusTarget(stylusTargets, edgeBorder, minNormalDistance, maxNormalDistance) { for (var i = 0; i < stylusTargets.length; i++) { var stylusTarget = stylusTargets[i]; @@ -330,7 +106,7 @@ Script.include("/~/system/libraries/controllers.js"); 100); this.getOtherHandController = function() { - return (this.hand === RIGHT_HAND) ? Controller.Standard.LeftHand : Controller.Standard.RightHand; + return (this.hand === RIGHT_HAND) ? leftTabletStylusInput : rightTabletStylusInput; }; this.handToController = function() { @@ -430,12 +206,12 @@ Script.include("/~/system/libraries/controllers.js"); stylusTarget.entityID !== this.hoverEntity && stylusTarget.entityID !== this.getOtherHandController().hoverEntity) { this.hoverEntity = stylusTarget.entityID; - sendHoverEnterEventToStylusTarget(this.hand, stylusTarget); + touchEvent.sendHoverEnterEventToTouchTarget(this.hand, stylusTarget); } else if (stylusTarget.overlayID && stylusTarget.overlayID !== this.hoverOverlay && stylusTarget.overlayID !== this.getOtherHandController().hoverOverlay) { this.hoverOverlay = stylusTarget.overlayID; - sendHoverEnterEventToStylusTarget(this.hand, stylusTarget); + touchEvent.sendHoverEnterEventToTouchTarget(this.hand, stylusTarget); } }; @@ -492,7 +268,7 @@ Script.include("/~/system/libraries/controllers.js"); for (i = 0; i < candidateEntities.length; i++) { props = candidateEntities[i]; if (props && props.type === "Web") { - stylusTarget = calculateStylusTargetFromEntity(this.stylusTip, candidateEntities[i]); + stylusTarget = touchEvent.calculateTouchTargetFromEntity(this.stylusTip, candidateEntities[i]); if (stylusTarget) { stylusTargets.push(stylusTarget); } @@ -502,7 +278,7 @@ Script.include("/~/system/libraries/controllers.js"); // add the tabletScreen, if it is valid if (HMD.tabletScreenID && HMD.tabletScreenID !== NULL_UUID && Overlays.getProperty(HMD.tabletScreenID, "visible")) { - stylusTarget = calculateStylusTargetFromOverlay(this.stylusTip, HMD.tabletScreenID); + stylusTarget = touchEvent.calculateTouchTargetFromOverlay(this.stylusTip, HMD.tabletScreenID); if (stylusTarget) { stylusTargets.push(stylusTarget); } @@ -511,7 +287,7 @@ Script.include("/~/system/libraries/controllers.js"); // add the tablet home button. if (HMD.homeButtonID && HMD.homeButtonID !== NULL_UUID && Overlays.getProperty(HMD.homeButtonID, "visible")) { - stylusTarget = calculateStylusTargetFromOverlay(this.stylusTip, HMD.homeButtonID); + stylusTarget = touchEvent.calculateTouchTargetFromOverlay(this.stylusTip, HMD.homeButtonID); if (stylusTarget) { stylusTargets.push(stylusTarget); } @@ -530,9 +306,9 @@ Script.include("/~/system/libraries/controllers.js"); var sensorScaleFactor = MyAvatar.sensorToWorldScale; this.isNearStylusTarget = isNearStylusTarget(stylusTargets, - (EDGE_BORDER + hysteresisOffset) * sensorScaleFactor, - (TABLET_MIN_TOUCH_DISTANCE - hysteresisOffset) * sensorScaleFactor, - (WEB_DISPLAY_STYLUS_DISTANCE + hysteresisOffset) * sensorScaleFactor); + (EDGE_BORDER + hysteresisOffset) * sensorScaleFactor, + (TABLET_MIN_TOUCH_DISTANCE - hysteresisOffset) * sensorScaleFactor, + (WEB_DISPLAY_STYLUS_DISTANCE + hysteresisOffset) * sensorScaleFactor); if (this.isNearStylusTarget) { if (!this.useFingerInsteadOfStylus) { @@ -556,12 +332,12 @@ Script.include("/~/system/libraries/controllers.js"); this.requestTouchFocus(nearestStylusTarget); - if (!stylusTargetHasKeyboardFocus(nearestStylusTarget)) { - setKeyboardFocusOnStylusTarget(nearestStylusTarget); + if (!touchEvent.touchTargetHasKeyboardFocus(nearestStylusTarget)) { + touchEvent.setKeyboardFocusOnTouchTarget(nearestStylusTarget); } - if (this.hasTouchFocus(nearestStylusTarget)) { - sendHoverOverEventToStylusTarget(this.hand, nearestStylusTarget); + if (this.hasTouchFocus(nearestStylusTarget) && !this.stylusTouchingTarget) { + touchEvent.sendHoverOverEventToTouchTarget(this.hand, nearestStylusTarget); } // filter out presses when tip is moving away from tablet. @@ -592,14 +368,14 @@ Script.include("/~/system/libraries/controllers.js"); this.stylusTouchingEnter = function () { this.stealTouchFocus(this.stylusTarget); - sendTouchStartEventToStylusTarget(this.hand, this.stylusTarget); + touchEvent.sendTouchStartEventToTouchTarget(this.hand, this.stylusTarget); Controller.triggerHapticPulse(HAPTIC_STYLUS_STRENGTH, HAPTIC_STYLUS_DURATION, this.hand); this.touchingEnterTimer = 0; this.touchingEnterStylusTarget = this.stylusTarget; this.deadspotExpired = false; - var TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0381; + var TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0481; this.deadspotRadius = TOUCH_PRESS_TO_MOVE_DEADSPOT; }; @@ -616,9 +392,9 @@ Script.include("/~/system/libraries/controllers.js"); // send press event if (this.deadspotExpired) { - sendTouchEndEventToStylusTarget(this.hand, this.stylusTarget); + touchEvent.sendTouchEndEventToTouchTarget(this.hand, this.stylusTarget); } else { - sendTouchEndEventToStylusTarget(this.hand, this.touchingEnterStylusTarget); + touchEvent.sendTouchEndEventToTouchTarget(this.hand, this.touchingEnterStylusTarget); } }; @@ -627,9 +403,9 @@ Script.include("/~/system/libraries/controllers.js"); this.touchingEnterTimer += dt; if (this.stylusTarget.entityID) { - this.stylusTarget = calculateStylusTargetFromEntity(this.stylusTip, this.stylusTarget.entityProps); + this.stylusTarget = touchEvent.calculateTouchTargetFromEntity(this.stylusTip, this.stylusTarget.entityProps); } else if (this.stylusTarget.overlayID) { - this.stylusTarget = calculateStylusTargetFromOverlay(this.stylusTip, this.stylusTarget.overlayID); + this.stylusTarget = touchEvent.calculateTouchTargetFromOverlay(this.stylusTip, this.stylusTarget.overlayID); } var TABLET_MIN_TOUCH_DISTANCE = -0.1; @@ -642,7 +418,7 @@ Script.include("/~/system/libraries/controllers.js"); if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || distance2D(this.stylusTarget.position2D, this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) { - sendTouchMoveEventToStylusTarget(this.hand, this.stylusTarget); + touchEvent.sendTouchMoveEventToTouchTarget(this.hand, this.stylusTarget); this.deadspotExpired = true; } } else { @@ -657,7 +433,7 @@ Script.include("/~/system/libraries/controllers.js"); var overlayLaserModule = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightOverlayLaserInput" : "LeftOverlayLaserInput"); if (overlayLaserModule) { - return overlayLaserModule.isReady(controllerData).active; + return !overlayLaserModule.shouldExit(controllerData); } return false; }; diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 33eec74111..10931e4e93 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -318,6 +318,8 @@ if (typeof module !== 'undefined') { makeRunningValues: makeRunningValues, LEFT_HAND: LEFT_HAND, RIGHT_HAND: RIGHT_HAND, - BUMPER_ON_VALUE: BUMPER_ON_VALUE + BUMPER_ON_VALUE: BUMPER_ON_VALUE, + projectOntoOverlayXYPlane: projectOntoOverlayXYPlane, + projectOntoEntityXYPlane: projectOntoEntityXYPlane }; } diff --git a/scripts/system/libraries/touchEventUtils.js b/scripts/system/libraries/touchEventUtils.js new file mode 100644 index 0000000000..fbd56e16ae --- /dev/null +++ b/scripts/system/libraries/touchEventUtils.js @@ -0,0 +1,270 @@ +"use strict"; + +// touchEventUtils.js +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, + controllerDispatcher.NULL_UUID, enableDispatcherModule, disableDispatcherModule, makeRunningValues, + Messages, Quat, Vec3, getControllerWorldLocation, makeDispatcherModuleParameters, Overlays, controllerDispatcher.ZERO_VEC, + AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset +*/ + +var controllerDispatcher = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); +function touchTargetHasKeyboardFocus(touchTarget) { + if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) { + return Entities.keyboardFocusEntity === touchTarget.entityID; + } else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) { + return Overlays.keyboardFocusOverlay === touchTarget.overlayID; + } +} + +function setKeyboardFocusOnTouchTarget(touchTarget) { + if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID && + Entities.wantsHandControllerPointerEvents(touchTarget.entityID)) { + Overlays.keyboardFocusOverlay = controllerDispatcher.NULL_UUID; + Entities.keyboardFocusEntity = touchTarget.entityID; + } else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) { + Overlays.keyboardFocusOverlay = touchTarget.overlayID; + Entities.keyboardFocusEntity = controllerDispatcher.NULL_UUID; + } +} + +function sendHoverEnterEventToTouchTarget(hand, touchTarget) { + var pointerEvent = { + type: "Move", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: touchTarget.position2D, + pos3D: touchTarget.position, + normal: touchTarget.normal, + direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal), + button: "None" + }; + + if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) { + Entities.sendHoverEnterEntity(touchTarget.entityID, pointerEvent); + } else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) { + Overlays.sendHoverEnterOverlay(touchTarget.overlayID, pointerEvent); + } +} + +function sendHoverOverEventToTouchTarget(hand, touchTarget) { + var pointerEvent = { + type: "Move", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: touchTarget.position2D, + pos3D: touchTarget.position, + normal: touchTarget.normal, + direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal), + button: "None" + }; + + if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) { + Entities.sendMouseMoveOnEntity(touchTarget.entityID, pointerEvent); + Entities.sendHoverOverEntity(touchTarget.entityID, pointerEvent); + } else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) { + Overlays.sendMouseMoveOnOverlay(touchTarget.overlayID, pointerEvent); + Overlays.sendHoverOverOverlay(touchTarget.overlayID, pointerEvent); + } +} + +function sendTouchStartEventToTouchTarget(hand, touchTarget) { + var pointerEvent = { + type: "Press", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: touchTarget.position2D, + pos3D: touchTarget.position, + normal: touchTarget.normal, + direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal), + button: "Primary", + isPrimaryHeld: true + }; + + if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) { + Entities.sendMousePressOnEntity(touchTarget.entityID, pointerEvent); + Entities.sendClickDownOnEntity(touchTarget.entityID, pointerEvent); + } else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) { + Overlays.sendMousePressOnOverlay(touchTarget.overlayID, pointerEvent); + } +} + +function sendTouchEndEventToTouchTarget(hand, touchTarget) { + var pointerEvent = { + type: "Release", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: touchTarget.position2D, + pos3D: touchTarget.position, + normal: touchTarget.normal, + direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal), + button: "Primary" + }; + + if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) { + Entities.sendMouseReleaseOnEntity(touchTarget.entityID, pointerEvent); + Entities.sendClickReleaseOnEntity(touchTarget.entityID, pointerEvent); + Entities.sendHoverLeaveEntity(touchTarget.entityID, pointerEvent); + } else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) { + Overlays.sendMouseReleaseOnOverlay(touchTarget.overlayID, pointerEvent); + } +} + +function sendTouchMoveEventToTouchTarget(hand, touchTarget) { + var pointerEvent = { + type: "Move", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: touchTarget.position2D, + pos3D: touchTarget.position, + normal: touchTarget.normal, + direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal), + button: "Primary", + isPrimaryHeld: true + }; + + if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) { + Entities.sendMouseMoveOnEntity(touchTarget.entityID, pointerEvent); + Entities.sendHoldingClickOnEntity(touchTarget.entityID, pointerEvent); + } else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) { + Overlays.sendMouseMoveOnOverlay(touchTarget.overlayID, pointerEvent); + } +} + +function composeTouchTargetFromIntersection(intersection) { + var isEntity = (intersection.type === RayPick.INTERSECTED_ENTITY); + var objectID = intersection.objectID; + var worldPos = intersection.intersection; + var props = null; + if (isEntity) { + props = Entities.getProperties(intersection.objectID); + } + + var position2D =(isEntity ? controllerDispatcher.projectOntoEntityXYPlane(objectID, worldPos, props) : + controllerDispatcher.projectOntoOverlayXYPlane(objectID, worldPos)); + return { + entityID: isEntity ? objectID : null, + overlayID: isEntity ? null : objectID, + distance: intersection.distance, + position: worldPos, + position2D: position2D, + normal: intersection.surfaceNormal + }; +} + +// will return undefined if overlayID does not exist. +function calculateTouchTargetFromOverlay(touchTip, overlayID) { + var overlayPosition = Overlays.getProperty(overlayID, "position"); + if (overlayPosition === undefined) { + return; + } + + // project touchTip onto overlay plane. + var overlayRotation = Overlays.getProperty(overlayID, "rotation"); + if (overlayRotation === undefined) { + return; + } + var normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1}); + var distance = Vec3.dot(Vec3.subtract(touchTip.position, overlayPosition), normal); + var position = Vec3.subtract(touchTip.position, Vec3.multiply(normal, distance)); + + // calclulate normalized position + var invRot = Quat.inverse(overlayRotation); + var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, overlayPosition)); + var dpi = Overlays.getProperty(overlayID, "dpi"); + + var dimensions; + if (dpi) { + // Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property + // is used as a scale. + var resolution = Overlays.getProperty(overlayID, "resolution"); + if (resolution === undefined) { + return; + } + resolution.z = 1; // Circumvent divide-by-zero. + var scale = Overlays.getProperty(overlayID, "dimensions"); + if (scale === undefined) { + return; + } + scale.z = 0.01; // overlay dimensions are 2D, not 3D. + dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); + } else { + dimensions = Overlays.getProperty(overlayID, "dimensions"); + if (dimensions === undefined) { + return; + } + if (!dimensions.z) { + dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D. + } + } + var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z }; + var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT); + + // 2D position on overlay plane in meters, relative to the bounding box upper-left hand corner. + var position2D = { + x: normalizedPosition.x * dimensions.x, + y: (1 - normalizedPosition.y) * dimensions.y // flip y-axis + }; + + return { + entityID: null, + overlayID: overlayID, + distance: distance, + position: position, + position2D: position2D, + normal: normal, + normalizedPosition: normalizedPosition, + dimensions: dimensions, + valid: true + }; +} + +// will return undefined if entity does not exist. +function calculateTouchTargetFromEntity(touchTip, props) { + if (props.rotation === undefined) { + // if rotation is missing from props object, then this entity has probably been deleted. + return; + } + + // project touch tip onto entity plane. + var normal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1}); + Vec3.multiplyQbyV(props.rotation, {x: 0, y: 1, z: 0}); + var distance = Vec3.dot(Vec3.subtract(touchTip.position, props.position), normal); + var position = Vec3.subtract(touchTip.position, Vec3.multiply(normal, distance)); + + // generate normalized coordinates + var invRot = Quat.inverse(props.rotation); + var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, props.position)); + var invDimensions = { x: 1 / props.dimensions.x, y: 1 / props.dimensions.y, z: 1 / props.dimensions.z }; + var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint); + + // 2D position on entity plane in meters, relative to the bounding box upper-left hand corner. + var position2D = { + x: normalizedPosition.x * props.dimensions.x, + y: (1 - normalizedPosition.y) * props.dimensions.y // flip y-axis + }; + + return { + entityID: props.id, + entityProps: props, + overlayID: null, + distance: distance, + position: position, + position2D: position2D, + normal: normal, + normalizedPosition: normalizedPosition, + dimensions: props.dimensions, + valid: true + }; +} + +module.exports = { + calculateTouchTargetFromEntity: calculateTouchTargetFromEntity, + calculateTouchTargetFromOverlay: calculateTouchTargetFromOverlay, + touchTargetHasKeyboardFocus: touchTargetHasKeyboardFocus, + setKeyboardFocusOnTouchTarget: setKeyboardFocusOnTouchTarget, + sendHoverEnterEventToTouchTarget: sendHoverEnterEventToTouchTarget, + sendHoverOverEventToTouchTarget: sendHoverOverEventToTouchTarget, + sendTouchStartEventToTouchTarget: sendTouchStartEventToTouchTarget, + sendTouchEndEventToTouchTarget: sendTouchEndEventToTouchTarget, + sendTouchMoveEventToTouchTarget: sendTouchMoveEventToTouchTarget, + composeTouchTargetFromIntersection: composeTouchTargetFromIntersection +};