opengl monitor
This commit is contained in:
parent
7e2ef5558a
commit
145e09ef53
714
Cargo.lock
generated
714
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -24,6 +24,11 @@ quadtree_rs = "0.1.2"
|
||||
proj-sys = "0.23.1"
|
||||
glib-macros = "0.17.10"
|
||||
svg = "0.13.1"
|
||||
libloading = "0.8.0"
|
||||
glue = "0.8.7"
|
||||
epoxy = "0.1.0"
|
||||
femtovg = "0.7.1"
|
||||
glow = "0.12.2"
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@ -2,6 +2,6 @@ fn main() {
|
||||
glib_build_tools::compile_resources(
|
||||
&["src/resources"],
|
||||
"src/resources/resources.gresource.xml",
|
||||
"window.gresource",
|
||||
"monitor.gresource",
|
||||
);
|
||||
}
|
||||
@ -193,20 +193,6 @@ where
|
||||
});
|
||||
result
|
||||
}
|
||||
|
||||
pub fn dpi(&self, axis: Axis) {
|
||||
match self.coord_type {
|
||||
CoordType::LatLon => {
|
||||
if axis == Axis::Asix0 {
|
||||
let mut new_dim1 = Array::zeros(self.dim1.raw_dim());
|
||||
let a = self.dim1.slice(s![0..3, ..]);
|
||||
} else if axis == Axis::Asix1 {
|
||||
} else {
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
60
src/main.rs
60
src/main.rs
@ -1,6 +1,4 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
use std::ptr;
|
||||
|
||||
use glib::{timeout_add, timeout_add_local};
|
||||
use glib_macros::clone;
|
||||
@ -8,30 +6,48 @@ use gtk::gdk::builders::RGBABuilder;
|
||||
use gtk::gdk::Display;
|
||||
use gtk::{
|
||||
gio, glib, style_context_add_provider_for_display, Application, ApplicationWindow, CssProvider,
|
||||
GestureDrag,
|
||||
FontButton, GestureDrag,
|
||||
};
|
||||
use gtk::{prelude::*, DrawingArea};
|
||||
mod data;
|
||||
mod monitor;
|
||||
mod painter;
|
||||
mod render;
|
||||
mod tree;
|
||||
mod window;
|
||||
use data::{Npz, Radar2d, RadarData2d};
|
||||
use monitor::Monitor;
|
||||
use ndarray::parallel::prelude::{IntoParallelIterator, ParallelIterator};
|
||||
use num_traits::{FromPrimitive, Num};
|
||||
use painter::wgs84::{LatLonCoord, Mercator, ProjectionS, Range};
|
||||
use painter::{Coord, Painter};
|
||||
use svg::node::element::Rectangle;
|
||||
use render::Render;
|
||||
use render::{BackgroundConfig, BackgroundWidget};
|
||||
|
||||
const APP_ID: &str = "org.gtk_rs.HelloWorld2";
|
||||
|
||||
fn main() -> glib::ExitCode {
|
||||
// gio::resources_register_include!("window.gresource").expect("Failed to register resources");
|
||||
// Load GL pointers from epoxy (GL context management library used by GTK).
|
||||
{
|
||||
#[cfg(target_os = "macos")]
|
||||
let library = unsafe { libloading::os::unix::Library::new("libepoxy.0.dylib") }.unwrap();
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
let library = unsafe { libloading::os::unix::Library::new("libepoxy.so.0") }.unwrap();
|
||||
#[cfg(windows)]
|
||||
let library = libloading::os::windows::Library::open_already_loaded("libepoxy-0.dll")
|
||||
.or_else(|_| libloading::os::windows::Library::open_already_loaded("epoxy-0.dll"))
|
||||
.unwrap();
|
||||
|
||||
epoxy::load_with(|name| {
|
||||
unsafe { library.get::<_>(name.as_bytes()) }
|
||||
.map(|symbol| *symbol)
|
||||
.unwrap_or(ptr::null())
|
||||
});
|
||||
}
|
||||
|
||||
// gio::resources_register_include!("monitor.gresource").expect("Failed to register resources");
|
||||
// Create a new application
|
||||
let app = Application::builder().application_id(APP_ID).build();
|
||||
// Connect to "activate" signal of `app`
|
||||
app.connect_activate(build_ui);
|
||||
|
||||
// Run the application
|
||||
app.run()
|
||||
}
|
||||
@ -43,19 +59,27 @@ fn build_ui(app: &Application) {
|
||||
.title("My GTK App")
|
||||
.build();
|
||||
|
||||
let drawing_area = DrawingArea::new();
|
||||
let path = "/Users/ruomu/test2.npz";
|
||||
let mut background_config = BackgroundConfig::new();
|
||||
|
||||
let data = Radar2d::<i8>::load(path, Npz).unwrap();
|
||||
let background_widget = BackgroundWidget::new(background_config);
|
||||
|
||||
let projection = Mercator::new();
|
||||
let aa = Range::from((*data.dim1.first().unwrap(), *data.dim1.last().unwrap()));
|
||||
let ab = Range::from((*data.dim2.first().unwrap(), *data.dim2.last().unwrap()));
|
||||
let coord = LatLonCoord::new(Some(aa), Some(ab), ((0, 1000), (0, 1000)), projection);
|
||||
let drawing_area = Render::new();
|
||||
|
||||
let result = data.mapped(&coord);
|
||||
// let path = "/Users/ruomu/test2.npz";
|
||||
|
||||
window.set_child(Some(&drawing_area));
|
||||
// let data = Radar2d::<i8>::load(path, Npz).unwrap();
|
||||
// let projection = Mercator::new();
|
||||
|
||||
// let aa = Range::from((*data.dim1.first().unwrap(), *data.dim1.last().unwrap()));
|
||||
// let ab = Range::from((*data.dim2.first().unwrap(), *data.dim2.last().unwrap()));
|
||||
|
||||
// let coord = LatLonCoord::new(Some(aa), Some(ab), ((0, 1000), (0, 1000)), projection);
|
||||
|
||||
// let result = data.mapped(&coord);
|
||||
|
||||
let monitor = Monitor::new();
|
||||
|
||||
window.set_child(Some(&monitor));
|
||||
window.set_default_width(1000);
|
||||
window.set_default_height(800);
|
||||
|
||||
|
||||
34
src/monitor/imp.rs
Normal file
34
src/monitor/imp.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use crate::render::Render;
|
||||
use glib::subclass::InitializingObject;
|
||||
use gtk::subclass::prelude::*;
|
||||
use gtk::CompositeTemplate;
|
||||
use gtk::{glib, prelude::WidgetExtManual};
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
#[derive(Default, CompositeTemplate)]
|
||||
#[template(file = "monitor.ui")]
|
||||
pub struct Monitor {
|
||||
#[template_child(id = "render")]
|
||||
pub(super) label: TemplateChild<Render>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for Monitor {
|
||||
const NAME: &'static str = "Monitor";
|
||||
type Type = super::Monitor;
|
||||
type ParentType = gtk::Box;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.bind_template();
|
||||
}
|
||||
|
||||
fn instance_init(obj: &InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for Monitor {}
|
||||
|
||||
impl WidgetImpl for Monitor {}
|
||||
|
||||
impl BoxImpl for Monitor {}
|
||||
16
src/monitor/mod.rs
Normal file
16
src/monitor/mod.rs
Normal file
@ -0,0 +1,16 @@
|
||||
mod imp;
|
||||
use crate::render::Render;
|
||||
use glib::subclass::prelude::*;
|
||||
use gtk::traits::WidgetExt;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct Monitor(ObjectSubclass<imp::Monitor>)
|
||||
@extends gtk::Box,gtk::Widget;
|
||||
}
|
||||
|
||||
impl Monitor {
|
||||
pub fn new() -> Self {
|
||||
let this: Self = glib::Object::new();
|
||||
this
|
||||
}
|
||||
}
|
||||
10
src/monitor/monitor.ui
Normal file
10
src/monitor/monitor.ui
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="Monitor" parent="GtkBox">
|
||||
<child>
|
||||
<object class="MyRender" id="render">
|
||||
<property name="hexpand">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
@ -9,6 +9,92 @@ type Lon = f64;
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Range(pub f64, pub f64);
|
||||
|
||||
impl Range {
|
||||
pub fn key_points(&self, max_points: usize) -> Vec<f64> {
|
||||
if max_points == 0 {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let range = (self.0.min(self.1) as f64, self.1.max(self.0) as f64);
|
||||
assert!(!(range.0.is_nan() || range.1.is_nan()));
|
||||
if (range.0 - range.1).abs() < std::f64::EPSILON {
|
||||
return vec![range.0 as f64];
|
||||
}
|
||||
|
||||
let mut scale = (10f64).powf((range.1 - range.0).log(10.0).floor());
|
||||
// The value granularity controls how we round the values.
|
||||
// To avoid generating key points like 1.00000000001, we round to the nearest multiple of the
|
||||
// value granularity.
|
||||
// By default, we make the granularity as the 1/10 of the scale.
|
||||
let mut value_granularity = scale / 10.0;
|
||||
fn rem_euclid(a: f64, b: f64) -> f64 {
|
||||
let ret = if b > 0.0 {
|
||||
a - (a / b).floor() * b
|
||||
} else {
|
||||
a - (a / b).ceil() * b
|
||||
};
|
||||
if (ret - b).abs() < std::f64::EPSILON {
|
||||
0.0
|
||||
} else {
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we need to make sure that the loop invariant:
|
||||
// The scale must yield number of points than requested
|
||||
if 1 + ((range.1 - range.0) / scale).floor() as usize > max_points {
|
||||
scale *= 10.0;
|
||||
value_granularity *= 10.0;
|
||||
}
|
||||
|
||||
'outer: loop {
|
||||
let old_scale = scale;
|
||||
for nxt in [2.0, 5.0, 10.0].iter() {
|
||||
let mut new_left = range.0 - rem_euclid(range.0, old_scale / nxt);
|
||||
if new_left < range.0 {
|
||||
new_left += old_scale / nxt;
|
||||
}
|
||||
let new_right = range.1 - rem_euclid(range.1, old_scale / nxt);
|
||||
|
||||
let npoints = 1.0 + ((new_right - new_left) / old_scale * nxt);
|
||||
|
||||
if npoints.round() as usize > max_points {
|
||||
break 'outer;
|
||||
}
|
||||
|
||||
scale = old_scale / nxt;
|
||||
}
|
||||
scale = old_scale / 10.0;
|
||||
value_granularity /= 10.0;
|
||||
}
|
||||
|
||||
let mut ret = vec![];
|
||||
// In some extreme cases, left might be too big, so that (left + scale) - left == 0 due to
|
||||
// floating point error.
|
||||
// In this case, we may loop forever. To avoid this, we need to use two variables to store
|
||||
// the current left value. So we need keep a left_base and a left_relative.
|
||||
let left = {
|
||||
let mut value = range.0 - rem_euclid(range.0, scale);
|
||||
if value < range.0 {
|
||||
value += scale;
|
||||
}
|
||||
value
|
||||
};
|
||||
let left_base = (left / value_granularity).floor() * value_granularity;
|
||||
let mut left_relative = left - left_base;
|
||||
let right = range.1 - rem_euclid(range.1, scale);
|
||||
while (right - left_relative - left_base) >= -std::f64::EPSILON {
|
||||
let new_left_relative = (left_relative / value_granularity).round() * value_granularity;
|
||||
if new_left_relative < 0.0 {
|
||||
left_relative += value_granularity;
|
||||
}
|
||||
ret.push((left_relative + left_base) as f64);
|
||||
left_relative += scale;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CoordError {
|
||||
#[error("")]
|
||||
|
||||
35
src/render/background/imp.rs
Normal file
35
src/render/background/imp.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use crate::render::{imp, WindowCoord};
|
||||
use femtovg::Paint;
|
||||
use geo_macros::Prj;
|
||||
use gtk::glib;
|
||||
use gtk::subclass::prelude::*;
|
||||
|
||||
#[derive(Prj, Default)]
|
||||
pub struct BackgroundConfig {
|
||||
pub show_lat_lines: bool,
|
||||
pub show_lon_lines: bool,
|
||||
pub lat_lines: Vec<Vec<WindowCoord>>,
|
||||
pub lon_lines: Vec<Vec<WindowCoord>>,
|
||||
pub painter: Paint,
|
||||
}
|
||||
|
||||
impl BackgroundConfig {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BackgroundWidget {
|
||||
pub(super) config: RefCell<BackgroundConfig>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for BackgroundWidget {
|
||||
const NAME: &'static str = "BackgroundWidget";
|
||||
type Type = super::BackgroundWidget;
|
||||
}
|
||||
|
||||
impl ObjectImpl for BackgroundWidget {}
|
||||
46
src/render/background/mod.rs
Normal file
46
src/render/background/mod.rs
Normal file
@ -0,0 +1,46 @@
|
||||
mod imp;
|
||||
use femtovg::{renderer::OpenGl, Canvas, Path};
|
||||
use glib::subclass::types::ObjectSubclassIsExt;
|
||||
use gtk::{ffi::gtk_widget_get_width, glib, graphene::Rect, prelude::SnapshotExtManual};
|
||||
use std::ops::Range;
|
||||
|
||||
pub use self::imp::BackgroundConfig;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct BackgroundWidget(ObjectSubclass<imp::BackgroundWidget>);
|
||||
}
|
||||
|
||||
impl Default for BackgroundWidget {
|
||||
fn default() -> Self {
|
||||
Self::new(BackgroundConfig::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl BackgroundWidget {
|
||||
pub fn new(config: BackgroundConfig) -> Self {
|
||||
let this: Self = glib::Object::new();
|
||||
let imp = this.imp();
|
||||
imp.config.replace(config);
|
||||
this
|
||||
}
|
||||
|
||||
fn mesh_lines(&self, canvas: &mut Canvas<OpenGl>) {
|
||||
let imp = self.imp();
|
||||
let line_painter = &imp.config.borrow().painter;
|
||||
if imp.config.borrow().show_lat_lines {
|
||||
for lat_line in imp.config.borrow().lat_lines.iter() {
|
||||
let mut path = Path::new();
|
||||
lat_line.iter().for_each(|v| {
|
||||
let (x, y) = *v;
|
||||
path.move_to(x, y);
|
||||
});
|
||||
canvas.stroke_path(&mut path, line_painter);
|
||||
}
|
||||
}
|
||||
if imp.config.borrow().show_lon_lines {}
|
||||
}
|
||||
|
||||
pub fn draw(&self, canvas: &mut Canvas<OpenGl>) {
|
||||
let imp = self.imp();
|
||||
}
|
||||
}
|
||||
@ -1,30 +1,111 @@
|
||||
use super::background::BackgroundWidget;
|
||||
use glib::{ObjectExt, ParamSpec, Properties, Value};
|
||||
use gtk::glib;
|
||||
use gtk::subclass::prelude::*;
|
||||
use gtk::traits::{GLAreaExt, WidgetExt};
|
||||
use gtk::{glib, prelude::WidgetExtManual};
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
use crate::data::Radar2d;
|
||||
use std::f32::consts::PI;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Render {
|
||||
data: RefCell<Option<Radar2d<i8>>>,
|
||||
// pub(super) background: RefCell<BackgroundWidget>,
|
||||
canvas: RefCell<Option<femtovg::Canvas<femtovg::renderer::OpenGl>>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for Render {
|
||||
const NAME: &'static str = "MyRender";
|
||||
type Type = super::Render;
|
||||
type ParentType = gtk::Widget;
|
||||
type ParentType = gtk::GLArea;
|
||||
}
|
||||
|
||||
// Trait shared by all GObjects
|
||||
impl ObjectImpl for Render {}
|
||||
|
||||
// Trait shared by all widgets
|
||||
impl WidgetImpl for Render {
|
||||
fn snapshot(&self, snapshot: >k::Snapshot) {
|
||||
if let Some(data) = self.data.borrow_mut().take() {}
|
||||
impl ObjectImpl for Render {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
let area = self.obj();
|
||||
area.set_has_stencil_buffer(true);
|
||||
area.add_tick_callback(|area, _| {
|
||||
area.queue_render();
|
||||
glib::Continue(true)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl DrawingAreaImpl for Render {}
|
||||
// Trait shared by all widgets
|
||||
impl WidgetImpl for Render {}
|
||||
|
||||
impl GLAreaImpl for Render {
|
||||
fn resize(&self, width: i32, height: i32) {
|
||||
self.ensure_canvas();
|
||||
let mut canvas = self.canvas.borrow_mut();
|
||||
let canvas = canvas.as_mut().unwrap();
|
||||
canvas.set_size(
|
||||
width as u32,
|
||||
height as u32,
|
||||
self.obj().scale_factor() as f32,
|
||||
);
|
||||
}
|
||||
|
||||
fn render(&self, context: >k::gdk::GLContext) -> bool {
|
||||
use femtovg::{Color, Paint, Path};
|
||||
|
||||
self.ensure_canvas();
|
||||
let mut canvas = self.canvas.borrow_mut();
|
||||
let canvas = canvas.as_mut().unwrap();
|
||||
|
||||
let dpi = self.obj().scale_factor();
|
||||
let w = self.obj().width();
|
||||
let h = self.obj().width();
|
||||
|
||||
canvas.clear_rect(
|
||||
0,
|
||||
0,
|
||||
(w * dpi) as u32,
|
||||
(h * dpi) as u32,
|
||||
Color::rgba(0, 0, 0, 255),
|
||||
);
|
||||
canvas.fill_text(
|
||||
0.,
|
||||
0.,
|
||||
"hello",
|
||||
&Paint::color(Color::rgba(255, 255, 255, 0)),
|
||||
);
|
||||
|
||||
canvas.flush();
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Render {
|
||||
fn ensure_canvas(&self) {
|
||||
use femtovg::{renderer, Canvas};
|
||||
use glow::HasContext;
|
||||
|
||||
if self.canvas.borrow().is_some() {
|
||||
return;
|
||||
}
|
||||
let widget = self.obj();
|
||||
widget.attach_buffers();
|
||||
|
||||
static LOAD_FN: fn(&str) -> *const std::ffi::c_void =
|
||||
|s| epoxy::get_proc_addr(s) as *const _;
|
||||
// SAFETY: Need to get the framebuffer id that gtk expects us to draw into, so femtovg
|
||||
// knows which framebuffer to bind. This is safe as long as we call attach_buffers
|
||||
// beforehand. Also unbind it here just in case, since this can be called outside render.
|
||||
let (mut renderer, fbo) = unsafe {
|
||||
let renderer =
|
||||
renderer::OpenGl::new_from_function(LOAD_FN).expect("Cannot create renderer");
|
||||
let ctx = glow::Context::from_loader_function(LOAD_FN);
|
||||
let id = NonZeroU32::new(ctx.get_parameter_i32(glow::DRAW_FRAMEBUFFER_BINDING) as u32)
|
||||
.expect("No GTK provided framebuffer binding");
|
||||
ctx.bind_framebuffer(glow::FRAMEBUFFER, None);
|
||||
(renderer, glow::NativeFramebuffer(id))
|
||||
};
|
||||
renderer.set_screen_target(Some(fbo));
|
||||
let canvas = Canvas::new(renderer).expect("Cannot create canvas");
|
||||
self.canvas.replace(Some(canvas));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,20 @@
|
||||
mod background;
|
||||
mod imp;
|
||||
use glib::Object;
|
||||
use gtk::glib;
|
||||
pub use self::background::{BackgroundConfig, BackgroundWidget};
|
||||
use glib::subclass::prelude::*;
|
||||
use gtk::{traits::GestureSingleExt, EventControllerScrollFlags};
|
||||
|
||||
pub(super) type WindowCoord = (f32, f32);
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct Render(ObjectSubclass<imp::Render>) @extends gtk::Widget;
|
||||
pub struct Render(ObjectSubclass<imp::Render>)
|
||||
@extends gtk::GLArea, gtk::Widget;
|
||||
}
|
||||
|
||||
impl Default for Render {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Render {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/org/cinrad_g/">
|
||||
<file compressed="true" preprocess="xml-stripblanks">window.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">monitor.ui</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="MyGtkAppWindow" parent="GtkApplicationWindow">
|
||||
<property name="title">My GTK App</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="button">
|
||||
<property name="label">Press me!</property>
|
||||
<property name="margin-top">12</property>
|
||||
<property name="margin-bottom">12</property>
|
||||
<property name="margin-start">12</property>
|
||||
<property name="margin-end">12</property>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
Loading…
Reference in New Issue
Block a user