Files
test/constructs/slice-every-n.sexp
gilesb 6ceaa37ab6 Add composable ASCII art with per-cell effects and explicit effect loading
Implements ascii_fx_zone effect that allows applying arbitrary sexp effects
to each character cell via cell_effect lambdas. Each cell is rendered as a
small image that effects can operate on.

Key changes:
- New ascii_fx_zone effect with cell_effect parameter for per-cell transforms
- Zone context (row, col, lum, sat, hue, etc.) available in cell_effect lambdas
- Effects are now loaded explicitly from recipe declarations, not auto-loaded
- Added effects_registry to plan for explicit effect dependency tracking
- Updated effect definition syntax across all sexp effects
- New run_staged.py for executing staged recipes
- Example recipes demonstrating alternating rotation and blur/rgb_split patterns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 21:58:05 +00:00

57 lines
2.5 KiB
Common Lisp

;; slice-every-n construct - group every N beats into one segment
;;
;; Usage:
;; (construct slice-every-n :path "constructs/slice-every-n.sexp")
;; (def segments (slice-every-n beats-data 4
;; :init 0
;; :reducer (fn [acc i start end]
;; {:source video-a
;; :effects (list {:effect invert})
;; :acc (inc acc)})))
;;
;; Groups every N analysis times into one segment, calling reducer once per group
(define-construct slice-every-n
:params (
(analysis :type any :desc "Analysis data with :times")
(n :type int :default 4 :desc "Number of beats per segment")
(init :type any :default 0 :desc "Initial accumulator value")
(reducer :type any :desc "Reducer function (fn [acc i start end] ...)")
)
;; Reducer receives: (acc, i, start, end) where start/end are audio beat times
;; Reducer returns: {:source src :effects fx :acc new-acc}
;; Optionally include :start/:end to override (e.g., for wrapping/randomizing)
;; :duration is calculated from start/end (use :duration to override)
;; Return :skip true to skip this segment
(let [times (get analysis :times)
;; Group times into chunks of n
grouped (chunk-every times n)]
(nth
(reduce
(fn [state group]
(let [acc (first state)
segments (nth state 1)
i (len segments)
audio-start (first group)
audio-end (last group)
audio-duration (- audio-end audio-start)
;; Call user's reducer with audio beat times
result (reducer acc i audio-start audio-end)
new-acc (get result :acc)]
;; Skip if reducer returns :skip true
(if (get result :skip false)
(list new-acc segments)
(let [;; Use reducer's start/end/duration if provided, else use audio times
seg-start (get result :start audio-start)
seg-end (get result :end audio-end)
seg-duration (get result :duration (- seg-end seg-start))
segment (dict :source (get result :source)
:start seg-start
:end seg-end
:duration seg-duration
:effects (get result :effects))]
(list new-acc (append segments segment))))))
(list init (list))
grouped)
1)))