diff --git a/HeadGazeLib/README.md b/HeadGazeLib/README.md
index 95da177..2d1c668 100644
--- a/HeadGazeLib/README.md
+++ b/HeadGazeLib/README.md
@@ -32,9 +32,16 @@ class MyViewController: UIHeadGazeViewController{
setupGestureRecognizer()
}
private func setupGestureRecognizer() {
+ // set button dwell duration
+ self.myButton.dwellDuration = 1 // in second
+
// add head gaze recognizer to handle head gaze event
self.headGazeRecognizer = UIHeadGazeRecognizer()
+
+ //Between [0,9]. Stablize the cursor reducing the wiggling noise.
+ //The higher the value the more smoothly the cursor moves.
super.virtualCursorView?.smoothness = 9
+
super.virtualCursorView?.addGestureRecognizer(headGazeRecognizer)
self.headGazeRecognizer?.move = { [weak self] gaze in
@@ -44,7 +51,12 @@ class MyViewController: UIHeadGazeViewController{
}
private func buttonAction(button: UIButton, gaze: UIHeadGaze){
guard let button = button as? UIHoverableButton else { return }
- button.hover(gaze: gaze)
+ // The button instance would trigger TouchUpInside event after user specified seconds
+ button.hover(gaze: gaze)
+ }
+
+ @IBAction func myBtnTouchUpInside(_ sender: UIBubbleButton) {
+ print("Button clicked by head gaze.")
}
//....
}
diff --git a/HeadGazeLib/UIHeadGazeViewController.swift b/HeadGazeLib/UIHeadGazeViewController.swift
index 3d696dd..9f7bd25 100644
--- a/HeadGazeLib/UIHeadGazeViewController.swift
+++ b/HeadGazeLib/UIHeadGazeViewController.swift
@@ -10,7 +10,7 @@ import ARKit
class UIHeadGazeViewController: UIHeadGazeViewControllerBase, ARSessionDelegate {
- private var sceneview: ARSCNView?
+ internal var sceneview: ARSCNView?
/**
An instance of UIVirtualCursorView that is responsible for visualizing the gaze location on the screen with a cursor/cross-hair.
*/
diff --git a/LICENSE.TXT b/LICENSE.TXT
index 2318d0c..8c3d10d 100644
--- a/LICENSE.TXT
+++ b/LICENSE.TXT
@@ -1,10 +1,9 @@
-The MIT License
-SPDX short identifier: MIT
+MIT License
-Copyright 2018 eBay Inc.
+Copyright (c) 2018 eBay
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
index 643afdb..df15ae9 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,11 @@


## What is the Project
-`HeadGazeLib` is a pure Swift library to empower iOS app control through head gaze without a finger touch.
+`HeadGazeLib` is a pure Swift library to empower iOS app control through head gaze without a finger touch.
+See the story behind it [here](https://www.ebayinc.com/stories/news/ebay-open-sources-technology-that-uses-head-motion-to-navigate-user-interface-on-iphone-x/)
+
+
+The above teaser is available at [example/demo](https://github.com/ebay/HeadGazeLib/tree/master/examples/demo).
## Who May Wish to Use the Project
Any iOS developer who would like to introduce head-based control to their app. Ideal for accessibility and game control.
@@ -21,7 +25,7 @@ class MyViewController: UIHeadGazeViewController{
```
3. Similarily, change the button class from `UIButton` to `UIHoverableButton` or `UIBubbleButton` in the identity inspector of storyboard
-4. Define head gaze even handler through `UIHeadGazeRecognizer` instance in `MyViewController` class
+4. Define head gaze event handler through `UIHeadGazeRecognizer` instance in `MyViewController` class
```swift
class MyViewController: UIHeadGazeViewController{
//.....
@@ -32,9 +36,16 @@ class MyViewController: UIHeadGazeViewController{
setupGestureRecognizer()
}
private func setupGestureRecognizer() {
+ // set button dwell duration
+ self.myButton.dwellDuration = 1 // in second
+
// add head gaze recognizer to handle head gaze event
self.headGazeRecognizer = UIHeadGazeRecognizer()
+
+ //Between [0,9]. Stablize the cursor reducing the wiggling noise.
+ //The higher the value the more smoothly the cursor moves.
super.virtualCursorView?.smoothness = 9
+
super.virtualCursorView?.addGestureRecognizer(headGazeRecognizer)
self.headGazeRecognizer?.move = { [weak self] gaze in
@@ -44,19 +55,41 @@ class MyViewController: UIHeadGazeViewController{
}
private func buttonAction(button: UIButton, gaze: UIHeadGaze){
guard let button = button as? UIHoverableButton else { return }
- button.hover(gaze: gaze)
+ // The button instance would trigger TouchUpInside event after user specified seconds
+ button.hover(gaze: gaze)
+ }
+
+ @IBAction func myBtnTouchUpInside(_ sender: UIBubbleButton) {
+ print("Button clicked by head gaze.")
}
//....
}
```
-For working demo, we have prepared two examples:
-1. HeadSwipe: Swipe daily deal on eBay with head control
-2. Sensitivity: Head control sensitivity analysis app is used to track the location and timestamp of the cursor as user is "clicking" the button. Useful for user study.
+For working demo, we have prepared three examples:
+1. demo: A simple jump start example showing how to empower a regular iOS app with head control with minimum code change.
+2. HeadSwipe: A more serious example - swipe daily deal on eBay with head control
+3. Sensitivity: An example on how to use `UIMultiFuncButton` to track the location and timestamp of the cursor as user is "clicking" the button. Useful for sensitivity analysis.
+
+## Citation
+
+If you find this work useful in your research, please consider citing:
+
+Cicek, Muratcan, Jinrong Xie, Qiaosong Wang, and Robinson Piramuthu. "Mobile Head Tracking for eCommerce and Beyond." arXiv preprint [arXiv:1812.07143 (2018)](http://arxiv.org/abs/1812.07143).
+
+BibTeX entry:
+```
+@article{cicek2018mobile,
+ title={Mobile Head Tracking for eCommerce and Beyond},
+ author={Cicek, Muratcan and Xie, Jinrong and Wang, Qiaosong and Piramuthu, Robinson},
+ journal={arXiv preprint arXiv:1812.07143},
+ year={2018}
+}
+```
## License
Copyright 2018 eBay Inc.
HeadGazeLib is available under the MIT license. See the LICENSE file for more info.
## Developers
-[Jinrong Xie](http://jinrongxie.net/), [Muratcan Cicek](https://users.soe.ucsc.edu/~cicekm/), Robinson Piramuthu
+[Jinrong Xie](http://jinrongxie.net/), [Muratcan Cicek](https://users.soe.ucsc.edu/~cicekm/), [Qiaosong Wang](https://github.com/qiaosongwang), [Robinson Piramuthu](https://github.com/rpiramuthu)
diff --git a/examples/HeadSwipe/README.md b/examples/HeadSwipe/README.md
index 7dc88cf..f991e09 100644
--- a/examples/HeadSwipe/README.md
+++ b/examples/HeadSwipe/README.md
@@ -1,7 +1,7 @@
# HeadSwipe
-
+

diff --git a/examples/HeadSwipeSensitivity/README.md b/examples/HeadSwipeSensitivity/README.md
index 31d5d99..b6cd16f 100644
--- a/examples/HeadSwipeSensitivity/README.md
+++ b/examples/HeadSwipeSensitivity/README.md
@@ -1,6 +1,6 @@
# HeadSwipeSensitivity
-
+
Experimental study of sensitivity analysis with respect to the distance between the head and mobile device.
diff --git a/examples/demo/.gitignore b/examples/demo/.gitignore
new file mode 100644
index 0000000..58ca57a
--- /dev/null
+++ b/examples/demo/.gitignore
@@ -0,0 +1,71 @@
+# Created by http://www.gitignore.io
+
+### OSX ###
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+
+### Swift ###
+# Xcode
+#
+build/
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata
+*.xccheckout
+*.moved-aside
+DerivedData
+*.hmap
+*.ipa
+*.xcuserstate
+
+# CocoaPods
+#
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
+#
+Podfile.lock
+Pods/
+
+
+### Xcode ###
+build/
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata
+*.xccheckout
+*.moved-aside
+DerivedData
+*.xcuserstate
diff --git a/examples/demo/README.md b/examples/demo/README.md
new file mode 100644
index 0000000..826d7f0
--- /dev/null
+++ b/examples/demo/README.md
@@ -0,0 +1,13 @@
+
+
+
+This simple demo gives you a jump start on how to use "HeadGazeLib" classes in your project.
+It illustrates three types of UI buttons that can switch between sensing head motion and being a regular touchable button.
+"Clicking" using head motion on these buttons trigger `TouchUpInside` event that can be captured by the same `@IBAction func` handler you might have defined earlier for regular UIButton, while moving the cursor away from the button triggers `TouchUpOutside` event.
+Currently, we have implemented three types of gazeable buttons plus a special button for statistic analysis:
+* UIHoverableButton: Expands its size upon cursor hovering, and triggers button click after a user specified hovering threshold in second. The cursor is required to leave the button before next click.
+* UIBubbleButton: Fills the background of the button in different colors to indicate the elapsed hovering time before click. The cursor is required to leave the button before next click
+* UIMultiFuncButton: Supports long gaze meaning that the cursor can keep hovering over the button to periodically click it without leaving the button. It triggers `TouchUpInside` event for the first click, and `TouchDownRepeat` event for subsequent clicks. For usage see example [HeadSwipe](https://github.com/eBay/HeadGazeLib/tree/master/examples/HeadSwipe).
+* UITrackButton: Hoverable button that can track cursor's time and location while it is inside the button. Designed for sensitivity analysis. For usage see example [HeadSwipeSensitivity](https://github.com/eBay/HeadGazeLib/tree/master/examples/HeadSwipeSensitivity).
+
+
diff --git a/examples/demo/demo.xcodeproj/project.pbxproj b/examples/demo/demo.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..f0912b0
--- /dev/null
+++ b/examples/demo/demo.xcodeproj/project.pbxproj
@@ -0,0 +1,424 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 50;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ DA537CE2214A29BD008673CC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA537CE1214A29BD008673CC /* AppDelegate.swift */; };
+ DA537CE4214A29BD008673CC /* art.scnassets in Resources */ = {isa = PBXBuildFile; fileRef = DA537CE3214A29BD008673CC /* art.scnassets */; };
+ DA537CE6214A29BD008673CC /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA537CE5214A29BD008673CC /* ViewController.swift */; };
+ DA537CE9214A29BD008673CC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DA537CE7214A29BD008673CC /* Main.storyboard */; };
+ DA537CEB214A29C0008673CC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA537CEA214A29C0008673CC /* Assets.xcassets */; };
+ DA537CEE214A29C0008673CC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DA537CEC214A29C0008673CC /* LaunchScreen.storyboard */; };
+ DA537D06214A2A26008673CC /* UIHeadGazeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA537CF6214A2A26008673CC /* UIHeadGazeViewController.swift */; };
+ DA537D07214A2A26008673CC /* UITrackButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA537CF7214A2A26008673CC /* UITrackButton.swift */; };
+ DA537D08214A2A26008673CC /* UIHoverableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA537CF8214A2A26008673CC /* UIHoverableButton.swift */; };
+ DA537D09214A2A26008673CC /* UIHeadGazeEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA537CF9214A2A26008673CC /* UIHeadGazeEvent.swift */; };
+ DA537D0A214A2A26008673CC /* UIMultiFuncButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA537CFA214A2A26008673CC /* UIMultiFuncButton.swift */; };
+ DA537D0B214A2A26008673CC /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = DA537CFB214A2A26008673CC /* README.md */; };
+ DA537D0C214A2A26008673CC /* Throttler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA537CFD214A2A26008673CC /* Throttler.swift */; };
+ DA537D0D214A2A26008673CC /* MathUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA537CFE214A2A26008673CC /* MathUtility.swift */; };
+ DA537D0E214A2A26008673CC /* ChargingThrottler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA537CFF214A2A26008673CC /* ChargingThrottler.swift */; };
+ DA537D0F214A2A26008673CC /* UIVirtualCursorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA537D00214A2A26008673CC /* UIVirtualCursorView.swift */; };
+ DA537D10214A2A26008673CC /* logo.png in Resources */ = {isa = PBXBuildFile; fileRef = DA537D01214A2A26008673CC /* logo.png */; };
+ DA537D11214A2A26008673CC /* UIHeadGazeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA537D02214A2A26008673CC /* UIHeadGazeView.swift */; };
+ DA537D12214A2A26008673CC /* UIBubbleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA537D03214A2A26008673CC /* UIBubbleButton.swift */; };
+ DA537D13214A2A26008673CC /* UIHeadGazeRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA537D04214A2A26008673CC /* UIHeadGazeRecognizer.swift */; };
+ DA537D14214A2A26008673CC /* UIHeadGazeViewControllerBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA537D05214A2A26008673CC /* UIHeadGazeViewControllerBase.swift */; };
+ DA537D16214A2DBE008673CC /* eBayColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA537D15214A2DBE008673CC /* eBayColor.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ DA537CDE214A29BD008673CC /* demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = demo.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ DA537CE1214A29BD008673CC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ DA537CE3214A29BD008673CC /* art.scnassets */ = {isa = PBXFileReference; lastKnownFileType = wrapper.scnassets; path = art.scnassets; sourceTree = ""; };
+ DA537CE5214A29BD008673CC /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
+ DA537CE8214A29BD008673CC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ DA537CEA214A29C0008673CC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ DA537CED214A29C0008673CC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ DA537CEF214A29C0008673CC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ DA537CF6214A2A26008673CC /* UIHeadGazeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIHeadGazeViewController.swift; sourceTree = ""; };
+ DA537CF7214A2A26008673CC /* UITrackButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITrackButton.swift; sourceTree = ""; };
+ DA537CF8214A2A26008673CC /* UIHoverableButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIHoverableButton.swift; sourceTree = ""; };
+ DA537CF9214A2A26008673CC /* UIHeadGazeEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIHeadGazeEvent.swift; sourceTree = ""; };
+ DA537CFA214A2A26008673CC /* UIMultiFuncButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIMultiFuncButton.swift; sourceTree = ""; };
+ DA537CFB214A2A26008673CC /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
+ DA537CFD214A2A26008673CC /* Throttler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Throttler.swift; sourceTree = ""; };
+ DA537CFE214A2A26008673CC /* MathUtility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MathUtility.swift; sourceTree = ""; };
+ DA537CFF214A2A26008673CC /* ChargingThrottler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChargingThrottler.swift; sourceTree = ""; };
+ DA537D00214A2A26008673CC /* UIVirtualCursorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIVirtualCursorView.swift; sourceTree = ""; };
+ DA537D01214A2A26008673CC /* logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo.png; sourceTree = ""; };
+ DA537D02214A2A26008673CC /* UIHeadGazeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIHeadGazeView.swift; sourceTree = ""; };
+ DA537D03214A2A26008673CC /* UIBubbleButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBubbleButton.swift; sourceTree = ""; };
+ DA537D04214A2A26008673CC /* UIHeadGazeRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIHeadGazeRecognizer.swift; sourceTree = ""; };
+ DA537D05214A2A26008673CC /* UIHeadGazeViewControllerBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIHeadGazeViewControllerBase.swift; sourceTree = ""; };
+ DA537D15214A2DBE008673CC /* eBayColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = eBayColor.swift; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ DA537CDB214A29BD008673CC /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ DA537CD5214A29BD008673CC = {
+ isa = PBXGroup;
+ children = (
+ DA537CE0214A29BD008673CC /* demo */,
+ DA537CDF214A29BD008673CC /* Products */,
+ );
+ sourceTree = "";
+ };
+ DA537CDF214A29BD008673CC /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ DA537CDE214A29BD008673CC /* demo.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ DA537CE0214A29BD008673CC /* demo */ = {
+ isa = PBXGroup;
+ children = (
+ DA537CF5214A2A26008673CC /* HeadGazeLib */,
+ DA537CE1214A29BD008673CC /* AppDelegate.swift */,
+ DA537CE5214A29BD008673CC /* ViewController.swift */,
+ DA537D15214A2DBE008673CC /* eBayColor.swift */,
+ DA537CE3214A29BD008673CC /* art.scnassets */,
+ DA537CE7214A29BD008673CC /* Main.storyboard */,
+ DA537CEA214A29C0008673CC /* Assets.xcassets */,
+ DA537CEC214A29C0008673CC /* LaunchScreen.storyboard */,
+ DA537CEF214A29C0008673CC /* Info.plist */,
+ );
+ path = demo;
+ sourceTree = "";
+ };
+ DA537CF5214A2A26008673CC /* HeadGazeLib */ = {
+ isa = PBXGroup;
+ children = (
+ DA537CFC214A2A26008673CC /* Utilities */,
+ DA537CF9214A2A26008673CC /* UIHeadGazeEvent.swift */,
+ DA537D04214A2A26008673CC /* UIHeadGazeRecognizer.swift */,
+ DA537D00214A2A26008673CC /* UIVirtualCursorView.swift */,
+ DA537CF6214A2A26008673CC /* UIHeadGazeViewController.swift */,
+ DA537D05214A2A26008673CC /* UIHeadGazeViewControllerBase.swift */,
+ DA537D02214A2A26008673CC /* UIHeadGazeView.swift */,
+ DA537CF8214A2A26008673CC /* UIHoverableButton.swift */,
+ DA537D03214A2A26008673CC /* UIBubbleButton.swift */,
+ DA537CFA214A2A26008673CC /* UIMultiFuncButton.swift */,
+ DA537CF7214A2A26008673CC /* UITrackButton.swift */,
+ DA537D01214A2A26008673CC /* logo.png */,
+ DA537CFB214A2A26008673CC /* README.md */,
+ );
+ name = HeadGazeLib;
+ path = ../../../HeadGazeLib;
+ sourceTree = "";
+ };
+ DA537CFC214A2A26008673CC /* Utilities */ = {
+ isa = PBXGroup;
+ children = (
+ DA537CFD214A2A26008673CC /* Throttler.swift */,
+ DA537CFE214A2A26008673CC /* MathUtility.swift */,
+ DA537CFF214A2A26008673CC /* ChargingThrottler.swift */,
+ );
+ path = Utilities;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ DA537CDD214A29BD008673CC /* demo */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DA537CF2214A29C0008673CC /* Build configuration list for PBXNativeTarget "demo" */;
+ buildPhases = (
+ DA537CDA214A29BD008673CC /* Sources */,
+ DA537CDB214A29BD008673CC /* Frameworks */,
+ DA537CDC214A29BD008673CC /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = demo;
+ productName = demo;
+ productReference = DA537CDE214A29BD008673CC /* demo.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ DA537CD6214A29BD008673CC /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 0940;
+ LastUpgradeCheck = 0940;
+ ORGANIZATIONNAME = "Xie,Jinrong";
+ TargetAttributes = {
+ DA537CDD214A29BD008673CC = {
+ CreatedOnToolsVersion = 9.4.1;
+ };
+ };
+ };
+ buildConfigurationList = DA537CD9214A29BD008673CC /* Build configuration list for PBXProject "demo" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = DA537CD5214A29BD008673CC;
+ productRefGroup = DA537CDF214A29BD008673CC /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ DA537CDD214A29BD008673CC /* demo */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ DA537CDC214A29BD008673CC /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA537CE4214A29BD008673CC /* art.scnassets in Resources */,
+ DA537CEE214A29C0008673CC /* LaunchScreen.storyboard in Resources */,
+ DA537D10214A2A26008673CC /* logo.png in Resources */,
+ DA537D0B214A2A26008673CC /* README.md in Resources */,
+ DA537CEB214A29C0008673CC /* Assets.xcassets in Resources */,
+ DA537CE9214A29BD008673CC /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ DA537CDA214A29BD008673CC /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA537D0A214A2A26008673CC /* UIMultiFuncButton.swift in Sources */,
+ DA537D06214A2A26008673CC /* UIHeadGazeViewController.swift in Sources */,
+ DA537D0F214A2A26008673CC /* UIVirtualCursorView.swift in Sources */,
+ DA537D0C214A2A26008673CC /* Throttler.swift in Sources */,
+ DA537D12214A2A26008673CC /* UIBubbleButton.swift in Sources */,
+ DA537D08214A2A26008673CC /* UIHoverableButton.swift in Sources */,
+ DA537D11214A2A26008673CC /* UIHeadGazeView.swift in Sources */,
+ DA537D0D214A2A26008673CC /* MathUtility.swift in Sources */,
+ DA537D13214A2A26008673CC /* UIHeadGazeRecognizer.swift in Sources */,
+ DA537D0E214A2A26008673CC /* ChargingThrottler.swift in Sources */,
+ DA537D16214A2DBE008673CC /* eBayColor.swift in Sources */,
+ DA537D07214A2A26008673CC /* UITrackButton.swift in Sources */,
+ DA537D14214A2A26008673CC /* UIHeadGazeViewControllerBase.swift in Sources */,
+ DA537D09214A2A26008673CC /* UIHeadGazeEvent.swift in Sources */,
+ DA537CE6214A29BD008673CC /* ViewController.swift in Sources */,
+ DA537CE2214A29BD008673CC /* AppDelegate.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ DA537CE7214A29BD008673CC /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ DA537CE8214A29BD008673CC /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ DA537CEC214A29C0008673CC /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ DA537CED214A29C0008673CC /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ DA537CF0214A29C0008673CC /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.4;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ DA537CF1214A29C0008673CC /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.4;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ DA537CF3214A29C0008673CC /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_TEAM = 877TC4Q5J5;
+ INFOPLIST_FILE = demo/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.ebay.HeadSwipe.demo;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 4.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ DA537CF4214A29C0008673CC /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_TEAM = 877TC4Q5J5;
+ INFOPLIST_FILE = demo/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.ebay.HeadSwipe.demo;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 4.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ DA537CD9214A29BD008673CC /* Build configuration list for PBXProject "demo" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DA537CF0214A29C0008673CC /* Debug */,
+ DA537CF1214A29C0008673CC /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DA537CF2214A29C0008673CC /* Build configuration list for PBXNativeTarget "demo" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DA537CF3214A29C0008673CC /* Debug */,
+ DA537CF4214A29C0008673CC /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = DA537CD6214A29BD008673CC /* Project object */;
+}
diff --git a/examples/demo/demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/demo/demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..e5aca88
--- /dev/null
+++ b/examples/demo/demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/examples/demo/demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/demo/demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/examples/demo/demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/examples/demo/demo/AppDelegate.swift b/examples/demo/demo/AppDelegate.swift
new file mode 100644
index 0000000..6dbdc49
--- /dev/null
+++ b/examples/demo/demo/AppDelegate.swift
@@ -0,0 +1,33 @@
+// Copyright 2018 eBay Inc.
+// Created by Xie,Jinrong on 9/12/18.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import UIKit
+import ARKit
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate {
+
+ var window: UIWindow?
+
+
+ func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
+ let storyBoard = UIStoryboard(name: "Main", bundle: nil)
+ let wnd = UIWindow(frame: UIScreen.main.bounds)
+
+ if ARFaceTrackingConfiguration.isSupported {
+ wnd.rootViewController = storyBoard.instantiateViewController(withIdentifier: "mainStoryBoard")
+ wnd.makeKeyAndVisible()
+ window = wnd
+ }else{
+ wnd.rootViewController = storyBoard.instantiateViewController(withIdentifier: "unsupportedDevice")
+ wnd.makeKeyAndVisible()
+ window = wnd
+ }
+ return true
+ }
+}
+
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100755
index 0000000..4062365
--- /dev/null
+++ b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,158 @@
+{
+ "images" : [
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "57x57",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-57x57@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "57x57",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-57x57@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "50x50",
+ "idiom" : "ipad",
+ "filename" : "Icon-Small-50x50@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "50x50",
+ "idiom" : "ipad",
+ "filename" : "Icon-Small-50x50@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "72x72",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-72x72@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "72x72",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-72x72@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-83.5x83.5@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "ItunesArtwork@2x.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 0000000..37882b2
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 0000000..d341640
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 0000000..e2ac1ec
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 0000000..9ec95af
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 0000000..b578492
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 0000000..bb6330d
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 0000000..d341640
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 0000000..057361b
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 0000000..8598560
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png
new file mode 100644
index 0000000..0f5f089
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png
new file mode 100644
index 0000000..f32ca60
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 0000000..8598560
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 0000000..9ebcdb4
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png
new file mode 100644
index 0000000..148211d
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png
new file mode 100644
index 0000000..d4f5319
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 0000000..d6015f7
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 0000000..5d917e3
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 0000000..7c2b24a
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png
new file mode 100644
index 0000000..b5dddb4
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png
new file mode 100644
index 0000000..95e8886
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png
new file mode 100644
index 0000000..31ff973
Binary files /dev/null and b/examples/demo/demo/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png differ
diff --git a/examples/demo/demo/Assets.xcassets/Contents.json b/examples/demo/demo/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/examples/demo/demo/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/demo/demo/Base.lproj/LaunchScreen.storyboard b/examples/demo/demo/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f83f6fd
--- /dev/null
+++ b/examples/demo/demo/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/demo/demo/Base.lproj/Main.storyboard b/examples/demo/demo/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..7567903
--- /dev/null
+++ b/examples/demo/demo/Base.lproj/Main.storyboard
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/demo/demo/Info.plist b/examples/demo/demo/Info.plist
new file mode 100644
index 0000000..3979c06
--- /dev/null
+++ b/examples/demo/demo/Info.plist
@@ -0,0 +1,48 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 0.1
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ NSCameraUsageDescription
+ This application will use the camera for Augmented Reality.
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UIRequiredDeviceCapabilities
+
+ armv7
+ arkit
+
+ UIStatusBarHidden
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+
+
diff --git a/examples/demo/demo/ViewController.swift b/examples/demo/demo/ViewController.swift
new file mode 100644
index 0000000..6557ec2
--- /dev/null
+++ b/examples/demo/demo/ViewController.swift
@@ -0,0 +1,228 @@
+// Copyright 2018 eBay Inc.
+// Created by Xie,Jinrong on 9/12/18.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import UIKit
+import SceneKit
+import ARKit
+
+class ViewController: UIHeadGazeViewController{
+
+ @IBOutlet weak var uiview: UIView!
+ @IBOutlet weak var bubbleButton: UIBubbleButton!
+ @IBOutlet weak var hoverButton: UIHoverableButton!
+ @IBOutlet weak var longGazeButton: UIMultiFuncButton!
+
+ @IBOutlet weak var headCtrSwitch: UISwitch!
+ @IBOutlet weak var deviceCtrSwitch: UISwitch!
+ @IBOutlet weak var headCtrLabel: UILabel!
+ @IBOutlet weak var deviceCtrLabel: UILabel!
+ @IBOutlet weak var xyLabel: UILabel!
+
+ @IBAction func switchDeviceControl(_ sender: UISwitch) {
+ super.virtualCursorView?.enableDeviceControl = sender.isOn
+ }
+
+ @IBAction func switchHeadControl(_ sender: UISwitch) {
+ self.uiview.removeFromSuperview()
+ self.deviceCtrSwitch.removeFromSuperview()
+ self.headCtrSwitch.removeFromSuperview()
+ super.virtualCursorView?.removeFromSuperview()
+ if sender.isOn {
+ // super.sceneview?.isHidden = false
+ self.headCtrLabel.text = "HeadSwipe: on"
+ super.view.addSubview(self.uiview)
+ self.view.addSubview(super.virtualCursorView!)
+ self.view.addSubview(self.deviceCtrSwitch)
+ self.view.addSubview(self.headCtrSwitch)
+ }else{
+ // super.sceneview?.isHidden = true
+ self.headCtrLabel.text = "HeadSwipe: off"
+ super.view.addSubview(self.uiview)
+ self.view.addSubview(self.deviceCtrSwitch)
+ self.view.addSubview(self.headCtrSwitch)
+ }
+ }
+
+ //--
+ @IBAction func bubbleBtnTouchDown(_ sender: UIBubbleButton) {
+ bubbleButton.setTitle("FingerTouched", for: .normal)
+ }
+
+ @IBAction func bubbleBtnTouchUpInside(_ sender: UIBubbleButton) {
+ if headCtrSwitch.isOn{// touch up by head gaze is interpreted as button click
+ print("bubble button clicked")
+ bubbleButton.setTitle("clicked", for: .normal)
+ }else{// touch triggered by finger
+ bubbleButton.setTitle("Bubble Button", for: .normal)
+ }
+ }
+
+ @IBAction func bubbleBtnTouchOutside(_ sender: UIBubbleButton) {
+ bubbleButton.setTitle("Bubble Button", for: .normal)
+ }
+
+ //----
+ @IBAction func hoverBtnTouchDown(_ sender: UIHoverableButton) {
+ hoverButton.setTitle("FingerTouched", for: .normal)
+ }
+
+ @IBAction func hoverBtnTouchUpInside(_ sender: UIHoverableButton) {
+ if headCtrSwitch.isOn{// touch up by head gaze is interpreted as button click
+ print("bubble button clicked")
+ hoverButton.setTitle("clicked", for: .normal)
+ }else{// touch triggered by finger
+ hoverButton.setTitle("Hoverable Button", for: .normal)
+ }
+ }
+
+ @IBAction func hoverBtnTouchOutside(_ sender: UIHoverableButton) {
+ hoverButton.setTitle("Hoverable Button", for: .normal)
+ }
+
+ private var clickCount: Int = 0
+ @IBAction func longGazeBtnTouchDown(_ sender: UIMultiFuncButton) {
+ longGazeButton.setTitle("FingerTouched", for: .normal)
+ }
+
+ @IBAction func longGazeBtnTouchDownRepeat(_ sender: UIMultiFuncButton) {
+ clickCount += 1
+ let title = String.init(format: "click count:%d", clickCount)
+ longGazeButton.setTitle(title, for: .normal)
+ }
+
+ @IBAction func longGazeBtnTouchUpInside(_ sender: UIMultiFuncButton) {
+ if headCtrSwitch.isOn{// touch up by head gaze is interpreted as button click
+ print("long gaze button clicked")
+ clickCount += 1
+ let title = String.init(format: "click count:%d", clickCount)
+ longGazeButton.setTitle(title, for: .normal)
+ }else{// touch triggered by finger
+ longGazeButton.setTitle("Long Gaze Button", for: .normal)
+ }
+ }
+
+ @IBAction func longGazeBtnTouchUpOutside(_ sender: UIMultiFuncButton) {
+ clickCount = 0
+ longGazeButton.setTitle("Long Gaze Button", for: .normal)
+ }
+
+ //--
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ super.sceneview?.isHidden = false
+ bubbleButton.backgroundColor = eBayColors.green
+ bubbleButton.dwellDuration = 1 // in second
+ hoverButton.dwellDuration = 1
+ longGazeButton.dwellDuration = 1
+ longGazeButton.longDwellDuration = 2
+
+ //add head gesture recognizer to handle head gaze event
+ //e.g. how the UI interface respond to the gaze movement, intersection test, etc.
+ let headGazeRecognizer = UIHeadGazeRecognizer()
+ super.virtualCursorView?.addGestureRecognizer(headGazeRecognizer)
+ headGazeRecognizer.move = { [weak self] gaze in
+ self?.buttonAction(gaze: gaze)
+ }
+
+ makecircularWithShadow(button: bubbleButton, name: "Bubble Button")
+ makecircularWithShadow(button: hoverButton, name: "Hoverable Button", masksToBounds: false)
+ makecircularWithShadow(button: longGazeButton, name: "Long Gaze Button")
+
+ blockFingerTouch(toggle: true, asMirror: true)
+
+ setupSceneNode()
+
+ deviceCtrSwitch.isHidden = true // set it to false if you want to experiment with device control e.g. tilt the device to change the cursor position
+ deviceCtrLabel.isHidden = true // set it to false if you want to experiment with device control e.g. tilt the device to change the cursor position
+ }
+
+ private func makecircularWithShadow(button: UIButton, name: String="untitled", masksToBounds: Bool = true) {
+ button.layer.cornerRadius = button.frame.height / 2
+ button.layer.masksToBounds = masksToBounds
+ button.layer.shadowColor = UIColor.black.cgColor
+ button.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
+ button.layer.shadowRadius = 1.0
+ button.layer.shadowOpacity = 0.5
+
+ guard let button = button as? UIHoverableButton else { return }
+ button.name = name
+ }
+
+ private func buttonAction(gaze: UIHeadGaze){
+ if headCtrSwitch.isOn {
+ self.bubbleButton.hover(gaze: gaze)
+ self.hoverButton.hover(gaze: gaze)
+ self.longGazeButton.hover(gaze: gaze)
+ let localCursorPos = gaze.location(in: self.uiview)
+ self.xyLabel.text = String.init(format: "(%.2f, %.2f)", localCursorPos.x, localCursorPos.y)
+ }
+ }
+
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+ UIApplication.shared.isIdleTimerDisabled = true
+ }
+ /**
+ @params toggle: true: enable head tracking and disable finger touch by overlaying virtual cursor view on top of uiview.
+ false: enable both
+ @params asMirror: display face on the screen (true) or not (false)
+ */
+ private func blockFingerTouch(toggle enableHeadCtr: Bool, asMirror showFace: Bool = true){
+
+ super.sceneview?.isHidden = !showFace
+ self.uiview.removeFromSuperview()
+ self.deviceCtrSwitch.removeFromSuperview()
+ self.headCtrSwitch.removeFromSuperview()
+ super.virtualCursorView?.removeFromSuperview()
+
+ //change the z-order of the UI widgets
+ if enableHeadCtr {
+ super.view.addSubview(self.uiview)
+ self.view.addSubview(super.virtualCursorView!)
+ self.view.addSubview(self.deviceCtrSwitch)
+ self.view.addSubview(self.headCtrSwitch)
+ }else{
+ self.view.addSubview(super.virtualCursorView!)
+ super.view.addSubview(self.uiview)
+ self.view.addSubview(self.deviceCtrSwitch)
+ self.view.addSubview(self.headCtrSwitch)
+ }
+ }
+
+ private var headNode: SCNNode?
+ private var headAnchor: ARFaceAnchor?
+
+ private let axesNode = loadModelFromAsset(named: "axes")
+
+ private func setupSceneNode(){
+ guard let node = headNode else { return }
+
+ node.addChildNode(axesNode)
+ }
+ // MARK: - ARSCNViewDelegate
+
+ /// - Tag: ARNodeTracking
+ override func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
+ self.headNode = node
+ setupSceneNode()
+ }
+
+ /// - Tag: ARFaceGeometryUpdate
+ override func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
+ super.renderer(renderer, didUpdate: node, for: anchor)
+// guard let faceAnchor = anchor as? ARFaceAnchor else { return }
+ }
+
+}
+
+func loadModelFromAsset(named assetName: String) -> SCNNode{
+ let url = Bundle.main.url(/service/forresource: assetName, withExtension: "scn", subdirectory: "art.scnassets")
+ let node = SCNReferenceNode(url: url!)
+ node?.load()
+ return node!
+}
diff --git a/examples/demo/demo/art.scnassets/axes.scn b/examples/demo/demo/art.scnassets/axes.scn
new file mode 100644
index 0000000..ec97617
Binary files /dev/null and b/examples/demo/demo/art.scnassets/axes.scn differ
diff --git a/examples/demo/demo/eBayColor.swift b/examples/demo/demo/eBayColor.swift
new file mode 100644
index 0000000..b79dfdc
--- /dev/null
+++ b/examples/demo/demo/eBayColor.swift
@@ -0,0 +1,14 @@
+// Copyright 2018 eBay Inc.
+// Architect/Developer: Jinrong Xie, Muratcan Cicek
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import UIKit
+final class eBayColors {
+ static let red = UIColor(red: 197.0/255.0, green: 71.0/255.0, blue: 60.0/255.0, alpha: 1.0)
+ static let blue = UIColor(red: 72.0/255.0, green: 114.0/255.0, blue: 244.0/255.0, alpha: 1.0)
+ static let yellow = UIColor(red: 234.0/255.0, green: 184.0/255.0, blue: 54.0/255.0, alpha: 1.0)
+ static let green = UIColor(red: 155.0/255.0, green: 188.0/255.0, blue: 53.0/255.0, alpha: 1.0)
+}
diff --git a/examples/demo/demo/video/teaser.gif b/examples/demo/demo/video/teaser.gif
new file mode 100644
index 0000000..3f57f4c
Binary files /dev/null and b/examples/demo/demo/video/teaser.gif differ