Model head anchors and independent pose timing
This commit is contained in:
parent
a45e89f4e4
commit
49ece8dee6
17 changed files with 521 additions and 215 deletions
|
|
@ -13,6 +13,7 @@
|
|||
[arthur.domain.channel :as ch]
|
||||
[arthur.domain.clip :as clip]
|
||||
[arthur.domain.geom :as geom]
|
||||
[arthur.domain.leaf :as leaf]
|
||||
[arthur.domain.node :as node]
|
||||
[arthur.domain.palette :as pal]
|
||||
[arthur.domain.raster :as raster]
|
||||
|
|
@ -77,10 +78,11 @@
|
|||
;; before the data is trusted — which is what it is for. It checks the clip's
|
||||
;; fields, every timeline in it and the tracking identities, so it is the whole
|
||||
;; of what a save would refuse.
|
||||
(doseq [mode [:as-filmed :locked]]
|
||||
(let [c (freeze/head-mode {:mode mode} @frozen)]
|
||||
(is (empty? (clip/problems c)) (str mode ": " (pr-str (clip/problems c))))))
|
||||
(let [c (freeze/head-mode {:mode :per-plate :kept #{0 12 40 88 150}} @frozen)]
|
||||
(doseq [spec [{:mode :free} {:mode :anchored :anchors {0 0}}]]
|
||||
(let [c (freeze/head-mode spec @frozen)]
|
||||
(is (empty? (clip/problems c)) (str spec ": " (pr-str (clip/problems c))))))
|
||||
(let [c (freeze/head-mode {:mode :anchored
|
||||
:anchors {0 12, 40 88, 150 150}} @frozen)]
|
||||
(is (empty? (clip/problems c)) (pr-str (clip/problems c)))))
|
||||
|
||||
(deftest the-tree-is-the-one-the-model-specifies
|
||||
|
|
@ -196,7 +198,7 @@
|
|||
;; of the two says it is. A test that recomputed the chain would only be
|
||||
;; checking arithmetic against itself; this checks `node/local!`, `node/world!`
|
||||
;; and `emit` as well.
|
||||
(let [c (freeze/head-mode {:mode :as-filmed} @frozen)
|
||||
(let [c (freeze/head-mode {:mode :free} @frozen)
|
||||
res (timeline/resolver (clip/root c) @store pal/index-of)
|
||||
k (first (:value (chan :face [:xform :scale])))
|
||||
anc (:value (chan :face [:xform :anchor]))
|
||||
|
|
@ -234,47 +236,48 @@
|
|||
"], the composition says [" wx " " wy "]"))))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; the three modes are the three channel shapes
|
||||
;; one dense measurement, with optional held anchor frames
|
||||
|
||||
(deftest the-three-head-modes-are-the-three-channel-shapes
|
||||
(let [kept #{0 12 40 88 150}
|
||||
of (fn [c path] (get-in (clip/nodes c) [:head :channels path]))]
|
||||
(testing "locked is framed identity"
|
||||
(let [sc (freeze/head-mode {:mode :locked} @frozen)]
|
||||
(is (= [:framed :framed :framed]
|
||||
(mapv #(ch/describe (of sc %))
|
||||
[[:xform :pos] [:xform :rot] [:xform :scale]])))
|
||||
(is (= [0.0 0.0] (:value (of sc [:xform :pos]))))
|
||||
(is (= 0.0 (:value (of sc [:xform :rot]))))
|
||||
(is (= [1.0 1.0] (:value (of sc [:xform :scale]))))))
|
||||
(testing "as filmed is dense"
|
||||
(let [sc (freeze/head-mode {:mode :as-filmed} @frozen)]
|
||||
(is (= [:dense :dense :dense]
|
||||
(mapv #(ch/describe (of sc %))
|
||||
[[:xform :pos] [:xform :rot] [:xform :scale]])))))
|
||||
(testing "per plate is keyed at exactly the kept frames"
|
||||
(let [sc (freeze/head-mode {:mode :per-plate :kept kept} @frozen)]
|
||||
(doseq [path [[:xform :pos] [:xform :rot] [:xform :scale]]]
|
||||
(is (= :keyed (ch/describe (of sc path))))
|
||||
(is (= (sort kept) (sort (keys (:keys (of sc path))))))
|
||||
;; A dense read is a VIEW into tier 2. Storing one in the document would
|
||||
;; be storing a value that changes when a re-freeze rewrites the array
|
||||
;; under it, so the keys hold plain data.
|
||||
(doseq [[_ v] (:keys (of sc path))]
|
||||
(is (or (number? v) (vector? v)) (str path " key is " (pr-str v)))))
|
||||
;; And the keys are the dense track sampled at those frames, which is the
|
||||
;; whole of what "per plate" means.
|
||||
(is (= (mapv #(ch/value-at (get-in (node :head) [:measured [:xform :rot]]) % @store)
|
||||
(sort kept))
|
||||
(mapv (:keys (of sc [:xform :rot])) (sort kept))))))))
|
||||
(deftest head-anchors-select-measured-frames-without-copying-channels
|
||||
(let [free (freeze/head-mode {:mode :free} @frozen)
|
||||
one (freeze/head-mode {:mode :anchored :anchors {0 12}} @frozen)
|
||||
keyed (freeze/head-mode {:mode :anchored
|
||||
:anchors {0 12, 40 88, 150 150}} @frozen)
|
||||
of (fn [c path] (get-in (clip/nodes c) [:head :channels path]))]
|
||||
(doseq [c [free one keyed]
|
||||
path [[:xform :pos] [:xform :rot] [:xform :scale]]]
|
||||
(is (= :dense (ch/describe (of c path))))
|
||||
(is (= (of free path) (of c path)) "anchor edits do not copy measurements"))
|
||||
(is (nil? (get-in (clip/nodes free) [:head :anchors])))
|
||||
(is (= {0 12} (get-in (clip/nodes one) [:head :anchors])))
|
||||
(is (= {0 12, 40 88, 150 150}
|
||||
(get-in (clip/nodes keyed) [:head :anchors])))
|
||||
(is (= keyed (leaf/clip "head" (leaf/leaves "head" keyed)))
|
||||
"anchor source addresses survive the document round trip")))
|
||||
|
||||
(deftest head-anchor-keys-hold-the-whole-measured-transform
|
||||
(let [free (timeline/resolver (clip/root
|
||||
(freeze/head-mode {:mode :free} @frozen))
|
||||
@store pal/index-of)
|
||||
held (timeline/resolver (clip/root
|
||||
(freeze/head-mode {:mode :anchored
|
||||
:anchors {0 12, 40 88}} @frozen))
|
||||
@store pal/index-of)
|
||||
world (fn [resolver frame]
|
||||
(resolver frame)
|
||||
(vec (array-seq (timeline/world-of resolver :head))))]
|
||||
(is (= (world free 12) (world held 0)))
|
||||
(is (= (world free 12) (world held 38)))
|
||||
(is (= (world free 88) (world held 40)))
|
||||
(is (= (world free 88) (world held 100)))))
|
||||
|
||||
(deftest switching-modes-rewrites-the-head-and-nothing-else
|
||||
;; It has to be impossible for the toggle to move something a hand placed, and
|
||||
;; it has to be a DOCUMENT edit: tier 1, undoable, syncable, instant, and not a
|
||||
;; reason to re-analyse.
|
||||
(let [a (freeze/head-mode {:mode :as-filmed} @frozen)
|
||||
b (freeze/head-mode {:mode :locked} @frozen)
|
||||
c (freeze/head-mode {:mode :per-plate :kept #{0 40}} @frozen)]
|
||||
(let [a (freeze/head-mode {:mode :free} @frozen)
|
||||
b (freeze/head-mode {:mode :anchored :anchors {0 0}} @frozen)
|
||||
c (freeze/head-mode {:mode :anchored :anchors {0 0, 40 40}} @frozen)]
|
||||
(doseq [x [b c]]
|
||||
(is (= (get (clip/nodes a) :face) (get (clip/nodes x) :face))
|
||||
":face moved")
|
||||
|
|
@ -288,13 +291,14 @@
|
|||
;; channels, so the clip's own fields and its other timelines are untouched.
|
||||
(is (= (dissoc a :timelines) (dissoc x :timelines))))))
|
||||
|
||||
(deftest a-head-mode-that-is-not-one-of-the-three-is-refused
|
||||
(is (thrown-with-msg? ExceptionInfo #"not one of the three channel shapes"
|
||||
(deftest invalid-head-anchor-maps-are-refused
|
||||
(is (thrown-with-msg? ExceptionInfo #"free or anchored"
|
||||
(freeze/head-mode {:mode :stabilised} @frozen)))
|
||||
;; The kept-frame set belongs to the plate strip, not to measurement, so freeze
|
||||
;; cannot invent one.
|
||||
(is (thrown-with-msg? ExceptionInfo #"kept-frame set"
|
||||
(freeze/head-mode {:mode :per-plate} @frozen))))
|
||||
(doseq [anchors [nil {} {12 12} {0 take/frames} {0 0, 10 -1}]]
|
||||
(is (thrown-with-msg? ExceptionInfo #"frame-zero key"
|
||||
(freeze/head-mode {:mode :anchored :anchors anchors} @frozen))))
|
||||
(is (thrown-with-msg? ExceptionInfo #"has no anchors"
|
||||
(freeze/head-mode {:mode :free :anchors {0 0}} @frozen))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; the face: authored, and what makes makeXform deletable
|
||||
|
|
@ -597,7 +601,7 @@
|
|||
;; from different beats are genuinely different mouths and frames inside one
|
||||
;; beat are not. This is the numeric half of step 5's done-criterion; the other
|
||||
;; half is a picture and lives in test/browser/take.mjs.
|
||||
(let [locked (freeze/head-mode {:mode :locked} @frozen)
|
||||
(let [locked (freeze/head-mode {:mode :anchored :anchors {0 0}} @frozen)
|
||||
shot (fn [f]
|
||||
(let [r (raster/make W H)
|
||||
mouth (filter #(= :mouth (:node %)) (ops-at (clip/root locked) f))]
|
||||
|
|
@ -614,6 +618,6 @@
|
|||
(is (< (differ (shot 10) (shot 12)) 200)
|
||||
"a held pose is moving more than the detector noise it should have lost"))
|
||||
;; As filmed, the head carries it around the stage as well.
|
||||
(let [filmed (freeze/head-mode {:mode :as-filmed} @frozen)]
|
||||
(let [filmed (freeze/head-mode {:mode :free} @frozen)]
|
||||
(is (> (count (remove true? (map = (render filmed 10) (render filmed 120)))) 300)
|
||||
"the head does not move across the take")))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue