Skip to content

Commit f8a042d

Browse files
committed
More efficient animation
1 parent 360738a commit f8a042d

1 file changed

Lines changed: 72 additions & 130 deletions

File tree

examples/demo_app/animation.rs

Lines changed: 72 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,162 +1,104 @@
1-
use appcui::prelude::*;
21
use std::time::Duration;
2+
3+
use appcui::prelude::*;
34
use rand;
45

5-
#[derive(Clone)]
6+
#[derive(Copy, Clone, Default)]
67
struct Bar {
7-
height: i32,
8-
color: Color,
9-
x: i32, // Add x position to track movement
8+
value: i32,
9+
col: Color,
1010
}
1111

12+
const COLORS: [Color; 7] = [
13+
Color::Red,
14+
Color::Green,
15+
Color::Blue,
16+
Color::Aqua,
17+
Color::Yellow,
18+
Color::Pink,
19+
Color::Gray,
20+
];
21+
1222
#[Window(events = TimerEvents)]
13-
pub(crate) struct Win {
23+
pub(super) struct Win {
1424
bars: Vec<Bar>,
15-
last_update: u64,
16-
canvas: Handle<Canvas>,
25+
c: Handle<Canvas>,
1726
}
1827

1928
impl Win {
20-
pub(crate) fn new() -> Self {
29+
pub(super) fn new() -> Self {
2130
let mut w = Self {
22-
base: window!("Animation,d:c,w:50,h:20"),
23-
bars: Vec::new(),
24-
last_update: 0,
25-
canvas: Handle::None,
31+
base: window!("Animation,d:c,w:50,h:10"),
32+
bars: Vec::with_capacity(100),
33+
c: Handle::None,
2634
};
27-
28-
// Add canvas using the public add method
29-
w.canvas = w.add(canvas!("l:0,t:0,r:0,b:0,size:100x100"));
35+
w.c = w.add(canvas!("d:c,w:100%,h:100%,size:100x50"));
3036
if let Some(timer) = w.timer() {
31-
timer.start(Duration::from_millis(300)); // Update every 0.3 seconds
37+
timer.start(Duration::from_millis(300));
3238
}
3339
w
3440
}
41+
fn repaint_graphic(&mut self) {
42+
let h = self.c;
43+
let line_color = self.theme().lines.normal;
44+
let bars_width = self.bars.len() as i32 * 2;
45+
let bars_count = self.bars.len();
46+
let sz = self.size().reduce_by(2); // window margins
47+
let mut arr: [Bar; 100] = [Bar::default(); 100];
48+
for i in 0..bars_count.min(100) {
49+
arr[i] = self.bars[i];
50+
}
51+
if let Some(canvas) = self.control_mut(h) {
52+
let surface = canvas.get_drawing_surface();
53+
surface.clear(char!("' ',w,black"));
3554

36-
fn get_height_change(&self, current_height: i32, max_height: i32) -> i32 {
37-
let quarter_height = max_height / 4;
38-
let three_quarters_height = (max_height * 3) / 4;
39-
40-
if current_height < quarter_height {
41-
// When height is less than 25%, higher chance to increase
42-
match rand::random::<u32>() % 10 {
43-
0..=1 => -1, // 20% chance
44-
2..=4 => 0, // 30% chance
45-
5..=9 => 1, // 50% chance
46-
_ => 0,
47-
}
48-
} else if current_height > three_quarters_height {
49-
// When height is more than 75%, higher chance to decrease
50-
match rand::random::<u32>() % 10 {
51-
0..=4 => -1, // 50% chance
52-
5..=7 => 0, // 30% chance
53-
8..=9 => 1, // 20% chance
54-
_ => 0,
55+
// draw the lines
56+
surface.draw_horizontal_line(0, (sz.height / 4) as i32, sz.width as i32, LineType::Single, line_color);
57+
surface.draw_horizontal_line(0, (sz.height / 2) as i32, sz.width as i32, LineType::Single, line_color);
58+
surface.draw_horizontal_line(0, (sz.height * 3 / 4) as i32, sz.width as i32, LineType::Single, line_color);
59+
60+
// draw the bars
61+
let mut x = sz.width as i32 - bars_width;
62+
for b in &arr {
63+
surface.fill_vertical_line(
64+
x,
65+
sz.height as i32 - b.value,
66+
sz.height as i32,
67+
Character::new(' ', Color::Black, b.col, CharFlags::None),
68+
);
69+
x += 2;
5570
}
56-
} else {
57-
// Normal case: equal probability
58-
rand::random::<i32>() % 3 - 1
5971
}
6072
}
6173
}
6274

6375
impl TimerEvents for Win {
6476
fn on_update(&mut self, _ticks: u64) -> EventProcessStatus {
65-
let canvas_handle = self.canvas;
66-
67-
// Get canvas size first
68-
let size = if let Some(canvas) = self.control(canvas_handle) {
69-
canvas.size()
70-
} else {
71-
return EventProcessStatus::Processed;
72-
};
73-
74-
// Move all existing bars to the left
75-
for bar in &mut self.bars {
76-
bar.x -= 2; // Move 2 positions to leave space between columns
77-
}
78-
79-
// Remove bars that have moved off screen
80-
self.bars.retain(|bar| bar.x >= 0);
81-
82-
// Add a new random bar at the right edge
83-
let colors = [
84-
Color::Red,
85-
Color::Green,
86-
Color::Blue,
87-
Color::Aqua,
88-
Color::Yellow,
89-
Color::Pink,
90-
Color::Gray,
91-
];
92-
let random_color = colors[rand::random::<usize>() % colors.len()];
93-
94-
// Calculate new height based on previous bar or random if first bar
95-
let new_height = if let Some(last_bar) = self.bars.last() {
96-
let change = self.get_height_change(last_bar.height, size.height as i32);
97-
(last_bar.height + change).clamp(1, size.height as i32 - 2)
98-
} else {
99-
// First bar - random height
100-
(rand::random::<u32>() % (size.height as u32 - 2)) as i32 + 1
77+
let sz = self.size().reduce_by(2); // window margins
78+
let h = sz.height as i32;
79+
let last_height = self
80+
.bars
81+
.last()
82+
.unwrap_or(&Bar {
83+
value: (rand::random::<i32>() % h).clamp(1, h),
84+
col: Color::Black,
85+
})
86+
.value;
87+
let add = match () {
88+
_ if last_height <= (h / 4) => ((rand::random::<i32>() % 5) - 1).min(1),
89+
_ if last_height >= (3 * h / 4) => ((rand::random::<i32>() % 5) - 3).max(-1),
90+
_ => (rand::random::<i32>() % 3) - 1,
10191
};
102-
10392
self.bars.push(Bar {
104-
height: new_height,
105-
color: random_color,
106-
x: size.width as i32 - 1,
93+
value: (last_height + add).clamp(1, h),
94+
col: COLORS[rand::random::<usize>() % COLORS.len()],
10795
});
108-
109-
// Prepare bar data for drawing
110-
let bars_data: Vec<_> = self.bars.iter()
111-
.map(|bar| (bar.x, bar.height, bar.color))
112-
.collect();
113-
114-
// Draw bars
115-
if let Some(canvas) = self.control_mut(canvas_handle) {
116-
let surface = canvas.get_drawing_surface();
117-
surface.clear(Character::with_char(' '));
118-
119-
// Draw marker lines
120-
let quarter_height = size.height as i32 / 4;
121-
let half_height = size.height as i32 / 2;
122-
let three_quarters_height = (size.height as i32 * 3) / 4;
123-
124-
// Draw horizontal lines for each threshold
125-
for x in 0..size.width as i32 {
126-
// 25% line
127-
surface.write_char(
128-
x,
129-
size.height as i32 - 1 - quarter_height,
130-
Character::new('─', Color::White, Color::Black, CharFlags::None),
131-
);
132-
// 50% line
133-
surface.write_char(
134-
x,
135-
size.height as i32 - 1 - half_height,
136-
Character::new('─', Color::White, Color::Black, CharFlags::None),
137-
);
138-
// 75% line
139-
surface.write_char(
140-
x,
141-
size.height as i32 - 1 - three_quarters_height,
142-
Character::new('─', Color::White, Color::Black, CharFlags::None),
143-
);
144-
}
145-
146-
// Draw each bar using the prepared data
147-
for (x, height, color) in bars_data {
148-
if x >= 0 {
149-
for y in 0..height {
150-
surface.write_char(
151-
x,
152-
size.height as i32 - 1 - y,
153-
Character::new('█', color, Color::Black, CharFlags::None),
154-
);
155-
}
156-
}
157-
}
96+
if self.bars.len() > (sz.width as usize) * 2 {
97+
self.bars.remove(0);
15898
}
99+
// repaint all
100+
self.repaint_graphic();
159101

160102
EventProcessStatus::Processed
161103
}
162-
}
104+
}

0 commit comments

Comments
 (0)