1
+ /*
2
+ * FXGL - JavaFX Game Library. The MIT License (MIT).
3
+ * Copyright (c) AlmasB (almaslvl@gmail.com).
4
+ * See LICENSE for details.
5
+ */
6
+
7
+ package com.almasb.fxgl.gesturerecog
8
+
9
+ import com.almasb.fxgl.core.EngineService
10
+ import com.almasb.fxgl.core.concurrent.Async
11
+ import com.almasb.fxgl.intelligence.WebAPI
12
+ import com.almasb.fxgl.logging.Logger
13
+ import com.almasb.fxgl.speechrecog.SpeechRecognitionService
14
+ import com.almasb.fxgl.ws.LocalWebSocketServer
15
+ import javafx.geometry.Point3D
16
+ import org.openqa.selenium.WebDriver
17
+ import org.openqa.selenium.chrome.ChromeDriver
18
+ import org.openqa.selenium.chrome.ChromeOptions
19
+ import java.util.function.Consumer
20
+
21
+ /* *
22
+ * TODO: remove duplicate code
23
+ *
24
+ * @author Almas Baim (https://github.com/AlmasB)
25
+ */
26
+ class HandTrackingService : EngineService () {
27
+
28
+ private val log = Logger .get(SpeechRecognitionService ::class .java)
29
+ private val server = LocalWebSocketServer (" HandTrackingServer" , WebAPI .GESTURE_RECOGNITION_PORT )
30
+
31
+ private var webDriver: WebDriver ? = null
32
+
33
+ private val handDataHandlers = arrayListOf<Consumer <Hand >>()
34
+
35
+ override fun onInit () {
36
+ server.addMessageHandler { message ->
37
+ try {
38
+ val rawData = message.split(" ," ).filter { it.isNotEmpty() }
39
+
40
+ val id = rawData[0 ].toInt()
41
+ val points = ArrayList <Point3D >()
42
+
43
+ var i = 1
44
+ while (i < rawData.size) {
45
+ val x = rawData[i + 0 ].toDouble()
46
+ val y = rawData[i + 1 ].toDouble()
47
+ val z = rawData[i + 2 ].toDouble()
48
+
49
+ points.add(Point3D (x, y, z))
50
+
51
+ i + = 3
52
+ }
53
+
54
+ Async .startAsyncFX {
55
+ handDataHandlers.forEach { it.accept(Hand (id, points)) }
56
+ }
57
+
58
+ } catch (e: Exception ) {
59
+ log.warning(" Failed to parse message." , e)
60
+ }
61
+ }
62
+
63
+ server.start()
64
+ }
65
+
66
+ /* *
67
+ * Starts this service in a background thread.
68
+ * Can be called after stop() to restart the service.
69
+ * If the service has already started, then calls stop() and restarts it.
70
+ */
71
+ fun start () {
72
+ Async .startAsync {
73
+ try {
74
+ if (webDriver != null ) {
75
+ stop()
76
+ }
77
+
78
+ val options = ChromeOptions ()
79
+ options.addArguments(" --headless=new" )
80
+ options.addArguments(" --use-fake-ui-for-media-stream" )
81
+
82
+ webDriver = ChromeDriver (options)
83
+ webDriver!! .get(WebAPI .GESTURE_RECOGNITION_API )
84
+
85
+ // we are ready to use the web api service
86
+ } catch (e: Exception ) {
87
+ log.warning(" Failed to start Chrome web driver. Ensure Chrome is installed in default location" )
88
+ log.warning(" Error data" , e)
89
+ }
90
+ }
91
+ }
92
+
93
+ /* *
94
+ * Stops this service.
95
+ * No-op if it has not started via start() before.
96
+ */
97
+ fun stop () {
98
+ try {
99
+ if (webDriver != null ) {
100
+ webDriver!! .quit()
101
+ webDriver = null
102
+ }
103
+ } catch (e: Exception ) {
104
+ log.warning(" Failed to quit web driver" , e)
105
+ }
106
+ }
107
+
108
+ fun addInputHandler (handler : Consumer <Hand >) {
109
+ handDataHandlers + = handler
110
+ }
111
+
112
+ fun removeInputHandler (handler : Consumer <Hand >) {
113
+ handDataHandlers - = handler
114
+ }
115
+
116
+ override fun onExit () {
117
+ stop()
118
+ server.stop()
119
+ }
120
+ }
0 commit comments