Compare commits

..

No commits in common. "77d8c1f0ebda4bc4aee6c333eb1eb60e351a4f08" and "a07acecea2e76a5a5664c72f14d754d2c9144afe" have entirely different histories.

177 changed files with 4325 additions and 16004 deletions

3500
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,92 @@
[package]
name = "cinrad_g"
version = "0.1.0"
edition = "2021"
[workspace]
members = [
"etws_loader",
"radarg_plugin_interface",
"geo-macros",
"radar-g",
"gi",
]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cairo-rs = { version = "0.17.0" }
# glib = "0.17.9"
gtk = { version = "0.8.1", package = "gtk4", features = ["v4_12"] }
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 Normal file
View File

@ -0,0 +1,395 @@
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 Normal file
View File

@ -0,0 +1,9 @@
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
View File

@ -56,24 +56,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "android-tzdata"
version = "0.1.1"
@ -95,15 +77,6 @@ version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "as_derive_utils"
version = "0.11.0"
@ -221,22 +194,6 @@ version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "etws_loader"
version = "0.1.0"
@ -246,7 +203,6 @@ dependencies = [
"byteorder",
"chrono",
"flate2",
"geo",
"nom",
"nom-derive",
"num-traits",
@ -266,12 +222,6 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "float_next_after"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8"
[[package]]
name = "generational-arena"
version = "0.2.9"
@ -281,73 +231,6 @@ dependencies = [
"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]]
name = "iana-time-zone"
version = "0.1.59"
@ -371,15 +254,6 @@ dependencies = [
"cc",
]
[[package]]
name = "itertools"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.10"
@ -411,12 +285,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "libm"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "lock_api"
version = "0.4.11"
@ -493,7 +361,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
"libm",
]
[[package]]
@ -574,23 +441,6 @@ dependencies = [
"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]]
name = "rustc_version"
version = "0.4.0"
@ -661,24 +511,6 @@ version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "syn"
version = "1.0.109"
@ -748,12 +580,6 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasm-bindgen"
version = "0.2.90"
@ -952,23 +778,3 @@ name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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",
]

View File

@ -10,7 +10,6 @@ anyhow = "1.0.79"
byteorder = "1.5.0"
chrono = {version="0.4.33", features=["serde"]}
flate2 = "1.0.28"
geo = "0.28.0"
nom = "7.1.3"
nom-derive = "0.10.1"
num-traits = "0.2.17"

View File

@ -1,5 +1,4 @@
mod error;
use geo::{algorithm::haversine_destination::HaversineDestination, Point};
mod parser;
use abi_stable::{
export_root_module,
@ -15,7 +14,7 @@ use abi_stable::{
};
use parser::{Record, ValueResult};
use radarg_plugin_interface::{
CoordType, Error, Loc3, MetaData, Plugin, PluginId, PluginMod, PluginMod_Ref, PluginResult,
CoordType, Error, MetaData, Plugin, PluginId, PluginMod, PluginMod_Ref, PluginResult,
PluginResultType, PluginType, Plugin_TO,
};
@ -28,23 +27,6 @@ struct ETWSLoader {
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 {
fn plugin_id(&self) -> &PluginId {
&self.id
@ -98,55 +80,33 @@ impl Plugin for ETWSLoader {
};
let dimension_des = b.info.dimension_des;
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
} else if dimension_des.contains(&format!("ele"))
|| dimension_des.contains(&format!("el"))
|| dimension_des.contains(&format!("elevation"))
{
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],
)
CoordType::Polar
} else {
CoordType::Other
};
let shape = match b.info.dimension_size.len() {
1 => radarg_plugin_interface::DataShape::Vector,
2 => {
// let lat = b.info.dimension_values.get(0).unwrap();
// let lon = b.info.dimension_values.get(1).unwrap();
// lat_range = [lat[0], lat[lat.len() - 1]];
// lon_range = [lon[0], lon[lon.len() - 1]];
let lat = b.info.dimension_values.get(0).unwrap();
let lon = b.info.dimension_values.get(1).unwrap();
lat_range = [lat[0], lat[lat.len() - 1]];
lon_range = [lon[0], lon[lon.len() - 1]];
radarg_plugin_interface::DataShape::Matrix
}
},
_ => {
// let lat = b.info.dimension_values.get(1).unwrap();
// let lon = b.info.dimension_values.get(2).unwrap();
// lat_range = [lat[0], lat[lat.len() - 1]];
// lon_range = [lon[0], lon[lon.len() - 1]];
let lat = b.info.dimension_values.get(1).unwrap();
let lon = b.info.dimension_values.get(2).unwrap();
lat_range = [lat[0], lat[lat.len() - 1]];
lon_range = [lon[0], lon[lon.len() - 1]];
radarg_plugin_interface::DataShape::Cube
}
},
};
let data_type = match b.info.value_name.as_str() {
@ -237,7 +197,7 @@ mod tests {
fn test() {
let result =
Record::parse_from_path("/Volumes/data2/RadarArray/ShaoXing/radarData/OutputProducts/RadarProducts/BasicProductsX/20230627/20230627163400/ZJSXAA_20230627163400_VIL.dat.gz")
Record::parse_from_path("/Volumes/data2/RadarArray/HangZhou/radarData/OutputProducts/RadarProducts/FuseDataX/20220727/ZJHZAA_20220727200000_FR.dat.gz")
.unwrap();
}
}

View File

@ -364,13 +364,237 @@ pub struct BlockJsonInfo {
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,
}
// 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))
// }
// }
// }

View File

@ -1 +1 @@
{"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":{}}
{"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":{}}

View File

@ -1 +1 @@
498af0718dbbcb83
16f3840281ed1c0f

View File

@ -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,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}
{"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}

View File

@ -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 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: `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":"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":"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"}

View File

@ -1,58 +0,0 @@
[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"

View File

@ -1,66 +0,0 @@
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),
}
}
}

View File

@ -1,5 +0,0 @@
mod program;
mod shader;
pub use program::Program;
pub use shader::Shader;

View File

@ -1,136 +0,0 @@
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()
}
}

View File

@ -1,83 +0,0 @@
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
}
}

View File

@ -1,14 +0,0 @@
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,
},
}

View File

@ -1,260 +0,0 @@
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);
}
}

View File

@ -1,388 +0,0 @@
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,
}

View File

@ -1,33 +0,0 @@
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),
}

View File

@ -1,211 +0,0 @@
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();
}
}
}

View File

@ -1,396 +0,0 @@
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();
}
}

View File

@ -1,7 +0,0 @@
pub mod agg_fast_path;
pub trait Colletion {
type Item;
fn append(&mut self, item: Self::Item);
}

View File

@ -1,125 +0,0 @@
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);
}
}

View File

@ -1,15 +0,0 @@
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);
}

View File

@ -1 +0,0 @@
pub struct ColorMesh {}

View File

@ -1,740 +0,0 @@
//! 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);
}
}
}

View File

@ -1,411 +0,0 @@
// 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,
);
}
}

View File

@ -1,5 +0,0 @@
mod esdt;
mod img;
pub use esdt::*;
pub use img::{Image2d, Unorm8};

View File

@ -1,345 +0,0 @@
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();
}
}

View File

@ -1,258 +0,0 @@
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
}
}

View File

@ -1,147 +0,0 @@
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],
}

View File

@ -1,7 +0,0 @@
pub struct Plane {}
impl Plane {
pub fn new() -> Self {
Self {}
}
}

View File

@ -1,243 +0,0 @@
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();
}
}

View File

@ -1,77 +0,0 @@
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),
)
}
}

View File

@ -1 +0,0 @@

View File

@ -1,59 +0,0 @@
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());
// }
// }

View File

@ -1,127 +0,0 @@
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 }
}
}

View File

@ -1,182 +0,0 @@
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);
}
}

View File

@ -1,3 +0,0 @@
use bytemuck::{Pod, Zeroable};
pub trait Ty: Pod {}

View File

@ -1,16 +0,0 @@
#![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::*;

View File

@ -1,65 +0,0 @@
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
}
}

View File

@ -1,242 +0,0 @@
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},
);

View File

@ -1,174 +0,0 @@
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;
}
}

View File

@ -1,120 +0,0 @@
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);
}

View File

@ -1,406 +0,0 @@
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);
}
}

View File

@ -1,117 +0,0 @@
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)
}
}

View File

@ -1,126 +0,0 @@
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);

View File

@ -1,50 +0,0 @@
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);

View File

@ -1,213 +0,0 @@
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);

View File

@ -1,73 +0,0 @@
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);

View File

@ -1,99 +0,0 @@
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);
}
}

View File

@ -1,58 +0,0 @@
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);

View File

@ -1,147 +0,0 @@
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);

View File

@ -1,70 +0,0 @@
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);

View File

@ -1,52 +0,0 @@
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);
}
});
}
}

View File

@ -1,17 +0,0 @@
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,
}

View File

@ -1,16 +0,0 @@
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;

View File

@ -1,137 +0,0 @@
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()
}
}

View File

@ -1,122 +0,0 @@
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(())
}
}

View File

@ -1,66 +0,0 @@
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
// },);

View File

@ -1,61 +0,0 @@
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)?)))
}
}

View File

@ -1,18 +0,0 @@
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])
}

View File

@ -1,17 +0,0 @@
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)
}

View File

@ -1,22 +0,0 @@
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())
}
}

View File

@ -1,320 +0,0 @@
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

File diff suppressed because it is too large Load Diff

View File

@ -1,95 +0,0 @@
[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"]

View File

@ -1,142 +0,0 @@
use crate::pipeline::element_imp::ElementImpl::MultiLayerGrid;
use crate::pipeline::element_imp::*;
use crate::utils::*;
use crate::CONFIG;
use abi_stable::traits::IntoOwned;
use num_traits::FromPrimitive;
use radarg_plugin_interface::{CoordType, DataShape, PluginResult, PluginResultType};
use std::any::Any;
use std::sync::Arc;
macro_rules! dispatch {
($block:ident,$conf:ident, $wrap:tt , $fill_value: ident, $(
{
$t:ty | $branch: pat => $v:ident
}
),+ $(,)?) => {
match $block.data_type {
$(
$branch => {
let mut $v = $wrap::default();
$v.color_map = $conf.drawers.$v.color_mapper.clone();
$v.fill_value = <$t>::from_f64($fill_value).unwrap();
Arc::new($v) as Arc<dyn Any + Send + Sync>
}
)+
_ => { panic!("") }
}
};
}
macro_rules! dispatch_polar {
($block:ident,$conf:ident, $wrap:tt , $fill_value: ident, $center:ident, $(
{
$t:ty | $branch: pat => $v:ident
}
),+ $(,)?) => {
match $block.data_type {
$(
$branch => {
let mut $v = $wrap::default();
$v.color_map = $conf.drawers.$v.color_mapper.clone();
$v.fill_value = <$t>::from_f64($fill_value).unwrap();
$v.center = $center;
Arc::new($v) as Arc<dyn Any + Send + Sync>
}
)+
_ => { panic!("") }
}
};
}
macro_rules! dispatch_3d {
($block:ident,$conf:ident, $wrap:tt , $fill_value: ident, $(
{
$t:ty | $branch: pat => $v:ident
}
),+ $(,)?) => {
match $block.data_type {
$(
$branch => {
let mut $v = $wrap::default();
$v.color_map = $conf.drawers.$v.color_mapper.clone();
$v.fill_value = <$t>::from_f64($fill_value).unwrap();
let mut cfg = MultiLayerGridImpConfig{
two_d_config: $v,
layer: 0,
};
Arc::new(cfg) as Arc<dyn Any + Send + Sync>
}
)+
_ => { panic!("") }
}
};
}
macro_rules! dis {
($mac:ident ,$block:ident, $config:ident, $wrap:tt, $fill_value: ident, $($other:ident,)?) => {
$mac!(
$block, $config, $wrap, $fill_value, $($other,)?
{ i8|PluginResultType::R => reflectivity },
{ i8|PluginResultType::DBZ => reflectivity },
{ f32|PluginResultType::ZDR => differential_reflectivity },
{ f32|PluginResultType::KDP => specific_differential_phase },
{ f32|PluginResultType::PHIDP => differential_phase },
{ i8 |PluginResultType::V => velocity },
{ i8 |PluginResultType::SW => spectrum_width },
{ f32|PluginResultType::CC => correlation_coefficient },
{ i8 |PluginResultType::HCA => hydrometeor_classification },
{ f32|PluginResultType::VIL => vertically_integrated_liquid },
{ f32|PluginResultType::OHP => one_hour_precipitation },
{ f32|PluginResultType::THP => three_hour_precipitation },
{ f32|PluginResultType::ET => echo_tops },
{ f32|PluginResultType::EB => echo_bases }
)
};
}
type Cfg = Arc<dyn Any + Send + Sync>;
pub fn tools(data: &PluginResult) -> (ElementImpl, Cfg) {
let blocks_num = data.blocks.len();
if blocks_num == 0 {
panic!("No blocks found");
}
if blocks_num > 1 {
panic!("Too many blocks found");
}
let block = data.blocks.first().unwrap();
let fill_value = block.fill_value;
let config = CONFIG.read().unwrap();
let imp = match block.coord_type {
CoordType::Polar(loc, range) => {
let center = (loc.x, loc.y);
let cfg = dis!(dispatch_polar, block, config, PolarElementConfig, fill_value, center,);
(PolarElementImp().into(), cfg)
}
CoordType::Cartesian => match block.shape {
DataShape::Cube => {
let cfg = dis!(dispatch_3d, block, config, GridImpConfig, fill_value,);
(MultiLayerGridImp::new().into(), cfg)
}
DataShape::Matrix => (
GridImp::new().into(),
dis!(dispatch, block, config, GridImpConfig, fill_value,),
),
_ => panic!("Invalid shape"),
},
_ => {
panic!("Invalid type")
}
};
imp
}

View File

@ -1,93 +0,0 @@
mod imp;
use super::super::Render;
use crate::coords::Range;
use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path};
use gtk::glib;
use gtk::prelude::*;
use gtk::subclass::prelude::ObjectSubclassIsExt;
glib::wrapper! {
pub struct ExteriorWidget(ObjectSubclass<imp::ExteriorWidget>);
}
impl Default for ExteriorWidget {
fn default() -> Self {
Self::new()
}
}
impl ExteriorWidget {
pub fn new() -> Self {
let this: Self = glib::Object::new();
this
}
pub fn draw(&self, canvas: &mut Canvas<OpenGl>, render: &Render) {
let padding = render.imp().config.borrow().padding;
let (w, h) = render.window_size();
let (w, h) = (w as f32, h as f32);
let origins = [
(0.0, 0.0),
(w - padding[1], 0.0),
(0.0, h - padding[2]),
(0.0, 0.0),
];
let whs = [
(w, padding[0]),
(padding[1], h),
(w, padding[2]),
(padding[3], h),
];
let painter = Paint::color(Color::rgb(0, 0, 0));
for edge in 0..4 {
let mut path = Path::new();
let (ox, oy) = origins[edge];
let (w, h) = whs[edge];
path.rect(ox, oy, w, h);
canvas.fill_path(&path, &painter);
}
// let (lon_range, lat_range) = render.render_range();
// let (lon_range, lat_range): (Range, Range) = (lon_range.into(), lat_range.into());
// let lon_keypoints = lon_range.key_points(10);
// let lat_keypoints = lat_range.key_points(5);
// let mut paint = Paint::color(Color::white()); // 黑色字体
// let dpi = render.scale_factor();
// paint.set_font_size((dpi * 12) as f32);
// for lon in lon_keypoints.iter() {
// let (x, _) = render.map((*lon, lat_range.0)).unwrap();
// let text = format!("{:.2}", lon);
// let metrics = canvas
// .measure_text(x, 0.0, text.as_str(), &paint)
// .expect("Cannot measure text");
// let text_x = x - metrics.width() / 2.0;
// let text_y = h - metrics.height() / 2.0;
// canvas
// .fill_text(text_x, text_y, text.as_str(), &paint)
// .expect("Cannot draw text");
// }
// for lat in lat_keypoints.iter() {
// let (_, y) = render.map((lon_range.0, *lat)).unwrap();
// let text = format!("{:.2}", lat);
// let metrics = canvas
// .measure_text(0.0, y, text.as_str(), &paint)
// .expect("Cannot measure text");
// let text_x = 0.0;
// let text_y = y - metrics.height() / 2.0;
// canvas
// .fill_text(text_x, text_y, text.as_str(), &paint)
// .expect("Cannot draw text");
// }
}
}

View File

@ -19,22 +19,11 @@ pub struct PluginId {
pub instance: u64,
}
#[repr(C)]
#[derive(Debug, Clone, StableAbi, Copy)]
#[sabi(impl_InterfaceType(Sync, Send, Debug, Debug))]
pub struct Loc3 {
pub x: f64,
pub y: f64,
pub z: f64,
}
pub type Range = f64;
#[repr(C)]
#[derive(StableAbi, Clone, Debug, Copy)]
#[sabi(impl_InterfaceType(Sync, Send, Debug, Debug))]
pub enum CoordType {
Polar(Loc3, Range),
Polar,
Cartesian,
Other,
}

View File

@ -1 +1 @@
{"rustc_fingerprint":756262368934451307,"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":8475780184195034948,"outputs":{"15729799797837862367":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/ruomu/.rustup/toolchains/nightly-x86_64-apple-darwin\noff\npacked\nunpacked\n___\ndebug_assertions\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"cmpxchg16b\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_feature=\"ssse3\"\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=\"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":""},"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.74.0-nightly (8142a319e 2023-09-13)\nbinary: rustc\ncommit-hash: 8142a319ed5c1d1f96e5a1881a6546e463b77c8f\ncommit-date: 2023-09-13\nhost: x86_64-apple-darwin\nrelease: 1.74.0-nightly\nLLVM version: 17.0.0\n","stderr":""}},"successes":{}}

340
src/chart/backend.rs Normal file
View File

@ -0,0 +1,340 @@
use gtk::cairo::{Context as CairoContext, FontSlant, FontWeight};
use plotters_backend::text_anchor::{HPos, VPos};
#[allow(unused_imports)]
use plotters_backend::{
BackendColor, BackendCoord, BackendStyle, BackendTextStyle, DrawingBackend, DrawingErrorKind,
FontStyle, FontTransform,
};
/// The drawing backend that is backed with a Cairo context
pub struct CairoBackend<'a> {
context: &'a CairoContext,
width: u32,
height: u32,
init_flag: bool,
}
#[derive(Debug)]
pub struct CairoError;
impl std::fmt::Display for CairoError {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "{:?}", self)
}
}
impl std::error::Error for CairoError {}
impl<'a> CairoBackend<'a> {
fn set_color(&self, color: &BackendColor) {
self.context.set_source_rgba(
f64::from(color.rgb.0) / 255.0,
f64::from(color.rgb.1) / 255.0,
f64::from(color.rgb.2) / 255.0,
color.alpha,
);
}
fn set_stroke_width(&self, width: u32) {
self.context.set_line_width(f64::from(width));
}
fn set_font<S: BackendTextStyle>(&self, font: &S) {
match font.style() {
FontStyle::Normal => self.context.select_font_face(
font.family().as_str(),
FontSlant::Normal,
FontWeight::Normal,
),
FontStyle::Bold => self.context.select_font_face(
font.family().as_str(),
FontSlant::Normal,
FontWeight::Bold,
),
FontStyle::Oblique => self.context.select_font_face(
font.family().as_str(),
FontSlant::Oblique,
FontWeight::Normal,
),
FontStyle::Italic => self.context.select_font_face(
font.family().as_str(),
FontSlant::Italic,
FontWeight::Normal,
),
};
self.context.set_font_size(font.size());
}
pub fn new(context: &'a CairoContext, (w, h): (u32, u32)) -> Result<Self, CairoError> {
Ok(Self {
context,
width: w,
height: h,
init_flag: false,
})
}
}
impl<'a> DrawingBackend for CairoBackend<'a> {
type ErrorType = gtk::cairo::Error;
fn get_size(&self) -> (u32, u32) {
(self.width, self.height)
}
fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
if !self.init_flag {
let (x0, y0, x1, y1) = self
.context
.clip_extents()
.map_err(DrawingErrorKind::DrawingError)?;
self.context.scale(
(x1 - x0) / f64::from(self.width),
(y1 - y0) / f64::from(self.height),
);
self.init_flag = true;
}
Ok(())
}
fn present(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
Ok(())
}
fn draw_pixel(
&mut self,
point: BackendCoord,
color: BackendColor,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.context
.rectangle(f64::from(point.0), f64::from(point.1), 1.0, 1.0);
self.context.set_source_rgba(
f64::from(color.rgb.0) / 255.0,
f64::from(color.rgb.1) / 255.0,
f64::from(color.rgb.2) / 255.0,
color.alpha,
);
self.context
.fill()
.map_err(DrawingErrorKind::DrawingError)?;
Ok(())
}
fn draw_line<S: BackendStyle>(
&mut self,
from: BackendCoord,
to: BackendCoord,
style: &S,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.set_color(&style.color());
self.set_stroke_width(style.stroke_width());
self.context.move_to(f64::from(from.0), f64::from(from.1));
self.context.line_to(f64::from(to.0), f64::from(to.1));
self.context
.stroke()
.map_err(DrawingErrorKind::DrawingError)?;
Ok(())
}
fn draw_rect<S: BackendStyle>(
&mut self,
upper_left: BackendCoord,
bottom_right: BackendCoord,
style: &S,
fill: bool,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.set_color(&style.color());
self.set_stroke_width(style.stroke_width());
self.context.rectangle(
f64::from(upper_left.0),
f64::from(upper_left.1),
f64::from(bottom_right.0 - upper_left.0),
f64::from(bottom_right.1 - upper_left.1),
);
if fill {
self.context
.fill()
.map_err(DrawingErrorKind::DrawingError)?;
} else {
self.context
.stroke()
.map_err(DrawingErrorKind::DrawingError)?;
}
Ok(())
}
fn draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
&mut self,
path: I,
style: &S,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.set_color(&style.color());
self.set_stroke_width(style.stroke_width());
let mut path = path.into_iter();
if let Some((x, y)) = path.next() {
self.context.move_to(f64::from(x), f64::from(y));
}
for (x, y) in path {
self.context.line_to(f64::from(x), f64::from(y));
}
self.context
.stroke()
.map_err(DrawingErrorKind::DrawingError)?;
Ok(())
}
fn fill_polygon<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
&mut self,
path: I,
style: &S,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.set_color(&style.color());
self.set_stroke_width(style.stroke_width());
let mut path = path.into_iter();
if let Some((x, y)) = path.next() {
self.context.move_to(f64::from(x), f64::from(y));
for (x, y) in path {
self.context.line_to(f64::from(x), f64::from(y));
}
self.context.close_path();
self.context
.fill()
.map_err(DrawingErrorKind::DrawingError)?;
}
Ok(())
}
fn draw_circle<S: BackendStyle>(
&mut self,
center: BackendCoord,
radius: u32,
style: &S,
fill: bool,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.set_color(&style.color());
self.set_stroke_width(style.stroke_width());
self.context.new_sub_path();
self.context.arc(
f64::from(center.0),
f64::from(center.1),
f64::from(radius),
0.0,
std::f64::consts::PI * 2.0,
);
if fill {
self.context
.fill()
.map_err(DrawingErrorKind::DrawingError)?;
} else {
self.context
.stroke()
.map_err(DrawingErrorKind::DrawingError)?;
}
Ok(())
}
fn estimate_text_size<S: BackendTextStyle>(
&self,
text: &str,
font: &S,
) -> Result<(u32, u32), DrawingErrorKind<Self::ErrorType>> {
self.set_font(font);
let extents = self
.context
.text_extents(text)
.map_err(DrawingErrorKind::DrawingError)?;
Ok((extents.width() as u32, extents.height() as u32))
}
fn draw_text<S: BackendTextStyle>(
&mut self,
text: &str,
style: &S,
pos: BackendCoord,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
let color = style.color();
let (mut x, mut y) = (pos.0, pos.1);
let degree = match style.transform() {
FontTransform::None => 0.0,
FontTransform::Rotate90 => 90.0,
FontTransform::Rotate180 => 180.0,
FontTransform::Rotate270 => 270.0,
//FontTransform::RotateAngle(angle) => angle as f64,
} / 180.0
* std::f64::consts::PI;
if degree != 0.0 {
self.context
.save()
.map_err(DrawingErrorKind::DrawingError)?;
self.context.translate(f64::from(x), f64::from(y));
self.context.rotate(degree);
x = 0;
y = 0;
}
self.set_font(style);
self.set_color(&color);
let extents = self
.context
.text_extents(text)
.map_err(DrawingErrorKind::DrawingError)?;
let dx = match style.anchor().h_pos {
HPos::Left => 0.0,
HPos::Right => -extents.width(),
HPos::Center => -extents.width() / 2.0,
};
let dy = match style.anchor().v_pos {
VPos::Top => extents.height(),
VPos::Center => extents.height() / 2.0,
VPos::Bottom => 0.0,
};
self.context.move_to(
f64::from(x) + dx - extents.x_bearing(),
f64::from(y) + dy - extents.y_bearing() - extents.height(),
);
self.context
.show_text(text)
.map_err(DrawingErrorKind::DrawingError)?;
if degree != 0.0 {
self.context
.restore()
.map_err(DrawingErrorKind::DrawingError)?;
}
Ok(())
}
}

19
src/chart/imp.rs Normal file
View File

@ -0,0 +1,19 @@
use gtk::subclass::prelude::*;
use gtk::{glib, prelude::WidgetExtManual};
use std::cell::{Cell, RefCell};
#[derive(Default)]
pub struct Chart {}
#[glib::object_subclass]
impl ObjectSubclass for Chart {
const NAME: &'static str = "Chart";
type Type = super::Chart;
type ParentType = gtk::DrawingArea;
}
impl ObjectImpl for Chart {}
impl WidgetImpl for Chart {}
impl DrawingAreaImpl for Chart {}

109
src/chart/mod.rs Normal file
View File

@ -0,0 +1,109 @@
mod backend;
mod imp;
use self::backend::CairoBackend;
use crate::widgets::render::{Render, RenderConfig, RenderMotion};
use gtk::prelude::*;
use gtk::{glib, AspectFrame};
use plotters::prelude::*;
glib::wrapper! {
pub struct Chart(ObjectSubclass<imp::Chart>)
@extends gtk::DrawingArea, gtk::Widget;
}
impl Chart {
pub fn new() -> Self {
let this: Self = glib::Object::new();
this.set_hexpand(true);
this.set_vexpand(true);
this.set_draw_func(|_s, context, w, h| {
let root_area = CairoBackend::new(context, (w as u32, h as u32))
.expect("Can't create backend")
.into_drawing_area();
root_area.fill(&BLACK).unwrap();
let root_area = root_area.titled("Image Title", ("sans-serif", 60)).unwrap();
let (upper, lower) = root_area.split_vertically(512);
let x_axis = (-3.4f32..3.4).step(0.1);
let mut cc = ChartBuilder::on(&upper)
.margin(5)
.set_all_label_area_size(50)
.caption("Sine and Cosine", ("sans-serif", 40))
.build_cartesian_2d(-3.4f32..3.4, -1.2f32..1.2f32).unwrap();
cc.configure_mesh()
.x_labels(20)
.y_labels(10)
.disable_mesh()
.x_label_formatter(&|v| format!("{:.1}", v))
.y_label_formatter(&|v| format!("{:.1}", v))
.draw().unwrap();
cc.draw_series(LineSeries::new(x_axis.values().map(|x| (x, x.sin())), &RED)).unwrap()
.label("Sine")
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], RED));
cc.draw_series(LineSeries::new(
x_axis.values().map(|x| (x, x.cos())),
&BLUE,
)).unwrap()
.label("Cosine")
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], BLUE));
cc.configure_series_labels().border_style(WHITE).draw().unwrap();
/*
// It's possible to use a existing pointing element
cc.draw_series(PointSeries::<_, _, Circle<_>>::new(
(-3.0f32..2.1f32).step(1.0).values().map(|x| (x, x.sin())),
5,
Into::<ShapeStyle>::into(&RGBColor(255,0,0)).filled(),
)).unwrap();*/
// Otherwise you can use a function to construct your pointing element yourself
cc.draw_series(PointSeries::of_element(
(-3.0f32..2.1f32).step(1.0).values().map(|x| (x, x.sin())),
5,
ShapeStyle::from(&RED).filled(),
&|coord, size, style| {
EmptyElement::at(coord)
+ Circle::new((0, 0), size, style)
+ Text::new(format!("{:.?}", coord), (0, 15), ("sans-serif", 15))
},
)).unwrap();
let drawing_areas = lower.split_evenly((1, 2));
for (drawing_area, idx) in drawing_areas.iter().zip(1..) {
let mut cc = ChartBuilder::on(drawing_area)
.x_label_area_size(30)
.y_label_area_size(30)
.margin_right(20)
.caption(format!("y = x^{}", 1 + 2 * idx), ("sans-serif", 40))
.build_cartesian_2d(-1f32..1f32, -1f32..1f32).unwrap();
cc.configure_mesh()
.x_labels(5)
.y_labels(3)
.max_light_lines(4)
.draw().unwrap();
cc.draw_series(LineSeries::new(
(-1f32..1f32)
.step(0.01)
.values()
.map(|x| (x, x.powf(idx as f32 * 2.0 + 1.0))),
&BLUE,
)).unwrap();
}
// To avoid the IO failure being ignored silently, we manually call the present function
root_area.present().expect("Unable to write result to file, please make sure 'plotters-doc-data' dir exists under current dir");
});
this
}
}

View File

@ -8,9 +8,9 @@ use super::{
};
use crate::components::sidebar::{SideBarInputMsg, SideBarModel};
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::new_element::Buffer;
use crate::pipeline::new_element::Element;
use crate::pipeline::runner::Runner;
use crate::pipeline::{DataTarget, Key};
use crate::pipeline::{KVBuffer, OffscreenRenderer};
@ -48,7 +48,6 @@ use relm4_components::open_dialog::{
};
use smallvec::SmallVec;
use std::marker::PhantomData;
use std::num::NonZeroU32;
use std::{
any::Any,
borrow::{Borrow, BorrowMut},
@ -291,7 +290,7 @@ impl Component for AppModel {
"CR",
dialog_cms.clone(),
dialog_dispatcher.clone(),
true,
false,
cfg,
path.clone(),
dialog_buffer.clone(),
@ -340,7 +339,6 @@ impl Component for AppModel {
};
group.add_action(action);
group.register_for_widget(&widgets.main_window);
ComponentParts { model, widgets }
}

View File

@ -27,7 +27,6 @@ use crate::utils::estimate_zoom_level;
use adw::prelude::*;
use femtovg::ImageId;
use fns::debounce;
use gi::ui::typ::MainLoadAttach;
use relm4::{component::Component, *};
use slippy_map_tiles::Tile;
use tokio::task;
@ -46,8 +45,6 @@ pub struct MonitorModel {
sidebar_width: i32,
zoom: u8,
#[do_not_track]
mainload: MainLoadAttach,
#[do_not_track]
last_call: Rc<RefCell<std::time::Instant>>,
#[do_not_track]
map_tile_getter: Rc<MapTile>,
@ -81,9 +78,9 @@ impl Component for MonitorModel {
set_child = &Render{
#[track = "model.changed(MonitorModel::render_cfg())"]
set_cfg: model.render_cfg,
// #[track = "model.changed(MonitorModel::render_range())"]
// set_view: model.render_range,
// set_tiles: model.map_tile_getter.clone(),
#[track = "model.changed(MonitorModel::render_range())"]
set_view: model.render_range,
set_tiles: model.map_tile_getter.clone(),
connect_render_status_notify[sender] => move |r| {
sender.output(MonitorOutputMsg::LayerRenderFinished);
},
@ -92,14 +89,14 @@ impl Component for MonitorModel {
},
connect_scale_notify[sender] => move |r| {
let scale = r.scale();
// {
// let initial = r.scale_rate();
// let mut rate_start = initial_rate.lock().unwrap();
// if rate_start.is_none() {
// *rate_start = Some(scale);
// }
// }
// debouncer.call(scale);
{
let initial = r.scale_rate();
let mut rate_start = initial_rate.lock().unwrap();
if rate_start.is_none() {
*rate_start = Some(scale);
}
}
debouncer.call(scale);
},
set_interior_layers: model.layers.clone(),
},
@ -129,14 +126,34 @@ impl Component for MonitorModel {
MonitorInputMsg::RefreshRender => {
widgets.renderer.queue_render();
}
MonitorInputMsg::SetRenderRange(lon_start, lon_end, lat_start, lat_end) => {}
MonitorInputMsg::RefreshTiles => {}
MonitorInputMsg::SetRenderRange(lon_start, lon_end, lat_start, lat_end) => {
let current_rate = widgets.renderer.scale_rate();
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() {
WidgetType::Cairo => {}
WidgetType::Cairo => {
let frame = WidgetFrame::new();
frame.set_widget(widget);
self.widgets.push(frame);
}
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::None => {}
_ => {}
@ -150,15 +167,24 @@ impl Component for MonitorModel {
root: Self::Root,
sender: ComponentSender<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 {
padding: [0.0, 0.0, 0.0, 0.0],
};
let new_sender = sender.clone();
let mainload = MainLoadAttach::new(None);
let mut model = MonitorModel {
mainload,
render_range: (4.0, 53.3, 73.3, 135.0),
new_layer: 0,
widgets: vec![],

Some files were not shown because too many files have changed in this diff Show More