One word for one thing: it is a cel
Four words had accumulated for a node that puts a symbol inside another symbol.
`instance` was the document's, from the model. `placement` was the stage and
export work's. `occurrence` came in with the lane model. `exposure` came in with
me, because it is what an animator would say. Three bodies of work each brought
a word and none of them retired anybody else's, which is how you get a codebase
that reads like three people describing the same object over each other.
It is a CEL. One drawing, held for some duration. `cel` was already the view's
word — `.tl-cel`, the cel strip — so choosing it was also the smallest change,
and the app already says "drawing" for the content, which is what frees the word
up: historically a cel IS the celluloid with the drawing on it, and that sense
has somewhere else to live here.
instance the `:kind`. The general thing, anywhere in a document.
cel an instance in a lane. UI labels, command names, prose.
lane the group with `:layout :sequence`.
drawing the content a cel names.
placement kept ONLY for where a node sits — `nest/placement` and the
transform that puts a face on the stage. Retired as a noun for the
node itself.
occurrence gone.
AND IT SETTLES A COLLISION I SHOULD HAVE SEEN EARLIER. `:time :expose` already
existed and means something else entirely: how many frames each step of a
subtree lasts, which is what shooting on twos is. Had the block been called an
exposure too, `node/expose`, `clock/exposed-frame` and `subs/render ::exposure`
would have been permanently confusable with it. Choosing `cel` lets the word
`exposure` keep the thing it actually names, and every remaining use of it in
`src` is now that one.
`:layout :sequence` stays as the field, and it is the one place two words are
kept deliberately: the layout names the RULE — children follow one another and
may not overlap — and a group carrying it is called a lane. `node/lane?` says so
where the two meet.
The second view is traditionally the exposure sheet. It will be the CEL SHEET,
for one vocabulary.
Renamed with a script and then read, because a blind pass does real damage: it
produced "an cel" thirty times, renamed the `::exposure` sub that is about the
`:expose` grid, and turned an "exposure grid" into a "cel grid" in two
docstrings. All three classes are fixed. `arthur.domain.sequence` is now
`arthur.domain.lane`, which is what its test file was already called.
424 tests, 5,749 assertions, and both browser flows — `test/browser/lane.mjs`,
renamed too.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
76106d36ee
commit
598c186c4f
21 changed files with 427 additions and 419 deletions
|
|
@ -9,7 +9,7 @@
|
|||
[arthur.domain.node :as node]
|
||||
[arthur.domain.palette :as pal]
|
||||
[arthur.domain.pick :as pick]
|
||||
[arthur.domain.sequence :as sequence]
|
||||
[arthur.domain.lane :as lane]
|
||||
[arthur.domain.symbol :as symbol]))
|
||||
|
||||
(defn drawing [id x frames]
|
||||
|
|
@ -18,17 +18,17 @@
|
|||
:channels {[:geom :size] (ch/framed 4)
|
||||
[:xform :pos] (ch/framed [x 0])}}}})
|
||||
|
||||
(defn occurrence [id source at duration speed]
|
||||
(defn cel [id source at duration speed]
|
||||
{:id id :kind :instance :parent :girl :z (name id)
|
||||
:source {:symbol source} :playback {:in 0 :speed speed :end :stop}
|
||||
:time {:at at :rate 1} :span [0 duration]})
|
||||
|
||||
(defn document []
|
||||
(let [a (occurrence :a :drawing-a 0 4 0)
|
||||
b (assoc-in (occurrence :b :drawing-b 4 4 0)
|
||||
(let [a (cel :a :drawing-a 0 4 0)
|
||||
b (assoc-in (cel :b :drawing-b 4 4 0)
|
||||
[:channels [:xform :pos]] (ch/keyed {0 [0 0] 1 [2 0]} :hold))
|
||||
insert (assoc-in (occurrence :insert :wave 8 4 1) [:playback :in] 3)]
|
||||
{:name "exposures" :fps 24 :width 320 :height 200
|
||||
insert (assoc-in (cel :insert :wave 8 4 1) [:playback :in] 3)]
|
||||
{:name "cels" :fps 24 :width 320 :height 200
|
||||
:symbols
|
||||
{:main {:id :main :frames 12
|
||||
:nodes {:girl {:id :girl :kind :group :layout :sequence :z "b"
|
||||
|
|
@ -58,9 +58,9 @@
|
|||
(into {} (map (fn [[f ops]] [f (js/Math.round (:plate ops))])) at)))
|
||||
(is (nil? (get-in at [4 [:a :mark]])) "half-open cuts have a single owner")))
|
||||
|
||||
(deftest exposure-ripple-keeps-lane-keys-and-moves-occurrence-corrections
|
||||
(deftest cel-ripple-keeps-lane-keys-and-moves-cel-corrections
|
||||
(let [doc (document)
|
||||
result (sequence/extend-hold doc :main :a 2 {:extent :grow-symbol})
|
||||
result (lane/extend-hold doc :main :a 2 {:extent :grow-symbol})
|
||||
after (:clip result)
|
||||
nodes (get-in after [:symbols :main :nodes])]
|
||||
(is (= :a (:selection result)))
|
||||
|
|
@ -78,19 +78,19 @@
|
|||
(is (= 320 (get-in at [10 [:insert :mark]])) "insert starts on source frame 3"))
|
||||
(is (empty? (clip/problems after)))
|
||||
(is (= (assoc-in doc [:symbols :main :frames] 14)
|
||||
(:clip (sequence/extend-hold after :main :a -2 {})))
|
||||
(:clip (lane/extend-hold after :main :a -2 {})))
|
||||
"shrinking restores content, except the explicitly grown shot")))
|
||||
|
||||
(deftest overflow-and-invalid-edits-are-atomic
|
||||
(let [doc (document)
|
||||
result (sequence/extend-hold doc :main :a 2 {})]
|
||||
result (lane/extend-hold doc :main :a 2 {})]
|
||||
(is (:refused result))
|
||||
(is (= 14 (:required-frames result)))
|
||||
(is (not (contains? result :clip)))
|
||||
(doseq [delta [0 -4 0.5 js/NaN]]
|
||||
(is (:refused (sequence/extend-hold doc :main :a delta {}))))
|
||||
(is (:refused (sequence/extend-hold doc :main :insert 1 {})))
|
||||
(is (:refused (sequence/extend-hold doc :main :missing 1 {})))))
|
||||
(is (:refused (lane/extend-hold doc :main :a delta {}))))
|
||||
(is (:refused (lane/extend-hold doc :main :insert 1 {})))
|
||||
(is (:refused (lane/extend-hold doc :main :missing 1 {})))))
|
||||
|
||||
(deftest a-gap-is-an-uncovered-interval
|
||||
(let [doc (update-in (document) [:symbols :main :nodes] dissoc :b)
|
||||
|
|
@ -148,8 +148,8 @@
|
|||
(is (= 99 (get-in (sample edited [0 4]) [0 [:a :mark]])))
|
||||
(is (= 139 (get-in (sample edited [0 4]) [4 [:b :mark]]))))))
|
||||
|
||||
(deftest occurrence-identities-and-playback-round-trip
|
||||
(let [doc (:clip (sequence/extend-hold (document) :main :a 2 {:extent :grow-symbol}))
|
||||
(deftest cel-identities-and-playback-round-trip
|
||||
(let [doc (:clip (lane/extend-hold (document) :main :a 2 {:extent :grow-symbol}))
|
||||
leaves (leaf/leaves :project doc)]
|
||||
(is (= doc (leaf/clip :project leaves)))
|
||||
(is (contains? leaves "clip/project/symbol/main/node/a"))
|
||||
|
|
@ -163,7 +163,7 @@
|
|||
|
||||
(deftest one-transaction-undoes-the-ripple-and-shot-extension
|
||||
(let [doc (document)
|
||||
after (:clip (sequence/extend-hold doc :main :a 2 {:extent :grow-symbol}))
|
||||
after (:clip (lane/extend-hold doc :main :a 2 {:extent :grow-symbol}))
|
||||
before-leaves (leaf/leaves :p doc)
|
||||
after-leaves (leaf/leaves :p after)
|
||||
h (-> nil history/hold (history/record before-leaves after-leaves 0) history/settle)
|
||||
|
|
@ -174,19 +174,19 @@
|
|||
(is (= after-leaves (:leaves redo)))))
|
||||
|
||||
(deftest create-lane-and-append-drawings
|
||||
(let [doc (:clip (sequence/add-lane (clip/blank) :main :girl))
|
||||
a (:clip (sequence/append-drawing doc :main :girl :a :drawing-a {}))
|
||||
b (:clip (sequence/append-drawing a :main :girl :b :drawing-b {}))]
|
||||
(let [doc (:clip (lane/add-lane (clip/blank) :main :girl))
|
||||
a (:clip (lane/append-drawing doc :main :girl :a :drawing-a {}))
|
||||
b (:clip (lane/append-drawing a :main :girl :b :drawing-b {}))]
|
||||
(is (empty? (clip/problems b)))
|
||||
(is (= [1 2] (node/placed-span (get-in b [:symbols :main :nodes :b]))))
|
||||
(is (= 0 (get-in b [:symbols :main :nodes :b :playback :speed])))
|
||||
(is (:refused (sequence/append-drawing b :main :girl :a :new {})))))
|
||||
(is (:refused (lane/append-drawing b :main :girl :a :new {})))))
|
||||
|
||||
(deftest fractional-placement-rates-convert-the-hold-delta
|
||||
(let [doc (-> (document)
|
||||
(assoc-in [:symbols :main :nodes :a :time :rate] 2)
|
||||
(assoc-in [:symbols :main :nodes :a :span] [0 8]))
|
||||
after (:clip (sequence/extend-hold doc :main :a 2 {:extent :grow-symbol}))]
|
||||
after (:clip (lane/extend-hold doc :main :a 2 {:extent :grow-symbol}))]
|
||||
(is (= [0 12] (get-in after [:symbols :main :nodes :a :span])))
|
||||
(is (= [6 10] (node/placed-span (get-in after [:symbols :main :nodes :b]))))))
|
||||
|
||||
|
|
@ -196,7 +196,7 @@
|
|||
((symbol/resolver sym nil pal/index-of nil) 0))
|
||||
"omitted style colour must not crash a missing cursor")))
|
||||
|
||||
(deftest audio-follows-only-the-playing-occurrence
|
||||
(deftest audio-follows-only-the-playing-cel
|
||||
(let [voice {:id :voice :kind :audio :z "a" :source {:sound "voice"}
|
||||
:span [0 10]
|
||||
:channels {[:audio :gain] (ch/keyed {0 0 5 1} :linear)}}
|
||||
|
|
@ -208,7 +208,7 @@
|
|||
(is (= [8 12] (node/placed-span track)))
|
||||
(is (= [3 7] (:span track)) "the source in-point trims the audio too")
|
||||
(is (= {5 0 10 1} (get-in track [:channels [:audio :gain] :keys])))
|
||||
(let [moved (:clip (sequence/extend-hold doc :main :a 2 {:extent :grow-symbol}))
|
||||
(let [moved (:clip (lane/extend-hold doc :main :a 2 {:extent :grow-symbol}))
|
||||
[track] (nest/audio-tracks moved :main)]
|
||||
(is (= [10 14] (node/placed-span track)))
|
||||
(is (= [3 7] (:span track))))
|
||||
|
|
@ -231,56 +231,56 @@
|
|||
|
||||
(deftest enclosing-retiming-is-respected-when-extending-the-shot
|
||||
(let [doc (assoc-in (document) [:symbols :main :nodes :girl :time] {:at 8 :rate 2})
|
||||
result (sequence/extend-hold doc :main :a 2 {})]
|
||||
result (lane/extend-hold doc :main :a 2 {})]
|
||||
(is (= 15 (:required-frames result)))
|
||||
(is (nil? (:clip result)))
|
||||
(is (= 15 (get-in (sequence/extend-hold doc :main :a 2 {:extent :grow-symbol})
|
||||
(is (= 15 (get-in (lane/extend-hold doc :main :a 2 {:extent :grow-symbol})
|
||||
[:clip :symbols :main :frames])))))
|
||||
|
||||
(deftest reuse-shares-content-and-make-unique-decouples-one-exposure
|
||||
(deftest reuse-shares-content-and-make-unique-decouples-one-cel
|
||||
(let [doc (document)
|
||||
shared (:clip (sequence/reuse-drawing doc :main :girl :c :drawing-a
|
||||
shared (:clip (lane/reuse-drawing doc :main :girl :c :drawing-a
|
||||
{:extent :grow-symbol}))
|
||||
edit (fn [c sym x]
|
||||
(assoc-in c [:symbols sym :nodes :mark :channels [:xform :pos]]
|
||||
(ch/framed [x 0])))]
|
||||
(is (:refused (sequence/reuse-drawing doc :main :girl :c :drawing-a {}))
|
||||
(is (:refused (lane/reuse-drawing doc :main :girl :c :drawing-a {}))
|
||||
"the shot has to be extended on purpose")
|
||||
(is (= :drawing-a (node/source (get-in shared [:symbols :main :nodes :c]))))
|
||||
(is (= [12 13] (node/placed-span (get-in shared [:symbols :main :nodes :c]))))
|
||||
(is (empty? (clip/problems shared)))
|
||||
;; One drawing, two exposures: the edit arrives at both.
|
||||
;; One drawing, two cels: the edit arrives at both.
|
||||
(let [at (sample (edit shared :drawing-a 99) [0 12])]
|
||||
(is (= 99 (get-in at [0 [:a :mark]])))
|
||||
(is (= 99 (get-in at [12 [:c :mark]]))))
|
||||
(let [unique (:clip (sequence/make-unique shared :main :c {}))]
|
||||
(let [unique (:clip (lane/make-unique shared :main :c {}))]
|
||||
(is (= :drawing-a-2 (node/source (get-in unique [:symbols :main :nodes :c]))))
|
||||
(is (= (:nodes (get-in shared [:symbols :drawing-a]))
|
||||
(:nodes (get-in unique [:symbols :drawing-a-2])))
|
||||
"a copy of the same drawing, not an empty one")
|
||||
(is (= :drawing-a (node/source (get-in unique [:symbols :main :nodes :a])))
|
||||
"the other exposure keeps the original")
|
||||
"the other cel keeps the original")
|
||||
(let [at (sample (edit unique :drawing-a 99) [0 12])]
|
||||
(is (= 99 (get-in at [0 [:a :mark]])))
|
||||
(is (= 10 (get-in at [12 [:c :mark]])) "the exposure made unique is untouched"))
|
||||
(is (= 10 (get-in at [12 [:c :mark]])) "the cel made unique is untouched"))
|
||||
(let [at (sample (edit unique :drawing-a-2 99) [0 12])]
|
||||
(is (= 10 (get-in at [0 [:a :mark]])) "and does not reach back"))
|
||||
(is (empty? (clip/problems unique))))
|
||||
;; Nothing else places drawing-b, so there is nothing to decouple from.
|
||||
(is (:refused (sequence/make-unique doc :main :b {})))
|
||||
(is (:refused (sequence/make-unique doc :main :girl {}))
|
||||
(is (:refused (lane/make-unique doc :main :b {})))
|
||||
(is (:refused (lane/make-unique doc :main :girl {}))
|
||||
"a lane places nothing itself")))
|
||||
|
||||
(deftest duplicate-copies-the-drawing-and-not-the-exposure
|
||||
(deftest duplicate-copies-the-drawing-and-not-the-cel
|
||||
(let [doc (document)
|
||||
made (:clip (sequence/duplicate-drawing doc :main :b :d {:extent :grow-symbol}))
|
||||
made (:clip (lane/duplicate-drawing doc :main :b :d {:extent :grow-symbol}))
|
||||
n (get-in made [:symbols :main :nodes :d])]
|
||||
(is (= :drawing-b-2 (node/source n)))
|
||||
(is (= (:nodes (get-in doc [:symbols :drawing-b]))
|
||||
(:nodes (get-in made [:symbols :drawing-b-2]))))
|
||||
(is (= [12 13] (node/placed-span n)))
|
||||
(is (= {:in 0 :speed 0 :end :stop} (:playback n)))
|
||||
(is (nil? (:channels n)) "B's own position correction belongs to B's exposure")
|
||||
(is (nil? (:channels n)) "B's own position correction belongs to B's cel")
|
||||
(is (= (get-in doc [:symbols :main :nodes :b])
|
||||
(get-in made [:symbols :main :nodes :b]))
|
||||
"the drawing duplicated is left as it was")
|
||||
|
|
@ -293,7 +293,7 @@
|
|||
{:id :part :kind :instance :z "b" :span [0 1]
|
||||
:time {:at 0 :rate 1} :source {:symbol :wave}
|
||||
:playback {:in 0 :speed 0 :end :stop}})
|
||||
copy (fn [opts] (:clip (sequence/duplicate-drawing
|
||||
copy (fn [opts] (:clip (lane/duplicate-drawing
|
||||
doc :main :a :d (merge {:extent :grow-symbol} opts))))
|
||||
shallow (copy {})
|
||||
deep (copy {:deep? true})]
|
||||
|
|
@ -306,19 +306,19 @@
|
|||
|
||||
(deftest reuse-refuses-what-would-not-be-a-document
|
||||
(let [doc (document)]
|
||||
(is (:refused (sequence/reuse-drawing doc :main :girl :c :nothing-here {})))
|
||||
(is (:refused (sequence/reuse-drawing doc :main :girl :c :main {:extent :grow-symbol}))
|
||||
(is (:refused (lane/reuse-drawing doc :main :girl :c :nothing-here {})))
|
||||
(is (:refused (lane/reuse-drawing doc :main :girl :c :main {:extent :grow-symbol}))
|
||||
"a symbol cannot go inside itself")
|
||||
(is (:refused (sequence/reuse-drawing doc :main :girl :a :drawing-a {:extent :grow-symbol}))
|
||||
"an occurrence ID in use is not free")
|
||||
(is (:refused (sequence/reuse-drawing doc :main :plate :c :drawing-a {})))
|
||||
(is (:refused (sequence/duplicate-drawing doc :main :girl :d {})))))
|
||||
(is (:refused (lane/reuse-drawing doc :main :girl :a :drawing-a {:extent :grow-symbol}))
|
||||
"a cel ID in use is not free")
|
||||
(is (:refused (lane/reuse-drawing doc :main :plate :c :drawing-a {})))
|
||||
(is (:refused (lane/duplicate-drawing doc :main :girl :d {})))))
|
||||
|
||||
(deftest drawing-on-twos-does-not-quantize-the-lane-transform
|
||||
;; Exposure length IS the drawing cadence, and it is the only thing on twos
|
||||
;; Cel length IS the drawing cadence, and it is the only thing on twos
|
||||
;; here: the lane's transform has its own clock and keeps moving every frame.
|
||||
;; Stepping it would be the cel cadence leaking into continuous motion.
|
||||
(let [cel (fn [id source at] (occurrence id source at 2 0))
|
||||
(let [cel (fn [id source at] (cel id source at 2 0))
|
||||
doc (-> (document)
|
||||
(update-in [:symbols :main :nodes] dissoc :a :b :insert)
|
||||
(update-in [:symbols :main :nodes] merge
|
||||
|
|
@ -337,7 +337,7 @@
|
|||
|
||||
(defn- drawn
|
||||
"What every frame draws, as sorted values, so a picture can be compared
|
||||
without naming the occurrences that produced it."
|
||||
without naming the cels that produced it."
|
||||
[doc fs]
|
||||
(let [at (sample doc fs)]
|
||||
(mapv #(sort (vals (get at %))) fs)))
|
||||
|
|
@ -347,7 +347,7 @@
|
|||
keys-of #(get-in % [:symbols :main :nodes :girl :channels [:xform :pos] :keys])
|
||||
spans #(mapv (fn [id] (node/placed-span (get-in % [:symbols :main :nodes id])))
|
||||
[:a :n :b :insert])
|
||||
r (sequence/append-drawing doc :main :girl :n :drawing-n
|
||||
r (lane/append-drawing doc :main :girl :n :drawing-n
|
||||
{:at 4 :extent :grow-symbol})]
|
||||
(is (= [[0 4] [4 5] [5 9] [9 13]] (spans (:clip r))))
|
||||
(is (= 13 (get-in r [:clip :symbols :main :frames])))
|
||||
|
|
@ -356,45 +356,45 @@
|
|||
(is (= 4 (:frame r)))
|
||||
(is (empty? (clip/problems (:clip r))))
|
||||
;; The same command with no room refuses, and says how much it needs.
|
||||
(is (= 13 (:required-frames (sequence/append-drawing doc :main :girl :n :drawing-n {:at 4}))))
|
||||
(is (= 13 (:required-frames (lane/append-drawing doc :main :girl :n :drawing-n {:at 4}))))
|
||||
;; At the very front everything moves.
|
||||
(is (= [[1 5] [0 1] [5 9] [9 13]]
|
||||
(spans (:clip (sequence/append-drawing doc :main :girl :n :drawing-n
|
||||
(spans (:clip (lane/append-drawing doc :main :girl :n :drawing-n
|
||||
{:at 0 :extent :grow-symbol})))))
|
||||
;; Inside an exposure is not a position for another one.
|
||||
;; Inside a cel is not a position for another one.
|
||||
(is (re-find #"split it first"
|
||||
(:refused (sequence/append-drawing doc :main :girl :n :drawing-n
|
||||
(:refused (lane/append-drawing doc :main :girl :n :drawing-n
|
||||
{:at 2 :extent :grow-symbol}))))
|
||||
(is (:refused (sequence/append-drawing doc :main :girl :n :drawing-n
|
||||
(is (:refused (lane/append-drawing doc :main :girl :n :drawing-n
|
||||
{:at -1 :extent :grow-symbol})))
|
||||
(is (:refused (sequence/append-drawing doc :main :girl :n :drawing-n
|
||||
(is (:refused (lane/append-drawing doc :main :girl :n :drawing-n
|
||||
{:at ##Inf :extent :grow-symbol})))
|
||||
;; Reuse and duplicate take a position too; it is one placement rule.
|
||||
(is (= [4 5] (node/placed-span
|
||||
(get-in (sequence/reuse-drawing doc :main :girl :n :drawing-b
|
||||
(get-in (lane/reuse-drawing doc :main :girl :n :drawing-b
|
||||
{:at 4 :extent :grow-symbol})
|
||||
[:clip :symbols :main :nodes :n]))))
|
||||
(is (= [4 5] (node/placed-span
|
||||
(get-in (sequence/duplicate-drawing doc :main :b :n
|
||||
(get-in (lane/duplicate-drawing doc :main :b :n
|
||||
{:at 4 :extent :grow-symbol})
|
||||
[:clip :symbols :main :nodes :n]))))))
|
||||
|
||||
(deftest splitting-an-exposure-changes-nothing-that-is-drawn
|
||||
(deftest splitting-an-cel-changes-nothing-that-is-drawn
|
||||
(let [doc (document)
|
||||
fs (range 12)
|
||||
before (drawn doc fs)]
|
||||
(doseq [[label id cut] [["a held drawing" :a 2]
|
||||
["an exposure with a correction of its own" :b 6]
|
||||
["a cel with a correction of its own" :b 6]
|
||||
["a playing insert" :insert 10]]]
|
||||
(testing label
|
||||
(let [r (sequence/split doc :main id cut :right)
|
||||
(let [r (lane/split doc :main id cut :right)
|
||||
after (:clip r)]
|
||||
(is (= :right (:selection r)))
|
||||
(is (= before (drawn after fs)) "the same picture, frame for frame")
|
||||
(is (= (node/placed-span (get-in doc [:symbols :main :nodes id]))
|
||||
[(first (node/placed-span (get-in after [:symbols :main :nodes id])))
|
||||
(second (node/placed-span (get-in after [:symbols :main :nodes :right])))])
|
||||
"the pieces occupy the frames the exposure did")
|
||||
"the pieces occupy the frames the cel did")
|
||||
(is (= cut (second (node/placed-span (get-in after [:symbols :main :nodes id])))
|
||||
(first (node/placed-span (get-in after [:symbols :main :nodes :right])))))
|
||||
(is (= (:time (get-in doc [:symbols :main :nodes id]))
|
||||
|
|
@ -405,19 +405,19 @@
|
|||
(is (= 12 (get-in after [:symbols :main :frames])) "and no shot-length question")
|
||||
(is (empty? (clip/problems after))))))))
|
||||
|
||||
(deftest split-refuses-anything-that-is-not-one-cut-inside-one-exposure
|
||||
(deftest split-refuses-anything-that-is-not-one-cut-inside-one-cel
|
||||
(let [doc (document)]
|
||||
(doseq [cut [0 4 8 12 -1 2.5 ##NaN nil]]
|
||||
(is (:refused (sequence/split doc :main :b cut :right)) (str "cut at " (pr-str cut))))
|
||||
(is (:refused (sequence/split doc :main :girl 2 :right)) "a lane is not an exposure")
|
||||
(is (:refused (sequence/split doc :main :plate 2 :right)) "nor is a shape outside one")
|
||||
(is (:refused (sequence/split doc :main :a 2 :b)) "the new ID has to be free")))
|
||||
(is (:refused (lane/split doc :main :b cut :right)) (str "cut at " (pr-str cut))))
|
||||
(is (:refused (lane/split doc :main :girl 2 :right)) "a lane is not a cel")
|
||||
(is (:refused (lane/split doc :main :plate 2 :right)) "nor is a shape outside one")
|
||||
(is (:refused (lane/split doc :main :a 2 :b)) "the new ID has to be free")))
|
||||
|
||||
(deftest split-then-place-puts-a-drawing-inside-a-hold
|
||||
;; The two commands the doc asks for, composed: neither one guesses.
|
||||
(let [doc (document)
|
||||
cut (:clip (sequence/split doc :main :a 2 :right))
|
||||
r (sequence/append-drawing cut :main :girl :n :drawing-n
|
||||
cut (:clip (lane/split doc :main :a 2 :right))
|
||||
r (lane/append-drawing cut :main :girl :n :drawing-n
|
||||
{:at 2 :extent :grow-symbol})
|
||||
after (:clip r)]
|
||||
(is (= [[0 2] [2 3] [3 5] [5 9] [9 13]]
|
||||
|
|
@ -459,14 +459,14 @@
|
|||
(is (= b (:leaves (history/undo h a))))
|
||||
(is (= c (leaf/clip :p a)) "a correction needs no codec of its own"))))
|
||||
|
||||
(deftest a-correction-on-one-exposure-travels-with-it
|
||||
;; The other half of ownership: a layer on an occurrence is in that
|
||||
;; occurrence's own frames, so moving the exposure moves the correction and
|
||||
(deftest a-correction-on-one-cel-travels-with-it
|
||||
;; The other half of ownership: a layer on a cel is in that
|
||||
;; cel's own frames, so moving the cel moves the correction and
|
||||
;; nothing has to say so.
|
||||
(let [beat (ch/layer :beat [0 2] :offset (ch/framed [7 0]))
|
||||
doc (update-in (document) [:symbols :main :nodes :b :channels [:xform :pos] :over]
|
||||
(fnil conj []) beat)
|
||||
moved (:clip (sequence/extend-hold doc :main :a 2 {:extent :grow-symbol}))]
|
||||
moved (:clip (lane/extend-hold doc :main :a 2 {:extent :grow-symbol}))]
|
||||
;; Stated as the difference from the same document without the correction,
|
||||
;; so the claim is about WHERE the layer applies and not about arithmetic.
|
||||
(let [nudge (fn [with without f]
|
||||
|
|
@ -475,7 +475,7 @@
|
|||
(is (= [7 7 0 0] (mapv #(nudge doc (document) %) [4 5 6 7]))
|
||||
"B's first two frames, which are lane frames 4 and 5")
|
||||
(is (= [7 7 0 0]
|
||||
(mapv #(nudge moved (:clip (sequence/extend-hold (document) :main :a 2
|
||||
(mapv #(nudge moved (:clip (lane/extend-hold (document) :main :a 2
|
||||
{:extent :grow-symbol}))
|
||||
%)
|
||||
[6 7 8 9]))
|
||||
|
|
@ -488,9 +488,9 @@
|
|||
(defn- spans [clip ids]
|
||||
(mapv #(node/placed-span (get-in clip [:symbols :main :nodes %])) ids))
|
||||
|
||||
(deftest trimming-narrows-one-exposure-and-moves-nothing-else
|
||||
(deftest trimming-narrows-one-cel-and-moves-nothing-else
|
||||
(let [doc (document)
|
||||
r (sequence/trim doc :main :b :out 6)
|
||||
r (lane/trim doc :main :b :out 6)
|
||||
after (:clip r)]
|
||||
(is (= [[0 4] [4 6] [8 12]] (spans after [:a :b :insert])))
|
||||
(is (= :b (:selection r)))
|
||||
|
|
@ -505,7 +505,7 @@
|
|||
;; were, so the frames that survive show exactly what they showed.
|
||||
(let [doc (document)
|
||||
before (sample doc [10 11])
|
||||
after (:clip (sequence/trim doc :main :insert :in 10))]
|
||||
after (:clip (lane/trim doc :main :insert :in 10))]
|
||||
(is (= [10 12] (node/placed-span (get-in after [:symbols :main :nodes :insert]))))
|
||||
(is (= (:playback (get-in doc [:symbols :main :nodes :insert]))
|
||||
(:playback (get-in after [:symbols :main :nodes :insert]))))
|
||||
|
|
@ -520,13 +520,13 @@
|
|||
["past its end" :out 9]
|
||||
["before its start" :in 2]
|
||||
["off a whole frame" :out 5.5]]]
|
||||
(is (:refused (sequence/trim doc :main :b edge to)) label))
|
||||
(is (:refused (sequence/trim doc :main :b :middle 6)))
|
||||
(is (:refused (sequence/trim doc :main :girl :out 6)) "a lane is not an exposure")))
|
||||
(is (:refused (lane/trim doc :main :b edge to)) label))
|
||||
(is (:refused (lane/trim doc :main :b :middle 6)))
|
||||
(is (:refused (lane/trim doc :main :girl :out 6)) "a lane is not a cel")))
|
||||
|
||||
(deftest moving-an-exposure-keeps-its-length-and-its-source-origin
|
||||
(deftest moving-an-cel-keeps-its-length-and-its-source-origin
|
||||
(let [doc (update-in (document) [:symbols :main :nodes] dissoc :b)
|
||||
r (sequence/move doc :main :insert 4)
|
||||
r (lane/move doc :main :insert 4)
|
||||
after (:clip r)]
|
||||
(is (= [[0 4] [4 8]] (spans after [:a :insert])))
|
||||
(is (= :insert (:selection r)))
|
||||
|
|
@ -539,19 +539,19 @@
|
|||
|
||||
(deftest a-move-onto-an-occupied-frame-is-refused-rather-than-rippled
|
||||
(let [doc (document)]
|
||||
(is (:refused (sequence/move doc :main :insert 6)) "it would overlap B")
|
||||
(is (:refused (sequence/move doc :main :insert 4.5)))
|
||||
(is (:refused (sequence/move doc :main :girl 2)))
|
||||
(is (:refused (lane/move doc :main :insert 6)) "it would overlap B")
|
||||
(is (:refused (lane/move doc :main :insert 4.5)))
|
||||
(is (:refused (lane/move doc :main :girl 2)))
|
||||
;; Clearing the room first is the composition, and then it goes.
|
||||
(let [cleared (:clip (sequence/blank doc :main :girl [4 8] {}))]
|
||||
(is (= [[0 4] [4 8]] (spans (:clip (sequence/move cleared :main :insert 4))
|
||||
(let [cleared (:clip (lane/blank doc :main :girl [4 8] {}))]
|
||||
(is (= [[0 4] [4 8]] (spans (:clip (lane/move cleared :main :insert 4))
|
||||
[:a :insert]))))))
|
||||
|
||||
(deftest blanking-leaves-a-gap-and-does-not-close-it
|
||||
(let [doc (document)
|
||||
r (sequence/blank doc :main :girl [5 7] {:id :rest})
|
||||
r (lane/blank doc :main :girl [5 7] {:id :rest})
|
||||
after (:clip r)]
|
||||
;; B spanned the range, so it became two exposures with a hole between them.
|
||||
;; B spanned the range, so it became two cels with a hole between them.
|
||||
(is (= [[0 4] [4 5] [7 8] [8 12]] (spans after [:a :b :rest :insert])))
|
||||
(is (= :rest (:selection r)))
|
||||
(let [at (sample after [4 5 6 7])]
|
||||
|
|
@ -562,9 +562,9 @@
|
|||
(is (= 12 (get-in after [:symbols :main :frames])))
|
||||
(is (empty? (clip/problems after)))))
|
||||
|
||||
(deftest blanking-a-whole-exposure-removes-it-and-keeps-its-drawing
|
||||
(deftest blanking-a-whole-cel-removes-it-and-keeps-its-drawing
|
||||
(let [doc (document)
|
||||
after (:clip (sequence/blank doc :main :girl [4 8] {}))]
|
||||
after (:clip (lane/blank doc :main :girl [4 8] {}))]
|
||||
(is (nil? (get-in after [:symbols :main :nodes :b])))
|
||||
(is (= [[0 4] [8 12]] (spans after [:a :insert])) "and moves nothing")
|
||||
(is (= (get-in doc [:symbols :drawing-b]) (get-in after [:symbols :drawing-b]))
|
||||
|
|
@ -573,7 +573,7 @@
|
|||
|
||||
(deftest blanking-a-range-trims-what-it-only-partly-covers
|
||||
(let [doc (document)
|
||||
after (:clip (sequence/blank doc :main :girl [3 9] {}))]
|
||||
after (:clip (lane/blank doc :main :girl [3 9] {}))]
|
||||
(is (= [[0 3] [9 12]] (spans after [:a :insert])))
|
||||
(is (nil? (get-in after [:symbols :main :nodes :b])))
|
||||
(is (= (get-in (sample doc [9]) [9 [:insert :mark]])
|
||||
|
|
@ -583,28 +583,28 @@
|
|||
|
||||
(deftest blank-refuses-what-it-cannot-do-in-one-piece
|
||||
(let [doc (document)]
|
||||
(is (re-find #"free ID" (:refused (sequence/blank doc :main :girl [5 7] {})))
|
||||
"splitting an exposure needs an ID for the remainder")
|
||||
(is (:refused (sequence/blank doc :main :girl [5 7] {:id :a})) "and a free one")
|
||||
(is (:refused (sequence/blank doc :main :girl [7 5] {})))
|
||||
(is (:refused (sequence/blank doc :main :girl [5 5] {})))
|
||||
(is (:refused (sequence/blank doc :main :girl [5 6.5] {})))
|
||||
(is (:refused (sequence/blank doc :main :plate [0 2] {})))))
|
||||
(is (re-find #"free ID" (:refused (lane/blank doc :main :girl [5 7] {})))
|
||||
"splitting a cel needs an ID for the remainder")
|
||||
(is (:refused (lane/blank doc :main :girl [5 7] {:id :a})) "and a free one")
|
||||
(is (:refused (lane/blank doc :main :girl [7 5] {})))
|
||||
(is (:refused (lane/blank doc :main :girl [5 5] {})))
|
||||
(is (:refused (lane/blank doc :main :girl [5 6.5] {})))
|
||||
(is (:refused (lane/blank doc :main :plate [0 2] {})))))
|
||||
|
||||
(deftest the-shot-length-is-authored-and-emptying-a-lane-does-not-shorten-it
|
||||
;; The window and the occupied extent are two facts. A shot with nothing in
|
||||
;; the last half is a shot somebody authored that long, and deleting the last
|
||||
;; drawing must not quietly shorten the film.
|
||||
(let [doc (document)
|
||||
empty-lane (:clip (sequence/blank doc :main :girl [0 12] {}))]
|
||||
(is (empty? (symbol/sequence-members (get-in empty-lane [:symbols :main :nodes]) :girl)))
|
||||
empty-lane (:clip (lane/blank doc :main :girl [0 12] {}))]
|
||||
(is (empty? (symbol/lane-cels (get-in empty-lane [:symbols :main :nodes]) :girl)))
|
||||
(is (= 12 (get-in empty-lane [:symbols :main :frames])))
|
||||
(is (empty? (clip/problems empty-lane)))
|
||||
;; Growing is still the caller's word, and only ever grows.
|
||||
(is (:refused (sequence/append-drawing empty-lane :main :girl :n :drawing-n {:at 20})))
|
||||
(is (= 21 (get-in (sequence/append-drawing empty-lane :main :girl :n :drawing-n
|
||||
(is (:refused (lane/append-drawing empty-lane :main :girl :n :drawing-n {:at 20})))
|
||||
(is (= 21 (get-in (lane/append-drawing empty-lane :main :girl :n :drawing-n
|
||||
{:at 20 :extent :grow-symbol})
|
||||
[:clip :symbols :main :frames])))
|
||||
(is (= 12 (get-in (:clip (sequence/trim doc :main :insert :out 9))
|
||||
(is (= 12 (get-in (:clip (lane/trim doc :main :insert :out 9))
|
||||
[:symbols :main :frames]))
|
||||
"and trimming the last exposure leaves the window where it was")))
|
||||
"and trimming the last cel leaves the window where it was")))
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
(ns arthur.events.sequence-test
|
||||
(ns arthur.events.lane-test
|
||||
(:require [cljs.test :refer [deftest is]]
|
||||
[arthur.domain.lane-test :as fixture]
|
||||
[arthur.domain.sequence :as sequence]
|
||||
[arthur.domain.lane :as lane]
|
||||
[arthur.events.ui :as ui]
|
||||
[arthur.domain.history :as history]
|
||||
[arthur.domain.leaf :as leaf]
|
||||
[arthur.footage.store :as store]
|
||||
[arthur.ui.timeline :as timeline]))
|
||||
|
||||
(deftest one-row-projects-all-occurrences-and-keeps-selection-addresses
|
||||
(deftest one-row-projects-all-cels-and-keeps-selection-addresses
|
||||
(let [doc (fixture/document)
|
||||
rows (timeline/rows doc :main #{})
|
||||
lane (first (filter :cels rows))]
|
||||
|
|
@ -24,15 +24,15 @@
|
|||
id (store/install! {:clip doc :store {}} "sequence-test")
|
||||
db {:clip/current id :paint/revision 0
|
||||
:ui {:open :main :selection [:node :main :a [:a]]}}
|
||||
refused (ui/apply-sequence-command db :main
|
||||
(sequence/extend-hold doc :main :a 1 {}) [:retry])]
|
||||
refused (ui/apply-lane-command db :main
|
||||
(lane/extend-hold doc :main :a 1 {}) [:retry])]
|
||||
(is (= doc (:clip (store/entry id))))
|
||||
(is (nil? (:history (store/entry id))))
|
||||
(is (= [:retry] (get-in refused [:ui :sequence-retry])))
|
||||
(let [r1 (sequence/extend-hold doc :main :a 1 {:extent :grow-symbol})
|
||||
db1 (ui/apply-sequence-command db :main r1 nil)
|
||||
r2 (sequence/extend-hold (:clip r1) :main :a 1 {:extent :grow-symbol})
|
||||
db2 (ui/apply-sequence-command db1 :main r2 nil)
|
||||
(is (= [:retry] (get-in refused [:ui :lane-retry])))
|
||||
(let [r1 (lane/extend-hold doc :main :a 1 {:extent :grow-symbol})
|
||||
db1 (ui/apply-lane-command db :main r1 nil)
|
||||
r2 (lane/extend-hold (:clip r1) :main :a 1 {:extent :grow-symbol})
|
||||
db2 (ui/apply-lane-command db1 :main r2 nil)
|
||||
h (:history (store/entry id))
|
||||
undo (history/undo h (leaf/leaves "u" (:clip r2)))
|
||||
undo2 (history/undo (:history undo) (:leaves undo))]
|
||||
|
|
@ -59,7 +59,7 @@ try {
|
|||
await evaluate(`(() => {
|
||||
const k = cljs.core.keyword;
|
||||
cljs.core.swap_BANG_(re_frame.db.app_db, db => cljs.core.assoc(db, k('route'), k('local-test')));
|
||||
window.sequenceSnapshot = () => {
|
||||
window.laneSnapshot = () => {
|
||||
const db = cljs.core.deref(re_frame.db.app_db);
|
||||
const entry = arthur.footage.store.entry(cljs.core.get(db, k('clip/current')));
|
||||
return cljs.core.clj__GT_js(entry);
|
||||
|
|
@ -75,7 +75,7 @@ try {
|
|||
})()`), `enabled button: ${label}`);
|
||||
await sleep(180);
|
||||
};
|
||||
const shot = async () => (await evaluate('sequenceSnapshot()'));
|
||||
const shot = async () => (await evaluate('laneSnapshot()'));
|
||||
const instances = s => Object.values(s.clip.symbols.main.nodes).filter(n => n.kind === 'instance')
|
||||
.sort((a, b) => a.time.at - b.time.at);
|
||||
await click('+ lane');
|
||||
|
|
@ -90,7 +90,7 @@ try {
|
|||
assert.equal(await evaluate('document.querySelectorAll(".tl-label:not(.tl-corner)").length'), 1);
|
||||
assert.equal(await evaluate(`cljs.core.get_in(cljs.core.deref(re_frame.db.app_db),
|
||||
cljs.core.vector(cljs.core.keyword('playback'), cljs.core.keyword('frame')))`), 4,
|
||||
'new drawing seeks to its occurrence');
|
||||
'new drawing seeks to its cel');
|
||||
|
||||
// Shorten this test shot to the occupied extent, purely in memory.
|
||||
await evaluate(`(() => {
|
||||
|
|
@ -112,9 +112,9 @@ try {
|
|||
assert.equal(s.history.done.length, before.history.done.length + 1);
|
||||
await evaluate(`document.dispatchEvent(new KeyboardEvent('keydown', {key:'z', ctrlKey:true, bubbles:true}))`);
|
||||
await sleep(250);
|
||||
assert.deepEqual((await shot()).clip, before.clip, 'one undo restores exposure, ripple, and shot length');
|
||||
assert.deepEqual((await shot()).clip, before.clip, 'one undo restores cel, ripple, and shot length');
|
||||
|
||||
// Sharing: one drawing exposed twice, then one exposure decoupled. Room is
|
||||
// Sharing: one drawing exposed twice, then one cel decoupled. Room is
|
||||
// made first so these assertions are about content and not about overflow.
|
||||
await evaluate(`(() => {
|
||||
const k = cljs.core.keyword, db = cljs.core.deref(re_frame.db.app_db);
|
||||
|
|
@ -135,19 +135,19 @@ try {
|
|||
assert.equal(cels[2].source.symbol, cels[0].source.symbol, 'reuse exposes the same drawing');
|
||||
assert.equal(await evaluate('document.querySelectorAll(".tl-cel").length'), 3);
|
||||
assert.equal(await evaluate('document.querySelectorAll(".tl-label:not(.tl-corner)").length'), 1,
|
||||
'three exposures, still one row');
|
||||
'three cels, still one row');
|
||||
assert.equal(await enabled('make unique'), true);
|
||||
await click('make unique');
|
||||
s = await shot();
|
||||
cels = instances(s);
|
||||
assert.notEqual(cels[2].source.symbol, cels[0].source.symbol, 'that exposure has its own drawing');
|
||||
assert.notEqual(cels[2].source.symbol, cels[0].source.symbol, 'that cel has its own drawing');
|
||||
assert.equal(await enabled('make unique'), false, 'and is not shared any more');
|
||||
await click('duplicate');
|
||||
s = await shot();
|
||||
cels = instances(s);
|
||||
assert.equal(cels.length, 4);
|
||||
assert.equal(new Set(cels.map(n => n.source.symbol)).size, 4,
|
||||
'four exposures of four drawings: nothing is shared once every copy is made');
|
||||
'four cels of four drawings: nothing is shared once every copy is made');
|
||||
assert.equal(s.history.done.length, before.history.done.length + 3, 'three more commands, three more steps');
|
||||
|
||||
// A drawing into the middle of a hold: split, then insert. Both act at the
|
||||
|
|
@ -158,25 +158,25 @@ try {
|
|||
assert.deepEqual(placed(s), [[0, 4], [4, 5], [5, 6], [6, 7]]);
|
||||
await evaluate(`document.querySelector('.tl-cel').click()`);
|
||||
await sleep(200);
|
||||
assert.equal(await enabled('split'), false, 'the start of an exposure is not inside it');
|
||||
assert.equal(await enabled('split'), false, 'the start of a cel is not inside it');
|
||||
await click('+1');
|
||||
await click('+1');
|
||||
assert.equal(await enabled('split'), true);
|
||||
await click('split');
|
||||
s = await shot();
|
||||
assert.deepEqual(placed(s), [[0, 2], [2, 4], [4, 5], [5, 6], [6, 7]],
|
||||
'one exposure became two, over the frames it had');
|
||||
'one cel became two, over the frames it had');
|
||||
await click('insert');
|
||||
s = await shot();
|
||||
assert.deepEqual(placed(s), [[0, 2], [2, 3], [3, 5], [5, 6], [6, 7], [7, 8]],
|
||||
'the new drawing took frame 2 and everything from there rippled later');
|
||||
assert.equal(await evaluate('document.querySelectorAll(".tl-cel").length'), 6);
|
||||
assert.equal(await evaluate('document.querySelectorAll(".tl-label:not(.tl-corner)").length'), 1,
|
||||
'six exposures, still one row');
|
||||
'six cels, still one row');
|
||||
assert.equal(s.history.done.length, before.history.done.length + 5);
|
||||
|
||||
// Trim, move and blank: three gestures that move nothing but their own
|
||||
// exposure, and a shot whose length does not follow what is in it.
|
||||
// cel, and a shot whose length does not follow what is in it.
|
||||
await evaluate(`[...document.querySelectorAll('.tl-cel')][2].click()`);
|
||||
await sleep(200);
|
||||
assert.deepEqual(placed(await shot()).slice(2, 4), [[3, 5], [5, 6]]);
|
||||
|
|
@ -185,7 +185,7 @@ try {
|
|||
await click('trim out');
|
||||
s = await shot();
|
||||
assert.deepEqual(placed(s), [[0, 2], [2, 3], [3, 4], [5, 6], [6, 7], [7, 8]],
|
||||
'it ends at the playhead and every other exposure stayed');
|
||||
'it ends at the playhead and every other cel stayed');
|
||||
assert.equal(await enabled('move here'), true);
|
||||
await click('move here');
|
||||
s = await shot();
|
||||
Loading…
Add table
Add a link
Reference in a new issue