Berry animation remove noise_animation (#24291)
This commit is contained in:
parent
f5d8ec43fc
commit
36424dd8e7
@ -167,7 +167,6 @@ Animation|Description
|
|||||||
`palette_gradient_animation`|Gradient patterns with palette colors
|
`palette_gradient_animation`|Gradient patterns with palette colors
|
||||||
`palette_meter_animation`|Meter/bar patterns
|
`palette_meter_animation`|Meter/bar patterns
|
||||||
`gradient_meter_animation`|VU meter with gradient and peak hold
|
`gradient_meter_animation`|VU meter with gradient and peak hold
|
||||||
`noise_animation`|Perlin noise patterns
|
|
||||||
`wave_animation`|Wave motion effects
|
`wave_animation`|Wave motion effects
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|||||||
@ -30,7 +30,6 @@ ParameterizedObject (base class with parameter management and playable interface
|
|||||||
│ ├── CometAnimation (moving comet with tail)
|
│ ├── CometAnimation (moving comet with tail)
|
||||||
│ ├── FireAnimation (realistic fire effect)
|
│ ├── FireAnimation (realistic fire effect)
|
||||||
│ ├── TwinkleAnimation (twinkling stars effect)
|
│ ├── TwinkleAnimation (twinkling stars effect)
|
||||||
│ ├── NoiseAnimation (Perlin noise patterns)
|
|
||||||
│ ├── WaveAnimation (wave motion effects)
|
│ ├── WaveAnimation (wave motion effects)
|
||||||
│ └── RichPaletteAnimation (smooth palette transitions)
|
│ └── RichPaletteAnimation (smooth palette transitions)
|
||||||
├── SequenceManager (orchestrates animation sequences)
|
├── SequenceManager (orchestrates animation sequences)
|
||||||
@ -654,76 +653,6 @@ audio_meter.level = audio_level
|
|||||||
|
|
||||||
**Factory**: `animation.gradient_meter_animation(engine)`
|
**Factory**: `animation.gradient_meter_animation(engine)`
|
||||||
|
|
||||||
### NoiseAnimation
|
|
||||||
|
|
||||||
Creates pseudo-random noise patterns with configurable scale, speed, and fractal complexity. Perfect for organic, natural-looking effects like clouds, fire textures, or abstract patterns. Inherits from `Animation`.
|
|
||||||
|
|
||||||
| Parameter | Type | Default | Constraints | Description |
|
|
||||||
|-----------|------|---------|-------------|-------------|
|
|
||||||
| `color` | instance | nil | - | Color provider for noise mapping (nil = rainbow) |
|
|
||||||
| `scale` | int | 50 | 1-255 | Noise scale/frequency (lower = larger patterns) |
|
|
||||||
| `speed` | int | 30 | 0-255 | Animation speed (0 = static pattern) |
|
|
||||||
| `octaves` | int | 1 | 1-4 | Number of noise octaves for fractal complexity |
|
|
||||||
| `persistence` | int | 128 | 0-255 | How much each octave contributes to final pattern |
|
|
||||||
| `seed` | int | 12345 | 0-65535 | Random seed for reproducible patterns |
|
|
||||||
| *(inherits all Animation parameters)* | | | | |
|
|
||||||
|
|
||||||
#### Noise Characteristics
|
|
||||||
|
|
||||||
**Scale Effects:**
|
|
||||||
- **Low scale (10-30)**: Large, flowing patterns
|
|
||||||
- **Medium scale (40-80)**: Balanced detail and flow
|
|
||||||
- **High scale (100-200)**: Fine, detailed textures
|
|
||||||
|
|
||||||
**Octave Effects:**
|
|
||||||
- **1 octave**: Smooth, simple patterns
|
|
||||||
- **2 octaves**: Added medium-frequency detail
|
|
||||||
- **3+ octaves**: Complex, natural-looking textures
|
|
||||||
|
|
||||||
**Speed Effects:**
|
|
||||||
- **Static (0)**: Fixed pattern for backgrounds
|
|
||||||
- **Slow (10-40)**: Gentle, organic movement
|
|
||||||
- **Fast (80-200)**: Dynamic, energetic patterns
|
|
||||||
|
|
||||||
#### Usage Examples
|
|
||||||
|
|
||||||
```berry
|
|
||||||
# Rainbow noise with medium detail
|
|
||||||
animation rainbow_noise = noise_animation(
|
|
||||||
scale=60,
|
|
||||||
speed=40,
|
|
||||||
octaves=1
|
|
||||||
)
|
|
||||||
|
|
||||||
# Blue fire texture with fractal detail
|
|
||||||
color blue_fire = 0xFF0066FF
|
|
||||||
animation blue_texture = noise_animation(
|
|
||||||
color=blue_fire,
|
|
||||||
scale=120,
|
|
||||||
speed=60,
|
|
||||||
octaves=3,
|
|
||||||
persistence=100
|
|
||||||
)
|
|
||||||
|
|
||||||
# Static cloud pattern
|
|
||||||
animation cloud_pattern = noise_animation(
|
|
||||||
color=white,
|
|
||||||
scale=30,
|
|
||||||
speed=0,
|
|
||||||
octaves=2
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Common Use Cases
|
|
||||||
|
|
||||||
- **Ambient Lighting**: Slow, low-scale noise for background ambiance
|
|
||||||
- **Fire Effects**: Orange/red colors with medium scale and speed
|
|
||||||
- **Water Effects**: Blue/cyan colors with flowing movement
|
|
||||||
- **Cloud Simulation**: White/gray colors with large-scale patterns
|
|
||||||
- **Abstract Art**: Rainbow colors with high detail and multiple octaves
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### PulseAnimation
|
### PulseAnimation
|
||||||
|
|
||||||
Creates a pulsing effect oscillating between min and max brightness. Inherits from `Animation`.
|
Creates a pulsing effect oscillating between min and max brightness. Inherits from `Animation`.
|
||||||
@ -1230,7 +1159,6 @@ run gradient_wave
|
|||||||
- Each animation uses approximately 4 bytes per pixel for color storage
|
- Each animation uses approximately 4 bytes per pixel for color storage
|
||||||
- Fire animation includes additional flicker calculations
|
- Fire animation includes additional flicker calculations
|
||||||
- Gradient animation requires color interpolation calculations
|
- Gradient animation requires color interpolation calculations
|
||||||
- Noise animation includes pseudo-random pattern generation
|
|
||||||
- Consider strip length impact on transformation calculations
|
- Consider strip length impact on transformation calculations
|
||||||
|
|
||||||
## Parameter Constraints
|
## Parameter Constraints
|
||||||
|
|||||||
@ -1441,7 +1441,6 @@ Animation classes create visual effects on LED strips:
|
|||||||
| `fire_animation` | Realistic fire simulation |
|
| `fire_animation` | Realistic fire simulation |
|
||||||
| `twinkle_animation` | Twinkling stars effect |
|
| `twinkle_animation` | Twinkling stars effect |
|
||||||
| `gradient_animation` | Color gradient effects |
|
| `gradient_animation` | Color gradient effects |
|
||||||
| `noise_animation` | Perlin noise-based patterns |
|
|
||||||
| `wave_animation` | Wave propagation effects |
|
| `wave_animation` | Wave propagation effects |
|
||||||
| `rich_palette_animation` | Palette-based color cycling |
|
| `rich_palette_animation` | Palette-based color cycling |
|
||||||
| `palette_wave_animation` | Wave patterns using palettes |
|
| `palette_wave_animation` | Wave patterns using palettes |
|
||||||
|
|||||||
@ -145,8 +145,6 @@ import "animations/gradient" as gradient_animation
|
|||||||
register_to_animation(gradient_animation)
|
register_to_animation(gradient_animation)
|
||||||
import "animations/palette_meter" as palette_meter_animation
|
import "animations/palette_meter" as palette_meter_animation
|
||||||
register_to_animation(palette_meter_animation)
|
register_to_animation(palette_meter_animation)
|
||||||
import "animations/noise" as noise_animation
|
|
||||||
register_to_animation(noise_animation)
|
|
||||||
# import "animations/plasma" as plasma_animation
|
# import "animations/plasma" as plasma_animation
|
||||||
# register_to_animation(plasma_animation)
|
# register_to_animation(plasma_animation)
|
||||||
# import "animations/sparkle" as sparkle_animation
|
# import "animations/sparkle" as sparkle_animation
|
||||||
|
|||||||
@ -1,288 +0,0 @@
|
|||||||
# Noise animation effect for Berry Animation Framework
|
|
||||||
#
|
|
||||||
# This animation creates pseudo-random noise patterns with configurable
|
|
||||||
# scale, speed, and color mapping through palettes or single colors.
|
|
||||||
|
|
||||||
import "./core/param_encoder" as encode_constraints
|
|
||||||
|
|
||||||
#@ solidify:NoiseAnimation,weak
|
|
||||||
class NoiseAnimation : animation.animation
|
|
||||||
# Non-parameter instance variables only
|
|
||||||
var current_colors # Array of current colors for each pixel
|
|
||||||
var time_offset # Current time offset for animation
|
|
||||||
var noise_table # Pre-computed noise values for performance
|
|
||||||
|
|
||||||
# Parameter definitions following new specification
|
|
||||||
static var PARAMS = animation.enc_params({
|
|
||||||
"color": {"default": nil},
|
|
||||||
"scale": {"min": 1, "max": 255, "default": 50},
|
|
||||||
"speed": {"min": 0, "max": 255, "default": 30},
|
|
||||||
"octaves": {"min": 1, "max": 4, "default": 1},
|
|
||||||
"persistence": {"min": 0, "max": 255, "default": 128},
|
|
||||||
"seed": {"min": 0, "max": 65535, "default": 12345}
|
|
||||||
})
|
|
||||||
|
|
||||||
# Initialize a new Noise animation
|
|
||||||
def init(engine)
|
|
||||||
# Call parent constructor with engine only
|
|
||||||
super(self).init(engine)
|
|
||||||
|
|
||||||
# Initialize non-parameter instance variables only
|
|
||||||
var strip_length = self.engine.strip_length
|
|
||||||
self.current_colors = []
|
|
||||||
self.current_colors.resize(strip_length)
|
|
||||||
self.time_offset = 0
|
|
||||||
|
|
||||||
# Initialize colors to black
|
|
||||||
var i = 0
|
|
||||||
while i < strip_length
|
|
||||||
self.current_colors[i] = 0xFF000000
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
# Initialize noise table - will be done in start method
|
|
||||||
self.noise_table = []
|
|
||||||
|
|
||||||
# Set default color if not set
|
|
||||||
if self.color == nil
|
|
||||||
var rainbow_provider = animation.rich_palette(engine)
|
|
||||||
rainbow_provider.colors = animation.PALETTE_RAINBOW
|
|
||||||
rainbow_provider.period = 5000
|
|
||||||
rainbow_provider.transition_type = 1
|
|
||||||
rainbow_provider.brightness = 255
|
|
||||||
self.color = rainbow_provider
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Override start method for initialization
|
|
||||||
def start(time_ms)
|
|
||||||
# Call parent start first
|
|
||||||
super(self).start(time_ms)
|
|
||||||
|
|
||||||
# Initialize noise table with current seed
|
|
||||||
self._init_noise_table()
|
|
||||||
|
|
||||||
# Reset time offset
|
|
||||||
self.time_offset = 0
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
# Initialize noise lookup table for performance
|
|
||||||
def _init_noise_table()
|
|
||||||
self.noise_table = []
|
|
||||||
self.noise_table.resize(256)
|
|
||||||
|
|
||||||
# Generate pseudo-random values using seed
|
|
||||||
var current_seed = self.seed
|
|
||||||
var rng_state = current_seed
|
|
||||||
var i = 0
|
|
||||||
while i < 256
|
|
||||||
rng_state = (rng_state * 1103515245 + 12345) & 0x7FFFFFFF
|
|
||||||
self.noise_table[i] = rng_state % 256
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Override setmember to handle color conversion
|
|
||||||
def setmember(name, value)
|
|
||||||
if name == "color" && type(value) == "int"
|
|
||||||
# Convert integer color to gradient palette from black to color
|
|
||||||
var palette = bytes()
|
|
||||||
palette.add(0x00, 1) # Position 0: black
|
|
||||||
palette.add(0x00, 1) # R
|
|
||||||
palette.add(0x00, 1) # G
|
|
||||||
palette.add(0x00, 1) # B
|
|
||||||
palette.add(0xFF, 1) # Position 255: full color
|
|
||||||
palette.add((value >> 16) & 0xFF, 1) # R
|
|
||||||
palette.add((value >> 8) & 0xFF, 1) # G
|
|
||||||
palette.add(value & 0xFF, 1) # B
|
|
||||||
|
|
||||||
var gradient_provider = animation.rich_palette(self.engine)
|
|
||||||
gradient_provider.colors = palette
|
|
||||||
gradient_provider.period = 5000
|
|
||||||
gradient_provider.transition_type = 1
|
|
||||||
gradient_provider.brightness = 255
|
|
||||||
|
|
||||||
# Set the gradient provider instead of the integer
|
|
||||||
super(self).setmember(name, gradient_provider)
|
|
||||||
else
|
|
||||||
# Use parent implementation for other parameters
|
|
||||||
super(self).setmember(name, value)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Handle parameter changes
|
|
||||||
def on_param_changed(name, value)
|
|
||||||
super(self).on_param_changed(name, value)
|
|
||||||
if name == "seed"
|
|
||||||
self._init_noise_table()
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update current_colors array size when strip length changes via engine
|
|
||||||
var new_strip_length = self.engine.strip_length
|
|
||||||
if size(self.current_colors) != new_strip_length
|
|
||||||
self.current_colors.resize(new_strip_length)
|
|
||||||
var i = size(self.current_colors)
|
|
||||||
while i < new_strip_length
|
|
||||||
self.current_colors[i] = 0xFF000000
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Simple noise function using lookup table
|
|
||||||
def _noise_1d(x)
|
|
||||||
var ix = int(x) & 255
|
|
||||||
var fx = x - int(x)
|
|
||||||
|
|
||||||
# Get noise values at integer positions
|
|
||||||
var a = self.noise_table[ix]
|
|
||||||
var b = self.noise_table[(ix + 1) & 255]
|
|
||||||
|
|
||||||
# Linear interpolation using integer math
|
|
||||||
var lerp_amount = tasmota.scale_uint(int(fx * 256), 0, 256, 0, 255)
|
|
||||||
return tasmota.scale_uint(lerp_amount, 0, 255, a, b)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Fractal noise with multiple octaves
|
|
||||||
def _fractal_noise(x, time_offset)
|
|
||||||
var value = 0
|
|
||||||
var amplitude = 255
|
|
||||||
var current_scale = self.scale
|
|
||||||
var current_octaves = self.octaves
|
|
||||||
var current_persistence = self.persistence
|
|
||||||
var frequency = current_scale
|
|
||||||
var max_value = 0
|
|
||||||
|
|
||||||
var octave = 0
|
|
||||||
while octave < current_octaves
|
|
||||||
var sample_x = tasmota.scale_uint(x * frequency, 0, 255 * 255, 0, 255) + time_offset
|
|
||||||
var noise_val = self._noise_1d(sample_x)
|
|
||||||
|
|
||||||
value += tasmota.scale_uint(noise_val, 0, 255, 0, amplitude)
|
|
||||||
max_value += amplitude
|
|
||||||
|
|
||||||
amplitude = tasmota.scale_uint(amplitude, 0, 255, 0, current_persistence)
|
|
||||||
frequency = frequency * 2
|
|
||||||
if frequency > 255
|
|
||||||
frequency = 255
|
|
||||||
end
|
|
||||||
|
|
||||||
octave += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
# Normalize to 0-255 range
|
|
||||||
if max_value > 0
|
|
||||||
value = tasmota.scale_uint(value, 0, max_value, 0, 255)
|
|
||||||
end
|
|
||||||
|
|
||||||
return value
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update animation state
|
|
||||||
def update(time_ms)
|
|
||||||
super(self).update(time_ms)
|
|
||||||
|
|
||||||
# Update time offset based on speed
|
|
||||||
var current_speed = self.speed
|
|
||||||
if current_speed > 0
|
|
||||||
var elapsed = time_ms - self.start_time
|
|
||||||
# Speed: 0-255 maps to 0-5 units per second
|
|
||||||
var units_per_second = tasmota.scale_uint(current_speed, 0, 255, 0, 5)
|
|
||||||
if units_per_second > 0
|
|
||||||
self.time_offset = (elapsed * units_per_second / 1000) % 256
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Calculate noise colors
|
|
||||||
self._calculate_noise(time_ms)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Calculate noise colors for all pixels
|
|
||||||
def _calculate_noise(time_ms)
|
|
||||||
var strip_length = self.engine.strip_length
|
|
||||||
var current_color = self.color
|
|
||||||
|
|
||||||
var i = 0
|
|
||||||
while i < strip_length
|
|
||||||
# Calculate noise value for this pixel
|
|
||||||
var noise_value = self._fractal_noise(i, self.time_offset)
|
|
||||||
|
|
||||||
# Get color from provider
|
|
||||||
var color = 0xFF000000
|
|
||||||
|
|
||||||
# If the color is a provider that supports get_color_for_value, use it
|
|
||||||
if animation.is_color_provider(current_color) && current_color.get_color_for_value != nil
|
|
||||||
color = current_color.get_color_for_value(noise_value, 0)
|
|
||||||
else
|
|
||||||
# Use resolve_value with noise influence
|
|
||||||
color = self.resolve_value(current_color, "color", time_ms + noise_value * 10)
|
|
||||||
end
|
|
||||||
|
|
||||||
self.current_colors[i] = color
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Render noise to frame buffer
|
|
||||||
def render(frame, time_ms, strip_length)
|
|
||||||
var i = 0
|
|
||||||
while i < strip_length
|
|
||||||
if i < frame.width
|
|
||||||
frame.set_pixel_color(i, self.current_colors[i])
|
|
||||||
end
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Factory functions following new specification
|
|
||||||
|
|
||||||
# Create a rainbow noise animation preset
|
|
||||||
def noise_rainbow(engine)
|
|
||||||
var anim = animation.noise_animation(engine)
|
|
||||||
# Set up rainbow color provider
|
|
||||||
var rainbow_provider = animation.rich_palette(engine)
|
|
||||||
rainbow_provider.colors = animation.PALETTE_RAINBOW
|
|
||||||
rainbow_provider.period = 5000
|
|
||||||
rainbow_provider.transition_type = 1
|
|
||||||
rainbow_provider.brightness = 255
|
|
||||||
anim.color = rainbow_provider
|
|
||||||
anim.scale = 50
|
|
||||||
anim.speed = 30
|
|
||||||
anim.octaves = 1
|
|
||||||
return anim
|
|
||||||
end
|
|
||||||
|
|
||||||
# Create a single color noise animation preset
|
|
||||||
def noise_single_color(engine)
|
|
||||||
var anim = animation.noise_animation(engine)
|
|
||||||
# Set up a simple white color - user can change it after creation
|
|
||||||
anim.color = 0xFFFFFFFF
|
|
||||||
anim.scale = 50
|
|
||||||
anim.speed = 30
|
|
||||||
anim.octaves = 1
|
|
||||||
return anim
|
|
||||||
end
|
|
||||||
|
|
||||||
# Create a fractal noise animation preset
|
|
||||||
def noise_fractal(engine)
|
|
||||||
var anim = animation.noise_animation(engine)
|
|
||||||
# Set up rainbow color provider
|
|
||||||
var rainbow_provider = animation.rich_palette(engine)
|
|
||||||
rainbow_provider.colors = animation.PALETTE_RAINBOW
|
|
||||||
rainbow_provider.period = 5000
|
|
||||||
rainbow_provider.transition_type = 1
|
|
||||||
rainbow_provider.brightness = 255
|
|
||||||
anim.color = rainbow_provider
|
|
||||||
anim.scale = 30
|
|
||||||
anim.speed = 20
|
|
||||||
anim.octaves = 3
|
|
||||||
anim.persistence = 128
|
|
||||||
return anim
|
|
||||||
end
|
|
||||||
|
|
||||||
return {'noise_animation': NoiseAnimation, 'noise_rainbow': noise_rainbow, 'noise_single_color': noise_single_color, 'noise_fractal': noise_fractal}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,232 +0,0 @@
|
|||||||
# Test suite for NoiseAnimation
|
|
||||||
#
|
|
||||||
# This test verifies that the NoiseAnimation works correctly
|
|
||||||
# with different parameters and color providers.
|
|
||||||
|
|
||||||
import animation
|
|
||||||
import string
|
|
||||||
|
|
||||||
# Test basic NoiseAnimation creation and functionality
|
|
||||||
def test_noise_animation_basic()
|
|
||||||
print("Testing basic NoiseAnimation...")
|
|
||||||
|
|
||||||
# Create LED strip and engine
|
|
||||||
var strip = global.Leds(10)
|
|
||||||
var engine = animation.create_engine(strip)
|
|
||||||
|
|
||||||
# Test with default parameters
|
|
||||||
var noise_anim = animation.noise_animation(engine)
|
|
||||||
|
|
||||||
assert(noise_anim != nil, "NoiseAnimation should be created")
|
|
||||||
assert(noise_anim.scale == 50, "Default scale should be 50")
|
|
||||||
assert(noise_anim.speed == 30, "Default speed should be 30")
|
|
||||||
assert(noise_anim.octaves == 1, "Default octaves should be 1")
|
|
||||||
assert(noise_anim.is_running == false, "Animation should not be running initially")
|
|
||||||
|
|
||||||
print("✓ Basic NoiseAnimation test passed")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test NoiseAnimation with custom parameters
|
|
||||||
def test_noise_animation_custom()
|
|
||||||
print("Testing NoiseAnimation with custom parameters...")
|
|
||||||
|
|
||||||
# Create LED strip and engine
|
|
||||||
var strip = global.Leds(20)
|
|
||||||
var engine = animation.create_engine(strip)
|
|
||||||
|
|
||||||
# Test with custom parameters
|
|
||||||
var noise_anim = animation.noise_animation(engine)
|
|
||||||
noise_anim.color = 0xFF00FF00
|
|
||||||
noise_anim.scale = 100
|
|
||||||
noise_anim.speed = 80
|
|
||||||
noise_anim.octaves = 2
|
|
||||||
noise_anim.persistence = 200
|
|
||||||
noise_anim.seed = 12345
|
|
||||||
noise_anim.priority = 15
|
|
||||||
noise_anim.duration = 5000
|
|
||||||
noise_anim.loop = false
|
|
||||||
|
|
||||||
assert(noise_anim.scale == 100, "Custom scale should be 100")
|
|
||||||
assert(noise_anim.speed == 80, "Custom speed should be 80")
|
|
||||||
assert(noise_anim.octaves == 2, "Custom octaves should be 2")
|
|
||||||
assert(noise_anim.persistence == 200, "Custom persistence should be 200")
|
|
||||||
assert(noise_anim.seed == 12345, "Custom seed should be 12345")
|
|
||||||
assert(noise_anim.priority == 15, "Custom priority should be 15")
|
|
||||||
assert(noise_anim.duration == 5000, "Custom duration should be 5000")
|
|
||||||
assert(noise_anim.loop == false, "Custom loop should be false")
|
|
||||||
|
|
||||||
print("✓ Custom NoiseAnimation test passed")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test NoiseAnimation parameter changes
|
|
||||||
def test_noise_animation_parameters()
|
|
||||||
print("Testing NoiseAnimation parameter changes...")
|
|
||||||
|
|
||||||
# Create LED strip and engine
|
|
||||||
var strip = global.Leds(15)
|
|
||||||
var engine = animation.create_engine(strip)
|
|
||||||
|
|
||||||
var noise_anim = animation.noise_animation(engine)
|
|
||||||
|
|
||||||
# Test parameter changes via virtual member assignment
|
|
||||||
noise_anim.scale = 75
|
|
||||||
assert(noise_anim.scale == 75, "Scale should be updated to 75")
|
|
||||||
|
|
||||||
noise_anim.speed = 120
|
|
||||||
assert(noise_anim.speed == 120, "Speed should be updated to 120")
|
|
||||||
|
|
||||||
noise_anim.octaves = 3
|
|
||||||
assert(noise_anim.octaves == 3, "Octaves should be updated to 3")
|
|
||||||
|
|
||||||
# Test that current_colors array adapts to engine strip length
|
|
||||||
var initial_size = size(noise_anim.current_colors)
|
|
||||||
assert(initial_size == 15, "Current colors array should match engine strip length")
|
|
||||||
|
|
||||||
print("✓ NoiseAnimation parameter test passed")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test NoiseAnimation update and render
|
|
||||||
def test_noise_animation_update_render()
|
|
||||||
print("Testing NoiseAnimation update and render...")
|
|
||||||
|
|
||||||
# Create LED strip and engine
|
|
||||||
var strip = global.Leds(10)
|
|
||||||
var engine = animation.create_engine(strip)
|
|
||||||
|
|
||||||
var noise_anim = animation.noise_animation(engine)
|
|
||||||
noise_anim.color = 0xFFFF0000
|
|
||||||
noise_anim.scale = 60
|
|
||||||
noise_anim.speed = 40
|
|
||||||
|
|
||||||
var frame = animation.frame_buffer(10)
|
|
||||||
|
|
||||||
# Start animation
|
|
||||||
# Note: When testing animations directly (not through engine_proxy), we must set start_time manually
|
|
||||||
noise_anim.start_time = 1000 # Set start_time manually for direct testing
|
|
||||||
noise_anim.start(1000)
|
|
||||||
assert(noise_anim.is_running == true, "Animation should be running after start")
|
|
||||||
|
|
||||||
# Test update
|
|
||||||
noise_anim.update(1500)
|
|
||||||
assert(noise_anim.is_running == true, "Animation should still be running after update")
|
|
||||||
|
|
||||||
# Test render
|
|
||||||
var result = noise_anim.render(frame, 1500, engine.strip_length)
|
|
||||||
assert(result == true, "Render should return true for running animation")
|
|
||||||
|
|
||||||
# Check that colors were set (should not all be black)
|
|
||||||
var has_non_black = false
|
|
||||||
var i = 0
|
|
||||||
while i < frame.width
|
|
||||||
if frame.get_pixel_color(i) != 0xFF000000
|
|
||||||
has_non_black = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
assert(has_non_black == true, "Frame should have non-black pixels after render")
|
|
||||||
|
|
||||||
print("✓ NoiseAnimation update/render test passed")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test global constructor functions
|
|
||||||
def test_noise_constructors()
|
|
||||||
print("Testing noise constructor functions...")
|
|
||||||
|
|
||||||
# Create LED strip and engine
|
|
||||||
var strip = global.Leds(15)
|
|
||||||
var engine = animation.create_engine(strip)
|
|
||||||
|
|
||||||
# Test noise_rainbow
|
|
||||||
var rainbow_noise = animation.noise_rainbow(engine)
|
|
||||||
assert(rainbow_noise != nil, "noise_rainbow should create animation")
|
|
||||||
assert(rainbow_noise.scale == 50, "Rainbow noise should have correct scale")
|
|
||||||
assert(rainbow_noise.speed == 30, "Rainbow noise should have correct speed")
|
|
||||||
assert(rainbow_noise.octaves == 1, "Rainbow noise should have correct octaves")
|
|
||||||
|
|
||||||
# Test noise_single_color
|
|
||||||
var single_noise = animation.noise_single_color(engine)
|
|
||||||
assert(single_noise != nil, "noise_single_color should create animation")
|
|
||||||
assert(single_noise.scale == 50, "Single color noise should have correct scale")
|
|
||||||
assert(single_noise.speed == 30, "Single color noise should have correct speed")
|
|
||||||
assert(single_noise.color == 0xFFFFFFFF, "Single color noise should have white color")
|
|
||||||
|
|
||||||
# Test noise_fractal
|
|
||||||
var fractal_noise = animation.noise_fractal(engine)
|
|
||||||
assert(fractal_noise != nil, "noise_fractal should create animation")
|
|
||||||
assert(fractal_noise.scale == 30, "Fractal noise should have correct scale")
|
|
||||||
assert(fractal_noise.octaves == 3, "Fractal noise should have correct octaves")
|
|
||||||
|
|
||||||
print("✓ Noise constructor functions test passed")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test NoiseAnimation string representation
|
|
||||||
def test_noise_tostring()
|
|
||||||
print("Testing NoiseAnimation string representation...")
|
|
||||||
|
|
||||||
# Create LED strip and engine
|
|
||||||
var strip = global.Leds(12)
|
|
||||||
var engine = animation.create_engine(strip)
|
|
||||||
|
|
||||||
var noise_anim = animation.noise_animation(engine)
|
|
||||||
noise_anim.scale = 75
|
|
||||||
noise_anim.speed = 45
|
|
||||||
noise_anim.octaves = 2
|
|
||||||
noise_anim.persistence = 150
|
|
||||||
|
|
||||||
var str_repr = str(noise_anim)
|
|
||||||
|
|
||||||
assert(type(str_repr) == "string", "String representation should be a string")
|
|
||||||
|
|
||||||
print("✓ NoiseAnimation string representation test passed")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test integer color conversion to gradient
|
|
||||||
def test_noise_integer_color_conversion()
|
|
||||||
print("Testing NoiseAnimation integer color conversion...")
|
|
||||||
|
|
||||||
# Create LED strip and engine
|
|
||||||
var strip = global.Leds(5)
|
|
||||||
var engine = animation.create_engine(strip)
|
|
||||||
|
|
||||||
var noise_anim = animation.noise_animation(engine)
|
|
||||||
|
|
||||||
# Set an integer color - should be converted to gradient provider
|
|
||||||
noise_anim.color = 0xFFFF0000 # Red
|
|
||||||
|
|
||||||
# Check the raw parameter value (should be a color provider)
|
|
||||||
var raw_color = noise_anim.get_param("color")
|
|
||||||
|
|
||||||
# Test that the raw parameter is a color provider (the conversion worked)
|
|
||||||
assert(animation.is_color_provider(raw_color), "Integer color should be converted to color provider")
|
|
||||||
|
|
||||||
print("✓ NoiseAnimation integer color conversion test passed")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Run all tests
|
|
||||||
def run_noise_animation_tests()
|
|
||||||
print("=== NoiseAnimation Tests ===")
|
|
||||||
|
|
||||||
try
|
|
||||||
test_noise_animation_basic()
|
|
||||||
test_noise_animation_custom()
|
|
||||||
test_noise_animation_parameters()
|
|
||||||
test_noise_animation_update_render()
|
|
||||||
test_noise_constructors()
|
|
||||||
test_noise_tostring()
|
|
||||||
test_noise_integer_color_conversion()
|
|
||||||
|
|
||||||
print("=== All NoiseAnimation tests passed! ===")
|
|
||||||
return true
|
|
||||||
except .. as e, msg
|
|
||||||
print(f"Test failed: {e} - {msg}")
|
|
||||||
raise "test_failed"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Export the test function
|
|
||||||
animation.run_noise_animation_tests = run_noise_animation_tests
|
|
||||||
|
|
||||||
run_noise_animation_tests()
|
|
||||||
|
|
||||||
return run_noise_animation_tests
|
|
||||||
@ -75,9 +75,9 @@ def run_all_tests()
|
|||||||
"lib/libesp32/berry_animation/src/tests/twinkle_animation_test.be",
|
"lib/libesp32/berry_animation/src/tests/twinkle_animation_test.be",
|
||||||
"lib/libesp32/berry_animation/src/tests/crenel_position_animation_test.be",
|
"lib/libesp32/berry_animation/src/tests/crenel_position_animation_test.be",
|
||||||
"lib/libesp32/berry_animation/src/tests/beacon_animation_test.be",
|
"lib/libesp32/berry_animation/src/tests/beacon_animation_test.be",
|
||||||
|
"lib/libesp32/berry_animation/src/tests/test_beacon_direction.be",
|
||||||
"lib/libesp32/berry_animation/src/tests/gradient_animation_test.be",
|
"lib/libesp32/berry_animation/src/tests/gradient_animation_test.be",
|
||||||
"lib/libesp32/berry_animation/src/tests/palette_meter_animation_test.be",
|
"lib/libesp32/berry_animation/src/tests/palette_meter_animation_test.be",
|
||||||
"lib/libesp32/berry_animation/src/tests/noise_animation_test.be",
|
|
||||||
# "lib/libesp32/berry_animation/src/tests/plasma_animation_test.be",
|
# "lib/libesp32/berry_animation/src/tests/plasma_animation_test.be",
|
||||||
# "lib/libesp32/berry_animation/src/tests/sparkle_animation_test.be",
|
# "lib/libesp32/berry_animation/src/tests/sparkle_animation_test.be",
|
||||||
"lib/libesp32/berry_animation/src/tests/wave_animation_test.be",
|
"lib/libesp32/berry_animation/src/tests/wave_animation_test.be",
|
||||||
|
|||||||
138
lib/libesp32/berry_animation/src/tests/test_beacon_direction.be
Normal file
138
lib/libesp32/berry_animation/src/tests/test_beacon_direction.be
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
# Test for beacon animation right_edge parameter
|
||||||
|
# Tests that right_edge=1 positions the beacon using pos as the right edge of the beacon
|
||||||
|
|
||||||
|
import animation
|
||||||
|
import global
|
||||||
|
|
||||||
|
# Test counter
|
||||||
|
var test_count = 0
|
||||||
|
var passed_count = 0
|
||||||
|
|
||||||
|
# Create LED strip and engine for testing
|
||||||
|
var strip = global.Leds(10) # Use built-in LED strip for testing
|
||||||
|
var engine = animation.create_engine(strip)
|
||||||
|
|
||||||
|
def test_assert(condition, message)
|
||||||
|
test_count += 1
|
||||||
|
if condition
|
||||||
|
passed_count += 1
|
||||||
|
print(f"✓ Test {test_count}: {message}")
|
||||||
|
else
|
||||||
|
print(f"✗ Test {test_count}: {message}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_tests()
|
||||||
|
print("Running Beacon Animation right_edge Tests...")
|
||||||
|
print("==================================================")
|
||||||
|
|
||||||
|
var strip_length = 10
|
||||||
|
var frame = animation.frame_buffer(strip_length)
|
||||||
|
|
||||||
|
# Test 1: right_edge=0 (default, left edge), pos=0, beacon_size=1
|
||||||
|
# Should show 1 pixel at position 0 (left edge)
|
||||||
|
var beacon1 = animation.beacon_animation(engine)
|
||||||
|
beacon1.color = 0xFFFF0000 # Red
|
||||||
|
beacon1.back_color = 0xFF000000 # Black
|
||||||
|
beacon1.pos = 0
|
||||||
|
beacon1.beacon_size = 1
|
||||||
|
beacon1.slew_size = 0
|
||||||
|
beacon1.right_edge = 0
|
||||||
|
beacon1.start()
|
||||||
|
|
||||||
|
frame.fill_pixels(frame.pixels, 0xFF000000) # Clear to black
|
||||||
|
beacon1.render(frame, 0, strip_length)
|
||||||
|
|
||||||
|
# Check pixel 0 is red, others are black
|
||||||
|
test_assert(frame.get_pixel_color(0) == 0xFFFF0000, "right_edge=0, pos=0: pixel 0 is red")
|
||||||
|
test_assert(frame.get_pixel_color(1) == 0xFF000000, "right_edge=0, pos=0: pixel 1 is black")
|
||||||
|
test_assert(frame.get_pixel_color(9) == 0xFF000000, "right_edge=0, pos=0: pixel 9 is black")
|
||||||
|
|
||||||
|
# Test 2: right_edge=1 (right edge), pos=0, beacon_size=1
|
||||||
|
# With right_edge=1: effective_pos = pos - beacon_size + 1 = 0 - 1 + 1 = 0
|
||||||
|
# So pixel 0 should be lit
|
||||||
|
var beacon2 = animation.beacon_animation(engine)
|
||||||
|
beacon2.color = 0xFF00FF00 # Green
|
||||||
|
beacon2.back_color = 0xFF000000 # Black
|
||||||
|
beacon2.pos = 0
|
||||||
|
beacon2.beacon_size = 1
|
||||||
|
beacon2.slew_size = 0
|
||||||
|
beacon2.right_edge = 1
|
||||||
|
beacon2.start()
|
||||||
|
|
||||||
|
frame.fill_pixels(frame.pixels, 0xFF000000) # Clear to black
|
||||||
|
beacon2.render(frame, 0, strip_length)
|
||||||
|
|
||||||
|
# Check pixel 0 is green (right edge of beacon at pos=0)
|
||||||
|
test_assert(frame.get_pixel_color(0) == 0xFF00FF00, "right_edge=1, pos=0: pixel 0 is green")
|
||||||
|
test_assert(frame.get_pixel_color(1) == 0xFF000000, "right_edge=1, pos=0: pixel 1 is black")
|
||||||
|
|
||||||
|
# Test 3: right_edge=1, pos=5, beacon_size=3
|
||||||
|
# With right_edge=1: effective_pos = pos - beacon_size + 1 = 5 - 3 + 1 = 3
|
||||||
|
# So pixels 3,4,5 should be lit (beacon from 3 to 5, with right edge at 5)
|
||||||
|
var beacon3 = animation.beacon_animation(engine)
|
||||||
|
beacon3.color = 0xFF0000FF # Blue
|
||||||
|
beacon3.back_color = 0xFF000000 # Black
|
||||||
|
beacon3.pos = 5
|
||||||
|
beacon3.beacon_size = 3
|
||||||
|
beacon3.slew_size = 0
|
||||||
|
beacon3.right_edge = 1
|
||||||
|
beacon3.start()
|
||||||
|
|
||||||
|
frame.fill_pixels(frame.pixels, 0xFF000000) # Clear to black
|
||||||
|
beacon3.render(frame, 0, strip_length)
|
||||||
|
|
||||||
|
test_assert(frame.get_pixel_color(2) == 0xFF000000, "right_edge=1, pos=5, size=3: pixel 2 is black")
|
||||||
|
test_assert(frame.get_pixel_color(3) == 0xFF0000FF, "right_edge=1, pos=5, size=3: pixel 3 is blue")
|
||||||
|
test_assert(frame.get_pixel_color(4) == 0xFF0000FF, "right_edge=1, pos=5, size=3: pixel 4 is blue")
|
||||||
|
test_assert(frame.get_pixel_color(5) == 0xFF0000FF, "right_edge=1, pos=5, size=3: pixel 5 is blue")
|
||||||
|
test_assert(frame.get_pixel_color(6) == 0xFF000000, "right_edge=1, pos=5, size=3: pixel 6 is black")
|
||||||
|
|
||||||
|
# Test 4: right_edge=0, pos=2, beacon_size=3 (same params, different right_edge)
|
||||||
|
# Should show pixels 2,3,4 lit
|
||||||
|
var beacon4 = animation.beacon_animation(engine)
|
||||||
|
beacon4.color = 0xFFFFFF00 # Yellow
|
||||||
|
beacon4.back_color = 0xFF000000 # Black
|
||||||
|
beacon4.pos = 2
|
||||||
|
beacon4.beacon_size = 3
|
||||||
|
beacon4.slew_size = 0
|
||||||
|
beacon4.right_edge = 0
|
||||||
|
beacon4.start()
|
||||||
|
|
||||||
|
frame.fill_pixels(frame.pixels, 0xFF000000) # Clear to black
|
||||||
|
beacon4.render(frame, 0, strip_length)
|
||||||
|
|
||||||
|
test_assert(frame.get_pixel_color(1) == 0xFF000000, "right_edge=0, pos=2, size=3: pixel 1 is black")
|
||||||
|
test_assert(frame.get_pixel_color(2) == 0xFFFFFF00, "right_edge=0, pos=2, size=3: pixel 2 is yellow")
|
||||||
|
test_assert(frame.get_pixel_color(3) == 0xFFFFFF00, "right_edge=0, pos=2, size=3: pixel 3 is yellow")
|
||||||
|
test_assert(frame.get_pixel_color(4) == 0xFFFFFF00, "right_edge=0, pos=2, size=3: pixel 4 is yellow")
|
||||||
|
test_assert(frame.get_pixel_color(5) == 0xFF000000, "right_edge=0, pos=2, size=3: pixel 5 is black")
|
||||||
|
|
||||||
|
# Test 5: Default right_edge should be 0
|
||||||
|
var beacon5 = animation.beacon_animation(engine)
|
||||||
|
test_assert(beacon5.right_edge == 0, "Default right_edge is 0")
|
||||||
|
|
||||||
|
# Test 6: Invalid right_edge value should be rejected
|
||||||
|
try
|
||||||
|
beacon5.right_edge = 2
|
||||||
|
test_assert(false, "Invalid right_edge value should be rejected")
|
||||||
|
except "value_error"
|
||||||
|
test_assert(true, "Invalid right_edge value properly rejected")
|
||||||
|
end
|
||||||
|
|
||||||
|
print("==================================================")
|
||||||
|
print(f"Tests completed: {passed_count}/{test_count} passed")
|
||||||
|
|
||||||
|
if passed_count == test_count
|
||||||
|
print("🎉 All tests passed!")
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
print(f"❌ {test_count - passed_count} tests failed")
|
||||||
|
raise "test_failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run the tests
|
||||||
|
var success = run_tests()
|
||||||
|
|
||||||
|
return success
|
||||||
Loading…
Reference in New Issue
Block a user