Compare commits
2 Commits
a07acecea2
...
77d8c1f0eb
| Author | SHA1 | Date | |
|---|---|---|---|
| 77d8c1f0eb | |||
| 57c24a1de8 |
3548
Cargo.lock
generated
3548
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
99
Cargo.toml
99
Cargo.toml
@ -1,92 +1,9 @@
|
|||||||
[package]
|
|
||||||
name = "cinrad_g"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"etws_loader",
|
||||||
[dependencies]
|
"radarg_plugin_interface",
|
||||||
cairo-rs = { version = "0.17.0" }
|
"geo-macros",
|
||||||
# glib = "0.17.9"
|
"radar-g",
|
||||||
gtk = { version = "0.8.1", package = "gtk4", features = ["v4_12"] }
|
"gi",
|
||||||
geo-types = "0.7.9"
|
]
|
||||||
shapefile = { version = "0.4", features = ["geo-types"] }
|
|
||||||
thiserror = "1.0.40"
|
|
||||||
num-traits = "0.2.15"
|
|
||||||
npyz = { version = "0.8.0", features = ["npz"] }
|
|
||||||
ndarray = { version = "0.15.6", features = ["rayon"] }
|
|
||||||
quadtree_rs = "0.1.2"
|
|
||||||
proj-sys = "0.23.1"
|
|
||||||
glib-macros = "0.19.2"
|
|
||||||
svg = "0.13.1"
|
|
||||||
libloading = "0.8.3"
|
|
||||||
glue = "0.8.7"
|
|
||||||
epoxy = "0.1.0"
|
|
||||||
femtovg = "0.9.0"
|
|
||||||
glow = "0.13.1"
|
|
||||||
proj = "0.27.2"
|
|
||||||
image = "0.24.7"
|
|
||||||
anyhow = "1.0.72"
|
|
||||||
relm4 = { version = "0.8.1", features = ["libadwaita"] }
|
|
||||||
relm4-components = "0.8.1"
|
|
||||||
rstar = "*"
|
|
||||||
geo = "0.26.0"
|
|
||||||
topojson = "0.5.1"
|
|
||||||
geojson = "0.24.1"
|
|
||||||
plotters = "0.3.5"
|
|
||||||
core_extensions = { version = "1.5.2", default_features = false, features = [
|
|
||||||
"std",
|
|
||||||
] }
|
|
||||||
plotters-backend = "0.3.5"
|
|
||||||
tokio = { version = "1.36.0", features = ["full"] }
|
|
||||||
async-trait = "0.1.77"
|
|
||||||
lazy_static = "1.4.0"
|
|
||||||
once_cell = "1.19.0"
|
|
||||||
relm4-icons = { version = "0.8.2" }
|
|
||||||
surfman = "0.8.1"
|
|
||||||
euclid = "0.22.9"
|
|
||||||
gl = "0.14.0"
|
|
||||||
crossbeam = "0.8.4"
|
|
||||||
chrono = "0.4.32"
|
|
||||||
tracker = "0.2.1"
|
|
||||||
abi_stable = "0.11.3"
|
|
||||||
serde = "1.0.196"
|
|
||||||
serde_json = "1.0.112"
|
|
||||||
flate2 = "1.0.28"
|
|
||||||
toml = "0.8.8"
|
|
||||||
dirs = "5.0.1"
|
|
||||||
regex = "1.10.3"
|
|
||||||
smallvec = "1.13.1"
|
|
||||||
rayon = "1.8.1"
|
|
||||||
futures = "0.3.30"
|
|
||||||
sorted-vec = "0.8.3"
|
|
||||||
tracing = "0.1.40"
|
|
||||||
tracing-subscriber = "0.3.18"
|
|
||||||
indexmap = "2.2.2"
|
|
||||||
tokio-condvar = "0.1.0"
|
|
||||||
imgref = "1.10.1"
|
|
||||||
rgb = "0.8.37"
|
|
||||||
slippy-map-tiles = "0.16.0"
|
|
||||||
reqwest = "0.11.25"
|
|
||||||
url = "2.5.0"
|
|
||||||
quick_cache = "0.4.1"
|
|
||||||
fns = "0.0.7"
|
|
||||||
enum_dispatch = "0.3.12"
|
|
||||||
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
glib-build-tools = "0.17.0"
|
|
||||||
[dependencies.geo-macros]
|
|
||||||
path = "geo-macros"
|
|
||||||
|
|
||||||
[dependencies.radarg_plugin_interface]
|
|
||||||
path = "radarg_plugin_interface"
|
|
||||||
|
|
||||||
#[dependencies.etws_loader]
|
|
||||||
#path = "etws_loader"
|
|
||||||
|
|
||||||
[dependencies.adw]
|
|
||||||
package = "libadwaita"
|
|
||||||
version = "0.6.0"
|
|
||||||
features = ["v1_4"]
|
|
||||||
|
|||||||
395
back.txt
395
back.txt
@ -1,395 +0,0 @@
|
|||||||
|
|
||||||
Layer::geojson_layer_with_path(
|
|
||||||
"/Users/tsuki/Downloads/new_zhejiang.json",
|
|
||||||
true,
|
|
||||||
|_self, c, render, _| {
|
|
||||||
if let Some(json_resources) = _self.get_resources() {
|
|
||||||
if let Resources::GeoJson(geojson) = json_resources.deref() {
|
|
||||||
MapRender::test(&geojson, c, render);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Layer::new(true, None, |s, c, render, _| {
|
|
||||||
if let Some(target) = s.render_target() {
|
|
||||||
if let Ok(_) = c.image_size(target.target) {
|
|
||||||
let (x, y) = target.size(render);
|
|
||||||
let (ox, oy) = target.origin(render);
|
|
||||||
let painter = Paint::image(target.target, ox, oy, x, y, 0.0, 1.0);
|
|
||||||
let mut path = Path::new();
|
|
||||||
path.rect(ox, oy, x, y);
|
|
||||||
c.fill_path(&path, &painter);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let renderer = RadarEchoRenderer::new(BoundaryNorm::new(
|
|
||||||
vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65],
|
|
||||||
vec![
|
|
||||||
Color::rgb(0, 172, 164),
|
|
||||||
Color::rgb(192, 192, 254),
|
|
||||||
Color::rgb(122, 114, 238),
|
|
||||||
Color::rgb(30, 38, 208),
|
|
||||||
Color::rgb(166, 252, 168),
|
|
||||||
Color::rgb(0, 234, 0),
|
|
||||||
Color::rgb(16, 146, 26),
|
|
||||||
Color::rgb(252, 244, 100),
|
|
||||||
Color::rgb(200, 200, 2),
|
|
||||||
Color::rgb(140, 140, 0),
|
|
||||||
Color::rgb(254, 172, 172),
|
|
||||||
Color::rgb(254, 100, 92),
|
|
||||||
Color::rgb(238, 2, 48),
|
|
||||||
Color::rgb(212, 142, 254),
|
|
||||||
Color::rgb(170, 36, 250),
|
|
||||||
],
|
|
||||||
true,
|
|
||||||
));
|
|
||||||
let loader = Npz;
|
|
||||||
let data: RadarData2d<i8, OwnedRepr<i8>> = loader
|
|
||||||
.load("/Users/tsuki/projects/radar-g/test2.npz")
|
|
||||||
.unwrap();
|
|
||||||
let img = renderer.render(render, c, &data);
|
|
||||||
if let Ok(_) = c.image_size(img.target) {
|
|
||||||
let (x, y) = img.size(render);
|
|
||||||
let (ox, oy) = img.origin(render);
|
|
||||||
println!("{} {} {} {}", x, y, ox, oy);
|
|
||||||
let painter = Paint::image(img.target, ox, oy, x, y, 0.0, 1.0);
|
|
||||||
let mut path = Path::new();
|
|
||||||
path.rect(ox, oy, x, y);
|
|
||||||
s.set_render_target(img);
|
|
||||||
c.fill_path(&path, &painter);
|
|
||||||
c.flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
############# TEMPLATES ###################
|
|
||||||
// Monitor::new(
|
|
||||||
// Render::new(
|
|
||||||
// Some(Mapper::new(
|
|
||||||
// Proj::new(Mercator::default().build().as_str()).unwrap(),
|
|
||||||
// (119.539..121.135).into(),
|
|
||||||
// (29.13..30.164).into(),
|
|
||||||
// )),
|
|
||||||
// RenderConfig { padding: [50.0;4] }
|
|
||||||
// )
|
|
||||||
// )
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Layer::grid_render_layer_with_path(
|
|
||||||
// "/users/tsuki/projects/radar-g/test2.npz",
|
|
||||||
// Npz,
|
|
||||||
// BoundaryNorm::default(),
|
|
||||||
// )
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// fn resample(
|
|
||||||
// &self,
|
|
||||||
// width_rate: f64,
|
|
||||||
// height_rate: f64,
|
|
||||||
// filter_len: f64,
|
|
||||||
// ) -> Result<RadarData2d<T, OwnedRepr<T>>, DataError> {
|
|
||||||
// let width_rate = width_rate.min(1.0);
|
|
||||||
// let height_rate = height_rate.min(1.0);
|
|
||||||
// match self.coord_type {
|
|
||||||
// CoordType::Polar => Err(DataError::FormatError),
|
|
||||||
// CoordType::LatLon => {
|
|
||||||
// let width_filtered: ArrayBase<ndarray::OwnedRepr<T>, Ix2> =
|
|
||||||
// Self::_resample(&self.data, width_rate, filter_len);
|
|
||||||
|
|
||||||
// let result: ArrayBase<OwnedRepr<T>, Ix2> =
|
|
||||||
// Self::_resample(&width_filtered.t(), height_rate, filter_len)
|
|
||||||
// .t()
|
|
||||||
// .to_owned();
|
|
||||||
|
|
||||||
// let new_dim1: ArrayBase<OwnedRepr<f64>, Ix1> = Self::_resample(
|
|
||||||
// &Array2::from_shape_vec((1, self.dim1.len()), self.dim1.to_vec()).unwrap(),
|
|
||||||
// width_rate,
|
|
||||||
// filter_len,
|
|
||||||
// )
|
|
||||||
// .slice(s![0, ..])
|
|
||||||
// .to_owned();
|
|
||||||
|
|
||||||
// let new_dim2: ArrayBase<OwnedRepr<f64>, Ix1> = Self::_resample(
|
|
||||||
// &Array2::from_shape_vec((1, self.dim2.len()), self.dim2.to_vec()).unwrap(),
|
|
||||||
// height_rate,
|
|
||||||
// filter_len,
|
|
||||||
// )
|
|
||||||
// .slice(s![0, ..])
|
|
||||||
// .to_owned();
|
|
||||||
|
|
||||||
// Ok(RadarData2d {
|
|
||||||
// dim1: new_dim1,
|
|
||||||
// dim2: new_dim2,
|
|
||||||
// data: result,
|
|
||||||
// coord_type: self.coord_type.to_owned(),
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn _resample<'a, V, R: ndarray::Data<Elem = V>>(
|
|
||||||
// data: &'a ArrayBase<R, Ix2>,
|
|
||||||
// rate: f64,
|
|
||||||
// filter_len: f64,
|
|
||||||
// ) -> Array2<V>
|
|
||||||
// where
|
|
||||||
// V: Num + Clone + AsPrimitive<f64> + FromPrimitive,
|
|
||||||
// {
|
|
||||||
// let ori_width = data.ncols();
|
|
||||||
// let ori_height = data.nrows();
|
|
||||||
|
|
||||||
// let new_width = (ori_width as f64 * rate).ceil() as usize;
|
|
||||||
// let mut result: Array2<V> = Array2::zeros((ori_height, new_width));
|
|
||||||
// (0..ori_height).into_iter().for_each(|height| {
|
|
||||||
// for width in 0..new_width {
|
|
||||||
// let center_x = (width as f64 + 0.5) / new_width as f64 * ori_width as f64;
|
|
||||||
// let filter_start = center_x - filter_len / 2.0;
|
|
||||||
// let start_idx = (filter_start - 0.5).ceil() as usize;
|
|
||||||
|
|
||||||
// let mut value_sum = 0.0;
|
|
||||||
// let mut filter_sum = 0.0;
|
|
||||||
|
|
||||||
// for i in 0..filter_len as usize {
|
|
||||||
// let input_x = start_idx + i;
|
|
||||||
// let weight = windowed_sinc(
|
|
||||||
// (input_x as f64 + 0.5 - center_x) * rate,
|
|
||||||
// (input_x as f64 + 0.5 - filter_start) / filter_len,
|
|
||||||
// );
|
|
||||||
// value_sum += weight * data[[height, input_x.clamp(0, ori_width - 1)]].as_();
|
|
||||||
// filter_sum += weight;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// result[[height, width]] = V::from_f64(value_sum / filter_sum).unwrap();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// result
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub struct LevelData<T: Num + Clone + PartialEq + PartialOrd>(
|
|
||||||
// pub Quadtree<i64, RadarData2d<T, OwnedRepr<T>>>,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// impl<T> LevelData<T>
|
|
||||||
// where
|
|
||||||
// T: Num + Clone + AsPrimitive<f64> + FromPrimitive + Debug + PartialEq + PartialOrd,
|
|
||||||
// {
|
|
||||||
// fn value(
|
|
||||||
// level_data: Vec<RadarData2d<T, OwnedRepr<T>>>,
|
|
||||||
// level_num: usize,
|
|
||||||
// ) -> Vec<RadarData2d<T, OwnedRepr<T>>> {
|
|
||||||
// if level_num == 0 {
|
|
||||||
// return level_data;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let mut result: Vec<RadarData2d<T, OwnedRepr<T>>> =
|
|
||||||
// Vec::with_capacity(level_data.len() * 4);
|
|
||||||
|
|
||||||
// let results = level_data
|
|
||||||
// .iter()
|
|
||||||
// .flat_map(|v| v.split().into_iter().map(|x| x.value_to_owned()));
|
|
||||||
|
|
||||||
// result.extend(results);
|
|
||||||
|
|
||||||
// return Self::value(result, level_num - 1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn new(data: &RadarData2d<T, OwnedRepr<T>>, level: usize, rate: f64) -> Self {
|
|
||||||
// // let rate = 1.0 / level as f64;
|
|
||||||
// let resampled = data.resample(rate, rate, 2.0).unwrap();
|
|
||||||
// let blocks = Self::value(vec![resampled], level);
|
|
||||||
// let mut tree: Quadtree<i64, RadarData2d<T, OwnedRepr<T>>> =
|
|
||||||
// quadtree_rs::Quadtree::new(level);
|
|
||||||
|
|
||||||
// blocks.into_iter().for_each(|block| {
|
|
||||||
// tree.insert(
|
|
||||||
// AreaBuilder::default()
|
|
||||||
// .anchor(quadtree_rs::point::Point {
|
|
||||||
// x: *block.dim1.first().unwrap() as i64,
|
|
||||||
// y: *block.dim2.first().unwrap() as i64,
|
|
||||||
// })
|
|
||||||
// .dimensions((block.dim1.len() as i64, block.dim2.len() as i64))
|
|
||||||
// .build()
|
|
||||||
// .unwrap(),
|
|
||||||
// block,
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// Self(tree)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn levels<T>(data: Radar2d<T>, levels: usize) -> Vec<LevelData<T>>
|
|
||||||
// where
|
|
||||||
// T: Num + Clone + AsPrimitive<f64> + FromPrimitive + Debug,
|
|
||||||
// T: PartialEq + PartialOrd,
|
|
||||||
// {
|
|
||||||
// let numerator = 1.0 / levels as f64;
|
|
||||||
// (0..levels)
|
|
||||||
// .into_iter()
|
|
||||||
// .map(|level| LevelData::new(&data, level + 1, 1.0 - level as f64 * numerator))
|
|
||||||
// .collect()
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn windowed_sinc(x: f64, y: f64) -> f64 {
|
|
||||||
let x = x * PI;
|
|
||||||
let sinc = if x != 0.0 { f64::sin(x) / x } else { 1.0 };
|
|
||||||
let window = if 0f64 <= y && y <= 1.0 {
|
|
||||||
1.0 - (y - 0.5).abs() * 2.0
|
|
||||||
} else {
|
|
||||||
0f64
|
|
||||||
};
|
|
||||||
sinc * window
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum DownSampleMeth {
|
|
||||||
STD,
|
|
||||||
MEAN,
|
|
||||||
VAR,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub enum CoordType {
|
|
||||||
Polar,
|
|
||||||
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(),
|
|
||||||
// ];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// let texture = ctx.create_texture().expect("Cannot create texture");
|
|
||||||
|
|
||||||
// ctx.bind_texture(glow::TEXTURE_2D, Some(texture));
|
|
||||||
// ctx.tex_image_2d(
|
|
||||||
// glow::TEXTURE_2D,
|
|
||||||
// 0,
|
|
||||||
// glow::RGBA as i32,
|
|
||||||
// 3000,
|
|
||||||
// 3000,
|
|
||||||
// 0,
|
|
||||||
// glow::RGBA,
|
|
||||||
// glow::UNSIGNED_BYTE,
|
|
||||||
// None,
|
|
||||||
// );
|
|
||||||
// ctx.tex_parameter_i32(
|
|
||||||
// glow::TEXTURE_2D,
|
|
||||||
// glow::TEXTURE_MIN_FILTER,
|
|
||||||
// glow::LINEAR as i32,
|
|
||||||
// );
|
|
||||||
// ctx.tex_parameter_i32(
|
|
||||||
// glow::TEXTURE_2D,
|
|
||||||
// glow::TEXTURE_MAG_FILTER,
|
|
||||||
// glow::LINEAR as i32,
|
|
||||||
// );
|
|
||||||
// ctx.framebuffer_texture_2d(
|
|
||||||
// glow::FRAMEBUFFER,
|
|
||||||
// glow::COLOR_ATTACHMENT0,
|
|
||||||
// glow::TEXTURE_2D,
|
|
||||||
// Some(texture),
|
|
||||||
// 0,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// let depth_buffer = ctx
|
|
||||||
// .create_renderbuffer()
|
|
||||||
// .expect("Cannot create renderbuffer");
|
|
||||||
// ctx.bind_renderbuffer(glow::RENDERBUFFER, Some(depth_buffer));
|
|
||||||
// ctx.renderbuffer_storage(glow::RENDERBUFFER, glow::DEPTH_COMPONENT16, 3000, 3000);
|
|
||||||
// ctx.framebuffer_renderbuffer(
|
|
||||||
// glow::FRAMEBUFFER,
|
|
||||||
// glow::DEPTH_ATTACHMENT,
|
|
||||||
// glow::RENDERBUFFER,
|
|
||||||
// Some(depth_buffer),
|
|
||||||
// );
|
|
||||||
// // let id = NonZeroU32::new(ctx.get_parameter_i32(glow::DRAW_FRAMEBUFFER_BINDING) as u32)
|
|
||||||
// // .expect("No GTK provided framebuffer binding");
|
|
||||||
|
|
||||||
|
|
||||||
9
check.py
9
check.py
@ -1,9 +0,0 @@
|
|||||||
import numpy as np
|
|
||||||
|
|
||||||
data = np.load("/Users/tsuki/projects/radar-g/test2.npz")
|
|
||||||
|
|
||||||
print(data['lat'][0])
|
|
||||||
print(data['lat'][-1])
|
|
||||||
|
|
||||||
print(data['lon'][0])
|
|
||||||
print(data['lon'][-1])
|
|
||||||
194
etws_loader/Cargo.lock
generated
194
etws_loader/Cargo.lock
generated
@ -56,6 +56,24 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "allocator-api2"
|
||||||
|
version = "0.2.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android-tzdata"
|
name = "android-tzdata"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@ -77,6 +95,15 @@ version = "1.0.79"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "approx"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "as_derive_utils"
|
name = "as_derive_utils"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@ -194,6 +221,22 @@ version = "0.8.19"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "earcutr"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79127ed59a85d7687c409e9978547cffb7dc79675355ed22da6b66fd5f6ead01"
|
||||||
|
dependencies = [
|
||||||
|
"itertools",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "etws_loader"
|
name = "etws_loader"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -203,6 +246,7 @@ dependencies = [
|
|||||||
"byteorder",
|
"byteorder",
|
||||||
"chrono",
|
"chrono",
|
||||||
"flate2",
|
"flate2",
|
||||||
|
"geo",
|
||||||
"nom",
|
"nom",
|
||||||
"nom-derive",
|
"nom-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@ -222,6 +266,12 @@ dependencies = [
|
|||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "float_next_after"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generational-arena"
|
name = "generational-arena"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
@ -231,6 +281,73 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "geo"
|
||||||
|
version = "0.28.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f811f663912a69249fa620dcd2a005db7254529da2d8a0b23942e81f47084501"
|
||||||
|
dependencies = [
|
||||||
|
"earcutr",
|
||||||
|
"float_next_after",
|
||||||
|
"geo-types",
|
||||||
|
"geographiclib-rs",
|
||||||
|
"log",
|
||||||
|
"num-traits",
|
||||||
|
"robust",
|
||||||
|
"rstar",
|
||||||
|
"spade",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "geo-types"
|
||||||
|
version = "0.7.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ff16065e5720f376fbced200a5ae0f47ace85fd70b7e54269790281353b6d61"
|
||||||
|
dependencies = [
|
||||||
|
"approx",
|
||||||
|
"num-traits",
|
||||||
|
"rstar",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "geographiclib-rs"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6e5ed84f8089c70234b0a8e0aedb6dc733671612ddc0d37c6066052f9781960"
|
||||||
|
dependencies = [
|
||||||
|
"libm",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hash32"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"allocator-api2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heapless"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||||
|
dependencies = [
|
||||||
|
"hash32",
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iana-time-zone"
|
name = "iana-time-zone"
|
||||||
version = "0.1.59"
|
version = "0.1.59"
|
||||||
@ -254,6 +371,15 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.10"
|
version = "1.0.10"
|
||||||
@ -285,6 +411,12 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libm"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.11"
|
version = "0.4.11"
|
||||||
@ -361,6 +493,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
|
"libm",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -441,6 +574,23 @@ dependencies = [
|
|||||||
"tstr",
|
"tstr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "robust"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cbf4a6aa5f6d6888f39e980649f3ad6b666acdce1d78e95b8a2cb076e687ae30"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rstar"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "133315eb94c7b1e8d0cb097e5a710d850263372fd028fff18969de708afc7008"
|
||||||
|
dependencies = [
|
||||||
|
"heapless",
|
||||||
|
"num-traits",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -511,6 +661,24 @@ version = "1.13.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spade"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61addf9117b11d1f5b4bf6fe94242ba25f59d2d4b2080544b771bd647024fd00"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
"num-traits",
|
||||||
|
"robust",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable_deref_trait"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
@ -580,6 +748,12 @@ version = "1.0.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.90"
|
version = "0.2.90"
|
||||||
@ -778,3 +952,23 @@ name = "windows_x86_64_msvc"
|
|||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.7.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.48",
|
||||||
|
]
|
||||||
|
|||||||
@ -10,6 +10,7 @@ anyhow = "1.0.79"
|
|||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
chrono = {version="0.4.33", features=["serde"]}
|
chrono = {version="0.4.33", features=["serde"]}
|
||||||
flate2 = "1.0.28"
|
flate2 = "1.0.28"
|
||||||
|
geo = "0.28.0"
|
||||||
nom = "7.1.3"
|
nom = "7.1.3"
|
||||||
nom-derive = "0.10.1"
|
nom-derive = "0.10.1"
|
||||||
num-traits = "0.2.17"
|
num-traits = "0.2.17"
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
mod error;
|
mod error;
|
||||||
|
use geo::{algorithm::haversine_destination::HaversineDestination, Point};
|
||||||
mod parser;
|
mod parser;
|
||||||
use abi_stable::{
|
use abi_stable::{
|
||||||
export_root_module,
|
export_root_module,
|
||||||
@ -14,7 +15,7 @@ use abi_stable::{
|
|||||||
};
|
};
|
||||||
use parser::{Record, ValueResult};
|
use parser::{Record, ValueResult};
|
||||||
use radarg_plugin_interface::{
|
use radarg_plugin_interface::{
|
||||||
CoordType, Error, MetaData, Plugin, PluginId, PluginMod, PluginMod_Ref, PluginResult,
|
CoordType, Error, Loc3, MetaData, Plugin, PluginId, PluginMod, PluginMod_Ref, PluginResult,
|
||||||
PluginResultType, PluginType, Plugin_TO,
|
PluginResultType, PluginType, Plugin_TO,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -27,6 +28,23 @@ struct ETWSLoader {
|
|||||||
id: PluginId,
|
id: PluginId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn calculate_coverage(lat_deg: f64, lon_deg: f64, radius_km: f64) -> (f64, f64, f64, f64) {
|
||||||
|
let center = Point::new(lon_deg, lat_deg);
|
||||||
|
|
||||||
|
// 计算四个方向(北、南、东、西)的点
|
||||||
|
let north = center.haversine_destination(0.0, radius_km);
|
||||||
|
let south = center.haversine_destination(180.0, radius_km);
|
||||||
|
let east = center.haversine_destination(90.0, radius_km);
|
||||||
|
let west = center.haversine_destination(270.0, radius_km);
|
||||||
|
|
||||||
|
let min_lat = south.y();
|
||||||
|
let max_lat = north.y();
|
||||||
|
let min_lon = west.x();
|
||||||
|
let max_lon = east.x();
|
||||||
|
|
||||||
|
(min_lat, max_lat, min_lon, max_lon)
|
||||||
|
}
|
||||||
|
|
||||||
impl Plugin for ETWSLoader {
|
impl Plugin for ETWSLoader {
|
||||||
fn plugin_id(&self) -> &PluginId {
|
fn plugin_id(&self) -> &PluginId {
|
||||||
&self.id
|
&self.id
|
||||||
@ -80,33 +98,55 @@ impl Plugin for ETWSLoader {
|
|||||||
};
|
};
|
||||||
let dimension_des = b.info.dimension_des;
|
let dimension_des = b.info.dimension_des;
|
||||||
let c = if dimension_des.contains(&format!("lat")) {
|
let c = if dimension_des.contains(&format!("lat")) {
|
||||||
|
let len = b.info.dimension_values.len();
|
||||||
|
|
||||||
|
let lon = b.info.dimension_values.get(len - 1).unwrap();
|
||||||
|
let lat = b.info.dimension_values.get(len - 2).unwrap();
|
||||||
|
|
||||||
|
lon_range = [lon[0], lon[lon.len() - 1]];
|
||||||
|
lat_range = [lat[0], lat[lat.len() - 1]];
|
||||||
|
|
||||||
CoordType::Cartesian
|
CoordType::Cartesian
|
||||||
} else if dimension_des.contains(&format!("ele"))
|
} else if dimension_des.contains(&format!("ele"))
|
||||||
|
|| dimension_des.contains(&format!("el"))
|
||||||
|| dimension_des.contains(&format!("elevation"))
|
|| dimension_des.contains(&format!("elevation"))
|
||||||
{
|
{
|
||||||
CoordType::Polar
|
let (min_lat, max_lat, min_lon, max_lon) = calculate_coverage(
|
||||||
|
b.info.radar_lat.unwrap(),
|
||||||
|
b.info.radar_lon.unwrap(),
|
||||||
|
b.info.dimension_end[2],
|
||||||
|
);
|
||||||
|
|
||||||
|
lon_range = [min_lon, max_lon];
|
||||||
|
lat_range = [min_lat, max_lat];
|
||||||
|
CoordType::Polar(
|
||||||
|
Loc3 {
|
||||||
|
x: b.info.radar_lon.unwrap(),
|
||||||
|
y: b.info.radar_lat.unwrap(),
|
||||||
|
z: b.info.radar_alt.unwrap(),
|
||||||
|
},
|
||||||
|
b.info.dimension_end[2],
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
CoordType::Other
|
CoordType::Other
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let shape = match b.info.dimension_size.len() {
|
let shape = match b.info.dimension_size.len() {
|
||||||
1 => radarg_plugin_interface::DataShape::Vector,
|
1 => radarg_plugin_interface::DataShape::Vector,
|
||||||
2 => {
|
2 => {
|
||||||
let lat = b.info.dimension_values.get(0).unwrap();
|
// let lat = b.info.dimension_values.get(0).unwrap();
|
||||||
let lon = b.info.dimension_values.get(1).unwrap();
|
// let lon = b.info.dimension_values.get(1).unwrap();
|
||||||
lat_range = [lat[0], lat[lat.len() - 1]];
|
// lat_range = [lat[0], lat[lat.len() - 1]];
|
||||||
lon_range = [lon[0], lon[lon.len() - 1]];
|
// lon_range = [lon[0], lon[lon.len() - 1]];
|
||||||
radarg_plugin_interface::DataShape::Matrix
|
radarg_plugin_interface::DataShape::Matrix
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let lat = b.info.dimension_values.get(1).unwrap();
|
// let lat = b.info.dimension_values.get(1).unwrap();
|
||||||
let lon = b.info.dimension_values.get(2).unwrap();
|
// let lon = b.info.dimension_values.get(2).unwrap();
|
||||||
lat_range = [lat[0], lat[lat.len() - 1]];
|
// lat_range = [lat[0], lat[lat.len() - 1]];
|
||||||
lon_range = [lon[0], lon[lon.len() - 1]];
|
// lon_range = [lon[0], lon[lon.len() - 1]];
|
||||||
radarg_plugin_interface::DataShape::Cube
|
radarg_plugin_interface::DataShape::Cube
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let data_type = match b.info.value_name.as_str() {
|
let data_type = match b.info.value_name.as_str() {
|
||||||
@ -197,7 +237,7 @@ mod tests {
|
|||||||
|
|
||||||
fn test() {
|
fn test() {
|
||||||
let result =
|
let result =
|
||||||
Record::parse_from_path("/Volumes/data2/RadarArray/HangZhou/radarData/OutputProducts/RadarProducts/FuseDataX/20220727/ZJHZAA_20220727200000_FR.dat.gz")
|
Record::parse_from_path("/Volumes/data2/RadarArray/ShaoXing/radarData/OutputProducts/RadarProducts/BasicProductsX/20230627/20230627163400/ZJSXAA_20230627163400_VIL.dat.gz")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -364,237 +364,13 @@ pub struct BlockJsonInfo {
|
|||||||
pub fill_value: f64,
|
pub fill_value: f64,
|
||||||
pub value_scale: f32,
|
pub value_scale: f32,
|
||||||
pub value_offset: f32,
|
pub value_offset: f32,
|
||||||
|
pub radar_alt: Option<f64>,
|
||||||
|
pub radar_lat: Option<f64>,
|
||||||
|
pub radar_lon: Option<f64>,
|
||||||
|
pub radar_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ParsedBlock {
|
pub struct ParsedBlock {
|
||||||
pub info: BlockJsonInfo,
|
pub info: BlockJsonInfo,
|
||||||
pub data: ValueResult,
|
pub data: ValueResult,
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn _parse_matrix(
|
|
||||||
// input: &[u8],
|
|
||||||
// type_: ValueTypes,
|
|
||||||
// order: Order,
|
|
||||||
// len: usize,
|
|
||||||
// offset: f32,
|
|
||||||
// scale: f32,
|
|
||||||
// fill_value: f64,
|
|
||||||
// ) -> IResult<&[u8], ValueResult> {
|
|
||||||
// use std::mem;
|
|
||||||
// let need_trans = offset != 0.0 || scale != 1.0;
|
|
||||||
// let trans_to_bigger = offset.trunc() != offset || scale != 1.0;
|
|
||||||
// match type_ {
|
|
||||||
// ValueTypes::I64 => {
|
|
||||||
// let ratio = mem::size_of::<i64>() / mem::size_of::<u8>();
|
|
||||||
// let (input, result) = take(len * ratio)(input)?;
|
|
||||||
// let result = unsafe {
|
|
||||||
// let ptr = result.as_ptr() as *const i64;
|
|
||||||
// let slice = std::slice::from_raw_parts(ptr, len);
|
|
||||||
// let slice = slice.to_vec();
|
|
||||||
|
|
||||||
// if trans_to_bigger {
|
|
||||||
// let offset = offset as f32;
|
|
||||||
// ValueResult::F32(
|
|
||||||
// slice
|
|
||||||
// .into_iter()
|
|
||||||
// .map(|p| (p as f32 - offset) / scale)
|
|
||||||
// .collect::<Vec<f32>>(),
|
|
||||||
// )
|
|
||||||
// } else {
|
|
||||||
// ValueResult::I64(if need_trans {
|
|
||||||
// let offset = offset as i64;
|
|
||||||
// slice.into_iter().map(|p| p - offset).collect::<Vec<i64>>()
|
|
||||||
// } else {
|
|
||||||
// slice
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// Ok((input, result))
|
|
||||||
// }
|
|
||||||
// ValueTypes::F64 => {
|
|
||||||
// let ratio = mem::size_of::<f64>() / mem::size_of::<u8>();
|
|
||||||
// let (input, result) = take(len * ratio)(input)?;
|
|
||||||
// let result = unsafe {
|
|
||||||
// let ptr = result.as_ptr() as *const f64;
|
|
||||||
// let slice = std::slice::from_raw_parts(ptr, len);
|
|
||||||
// let slice = slice.to_vec();
|
|
||||||
|
|
||||||
// ValueResult::F64(if need_trans {
|
|
||||||
// let offset = offset as f64;
|
|
||||||
// slice
|
|
||||||
// .into_iter()
|
|
||||||
// .map(|p| (p - offset) / scale as f64)
|
|
||||||
// .collect::<Vec<f64>>()
|
|
||||||
// } else {
|
|
||||||
// slice
|
|
||||||
// })
|
|
||||||
// };
|
|
||||||
|
|
||||||
// Ok((input, result))
|
|
||||||
// }
|
|
||||||
// ValueTypes::I32 => {
|
|
||||||
// let ratio = mem::size_of::<i32>() / mem::size_of::<u8>();
|
|
||||||
// let (input, result) = take(len * ratio)(input)?;
|
|
||||||
// let result = unsafe {
|
|
||||||
// let ptr = result.as_ptr() as *const i32;
|
|
||||||
// let slice = std::slice::from_raw_parts(ptr, len);
|
|
||||||
// let slice = slice.to_vec();
|
|
||||||
|
|
||||||
// if trans_to_bigger {
|
|
||||||
// let offset = offset as f32;
|
|
||||||
// ValueResult::F32(
|
|
||||||
// slice
|
|
||||||
// .into_iter()
|
|
||||||
// .map(|p| (p as f32 - offset) / scale)
|
|
||||||
// .collect::<Vec<f32>>(),
|
|
||||||
// )
|
|
||||||
// } else {
|
|
||||||
// ValueResult::I32(if need_trans {
|
|
||||||
// let offset = offset as i32;
|
|
||||||
// slice.into_iter().map(|p| p - offset).collect::<Vec<i32>>()
|
|
||||||
// } else {
|
|
||||||
// slice
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// Ok((input, result))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ValueTypes::U32 => {
|
|
||||||
// let ratio = mem::size_of::<u32>() / mem::size_of::<u8>();
|
|
||||||
// let (input, result) = take(len * ratio)(input)?;
|
|
||||||
// let result = unsafe {
|
|
||||||
// let ptr = result.as_ptr() as *const u32;
|
|
||||||
// let slice = std::slice::from_raw_parts(ptr, len);
|
|
||||||
// let slice = slice.to_vec();
|
|
||||||
|
|
||||||
// if trans_to_bigger {
|
|
||||||
// let offset = offset as f32;
|
|
||||||
// ValueResult::F32(
|
|
||||||
// slice
|
|
||||||
// .into_iter()
|
|
||||||
// .map(|p| (p as f32 - offset) / scale)
|
|
||||||
// .collect::<Vec<f32>>(),
|
|
||||||
// )
|
|
||||||
// } else {
|
|
||||||
// ValueResult::U32(if need_trans {
|
|
||||||
// let offset = offset as u32;
|
|
||||||
// slice.into_iter().map(|p| p - offset).collect::<Vec<u32>>()
|
|
||||||
// } else {
|
|
||||||
// slice
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// Ok((input, result))
|
|
||||||
// }
|
|
||||||
// ValueTypes::F32 => {
|
|
||||||
// let ratio = mem::size_of::<f32>() / mem::size_of::<u8>();
|
|
||||||
// let (input, result) = take(len * ratio)(input)?;
|
|
||||||
// let result = unsafe {
|
|
||||||
// let ptr = result.as_ptr() as *const f32;
|
|
||||||
// let slice = std::slice::from_raw_parts(ptr, len);
|
|
||||||
// let slice = slice.to_vec();
|
|
||||||
|
|
||||||
// ValueResult::F32(if need_trans {
|
|
||||||
// let offset = offset as f32;
|
|
||||||
// slice
|
|
||||||
// .into_iter()
|
|
||||||
// .map(|p| (p - offset) / scale)
|
|
||||||
// .collect::<Vec<f32>>()
|
|
||||||
// } else {
|
|
||||||
// slice
|
|
||||||
// })
|
|
||||||
// };
|
|
||||||
|
|
||||||
// Ok((input, result))
|
|
||||||
// }
|
|
||||||
// ValueTypes::U64 => {
|
|
||||||
// let ratio = mem::size_of::<u64>() / mem::size_of::<u8>();
|
|
||||||
// let (input, result) = take(len * ratio)(input)?;
|
|
||||||
// let result = unsafe {
|
|
||||||
// let ptr = result.as_ptr() as *const u64;
|
|
||||||
// let slice = std::slice::from_raw_parts(ptr, len);
|
|
||||||
// let slice = slice.to_vec();
|
|
||||||
|
|
||||||
// if trans_to_bigger {
|
|
||||||
// let offset = offset as f64;
|
|
||||||
// let scale = scale as f64;
|
|
||||||
// ValueResult::F64(
|
|
||||||
// slice
|
|
||||||
// .into_iter()
|
|
||||||
// .map(|p| (p as f64 - offset) / scale)
|
|
||||||
// .collect::<Vec<f64>>(),
|
|
||||||
// )
|
|
||||||
// } else {
|
|
||||||
// ValueResult::U64(if need_trans {
|
|
||||||
// let offset = offset as u64;
|
|
||||||
// slice.into_iter().map(|p| p - offset).collect::<Vec<u64>>()
|
|
||||||
// } else {
|
|
||||||
// slice
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// Ok((input, result))
|
|
||||||
// }
|
|
||||||
// ValueTypes::I8 => {
|
|
||||||
// let (input, result) = take(len)(input)?;
|
|
||||||
// let result = unsafe {
|
|
||||||
// let ptr = result.as_ptr() as *const i8;
|
|
||||||
// let slice = std::slice::from_raw_parts(ptr, len);
|
|
||||||
|
|
||||||
// let slice = slice.to_vec();
|
|
||||||
|
|
||||||
// if trans_to_bigger {
|
|
||||||
// let offset = offset as f32;
|
|
||||||
// ValueResult::F32(
|
|
||||||
// slice
|
|
||||||
// .into_iter()
|
|
||||||
// .map(|p| (p as f32 - offset) / scale)
|
|
||||||
// .collect::<Vec<f32>>(),
|
|
||||||
// )
|
|
||||||
// } else {
|
|
||||||
// ValueResult::I8(if need_trans {
|
|
||||||
// let offset = offset as i8;
|
|
||||||
// slice.into_iter().map(|p| p - offset).collect::<Vec<i8>>()
|
|
||||||
// } else {
|
|
||||||
// slice
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// Ok((input, result))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ValueTypes::U8 => {
|
|
||||||
// let (input, slice) = take(len)(input)?;
|
|
||||||
// let slice = slice.to_vec();
|
|
||||||
// let result = if trans_to_bigger {
|
|
||||||
// let offset = offset as f32;
|
|
||||||
// ValueResult::F32(
|
|
||||||
// slice
|
|
||||||
// .into_iter()
|
|
||||||
// .map(|p| {
|
|
||||||
// if p as f64 != fill_value {
|
|
||||||
// (p as f32 - offset) / scale
|
|
||||||
// } else {
|
|
||||||
// p as f32
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// .collect::<Vec<f32>>(),
|
|
||||||
// )
|
|
||||||
// } else {
|
|
||||||
// ValueResult::U8(if need_trans {
|
|
||||||
// let offset = offset as u8;
|
|
||||||
// slice.into_iter().map(|p| p - offset).collect::<Vec<u8>>()
|
|
||||||
// } else {
|
|
||||||
// slice
|
|
||||||
// })
|
|
||||||
// };
|
|
||||||
|
|
||||||
// Ok((input, result))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
{"rustc_fingerprint":6534886339914138005,"outputs":{"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.77.0-nightly (d78329b92 2024-01-13)\nbinary: rustc\ncommit-hash: d78329b92e8d141d19505e7c1527181c4ab87ed4\ncommit-date: 2024-01-13\nhost: aarch64-apple-darwin\nrelease: 1.77.0-nightly\nLLVM version: 17.0.6\n","stderr":""},"15729799797837862367":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/tsuki/.rustup/toolchains/nightly-aarch64-apple-darwin\noff\npacked\nunpacked\n___\ndebug_assertions\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"v8.1a\"\ntarget_feature=\"v8.2a\"\ntarget_feature=\"v8.3a\"\ntarget_feature=\"v8.4a\"\ntarget_feature=\"vh\"\ntarget_has_atomic\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nunix\n","stderr":""}},"successes":{}}
|
{"rustc_fingerprint":7112235087551312656,"outputs":{"16495917692426387086":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n","stderr":""},"15729799797837862367":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/tsuki/.rustup/toolchains/nightly-aarch64-apple-darwin\noff\npacked\nunpacked\n___\ndebug_assertions\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"v8.1a\"\ntarget_feature=\"v8.2a\"\ntarget_feature=\"v8.3a\"\ntarget_feature=\"v8.4a\"\ntarget_feature=\"vh\"\ntarget_has_atomic\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nub_checks\nunix\n","stderr":""},"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.80.0-nightly (867900499 2024-05-23)\nbinary: rustc\ncommit-hash: 8679004993f08807289911d9f400f4ac4391d2bc\ncommit-date: 2024-05-23\nhost: aarch64-apple-darwin\nrelease: 1.80.0-nightly\nLLVM version: 18.1.6\n","stderr":""}},"successes":{}}
|
||||||
@ -1 +1 @@
|
|||||||
16f3840281ed1c0f
|
498af0718dbbcb83
|
||||||
@ -1 +1 @@
|
|||||||
{"rustc":17942353898403517573,"features":"[]","declared_features":"","target":8454914719411586997,"profile":14094339167972473758,"path":17523903030608720598,"deps":[[3470807962260834726,"serde",false,3464438898850424389],[6147374319788932929,"serde_json",false,4018346002534341099],[6644485573429891122,"thiserror",false,15376366722783231122],[6954241390595330609,"nom",false,17004332135801955629],[8926101378076943148,"byteorder",false,6029042559770906212],[8944703748776155531,"chrono",false,14085117952510397354],[10043922549268360936,"radarg_plugin_interface",false,2929903515477199145],[11138931377059941435,"num_traits",false,16208028648554347177],[12701726091060201577,"abi_stable",false,46919867052192855],[12732307821348191974,"anyhow",false,13132421988549874417],[12935855096716563853,"flate2",false,9880693184163145737],[16098302879908240583,"nom_derive",false,9783335926581460414]],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/etws_loader-826e392dbec97b5d/dep-lib-etws_loader"}}],"rustflags":[],"metadata":41698518999418921,"config":2202906307356721367,"compile_kind":0}
|
{"rustc":17942353898403517573,"features":"[]","declared_features":"","target":8454914719411586997,"profile":14094339167972473758,"path":17523903030608720598,"deps":[[3470807962260834726,"serde",false,3464438898850424389],[6147374319788932929,"serde_json",false,4018346002534341099],[6644485573429891122,"thiserror",false,15376366722783231122],[6954241390595330609,"nom",false,17004332135801955629],[8926101378076943148,"byteorder",false,6029042559770906212],[8944703748776155531,"chrono",false,1826932702640569830],[10043922549268360936,"radarg_plugin_interface",false,2929903515477199145],[11138931377059941435,"num_traits",false,4243888007213205890],[11576086755270729966,"geo",false,13469853973119584516],[12701726091060201577,"abi_stable",false,46919867052192855],[12732307821348191974,"anyhow",false,13132421988549874417],[12935855096716563853,"flate2",false,9880693184163145737],[16098302879908240583,"nom_derive",false,9783335926581460414]],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/etws_loader-826e392dbec97b5d/dep-lib-etws_loader"}}],"rustflags":[],"metadata":41698518999418921,"config":2202906307356721367,"compile_kind":0}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{"$message_type":"diagnostic","message":"unused imports: `Write`, `self`","code":{"code":"unused_imports","explanation":null},"level":"warning","spans":[{"file_name":"src/parser.rs","byte_start":323,"byte_end":327,"line_start":13,"line_end":13,"column_start":15,"column_end":19,"is_primary":true,"text":[{"text":"use std::io::{self, Read, Write};","highlight_start":15,"highlight_end":19}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"src/parser.rs","byte_start":335,"byte_end":340,"line_start":13,"line_end":13,"column_start":27,"column_end":32,"is_primary":true,"text":[{"text":"use std::io::{self, Read, Write};","highlight_start":27,"highlight_end":32}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"`#[warn(unused_imports)]` on by default","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"remove the unused imports","code":null,"level":"help","spans":[{"file_name":"src/parser.rs","byte_start":323,"byte_end":329,"line_start":13,"line_end":13,"column_start":15,"column_end":21,"is_primary":true,"text":[{"text":"use std::io::{self, Read, Write};","highlight_start":15,"highlight_end":21}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"src/parser.rs","byte_start":333,"byte_end":340,"line_start":13,"line_end":13,"column_start":25,"column_end":32,"is_primary":true,"text":[{"text":"use std::io::{self, Read, Write};","highlight_start":25,"highlight_end":32}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused imports: `Write`, `self`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/parser.rs:13:15\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m13\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0muse std::io::{self, Read, Write};\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(unused_imports)]` on by default\u001b[0m\n\n"}
|
{"$message_type":"diagnostic","message":"unused imports: `Write`, `self`","code":{"code":"unused_imports","explanation":null},"level":"warning","spans":[{"file_name":"src/parser.rs","byte_start":323,"byte_end":327,"line_start":13,"line_end":13,"column_start":15,"column_end":19,"is_primary":true,"text":[{"text":"use std::io::{self, Read, Write};","highlight_start":15,"highlight_end":19}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"src/parser.rs","byte_start":335,"byte_end":340,"line_start":13,"line_end":13,"column_start":27,"column_end":32,"is_primary":true,"text":[{"text":"use std::io::{self, Read, Write};","highlight_start":27,"highlight_end":32}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"`#[warn(unused_imports)]` on by default","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"remove the unused imports","code":null,"level":"help","spans":[{"file_name":"src/parser.rs","byte_start":323,"byte_end":329,"line_start":13,"line_end":13,"column_start":15,"column_end":21,"is_primary":true,"text":[{"text":"use std::io::{self, Read, Write};","highlight_start":15,"highlight_end":21}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"src/parser.rs","byte_start":333,"byte_end":340,"line_start":13,"line_end":13,"column_start":25,"column_end":32,"is_primary":true,"text":[{"text":"use std::io::{self, Read, Write};","highlight_start":25,"highlight_end":32}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused imports: `Write`, `self`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/parser.rs:13:15\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m13\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0muse std::io::{self, Read, Write};\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(unused_imports)]` on by default\u001b[0m\n\n"}
|
||||||
{"$message_type":"diagnostic","message":"unused variable: `hlen2`","code":{"code":"unused_variables","explanation":null},"level":"warning","spans":[{"file_name":"src/parser.rs","byte_start":6651,"byte_end":6656,"line_start":198,"line_end":198,"column_start":21,"column_end":26,"is_primary":true,"text":[{"text":" let (input, hlen2) = Self::_parse_u32(input, order)?;","highlight_start":21,"highlight_end":26}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"`#[warn(unused_variables)]` on by default","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"if this is intentional, prefix it with an underscore","code":null,"level":"help","spans":[{"file_name":"src/parser.rs","byte_start":6651,"byte_end":6656,"line_start":198,"line_end":198,"column_start":21,"column_end":26,"is_primary":true,"text":[{"text":" let (input, hlen2) = Self::_parse_u32(input, order)?;","highlight_start":21,"highlight_end":26}],"label":null,"suggested_replacement":"_hlen2","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `hlen2`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/parser.rs:198:21\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m198\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m let (input, hlen2) = Self::_parse_u32(input, order)?;\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_hlen2`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(unused_variables)]` on by default\u001b[0m\n\n"}
|
{"$message_type":"diagnostic","message":"unused variable: `hlen2`","code":{"code":"unused_variables","explanation":null},"level":"warning","spans":[{"file_name":"src/parser.rs","byte_start":6651,"byte_end":6656,"line_start":198,"line_end":198,"column_start":21,"column_end":26,"is_primary":true,"text":[{"text":" let (input, hlen2) = Self::_parse_u32(input, order)?;","highlight_start":21,"highlight_end":26}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"`#[warn(unused_variables)]` on by default","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"if this is intentional, prefix it with an underscore","code":null,"level":"help","spans":[{"file_name":"src/parser.rs","byte_start":6651,"byte_end":6656,"line_start":198,"line_end":198,"column_start":21,"column_end":26,"is_primary":true,"text":[{"text":" let (input, hlen2) = Self::_parse_u32(input, order)?;","highlight_start":21,"highlight_end":26}],"label":null,"suggested_replacement":"_hlen2","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `hlen2`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/parser.rs:198:21\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m198\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m let (input, hlen2) = Self::_parse_u32(input, order)?;\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_hlen2`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(unused_variables)]` on by default\u001b[0m\n\n"}
|
||||||
{"$message_type":"diagnostic","message":"unused variable: `order`","code":{"code":"unused_variables","explanation":null},"level":"warning","spans":[{"file_name":"src/parser.rs","byte_start":9035,"byte_end":9040,"line_start":279,"line_end":279,"column_start":9,"column_end":14,"is_primary":true,"text":[{"text":" order: Order,","highlight_start":9,"highlight_end":14}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"if this is intentional, prefix it with an underscore","code":null,"level":"help","spans":[{"file_name":"src/parser.rs","byte_start":9035,"byte_end":9040,"line_start":279,"line_end":279,"column_start":9,"column_end":14,"is_primary":true,"text":[{"text":" order: Order,","highlight_start":9,"highlight_end":14}],"label":null,"suggested_replacement":"_order","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `order`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/parser.rs:279:9\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m279\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m order: Order,\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_order`\u001b[0m\n\n"}
|
{"$message_type":"diagnostic","message":"unused variable: `order`","code":{"code":"unused_variables","explanation":null},"level":"warning","spans":[{"file_name":"src/parser.rs","byte_start":9035,"byte_end":9040,"line_start":279,"line_end":279,"column_start":9,"column_end":14,"is_primary":true,"text":[{"text":" order: Order,","highlight_start":9,"highlight_end":14}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"if this is intentional, prefix it with an underscore","code":null,"level":"help","spans":[{"file_name":"src/parser.rs","byte_start":9035,"byte_end":9040,"line_start":279,"line_end":279,"column_start":9,"column_end":14,"is_primary":true,"text":[{"text":" order: Order,","highlight_start":9,"highlight_end":14}],"label":null,"suggested_replacement":"_order","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `order`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/parser.rs:279:9\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m279\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m order: Order,\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_order`\u001b[0m\n\n"}
|
||||||
{"$message_type":"diagnostic","message":"unused variable: `dimension_len`","code":{"code":"unused_variables","explanation":null},"level":"warning","spans":[{"file_name":"src/lib.rs","byte_start":1109,"byte_end":1122,"line_start":43,"line_end":43,"column_start":26,"column_end":39,"is_primary":true,"text":[{"text":" let (dimension_len, data) = match b.data {","highlight_start":26,"highlight_end":39}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"if this is intentional, prefix it with an underscore","code":null,"level":"help","spans":[{"file_name":"src/lib.rs","byte_start":1109,"byte_end":1122,"line_start":43,"line_end":43,"column_start":26,"column_end":39,"is_primary":true,"text":[{"text":" let (dimension_len, data) = match b.data {","highlight_start":26,"highlight_end":39}],"label":null,"suggested_replacement":"_dimension_len","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `dimension_len`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:43:26\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m43\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m let (dimension_len, data) = match b.data {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_dimension_len`\u001b[0m\n\n"}
|
{"$message_type":"diagnostic","message":"unused variable: `dimension_len`","code":{"code":"unused_variables","explanation":null},"level":"warning","spans":[{"file_name":"src/lib.rs","byte_start":1799,"byte_end":1812,"line_start":61,"line_end":61,"column_start":26,"column_end":39,"is_primary":true,"text":[{"text":" let (dimension_len, data) = match b.data {","highlight_start":26,"highlight_end":39}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"if this is intentional, prefix it with an underscore","code":null,"level":"help","spans":[{"file_name":"src/lib.rs","byte_start":1799,"byte_end":1812,"line_start":61,"line_end":61,"column_start":26,"column_end":39,"is_primary":true,"text":[{"text":" let (dimension_len, data) = match b.data {","highlight_start":26,"highlight_end":39}],"label":null,"suggested_replacement":"_dimension_len","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `dimension_len`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:61:26\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m61\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m let (dimension_len, data) = match b.data {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_dimension_len`\u001b[0m\n\n"}
|
||||||
{"$message_type":"diagnostic","message":"variants `I64` and `U64` are never constructed","code":{"code":"dead_code","explanation":null},"level":"warning","spans":[{"file_name":"src/parser.rs","byte_start":563,"byte_end":573,"line_start":28,"line_end":28,"column_start":6,"column_end":16,"is_primary":false,"text":[{"text":"enum ValueTypes {","highlight_start":6,"highlight_end":16}],"label":"variants in this enum","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"src/parser.rs","byte_start":580,"byte_end":583,"line_start":29,"line_end":29,"column_start":5,"column_end":8,"is_primary":true,"text":[{"text":" I64,","highlight_start":5,"highlight_end":8}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"src/parser.rs","byte_start":616,"byte_end":619,"line_start":33,"line_end":33,"column_start":5,"column_end":8,"is_primary":true,"text":[{"text":" U64,","highlight_start":5,"highlight_end":8}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"`#[warn(dead_code)]` on by default","code":null,"level":"note","spans":[],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: variants `I64` and `U64` are never constructed\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/parser.rs:29:5\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m28\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0menum ValueTypes {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m----------\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mvariants in this enum\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m29\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m I64,\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m33\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m U64,\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(dead_code)]` on by default\u001b[0m\n\n"}
|
{"$message_type":"diagnostic","message":"variants `I64` and `U64` are never constructed","code":{"code":"dead_code","explanation":null},"level":"warning","spans":[{"file_name":"src/parser.rs","byte_start":563,"byte_end":573,"line_start":28,"line_end":28,"column_start":6,"column_end":16,"is_primary":false,"text":[{"text":"enum ValueTypes {","highlight_start":6,"highlight_end":16}],"label":"variants in this enum","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"src/parser.rs","byte_start":580,"byte_end":583,"line_start":29,"line_end":29,"column_start":5,"column_end":8,"is_primary":true,"text":[{"text":" I64,","highlight_start":5,"highlight_end":8}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"src/parser.rs","byte_start":616,"byte_end":619,"line_start":33,"line_end":33,"column_start":5,"column_end":8,"is_primary":true,"text":[{"text":" U64,","highlight_start":5,"highlight_end":8}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"`#[warn(dead_code)]` on by default","code":null,"level":"note","spans":[],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: variants `I64` and `U64` are never constructed\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/parser.rs:29:5\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m28\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0menum ValueTypes {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m----------\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mvariants in this enum\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m29\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m I64,\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m33\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m U64,\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(dead_code)]` on by default\u001b[0m\n\n"}
|
||||||
{"$message_type":"diagnostic","message":"5 warnings emitted","code":null,"level":"warning","spans":[],"children":[],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: 5 warnings emitted\u001b[0m\n\n"}
|
{"$message_type":"diagnostic","message":"5 warnings emitted","code":null,"level":"warning","spans":[],"children":[],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: 5 warnings emitted\u001b[0m\n\n"}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
58
gi/Cargo.toml
Normal file
58
gi/Cargo.toml
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
[package]
|
||||||
|
edition = "2021"
|
||||||
|
name = "gi"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
aligned-vec = "0.6.0"
|
||||||
|
anyhow = "1.0.86"
|
||||||
|
bytemuck = { version = "1.16.1", features = ["derive"] }
|
||||||
|
byteorder = "1.5.0"
|
||||||
|
cgmath = "0.18.0"
|
||||||
|
chrono = "0.4.38"
|
||||||
|
copypasta = "0.10.1"
|
||||||
|
dirs = "5.0.1"
|
||||||
|
env_logger = "0.11.3"
|
||||||
|
flate2 = "1.0.30"
|
||||||
|
freetype-rs = { version = "0.37.0", features = ["bundled"] }
|
||||||
|
geo = "0.28.0"
|
||||||
|
glow = "0.13.1"
|
||||||
|
glutin = "0.31.3"
|
||||||
|
glutin-winit = "0.4.2"
|
||||||
|
image = "0.25.2"
|
||||||
|
include_dir = "0.7.4"
|
||||||
|
log = "0.4.22"
|
||||||
|
lru = "0.12.4"
|
||||||
|
nalgebra = "0.33.0"
|
||||||
|
nalgebra-glm = "0.18.0"
|
||||||
|
ndarray = "0.15.6"
|
||||||
|
nom = "7.1.3"
|
||||||
|
nom-derive = "0.10.1"
|
||||||
|
once_cell = "1.19.0"
|
||||||
|
pathfinder_geometry = "0.5.1"
|
||||||
|
raw-window-handle = "0.5.2"
|
||||||
|
regex = "1.10.5"
|
||||||
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
|
serde_json = "1.0.120"
|
||||||
|
thiserror = "1.0.61"
|
||||||
|
tinyfiledialogs = "3.0"
|
||||||
|
tracker = "0.2.2"
|
||||||
|
glsl = "7.0.0"
|
||||||
|
glsl-quasiquote = "7.0.0"
|
||||||
|
toml = "0.8.19"
|
||||||
|
femtovg = "0.9.2"
|
||||||
|
rust-embed = "8.5.0"
|
||||||
|
tempfile = "3.12.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["sdf_font"]
|
||||||
|
|
||||||
|
sdfer_use_f64_instead_of_f32 = []
|
||||||
|
normal_font = []
|
||||||
|
sdf_font = []
|
||||||
|
inparser = []
|
||||||
|
femtovg = []
|
||||||
|
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "gi"
|
||||||
66
gi/src/camera.rs
Normal file
66
gi/src/camera.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use cgmath::Euler;
|
||||||
|
use glow::NativeProgram;
|
||||||
|
use nalgebra_glm::{look_at, Mat4x4, Vec3};
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct Camera {
|
||||||
|
pos: Vec3,
|
||||||
|
upward: Vec3,
|
||||||
|
center: Vec3,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CameraPositon = Vec3;
|
||||||
|
|
||||||
|
impl Camera {
|
||||||
|
pub(crate) fn new(world_loc: CameraPositon, upward: Vec3, center: Vec3) -> Self {
|
||||||
|
Self {
|
||||||
|
pos: world_loc,
|
||||||
|
upward,
|
||||||
|
center,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_position(&self) -> CameraPositon {
|
||||||
|
self.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_position(&mut self, pos: CameraPositon) {
|
||||||
|
self.pos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_upward(&self) -> Vec3 {
|
||||||
|
self.upward
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_center(&self) -> Vec3 {
|
||||||
|
self.center
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_upward(&mut self, upward: Vec3) {
|
||||||
|
self.upward = upward;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_center(&mut self, center: Vec3) {
|
||||||
|
self.center = center;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_view_matrix(&self) -> Mat4x4 {
|
||||||
|
let l = self.center;
|
||||||
|
look_at(&self.pos, &l, &self.upward)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn front(&self) -> Vec3 {
|
||||||
|
self.center - self.pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Camera {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
pos: Vec3::new(0.0, 0.0, 0.0),
|
||||||
|
upward: Vec3::new(0.0, 1.0, 0.0),
|
||||||
|
center: Vec3::new(0.0, 0.0, -1.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
gi/src/components/mod.rs
Normal file
5
gi/src/components/mod.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
mod program;
|
||||||
|
mod shader;
|
||||||
|
|
||||||
|
pub use program::Program;
|
||||||
|
pub use shader::Shader;
|
||||||
136
gi/src/components/program.rs
Normal file
136
gi/src/components/program.rs
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
use glow::{HasContext, NativeUniformLocation};
|
||||||
|
|
||||||
|
use super::shader::Shader;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Program {
|
||||||
|
version: &'static str,
|
||||||
|
vertex: Shader,
|
||||||
|
fragment: Shader,
|
||||||
|
|
||||||
|
geometry: Option<Shader>,
|
||||||
|
pub native_program: Option<<glow::Context as HasContext>::Program>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Program {
|
||||||
|
pub fn new(
|
||||||
|
vertex: Shader,
|
||||||
|
fragment: Shader,
|
||||||
|
geometry: Option<Shader>,
|
||||||
|
version: &'static str,
|
||||||
|
) -> Self {
|
||||||
|
let res = Self {
|
||||||
|
version,
|
||||||
|
vertex,
|
||||||
|
fragment,
|
||||||
|
geometry,
|
||||||
|
native_program: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vertex(&self) -> &Shader {
|
||||||
|
&self.vertex
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fragment(&self) -> &Shader {
|
||||||
|
&self.fragment
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn geometry(&self) -> Option<&Shader> {
|
||||||
|
self.geometry.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "inparser")]
|
||||||
|
pub fn set_hook(&mut self, hook: &str, code: &Snippet) {
|
||||||
|
self.vertex.set_hook(hook, code);
|
||||||
|
self.fragment.set_hook(hook, code);
|
||||||
|
self.geometry.as_mut().map(|g| g.set_hook(hook, code));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "inparser")]
|
||||||
|
pub fn set_transform<T: Transform>(&mut self, value: &T) {
|
||||||
|
self.set_hook("transform", value.snippet());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "inparser")]
|
||||||
|
pub fn set_viewport(&mut self, viewport: &Viewport) {
|
||||||
|
self.set_hook("viewport", viewport.snippet());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(&mut self, gl: &glow::Context) -> crate::errors::Result<()> {
|
||||||
|
use crate::errors::Error;
|
||||||
|
|
||||||
|
#[cfg(feature = "inparser")]
|
||||||
|
{
|
||||||
|
self.vertex.define("_GLUMPY__VERTEX_SHADER__");
|
||||||
|
self.fragment.define("_GLUMPY__FRAGMENT_SHADER__");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let program = gl.create_program().map_err(|e| Error::InvalidProgram(e))?;
|
||||||
|
|
||||||
|
let vertex_shader = self.vertex.compile(&self.version, gl);
|
||||||
|
let fragment_shader = self.fragment.compile(&self.version, gl);
|
||||||
|
|
||||||
|
gl.attach_shader(program, vertex_shader);
|
||||||
|
gl.attach_shader(program, fragment_shader);
|
||||||
|
|
||||||
|
// Geom Shader
|
||||||
|
let geom_shader = if let Some(geometry) = self.geometry.as_mut() {
|
||||||
|
#[cfg(feature = "inparser")]
|
||||||
|
geometry.define("_GLUMPY__GEOMETRY_SHADER__");
|
||||||
|
let geometry_shader = geometry.compile(&self.version, gl);
|
||||||
|
|
||||||
|
gl.attach_shader(program, geometry_shader);
|
||||||
|
|
||||||
|
Some(geometry_shader)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
gl.link_program(program);
|
||||||
|
|
||||||
|
if !gl.get_program_link_status(program) {
|
||||||
|
return Err(Error::InvalidProgram(gl.get_program_info_log(program)));
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.detach_shader(program, vertex_shader);
|
||||||
|
gl.detach_shader(program, fragment_shader);
|
||||||
|
if let Some(_) = self.geometry.as_ref() {
|
||||||
|
gl.detach_shader(program, geom_shader.unwrap());
|
||||||
|
gl.delete_shader(geom_shader.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.delete_shader(vertex_shader);
|
||||||
|
gl.delete_shader(fragment_shader);
|
||||||
|
|
||||||
|
gl.use_program(Some(program));
|
||||||
|
|
||||||
|
self.native_program = Some(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(&self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
self.native_program.map(|p| gl.delete_program(p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_uniform_location(
|
||||||
|
&self,
|
||||||
|
gl: &glow::Context,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<NativeUniformLocation> {
|
||||||
|
self.native_program
|
||||||
|
.as_ref()
|
||||||
|
.map(|p| unsafe {
|
||||||
|
let location = gl.get_uniform_location(*p, name);
|
||||||
|
location
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
}
|
||||||
83
gi/src/components/shader.rs
Normal file
83
gi/src/components/shader.rs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
errors::{Error, Result},
|
||||||
|
shaders::CodePiece,
|
||||||
|
};
|
||||||
|
use glow::HasContext;
|
||||||
|
use log::{info, warn};
|
||||||
|
pub type ShaderType = u32;
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Shader {
|
||||||
|
target: u32,
|
||||||
|
#[cfg(feature = "inparser")]
|
||||||
|
parsed: CodeBlock,
|
||||||
|
parsed: String,
|
||||||
|
#[cfg(feature = "inparser")]
|
||||||
|
pub hooks: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Shader {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.parsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shader {
|
||||||
|
pub fn new(target: ShaderType, code: impl CodePiece) -> Result<Self> {
|
||||||
|
let code = code.to_string();
|
||||||
|
|
||||||
|
#[cfg(feature = "inparser")]
|
||||||
|
{
|
||||||
|
let code = merge_includes(code).map_err(|e| Error::InvalidSnippet(e.to_string()))?;
|
||||||
|
let parsed = CodeBlock::new(&code)?;
|
||||||
|
let hooks = parsed.all_hooks().iter().map(|h| h.name.clone()).collect();
|
||||||
|
info!("Shader:{} Hooks: {:?}", target, hooks);
|
||||||
|
return Ok(Self {
|
||||||
|
target,
|
||||||
|
parsed,
|
||||||
|
hooks,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
target,
|
||||||
|
parsed: code,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(
|
||||||
|
&self,
|
||||||
|
version: &str,
|
||||||
|
gl: &glow::Context,
|
||||||
|
) -> <glow::Context as HasContext>::Shader {
|
||||||
|
let shader = unsafe {
|
||||||
|
let shader = gl.create_shader(self.target).expect("Cannot create shader");
|
||||||
|
gl.shader_source(
|
||||||
|
shader,
|
||||||
|
&format!("{}\n{}", &format!("#version {}", version), self.to_string()),
|
||||||
|
);
|
||||||
|
gl.compile_shader(shader);
|
||||||
|
if !gl.get_shader_compile_status(shader) {
|
||||||
|
panic!("{}", gl.get_shader_info_log(shader))
|
||||||
|
}
|
||||||
|
shader
|
||||||
|
};
|
||||||
|
shader
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "inparser")]
|
||||||
|
pub fn define(&mut self, s: &str) {
|
||||||
|
self.parsed
|
||||||
|
.insert(Code::new(&format!("#define {}\n", s)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "inparser")]
|
||||||
|
pub fn set_hook(&mut self, hook: &str, code: &Snippet) {
|
||||||
|
self.parsed.set_hook(hook, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn target(&self) -> u32 {
|
||||||
|
self.target
|
||||||
|
}
|
||||||
|
}
|
||||||
14
gi/src/data_loader/error.rs
Normal file
14
gi/src/data_loader/error.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ETWSError {
|
||||||
|
#[error("IO Error")]
|
||||||
|
IOError {
|
||||||
|
#[from]
|
||||||
|
source: std::io::Error,
|
||||||
|
},
|
||||||
|
#[error("Format Error")]
|
||||||
|
FormatError {
|
||||||
|
#[from]
|
||||||
|
source: anyhow::Error,
|
||||||
|
},
|
||||||
|
}
|
||||||
260
gi/src/data_loader/mod.rs
Normal file
260
gi/src/data_loader/mod.rs
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
use crate::errors::*;
|
||||||
|
use crate::utils::geo_tools::calculate_coverage;
|
||||||
|
use ndarray::ArrayD;
|
||||||
|
pub mod error;
|
||||||
|
mod parser;
|
||||||
|
|
||||||
|
use chrono::Utc;
|
||||||
|
|
||||||
|
macro_rules! block_prepare {
|
||||||
|
($data: ident, $_ty: ty, $shape:ident, $(($branch: tt),)+) => {
|
||||||
|
match $data {
|
||||||
|
$(
|
||||||
|
$branch(data) => ArrayD::from_shape_vec($shape.as_slice(), data.into_iter().map(|b| b as $_ty).collect::<Vec<$_ty>>()).unwrap(),
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! block_data_type_prepare {
|
||||||
|
($data: ident, $($branch: tt => $data_type: tt,)+) => {
|
||||||
|
match $data {
|
||||||
|
$(
|
||||||
|
$branch => $data_type,
|
||||||
|
)+
|
||||||
|
_ => DataType::Unknown
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Data {
|
||||||
|
pub filetime: chrono::DateTime<Utc>,
|
||||||
|
pub radar_info: RadarInfo,
|
||||||
|
pub blocks: Vec<Block>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum DataType {
|
||||||
|
ET,
|
||||||
|
VIL,
|
||||||
|
EB,
|
||||||
|
DBZ,
|
||||||
|
VEl,
|
||||||
|
ZDR,
|
||||||
|
PHIDP,
|
||||||
|
KDP,
|
||||||
|
CC,
|
||||||
|
HCA,
|
||||||
|
QPE,
|
||||||
|
QPF,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Block {
|
||||||
|
pub data: ArrayD<f32>,
|
||||||
|
pub data_type: DataType,
|
||||||
|
pub coord_type: CoordType,
|
||||||
|
pub min_value: f32,
|
||||||
|
pub max_value: f32,
|
||||||
|
pub unvalid_value: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum CoordType {
|
||||||
|
Polar {
|
||||||
|
azimuth_range: [f64; 2],
|
||||||
|
r_range: [f64; 2],
|
||||||
|
elevation_range: [f64; 2],
|
||||||
|
azimuth: Vec<f32>,
|
||||||
|
r: Vec<f32>,
|
||||||
|
elevation: Vec<f32>,
|
||||||
|
},
|
||||||
|
Cartesian {
|
||||||
|
lat_range: [f64; 2],
|
||||||
|
lon_range: [f64; 2],
|
||||||
|
hgt_range: [f64; 2],
|
||||||
|
lat: Vec<f32>,
|
||||||
|
lon: Vec<f32>,
|
||||||
|
hgt: Vec<f32>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RadarInfo {
|
||||||
|
pub lat_range: [f32; 2],
|
||||||
|
pub lon_range: [f32; 2],
|
||||||
|
pub center: [f32; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Data {
|
||||||
|
pub fn from_path(path: impl AsRef<std::path::Path>) -> Result<Self> {
|
||||||
|
let record = parser::Record::parse_from_path(path)?;
|
||||||
|
|
||||||
|
let mut radar_loc: [Option<f64>; 3] = [None, None, None];
|
||||||
|
|
||||||
|
let blocks = record
|
||||||
|
.blocks
|
||||||
|
.into_iter()
|
||||||
|
.map(|b| {
|
||||||
|
let vec_data = b.data;
|
||||||
|
use parser::ValueResult::*;
|
||||||
|
|
||||||
|
let info = b.info;
|
||||||
|
|
||||||
|
radar_loc = [info.radar_alt, info.radar_lat, info.radar_lon];
|
||||||
|
let shape = info
|
||||||
|
.dimension_size
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s as usize)
|
||||||
|
.collect::<Vec<usize>>();
|
||||||
|
|
||||||
|
let new_data = block_prepare!(
|
||||||
|
vec_data,
|
||||||
|
f32,
|
||||||
|
shape,
|
||||||
|
(I64),
|
||||||
|
(F64),
|
||||||
|
(I32),
|
||||||
|
(F32),
|
||||||
|
(I16),
|
||||||
|
(U64),
|
||||||
|
(U32),
|
||||||
|
(I8),
|
||||||
|
(U8),
|
||||||
|
);
|
||||||
|
|
||||||
|
let dim_len = info.dimension_values.len();
|
||||||
|
let dim_last = info.dimension_values.get(dim_len - 1).unwrap();
|
||||||
|
let dim_minus_one = info.dimension_values.get(dim_len - 2).unwrap();
|
||||||
|
let (dim_minus_two_range, dim_minus_two) = if dim_len == 3 {
|
||||||
|
let hgt = info.dimension_values.get(dim_len - 3).unwrap();
|
||||||
|
(
|
||||||
|
[hgt[0], hgt[hgt.len() - 1]],
|
||||||
|
hgt.iter().map(|v| *v as f32).collect(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
([0.0, 0.0], vec![])
|
||||||
|
};
|
||||||
|
|
||||||
|
let dim_last_range = [dim_last[0], dim_last[dim_last.len() - 1]];
|
||||||
|
let dim_minus_one_range =
|
||||||
|
[dim_minus_one[0], dim_minus_one[dim_minus_one.len() - 1]];
|
||||||
|
|
||||||
|
let coord_type = if info.dimension_des.contains(&format!("lat")) {
|
||||||
|
CoordType::Cartesian {
|
||||||
|
lat_range: dim_minus_one_range,
|
||||||
|
lon_range: dim_last_range,
|
||||||
|
hgt_range: dim_minus_two_range,
|
||||||
|
lat: dim_minus_one.iter().map(|v| *v as f32).collect(),
|
||||||
|
lon: dim_last.iter().map(|v| *v as f32).collect(),
|
||||||
|
hgt: dim_minus_two,
|
||||||
|
}
|
||||||
|
} else if info.dimension_des.contains(&format!("ele"))
|
||||||
|
|| info.dimension_des.contains(&format!("el"))
|
||||||
|
|| info.dimension_des.contains(&format!("elevation"))
|
||||||
|
{
|
||||||
|
CoordType::Polar {
|
||||||
|
azimuth_range: dim_minus_one_range,
|
||||||
|
r_range: dim_last_range,
|
||||||
|
elevation_range: dim_minus_two_range,
|
||||||
|
azimuth: dim_minus_one.iter().map(|v| *v as f32).collect(),
|
||||||
|
r: dim_last.iter().map(|v| *v as f32).collect(),
|
||||||
|
elevation: dim_minus_two,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CoordType::Other
|
||||||
|
};
|
||||||
|
|
||||||
|
use DataType::*;
|
||||||
|
let valuetype = info.value_name.as_str();
|
||||||
|
|
||||||
|
let data_type = block_data_type_prepare!(valuetype,
|
||||||
|
"ET" => ET,
|
||||||
|
"VIL" => VIL,
|
||||||
|
"EB" => EB,
|
||||||
|
"DBZ" =>DBZ,
|
||||||
|
"CR" => DBZ,
|
||||||
|
"R" => DBZ,
|
||||||
|
"V" => VEl,
|
||||||
|
"ZDR" => ZDR,
|
||||||
|
"PHIDP" => PHIDP,
|
||||||
|
"KDP" => KDP,
|
||||||
|
"CC" => CC,
|
||||||
|
"HCA" => HCA,
|
||||||
|
"QPE" => QPE,
|
||||||
|
"QPF" => QPF,
|
||||||
|
"FR" => DBZ,
|
||||||
|
);
|
||||||
|
|
||||||
|
Block {
|
||||||
|
data: new_data,
|
||||||
|
data_type,
|
||||||
|
coord_type,
|
||||||
|
min_value: 0.0,
|
||||||
|
max_value: 204.0,
|
||||||
|
unvalid_value: info.fill_value as f32,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let radar_info = match blocks.first().unwrap().coord_type {
|
||||||
|
CoordType::Cartesian {
|
||||||
|
lat_range,
|
||||||
|
lon_range,
|
||||||
|
..
|
||||||
|
} => RadarInfo {
|
||||||
|
lat_range: [lat_range[0] as f32, lat_range[1] as f32],
|
||||||
|
lon_range: [lon_range[0] as f32, lon_range[1] as f32],
|
||||||
|
center: [
|
||||||
|
lat_range[0] as f32 + (lat_range[1] - lat_range[0]) as f32 / 2.0,
|
||||||
|
lon_range[0] as f32 + (lon_range[1] - lon_range[0]) as f32 / 2.0,
|
||||||
|
0.0,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
CoordType::Polar { r_range, .. } => {
|
||||||
|
let (min_lat, max_lat, min_lon, max_lon) =
|
||||||
|
calculate_coverage(radar_loc[1].unwrap(), radar_loc[2].unwrap(), r_range[1]);
|
||||||
|
|
||||||
|
RadarInfo {
|
||||||
|
lat_range: [min_lat as f32, max_lat as f32],
|
||||||
|
lon_range: [min_lon as f32, max_lon as f32],
|
||||||
|
center: [
|
||||||
|
radar_loc[1].unwrap() as f32,
|
||||||
|
radar_loc[2].unwrap() as f32,
|
||||||
|
radar_loc[0].unwrap() as f32,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Unknown coord type");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
filetime: record.filetime,
|
||||||
|
blocks,
|
||||||
|
radar_info,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_data_slice(&self, index: usize) -> Option<&[f32]> {
|
||||||
|
self.blocks.get(index).map(|b| b.data.as_slice().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
use super::Data;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_load() {
|
||||||
|
// let path = "/Volumes/data6/RadarArray/ShaoXing/radarData/OutputProducts/RadarProducts/FuseDataX/20030506/ZJSXAA_20030506200000_FR.dat.gz";
|
||||||
|
let path = "/Users/tsuki/Desktop/ZJSXAA_20230627163400_VIL.dat.gz";
|
||||||
|
|
||||||
|
let data = Data::from_path(path).unwrap();
|
||||||
|
println!("{:?}", data);
|
||||||
|
}
|
||||||
|
}
|
||||||
388
gi/src/data_loader/parser.rs
Normal file
388
gi/src/data_loader/parser.rs
Normal file
@ -0,0 +1,388 @@
|
|||||||
|
use super::error::ETWSError;
|
||||||
|
use aligned_vec::AVec;
|
||||||
|
use byteorder::{BigEndian, ByteOrder, LittleEndian};
|
||||||
|
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||||
|
use flate2::read::GzDecoder;
|
||||||
|
use nom::{
|
||||||
|
bytes::complete::{tag, take},
|
||||||
|
multi::count,
|
||||||
|
IResult,
|
||||||
|
};
|
||||||
|
use nom_derive::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub enum ValueResult {
|
||||||
|
I64(Vec<i64>),
|
||||||
|
F64(Vec<f64>),
|
||||||
|
I32(Vec<i32>),
|
||||||
|
F32(Vec<f32>),
|
||||||
|
I16(Vec<i16>),
|
||||||
|
U64(Vec<u64>),
|
||||||
|
U32(Vec<u32>),
|
||||||
|
I8(Vec<i8>),
|
||||||
|
U8(Vec<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ValueTypes {
|
||||||
|
I64,
|
||||||
|
F64,
|
||||||
|
I32,
|
||||||
|
F32,
|
||||||
|
U64,
|
||||||
|
U32,
|
||||||
|
I16,
|
||||||
|
I8,
|
||||||
|
U8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum Order {
|
||||||
|
BigEndian,
|
||||||
|
LittleEndian,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Record {
|
||||||
|
pub filetime: DateTime<Utc>,
|
||||||
|
pub blocks: Vec<ParsedBlock>, // Fill in generic types appropriately
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! match_in_macro {
|
||||||
|
($block:ident,$len:ident,$input:ident,$offset:ident,$scale:ident,$fill_value:ident,$(($branch:path, $t:ty, $bigger:ty,$raw_result:path, $bigger_result:path)),+) => {
|
||||||
|
{
|
||||||
|
use std::mem;
|
||||||
|
let need_trans = $offset != 0.0 || $scale != 1.0;
|
||||||
|
let trans_to_bigger = $offset.trunc() != $offset || $scale != 1.0;
|
||||||
|
match $block {
|
||||||
|
$(
|
||||||
|
$branch => {
|
||||||
|
let ratio = mem::size_of::<$t>() / mem::size_of::<u8>();
|
||||||
|
let (input, result) = take($len * ratio)($input)?;
|
||||||
|
|
||||||
|
let result = unsafe {
|
||||||
|
|
||||||
|
let mut ptr = result.as_ptr() as *const u8;
|
||||||
|
let aligned = ptr.align_offset(mem::align_of::<$t>());
|
||||||
|
|
||||||
|
if aligned != 0 {
|
||||||
|
let p = AVec::<u8>::from_slice(mem::align_of::<$t>(), result);
|
||||||
|
ptr = p.into_raw_parts().0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ptr = ptr.cast::<$t>();
|
||||||
|
|
||||||
|
let slice = std::slice::from_raw_parts(ptr, $len);
|
||||||
|
let slice = slice.to_vec();
|
||||||
|
|
||||||
|
if trans_to_bigger {
|
||||||
|
let offset = $offset as $bigger;
|
||||||
|
let scale = $scale as $bigger;
|
||||||
|
$bigger_result(
|
||||||
|
slice
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| if (p as f64 - $fill_value).abs() < f64::EPSILON {p as $bigger} else {(p as $bigger - offset) / scale} )
|
||||||
|
.collect::<Vec<$bigger>>(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
$raw_result(if need_trans {
|
||||||
|
let offset = $offset as $t;
|
||||||
|
slice.into_iter().map(|p| if (p as f64 - $fill_value).abs() < f64::EPSILON {p} else {p - offset}).collect::<Vec<$t>>()
|
||||||
|
} else {
|
||||||
|
slice
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok((input, result))
|
||||||
|
},
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Record {
|
||||||
|
pub fn parse_from_path(path: impl AsRef<Path>) -> Result<Self, ETWSError> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
if !(path.ends_with(".dat") || !path.ends_with(".dat.gz")) {
|
||||||
|
return Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::InvalidInput,
|
||||||
|
"Invalid file extension",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !path.exists() {
|
||||||
|
return Err(std::io::Error::new(std::io::ErrorKind::NotFound, "File not found").into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = File::open(path)?;
|
||||||
|
let binary_data = if path.extension().unwrap() == "gz" {
|
||||||
|
let mut result: Vec<u8> = Vec::new();
|
||||||
|
let mut decoder = GzDecoder::new(file);
|
||||||
|
decoder.read_to_end(&mut result)?;
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
file.read_to_end(&mut result)?;
|
||||||
|
result
|
||||||
|
};
|
||||||
|
|
||||||
|
let (_, parsed) =
|
||||||
|
Self::_parse(binary_data.as_slice()).map_err(|_| anyhow::Error::msg("Parse error"))?;
|
||||||
|
|
||||||
|
Ok(parsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _parse(binary_data: &[u8]) -> IResult<&[u8], Record> {
|
||||||
|
let start_tag = b"UNI_DATA";
|
||||||
|
let (input, _) = tag(start_tag)(binary_data)?;
|
||||||
|
let (input, order) = Self::_parse_split_fn(input, 8, 8, Self::_parse_order)?;
|
||||||
|
let (input, hlen) = take(4usize)(input)?;
|
||||||
|
|
||||||
|
let hlen = match order {
|
||||||
|
Order::BigEndian => BigEndian::read_u32(hlen),
|
||||||
|
_ => LittleEndian::read_u32(hlen),
|
||||||
|
};
|
||||||
|
let (input, record_info) = Self::_parse_split_fn(input, 0, 8, |input| {
|
||||||
|
let (input, p) = take(hlen)(input)?;
|
||||||
|
let p: RecordInfo = serde_json::from_slice(p).unwrap();
|
||||||
|
Ok((input, p))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let (input, blocks) =
|
||||||
|
count(Self::_parse_block_fn(order), record_info.block_num as usize)(input)?;
|
||||||
|
|
||||||
|
let data_time =
|
||||||
|
NaiveDateTime::parse_from_str(&record_info.file_time, r"%Y%m%d%H%M%S").unwrap();
|
||||||
|
let filetime = data_time.and_utc();
|
||||||
|
|
||||||
|
Ok((input, Record { filetime, blocks }))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _parse_order(input: &[u8]) -> IResult<&[u8], Order> {
|
||||||
|
let (input, order) = take(4usize)(input)?;
|
||||||
|
let result = if order == b"LEND" {
|
||||||
|
Order::LittleEndian
|
||||||
|
} else {
|
||||||
|
Order::BigEndian
|
||||||
|
};
|
||||||
|
let (input, _) = take(4usize)(input)?;
|
||||||
|
Ok((input, result))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _parse_split(input: &[u8], s: usize, fore: usize, after: usize) -> IResult<&[u8], &[u8]> {
|
||||||
|
let (input, _) = take(fore)(input)?;
|
||||||
|
let (input, result) = take(s)(input)?;
|
||||||
|
let (input, _) = take(after)(input)?;
|
||||||
|
Ok((input, result))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _parse_split_fn<E, F: Fn(&[u8]) -> IResult<&[u8], E>>(
|
||||||
|
input: &[u8],
|
||||||
|
fore: usize,
|
||||||
|
after: usize,
|
||||||
|
f: F,
|
||||||
|
) -> IResult<&[u8], E> {
|
||||||
|
let (input, _) = take(fore)(input)?;
|
||||||
|
let (input, result) = (f)(input)?;
|
||||||
|
let (input, _) = take(after)(input)?;
|
||||||
|
Ok((input, result))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _parse_block_fn(order: Order) -> impl FnMut(&[u8]) -> IResult<&[u8], ParsedBlock> {
|
||||||
|
move |input| Self::_parse_split_fn(input, 0, 0, |input| Self::_parse_block(input, order))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _parse_block(input: &[u8], order: Order) -> IResult<&[u8], ParsedBlock> {
|
||||||
|
let (input, _) = take(8usize)(input)?;
|
||||||
|
let (input, hlen1) = Self::_parse_u32(input, order)?;
|
||||||
|
let (input, block_info) = Self::_parse_split_fn(input, 0, 8, |input| {
|
||||||
|
let (input, p) = take(hlen1)(input)?;
|
||||||
|
|
||||||
|
let p: BlockJsonInfo = serde_json::from_slice(p).unwrap();
|
||||||
|
Ok((input, p))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let (input, _) = take(8usize)(input)?; // skip 8 bytes
|
||||||
|
let (input, hlen2) = Self::_parse_u32(input, order)?;
|
||||||
|
let dimension_size = block_info.dimension_size.clone();
|
||||||
|
let size = dimension_size.iter().fold(1, |acc, x| acc * x) as usize;
|
||||||
|
let value_type = block_info.value_type.clone();
|
||||||
|
let value_type = Self::_parse_type(&value_type);
|
||||||
|
|
||||||
|
let (input, data) = Self::_parse_matrix(
|
||||||
|
input,
|
||||||
|
value_type,
|
||||||
|
order,
|
||||||
|
size,
|
||||||
|
block_info.value_offset,
|
||||||
|
block_info.value_scale,
|
||||||
|
block_info.fill_value,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
ParsedBlock {
|
||||||
|
info: block_info,
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _parse_i32(input: &[u8], order: Order) -> IResult<&[u8], i32> {
|
||||||
|
let (input, hlen) = take(4usize)(input)?;
|
||||||
|
let hlen = match order {
|
||||||
|
Order::BigEndian => BigEndian::read_i32(hlen),
|
||||||
|
_ => LittleEndian::read_i32(hlen),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((input, hlen))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _parse_u32(input: &[u8], order: Order) -> IResult<&[u8], u32> {
|
||||||
|
let (input, hlen) = take(4usize)(input)?;
|
||||||
|
let hlen = match order {
|
||||||
|
Order::BigEndian => BigEndian::read_u32(hlen),
|
||||||
|
_ => LittleEndian::read_u32(hlen),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((input, hlen))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _parse_i64(input: &[u8], order: Order) -> IResult<&[u8], i64> {
|
||||||
|
let (input, hlen) = take(8usize)(input)?;
|
||||||
|
let hlen = match order {
|
||||||
|
Order::BigEndian => BigEndian::read_i64(hlen),
|
||||||
|
_ => LittleEndian::read_i64(hlen),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((input, hlen))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _parse_u64(input: &[u8], order: Order) -> IResult<&[u8], u64> {
|
||||||
|
let (input, hlen) = take(8usize)(input)?;
|
||||||
|
let hlen = match order {
|
||||||
|
Order::BigEndian => BigEndian::read_u64(hlen),
|
||||||
|
_ => LittleEndian::read_u64(hlen),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((input, hlen))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _parse_type<'a>(type_str: &'a str) -> ValueTypes {
|
||||||
|
match type_str {
|
||||||
|
"b" => ValueTypes::I8,
|
||||||
|
"B" => ValueTypes::U8,
|
||||||
|
"i" => ValueTypes::I32,
|
||||||
|
"I" => ValueTypes::U32,
|
||||||
|
"f" => ValueTypes::F32,
|
||||||
|
"d" => ValueTypes::F64,
|
||||||
|
"h" => ValueTypes::I16,
|
||||||
|
_ => panic!("Invalid type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _parse_matrix(
|
||||||
|
input: &[u8],
|
||||||
|
type_: ValueTypes,
|
||||||
|
order: Order,
|
||||||
|
len: usize,
|
||||||
|
offset: f32,
|
||||||
|
scale: f32,
|
||||||
|
fill_value: f64,
|
||||||
|
) -> IResult<&[u8], ValueResult> {
|
||||||
|
match_in_macro!(
|
||||||
|
type_,
|
||||||
|
len,
|
||||||
|
input,
|
||||||
|
offset,
|
||||||
|
scale,
|
||||||
|
fill_value,
|
||||||
|
(
|
||||||
|
ValueTypes::I64,
|
||||||
|
i64,
|
||||||
|
f64,
|
||||||
|
ValueResult::I64,
|
||||||
|
ValueResult::F64
|
||||||
|
),
|
||||||
|
(
|
||||||
|
ValueTypes::F64,
|
||||||
|
f64,
|
||||||
|
f64,
|
||||||
|
ValueResult::F64,
|
||||||
|
ValueResult::F64
|
||||||
|
),
|
||||||
|
(
|
||||||
|
ValueTypes::U64,
|
||||||
|
u64,
|
||||||
|
f64,
|
||||||
|
ValueResult::U64,
|
||||||
|
ValueResult::F64
|
||||||
|
),
|
||||||
|
(
|
||||||
|
ValueTypes::I32,
|
||||||
|
i32,
|
||||||
|
f32,
|
||||||
|
ValueResult::I32,
|
||||||
|
ValueResult::F32
|
||||||
|
),
|
||||||
|
(
|
||||||
|
ValueTypes::F32,
|
||||||
|
f32,
|
||||||
|
f32,
|
||||||
|
ValueResult::F32,
|
||||||
|
ValueResult::F32
|
||||||
|
),
|
||||||
|
(
|
||||||
|
ValueTypes::U32,
|
||||||
|
u32,
|
||||||
|
f32,
|
||||||
|
ValueResult::U32,
|
||||||
|
ValueResult::F32
|
||||||
|
),
|
||||||
|
(
|
||||||
|
ValueTypes::I16,
|
||||||
|
i16,
|
||||||
|
f32,
|
||||||
|
ValueResult::I16,
|
||||||
|
ValueResult::F32
|
||||||
|
),
|
||||||
|
(ValueTypes::I8, i8, f32, ValueResult::I8, ValueResult::F32),
|
||||||
|
(ValueTypes::U8, u8, f32, ValueResult::U8, ValueResult::F32)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct RecordInfo {
|
||||||
|
file_time: String,
|
||||||
|
block_num: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct BlockJsonInfo {
|
||||||
|
pub value_name: String,
|
||||||
|
pub value_des: String,
|
||||||
|
pub value_type: String,
|
||||||
|
pub dimension_size: Vec<u64>,
|
||||||
|
pub dimension: usize,
|
||||||
|
pub dimension_des: Vec<String>,
|
||||||
|
pub dimension_start: Vec<f64>,
|
||||||
|
pub dimension_end: Vec<f64>,
|
||||||
|
pub dimension_values: Vec<Vec<f64>>,
|
||||||
|
pub fill_value: f64,
|
||||||
|
pub value_scale: f32,
|
||||||
|
pub value_offset: f32,
|
||||||
|
pub radar_alt: Option<f64>,
|
||||||
|
pub radar_lat: Option<f64>,
|
||||||
|
pub radar_lon: Option<f64>,
|
||||||
|
pub radar_name: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ParsedBlock {
|
||||||
|
pub info: BlockJsonInfo,
|
||||||
|
pub data: ValueResult,
|
||||||
|
}
|
||||||
33
gi/src/errors.rs
Normal file
33
gi/src/errors.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub type Result<T = ()> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("Invalid Snippet {0}")]
|
||||||
|
InvalidSnippet(String),
|
||||||
|
|
||||||
|
#[error("Invalid Shader {0}")]
|
||||||
|
InvalidShader(String),
|
||||||
|
|
||||||
|
#[error("Invalid Program {0}")]
|
||||||
|
InvalidProgram(String),
|
||||||
|
|
||||||
|
#[error("Invalid Data")]
|
||||||
|
InvalidData {
|
||||||
|
#[from]
|
||||||
|
source: crate::data_loader::error::ETWSError,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Invalid CoordType")]
|
||||||
|
InvalidDataType,
|
||||||
|
|
||||||
|
#[error("Init Error, cause of {0}")]
|
||||||
|
InitError(anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Invalid Font {0}")]
|
||||||
|
FontError(String),
|
||||||
|
|
||||||
|
#[error("Invalid Setting {0}")]
|
||||||
|
SettingError(String),
|
||||||
|
}
|
||||||
211
gi/src/font_manager/mod.rs
Normal file
211
gi/src/font_manager/mod.rs
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
use crate::errors::*;
|
||||||
|
use dirs::font_dir;
|
||||||
|
use freetype::face::LoadFlag;
|
||||||
|
use freetype::{Face, GlyphMetrics, Library};
|
||||||
|
|
||||||
|
use image::GrayImage;
|
||||||
|
use image::ImageBuffer;
|
||||||
|
use log::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::{env, fs};
|
||||||
|
pub struct FontManager {
|
||||||
|
library: Library,
|
||||||
|
fonts: HashMap<String, Font>,
|
||||||
|
}
|
||||||
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
|
impl FontManager {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
// let source = fk::source::SystemSource::new();
|
||||||
|
|
||||||
|
let library = Library::init().map_err(|e| Error::InitError(e.into()))?;
|
||||||
|
|
||||||
|
let root_path = font_dir().unwrap();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
library,
|
||||||
|
fonts: HashMap::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_font_or_insert(&mut self, path: &str) -> Option<&Font> {
|
||||||
|
if self.fonts.contains_key(path) {
|
||||||
|
return self.fonts.get(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let font = self.library.new_face(path, 0).ok()?;
|
||||||
|
let font = Font::new(font).ok()?;
|
||||||
|
self.fonts.insert(path.to_string(), font);
|
||||||
|
|
||||||
|
self.fonts.get(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Font {
|
||||||
|
font: Face,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CharImg {
|
||||||
|
img: Vec<u8>,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CharImg {
|
||||||
|
pub fn to_image(&self) -> GrayImage {
|
||||||
|
ImageBuffer::from_raw(self.width as u32, self.height as u32, self.img.clone())
|
||||||
|
.map(|img| img.into())
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pixels(&self) -> &[u8] {
|
||||||
|
&self.img
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn width(&self) -> usize {
|
||||||
|
self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> usize {
|
||||||
|
self.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Font {
|
||||||
|
fn new(font: Face) -> Result<Self> {
|
||||||
|
Ok(Self { font })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_char(&self, c: char, size: isize) -> Result<CharImg> {
|
||||||
|
self.font.set_char_size(0, size * 64, 0, 96).unwrap();
|
||||||
|
|
||||||
|
if let Err(_) = self.font.load_char(c as usize, LoadFlag::RENDER) {
|
||||||
|
self.font.load_char(0, LoadFlag::RENDER).unwrap();
|
||||||
|
}
|
||||||
|
let glyph = self.font.glyph();
|
||||||
|
let bitmap = glyph.bitmap();
|
||||||
|
if bitmap.width() == 0 || bitmap.rows() == 0 {
|
||||||
|
return Err(Error::FontError("bitmap is empty".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(CharImg {
|
||||||
|
img: bitmap.buffer().to_vec(),
|
||||||
|
width: bitmap.width() as usize,
|
||||||
|
height: bitmap.rows() as usize,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_char_size(&self, size: isize) {
|
||||||
|
self.font.set_char_size(0, size * 64, 0, 96).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_char(&self, c: char) {
|
||||||
|
self.font.load_char(c as usize, LoadFlag::RENDER).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn glyph(&self) -> &freetype::GlyphSlot {
|
||||||
|
self.font.glyph()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_advance(&self) -> (f32, f32) {
|
||||||
|
let advance = self.font.glyph().advance();
|
||||||
|
(advance.x as f32 / 64.0, advance.y as f32 / 64.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_kerning(&self, prev: char, c: char) -> (f32, f32) {
|
||||||
|
let prev = self.font.get_char_index(prev as usize).unwrap();
|
||||||
|
let curr = self.font.get_char_index(c as usize).unwrap();
|
||||||
|
let kerning = self
|
||||||
|
.font
|
||||||
|
.get_kerning(prev, curr, freetype::face::KerningMode::KerningDefault)
|
||||||
|
.unwrap();
|
||||||
|
(kerning.x as f32 / 64.0, kerning.y as f32 / 64.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_line_height(&self) -> i16 {
|
||||||
|
self.font.height()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_metrics(&self, c: char) -> GlyphMetrics {
|
||||||
|
self.font.glyph().metrics()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum FontSize {
|
||||||
|
Absolute(f32),
|
||||||
|
WindowScale(f32),
|
||||||
|
DistanceScale(f32),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FontStyle {
|
||||||
|
pub postscript_name: String,
|
||||||
|
pub size: FontSize,
|
||||||
|
pub color: [f32; 4],
|
||||||
|
pub style: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FontStyle {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
postscript_name: "resources/Roboto-Regular.ttf".to_string(),
|
||||||
|
size: FontSize::Absolute(12.0),
|
||||||
|
color: [1.0, 1.0, 1.0, 1.0],
|
||||||
|
style: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct FontStyleBuilder {
|
||||||
|
style: FontStyle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontStyleBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> FontStyle {
|
||||||
|
self.style
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn postscript_name(mut self, name: &str) -> Self {
|
||||||
|
self.style.postscript_name = name.to_string();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(mut self, size: FontSize) -> Self {
|
||||||
|
self.style.size = size;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn color(mut self, color: [f32; 4]) -> Self {
|
||||||
|
self.style.color = color;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontStyle {
|
||||||
|
pub fn buider() -> FontStyleBuilder {
|
||||||
|
FontStyleBuilder::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
use super::{FontManager, FontStyle};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let mut fon = FontManager::new().unwrap();
|
||||||
|
let font =
|
||||||
|
fon.get_font_or_insert("/Users/tsuki/projects/radar-gi/resources/Dokdo-Regular.ttf");
|
||||||
|
|
||||||
|
if let Some(font) = font {
|
||||||
|
font.get_char('g', 64).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
396
gi/src/graphics/collections/agg_fast_path.rs
Normal file
396
gi/src/graphics/collections/agg_fast_path.rs
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use glow::{HasContext, NativeBuffer, NativeVertexArray};
|
||||||
|
|
||||||
|
use crate::components::{Program, Shader};
|
||||||
|
use crate::errors::*;
|
||||||
|
use crate::graphics::ty::Ty;
|
||||||
|
use crate::graphics::{AttaWithBuffer, Graphics};
|
||||||
|
use crate::shaders::agg_path::{AggPathFragment, AggPathVertex};
|
||||||
|
|
||||||
|
use super::Colletion;
|
||||||
|
|
||||||
|
pub struct AggFastPath {
|
||||||
|
program: Program,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AggFastPathConfig {
|
||||||
|
pub color: [f32; 4],
|
||||||
|
pub linewidth: f32,
|
||||||
|
pub antialias: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AggFastPath {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
let vertex = Shader::new(glow::VERTEX_SHADER, AggPathVertex::new())?;
|
||||||
|
|
||||||
|
let fragment = Shader::new(glow::FRAGMENT_SHADER, AggPathFragment::new())?;
|
||||||
|
|
||||||
|
let program = Program::new(vertex, fragment, None, "330 core");
|
||||||
|
|
||||||
|
Ok(Self { program })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_viewport(&mut self, gl: &glow::Context, viewport: [f32; 2]) {
|
||||||
|
let loc = self.program.get_uniform_location(gl, "viewport");
|
||||||
|
unsafe {
|
||||||
|
gl.uniform_2_f32(loc.as_ref(), viewport[0], viewport[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_linecolor(&mut self, gl: &glow::Context, color: [f32; 4]) {
|
||||||
|
let loc = self.program.get_uniform_location(gl, "color");
|
||||||
|
unsafe {
|
||||||
|
gl.uniform_4_f32(loc.as_ref(), color[0], color[1], color[2], color[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_linewidth(&mut self, gl: &glow::Context, linewidth: f32) {
|
||||||
|
let loc = self.program.get_uniform_location(gl, "thickness");
|
||||||
|
unsafe {
|
||||||
|
gl.uniform_1_f32(loc.as_ref(), linewidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_anatialias(&mut self, gl: &glow::Context, antialias: f32) {
|
||||||
|
let loc = self.program.get_uniform_location(gl, "antialias");
|
||||||
|
unsafe {
|
||||||
|
gl.uniform_1_f32(loc.as_ref(), antialias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_config(&mut self, gl: &glow::Context, config: &AggFastPathConfig) {
|
||||||
|
self.set_linecolor(gl, config.color);
|
||||||
|
self.set_linewidth(gl, config.linewidth);
|
||||||
|
self.set_anatialias(gl, config.antialias);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn program(&mut self) -> &mut Program {
|
||||||
|
&mut self.program
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn program_ref(&self) -> &Program {
|
||||||
|
&self.program
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Graphics for AggFastPath {
|
||||||
|
const id: &'static str = "AggPath";
|
||||||
|
type Config = AggFastPathConfig;
|
||||||
|
fn compile(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
|
self.program.compile(gl)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&self, gl: &glow::Context, count: i32) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
gl.draw_elements(glow::TRIANGLES, count, glow::UNSIGNED_INT, 0);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
|
self.program.destroy(gl);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn program_ref(&self) -> &Program {
|
||||||
|
&self.program
|
||||||
|
}
|
||||||
|
|
||||||
|
fn program_mut(&mut self) -> &mut Program {
|
||||||
|
&mut self.program
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_config(&mut self, gl: &glow::Context, config: &Self::Config) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mount(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
gl.use_program(self.program.native_program);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unmount(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
gl.use_program(None);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttaWithBuffer for AggFastPath {
|
||||||
|
type Data = Vec<Path>;
|
||||||
|
|
||||||
|
fn bake<'a, 'gl: 'a>(
|
||||||
|
&'a self,
|
||||||
|
gl: &'gl glow::Context,
|
||||||
|
data: &Self::Data,
|
||||||
|
config: &<Self as Graphics>::Config,
|
||||||
|
) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)> {
|
||||||
|
let points = data.iter().map(|v| &v.points).flatten().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut lens = Vec::with_capacity(data.len());
|
||||||
|
let mut sum = 0;
|
||||||
|
for num in data.iter().map(|v| v.len()) {
|
||||||
|
lens.push(sum);
|
||||||
|
sum += num;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut vbo = Vec::with_capacity(points.len() * 10);
|
||||||
|
for p in points {
|
||||||
|
vbo.extend_from_slice(&[
|
||||||
|
p.prev[0], p.prev[1], p.prev[2], p.curr[0], p.curr[1], p.curr[2], p.next[0],
|
||||||
|
p.next[1], p.next[2], p.id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
let mut ebo = vec![];
|
||||||
|
for (idx, e) in data.iter().map(|v| &v.ebo).enumerate() {
|
||||||
|
let len = lens[idx] as u32;
|
||||||
|
for e in e.iter() {
|
||||||
|
ebo.extend_from_slice(&[e[0] + len, e[1] + len, e[2] + len]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let len = ebo.len() as i32;
|
||||||
|
|
||||||
|
Ok((vbo, Some(ebo), len))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&self, gl: &glow::Context) -> (NativeVertexArray, NativeBuffer, Option<NativeBuffer>) {
|
||||||
|
unsafe {
|
||||||
|
let vao = gl.create_vertex_array().unwrap();
|
||||||
|
gl.bind_vertex_array(Some(vao));
|
||||||
|
|
||||||
|
let vbo = gl.create_buffer().unwrap();
|
||||||
|
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
|
||||||
|
|
||||||
|
gl.enable_vertex_attrib_array(0);
|
||||||
|
gl.vertex_attrib_pointer_f32(0, 3, glow::FLOAT, false, 40, 0);
|
||||||
|
|
||||||
|
gl.enable_vertex_attrib_array(1);
|
||||||
|
gl.vertex_attrib_pointer_f32(1, 3, glow::FLOAT, false, 40, 12);
|
||||||
|
|
||||||
|
gl.enable_vertex_attrib_array(2);
|
||||||
|
gl.vertex_attrib_pointer_f32(2, 3, glow::FLOAT, false, 40, 24);
|
||||||
|
|
||||||
|
gl.enable_vertex_attrib_array(3);
|
||||||
|
gl.vertex_attrib_pointer_f32(3, 1, glow::FLOAT, false, 40, 36);
|
||||||
|
let ebo = gl.create_buffer().unwrap();
|
||||||
|
gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(ebo));
|
||||||
|
gl.bind_vertex_array(None);
|
||||||
|
|
||||||
|
gl.bind_buffer(glow::ARRAY_BUFFER, None);
|
||||||
|
(vao, vbo, Some(ebo))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||||
|
pub struct Point {
|
||||||
|
prev: [f32; 3],
|
||||||
|
curr: [f32; 3],
|
||||||
|
next: [f32; 3],
|
||||||
|
id: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Point {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Point {{ prev: {:?}, curr: {:?}, next: {:?}, id: {} }}",
|
||||||
|
self.prev, self.curr, self.next, self.id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Point {
|
||||||
|
fn f32_slice(&self) -> &[f32] {
|
||||||
|
unsafe { std::slice::from_raw_parts(self as *const Self as *const f32, 13) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ty for Point {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Path {
|
||||||
|
points: Vec<Point>,
|
||||||
|
ebo: Vec<[u32; 3]>,
|
||||||
|
|
||||||
|
is_closed: bool,
|
||||||
|
is_empty: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Path {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
points: Vec::with_capacity(500),
|
||||||
|
ebo: Vec::with_capacity(500),
|
||||||
|
|
||||||
|
is_closed: false,
|
||||||
|
is_empty: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PathBuilder {
|
||||||
|
is_closed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PathBuilder {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { is_closed: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PathBuilder {
|
||||||
|
pub fn is_closed(&mut self, is_closed: bool) -> &mut Self {
|
||||||
|
self.is_closed = is_closed;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(&self) -> Path {
|
||||||
|
Path {
|
||||||
|
points: Vec::with_capacity(500),
|
||||||
|
is_closed: self.is_closed,
|
||||||
|
ebo: Vec::with_capacity(500),
|
||||||
|
|
||||||
|
is_empty: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Path {
|
||||||
|
pub fn new(is_closed: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
points: Vec::with_capacity(500),
|
||||||
|
ebo: Vec::with_capacity(500),
|
||||||
|
is_closed,
|
||||||
|
is_empty: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn builder() -> PathBuilder {
|
||||||
|
PathBuilder::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, point: [f32; 3]) {
|
||||||
|
if self.is_empty {
|
||||||
|
self.points.push(Point {
|
||||||
|
prev: point,
|
||||||
|
curr: point,
|
||||||
|
next: point,
|
||||||
|
id: 1.0,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.points.push(Point {
|
||||||
|
prev: point,
|
||||||
|
curr: point,
|
||||||
|
next: point,
|
||||||
|
id: -1.0,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.is_empty = false;
|
||||||
|
} else {
|
||||||
|
let len = self.points.len();
|
||||||
|
let prev = self.points[len - 1].curr;
|
||||||
|
let curr = point;
|
||||||
|
let next = point;
|
||||||
|
self.points.push(Point {
|
||||||
|
prev,
|
||||||
|
curr,
|
||||||
|
next,
|
||||||
|
id: 1.0,
|
||||||
|
});
|
||||||
|
self.points.push(Point {
|
||||||
|
prev,
|
||||||
|
curr,
|
||||||
|
next,
|
||||||
|
id: -1.0,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.points[len - 1].next = curr;
|
||||||
|
self.points[len - 2].next = curr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(&mut self) {
|
||||||
|
if self.is_closed {
|
||||||
|
let len = self.points.len();
|
||||||
|
let curr = self.points.first().unwrap().curr;
|
||||||
|
let prev = self.points[len - 1].curr;
|
||||||
|
let next = self.points[1].next;
|
||||||
|
self.points.push(Point {
|
||||||
|
prev,
|
||||||
|
curr,
|
||||||
|
next,
|
||||||
|
id: 1.0,
|
||||||
|
});
|
||||||
|
self.points.push(Point {
|
||||||
|
prev,
|
||||||
|
curr,
|
||||||
|
next,
|
||||||
|
id: -1.0,
|
||||||
|
});
|
||||||
|
self.points[len - 1].next = curr;
|
||||||
|
self.points[len - 2].next = curr;
|
||||||
|
}
|
||||||
|
for s in 0..(self.points.len() / 2) - 1 {
|
||||||
|
let s = s as u32;
|
||||||
|
self.ebo.push([s * 2, s * 2 + 1, (s + 1) * 2 + 1]);
|
||||||
|
self.ebo.push([s * 2, (s + 1) * 2 + 1, (s + 1) * 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.points.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Path {
|
||||||
|
type Target = [u8];
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
use bytemuck::cast_slice;
|
||||||
|
cast_slice(&self.points)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Path {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
use bytemuck::cast_slice_mut;
|
||||||
|
cast_slice_mut(&mut self.points)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_path() {
|
||||||
|
let mut path = Path::builder().is_closed(false).build();
|
||||||
|
|
||||||
|
path.push([9.0, 9.0, 9.0]);
|
||||||
|
|
||||||
|
path.push([9.0, 19.0, 9.0]);
|
||||||
|
|
||||||
|
path.finish();
|
||||||
|
|
||||||
|
println!("{:?}, len: {}", path, path.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_agg() {
|
||||||
|
// let viewport = Trackball::new().unwrap();
|
||||||
|
// let trans = Polar::new().unwrap();
|
||||||
|
// let position = Position::new().unwrap();
|
||||||
|
|
||||||
|
// let transform = viewport.chain(trans.chain(position));
|
||||||
|
|
||||||
|
// let viewport = &Viewport::new().unwrap();
|
||||||
|
|
||||||
|
// let agg_path = AggFastPath::new(&transform, &viewport).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
7
gi/src/graphics/collections/mod.rs
Normal file
7
gi/src/graphics/collections/mod.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
pub mod agg_fast_path;
|
||||||
|
|
||||||
|
pub trait Colletion {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn append(&mut self, item: Self::Item);
|
||||||
|
}
|
||||||
125
gi/src/graphics/colormap/linear.rs
Normal file
125
gi/src/graphics/colormap/linear.rs
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
use super::ColorMap;
|
||||||
|
use crate::components::Program;
|
||||||
|
use crate::errors::*;
|
||||||
|
use crate::shaders::colormap::ColorMap as CmapPiece;
|
||||||
|
use glow::HasContext;
|
||||||
|
|
||||||
|
pub struct LinearColormap {
|
||||||
|
colors: Vec<[u8; 4]>,
|
||||||
|
|
||||||
|
color_changed: bool,
|
||||||
|
texture: Option<glow::NativeTexture>,
|
||||||
|
|
||||||
|
pub unvalid: f32,
|
||||||
|
pub min: f32,
|
||||||
|
pub max: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LinearColormap {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
colors: vec![],
|
||||||
|
texture: None,
|
||||||
|
color_changed: true,
|
||||||
|
min: 0.0,
|
||||||
|
max: 1.0,
|
||||||
|
unvalid: 0.0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_unvalid_value(&mut self, value: f32) {
|
||||||
|
self.unvalid = value;
|
||||||
|
self.color_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_colors(&mut self, colors: Vec<[u8; 4]>) {
|
||||||
|
self.colors = colors;
|
||||||
|
self.color_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_range(&mut self, min: f32, max: f32) {
|
||||||
|
self.min = min;
|
||||||
|
self.max = max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColorMap for LinearColormap {
|
||||||
|
fn attach_with_program(
|
||||||
|
&mut self,
|
||||||
|
gl: &glow::Context,
|
||||||
|
program: &crate::components::Program,
|
||||||
|
) -> crate::errors::Result<()> {
|
||||||
|
use bytemuck::cast_slice;
|
||||||
|
unsafe {
|
||||||
|
if self.color_changed {
|
||||||
|
let texture = gl.create_texture().unwrap();
|
||||||
|
gl.bind_texture(glow::TEXTURE_1D, Some(texture));
|
||||||
|
gl.tex_image_1d(
|
||||||
|
glow::TEXTURE_1D,
|
||||||
|
0,
|
||||||
|
glow::RGBA as i32,
|
||||||
|
self.colors.len() as i32,
|
||||||
|
0,
|
||||||
|
glow::RGBA,
|
||||||
|
glow::UNSIGNED_BYTE,
|
||||||
|
Some(cast_slice(&self.colors)),
|
||||||
|
);
|
||||||
|
|
||||||
|
gl.tex_parameter_i32(
|
||||||
|
glow::TEXTURE_1D,
|
||||||
|
glow::TEXTURE_MIN_FILTER,
|
||||||
|
glow::NEAREST as i32,
|
||||||
|
);
|
||||||
|
gl.tex_parameter_i32(
|
||||||
|
glow::TEXTURE_1D,
|
||||||
|
glow::TEXTURE_MAG_FILTER,
|
||||||
|
glow::NEAREST as i32,
|
||||||
|
);
|
||||||
|
|
||||||
|
let location = program.get_uniform_location(gl, "colormap_conf");
|
||||||
|
gl.uniform_4_f32(
|
||||||
|
location.as_ref(),
|
||||||
|
self.min,
|
||||||
|
self.max,
|
||||||
|
self.colors.len() as f32,
|
||||||
|
self.unvalid,
|
||||||
|
);
|
||||||
|
self.texture = Some(texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(&mut self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
if let Some(texture) = self.texture {
|
||||||
|
gl.delete_texture(texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind_texture(&self, gl: &glow::Context, program: &Program) -> crate::errors::Result<()> {
|
||||||
|
unsafe {
|
||||||
|
gl.active_texture(glow::TEXTURE0);
|
||||||
|
gl.bind_texture(glow::TEXTURE_1D, self.texture);
|
||||||
|
let location = program.get_uniform_location(gl, "colormap");
|
||||||
|
gl.uniform_1_i32(location.as_ref(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
#[test]
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
use super::LinearColormap;
|
||||||
|
|
||||||
|
let mut colormap = LinearColormap::new().unwrap();
|
||||||
|
|
||||||
|
// colormap.set_colors(vec![[0.0, 0.0, 0.0, 1.0], [1.0, 1.0, 1.0, 1.0]]);
|
||||||
|
colormap.set_range(0.0, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
gi/src/graphics/colormap/mod.rs
Normal file
15
gi/src/graphics/colormap/mod.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
use crate::components::Program;
|
||||||
|
|
||||||
|
pub mod linear;
|
||||||
|
|
||||||
|
pub trait ColorMap {
|
||||||
|
fn attach_with_program(
|
||||||
|
&mut self,
|
||||||
|
gl: &glow::Context,
|
||||||
|
program: &crate::components::Program,
|
||||||
|
) -> crate::errors::Result<()>;
|
||||||
|
|
||||||
|
fn bind_texture(&self, gl: &glow::Context, program: &Program) -> crate::errors::Result<()>;
|
||||||
|
|
||||||
|
fn destroy(&mut self, gl: &glow::Context);
|
||||||
|
}
|
||||||
1
gi/src/graphics/colormesh.rs
Normal file
1
gi/src/graphics/colormesh.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub struct ColorMesh {}
|
||||||
740
gi/src/graphics/font/esdt/esdt.rs
Normal file
740
gi/src/graphics/font/esdt/esdt.rs
Normal file
@ -0,0 +1,740 @@
|
|||||||
|
//! Rust port of the ESDT ("Euclidean Subpixel Distance Transform") algorithm.
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! This algorithm was originally published as the [`@use-gpu/glyph`](https://www.npmjs.com/package/@use-gpu/glyph)
|
||||||
|
//! `npm` package, and was described in <https://acko.net/blog/subpixel-distance-transform/>.
|
||||||
|
|
||||||
|
use super::img::{Bitmap, Image2d, NDCursor, NDCursorExt as _, Unorm8};
|
||||||
|
|
||||||
|
// HACK(eddyb) only exists to allow toggling precision for testing purposes.
|
||||||
|
#[cfg(sdfer_use_f64_instead_of_f32)]
|
||||||
|
type f32 = f64;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct Params {
|
||||||
|
pub pad: usize,
|
||||||
|
pub radius: f32,
|
||||||
|
pub cutoff: f32,
|
||||||
|
pub solidify: bool,
|
||||||
|
pub preprocess: bool,
|
||||||
|
// FIXME(eddyb) implement.
|
||||||
|
// pub postprocess: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Params {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
pad: 4,
|
||||||
|
radius: 3.0,
|
||||||
|
cutoff: 0.25,
|
||||||
|
solidify: true,
|
||||||
|
preprocess: false,
|
||||||
|
// FIXME(eddyb) implement.
|
||||||
|
// postprocess: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opaque `struct` allowing buffer reuse between SDF computations, instead of
|
||||||
|
/// reallocating all the buffers every time.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ReusableBuffers(ReusableBuffers2d, ReusableBuffers1d);
|
||||||
|
|
||||||
|
// Convert grayscale glyph to SDF
|
||||||
|
pub fn glyph_to_sdf(
|
||||||
|
glyph: &mut Image2d<Unorm8, impl AsMut<[Unorm8]> + AsRef<[Unorm8]>>,
|
||||||
|
params: Params,
|
||||||
|
reuse_bufs: Option<ReusableBuffers>,
|
||||||
|
) -> (Image2d<Unorm8>, ReusableBuffers) {
|
||||||
|
if params.solidify {
|
||||||
|
solidify_alpha(glyph.reborrow_mut());
|
||||||
|
}
|
||||||
|
glyph_to_esdt(glyph.reborrow_mut(), params, reuse_bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Solidify semi-transparent areas
|
||||||
|
fn solidify_alpha(mut glyph: Image2d<Unorm8, &mut [Unorm8]>) {
|
||||||
|
let (w, h) = (glyph.width(), glyph.height());
|
||||||
|
|
||||||
|
let mut mask: Image2d<u8> = Image2d::new(w, h);
|
||||||
|
|
||||||
|
let get_data = |x: isize, y: isize| {
|
||||||
|
if x >= 0 && (x as usize) < w && y >= 0 && (y as usize) < h {
|
||||||
|
glyph[(x as usize, y as usize)]
|
||||||
|
} else {
|
||||||
|
Unorm8::MIN
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut masked = 0;
|
||||||
|
|
||||||
|
// Mask pixels whose alpha matches their 4 adjacent neighbors (within 16 steps)
|
||||||
|
// and who don't have black or white neighbors.
|
||||||
|
for y in 0..(h as isize) {
|
||||||
|
for x in 0..(w as isize) {
|
||||||
|
let a = get_data(x, y);
|
||||||
|
// FIXME(eddyb) audit all comparisons with `254` and try removing them.
|
||||||
|
if a == Unorm8::MIN || a >= Unorm8::from_bits(254) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let l = get_data(x - 1, y);
|
||||||
|
let r = get_data(x + 1, y);
|
||||||
|
let t = get_data(x, y - 1);
|
||||||
|
let b = get_data(x, y + 1);
|
||||||
|
|
||||||
|
let (min, max) = [a, l, r, t, b]
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| (x, x))
|
||||||
|
.reduce(|(a_min, a_max), (b_min, b_max)| (a_min.min(b_min), a_max.max(b_max)))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let [a, min, max] = [a, min, max].map(Unorm8::to_bits);
|
||||||
|
|
||||||
|
// FIXME(eddyb) audit all comparisons with `254` and try removing them.
|
||||||
|
if (max - min) < 16 && min > 0 && max < 254 {
|
||||||
|
// NOTE(eddyb) `min > 0` guarantees all neighbors are in-bounds.
|
||||||
|
let (x, y) = (x as usize, y as usize);
|
||||||
|
|
||||||
|
// Spread to 4 neighbors with max
|
||||||
|
mask[(x - 1, y)] = mask[(x - 1, y)].max(a);
|
||||||
|
mask[(x, y - 1)] = mask[(x, y - 1)].max(a);
|
||||||
|
mask[(x, y)] = a;
|
||||||
|
mask[(x + 1, y)] = mask[(x + 1, y)].max(a);
|
||||||
|
mask[(x, y + 1)] = mask[(x, y + 1)].max(a);
|
||||||
|
masked += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if masked == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let get_mask = |x: isize, y: isize| {
|
||||||
|
if x >= 0 && (x as usize) < w && y >= 0 && (y as usize) < h {
|
||||||
|
mask[(x as usize, y as usize)]
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sample 3x3 area for alpha normalization factor
|
||||||
|
for y in 0..(h as isize) {
|
||||||
|
for x in 0..(w as isize) {
|
||||||
|
let a = &mut glyph[(x as usize, y as usize)];
|
||||||
|
// FIXME(eddyb) audit all comparisons with `254` and try removing them.
|
||||||
|
if *a == Unorm8::MIN || *a >= Unorm8::from_bits(254) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let c = get_mask(x, y);
|
||||||
|
|
||||||
|
let l = get_mask(x - 1, y);
|
||||||
|
let r = get_mask(x + 1, y);
|
||||||
|
let t = get_mask(x, y - 1);
|
||||||
|
let b = get_mask(x, y + 1);
|
||||||
|
|
||||||
|
let tl = get_mask(x - 1, y - 1);
|
||||||
|
let tr = get_mask(x + 1, y - 1);
|
||||||
|
let bl = get_mask(x - 1, y + 1);
|
||||||
|
let br = get_mask(x + 1, y + 1);
|
||||||
|
|
||||||
|
if let Some(m) = [c, l, r, t, b, tl, tr, bl, br]
|
||||||
|
.into_iter()
|
||||||
|
.find(|&x| x != 0)
|
||||||
|
{
|
||||||
|
*a = Unorm8::from_bits((a.to_bits() as f32 / m as f32 * 255.0) as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert grayscale or color glyph to SDF using subpixel distance transform
|
||||||
|
fn glyph_to_esdt(
|
||||||
|
mut glyph: Image2d<Unorm8, &mut [Unorm8]>,
|
||||||
|
params: Params,
|
||||||
|
reuse_bufs: Option<ReusableBuffers>,
|
||||||
|
) -> (Image2d<Unorm8>, ReusableBuffers) {
|
||||||
|
// FIXME(eddyb) use `Params` itself directly in more places.
|
||||||
|
let Params {
|
||||||
|
pad,
|
||||||
|
radius,
|
||||||
|
cutoff,
|
||||||
|
solidify: _,
|
||||||
|
preprocess,
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
let wp = glyph.width() + pad * 2;
|
||||||
|
let hp = glyph.height() + pad * 2;
|
||||||
|
|
||||||
|
let mut state = State::from_glyph(glyph.reborrow_mut(), params, reuse_bufs);
|
||||||
|
|
||||||
|
state.esdt_outer_and_inner(wp, hp);
|
||||||
|
|
||||||
|
// FIXME(eddyb) implement.
|
||||||
|
// if postprocess { state.relax_subpixel_offsets(glyph, pad); }
|
||||||
|
|
||||||
|
let mut sdf = Image2d::from_fn(wp, hp, |x, y| {
|
||||||
|
let i = y * wp + x;
|
||||||
|
let ReusableBuffers2d { xo, yo, xi, yi, .. } = &state.bufs_2d;
|
||||||
|
let outer = ((xo[i].powi(2) + yo[i].powi(2)).sqrt() - 0.5).max(0.0);
|
||||||
|
let inner = ((xi[i].powi(2) + yi[i].powi(2)).sqrt() - 0.5).max(0.0);
|
||||||
|
let d = if outer >= inner { outer } else { -inner };
|
||||||
|
Unorm8::encode(1.0 - (d / radius + cutoff))
|
||||||
|
});
|
||||||
|
|
||||||
|
if !preprocess {
|
||||||
|
paint_into_distance_field(&mut sdf, glyph.reborrow(), params);
|
||||||
|
}
|
||||||
|
|
||||||
|
(sdf, ReusableBuffers(state.bufs_2d, state.reuse_bufs_1d))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
fn is_black(x: f32) -> bool {
|
||||||
|
x == 0.0
|
||||||
|
}
|
||||||
|
fn is_white(x: f32) -> bool {
|
||||||
|
x == 1.0
|
||||||
|
}
|
||||||
|
fn is_solid(x: f32) -> bool {
|
||||||
|
x == 0.0 || x == 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paint original alpha channel into final SDF when gray
|
||||||
|
fn paint_into_distance_field(
|
||||||
|
sdf: &mut Image2d<Unorm8>,
|
||||||
|
glyph: Image2d<Unorm8, &[Unorm8]>,
|
||||||
|
params: Params,
|
||||||
|
) {
|
||||||
|
let Params {
|
||||||
|
pad,
|
||||||
|
radius,
|
||||||
|
cutoff,
|
||||||
|
..
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
for y in 0..glyph.height() {
|
||||||
|
for x in 0..glyph.width() {
|
||||||
|
let a = glyph[(x, y)].decode();
|
||||||
|
if !is_solid(a) {
|
||||||
|
let d = 0.5 - a;
|
||||||
|
sdf[(x + pad, y + pad)] = Unorm8::encode(1.0 - (d / radius + cutoff));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 2D buffers, which get reused (see also `ReusableBuffers` itself).
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ReusableBuffers2d {
|
||||||
|
// FIXME(eddyb) group `outer` with `{x,y}o`.
|
||||||
|
outer: Bitmap,
|
||||||
|
// FIXME(eddyb) group `inner` with `{x,y}i``.
|
||||||
|
inner: Bitmap,
|
||||||
|
|
||||||
|
xo: Vec<f32>,
|
||||||
|
yo: Vec<f32>,
|
||||||
|
xi: Vec<f32>,
|
||||||
|
yi: Vec<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
// FIXME(eddyb) do the grouping suggested in `ReusableBuffers2d`, to have
|
||||||
|
// `outer` and `inner` fields in here, to use instead of `ReusableBuffers2d`.
|
||||||
|
bufs_2d: ReusableBuffers2d,
|
||||||
|
reuse_bufs_1d: ReusableBuffers1d,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn from_glyph(
|
||||||
|
mut glyph: Image2d<Unorm8, &mut [Unorm8]>,
|
||||||
|
params: Params,
|
||||||
|
reuse_bufs: Option<ReusableBuffers>,
|
||||||
|
) -> Self {
|
||||||
|
let Params {
|
||||||
|
pad,
|
||||||
|
// FIXME(eddyb) should this still be taken as a separate `bool`?
|
||||||
|
preprocess: relax,
|
||||||
|
..
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
let wp = glyph.width() + pad * 2;
|
||||||
|
let hp = glyph.height() + pad * 2;
|
||||||
|
let np = wp * hp;
|
||||||
|
|
||||||
|
let ReusableBuffers(bufs_2d, reuse_bufs_1d) = reuse_bufs.unwrap_or_default();
|
||||||
|
let mut state = Self {
|
||||||
|
bufs_2d,
|
||||||
|
reuse_bufs_1d,
|
||||||
|
};
|
||||||
|
let ReusableBuffers2d {
|
||||||
|
outer,
|
||||||
|
inner,
|
||||||
|
xo,
|
||||||
|
yo,
|
||||||
|
xi,
|
||||||
|
yi,
|
||||||
|
} = &mut state.bufs_2d;
|
||||||
|
|
||||||
|
outer.resize_and_fill_with(wp, hp, true);
|
||||||
|
inner.resize_and_fill_with(wp, hp, false);
|
||||||
|
for buf2d in [&mut *xo, yo, xi, yi] {
|
||||||
|
buf2d.clear();
|
||||||
|
buf2d.resize(np, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for y in 0..glyph.height() {
|
||||||
|
for x in 0..glyph.width() {
|
||||||
|
let a = &mut glyph[(x, y)];
|
||||||
|
if *a == Unorm8::MIN {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(eddyb) audit all comparisons with `254` and try removing them,
|
||||||
|
// especially this step that modifies the `glyph` itself.
|
||||||
|
if *a >= Unorm8::from_bits(254) {
|
||||||
|
// Fix for bad rasterizer rounding
|
||||||
|
*a = Unorm8::MAX;
|
||||||
|
|
||||||
|
outer.at(x + pad, y + pad).set(false);
|
||||||
|
inner.at(x + pad, y + pad).set(true);
|
||||||
|
} else {
|
||||||
|
outer.at(x + pad, y + pad).set(false);
|
||||||
|
inner.at(x + pad, y + pad).set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Generate subpixel offsets for all border pixels
|
||||||
|
//
|
||||||
|
|
||||||
|
let get_data = |x: isize, y: isize| {
|
||||||
|
if x >= 0 && (x as usize) < glyph.width() && y >= 0 && (y as usize) < glyph.height() {
|
||||||
|
glyph[(x as usize, y as usize)].decode()
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make vector from pixel center to nearest boundary
|
||||||
|
for y in 0..(glyph.height() as isize) {
|
||||||
|
for x in 0..(glyph.width() as isize) {
|
||||||
|
let c = get_data(x, y);
|
||||||
|
// NOTE(eddyb) `j - 1` (X-) / `j - wp` (Y-) positive (`pad >= 1`).
|
||||||
|
let j = ((y as usize) + pad) * wp + (x as usize) + pad;
|
||||||
|
|
||||||
|
if !is_solid(c) {
|
||||||
|
let dc = c - 0.5;
|
||||||
|
|
||||||
|
// NOTE(eddyb) l(eft) r(ight) t(op) b(ottom)
|
||||||
|
let l = get_data(x - 1, y);
|
||||||
|
let r = get_data(x + 1, y);
|
||||||
|
let t = get_data(x, y - 1);
|
||||||
|
let b = get_data(x, y + 1);
|
||||||
|
|
||||||
|
let tl = get_data(x - 1, y - 1);
|
||||||
|
let tr = get_data(x + 1, y - 1);
|
||||||
|
let bl = get_data(x - 1, y + 1);
|
||||||
|
let br = get_data(x + 1, y + 1);
|
||||||
|
|
||||||
|
let ll = (tl + l * 2.0 + bl) / 4.0;
|
||||||
|
let rr = (tr + r * 2.0 + br) / 4.0;
|
||||||
|
let tt = (tl + t * 2.0 + tr) / 4.0;
|
||||||
|
let bb = (bl + b * 2.0 + br) / 4.0;
|
||||||
|
|
||||||
|
let (min, max) = [l, r, t, b, tl, tr, bl, br]
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| (x, x))
|
||||||
|
.reduce(|(a_min, a_max), (b_min, b_max)| {
|
||||||
|
(a_min.min(b_min), a_max.max(b_max))
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if min > 0.0 {
|
||||||
|
// Interior creases
|
||||||
|
inner.at(x as usize + pad, y as usize + pad).set(true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if max < 1.0 {
|
||||||
|
// Exterior creases
|
||||||
|
outer.at(x as usize + pad, y as usize + pad).set(true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut dx = rr - ll;
|
||||||
|
let mut dy = bb - tt;
|
||||||
|
let dl = 1.0 / (dx.powi(2) + dy.powi(2)).sqrt();
|
||||||
|
dx *= dl;
|
||||||
|
dy *= dl;
|
||||||
|
|
||||||
|
xo[j] = -dc * dx;
|
||||||
|
yo[j] = -dc * dy;
|
||||||
|
} else if is_white(c) {
|
||||||
|
// NOTE(eddyb) l(eft) r(ight) t(op) b(ottom)
|
||||||
|
let l = get_data(x - 1, y);
|
||||||
|
let r = get_data(x + 1, y);
|
||||||
|
let t = get_data(x, y - 1);
|
||||||
|
let b = get_data(x, y + 1);
|
||||||
|
|
||||||
|
if is_black(l) {
|
||||||
|
xo[j - 1] = 0.4999;
|
||||||
|
outer.at(x as usize + pad - 1, y as usize + pad).set(false);
|
||||||
|
inner.at(x as usize + pad - 1, y as usize + pad).set(false);
|
||||||
|
}
|
||||||
|
if is_black(r) {
|
||||||
|
xo[j + 1] = -0.4999;
|
||||||
|
outer.at(x as usize + pad + 1, y as usize + pad).set(false);
|
||||||
|
inner.at(x as usize + pad + 1, y as usize + pad).set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_black(t) {
|
||||||
|
yo[j - wp] = 0.4999;
|
||||||
|
outer.at(x as usize + pad, y as usize + pad - 1).set(false);
|
||||||
|
inner.at(x as usize + pad, y as usize + pad - 1).set(false);
|
||||||
|
}
|
||||||
|
if is_black(b) {
|
||||||
|
yo[j + wp] = -0.4999;
|
||||||
|
outer.at(x as usize + pad, y as usize + pad + 1).set(false);
|
||||||
|
inner.at(x as usize + pad, y as usize + pad + 1).set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blend neighboring offsets but preserve normal direction
|
||||||
|
// Uses xo as input, xi as output
|
||||||
|
// Improves quality slightly, but slows things down.
|
||||||
|
if relax {
|
||||||
|
let check_cross = |nx, ny, dc, dl, dr, dxl, dyl, dxr, dyr| {
|
||||||
|
((dxl * nx + dyl * ny) * (dc * dl) > 0.0)
|
||||||
|
&& ((dxr * nx + dyr * ny) * (dc * dr) > 0.0)
|
||||||
|
&& ((dxl * dxr + dyl * dyr) * (dl * dr) > 0.0)
|
||||||
|
};
|
||||||
|
|
||||||
|
for y in 0..(glyph.height() as isize) {
|
||||||
|
for x in 0..(glyph.width() as isize) {
|
||||||
|
// NOTE(eddyb) `j - 1` (X-) / `j - wp` (Y-) positive (`pad >= 1`).
|
||||||
|
let j = ((y as usize) + pad) * wp + (x as usize) + pad;
|
||||||
|
|
||||||
|
let nx = xo[j];
|
||||||
|
let ny = yo[j];
|
||||||
|
if nx == 0.0 && ny == 0.0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(eddyb) c(enter) l(eft) r(ight) t(op) b(ottom)
|
||||||
|
let c = get_data(x, y);
|
||||||
|
let l = get_data(x - 1, y);
|
||||||
|
let r = get_data(x + 1, y);
|
||||||
|
let t = get_data(x, y - 1);
|
||||||
|
let b = get_data(x, y + 1);
|
||||||
|
|
||||||
|
let dxl = xo[j - 1];
|
||||||
|
let dxr = xo[j + 1];
|
||||||
|
let dxt = xo[j - wp];
|
||||||
|
let dxb = xo[j + wp];
|
||||||
|
|
||||||
|
let dyl = yo[j - 1];
|
||||||
|
let dyr = yo[j + 1];
|
||||||
|
let dyt = yo[j - wp];
|
||||||
|
let dyb = yo[j + wp];
|
||||||
|
|
||||||
|
let mut dx = nx;
|
||||||
|
let mut dy = ny;
|
||||||
|
let mut dw = 1.0;
|
||||||
|
|
||||||
|
let dc = c - 0.5;
|
||||||
|
let dl = l - 0.5;
|
||||||
|
let dr = r - 0.5;
|
||||||
|
let dt = t - 0.5;
|
||||||
|
let db = b - 0.5;
|
||||||
|
|
||||||
|
if !is_solid(l) && !is_solid(r) {
|
||||||
|
if check_cross(nx, ny, dc, dl, dr, dxl, dyl, dxr, dyr) {
|
||||||
|
dx += (dxl + dxr) / 2.0;
|
||||||
|
dy += (dyl + dyr) / 2.0;
|
||||||
|
dw += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_solid(t) && !is_solid(b) {
|
||||||
|
if check_cross(nx, ny, dc, dt, db, dxt, dyt, dxb, dyb) {
|
||||||
|
dx += (dxt + dxb) / 2.0;
|
||||||
|
dy += (dyt + dyb) / 2.0;
|
||||||
|
dw += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_solid(l) && !is_solid(t) {
|
||||||
|
if check_cross(nx, ny, dc, dl, dt, dxl, dyl, dxt, dyt) {
|
||||||
|
dx += (dxl + dxt - 1.0) / 2.0;
|
||||||
|
dy += (dyl + dyt - 1.0) / 2.0;
|
||||||
|
dw += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_solid(r) && !is_solid(t) {
|
||||||
|
if check_cross(nx, ny, dc, dr, dt, dxr, dyr, dxt, dyt) {
|
||||||
|
dx += (dxr + dxt + 1.0) / 2.0;
|
||||||
|
dy += (dyr + dyt - 1.0) / 2.0;
|
||||||
|
dw += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_solid(l) && !is_solid(b) {
|
||||||
|
if check_cross(nx, ny, dc, dl, db, dxl, dyl, dxb, dyb) {
|
||||||
|
dx += (dxl + dxb - 1.0) / 2.0;
|
||||||
|
dy += (dyl + dyb + 1.0) / 2.0;
|
||||||
|
dw += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_solid(r) && !is_solid(b) {
|
||||||
|
if check_cross(nx, ny, dc, dr, db, dxr, dyr, dxb, dyb) {
|
||||||
|
dx += (dxr + dxb + 1.0) / 2.0;
|
||||||
|
dy += (dyr + dyb + 1.0) / 2.0;
|
||||||
|
dw += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let nn = (nx * nx + ny * ny).sqrt();
|
||||||
|
let ll = (dx * nx + dy * ny) / nn;
|
||||||
|
|
||||||
|
dx = nx * ll / dw / nn;
|
||||||
|
dy = ny * ll / dw / nn;
|
||||||
|
|
||||||
|
xi[j] = dx;
|
||||||
|
yi[j] = dy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produce zero points for positive and negative DF, at +0.5 / -0.5.
|
||||||
|
// Splits xs into xo/xi
|
||||||
|
for y in 0..(glyph.height() as isize) {
|
||||||
|
for x in 0..(glyph.width() as isize) {
|
||||||
|
// NOTE(eddyb) `j - 1` (X-) / `j - wp` (Y-) positive (`pad >= 1`).
|
||||||
|
let j = ((y as usize) + pad) * wp + (x as usize) + pad;
|
||||||
|
|
||||||
|
// NOTE(eddyb) `if relax` above changed `xs`/`ys` in the original.
|
||||||
|
let (nx, ny) = if relax {
|
||||||
|
(xi[j], yi[j])
|
||||||
|
} else {
|
||||||
|
(xo[j], yo[j])
|
||||||
|
};
|
||||||
|
if nx == 0.0 && ny == 0.0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let nn = (nx.powi(2) + ny.powi(2)).sqrt();
|
||||||
|
|
||||||
|
let sx = if ((nx / nn).abs() - 0.5) > 0.0 {
|
||||||
|
nx.signum() as isize
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
let sy = if ((ny / nn).abs() - 0.5) > 0.0 {
|
||||||
|
ny.signum() as isize
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let c = get_data(x, y);
|
||||||
|
let d = get_data(x + sx, y + sy);
|
||||||
|
// FIXME(eddyb) is this inefficient? (was `Math.sign(d - c)`)
|
||||||
|
let s = (d - c).total_cmp(&0.0) as i8 as f32;
|
||||||
|
|
||||||
|
let dlo = (nn + 0.4999 * s) / nn;
|
||||||
|
let dli = (nn - 0.4999 * s) / nn;
|
||||||
|
|
||||||
|
xo[j] = nx * dlo;
|
||||||
|
yo[j] = ny * dlo;
|
||||||
|
xi[j] = nx * dli;
|
||||||
|
yi[j] = ny * dli;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn esdt_outer_and_inner(&mut self, w: usize, h: usize) {
|
||||||
|
{
|
||||||
|
let Self {
|
||||||
|
bufs_2d:
|
||||||
|
ReusableBuffers2d {
|
||||||
|
outer,
|
||||||
|
inner,
|
||||||
|
xo,
|
||||||
|
yo,
|
||||||
|
xi,
|
||||||
|
yi,
|
||||||
|
},
|
||||||
|
reuse_bufs_1d,
|
||||||
|
} = self;
|
||||||
|
esdt(outer, xo, yo, w, h, reuse_bufs_1d);
|
||||||
|
esdt(inner, xi, yi, w, h, reuse_bufs_1d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2D subpixel distance transform by unconed
|
||||||
|
// extended from Felzenszwalb & Huttenlocher https://cs.brown.edu/~pff/papers/dt-final.pdf
|
||||||
|
fn esdt(
|
||||||
|
mask: &mut Bitmap,
|
||||||
|
xs: &mut [f32],
|
||||||
|
ys: &mut [f32],
|
||||||
|
w: usize,
|
||||||
|
h: usize,
|
||||||
|
reuse_bufs_1d: &mut ReusableBuffers1d,
|
||||||
|
) {
|
||||||
|
reuse_bufs_1d.critical_minima.clear();
|
||||||
|
reuse_bufs_1d.critical_minima.reserve(w.max(h));
|
||||||
|
|
||||||
|
let mut xs = Image2d::from_storage(w, h, xs);
|
||||||
|
let mut ys = Image2d::from_storage(w, h, ys);
|
||||||
|
|
||||||
|
for x in 0..w {
|
||||||
|
let mut mask_xy_cursor = mask
|
||||||
|
.cursor_at(0, 0)
|
||||||
|
.zip(
|
||||||
|
// FIXME(eddyb) combine `xs` and `ys` into the same `Image2d`.
|
||||||
|
ys.cursor_at(0, 0).zip(xs.cursor_at(0, 0)),
|
||||||
|
)
|
||||||
|
.map_abs_and_rel(move |y| (x, y), |dy| (0, dy));
|
||||||
|
mask_xy_cursor.reset(0);
|
||||||
|
|
||||||
|
esdt1d(mask_xy_cursor, h, reuse_bufs_1d)
|
||||||
|
}
|
||||||
|
for y in 0..h {
|
||||||
|
let mut mask_xy_cursor = mask
|
||||||
|
.cursor_at(0, 0)
|
||||||
|
.zip(
|
||||||
|
// FIXME(eddyb) combine `xs` and `ys` into the same `Image2d`.
|
||||||
|
xs.cursor_at(0, 0).zip(ys.cursor_at(0, 0)),
|
||||||
|
)
|
||||||
|
.map_abs_and_rel(move |x| (x, y), |dx| (dx, 0));
|
||||||
|
mask_xy_cursor.reset(0);
|
||||||
|
|
||||||
|
esdt1d(mask_xy_cursor, w, reuse_bufs_1d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 1D buffers (for `esdt1d`), which get reused between calls.
|
||||||
|
//
|
||||||
|
// FIXME(eddyb) the name is outdated now that there's only one buffer.
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ReusableBuffers1d {
|
||||||
|
critical_minima: Vec<CriticalMinimum>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(eddyb) clean up the names after all the refactors.
|
||||||
|
struct CriticalMinimum {
|
||||||
|
// FIXME(eddyb) this is really just a position, since it's not used to
|
||||||
|
// index anything indirectly anymore, but rather indicates the original `q`,
|
||||||
|
// and is used to compare against it in the second iteration of `esdt1d`.
|
||||||
|
v: usize, // Array index
|
||||||
|
|
||||||
|
z: f32, // Voronoi threshold
|
||||||
|
f: f32, // Squared distance
|
||||||
|
b: f32, // Subpixel offset parallel
|
||||||
|
t: f32, // Subpixel offset perpendicular
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1D subpixel distance transform
|
||||||
|
fn esdt1d(
|
||||||
|
mut mask_xy_cursor: impl for<'a> NDCursor<
|
||||||
|
'a,
|
||||||
|
usize,
|
||||||
|
RefMut = (super::img::BitmapEntry<'a>, (&'a mut f32, &'a mut f32)),
|
||||||
|
>,
|
||||||
|
// FIXME(eddyb) provide this through the cursor, maybe?
|
||||||
|
length: usize,
|
||||||
|
reuse_bufs_1d: &mut ReusableBuffers1d,
|
||||||
|
) {
|
||||||
|
// FIXME(eddyb) this is a pretty misleading name.
|
||||||
|
const INF: f32 = 1e10;
|
||||||
|
|
||||||
|
let cm = &mut reuse_bufs_1d.critical_minima;
|
||||||
|
cm.clear();
|
||||||
|
|
||||||
|
{
|
||||||
|
let (mask, (&mut dx, &mut dy)) = mask_xy_cursor.get_mut();
|
||||||
|
cm.push(CriticalMinimum {
|
||||||
|
v: 0,
|
||||||
|
z: -INF,
|
||||||
|
f: if mask.get() { INF } else { dy.powi(2) },
|
||||||
|
|
||||||
|
b: dx,
|
||||||
|
t: dy,
|
||||||
|
});
|
||||||
|
mask_xy_cursor.advance(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan along array and build list of critical minima
|
||||||
|
for q in 1..length {
|
||||||
|
// Perpendicular
|
||||||
|
let (mask, (&mut dx, &mut dy)) = mask_xy_cursor.get_mut();
|
||||||
|
let fq = if mask.get() { INF } else { dy.powi(2) };
|
||||||
|
mask_xy_cursor.advance(1);
|
||||||
|
|
||||||
|
// Parallel
|
||||||
|
let qs = q as f32 + dx;
|
||||||
|
let q2 = qs.powi(2);
|
||||||
|
|
||||||
|
// Remove any minima eclipsed by this one
|
||||||
|
let mut s;
|
||||||
|
loop {
|
||||||
|
let r = &cm[cm.len() - 1];
|
||||||
|
|
||||||
|
s = (fq - r.f + q2 - r.b.powi(2)) / (qs - r.b) / 2.0;
|
||||||
|
|
||||||
|
if !(s <= r.z) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cm.pop();
|
||||||
|
if cm.len() == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to minima list
|
||||||
|
cm.push(CriticalMinimum {
|
||||||
|
v: q,
|
||||||
|
z: s,
|
||||||
|
f: fq,
|
||||||
|
b: qs,
|
||||||
|
t: dy,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mask_xy_cursor.reset(0);
|
||||||
|
|
||||||
|
// Resample array based on critical minima
|
||||||
|
{
|
||||||
|
let mut k = 0;
|
||||||
|
for q in 0..length {
|
||||||
|
// Skip eclipsed minima
|
||||||
|
while k + 1 < cm.len() && cm[k + 1].z < q as f32 {
|
||||||
|
k += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = &cm[k];
|
||||||
|
|
||||||
|
// Distance from integer index to subpixel location of minimum
|
||||||
|
let rq = r.b - q as f32;
|
||||||
|
|
||||||
|
let (mut mask, (dx, dy)) = mask_xy_cursor.get_mut();
|
||||||
|
*dx = rq;
|
||||||
|
*dy = r.t;
|
||||||
|
// Mark cell as having propagated
|
||||||
|
if r.v != q {
|
||||||
|
mask.set(false);
|
||||||
|
}
|
||||||
|
mask_xy_cursor.advance(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
411
gi/src/graphics/font/esdt/img.rs
Normal file
411
gi/src/graphics/font/esdt/img.rs
Normal file
@ -0,0 +1,411 @@
|
|||||||
|
// NOTE(eddyb) this is a separate module so that privacy affects sibling modules.
|
||||||
|
// FIXME(eddyb) deduplicate with `image` crate?
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
|
// HACK(eddyb) only exists to allow toggling precision for testing purposes.
|
||||||
|
#[cfg(sdfer_use_f64_instead_of_f32)]
|
||||||
|
type f32 = f64;
|
||||||
|
|
||||||
|
/// `[0, 1]` represented by uniformly spaced `u8` values (`0..=255`),
|
||||||
|
/// i.e. `Unorm8(byte)` corresponds to the `f32` value `byte as f32 / 255.0`.
|
||||||
|
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Unorm8(u8);
|
||||||
|
|
||||||
|
impl Unorm8 {
|
||||||
|
pub const MIN: Self = Self::from_bits(0);
|
||||||
|
pub const MAX: Self = Self::from_bits(u8::MAX);
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn encode(x: f32) -> Self {
|
||||||
|
// NOTE(eddyb) manual `clamp` not needed, `(_: f32) as u8` will saturate:
|
||||||
|
// https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast
|
||||||
|
Self((x * 255.0).round() as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn decode(self) -> f32 {
|
||||||
|
self.0 as f32 / 255.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn from_bits(bits: u8) -> Self {
|
||||||
|
Self(bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn to_bits(self) -> u8 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Copy, Clone)]
|
||||||
|
pub struct Image2d<T, Storage: AsRef<[T]> = Vec<T>> {
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
data: Storage,
|
||||||
|
_marker: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, Storage: AsRef<[T]>> Image2d<T, Storage> {
|
||||||
|
pub fn new(width: usize, height: usize) -> Self
|
||||||
|
where
|
||||||
|
T: Default,
|
||||||
|
Storage: FromIterator<T>,
|
||||||
|
{
|
||||||
|
Self::from_fn(width, height, |_, _| T::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_fn(width: usize, height: usize, mut f: impl FnMut(usize, usize) -> T) -> Self
|
||||||
|
where
|
||||||
|
Storage: FromIterator<T>,
|
||||||
|
{
|
||||||
|
Self::from_storage(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
(0..height)
|
||||||
|
.flat_map(|y| (0..width).map(move |x| (x, y)))
|
||||||
|
.map(|(x, y)| f(x, y))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_storage(width: usize, height: usize, storage: Storage) -> Self {
|
||||||
|
assert_eq!(storage.as_ref().len(), width * height);
|
||||||
|
Self {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
data: storage,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn width(&self) -> usize {
|
||||||
|
self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> usize {
|
||||||
|
self.height
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reborrow(&self) -> Image2d<T, &[T]> {
|
||||||
|
Image2d {
|
||||||
|
width: self.width,
|
||||||
|
height: self.height,
|
||||||
|
data: self.data.as_ref(),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reborrow_mut(&mut self) -> Image2d<T, &mut [T]>
|
||||||
|
where
|
||||||
|
Storage: AsMut<[T]>,
|
||||||
|
{
|
||||||
|
Image2d {
|
||||||
|
width: self.width,
|
||||||
|
height: self.height,
|
||||||
|
data: self.data.as_mut(),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cursor_at(&mut self, x: usize, y: usize) -> Image2dCursor<'_, T>
|
||||||
|
where
|
||||||
|
Storage: AsMut<[T]>,
|
||||||
|
{
|
||||||
|
let mut cursor = Image2dCursor {
|
||||||
|
image: self.reborrow_mut(),
|
||||||
|
xy_offset: 0,
|
||||||
|
};
|
||||||
|
cursor.reset((x, y));
|
||||||
|
cursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, Storage: AsRef<[T]>> Index<(usize, usize)> for Image2d<T, Storage> {
|
||||||
|
type Output = T;
|
||||||
|
|
||||||
|
fn index(&self, (x, y): (usize, usize)) -> &T {
|
||||||
|
&self.data.as_ref()[y * self.width..][..self.width][x]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, Storage: AsMut<[T]> + AsRef<[T]>> IndexMut<(usize, usize)> for Image2d<T, Storage> {
|
||||||
|
fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut T {
|
||||||
|
&mut self.data.as_mut()[y * self.width..][..self.width][x]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<image::GrayImage> for Image2d<Unorm8> {
|
||||||
|
fn from(img: image::GrayImage) -> Self {
|
||||||
|
Self {
|
||||||
|
width: img.width().try_into().unwrap(),
|
||||||
|
height: img.height().try_into().unwrap(),
|
||||||
|
// HACK(eddyb) this should be a noop if the right specializations
|
||||||
|
// all kick in, and LLVM optimizes out the in-place transformation.
|
||||||
|
data: img.into_vec().into_iter().map(Unorm8::from_bits).collect(),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Image2d<Unorm8>> for image::GrayImage {
|
||||||
|
fn from(img: Image2d<Unorm8>) -> Self {
|
||||||
|
image::GrayImage::from_vec(
|
||||||
|
img.width().try_into().unwrap(),
|
||||||
|
img.height().try_into().unwrap(),
|
||||||
|
// HACK(eddyb) this should be a noop if the right specializations
|
||||||
|
// all kick in, and LLVM optimizes out the in-place transformation.
|
||||||
|
img.data.into_iter().map(Unorm8::to_bits).collect(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Image2d<Unorm8>> for ndarray::Array2<u8> {
|
||||||
|
fn from(value: Image2d<Unorm8>) -> Self {
|
||||||
|
ndarray::Array2::from_shape_vec(
|
||||||
|
[value.height(), value.width()],
|
||||||
|
value.data.into_iter().map(Unorm8::to_bits).collect(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Copy> Image2d<T> {
|
||||||
|
fn resize_and_fill_with(&mut self, width: usize, height: usize, initial: T) {
|
||||||
|
self.width = width;
|
||||||
|
self.height = height;
|
||||||
|
self.data.clear();
|
||||||
|
self.data.resize(width * height, initial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Bitmap {
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
bit_8x8_blocks: Image2d<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BitmapEntry<'a> {
|
||||||
|
bit_8x8_block: &'a mut u64,
|
||||||
|
mask: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bitmap {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new(width: usize, height: usize) -> Self {
|
||||||
|
let mut r = Self::default();
|
||||||
|
r.resize_and_fill_with(width, height, false);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn resize_and_fill_with(&mut self, width: usize, height: usize, initial: bool) {
|
||||||
|
self.width = width;
|
||||||
|
self.height = height;
|
||||||
|
self.bit_8x8_blocks.resize_and_fill_with(
|
||||||
|
width.div_ceil(8),
|
||||||
|
height.div_ceil(8),
|
||||||
|
if initial { !0 } else { 0 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn width(&self) -> usize {
|
||||||
|
self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn height(&self) -> usize {
|
||||||
|
self.height
|
||||||
|
}
|
||||||
|
|
||||||
|
const BW: usize = 8;
|
||||||
|
const BH: usize = 8;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
const fn bit_8x8_block_xy_and_mask(x: usize, y: usize) -> ((usize, usize), u64) {
|
||||||
|
(
|
||||||
|
(x / Self::BW, y / Self::BH),
|
||||||
|
1 << ((y % Self::BH) * Self::BW + x % Self::BW),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn get(&self, x: usize, y: usize) -> bool {
|
||||||
|
let (block_xy, mask) = Self::bit_8x8_block_xy_and_mask(x, y);
|
||||||
|
(self.bit_8x8_blocks[block_xy] & mask) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn at(&mut self, x: usize, y: usize) -> BitmapEntry<'_> {
|
||||||
|
let (block_xy, mask) = Self::bit_8x8_block_xy_and_mask(x, y);
|
||||||
|
BitmapEntry {
|
||||||
|
bit_8x8_block: &mut self.bit_8x8_blocks[block_xy],
|
||||||
|
mask,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn cursor_at(&mut self, x: usize, y: usize) -> BitmapCursor<'_> {
|
||||||
|
let mut cursor = BitmapCursor {
|
||||||
|
bit_8x8_blocks: self.bit_8x8_blocks.cursor_at(0, 0),
|
||||||
|
intra_block_xy: (0, 0),
|
||||||
|
};
|
||||||
|
cursor.reset((x, y));
|
||||||
|
cursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitmapEntry<'_> {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn get(&self) -> bool {
|
||||||
|
(*self.bit_8x8_block & self.mask) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set(&mut self, value: bool) {
|
||||||
|
if value {
|
||||||
|
*self.bit_8x8_block |= self.mask;
|
||||||
|
} else {
|
||||||
|
*self.bit_8x8_block &= !self.mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(eddyb) this doesn't really belong here, and should use GATs.
|
||||||
|
pub trait NDCursor<'a, P> {
|
||||||
|
type RefMut;
|
||||||
|
fn reset(&'a mut self, position: P);
|
||||||
|
fn get_mut(&'a mut self) -> Self::RefMut;
|
||||||
|
fn advance(&'a mut self, delta: P);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait NDCursorExt<P>: for<'a> NDCursor<'a, P> {
|
||||||
|
fn zip<C2: NDCursorExt<P>>(self, other: C2) -> NDCursorZip<Self, C2>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
NDCursorZip(self, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(eddyb) this is a really bad API but a whole coordinate system would be overkill.
|
||||||
|
fn map_abs_and_rel<P2, FA: Fn(P2) -> P, FR: Fn(P2) -> P>(
|
||||||
|
self,
|
||||||
|
fa: FA,
|
||||||
|
fr: FR,
|
||||||
|
) -> NDCursorMapPos<Self, FA, FR>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
NDCursorMapPos(self, fa, fr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<P, C: for<'a> NDCursor<'a, P>> NDCursorExt<P> for C {}
|
||||||
|
|
||||||
|
pub struct NDCursorZip<C1, C2>(C1, C2);
|
||||||
|
impl<'a, P: Copy, C1: NDCursor<'a, P>, C2: NDCursor<'a, P>> NDCursor<'a, P>
|
||||||
|
for NDCursorZip<C1, C2>
|
||||||
|
{
|
||||||
|
type RefMut = (C1::RefMut, C2::RefMut);
|
||||||
|
#[inline(always)]
|
||||||
|
fn reset(&'a mut self, position: P) {
|
||||||
|
self.0.reset(position);
|
||||||
|
self.1.reset(position);
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_mut(&'a mut self) -> Self::RefMut {
|
||||||
|
(self.0.get_mut(), self.1.get_mut())
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn advance(&'a mut self, delta: P) {
|
||||||
|
self.0.advance(delta);
|
||||||
|
self.1.advance(delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NDCursorMapPos<C, FA, FR>(C, FA, FR);
|
||||||
|
impl<'a, C: NDCursor<'a, P>, P, P2, FA: Fn(P2) -> P, FR: Fn(P2) -> P> NDCursor<'a, P2>
|
||||||
|
for NDCursorMapPos<C, FA, FR>
|
||||||
|
{
|
||||||
|
type RefMut = C::RefMut;
|
||||||
|
#[inline(always)]
|
||||||
|
fn reset(&'a mut self, position: P2) {
|
||||||
|
self.0.reset((self.1)(position));
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_mut(&'a mut self) -> Self::RefMut {
|
||||||
|
self.0.get_mut()
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn advance(&'a mut self, delta: P2) {
|
||||||
|
self.0.advance((self.2)(delta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Image2dCursor<'a, T> {
|
||||||
|
// FIXME(eddyb) find a way to use something closer to `slice::IterMut` here.
|
||||||
|
image: Image2d<T, &'a mut [T]>,
|
||||||
|
xy_offset: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: 'a> NDCursor<'a, (usize, usize)> for Image2dCursor<'_, T> {
|
||||||
|
type RefMut = &'a mut T;
|
||||||
|
#[inline(always)]
|
||||||
|
fn reset(&'a mut self, (x, y): (usize, usize)) {
|
||||||
|
self.xy_offset = y * self.image.width + x;
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_mut(&'a mut self) -> Self::RefMut {
|
||||||
|
&mut self.image.data[self.xy_offset]
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn advance(&'a mut self, (dx, dy): (usize, usize)) {
|
||||||
|
// FIXME(eddyb) check for edge conditions? (should be more like an iterator)
|
||||||
|
self.xy_offset += dy * self.image.width + dx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BitmapCursor<'a> {
|
||||||
|
bit_8x8_blocks: Image2dCursor<'a, u64>,
|
||||||
|
// FIXME(eddyb) because of this we can't just use `bit_8x8_block_xy_and_mask`.
|
||||||
|
intra_block_xy: (u8, u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NDCursor<'a, (usize, usize)> for BitmapCursor<'_> {
|
||||||
|
type RefMut = BitmapEntry<'a>;
|
||||||
|
#[inline(always)]
|
||||||
|
fn reset(&'a mut self, (x, y): (usize, usize)) {
|
||||||
|
self.bit_8x8_blocks.reset((x / Bitmap::BW, y / Bitmap::BH));
|
||||||
|
self.intra_block_xy = ((x % Bitmap::BW) as u8, (y % Bitmap::BH) as u8);
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_mut(&'a mut self) -> Self::RefMut {
|
||||||
|
let bxy = self.intra_block_xy;
|
||||||
|
let (_, mask) = Bitmap::bit_8x8_block_xy_and_mask(bxy.0 as usize, bxy.1 as usize);
|
||||||
|
BitmapEntry {
|
||||||
|
bit_8x8_block: self.bit_8x8_blocks.get_mut(),
|
||||||
|
mask,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn advance(&'a mut self, (dx, dy): (usize, usize)) {
|
||||||
|
// FIXME(eddyb) check for edge conditions? (should be more like an iterator)
|
||||||
|
let bxy = self.intra_block_xy;
|
||||||
|
let new_bxy = (bxy.0 as usize + dx, bxy.1 as usize + dy);
|
||||||
|
|
||||||
|
let whole_block_dxy = (new_bxy.0 / Bitmap::BW, new_bxy.1 / Bitmap::BH);
|
||||||
|
if whole_block_dxy != (0, 0) {
|
||||||
|
self.bit_8x8_blocks.advance(whole_block_dxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.intra_block_xy = (
|
||||||
|
(new_bxy.0 % Bitmap::BW) as u8,
|
||||||
|
(new_bxy.1 % Bitmap::BH) as u8,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
5
gi/src/graphics/font/esdt/mod.rs
Normal file
5
gi/src/graphics/font/esdt/mod.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
mod esdt;
|
||||||
|
mod img;
|
||||||
|
|
||||||
|
pub use esdt::*;
|
||||||
|
pub use img::{Image2d, Unorm8};
|
||||||
345
gi/src/graphics/font/mod.rs
Normal file
345
gi/src/graphics/font/mod.rs
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
mod esdt;
|
||||||
|
use esdt::{Image2d, Unorm8};
|
||||||
|
use glow::{HasContext, NativeTexture, TEXTURE_2D};
|
||||||
|
use std::{cell::RefCell, collections::HashMap};
|
||||||
|
use text_items::Text as TextTrait;
|
||||||
|
mod text_items;
|
||||||
|
pub use text_items::{Anchor, LineStyle, PositionText, TextLine};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
components::{Program, Shader},
|
||||||
|
errors::*,
|
||||||
|
font_manager::{FontManager, FontStyle},
|
||||||
|
resources::RcGlRcResource,
|
||||||
|
shaders::font::{FontFragment, FontGeometry, FontVertex},
|
||||||
|
utils::resources::RcGlTexture,
|
||||||
|
GL,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{AttaWithBuffer, Graphics};
|
||||||
|
pub struct Text {
|
||||||
|
gl: GL,
|
||||||
|
font_manager: RefCell<FontManager>,
|
||||||
|
cache: RefCell<HashMap<String, Cache>>,
|
||||||
|
program: Program,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cache {
|
||||||
|
gl: GL,
|
||||||
|
cache: HashMap<char, TextVType>,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
last_pos: [usize; 2],
|
||||||
|
tex: RcGlRcResource<NativeTexture>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cache {
|
||||||
|
pub fn new(gl: GL) -> Self {
|
||||||
|
let tex = unsafe {
|
||||||
|
let tex = gl.create_texture().unwrap();
|
||||||
|
gl.bind_texture(glow::TEXTURE_2D, Some(tex));
|
||||||
|
gl.tex_parameter_i32(
|
||||||
|
glow::TEXTURE_2D,
|
||||||
|
glow::TEXTURE_MIN_FILTER,
|
||||||
|
glow::LINEAR as i32,
|
||||||
|
);
|
||||||
|
gl.tex_parameter_i32(
|
||||||
|
glow::TEXTURE_2D,
|
||||||
|
glow::TEXTURE_MAG_FILTER,
|
||||||
|
glow::LINEAR as i32,
|
||||||
|
);
|
||||||
|
|
||||||
|
gl.tex_image_2d(
|
||||||
|
glow::TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
glow::R8 as i32,
|
||||||
|
1024,
|
||||||
|
1024,
|
||||||
|
0,
|
||||||
|
glow::RED,
|
||||||
|
glow::UNSIGNED_BYTE,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
tex
|
||||||
|
};
|
||||||
|
|
||||||
|
let tex = RcGlRcResource::new(gl.gl_rc(), tex);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
gl,
|
||||||
|
cache: HashMap::new(),
|
||||||
|
// cache_tex: vec![0; 1024 * 1024],
|
||||||
|
width: 1024,
|
||||||
|
height: 1024,
|
||||||
|
last_pos: [0, 0],
|
||||||
|
tex,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, c: char) -> Option<&TextVType> {
|
||||||
|
self.cache.get(&c)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_glyph(&mut self, tex: Image2d<Unorm8>, c: char) -> &TextVType {
|
||||||
|
// use image::GrayImage;
|
||||||
|
|
||||||
|
use ndarray::{s, Array2};
|
||||||
|
let width = tex.width();
|
||||||
|
let height = tex.height();
|
||||||
|
|
||||||
|
let data: Array2<u8> = tex.into();
|
||||||
|
|
||||||
|
let x = self.last_pos[0];
|
||||||
|
let y = self.last_pos[1];
|
||||||
|
|
||||||
|
use glow::PixelUnpackData;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.gl
|
||||||
|
.bind_texture(glow::TEXTURE_2D, Some(self.tex.native()));
|
||||||
|
self.gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
|
self.gl.tex_sub_image_2d(
|
||||||
|
glow::TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
x as i32,
|
||||||
|
y as i32,
|
||||||
|
width as i32,
|
||||||
|
height as i32,
|
||||||
|
glow::RED,
|
||||||
|
glow::UNSIGNED_BYTE,
|
||||||
|
PixelUnpackData::Slice(&data.as_slice().unwrap()),
|
||||||
|
);
|
||||||
|
println!("{} {} {} {}", x, y, width, height);
|
||||||
|
println!("size: {}", &data.len());
|
||||||
|
self.gl.bind_texture(glow::TEXTURE_2D, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cache.insert(
|
||||||
|
c,
|
||||||
|
TextVType {
|
||||||
|
tex_coords: [
|
||||||
|
x as f32,
|
||||||
|
(y + height - 1) as f32,
|
||||||
|
(x + width - 1) as f32,
|
||||||
|
y as f32,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if x + width >= 1024 {
|
||||||
|
self.last_pos[0] = 0;
|
||||||
|
self.last_pos[1] += height;
|
||||||
|
} else {
|
||||||
|
self.last_pos[0] += width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if y + height > self.height {
|
||||||
|
self.height += 1024;
|
||||||
|
// self.cache_tex.extend(vec![0; 1024 * 1024]);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cache.get(&c).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test(&self) {
|
||||||
|
let mut data = vec![0; 1024 * 1024];
|
||||||
|
unsafe {
|
||||||
|
self.gl
|
||||||
|
.bind_texture(glow::TEXTURE_2D, Some(self.tex.native()));
|
||||||
|
self.gl.get_tex_image(
|
||||||
|
glow::TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
glow::RED,
|
||||||
|
glow::UNSIGNED_BYTE,
|
||||||
|
glow::PixelPackData::Slice(&mut data),
|
||||||
|
);
|
||||||
|
self.gl.bind_texture(glow::TEXTURE_2D, None);
|
||||||
|
}
|
||||||
|
let img = image::GrayImage::from_raw(1024, 1024, data).unwrap();
|
||||||
|
img.save("test.png").unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TextVType {
|
||||||
|
tex_coords: [f32; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextVType {
|
||||||
|
fn left_top(&self) -> [f32; 2] {
|
||||||
|
[self.tex_coords[0], self.tex_coords[1]]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn right_bottom(&self) -> [f32; 2] {
|
||||||
|
[self.tex_coords[2], self.tex_coords[3]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Text {
|
||||||
|
pub fn new(gl: GL, font_manager: FontManager) -> Result<Self> {
|
||||||
|
let vertex = Shader::new(glow::VERTEX_SHADER, FontVertex::new())?;
|
||||||
|
let geom = Shader::new(glow::GEOMETRY_SHADER, FontGeometry::new())?;
|
||||||
|
let fragment = Shader::new(glow::FRAGMENT_SHADER, FontFragment::new())?;
|
||||||
|
let mut program = Program::new(vertex, fragment, Some(geom), "330 core");
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
gl,
|
||||||
|
font_manager: RefCell::new(font_manager),
|
||||||
|
cache: RefCell::new(HashMap::new()),
|
||||||
|
program,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_viewport(&self, gl: &glow::Context, viewport: [f32; 2]) {
|
||||||
|
let loc = self.program.get_uniform_location(gl, "viewport");
|
||||||
|
unsafe {
|
||||||
|
gl.uniform_2_f32(loc.as_ref(), viewport[0], viewport[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_config(&self, gl: &glow::Context, config: FontConfig) {
|
||||||
|
match config {
|
||||||
|
FontConfig::Textline(style, font) => {
|
||||||
|
let loc = self.program.get_uniform_location(&gl, "location");
|
||||||
|
let anchor = self.program.get_uniform_location(&gl, "anchor");
|
||||||
|
unsafe {
|
||||||
|
gl.uniform_3_f32(loc.as_ref(), 0.0, 0.0, 1.0);
|
||||||
|
gl.uniform_3_f32(anchor.as_ref(), 0.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_uniforms(&self, gl: &glow::Context) {
|
||||||
|
let conf = self.program.get_uniform_location(&gl, "uSdfConfig");
|
||||||
|
let u_mode = self.program.get_uniform_location(&gl, "uMode");
|
||||||
|
let u_border = self.program.get_uniform_location(&gl, "uBorder");
|
||||||
|
let u_stroke = self.program.get_uniform_location(&gl, "uStroke");
|
||||||
|
let u_fill = self.program.get_uniform_location(&gl, "uFill");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
gl.uniform_4_f32(conf.as_ref(), 5.0, 3.0, 0.0, 0.0);
|
||||||
|
gl.uniform_1_i32(u_mode.as_ref(), -1);
|
||||||
|
gl.uniform_4_f32(u_border.as_ref(), 0.0, 0.0, 0.0, 0.0);
|
||||||
|
gl.uniform_4_f32(u_stroke.as_ref(), 1.0, 1.0, 1.0, 1.0);
|
||||||
|
gl.uniform_4_f32(u_fill.as_ref(), 1.0, 1.0, 1.0, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Graphics for Text {
|
||||||
|
const id: &'static str = "Text";
|
||||||
|
type Config = FontConfig;
|
||||||
|
fn compile(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
|
self.program.compile(gl)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
|
self.program.destroy(gl);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&self, gl: &glow::Context, count: i32) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let loc = self.program.get_uniform_location(gl, "atlas_data");
|
||||||
|
gl.uniform_1_i32(loc.as_ref(), 0);
|
||||||
|
|
||||||
|
gl.active_texture(glow::TEXTURE0);
|
||||||
|
gl.bind_texture(
|
||||||
|
glow::TEXTURE_2D,
|
||||||
|
self.cache
|
||||||
|
.borrow()
|
||||||
|
.get("resources/Roboto-Regular.ttf")
|
||||||
|
.map(|v| v.tex.native()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let width_loc = self.program.get_uniform_location(gl, "atlas_shape");
|
||||||
|
gl.uniform_2_f32(width_loc.as_ref(), 1024.0, 1024.0);
|
||||||
|
|
||||||
|
self.set_uniforms(gl);
|
||||||
|
|
||||||
|
gl.draw_arrays(glow::POINTS, 0, count);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn program_mut(&mut self) -> &mut Program {
|
||||||
|
&mut self.program
|
||||||
|
}
|
||||||
|
|
||||||
|
fn program_ref(&self) -> &Program {
|
||||||
|
&self.program
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_config(&mut self, gl: &glow::Context, config: &Self::Config) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
match config {
|
||||||
|
FontConfig::Textline(style, font) => {
|
||||||
|
let loc = self.program.get_uniform_location(gl, "location");
|
||||||
|
let anchor = self.program.get_uniform_location(gl, "anchor");
|
||||||
|
gl.uniform_3_f32(loc.as_ref(), 0.0, 0.0, 2.0);
|
||||||
|
gl.uniform_3_f32(anchor.as_ref(), 0.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttaWithBuffer for Text {
|
||||||
|
type Data = PositionText;
|
||||||
|
|
||||||
|
fn bake<'a, 'gl: 'a>(
|
||||||
|
&'a self,
|
||||||
|
gl: &'gl glow::Context,
|
||||||
|
data: &Self::Data,
|
||||||
|
config: &<Self as Graphics>::Config,
|
||||||
|
) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)> {
|
||||||
|
let v = data.bake(
|
||||||
|
&self.gl,
|
||||||
|
&mut *self.font_manager.borrow_mut(),
|
||||||
|
&mut *self.cache.borrow_mut(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok((v.vertex(), None, v.len() as i32))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(
|
||||||
|
&self,
|
||||||
|
gl: &glow::Context,
|
||||||
|
) -> (
|
||||||
|
glow::NativeVertexArray,
|
||||||
|
glow::NativeBuffer,
|
||||||
|
Option<glow::NativeBuffer>,
|
||||||
|
) {
|
||||||
|
unsafe {
|
||||||
|
let vao = gl.create_vertex_array().unwrap();
|
||||||
|
gl.bind_vertex_array(Some(vao));
|
||||||
|
let vbo = gl.create_buffer().unwrap();
|
||||||
|
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
|
||||||
|
gl.enable_vertex_attrib_array(0);
|
||||||
|
gl.vertex_attrib_pointer_f32(0, 4, glow::FLOAT, false, 48, 0);
|
||||||
|
|
||||||
|
gl.enable_vertex_attrib_array(1);
|
||||||
|
gl.vertex_attrib_pointer_f32(1, 4, glow::FLOAT, false, 48, 16);
|
||||||
|
|
||||||
|
gl.bind_vertex_array(None);
|
||||||
|
gl.bind_buffer(glow::ARRAY_BUFFER, None);
|
||||||
|
|
||||||
|
(vao, vbo, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum FontConfig {
|
||||||
|
Textline(LineStyle, FontStyle),
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
use super::*;
|
||||||
|
let mut font_manager = FontManager::new().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
258
gi/src/graphics/font/text_items.rs
Normal file
258
gi/src/graphics/font/text_items.rs
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
use super::esdt::{Image2d, Params, Unorm8};
|
||||||
|
use super::TextVType;
|
||||||
|
use super::{esdt::glyph_to_sdf, Cache};
|
||||||
|
use crate::font_manager::{CharImg, FontManager, FontSize};
|
||||||
|
use crate::GL;
|
||||||
|
use crate::{errors::*, font_manager::FontStyle};
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use geo::kernels;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
const SDF_PARAM: Params = Params {
|
||||||
|
radius: 5.0,
|
||||||
|
pad: 4,
|
||||||
|
cutoff: 0.25,
|
||||||
|
solidify: true,
|
||||||
|
preprocess: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct LineStyle {
|
||||||
|
line_height: f32,
|
||||||
|
line_spacing: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PositionText {
|
||||||
|
item: TextLine,
|
||||||
|
position: [f32; 3],
|
||||||
|
anchor: Anchor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PositionText {
|
||||||
|
pub fn new(item: TextLine, position: [f32; 3], anchor: Anchor) -> Self {
|
||||||
|
Self {
|
||||||
|
item,
|
||||||
|
position,
|
||||||
|
anchor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Text for PositionText {
|
||||||
|
fn bake<'a>(
|
||||||
|
&self,
|
||||||
|
gl: &'a GL,
|
||||||
|
font_manager: &mut FontManager,
|
||||||
|
cache: &mut HashMap<String, Cache>,
|
||||||
|
) -> Result<TextVertexArray> {
|
||||||
|
self.item.bake(gl, font_manager, cache)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TextLine {
|
||||||
|
text: String,
|
||||||
|
font_style: FontStyle,
|
||||||
|
line_style: LineStyle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextLine {
|
||||||
|
pub fn new<P: Into<String>>(
|
||||||
|
text: P,
|
||||||
|
font_style: Option<FontStyle>,
|
||||||
|
line_style: Option<LineStyle>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
text: text.into(),
|
||||||
|
font_style: font_style.unwrap_or_default(),
|
||||||
|
line_style: line_style.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Anchor {
|
||||||
|
TopLeft,
|
||||||
|
TopCenter,
|
||||||
|
TopRight,
|
||||||
|
CenterLeft,
|
||||||
|
Center,
|
||||||
|
CenterRight,
|
||||||
|
BottomLeft,
|
||||||
|
BottomCenter,
|
||||||
|
BottomRight,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum TextAlign {
|
||||||
|
Left,
|
||||||
|
Center,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TextAlign {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Left
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Zeroable, Pod)]
|
||||||
|
pub struct TextVertexItem {
|
||||||
|
tex_coords: [f32; 4],
|
||||||
|
position: [f32; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TextVertexArray {
|
||||||
|
points: Vec<TextVertexItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextVertexArray {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
points: Vec::with_capacity(30),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.points.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, item: TextVertexItem) {
|
||||||
|
self.points.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_text(&mut self, tex_coords: TextVType, position: [f32; 2], size: [f32; 2]) {
|
||||||
|
let tex_left_top = tex_coords.left_top();
|
||||||
|
let tex_right_bottom = tex_coords.right_bottom();
|
||||||
|
|
||||||
|
self.push(TextVertexItem {
|
||||||
|
position: [position[0], position[1], size[0], size[1]],
|
||||||
|
tex_coords: [
|
||||||
|
tex_left_top[0],
|
||||||
|
tex_left_top[1],
|
||||||
|
tex_right_bottom[0],
|
||||||
|
tex_right_bottom[1],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub fn vertex(&self) -> Vec<f32> {
|
||||||
|
let mut result = Vec::with_capacity(self.len() * 8);
|
||||||
|
self.points.iter().for_each(|v| {
|
||||||
|
result.extend_from_slice(&v.tex_coords);
|
||||||
|
result.extend_from_slice(&v.position);
|
||||||
|
});
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bits(&self) -> &[u8] {
|
||||||
|
bytemuck::cast_slice(&self.points)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Text: Sized {
|
||||||
|
fn bake<'a>(
|
||||||
|
&self,
|
||||||
|
gl: &'a GL,
|
||||||
|
font_manager: &mut FontManager,
|
||||||
|
cache: &mut HashMap<String, Cache>,
|
||||||
|
) -> Result<TextVertexArray>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Text for TextLine {
|
||||||
|
fn bake<'a>(
|
||||||
|
&self,
|
||||||
|
gl: &'a GL,
|
||||||
|
font_manager: &mut FontManager,
|
||||||
|
cache: &mut HashMap<String, Cache>,
|
||||||
|
) -> Result<TextVertexArray> {
|
||||||
|
let font_style = &self.font_style;
|
||||||
|
let font = font_manager.get_font_or_insert(&font_style.postscript_name);
|
||||||
|
if let Some(font) = font {
|
||||||
|
cache
|
||||||
|
.entry(font_style.postscript_name.clone())
|
||||||
|
.or_insert_with(|| Cache::new(gl.clone()));
|
||||||
|
|
||||||
|
let cache = cache.get_mut(&font_style.postscript_name).unwrap();
|
||||||
|
let mut baked = TextVertexArray::new();
|
||||||
|
let mut pen = [0.0, 0.0];
|
||||||
|
|
||||||
|
let mut prev: Option<char> = None;
|
||||||
|
|
||||||
|
for char in self.text.chars() {
|
||||||
|
if char == '\n' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if char == ' ' {
|
||||||
|
pen[0] += 10.0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let font_size = match &font_style.size {
|
||||||
|
FontSize::Absolute(s) => *s,
|
||||||
|
FontSize::DistanceScale(_) => panic!(""),
|
||||||
|
FontSize::WindowScale(_) => panic!(""),
|
||||||
|
};
|
||||||
|
|
||||||
|
font.set_char_size(font_size.floor() as isize);
|
||||||
|
font.set_char(char);
|
||||||
|
|
||||||
|
let (x_advanced, y_advanced) = font.get_advance();
|
||||||
|
let (x_kerning, _) = prev.map_or((0.0, 0.0), |v| {
|
||||||
|
let kerning = font.get_kerning(v, char);
|
||||||
|
kerning
|
||||||
|
});
|
||||||
|
|
||||||
|
let metrics = font.get_metrics(char);
|
||||||
|
let width = (metrics.width >> 6) as f32;
|
||||||
|
let height = (metrics.height >> 6) as f32;
|
||||||
|
|
||||||
|
let bear_x = (metrics.horiBearingX >> 6) as f32;
|
||||||
|
let bear_y = (metrics.horiBearingY >> 6) as f32;
|
||||||
|
|
||||||
|
let x0 = pen[0] + bear_x as f32 + x_kerning;
|
||||||
|
let y0 = pen[1] - bear_y as f32;
|
||||||
|
let x1 = pen[0] + metrics.width as f32 + x_kerning;
|
||||||
|
let y1 = pen[1] + metrics.height as f32;
|
||||||
|
|
||||||
|
if let Some(cache) = cache.get(char) {
|
||||||
|
baked.insert_text(cache.to_owned(), [x0, y0], [width, height]);
|
||||||
|
} else {
|
||||||
|
let char_glyph = font.get_char(char, 64)?;
|
||||||
|
let mut img = char_glyph.into();
|
||||||
|
let (result, _) = glyph_to_sdf(&mut img, SDF_PARAM, None);
|
||||||
|
let b = cache.insert_glyph(result, char);
|
||||||
|
baked.insert_text(b.to_owned(), [x0, y0], [width, height]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pen[0] += x_advanced;
|
||||||
|
pen[1] += y_advanced;
|
||||||
|
prev = Some(char);
|
||||||
|
}
|
||||||
|
Ok(baked)
|
||||||
|
} else {
|
||||||
|
Err(Error::FontError(format!(
|
||||||
|
"Font {} not found",
|
||||||
|
font_style.postscript_name
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CharImg> for Image2d<Unorm8> {
|
||||||
|
fn from(value: CharImg) -> Self {
|
||||||
|
// let img = Image2d::new(value.width() as u32, value.height() as u32, value.pixels);
|
||||||
|
let img = Image2d::from_storage(
|
||||||
|
value.width(),
|
||||||
|
value.height(),
|
||||||
|
value
|
||||||
|
.pixels()
|
||||||
|
.iter()
|
||||||
|
.map(|v| Unorm8::from_bits(*v))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
img
|
||||||
|
}
|
||||||
|
}
|
||||||
147
gi/src/graphics/mod.rs
Normal file
147
gi/src/graphics/mod.rs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
pub mod collections;
|
||||||
|
pub mod colormap;
|
||||||
|
mod colormesh;
|
||||||
|
pub mod font;
|
||||||
|
pub mod ppi;
|
||||||
|
pub mod threed;
|
||||||
|
pub mod tools;
|
||||||
|
pub mod transforms;
|
||||||
|
pub mod ty;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
camera::Camera,
|
||||||
|
components::Program,
|
||||||
|
errors::*,
|
||||||
|
graphics::font::FontConfig,
|
||||||
|
pg::layout_type::ViewPort,
|
||||||
|
ui::{operation::Projection, typ::CameraOP},
|
||||||
|
};
|
||||||
|
|
||||||
|
use glow::{HasContext, NativeBuffer, NativeVertexArray};
|
||||||
|
|
||||||
|
pub trait Graphics {
|
||||||
|
const id: &'static str;
|
||||||
|
type Config;
|
||||||
|
|
||||||
|
fn draw(&self, gl: &glow::Context, count: i32) -> Result<()>;
|
||||||
|
|
||||||
|
fn compile(&mut self, gl: &glow::Context) -> Result<()>;
|
||||||
|
|
||||||
|
fn destroy(&mut self, gl: &glow::Context) -> Result<()>;
|
||||||
|
|
||||||
|
fn program_ref(&self) -> &Program;
|
||||||
|
|
||||||
|
fn program_mut(&mut self) -> &mut Program;
|
||||||
|
|
||||||
|
fn mount(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
gl.use_program(self.program_ref().native_program.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unmount(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
gl.use_program(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_config(&mut self, gl: &glow::Context, config: &Self::Config) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait AttaWithProgram {
|
||||||
|
fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait AttaWithBuffer: Graphics {
|
||||||
|
type Data;
|
||||||
|
|
||||||
|
fn bake<'a, 'gl: 'a>(
|
||||||
|
&'a self,
|
||||||
|
gl: &'gl glow::Context,
|
||||||
|
data: &Self::Data,
|
||||||
|
config: &<Self as Graphics>::Config,
|
||||||
|
) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)>;
|
||||||
|
fn init(&self, gl: &glow::Context) -> (NativeVertexArray, NativeBuffer, Option<NativeBuffer>);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! config_for_everyitem {
|
||||||
|
($({$conf:ty => $name:tt},)+) => {
|
||||||
|
$(
|
||||||
|
impl From<$conf> for Config {
|
||||||
|
fn from(value: $conf) -> Self {
|
||||||
|
Self::$name(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Config> for $conf {
|
||||||
|
fn from(value: Config) -> Self {
|
||||||
|
if let Config::$name(value) = value {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
panic!("error transfer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Config> for &'a $conf {
|
||||||
|
fn from(value: &'a Config) -> &'a $conf {
|
||||||
|
if let Config::$name(value) = value {
|
||||||
|
&value
|
||||||
|
} else {
|
||||||
|
panic!("error transfer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
)+
|
||||||
|
|
||||||
|
impl From<()> for Config {
|
||||||
|
fn from(_: ()) -> Self {
|
||||||
|
Self::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[derive(Debug, Clone)]
|
||||||
|
// pub enum Config {
|
||||||
|
// PPI(PPIConfig),
|
||||||
|
// Font(FontConfig),
|
||||||
|
// None,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// config_for_everyitem!({PPIConfig => PPI},{FontConfig => Font}, );
|
||||||
|
|
||||||
|
pub trait AttachWithIO {
|
||||||
|
type State: CameraOP;
|
||||||
|
fn attach_with_mouse(
|
||||||
|
&mut self,
|
||||||
|
state: &Self::State,
|
||||||
|
camera: &mut Camera,
|
||||||
|
projection: &mut Projection,
|
||||||
|
viewport: &ViewPort,
|
||||||
|
) -> bool;
|
||||||
|
|
||||||
|
fn reset(&mut self);
|
||||||
|
|
||||||
|
fn init_camera(&self) -> Camera;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum MouseState {
|
||||||
|
Drag { from: [f32; 2], delta: [f32; 2] },
|
||||||
|
Wheel(f32),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MouseKeyboardState {
|
||||||
|
pub mouse_state: MouseState,
|
||||||
|
pub keyboard_state: [bool; 652],
|
||||||
|
}
|
||||||
7
gi/src/graphics/plane.rs
Normal file
7
gi/src/graphics/plane.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
pub struct Plane {}
|
||||||
|
|
||||||
|
impl Plane {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
243
gi/src/graphics/ppi.rs
Normal file
243
gi/src/graphics/ppi.rs
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
use super::colormap::ColorMap;
|
||||||
|
use super::{transforms, AttaWithBuffer, AttaWithProgram, AttachWithIO, Graphics};
|
||||||
|
use crate::components::{Program, Shader};
|
||||||
|
use crate::data_loader::{CoordType, Data, DataType};
|
||||||
|
use crate::errors::*;
|
||||||
|
use crate::graphics::colormap::linear::LinearColormap;
|
||||||
|
use glow::{HasContext, NativeBuffer, NativeVertexArray};
|
||||||
|
|
||||||
|
pub struct PPI {
|
||||||
|
program: Program,
|
||||||
|
cmap: LinearColormap,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PPI {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
use crate::shaders::ppi::*;
|
||||||
|
let vertex = Shader::new(glow::VERTEX_SHADER, PPIVertex::new())?;
|
||||||
|
let geom = Shader::new(glow::GEOMETRY_SHADER, PPIGeom::new())?;
|
||||||
|
let fragment = Shader::new(glow::FRAGMENT_SHADER, PPIFragment::new())?;
|
||||||
|
let mut cmap = LinearColormap::new()?;
|
||||||
|
let mut program = Program::new(vertex, fragment, Some(geom), "330 core");
|
||||||
|
|
||||||
|
Ok(Self { program, cmap })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn program(&mut self) -> &mut Program {
|
||||||
|
&mut self.program
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_conf(&mut self, gl: &glow::Context, config: &PPIConfig) {
|
||||||
|
let rdpi = config.rdpi;
|
||||||
|
let adpi = config.adpi;
|
||||||
|
let location = self.program.get_uniform_location(gl, "conf");
|
||||||
|
self.cmap
|
||||||
|
.set_range(config.color_range[0], config.color_range[1]);
|
||||||
|
self.cmap.set_colors(config.colors.clone());
|
||||||
|
self.cmap.set_unvalid_value(config.unvalid_value);
|
||||||
|
self.cmap.attach_with_program(gl, &self.program);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
gl.uniform_4_f32(
|
||||||
|
location.as_ref(),
|
||||||
|
rdpi,
|
||||||
|
adpi,
|
||||||
|
0f32,
|
||||||
|
if config.three_d { 1.0 } else { 0.0 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data_info(&self, data: &Data) -> Result<(f32, f32, DataType, usize, f32)> {
|
||||||
|
let first_block = data.blocks.get(0).unwrap();
|
||||||
|
if let CoordType::Polar {
|
||||||
|
azimuth,
|
||||||
|
r,
|
||||||
|
r_range,
|
||||||
|
elevation,
|
||||||
|
..
|
||||||
|
} = &first_block.coord_type
|
||||||
|
{
|
||||||
|
let mut sorted_azimuth = azimuth.clone();
|
||||||
|
sorted_azimuth.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Greater));
|
||||||
|
let azi_step = max_step(&sorted_azimuth);
|
||||||
|
let r_step = min_step(r) / (r_range[1]) as f32;
|
||||||
|
|
||||||
|
let unvalid = first_block.unvalid_value;
|
||||||
|
|
||||||
|
return Ok((
|
||||||
|
r_step,
|
||||||
|
azi_step,
|
||||||
|
first_block.data_type,
|
||||||
|
elevation.len(),
|
||||||
|
unvalid,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
return Err(Error::InvalidDataType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self, gl: &glow::Context, config: &PPIConfig) {
|
||||||
|
self.set_conf(gl, config);
|
||||||
|
self.cmap.attach_with_program(gl, &self.program).unwrap();
|
||||||
|
let origin = self.program.get_uniform_location(gl, "polar_origin");
|
||||||
|
unsafe {
|
||||||
|
gl.uniform_1_f32(origin.as_ref(), 90.0f32.to_radians());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn min_step(data: &Vec<f32>) -> f32 {
|
||||||
|
// 计算相邻元素之间的间距
|
||||||
|
let mut min_gap = f32::MAX;
|
||||||
|
for window in data.windows(2) {
|
||||||
|
if let [a, b] = window {
|
||||||
|
let gap = b - a;
|
||||||
|
if gap < min_gap {
|
||||||
|
min_gap = gap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return min_gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn max_step(data: &Vec<f32>) -> f32 {
|
||||||
|
// 计算相邻元素之间的间距
|
||||||
|
let mut max_gap = f32::MIN;
|
||||||
|
for window in data.windows(2) {
|
||||||
|
if let [a, b] = window {
|
||||||
|
let gap = b - a;
|
||||||
|
if gap > max_gap {
|
||||||
|
max_gap = gap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max_gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Graphics for PPI {
|
||||||
|
const id: &'static str = "PPI";
|
||||||
|
type Config = PPIConfig;
|
||||||
|
fn compile(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
|
self.program.compile(gl)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
|
self.program.destroy(gl);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&self, gl: &glow::Context, count: i32) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
self.cmap.bind_texture(gl, &self.program)?;
|
||||||
|
gl.draw_arrays(glow::POINTS, 0, count);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn program_ref(&self) -> &Program {
|
||||||
|
&self.program
|
||||||
|
}
|
||||||
|
|
||||||
|
fn program_mut(&mut self) -> &mut Program {
|
||||||
|
&mut self.program
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_config(&mut self, gl: &glow::Context, config: &Self::Config) -> Result<()> {
|
||||||
|
self.init(gl, config);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mount(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
gl.use_program(self.program.native_program);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unmount(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
gl.use_program(None);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttaWithBuffer for PPI {
|
||||||
|
type Data = Data;
|
||||||
|
|
||||||
|
fn bake<'a, 'gl: 'a>(
|
||||||
|
&'a self,
|
||||||
|
gl: &'gl glow::Context,
|
||||||
|
data: &Self::Data,
|
||||||
|
config: &<Self as Graphics>::Config,
|
||||||
|
) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)> {
|
||||||
|
let layer = config.layer;
|
||||||
|
let first_block = data.blocks.get(0).unwrap();
|
||||||
|
let first_block_data = first_block.data.view();
|
||||||
|
if let CoordType::Polar {
|
||||||
|
r_range,
|
||||||
|
azimuth,
|
||||||
|
elevation,
|
||||||
|
r,
|
||||||
|
..
|
||||||
|
} = &first_block.coord_type
|
||||||
|
{
|
||||||
|
let azimuth_len = azimuth.len();
|
||||||
|
let r_len = r.len();
|
||||||
|
|
||||||
|
let mut vertices = Vec::with_capacity(azimuth_len * r_len);
|
||||||
|
let ele = elevation.get(layer).unwrap();
|
||||||
|
|
||||||
|
for azi_idx in 0..azimuth_len {
|
||||||
|
for r_idx in 0..r_len {
|
||||||
|
let azi = azimuth.get(azi_idx).unwrap();
|
||||||
|
let r = r.get(r_idx).unwrap() / r_range[1] as f32;
|
||||||
|
// let r = *r.get(r_idx).unwrap();
|
||||||
|
let dt = first_block_data.get([layer, azi_idx, r_idx]).unwrap();
|
||||||
|
vertices.extend([r, *azi, *ele, *dt]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let len = vertices.len() as i32 / 4;
|
||||||
|
return Ok((vertices, None, len));
|
||||||
|
} else {
|
||||||
|
return Err(Error::InvalidDataType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&self, gl: &glow::Context) -> (NativeVertexArray, NativeBuffer, Option<NativeBuffer>) {
|
||||||
|
unsafe {
|
||||||
|
let vao = gl.create_vertex_array().unwrap();
|
||||||
|
gl.bind_vertex_array(Some(vao));
|
||||||
|
let vbo = gl.create_buffer().unwrap();
|
||||||
|
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
|
||||||
|
gl.enable_vertex_attrib_array(0);
|
||||||
|
gl.vertex_attrib_pointer_f32(0, 4, glow::FLOAT, false, 16, 0);
|
||||||
|
gl.bind_vertex_array(None);
|
||||||
|
gl.bind_buffer(glow::ARRAY_BUFFER, None);
|
||||||
|
|
||||||
|
(vao, vbo, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Debug)]
|
||||||
|
pub struct PPIConfig {
|
||||||
|
pub unvalid_value: f32,
|
||||||
|
pub layer: usize,
|
||||||
|
pub colors: Vec<[u8; 4]>,
|
||||||
|
pub color_range: [f32; 2],
|
||||||
|
pub rdpi: f32,
|
||||||
|
pub adpi: f32,
|
||||||
|
pub three_d: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
#[test]
|
||||||
|
fn test_ppi() {
|
||||||
|
use super::*;
|
||||||
|
// let ppi = PPI::new().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
77
gi/src/graphics/threed.rs
Normal file
77
gi/src/graphics/threed.rs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
use tracker::track;
|
||||||
|
|
||||||
|
use super::transforms::trackball::TrackballModel;
|
||||||
|
use super::{AttaWithProgram, AttachWithIO};
|
||||||
|
use crate::camera::{self, Camera};
|
||||||
|
use crate::errors::*;
|
||||||
|
use crate::pg::layout_type::ViewPort;
|
||||||
|
use crate::ui::operation::Projection;
|
||||||
|
use glow::HasContext;
|
||||||
|
use nalgebra_glm::{Mat4, Vec3};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Trackball {
|
||||||
|
trackball: TrackballModel,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trackball {
|
||||||
|
pub fn new(aspect: f32, z_near: f32, z_far: f32, fov: f32) -> Result<Self> {
|
||||||
|
let trackball = TrackballModel::new(0.0, 90.0, 20.0);
|
||||||
|
|
||||||
|
Ok(Self { trackball })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Trackball {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(16.0 / 9.0, 0.1, 1000.0, 45.0).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttaWithProgram for Trackball {
|
||||||
|
fn attach_with_program(
|
||||||
|
&self,
|
||||||
|
gl: &glow::Context,
|
||||||
|
program: &crate::components::Program,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.trackball.attach_with_program(gl, program);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttachWithIO for Trackball {
|
||||||
|
type State = super::MouseState;
|
||||||
|
fn attach_with_mouse(
|
||||||
|
&mut self,
|
||||||
|
state: &Self::State,
|
||||||
|
camera: &mut Camera,
|
||||||
|
projection: &mut Projection,
|
||||||
|
viewport: &ViewPort,
|
||||||
|
) -> bool {
|
||||||
|
match state {
|
||||||
|
&super::MouseState::Wheel(delta) => {
|
||||||
|
projection.set_fov((projection.fov() - delta).clamp(15.0, 120.0));
|
||||||
|
true
|
||||||
|
}
|
||||||
|
super::MouseState::Drag { from, delta } => {
|
||||||
|
self.trackball.drag_to(from[0], from[1], delta[0], delta[1]);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.trackball.set_phi(90.0);
|
||||||
|
self.trackball.set_theta(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_camera(&self) -> Camera {
|
||||||
|
Camera::new(
|
||||||
|
Vec3::new(0.0, 30.0, 10.0),
|
||||||
|
Vec3::new(0.0, 1.0, 0.0),
|
||||||
|
Vec3::new(0.0, 0.0, 0.0),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
59
gi/src/graphics/transforms/mod.rs
Normal file
59
gi/src/graphics/transforms/mod.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
pub mod plane;
|
||||||
|
pub mod trackball;
|
||||||
|
use crate::components::Program;
|
||||||
|
|
||||||
|
use super::AttaWithProgram;
|
||||||
|
|
||||||
|
// pub struct ChainedTransform {
|
||||||
|
// snippet: Snippet,
|
||||||
|
// chain: Vec<Box<dyn Transform>>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl ChainedTransform {
|
||||||
|
// pub fn from<T: Transform + Clone + 'static>(transform: &T) -> Self {
|
||||||
|
// let snippet = transform.snippet().clone();
|
||||||
|
// // let snippet = transform.snippet().to_owned();
|
||||||
|
// Self {
|
||||||
|
// snippet,
|
||||||
|
// chain: vec![Box::new(transform.clone())],
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn chain<P: Transform + 'static + Clone>(mut self, other: &P) -> Self {
|
||||||
|
// let new_snippet = self.snippet.clone().chain(other.snippet().to_owned());
|
||||||
|
// self.snippet = new_snippet;
|
||||||
|
// self.chain.push(Box::new(other.clone()));
|
||||||
|
// self
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl Transform for ChainedTransform {
|
||||||
|
// fn snippet(&self) -> &Snippet {
|
||||||
|
// &self.snippet
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl AttaWithProgram for ChainedTransform {
|
||||||
|
// fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> super::Result<()> {
|
||||||
|
// for transform in &self.chain {
|
||||||
|
// transform.attach_with_program(gl, program)?;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// mod test {
|
||||||
|
// use super::*;
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn test_transform() {
|
||||||
|
// let polar = polar::Polar::new().unwrap();
|
||||||
|
// // let trackball = trackball::Trackball::new().unwrap();
|
||||||
|
|
||||||
|
// // let chained = ChainedTransform::from(polar).chain(trackball);
|
||||||
|
|
||||||
|
// // println!("{}", chained.snippet().prepare_code());
|
||||||
|
// // println!("{}", chained.snippet().call(&vec![]).unwrap());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
127
gi/src/graphics/transforms/plane.rs
Normal file
127
gi/src/graphics/transforms/plane.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
use crate::components::Program;
|
||||||
|
use crate::errors::Result;
|
||||||
|
use crate::graphics::{AttaWithProgram, AttachWithIO, MouseKeyboardState, MouseState};
|
||||||
|
use crate::pg::layout_type::ViewPort;
|
||||||
|
use crate::ui::operation::Projection;
|
||||||
|
|
||||||
|
use glow::HasContext;
|
||||||
|
use nalgebra::{Matrix4, Quaternion, Translation3, Unit, UnitQuaternion, Vector3};
|
||||||
|
use nalgebra_glm::vec3;
|
||||||
|
use serde::de;
|
||||||
|
|
||||||
|
use super::trackball::{self, TrackballModel};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PlaneTrans {
|
||||||
|
trackball: TrackballModel,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlaneTrans {
|
||||||
|
fn translate(
|
||||||
|
&self,
|
||||||
|
projection: &Projection,
|
||||||
|
camera: &mut crate::camera::Camera,
|
||||||
|
delta: &[f32; 2],
|
||||||
|
viewport_size: &[f32; 2],
|
||||||
|
) {
|
||||||
|
let watch_vec = camera.front();
|
||||||
|
let fov = projection.fov().to_radians();
|
||||||
|
let aspect = projection.aspect();
|
||||||
|
|
||||||
|
// Z_near
|
||||||
|
let z_distance = camera.front().norm().abs();
|
||||||
|
|
||||||
|
let h = 2.0 * z_distance * (fov / 2.0).tan();
|
||||||
|
let w = h * aspect;
|
||||||
|
|
||||||
|
let move_width = (delta[0] * w) / viewport_size[0];
|
||||||
|
let move_height = (delta[1] * h) / viewport_size[1];
|
||||||
|
|
||||||
|
let up = camera.get_upward();
|
||||||
|
let right = watch_vec.cross(&up).normalize();
|
||||||
|
let _move = move_width * right + move_height * up;
|
||||||
|
camera.set_position(camera.get_position() - _move);
|
||||||
|
camera.set_center(camera.get_center() - _move);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttachWithIO for PlaneTrans {
|
||||||
|
type State = MouseKeyboardState;
|
||||||
|
|
||||||
|
fn attach_with_mouse(
|
||||||
|
&mut self,
|
||||||
|
state: &Self::State,
|
||||||
|
camera: &mut crate::camera::Camera,
|
||||||
|
projection: &mut crate::ui::operation::Projection,
|
||||||
|
viewport: &ViewPort,
|
||||||
|
) -> bool {
|
||||||
|
let viewport_size = viewport.size();
|
||||||
|
// let shift_key = state.keyboard_state[imgui::Key::LeftShift as usize];
|
||||||
|
// let ctrl_key = state.keyboard_state[imgui::Key::LeftCtrl as usize];
|
||||||
|
|
||||||
|
let shift_key = true;
|
||||||
|
let ctrl_key = true;
|
||||||
|
|
||||||
|
match &state.mouse_state {
|
||||||
|
MouseState::Drag { from, delta } => {
|
||||||
|
if shift_key {
|
||||||
|
self.trackball.drag_to(
|
||||||
|
from[0] - viewport_size[0] / 2.0,
|
||||||
|
from[1] - viewport_size[1] / 2.0,
|
||||||
|
0.0,
|
||||||
|
-delta[1],
|
||||||
|
);
|
||||||
|
} else if ctrl_key {
|
||||||
|
self.trackball.drag_to(
|
||||||
|
from[0] - viewport_size[0] / 2.0,
|
||||||
|
from[1] - viewport_size[1] / 2.0,
|
||||||
|
-delta[0],
|
||||||
|
0.0,
|
||||||
|
);
|
||||||
|
} else if shift_key && ctrl_key {
|
||||||
|
self.trackball.drag_to(
|
||||||
|
from[0] - viewport_size[0] / 2.0,
|
||||||
|
from[1] - viewport_size[1] / 2.0,
|
||||||
|
-delta[0],
|
||||||
|
-delta[1],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
self.translate(projection, camera, delta, &viewport_size);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
MouseState::Wheel(delta) => {
|
||||||
|
projection.set_fov((projection.fov() - delta).clamp(15.0, 120.0));
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_camera(&self) -> crate::camera::Camera {
|
||||||
|
crate::camera::Camera::new(
|
||||||
|
vec3(0.0, 0.0, 20.0),
|
||||||
|
vec3(0.0, 1.0, 0.0),
|
||||||
|
vec3(0.0, 0.0, 0.0),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttaWithProgram for PlaneTrans {
|
||||||
|
fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let loc = program.get_uniform_location(gl, "trackball_model");
|
||||||
|
self.trackball.attach_with_program(gl, program)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PlaneTrans {
|
||||||
|
fn default() -> Self {
|
||||||
|
let trackball = TrackballModel::new(0.0, 0.0, 200.0);
|
||||||
|
Self { trackball }
|
||||||
|
}
|
||||||
|
}
|
||||||
182
gi/src/graphics/transforms/trackball.rs
Normal file
182
gi/src/graphics/transforms/trackball.rs
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
use crate::components::Program;
|
||||||
|
use crate::errors::Result;
|
||||||
|
use crate::graphics::AttaWithProgram;
|
||||||
|
|
||||||
|
use glow::HasContext;
|
||||||
|
use nalgebra::{Matrix4, Quaternion, Translation3, Unit, UnitQuaternion, Vector3};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TrackballModel {
|
||||||
|
rotation: UnitQuaternion<f32>,
|
||||||
|
count: usize,
|
||||||
|
model: Matrix4<f32>,
|
||||||
|
renorm_count: usize,
|
||||||
|
trackball_size: f32,
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
theta: f32,
|
||||||
|
phi: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TrackballModel {
|
||||||
|
pub fn new(phi: f32, theta: f32, range: f32) -> Self {
|
||||||
|
let mut trackball = Self {
|
||||||
|
rotation: UnitQuaternion::identity(),
|
||||||
|
count: 0,
|
||||||
|
model: Matrix4::identity(),
|
||||||
|
renorm_count: 97,
|
||||||
|
trackball_size: range,
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
theta,
|
||||||
|
phi,
|
||||||
|
};
|
||||||
|
trackball.set_orientation(theta, phi);
|
||||||
|
trackball
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotate_z(&mut self, angle: f32) {
|
||||||
|
let q = UnitQuaternion::from_axis_angle(&Vector3::z_axis(), angle);
|
||||||
|
self.rotation *= q;
|
||||||
|
self.count += 1;
|
||||||
|
if self.count > self.renorm_count {
|
||||||
|
self.rotation = UnitQuaternion::new_normalize(*self.rotation.clone());
|
||||||
|
self.count = 0;
|
||||||
|
}
|
||||||
|
self.model = self.rotation.to_homogeneous();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drag_to(&mut self, x: f32, y: f32, dx: f32, dy: f32) {
|
||||||
|
let q = self.rotate(x, y, dx, dy);
|
||||||
|
self.rotation *= q;
|
||||||
|
self.count += 1;
|
||||||
|
if self.count > self.renorm_count {
|
||||||
|
self.rotation = UnitQuaternion::new_normalize(*self.rotation.clone());
|
||||||
|
self.count = 0;
|
||||||
|
}
|
||||||
|
self.model = self.rotation.to_homogeneous();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn model(&self) -> &Matrix4<f32> {
|
||||||
|
&self.model
|
||||||
|
}
|
||||||
|
|
||||||
|
fn theta(&self) -> f32 {
|
||||||
|
self.theta
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_theta(&mut self, theta: f32) {
|
||||||
|
self.set_orientation(theta % 360.0, self.phi % 360.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn phi(&self) -> f32 {
|
||||||
|
self.phi
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_phi(&mut self, phi: f32) {
|
||||||
|
self.set_orientation(self.theta % 360.0, phi % 360.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_orientation(&self) -> (f32, f32) {
|
||||||
|
let q = self.rotation.quaternion();
|
||||||
|
let ax = (2.0 * (q.w * q.i + q.j * q.k) / (1.0 - 2.0 * (q.i * q.i + q.j * q.j))).atan()
|
||||||
|
* 180.0
|
||||||
|
/ std::f32::consts::PI;
|
||||||
|
let az = (2.0 * (q.w * q.k + q.i * q.j) / (1.0 - 2.0 * (q.j * q.j + q.k * q.k))).atan()
|
||||||
|
* 180.0
|
||||||
|
/ std::f32::consts::PI;
|
||||||
|
(-az, ax)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_orientation(&mut self, theta: f32, phi: f32) {
|
||||||
|
self.theta = theta;
|
||||||
|
self.phi = phi;
|
||||||
|
|
||||||
|
let angle = self.theta.to_radians();
|
||||||
|
let sine = (0.5 * angle).sin();
|
||||||
|
let xrot =
|
||||||
|
UnitQuaternion::from_quaternion(Quaternion::new((0.5 * angle).cos(), sine, 0.0, 0.0));
|
||||||
|
|
||||||
|
let angle = self.phi.to_radians();
|
||||||
|
let sine = (0.5 * angle).sin();
|
||||||
|
let zrot =
|
||||||
|
UnitQuaternion::from_quaternion(Quaternion::new((0.5 * angle).cos(), 0.0, 0.0, sine));
|
||||||
|
|
||||||
|
self.rotation = xrot * zrot;
|
||||||
|
self.model = self.rotation.to_homogeneous();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn project(&self, r: f32, x: f32, y: f32) -> f32 {
|
||||||
|
let d = (x * x + y * y).sqrt();
|
||||||
|
if d < r * 0.70710678118654752440 {
|
||||||
|
(r * r - d * d).sqrt()
|
||||||
|
} else {
|
||||||
|
let t = r / 1.41421356237309504880;
|
||||||
|
t * t / d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rotate(&self, x: f32, y: f32, dx: f32, dy: f32) -> UnitQuaternion<f32> {
|
||||||
|
if dx == 0.0 && dy == 0.0 {
|
||||||
|
return UnitQuaternion::identity();
|
||||||
|
}
|
||||||
|
|
||||||
|
let last = Vector3::new(x, y, self.project(self.trackball_size, x, y));
|
||||||
|
let new = Vector3::new(
|
||||||
|
x + dx,
|
||||||
|
y + dy,
|
||||||
|
self.project(self.trackball_size, x + dx, y + dy),
|
||||||
|
);
|
||||||
|
|
||||||
|
let a = new.cross(&last);
|
||||||
|
let d = last - new;
|
||||||
|
let t = d.norm() / (2.0 * self.trackball_size);
|
||||||
|
let t = t.clamp(-1.0, 1.0);
|
||||||
|
|
||||||
|
let phi = 2.0 * t.asin();
|
||||||
|
UnitQuaternion::from_axis_angle(&Unit::new_normalize(a), phi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl Trackball {
|
||||||
|
// pub fn new() -> Result<Self> {
|
||||||
|
// let model = TrackballModel::new(0.0, 0.0);
|
||||||
|
|
||||||
|
// Ok(Self { model })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn on_mouse_drag(&mut self, x: f32, y: f32, dx: f32, dy: f32) {
|
||||||
|
// self.model.drag_to(x, y, dx, dy);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn model(&self) -> &Matrix4<f32> {
|
||||||
|
// self.model.model()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl AttaWithProgram for TrackballModel {
|
||||||
|
fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let l = program.get_uniform_location(gl, "trackball_model");
|
||||||
|
gl.uniform_matrix_4_f32_slice(l.as_ref(), false, self.model().as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trackball() {
|
||||||
|
let mut trackball = TrackballModel::new(45.0, 45.0, 20.0);
|
||||||
|
println!("{:?}", trackball.model());
|
||||||
|
|
||||||
|
trackball.drag_to(0.0, 10.0, 15.0, 30.0);
|
||||||
|
println!("{:?}", trackball.model());
|
||||||
|
|
||||||
|
// let trackball = Trackball::new().unwrap();
|
||||||
|
// println!("{}", trackball.snippet);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
gi/src/graphics/ty/mod.rs
Normal file
3
gi/src/graphics/ty/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
|
||||||
|
pub trait Ty: Pod {}
|
||||||
16
gi/src/lib.rs
Normal file
16
gi/src/lib.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#![feature(proc_macro_hygiene)]
|
||||||
|
#![allow(unused)]
|
||||||
|
mod camera;
|
||||||
|
mod components;
|
||||||
|
mod data_loader;
|
||||||
|
mod errors;
|
||||||
|
mod font_manager;
|
||||||
|
mod graphics;
|
||||||
|
mod pg;
|
||||||
|
mod setting;
|
||||||
|
mod shaders;
|
||||||
|
pub mod ui;
|
||||||
|
mod utils;
|
||||||
|
pub use pg::App;
|
||||||
|
pub use ui::helper::Helper;
|
||||||
|
pub use utils::*;
|
||||||
65
gi/src/pg/app.rs
Normal file
65
gi/src/pg/app.rs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
use log::*;
|
||||||
|
use std::{cell::RefCell, path::PathBuf, rc::Rc};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
data_loader::Data,
|
||||||
|
errors::*,
|
||||||
|
graphics::{
|
||||||
|
colormap::linear::LinearColormap, threed::Trackball, AttaWithBuffer, AttaWithProgram,
|
||||||
|
Graphics,
|
||||||
|
},
|
||||||
|
ui::helper::{self, Helper},
|
||||||
|
utils::{
|
||||||
|
cache::{Cache, CachedData},
|
||||||
|
resources::{
|
||||||
|
ManagedResource, RcGlBuffer, RcGlFramebuffer, RcGlRcFramebuffer, RcGlRcRenderbuffer,
|
||||||
|
RcGlRcResource, RcGlRenderbuffer, RcGlResource, RcGlVertexArray, GL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use glow::HasContext;
|
||||||
|
|
||||||
|
use super::layout_type;
|
||||||
|
use super::{ModulePackage, Programs};
|
||||||
|
use crate::{font_manager::FontManager, graphics::font::Text};
|
||||||
|
|
||||||
|
pub struct App {
|
||||||
|
gl: GL,
|
||||||
|
context: Context,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub fn new(gl: GL, helper: Helper) -> Result<Self> {
|
||||||
|
let programs = Programs::new(gl.clone()).unwrap();
|
||||||
|
let context = Context::new(gl.clone(), helper, programs);
|
||||||
|
Ok(Self { gl, context })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepare(&mut self) {
|
||||||
|
if let Err(e) = self.context.programs.prepare() {
|
||||||
|
error!("prepare failed: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render<'a>(&'a mut self) {}
|
||||||
|
pub fn destroy(&mut self) {
|
||||||
|
self.context.programs.destroy().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Context {
|
||||||
|
pub gl: GL,
|
||||||
|
pub helper: Helper,
|
||||||
|
pub programs: Programs,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
fn new(gl: GL, helper: Helper, programs: Programs) -> Self {
|
||||||
|
let context = Context {
|
||||||
|
gl,
|
||||||
|
helper,
|
||||||
|
programs: programs,
|
||||||
|
};
|
||||||
|
context
|
||||||
|
}
|
||||||
|
}
|
||||||
242
gi/src/pg/layout_type.rs
Normal file
242
gi/src/pg/layout_type.rs
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
use crate::errors::*;
|
||||||
|
use crate::graphics::ty;
|
||||||
|
use crate::pg::{Context, ModulePackage};
|
||||||
|
use crate::resources::{RcGlFramebuffer, RcGlResource};
|
||||||
|
use crate::ui::typ::{LayoutAttach, MainLoadAttach};
|
||||||
|
use crate::utils::resources::{
|
||||||
|
ManagedResource, RcGlRcFramebuffer, RcGlRcRenderbuffer, RcGlRcResource, Resource, GL,
|
||||||
|
};
|
||||||
|
use glow::{HasContext, NativeRenderbuffer, NativeTexture};
|
||||||
|
|
||||||
|
const RBO_WIDTH: i32 = 3840;
|
||||||
|
const RBO_HEIGHT: i32 = 2160;
|
||||||
|
|
||||||
|
// App Layout Type
|
||||||
|
macro_rules! impl_layout {
|
||||||
|
($({$name:ident => $attach:ty}),+ $(,)?) => {
|
||||||
|
pub enum Layout {
|
||||||
|
$(
|
||||||
|
$name($attach),
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layout {
|
||||||
|
pub fn launch_render_task(&mut self, mut context: &mut Context) -> Result<()>
|
||||||
|
{
|
||||||
|
let programs = &mut context.programs;
|
||||||
|
let gl = &context.gl;
|
||||||
|
let helper = &mut context.helper;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
$(
|
||||||
|
Self::$name(typ) => {
|
||||||
|
typ.render_task(&gl, programs, helper)?;
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append_package(&mut self, package: ModulePackage) {
|
||||||
|
match self {
|
||||||
|
$(
|
||||||
|
Self::$name(typ) => {
|
||||||
|
typ.append(package);
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ViewPort<T: Resource = NativeRenderbuffer> {
|
||||||
|
renderer_size: [f32; 2],
|
||||||
|
main_fbo: RcGlRcFramebuffer,
|
||||||
|
_main_rbo: RcGlRcResource<T>,
|
||||||
|
_main_depth_rbo: RcGlRcResource<NativeRenderbuffer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> ViewPort<T> {
|
||||||
|
pub fn bind(&self, gl: &glow::Context) {
|
||||||
|
self.main_fbo.bind(glow::FRAMEBUFFER);
|
||||||
|
unsafe {
|
||||||
|
gl.viewport(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
self.renderer_size[0] as i32,
|
||||||
|
self.renderer_size[1] as i32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unbind(&self) {
|
||||||
|
self.main_fbo.unbind(glow::FRAMEBUFFER);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_size(&mut self, size: [f32; 2]) -> bool {
|
||||||
|
if size != self.renderer_size {
|
||||||
|
self.renderer_size = size;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fbo(&self) -> &RcGlRcFramebuffer {
|
||||||
|
&self.main_fbo
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> [f32; 2] {
|
||||||
|
self.renderer_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViewPort<NativeTexture> {
|
||||||
|
pub fn new(gl: &GL, depth: bool) -> Self {
|
||||||
|
let main_fbo: RcGlRcFramebuffer = gl.create_resource_rc();
|
||||||
|
let main_rbo: RcGlRcResource<NativeTexture> = gl.create_resource_rc();
|
||||||
|
let main_depth_rbo: RcGlRcResource<NativeRenderbuffer> = gl.create_resource_rc();
|
||||||
|
|
||||||
|
main_fbo.bind(glow::FRAMEBUFFER);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// Color Attachment
|
||||||
|
gl.active_texture(glow::TEXTURE0);
|
||||||
|
main_rbo.bind(glow::TEXTURE_2D);
|
||||||
|
gl.tex_image_2d(
|
||||||
|
glow::TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
glow::RGBA8 as i32,
|
||||||
|
RBO_WIDTH,
|
||||||
|
RBO_HEIGHT,
|
||||||
|
0,
|
||||||
|
glow::RGBA,
|
||||||
|
glow::UNSIGNED_BYTE,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
gl.tex_parameter_i32(
|
||||||
|
glow::TEXTURE_2D,
|
||||||
|
glow::TEXTURE_MIN_FILTER,
|
||||||
|
glow::LINEAR as i32,
|
||||||
|
);
|
||||||
|
gl.tex_parameter_i32(
|
||||||
|
glow::TEXTURE_2D,
|
||||||
|
glow::TEXTURE_MAG_FILTER,
|
||||||
|
glow::LINEAR as i32,
|
||||||
|
);
|
||||||
|
|
||||||
|
gl.framebuffer_texture_2d(
|
||||||
|
glow::FRAMEBUFFER,
|
||||||
|
glow::COLOR_ATTACHMENT0,
|
||||||
|
glow::TEXTURE_2D,
|
||||||
|
Some(main_rbo.native()),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
if depth {
|
||||||
|
// Depth Attachment
|
||||||
|
main_depth_rbo.bind(glow::RENDERBUFFER);
|
||||||
|
gl.renderbuffer_storage(
|
||||||
|
glow::RENDERBUFFER,
|
||||||
|
glow::DEPTH_COMPONENT24,
|
||||||
|
RBO_WIDTH,
|
||||||
|
RBO_HEIGHT,
|
||||||
|
);
|
||||||
|
|
||||||
|
gl.framebuffer_renderbuffer(
|
||||||
|
glow::FRAMEBUFFER,
|
||||||
|
glow::DEPTH_ATTACHMENT,
|
||||||
|
glow::RENDERBUFFER,
|
||||||
|
Some(main_depth_rbo.native()),
|
||||||
|
);
|
||||||
|
main_depth_rbo.unbind(glow::RENDERBUFFER);
|
||||||
|
gl.enable(glow::DEPTH_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.clear_color(0.0, 0.0, 0.0, 0.0);
|
||||||
|
gl.clear(glow::COLOR_BUFFER_BIT);
|
||||||
|
// 检查帧缓冲是否完整
|
||||||
|
if gl.check_framebuffer_status(glow::FRAMEBUFFER) != glow::FRAMEBUFFER_COMPLETE {
|
||||||
|
panic!("Framebuffer is not complete!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main_fbo.unbind(glow::FRAMEBUFFER);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
renderer_size: [0.0, 0.0],
|
||||||
|
main_fbo,
|
||||||
|
_main_rbo: main_rbo,
|
||||||
|
_main_depth_rbo: main_depth_rbo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn texture(&self) -> &RcGlRcResource<NativeTexture> {
|
||||||
|
&self._main_rbo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViewPort<NativeRenderbuffer> {
|
||||||
|
pub fn new(gl: &GL, depth: bool) -> Self {
|
||||||
|
let main_fbo: RcGlRcFramebuffer = gl.create_resource_rc();
|
||||||
|
let main_rbo: RcGlRcResource<NativeRenderbuffer> = gl.create_resource_rc();
|
||||||
|
let main_depth_rbo: RcGlRcResource<NativeRenderbuffer> = gl.create_resource_rc();
|
||||||
|
|
||||||
|
main_fbo.bind(glow::FRAMEBUFFER);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// Color Attachment
|
||||||
|
main_rbo.bind(glow::RENDERBUFFER);
|
||||||
|
gl.renderbuffer_storage(glow::RENDERBUFFER, glow::RGBA8, RBO_WIDTH, RBO_HEIGHT);
|
||||||
|
gl.framebuffer_renderbuffer(
|
||||||
|
glow::FRAMEBUFFER,
|
||||||
|
glow::COLOR_ATTACHMENT0,
|
||||||
|
glow::RENDERBUFFER,
|
||||||
|
Some(main_rbo.native()),
|
||||||
|
);
|
||||||
|
main_rbo.unbind(glow::RENDERBUFFER);
|
||||||
|
|
||||||
|
if depth {
|
||||||
|
// Depth Attachment
|
||||||
|
main_depth_rbo.bind(glow::RENDERBUFFER);
|
||||||
|
gl.renderbuffer_storage(
|
||||||
|
glow::RENDERBUFFER,
|
||||||
|
glow::DEPTH_COMPONENT24,
|
||||||
|
RBO_WIDTH,
|
||||||
|
RBO_HEIGHT,
|
||||||
|
);
|
||||||
|
|
||||||
|
gl.framebuffer_renderbuffer(
|
||||||
|
glow::FRAMEBUFFER,
|
||||||
|
glow::DEPTH_ATTACHMENT,
|
||||||
|
glow::RENDERBUFFER,
|
||||||
|
Some(main_depth_rbo.native()),
|
||||||
|
);
|
||||||
|
main_depth_rbo.unbind(glow::RENDERBUFFER);
|
||||||
|
|
||||||
|
gl.enable(glow::DEPTH_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.clear_color(0.0, 0.0, 0.0, 0.0);
|
||||||
|
gl.clear(glow::COLOR_BUFFER_BIT);
|
||||||
|
// 检查帧缓冲是否完整
|
||||||
|
if gl.check_framebuffer_status(glow::FRAMEBUFFER) != glow::FRAMEBUFFER_COMPLETE {
|
||||||
|
panic!("Framebuffer is not complete!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main_fbo.unbind(glow::FRAMEBUFFER);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
renderer_size: [0.0, 0.0],
|
||||||
|
main_fbo,
|
||||||
|
_main_rbo: main_rbo,
|
||||||
|
_main_depth_rbo: main_depth_rbo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_layout!(
|
||||||
|
{MainLoad => MainLoadAttach},
|
||||||
|
);
|
||||||
174
gi/src/pg/mod.rs
Normal file
174
gi/src/pg/mod.rs
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
mod app;
|
||||||
|
use femtovg::renderer::OpenGl;
|
||||||
|
use femtovg::Canvas;
|
||||||
|
use glow::HasContext;
|
||||||
|
use layout_type::ViewPort;
|
||||||
|
pub mod layout_type;
|
||||||
|
mod modules;
|
||||||
|
|
||||||
|
use crate::font_manager::FontManager;
|
||||||
|
use crate::graphics::collections::agg_fast_path::AggFastPath;
|
||||||
|
use crate::graphics::font::Text;
|
||||||
|
use crate::graphics::ppi::PPI;
|
||||||
|
use crate::graphics::threed::Trackball;
|
||||||
|
use crate::graphics::transforms::plane::PlaneTrans;
|
||||||
|
use crate::graphics::{AttaWithProgram, AttachWithIO};
|
||||||
|
use crate::ui::operation::Operation;
|
||||||
|
use crate::ui::typ::LayoutAttach;
|
||||||
|
use crate::utils::cache::CachedData;
|
||||||
|
use crate::utils::resources::GL;
|
||||||
|
use crate::{errors::*, graphics::Graphics};
|
||||||
|
pub use app::{App, Context};
|
||||||
|
pub use modules::{Module, ModuleCursor, ModuleData, PPIModule, PPIPackage};
|
||||||
|
use std::sync::atomic::AtomicUsize;
|
||||||
|
|
||||||
|
static MODULE_PACKAGE_ID: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
|
pub(super) struct Programs {
|
||||||
|
gl: GL,
|
||||||
|
_ppi: PPI,
|
||||||
|
_text: Text,
|
||||||
|
_line: AggFastPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Programs {
|
||||||
|
pub fn new(gl: GL) -> Result<Self> {
|
||||||
|
let font_manager = FontManager::new()?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
gl: gl.clone(),
|
||||||
|
_ppi: PPI::new()?,
|
||||||
|
_text: Text::new(gl, font_manager)?,
|
||||||
|
_line: AggFastPath::new()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepare(&mut self) -> Result<()> {
|
||||||
|
self._ppi.program().compile(&self.gl)?;
|
||||||
|
self._line.program().compile(&self.gl)?;
|
||||||
|
self._text.program_mut().compile(&self.gl)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn ppi<'b>(&'b mut self) -> modules::PPIModule<'b, 'gl> {
|
||||||
|
// modules::PPIModule::new(&self.gl, &mut self._ppi, &mut self._text, &mut self._line).unwrap()
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn destroy(&mut self) -> Result<()> {
|
||||||
|
self._ppi.destroy(&self.gl)?;
|
||||||
|
self._text.destroy(&self.gl)?;
|
||||||
|
self._line.destroy(&self.gl)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn load_data(
|
||||||
|
// &mut self,
|
||||||
|
// data: &CachedData<crate::data_loader::Data>,
|
||||||
|
// ) -> Result<ModulePackage<'gl>> {
|
||||||
|
// let cdata = data.borrow();
|
||||||
|
// if cdata.blocks.len() == 0 {
|
||||||
|
// return Err(Error::InvalidDataType);
|
||||||
|
// }
|
||||||
|
// let first_block = cdata.blocks.first().unwrap();
|
||||||
|
|
||||||
|
// match first_block.coord_type {
|
||||||
|
// crate::data_loader::CoordType::Polar { .. } => {
|
||||||
|
// // let package: PPIPackage<'gl> = self.ppi().load_data(data)?;
|
||||||
|
// // return Ok(package.into());
|
||||||
|
// }
|
||||||
|
// _ => {
|
||||||
|
// unimplemented!("Only polar data is supported for now");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn draw_modules(
|
||||||
|
// &mut self,
|
||||||
|
// modules: &mut ModulePackage<'gl>,
|
||||||
|
// operation: &Operation<PlaneTrans>,
|
||||||
|
// viewport: &ViewPort,
|
||||||
|
// ) -> Result<()> {
|
||||||
|
// match &mut modules.modules {
|
||||||
|
// _ModulePackage::PPI(ppi) => {
|
||||||
|
// self.ppi().render(ppi, operation, viewport)?;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// modules.need_update = false;
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Data<'a> {
|
||||||
|
Data(&'a crate::data_loader::Data),
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_module_data {
|
||||||
|
($({$t:ty => $b: tt}),+) => {
|
||||||
|
$(
|
||||||
|
impl ModuleData for $t {
|
||||||
|
fn to_data(&self) -> Data {
|
||||||
|
Data::$b(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_module_package {
|
||||||
|
($({$t:ty => $b: tt}),+) => {
|
||||||
|
$(
|
||||||
|
|
||||||
|
impl From<$t> for _ModulePackage {
|
||||||
|
fn from(t: $t) -> Self {
|
||||||
|
_ModulePackage::$b(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_module_data!({
|
||||||
|
crate::data_loader::Data => Data
|
||||||
|
});
|
||||||
|
|
||||||
|
pub struct ModulePackage {
|
||||||
|
id: usize,
|
||||||
|
pub need_update: bool,
|
||||||
|
modules: _ModulePackage,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum _ModulePackage {
|
||||||
|
PPI(PPIPackage),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_module_package!({
|
||||||
|
PPIPackage => PPI
|
||||||
|
});
|
||||||
|
|
||||||
|
impl<T> From<T> for ModulePackage
|
||||||
|
where
|
||||||
|
T: ModuleCursor + Into<_ModulePackage>,
|
||||||
|
{
|
||||||
|
fn from(t: T) -> Self {
|
||||||
|
Self {
|
||||||
|
id: MODULE_PACKAGE_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst),
|
||||||
|
modules: t.into(),
|
||||||
|
need_update: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for ModulePackage {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.id == other.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModulePackage {
|
||||||
|
pub fn dirty(&mut self) {
|
||||||
|
self.need_update = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
120
gi/src/pg/modules/mod.rs
Normal file
120
gi/src/pg/modules/mod.rs
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
use crate::{
|
||||||
|
graphics::AttachWithIO,
|
||||||
|
resources::{RcGlRcBuffer, RcGlRcVertexArray},
|
||||||
|
ui::{
|
||||||
|
operation::{self, Operation},
|
||||||
|
typ::CameraOP,
|
||||||
|
},
|
||||||
|
utils::{
|
||||||
|
cache::CachedData,
|
||||||
|
resources::{ManagedResource, RcGlBuffer, RcGlVertexArray},
|
||||||
|
},
|
||||||
|
GL,
|
||||||
|
};
|
||||||
|
use femtovg::{renderer::OpenGl, Canvas};
|
||||||
|
use glow::{HasContext, NativeBuffer, NativeVertexArray};
|
||||||
|
mod ppi;
|
||||||
|
use crate::errors::*;
|
||||||
|
pub use ppi::{PPIModule, PPIPackage};
|
||||||
|
|
||||||
|
use super::{layout_type::ViewPort, Data};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Attach {
|
||||||
|
gl: GL,
|
||||||
|
pub vao: RcGlRcVertexArray,
|
||||||
|
pub vbo: RcGlRcBuffer,
|
||||||
|
pub ebo: Option<RcGlRcBuffer>,
|
||||||
|
pub len: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Attach {
|
||||||
|
fn new(
|
||||||
|
gl: GL,
|
||||||
|
vao: NativeVertexArray,
|
||||||
|
vbo: NativeBuffer,
|
||||||
|
ebo: Option<NativeBuffer>,
|
||||||
|
len: Option<i32>,
|
||||||
|
) -> Self {
|
||||||
|
let vao = gl.create_resource_rc_with(vao);
|
||||||
|
let vbo = gl.create_resource_rc_with(vbo);
|
||||||
|
let ebo = ebo.map(|ebo| gl.create_resource_rc_with(ebo));
|
||||||
|
Self {
|
||||||
|
gl,
|
||||||
|
vao,
|
||||||
|
vbo,
|
||||||
|
ebo,
|
||||||
|
len: len.unwrap_or(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind_self(&self) {
|
||||||
|
self.vao.bind(glow::VERTEX_ARRAY);
|
||||||
|
self.vbo.bind(glow::ARRAY_BUFFER);
|
||||||
|
if let Some(ebo) = self.ebo.as_ref() {
|
||||||
|
ebo.bind(glow::ELEMENT_ARRAY_BUFFER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind_self(&self) {
|
||||||
|
self.vao.unbind(glow::VERTEX_ARRAY);
|
||||||
|
self.vbo.unbind(glow::ARRAY_BUFFER);
|
||||||
|
if let Some(ebo) = self.ebo.as_ref() {
|
||||||
|
ebo.unbind(glow::ELEMENT_ARRAY_BUFFER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind_data(&mut self, vbo: &Vec<f32>, ebo: Option<&Vec<u32>>, len: i32, usage: u32) {
|
||||||
|
use bytemuck::cast_slice;
|
||||||
|
self.vbo.bind(glow::ARRAY_BUFFER);
|
||||||
|
unsafe {
|
||||||
|
self.gl
|
||||||
|
.buffer_data_u8_slice(glow::ARRAY_BUFFER, cast_slice(&vbo), usage);
|
||||||
|
if let Some(ebo) = ebo {
|
||||||
|
self.gl.bind_buffer(
|
||||||
|
glow::ELEMENT_ARRAY_BUFFER,
|
||||||
|
Some(self.ebo.as_ref().unwrap().native()),
|
||||||
|
);
|
||||||
|
self.gl
|
||||||
|
.buffer_data_u8_slice(glow::ELEMENT_ARRAY_BUFFER, cast_slice(&ebo), usage);
|
||||||
|
|
||||||
|
self.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.vbo.unbind(glow::ARRAY_BUFFER);
|
||||||
|
self.len = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> i32 {
|
||||||
|
self.len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Module: Sized {
|
||||||
|
type Cursor: ModuleCursor;
|
||||||
|
type Data;
|
||||||
|
type Operation: AttachWithIO;
|
||||||
|
|
||||||
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
cursor: &mut Self::Cursor,
|
||||||
|
operation: &Operation<Self::Operation>,
|
||||||
|
viewport: &ViewPort,
|
||||||
|
) -> Result<()>;
|
||||||
|
|
||||||
|
fn load_data<'dt>(&self, data: &CachedData<Self::Data>) -> Result<Self::Cursor>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ModuleData: Sized {
|
||||||
|
fn to_data(&self) -> Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ModuleCursor {
|
||||||
|
type Module<'rf, 'gl: 'rf>: Module;
|
||||||
|
type Data: ModuleData;
|
||||||
|
type Config;
|
||||||
|
|
||||||
|
fn set_config<F>(&mut self, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self::Config);
|
||||||
|
}
|
||||||
406
gi/src/pg/modules/ppi.rs
Normal file
406
gi/src/pg/modules/ppi.rs
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
use crate::{
|
||||||
|
graphics::{
|
||||||
|
collections::agg_fast_path::{AggFastPath, AggFastPathConfig, Path},
|
||||||
|
font::{Anchor, FontConfig, LineStyle, PositionText, Text, TextLine},
|
||||||
|
ppi::{PPIConfig, PPI},
|
||||||
|
transforms::plane::PlaneTrans,
|
||||||
|
AttaWithBuffer, Graphics,
|
||||||
|
},
|
||||||
|
GL,
|
||||||
|
};
|
||||||
|
|
||||||
|
use core::f32;
|
||||||
|
use glow::HasContext;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use tracker::track;
|
||||||
|
|
||||||
|
use crate::font_manager::{FontSize, FontStyle};
|
||||||
|
|
||||||
|
use crate::pg::layout_type::ViewPort;
|
||||||
|
use crate::ui::operation::{self, Operation};
|
||||||
|
use crate::{
|
||||||
|
data_loader::Data,
|
||||||
|
errors::*,
|
||||||
|
font_manager::FontManager,
|
||||||
|
ui::typ,
|
||||||
|
utils::{cache::CachedData, resources::ManagedResource},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{Attach, Module, ModuleCursor};
|
||||||
|
pub struct PPIModule<'b, 'gl: 'b> {
|
||||||
|
gl: &'gl GL,
|
||||||
|
ppi_program: &'b mut PPI,
|
||||||
|
line_program: &'b mut AggFastPath,
|
||||||
|
text_program: &'b mut Text,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'b, 'a: 'b> PPIModule<'b, 'a> {
|
||||||
|
pub fn new(
|
||||||
|
gl: &'a GL,
|
||||||
|
ppi: &'b mut PPI,
|
||||||
|
text: &'b mut Text,
|
||||||
|
line: &'b mut AggFastPath,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let config = PPIConfig::default();
|
||||||
|
Ok(Self {
|
||||||
|
gl,
|
||||||
|
ppi_program: ppi,
|
||||||
|
text_program: text,
|
||||||
|
line_program: line,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind_ppi_pg(
|
||||||
|
&self,
|
||||||
|
attach: &mut Attach,
|
||||||
|
data: &Data,
|
||||||
|
config: &PPIModuleConfig,
|
||||||
|
) -> Result<()> {
|
||||||
|
let (vbo, ebo, len) = self
|
||||||
|
.ppi_program
|
||||||
|
.bake(&self.gl, data, &config.to_ppi_config())?;
|
||||||
|
attach.bind_data(&vbo, ebo.as_ref(), len, glow::DYNAMIC_DRAW);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind_line_pg(
|
||||||
|
&self,
|
||||||
|
attach: &mut Attach,
|
||||||
|
data: &Data,
|
||||||
|
config: &PPIModuleConfig,
|
||||||
|
) -> Result<()> {
|
||||||
|
let raw_config = config.to_line_config();
|
||||||
|
|
||||||
|
// Will be changed in the future
|
||||||
|
let outskirt = 10.0;
|
||||||
|
|
||||||
|
let mut paths = vec![];
|
||||||
|
|
||||||
|
let range_line_num = config.range_line_num;
|
||||||
|
let r = outskirt / range_line_num as f32;
|
||||||
|
let seg_num = 200;
|
||||||
|
let angle = 2f32 * f32::consts::PI / seg_num as f32;
|
||||||
|
|
||||||
|
for range in 1..=range_line_num {
|
||||||
|
// Create the path
|
||||||
|
let mut path = Path::new(true);
|
||||||
|
let r = r * range as f32;
|
||||||
|
// Draw the circle
|
||||||
|
for seg in 0..seg_num {
|
||||||
|
let angle = angle * seg as f32;
|
||||||
|
let x = (angle.cos() * r) as f32;
|
||||||
|
let y = (angle.sin() * r) as f32;
|
||||||
|
path.push([x, y, 0.01]);
|
||||||
|
}
|
||||||
|
path.finish();
|
||||||
|
paths.push(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ath_lin_num = config.ath_line_num;
|
||||||
|
|
||||||
|
let a = 2.0 * f32::consts::PI / ath_lin_num as f32;
|
||||||
|
for _a in 0..=ath_lin_num {
|
||||||
|
let mut path = Path::new(false);
|
||||||
|
let x = (a * _a as f32).cos() * outskirt;
|
||||||
|
let y = (a * _a as f32).sin() * outskirt;
|
||||||
|
path.push([0.0, 0.0, 0.01]);
|
||||||
|
path.push([x, y, 0.01]);
|
||||||
|
path.finish();
|
||||||
|
paths.push(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.vertical_axis {
|
||||||
|
let mut path = Path::new(false);
|
||||||
|
path.push([0.0, 0.0, 0.0]);
|
||||||
|
path.push([0.0, 0.0, outskirt]);
|
||||||
|
path.finish();
|
||||||
|
paths.push(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (vbo, ebo, len) = self.line_program.bake(&self.gl, &paths, &raw_config)?;
|
||||||
|
attach.bind_data(&vbo, ebo.as_ref(), len, glow::STATIC_DRAW);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind_tick(&self, attach: &mut Attach, data: &Data, config: &PPIModuleConfig) -> Result<()> {
|
||||||
|
let font_style = config.to_font_config();
|
||||||
|
|
||||||
|
match font_style {
|
||||||
|
FontConfig::Textline(line_style, font_style) => {
|
||||||
|
let new_text = TextLine::new("Hello,World", Some(font_style), None);
|
||||||
|
let position_text =
|
||||||
|
PositionText::new(new_text, [0.0, 0.0, 0.0], Anchor::BottomCenter);
|
||||||
|
let text_pg_config = config.to_font_config();
|
||||||
|
let (vbo, ebo, len) =
|
||||||
|
self.text_program
|
||||||
|
.bake(&self.gl, &position_text, &text_pg_config)?;
|
||||||
|
attach.bind_data(&vbo, ebo.as_ref(), len, glow::STATIC_DRAW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'b, 'a: 'b> Module for PPIModule<'b, 'a> {
|
||||||
|
type Cursor = PPIPackage;
|
||||||
|
type Data = Data;
|
||||||
|
type Operation = PlaneTrans;
|
||||||
|
|
||||||
|
fn render<'dt>(
|
||||||
|
&mut self,
|
||||||
|
cursor: &mut Self::Cursor,
|
||||||
|
operation: &Operation<Self::Operation>,
|
||||||
|
viewport: &ViewPort,
|
||||||
|
) -> Result<()> {
|
||||||
|
// Mount PPI Program
|
||||||
|
self.ppi_program.mount(&self.gl)?;
|
||||||
|
// Deal with the operation
|
||||||
|
operation.attach_with_program(&self.gl, self.ppi_program.program());
|
||||||
|
// PPI Program
|
||||||
|
let ppi_attach = &mut cursor.ppi_attach;
|
||||||
|
let data = &cursor.ppi_data.borrow();
|
||||||
|
let config = &mut cursor.ppi_config;
|
||||||
|
|
||||||
|
// Update the config
|
||||||
|
self.ppi_program
|
||||||
|
.set_config(&self.gl, &config.to_ppi_config())?;
|
||||||
|
|
||||||
|
// if the layer is changed, we need to rebind the data
|
||||||
|
if config.changed_layer() {
|
||||||
|
self.bind_ppi_pg(ppi_attach, data, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.changed_range_line_num()
|
||||||
|
|| config.changed_ath_line_num()
|
||||||
|
|| config.changed_vertical_axis()
|
||||||
|
{
|
||||||
|
self.bind_line_pg(&mut cursor.line_attach, data, config)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PPI Draw
|
||||||
|
ppi_attach.bind_self();
|
||||||
|
self.ppi_program.draw(&self.gl, ppi_attach.len())?;
|
||||||
|
ppi_attach.unbind_self();
|
||||||
|
|
||||||
|
// Unmount PPI Program
|
||||||
|
self.ppi_program.unmount(&self.gl)?;
|
||||||
|
|
||||||
|
// Mount Line Program
|
||||||
|
self.line_program.mount(&self.gl)?;
|
||||||
|
|
||||||
|
// Deal with the operation
|
||||||
|
operation.attach_with_program(&self.gl, self.line_program.program());
|
||||||
|
|
||||||
|
// Set the viewport, this is important
|
||||||
|
self.line_program.set_viewport(&self.gl, viewport.size());
|
||||||
|
|
||||||
|
// Update the config
|
||||||
|
self.line_program
|
||||||
|
.set_config(&self.gl, &config.to_line_config());
|
||||||
|
|
||||||
|
// PPI Tick Draw
|
||||||
|
let attach = &mut cursor.line_attach;
|
||||||
|
attach.bind_self();
|
||||||
|
self.line_program.draw(&self.gl, attach.len())?;
|
||||||
|
attach.unbind_self();
|
||||||
|
|
||||||
|
self.line_program.unmount(&self.gl)?;
|
||||||
|
|
||||||
|
self.text_program.mount(&self.gl);
|
||||||
|
|
||||||
|
let tick_attach = &mut cursor.tick_attach;
|
||||||
|
// Deal with the operation
|
||||||
|
operation.attach_with_program(&self.gl, self.text_program.program_mut());
|
||||||
|
self.text_program.set_viewport(&self.gl, viewport.size());
|
||||||
|
self.text_program
|
||||||
|
.set_config(&self.gl, &config.to_font_config());
|
||||||
|
|
||||||
|
tick_attach.bind_self();
|
||||||
|
self.text_program.draw(&self.gl, tick_attach.len())?;
|
||||||
|
tick_attach.unbind_self();
|
||||||
|
|
||||||
|
config.reset();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_data<'dt>(&self, data: &CachedData<Self::Data>) -> Result<Self::Cursor> {
|
||||||
|
let _data = data.borrow();
|
||||||
|
|
||||||
|
// Check if the data is valid
|
||||||
|
if _data.blocks.len() == 0 {
|
||||||
|
return Err(Error::InvalidDataType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init the memory
|
||||||
|
let (vao, vbo, ebo) = self.ppi_program.init(&self.gl);
|
||||||
|
let mut ppi_attach = Attach::new(self.gl.clone(), vao, vbo, ebo, None);
|
||||||
|
|
||||||
|
// Get the data info
|
||||||
|
let (r, a, t, max_layer, unvalid) = self.ppi_program.data_info(&_data)?;
|
||||||
|
|
||||||
|
// Find the color map
|
||||||
|
// let cmap = SETTING.find(&t);
|
||||||
|
|
||||||
|
// let cmap = None;
|
||||||
|
|
||||||
|
// Check if the color map is valid
|
||||||
|
// if cmap.is_none() {
|
||||||
|
// return Err(Error::InvalidDataType);
|
||||||
|
// }
|
||||||
|
// let cmap = cmap.unwrap();
|
||||||
|
|
||||||
|
// Init the memory for the line program
|
||||||
|
let (vao, vbo, ebo) = self.line_program.init(&self.gl);
|
||||||
|
let mut line_attach = Attach::new(self.gl.clone(), vao, vbo, ebo, None);
|
||||||
|
|
||||||
|
// Tick Label
|
||||||
|
let (vao, vbo, ebo) = self.text_program.init(&self.gl);
|
||||||
|
let mut tick_attach = Attach::new(self.gl.clone(), vao, vbo, ebo, None);
|
||||||
|
|
||||||
|
let mut config = PPIModuleConfig::default();
|
||||||
|
config.rdpi = r;
|
||||||
|
config.adpi = a;
|
||||||
|
config.max_layer = max_layer;
|
||||||
|
config.unvalid_value = unvalid;
|
||||||
|
// config.colors = cmap.color()?;
|
||||||
|
// config.color_range = cmap.value_range();
|
||||||
|
|
||||||
|
// Bind the data
|
||||||
|
self.bind_ppi_pg(&mut ppi_attach, &data.borrow(), &config);
|
||||||
|
self.bind_line_pg(&mut line_attach, &data.borrow(), &config);
|
||||||
|
// self.bind_tick(&mut tick_attach, &data.borrow(), &config);
|
||||||
|
|
||||||
|
Ok(PPIPackage::new(
|
||||||
|
config,
|
||||||
|
ppi_attach,
|
||||||
|
line_attach,
|
||||||
|
tick_attach,
|
||||||
|
data,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PPIPackage {
|
||||||
|
draw_helper: bool,
|
||||||
|
ppi_config: PPIModuleConfig,
|
||||||
|
ppi_attach: Attach,
|
||||||
|
line_attach: Attach,
|
||||||
|
tick_attach: Attach,
|
||||||
|
ppi_data: CachedData<Data>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PPIPackage {
|
||||||
|
fn new(
|
||||||
|
ppi_config: PPIModuleConfig,
|
||||||
|
ppi_attach: Attach,
|
||||||
|
line_attach: Attach,
|
||||||
|
tick_attach: Attach,
|
||||||
|
data: &CachedData<Data>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
draw_helper: true,
|
||||||
|
ppi_config,
|
||||||
|
ppi_attach,
|
||||||
|
line_attach,
|
||||||
|
tick_attach,
|
||||||
|
ppi_data: Rc::clone(data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track]
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct PPIModuleConfig {
|
||||||
|
pub ticks: bool,
|
||||||
|
pub line_color: [f32; 4],
|
||||||
|
pub line_width: f32,
|
||||||
|
pub line_antialias: f32,
|
||||||
|
pub range_line_num: usize,
|
||||||
|
pub ath_line_num: usize,
|
||||||
|
pub vertical_axis: bool,
|
||||||
|
pub layer: usize,
|
||||||
|
pub colors: Vec<[u8; 4]>,
|
||||||
|
pub color_range: [f32; 2],
|
||||||
|
pub is_three_d: bool,
|
||||||
|
|
||||||
|
pub tick_label_color: [f32; 4],
|
||||||
|
pub tick_label_size: f32,
|
||||||
|
#[do_not_track]
|
||||||
|
pub unvalid_value: f32,
|
||||||
|
#[do_not_track]
|
||||||
|
pub max_layer: usize,
|
||||||
|
#[do_not_track]
|
||||||
|
pub rdpi: f32,
|
||||||
|
#[do_not_track]
|
||||||
|
pub adpi: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PPIModuleConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
ticks: true,
|
||||||
|
line_color: [1.0, 1.0, 1.0, 1.0],
|
||||||
|
line_width: 1.5,
|
||||||
|
line_antialias: 0.5,
|
||||||
|
layer: 0,
|
||||||
|
vertical_axis: false,
|
||||||
|
range_line_num: 5,
|
||||||
|
ath_line_num: 6,
|
||||||
|
colors: vec![],
|
||||||
|
color_range: [0.0, 0.0],
|
||||||
|
is_three_d: true,
|
||||||
|
unvalid_value: 0.0,
|
||||||
|
tick_label_color: [1.0, 1.0, 1.0, 1.0],
|
||||||
|
tick_label_size: 24.0,
|
||||||
|
max_layer: 0,
|
||||||
|
rdpi: 0.0,
|
||||||
|
adpi: 0.0,
|
||||||
|
tracker: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PPIModuleConfig {
|
||||||
|
fn to_ppi_config(&self) -> PPIConfig {
|
||||||
|
PPIConfig {
|
||||||
|
unvalid_value: self.unvalid_value,
|
||||||
|
color_range: self.color_range,
|
||||||
|
colors: self.colors.clone(),
|
||||||
|
layer: self.layer,
|
||||||
|
rdpi: self.rdpi,
|
||||||
|
adpi: self.adpi,
|
||||||
|
three_d: self.is_three_d,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_line_config(&self) -> AggFastPathConfig {
|
||||||
|
AggFastPathConfig {
|
||||||
|
color: self.line_color,
|
||||||
|
linewidth: self.line_width,
|
||||||
|
antialias: self.line_antialias,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_font_config(&self) -> FontConfig {
|
||||||
|
let line_style = LineStyle::default();
|
||||||
|
let mut font_style = FontStyle::default();
|
||||||
|
font_style.size = FontSize::Absolute(self.tick_label_size);
|
||||||
|
font_style.color = self.tick_label_color;
|
||||||
|
FontConfig::Textline(line_style, font_style)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleCursor for PPIPackage {
|
||||||
|
type Module<'rf, 'gl: 'rf> = PPIModule<'rf, 'gl>;
|
||||||
|
type Config = PPIModuleConfig;
|
||||||
|
type Data = Data;
|
||||||
|
|
||||||
|
fn set_config<F>(&mut self, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self::Config),
|
||||||
|
{
|
||||||
|
f(&mut self.ppi_config);
|
||||||
|
}
|
||||||
|
}
|
||||||
117
gi/src/setting.rs
Normal file
117
gi/src/setting.rs
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
use crate::errors::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{
|
||||||
|
env,
|
||||||
|
fs::{read, read_to_string},
|
||||||
|
io::Write,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{data_loader::DataType, utils::color_tools::hex_to_rgba_u8};
|
||||||
|
|
||||||
|
macro_rules! find_cmap {
|
||||||
|
($c:ident,$find_on:ident,$({$b:tt => $name:literal}),*) => {
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cmap = None;
|
||||||
|
match $c {
|
||||||
|
$(
|
||||||
|
$b => {
|
||||||
|
let find_v = $find_on.iter().find(|cb| cb.type_name == $name).map(|cb| cb);
|
||||||
|
cmap = find_v;
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
cmap
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Setting {
|
||||||
|
pub cmap: Vec<CB>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Setting {
|
||||||
|
pub fn find(&self, name: &DataType) -> Option<&CB> {
|
||||||
|
let cmap = &self.cmap;
|
||||||
|
use DataType::*;
|
||||||
|
find_cmap!(
|
||||||
|
name, cmap,
|
||||||
|
{DBZ => "DBZ"},
|
||||||
|
{VEl => "VEL"},
|
||||||
|
{VIL => "VIL"}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Setting {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let current_dir = env::current_dir().unwrap();
|
||||||
|
|
||||||
|
// if !current_dir.join("radar.toml").exists() {
|
||||||
|
// let default_config = Asset::get("radar.toml").unwrap();
|
||||||
|
|
||||||
|
// let mut folder_path = current_dir.clone();
|
||||||
|
// let mut conf = folder_path.join("radar.toml");
|
||||||
|
|
||||||
|
// let mut file = std::fs::File::create_new(&conf).unwrap();
|
||||||
|
// file.write_all(&default_config.data).unwrap();
|
||||||
|
// }
|
||||||
|
|
||||||
|
let file = read_to_string(current_dir.join("radar.toml")).unwrap();
|
||||||
|
let setting: Setting = toml::from_str(&file).unwrap();
|
||||||
|
setting
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct CB {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub type_name: String,
|
||||||
|
pub colors: Vec<String>,
|
||||||
|
pub levels: Vec<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CB {
|
||||||
|
pub fn value_range(&self) -> [f32; 2] {
|
||||||
|
let mut range = [0.0, 0.0];
|
||||||
|
let levels = &self.levels;
|
||||||
|
range[0] = levels[0];
|
||||||
|
range[1] = levels[levels.len() - 1];
|
||||||
|
range
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn color(&self) -> Result<Vec<[u8; 4]>> {
|
||||||
|
if self.colors.len() != self.levels.len() - 1 {
|
||||||
|
return Err(Error::SettingError("Color and level mismatch".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = self
|
||||||
|
.colors
|
||||||
|
.iter()
|
||||||
|
.map(|v| hex_to_rgba_u8(v))
|
||||||
|
.collect::<std::result::Result<Vec<_>, String>>()
|
||||||
|
.map_err(|v| Error::SettingError(v.to_string()))?;
|
||||||
|
|
||||||
|
let mut span = Vec::with_capacity(self.levels.len() - 1);
|
||||||
|
|
||||||
|
for idx in 0..self.levels.len() - 1 {
|
||||||
|
let start = self.levels[idx];
|
||||||
|
let end = self.levels[idx + 1];
|
||||||
|
let range = end - start;
|
||||||
|
span.push(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
let range = self.value_range();
|
||||||
|
let all_range = range[1] - range[0];
|
||||||
|
|
||||||
|
for (level, r) in span.iter().zip(result.iter_mut()) {
|
||||||
|
r[3] = ((*level / all_range) * 255.0) as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
126
gi/src/shaders/agg_path.rs
Normal file
126
gi/src/shaders/agg_path.rs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
use glsl::syntax::ShaderStage;
|
||||||
|
use glsl::syntax::TranslationUnit;
|
||||||
|
use glsl::transpiler::glsl::show_translation_unit;
|
||||||
|
use glsl_quasiquote::glsl;
|
||||||
|
|
||||||
|
use crate::impl_code_piece;
|
||||||
|
|
||||||
|
use super::{trackball::Trackball, CodePiece};
|
||||||
|
|
||||||
|
pub struct AggPathVertex(ShaderStage);
|
||||||
|
|
||||||
|
pub struct AggPathFragment(ShaderStage);
|
||||||
|
|
||||||
|
impl AggPathVertex {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut transform = Trackball::new().0;
|
||||||
|
let raw = glsl! {
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 prev;
|
||||||
|
layout(location = 1) in vec3 curr;
|
||||||
|
layout(location = 2) in vec3 next;
|
||||||
|
layout(location = 3) in float id;
|
||||||
|
|
||||||
|
uniform float antialias;
|
||||||
|
uniform float thickness;
|
||||||
|
uniform vec2 viewport;
|
||||||
|
uniform vec4 color;
|
||||||
|
|
||||||
|
out float v_distance;
|
||||||
|
out vec4 v_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 ndc_prev = transform(position(prev));
|
||||||
|
vec4 ndc_curr = transform(position(curr));
|
||||||
|
vec4 ndc_next = transform(position(next));
|
||||||
|
|
||||||
|
// screen space
|
||||||
|
vec2 screen_prev = viewport * ((ndc_prev.xy/ndc_prev.w) + 1.0)/2.0;
|
||||||
|
vec2 screen_curr = viewport * ((ndc_curr.xy/ndc_curr.w) + 1.0)/2.0;
|
||||||
|
vec2 screen_next = viewport * ((ndc_next.xy/ndc_next.w) + 1.0)/2.0;
|
||||||
|
|
||||||
|
|
||||||
|
vec2 P;
|
||||||
|
float w = thickness/2.0 + antialias;
|
||||||
|
|
||||||
|
if (prev.xy == curr.xy) {
|
||||||
|
vec2 v = normalize(screen_next.xy - screen_curr.xy);
|
||||||
|
vec2 normal = normalize(vec2(-v.y, v.x));
|
||||||
|
P = screen_curr.xy + w*normal*id;
|
||||||
|
} else if (curr.xy == next.xy) {
|
||||||
|
vec2 v = normalize(screen_curr.xy - screen_prev.xy);
|
||||||
|
vec2 normal = normalize(vec2(-v.y, v.x));
|
||||||
|
P = screen_curr.xy + w*normal*id;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
vec2 v0 = normalize(screen_curr.xy - screen_prev.xy);
|
||||||
|
vec2 v1 = normalize(screen_next.xy - screen_curr.xy);
|
||||||
|
vec2 normal = normalize(vec2(-v0.y, v0.x));
|
||||||
|
vec2 tangent = normalize(v0 + v1);
|
||||||
|
|
||||||
|
vec2 miter = vec2(-tangent.y, tangent.x);
|
||||||
|
|
||||||
|
float l = abs(w/dot(miter,normal));
|
||||||
|
P = screen_curr.xy + l*miter*sign(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
v_color = color;
|
||||||
|
|
||||||
|
if (abs(id) > 1.5) v_color.a = 0.0;
|
||||||
|
v_distance = w * id;
|
||||||
|
|
||||||
|
// Back to NDC coordinates
|
||||||
|
gl_Position = vec4(2.0*P/viewport-1.0, ndc_curr.z / ndc_curr.w, 1.0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
transform.extend(raw);
|
||||||
|
|
||||||
|
Self(transform)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AggPathFragment {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let raw = glsl! {
|
||||||
|
uniform float antialias;
|
||||||
|
uniform float thickness;
|
||||||
|
|
||||||
|
in float v_distance;
|
||||||
|
in vec4 v_color;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
|
||||||
|
vec4 stroke(float distance, float linewidth, float antialias, vec4 fg_color)
|
||||||
|
{
|
||||||
|
vec4 frag_color;
|
||||||
|
float t = linewidth/2.0 - antialias;
|
||||||
|
float signed_distance = distance;
|
||||||
|
float border_distance = abs(signed_distance) - t;
|
||||||
|
float alpha = border_distance/antialias;
|
||||||
|
alpha = exp(-alpha*alpha);
|
||||||
|
|
||||||
|
if( border_distance < 0.0 )
|
||||||
|
frag_color = fg_color;
|
||||||
|
else
|
||||||
|
frag_color = vec4(fg_color.rgb, fg_color.a * alpha);
|
||||||
|
|
||||||
|
return frag_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 stroke(float distance, float linewidth, float antialias, vec4 fg_color, vec4 bg_color)
|
||||||
|
{
|
||||||
|
return stroke(distance, linewidth, antialias, fg_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
if (v_color.a == 0) {discard;}
|
||||||
|
fragColor = stroke(v_distance, thickness, antialias, v_color);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Self(raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_code_piece!(AggPathVertex, 0);
|
||||||
|
impl_code_piece!(AggPathFragment, 0);
|
||||||
50
gi/src/shaders/colormap.rs
Normal file
50
gi/src/shaders/colormap.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
use super::CodePiece;
|
||||||
|
use crate::impl_code_piece;
|
||||||
|
use glsl::syntax::ShaderStage;
|
||||||
|
use glsl::syntax::TranslationUnit;
|
||||||
|
use glsl::transpiler::glsl::show_translation_unit;
|
||||||
|
use glsl_quasiquote::glsl;
|
||||||
|
|
||||||
|
pub struct ColorMap(pub ShaderStage);
|
||||||
|
|
||||||
|
impl ColorMap {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let raw = glsl! {
|
||||||
|
uniform sampler1D colormap;
|
||||||
|
uniform vec4 colormap_conf;
|
||||||
|
|
||||||
|
float find_idx(float ratio) {
|
||||||
|
float i = 0;
|
||||||
|
float count = colormap_conf.z - 1.0;
|
||||||
|
float sum = 0.0;
|
||||||
|
|
||||||
|
while (ratio > sum) {
|
||||||
|
sum += texture(colormap, float(i++) / count).w;
|
||||||
|
}
|
||||||
|
return i / count;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 linear_colormap(float value)
|
||||||
|
{
|
||||||
|
float vmin = colormap_conf.x;
|
||||||
|
float vmax = colormap_conf.y;
|
||||||
|
float count = colormap_conf.z - 1.0;
|
||||||
|
|
||||||
|
float invalid = colormap_conf.w;
|
||||||
|
|
||||||
|
if (abs(value - invalid) < 0.0001) {
|
||||||
|
return vec4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
float v = clamp((value - vmin), vmin, vmax) / (vmax - vmin);
|
||||||
|
float idx = find_idx(v);
|
||||||
|
vec4 result = texture(colormap, idx);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Self(raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_code_piece!(ColorMap, 0);
|
||||||
213
gi/src/shaders/font.rs
Normal file
213
gi/src/shaders/font.rs
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
use super::trackball::Trackball;
|
||||||
|
use super::CodePiece;
|
||||||
|
use crate::impl_code_piece;
|
||||||
|
use glsl::syntax::ShaderStage;
|
||||||
|
use glsl::syntax::TranslationUnit;
|
||||||
|
use glsl::transpiler::glsl::show_translation_unit;
|
||||||
|
use glsl_quasiquote::glsl;
|
||||||
|
|
||||||
|
pub struct FontVertex(pub ShaderStage);
|
||||||
|
|
||||||
|
pub struct FontFragment(pub ShaderStage);
|
||||||
|
|
||||||
|
pub struct FontGeometry(pub ShaderStage);
|
||||||
|
|
||||||
|
impl FontVertex {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let raw = glsl! {
|
||||||
|
|
||||||
|
// texcoord (left top, right bottom)
|
||||||
|
layout(location = 0) in vec4 texcoord;
|
||||||
|
// Relative position (x, y, width, height)
|
||||||
|
layout(location = 1) in vec4 relative_position;
|
||||||
|
|
||||||
|
out VS_OUT {
|
||||||
|
vec4 texcoord;
|
||||||
|
vec4 pos;
|
||||||
|
} vs_out;
|
||||||
|
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vs_out.texcoord = texcoord;
|
||||||
|
vs_out.pos = relative_position;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Self(raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontGeometry {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut transform = Trackball::new().0;
|
||||||
|
let raw = glsl! {
|
||||||
|
|
||||||
|
layout(points) in;
|
||||||
|
layout(triangle_strip, max_vertices = 4) out;
|
||||||
|
|
||||||
|
// Uniforms
|
||||||
|
uniform vec3 location;
|
||||||
|
uniform vec3 anchor;
|
||||||
|
uniform vec2 viewport;
|
||||||
|
|
||||||
|
// Texture Shape
|
||||||
|
uniform vec2 atlas_shape;
|
||||||
|
|
||||||
|
out vec2 v_texCoord;
|
||||||
|
out vec2 UV_coord;
|
||||||
|
|
||||||
|
in VS_OUT {
|
||||||
|
vec4 texcoord;
|
||||||
|
vec4 pos;
|
||||||
|
} gs_in[];
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
vec4 pos = gs_in[0].pos;
|
||||||
|
float x, y, width, hgt;
|
||||||
|
|
||||||
|
// Char position and size
|
||||||
|
x = pos.x;
|
||||||
|
y = pos.y;
|
||||||
|
width = pos.z;
|
||||||
|
hgt = pos.w;
|
||||||
|
|
||||||
|
// texcoord
|
||||||
|
vec2 tex_coord_lt = gs_in[0].texcoord.xy / atlas_shape;
|
||||||
|
vec2 tex_coord_rb = gs_in[0].texcoord.zw / atlas_shape;
|
||||||
|
|
||||||
|
// uv_coord
|
||||||
|
vec2 uv_coord_lt = gs_in[0].texcoord.xy;
|
||||||
|
vec2 uv_coord_rb = gs_in[0].texcoord.zw;
|
||||||
|
|
||||||
|
// Char Size
|
||||||
|
vec2 size = vec2(width, hgt);
|
||||||
|
|
||||||
|
// Base Text Location
|
||||||
|
vec3 base_pos = location + anchor;
|
||||||
|
|
||||||
|
// base_pos in NDC
|
||||||
|
vec4 ndc_base_pos = transform(position(base_pos));
|
||||||
|
|
||||||
|
// Screen Space
|
||||||
|
vec2 screen_base_pos = viewport * ((ndc_base_pos.xy / ndc_base_pos.w) + 1.0) / 2.0;
|
||||||
|
|
||||||
|
// Billboard Type
|
||||||
|
vec2 left_top = screen_base_pos + vec2(x, y);
|
||||||
|
vec2 right_bottom = left_top + size;
|
||||||
|
|
||||||
|
vec2 ndc_left_top = (2.0 * left_top / viewport - 1.0);
|
||||||
|
vec2 ndc_right_bottom = (2.0 * right_bottom / viewport - 1.0);
|
||||||
|
|
||||||
|
// Emit vertices
|
||||||
|
gl_Position = vec4(ndc_left_top.x, ndc_right_bottom.y, ndc_base_pos.z / ndc_base_pos.w, 1.0);
|
||||||
|
v_texCoord = vec2(tex_coord_lt.x, tex_coord_rb.y);
|
||||||
|
UV_coord = vec2(uv_coord_lt.x, uv_coord_rb.y);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
gl_Position = vec4(ndc_right_bottom.xy, ndc_base_pos.z / ndc_base_pos.w, 1.0);
|
||||||
|
v_texCoord = tex_coord_rb;
|
||||||
|
UV_coord = uv_coord_rb;
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
gl_Position = vec4(ndc_left_top.xy, ndc_base_pos.z / ndc_base_pos.w, 1.0);
|
||||||
|
v_texCoord = tex_coord_lt;
|
||||||
|
UV_coord = uv_coord_lt;
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
gl_Position = vec4(ndc_right_bottom.x, ndc_left_top.y, ndc_base_pos.z / ndc_base_pos.w, 1.0);
|
||||||
|
v_texCoord = vec2(tex_coord_rb.x, tex_coord_lt.y);
|
||||||
|
UV_coord = vec2(uv_coord_rb.x, uv_coord_lt.y);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
transform.extend(raw);
|
||||||
|
|
||||||
|
Self(transform)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontFragment {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let raw = glsl! {
|
||||||
|
uniform sampler2D atlas_data;
|
||||||
|
in vec2 v_texCoord;
|
||||||
|
in vec2 UV_coord;
|
||||||
|
|
||||||
|
uniform vec4 uClipUV;
|
||||||
|
uniform vec4 uSdfConfig;
|
||||||
|
uniform int uMode;
|
||||||
|
uniform vec4 uBorder;
|
||||||
|
uniform vec4 uStroke;
|
||||||
|
uniform vec4 uFill;
|
||||||
|
|
||||||
|
float getUVScale(vec2 sdfUV) {
|
||||||
|
float dx = dFdx(sdfUV.x);
|
||||||
|
float dy = dFdy(sdfUV.y);
|
||||||
|
return (sqrt(dx * dx + dy * dy) + sqrt(dy * dy + dx * dx)) * 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SDF {
|
||||||
|
float outer;
|
||||||
|
float inner;
|
||||||
|
};
|
||||||
|
|
||||||
|
vec4 getTexture(vec2 uv) {
|
||||||
|
return texture(atlas_data, uv);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 getMask(vec4 color, vec2 uv, vec2 st) {
|
||||||
|
return color; // 默认不修改颜色
|
||||||
|
}
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 fillColor = uFill;
|
||||||
|
vec4 strokeColor = uStroke;
|
||||||
|
|
||||||
|
float scale = getUVScale(UV_coord);
|
||||||
|
|
||||||
|
vec4 texture = getTexture(v_texCoord);
|
||||||
|
|
||||||
|
float dis = texture.r;
|
||||||
|
|
||||||
|
// float sdfRaw = 0.0;
|
||||||
|
// float mark = 0.0;
|
||||||
|
// float sdf;
|
||||||
|
|
||||||
|
// float sdfRadius = uSdfConfig.x;
|
||||||
|
// float expand = uBorder.x;
|
||||||
|
// float bleed = uBorder.y;
|
||||||
|
|
||||||
|
// float d = (texture.r - 0.75) * sdfRadius;
|
||||||
|
// float s = (d + expand / uSdfConfig.y) / scale + 0.5 + bleed;
|
||||||
|
// sdf = s; // Assuming SDF returns a single float, adjust as necessary
|
||||||
|
|
||||||
|
// if (uMode == -2) {
|
||||||
|
// fillColor = vec4(texture.rgb, fillColor.a);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Compute mask based on SDF
|
||||||
|
// float mask = clamp(sdf, 0.0, 1.0);
|
||||||
|
// Final color blending logic here
|
||||||
|
|
||||||
|
// fragColor = vec4(fillColor.rgb + mark, mask + mark);
|
||||||
|
|
||||||
|
fragColor = vec4(dis,dis,dis,dis);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Self(raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_code_piece!(FontVertex, 0);
|
||||||
|
impl_code_piece!(FontGeometry, 0);
|
||||||
|
impl_code_piece!(FontFragment, 0);
|
||||||
73
gi/src/shaders/math.rs
Normal file
73
gi/src/shaders/math.rs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
use glsl::syntax::ShaderStage;
|
||||||
|
use glsl_quasiquote::glsl;
|
||||||
|
|
||||||
|
use super::CodePiece;
|
||||||
|
use glsl::syntax::TranslationUnit;
|
||||||
|
use glsl::transpiler::glsl::show_translation_unit;
|
||||||
|
|
||||||
|
use crate::impl_code_piece;
|
||||||
|
|
||||||
|
pub struct Constants {
|
||||||
|
pub raw: ShaderStage,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Constants {
|
||||||
|
pub fn new() -> Constants {
|
||||||
|
let raw = glsl! {
|
||||||
|
|
||||||
|
#ifndef _GLUMPY__CONSTANTS__
|
||||||
|
#define _GLUMPY__CONSTANTS__
|
||||||
|
|
||||||
|
// The base of natural logarithms (e)
|
||||||
|
const float M_E = 2.71828182845904523536028747135266250;
|
||||||
|
|
||||||
|
// The logarithm to base 2 of M_E (log2(e))
|
||||||
|
const float M_LOG2E = 1.44269504088896340735992468100189214;
|
||||||
|
|
||||||
|
// The logarithm to base 10 of M_E (log10(e))
|
||||||
|
const float M_LOG10E = 0.434294481903251827651128918916605082;
|
||||||
|
|
||||||
|
// The natural logarithm of 2 (loge(2))
|
||||||
|
const float M_LN2 = 0.693147180559945309417232121458176568;
|
||||||
|
|
||||||
|
// The natural logarithm of 10 (loge(10))
|
||||||
|
const float M_LN10 = 2.30258509299404568401799145468436421;
|
||||||
|
|
||||||
|
// Pi, the ratio of a circle's circumference to its diameter.
|
||||||
|
const float M_PI = 3.14159265358979323846264338327950288;
|
||||||
|
|
||||||
|
// Pi divided by two (pi/2)
|
||||||
|
const float M_PI_2 = 1.57079632679489661923132169163975144;
|
||||||
|
|
||||||
|
// Pi divided by four (pi/4)
|
||||||
|
const float M_PI_4 = 0.785398163397448309615660845819875721;
|
||||||
|
|
||||||
|
// The reciprocal of pi (1/pi)
|
||||||
|
const float M_1_PI = 0.318309886183790671537767526745028724;
|
||||||
|
|
||||||
|
// Two times the reciprocal of pi (2/pi)
|
||||||
|
const float M_2_PI = 0.636619772367581343075535053490057448;
|
||||||
|
|
||||||
|
// Two times the reciprocal of the square root of pi (2/sqrt(pi))
|
||||||
|
const float M_2_SQRTPI = 1.12837916709551257389615890312154517;
|
||||||
|
|
||||||
|
// The square root of two (sqrt(2))
|
||||||
|
const float M_SQRT2 = 1.41421356237309504880168872420969808;
|
||||||
|
|
||||||
|
// The reciprocal of the square root of two (1/sqrt(2))
|
||||||
|
const float M_SQRT1_2 = 0.707106781186547524400844362104849039;
|
||||||
|
|
||||||
|
// 1 degree in radians
|
||||||
|
const float degree = 180.0/M_PI;
|
||||||
|
|
||||||
|
// 1 radian in degrees
|
||||||
|
const float radian = M_PI/180.0;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
Constants { raw }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_code_piece!(Constants, raw);
|
||||||
99
gi/src/shaders/mod.rs
Normal file
99
gi/src/shaders/mod.rs
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
pub mod agg_path;
|
||||||
|
pub mod colormap;
|
||||||
|
pub mod font;
|
||||||
|
pub mod math;
|
||||||
|
pub mod polar;
|
||||||
|
pub mod ppi;
|
||||||
|
pub mod trackball;
|
||||||
|
use glsl::{
|
||||||
|
syntax::{ShaderStage, TranslationUnit},
|
||||||
|
transpiler::glsl::{show_struct, show_translation_unit},
|
||||||
|
};
|
||||||
|
use glsl_quasiquote::glsl;
|
||||||
|
|
||||||
|
use crate::components::Shader;
|
||||||
|
|
||||||
|
struct PPI {
|
||||||
|
vertex: ShaderStage,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PPI {
|
||||||
|
fn new() -> Self {
|
||||||
|
let vertex: ShaderStage = glsl! {
|
||||||
|
layout(location = 0) in vec3 position;
|
||||||
|
out float in_value;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(position.x, position.y, 0.5, 1.0);
|
||||||
|
in_value = position.z;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Self { vertex }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for PPI {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
show_translation_unit(f, &self.vertex);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(std::fmt::Debug)]
|
||||||
|
pub struct EmptyPiece(pub TranslationUnit);
|
||||||
|
|
||||||
|
pub trait CodePiece: std::fmt::Display {
|
||||||
|
fn raw(&self) -> &TranslationUnit;
|
||||||
|
|
||||||
|
fn include<T: CodePiece>(i: T, raw: TranslationUnit) -> TranslationUnit {
|
||||||
|
let mut s = i.raw().clone();
|
||||||
|
s.extend(raw);
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CodePiece for EmptyPiece {
|
||||||
|
fn raw(&self) -> &TranslationUnit {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for EmptyPiece {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! impl_code_piece {
|
||||||
|
($t:ty, $c:tt) => {
|
||||||
|
impl CodePiece for $t {
|
||||||
|
fn raw(&self) -> &TranslationUnit {
|
||||||
|
&self.$c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for $t {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
show_translation_unit(f, &self.$c);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
|
||||||
|
use glsl::transpiler::glsl::show_translation_unit;
|
||||||
|
|
||||||
|
use super::math::Constants;
|
||||||
|
use super::polar::PolarTransform;
|
||||||
|
use super::PPI;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let polar = PolarTransform::new();
|
||||||
|
println!("{}", polar);
|
||||||
|
}
|
||||||
|
}
|
||||||
58
gi/src/shaders/polar.rs
Normal file
58
gi/src/shaders/polar.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
use super::CodePiece;
|
||||||
|
use crate::impl_code_piece;
|
||||||
|
use glsl::syntax::ShaderStage;
|
||||||
|
use glsl::syntax::TranslationUnit;
|
||||||
|
use glsl::transpiler::glsl::show_translation_unit;
|
||||||
|
|
||||||
|
use super::math::Constants;
|
||||||
|
use glsl_quasiquote::glsl;
|
||||||
|
|
||||||
|
pub struct PolarTransform {
|
||||||
|
pub raw: ShaderStage,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PolarTransform {
|
||||||
|
pub fn new() -> PolarTransform {
|
||||||
|
let raw = glsl! {
|
||||||
|
|
||||||
|
uniform float polar_origin;
|
||||||
|
|
||||||
|
vec4 forward(float rho, float theta, float z, float w)
|
||||||
|
{
|
||||||
|
return vec4(rho * cos(theta + polar_origin),
|
||||||
|
rho * sin(theta + polar_origin),
|
||||||
|
rho * sin(z), w);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 forward(float x, float y) {return forward(x, y, 0.0, 1.0);}
|
||||||
|
vec4 forward(float x, float y, float z) {return forward(x, y, z, 1.0);}
|
||||||
|
vec4 forward(vec2 P) { return forward(P.x, P.y); }
|
||||||
|
vec4 forward(vec3 P) { return forward(P.x, P.y, P.z, 1.0); }
|
||||||
|
vec4 forward(vec4 P) { return forward(P.x, P.y, P.z, P.w); }
|
||||||
|
|
||||||
|
vec4 inverse(float x, float y, float z, float w)
|
||||||
|
{
|
||||||
|
float rho = length(vec2(x,y));
|
||||||
|
float theta = atan(y,x);
|
||||||
|
if( theta < 0.0 )
|
||||||
|
theta = 2.0*M_PI+theta;
|
||||||
|
return vec4(rho, theta-polar_origin, z, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vec4 inverse(float x, float y) {return inverse(x,y,0.0,1.0); }
|
||||||
|
vec4 inverse(float x, float y, float z) {return inverse(x,y,z,1.0); }
|
||||||
|
vec4 inverse(vec2 P) { return inverse(P.x, P.y, 0.0, 1.0); }
|
||||||
|
vec4 inverse(vec3 P) { return inverse(P.x, P.y, P.z, 1.0); }
|
||||||
|
vec4 inverse(vec4 P) { return inverse(P.x, P.y, P.z, P.w); }
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut constant = Constants::new().raw;
|
||||||
|
|
||||||
|
constant.extend(raw);
|
||||||
|
|
||||||
|
PolarTransform { raw: constant }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_code_piece!(PolarTransform, raw);
|
||||||
147
gi/src/shaders/ppi.rs
Normal file
147
gi/src/shaders/ppi.rs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
use super::trackball::Trackball;
|
||||||
|
use super::CodePiece;
|
||||||
|
use crate::impl_code_piece;
|
||||||
|
use glsl::syntax::ShaderStage;
|
||||||
|
use glsl::syntax::TranslationUnit;
|
||||||
|
use glsl::transpiler::glsl::show_translation_unit;
|
||||||
|
use glsl_quasiquote::glsl;
|
||||||
|
|
||||||
|
pub struct PPIVertex(pub ShaderStage);
|
||||||
|
pub struct PPIGeom(pub ShaderStage);
|
||||||
|
pub struct PPIFragment(pub ShaderStage);
|
||||||
|
|
||||||
|
impl PPIVertex {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let raw = glsl! {
|
||||||
|
layout(location = 0) in vec4 position;
|
||||||
|
out float in_value;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(position.x * 10.0, position.y, position.z, 1.0);
|
||||||
|
in_value = position.w;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Self(raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PPIGeom {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut trackball = Trackball::new().0;
|
||||||
|
let polar = super::polar::PolarTransform::new().raw;
|
||||||
|
let raw = glsl! {
|
||||||
|
layout(points) in;
|
||||||
|
layout(triangle_strip, max_vertices = 4) out;
|
||||||
|
|
||||||
|
// conf: Range, Elevation, Resolution, mode
|
||||||
|
uniform vec4 conf;
|
||||||
|
in float in_value[];
|
||||||
|
|
||||||
|
out float x;
|
||||||
|
out float y;
|
||||||
|
out float value;
|
||||||
|
|
||||||
|
flat out vec4 vrange;
|
||||||
|
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float rdpi = conf.x * 10.0;
|
||||||
|
float mode = conf.w;
|
||||||
|
vec4 reso = vec4(rdpi/2.0, conf.y/2.0 * radian, 0.0, 0.0);
|
||||||
|
vec4 loc;
|
||||||
|
float c = cos(reso.y);
|
||||||
|
|
||||||
|
vec4 po = gl_in[0].gl_Position;
|
||||||
|
po.y *= radian;
|
||||||
|
po.z *= radian * mode;
|
||||||
|
|
||||||
|
vrange = vec4(po.x - reso.x, po.y - reso.y, po.x + reso.x, po.y + reso.y);
|
||||||
|
value = in_value[0];
|
||||||
|
|
||||||
|
gl_Position = po - reso;
|
||||||
|
loc = forward(gl_Position);
|
||||||
|
x = loc.x;
|
||||||
|
y = loc.y;
|
||||||
|
gl_Position = transform(loc);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
gl_Position = po + vec4(reso.x, -reso.y, 0.0, 0.0);
|
||||||
|
gl_Position.x = gl_Position.x / c;
|
||||||
|
loc = forward(gl_Position);
|
||||||
|
x = loc.x;
|
||||||
|
y = loc.y;
|
||||||
|
gl_Position = transform(loc);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
gl_Position = po + vec4(-reso.x, reso.y, 0.0, 0.0);
|
||||||
|
loc = forward(gl_Position);
|
||||||
|
x = loc.x;
|
||||||
|
y = loc.y;
|
||||||
|
gl_Position = transform(loc);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
gl_Position = po + reso;
|
||||||
|
gl_Position.x = gl_Position.x / c;
|
||||||
|
loc = forward(gl_Position);
|
||||||
|
x = loc.x;
|
||||||
|
y = loc.y;
|
||||||
|
gl_Position = transform(loc);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
trackball.extend(polar);
|
||||||
|
trackball.extend(raw);
|
||||||
|
|
||||||
|
Self(trackball)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PPIFragment {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
use super::polar::PolarTransform;
|
||||||
|
let mut include = PolarTransform::new().raw;
|
||||||
|
let mut colormap = super::colormap::ColorMap::new().0;
|
||||||
|
let raw = glsl! {
|
||||||
|
in float x;
|
||||||
|
in float y;
|
||||||
|
|
||||||
|
in float value;
|
||||||
|
flat in vec4 vrange;
|
||||||
|
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
vec4 inversed = inverse(x, y);
|
||||||
|
|
||||||
|
|
||||||
|
if (inversed.x < vrange.x || inversed.x > vrange.z) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 result = linear_colormap(value);
|
||||||
|
|
||||||
|
if (result.w == 0.0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
FragColor = result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
include.extend(colormap);
|
||||||
|
include.extend(raw);
|
||||||
|
|
||||||
|
Self(include)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_code_piece!(PPIVertex, 0);
|
||||||
|
impl_code_piece!(PPIGeom, 0);
|
||||||
|
impl_code_piece!(PPIFragment, 0);
|
||||||
70
gi/src/shaders/trackball.rs
Normal file
70
gi/src/shaders/trackball.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use super::CodePiece;
|
||||||
|
use glsl::syntax::ShaderStage;
|
||||||
|
use glsl::syntax::TranslationUnit;
|
||||||
|
use glsl::transpiler::glsl::show_translation_unit;
|
||||||
|
|
||||||
|
use crate::impl_code_piece;
|
||||||
|
|
||||||
|
pub struct Trackball(pub ShaderStage);
|
||||||
|
|
||||||
|
impl Trackball {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let raw = glsl_quasiquote::glsl! {
|
||||||
|
uniform mat4 trackball_view;
|
||||||
|
uniform mat4 trackball_model;
|
||||||
|
uniform mat4 trackball_projection;
|
||||||
|
|
||||||
|
vec4 transform(vec4 position)
|
||||||
|
{
|
||||||
|
return trackball_projection
|
||||||
|
* trackball_view
|
||||||
|
* trackball_model
|
||||||
|
* position;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 position(float x)
|
||||||
|
{
|
||||||
|
return vec4(x, 0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 position(float x, float y)
|
||||||
|
{
|
||||||
|
return vec4(x, y, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 position(vec2 xy)
|
||||||
|
{
|
||||||
|
return vec4(xy, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 position(float x, float y, float z)
|
||||||
|
{
|
||||||
|
return vec4(x, y, z, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 position(vec3 xyz)
|
||||||
|
{
|
||||||
|
return vec4(xyz, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 position(vec4 xyzw)
|
||||||
|
{
|
||||||
|
return xyzw;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 position(vec2 xy, float z)
|
||||||
|
{
|
||||||
|
return vec4(xy, z, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 position(float x, vec2 yz)
|
||||||
|
{
|
||||||
|
return vec4(x, yz, 1.0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Self(raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_code_piece!(Trackball, 0);
|
||||||
52
gi/src/ui/helper.rs
Normal file
52
gi/src/ui/helper.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
use crate::{
|
||||||
|
pg::{layout_type::ViewPort, ModulePackage},
|
||||||
|
utils::resources::GL,
|
||||||
|
};
|
||||||
|
use femtovg::{renderer::OpenGl, Canvas};
|
||||||
|
use glow::{NativeRenderbuffer, NativeTexture};
|
||||||
|
|
||||||
|
pub struct Helper {
|
||||||
|
canvas: Canvas<OpenGl>,
|
||||||
|
viewport: ViewPort<NativeTexture>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Helper {
|
||||||
|
pub fn new(gl: &GL, mut renderer: OpenGl) -> Self {
|
||||||
|
let mut viewport = ViewPort::<NativeTexture>::new(&gl, false);
|
||||||
|
renderer.set_screen_target(Some(viewport.fbo().native()));
|
||||||
|
let mut canvas = Canvas::new(renderer).expect("Cannot create canvas");
|
||||||
|
|
||||||
|
viewport.set_size([3840.0, 2160.0]);
|
||||||
|
canvas.set_size(3840, 2160, 1.0);
|
||||||
|
|
||||||
|
Self { canvas, viewport }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn canvas(&mut self) -> &mut Canvas<OpenGl> {
|
||||||
|
&mut self.canvas
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_size(&mut self, size: [f32; 2], dpi: f32) {
|
||||||
|
self.viewport.set_size([size[0] * dpi, size[1] * dpi]);
|
||||||
|
self.canvas.set_size(size[0] as u32, size[1] as u32, dpi);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_task(&mut self, gl: &glow::Context, f: impl FnOnce(&mut Canvas<OpenGl>)) {
|
||||||
|
self.viewport.bind(gl);
|
||||||
|
f(&mut self.canvas);
|
||||||
|
self.canvas.flush();
|
||||||
|
self.viewport.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn viewport(&self) -> &ViewPort<NativeTexture> {
|
||||||
|
&self.viewport
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_modules(&mut self, gl: &glow::Context, modules: &Vec<ModulePackage>) {
|
||||||
|
self.render_task(gl, |canvas| {
|
||||||
|
for module in modules {
|
||||||
|
// module.helper_task(canvas);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
17
gi/src/ui/io.rs
Normal file
17
gi/src/ui/io.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
pub struct MouseIO {
|
||||||
|
pub position: [f32; 2], // 鼠标当前位置
|
||||||
|
pub drag_delta: Option<[f32; 2]>, // 拖动开始时的鼠标位置
|
||||||
|
pub is_dragging: bool, // 是否正在拖动
|
||||||
|
pub left_button_pressed: bool, // 左键是否被按下
|
||||||
|
pub right_button_pressed: bool, // 右键是否被按下
|
||||||
|
pub wheel_delta: f32, // 鼠标滚轮变化值
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct KeyboardIO {
|
||||||
|
pub keys: [bool; 652], // 键盘按键状态
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IO {
|
||||||
|
pub mouse: MouseIO,
|
||||||
|
pub keyboard: KeyboardIO,
|
||||||
|
}
|
||||||
16
gi/src/ui/mod.rs
Normal file
16
gi/src/ui/mod.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use log::*;
|
||||||
|
|
||||||
|
use crate::data_loader::Data;
|
||||||
|
use crate::errors::*;
|
||||||
|
use crate::pg::{ModulePackage, Programs};
|
||||||
|
use crate::utils::cache::Cache;
|
||||||
|
use crate::utils::resources::{RcGlRcFramebuffer, RcGlRcRenderbuffer, RcGlRcResource, GL};
|
||||||
|
use glow::HasContext;
|
||||||
|
use glow::{NativeFramebuffer, NativeRenderbuffer};
|
||||||
|
|
||||||
|
pub mod helper;
|
||||||
|
pub mod io;
|
||||||
|
pub mod operation;
|
||||||
|
pub mod typ;
|
||||||
137
gi/src/ui/operation.rs
Normal file
137
gi/src/ui/operation.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
use super::typ::CameraOP;
|
||||||
|
use crate::{
|
||||||
|
camera::Camera,
|
||||||
|
components::Program,
|
||||||
|
graphics::{AttaWithProgram, AttachWithIO},
|
||||||
|
pg::layout_type::ViewPort,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Operation<T: AttachWithIO> {
|
||||||
|
camera: Camera,
|
||||||
|
projection: Projection,
|
||||||
|
operation: T,
|
||||||
|
need_update: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AttachWithIO + AttaWithProgram> Operation<T> {
|
||||||
|
pub fn new(operation: T, aspect: f32, fov: f32, z_near: f32, z_far: f32) -> Self {
|
||||||
|
let projection = Projection::new(aspect, fov, z_near, z_far);
|
||||||
|
Self {
|
||||||
|
projection,
|
||||||
|
camera: operation.init_camera(),
|
||||||
|
operation,
|
||||||
|
need_update: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deal_io(&mut self, viewport: &ViewPort, io: &super::io::IO) {
|
||||||
|
self.need_update = self.operation.attach_with_mouse(
|
||||||
|
&T::State::from_context(io),
|
||||||
|
&mut self.camera,
|
||||||
|
&mut self.projection,
|
||||||
|
viewport,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_camera(&mut self, f: impl Fn(&mut Camera)) {
|
||||||
|
f(&mut self.camera);
|
||||||
|
self.need_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_projection(&mut self, f: impl Fn(&mut Projection)) {
|
||||||
|
f(&mut self.projection);
|
||||||
|
self.need_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attach_with_program(&self, gl: &glow::Context, program: &mut Program) {
|
||||||
|
use glow::HasContext;
|
||||||
|
unsafe {
|
||||||
|
let projection = program.get_uniform_location(gl, "trackball_projection");
|
||||||
|
gl.uniform_matrix_4_f32_slice(projection.as_ref(), false, self.projection.as_slice());
|
||||||
|
let view = program.get_uniform_location(gl, "trackball_view");
|
||||||
|
gl.uniform_matrix_4_f32_slice(
|
||||||
|
view.as_ref(),
|
||||||
|
false,
|
||||||
|
self.camera.get_view_matrix().as_slice(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.operation.attach_with_program(gl, program);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_need_update(&self) -> bool {
|
||||||
|
self.need_update
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.operation.reset();
|
||||||
|
self.need_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clean(&mut self) {
|
||||||
|
self.need_update = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Projection {
|
||||||
|
fov: f32,
|
||||||
|
z_near: f32,
|
||||||
|
z_far: f32,
|
||||||
|
aspect: f32,
|
||||||
|
projection: nalgebra_glm::Mat4,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Projection {
|
||||||
|
fn new(aspect: f32, fov: f32, z_near: f32, z_far: f32) -> Self {
|
||||||
|
let projection = nalgebra_glm::perspective(aspect, fov.to_radians(), z_near, z_far);
|
||||||
|
Self {
|
||||||
|
aspect,
|
||||||
|
fov,
|
||||||
|
z_far,
|
||||||
|
z_near,
|
||||||
|
projection,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn new_perspective(&self) -> nalgebra_glm::Mat4 {
|
||||||
|
nalgebra_glm::perspective(self.aspect, self.fov.to_radians(), self.z_near, self.z_far)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_aspect(&mut self, aspect: f32) {
|
||||||
|
self.aspect = aspect;
|
||||||
|
self.projection = self.new_perspective();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_fov(&mut self, fov: f32) {
|
||||||
|
self.fov = fov;
|
||||||
|
self.projection = self.new_perspective();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_z_near(&mut self, z_near: f32) {
|
||||||
|
self.z_near = z_near;
|
||||||
|
self.projection = self.new_perspective();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_z_far(&mut self, z_far: f32) {
|
||||||
|
self.z_far = z_far;
|
||||||
|
self.projection = self.new_perspective();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fov(&self) -> f32 {
|
||||||
|
self.fov
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn z_far(&self) -> f32 {
|
||||||
|
self.z_far
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn z_near(&self) -> f32 {
|
||||||
|
self.z_near
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn aspect(&self) -> f32 {
|
||||||
|
self.aspect
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_slice(&self) -> &[f32] {
|
||||||
|
self.projection.as_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
122
gi/src/ui/typ/main_load.rs
Normal file
122
gi/src/ui/typ/main_load.rs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
use std::thread::panicking;
|
||||||
|
|
||||||
|
use super::{CameraOP, LayoutAttach};
|
||||||
|
use crate::camera::Camera;
|
||||||
|
use crate::graphics::threed::Trackball;
|
||||||
|
use crate::graphics::transforms::plane::PlaneTrans;
|
||||||
|
use crate::graphics::{AttaWithProgram, AttachWithIO, MouseKeyboardState, MouseState};
|
||||||
|
use crate::pg::ModulePackage;
|
||||||
|
use crate::pg::{_ModulePackage, layout_type::ViewPort};
|
||||||
|
use crate::ui::helper::Helper;
|
||||||
|
use crate::ui::io::IO;
|
||||||
|
use crate::ui::operation::Operation;
|
||||||
|
use crate::utils::resources::GL;
|
||||||
|
use bytemuck::Contiguous;
|
||||||
|
use glow::{HasContext, NativeRenderbuffer};
|
||||||
|
use nalgebra_glm::Vec3;
|
||||||
|
|
||||||
|
pub struct MainLoadAttach {
|
||||||
|
operation: Operation<PlaneTrans>,
|
||||||
|
packages: Vec<ModulePackage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ProjectionValues {
|
||||||
|
pub near: f32,
|
||||||
|
pub far: f32,
|
||||||
|
pub fovy: f32,
|
||||||
|
pub aspect: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ProjectionValues {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
near: 0.1,
|
||||||
|
far: 1000.0,
|
||||||
|
fovy: 45.0,
|
||||||
|
aspect: 16.0 / 9.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MainLoadAttach {
|
||||||
|
pub fn new(projection: Option<ProjectionValues>) -> Self {
|
||||||
|
let projection = projection.unwrap_or_default();
|
||||||
|
Self {
|
||||||
|
operation: Operation::new(
|
||||||
|
PlaneTrans::default(),
|
||||||
|
projection.aspect,
|
||||||
|
projection.fovy,
|
||||||
|
projection.near,
|
||||||
|
projection.far,
|
||||||
|
),
|
||||||
|
packages: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CameraOP for MouseState {
|
||||||
|
fn from_context(context: &IO) -> Self {
|
||||||
|
if context.mouse.is_dragging {
|
||||||
|
Self::Drag {
|
||||||
|
from: context.mouse.position,
|
||||||
|
delta: context.mouse.drag_delta.unwrap(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if context.mouse.wheel_delta != 0.0 {
|
||||||
|
Self::Wheel(context.mouse.wheel_delta)
|
||||||
|
} else {
|
||||||
|
Self::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CameraOP for MouseKeyboardState {
|
||||||
|
fn from_context(context: &IO) -> Self {
|
||||||
|
Self {
|
||||||
|
mouse_state: MouseState::from_context(context),
|
||||||
|
keyboard_state: context.keyboard.keys,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayoutAttach for MainLoadAttach {
|
||||||
|
fn append(&mut self, package: ModulePackage) {
|
||||||
|
self.packages.push(package);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_task(
|
||||||
|
&mut self,
|
||||||
|
gl: &glow::Context,
|
||||||
|
programs: &mut crate::pg::Programs,
|
||||||
|
helper: &mut Helper,
|
||||||
|
) -> crate::errors::Result<()> {
|
||||||
|
// helper.draw_modules(gl, &self.packages);
|
||||||
|
|
||||||
|
let need_launch =
|
||||||
|
self.packages.iter().any(|v| v.need_update) || self.operation.is_need_update();
|
||||||
|
if need_launch {
|
||||||
|
// Helper task
|
||||||
|
// self.main_viewport.bind(gl);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
gl.clear(glow::COLOR_BUFFER_BIT | glow::DEPTH_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
for package in self.packages.iter_mut() {
|
||||||
|
// programs.draw_modules(package, &self.operation, &self.main_viewport)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.operation.clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.packages.is_empty() {
|
||||||
|
unsafe {
|
||||||
|
gl.clear(glow::COLOR_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// self.main_viewport.unbind();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
66
gi/src/ui/typ/mod.rs
Normal file
66
gi/src/ui/typ/mod.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use super::helper;
|
||||||
|
use super::io::IO;
|
||||||
|
mod main_load;
|
||||||
|
use crate::pg::{self, ModuleCursor, ModulePackage, Programs};
|
||||||
|
use crate::utils::resources::GL;
|
||||||
|
pub use main_load::*;
|
||||||
|
|
||||||
|
// macro_rules! app_type_into {
|
||||||
|
// ($({$t: ty => $b:tt},)+) => {
|
||||||
|
// $(
|
||||||
|
// impl From<$t> for _Layout {
|
||||||
|
// fn from(t: $t) -> Self {
|
||||||
|
// Self::$b(t)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl<'a> From<&'a _Layout> for &'a $t {
|
||||||
|
// fn from(t: &'a _Layout) -> Self {
|
||||||
|
// if let _Layout::$b(t) = t {
|
||||||
|
// t
|
||||||
|
// } else {
|
||||||
|
// panic!("_Layout is not {}", stringify!($b));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl<'a> From<&'a mut _Layout> for &'a mut $t {
|
||||||
|
// fn from(t: &'a mut _Layout) -> Self {
|
||||||
|
// if let _Layout::$b(t) = t {
|
||||||
|
// t
|
||||||
|
// } else {
|
||||||
|
// panic!("_Layout is not {}", stringify!($b));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// )+
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub trait LayoutAttach {
|
||||||
|
fn append(&mut self, package: ModulePackage);
|
||||||
|
|
||||||
|
fn render_task(
|
||||||
|
&mut self,
|
||||||
|
gl: &glow::Context,
|
||||||
|
programs: &mut Programs,
|
||||||
|
helper: &mut helper::Helper,
|
||||||
|
) -> crate::errors::Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CameraOP {
|
||||||
|
fn from_context(context: &IO) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CameraOP for () {
|
||||||
|
fn from_context(context: &IO) -> Self {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// app_type_into!({
|
||||||
|
// MainLoadAttach => MainLoad
|
||||||
|
// },);
|
||||||
61
gi/src/utils/cache.rs
Normal file
61
gi/src/utils/cache.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use crate::errors::*;
|
||||||
|
use std::{cell::RefCell, hash::Hash, num::NonZeroUsize, rc::Rc};
|
||||||
|
|
||||||
|
use lru::LruCache;
|
||||||
|
|
||||||
|
use crate::data_loader::Data;
|
||||||
|
pub struct Cache<K: Hash + Eq, V> {
|
||||||
|
cache: LruCache<K, V>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Hash + Eq, V> Cache<K, V> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
cache: LruCache::new(NonZeroUsize::new(10).unwrap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get(&mut self, key: &K) -> Option<&V> {
|
||||||
|
self.cache.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
|
||||||
|
self.cache.get_mut(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> Cache<K, V>
|
||||||
|
where
|
||||||
|
K: AsRef<std::path::Path> + Hash + Eq + Clone,
|
||||||
|
V: CacheData,
|
||||||
|
{
|
||||||
|
pub fn insert(&mut self, key: K) -> Result<()> {
|
||||||
|
let value = V::from_path(&key)?;
|
||||||
|
self.cache.put(key, value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_or_insert(&mut self, key: K) -> Result<&V> {
|
||||||
|
if !self.cache.contains(&key) {
|
||||||
|
self.insert(key.clone())?;
|
||||||
|
}
|
||||||
|
Ok(self.cache.get(&key).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CachedData<T> = Rc<RefCell<T>>;
|
||||||
|
|
||||||
|
pub trait CacheData: Sized {
|
||||||
|
fn from_path(path: impl AsRef<std::path::Path>) -> Result<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheData for Data {
|
||||||
|
fn from_path(path: impl AsRef<std::path::Path>) -> Result<Self> {
|
||||||
|
Data::from_path(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: CacheData> CacheData for CachedData<T> {
|
||||||
|
fn from_path(path: impl AsRef<std::path::Path>) -> Result<Self> {
|
||||||
|
Ok(Rc::new(RefCell::new(T::from_path(path)?)))
|
||||||
|
}
|
||||||
|
}
|
||||||
18
gi/src/utils/color_tools.rs
Normal file
18
gi/src/utils/color_tools.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
pub fn hex_to_rgba_u8(hex: &str) -> Result<[u8; 4], String> {
|
||||||
|
let hex = hex.trim_start_matches('#');
|
||||||
|
|
||||||
|
if hex.len() != 6 && hex.len() != 8 {
|
||||||
|
return Err("Hex color should be in #RRGGBB or #RRGGBBAA format".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = u8::from_str_radix(&hex[0..2], 16).map_err(|e| e.to_string())?;
|
||||||
|
let g = u8::from_str_radix(&hex[2..4], 16).map_err(|e| e.to_string())?;
|
||||||
|
let b = u8::from_str_radix(&hex[4..6], 16).map_err(|e| e.to_string())?;
|
||||||
|
let a = if hex.len() == 8 {
|
||||||
|
u8::from_str_radix(&hex[6..8], 16).map_err(|e| e.to_string())?
|
||||||
|
} else {
|
||||||
|
255 // 默认不透明
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok([r, g, b, a])
|
||||||
|
}
|
||||||
17
gi/src/utils/geo_tools.rs
Normal file
17
gi/src/utils/geo_tools.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use geo::{algorithm::haversine_destination::HaversineDestination, Point};
|
||||||
|
pub fn calculate_coverage(lat_deg: f64, lon_deg: f64, radius_km: f64) -> (f64, f64, f64, f64) {
|
||||||
|
let center = Point::new(lon_deg, lat_deg);
|
||||||
|
|
||||||
|
// 计算四个方向(北、南、东、西)的点
|
||||||
|
let north = center.haversine_destination(0.0, radius_km);
|
||||||
|
let south = center.haversine_destination(180.0, radius_km);
|
||||||
|
let east = center.haversine_destination(90.0, radius_km);
|
||||||
|
let west = center.haversine_destination(270.0, radius_km);
|
||||||
|
|
||||||
|
let min_lat = south.y();
|
||||||
|
let max_lat = north.y();
|
||||||
|
let min_lon = west.x();
|
||||||
|
let max_lon = east.x();
|
||||||
|
|
||||||
|
(min_lat, max_lat, min_lon, max_lon)
|
||||||
|
}
|
||||||
22
gi/src/utils/mod.rs
Normal file
22
gi/src/utils/mod.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
pub mod color_tools;
|
||||||
|
pub mod resources;
|
||||||
|
use include_dir::{include_dir, Dir};
|
||||||
|
use std::{num::NonZeroU32, path::Path};
|
||||||
|
pub mod cache;
|
||||||
|
pub mod geo_tools;
|
||||||
|
|
||||||
|
use glow::HasContext;
|
||||||
|
use glutin::{
|
||||||
|
config::ConfigTemplateBuilder,
|
||||||
|
context::{ContextApi, ContextAttributesBuilder, NotCurrentGlContext, PossiblyCurrentContext},
|
||||||
|
display::{GetGlDisplay, GlDisplay},
|
||||||
|
surface::{GlSurface, Surface, SurfaceAttributesBuilder, SwapInterval, WindowSurface},
|
||||||
|
};
|
||||||
|
use raw_window_handle::HasRawWindowHandle;
|
||||||
|
pub use resources::GL;
|
||||||
|
|
||||||
|
pub fn glow_context(context: &PossiblyCurrentContext) -> glow::Context {
|
||||||
|
unsafe {
|
||||||
|
glow::Context::from_loader_function_cstr(|s| context.display().get_proc_address(s).cast())
|
||||||
|
}
|
||||||
|
}
|
||||||
320
gi/src/utils/resources.rs
Normal file
320
gi/src/utils/resources.rs
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
use glow::{
|
||||||
|
HasContext, NativeBuffer, NativeFramebuffer, NativeRenderbuffer, NativeTexture,
|
||||||
|
NativeVertexArray,
|
||||||
|
};
|
||||||
|
use log::info;
|
||||||
|
use std::{borrow::Borrow, ops::Deref, rc::Rc};
|
||||||
|
|
||||||
|
pub type RcGlFramebuffer<'a> = RcGlResource<'a, NativeFramebuffer>;
|
||||||
|
pub type RcGlRenderbuffer<'a> = RcGlResource<'a, NativeRenderbuffer>;
|
||||||
|
pub type RcGlRcFramebuffer = RcGlRcResource<NativeFramebuffer>;
|
||||||
|
pub type RcGlRcRenderbuffer = RcGlRcResource<NativeRenderbuffer>;
|
||||||
|
pub type RcGlTexture<'a> = RcGlResource<'a, NativeTexture>;
|
||||||
|
pub type RcGlVertexArray<'a> = RcGlResource<'a, NativeVertexArray>;
|
||||||
|
pub type RcGlBuffer<'a> = RcGlResource<'a, NativeBuffer>;
|
||||||
|
pub type RcGlRcVertexArray = RcGlRcResource<NativeVertexArray>;
|
||||||
|
pub type RcGlRcBuffer = RcGlRcResource<NativeBuffer>;
|
||||||
|
|
||||||
|
// pub type RcGlResource<'a, T> = Rc<GlResource<'a, T>>;
|
||||||
|
pub trait ManagedResource {
|
||||||
|
fn bind(&self, target: u32);
|
||||||
|
fn unbind(&self, target: u32);
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RcGlResource<'a, T: Resource>(Rc<GlResource<'a, T>>);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct RcGlRcResource<T: Resource>(Rc<GlRcResource<T>>);
|
||||||
|
|
||||||
|
impl<'a, T: Resource> RcGlResource<'a, T> {
|
||||||
|
pub fn new(gl: &'a glow::Context, resource: T) -> Self {
|
||||||
|
Self(Rc::new(GlResource::new(resource, gl)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn native(&self) -> T {
|
||||||
|
self.0.resource.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> From<&'a GlRcResource<T>> for GlResource<'a, T>
|
||||||
|
where
|
||||||
|
T: Resource,
|
||||||
|
{
|
||||||
|
fn from(value: &'a GlRcResource<T>) -> Self {
|
||||||
|
GlResource::new(value.resource.clone(), &value.gl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Resource> Deref for RcGlResource<'a, T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.0.deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> RcGlRcResource<T> {
|
||||||
|
pub fn new(gl: Rc<glow::Context>, resource: T) -> Self {
|
||||||
|
Self(Rc::new(GlRcResource::new(resource, gl)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn native(&self) -> T {
|
||||||
|
self.0.resource.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> Deref for RcGlRcResource<T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.0.deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Resource: Clone + std::fmt::Debug {
|
||||||
|
fn drop_self(&self, gl: &glow::Context);
|
||||||
|
|
||||||
|
fn create(gl: &glow::Context) -> Self;
|
||||||
|
|
||||||
|
fn bind(&self, gl: &glow::Context, target: u32);
|
||||||
|
|
||||||
|
fn unbind(&self, gl: &glow::Context, target: u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resource for NativeVertexArray {
|
||||||
|
fn drop_self(&self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
gl.delete_vertex_array(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(gl: &glow::Context) -> Self {
|
||||||
|
unsafe { gl.create_vertex_array().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_vertex_array(Some(*self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_vertex_array(Some(*self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resource for NativeBuffer {
|
||||||
|
fn drop_self(&self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
gl.delete_buffer(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(gl: &glow::Context) -> Self {
|
||||||
|
unsafe { gl.create_buffer().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_buffer(target, Some(*self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_buffer(target, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resource for NativeFramebuffer {
|
||||||
|
fn drop_self(&self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
gl.delete_framebuffer(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(gl: &glow::Context) -> Self {
|
||||||
|
unsafe { gl.create_framebuffer().unwrap() }
|
||||||
|
}
|
||||||
|
fn bind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_framebuffer(target, Some(*self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn unbind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_framebuffer(target, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resource for NativeRenderbuffer {
|
||||||
|
fn drop_self(&self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
gl.delete_renderbuffer(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(gl: &glow::Context) -> Self {
|
||||||
|
unsafe { gl.create_renderbuffer().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_renderbuffer(target, Some(*self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn unbind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_renderbuffer(target, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resource for NativeTexture {
|
||||||
|
fn drop_self(&self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
gl.delete_texture(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(gl: &glow::Context) -> Self {
|
||||||
|
unsafe { gl.create_texture().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_texture(target, Some(*self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_texture(target, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct GlResource<'a, T: Resource> {
|
||||||
|
gl: &'a glow::Context,
|
||||||
|
resource: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GlRcResource<T: Resource> {
|
||||||
|
gl: Rc<glow::Context>,
|
||||||
|
resource: T,
|
||||||
|
}
|
||||||
|
impl<T: Resource> Deref for GlRcResource<T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.resource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> AsRef<GlResource<'a, T>> for RcGlResource<'a, T>
|
||||||
|
where
|
||||||
|
T: Resource,
|
||||||
|
{
|
||||||
|
fn as_ref(&self) -> &GlResource<'a, T> {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> GlRcResource<T> {
|
||||||
|
pub fn new(resource: T, gl: Rc<glow::Context>) -> Self {
|
||||||
|
Self { resource, gl }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> Drop for GlRcResource<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.resource.drop_self(&*self.gl);
|
||||||
|
info!("Dropping resource: {:?}", self.resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> Deref for GlResource<'_, T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.resource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Resource> GlResource<'a, T> {
|
||||||
|
pub fn new(resource: T, gl: &'a glow::Context) -> Self {
|
||||||
|
Self { resource, gl }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> Drop for GlResource<'_, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.resource.drop_self(self.gl);
|
||||||
|
info!("Dropping resource: {:?}", self.resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RcGlResource<'a, NativeTexture> {}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct GL {
|
||||||
|
gl: Rc<glow::Context>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GL {
|
||||||
|
pub fn new(gl: glow::Context) -> Self {
|
||||||
|
Self { gl: Rc::new(gl) }
|
||||||
|
}
|
||||||
|
pub fn gl<'a>(&'a self) -> &'a glow::Context {
|
||||||
|
&self.gl
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gl_rc(&self) -> Rc<glow::Context> {
|
||||||
|
self.gl.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_resource<T: Resource>(&self) -> RcGlResource<'_, T> {
|
||||||
|
RcGlResource::new(self.gl(), T::create(self.gl()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_resource_rc<T: Resource>(&self) -> RcGlRcResource<T> {
|
||||||
|
RcGlRcResource::new(self.gl_rc(), T::create(self.gl()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_resource_rc_with<T: Resource>(&self, resource: T) -> RcGlRcResource<T> {
|
||||||
|
RcGlRcResource::new(self.gl_rc(), resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_resource_with<T: Resource>(&self, resource: T) -> RcGlResource<'_, T> {
|
||||||
|
RcGlResource::new(self.gl(), resource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for GL {
|
||||||
|
type Target = glow::Context;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.gl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> ManagedResource for RcGlResource<'_, T> {
|
||||||
|
fn bind(&self, target: u32) {
|
||||||
|
self.0.resource.bind(self.0.gl, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind(&self, target: u32) {
|
||||||
|
self.0.resource.unbind(&self.0.gl, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> ManagedResource for RcGlRcResource<T> {
|
||||||
|
fn bind(&self, target: u32) {
|
||||||
|
self.0.resource.bind(&self.0.gl, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind(&self, target: u32) {
|
||||||
|
self.0.resource.unbind(&self.0.gl, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
5529
radar-g/Cargo.lock
generated
Normal file
5529
radar-g/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
95
radar-g/Cargo.toml
Normal file
95
radar-g/Cargo.toml
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
[package]
|
||||||
|
name = "cinrad_g"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cairo-rs = { version = "0.17.0" }
|
||||||
|
gtk = { version = "0.9", package = "gtk4", features = ["v4_14"] }
|
||||||
|
geo-types = "0.7.9"
|
||||||
|
shapefile = { version = "0.4", features = ["geo-types"] }
|
||||||
|
thiserror = "1.0.40"
|
||||||
|
num-traits = "0.2.15"
|
||||||
|
npyz = { version = "0.8.0", features = ["npz"] }
|
||||||
|
ndarray = { version = "0.15.6", features = ["rayon"] }
|
||||||
|
quadtree_rs = "0.1.2"
|
||||||
|
proj-sys = "0.23.1"
|
||||||
|
glib-macros = "0.19.2"
|
||||||
|
svg = "0.13.1"
|
||||||
|
libloading = "0.8.3"
|
||||||
|
glue = "0.8.7"
|
||||||
|
epoxy = "0.1.0"
|
||||||
|
femtovg = "0.9.0"
|
||||||
|
glow = "0.13.1"
|
||||||
|
proj = "0.27.2"
|
||||||
|
image = "0.24.7"
|
||||||
|
anyhow = "1.0.72"
|
||||||
|
relm4 = { version = "0.9", features = ["libadwaita"] }
|
||||||
|
relm4-components = "0.9"
|
||||||
|
rstar = "*"
|
||||||
|
geo = "0.26.0"
|
||||||
|
topojson = "0.5.1"
|
||||||
|
geojson = "0.24.1"
|
||||||
|
# plotters = "0.3.5"
|
||||||
|
core_extensions = { version = "1.5.2", default_features = false, features = [
|
||||||
|
"std",
|
||||||
|
] }
|
||||||
|
# plotters-backend = "0.3.5"
|
||||||
|
tokio = { version = "1.36.0", features = ["full"] }
|
||||||
|
async-trait = "0.1.77"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
once_cell = "1.19.0"
|
||||||
|
relm4-icons = { version = "0.9" }
|
||||||
|
surfman = "0.8.1"
|
||||||
|
euclid = "0.22.9"
|
||||||
|
gl = "0.14.0"
|
||||||
|
crossbeam = "0.8.4"
|
||||||
|
chrono = "0.4.32"
|
||||||
|
tracker = "0.2.1"
|
||||||
|
abi_stable = "0.11.3"
|
||||||
|
serde = "1.0.196"
|
||||||
|
serde_json = "1.0.112"
|
||||||
|
flate2 = "1.0.28"
|
||||||
|
toml = "0.8.8"
|
||||||
|
dirs = "5.0.1"
|
||||||
|
regex = "1.10.3"
|
||||||
|
smallvec = "1.13.1"
|
||||||
|
rayon = "1.8.1"
|
||||||
|
futures = "0.3.30"
|
||||||
|
sorted-vec = "0.8.3"
|
||||||
|
tracing = "0.1.40"
|
||||||
|
tracing-subscriber = "0.3.18"
|
||||||
|
indexmap = "2.2.2"
|
||||||
|
tokio-condvar = "0.1.0"
|
||||||
|
imgref = "1.10.1"
|
||||||
|
rgb = "0.8.37"
|
||||||
|
slippy-map-tiles = "0.16.0"
|
||||||
|
reqwest = "0.11.25"
|
||||||
|
url = "2.5.0"
|
||||||
|
quick_cache = "0.4.1"
|
||||||
|
fns = "0.0.7"
|
||||||
|
enum_dispatch = "0.3.12"
|
||||||
|
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
glib-build-tools = "0.17.0"
|
||||||
|
|
||||||
|
[dependencies.geo-macros]
|
||||||
|
path = "../geo-macros"
|
||||||
|
|
||||||
|
[dependencies.radarg_plugin_interface]
|
||||||
|
path = "../radarg_plugin_interface"
|
||||||
|
|
||||||
|
[dependencies.gi]
|
||||||
|
path = "../gi"
|
||||||
|
|
||||||
|
#[dependencies.etws_loader]
|
||||||
|
#path = "etws_loader"
|
||||||
|
|
||||||
|
[dependencies.adw]
|
||||||
|
package = "libadwaita"
|
||||||
|
version = "0.7.0"
|
||||||
|
features = ["v1_4"]
|
||||||
@ -8,9 +8,9 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::components::sidebar::{SideBarInputMsg, SideBarModel};
|
use crate::components::sidebar::{SideBarInputMsg, SideBarModel};
|
||||||
use crate::data_utils::tools;
|
use crate::data_utils::tools;
|
||||||
|
use crate::pipeline::element::Buffer;
|
||||||
|
use crate::pipeline::element::Element;
|
||||||
use crate::pipeline::element_imp::{Context, ElementInput, GridImpConfig};
|
use crate::pipeline::element_imp::{Context, ElementInput, GridImpConfig};
|
||||||
use crate::pipeline::new_element::Buffer;
|
|
||||||
use crate::pipeline::new_element::Element;
|
|
||||||
use crate::pipeline::runner::Runner;
|
use crate::pipeline::runner::Runner;
|
||||||
use crate::pipeline::{DataTarget, Key};
|
use crate::pipeline::{DataTarget, Key};
|
||||||
use crate::pipeline::{KVBuffer, OffscreenRenderer};
|
use crate::pipeline::{KVBuffer, OffscreenRenderer};
|
||||||
@ -48,6 +48,7 @@ use relm4_components::open_dialog::{
|
|||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
use std::num::NonZeroU32;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
borrow::{Borrow, BorrowMut},
|
borrow::{Borrow, BorrowMut},
|
||||||
@ -290,7 +291,7 @@ impl Component for AppModel {
|
|||||||
"CR",
|
"CR",
|
||||||
dialog_cms.clone(),
|
dialog_cms.clone(),
|
||||||
dialog_dispatcher.clone(),
|
dialog_dispatcher.clone(),
|
||||||
false,
|
true,
|
||||||
cfg,
|
cfg,
|
||||||
path.clone(),
|
path.clone(),
|
||||||
dialog_buffer.clone(),
|
dialog_buffer.clone(),
|
||||||
@ -339,6 +340,7 @@ impl Component for AppModel {
|
|||||||
};
|
};
|
||||||
group.add_action(action);
|
group.add_action(action);
|
||||||
group.register_for_widget(&widgets.main_window);
|
group.register_for_widget(&widgets.main_window);
|
||||||
|
|
||||||
ComponentParts { model, widgets }
|
ComponentParts { model, widgets }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,6 +27,7 @@ use crate::utils::estimate_zoom_level;
|
|||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
use femtovg::ImageId;
|
use femtovg::ImageId;
|
||||||
use fns::debounce;
|
use fns::debounce;
|
||||||
|
use gi::ui::typ::MainLoadAttach;
|
||||||
use relm4::{component::Component, *};
|
use relm4::{component::Component, *};
|
||||||
use slippy_map_tiles::Tile;
|
use slippy_map_tiles::Tile;
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
@ -45,6 +46,8 @@ pub struct MonitorModel {
|
|||||||
sidebar_width: i32,
|
sidebar_width: i32,
|
||||||
zoom: u8,
|
zoom: u8,
|
||||||
#[do_not_track]
|
#[do_not_track]
|
||||||
|
mainload: MainLoadAttach,
|
||||||
|
#[do_not_track]
|
||||||
last_call: Rc<RefCell<std::time::Instant>>,
|
last_call: Rc<RefCell<std::time::Instant>>,
|
||||||
#[do_not_track]
|
#[do_not_track]
|
||||||
map_tile_getter: Rc<MapTile>,
|
map_tile_getter: Rc<MapTile>,
|
||||||
@ -78,9 +81,9 @@ impl Component for MonitorModel {
|
|||||||
set_child = &Render{
|
set_child = &Render{
|
||||||
#[track = "model.changed(MonitorModel::render_cfg())"]
|
#[track = "model.changed(MonitorModel::render_cfg())"]
|
||||||
set_cfg: model.render_cfg,
|
set_cfg: model.render_cfg,
|
||||||
#[track = "model.changed(MonitorModel::render_range())"]
|
// #[track = "model.changed(MonitorModel::render_range())"]
|
||||||
set_view: model.render_range,
|
// set_view: model.render_range,
|
||||||
set_tiles: model.map_tile_getter.clone(),
|
// set_tiles: model.map_tile_getter.clone(),
|
||||||
connect_render_status_notify[sender] => move |r| {
|
connect_render_status_notify[sender] => move |r| {
|
||||||
sender.output(MonitorOutputMsg::LayerRenderFinished);
|
sender.output(MonitorOutputMsg::LayerRenderFinished);
|
||||||
},
|
},
|
||||||
@ -89,14 +92,14 @@ impl Component for MonitorModel {
|
|||||||
},
|
},
|
||||||
connect_scale_notify[sender] => move |r| {
|
connect_scale_notify[sender] => move |r| {
|
||||||
let scale = r.scale();
|
let scale = r.scale();
|
||||||
{
|
// {
|
||||||
let initial = r.scale_rate();
|
// let initial = r.scale_rate();
|
||||||
let mut rate_start = initial_rate.lock().unwrap();
|
// let mut rate_start = initial_rate.lock().unwrap();
|
||||||
if rate_start.is_none() {
|
// if rate_start.is_none() {
|
||||||
*rate_start = Some(scale);
|
// *rate_start = Some(scale);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
debouncer.call(scale);
|
// debouncer.call(scale);
|
||||||
},
|
},
|
||||||
set_interior_layers: model.layers.clone(),
|
set_interior_layers: model.layers.clone(),
|
||||||
},
|
},
|
||||||
@ -126,34 +129,14 @@ impl Component for MonitorModel {
|
|||||||
MonitorInputMsg::RefreshRender => {
|
MonitorInputMsg::RefreshRender => {
|
||||||
widgets.renderer.queue_render();
|
widgets.renderer.queue_render();
|
||||||
}
|
}
|
||||||
MonitorInputMsg::SetRenderRange(lon_start, lon_end, lat_start, lat_end) => {
|
MonitorInputMsg::SetRenderRange(lon_start, lon_end, lat_start, lat_end) => {}
|
||||||
let current_rate = widgets.renderer.scale_rate();
|
MonitorInputMsg::RefreshTiles => {}
|
||||||
self.set_render_range((lat_start, lat_end, lon_start, lon_end));
|
|
||||||
let new_rate = widgets.renderer.scale_rate();
|
|
||||||
let zoom: f64 = (new_rate / current_rate).log2();
|
|
||||||
sender.input(MonitorInputMsg::ChangeZoom(zoom));
|
|
||||||
}
|
|
||||||
MonitorInputMsg::RefreshTiles => {
|
|
||||||
let ((x1, x2), (y1, y2)) = widgets.renderer.render_range();
|
|
||||||
self.load_tile(&sender, ((y1 as f32, y2 as f32), (x1 as f32, x2 as f32)));
|
|
||||||
}
|
|
||||||
MonitorInputMsg::AddWidget(widget) => match widget.widget_type() {
|
MonitorInputMsg::AddWidget(widget) => match widget.widget_type() {
|
||||||
WidgetType::Cairo => {
|
WidgetType::Cairo => {}
|
||||||
let frame = WidgetFrame::new();
|
|
||||||
frame.set_widget(widget);
|
|
||||||
self.widgets.push(frame);
|
|
||||||
}
|
|
||||||
WidgetType::OpenGl => {}
|
WidgetType::OpenGl => {}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
MonitorInputMsg::ChangeZoom(zoom) => {
|
MonitorInputMsg::ChangeZoom(zoom) => {}
|
||||||
let new_zoom = (self.zoom as f64 + zoom).clamp(0.0, 19.0).round() as u8;
|
|
||||||
if self.zoom != new_zoom {
|
|
||||||
self.zoom = new_zoom;
|
|
||||||
self.map_tile_getter.set_zoom(new_zoom);
|
|
||||||
sender.input(MonitorInputMsg::RefreshTiles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MonitorInputMsg::RemoveWidget => {}
|
MonitorInputMsg::RemoveWidget => {}
|
||||||
MonitorInputMsg::None => {}
|
MonitorInputMsg::None => {}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -167,24 +150,15 @@ impl Component for MonitorModel {
|
|||||||
root: Self::Root,
|
root: Self::Root,
|
||||||
sender: ComponentSender<Self>,
|
sender: ComponentSender<Self>,
|
||||||
) -> ComponentParts<Self> {
|
) -> ComponentParts<Self> {
|
||||||
// let sidebar_sender = sender.clone();
|
|
||||||
// let sidebar: Controller<SideBarModel> = SideBarModel::builder()
|
|
||||||
// .launch(init.clone())
|
|
||||||
// .forward(sender.input_sender(), move |msg| match msg {
|
|
||||||
// SideBarOutputMsg::SwitchToTimeSeries(layer) => {
|
|
||||||
// sidebar_sender.output(MonitorOutputMsg::LayerSwitchToTime(layer));
|
|
||||||
// MonitorInputMsg::None
|
|
||||||
// }
|
|
||||||
// _ => MonitorInputMsg::None,
|
|
||||||
// });
|
|
||||||
|
|
||||||
let render_cfg = RenderConfig {
|
let render_cfg = RenderConfig {
|
||||||
padding: [0.0, 0.0, 0.0, 0.0],
|
padding: [0.0, 0.0, 0.0, 0.0],
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_sender = sender.clone();
|
let new_sender = sender.clone();
|
||||||
|
|
||||||
|
let mainload = MainLoadAttach::new(None);
|
||||||
let mut model = MonitorModel {
|
let mut model = MonitorModel {
|
||||||
|
mainload,
|
||||||
render_range: (4.0, 53.3, 73.3, 135.0),
|
render_range: (4.0, 53.3, 73.3, 135.0),
|
||||||
new_layer: 0,
|
new_layer: 0,
|
||||||
widgets: vec![],
|
widgets: vec![],
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user