Tasmota/lib/libesp32/berry_animation/src/animation.be
2025-08-01 23:19:24 +02:00

241 lines
8.9 KiB
Plaintext

# Berry Animation Framework - Main Entry Point
#
# This is the central module that imports and registers all animation framework components
# into a unified "animation" object for use in Tasmota LED strip control.
#
# The framework provides:
# - Unified Pattern-Animation architecture (Animation extends Pattern)
# - DSL (Domain Specific Language) for declarative animation definitions
# - Value providers for dynamic parameters (oscillators, color providers)
# - Event system for interactive animations
# - Optimized performance for embedded ESP32 systems
#
# Usage in Tasmota:
# import animation
# var engine = animation.create_engine(strip)
# var pulse_anim = animation.pulse(animation.solid(0xFF0000), 2000, 50, 255)
# engine.add_animation(pulse_anim).start()
#
# Launch standalone with: "./berry -s -g -m lib/libesp32/berry_animation"
# Import Tasmota integration if available (for embedded use)
import global
if !global.contains("tasmota")
import tasmota
end
# Create the main animation module and make it globally accessible
# The @solidify directive enables compilation to C++ for performance
#@ solidify:animation,weak
var animation = module("animation")
global.animation = animation
# Version information for compatibility tracking
# Format: 0xAABBCCDD (AA=major, BB=minor, CC=patch, DD=build)
animation.VERSION = 0x00010000
# Convert version number to human-readable string format "major.minor.patch"
def animation_version_string(version_num)
if version_num == nil version_num = animation.VERSION end
var major = (version_num >> 24) & 0xFF
var minor = (version_num >> 16) & 0xFF
var patch = (version_num >> 8) & 0xFF
return f"{major}.{minor}.{patch}"
end
animation.version_string = animation_version_string
import sys
# Helper function to register all exports from imported modules into the main animation object
# This creates a flat namespace where all animation functions are accessible as animation.function_name()
# Takes a map returned by "import XXX" and adds each key/value to module `animation`
def register_to_animation(m)
for k: m.keys()
animation.(k) = m[k]
end
end
# Import core framework components
# These provide the fundamental architecture for the animation system
# Frame buffer management for LED strip pixel data
import "core/frame_buffer" as frame_buffer
register_to_animation(frame_buffer)
# Base Pattern class - foundation for all visual elements
import "core/pattern_base" as pattern_base
register_to_animation(pattern_base)
# Base Animation class - extends Pattern with temporal behavior
import "core/animation_base" as animation_base
register_to_animation(animation_base)
# Sequence manager for complex animation choreography
import "core/sequence_manager" as sequence_manager
register_to_animation(sequence_manager)
# Unified animation engine - central controller for all animations
# Provides priority-based layering, automatic blending, and performance optimization
import "core/animation_engine" as animation_engine
register_to_animation(animation_engine)
# Event system for interactive animations (button presses, timers, etc.)
import "core/event_handler" as event_handler
register_to_animation(event_handler)
# User-defined function registry for DSL extensibility
import "core/user_functions" as user_functions
register_to_animation(user_functions)
# Import effects
import "effects/filled" as filled_animation
register_to_animation(filled_animation)
import "effects/pulse" as pulse_animation
register_to_animation(pulse_animation)
import "effects/pulse_position" as pulse_position_animation
register_to_animation(pulse_position_animation)
import "effects/crenel_position" as crenel_position_animation
register_to_animation(crenel_position_animation)
import "effects/breathe" as breathe_animation
register_to_animation(breathe_animation)
import "effects/palette_pattern" as palette_pattern_animation
register_to_animation(palette_pattern_animation)
import "effects/comet" as comet_animation
register_to_animation(comet_animation)
import "effects/fire" as fire_animation
register_to_animation(fire_animation)
import "effects/twinkle" as twinkle_animation
register_to_animation(twinkle_animation)
import "effects/gradient" as gradient_animation
register_to_animation(gradient_animation)
import "effects/noise" as noise_animation
register_to_animation(noise_animation)
import "effects/plasma" as plasma_animation
register_to_animation(plasma_animation)
import "effects/sparkle" as sparkle_animation
register_to_animation(sparkle_animation)
import "effects/wave" as wave_animation
register_to_animation(wave_animation)
import "effects/shift" as shift_animation
register_to_animation(shift_animation)
import "effects/bounce" as bounce_animation
register_to_animation(bounce_animation)
import "effects/scale" as scale_animation
register_to_animation(scale_animation)
import "effects/jitter" as jitter_animation
register_to_animation(jitter_animation)
# Import palette examples
import "effects/palettes" as palettes
register_to_animation(palettes)
# Import pattern implementations
import "patterns/solid_pattern" as solid_pattern_impl
register_to_animation(solid_pattern_impl)
# Import animation implementations
# Note: pulse_animation is already imported from effects/pulse.be
import "effects/pattern_animation" as pattern_animation_impl
register_to_animation(pattern_animation_impl)
# Import value providers
import "providers/value_provider.be" as value_provider
register_to_animation(value_provider)
import "providers/static_value_provider.be" as static_value_provider
register_to_animation(static_value_provider)
import "providers/oscillator_value_provider.be" as oscillator_value_provider
register_to_animation(oscillator_value_provider)
# Import color providers
import "providers/color_provider.be" as color_provider
register_to_animation(color_provider)
import "providers/color_cycle_color_provider.be" as color_cycle_color_provider
register_to_animation(color_cycle_color_provider)
import "providers/composite_color_provider.be" as composite_color_provider
register_to_animation(composite_color_provider)
import "providers/solid_color_provider.be" as solid_color_provider
register_to_animation(solid_color_provider)
import "providers/rich_palette_color_provider.be" as rich_palette_color_provider
register_to_animation(rich_palette_color_provider)
# Import DSL components
import "dsl/token.be" as dsl_token
register_to_animation(dsl_token)
import "dsl/lexer.be" as dsl_lexer
register_to_animation(dsl_lexer)
import "dsl/transpiler.be" as dsl_transpiler
register_to_animation(dsl_transpiler)
import "dsl/runtime.be" as dsl_runtime
register_to_animation(dsl_runtime)
# Function called to initialize the `Leds` and `engine` objects
#
# Parameters:
# l - list of arguments (vararg)
#
# Returns:
# An instance of `AnimationEngine` managing the strip
def animation_init_strip(*l)
import global
import animation
var strip = call(global.Leds, l) # call global.Leds() with vararg
var engine = animation.create_engine(strip)
return engine
end
animation.init_strip = animation_init_strip
# Global variable resolver with error checking
# Used by DSL transpiler to resolve variable names during compilation
# First checks animation module, then global scope for user-defined variables
def animation_global(name, module_name)
import global
import introspect
import animation
# First try to find in animation module (built-in functions/classes)
if (module_name != nil) && introspect.contains(animation, module_name)
return animation.(module_name)
end
# Then try global scope (user-defined variables)
if global.contains(name)
return global.(name)
else
raise "syntax_error", f"'{name}' undeclared"
end
end
animation.global = animation_global
# This function is called from C++ code to set up the Berry animation environment
# It creates a mutable 'animation' module on top of the immutable solidified
#
# Parameters:
# m - Solidified immutable module
#
# Returns:
# A new animation module instance that is return for `import animation`
def animation_init(m)
var animation_new = module("animation") # Create new non-solidified module for runtime use
animation_new._ntv = m # Keep reference to native solidified module
animation_new.event_manager = m.EventManager() # Create event manager instance for handling triggers
# Create dynamic member lookup function for extensibility
# This allows the module to find members in both Berry and solidified components
#
# Note: if the module already contained the member, then `member()` would not be called in the first place
animation_new.member = def (k)
import animation
import introspect
if introspect.contains(animation._ntv, k)
return animation._ntv.(k) # Return native solidified member if available
else
return module("undefined") # Return undefined module for missing members
end
end
return animation_new
end
animation.init = animation_init
return animation