use std::sync::Arc;
use serde::{Deserialize, Serialize};
use pluot_core::{maybe_timeout, FutureExt, Duration, log};
use pluot_core::wgpu;
use pluot_core::cache::use_memo_vec_f32;
use pluot_core::zarr::is_timed_out_zarrs_error;
use zarrs::storage::AsyncReadableStorageTraits;
use pluot_core::two::svg::SvgContext;
use pluot_core::render_traits::{ColorMode, DrawToRasterCpu, DrawToRasterGpu, DrawToSvg, MarginParams, PickableLayer, PreparedAndDraw, PreparedLayer, UnitsMode, ViewParams, resolve_store_name};
use pluot_core::render_types::{CpuContext, CpuRenderPass, PrepareResult};
use pluot_core::render_types::GpuContext;
use pluot_core::composite_layer::{base_draw_composite_layer, base_draw_composite_layer_svg};
use pluot_core::compute::reduce::{reduce_extent, reduce_histogram_with_known_extent};
use pluot_core::composite_layers::bar_plot_layer::{BarOrientation, BarPlotLayer, BarPlotLayerParams};
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(default)]
pub struct ZarrHistogramLayerParams {
pub layer_id: String,
pub bounds: Option<MarginParams>,
pub orientation: BarOrientation,
pub store_name: Option<String>,
pub data_key: String,
pub num_bins: u32,
pub cache_data: bool,
pub fill_color: Option<(u8, u8, u8)>,
}
impl Default for ZarrHistogramLayerParams {
fn default() -> Self {
Self {
layer_id: "".to_string(),
bounds: None,
orientation: BarOrientation::Vertical,
store_name: None,
data_key: "".to_string(),
num_bins: 50,
cache_data: true,
fill_color: None,
}
}
}
pub struct ZarrHistogramLayer {
view_params: ViewParams,
layer_params: ZarrHistogramLayerParams,
store: Arc<dyn AsyncReadableStorageTraits>,
store_name: String,
sub_layer_instances: Vec<Box<dyn PreparedAndDraw>>,
}
impl ZarrHistogramLayer {
pub fn new(view_params: ViewParams, layer_params: ZarrHistogramLayerParams) -> Self {
let store_name = resolve_store_name(&layer_params.store_name, &view_params);
let store = view_params.get_store(&store_name);
Self {
view_params,
layer_params,
store,
store_name,
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 ZarrHistogramLayer {
async fn prepare(&mut self, gpu_context: Option<&GpuContext<'_>>) -> PrepareResult {
let store = self.store.clone();
let num_bins = self.layer_params.num_bins;
let hist_future_deps = vec![
"histogram_result".to_string(),
self.store_name.clone(),
self.layer_params.layer_id.clone(),
num_bins.to_string(),
];
let quant_future_deps = vec!["histogram_input_arr".to_string(), self.store_name.clone(), self.layer_params.layer_id.clone(), self.layer_params.data_key.clone()];
let extent_future_deps = vec!["histogram_input_extent".to_string(), self.store_name.clone(), self.layer_params.layer_id.clone(), self.layer_params.data_key.clone()];
let hist_future = use_memo_vec_f32(async || {
let quant_arr = use_memo_vec_f32(async || {
let array_path = &self.layer_params.data_key;
let array = zarrs::array::Array::async_open(store.clone(), array_path).await.unwrap();
let subset = array.subset_all();
let arr_raw = array.async_retrieve_array_subset::<Vec<f64>>(&subset).await?;
let arr_inner: Vec<f32> = arr_raw.iter().map(|&x| x as f32).collect();
Ok::<Vec<f32>, zarrs::array::ArrayError>(arr_inner)
}, &quant_future_deps, self.view_params.cache_enabled && self.layer_params.cache_data)
.await?;
let quant_arr_for_extent = quant_arr.clone();
let extent = use_memo_vec_f32(async || {
let (lo, hi) = reduce_extent(gpu_context, quant_arr_for_extent, &[], &[]).await.background;
Ok::<Vec<f32>, std::convert::Infallible>(vec![lo, hi])
}, &extent_future_deps, self.view_params.cache_enabled)
.await
.expect("Extent computation failed in ZarrHistogramLayer.prepare");
let bin_counts = reduce_histogram_with_known_extent(
gpu_context,
quant_arr,
num_bins,
extent[0],
extent[1],
&[],
&[],
).await.background;
let mut result = vec![extent[0], extent[1]];
result.extend(bin_counts.iter().map(|&c| c as f32));
Ok(result)
}, &hist_future_deps, self.view_params.cache_enabled);
let future_result = maybe_timeout!(hist_future, self.view_params.timeout).await;
let hist_data = match future_result {
Ok(Ok(hist_result)) => hist_result,
Ok(Err(e)) => {
if is_timed_out_zarrs_error(&e) {
return PrepareResult { bailed_early: true };
} else {
panic!("Zarrs error during ZarrHistogramLayer prepare: {:?}", e);
}
}
Err(e) => {
log(&format!("Other error during ZarrHistogramLayer prepare: {:?}", e));
return PrepareResult { bailed_early: true };
}
};
let data_min = hist_data[0];
let data_max = hist_data[1];
let hist_arr: Arc<Vec<f32>> = Arc::new(hist_data[2..].to_vec());
let labels = Self::bin_labels(data_min, data_max, num_bins);
let bar_layer = BarPlotLayer::new(
self.view_params.clone(),
BarPlotLayerParams {
layer_id: format!("{}_bar_plot_sublayer", self.layer_params.layer_id),
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: hist_arr,
fill_color: Some(ColorMode::UniformRgb(
self.layer_params.fill_color.unwrap_or((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 ZarrHistogramLayer {
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 ZarrHistogramLayer {
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 ZarrHistogramLayer {
async fn draw(&self, ctx: &mut SvgContext) {
base_draw_composite_layer_svg(&self.sub_layer_instances, ctx).await
}
}
impl PickableLayer for ZarrHistogramLayer {}