WIP - drawlib animation demo

cepetr 2 weeks ago
parent 3260386292
commit cc450203e7

@ -237,6 +237,10 @@ int main(void) {
mp_obj_list_init(mp_sys_path, 0);
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__dot_frozen));
drawlib_demo(); // TODO: !!! remove
while (1)
;
// Execute the main script
printf("CORE: Executing main script\n");
pyexec_frozen_module("main.py");

@ -4,3 +4,5 @@
#include "rust_ui_bootloader.h"
#include "rust_ui_common.h"
void drawlib_demo();

@ -0,0 +1,43 @@
use crate::time;
pub struct AnimTimer {
base: Option<time::Instant>,
limit: Option<time::Duration>,
}
impl AnimTimer {
pub fn new() -> Self {
Self {
base: None,
limit: None,
}
}
pub fn reset(&mut self) {
self.base = None;
}
pub fn start(&mut self) {
self.base = Some(time::Instant::now());
}
pub fn stop(&mut self) {
self.base = None;
}
pub fn is_running(&self) -> bool {
self.base.is_some()
}
pub fn elapsed(&self) -> f32 {
if let Some(t) = self.base {
time::Instant::now()
.checked_duration_since(t)
.unwrap()
.to_millis() as f32
/ 1000.0
} else {
0.0
}
}
}

@ -0,0 +1,103 @@
use super::anim_timer::AnimTimer;
use crate::ui::{
component::Event,
display::{self, Color, Font},
event::TouchEvent,
geometry::{Offset, Point, Rect},
shape::{self, Viewport},
};
use crate::{time, trezorhal::io::io_touch_read};
use core::fmt::Write;
use heapless::String;
use super::{screen1::build_screen1, screen2::build_screen2};
fn render_time_overlay(duration: time::Duration) {
shape::render_on_display(
Some(Viewport::new(Rect::new(
Point::new(200, 0),
Point::new(240, 20),
))),
Some(Color::rgb(0, 0, 255)),
|target| {
let text_color = Color::rgb(255, 255, 0);
let mut info = String::<128>::new();
write!(info, "{}", duration.to_millis() as f32 / 1.0).unwrap();
let font = Font::NORMAL;
let pt = Point::new(200, font.vert_center(0, 20, "A"));
shape::Text::new(pt, info.as_str())
.with_fg(text_color)
.with_font(font)
.render(target);
},
);
}
fn touch_event() -> Option<TouchEvent> {
let event = io_touch_read();
if event == 0 {
return None;
}
let event_type = event >> 24;
let ex = ((event >> 12) & 0xFFF) as i16;
let ey = (event & 0xFFF) as i16;
TouchEvent::new(event_type, ex as _, ey as _).ok()
}
// Returns the split point between two screens
// (ranging from -240 to 0)
fn anim_screen_transition(timer: &AnimTimer) -> i16 {
let anim = pareen::constant(0.0)
.seq_ease_in_out(
0.0,
pareen::easer::functions::Back,
1.5,
pareen::constant(-240.0),
)
.seq_ease_in_out(
5.0,
pareen::easer::functions::Back,
0.75,
pareen::constant(0.0),
)
.repeat(10.0);
anim.eval(timer.elapsed()) as i16
}
#[no_mangle]
extern "C" fn drawlib_demo() {
let screen1 = build_screen1().unwrap();
let screen2 = build_screen2().unwrap();
let mut timer = AnimTimer::new();
timer.start();
loop {
if let Some(e) = touch_event() {
screen1.obj_event(Event::Touch(e)).unwrap();
screen2.obj_event(Event::Touch(e)).unwrap();
}
let split = anim_screen_transition(&timer);
display::sync();
let start_time = time::Instant::now();
screen1.obj_render(Offset::x(split));
screen2.obj_render(Offset::x(240 + split));
let duration = time::Instant::now()
.checked_duration_since(start_time)
.unwrap();
render_time_overlay(duration);
display::refresh();
}
}

@ -0,0 +1,4 @@
pub mod anim_timer;
pub mod demo_core;
pub mod screen1;
pub mod screen2;

@ -0,0 +1,129 @@
use crate::ui::{
component::{Component, Event, EventCtx, Never},
display::{font::Font, Color},
event::TouchEvent,
geometry::{Point, Rect},
layout::obj::{ComponentMsgObj, LayoutObj},
model_tt::{component::Frame, theme},
shape,
shape::Renderer,
};
use crate::{
error::Error,
micropython::{gc::Gc, obj::Obj},
};
use pareen;
use super::anim_timer::AnimTimer;
pub struct Screen1 {
jump_time: AnimTimer,
}
impl Screen1 {
pub fn new() -> Self {
Self {
jump_time: AnimTimer::new(),
}
}
}
impl Screen1 {
fn anim_ball_coords(&self) -> Point {
let x_anim = pareen::constant(30.0).seq_ease_out(
0.0,
pareen::easer::functions::Cubic,
1.5,
pareen::constant(210.0),
);
let y_anim = pareen::constant(30.0).seq_ease_out(
0.0,
pareen::easer::functions::Bounce,
1.5,
pareen::constant(210.0),
);
let t = self.jump_time.elapsed();
Point::new(x_anim.eval(t) as i16, y_anim.eval(t) as i16)
}
fn anim_text_coords(&self) -> Point {
let x_anim = pareen::constant(30.0)
.seq_ease_in(
0.0,
pareen::easer::functions::Cubic,
0.3,
pareen::constant(240.0),
)
.seq_ease_out(
2.5,
pareen::easer::functions::Elastic,
0.9,
pareen::constant(30.0),
);
let t = self.jump_time.elapsed();
Point::new(x_anim.eval(t) as i16, 120)
}
}
impl Component for Screen1 {
type Msg = Never;
fn place(&mut self, bounds: Rect) -> Rect {
bounds
}
fn event(&mut self, _ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
match event {
Event::Touch(TouchEvent::TouchStart(_)) => {
self.jump_time.start();
}
_ => {}
}
None
}
fn paint(&mut self) {}
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
let r = Rect::new(Point::new(10, 30), Point::new(230, 230));
shape::Bar::new(r)
.with_bg(Color::rgb(0, 20, 40))
.render(target);
shape::Text::new(self.anim_text_coords(), "Touch to start")
.with_font(Font::BOLD)
.render(target);
shape::Circle::new(self.anim_ball_coords(), 16)
.with_bg(Color::rgb(96, 128, 128))
.render(target);
}
}
#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for Screen1 {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Demo screen");
}
}
impl ComponentMsgObj for Screen1 {
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
unreachable!();
}
}
pub fn build_screen1() -> Result<Gc<LayoutObj>, Error> {
LayoutObj::new(Frame::left_aligned(
theme::label_title(),
"Animation Demo".into(),
Screen1::new(),
))
}

@ -0,0 +1,134 @@
use crate::ui::{
component::{Component, Event, EventCtx, Never},
display::Color,
event::TouchEvent,
geometry::{Offset, Point, Rect},
layout::obj::{ComponentMsgObj, LayoutObj},
model_tt::{component::Frame, theme},
shape,
shape::Renderer,
};
use crate::{
error::Error,
micropython::{gc::Gc, obj::Obj},
};
use pareen;
use super::anim_timer::AnimTimer;
pub struct Screen1 {
jump_time: AnimTimer,
}
impl Screen1 {
pub fn new() -> Self {
Self {
jump_time: {
let mut timer = AnimTimer::new();
timer.start();
timer
},
}
}
}
impl Screen1 {
fn anim_radius(&self) -> (i16, i16, i16) {
let r1_anim = pareen::circle()
.cos()
.scale_min_max(60f32, 64f32)
.repeat(1.0f32);
let r2_anim = pareen::circle()
.cos()
.scale_min_max(52f32, 58f32)
.squeeze(0f32..=0.5f32)
.repeat(0.5f32);
let r3_anim = pareen::circle()
.cos()
.scale_min_max(40f32, 50f32)
.squeeze(0f32..=2f32)
.repeat(2f32);
let t = self.jump_time.elapsed();
(
r1_anim.eval(t) as i16,
r2_anim.eval(t) as i16,
r3_anim.eval(t) as i16,
)
}
}
impl Component for Screen1 {
type Msg = Never;
fn place(&mut self, bounds: Rect) -> Rect {
bounds
}
fn event(&mut self, _ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
match event {
Event::Touch(TouchEvent::TouchStart(_)) => {
self.jump_time.start();
}
_ => {}
}
None
}
fn paint(&mut self) {}
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
let bg_color = Color::rgb(40, 20, 0);
let r = Rect::new(Point::new(10, 30), Point::new(230, 230));
shape::Bar::new(r).with_bg(bg_color).render(target);
let center = Point::new(120, 120);
let (r1, r2, r3) = self.anim_radius();
shape::Circle::new(center, r1)
.with_thickness(2)
.with_bg(bg_color)
.with_fg(Color::rgb(0, 120, 0))
.render(target);
shape::Circle::new(center, r2)
.with_thickness(3)
.with_bg(bg_color)
.with_fg(Color::rgb(0, 120, 30))
.render(target);
shape::Circle::new(center, r3)
.with_thickness(4)
.with_bg(bg_color)
.with_fg(Color::rgb(0, 120, 30))
.render(target);
}
}
#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for Screen1 {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Demo screen");
}
}
impl ComponentMsgObj for Screen1 {
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
unreachable!();
}
}
pub fn build_screen2() -> Result<Gc<LayoutObj>, Error> {
LayoutObj::new(Frame::left_aligned(
theme::label_title(),
"Animation Demo 2".into(),
Screen1::new(),
))
}

@ -20,8 +20,8 @@ use crate::{
component::{Component, Event, EventCtx, Never, Root, TimerToken},
constant,
display::{sync, Color},
geometry::Rect,
shape::render_on_display,
geometry::{Offset, Rect},
shape::{render_on_display, Viewport},
},
};
@ -48,6 +48,8 @@ pub trait ObjComponent: MaybeTrace {
fn obj_place(&mut self, bounds: Rect) -> Rect;
fn obj_event(&mut self, ctx: &mut EventCtx, event: Event) -> Result<Obj, Error>;
fn obj_paint(&mut self) -> bool;
#[cfg(feature = "new_rendering")]
fn obj_render(&mut self, offset: Offset);
fn obj_bounds(&self, _sink: &mut dyn FnMut(Rect)) {}
fn obj_skip_paint(&mut self) {}
fn obj_request_clear(&mut self) {}
@ -92,6 +94,15 @@ where
}
}
#[cfg(feature = "new_rendering")]
fn obj_render(&mut self, offset: Offset) {
let screen = crate::ui::constant::screen();
let vp = Viewport::new(screen).translate(offset);
render_on_display(Some(vp), Some(Color::black()), |target| {
self.render(target);
});
}
#[cfg(feature = "ui_bounds")]
fn obj_bounds(&self, sink: &mut dyn FnMut(Rect)) {
self.bounds(sink)
@ -160,7 +171,7 @@ impl LayoutObj {
/// pending timers are drained into `self.timer_callback`. Returns `Err`
/// in case the timer callback raises or one of the components returns
/// an error, `Ok` with the message otherwise.
fn obj_event(&self, event: Event) -> Result<Obj, Error> {
pub fn obj_event(&self, event: Event) -> Result<Obj, Error> {
let inner = &mut *self.inner.borrow_mut();
// Place the root component on the screen in case it was previously requested.
@ -205,7 +216,7 @@ impl LayoutObj {
/// Run a paint pass over the component tree. Returns true if any component
/// actually requested painting since last invocation of the function.
fn obj_paint_if_requested(&self) -> bool {
pub fn obj_paint_if_requested(&self) -> bool {
let mut inner = self.inner.borrow_mut();
// Place the root component on the screen in case it was previously requested.
@ -220,6 +231,21 @@ impl LayoutObj {
unsafe { Gc::as_mut(&mut inner.root) }.obj_paint()
}
/// Run a paint pass over the component tree. Returns true if any component
/// actually requested painting since last invocation of the function.
pub fn obj_render(&self, offset: Offset) {
let mut inner = self.inner.borrow_mut();
// Place the root component on the screen in case it was previously requested.
if inner.event_ctx.needs_place_before_next_event_or_paint() {
// SAFETY: `inner.root` is unique because of the `inner.borrow_mut()`.
unsafe { Gc::as_mut(&mut inner.root) }.obj_place(constant::screen());
}
// SAFETY: `inner.root` is unique because of the `inner.borrow_mut()`.
unsafe { Gc::as_mut(&mut inner.root) }.obj_render(offset)
}
/// Run a tracing pass over the component tree. Passed `callback` is called
/// with each piece of tracing information. Panics in case the callback
/// raises an exception.

@ -23,5 +23,7 @@ pub mod model_tr;
#[cfg(feature = "model_tt")]
pub mod model_tt;
pub mod ui_features;
pub use ui_features::UIFeaturesCommon;
#[cfg(any(feature = "model_tt", feature = "model_mercury"))]
pub mod demo; // !!! REMOVE

@ -56,6 +56,8 @@
#include "common.h"
#include "rust_ui.h"
// Command line options, with their defaults
STATIC bool compile_only = false;
STATIC uint emit_opt = MP_EMIT_OPT_NONE;
@ -547,6 +549,10 @@ MP_NOINLINE int main_(int argc, char **argv) {
mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_argv), 0);
display_backlight(255);
drawlib_demo(); // TODO: !!! remove
while (1) ;
// Here is some example code to create a class and instance of that class.
// First is the Python, then the C code.
//

Loading…
Cancel
Save