use std::fmt; use crate::node::CompileError; #[derive(Copy, Clone, Default, Debug, PartialEq, serde::Serialize, serde::Deserialize)] pub enum FloatPrecision { #[default] Single, Double, } #[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] pub enum ScalarType { Float(FloatPrecision), Int, UInt, Bool, } impl ScalarType { const SCALARS: [Self; 5] = [ Self::Float(FloatPrecision::Single), Self::Float(FloatPrecision::Double), Self::Int, Self::UInt, Self::Bool, ]; pub fn default_value(&self) -> &'static str { match self { Self::Float(_) => "0.0", Self::Int | Self::UInt => "0", Self::Bool => "false", } } pub fn pick(ui: &mut egui::Ui) -> Option { for value in Self::SCALARS { if ui.button(format!("{}", value)).clicked() { return Some(value); } } None } } impl Default for ScalarType { fn default() -> Self { Self::Float(Default::default()) } } impl fmt::Display for ScalarType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Float(FloatPrecision::Single) => write!(f, "float"), Self::Float(FloatPrecision::Double) => write!(f, "double"), Self::Int => write!(f, "int"), Self::UInt => write!(f, "uint"), Self::Bool => write!(f, "bool"), } } } #[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] #[repr(u8)] pub enum Dimension { D2 = 2, D3 = 3, D4 = 4, } impl fmt::Display for Dimension { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", *self as u8) } } impl Dimension { pub const fn from_const(v: usize) -> Dimension { match v { 2 => Self::D2, 3 => Self::D3, 4 => Self::D4, _ => panic!("invalid dimension value"), } } } impl TryFrom for Dimension { type Error = (); fn try_from(value: usize) -> Result { match value { 2 => Ok(Self::D2), 3 => Ok(Self::D3), 4 => Ok(Self::D4), _ => Err(()), } } } /// a GLSL type #[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] pub enum Type { Scalar(ScalarType), Vector(ScalarType, Dimension), Matrix(FloatPrecision, Dimension, Dimension), } impl From for Type { fn from(val: ScalarType) -> Self { Self::Scalar(val) } } impl Type { pub fn scalar(&self) -> ScalarType { match self { Type::Scalar(s) => *s, Type::Vector(s, _) => *s, Type::Matrix(p, _, _) => ScalarType::Float(*p), } } pub fn num_components(&self) -> usize { match self { Type::Scalar(_) => 1, Type::Vector(_, d) => *d as usize, Type::Matrix(_, r, c) => (*r as usize) * (*c as usize), } } pub fn default_value(&self) -> String { match self { Type::Scalar(_) => self.scalar().default_value().to_string(), _ => format!("{self}({})", self.scalar().default_value()), } } pub fn upcast_gentype(self, other: Self) -> Result { if self == other { // same scalar or complex type Ok(self) } else if Self::Scalar(self.scalar()) == other { // complex and scalar type Ok(self) } else if Self::Scalar(other.scalar()) == self { // complex and scalar type Ok(other) } else { Err(CompileError::InvalidArguments) } } pub fn downcast_gentype(self, other: ScalarType) -> Result { if self.scalar() == other { Ok(other) } else { Err(CompileError::InvalidArguments) } } pub fn pick(ui: &mut egui::Ui) -> Option { let mut result: Option = ScalarType::pick(ui).map(Self::Scalar); ui.separator(); for dim in [Dimension::D2, Dimension::D3, Dimension::D4] { result = result.or(ui .menu_button(format!("Vector {dim}"), |ui| { ScalarType::pick(ui).map(|s| Self::Vector(s, dim)) }) .inner .flatten()); } result } } impl fmt::Display for Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Type::Scalar(s) => write!( f, "{}", match s { ScalarType::Float(FloatPrecision::Single) => "float", ScalarType::Float(FloatPrecision::Double) => "double", ScalarType::Int => "int", ScalarType::UInt => "uint", ScalarType::Bool => "bool", } ), Type::Vector(s, d) => write!( f, "{}vec{d}", match s { ScalarType::Float(FloatPrecision::Single) => "", ScalarType::Float(FloatPrecision::Double) => "d", ScalarType::Int => "i", ScalarType::UInt => "u", ScalarType::Bool => "b", } ), Type::Matrix(p, r, c) => { let pr = match p { FloatPrecision::Single => "", FloatPrecision::Double => "d", }; if r == c { write!(f, "{pr}mat{r}") } else { write!(f, "{pr}mat{r}{c}") } } } } } impl Default for Type { fn default() -> Self { Self::Scalar(Default::default()) } } /// a single concrete type signature for a function #[derive(Clone, PartialEq, Debug)] pub struct TypeSignature { pub inputs: Box<[Type]>, pub outputs: Box<[Type]>, } impl TypeSignature { pub fn new(inputs: [Type; NI], outputs: [Type; NO]) -> Self { Self { inputs: Box::new(inputs), outputs: Box::new(outputs), } } } impl TypeSignature { pub fn matches_inputs(&self, connected: &[Option]) -> bool { let have_inputs = connected .iter() .enumerate() .filter(|(_, t)| t.is_some()) .map(|(i, _)| i + 1) .max() .unwrap_or(0); if have_inputs > self.inputs.len() { false } else { connected .iter() .zip(self.inputs.iter()) .all(|(connected_input, expected)| { if let Some(input) = connected_input { *input == *expected } else { true } }) } } }