layout manager test

This commit is contained in:
Tsuki 2024-01-18 22:35:43 +08:00
parent 4041190f57
commit 99c3bf35d7
14 changed files with 1192 additions and 189 deletions

844
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -45,6 +45,8 @@ async-trait = "0.1.77"
lazy_static = "1.4.0" lazy_static = "1.4.0"
once_cell = "1.19.0" once_cell = "1.19.0"
relm4-icons = {version="0.6.0",features=["add-filled","delete-filled","chevron-up-filled","chevron-down-filled"]} relm4-icons = {version="0.6.0",features=["add-filled","delete-filled","chevron-up-filled","chevron-down-filled"]}
surfman = "0.8.1"
euclid = "0.22.9"
# plotters-cairo = "0.5.0" # plotters-cairo = "0.5.0"

View File

@ -262,4 +262,84 @@ pub enum DownSampleMeth {
pub enum CoordType { pub enum CoordType {
Polar, Polar,
LatLon, LatLon,
} }
// let levels: Vec<i8> = vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65];
// let colors = vec![
// RGBABuilder::default()
// .red(0 as f32 / 255.0)
// .green(172 as f32 / 255.0)
// .blue(164 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(192 as f32 / 255.0)
// .green(192 as f32 / 255.0)
// .blue(254 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(122 as f32 / 255.0)
// .green(114 as f32 / 255.0)
// .blue(238 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(30 as f32 / 255.0)
// .green(38 as f32 / 255.0)
// .blue(208 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(166 as f32 / 255.0)
// .green(252 as f32 / 255.0)
// .blue(168 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(0 as f32 / 255.0)
// .green(234 as f32 / 255.0)
// .blue(0 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(16 as f32 / 255.0)
// .green(146 as f32 / 255.0)
// .blue(26 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(252 as f32 / 255.0)
// .green(244 as f32 / 255.0)
// .blue(100 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(200 as f32 / 255.0)
// .green(200 as f32 / 255.0)
// .blue(2 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(140 as f32 / 255.0)
// .green(140 as f32 / 255.0)
// .blue(0 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(254 as f32 / 255.0)
// .green(172 as f32 / 255.0)
// .blue(172 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(254 as f32 / 255.0)
// .green(100 as f32 / 255.0)
// .blue(92 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(238 as f32 / 255.0)
// .green(2 as f32 / 255.0)
// .blue(48 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(212 as f32 / 255.0)
// .green(142 as f32 / 255.0)
// .blue(254 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(170 as f32 / 255.0)
// .green(36 as f32 / 255.0)
// .blue(250 as f32 / 255.0)
// .build(),
// ];

View File

@ -1,11 +1,15 @@
use adw::subclass::bin::BinImpl; use adw::subclass::bin::BinImpl;
use glib::Properties;
use gtk::glib::prelude::*; use gtk::glib::prelude::*;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::subclass::prelude::*; use gtk::subclass::prelude::*;
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::num::NonZeroU32; use std::num::NonZeroU32;
#[derive(Properties)]
#[properties(wrapper_type = super::DynamicCol)]
pub struct DynamicCol { pub struct DynamicCol {
#[property(get, set)]
pub(super) child: RefCell<Option<gtk::Paned>>, pub(super) child: RefCell<Option<gtk::Paned>>,
pub(super) ratio: RefCell<Option<f64>>, pub(super) ratio: RefCell<Option<f64>>,
width: Cell<i32>, width: Cell<i32>,
@ -33,6 +37,11 @@ impl ObjectSubclass for DynamicCol {
} }
impl ObjectImpl for DynamicCol { impl ObjectImpl for DynamicCol {
fn constructed(&self) {
self.parent_constructed();
}
fn dispose(&self) { fn dispose(&self) {
if let Some(child) = self.child.borrow_mut().take() { if let Some(child) = self.child.borrow_mut().take() {
child.unparent(); child.unparent();

View File

@ -1,10 +1,12 @@
mod utils; mod utils;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{gio, glib, Application, ApplicationWindow}; use gtk::{gio, glib, Application, ApplicationWindow};
use pipeline::offscreen_renderer::OffscreenRenderer;
use relm4::menu; use relm4::menu;
use relm4::RelmApp; use relm4::RelmApp;
use std::ptr; use std::ptr;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
use utils::creator;
mod chart; mod chart;
mod components; mod components;
mod coords; mod coords;
@ -23,14 +25,16 @@ const APP_ID: &str = "org.gtk_rs.HelloWorld2";
static RUNTIME: Lazy<Runtime> = static RUNTIME: Lazy<Runtime> =
Lazy::new(|| Runtime::new().expect("Setting up tokio runtime needs to succeed.")); Lazy::new(|| Runtime::new().expect("Setting up tokio runtime needs to succeed."));
static OFFSCREEN: Lazy<OffscreenRenderer> = Lazy::new(|| OffscreenRenderer::new().expect("Can't create offscreen renderer."));
fn main() { fn main() {
// Load GL pointers from epoxy (GL context management library used by GTK). // Load GL pointers from epoxy (GL context management library used by GTK).
{ {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
let library = let library =
unsafe { libloading::os::unix::Library::new("/opt/homebrew/lib/libepoxy.0.dylib") }.or(unsafe{ unsafe { libloading::os::unix::Library::new("/opt/homebrew/lib/libepoxy.0.dylib") }
libloading::os::unix::Library::new("libepoxy.0.dylib") .or(unsafe { libloading::os::unix::Library::new("libepoxy.0.dylib") })
}).unwrap(); .unwrap();
#[cfg(all(unix, not(target_os = "macos")))] #[cfg(all(unix, not(target_os = "macos")))]
let library = unsafe { libloading::os::unix::Library::new("libepoxy.so.0") }.unwrap(); let library = unsafe { libloading::os::unix::Library::new("libepoxy.so.0") }.unwrap();

View File

@ -5,6 +5,7 @@ use image::RgbImage;
use ndarray::parallel::prelude::*; use ndarray::parallel::prelude::*;
use ndarray::{Array2, ArrayView2}; use ndarray::{Array2, ArrayView2};
use num_traits::{Num, AsPrimitive, FromPrimitive}; use num_traits::{Num, AsPrimitive, FromPrimitive};
pub mod offscreen_renderer;
use crate::{ use crate::{
coords::Mapper, coords::Mapper,

View File

@ -0,0 +1,79 @@
use euclid::Size2D;
use femtovg::{renderer::OpenGl, Canvas};
use std::num::NonZeroU32;
use std::sync::{Mutex, RwLock};
use std::{cell::RefCell, sync::Arc};
use surfman::{device, Adapter, Connection, Context, Device, Error};
pub struct OffscreenRenderer {
context: Arc<RwLock<Context>>,
device: Device,
// renderer: Arc<Mutex<femtovg::renderer::OpenGl>>,
canvas: Arc<RwLock<Canvas<OpenGl>>>,
}
impl OffscreenRenderer {
pub fn new() -> Result<Self, surfman::Error> {
let connection = Connection::new()?;
let adapter = connection.create_adapter()?;
let mut device = connection.create_device(&adapter)?;
let api = device.gl_api();
let descriptor = device.create_context_descriptor(&surfman::ContextAttributes {
version: surfman::GLVersion::new(3, 3),
flags: surfman::ContextAttributeFlags::empty(),
})?;
let mut context = device.create_context(&descriptor, None)?;
let mut surface = device.create_surface(
&context,
surfman::SurfaceAccess::GPUOnly,
surfman::SurfaceType::Generic {
size: euclid::Size2D::new(500, 500),
},
)?;
let surface_info = device.surface_info(&surface);
device
.bind_surface_to_context(&mut context, surface)
.expect("Failed to bind surface to context");
device.make_context_current(&context).unwrap();
static LOAD_FN: fn(&str) -> *const std::ffi::c_void =
|s| epoxy::get_proc_addr(s) as *const _;
let (mut renderer, fbo) = unsafe {
let renderer = OpenGl::new_from_function(LOAD_FN).expect("Cannot create renderer");
let fbo =
glow::NativeFramebuffer(NonZeroU32::new(surface_info.framebuffer_object).unwrap());
(renderer, fbo)
};
renderer.set_screen_target(Some(fbo));
let mut canvas = Canvas::new(renderer).expect("Cannot create canvas");
canvas.set_size(500, 500, 1.0);
Ok(Self {
context: Arc::new(RwLock::new(context)),
device,
canvas: Arc::new(RwLock::new(canvas)),
})
}
pub fn canvas(&self) -> Arc<RwLock<Canvas<OpenGl>>> {
self.canvas.clone()
}
}
impl Drop for OffscreenRenderer {
fn drop(&mut self) {
let mut context = self.context.write().unwrap();
self.device.destroy_context(&mut context).unwrap();
let _ = self;
}
}
unsafe impl Send for OffscreenRenderer {}
unsafe impl Sync for OffscreenRenderer {}

View File

@ -8,6 +8,9 @@ pub struct CMS {
bounds: (f64, f64, f64, f64), bounds: (f64, f64, f64, f64),
} }
unsafe impl Send for CMS {}
unsafe impl Sync for CMS {}
impl CMS { impl CMS {
pub fn new(mapper: Mapper, window_size: (f32, f32)) -> Self { pub fn new(mapper: Mapper, window_size: (f32, f32)) -> Self {
let bounds = mapper.get_bounds(); let bounds = mapper.get_bounds();

View File

@ -1,3 +1,4 @@
use crate::render::cms::CMS;
use crate::{coords::Range, render::Render}; use crate::{coords::Range, render::Render};
use femtovg::Paint; use femtovg::Paint;
use femtovg::{renderer::OpenGl, Canvas, ImageId}; use femtovg::{renderer::OpenGl, Canvas, ImageId};
@ -10,8 +11,15 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
type PrepareFunc = type PrepareFunc = Box<
Box<dyn FnOnce(Arc<Layer>, Render) -> Pin<Box<dyn Future<Output = ()>>> + Sync + Send>; dyn FnOnce(
Box<dyn LayerImpl>,
&mut Canvas<OpenGl>,
CMS,
) -> Pin<Box<dyn Future<Output = Target>>>
+ Sync
+ Send,
>;
type DrawFunc = Box<dyn Fn(&Layer, Render, (f32, f32)) + Send + Sync>; type DrawFunc = Box<dyn Fn(&Layer, Render, (f32, f32)) + Send + Sync>;
type LayerImplSync = Box<dyn LayerImpl + Send + Sync>; type LayerImplSync = Box<dyn LayerImpl + Send + Sync>;
@ -37,14 +45,14 @@ impl Debug for Layer {
} }
pub trait LayerImpl: Debug { pub trait LayerImpl: Debug {
fn draw(&self, render: Render) -> Option<Target>; fn draw(&self, canvas: &mut Canvas<OpenGl>, cms: &CMS) -> Option<Target>;
} }
impl Layer { impl Layer {
pub fn new< pub fn new<
FU: Future<Output = ()> + 'static, FU: Future<Output = Target> + Send + Sync + 'static,
F: 'static + Fn(&Self, Render, (f32, f32)) + Send + Sync, F: 'static + Fn(&Self, Render, (f32, f32)) + Send + Sync,
PREPARE: FnOnce(Arc<Layer>, Render) -> FU + Send + Sync + 'static, PREPARE: FnOnce(Box<dyn LayerImpl>, &mut Canvas<OpenGl>, CMS) -> FU + Send + Sync + 'static,
IMP: LayerImpl + Sync + Send + 'static, IMP: LayerImpl + Sync + Send + 'static,
>( >(
visiable: bool, visiable: bool,

View File

@ -190,4 +190,10 @@ impl Render {
pub fn render_range(&self) -> ((f64, f64), (f64, f64)) { pub fn render_range(&self) -> ((f64, f64), (f64, f64)) {
self.imp().window_range().unwrap() self.imp().window_range().unwrap()
} }
pub fn create_cms(&self) -> CMS {
let borrowed_mapper = &*self.imp().mapper.borrow();
let new_mapper = borrowed_mapper.clone();
CMS::new(new_mapper, (500.0, 500.0))
}
} }

View File

@ -1,5 +1,7 @@
use super::color_mapper::{BoundaryNorm, ColorMapper}; use super::color_mapper::{BoundaryNorm, ColorMapper};
use femtovg::{ImageFlags, Paint, Path, PixelFormat::Rgba8, RenderTarget}; use femtovg::{
renderer::OpenGl, Canvas, ImageFlags, Paint, Path, PixelFormat::Rgba8, RenderTarget,
};
use geo_types::LineString; use geo_types::LineString;
use ndarray::ArrayView2; use ndarray::ArrayView2;
use num_traits::{Num, NumOps}; use num_traits::{Num, NumOps};
@ -8,7 +10,7 @@ use std::{fmt::Debug, marker::PhantomData};
use super::super::renders::DataRenderer; use super::super::renders::DataRenderer;
use crate::{ use crate::{
data::Radar2d, data::Radar2d,
render::{LayerImpl, Render, Target}, render::{cms::CMS, LayerImpl, Render, Target},
utils::meshgrid, utils::meshgrid,
}; };
@ -32,8 +34,8 @@ impl<T: NumOps + PartialOrd + Copy, CMAP: ColorMapper<T>> GridFieldRenderer<CMAP
fn draw_2d( fn draw_2d(
&self, &self,
canvas: &mut femtovg::Canvas<femtovg::renderer::OpenGl>, canvas: &mut femtovg::Canvas<femtovg::renderer::OpenGl>,
cms: &CMS,
data: ArrayView2<T>, data: ArrayView2<T>,
render: &Render,
dims: (ArrayView2<f64>, ArrayView2<f64>), dims: (ArrayView2<f64>, ArrayView2<f64>),
window_size: (f32, f32), window_size: (f32, f32),
fill_value: T, fill_value: T,
@ -48,45 +50,43 @@ impl<T: NumOps + PartialOrd + Copy, CMAP: ColorMapper<T>> GridFieldRenderer<CMAP
let d2_s = dim2[[0, 0]]; let d2_s = dim2[[0, 0]];
let d2_e = dim2[[rows - 1, 0]]; let d2_e = dim2[[rows - 1, 0]];
render.create_drawer((d1_s, d1_e, d2_s, d2_e), window_size, |cms| { for r in 0..rows - 1 {
for r in 0..rows - 1 { for c in 0..cols - 1 {
for c in 0..cols - 1 { let lb_lat = dim2[[r, c]];
let lb_lat = dim2[[r, c]]; let lb_lon = dim1[[r, c]];
let lb_lon = dim1[[r, c]];
let rt_lat = dim2[[r + 1, c + 1]]; let rt_lat = dim2[[r + 1, c + 1]];
let rt_lon = dim1[[r + 1, c + 1]]; let rt_lon = dim1[[r + 1, c + 1]];
let cell: LineString = vec![ let cell: LineString = vec![
(lb_lon, lb_lat), (lb_lon, lb_lat),
(rt_lon + 0.001, lb_lat), (rt_lon + 0.001, lb_lat),
(rt_lon + 0.001, rt_lat), (rt_lon + 0.001, rt_lat),
(lb_lon, rt_lat + 0.001), (lb_lon, rt_lat + 0.001),
(lb_lon, lb_lat + 0.001), (lb_lon, lb_lat + 0.001),
] ]
.into(); .into();
let v = &data[[r, c]]; let v = &data[[r, c]];
let mapped_color = self.cmap.map_value_to_color(*v, fill_value); let mapped_color = self.cmap.map_value_to_color(*v, fill_value);
if None == mapped_color { if None == mapped_color {
continue; continue;
}
let mapped_ring = cms.ring_map(&cell).unwrap();
let mut path = Path::new();
let mut points = mapped_ring.points();
let first_point = points.next().unwrap();
path.move_to(first_point.x(), first_point.y());
for point in points {
path.line_to(point.x(), point.y());
}
path.close();
canvas.fill_path(&path, &Paint::color(mapped_color.unwrap()));
} }
let mapped_ring = cms.ring_map(&cell).unwrap();
let mut path = Path::new();
let mut points = mapped_ring.points();
let first_point = points.next().unwrap();
path.move_to(first_point.x(), first_point.y());
for point in points {
path.line_to(point.x(), point.y());
}
path.close();
canvas.fill_path(&path, &Paint::color(mapped_color.unwrap()));
} }
}); }
} }
} }
@ -96,47 +96,42 @@ where
CMAP: ColorMapper<T>, CMAP: ColorMapper<T>,
{ {
type Data = Radar2d<T>; type Data = Radar2d<T>;
fn render(&self, render: Render, data: &Self::Data) -> Target {
let mut canvas = render.get_canvas();
let canvas = canvas.as_mut().unwrap();
let new_img = canvas fn render(
.create_image_empty(3000, 3000, Rgba8, ImageFlags::empty()) &self,
.expect("Can't Create Image"); canvas: &mut Canvas<OpenGl>,
cms: &CMS,
canvas.save(); data: &Self::Data,
canvas.reset(); size: (f32, f32),
) -> Target {
if let Ok(_) = canvas.image_size(new_img) { let (w, h) = size;
canvas.set_render_target(RenderTarget::Image(new_img));
let _data = data.data.view();
let (_dim1, _dim2) = meshgrid(data.dim1.view(), data.dim2.view());
self.draw_2d(
canvas,
_data,
&render,
(_dim1.view(), _dim2.view()),
(3000.0, 3000.0),
data.fill_value,
);
}
canvas.restore();
canvas.set_render_target(RenderTarget::Screen);
let d1_start = (data.dim1.view()).first().unwrap().clone();
let d1_end = (data.dim1.view()).last().unwrap().clone();
let d2_start = data.dim2.view().first().unwrap().clone();
let d2_end = data.dim2.view().last().unwrap().clone();
Target::new(
new_img,
3000f32,
3000f32,
((d1_start, d1_end).into(), (d2_start, d2_end).into()),
)
} }
// fn render(&self, canvas: &mut Canvas<OpenGl>, cms: &CMS, data: &Self::Data) -> Target {
// let _data = data.data.view();
// let (_dim1, _dim2) = meshgrid(data.dim1.view(), data.dim2.view());
// self.draw_2d(
// canvas,
// cms,
// _data,
// (_dim1.view(), _dim2.view()),
// (3000.0, 3000.0),
// data.fill_value,
// );
// let d1_start = (data.dim1.view()).first().unwrap().clone();
// let d1_end = (data.dim1.view()).last().unwrap().clone();
// let d2_start = data.dim2.view().first().unwrap().clone();
// let d2_end = data.dim2.view().last().unwrap().clone();
// Target::new(
// new_img,
// 3000f32,
// 3000f32,
// ((d1_start, d1_end).into(), (d2_start, d2_end).into()),
// )
// }
} }
#[derive(Debug)] #[derive(Debug)]
@ -166,7 +161,22 @@ where
T: Num + NumOps + PartialOrd + Copy + Clone + Debug + Send + Sync, T: Num + NumOps + PartialOrd + Copy + Clone + Debug + Send + Sync,
CMAP: ColorMapper<T> + Debug, CMAP: ColorMapper<T> + Debug,
{ {
fn draw(&self, render: Render) -> Option<Target> { fn draw(
Some(self.renderer.render(render, &self.data)) &self,
canvas: &mut femtovg::Canvas<femtovg::renderer::OpenGl>,
cms: &crate::render::cms::CMS,
) -> Option<Target> {
let new_img = canvas
.create_image_empty(3000, 3000, Rgba8, ImageFlags::empty())
.expect("Can't Create Image");
if let Ok(_) = canvas.image_size(new_img) {
canvas.reset();
canvas.set_render_target(RenderTarget::Image(new_img));
}
canvas.set_render_target(RenderTarget::Screen);
Some(self.renderer.render(Render::new(canvas, cms), &self.data))
} }
} }

View File

@ -1,8 +1,12 @@
use femtovg::{renderer::OpenGl, Canvas, Paint}; use femtovg::{renderer::OpenGl, Canvas, Paint};
use num_traits::{Num, NumOps}; use num_traits::{Num, NumOps};
use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use std::thread::spawn;
use std::{os::unix::thread, path::Path};
use crate::render::cms::CMS;
use crate::render::{LayerImpl, Target};
use crate::OFFSCREEN;
use crate::{ use crate::{
data::{AsyncDataLoader, DataLoader, Radar2d}, data::{AsyncDataLoader, DataLoader, Radar2d},
render::{Layer, Render}, render::{Layer, Render},
@ -33,12 +37,13 @@ impl Layer {
path.rect(ox, oy, x, y); path.rect(ox, oy, x, y);
c.fill_path(&path, &painter); c.fill_path(&path, &painter);
} }
} else { }
if let Some(renderer) = s.get_imp().as_ref() { },
layer_name,
Some(
|s_imp: Box<dyn LayerImpl>, c: &mut Canvas<OpenGl>, cms: CMS| async move {
if let Some(renderer) = s.as_ref() {
let img = renderer.draw(render.clone()).unwrap(); let img = renderer.draw(render.clone()).unwrap();
let mut c = render.get_canvas();
let c = c.as_mut().unwrap();
if let Ok(_) = c.image_size(img.target) { if let Ok(_) = c.image_size(img.target) {
let (x, y) = img.size(&render); let (x, y) = img.size(&render);
let (ox, oy) = img.origin(&render); let (ox, oy) = img.origin(&render);
@ -50,14 +55,8 @@ impl Layer {
c.fill_path(&path, &painter); c.fill_path(&path, &painter);
} }
} }
} },
}, ),
layer_name,
Some(|s: Arc<Layer>, render: Render| async move {
if let None = s.render_target() {
if let Some(renderer) = s.get_imp().as_ref() {}
}
}),
Some(GridLayerImpl::new(GridFieldRenderer::new(color_map), data)), Some(GridLayerImpl::new(GridFieldRenderer::new(color_map), data)),
) )
} }

View File

@ -1,8 +1,15 @@
use super::cms::CMS;
use super::Render; use super::Render;
use super::Target; use super::Target;
use femtovg::{renderer::OpenGl, Canvas}; use femtovg::{renderer::OpenGl, Canvas};
pub trait DataRenderer { pub trait DataRenderer {
type Data; type Data;
fn render(&self, render: Render, data: &Self::Data) -> Target; fn render(
&self,
canvas: &mut Canvas<OpenGl>,
cms: &CMS,
data: &Self::Data,
size: (f32, f32),
) -> Target;
} }

View File

@ -1,4 +1,12 @@
use std::{borrow::BorrowMut, num::NonZeroU32};
use euclid::Size2D;
use femtovg::Canvas;
use ndarray::{Array2, ArrayView1}; use ndarray::{Array2, ArrayView1};
use surfman::{
Connection, Context, ContextAttributeFlags, ContextAttributes, ContextDescriptor,
NativeConnection, NativeContext, SurfaceAccess, SurfaceType, Device, GLVersion,
};
pub fn meshgrid<T>(x: ArrayView1<T>, y: ArrayView1<T>) -> (Array2<T>, Array2<T>) pub fn meshgrid<T>(x: ArrayView1<T>, y: ArrayView1<T>) -> (Array2<T>, Array2<T>)
where where
@ -10,81 +18,42 @@ where
let yy = Array2::from_shape_fn(shape, |(i, _)| y[i].clone()); let yy = Array2::from_shape_fn(shape, |(i, _)| y[i].clone());
(xx, yy) (xx, yy)
} }
// let levels: Vec<i8> = vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65];
// let colors = vec![ pub fn creator() {
// RGBABuilder::default() let connection = Connection::new().unwrap();
// .red(0 as f32 / 255.0) let adapter = connection.create_adapter().unwrap();
// .green(172 as f32 / 255.0) let mut device = connection.create_device(&adapter).unwrap();
// .blue(164 as f32 / 255.0) let api = device.gl_api();
// .build(), let descriptor = device.create_context_descriptor(&ContextAttributes {
// RGBABuilder::default() version: GLVersion::new(3, 3),
// .red(192 as f32 / 255.0) flags: ContextAttributeFlags::empty(),
// .green(192 as f32 / 255.0) }).unwrap();
// .blue(254 as f32 / 255.0)
// .build(), let mut context = device.create_context(&descriptor, None).unwrap();
// RGBABuilder::default() let mut surface = device
// .red(122 as f32 / 255.0) .create_surface(
// .green(114 as f32 / 255.0) &context,
// .blue(238 as f32 / 255.0) SurfaceAccess::GPUOnly,
// .build(), SurfaceType::Generic {
// RGBABuilder::default() size: Size2D::new(500, 500),
// .red(30 as f32 / 255.0) },
// .green(38 as f32 / 255.0) )
// .blue(208 as f32 / 255.0) .unwrap();
// .build(),
// RGBABuilder::default() let surface_info = device.surface_info(&surface);
// .red(166 as f32 / 255.0) device
// .green(252 as f32 / 255.0) .bind_surface_to_context(&mut context, surface)
// .blue(168 as f32 / 255.0) .expect("Failed to bind surface to context");
// .build(), device.make_context_current(&context).unwrap();
// RGBABuilder::default() use femtovg::renderer::OpenGl;
// .red(0 as f32 / 255.0) static LOAD_FN: fn(&str) -> *const std::ffi::c_void = |s| epoxy::get_proc_addr(s) as *const _;
// .green(234 as f32 / 255.0) let (mut renderer, fbo) = unsafe {
// .blue(0 as f32 / 255.0) let renderer = OpenGl::new_from_function(LOAD_FN).expect("Cannot create renderer");
// .build(), let fbo =
// RGBABuilder::default() glow::NativeFramebuffer(NonZeroU32::new(surface_info.framebuffer_object).unwrap());
// .red(16 as f32 / 255.0) (renderer, fbo)
// .green(146 as f32 / 255.0) };
// .blue(26 as f32 / 255.0)
// .build(), renderer.set_screen_target(Some(fbo));
// RGBABuilder::default() device.destroy_context(&mut context);
// .red(252 as f32 / 255.0) }
// .green(244 as f32 / 255.0)
// .blue(100 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(200 as f32 / 255.0)
// .green(200 as f32 / 255.0)
// .blue(2 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(140 as f32 / 255.0)
// .green(140 as f32 / 255.0)
// .blue(0 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(254 as f32 / 255.0)
// .green(172 as f32 / 255.0)
// .blue(172 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(254 as f32 / 255.0)
// .green(100 as f32 / 255.0)
// .blue(92 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(238 as f32 / 255.0)
// .green(2 as f32 / 255.0)
// .blue(48 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(212 as f32 / 255.0)
// .green(142 as f32 / 255.0)
// .blue(254 as f32 / 255.0)
// .build(),
// RGBABuilder::default()
// .red(170 as f32 / 255.0)
// .green(36 as f32 / 255.0)
// .blue(250 as f32 / 255.0)
// .build(),
// ];