use std::sync::Arc;
use schemars::JsonSchema;
use serde::{Deserialize, Deserializer};
use super::form::{Presets, preset_or, presets};
use super::mark::Markers;
use crate::gpu::shaders as wgsl;
use crate::{Color, Expr, Image};
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema)]
#[serde(remote = "Self", rename_all = "camelCase", deny_unknown_fields)]
#[schemars(transform = presets::<Marker>)]
pub enum Marker {
Shape(Shape),
Block {
#[serde(default = "corner")]
corner: f32,
},
Gauge {
fill: Expr,
#[serde(default)]
shape: Shape,
},
Pie {
slices: Vec<Expr>,
colors: Vec<Color>,
#[serde(default)]
hole: f32,
},
Icon {
images: Vec<Image>,
index: Expr,
},
Wgsl {
source: Arc<str>,
#[serde(default)]
inputs: Vec<Expr>,
},
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub enum Shape {
#[default]
Ellipse,
Rectangle,
Rounded,
Diamond,
}
impl Presets for Marker {
const NAMES: &'static [&'static str] = &["ellipse", "rectangle", "rounded", "diamond", "block"];
fn preset(name: &str) -> Option<Marker> {
Some(match name {
"ellipse" => Marker::Shape(Shape::Ellipse),
"rectangle" => Marker::Shape(Shape::Rectangle),
"rounded" => Marker::Shape(Shape::Rounded),
"diamond" => Marker::Shape(Shape::Diamond),
"block" => Marker::block(),
_ => return None,
})
}
fn options<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Marker, D::Error> {
Marker::deserialize(deserializer)
}
}
impl<'de> Deserialize<'de> for Marker {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Marker, D::Error> {
preset_or(deserializer)
}
}
fn corner() -> f32 {
4.0
}
impl Default for Marker {
fn default() -> Marker {
Marker::Shape(Shape::Ellipse)
}
}
impl From<Shape> for Marker {
fn from(shape: Shape) -> Marker {
Marker::Shape(shape)
}
}
impl Marker {
pub fn block() -> Marker {
Marker::Block { corner: corner() }
}
pub(crate) fn inputs(&self) -> Vec<&Expr> {
match self {
Marker::Shape(_) | Marker::Block { .. } => Vec::new(),
Marker::Gauge { fill, .. } => vec![fill],
Marker::Pie { slices, .. } => slices.iter().collect(),
Marker::Icon { index, .. } => vec![index],
Marker::Wgsl { inputs, .. } => inputs.iter().collect(),
}
}
pub(crate) fn markers(&self, input: u32) -> Markers {
let inputs = self.inputs().len() as u32;
let base = Markers { inputs, input, ..Markers::default() };
match self {
Marker::Shape(shape) => Markers { marker: wgsl::MARKER_SHAPE, shape: shape.code(), ..base },
Marker::Block { corner } => {
Markers { marker: wgsl::MARKER_BLOCK, shape: wgsl::SHAPE_ROUNDED, corner: *corner, ..base }
}
Marker::Gauge { shape, .. } => Markers { marker: wgsl::MARKER_GAUGE, shape: shape.code(), ..base },
Marker::Pie { colors, hole, .. } => Markers {
marker: wgsl::MARKER_PIE,
hole: *hole,
palette: colors.iter().map(|c| c.packed()).collect(),
..base
},
Marker::Icon { images, .. } => Markers { marker: wgsl::MARKER_ICON, icons: images.clone(), ..base },
Marker::Wgsl { source, .. } => {
Markers { marker: wgsl::MARKER_CUSTOM, custom: Some(source.clone()), ..base }
}
}
}
}
impl Shape {
fn code(self) -> u32 {
match self {
Shape::Ellipse => wgsl::SHAPE_ELLIPSE,
Shape::Rectangle => wgsl::SHAPE_RECTANGLE,
Shape::Rounded => wgsl::SHAPE_ROUNDED,
Shape::Diamond => wgsl::SHAPE_DIAMOND,
}
}
}