diff --git a/Assets/TouchKit/Recognizers/TKMFSwipeRecognizer.cs b/Assets/TouchKit/Recognizers/TKMFSwipeRecognizer.cs
new file mode 100644
index 0000000..0a31bec
--- /dev/null
+++ b/Assets/TouchKit/Recognizers/TKMFSwipeRecognizer.cs
@@ -0,0 +1,204 @@
+using UnityEngine;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+///
+/// Class used to track finger swipes. Supports multiple fingers
+///
+public class TKMFSwipe {
+ public List points = new List();
+ public float startTime;
+ public float swipeVelocity;
+ public TKSwipeDirection direction;
+
+ public Vector2 StartPoint {
+ get{ return points.FirstOrDefault(); }
+ }
+
+ public Vector2 EndPoint {
+ get { return points.LastOrDefault(); }
+ }
+
+ public Vector2 VectorDirection {
+ get { return (EndPoint - StartPoint).normalized; }
+ }
+}
+
+public class TKMFSwipeRecognizer : TKAbstractGestureRecognizer {
+ ///
+ /// The event that fires when a swipe is recognized.
+ ///
+ public event System.Action gestureRecognizedEvent;
+
+ ///
+ /// The maximum amount of time for the motion to be considered a swipe.
+ /// Setting to 0f will disable the time restriction completely.
+ ///
+ public float timeToSwipe = 0.5f;
+
+ ///
+ /// The maximum number of simultaneous touches (fingers) on the screen to trigger
+ /// this swipe recognizer. Default is 2.
+ ///
+ public int maximumNumberOfTouches = 2;
+
+ ///
+ /// If true, will trigger on the frame that the criteria for a swipe are first met.
+ /// If false, will only trigger on completion of the motion, when the touch is lifted.
+ ///
+ public bool triggerWhenCriteriaMet = true;
+
+
+ ///
+ /// The minimum distance in centimeters that the gesture has to make to be considered
+ /// a proper swipe, based on resolution and pixel density. Default is 2cm.
+ ///
+ private float _minimumDistance = 2f;
+
+ ///
+ /// A dictionary keyed on touches that represents the swipe data for each individual
+ /// finger one the screen
+ ///
+// private List fingers = new List();
+ private Dictionary fingers = new Dictionary();
+
+ public TKMFSwipeRecognizer() : this(2f)
+ { }
+
+ public TKMFSwipeRecognizer(float minimumDistanceCm)
+ {
+ this._minimumDistance = minimumDistanceCm;
+ }
+
+
+ private bool CheckForSwipeCompletion(TKTouch touch) {
+ //Grab the swipe tracking we're dealing with
+ TKMFSwipe swipe = fingers[touch];
+
+ // if we have a time stipulation and we exceeded it stop listening for swipes, fail
+ if (timeToSwipe > 0.0f && (Time.time - swipe.startTime) > timeToSwipe)
+ return false;
+
+ // if we don't have at least two points to test yet, then fail
+ if (this.fingers[touch].points.Count < 2)
+ return false;
+
+ // the ideal distance in pixels from the start to the finish
+ float idealDistance = Vector2.Distance(swipe.StartPoint, swipe.EndPoint);
+
+ // the ideal distance in centimeters, based on the screen pixel density
+ float idealDistanceCM = idealDistance / TouchKit.instance.ScreenPixelsPerCm;
+
+ // if the distance moved in cm was less than the minimum,
+ if (idealDistanceCM < this._minimumDistance)
+ return false;
+
+ // add up distances between all points sampled during the gesture to get the real distance
+ float realDistance = 0f;
+ for (int i = 1; i < this.fingers[touch].points.Count; i++)
+ realDistance += Vector2.Distance(this.fingers[touch].points[i], this.fingers[touch].points[i - 1]);
+
+ // if the real distance is 10% greater than the ideal distance, then fail
+ // this weeds out really irregular "lines" and curves from being considered swipes
+ if (realDistance > idealDistance * 1.1f)
+ return false;
+
+ // the speed in cm/s of the swipe
+ swipe.swipeVelocity = idealDistanceCM / (Time.time - swipe.startTime);
+
+ // turn the slope of the ideal swipe line into an angle in degrees
+ Vector2 v2 = (swipe.EndPoint - swipe.StartPoint).normalized;
+ float swipeAngle = Mathf.Atan2(v2.y, v2.x) * Mathf.Rad2Deg;
+ if (swipeAngle < 0)
+ swipeAngle = 360 + swipeAngle;
+ swipeAngle = 360 - swipeAngle;
+
+ // depending on the angle of the line, give a logical swipe direction
+ if (swipeAngle >= 292.5f && swipeAngle <= 337.5f)
+ swipe.direction = TKSwipeDirection.UpRight;
+ else if (swipeAngle >= 247.5f && swipeAngle <= 292.5f)
+ swipe.direction = TKSwipeDirection.Up;
+ else if (swipeAngle >= 202.5f && swipeAngle <= 247.5f)
+ swipe.direction = TKSwipeDirection.UpLeft;
+ else if (swipeAngle >= 157.5f && swipeAngle <= 202.5f)
+ swipe.direction = TKSwipeDirection.Left;
+ else if (swipeAngle >= 112.5f && swipeAngle <= 157.5f)
+ swipe.direction = TKSwipeDirection.DownLeft;
+ else if (swipeAngle >= 67.5f && swipeAngle <= 112.5f)
+ swipe.direction = TKSwipeDirection.Down;
+ else if (swipeAngle >= 22.5f && swipeAngle <= 67.5f)
+ swipe.direction = TKSwipeDirection.DownRight;
+ else // swipeAngle >= 337.5f || swipeAngle <= 22.5f
+ swipe.direction = TKSwipeDirection.Right;
+
+ return true;
+ }
+
+ internal override void fireRecognizedEvent () {}
+
+ //TODO call this manually
+ internal virtual void FireRecognizedEvent(TKTouch touch) {
+ //Remove tracking
+ _trackingTouches.Remove(touch);
+ TKMFSwipe swipingFinger = fingers[touch];
+ fingers.Remove(touch);
+
+ if (gestureRecognizedEvent != null)
+ gestureRecognizedEvent(swipingFinger);
+
+ }
+
+ internal override bool touchesBegan(List touches) {
+ foreach (var touch in touches)
+ if (fingers.Count < maximumNumberOfTouches) {
+ for (int i = 0; i < touches.Count; i++) {
+ if (!fingers.ContainsKey(touches[i])) {
+ this._trackingTouches.Add(touches[i]); //Add the touch to further tracking updates
+ Debug.Log("SWIPE Adding Touch " + touches[i].ToString());
+ fingers.Add(touches[i], new TKMFSwipe()); //Add the touch to internal swipe tracking
+ fingers[touches[i]].points.Add(touches[i].position); //Update position
+ fingers[touches[i]].startTime = Time.time; //timestamp
+ }
+ }
+ state = TKGestureRecognizerState.Began;
+ return true;
+ }
+ return false;
+ }
+
+ internal override void touchesMoved(List touches) {
+ for (int i = 0; i < touches.Count; i++) {
+ //Update points
+ fingers[touches[i]].points.Add(touches[i].position);
+
+ // if we're triggering when the criteria is met, then check for completion every frame
+ if (triggerWhenCriteriaMet && CheckForSwipeCompletion(touches[i])) {
+ FireRecognizedEvent(touches[i]);
+ state = TKGestureRecognizerState.RecognizedAndStillRecognizing;
+ }
+ }
+ }
+
+ internal override void touchesEnded(List touches) {
+ for (int i = 0; i < touches.Count; i++) {
+ if (!fingers.ContainsKey(touches[i]))
+ continue;
+
+ //Update points
+ fingers[touches[i]].points.Add(touches[i].position);
+
+ // last frame, one last check for recognition
+ if (CheckForSwipeCompletion(touches[i])) {
+ FireRecognizedEvent(touches[i]);
+ state = TKGestureRecognizerState.RecognizedAndStillRecognizing;
+ }
+
+ Debug.Log("SWIPE Removing Touch " + touches[i].ToString());
+
+ //Remove from touch tracking
+ fingers.Remove(touches[i]);
+ _trackingTouches.Remove(touches[i]);
+ }
+ }
+}
diff --git a/Assets/TouchKit/Recognizers/TKMFSwipeRecognizer.cs.meta b/Assets/TouchKit/Recognizers/TKMFSwipeRecognizer.cs.meta
new file mode 100644
index 0000000..2342f4e
--- /dev/null
+++ b/Assets/TouchKit/Recognizers/TKMFSwipeRecognizer.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: fbce7135fc90b4a39af12251e5b1c1e7
+timeCreated: 1498593370
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TouchKit/Recognizers/TKTriggerRecognizer.cs b/Assets/TouchKit/Recognizers/TKTriggerRecognizer.cs
new file mode 100644
index 0000000..7aaaa1b
--- /dev/null
+++ b/Assets/TouchKit/Recognizers/TKTriggerRecognizer.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+///
+/// Tracks a single touch that starts within a given rect until that touch ends
+///
+public class TKTriggerRecognizer : TKAbstractGestureRecognizer {
+ public event Action OnTriggerEvent;
+ public event Action OnTouchMovedEvent;
+ public event Action OnTouchEnd;
+
+ public TKTouch touch {
+ get { return _trackingTouches[0]; }
+ }
+
+ public TKTriggerRecognizer (TKRect _bounds) {
+ boundaryFrame = _bounds;
+ }
+
+ #region implement TKAbstractGestureRecognizer
+ internal override void fireRecognizedEvent () {}
+
+ internal override bool touchesBegan (List touches) {
+ if (_trackingTouches.Count == 0) {
+ for( int i = 0; i < touches.Count; i++ ) {
+ if (boundaryFrame.Value.contains(touches[i].position)) {
+ _trackingTouches.Add(touches[i]);
+ Debug.Log("TRIGGER Adding touchID " + touches[i].ToString());
+ if (OnTriggerEvent != null)
+ OnTriggerEvent(touch);
+
+ state = TKGestureRecognizerState.Began;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ internal override void touchesMoved (List touches) {
+ if (OnTouchMovedEvent != null)
+ OnTouchMovedEvent(touch);
+
+ state = TKGestureRecognizerState.RecognizedAndStillRecognizing;
+ }
+
+ internal override void touchesEnded (List touches) {
+ //FIXME I believe this is masking a bug lower in tocuhkit where a touch not applicable to this is coming in
+ for( int i = 0; i < touches.Count; i++ ) {
+// if (touches[i] != touch)
+// continue;
+
+ Debug.Log("TRIGGER Ending touchID " + touches[i].ToString());
+ if (OnTouchEnd != null)
+ OnTouchEnd(touch);
+
+ state = TKGestureRecognizerState.FailedOrEnded;
+ }
+ }
+ #endregion
+}
diff --git a/Assets/TouchKit/Recognizers/TKTriggerRecognizer.cs.meta b/Assets/TouchKit/Recognizers/TKTriggerRecognizer.cs.meta
new file mode 100644
index 0000000..3077f2a
--- /dev/null
+++ b/Assets/TouchKit/Recognizers/TKTriggerRecognizer.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 0ced92d6ee7144a68b21d5a62267c8fa
+timeCreated: 1498593360
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: