-
Udo Eisenbarth authoredUdo Eisenbarth authored
source.rs 4.77 KiB
#![warn(missing_docs)]
use std::collections::HashMap;
use std::fmt::Debug;
use crate::{
dottable::Dottable,
error::{OpmResult, OpossumError},
lightdata::LightData,
optic_ports::OpticPorts,
optical::{LightResult, Optical},
properties::{Properties, Property, Proptype},
};
/// This node represents a source of light.
///
/// Hence it has only one output port (out1) and no input ports. Source nodes usually are the first nodes of an [`OpticScenery`](crate::OpticScenery).
///
/// ## Optical Ports
/// - Inputs
/// - none
/// - Outputs
/// - `out1`
pub struct Source {
props: Properties,
}
fn create_default_props() -> Properties {
let mut props = Properties::default();
props.set("name", "source".into());
props.set(
"light data",
Property {
prop: Proptype::LightData(None),
},
);
props
}
impl Default for Source {
fn default() -> Self {
Self {
props: create_default_props(),
}
}
}
impl Source {
/// Creates a new [`Source`].
///
/// The light to be emitted from this source is defined in a [`LightData`] structure.
///
/// ## Example
///
/// ```rust
/// use opossum::{
/// lightdata::{DataEnergy, LightData},
/// nodes::Source,
/// spectrum::create_he_ne_spectrum};
///
/// let source=Source::new("My Source", LightData::Energy(DataEnergy {spectrum: create_he_ne_spectrum(1.0)}));
/// ```
pub fn new(name: &str, light: LightData) -> Self {
let mut props = create_default_props();
props.set("name", name.into());
props.set(
"light data",
Property {
prop: Proptype::LightData(Some(light.clone())),
},
);
Source { props }
}
/// Sets the light data of this [`Source`]. The [`LightData`] provided here represents the input data of an `OpticScenery`.
pub fn set_light_data(&mut self, light_data: LightData) {
self.props.set(
"light data",
Property {
prop: Proptype::LightData(Some(light_data.clone())),
},
);
}
}
impl Debug for Source {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let light_prop = self.props.get("light data").unwrap();
let data = if let Proptype::LightData(data) = &light_prop.prop {
data
} else {
&None
};
match data {
Some(data) => write!(f, "{}", data),
None => write!(f, "no data"),
}
}
}
impl Optical for Source {
fn node_type(&self) -> &str {
"light source"
}
fn name(&self) -> &str {
if let Proptype::String(name) = &self.props.get("name").unwrap().prop {
name
} else {
"light source"
}
}
fn ports(&self) -> OpticPorts {
let mut ports = OpticPorts::new();
ports.add_output("out1").unwrap();
ports
}
fn analyze(
&mut self,
_incoming_edges: LightResult,
_analyzer_type: &crate::analyzer::AnalyzerType,
) -> OpmResult<LightResult> {
let light_prop = self.props.get("light data").unwrap();
let data = if let Proptype::LightData(data) = &light_prop.prop {
data
} else {
&None
};
if data.is_some() {
Ok(HashMap::from([("out1".into(), data.to_owned())]))
} else {
Err(OpossumError::Analysis("no input data available".into()))
}
}
fn properties(&self) -> &Properties {
&self.props
}
fn set_property(&mut self, name: &str, prop: Property) -> OpmResult<()> {
if self.props.set(name, prop).is_none() {
Err(OpossumError::Other("property not defined".into()))
} else {
Ok(())
}
}
}
impl Dottable for Source {
fn node_color(&self) -> &str {
"slateblue"
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn default() {
let node = Source::default();
assert_eq!(node.name(), "source");
assert_eq!(node.node_type(), "light source");
assert_eq!(node.is_detector(), false);
assert_eq!(node.inverted(), false);
assert_eq!(node.node_color(), "slateblue");
assert!(node.as_group().is_err());
}
#[test]
fn new() {
let source = Source::new("test", LightData::Fourier);
assert_eq!(source.name(), "test");
}
#[test]
fn not_invertable() {
let mut node = Source::default();
assert!(node.set_property("inverted", true.into()).is_err());
}
#[test]
fn ports() {
let detector = Source::default();
assert!(detector.ports().inputs().is_empty());
assert_eq!(detector.ports().outputs(), vec!["out1"]);
}
}