pub struct Renderer<B, M>{ /* private fields */ }Implementations§
Source§impl<B, M> Renderer<B, M>
impl<B, M> Renderer<B, M>
Sourcepub fn set_update_interval(&mut self, amount: usize)
pub fn set_update_interval(&mut self, amount: usize)
set how aggrisive draw batches should try to merge
pub fn get_terminal_size(&self) -> (u16, u16)
Sourcepub fn create_renderer(size: (u16, u16)) -> Self
pub fn create_renderer(size: (u16, u16)) -> Self
Create a new renderer with an initial terminal size.
Examples found in repository?
examples/screens.rs (lines 118-120)
113pub fn main() -> Result<(), AppError> {
114 init_terminal!();
115 init_logger("./screens_showcase.log")?;
116
117 let (cols, rows) = size()?;
118 let renderer = Renderer::<DefaultScreenBuffer<CrosstermCellDrawer>, Instant>::create_renderer(
119 (cols, rows),
120 );
121 let (mut r, ev_handler, _ev_manager) = start_renderer_with_input(renderer);
122 r.set_update_interval(1)?;
123
124 let mut hook = ev_handler.create_hook();
125
126 let mut ad_screens = AdjustableScreens::new_uniform_grid(WIDTH, HEIGHT, &mut r)?;
127
128 for screen in &mut ad_screens {
129 let builder = DrawObjectBuilder::default();
130 let d_key = builder
131 .layer(100)
132 .rect_drawable(|r| {
133 r.border_style(
134 ltrender::terminal_buffer::standard_drawables::rect_drawable::BorderStyle::Custom {
135 top: TerminalChar::from_char('T'),
136 bottom: TerminalChar::from_char('B'),
137 left: TerminalChar::from_char('L'),
138 right: TerminalChar::from_char('R'),
139 },
140 )
141 .border_thickness(1)
142 .fill_style(' ')
143 .rect(Rect::from_coords(0, 0, 2, 2))
144 .screen_fit(ScreenFitType::Full)
145 })?
146 .add_lifetime(ObjectLifetime::ExplicitRemove)
147 .screen(screen)
148 .build(&mut r)?;
149 info!("manually rendering drawable: {:?}", d_key);
150 r.render_drawable(d_key)?;
151 }
152
153 let (s, recv) = mpsc::channel();
154 let builder = DrawObjectBuilder::default();
155 let text_stream = builder
156 .layer(200)
157 .videostream_drawable(|b| b.position((0, 0)).recv(recv))?
158 .add_lifetime(ObjectLifetime::ExplicitRemove)
159 .screen(0)
160 .build(&mut r)?;
161 let callback_hook = ev_handler.create_hook();
162 let callback_renderh: RenderHandle<Instant> = r.clone();
163 hook.on_mouse_button_press(MouseButtons::Left, move |_| {
164 if let TargetScreen::Screen(id) = callback_hook.current_selected_screen() {
165 let msg = format!("selected_screen: {} ", id);
166 info!("{}", msg);
167 let mut data = Vec::new();
168 for chr in msg.chars() {
169 data.push(Some(TerminalChar::from_char(chr)));
170 }
171 let _ = s.send(StreamFrame {
172 size: (data.len() as u16, 1),
173 data,
174 });
175 let _ = callback_renderh.render_drawable(text_stream);
176 }
177 })?;
178 loop {
179 if hook.is_pressed(InputButton::Key(KeyCode::Char('c'))) {
180 info!("breaking out of the loop");
181 break;
182 }
183
184 let cur_size = size()?;
185 let rendered_size = r.get_terminal_size()?;
186 if cur_size != rendered_size {
187 info!(
188 "redrawing with cur_size: {:?}, rendered: {:?}",
189 cur_size, rendered_size
190 );
191 r.handle_resize(cur_size)?;
192 ad_screens.place_screens_uniformly(&mut r)?;
193 }
194 }
195 restore_terminal()?;
196 info!("returning with ok");
197 Ok(())
198}More examples
examples/swarm.rs (lines 196-198)
186pub fn main() -> Result<(), AppError> {
187 init_terminal!();
188 init_logger("./organic_swarm.log")?;
189
190 // creating the tick manager for syncing the waves with the main swarm
191 let (_manager, manager_handle) = TickManager::new(tick_manager_rs::Speed::Fps(50));
192 let main_member = tick_manager_rs::TickMember::new(manager_handle.clone(), 1);
193 let bg_member = tick_manager_rs::TickMember::new(manager_handle, 6);
194
195 let (cols, rows) = size()?;
196 let renderer = Renderer::<DefaultScreenBuffer<CrosstermCellDrawer>, Buffered>::create_renderer(
197 (cols, rows),
198 );
199 let mut r = start_renderer(renderer);
200 r.set_update_interval(16)?;
201
202 let screen = r.create_screen(AreaRect::FullScreen, 5)?;
203
204 let (tx, video_drawable) = make_videostream_drawable((0, 0), 1)?;
205 let video_id = DrawObjectBuilder::default()
206 .layer(1)
207 .screen(screen)
208 .drawable(video_drawable)
209 .shader(Grayscale)
210 .add_lifetime(ObjectLifetime::ExplicitRemove)
211 .build(&mut r)?;
212 spawn_soft_wave(
213 tx,
214 RenderHandle::<Buffered>::clone(&r),
215 video_id,
216 (cols, rows),
217 bg_member,
218 );
219
220 // Optional: tiny sprite clock in the corner (if you have a .ascv around, otherwise harmless).
221 if let Ok(sprite) = r.register_sprite_from_source("./assets/debugging/test_video.ascv") {
222 let spr_draw = SpriteDrawableBuilder::default()
223 .sprite_id(sprite)
224 .position((0, 50))
225 .animation_type(AnimationInfo::Video {
226 loop_type: VideoLoopType::Loop,
227 speed: VideoSpeed::Fps(4),
228 start_frame: FrameIdent::FirstFrame,
229 end_frame: FrameIdent::LastFrame,
230 })
231 .build()?;
232 let _spr_id = DrawObjectBuilder::default()
233 .layer(2)
234 .screen(screen)
235 .drawable(spr_draw)
236 .add_lifetime(ObjectLifetime::ExplicitRemove)
237 .build(&mut r)?;
238 }
239 let rect_id: DrawObjectKey = DrawObjectBuilder::default()
240 .layer(100)
241 .screen(screen)
242 .rect_drawable(|r| {
243 r.border_style(
244 ltrender::terminal_buffer::standard_drawables::rect_drawable::BorderStyle::Custom {
245 top: TerminalChar::from_char('T'),
246 bottom: TerminalChar::from_char('B'),
247 left: TerminalChar::from_char('L'),
248 right: TerminalChar::from_char('R'),
249 },
250 )
251 .border_thickness(2)
252 .fill_style(' ')
253 .rect(Rect::from_coords(20, 20, 40, 40))
254 })?
255 .add_lifetime(ObjectLifetime::ExplicitRemove)
256 .build(&mut r)?;
257
258 // HUD text (toggle with 'h').
259 let mut show_hud = true;
260 let hud_drawable = Box::new(text_drawable::TextDrawable {
261 area: Rect::from_coords(1, 1, 50, 10),
262 lines: vec![text_drawable::LineInfo {
263 text: String::from(
264 "organic swarm — drag mouse, left: attract, right: repel, scroll: strength, n: reseed, h: HUD, q: quit",
265 ),
266 spans: vec![],
267 alignment: text_drawable::TextAlignment::Left,
268 default_style: text_drawable::TextStyle {
269 foreground: Some(Color::White),
270 bold: Some(true),
271 ..Default::default()
272 },
273 }],
274 wrapping: true,
275 scroll_y: 0,
276 });
277 let hud_id = DrawObjectBuilder::default()
278 .layer(9)
279 .screen(screen)
280 .drawable(hud_drawable)
281 .add_lifetime(ObjectLifetime::ExplicitRemove)
282 .build(&mut r)?;
283
284 // Seed agents.
285 let mut agents = seeded_swarm(&mut r, screen, 140, cols, rows);
286
287 // Input and timing state.
288 let start = Instant::now();
289 let mut mouse = MouseState {
290 x: (cols / 2) as i32,
291 y: (rows / 2) as i32,
292 left: false,
293 right: false,
294 strength: 18.0,
295 };
296 let mut running = true;
297
298 let mut render_start;
299 let mut fps_vec: Vec<f64> = Vec::new();
300 let mut time_since_last_fps_log = Instant::now();
301
302 while running {
303 render_start = Instant::now();
304 // Input ----------------------------------------------------
305 if poll(Duration::from_millis(0))? {
306 match read()? {
307 Event::Key(k) => {
308 match k.code {
309 KeyCode::Esc | KeyCode::Char('q') => running = false,
310 KeyCode::Char('n') => {
311 // Reseed: drop old agents visually and create new ones.
312 for a in &agents {
313 r.explicit_remove_drawable(&a.id)?;
314 }
315 agents = seeded_swarm(&mut r, screen, 140, cols, rows);
316 }
317 KeyCode::Char('h') => {
318 show_hud = !show_hud;
319 if show_hud {
320 r.render_drawable(hud_id)?;
321 } else {
322 r.explicit_remove_drawable(&hud_id)?;
323 }
324 }
325 _ => {}
326 }
327 }
328 Event::Mouse(me) => match me.kind {
329 MouseEventKind::Moved | MouseEventKind::Drag(_) => {
330 mouse.x = me.column as i32;
331 mouse.y = me.row as i32;
332 }
333 MouseEventKind::Down(btn) => {
334 use crossterm::event::MouseButton;
335 match btn {
336 MouseButton::Left => mouse.left = true,
337 MouseButton::Right => mouse.right = true,
338 MouseButton::Middle => {
339 mouse.strength = 12.0;
340 }
341 }
342 }
343 MouseEventKind::Up(btn) => {
344 use crossterm::event::MouseButton;
345 match btn {
346 MouseButton::Left => mouse.left = false,
347 MouseButton::Right => mouse.right = false,
348 MouseButton::Middle => {}
349 }
350 }
351 MouseEventKind::ScrollDown => {
352 mouse.strength = (mouse.strength - 2.0).max(4.0);
353 }
354 MouseEventKind::ScrollUp => {
355 mouse.strength = (mouse.strength + 2.0).min(40.0);
356 }
357 _ => {}
358 },
359 Event::Resize(c, rws) => {
360 r.handle_resize((c, rws))?;
361 }
362 _ => {}
363 }
364 }
365
366 // Simulation ----------------------------------------------
367 let (cw, ch) = (cols as f64, rows as f64);
368 let t = start.elapsed().as_secs_f64();
369
370 for a in &mut agents {
371 let fx = ((a.pos.1 * 0.05 + t * 0.3).sin() * 0.8) + (t * 0.1 + a.wobble).cos() * 0.2;
372 let fy = ((a.pos.0 * 0.05 - t * 0.25).cos() * 0.8) + (t * 0.13).sin() * 0.2;
373
374 let mut steer_x = fx;
375 let mut steer_y = fy;
376
377 // Mouse field
378 let dx = mouse.x as f64 - a.pos.0;
379 let dy = mouse.y as f64 - a.pos.1;
380 let d2 = (dx * dx + dy * dy).max(0.0001);
381 let inv = 1.0 / d2.sqrt();
382 let toward = (dx * inv, dy * inv);
383
384 if mouse.left {
385 // attract
386 steer_x += toward.0 * (mouse.strength * inv);
387 steer_y += toward.1 * (mouse.strength * inv);
388 }
389 if mouse.right {
390 // repel
391 steer_x -= toward.0 * (mouse.strength * inv);
392 steer_y -= toward.1 * (mouse.strength * inv);
393 }
394
395 // Convert steer into angle change.
396 let desired = steer_y.atan2(steer_x);
397 let mut diff = desired - a.dir;
398 while diff > std::f64::consts::PI {
399 diff -= std::f64::consts::TAU;
400 }
401 while diff < -std::f64::consts::PI {
402 diff += std::f64::consts::TAU;
403 }
404
405 a.dir += diff * 0.15 + (t * a.wobble).sin() * 0.02;
406 a.speed = lerp(a.speed, 1.2 + (t * 0.7 + a.wobble).sin().abs(), 0.05);
407
408 // Integrate position with gentle easing near mouse to make it feel alive.
409 let step = a.speed;
410 a.pos.0 = wrap(a.pos.0 + a.dir.cos() * step, cw);
411 a.pos.1 = wrap(a.pos.1 + a.dir.sin() * step, ch);
412
413 // Push to renderer
414 let pts = triangle_points(a.pos.0, a.pos.1, a.dir, a.size);
415 r.replace_drawable_points(a.id, pts)?;
416 r.render_drawable(a.id)?;
417 }
418
419 r.render_drawable(rect_id)?;
420
421 let frame_preperation_duration = render_start.elapsed();
422
423 if show_hud {
424 let fps = 1.0 / frame_preperation_duration.as_secs_f64();
425 fps_vec.push(fps);
426
427 if time_since_last_fps_log.elapsed() > Duration::from_secs(5) {
428 let count = fps_vec.len();
429
430 if count > 0 {
431 let sum: f64 = fps_vec.iter().sum();
432 let avg_fps = sum / count as f64;
433 info!("Avg FPS over the last 5s: {:.2} ", avg_fps);
434 }
435 fps_vec.clear();
436 time_since_last_fps_log = Instant::now();
437 }
438
439 let txt = format!(
440 "time spent rendering frame: ~{fps:.1} | agents: {} | force: {:.1} | mouse: ({}, {}) L:{} R:{}",
441 agents.len(),
442 mouse.strength,
443 mouse.x,
444 mouse.y,
445 mouse.left as u8,
446 mouse.right as u8
447 );
448
449 let hud = text_drawable::TextDrawable {
450 area: Rect::from_coords(1, 1, 100, 6),
451 lines: vec![text_drawable::LineInfo {
452 text: txt,
453 spans: vec![],
454 alignment: text_drawable::TextAlignment::Left,
455 default_style: text_drawable::TextStyle {
456 foreground: Some(Color::White),
457 bold: Some(true),
458 ..Default::default()
459 },
460 }],
461 wrapping: true,
462 scroll_y: 0,
463 };
464 r.replace_drawable(hud_id, Box::new(hud))?;
465 r.render_drawable(hud_id)?;
466 }
467 main_member.wait_for_tick();
468 r.render_frame()?;
469 }
470
471 // cleanup
472 for a in &agents {
473 r.explicit_remove_drawable(&a.id)?;
474 }
475 r.explicit_remove_drawable(&video_id)?;
476
477 ltrender::restore_terminal()?;
478 Ok(())
479}Sourcepub fn create_screen(&mut self, rect: AreaRect, layer: usize) -> ScreenKey
pub fn create_screen(&mut self, rect: AreaRect, layer: usize) -> ScreenKey
Create a new screen and return its key.
pub fn change_screen_area( &mut self, screen_id: ScreenKey, new_area: AreaRect, ) -> Result<(), DrawError>
pub fn change_screen_layer( &mut self, screen_id: ScreenKey, new_layer: usize, ) -> Result<(), DrawError>
Sourcepub fn register_drawable(
&mut self,
screen_id: ScreenKey,
obj: DrawObject,
) -> Result<DrawObjectKey, DrawError>
pub fn register_drawable( &mut self, screen_id: ScreenKey, obj: DrawObject, ) -> Result<DrawObjectKey, DrawError>
Register a drawable object on a screen.
pub fn remove_drawable(&mut self, id: DrawObjectKey) -> Result<(), DrawError>
pub fn explicit_remove_drawable( &mut self, id: &DrawObjectKey, ) -> Result<(), DrawError>
pub fn replace_drawable( &mut self, id: DrawObjectKey, drawable: Box<dyn Drawable>, ) -> Result<(), DrawError>
Sourcepub fn check_if_object_lifetime_ended(&mut self) -> Result<(), DrawError>
pub fn check_if_object_lifetime_ended(&mut self) -> Result<(), DrawError>
checks if any of the currently existing drawobjects should be removed, because its duration on screen has ended
Sourcepub fn register_sprite_from_source(
&mut self,
path: &str,
) -> Result<SpriteId, AppError>
pub fn register_sprite_from_source( &mut self, path: &str, ) -> Result<SpriteId, AppError>
Load a sprite from an ASCII video file.
Sourcepub fn render_drawable(
&mut self,
object_key: DrawObjectKey,
) -> Result<(), DrawError>
pub fn render_drawable( &mut self, object_key: DrawObjectKey, ) -> Result<(), DrawError>
Render a single drawable object.
pub fn handle_resize(&mut self, new_size: (u16, u16)) -> Result<(), DrawError>
Sourcepub fn generate_screen_key(&self) -> ScreenKey
pub fn generate_screen_key(&self) -> ScreenKey
Generate a unique screen key.
pub fn move_drawable_to( &mut self, handle: DrawObjectKey, new_pos: Point<i32>, ) -> Result<(), DrawError>
pub fn move_drawable_by( &mut self, handle: DrawObjectKey, dx: i32, dy: i32, ) -> Result<(), DrawError>
pub fn move_drawable_point( &mut self, handle: DrawObjectKey, point_index: usize, new_pos: Point<i32>, ) -> Result<(), DrawError>
pub fn move_multipoint_drawable_point( &mut self, handle: DrawObjectKey, point_index: usize, new_pos: Point<i32>, ) -> Result<(), DrawError>
Sourcepub fn replace_drawable_points(
&mut self,
handle: DrawObjectKey,
new_points: Vec<Point<i32>>,
) -> Result<(), DrawError>
pub fn replace_drawable_points( &mut self, handle: DrawObjectKey, new_points: Vec<Point<i32>>, ) -> Result<(), DrawError>
replace a drawables points
pub fn run_drawable_screen_fitting( &mut self, handle: DrawObjectKey, unadjusted_screen_area: Rect<i32>, ) -> Result<(), DrawError>
pub fn get_amount_of_points( &mut self, handle: DrawObjectKey, ) -> Result<Option<usize>, DrawError>
Sourcepub fn handle_screen_selection(&self, hook: &EventHook) -> Result<(), AppError>
pub fn handle_screen_selection(&self, hook: &EventHook) -> Result<(), AppError>
selects a different screen, if a mouse button has been clicked the EventHook is needed to access the mouse’s current position