From b943a39052e39f1cf21b5184e347fc38b06eca86 Mon Sep 17 00:00:00 2001 From: seancarroll Date: Mon, 28 Dec 2020 23:04:24 -0600 Subject: bit of cleanup. adding colorlist fns --- src/lib.rs | 200 +++++++++++++++++++++++-------------------------------------- 1 file changed, 76 insertions(+), 124 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index bbcef36..56bb96c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,19 +11,16 @@ use std::io::prelude::*; static INDENT: &str = " "; // TODO: support adding edge based on index of nodes? -// TODO: support fluent graph builder methods // TODO: handle render options // TODO: explicit attribute methods with type safety and enforce constraints -// i'm thinking we have NodeTraits/GraphTraints/EdgeTraits (what about none? is that a graph trait?) -// which will have default methods that use an associated type field called "state" or "attribtues" etc +// i'm thinking we have NodeTraits/GraphTraits/EdgeTraits (what about none? is that a graph trait?) +// which will have default methods that use an associated type field called "state" or "attributes" etc // TODO: implement Clone for Graph -// TODO: remove duplicate fns // TODO: Node port and compass -// TODO: > / > // TODO: see if we can get any insights from Haskell implementation // https://hackage.haskell.org/package/graphviz-2999.20.1.0/docs/Data-GraphViz-Attributes-Complete.html#t:Point // - I like this: A summary of known current constraints/limitations/differences: -// Has GlobalAttributes which hs GraphAttrs, NodeAttrs, EdgeAttrs + /// Most of this comes from core rust. Where to provide attribution? /// The text for a graphviz label on a node or edge. @@ -230,68 +227,6 @@ impl<'a> Dot<'a> { write!(w, "{}", edge_attributes.to_dot_string())?; } - // TODO: clean this up - for (key, value) in self.graph.attributes { - write!(w, "{}", INDENT)?; - match key { - AttributeType::Edge => { - write!(w, "edge")?; - if !value.is_empty() { - write!(w, " [")?; - - let mut iter = value.iter(); - let first = iter.next().unwrap(); - write!(w, "{}={}", first.0, first.1.to_dot_string())?; - for (key, value) in iter { - write!(w, ", ")?; - write!(w, "{}={}", key, value.to_dot_string())?; - } - write!(w, "]")?; - } - writeln!(w, ";")?; - }, - AttributeType::Graph => { - write!(w, "graph")?; - if !value.is_empty() { - write!(w, " [")?; - - let mut iter = value.iter(); - let first = iter.next().unwrap(); - write!(w, "{}={}", first.0, first.1.to_dot_string())?; - for (key, value) in iter { - write!(w, ", ")?; - write!(w, "{}={}", key, value.to_dot_string())?; - } - write!(w, "]")?; - } - writeln!(w, ";")?; - }, - AttributeType::Node => { - write!(w, "node")?; - if !value.is_empty() { - write!(w, " [")?; - - let mut iter = value.iter(); - let first = iter.next().unwrap(); - write!(w, "{}={}", first.0, first.1.to_dot_string())?; - for (key, value) in iter { - write!(w, ", ")?; - write!(w, "{}={}", key, value.to_dot_string())?; - } - write!(w, "]")?; - } - writeln!(w, ";")?; - }, - AttributeType::None => { - if !value.is_empty() { - for (key, value) in value.iter() { - writeln!(w, "{}={};", key, value.to_dot_string())?; - } - } - } - } - } - for n in self.graph.nodes { // TODO: handle render options // Are render options something we need? @@ -357,18 +292,15 @@ pub struct Graph<'a> { pub strict: bool, - // Comment added to the first line of the source. + /// Comment added to the first line of the source. pub comment: Option, pub graph_attributes: Option>, pub node_attributes: Option>, - // pub edge_attributes: IndexMap>, pub edge_attributes: Option>, - pub attributes: IndexMap>>, - pub nodes: Vec>, pub edges: Vec>, @@ -383,9 +315,7 @@ impl<'a> Graph<'a> { comment: Option, graph_attributes: Option>, node_attributes: Option>, - // edge_attributes: IndexMap>, edge_attributes: Option>, - attributes: IndexMap>>, nodes: Vec>, edges: Vec>, ) -> Self { @@ -397,7 +327,6 @@ impl<'a> Graph<'a> { graph_attributes, node_attributes, edge_attributes, - attributes, nodes, edges, } @@ -428,17 +357,12 @@ pub struct GraphBuilder<'a> { strict: bool, - // graph_attributes: IndexMap>, graph_attributes: Option>, - // node_attributes: IndexMap>, node_attributes: Option>, - // edge_attributes: IndexMap>, edge_attributes: Option>, - attributes: IndexMap>>, - nodes: Vec>, edges: Vec>, @@ -456,8 +380,6 @@ impl<'a> GraphBuilder<'a> { graph_attributes: None, node_attributes: None, edge_attributes: None, - // edge_attributes: IndexMap::new(), - attributes: IndexMap::new(), nodes: Vec::new(), edges: Vec::new(), comment: None, @@ -471,17 +393,13 @@ impl<'a> GraphBuilder<'a> { strict: false, graph_attributes: None, node_attributes: None, - // edge_attributes: IndexMap::new(), edge_attributes: None, - attributes: IndexMap::new(), nodes: Vec::new(), edges: Vec::new(), comment: None, } } - // TODO: do we want a map for attribute list ie attributes not mapped to (graph/node/edge) - pub fn add_graph_attributes(&mut self, graph_attributes: GraphAttributeStatement<'a>) -> &mut Self { self.graph_attributes = Some(graph_attributes); self @@ -492,38 +410,60 @@ impl<'a> GraphBuilder<'a> { self } - // pub fn add_edge_attributes(&mut self, edge_attributes: IndexMap>) -> &mut Self { - // self.edge_attributes = edge_attributes; - // self - // } pub fn add_edge_attributes(&mut self, edge_attributes: EdgeAttributeStatement<'a>) -> &mut Self { self.edge_attributes = Some(edge_attributes); self } // TODO: update to insert into appropriate statement or remove? + // pub fn add_attribute( + // &mut self, + // attribute_type: AttributeType, + // key: String, value: AttributeText<'a> + // ) -> &mut Self { + // self.get_attributes(attribute_type).insert(key, value); + // self + // } + // + // pub fn add_attributes( + // &mut self, + // attribute_type: AttributeType, + // attributes: HashMap> + // ) -> &mut Self { + // self.get_attributes(attribute_type).extend(attributes); + // self + // } + pub fn add_attribute( - &mut self, - attribute_type: AttributeType, - key: String, value: AttributeText<'a> + &mut self, + attribute_type: AttributeType, + key: String, + value: AttributeText<'a> ) -> &mut Self { - self.get_attributes(attribute_type).insert(key, value); - self - } + match attribute_type { - pub fn add_attributes( - &mut self, - attribute_type: AttributeType, - attributes: HashMap> - ) -> &mut Self { - self.get_attributes(attribute_type).extend(attributes); + AttributeType::Graph | AttributeType::None => { + if self.graph_attributes.is_none() { + self.graph_attributes = Some(GraphAttributeStatement::new()); + } + self.graph_attributes.as_mut().unwrap().add_attribute(key, value); + }, + AttributeType::Edge => { + if self.edge_attributes.is_none() { + self.edge_attributes = Some(EdgeAttributeStatement::new()); + } + self.edge_attributes.as_mut().unwrap().add_attribute(key, value); + }, + AttributeType::Node => { + if self.node_attributes.is_none() { + self.node_attributes = Some(NodeAttributeStatement::new()); + } + self.node_attributes.as_mut().unwrap().add_attribute(key, value); + }, + } self } - fn get_attributes(&mut self, attribute_type: AttributeType) -> &mut IndexMap> { - self.attributes.entry(attribute_type).or_insert(IndexMap::new()) - } - pub fn add_node(&mut self, node: Node<'a>) -> &mut Self { self.nodes.push(node); self @@ -547,9 +487,7 @@ impl<'a> GraphBuilder<'a> { comment: self.comment.clone(), // TODO: is clone the only option here? graph_attributes: self.graph_attributes.clone(), node_attributes: self.node_attributes.clone(), - // edge_attributes: self.edge_attributes.clone(), edge_attributes: self.edge_attributes.clone(), - attributes: self.attributes.clone(), // TODO: is clone the only option here? nodes: self.nodes.clone(), // TODO: is clone the only option here? edges: self.edges.clone(), // TODO: is clone the only option here? } @@ -563,14 +501,20 @@ trait GraphAttributes<'a> { self.add_attribute("_background", AttributeText::attr(background)) } - // TODO: color list - /// A colon-separated list of weighted color values: WC(:WC)* where each WC has the form C(;F)? - /// with C a color value and the optional F a floating-point number, 0 ≤ F ≤ 1. - /// The sum of the floating-point numbers in a colorList must sum to at most 1. + /// The color used as the background for entire canvas. fn background_color(&mut self, background_color: Color) -> &mut Self { self.add_attribute("bgcolor", AttributeText::quotted(background_color.to_dot_string())) } + // TODO: constrain + /// The color used as the background for entire canvas with a gradient fill. + /// A colon-separated list of weighted color values: WC(:WC)* where each WC has the form C(;F)? + /// with C a color value and the optional F a floating-point number, 0 ≤ F ≤ 1. + /// The sum of the floating-point numbers in a colorList must sum to at most 1. + fn background_colorlist(&mut self, background_colors: ColorList) -> &mut Self { + self.add_attribute("bgcolor", AttributeText::quotted(background_colors.to_dot_string())) + } + /// Type: rect which is "%f,%f,%f,%f" /// The rectangle llx,lly,urx,ury gives the coordinates, in points, of the lower-left corner (llx,lly) /// and the upper-right corner (urx,ury). @@ -636,14 +580,19 @@ trait GraphAttributes<'a> { self.add_attribute("dpi", AttributeText::quotted(dpi.to_string())) } - // color list /// Color used to fill the background of a node or cluster assuming style=filled, or a filled arrowhead. fn fill_color(&mut self, fill_color: Color) -> &mut Self { Attributes::fill_color(self.get_attributes_mut(), fill_color); self } - // color list + /// Color used to fill the background, with a graident, of a node or cluster assuming + /// style=filled, or a filled arrowhead. + fn fill_colorlist(&mut self, fill_colors: ColorList) -> &mut Self { + Attributes::fill_colorlist(self.get_attributes_mut(), fill_colors); + self + } + /// Color used for text. fn font_color(&mut self, font_color: Color) -> &mut Self { Attributes::font_color(self.get_attributes_mut(), font_color); @@ -1566,7 +1515,7 @@ impl<'a> EdgeAttributes<'a> for EdgeBuilder<'a> { &mut self.attributes } - // /// Add multiple attribures to the edge. + // /// Add multiple attributes to the edge. // fn add_attributes(&'a mut self, attributes: HashMap>) -> &mut Self { // self.attributes.extend(attributes); // self @@ -2728,6 +2677,7 @@ pub enum Color<'a> { blue: u8, alpha: u8, }, + // TODO: constrain? // Hue-Saturation-Value (HSV) 0.0 <= H,S,V <= 1.0 HSV { hue: f32, @@ -2761,7 +2711,8 @@ impl<'a> Color<'a> { pub struct WeightedColor<'a> { color: Color<'a>, - // Must be in range 0 <= W <= 1. + // TODO: constrain + /// Must be in range 0 <= W <= 1. weight: Option } @@ -2777,13 +2728,12 @@ impl<'a> WeightedColor<'a> { } -// type ColorList<'a> = Vec>; pub struct ColorList<'a> { colors: Vec>, } impl<'a> ColorList<'a> { - // A colon-separated list of weighted color values: WC(:WC)* where each WC has the form C(;F)? - // Ex: fillcolor=yellow;0.3:blue + /// A colon-separated list of weighted color values: WC(:WC)* where each WC has the form C(;F)? + /// Ex: fillcolor=yellow;0.3:blue pub fn to_dot_string(&self) -> String { let mut dot_string = String::new(); let mut iter = self.colors.iter(); @@ -2858,6 +2808,10 @@ impl Attributes { Self::add_attribute(attributes, "fillcolor", AttributeText::quotted(fill_color.to_dot_string())) } + fn fill_colorlist(attributes: &mut IndexMap, fill_colors: ColorList) { + Self::add_attribute(attributes, "fillcolor", AttributeText::quotted(fill_colors.to_dot_string())) + } + fn font_color(attributes: &mut IndexMap, font_color: Color) { Self::add_attribute(attributes, "fontcolor", AttributeText::quotted(font_color.to_dot_string())) } @@ -2950,8 +2904,8 @@ impl Attributes { fn add_attribute<'a, S: Into>( attributes: &mut IndexMap>, key: S, - value: AttributeText<'a> - ) { + value: AttributeText<'a>) + { attributes.insert(key.into(), value); } } @@ -3160,7 +3114,6 @@ fn graph_attributes() { .add_graph_attributes(graph_attributes) .add_node_attributes(node_attributes) .add_edge_attributes(edge_attributes) - .add_attribute(AttributeType::None, "ranksep".to_string(), AttributeText::attr("0.5")) .build(); let r = test_input(g); @@ -3171,8 +3124,7 @@ fn graph_attributes() { graph [rankdir=LR]; node [style=filled]; edge [minlen=1]; - ranksep=0.5; } "# ); -} \ No newline at end of file +} -- cgit v1.2.3