This is an alternative site for discovering Elm packages. You may be looking for the official Elm package site instead.

Piece

A type and functions on it for building up values as a function of time.

A Piece t a is essentially a function f : Time -> a with an associated duration dur which indicates that f is meant to be considered as a function on the time interval [0, dur].

The first type paramater t is ForATime if that duration is some finite time and is Forever if f is to be considered a total function.

As an example, we might have a value circlePos : Piece ForATime (Float, Float) indicating the position of a circle in an animation.

Formation

type alias Piece t a = I.Piece t a
type ForATime = ForATimeDummy

A tag type indicating that a Piece is defined on some bounded interval.

type Forever = ForeverDummy

A tag type indicating that a Piece is defined everywhere

Introduction

forever : (Time -> a) -> Piece Forever a

Create a total Piece, defined on all times.

for : Time -> (Time -> a) -> Piece ForATime a

Create a Piece which runs for the specified duration

stayFor : Time -> a -> Piece ForATime a

A constant Piece.

stayFor dur x = for dur (\_ -> x)
stayForever : a -> Piece Forever a

An eternally constant Piece

stayForever x = forever (\_ -> x)

Transformation

dilate : Float -> Piece t a -> Piece t a

Speed up or slow down time by the given factor. E.g.,

dilate 0.5 s

is a Piece which lasts twice as long as s and proceeds half as fast

cycle : Piece ForATime a -> Piece Forever a

Repeat the given Piece forever.

sustain : Piece ForATime a -> Piece Forever a

Turn a finite piece into a piece defined for all time by having it just maintain its final value forever.

map : (a -> b) -> Piece t a -> Piece t b

Composition

followedBy : Piece ForATime a -> Piece t a -> Piece t a

Sequence two Piece. Thinking of Pieces as functions of time, s1 followedBy s2 is a piecewise function which acts like s1 for the duration of s1 and afterwards acts like s2 (shifted by the duration of s1). For example, we could write

for (3 * second) (\_ -> "hi") `followedBy` for (1 * second) (\_ -> "bye")

which corresponds to a function of time which is "hi" for times in the interval [0, 3] and "bye" in the interval (3, 4]. Consider using the synonym (<>) from Piece.Infix.

chainTo : Piece ForATime a -> (a -> Piece t a) -> Piece t a

Create a sequence of two Pieces, giving the second access to the final value of the first. For example,

pos = for (3 * second) (\t -> t * (100 / 1*second))
      `chainTo` \finalPos -> for (1*second) (\t -> finalPos - t * (finalPos / 1*second))

Consider using the synonym (+>) from Piece.Infix.

Elimination

finalValue : Piece ForATime a -> a

Get the last value the Piece takes on.

run : Signal Time -> Signal (Piece Forever a) -> Signal a

Convert a Signal of Pieces into a Signal by sampling using the given Signal Time.

layer : Signal Time -> Signal (Piece ForATime a) -> Signal (List a)

Documentation forthcoming.

module Piece
  ( Piece
  , ForATime
  , Forever
  , forever
  , for
  , stayFor
  , stayForever
  , sustain
  , dilate
  , cycle
  , map
  , followedBy
  , chainTo
  , run
  , finalValue
  , layer
  ) where

{-| A type and functions on it for building up values as a function of time.

A `Piece t a` is essentially a function `f : Time -> a` with
an associated duration `dur` which indicates that `f` is
meant to be considered as a function on the
time interval [0, dur].

The first type paramater `t` is `ForATime` if that duration
is some finite time and is `Forever` if `f` is to be considered
a total function.

As an example, we might have a value 
`circlePos : Piece ForATime (Float, Float)` indicating the position
of a circle in an animation.

# Formation
@docs Piece, ForATime, Forever

# Introduction
@docs forever, for, stayFor, stayForever

# Transformation
@docs dilate, cycle, sustain, map

# Composition
@docs followedBy, chainTo

# Elimination
@docs finalValue, run, layer

-}

import Piece.Internal as I
import Queue
import Queue.Internal exposing (..)
import Signal
import Time exposing (Time)
import Time
import List
import List exposing ((::))
import Debug exposing (crash)

modFloat : Float -> Float -> Float
modFloat x m = x - m * toFloat (floor (x / m))

{-| -}
type alias Piece t a = I.Piece t a

{-| A tag type indicating that a `Piece` is defined everywhere -}
type Forever = ForeverDummy
{-| A tag type indicating that a `Piece` is defined on some bounded interval. -}
type ForATime = ForATimeDummy

{-| Create a total `Piece`, defined on all times. -}
forever : (Time -> a) -> Piece Forever a
forever f = I.Piece I.Forever f

{-| Create a `Piece` which runs for the specified duration -}
for : Time -> (Time -> a) -> Piece ForATime a
for dur f = I.Piece (I.ForATime dur) f

{-| A constant `Piece`.

    stayFor dur x = for dur (\_ -> x)
-}
stayFor : Time -> a -> Piece ForATime a
stayFor dur x = for dur (\_ -> x)

{-| An eternally constant `Piece` 

    stayForever x = forever (\_ -> x)
-}
stayForever : a -> Piece Forever a
stayForever x = forever (\_ -> x)

{-| Get the last value the `Piece` takes on. -}
finalValue : Piece ForATime a -> a
finalValue p =
  case p of
    I.Piece (I.ForATime d) f -> f d
    _ -> crash "Impossible!"

{-| Speed up or slow down time by the given factor. E.g.,

    dilate 0.5 s

is a `Piece` which lasts twice as long as `s` and proceeds half
as fast -}
dilate : Float -> Piece t a -> Piece t a
dilate s (I.Piece dur f) =
  let dur' =
    if s == 0
    then I.Forever
    else
      case dur of
        I.ForATime t -> I.ForATime (t / s)
        _ -> dur
  in I.Piece dur' (\t -> f (s * t))

{-| Repeat the given `Piece` forever. -}
cycle : Piece ForATime a -> Piece Forever a
cycle (I.Piece dur f) =
  case dur of
    I.ForATime d -> I.Piece I.Forever (\t -> f (modFloat t d))
    _            -> crash "The impossible happened: Piece.cycle"

{-| -}
map : (a -> b) -> Piece t a -> Piece t b
map g (I.Piece dur f) = I.Piece dur (g << f)

{-| Turn a finite piece into a piece defined for all time by having it
    just maintain its final value forever. -}
sustain : Piece ForATime a -> Piece Forever a
sustain st = st `followedBy` stayForever (finalValue st)

mkF d1 f1 f2 =
  \t -> if t <= d1 then f1 t else f2 (t - d1)

{-| Sequence two Piece. Thinking of Pieces as functions of
time, s1 `followedBy` s2 is a piecewise function which acts like
`s1` for the duration of s1 and afterwards acts like `s2` (shifted
by the duration of `s1`). For example, we could write

    for (3 * second) (\_ -> "hi") `followedBy` for (1 * second) (\_ -> "bye")

which corresponds to a function of time which is `"hi"` for times in
the interval `[0, 3]` and `"bye"` in the interval `(3, 4]`. Consider 
using the synonym `(<>)` from `Piece.Infix`.
-}
followedBy : Piece ForATime a -> Piece t a -> Piece t a
followedBy (I.Piece dur1 f1) (I.Piece dur2 f2) =
  case (dur1, dur2) of
    (I.ForATime d1, I.Forever)     -> I.Piece I.Forever (mkF d1 f1 f2)
    (I.ForATime d1, I.ForATime d2) -> I.Piece (I.ForATime (d1 + d2)) (mkF d1 f1 f2)
    (I.Forever, _)                 -> crash "The impossible happened: Piece.followedBy"

{-| Create a sequence of two Pieces, giving the second access to the
final value of the first. For example,

    pos = for (3 * second) (\t -> t * (100 / 1*second))
          `chainTo` \finalPos -> for (1*second) (\t -> finalPos - t * (finalPos / 1*second))

Consider using the synonym `(+>)` from `Piece.Infix`.
-}
chainTo : Piece ForATime a -> (a -> Piece t a) -> Piece t a
chainTo (I.Piece dur f) g =
  case dur of
    I.ForATime d -> I.Piece dur f `followedBy` g (f d)
    _          -> crash "The impossible happened: Piece.chainTo"

{-| Convert a Signal of `Piece`s into a `Signal` by sampling using the given
`Signal Time`. -}
run : Signal Time -> Signal (Piece Forever a) -> Signal a
run ts s =
  Signal.map2 (\(t0, I.Piece _ f) t -> f (t - t0)) (Signal.map2 (,) (Signal.sampleOn s ts) s) ts

type EndToEndUpdate a = CTime Time | CPiece (Piece ForATime a)

{-| Create a signal by chaining together Pieces end-to-end.
    The first argument (perhaps should be a -> Piece Forever a) -}
endToEnd : Piece Forever a -> Signal Time -> Signal (Piece ForATime a) -> Signal a
endToEnd (I.Piece _ gap) =
  let update u (x, t0, s, pieces) =
        case s of
          I.Piece (I.ForATime d) f ->
            case u of
              CTime t -> if t - t0 < d then (f t, t0, s, pieces) else case Queue.pop pieces of
                Nothing            -> (gap t, t0, s, Queue.empty)
                Just (s', pieces') -> let (I.Piece _ g) = s' in (g t, t, s', pieces')
              CPiece s' -> (x, t0, s, Queue.push s' pieces) -- should really filter out this event

          _ ->
            crash "Impossible!"
  in
  \ts ss ->
    Signal.foldp update (gap 0, 0, for 0 gap, Queue.empty) {-dummy args-}
      (Signal.merge (Signal.map CPiece ss) (Signal.map CTime ts))
    |> Signal.map (\(x,_,_,_) -> x)

duration : Piece ForATime a -> Time
duration p =
  case p of
    I.Piece (I.ForATime d) _ -> d
    _ -> crash "Impossible!"

type TwoThings a b
  = TheOneThing a
  | TheOtherThing b

{-| Documentation forthcoming. -}
layer : Signal Time -> Signal (Piece ForATime a) -> Signal (List a)
layer tSig pSig =
  let update u (ps, tCurr, _) = case u of
        TheOneThing t     -> (List.filter (\(tp, p) -> (t - tp) < duration p) ps, t, True)
        TheOtherThing tp' -> (tp' :: ps, tCurr, False)
  in
  Signal.foldp update ([], 0, False)
    (Signal.merge (Signal.map TheOtherThing (Time.timestamp pSig))
      (Signal.map TheOneThing tSig))
  |> Signal.filterMap (\(ps, t, ticked) ->
       if ticked then Just (List.map (\(t0, I.Piece _ f) -> f (t - t0)) ps) else Nothing)
       []

popTil : (a -> Bool) -> Queue a -> Maybe (a, Queue a)
popTil f =
  let go q = case Queue.pop q of
        Just (x, q') -> if f x then Just (x, q') else go q'
        Nothing      -> Nothing
  in
  go