@lovelace_lol/polymer 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +226 -0
- package/dist/index.d.ts +2603 -0
- package/dist/index.js +1031 -0
- package/dist/test.cjs +1519 -0
- package/dist/worker/agency-worker.js +972 -0
- package/dist/worker/manifest.edn +1 -0
- package/package.json +57 -0
- package/shadow-cljs.edn +173 -0
- package/src/polymer/animation/agency.cljs +84 -0
- package/src/polymer/animation/domain.cljs +110 -0
- package/src/polymer/animation/planner.cljs +88 -0
- package/src/polymer/animation/runtime.cljs +137 -0
- package/src/polymer/animation/scheduler.cljs +178 -0
- package/src/polymer/animation/service.cljs +981 -0
- package/src/polymer/animation/state.cljs +112 -0
- package/src/polymer/blink/agency.cljs +131 -0
- package/src/polymer/blink/planner.cljs +115 -0
- package/src/polymer/blink/scheduler.cljs +90 -0
- package/src/polymer/blink/snippet.cljs +50 -0
- package/src/polymer/blink/state.cljs +102 -0
- package/src/polymer/camera_context/agency.cljs +132 -0
- package/src/polymer/camera_context/domain.cljs +186 -0
- package/src/polymer/camera_context/planner.cljs +50 -0
- package/src/polymer/camera_context/scheduler.cljs +113 -0
- package/src/polymer/camera_context/state.cljs +121 -0
- package/src/polymer/character.cljs +471 -0
- package/src/polymer/conversation/agency.cljs +83 -0
- package/src/polymer/conversation/domain.cljs +35 -0
- package/src/polymer/conversation/effector.cljs +130 -0
- package/src/polymer/conversation/planner.cljs +178 -0
- package/src/polymer/conversation/scheduler.cljs +202 -0
- package/src/polymer/conversation/service.cljs +329 -0
- package/src/polymer/conversation/state.cljs +186 -0
- package/src/polymer/core.cljs +172 -0
- package/src/polymer/eye_head/agency.cljs +75 -0
- package/src/polymer/eye_head/domain.cljs +133 -0
- package/src/polymer/eye_head/goap.cljs +106 -0
- package/src/polymer/eye_head/scheduler.cljs +176 -0
- package/src/polymer/eye_head/service.cljs +259 -0
- package/src/polymer/eye_head/snippet.cljs +92 -0
- package/src/polymer/eye_head/state.cljs +167 -0
- package/src/polymer/gaze/agency.cljs +85 -0
- package/src/polymer/gaze/domain.cljs +211 -0
- package/src/polymer/gaze/goap.cljs +208 -0
- package/src/polymer/gaze/scheduler.cljs +184 -0
- package/src/polymer/gaze/state.cljs +202 -0
- package/src/polymer/gesture/agency.cljs +185 -0
- package/src/polymer/gesture/goap.cljs +461 -0
- package/src/polymer/gesture/scheduler.cljs +122 -0
- package/src/polymer/gesture/snippet.cljs +198 -0
- package/src/polymer/gesture/state.cljs +273 -0
- package/src/polymer/lipsync/agency.cljs +593 -0
- package/src/polymer/lipsync/articulation/jaw.cljs +258 -0
- package/src/polymer/lipsync/articulation/snippet.cljs +605 -0
- package/src/polymer/lipsync/articulation/tongue.cljs +418 -0
- package/src/polymer/lipsync/articulation/visemes.cljs +429 -0
- package/src/polymer/lipsync/goap.cljs +132 -0
- package/src/polymer/lipsync/pronunciation/effector.cljs +51 -0
- package/src/polymer/lipsync/scheduler.cljs +302 -0
- package/src/polymer/lipsync/state.cljs +202 -0
- package/src/polymer/nlp/phonemes.cljs +164 -0
- package/src/polymer/prosodic/agency.cljs +196 -0
- package/src/polymer/prosodic/goap.cljs +122 -0
- package/src/polymer/prosodic/scheduler.cljs +99 -0
- package/src/polymer/prosodic/snippet.cljs +106 -0
- package/src/polymer/prosodic/state.cljs +139 -0
- package/src/polymer/runtime/embody.cljs +117 -0
- package/src/polymer/runtime/embody_wasm.cljs +177 -0
- package/src/polymer/runtime/rust_embody_host.cljs +645 -0
- package/src/polymer/society.cljs +239 -0
- package/src/polymer/stream.cljs +130 -0
- package/src/polymer/transcription/agency.cljs +69 -0
- package/src/polymer/transcription/domain.cljs +68 -0
- package/src/polymer/transcription/effector.cljs +611 -0
- package/src/polymer/transcription/planner.cljs +203 -0
- package/src/polymer/transcription/scheduler.cljs +225 -0
- package/src/polymer/transcription/service.cljs +209 -0
- package/src/polymer/transcription/state.cljs +239 -0
- package/src/polymer/tts/agency.cljs +503 -0
- package/src/polymer/tts/azure.cljs +290 -0
- package/src/polymer/tts/planner.cljs +178 -0
- package/src/polymer/tts/runtime.cljs +624 -0
- package/src/polymer/tts/scheduler.cljs +162 -0
- package/src/polymer/tts/state.cljs +191 -0
- package/src/polymer/worker.cljs +70 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
(ns polymer.eye-head.agency
|
|
2
|
+
(:require [polymer.eye-head.domain :as domain]
|
|
3
|
+
[polymer.eye-head.goap :as goap]
|
|
4
|
+
[polymer.eye-head.scheduler :as scheduler]
|
|
5
|
+
[polymer.eye-head.state :as state]
|
|
6
|
+
[polymer.stream :as stream]))
|
|
7
|
+
|
|
8
|
+
;; Eye/Head owns synchronized gaze movement. Peer agencies publish look
|
|
9
|
+
;; requests; this agency plans and schedules eye/head AU snippets for Animation.
|
|
10
|
+
|
|
11
|
+
(defn now-ms
|
|
12
|
+
[]
|
|
13
|
+
(.now js/Date))
|
|
14
|
+
|
|
15
|
+
(defn create-eye-head-tracking-agency
|
|
16
|
+
[config]
|
|
17
|
+
(let [input-stream (stream/create-stream)
|
|
18
|
+
event-stream (stream/create-stream)
|
|
19
|
+
state-atom (atom (state/config->state config))
|
|
20
|
+
disposed? (atom false)
|
|
21
|
+
emit-input (:emit input-stream)
|
|
22
|
+
emit-event (:emit event-stream)
|
|
23
|
+
scheduler-atom (atom nil)]
|
|
24
|
+
(letfn [(dispatch! [command]
|
|
25
|
+
(when-not @disposed?
|
|
26
|
+
(let [payload (domain/data-map command)
|
|
27
|
+
plan (goap/plan-command payload @state-atom (now-ms))]
|
|
28
|
+
(emit-input {:type "command"
|
|
29
|
+
:agency "eyeHeadTracking"
|
|
30
|
+
:command payload})
|
|
31
|
+
(when-let [agency-scheduler @scheduler-atom]
|
|
32
|
+
((:schedule agency-scheduler) plan)))))
|
|
33
|
+
|
|
34
|
+
(dispatch-type! [type]
|
|
35
|
+
(dispatch! #js {:type type}))]
|
|
36
|
+
(let [agency-scheduler (scheduler/create-scheduler
|
|
37
|
+
{:state-atom state-atom
|
|
38
|
+
:emit-event emit-event})]
|
|
39
|
+
(reset! scheduler-atom agency-scheduler)
|
|
40
|
+
#js {:dispatch dispatch!
|
|
41
|
+
:snapshot (fn [] (state/visible-state @state-atom))
|
|
42
|
+
:input (stream/writable-port input-stream dispatch!)
|
|
43
|
+
:events (stream/readable-port event-stream)
|
|
44
|
+
:subscribeInput (fn [listener] ((:subscribe input-stream) listener))
|
|
45
|
+
:subscribeEvents (fn [listener] ((:subscribe event-stream) listener))
|
|
46
|
+
:subscribe (fn [listener] ((:subscribe event-stream) listener))
|
|
47
|
+
:subscribeStatus (fn [listener] ((:subscribe event-stream) listener))
|
|
48
|
+
:setTarget (fn [target]
|
|
49
|
+
(dispatch! #js {:type "setTarget"
|
|
50
|
+
:target target}))
|
|
51
|
+
:configure (fn [config]
|
|
52
|
+
(dispatch! #js {:type "configure"
|
|
53
|
+
:config config}))
|
|
54
|
+
:enable (fn [] (dispatch-type! "enable"))
|
|
55
|
+
:disable (fn [] (dispatch-type! "disable"))
|
|
56
|
+
:reset (fn
|
|
57
|
+
([] (dispatch-type! "reset"))
|
|
58
|
+
([duration-ms] (dispatch! #js {:type "reset"
|
|
59
|
+
:durationMs duration-ms})))
|
|
60
|
+
:cancel (fn
|
|
61
|
+
([] (dispatch-type! "cancel"))
|
|
62
|
+
([reason] (dispatch! #js {:type "cancel"
|
|
63
|
+
:reason reason})))
|
|
64
|
+
:flush (fn []
|
|
65
|
+
(when-let [agency-scheduler @scheduler-atom]
|
|
66
|
+
((:flush agency-scheduler))))
|
|
67
|
+
:queue (fn []
|
|
68
|
+
(when-let [agency-scheduler @scheduler-atom]
|
|
69
|
+
(clj->js ((:queue agency-scheduler)))))
|
|
70
|
+
:dispose (fn []
|
|
71
|
+
(when-not @disposed?
|
|
72
|
+
(reset! disposed? true)
|
|
73
|
+
((:dispose agency-scheduler))
|
|
74
|
+
((:dispose input-stream))
|
|
75
|
+
((:dispose event-stream))))}))))
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
(ns polymer.eye-head.domain)
|
|
2
|
+
|
|
3
|
+
;; Eye/Head domain code normalizes incoming gaze requests and configuration.
|
|
4
|
+
;; It stays pure so movement policy can be tested without any animation runtime.
|
|
5
|
+
|
|
6
|
+
(def zero-target {:x 0 :y 0 :z 0})
|
|
7
|
+
|
|
8
|
+
(defn finite-number?
|
|
9
|
+
[value]
|
|
10
|
+
(and (number? value) (js/isFinite value)))
|
|
11
|
+
|
|
12
|
+
(defn number-or
|
|
13
|
+
[value fallback]
|
|
14
|
+
(if (finite-number? value) value fallback))
|
|
15
|
+
|
|
16
|
+
(defn clamp
|
|
17
|
+
[lo hi value]
|
|
18
|
+
(min hi (max lo value)))
|
|
19
|
+
|
|
20
|
+
(defn data-map
|
|
21
|
+
[value]
|
|
22
|
+
(cond
|
|
23
|
+
(map? value) value
|
|
24
|
+
value (js->clj value :keywordize-keys true)
|
|
25
|
+
:else {}))
|
|
26
|
+
|
|
27
|
+
(defn normalize-target
|
|
28
|
+
([value] (normalize-target value zero-target))
|
|
29
|
+
([value fallback]
|
|
30
|
+
(let [input (data-map value)
|
|
31
|
+
fallback (merge zero-target fallback)]
|
|
32
|
+
{:x (clamp -1 1 (number-or (:x input) (:x fallback)))
|
|
33
|
+
:y (clamp -1 1 (number-or (:y input) (:y fallback)))
|
|
34
|
+
:z (number-or (:z input) (:z fallback))})))
|
|
35
|
+
|
|
36
|
+
(defn request-id
|
|
37
|
+
[prefix now-ms]
|
|
38
|
+
(str prefix ":" now-ms ":" (rand-int 1000000)))
|
|
39
|
+
|
|
40
|
+
(defn requested-target
|
|
41
|
+
[command]
|
|
42
|
+
(let [payload (data-map command)]
|
|
43
|
+
(or (:target payload)
|
|
44
|
+
(:gazeTarget payload)
|
|
45
|
+
(:lookTarget payload)
|
|
46
|
+
(:value payload))))
|
|
47
|
+
|
|
48
|
+
(defn has-requested-target?
|
|
49
|
+
[command]
|
|
50
|
+
(some? (requested-target command)))
|
|
51
|
+
|
|
52
|
+
(defn request-source
|
|
53
|
+
[command]
|
|
54
|
+
(let [payload (data-map command)]
|
|
55
|
+
(or (:source payload)
|
|
56
|
+
(:agency payload)
|
|
57
|
+
"gaze")))
|
|
58
|
+
|
|
59
|
+
(defn normalize-gaze-request
|
|
60
|
+
[command state now-ms]
|
|
61
|
+
(let [payload (data-map command)
|
|
62
|
+
config (:config state)
|
|
63
|
+
target (normalize-target (requested-target payload))
|
|
64
|
+
eye-enabled (if (contains? payload :eyeEnabled)
|
|
65
|
+
(boolean (:eyeEnabled payload))
|
|
66
|
+
true)
|
|
67
|
+
head-enabled (if (contains? payload :headEnabled)
|
|
68
|
+
(boolean (:headEnabled payload))
|
|
69
|
+
true)
|
|
70
|
+
head-follow-eyes (if (contains? payload :headFollowEyes)
|
|
71
|
+
(boolean (:headFollowEyes payload))
|
|
72
|
+
(:headFollowEyes config))]
|
|
73
|
+
{:type "eyeHeadTracking.requestGaze"
|
|
74
|
+
:agency "eyeHeadTracking"
|
|
75
|
+
:sourceAgency (request-source payload)
|
|
76
|
+
:requestId (or (:requestId payload) (request-id "eyeHeadTracking" now-ms))
|
|
77
|
+
:target target
|
|
78
|
+
:rawTarget (normalize-target (:rawTarget payload) target)
|
|
79
|
+
:mode (or (:mode payload) (:mode state) "manual")
|
|
80
|
+
:eyeEnabled (and (:eyeTrackingEnabled config) eye-enabled)
|
|
81
|
+
:headEnabled (and (:headTrackingEnabled config) head-enabled)
|
|
82
|
+
:headFollowEyes head-follow-eyes
|
|
83
|
+
:eyeIntensity (number-or (:eyeIntensity payload) (:eyeIntensity config))
|
|
84
|
+
:headIntensity (number-or (:headIntensity payload) (:headIntensity config))
|
|
85
|
+
:headRoll (number-or (:headRoll payload) (:headRoll config))
|
|
86
|
+
:eyeDurationMs (number-or (:eyeDurationMs payload)
|
|
87
|
+
(:transitionDurationMs config))
|
|
88
|
+
:headDurationMs (number-or (:headDurationMs payload)
|
|
89
|
+
(:transitionDurationMs config))
|
|
90
|
+
:createdAt (or (:createdAt payload) now-ms)}))
|
|
91
|
+
|
|
92
|
+
(defn movement-enabled?
|
|
93
|
+
[request]
|
|
94
|
+
(or (:eyeEnabled request)
|
|
95
|
+
(and (:headEnabled request) (:headFollowEyes request))))
|
|
96
|
+
|
|
97
|
+
(defn normalize-reset-request
|
|
98
|
+
[command state now-ms]
|
|
99
|
+
(let [payload (data-map command)
|
|
100
|
+
config (:config state)
|
|
101
|
+
duration (number-or (:durationMs payload)
|
|
102
|
+
(number-or (:duration-ms payload)
|
|
103
|
+
(:returnToCenterDurationMs config)))]
|
|
104
|
+
{:type "eyeHeadTracking.requestReset"
|
|
105
|
+
:agency "eyeHeadTracking"
|
|
106
|
+
:sourceAgency (request-source payload)
|
|
107
|
+
:requestId (or (:requestId payload) (request-id "eyeHeadTracking:reset" now-ms))
|
|
108
|
+
:target zero-target
|
|
109
|
+
:rawTarget zero-target
|
|
110
|
+
:mode (:mode state)
|
|
111
|
+
:eyeEnabled (if (contains? payload :eyes)
|
|
112
|
+
(and (:eyeTrackingEnabled config) (boolean (:eyes payload)))
|
|
113
|
+
(:eyeTrackingEnabled config))
|
|
114
|
+
:headEnabled (if (contains? payload :head)
|
|
115
|
+
(and (:headTrackingEnabled config) (boolean (:head payload)))
|
|
116
|
+
(:headTrackingEnabled config))
|
|
117
|
+
:headFollowEyes true
|
|
118
|
+
:eyeIntensity (:eyeIntensity config)
|
|
119
|
+
:headIntensity (:headIntensity config)
|
|
120
|
+
:headRoll 0
|
|
121
|
+
:eyeDurationMs duration
|
|
122
|
+
:headDurationMs duration
|
|
123
|
+
:createdAt now-ms}))
|
|
124
|
+
|
|
125
|
+
(defn normalize-cancel-request
|
|
126
|
+
[command now-ms]
|
|
127
|
+
(let [payload (data-map command)]
|
|
128
|
+
{:type "eyeHeadTracking.requestCancel"
|
|
129
|
+
:agency "eyeHeadTracking"
|
|
130
|
+
:sourceAgency (request-source payload)
|
|
131
|
+
:requestId (or (:requestId payload) (request-id "eyeHeadTracking:cancel" now-ms))
|
|
132
|
+
:reason (or (:reason payload) "cancelled")
|
|
133
|
+
:createdAt now-ms}))
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
(ns polymer.eye-head.goap
|
|
2
|
+
(:require [polymer.eye-head.domain :as domain]
|
|
3
|
+
[polymer.eye-head.snippet :as snippet]))
|
|
4
|
+
|
|
5
|
+
;; Eye/Head planning accepts gaze requests from peer agencies and turns them
|
|
6
|
+
;; into local movement work. Animation requests are produced only after the
|
|
7
|
+
;; scheduler accepts the plan.
|
|
8
|
+
|
|
9
|
+
(def supported-command-types
|
|
10
|
+
#{"configure"
|
|
11
|
+
"enable"
|
|
12
|
+
"disable"
|
|
13
|
+
"setTarget"
|
|
14
|
+
"set-target"
|
|
15
|
+
"requestGaze"
|
|
16
|
+
"eyeHeadTracking.requestGaze"
|
|
17
|
+
"reset"
|
|
18
|
+
"eyeHeadTracking.requestReset"
|
|
19
|
+
"cancel"
|
|
20
|
+
"eyeHeadTracking.requestCancel"})
|
|
21
|
+
|
|
22
|
+
(defn command-type
|
|
23
|
+
[command]
|
|
24
|
+
(:type command))
|
|
25
|
+
|
|
26
|
+
(defn plan-gaze
|
|
27
|
+
[command state now-ms]
|
|
28
|
+
(if-not (domain/has-requested-target? command)
|
|
29
|
+
[{:op "fail"
|
|
30
|
+
:reason "missing-target"
|
|
31
|
+
:commandType (command-type command)}]
|
|
32
|
+
(let [request (domain/normalize-gaze-request command state now-ms)]
|
|
33
|
+
(cond
|
|
34
|
+
(not (get-in state [:config :enabled]))
|
|
35
|
+
[{:op "ignore-request"
|
|
36
|
+
:request request
|
|
37
|
+
:reason "disabled"}]
|
|
38
|
+
|
|
39
|
+
(not (domain/movement-enabled? request))
|
|
40
|
+
[{:op "ignore-request"
|
|
41
|
+
:request request
|
|
42
|
+
:reason "movement-disabled"}]
|
|
43
|
+
|
|
44
|
+
:else
|
|
45
|
+
[{:op "queue-gaze"
|
|
46
|
+
:request request
|
|
47
|
+
:snippet (snippet/build-gaze-snippet request (:config state))}]))))
|
|
48
|
+
|
|
49
|
+
(defn plan-reset
|
|
50
|
+
[command state now-ms]
|
|
51
|
+
(let [request (domain/normalize-reset-request command state now-ms)]
|
|
52
|
+
(if (domain/movement-enabled? request)
|
|
53
|
+
[{:op "queue-reset"
|
|
54
|
+
:request request
|
|
55
|
+
:snippet (snippet/build-gaze-snippet request (:config state))}]
|
|
56
|
+
[{:op "ignore-request"
|
|
57
|
+
:request request
|
|
58
|
+
:reason "movement-disabled"}])))
|
|
59
|
+
|
|
60
|
+
(defn plan-cancel
|
|
61
|
+
[command now-ms]
|
|
62
|
+
[{:op "cancel-active"
|
|
63
|
+
:request (domain/normalize-cancel-request command now-ms)}])
|
|
64
|
+
|
|
65
|
+
(defn command-steps
|
|
66
|
+
[command state now-ms]
|
|
67
|
+
(let [type (command-type command)]
|
|
68
|
+
(cond
|
|
69
|
+
(not (contains? supported-command-types type))
|
|
70
|
+
[{:op "fail"
|
|
71
|
+
:reason "unsupported-command"
|
|
72
|
+
:commandType type}]
|
|
73
|
+
|
|
74
|
+
(= "configure" type)
|
|
75
|
+
[{:op "apply-config" :config (:config command)}
|
|
76
|
+
{:op "publish-status" :reason "configured"}]
|
|
77
|
+
|
|
78
|
+
(= "enable" type)
|
|
79
|
+
[{:op "set-enabled" :enabled true}
|
|
80
|
+
{:op "publish-status" :reason "enabled"}]
|
|
81
|
+
|
|
82
|
+
(= "disable" type)
|
|
83
|
+
[{:op "set-enabled" :enabled false}
|
|
84
|
+
{:op "cancel-active"
|
|
85
|
+
:request (domain/normalize-cancel-request
|
|
86
|
+
(assoc command :reason "disabled")
|
|
87
|
+
now-ms)}
|
|
88
|
+
{:op "publish-status" :reason "disabled"}]
|
|
89
|
+
|
|
90
|
+
(#{"reset" "eyeHeadTracking.requestReset"} type)
|
|
91
|
+
(plan-reset command state now-ms)
|
|
92
|
+
|
|
93
|
+
(#{"cancel" "eyeHeadTracking.requestCancel"} type)
|
|
94
|
+
(plan-cancel command now-ms)
|
|
95
|
+
|
|
96
|
+
:else
|
|
97
|
+
(plan-gaze command state now-ms))))
|
|
98
|
+
|
|
99
|
+
(defn plan-command
|
|
100
|
+
[command state now-ms]
|
|
101
|
+
(let [steps (command-steps command state now-ms)]
|
|
102
|
+
{:agency "eyeHeadTracking"
|
|
103
|
+
:commandType (command-type command)
|
|
104
|
+
:createdAt now-ms
|
|
105
|
+
:ok (not= "fail" (:op (first steps)))
|
|
106
|
+
:steps steps}))
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
(ns polymer.eye-head.scheduler
|
|
2
|
+
(:require [polymer.eye-head.state :as state]))
|
|
3
|
+
|
|
4
|
+
;; The Eye/Head scheduler is the boundary that replaces or cancels movement
|
|
5
|
+
;; snippets. It publishes animation requests as data; the Animation agency owns
|
|
6
|
+
;; the runtime side effect of playing those snippets.
|
|
7
|
+
|
|
8
|
+
(defn now-ms
|
|
9
|
+
[]
|
|
10
|
+
(.now js/Date))
|
|
11
|
+
|
|
12
|
+
(defn clear-timeout!
|
|
13
|
+
[timeout-atom]
|
|
14
|
+
(when-let [timeout @timeout-atom]
|
|
15
|
+
(js/clearTimeout timeout)
|
|
16
|
+
(reset! timeout-atom nil)))
|
|
17
|
+
|
|
18
|
+
(defn queue-entry
|
|
19
|
+
[step index]
|
|
20
|
+
(assoc step
|
|
21
|
+
:agency "eyeHeadTracking"
|
|
22
|
+
:queueIndex index
|
|
23
|
+
:queuedAt (now-ms)))
|
|
24
|
+
|
|
25
|
+
(defn create-scheduler
|
|
26
|
+
[{:keys [state-atom emit-event]}]
|
|
27
|
+
(let [coalesce-timeout (atom nil)
|
|
28
|
+
pending-work (atom nil)
|
|
29
|
+
queue (atom [])
|
|
30
|
+
disposed? (atom false)]
|
|
31
|
+
(letfn [(enqueue! [step]
|
|
32
|
+
(let [entry (queue-entry step (count @queue))]
|
|
33
|
+
(swap! queue conj entry)
|
|
34
|
+
entry))
|
|
35
|
+
|
|
36
|
+
(emit-status! [reason]
|
|
37
|
+
(emit-event {:type "eyeHeadTracking.status"
|
|
38
|
+
:agency "eyeHeadTracking"
|
|
39
|
+
:status (:status @state-atom)
|
|
40
|
+
:enabled (get-in @state-atom [:config :enabled])
|
|
41
|
+
:activeSnippetNames (:activeSnippetNames @state-atom)
|
|
42
|
+
:reason reason
|
|
43
|
+
:at (now-ms)}))
|
|
44
|
+
|
|
45
|
+
(emit-remove-active! [reason]
|
|
46
|
+
(doseq [name (:activeSnippetNames @state-atom)]
|
|
47
|
+
(let [removed-at (now-ms)]
|
|
48
|
+
(swap! state-atom state/record-removed name reason removed-at)
|
|
49
|
+
(emit-event {:type "animation.requestRemoveSnippet"
|
|
50
|
+
:agency "eyeHeadTracking"
|
|
51
|
+
:requestId (str "eyeHeadTracking:remove:" removed-at)
|
|
52
|
+
:name name
|
|
53
|
+
:reason reason
|
|
54
|
+
:queuedAt removed-at}))))
|
|
55
|
+
|
|
56
|
+
(publish-work-now! []
|
|
57
|
+
(when-not @disposed?
|
|
58
|
+
(clear-timeout! coalesce-timeout)
|
|
59
|
+
(when-let [{:keys [kind request snippet]} @pending-work]
|
|
60
|
+
(reset! pending-work nil)
|
|
61
|
+
(when (get-in @state-atom [:config :replaceExisting])
|
|
62
|
+
(emit-remove-active! "replaced"))
|
|
63
|
+
(let [published-at (now-ms)]
|
|
64
|
+
(enqueue! {:op "publish-animation"
|
|
65
|
+
:requestId (:requestId request)
|
|
66
|
+
:name (:name snippet)})
|
|
67
|
+
(if (= kind "reset")
|
|
68
|
+
(swap! state-atom state/record-reset request snippet published-at)
|
|
69
|
+
(swap! state-atom state/record-scheduled request snippet published-at))
|
|
70
|
+
(emit-event {:type "animation.requestScheduleSnippet"
|
|
71
|
+
:agency "eyeHeadTracking"
|
|
72
|
+
:requestId (:requestId request)
|
|
73
|
+
:snippet snippet
|
|
74
|
+
:options {:autoPlay true
|
|
75
|
+
:sourceAgency "eyeHeadTracking"}})))))
|
|
76
|
+
|
|
77
|
+
(queue-work! [kind request snippet]
|
|
78
|
+
(when-not @disposed?
|
|
79
|
+
(let [queued-at (now-ms)
|
|
80
|
+
work {:kind kind
|
|
81
|
+
:request (assoc request :queuedAt queued-at)
|
|
82
|
+
:snippet snippet}
|
|
83
|
+
delay-ms (get-in @state-atom [:config :coalesceMs])]
|
|
84
|
+
(reset! pending-work work)
|
|
85
|
+
(swap! state-atom state/record-pending (:request work))
|
|
86
|
+
(clear-timeout! coalesce-timeout)
|
|
87
|
+
(enqueue! {:op "coalesce-work"
|
|
88
|
+
:kind kind
|
|
89
|
+
:requestId (:requestId request)
|
|
90
|
+
:coalesceMs delay-ms})
|
|
91
|
+
(if (pos? delay-ms)
|
|
92
|
+
(reset! coalesce-timeout
|
|
93
|
+
(js/setTimeout publish-work-now! delay-ms))
|
|
94
|
+
(publish-work-now!)))))
|
|
95
|
+
|
|
96
|
+
(cancel-pending! []
|
|
97
|
+
(clear-timeout! coalesce-timeout)
|
|
98
|
+
(reset! pending-work nil)
|
|
99
|
+
(swap! state-atom assoc :pendingRequest nil))
|
|
100
|
+
|
|
101
|
+
(ignore-request! [request reason]
|
|
102
|
+
(let [ignored {:type "eyeHeadTracking.requestIgnored"
|
|
103
|
+
:agency "eyeHeadTracking"
|
|
104
|
+
:requestId (:requestId request)
|
|
105
|
+
:reason reason
|
|
106
|
+
:target (:target request)
|
|
107
|
+
:ignoredAt (now-ms)}]
|
|
108
|
+
(cancel-pending!)
|
|
109
|
+
(swap! state-atom state/record-ignored ignored)
|
|
110
|
+
(emit-event ignored)))
|
|
111
|
+
|
|
112
|
+
(cancel-active! [request]
|
|
113
|
+
(cancel-pending!)
|
|
114
|
+
(emit-remove-active! (:reason request))
|
|
115
|
+
(swap! state-atom state/record-cancel request)
|
|
116
|
+
(emit-event {:type "eyeHeadTracking.cancelled"
|
|
117
|
+
:agency "eyeHeadTracking"
|
|
118
|
+
:requestId (:requestId request)
|
|
119
|
+
:reason (:reason request)
|
|
120
|
+
:at (now-ms)}))
|
|
121
|
+
|
|
122
|
+
(execute-step! [step]
|
|
123
|
+
(enqueue! step)
|
|
124
|
+
(case (:op step)
|
|
125
|
+
"apply-config"
|
|
126
|
+
(do
|
|
127
|
+
(swap! state-atom state/configure (:config step))
|
|
128
|
+
(emit-status! (:reason step)))
|
|
129
|
+
|
|
130
|
+
"set-enabled"
|
|
131
|
+
(do
|
|
132
|
+
(swap! state-atom state/set-enabled (:enabled step))
|
|
133
|
+
(emit-status! (if (:enabled step) "enabled" "disabled")))
|
|
134
|
+
|
|
135
|
+
"publish-status"
|
|
136
|
+
(emit-status! (:reason step))
|
|
137
|
+
|
|
138
|
+
"queue-gaze"
|
|
139
|
+
(queue-work! "gaze" (:request step) (:snippet step))
|
|
140
|
+
|
|
141
|
+
"queue-reset"
|
|
142
|
+
(queue-work! "reset" (:request step) (:snippet step))
|
|
143
|
+
|
|
144
|
+
"cancel-active"
|
|
145
|
+
(cancel-active! (:request step))
|
|
146
|
+
|
|
147
|
+
"ignore-request"
|
|
148
|
+
(ignore-request! (:request step) (:reason step))
|
|
149
|
+
|
|
150
|
+
"fail"
|
|
151
|
+
(emit-event {:type "error"
|
|
152
|
+
:agency "eyeHeadTracking"
|
|
153
|
+
:message (:reason step)
|
|
154
|
+
:commandType (:commandType step)})
|
|
155
|
+
|
|
156
|
+
nil))]
|
|
157
|
+
{:schedule
|
|
158
|
+
(fn [plan]
|
|
159
|
+
(when-not @disposed?
|
|
160
|
+
(swap! state-atom state/record-plan plan)
|
|
161
|
+
(doseq [step (:steps plan)]
|
|
162
|
+
(execute-step! step))))
|
|
163
|
+
|
|
164
|
+
:flush
|
|
165
|
+
(fn []
|
|
166
|
+
(publish-work-now!))
|
|
167
|
+
|
|
168
|
+
:queue
|
|
169
|
+
(fn []
|
|
170
|
+
@queue)
|
|
171
|
+
|
|
172
|
+
:dispose
|
|
173
|
+
(fn []
|
|
174
|
+
(reset! disposed? true)
|
|
175
|
+
(cancel-pending!)
|
|
176
|
+
(reset! queue []))})))
|