(ns unheard.time-object (:require [missionary.core :as m] [helins.interval.map :as imap])) ;; TODO: Update description (defn time-object "A time-object takes a start time, and end time, and a value. Value is a flow that will be consumed when the corresponding time tree is consumed at a point in time within the time-object's interval. " [start duration value] {:start start, :duration duration, :value value}) (defn lift "Lift collection of time objects to a phrase" [& children] {:start 0, :time-objects children}) ;; FIXME: Currently, composing phrases has exponential ;; time complexity. (Each parent phrase causes recomputation of the ;; time offset of each child phrase.) ;; I had a variant of phrase that could accept other phrases ;; and time objects as children. I might want to revisit that. (defn phrase [& children] (fn [start] {:start start, :time-objects (for [child children time-object (:time-objects child)] (update time-object :start (partial + start)))})) (comment (def a (phrase (lift (time-object 0 4 :x)))) (def b (phrase (a 0) (a 1) (lift (time-object 10 2 :x)) (lift (time-object 0 2 :y)))) (def c (phrase (b 0) (b 3))) (c 0)) (defn timeline "Primary timeline bookkeeping mehanism." [{:keys [time-objects]}] (let [m imap/empty] (if (seq? time-objects) (loop [time-objects time-objects m m] (let [{:keys [start duration value]} (first time-objects) m (imap/mark m start (+ start duration) value) rem (rest time-objects)] (if (seq rem) (recur rem m) m))) m))) (comment (def t (timeline (a 0))) (def t (timeline (c 0))) (get t 2)) (defn point-query "Query a timeline. Returns a flow of time objects." [timeline >at] (m/ap (let [at (m/?< >at)] (get timeline at)))) (comment (def at (atom 0)) (def >at (m/watch at)) (def cancel ((m/reduce prn nil (point-query t >at)) prn prn)) (reset! at 0) (cancel))