Skip to content
Snippets Groups Projects
Commit b1905907 authored by Udo Eisenbarth's avatar Udo Eisenbarth :speech_balloon:
Browse files

Add dot display of NodeGroup

parent ccbad11a
No related branches found
No related tags found
No related merge requests found
use opossum::nodes::NodeDummy;
use opossum::nodes::{NodeDummy, NodeGroup};
use opossum::optic_node::OpticNode;
use opossum::optic_scenery::OpticScenery;
......@@ -35,6 +35,11 @@ fn main() {
scenery.connect_nodes(n3i, n2i).unwrap();
scenery.connect_nodes(n2i, n1i).unwrap();
let mut group=NodeGroup::new();
let g_n1=group.add_node(OpticNode::new("Beamsplitter", Box::new(NodeDummy)));
let g_n2=group.add_node(OpticNode::new("Lens", Box::new(NodeDummy)));
group.connect_nodes(g_n1,g_n2).unwrap();
scenery.add_node(OpticNode::new("CamBox", Box::new(group)));
let path = "graph.dot";
let mut output = File::create(path).unwrap();
write!(output, "{}", scenery.to_dot()).unwrap();
......
use crate::optic_node::{OpticNode, Optical};
use crate::error::OpossumError;
use crate::optic_node::{OpticNode, Optical};
use petgraph::algo::*;
use petgraph::prelude::{DiGraph, EdgeIndex, NodeIndex};
......@@ -26,27 +26,51 @@ impl NodeGroup {
/// Both node indices must exist. Otherwise an [`OpticSceneryError`] is returned. In addition, connections are
/// rejected and an [`OpticSceneryError`] is returned, if the graph would form a cycle (loop in the graph).
pub fn connect_nodes(
&mut self,
src_node: NodeIndex,
target_node: NodeIndex,
) -> Result<EdgeIndex> {
if self.g.node_weight(src_node).is_none() {
return Err(OpossumError);
}
if self.g.node_weight(target_node).is_none() {
return Err(OpossumError);
}
let edge_index = self.g.add_edge(src_node, target_node, ());
if is_cyclic_directed(&self.g) {
self.g.remove_edge(edge_index);
return Err(OpossumError);
}
Ok(edge_index)
}
&mut self,
src_node: NodeIndex,
target_node: NodeIndex,
) -> Result<EdgeIndex> {
if self.g.node_weight(src_node).is_none() {
return Err(OpossumError);
}
if self.g.node_weight(target_node).is_none() {
return Err(OpossumError);
}
let edge_index = self.g.add_edge(src_node, target_node, ());
if is_cyclic_directed(&self.g) {
self.g.remove_edge(edge_index);
return Err(OpossumError);
}
Ok(edge_index)
}
}
impl Optical for NodeGroup {
fn node_type(&self) -> &str {
"group"
}
fn to_dot(&self, node_index: &str, name: &str, inverted: bool) -> String {
let inv_string = if inverted { "(inv)" } else { "" };
let mut dot_string = format!(
" subgraph \"cluster_{}\" {{\n label=\"{}{}\"\n",
node_index, name, inv_string
);
for node_idx in self.g.node_indices() {
let node = self.g.node_weight(node_idx).unwrap();
dot_string += &node.to_dot(&format!(" {}_i{}", node_index, node_idx.index()));
}
for edge in self.g.edge_indices() {
let end_nodes = self.g.edge_endpoints(edge).unwrap();
dot_string.push_str(&format!(
" {}_i{} -> {}_i{}\n",
node_index,
end_nodes.0.index(),
node_index,
end_nodes.1.index()
));
}
dot_string += " }\n";
dot_string
}
}
......@@ -3,15 +3,15 @@ use std::fmt::Debug;
pub struct OpticNode {
name: String,
node: Box<dyn Optical>,
inverted: bool
inverted: bool,
}
impl OpticNode {
/// Creates a new [`OpticNode`]. The concrete type of the component must be given while using the `new` function.
/// The node type ist a struct implementing the [`Optical`] trait. Since the size of the node type is not known at compile time it must be added as `Box<nodetype>`.
///
///
/// # Examples
///
///
/// ```
/// use opossum::optic_node::OpticNode;
/// use opossum::nodes::NodeDummy;
......@@ -19,7 +19,11 @@ impl OpticNode {
/// let node=OpticNode::new("My node", Box::new(NodeDummy));
/// ```
pub fn new(name: &str, node: Box<dyn Optical>) -> Self {
Self { name: name.into(), node: node, inverted: false}
Self {
name: name.into(),
node: node,
inverted: false,
}
}
/// Sets the name of this [`OpticNode`].
pub fn set_name(&mut self, name: String) {
......@@ -29,18 +33,17 @@ impl OpticNode {
pub fn name(&self) -> &str {
self.name.as_ref()
}
/// Returns a string representation of the [`OpticNode`] in `graphviz` format. This function is normally called by the top-level `to_dot`function within
/// Returns a string representation of the [`OpticNode`] in `graphviz` format. This function is normally called by the top-level `to_dot`function within
/// `OpticScenery`.
pub fn to_dot(&self) -> String {
let is_inverted= if self.inverted==true {" (inv)"} else {""};
format!("[label=\"{}{}\"]\n", self.name, is_inverted)
pub fn to_dot(&self, node_index: &str) -> String {
self.node.to_dot(node_index, &self.name, self.inverted)
}
/// Returns the concrete node type as string representation.
pub fn node_type(&self) -> &str {
self.node.node_type()
}
/// Mark the [`OpticNode`] as inverted.
///
///
/// This means that the node is used in "reverse" direction. All output port become input parts and vice versa.
pub fn set_inverted(&mut self, inverted: bool) {
self.inverted = inverted;
......@@ -64,8 +67,9 @@ pub trait Optical {
"undefined"
}
/// Return component type specific code for `graphviz` visualization.
fn to_dot(&self) -> &str {
""
fn to_dot(&self, node_index: &str, name: &str, inverted: bool) -> String {
let inv_string = if inverted { "(inv)" } else { "" };
format!(" {} [label=\"{}{}\"]\n", node_index, name, inv_string)
}
}
......@@ -105,13 +109,13 @@ mod test {
#[test]
fn to_dot() {
let node = OpticNode::new("Test", Box::new(NodeDummy));
assert_eq!(node.to_dot(), "[label=\"Test\"]\n".to_owned())
assert_eq!(node.to_dot("i0"), " i0 [label=\"Test\"]\n".to_owned())
}
#[test]
fn to_dot_inverted() {
let mut node = OpticNode::new("Test", Box::new(NodeDummy));
node.set_inverted(true);
assert_eq!(node.to_dot(), "[label=\"Test (inv)\"]\n".to_owned())
assert_eq!(node.to_dot("i0"), " i0 [label=\"Test(inv)\"]\n".to_owned())
}
#[test]
fn node_type() {
......
......@@ -63,12 +63,11 @@ impl OpticScenery {
dot_string.push_str(&format!(" label=\"{}\"\n", self.description));
for node_idx in self.g.node_indices() {
let node=self.g.node_weight(node_idx).unwrap();
dot_string.push_str(&format!(" idx_{} ", node_idx.index()));
dot_string += &node.to_dot();
dot_string += &node.to_dot(&format!("i{}", node_idx.index()));
}
for edge in self.g.edge_indices() {
let end_nodes = self.g.edge_endpoints(edge).unwrap();
dot_string.push_str(&format!(" idx_{} -> idx_{}\n", end_nodes.0.index(), end_nodes.1.index()));
dot_string.push_str(&format!(" i{} -> i{}\n", end_nodes.0.index(), end_nodes.1.index()));
}
dot_string += "}";
dot_string
......@@ -142,7 +141,7 @@ mod test {
scenery.add_node(OpticNode::new("Test", Box::new(NodeDummy)));
assert_eq!(
scenery.to_dot(),
"digraph {\n label=\"SceneryTest\"\n idx_0 [label=\"Test\"]\n}"
"digraph {\n label=\"SceneryTest\"\n i0 [label=\"Test\"]\n}"
);
}
#[test]
......@@ -154,7 +153,7 @@ mod test {
if let Ok(_)=scenery.connect_nodes(n1,n2) {
assert_eq!(
scenery.to_dot(),
"digraph {\n label=\"SceneryTest\"\n idx_0 [label=\"Test1\"]\n idx_1 [label=\"Test2\"]\n idx_0 -> idx_1\n}"
"digraph {\n label=\"SceneryTest\"\n i0 [label=\"Test1\"]\n i1 [label=\"Test2\"]\n i0 -> i1\n}"
);
} else {
assert!(false);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment