Files
test/effects/ascii_alternating_rotate.sexp
gilesb d574d5badd Add modular primitive libraries and fix Symbol class compatibility
- Add primitive_libs/ with modular primitive loading (core, math, image,
  color, color_ops, filters, geometry, drawing, blending, arrays, ascii)
- Effects now explicitly declare dependencies via (require-primitives "...")
- Convert ascii-fx-zone from hardcoded special form to loadable primitive
- Add _is_symbol/_is_keyword helpers for duck typing to support both
  sexp_effects.parser.Symbol and artdag.sexp.parser.Symbol classes
- Auto-inject _interp and _env for primitives that need them
- Remove silent error swallowing in cell_effect evaluation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 09:02:34 +00:00

66 lines
2.3 KiB
Common Lisp

;; ASCII with Alternating Rotation Directions
;;
;; Checkerboard pattern: even cells rotate clockwise, odd cells rotate counter-clockwise
;; Rotation amount scaled by energy and position (more at top-right)
(recipe "ascii_alternating_rotate"
:version "1.0"
:description "ASCII art with alternating rotation directions per cell"
:minimal-primitives true
:encoding (:codec "libx264" :crf 20 :preset "medium" :audio-codec "aac" :fps 30)
:params (
(cols :type int :default 50 :range [20 100]
:desc "Number of character columns")
(rotation_scale :type float :default 60 :range [0 180]
:desc "Max rotation in degrees")
)
;; Registry
(effect ascii_fx_zone :path "../sexp_effects/effects/ascii_fx_zone.sexp")
;; Effects used in cell_effect lambda
(effect rotate :path "../sexp_effects/effects/rotate.sexp")
(analyzer energy :path "../../artdag-analyzers/energy/analyzer.py")
;; Source files
(def video (source :path "../monday.webm"))
(def audio (source :path "../dizzy.mp3"))
;; Stage 1: Analysis
(stage :analyze
:outputs [energy-data]
(def audio-clip (-> audio (segment :start 60 :duration 10)))
(def energy-data (-> audio-clip (analyze energy))))
;; Stage 2: Process
(stage :process
:requires [:analyze]
:inputs [energy-data]
:outputs [result audio-clip]
(def clip (-> video (segment :start 0 :duration 10)))
(def audio-clip (-> audio (segment :start 60 :duration 10)))
(def result (-> clip
(effect ascii_fx_zone
:cols cols
:char_size (bind energy-data values :range [10 20])
:color_mode "color"
:background "black"
:energy (bind energy-data values :range [0 1])
:rotation_scale rotation_scale
;; Alternating rotation: even cells clockwise, odd cells counter-clockwise
;; Scaled by energy * position (more at top-right)
:cell_effect (lambda [cell zone]
(rotate cell
(* (if (= (mod (+ (get zone "row") (get zone "col")) 2) 0) 1 -1)
(* (get zone "energy")
(get zone "rotation_scale")
(* 1.5 (+ (get zone "col-norm")
(- 1 (get zone "row-norm"))))))))))))
;; Stage 3: Output
(stage :output
:requires [:process]
:inputs [result audio-clip]
(mux result audio-clip)))