public async Task Init() { // Step1: Define the Rock-Paper-Scissors gestures // Create a pose for 'Rock'... var rockPose = new HandPose("RockPose", new FingerPose(new AllFingersContext(), FingerFlexion.Folded)); rockPose.Triggered += (s, arg) => UserStrategyChanged?.Invoke(GameStrategy.Rock); // ...another for 'Paper'... var paperPose = new HandPose("PaperPose", new PalmPose(new AnyHandContext(), PoseDirection.Left | PoseDirection.Right, PoseDirection.Forward), new FingerPose(new AllFingersContext(), FingerFlexion.Open)); paperPose.Triggered += (s, arg) => UserStrategyChanged?.Invoke(GameStrategy.Paper); // ...and last one for 'Scissors'... var scissorsPose = new HandPose("ScissorsPose", new FingerPose(new[] { Finger.Index, Finger.Middle }, FingerFlexion.Open), new FingertipDistanceRelation(Finger.Index, RelativeDistance.NotTouching, Finger.Middle), new FingerPose(new[] { Finger.Ring, Finger.Pinky }, FingerFlexion.Folded)); scissorsPose.Triggered += (s, arg) => UserStrategyChanged?.Invoke(GameStrategy.Scissors); // ...a PassThroughtGestureSegment is a structural gesture segment that provides a way to simplify a gesture state machine construction by 'short-circuiting' // between gesture segments connectd to it and gesture segements it connects to. It helps reduce the number of SubPaths that needs to be defined. // Very handy when you need to define a Clique (see https://en.wikipedia.org/wiki/Clique_(graph_theory)#1) // as in this case where Rock, Paper and Scissors are all connected to each other... var epsilonState = new PassThroughGestureSegment("Epsilon"); // ...this pose is an artificial stop pose. Namely, we want to keep the gesture detector in one of the pose states without ending the gesture so we add this // pose as a pose that completes the gesture assuming the user will not perform it frequently. // As long as the user continues to perform the 'Rock', 'Paper' or 'Scissors' poses we will remain within the gesture's state machine. var giveUpPose = new HandPose("GiveUpPose", new PalmPose(new AnyHandContext(), PoseDirection.Forward, PoseDirection.Up), new FingerPose(new AllFingersContext(), FingerFlexion.Open)); _gameGesture = new Gesture("RockPaperScissorGesture", epsilonState, giveUpPose); // ...add a sub path back and forth from the PassthroughGestureSegment to the various poses _gameGesture.AddSubPath(epsilonState, rockPose, epsilonState); _gameGesture.AddSubPath(epsilonState, paperPose, epsilonState); _gameGesture.AddSubPath(epsilonState, scissorsPose, epsilonState); // In case the user performs a pose that is not one of the game poses the gesture resets and this event will trigger _gameGesture.IdleTriggered += (s, arg) => UserStrategyChanged?.Invoke(GameStrategy.None); // Step2: Connect to Gesture Detection Service, route StatusChanged event to the UI and register the gesture _gesturesService = GesturesServiceEndpointFactory.Create(); _gesturesService.StatusChanged += (oldStatus, newStatus) => GesturesDetectionStatusChanged?.Invoke(oldStatus, newStatus); await _gesturesService.ConnectAsync(); await _gesturesService.RegisterGesture(_gameGesture); }
public async Task Init() { // Set the gesture service _gesturesService = GesturesServiceEndpointFactory.Create("localhost"); _gesturesService.StatusChanged += (oldVal, newVal) => GesturesDetectionStatusChanged?.Invoke(oldVal, newVal); await _gesturesService.ConnectAsync(); var hurufI = new HandPose( "Huruf_I", new PalmPose(Hand.RightHand, PoseDirection.Up | PoseDirection.Forward), new FingerPose(new[] { Finger.Pinky }, FingerFlexion.Open), new FingerPose(new[] { Finger.Index, Finger.Ring, Finger.Middle }, FingerFlexion.Folded)); hurufI.Triggered += (s, arg) => GestureChanged?.Invoke(arg.GestureSegment.Name); var piece = new HandPose( "Piece", new PalmPose(Hand.RightHand, PoseDirection.Up | PoseDirection.Forward), new FingerPose(new[] { Finger.Index, Finger.Middle }, FingerFlexion.Open), new FingerPose(new[] { Finger.Pinky, Finger.Ring, }, FingerFlexion.Folded)); piece.Triggered += (s, arg) => GestureChanged?.Invoke(arg.GestureSegment.Name); var baik = new HandPose( "baik", new PalmPose(Hand.RightHand, PoseDirection.Left | PoseDirection.Forward), new FingerPose(new[] { Finger.Ring, Finger.Pinky, Finger.Middle }, FingerFlexion.OpenStretched), new FingerPose(new[] { Finger.Index, Finger.Thumb, }, FingerFlexion.Folded)); baik.Triggered += (s, arg) => GestureChanged?.Invoke(arg.GestureSegment.Name); var alphabetGesture = new PassThroughGestureSegment("performing_state"); _detectedGesture = new Gesture("Sample_Gesture", alphabetGesture); _detectedGesture.AddSubPath(alphabetGesture, hurufI, alphabetGesture); _detectedGesture.AddSubPath(alphabetGesture, piece, alphabetGesture); _detectedGesture.AddSubPath(alphabetGesture, baik, alphabetGesture); await _gesturesService.RegisterGesture(_detectedGesture, isGlobal : true); await RegisterLikeGesture(); await RegisterThankYouGesture(); }
private async void WindowLoaded(object sender, RoutedEventArgs e) { // Step 1: Connect to Microsoft Gestures service _gesturesService = GesturesServiceEndpointFactory.Create(); _gesturesService.StatusChanged += (s, args) => Dispatcher.Invoke(() => GeturesServiceStatus.Text = $"[{args.Status}]"); Closed += (s, args) => _gesturesService?.Dispose(); await _gesturesService.ConnectAsync(); // Step 2: Define the RewindGesture gesture as follows: // ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ // │ │ │ │ │ │ │ │ │ │ │ │ │ │ // │ Idle │ -> │ Spread │ -> │ Pause │ -> │ Rewind │ -> │KeepRewind│ -> │ Release │ -> │ Idle │ // │ │ │(unpinch) │ │ (pinch) │ │ (left) │ │ (pinch) │ │(unpinch) │ │ │ // └──────────┘ └──────────┘ └────┬─────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ // │ ^ // └───────────────────────────────────────────────┘ // // Whenever the gesture returns to the Idle state, playback will resume // var spreadPose = GeneratePinchPose("Spread", true); var pausePose = GeneratePinchPose("Pause"); pausePose.Triggered += (s, args) => Dispatcher.Invoke(() => VideoStatus.Text = "⏸"); var rewindMotion = new HandMotion("Rewind", new PalmMotion(VerticalMotionSegment.Left)); rewindMotion.Triggered += (s, args) => Dispatcher.Invoke(() => VideoStatus.Text = "⏪"); var keepRewindingPose = GeneratePinchPose("KeepRewind"); var releasePose = GeneratePinchPose("Release", true); // Then define the gesture by concatenating the previous objects to form a simple state machine _rewindGesture = new Gesture("RewindGesture", spreadPose, pausePose, rewindMotion, keepRewindingPose, releasePose); // Detect if the user releases the pinch-grab hold in order to resume the playback _rewindGesture.AddSubPath(pausePose, releasePose); // Continue playing the video when the gesture resets (either successful or aborted) _rewindGesture.Triggered += (s, args) => Dispatcher.Invoke(() => VideoStatus.Text = "▶"); _rewindGesture.IdleTriggered += (s, args) => Dispatcher.Invoke(() => VideoStatus.Text = "▶"); // Step 3: Register the gesture (When window focus is lost (gained) the service will automatically unregister (register) the gesture) // To manually control the gesture registration, pass 'isGlobal: true' parameter in the function call below await _gesturesService.RegisterGesture(_rewindGesture); }