render_node/
render_node.rs1use std::sync::Arc;
7use std::time::Instant;
8
9use llimphi_hal::winit::application::ApplicationHandler;
10use llimphi_hal::winit::dpi::LogicalSize;
11use llimphi_hal::winit::event::WindowEvent;
12use llimphi_hal::winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
13use llimphi_hal::winit::window::{Window, WindowAttributes, WindowId};
14use llimphi_hal::{Hal, Surface, WinitSurface};
15use llimphi_raster::kurbo::{Affine, Circle, Stroke};
16use llimphi_raster::peniko::{color::palette, Color, Fill};
17use llimphi_raster::{vello, Renderer};
18
19struct State {
20 window: Arc<Window>,
21 hal: Hal,
22 surface: WinitSurface,
23 renderer: Renderer,
24 scene: vello::Scene,
25}
26
27struct App {
28 state: Option<State>,
29 started: Instant,
30}
31
32impl ApplicationHandler for App {
33 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
34 if self.state.is_some() {
35 return;
36 }
37 let window = event_loop
38 .create_window(
39 WindowAttributes::default()
40 .with_title("llimphi · render_node")
41 .with_inner_size(LogicalSize::new(960u32, 540u32)),
42 )
43 .expect("create window");
44 let window = Arc::new(window);
45 let hal = pollster::block_on(Hal::new(None)).expect("hal");
46 let surface = WinitSurface::new(&hal, window.clone()).expect("surface");
47 let renderer = Renderer::new(&hal).expect("renderer");
48 window.request_redraw();
49 self.state = Some(State {
50 window,
51 hal,
52 surface,
53 renderer,
54 scene: vello::Scene::new(),
55 });
56 }
57
58 fn window_event(
59 &mut self,
60 event_loop: &ActiveEventLoop,
61 _id: WindowId,
62 event: WindowEvent,
63 ) {
64 let Some(state) = self.state.as_mut() else {
65 return;
66 };
67 match event {
68 WindowEvent::CloseRequested => event_loop.exit(),
69 WindowEvent::Resized(size) => {
70 state.surface.resize(size.width, size.height);
71 state.window.request_redraw();
72 }
73 WindowEvent::RedrawRequested => {
74 let frame = match state.surface.acquire() {
75 Ok(f) => f,
76 Err(_) => {
77 let (w, h) = state.surface.size();
78 state.surface.resize(w, h);
79 state.window.request_redraw();
80 return;
81 }
82 };
83 let (w, h) = frame.size();
84 state.scene.reset();
85 build_node(&mut state.scene, w as f64, h as f64, self.started.elapsed().as_secs_f64());
86 if let Err(e) = state.renderer.render(
87 &state.hal,
88 &state.scene,
89 &frame,
90 palette::css::BLACK,
91 ) {
92 eprintln!("render error: {e}");
93 }
94 state.surface.present(frame, &state.hal);
95 state.window.request_redraw();
96 }
97 _ => {}
98 }
99 }
100}
101
102fn build_node(scene: &mut vello::Scene, w: f64, h: f64, t: f64) {
104 let cx = w * 0.5;
105 let cy = h * 0.5;
106 let pulse = 1.0 + 0.06 * (t * 1.6).sin();
107 let r = (h.min(w) * 0.18) * pulse;
108
109 scene.stroke(
111 &Stroke::new(2.0),
112 Affine::IDENTITY,
113 Color::from_rgba8(60, 120, 200, 180),
114 None,
115 &Circle::new((cx, cy), r * 1.35),
116 );
117 scene.fill(
119 Fill::NonZero,
120 Affine::IDENTITY,
121 Color::from_rgba8(90, 160, 230, 255),
122 None,
123 &Circle::new((cx, cy), r),
124 );
125 scene.stroke(
127 &Stroke::new(3.0),
128 Affine::IDENTITY,
129 Color::from_rgba8(20, 50, 100, 255),
130 None,
131 &Circle::new((cx, cy), r),
132 );
133}
134
135fn main() {
136 let event_loop = EventLoop::new().expect("event loop");
137 event_loop.set_control_flow(ControlFlow::Poll);
138 let mut app = App {
139 state: None,
140 started: Instant::now(),
141 };
142 event_loop.run_app(&mut app).expect("run app");
143}