From dd8cf4614b6734cbd83d94a50b076a5e542c3aa4 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Thu, 27 Apr 2017 22:47:46 -0400 Subject: scripting: Add a tweening procedure. * chickadee/scripting.scm (tween): Reimplement procedure. * chickadee/math/easings.scm: New file. * Makefile.am (SOURCES): Add it. * doc/api.text (Easings, Tweening): New subsections. --- Makefile.am | 1 + chickadee/math/easings.scm | 104 +++++++++++++++++++++++++++++++++++++++++++++ chickadee/scripting.scm | 27 +++++++++--- doc/api.texi | 87 +++++++++++++++++++++++++++++++++++++ 4 files changed, 212 insertions(+), 7 deletions(-) create mode 100644 chickadee/math/easings.scm diff --git a/Makefile.am b/Makefile.am index 82c6f83..50b8159 100644 --- a/Makefile.am +++ b/Makefile.am @@ -51,6 +51,7 @@ SOURCES = \ chickadee/math/vector.scm \ chickadee/math/matrix.scm \ chickadee/math/rect.scm \ + chickadee/math/easings.scm \ chickadee/color.scm \ chickadee/render/gl.scm \ chickadee/render/gpu.scm \ diff --git a/chickadee/math/easings.scm b/chickadee/math/easings.scm new file mode 100644 index 0000000..115c2bb --- /dev/null +++ b/chickadee/math/easings.scm @@ -0,0 +1,104 @@ +;;; Chickadee Game Toolkit +;;; Copyright © 2017 David Thompson +;;; +;;; Chickadee is free software: you can redistribute it and/or modify +;;; it under the terms of the GNU General Public License as published +;;; by the Free Software Foundation, either version 3 of the License, +;;; or (at your option) any later version. +;;; +;;; Chickadee is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;;; General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with this program. If not, see +;;; . + +(define-module (chickadee math easings) + #:use-module (chickadee math) + #:export (linear + smoothstep + ease-in-quad + ease-out-quad + ease-in-out-quad + ease-in-cubic + ease-out-cubic + ease-in-out-cubic + ease-in-quart + ease-out-quart + ease-in-out-quart + ease-in-quint + ease-out-quint + ease-in-out-quint + ease-in-sine + ease-out-sine + ease-in-out-sine)) + +(define-inlinable (linear t) + t) + +(define-inlinable (smoothstep t) + (* t t (- 3 (* 2 t)))) + +(define-inlinable (ease-in-quad t) + (* t t)) + +(define-inlinable (ease-out-quad t) + (* t (- 2.0 t))) + +(define-inlinable (ease-in-out-quad t) + (if (< t 0.5) + (* 2.0 t t) + (- (* (- 4 (* 2.0 t)) t) 1.0))) + +(define-inlinable (ease-in-cubic t) + (* t t t)) + +(define-inlinable (ease-out-cubic t) + (let ((t* (- t 1.0))) + (+ 1.0 (* t* t* t*)))) + +(define-inlinable (ease-in-out t) + (if (< t 0.5) + (* 4.0 t t t) + (+ 1.0 + (* (- t 1.0) + (* 2 (- t 2.0)) + (* 2 (- t 2.0)))))) + +(define-inlinable (ease-in-quart t) + (* t t t t)) + +(define-inlinable (ease-out-quart t) + (let ((t* (- t 1.0))) + (- 1.0 (* t* t* t* t*)))) + +(define-inlinable (ease-in-out-quart t) + (if (< t 0.5) + (* 8.0 t t t t) + (let ((t* (- t 1.0))) + (- 1.0 (* 8.0 t* t* t* t*))))) + +(define-inlinable (ease-in-quint t) + (* t t t t t)) + +(define-inlinable (ease-out-quint t) + (let ((t* (- t 1.0))) + (+ 1.0 (* t* t* t* t* t*)))) + +(define-inlinable (ease-in-out-quint t) + (if (< t 0.5) + (* 16.0 t t t t t) + (let ((t* (- t 1.0))) + (+ 1.0 (* 16.0 t* t* t* t* t*))))) + +(define-inlinable (ease-in-sine t) + (+ (* (- t) (cos (* t pi/2))) t)) + +(define-inlinable (ease-out-sine t) + (* t (sin (* t pi/2)))) + +(define-inlinable (ease-in-out-sine t) + (* (/ t -2) + (1- (cos (* t pi))))) diff --git a/chickadee/scripting.scm b/chickadee/scripting.scm index 7a661bb..5d24ce9 100644 --- a/chickadee/scripting.scm +++ b/chickadee/scripting.scm @@ -16,6 +16,8 @@ ;;; . (define-module (chickadee scripting) + #:use-module (chickadee math) + #:use-module (chickadee math easings) #:use-module (chickadee scripting agenda) #:use-module (chickadee scripting channel) #:use-module (chickadee scripting coroutine) @@ -42,12 +44,23 @@ "Wait DURATION before resuming the current coroutine." (yield (lambda (cont) (schedule-after duration cont)))) -(define (tween duration start end ease proc) +(define* (tween duration start end proc #:key + (step 1) + (ease smoothstep) + (interpolate lerp)) + "Transition a value from START to END over DURATION, sending each +succesive value to PROC. STEP controls the amount of time between +each update of the animation. + +The EASE procedure controls the rate at which the animation advances. +The smoothstep easing function is used by default. + +The INTERPOLATE procedure computes the values in between START and +END. By default, linear interpolation is used." (let loop ((t 0)) - (if (= t duration) + (if (>= t duration) (proc end) - (let ((alpha (/ t duration))) - (proc (+ (* start (- 1 alpha)) - (* end alpha))) - (wait 1) - (loop (1+ t)))))) + (let ((alpha (ease (/ t duration)))) + (proc (interpolate start end alpha)) + (wait step) + (loop (+ t step)))))) diff --git a/doc/api.texi b/doc/api.texi index 1a56039..c3543b8 100644 --- a/doc/api.texi +++ b/doc/api.texi @@ -410,6 +410,7 @@ detection. * Vectors:: Euclidean vectors. * Matrices:: Transformation matrices. * Rectangles:: Axis-aligned bounding boxes. +* Easings:: Easing functions for interesting animations. @end menu @node Basics @@ -435,6 +436,60 @@ Half of @var{pi}. @node Rectangles @subsection Rectangles +@node Easings +@subsection Easings + +@deffn {Scheme Procedure} linear @var{t} +@end deffn + +@deffn {Scheme Procedure} smoothstep @var{t} +@end deffn + +@deffn {Scheme Procedure} ease-in-quad @var{t} +@end deffn + +@deffn {Scheme Procedure} ease-out-quad @var{t} +@end deffn + +@deffn {Scheme Procedure} ease-in-out-quad @var{t} +@end deffn + +@deffn {Scheme Procedure} ease-in-cubic @var{t} +@end deffn + +@deffn {Scheme Procedure} ease-out-cubic @var{t} +@end deffn + +@deffn {Scheme Procedure} ease-in-out-cubic @var{t} +@end deffn + +@deffn {Scheme Procedure} ease-in-quart @var{t} +@end deffn + +@deffn {Scheme Procedure} ease-out-quart @var{t} +@end deffn + +@deffn {Scheme Procedure} ease-in-out-quart @var{t} +@end deffn + +@deffn {Scheme Procedure} ease-in-quint @var{t} +@end deffn + +@deffn {Scheme Procedure} ease-out-quint @var{t} +@end deffn + +@deffn {Scheme Procedure} ease-in-out-quint @var{t} +@end deffn + +@deffn {Scheme Procedure} ease-in-sine @var{t} +@end deffn + +@deffn {Scheme Procedure} ease-out-sine @var{t} +@end deffn + +@deffn {Scheme Procedure} ease-in-out-sine @var{t} +@end deffn + @node Graphics @section Graphics @@ -868,6 +923,7 @@ of scheduling tasks. @menu * Agendas:: Scheduling tasks. * Coroutines:: Cooperative multitasking. +* Tweening:: Animations. * Channels:: Publish data to listeners. @end menu @@ -1069,6 +1125,37 @@ Wait for a message from @var{channel}. Evaluate @var{body} in an endless loop. @end deffn +@node Tweening +@subsection Tweening + +Tweening is the process of transitioning something from an initial +state to a final state over a pre-determined period of time. In other +words, tweening is a way to create animation. The @code{tween} +procedure can be used within any coroutine like so: + +@example +(define x 0) +(coroutine + ;; 0 to 100 in 60 ticks of the agenda. + (tween 60 0 100 (lambda (y) (set! x y)))) +@end example + +@deffn {Scheme Procedure} tween @var{duration} @var{start} @var{end} @var{proc} [#:step 1 #:ease @code{smoothstep} #:interpolate @code{lerp}] +Transition a value from @var{start} to @var{end} over @var{duration}, +sending each succesive value to @var{proc}. @var{step} controls the +amount of time between each update of the animation. + +To control how the animation goes from the initial to final state, an +``easing'' procedure may be specified. By default, the +@code{smoothstep} easing is used, which is a more pleasing default +than a simplistic linear function. @xref{Easings} for a complete +list of available easing procedures. + +The @var{interpolate} procedure computes the values in between +@var{start} and @var{end}. By default, linear interpolation (``lerp'' +for short) is used. +@end deffn + @node Channels @subsection Channels -- cgit v1.2.3