aboutsummaryrefslogtreecommitdiff
path: root/tween.scm
diff options
context:
space:
mode:
Diffstat (limited to 'tween.scm')
-rw-r--r--tween.scm104
1 files changed, 72 insertions, 32 deletions
diff --git a/tween.scm b/tween.scm
index eb8abc3..3475a83 100644
--- a/tween.scm
+++ b/tween.scm
@@ -86,7 +86,9 @@
ease-fn ;; number → number
elapsed ;; ms since tween started (includes delay period)
done? ;; boolean
- callback) ;; (entity → unspecified) or #f; invoked once at completion
+ callback ;; (entity → unspecified) or #f; invoked once at completion
+ repeat ;; -1 = infinite, 0 = no more repeats, N = N repeats remaining
+ yoyo?) ;; swap starts/ends on each repeat cycle
;; ── Public API ────────────────────────────────────────────────────────────
@@ -96,13 +98,15 @@
;; props: alist of (keyword . target-number), e.g. ((#:x . 200) (#:y . 40))
(define (make-tween entity #!key props (duration 500) (delay 0) (ease 'linear)
- (on-complete #f))
+ (on-complete #f) (repeat 0) (yoyo? #f))
(unless (and (integer? duration) (> duration 0))
(error "make-tween: duration must be a positive integer (ms)" duration))
(unless (and (integer? delay) (>= delay 0))
(error "make-tween: delay must be a non-negative integer (ms)" delay))
(unless (pair? props)
(error "make-tween: props must be a non-empty alist" props))
+ (unless (and (integer? repeat) (>= repeat -1))
+ (error "make-tween: repeat must be -1 (infinite) or a non-negative integer" repeat))
(let ((ease-fn (ease-resolve ease))
(starts (map (lambda (p)
(let ((k (car p)))
@@ -117,7 +121,9 @@
ease-fn: ease-fn
elapsed: 0
done?: #f
- callback: on-complete)))
+ callback: on-complete
+ repeat: repeat
+ yoyo?: yoyo?)))
;; Linear interpolation with eased factor u in [0,1]
(define (lerp a b u)
@@ -132,37 +138,71 @@
entity
ends))
+ (define (tw-with-elapsed tw elapsed)
+ (make-tw starts: (tw-starts tw) ends: (tw-ends tw)
+ duration: (tw-duration tw) delay: (tw-delay tw)
+ ease-fn: (tw-ease-fn tw) elapsed: elapsed
+ done?: #f callback: (tw-callback tw)
+ repeat: (tw-repeat tw) yoyo?: (tw-yoyo? tw)))
+
+ (define (tw-finish tw elapsed)
+ (make-tw starts: (tw-starts tw) ends: (tw-ends tw)
+ duration: (tw-duration tw) delay: (tw-delay tw)
+ ease-fn: (tw-ease-fn tw) elapsed: elapsed
+ done?: #t callback: #f
+ repeat: 0 yoyo?: (tw-yoyo? tw)))
+
+ (define (tw-next-cycle tw overflow)
+ (let* ((yoyo? (tw-yoyo? tw))
+ (starts (tw-starts tw))
+ (ends (tw-ends tw)))
+ (make-tw starts: (if yoyo? ends starts)
+ ends: (if yoyo? starts ends)
+ duration: (tw-duration tw) delay: 0
+ ease-fn: (tw-ease-fn tw) elapsed: overflow
+ done?: #f callback: (tw-callback tw)
+ repeat: (let ((r (tw-repeat tw))) (if (= r -1) -1 (- r 1)))
+ yoyo?: yoyo?)))
+
+ (define (tween-complete tw entity elapsed)
+ (let ((final (apply-props entity (tw-starts tw) (tw-ends tw) 1.0)))
+ (if (zero? (tw-repeat tw))
+ (begin
+ (when (tw-callback tw) ((tw-callback tw) final))
+ (values (tw-finish tw elapsed) final))
+ (let ((overflow (- (- elapsed (tw-delay tw)) (tw-duration tw))))
+ (values (tw-next-cycle tw overflow) final)))))
+
+ (define (tween-interpolate tw entity elapsed)
+ (let* ((t0 (- elapsed (tw-delay tw)))
+ (u (min 1.0 (max 0.0 (/ t0 (tw-duration tw)))))
+ (eased ((tw-ease-fn tw) u))
+ (ent2 (apply-props entity (tw-starts tw) (tw-ends tw) eased)))
+ (if (>= u 1.0)
+ (tween-complete tw entity elapsed)
+ (values (tw-with-elapsed tw elapsed) ent2))))
+
(define (tween-step tw entity dt)
(unless (tw? tw) (error "tween-step: expected tween struct" tw))
(if (tw-done? tw)
(values tw entity)
- (let* ((elapsed (+ (tw-elapsed tw) dt))
- (delay (tw-delay tw))
- (duration (tw-duration tw))
- (ease-fn (tw-ease-fn tw))
- (starts (tw-starts tw))
- (ends (tw-ends tw)))
- (cond ((< elapsed delay)
- (values (make-tw starts: starts ends: ends duration: duration
- delay: delay ease-fn: ease-fn
- elapsed: elapsed done?: #f callback: (tw-callback tw))
- entity))
- (else
- (let* ((t0 (- elapsed delay))
- (u-raw (/ t0 duration))
- (u (min 1.0 (max 0.0 u-raw)))
- (eased (ease-fn u))
- (ent2 (apply-props entity starts ends eased)))
- (if (>= u 1.0)
- (let* ((final (apply-props entity starts ends 1.0))
- (cb (tw-callback tw))
- (_ (when cb (cb final)))
- (tw2 (make-tw starts: starts ends: ends duration: duration
- delay: delay ease-fn: ease-fn
- elapsed: elapsed done?: #t callback: #f)))
- (values tw2 final))
- (values (make-tw starts: starts ends: ends duration: duration
- delay: delay ease-fn: ease-fn
- elapsed: elapsed done?: #f callback: (tw-callback tw))
- ent2))))))))
+ (let ((elapsed (+ (tw-elapsed tw) dt)))
+ (if (< elapsed (tw-delay tw))
+ (values (tw-with-elapsed tw elapsed) entity)
+ (tween-interpolate tw entity elapsed)))))
+
+ ;; ── Pipeline step ──────────────────────────────────────────────────────────
+ ;; Auto-advance #:tween on an entity. Call from update: as part of the
+ ;; per-entity pipeline, e.g. (step-tweens entity dt). Removes #:tween
+ ;; when the tween finishes.
+
+ (define-pipeline (step-tweens tweens) (entity dt)
+ (let ((tw (entity-ref entity #:tween #f)))
+ (if (not tw)
+ entity
+ (receive (tw2 ent2) (tween-step tw entity dt)
+ (if (tween-finished? tw2)
+ (entity-set ent2 #:tween #f)
+ (entity-set ent2 #:tween tw2))))))
+
) ;; end module