multi fce stuff
This commit is contained in:
parent
49ece8dee6
commit
ddabfbeaa8
32 changed files with 1309 additions and 700 deletions
|
|
@ -30,13 +30,14 @@
|
|||
;; that has to be right.
|
||||
(def frozen (delay @take/frozen))
|
||||
(def clip* (delay (:clip @frozen)))
|
||||
;; The ROOT TIMELINE, which is what an evaluator takes. `clip*` is the document —
|
||||
;; `:fps`, the stage, the analysis, the tracking identities — and `tl*` is the bag
|
||||
;; of nodes in its frame space. `timeline/resolver` refuses the wrong one loudly.
|
||||
(def tl* (delay (clip/root @clip*)))
|
||||
;; Geometry assertions read the face timeline. Rendering assertions resolve
|
||||
;; the whole clip, including placement and inherited exposure.
|
||||
(defn- face-timeline [c] (clip/timeline c :face-1))
|
||||
(defn- nodes [c] (merge (clip/nodes c) (:nodes (face-timeline c))))
|
||||
(def tl* (delay (face-timeline @clip*)))
|
||||
(def store (delay (:store @frozen)))
|
||||
|
||||
(defn- node [id] (get-in @tl* [:nodes id]))
|
||||
(defn- node [id] (get (nodes @clip*) id))
|
||||
(defn- chan [id path] (get-in (node id) [:channels path]))
|
||||
|
||||
(defn- block-of
|
||||
|
|
@ -57,12 +58,11 @@
|
|||
|
||||
(defn- render
|
||||
"One frame of a CLIP into a byte buffer. The stage's size comes off the clip and
|
||||
the ops off its root timeline, because project dimensions are the project's and
|
||||
not the footage's — and not a timeline's either."
|
||||
the ops from the complete clip, including nested face timelines."
|
||||
[c f]
|
||||
(let [r (raster/make (:width c) (:height c))]
|
||||
(raster/clear! r (get pal/index-of :bg))
|
||||
(raster/draw-ops! r (ops-at (clip/root c) f))
|
||||
(raster/draw-ops! r ((clip/resolver c @store pal/index-of) f))
|
||||
(vec (array-seq (:buf r)))))
|
||||
|
||||
(defn- drawn
|
||||
|
|
@ -86,16 +86,11 @@
|
|||
(is (empty? (clip/problems c)) (pr-str (clip/problems c)))))
|
||||
|
||||
(deftest the-tree-is-the-one-the-model-specifies
|
||||
;; :face is AUTHORED and :head is MEASURED, and they are two nodes because two
|
||||
;; different things want that transform. A group node is free; keeping the
|
||||
;; hand-placed and the measured transform apart is the whole reason the
|
||||
;; transform is decomposed in the first place.
|
||||
(is (= [:face :root] (timeline/lineage (:nodes @tl*) :face)))
|
||||
(is (= [:head :face :root] (timeline/lineage (:nodes @tl*) :head)))
|
||||
(is (= [:mouth :head :face :root] (timeline/lineage (:nodes @tl*) :mouth)))
|
||||
(is (= [:mouth-in :mouth :head :face :root]
|
||||
(timeline/lineage (:nodes @tl*) :mouth-in)))
|
||||
;; Exposure lives on the clip root and inherits strictly.
|
||||
(is (= [:face :root] (timeline/lineage (clip/nodes @clip*) :face)))
|
||||
(is (= :face-1 (get-in @clip* [:timelines :main :nodes :face-1 :of])))
|
||||
(is (= [:head] (timeline/lineage (:nodes @tl*) :head)))
|
||||
(is (= [:mouth :head] (timeline/lineage (:nodes @tl*) :mouth)))
|
||||
(is (= [:mouth-in :mouth :head] (timeline/lineage (:nodes @tl*) :mouth-in)))
|
||||
(is (= {:mode :map :expose 2} (:time (node :root))))
|
||||
(is (every? #(nil? (:time (node %))) [:face :head :mouth :mouth-in])))
|
||||
|
||||
|
|
@ -158,8 +153,9 @@
|
|||
(is (thrown-with-msg?
|
||||
ExceptionInfo #"does not fit the block's fixed point"
|
||||
(freeze/clip (assoc take/params :name "huge")
|
||||
(update @take/measured :outer
|
||||
(fn [rings] (mapv (fn [r] (mapv #(update % :x + 3) r)) rings)))))))
|
||||
{:face-1 (update @take/measured :outer
|
||||
(fn [rings]
|
||||
(mapv (fn [r] (mapv #(update % :x + 3) r)) rings)))}))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; the anchor: three channels, and the inverse
|
||||
|
|
@ -199,14 +195,14 @@
|
|||
;; checking arithmetic against itself; this checks `node/local!`, `node/world!`
|
||||
;; and `emit` as well.
|
||||
(let [c (freeze/head-mode {:mode :free} @frozen)
|
||||
res (timeline/resolver (clip/root c) @store pal/index-of)
|
||||
res (clip/resolver c @store pal/index-of)
|
||||
k (first (:value (chan :face [:xform :scale])))
|
||||
anc (:value (chan :face [:xform :anchor]))
|
||||
pos (:value (chan :face [:xform :pos]))
|
||||
tfs (:transforms @take/measured)]
|
||||
(doseq [f (range 0 take/frames 13)]
|
||||
(let [ops (res f)
|
||||
op (first (filter #(= :mouth (:node %)) ops))
|
||||
op (first (filter #(= [:face-1 :mouth] (:node %)) ops))
|
||||
;; EXPOSURE FIRST. The clip root is on 2s and exposure inherits
|
||||
;; strictly, so frame 13 shows frame 12's pose — which is also the
|
||||
;; cheapest place to assert that the grid is actually being applied,
|
||||
|
|
@ -243,23 +239,23 @@
|
|||
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]))]
|
||||
of (fn [c path] (get-in (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 (nil? (get-in (nodes free) [:head :anchors])))
|
||||
(is (= {0 12} (get-in (nodes one) [:head :anchors])))
|
||||
(is (= {0 12, 40 88, 150 150}
|
||||
(get-in (clip/nodes keyed) [:head :anchors])))
|
||||
(get-in (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
|
||||
(let [free (timeline/resolver (face-timeline
|
||||
(freeze/head-mode {:mode :free} @frozen))
|
||||
@store pal/index-of)
|
||||
held (timeline/resolver (clip/root
|
||||
held (timeline/resolver (face-timeline
|
||||
(freeze/head-mode {:mode :anchored
|
||||
:anchors {0 12, 40 88}} @frozen))
|
||||
@store pal/index-of)
|
||||
|
|
@ -279,14 +275,14 @@
|
|||
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))
|
||||
(is (= (get (nodes a) :face) (get (nodes x) :face))
|
||||
":face moved")
|
||||
(is (= (dissoc (clip/nodes a) :head) (dissoc (clip/nodes x) :head))
|
||||
(is (= (dissoc (nodes a) :head) (dissoc (nodes x) :head))
|
||||
"a node other than :head changed")
|
||||
;; The measurement does not go away when the head is locked: always measure,
|
||||
;; always store factored, toggle the parent.
|
||||
(is (= (get-in (clip/nodes a) [:head :measured])
|
||||
(get-in (clip/nodes x) [:head :measured])))
|
||||
(is (= (get-in (nodes a) [:head :measured])
|
||||
(get-in (nodes x) [:head :measured])))
|
||||
;; And nothing above the timeline moved either: the toggle is one node's
|
||||
;; channels, so the clip's own fields and its other timelines are untouched.
|
||||
(is (= (dissoc a :timelines) (dissoc x :timelines))))))
|
||||
|
|
@ -331,8 +327,8 @@
|
|||
;; asking for a different stage moves and rescales the same geometry rather than
|
||||
;; re-measuring anything.
|
||||
(let [big (freeze/clip (assoc take/params :stage [640 480] :name "big")
|
||||
@take/measured)
|
||||
key-of (fn [c] (:store (:dense (get-in (clip/nodes c)
|
||||
{:face-1 @take/measured})
|
||||
key-of (fn [c] (:store (:dense (get-in (nodes c)
|
||||
[:mouth :channels [:geom :pts]]))))]
|
||||
(is (= [640 480] [(:width (:clip big)) (:height (:clip big))]))
|
||||
;; Stronger than it was, and for free: the stage is not an input to tier 2, so
|
||||
|
|
@ -344,7 +340,7 @@
|
|||
(is (= (vec (array-seq (:data (get @store (key-of @clip*)))))
|
||||
(vec (array-seq (:data (get (:store big) (key-of (:clip big)))))))
|
||||
"the geometry is the same numbers at either stage size")
|
||||
(is (not= (:value (get-in (clip/nodes (:clip big))
|
||||
(is (not= (:value (get-in (nodes (:clip big))
|
||||
[:face :channels [:xform :scale]]))
|
||||
(:value (chan :face [:xform :scale]))))))
|
||||
|
||||
|
|
@ -422,14 +418,14 @@
|
|||
(let [gap (set (range 40 60))
|
||||
presence {:eye-r (mapv #(not (contains? gap %)) (range take/frames))}
|
||||
c (freeze/clip (assoc take/params :name "one-eye-gappy")
|
||||
(assoc @take/measured :presence presence))
|
||||
tl (clip/root (:clip c))
|
||||
{:face-1 (assoc @take/measured :presence presence)})
|
||||
tl (face-timeline (:clip c))
|
||||
at (fn [id f]
|
||||
(ch/value-at (get-in (:nodes tl) [id :channels [:geom :pts]]) f (:store c)))]
|
||||
;; The identities are the CLIP's, and a gap does not touch them: a feature keeps
|
||||
;; its id and its pair membership across the frames it was not observed on.
|
||||
(is (= [:eye-r :eye-l] (get-in (:clip c) [:groups :eyes-1 :members])))
|
||||
(is (= :eye-r (get-in (:clip c) [:features :eye-r :id])))
|
||||
(is (= [:face-1/eye-r :face-1/eye-l] (get-in (:clip c) [:groups :face-1/eyes :members])))
|
||||
(is (= :face-1/eye-r (get-in (:clip c) [:features :face-1/eye-r :id])))
|
||||
(is (not (ch/nothing? (at :eye-r 39))))
|
||||
(is (ch/nothing? (at :eye-r 50)))
|
||||
(is (not (ch/nothing? (at :eye-r 60))))
|
||||
|
|
@ -460,8 +456,8 @@
|
|||
(range take/frames))]))
|
||||
windows)
|
||||
c (freeze/clip (assoc take/params :name "per-track-gaps")
|
||||
(assoc @take/measured :presence presence))
|
||||
tl (clip/root (:clip c))
|
||||
{:face-1 (assoc @take/measured :presence presence)})
|
||||
tl (face-timeline (:clip c))
|
||||
at (fn [id path f]
|
||||
(ch/value-at (get-in (:nodes tl) [id :channels path]) f (:store c)))
|
||||
;; Node, channel, and the feature whose gap it must follow. Every dense
|
||||
|
|
@ -501,7 +497,8 @@
|
|||
:shown (vec (repeat take/frames true))}
|
||||
clip (fn [nm extra]
|
||||
(freeze/clip (assoc take/params :name nm)
|
||||
(merge (assoc @take/measured :teeth teeth-in) extra)))
|
||||
{:face-1 (merge (assoc @take/measured :teeth teeth-in)
|
||||
extra)}))
|
||||
ref (clip "teeth-reference" nil)
|
||||
occ (clip "teeth-mouth-gap"
|
||||
{:presence {:mouth (mapv #(not (contains? gap %))
|
||||
|
|
@ -509,7 +506,7 @@
|
|||
;; Hoisted: the resolver caches its order and reuses its buffers, so the
|
||||
;; node ids come out before the next frame is asked for.
|
||||
nodes-at (fn [c]
|
||||
(let [r (timeline/resolver (clip/root (:clip c)) (:store c) pal/index-of)]
|
||||
(let [r (timeline/resolver (face-timeline (:clip c)) (:store c) pal/index-of)]
|
||||
(fn [f] (into #{} (map :node) (r f)))))
|
||||
ref-at (nodes-at ref)
|
||||
occ-at (nodes-at occ)
|
||||
|
|
@ -519,7 +516,7 @@
|
|||
open (filter #(contains? (ref-at %) :teeth) (range take/frames))
|
||||
inside (first (filter gap open))
|
||||
outside (first (remove gap open))]
|
||||
(is (= :mouth-in (get-in (clip/nodes (:clip occ)) [:teeth :stencil]))
|
||||
(is (= :mouth-in (get-in (nodes (:clip occ)) [:teeth :stencil]))
|
||||
"teeth stop inheriting the mouth's absence if this stops being their stencil")
|
||||
(is (some? inside) "no open-mouth frame inside the gap to test with")
|
||||
(is (some? outside) "no open-mouth frame outside the gap to test with")
|
||||
|
|
@ -527,7 +524,7 @@
|
|||
;; Annotating the MOUTH sets no bit on the teeth: they are a feature of
|
||||
;; their own and nothing named them.
|
||||
(is (not (ch/nothing?
|
||||
(ch/value-at (get-in (clip/nodes (:clip occ)) [:teeth :channels [:geom :pts]])
|
||||
(ch/value-at (get-in (nodes (:clip occ)) [:teeth :channels [:geom :pts]])
|
||||
inside (:store occ)))))
|
||||
;; They are dropped anyway — :mouth-in drew nothing to clip them against.
|
||||
(is (not (contains? (occ-at inside) :mouth-in)))
|
||||
|
|
@ -544,8 +541,8 @@
|
|||
(let [gap (set (range 40 60))
|
||||
det (mapv #(not (contains? gap %)) (range take/frames))
|
||||
c (freeze/clip (assoc take/params :name "gappy")
|
||||
(assoc @take/measured :detected det))
|
||||
tl (clip/root (:clip c))
|
||||
{:face-1 (assoc @take/measured :detected det)})
|
||||
tl (face-timeline (:clip c))
|
||||
res (timeline/resolver tl (:store c) pal/index-of)]
|
||||
(doseq [f [39 40 50 59 60]]
|
||||
(let [ops (res f)]
|
||||
|
|
@ -604,7 +601,8 @@
|
|||
(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))]
|
||||
mouth (filter #(= [:face-1 :mouth] (:node %))
|
||||
((clip/resolver locked @store pal/index-of) f))]
|
||||
(raster/clear! r (get pal/index-of :bg))
|
||||
(raster/draw-ops! r mouth)
|
||||
(vec (array-seq (:buf r)))))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue