diff --git a/collects/scribblings/slideshow/play.scrbl b/collects/scribblings/slideshow/play.scrbl new file mode 100644 index 0000000000..7330058ec7 --- /dev/null +++ b/collects/scribblings/slideshow/play.scrbl @@ -0,0 +1,262 @@ +#lang scribble/doc +@(require "ss.ss" + (for-label slideshow/play + slideshow/code)) + +@title[#:tag "play"]{Animations} + +@defmodule[slideshow/play]{The @schememodname[slideshow/play] module +provides tools for generating animations as multiple, automatically +advanced slides.} + +Many of the tools are based on a function that takes a number between +@scheme[0.0] and @scheme[1.0] inclusive and produces a +pict. The pict produced for the input @scheme[0.0] is +the starting image of the animation, and the pict produced for +@scheme[1.0] is the ending image, while intermediate values produced +intermediate images. For example, + +@schemeblock[ +(lambda (n) + (cellophane (t "Hello") n)) +] + +corresponds to an animation that fades in the word ``Hello.'' + +@; -------------------------------------------------- + +@section{Generating Animated Slides} + +@defproc[(play [gen ((real-in 0.0 1.0) . -> . pict?)] + [#:steps steps exact-positive-integer? 10] + [#:delay delay-secs real? 0.05] + [#:skip-first? skip-first? any/c #f] + [#:title title (or/c string? pict? #f + ((real-in 0.0 1.0) . -> . (or/c string? pict? #f))) + #f] + [#:name name (or/c string? #f + ((real-in 0.0 1.0) . -> . (or/c string? #f))) + title] + [#:layout layout (or/c 'auto 'center 'top 'tall) 'auto]) + void?]{ + +Generates @math{@scheme[steps]+1} slides by calling @scheme[gen] on +equally-spaced values from @scheme[0.0] (inclusve) to @scheme[1.0] +(exclusive). Except for the first of the slides, each slide has a +timeout of @scheme[delay-secs], so that the next slide appears +automatically. + +Normally, @scheme[play] is called via @scheme[play-n], which +effectively calls @scheme[gen] on @scheme[1.0] without a timeout to +complete the animation and stop the auto-advance of slides. The +@scheme[play-n] function also manages with multi-step animations. + +If @scheme[skip-first?] is @scheme[#f], then one less slide is +generated, because @scheme[gen] is not called on @scheme[0.0]. + +The @scheme[title], @scheme[name], and @scheme[layout] arguments are +passed on to @scheme[slide], at least when @scheme[title] and/or +@scheme[name] are not functions. When @scheme[title] or @scheme[name] +is a function, the function is applied to the value used to produce +the slide content, and the resulting title or name is passed on to +@scheme[slide]. + +In condensed mode (i.e., when @scheme[condense?] is @scheme[#t]), any +slide that would be registered with a timeout is instead skipped.} + + +@defproc[(play-n [gen* (() (listof (real-in 0.0 1.0)) . ->* . pict?)] + [#:steps steps exact-positive-integer? 10] + [#:delay delay-secs real? 0.05] + [#:skip-first? skip-first? any/c #f] + [#:skip-last? skip-last? any/c #f] + [#:title title (or/c string? pict? #f + ((real-in 0.0 1.0) . -> . (or/c string? pict? #f))) + #f] + [#:name name (or/c string? #f + ((real-in 0.0 1.0) . -> . (or/c string? #f))) + title] + [#:layout layout (or/c 'auto 'center 'top 'tall) 'auto]) + void?]{ + +Generates a sequence of slides by calling @scheme[gen*] with, for each +of its arguments, numbers from @scheme[0.0] to @scheme[1.0]. If +@scheme[gen*] accepts @math{n} arguments, then result is a sequence of +animations with a pause (i.e., not auto-advanced) between each of +@math{n} segments. + +If @scheme[gen*] accepts a single argument, then @scheme[play-n] is +like @scheme[play], except that @scheme[gen*] is also called with +@scheme[1.0] to generate a slide with no timeout. If @scheme[gen*] +accepts multiple arguments, then slides are generated by calling +@scheme[gen*] with the first argument varying from @scheme[0.0] to +@scheme[1.0] while all other arguments are @scheme[0.0]. Then, the +first argument is held at @scheme[1.0] while the second argument varies +from @scheme[0.0] to @scheme[1.0], and so on. + +For example, + +@schemeblock[ +(play-n (lambda (n1 n2) (cellophane (t "Hello") (* n1 (- 1.0 n2))))) +] + +generates an animation to fade in the word ``Hello,'' and then pauses +for a manual advance, and then fades ``Hello'' back out. + +If @scheme[skip-first?] is @scheme[#t], then the very first slide of +the sequence is skipped. Similarly, if @scheme[skip-last?] is +@scheme[#t], then the last slide of the sequence is skipped. + +The @scheme[steps], @scheme[delay-msecs], @scheme[title], +@scheme[name], and @scheme[layout] arguments are passed on to +@scheme[play] for each of the @math{n} segments of animation.} + + +@defproc[(animate-slide [element (flat-rec-contract elem/c + (or/c pict? 'next 'alts + (listof (listof elem/c))))] + ...) + (() (listof (real-in 0.0 1.0)) . ->* . pict?)]{ + +Accepts slide content similar to @scheme[slide] with @scheme['next] +and @scheme['alts] and produces a procedure suitable for use with +@scheme[play-n]. The result is similar to using @scheme[slide], but +with fades for @scheme['next] and @scheme['alts] transitions (to +better fit the style, perhaps, of surrounding animations).} + +@; -------------------------------------------------- + +@section{Animation Helpers} + +@defproc[(fade-pict [n (real-in 0.0 1.0)] [p1 pict?] [p2 pict?] + [#:combine combine (pict? pict? . -> . pict?) cc-superimpose]) + pict?]{ + +Interpolates @scheme[p1] and @scheme[p2], where the result with +@scheme[n] as @scheme[0.0] is @scheme[p1], and the result with +@scheme[n] as @scheme[1.0] is @scheme[p2]. For intermediate points, +@scheme[p1] fades out while @scheme[p2] fades in as @scheme[n] changes +from @scheme[0.0] to @scheme[1.0]. At the same time, the width and +height of the generated pict are intermediate between +@scheme[p1] and @scheme[p2], and the relative baselines and last +pict correspondingly morph within the bounding box. + +The @scheme[combine] argument determines how @scheme[p1] and +@scheme[p2] are aligned for morphing. For example, if @scheme[p1] and +@scheme[p2] both contain multiple lines of text with the same line +height but different number of lines, then using +@scheme[ctl-superimpose] would keep the ascent line in a fixed +location relative to the top of the resulting pict as the rest of the +shape morphs around it.} + +@defproc[(fade-around-pict [n (real-in 0.0 1.0)] + [p1 pict?] + [make-p2 (pict? . -> . pict?)]) + pict?]{ + +Similar to @scheme[fade-pict], but the target is not a fixed +@scheme[_p2], but instead a function @scheme[make-p2] that takes a +@scheme[launder]ed @scheme[ghost] of @scheme[p1] and places it into a +larger scene. Also, @scheme[p1] does not fade out as @scheme[n] +increases; instead, @scheme[p1] is placed wherever its ghost appears +in the result of @scheme[make-p2]. + +For example, + +@SCHEMEBLOCK[ +(lambda (n) + (fade-around-pict n + (code x) + (lambda (g) (code (+ #,x 1))))) +] + +animates the wrapping of @scheme[x] with a @scheme[(+ .... 1)] form.} + +@defproc[(slide-pict [base pict?] + [p pict?] + [p-from pict?] + [p-to pict?] + [n (in-real 0.0 1.0)]) + pict?]{ + +Pins @scheme[p] onto @scheme[base], sliding from @scheme[p-from] to +@scheme[p-to] (which are picts within @scheme[base]) as +@scheme[n] goes from @scheme[0.0] to @scheme[1.0]. The top-left +locations of @scheme[p-from] and @scheme[p-to] determine the placement +of the top-left of @scheme[p]. + +The @scheme[p-from] and @scheme[p-to] picts are typically +@scheme[launder]ed @scheme[ghost]s of @scheme[p] within @scheme[base], +but they can be any picts within @scheme[base].} + +@; -------------------------------------------------- + +@section{Merging Animations} + +@defproc[(sequence-animations [gen ((real-in 0.0 1.0) . ->* . pict?)] + ...) + ((real-in 0.0 1.0) . ->* . pict?)]{ + +Converts a list of @scheme[gen] functions into a single function that +uses each @scheme[gen] in sequence.} + +@defproc[(reverse-animations [gen ((real-in 0.0 1.0) . ->* . pict?)] + ...) + ((real-in 0.0 1.0) . ->* . pict?)]{ + +Converts a list of @scheme[gen] functions into a single function that +run @scheme[(sequence-animations gen ...)] in reverse.} + +@; -------------------------------------------------- + +@section{Stretching and Squashing Time} + +@deftogether[( +@defproc[(fast-start [n (in-real 0.0 1.0)]) (in-real 0.0 1.0)] +@defproc[(fast-end [n (in-real 0.0 1.0)]) (in-real 0.0 1.0)] +@defproc[(fast-edges [n (in-real 0.0 1.0)]) (in-real 0.0 1.0)] +@defproc[(fast-middle [n (in-real 0.0 1.0)]) (in-real 0.0 1.0)] +)]{ + +Monotonically but non-uniformly maps @scheme[n] with fixed +points at @scheme[0.0] and @scheme[1.0]. + +The @scheme[fast-start] mapping is convex, so that + +@schemeblock[(slide-pict _base p _p1 _p2 (fast-start n))] + +appears to move quickly away from @scheme[_p1] and then slowly as it +approaches @scheme[_p2], assuming that @scheme[n] increases uniformly. + +The @scheme[fast-end] mapping is concave, so that + +@schemeblock[(slide-pict _base _p _p1 _p2 (fast-start _n))] + +appears to move slowly away from @scheme[_p1] and then quicly as it +approaches @scheme[_p2], assuming that @scheme[_n] increases uniformly. + +The @scheme[fast-edges] mapping is convex at first and concave at the +end, so that + +@schemeblock[(slide-pict _base _p _p1 _p2 (fast-start _n))] + +appears to move quickly away from @scheme[_p1], then more slowly, and +then quickly again near @scheme[_p2], assuming that @scheme[_n] increases +uniformly. + +The @scheme[fast-middle] mapping is concave at first and convex at the +end, so that + +@schemeblock[(slide-pict _base _p _p1 _p2 (fast-start _n))] + +appears to move slowly away from @scheme[_p1], then more quickly, and +then slowly again near @scheme[_p2], assuming that @scheme[_n] increases +uniformly.} + +@defproc[(split-phase [n (in-real 0.0 1.0)]) + (values (in-real 0.0 1.0) (in-real 0.0 1.0))]{ + +Splits the progression of @scheme[n] from @scheme[0.0] to @scheme[1.0] +into a progression from @scheme[(values 0.0 0.0)] to @scheme[(values +1.0 0.0)] and then @scheme[(values 1.0 0.0)] to @scheme[(values 1.0 +1.0)].} diff --git a/collects/scribblings/slideshow/slides.scrbl b/collects/scribblings/slideshow/slides.scrbl index 36044581bb..4e59a18c5b 100644 --- a/collects/scribblings/slideshow/slides.scrbl +++ b/collects/scribblings/slideshow/slides.scrbl @@ -20,7 +20,7 @@ @section{Primary Slide Functions} -@defproc[(slide [#:title title (or/c #f string?) #f] +@defproc[(slide [#:title title (or/c #f string? pict?) #f] [#:name name (or/c #f string?) title] [#:layout layout (or/c 'auto 'center 'top 'tall) 'auto] [#:inset inset slide-inset? (make-slide-inset 0 0 0 0)] @@ -28,7 +28,7 @@ [#:condense? condense? any/c (and timeout #t)] [element (flat-rec-contract elem/c (or/c pict? - (or/c 'next 'next! 'alts 'alts~ 'nothing) + 'next 'next! 'alts 'alts~ 'nothing comment? (listof (listof elem/c))))] ...) void?]{ diff --git a/collects/scribblings/slideshow/slideshow.scrbl b/collects/scribblings/slideshow/slideshow.scrbl index 5a5e35aecd..2c3f01c59c 100644 --- a/collects/scribblings/slideshow/slideshow.scrbl +++ b/collects/scribblings/slideshow/slideshow.scrbl @@ -29,6 +29,7 @@ the manual are provided by the @schememodname[slideshow] language.} @include-section["picts.scrbl"] @include-section["slides.scrbl"] @include-section["code.scrbl"] +@include-section["play.scrbl"] @(bibliography (bib-entry #:key "Findler06" diff --git a/collects/slideshow/play.ss b/collects/slideshow/play.ss index fe88b0b82f..659ac481ed 100644 --- a/collects/slideshow/play.ss +++ b/collects/slideshow/play.ss @@ -12,6 +12,7 @@ reverse-animations animate-slide fast-start + fast-end fast-edges fast-middle split-phase) @@ -288,6 +289,9 @@ (define (fast-start n) (- 1 (* (- 1 n) (- 1 n)))) +(define (fast-end n) + (* n n)) + (define (fast-edges n) (+ 0.5 (* (sin (- (* n pi) (/ pi 2))) 0.5)))