diff options
-rw-r--r-- | src/attributes/mod.rs | 14 | ||||
-rw-r--r-- | src/attributes/viewport.rs | 108 |
2 files changed, 119 insertions, 3 deletions
diff --git a/src/attributes/mod.rs b/src/attributes/mod.rs index 7f4fb0d..8f50c56 100644 --- a/src/attributes/mod.rs +++ b/src/attributes/mod.rs @@ -20,6 +20,7 @@ mod shape; mod spline_type; mod splines; mod style; +mod viewport; pub use crate::attributes::arrow_type::ArrowType; pub use crate::attributes::cluster_mode::ClusterMode; @@ -49,6 +50,7 @@ use indexmap::map::IndexMap; use std::borrow::Cow; use std::collections::HashMap; use Cow::Borrowed; +use crate::attributes::viewport::ViewPort; /// The text for a graphviz label on a node or edge. #[derive(Clone, PartialEq, Eq, Debug)] @@ -310,6 +312,13 @@ impl<'a> From<Styles> for AttributeText<'a> { } } +impl<'a> From<ViewPort> for AttributeText<'a> { + fn from(viewport: ViewPort) -> Self { + AttributeText::quoted(viewport.dot_string()) + } +} + + impl<'a> From<u32> for AttributeText<'a> { fn from(v: u32) -> Self { AttributeText::attr(v.to_string()) @@ -853,7 +862,6 @@ pub trait GraphAttributes<'a> { self } - // TODO: add a ViewPort Struct? /// Clipping window on final drawing. /// viewport supersedes any size attribute. /// The width and height of the viewport specify precisely the final size of the output. @@ -865,8 +873,8 @@ pub trait GraphAttributes<'a> { /// of the graph, /// in points, of the center of the viewport, or the name N of a node whose center should used /// as the focus. - fn viewport(&mut self, viewport: String) -> &mut Self { - self.add_attribute("viewport", AttributeText::attr(viewport)) + fn viewport(&mut self, viewport: ViewPort) -> &mut Self { + self.add_attribute("viewport", AttributeText::from(viewport)) } /// Add an attribute to the node. diff --git a/src/attributes/viewport.rs b/src/attributes/viewport.rs new file mode 100644 index 0000000..f5bfe92 --- /dev/null +++ b/src/attributes/viewport.rs @@ -0,0 +1,108 @@ +use crate::attributes::Point; +use crate::DotString; +use std::borrow::Cow; + +pub struct ViewPort { + pub width: f32, + pub height: f32, + pub zoom: f32, + pub focus: Option<FocusType> +} + +impl ViewPort { + pub fn new(width: f32, height: f32, zoom: Option<f32>, focus: Option<FocusType>) -> Self { + Self { + width, + height, + zoom: zoom.unwrap_or(1 as f32), + focus + } + } + + pub fn new_point(width: f32, height: f32, zoom: Option<f32>, x: f32, y: f32) -> Self { + Self { + width, + height, + zoom: zoom.unwrap_or(1 as f32), + focus: Some(FocusType::Point(Point::new_2d(x, y))) + } + } + + pub fn new_node(width: f32, height: f32, zoom: Option<f32>, node: String) -> Self { + Self { + width, + height, + zoom: zoom.unwrap_or(1 as f32), + focus: Some(FocusType::Node(node)) + } + } +} + +impl<'a> DotString<'a> for ViewPort { + fn dot_string(&self) -> Cow<'a, str> { + let mut dot_string = String::from(""); + dot_string.push_str( + format!("{:.1},{:.1},{:.1}", + self.width, self.height, self.zoom + ).as_str()); + + if let Some(focus) = &self.focus { + match focus { + FocusType::Point(p) => { + dot_string.push_str(format!(",{}", p.dot_string()).as_str()); + }, + FocusType::Node(n) => { + dot_string.push_str(format!(",'{}'", n).as_str()); + }, + } + } + + dot_string.into() + } +} + +pub enum FocusType { + Point(Point), + Node(String) +} + + +#[cfg(test)] +mod test { + use crate::attributes::{ViewPort}; + use crate::DotString; + + #[test] + fn viewport_dot_string() { + assert_eq!( + "1.0,2.0,1.0", + ViewPort::new(1.0, 2.0, None, None).dot_string() + ); + } + + #[test] + fn viewport_zoom_dot_string() { + assert_eq!( + "1.0,2.0,3.0", + ViewPort::new(1.0, 2.0, Some(3.0), None).dot_string() + ); + } + + #[test] + fn viewport_point_focus_dot_string() { + assert_eq!( + "1.0,2.0,3.0,5.0,10.0", + ViewPort::new_point(1.0, 2.0, Some(3.0), 5.0, 10.0).dot_string() + ); + } + + #[test] + fn viewport_node_focus_dot_string() { + assert_eq!( + "1.0,2.0,3.0,'2.8 BSD'", + ViewPort::new_node( + 1.0, 2.0, Some(3.0), String::from("2.8 BSD") + ).dot_string() + ); + } +}
\ No newline at end of file |