Renderer

Struct Renderer 

Source
pub struct Renderer<B, M>{ /* private fields */ }

Implementations§

Source§

impl<B, M> Renderer<B, M>

Source

pub fn set_update_interval(&mut self, amount: usize)

set how aggrisive draw batches should try to merge

Source

pub fn get_terminal_size(&self) -> (u16, u16)

Source

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
Hide additional 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}
Source

pub fn create_screen(&mut self, rect: AreaRect, layer: usize) -> ScreenKey

Create a new screen and return its key.

Source

pub fn change_screen_area( &mut self, screen_id: ScreenKey, new_area: AreaRect, ) -> Result<(), DrawError>

Source

pub fn change_screen_layer( &mut self, screen_id: ScreenKey, new_layer: usize, ) -> Result<(), DrawError>

Source

pub fn register_drawable( &mut self, screen_id: ScreenKey, obj: DrawObject, ) -> Result<DrawObjectKey, DrawError>

Register a drawable object on a screen.

Source

pub fn remove_drawable(&mut self, id: DrawObjectKey) -> Result<(), DrawError>

Source

pub fn explicit_remove_drawable( &mut self, id: &DrawObjectKey, ) -> Result<(), DrawError>

Source

pub fn replace_drawable( &mut self, id: DrawObjectKey, drawable: Box<dyn Drawable>, ) -> Result<(), DrawError>

Source

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

Source

pub fn register_sprite_from_source( &mut self, path: &str, ) -> Result<SpriteId, AppError>

Load a sprite from an ASCII video file.

Source

pub fn render_drawable( &mut self, object_key: DrawObjectKey, ) -> Result<(), DrawError>

Render a single drawable object.

Source

pub fn handle_resize(&mut self, new_size: (u16, u16)) -> Result<(), DrawError>

Source

pub fn generate_screen_key(&self) -> ScreenKey

Generate a unique screen key.

Source

pub fn move_drawable_to( &mut self, handle: DrawObjectKey, new_pos: Point<i32>, ) -> Result<(), DrawError>

Source

pub fn move_drawable_by( &mut self, handle: DrawObjectKey, dx: i32, dy: i32, ) -> Result<(), DrawError>

Source

pub fn move_drawable_point( &mut self, handle: DrawObjectKey, point_index: usize, new_pos: Point<i32>, ) -> Result<(), DrawError>

Source

pub fn move_multipoint_drawable_point( &mut self, handle: DrawObjectKey, point_index: usize, new_pos: Point<i32>, ) -> Result<(), DrawError>

Source

pub fn replace_drawable_points( &mut self, handle: DrawObjectKey, new_points: Vec<Point<i32>>, ) -> Result<(), DrawError>

replace a drawables points

Source

pub fn run_drawable_screen_fitting( &mut self, handle: DrawObjectKey, unadjusted_screen_area: Rect<i32>, ) -> Result<(), DrawError>

Source

pub fn get_amount_of_points( &mut self, handle: DrawObjectKey, ) -> Result<Option<usize>, DrawError>

Source

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

Source

pub fn add_screen_select_handler( &mut self, screen_select_handler: ScreenSelectHandler, )

Source

pub fn into_buffered(self) -> Renderer<B, M>

Source

pub fn into_instant(self) -> Renderer<B, M>

Source§

impl<B, Buffered> Renderer<B, Buffered>

Source

pub fn render_frame(&mut self) -> Result<(), DrawError>

Source

pub fn remove_all_framebased_objects(&mut self) -> Result<(), DrawError>

removes all the objects from screen, that have frame-based removal

Auto Trait Implementations§

§

impl<B, M> Freeze for Renderer<B, M>
where B: Freeze,

§

impl<B, M> !RefUnwindSafe for Renderer<B, M>

§

impl<B, M> Send for Renderer<B, M>
where B: Send, M: Send,

§

impl<B, M> !Sync for Renderer<B, M>

§

impl<B, M> Unpin for Renderer<B, M>
where B: Unpin, M: Unpin,

§

impl<B, M> !UnwindSafe for Renderer<B, M>

Blanket Implementations§

§

impl<T> Any for T
where T: 'static + ?Sized,

§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<T> Borrow<T> for T
where T: ?Sized,

§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
§

impl<T> BorrowMut<T> for T
where T: ?Sized,

§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> From<T> for T

§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T, U> Into<U> for T
where U: From<T>,

§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.