use crate::render::buffer::Buffer;
use crate::render::cell::cellsym;
use crate::render::style::Color;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(usize)]
#[derive(Default)]
pub enum GpuTransition {
#[default]
Squares = 0,
Heart = 1,
Noise = 2,
RotateZoom = 3,
Bounce = 4,
Dispersion = 5,
Ripple = 6,
}
impl GpuTransition {
pub fn all() -> &'static [GpuTransition] {
&[
GpuTransition::Squares,
GpuTransition::Heart,
GpuTransition::Noise,
GpuTransition::RotateZoom,
GpuTransition::Bounce,
GpuTransition::Dispersion,
GpuTransition::Ripple,
]
}
pub fn name(&self) -> &'static str {
match self {
GpuTransition::Squares => "Squares",
GpuTransition::Heart => "Heart",
GpuTransition::Noise => "Noise",
GpuTransition::RotateZoom => "RotateZoom",
GpuTransition::Bounce => "Bounce",
GpuTransition::Dispersion => "Dispersion",
GpuTransition::Ripple => "Ripple",
}
}
pub fn name_cn(&self) -> &'static str {
match self {
GpuTransition::Squares => "方格渐变",
GpuTransition::Heart => "心形展开",
GpuTransition::Noise => "噪点过渡",
GpuTransition::RotateZoom => "旋转缩放",
GpuTransition::Bounce => "弹跳波浪",
GpuTransition::Dispersion => "色散分离",
GpuTransition::Ripple => "涟漪扩散",
}
}
pub fn description(&self) -> &'static str {
match self {
GpuTransition::Squares => "Grid squares transition with dynamic sizing",
GpuTransition::Heart => "Heart-shaped reveal from center",
GpuTransition::Noise => "TV static noise transition",
GpuTransition::RotateZoom => "Rotation with zoom in/out effect",
GpuTransition::Bounce => "Bouncing wave wipe transition",
GpuTransition::Dispersion => "RGB chromatic aberration effect",
GpuTransition::Ripple => "Circular ripple wave from center",
}
}
pub const fn count() -> usize {
7
}
pub fn from_index(index: usize) -> Self {
match index % Self::count() {
0 => GpuTransition::Squares,
1 => GpuTransition::Heart,
2 => GpuTransition::Noise,
3 => GpuTransition::RotateZoom,
4 => GpuTransition::Bounce,
5 => GpuTransition::Dispersion,
_ => GpuTransition::Ripple,
}
}
pub fn next(&self) -> Self {
Self::from_index(*self as usize + 1)
}
pub fn prev(&self) -> Self {
let idx = *self as usize;
Self::from_index(if idx == 0 { Self::count() - 1 } else { idx - 1 })
}
}
impl From<GpuTransition> for usize {
fn from(t: GpuTransition) -> Self {
t as usize
}
}
impl From<usize> for GpuTransition {
fn from(v: usize) -> Self {
GpuTransition::from_index(v)
}
}
#[derive(Clone, Debug)]
pub struct GpuBlendEffect {
pub transition: GpuTransition,
pub progress: f32,
}
impl GpuBlendEffect {
pub fn new(transition: GpuTransition, progress: f32) -> Self {
Self {
transition,
progress: progress.clamp(0.0, 1.0),
}
}
#[inline]
pub fn effect_type(&self) -> usize {
self.transition as usize
}
pub fn with_progress(mut self, progress: f32) -> Self {
self.progress = progress.clamp(0.0, 1.0);
self
}
pub fn set_progress(&mut self, progress: f32) {
self.progress = progress.clamp(0.0, 1.0);
}
pub fn squares(progress: f32) -> Self {
Self::new(GpuTransition::Squares, progress)
}
pub fn heart(progress: f32) -> Self {
Self::new(GpuTransition::Heart, progress)
}
pub fn noise(progress: f32) -> Self {
Self::new(GpuTransition::Noise, progress)
}
pub fn rotate_zoom(progress: f32) -> Self {
Self::new(GpuTransition::RotateZoom, progress)
}
pub fn bounce(progress: f32) -> Self {
Self::new(GpuTransition::Bounce, progress)
}
pub fn dispersion(progress: f32) -> Self {
Self::new(GpuTransition::Dispersion, progress)
}
pub fn ripple(progress: f32) -> Self {
Self::new(GpuTransition::Ripple, progress)
}
pub fn from_index(index: usize, progress: f32) -> Self {
Self::new(GpuTransition::from_index(index), progress)
}
}
impl Default for GpuBlendEffect {
fn default() -> Self {
Self::new(GpuTransition::Squares, 0.0)
}
}
#[derive(Clone, Debug)]
pub struct EffectParams {
pub time: f32,
pub stage: usize,
pub seed: u32,
pub intensity: f32,
}
impl EffectParams {
pub fn new(time: f32, stage: usize) -> Self {
Self {
time,
stage,
seed: 0,
intensity: 1.0,
}
}
pub fn with_seed(mut self, seed: u32) -> Self {
self.seed = seed;
self
}
pub fn with_intensity(mut self, intensity: f32) -> Self {
self.intensity = intensity.clamp(0.0, 1.0);
self
}
}
impl Default for EffectParams {
fn default() -> Self {
Self::new(0.0, 0)
}
}
pub trait BufferEffect: Send + Sync {
fn apply(&self, src: &Buffer, dst: &mut Buffer, params: &EffectParams);
fn name(&self) -> &'static str;
}
pub fn apply_distortion<F>(src: &Buffer, dst: &mut Buffer, distortion_fn: F)
where
F: Fn(f32, f32) -> (f32, f32),
{
let width = src.area.width as i32;
let height = src.area.height as i32;
for y in 0..height {
for x in 0..width {
let u = x as f32 / width as f32;
let v = y as f32 / height as f32;
let (du, dv) = distortion_fn(u, v);
let src_x = (du * width as f32).round() as i32;
let src_y = (dv * height as f32).round() as i32;
let src_x = src_x.clamp(0, width - 1);
let src_y = src_y.clamp(0, height - 1);
let src_index = (src_y * width + src_x) as usize;
let dest_index = (y * width + x) as usize;
if let (Some(src_cell), Some(dest_cell)) = (
src.content.get(src_index),
dst.content.get_mut(dest_index),
) {
*dest_cell = src_cell.clone();
}
}
}
}
#[derive(Clone, Debug)]
pub struct WaveEffect {
pub amplitude: f32,
pub frequency: f32,
}
impl WaveEffect {
pub fn new(amplitude: f32, frequency: f32) -> Self {
Self { amplitude, frequency }
}
}
impl BufferEffect for WaveEffect {
fn apply(&self, src: &Buffer, dst: &mut Buffer, params: &EffectParams) {
let time = params.time;
let amp = self.amplitude * params.intensity;
let freq = self.frequency;
apply_distortion(src, dst, |u, v| {
let offset_x = u + amp * (freq * v + time).sin();
(offset_x, v)
});
}
fn name(&self) -> &'static str {
"WaveEffect"
}
}
#[derive(Clone, Debug)]
pub struct RippleEffect {
pub amplitude: f32,
pub frequency: f32,
pub center_x: f32,
pub center_y: f32,
}
impl RippleEffect {
pub fn new(amplitude: f32, frequency: f32) -> Self {
Self {
amplitude,
frequency,
center_x: 0.5,
center_y: 0.5,
}
}
pub fn with_center(mut self, cx: f32, cy: f32) -> Self {
self.center_x = cx;
self.center_y = cy;
self
}
}
impl BufferEffect for RippleEffect {
fn apply(&self, src: &Buffer, dst: &mut Buffer, params: &EffectParams) {
let time = params.time;
let amp = self.amplitude * params.intensity;
let freq = self.frequency;
let cx = self.center_x;
let cy = self.center_y;
apply_distortion(src, dst, |u, v| {
let dx = u - cx;
let dy = v - cy;
let distance = (dx * dx + dy * dy).sqrt();
if distance < 0.001 {
return (u, v);
}
let offset = amp * (freq * distance - time).sin();
let du = u + (dx / distance) * offset;
let dv = v + (dy / distance) * offset;
(du, dv)
});
}
fn name(&self) -> &'static str {
"RippleEffect"
}
}
#[derive(Clone, Debug)]
pub struct SwirlEffect {
pub strength: f32,
pub radius: f32,
pub center_x: f32,
pub center_y: f32,
}
impl SwirlEffect {
pub fn new(strength: f32, radius: f32) -> Self {
Self {
strength,
radius,
center_x: 0.5,
center_y: 0.5,
}
}
}
impl BufferEffect for SwirlEffect {
fn apply(&self, src: &Buffer, dst: &mut Buffer, params: &EffectParams) {
let strength = self.strength * params.intensity;
let radius = self.radius;
let cx = self.center_x;
let cy = self.center_y;
apply_distortion(src, dst, |u, v| {
let dx = u - cx;
let dy = v - cy;
let distance = (dx * dx + dy * dy).sqrt();
if distance > radius || distance < 0.001 {
return (u, v);
}
let factor = 1.0 - distance / radius;
let angle = strength * factor * factor;
let cos_a = angle.cos();
let sin_a = angle.sin();
let new_dx = dx * cos_a - dy * sin_a;
let new_dy = dx * sin_a + dy * cos_a;
(cx + new_dx, cy + new_dy)
});
}
fn name(&self) -> &'static str {
"SwirlEffect"
}
}
#[derive(Clone, Debug)]
pub struct NoiseEffect {
pub density: f32,
pub color: Option<Color>,
}
impl NoiseEffect {
pub fn new(density: f32) -> Self {
Self {
density: density.clamp(0.0, 1.0),
color: None,
}
}
pub fn with_color(mut self, color: Color) -> Self {
self.color = Some(color);
self
}
}
impl BufferEffect for NoiseEffect {
fn apply(&self, src: &Buffer, dst: &mut Buffer, params: &EffectParams) {
dst.content.clone_from(&src.content);
let density = self.density * params.intensity;
let cell_count = dst.content.len();
let noise_count = (cell_count as f32 * density) as usize;
let mut rng = params.seed.wrapping_add(params.stage as u32);
let lcg_next = |r: &mut u32| -> u32 {
*r = r.wrapping_mul(1103515245).wrapping_add(12345);
*r
};
for _ in 0..noise_count {
let idx = lcg_next(&mut rng) as usize % cell_count;
let sym = (lcg_next(&mut rng) % 255) as u8;
let color = self.color.unwrap_or_else(|| {
let gray = 100 + (lcg_next(&mut rng) % 100) as u8;
Color::Rgba(gray, gray, gray, 200)
});
if let Some(cell) = dst.content.get_mut(idx) {
cell.set_symbol(&cellsym(sym)).set_fg(color);
}
}
}
fn name(&self) -> &'static str {
"NoiseEffect"
}
}
#[derive(Clone, Debug)]
pub struct FadeEffect {
pub target_alpha: u8,
}
impl FadeEffect {
pub fn new(alpha: u8) -> Self {
Self { target_alpha: alpha }
}
pub fn fade_in() -> Self {
Self { target_alpha: 255 }
}
pub fn fade_out() -> Self {
Self { target_alpha: 0 }
}
}
impl BufferEffect for FadeEffect {
fn apply(&self, src: &Buffer, dst: &mut Buffer, params: &EffectParams) {
dst.content.clone_from(&src.content);
let alpha = ((self.target_alpha as f32) * params.intensity) as u8;
for cell in dst.content.iter_mut() {
if let Color::Rgba(r, g, b, _) = cell.fg {
cell.fg = Color::Rgba(r, g, b, alpha);
}
}
}
fn name(&self) -> &'static str {
"FadeEffect"
}
}
#[derive(Clone, Debug)]
pub struct PixelateEffect {
pub block_size: u16,
}
impl PixelateEffect {
pub fn new(block_size: u16) -> Self {
Self {
block_size: block_size.max(1),
}
}
}
impl BufferEffect for PixelateEffect {
fn apply(&self, src: &Buffer, dst: &mut Buffer, params: &EffectParams) {
let width = src.area.width as usize;
let height = src.area.height as usize;
let block = (self.block_size as f32 * params.intensity).max(1.0) as usize;
dst.content.clone_from(&src.content);
for by in (0..height).step_by(block) {
for bx in (0..width).step_by(block) {
let sample_x = (bx + block / 2).min(width - 1);
let sample_y = (by + block / 2).min(height - 1);
let sample_idx = sample_y * width + sample_x;
if let Some(sample_cell) = src.content.get(sample_idx) {
for dy in 0..block {
for dx in 0..block {
let x = bx + dx;
let y = by + dy;
if x < width && y < height {
let idx = y * width + x;
if let Some(cell) = dst.content.get_mut(idx) {
*cell = sample_cell.clone();
}
}
}
}
}
}
}
}
fn name(&self) -> &'static str {
"PixelateEffect"
}
}
#[derive(Clone, Debug)]
pub struct BlurEffect {
pub radius: u16,
}
impl BlurEffect {
pub fn new(radius: u16) -> Self {
Self {
radius: radius.max(1),
}
}
}
impl BufferEffect for BlurEffect {
fn apply(&self, src: &Buffer, dst: &mut Buffer, params: &EffectParams) {
let width = src.area.width as i32;
let height = src.area.height as i32;
let radius = (self.radius as f32 * params.intensity).max(1.0) as i32;
dst.content.clone_from(&src.content);
for y in 0..height {
for x in 0..width {
let offset_x = ((x + radius / 2) % (radius * 2 + 1)) - radius;
let offset_y = ((y + radius / 2) % (radius * 2 + 1)) - radius;
let sample_x = (x + offset_x).clamp(0, width - 1);
let sample_y = (y + offset_y).clamp(0, height - 1);
let src_idx = (sample_y * width + sample_x) as usize;
let dst_idx = (y * width + x) as usize;
if let (Some(src_cell), Some(dst_cell)) = (
src.content.get(src_idx),
dst.content.get_mut(dst_idx),
) {
*dst_cell = src_cell.clone();
}
}
}
}
fn name(&self) -> &'static str {
"BlurEffect"
}
}
pub struct EffectChain {
effects: Vec<Box<dyn BufferEffect>>,
temp_buffer: Option<Buffer>,
}
impl EffectChain {
pub fn new() -> Self {
Self {
effects: Vec::new(),
temp_buffer: None,
}
}
pub fn add(&mut self, effect: Box<dyn BufferEffect>) -> &mut Self {
self.effects.push(effect);
self
}
pub fn clear(&mut self) {
self.effects.clear();
}
pub fn len(&self) -> usize {
self.effects.len()
}
pub fn is_empty(&self) -> bool {
self.effects.is_empty()
}
pub fn apply(&mut self, src: &Buffer, dst: &mut Buffer, params: &EffectParams) {
if self.effects.is_empty() {
dst.content.clone_from(&src.content);
return;
}
if self.effects.len() == 1 {
self.effects[0].apply(src, dst, params);
return;
}
if self.temp_buffer.is_none() || self.temp_buffer.as_ref().unwrap().area != src.area {
self.temp_buffer = Some(src.clone());
}
let temp = self.temp_buffer.as_mut().unwrap();
self.effects[0].apply(src, temp, params);
for i in 1..self.effects.len() - 1 {
if i % 2 == 1 {
self.effects[i].apply(temp, dst, params);
} else {
self.effects[i].apply(dst, temp, params);
}
}
let last_idx = self.effects.len() - 1;
if last_idx % 2 == 1 {
self.effects[last_idx].apply(temp, dst, params);
} else {
temp.content.clone_from(&dst.content);
self.effects[last_idx].apply(temp, dst, params);
}
}
}
impl Default for EffectChain {
fn default() -> Self {
Self::new()
}
}
pub fn dissolve_chain(noise_density: f32) -> EffectChain {
let mut chain = EffectChain::new();
chain.add(Box::new(NoiseEffect::new(noise_density)));
chain
}
pub fn distortion_chain(wave_amp: f32, ripple_amp: f32) -> EffectChain {
let mut chain = EffectChain::new();
chain.add(Box::new(WaveEffect::new(wave_amp, 15.0)));
chain.add(Box::new(RippleEffect::new(ripple_amp, 10.0)));
chain
}
pub fn glitch_chain(intensity: f32) -> EffectChain {
let mut chain = EffectChain::new();
chain.add(Box::new(PixelateEffect::new((intensity * 4.0) as u16 + 1)));
chain.add(Box::new(WaveEffect::new(intensity * 0.1, 20.0)));
chain.add(Box::new(NoiseEffect::new(intensity * 0.2)));
chain
}
pub trait BufferTransition: Send + Sync {
fn transition(&self, from: &Buffer, to: &Buffer, dst: &mut Buffer, progress: f32);
fn name(&self) -> &'static str;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WipeDirection {
Left,
Right,
Up,
Down,
}
#[derive(Clone, Debug)]
pub struct WipeTransition {
pub direction: WipeDirection,
}
impl WipeTransition {
pub fn new(direction: WipeDirection) -> Self {
Self { direction }
}
pub fn left() -> Self {
Self::new(WipeDirection::Left)
}
pub fn right() -> Self {
Self::new(WipeDirection::Right)
}
pub fn up() -> Self {
Self::new(WipeDirection::Up)
}
pub fn down() -> Self {
Self::new(WipeDirection::Down)
}
}
impl BufferTransition for WipeTransition {
fn transition(&self, from: &Buffer, to: &Buffer, dst: &mut Buffer, progress: f32) {
let width = from.area.width as usize;
let height = from.area.height as usize;
let progress = progress.clamp(0.0, 1.0);
for y in 0..height {
for x in 0..width {
let idx = y * width + x;
let show_to = match self.direction {
WipeDirection::Left => (x as f32) < (width as f32 * progress),
WipeDirection::Right => (x as f32) >= (width as f32 * (1.0 - progress)),
WipeDirection::Up => (y as f32) < (height as f32 * progress),
WipeDirection::Down => (y as f32) >= (height as f32 * (1.0 - progress)),
};
let src = if show_to { to } else { from };
if let (Some(src_cell), Some(dst_cell)) = (
src.content.get(idx),
dst.content.get_mut(idx),
) {
*dst_cell = src_cell.clone();
}
}
}
}
fn name(&self) -> &'static str {
match self.direction {
WipeDirection::Left => "WipeLeft",
WipeDirection::Right => "WipeRight",
WipeDirection::Up => "WipeUp",
WipeDirection::Down => "WipeDown",
}
}
}
#[derive(Clone, Debug)]
pub struct DissolveTransition {
pub seed: u32,
}
impl DissolveTransition {
pub fn new(seed: u32) -> Self {
Self { seed }
}
}
impl Default for DissolveTransition {
fn default() -> Self {
Self { seed: 12345 }
}
}
impl BufferTransition for DissolveTransition {
fn transition(&self, from: &Buffer, to: &Buffer, dst: &mut Buffer, progress: f32) {
let cell_count = from.content.len();
let progress = progress.clamp(0.0, 1.0);
let mut indices: Vec<usize> = (0..cell_count).collect();
let mut rng = self.seed;
let lcg_next = |r: &mut u32| -> u32 {
*r = r.wrapping_mul(1103515245).wrapping_add(12345);
*r
};
for i in (1..cell_count).rev() {
let j = lcg_next(&mut rng) as usize % (i + 1);
indices.swap(i, j);
}
let reveal_count = (cell_count as f32 * progress) as usize;
dst.content.clone_from(&from.content);
for &idx in indices.iter().take(reveal_count) {
if let (Some(to_cell), Some(dst_cell)) = (
to.content.get(idx),
dst.content.get_mut(idx),
) {
*dst_cell = to_cell.clone();
}
}
}
fn name(&self) -> &'static str {
"Dissolve"
}
}
#[derive(Clone, Debug)]
pub struct TypewriterTransition {
pub show_cursor: bool,
pub cursor_char: char,
}
impl TypewriterTransition {
pub fn new() -> Self {
Self {
show_cursor: true,
cursor_char: '▌',
}
}
pub fn without_cursor() -> Self {
Self {
show_cursor: false,
cursor_char: '▌',
}
}
pub fn with_cursor(cursor: char) -> Self {
Self {
show_cursor: true,
cursor_char: cursor,
}
}
}
impl Default for TypewriterTransition {
fn default() -> Self {
Self::new()
}
}
impl BufferTransition for TypewriterTransition {
fn transition(&self, from: &Buffer, to: &Buffer, dst: &mut Buffer, progress: f32) {
let cell_count = from.content.len();
let progress = progress.clamp(0.0, 1.0);
let reveal_count = (cell_count as f32 * progress) as usize;
for (idx, dst_cell) in dst.content.iter_mut().enumerate() {
if idx < reveal_count {
if let Some(to_cell) = to.content.get(idx) {
*dst_cell = to_cell.clone();
}
} else if idx == reveal_count && self.show_cursor && progress < 1.0 {
if let Some(from_cell) = from.content.get(idx) {
*dst_cell = from_cell.clone();
dst_cell.set_symbol(&self.cursor_char.to_string());
}
} else {
if let Some(from_cell) = from.content.get(idx) {
*dst_cell = from_cell.clone();
}
}
}
}
fn name(&self) -> &'static str {
"Typewriter"
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BlindsDirection {
Horizontal,
Vertical,
}
#[derive(Clone, Debug)]
pub struct BlindsTransition {
pub direction: BlindsDirection,
pub slices: usize,
}
impl BlindsTransition {
pub fn new(direction: BlindsDirection, slices: usize) -> Self {
Self {
direction,
slices: slices.max(2),
}
}
pub fn horizontal(slices: usize) -> Self {
Self::new(BlindsDirection::Horizontal, slices)
}
pub fn vertical(slices: usize) -> Self {
Self::new(BlindsDirection::Vertical, slices)
}
}
impl BufferTransition for BlindsTransition {
fn transition(&self, from: &Buffer, to: &Buffer, dst: &mut Buffer, progress: f32) {
let width = from.area.width as usize;
let height = from.area.height as usize;
let progress = progress.clamp(0.0, 1.0);
let (_total_size, slice_size) = match self.direction {
BlindsDirection::Horizontal => (height, height.div_ceil(self.slices)),
BlindsDirection::Vertical => (width, width.div_ceil(self.slices)),
};
for y in 0..height {
for x in 0..width {
let idx = y * width + x;
let pos = match self.direction {
BlindsDirection::Horizontal => y,
BlindsDirection::Vertical => x,
};
let slice_idx = pos / slice_size;
let pos_in_slice = pos % slice_size;
let slice_progress = (progress * self.slices as f32 - slice_idx as f32).clamp(0.0, 1.0);
let reveal_in_slice = (slice_size as f32 * slice_progress) as usize;
let show_to = pos_in_slice < reveal_in_slice;
let src = if show_to { to } else { from };
if let (Some(src_cell), Some(dst_cell)) = (
src.content.get(idx),
dst.content.get_mut(idx),
) {
*dst_cell = src_cell.clone();
}
}
}
}
fn name(&self) -> &'static str {
match self.direction {
BlindsDirection::Horizontal => "BlindsHorizontal",
BlindsDirection::Vertical => "BlindsVertical",
}
}
}
#[derive(Clone, Debug)]
pub struct CheckerboardTransition {
pub block_size: usize,
}
impl CheckerboardTransition {
pub fn new(block_size: usize) -> Self {
Self {
block_size: block_size.max(1),
}
}
}
impl Default for CheckerboardTransition {
fn default() -> Self {
Self { block_size: 2 }
}
}
impl BufferTransition for CheckerboardTransition {
fn transition(&self, from: &Buffer, to: &Buffer, dst: &mut Buffer, progress: f32) {
let width = from.area.width as usize;
let height = from.area.height as usize;
let progress = progress.clamp(0.0, 1.0);
let block = self.block_size;
let phase1 = (progress * 2.0).min(1.0);
let phase2 = ((progress - 0.5) * 2.0).clamp(0.0, 1.0);
for y in 0..height {
for x in 0..width {
let idx = y * width + x;
let block_x = x / block;
let block_y = y / block;
let is_odd_block = (block_x + block_y) % 2 == 1;
let show_to = if is_odd_block {
phase1 > 0.5
} else {
phase2 > 0.5
};
let src = if show_to { to } else { from };
if let (Some(src_cell), Some(dst_cell)) = (
src.content.get(idx),
dst.content.get_mut(idx),
) {
*dst_cell = src_cell.clone();
}
}
}
}
fn name(&self) -> &'static str {
"Checkerboard"
}
}
#[derive(Clone, Debug)]
pub struct SlideTransition {
pub direction: WipeDirection,
}
impl SlideTransition {
pub fn new(direction: WipeDirection) -> Self {
Self { direction }
}
pub fn left() -> Self {
Self::new(WipeDirection::Left)
}
pub fn right() -> Self {
Self::new(WipeDirection::Right)
}
pub fn up() -> Self {
Self::new(WipeDirection::Up)
}
pub fn down() -> Self {
Self::new(WipeDirection::Down)
}
}
impl BufferTransition for SlideTransition {
fn transition(&self, from: &Buffer, to: &Buffer, dst: &mut Buffer, progress: f32) {
let width = from.area.width as usize;
let height = from.area.height as usize;
let progress = progress.clamp(0.0, 1.0);
for y in 0..height {
for x in 0..width {
let idx = y * width + x;
let (src_x, src_y, use_to) = match self.direction {
WipeDirection::Left => {
let offset = (width as f32 * progress) as i32;
let new_x = x as i32 + offset;
if new_x < width as i32 {
(new_x as usize, y, false) } else {
((new_x - width as i32) as usize, y, true) }
}
WipeDirection::Right => {
let offset = (width as f32 * progress) as i32;
let new_x = x as i32 - offset;
if new_x >= 0 {
(new_x as usize, y, false) } else {
((width as i32 + new_x) as usize, y, true) }
}
WipeDirection::Up => {
let offset = (height as f32 * progress) as i32;
let new_y = y as i32 + offset;
if new_y < height as i32 {
(x, new_y as usize, false) } else {
(x, (new_y - height as i32) as usize, true) }
}
WipeDirection::Down => {
let offset = (height as f32 * progress) as i32;
let new_y = y as i32 - offset;
if new_y >= 0 {
(x, new_y as usize, false) } else {
(x, (height as i32 + new_y) as usize, true) }
}
};
let src = if use_to { to } else { from };
let src_idx = src_y * width + src_x;
if let (Some(src_cell), Some(dst_cell)) = (
src.content.get(src_idx),
dst.content.get_mut(idx),
) {
*dst_cell = src_cell.clone();
}
}
}
}
fn name(&self) -> &'static str {
match self.direction {
WipeDirection::Left => "SlideLeft",
WipeDirection::Right => "SlideRight",
WipeDirection::Up => "SlideUp",
WipeDirection::Down => "SlideDown",
}
}
}
#[derive(Clone, Debug)]
pub enum TransitionType {
WipeLeft,
WipeRight,
WipeUp,
WipeDown,
SlideLeft,
SlideRight,
SlideUp,
SlideDown,
Dissolve(u32),
Typewriter,
BlindsHorizontal(usize),
BlindsVertical(usize),
Checkerboard(usize),
}
impl TransitionType {
pub fn create(&self) -> Box<dyn BufferTransition> {
match self {
TransitionType::WipeLeft => Box::new(WipeTransition::left()),
TransitionType::WipeRight => Box::new(WipeTransition::right()),
TransitionType::WipeUp => Box::new(WipeTransition::up()),
TransitionType::WipeDown => Box::new(WipeTransition::down()),
TransitionType::SlideLeft => Box::new(SlideTransition::left()),
TransitionType::SlideRight => Box::new(SlideTransition::right()),
TransitionType::SlideUp => Box::new(SlideTransition::up()),
TransitionType::SlideDown => Box::new(SlideTransition::down()),
TransitionType::Dissolve(seed) => Box::new(DissolveTransition::new(*seed)),
TransitionType::Typewriter => Box::new(TypewriterTransition::new()),
TransitionType::BlindsHorizontal(slices) => Box::new(BlindsTransition::horizontal(*slices)),
TransitionType::BlindsVertical(slices) => Box::new(BlindsTransition::vertical(*slices)),
TransitionType::Checkerboard(size) => Box::new(CheckerboardTransition::new(*size)),
}
}
pub fn all() -> Vec<TransitionType> {
vec![
TransitionType::WipeLeft,
TransitionType::WipeRight,
TransitionType::WipeUp,
TransitionType::WipeDown,
TransitionType::SlideLeft,
TransitionType::SlideRight,
TransitionType::SlideUp,
TransitionType::SlideDown,
TransitionType::Dissolve(12345),
TransitionType::Typewriter,
TransitionType::BlindsHorizontal(4),
TransitionType::BlindsVertical(4),
TransitionType::Checkerboard(2),
]
}
pub fn name(&self) -> &'static str {
match self {
TransitionType::WipeLeft => "WipeLeft",
TransitionType::WipeRight => "WipeRight",
TransitionType::WipeUp => "WipeUp",
TransitionType::WipeDown => "WipeDown",
TransitionType::SlideLeft => "SlideLeft",
TransitionType::SlideRight => "SlideRight",
TransitionType::SlideUp => "SlideUp",
TransitionType::SlideDown => "SlideDown",
TransitionType::Dissolve(_) => "Dissolve",
TransitionType::Typewriter => "Typewriter",
TransitionType::BlindsHorizontal(_) => "BlindsH",
TransitionType::BlindsVertical(_) => "BlindsV",
TransitionType::Checkerboard(_) => "Checkerboard",
}
}
}