diff --git a/nexrad-browser/src/command.rs b/nexrad-browser/src/command.rs index 3cb394b..aab9e80 100644 --- a/nexrad-browser/src/command.rs +++ b/nexrad-browser/src/command.rs @@ -1,6 +1,6 @@ use crate::loadar2; use crate::mode::Mode; -use crate::scope::ScopeState; +use crate::rendering::scope::ScopeState; pub fn should_newline(state: &mut ScopeState) -> bool { diff --git a/nexrad-browser/src/fonts/Inconsolata-Regular.ttf b/nexrad-browser/src/fonts/Inconsolata-Regular.ttf new file mode 100644 index 0000000..d124151 Binary files /dev/null and b/nexrad-browser/src/fonts/Inconsolata-Regular.ttf differ diff --git a/nexrad-browser/src/lib.rs b/nexrad-browser/src/lib.rs index 2be61e0..13e3f23 100644 --- a/nexrad-browser/src/lib.rs +++ b/nexrad-browser/src/lib.rs @@ -1,6 +1,5 @@ pub mod command; pub mod mode; -pub mod scope; pub mod sites; pub mod utils; pub mod vcp; @@ -9,7 +8,7 @@ pub mod rendering; use crate::mode::Mode; -use crate::scope::{Preferences, ScopeState, WgpuState}; +use crate::rendering::scope::{Preferences, ScopeState, WgpuState}; use log::{debug, error, info}; @@ -71,39 +70,7 @@ pub async fn __nxrd_browser_init(w: u32, h: u32) -> AbiProxy { canvas.set_width(w); canvas.set_height(h); - let render_state = WgpuState::new(window, PhysicalSize::new(w, h)).await; - - info!("initializing the scope"); - - let document = web_sys::window() - .expect("window should exist") - .document() - .expect("document should exist"); - - info!("finding form input element"); - - let file = document - .get_element_by_id("file") - .expect("file element should exist"); - let file: web_sys::HtmlInputElement = file - .dyn_into::() - .map_err(|_| ()) - .expect("file input is not an input"); - - let mut scope_state = ScopeState { - ar2: None, - scope_mode: Mode::RadarInoperative, - file_input: file, - lat: 30.48500, // jacksonville - long: -81.70200, // jacksonville - prefs: Preferences { fcs: 20 }, - command_buf: String::new(), - command_buf_response_mode: false, - new_data_available: false, - selected_elevation: 1, - show_ui: false, - wgpu: render_state, - }; + let mut scope_state = ScopeState::new(window, PhysicalSize::new(w, h)).await; let event_proxy: EventLoopProxy = event_loop.create_proxy(); @@ -113,7 +80,6 @@ pub async fn __nxrd_browser_init(w: u32, h: u32) -> AbiProxy { Event::UserEvent(e) => match e { RenderMessage::Resize { new_w, new_h } => { scope_state - .wgpu .reconfigure(PhysicalSize::new(new_w, new_h)); } }, @@ -128,14 +94,14 @@ pub async fn __nxrd_browser_init(w: u32, h: u32) -> AbiProxy { window.exit(); } WindowEvent::Resized(physical_size) => { - scope_state.wgpu.reconfigure(*physical_size); + scope_state.reconfigure(*physical_size); } WindowEvent::RedrawRequested => { - scope_state.wgpu.update(); - match scope_state.wgpu.render() { + scope_state.update(); + match scope_state.render() { Ok(_) => {} Err(SurfaceError::Lost) => { - scope_state.wgpu.reconfigure(scope_state.wgpu.size) + scope_state.reconfigure(scope_state.wgpu.size) } Err(SurfaceError::OutOfMemory) => { error!("out of memory!"); @@ -147,7 +113,7 @@ pub async fn __nxrd_browser_init(w: u32, h: u32) -> AbiProxy { _ => {} }, Event::AboutToWait => { - scope_state.wgpu.window().request_redraw(); + scope_state.window().request_redraw(); } _ => {} }, diff --git a/nexrad-browser/src/rendering/mod.rs b/nexrad-browser/src/rendering/mod.rs index 222b41d..40e88f0 100644 --- a/nexrad-browser/src/rendering/mod.rs +++ b/nexrad-browser/src/rendering/mod.rs @@ -1,2 +1,4 @@ pub mod vertex; pub mod colors; +pub mod time; +pub mod scope; diff --git a/nexrad-browser/src/scope.rs b/nexrad-browser/src/rendering/scope.rs similarity index 69% rename from nexrad-browser/src/scope.rs rename to nexrad-browser/src/rendering/scope.rs index 4931cb4..e9df051 100644 --- a/nexrad-browser/src/scope.rs +++ b/nexrad-browser/src/rendering/scope.rs @@ -3,6 +3,9 @@ use nexrad2::Nexrad2Chunk; use std::iter; +use chrono::Utc; +use log::info; +use wasm_bindgen::JsCast; use web_sys::{HtmlInputElement}; @@ -20,6 +23,7 @@ use winit::dpi::PhysicalSize; use winit::event::WindowEvent; use winit::window::Window; use crate::rendering::colors::rgb_to_srgb; +use crate::rendering::time::zulu; use crate::rendering::vertex::{VERTICES}; pub struct ScopeState { @@ -50,7 +54,7 @@ pub struct WgpuState { pub glyph_brush: GlyphBrush<()> } -impl WgpuState { +impl ScopeState { pub async fn new(window: Window, size: PhysicalSize) -> Self { let instance = Instance::new(InstanceDescriptor { backends: Backends::all(), @@ -112,7 +116,7 @@ impl WgpuState { let shader = device.create_shader_module(ShaderModuleDescriptor { label: Some("Shader"), - source: ShaderSource::Wgsl(include_str!("./shader.wgsl").into()), + source: ShaderSource::Wgsl(include_str!("../shader.wgsl").into()), }); let render_pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor { @@ -157,10 +161,10 @@ impl WgpuState { }); let staging_belt = StagingBelt::new(1024); - let font = FontArc::try_from_slice(include_bytes!("./fonts/Hermit-Regular.otf")).unwrap(); + let font = FontArc::try_from_slice(include_bytes!("../fonts/Inconsolata-Regular.ttf")).unwrap(); let mut glyph_brush = GlyphBrushBuilder::using_font(font).build(&device, surface_config.format); - Self { + let wgpu = WgpuState { window, surface, device, @@ -171,19 +175,53 @@ impl WgpuState { pipeline: render_pipeline, staging_belt, glyph_brush - } + }; + + info!("initializing the scope"); + + let document = web_sys::window() + .expect("window should exist") + .document() + .expect("document should exist"); + + info!("finding form input element"); + + let file = document + .get_element_by_id("file") + .expect("file element should exist"); + let file: web_sys::HtmlInputElement = file + .dyn_into::() + .map_err(|_| ()) + .expect("file input is not an input"); + + let scope_state = ScopeState { + ar2: None, + scope_mode: Mode::RadarInoperative, + file_input: file, + lat: 30.48500, // jacksonville + long: -81.70200, // jacksonville + prefs: Preferences { fcs: 24.0 }, + command_buf: String::new(), + command_buf_response_mode: false, + new_data_available: false, + selected_elevation: 1, + show_ui: false, + wgpu, + }; + + scope_state } pub fn window(&self) -> &Window { - &self.window + &self.wgpu.window } pub fn reconfigure(&mut self, new_size: PhysicalSize) { if new_size.width > 0 && new_size.height > 0 { - self.size = new_size; - self.surface_config.width = new_size.width; - self.surface_config.height = new_size.height; - self.surface.configure(&self.device, &self.surface_config); + self.wgpu.size = new_size; + self.wgpu.surface_config.width = new_size.width; + self.wgpu.surface_config.height = new_size.height; + self.wgpu.surface.configure(&self.wgpu.device, &self.wgpu.surface_config); } } @@ -194,20 +232,19 @@ impl WgpuState { pub fn update(&mut self) {} pub fn render(&mut self) -> Result<(), SurfaceError> { - let output = self.surface.get_current_texture()?; + let output = self.wgpu.surface.get_current_texture()?; let view = output .texture .create_view(&TextureViewDescriptor::default()); let mut encoder = self - .device + .wgpu.device .create_command_encoder(&CommandEncoderDescriptor { label: Some("Render Encoder"), }); - let physical_width = (self.surface_config.width as f64 * self.window.scale_factor()) as f32; + let physical_width = (self.wgpu.surface_config.width as f64 * self.wgpu.window.scale_factor()) as f32; let physical_height = - (self.surface_config.height as f64 * self.window.scale_factor()) as f32; - + (self.wgpu.surface_config.height as f64 * self.wgpu.window.scale_factor()) as f32; // LOCK BLOCK @@ -232,59 +269,71 @@ impl WgpuState { occlusion_query_set: None, }); - render_pass.set_pipeline(&self.pipeline); + render_pass.set_pipeline(&self.wgpu.pipeline); render_pass.draw(0..3, 0..1); } - self.glyph_brush.queue(Section { - screen_position: (10.0, 10.0), - bounds: (self.surface_config.width as f32, self.surface_config.height as f32), - text: vec![Text::new("NEXRAD INOP 00:00:00Z") + // TEXT RENDERING // + let time = Utc::now(); + let line_height = self.prefs.fcs - 1.0; + + self.wgpu.glyph_brush.queue(Section { + screen_position: (10.0, 8.0), + bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32), + text: vec![Text::new(&format!( + "NEXRAD {} {}", + self + .ar2 + .as_ref() + .map(|u| u.volume_header_record.icao.as_str()) + .unwrap_or("INOP"), + zulu(time) + )) .with_color(rgb_to_srgb(0x32cd32, 1.0)) - .with_scale(20.0)], + .with_scale(self.prefs.fcs)], ..Section::default() }); - self.glyph_brush.queue(Section { - screen_position: (10.0, 32.0), - bounds: (self.surface_config.width as f32, self.surface_config.height as f32), + self.wgpu.glyph_brush.queue(Section { + screen_position: (10.0, 8.0 + line_height), + bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32), text: vec![Text::new("RADAR INOPERATIVE NO DATA LOADED") .with_color(rgb_to_srgb(0xff0000, 1.0)) - .with_scale(26.0)], + .with_scale(self.prefs.fcs)], ..Section::default() }); - self.glyph_brush.queue(Section { - screen_position: (self.surface_config.width as f32 - 10.0, 10.0), - bounds: (self.surface_config.width as f32, self.surface_config.height as f32), + self.wgpu.glyph_brush.queue(Section { + screen_position: (self.wgpu.surface_config.width as f32 - 10.0, 8.0), + bounds: (self.wgpu.surface_config.width as f32, self.wgpu.surface_config.height as f32), text: vec![Text::new("RADAR SITE") .with_color(rgb_to_srgb(0x32cd32, 1.0)) - .with_scale(12.0)], + .with_scale(self.prefs.fcs)], ..Section::default() }.with_layout(Layout::default().h_align(HorizontalAlign::Right))); - self.glyph_brush + self.wgpu.glyph_brush .draw_queued( - &self.device, - &mut self.staging_belt, + &self.wgpu.device, + &mut self.wgpu.staging_belt, &mut encoder, &view, - self.surface_config.width, - self.surface_config.height, + self.wgpu.surface_config.width, + self.wgpu.surface_config.height, ) .expect("Draw queued"); - self.staging_belt.finish(); - self.queue.submit(iter::once(encoder.finish())); + self.wgpu.staging_belt.finish(); + self.wgpu.queue.submit(iter::once(encoder.finish())); output.present(); - self.staging_belt.recall(); + self.wgpu.staging_belt.recall(); Ok(()) } } pub struct Preferences { - pub fcs: usize, + pub fcs: f32, } diff --git a/nexrad-browser/src/rendering/time.rs b/nexrad-browser/src/rendering/time.rs new file mode 100644 index 0000000..24f0bd0 --- /dev/null +++ b/nexrad-browser/src/rendering/time.rs @@ -0,0 +1,12 @@ +use chrono::{DateTime, Timelike, Utc}; + +pub fn time(h: usize, m: usize, s: usize, tag_end: &str) -> String { + format!("{:0>2}:{:0>2}:{:0>2}{}", h, m, s, tag_end) +} +pub fn hms(t: DateTime) -> (usize, usize, usize) { + (t.hour() as usize, t.minute() as usize, t.second() as usize) +} +pub fn zulu(t: DateTime) -> String { + let t2 = hms(t); + time(t2.0, t2.1, t2.2, "Z") +} \ No newline at end of file