use std::sync::Arc;
use serde::{Deserialize, Serialize};
use crate::render_traits::{
ColorMode, DrawToRasterCpu, DrawToRasterGpu, DrawToSvg, MarginParams, PickableLayer, PreparedAndDraw, PreparedLayer, UnitsMode, ViewParams
};
use crate::render_types::{CpuContext, CpuRenderPass, PrepareResult};
use crate::render_types::GpuContext;
use crate::two::svg::SvgContext;
use crate::wgpu;
use crate::layers::composite_layer::{base_draw_composite_layer, base_draw_composite_layer_svg};
use crate::cache::use_memo_vec_f32;
use crate::compute::reduce::{reduce_extent, reduce_histogram_with_known_extent};
use super::bar_plot_layer::{BarOrientation, BarPlotLayer, BarPlotLayerParams};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct HistogramLayerParams {
pub layer_id: String,
pub bounds: Option<MarginParams>,
pub orientation: BarOrientation,
pub data: Arc<Vec<f32>>,
pub num_bins: u32,
pub data_min: Option<f32>,
pub data_max: Option<f32>,
pub fill_color: Option<(u8, u8, u8)>,
}
pub struct HistogramLayer {
view_params: ViewParams,
layer_params: HistogramLayerParams,
sub_layer_instances: Vec<Box<dyn PreparedAndDraw>>,
}
impl HistogramLayer {
pub fn new(view_params: ViewParams, layer_params: HistogramLayerParams) -> Self {
Self {
view_params,
layer_params,
sub_layer_instances: Vec::new(),
}
}
fn bin_labels(data_min: f32, data_max: f32, num_bins: u32) -> Vec<String> {
let step = (data_max - data_min) / num_bins as f32;
(0..num_bins)
.map(|i| {
let lo = data_min + step * i as f32;
let hi = lo + step;
format!("{lo:.2}\u{2013}{hi:.2}")
})
.collect()
}
}
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
impl PreparedLayer for HistogramLayer {
async fn prepare(&mut self, gpu_context: Option<&GpuContext<'_>>) -> PrepareResult {
let p = &self.layer_params;
let num_bins = p.num_bins;
let (data_min, data_max) = match (p.data_min, p.data_max) {
(Some(lo), Some(hi)) => (lo, hi),
_ => {
let extent_data = Arc::clone(&p.data);
let extent_deps = vec![
"extent".to_string(),
p.layer_id.clone(),
];
let extent = use_memo_vec_f32(async || {
let (lo, hi) = reduce_extent(gpu_context, extent_data).await;
Ok::<Vec<f32>, std::convert::Infallible>(vec![lo, hi])
}, &extent_deps, self.view_params.cache_enabled)
.await
.unwrap();
(extent[0], extent[1])
}
};
let data = Arc::clone(&p.data);
let cache_deps = vec![
"histogram".to_string(),
p.layer_id.clone(),
num_bins.to_string(),
data_min.to_string(),
data_max.to_string(),
];
let quantity = use_memo_vec_f32(async || {
let bin_counts = reduce_histogram_with_known_extent(
gpu_context,
data,
num_bins,
data_min,
data_max,
)
.await;
let quantity: Vec<f32> = bin_counts.iter().map(|&c| c as f32).collect();
Ok::<Vec<f32>, std::convert::Infallible>(quantity)
}, &cache_deps, self.view_params.cache_enabled)
.await
.unwrap();
let labels = Self::bin_labels(data_min, data_max, num_bins);
let bar_layer = BarPlotLayer::new(
self.view_params.clone(),
BarPlotLayerParams {
layer_id: self.layer_params.layer_id.clone(),
bounds: self.layer_params.bounds.clone(),
data_unit_mode_for_identifier_dim: UnitsMode::Pixels,
data_unit_mode_for_quantity_dim: UnitsMode::Data,
orientation: self.layer_params.orientation.clone(),
identifier: Arc::new(labels),
quantity,
fill_color_mode: ColorMode::Static,
fill_color: match self.layer_params.fill_color {
Some(color) => Some(color),
None => Some((76, 120, 168)),
}
},
);
self.sub_layer_instances = vec![Box::new(bar_layer)];
for sub_layer in self.sub_layer_instances.iter_mut() {
sub_layer.prepare(gpu_context).await;
}
PrepareResult { bailed_early: false }
}
}
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
impl DrawToRasterGpu for HistogramLayer {
async fn draw(&self, gpu_context: &GpuContext<'_>, pass: &mut wgpu::RenderPass) {
base_draw_composite_layer(&self.sub_layer_instances, gpu_context, pass).await;
}
}
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
impl DrawToRasterCpu for HistogramLayer {
async fn draw(&self, _cpu_context: &CpuContext<'_>, _pass: &mut CpuRenderPass) {}
}
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
impl DrawToSvg for HistogramLayer {
async fn draw(&self, ctx: &mut SvgContext) {
base_draw_composite_layer_svg(&self.sub_layer_instances, ctx).await
}
}
inventory::submit! {
crate::registry::LayerRegistration {
layer_type_name: "HistogramLayer",
create_layer: |value, view_params| {
let params: HistogramLayerParams = serde_json::from_value(value).unwrap();
Box::new(HistogramLayer::new(view_params.clone(), params))
},
}
}
impl PickableLayer for HistogramLayer {}