Skip to content

Commit

Permalink
Merge pull request #38 from azriel91/feature/margins
Browse files Browse the repository at this point in the history
  • Loading branch information
azriel91 authored Oct 13, 2024
2 parents f4f3369 + 4fde92e commit 9ca1dec
Show file tree
Hide file tree
Showing 8 changed files with 401 additions and 64 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## unreleased

* Support `margins` in `GraphvizAttrs`.
* Support `margin_cluster_default` in `GraphvizAttrs`.
* Support `margin_node_default` in `GraphvizAttrs`.
* Support `node_width_default` in `GraphvizAttrs`.
* Support `node_widths` in `GraphvizAttrs`.
* Support `node_height_default` in `GraphvizAttrs`.
Expand Down
90 changes: 87 additions & 3 deletions crate/model/src/common/graphviz_attrs.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
use serde::{Deserialize, Serialize};

pub use self::{
edge_constraints::EdgeConstraints, edge_dir::EdgeDir, edge_dirs::EdgeDirs,
edge_minlens::EdgeMinlens, fixed_size::FixedSize, node_heights::NodeHeights,
node_widths::NodeWidths, pack_mode::PackMode, pack_mode_flag::PackModeFlag, splines::Splines,
cluster_margin::ClusterMargin, edge_constraints::EdgeConstraints, edge_dir::EdgeDir,
edge_dirs::EdgeDirs, edge_minlens::EdgeMinlens, fixed_size::FixedSize, margin::Margin,
margins::Margins, node_heights::NodeHeights, node_margin::NodeMargin, node_widths::NodeWidths,
pack_mode::PackMode, pack_mode_flag::PackModeFlag, splines::Splines,
};

mod cluster_margin;
mod edge_constraints;
mod edge_dir;
mod edge_dirs;
mod edge_minlens;
mod fixed_size;
mod margin;
mod margins;
mod node_heights;
mod node_margin;
mod node_widths;
mod pack_mode;
mod pack_mode_flag;
Expand Down Expand Up @@ -69,6 +74,24 @@ pub struct GraphvizAttrs {
///
/// [`minlen`]: https://graphviz.org/docs/attrs/minlen/
pub edge_minlens: EdgeMinlens,
/// The default value for each node's [`margin`], defaults to `0.11,0.055`.
///
/// May be a single float, or two floats separated by a comma.
///
/// [`margin`]: https://graphviz.org/docs/attrs/margin/
pub margin_cluster_default: ClusterMargin,
/// The default value for each cluster's [`margin`], defaults to `8.0`.
///
/// May be a single float, or two floats separated by a comma.
///
/// [`margin`]: https://graphviz.org/docs/attrs/margin/
pub margin_node_default: NodeMargin,
/// Each node or cluster's [`margin`].
///
/// May be a single float, or two floats separated by a comma.
///
/// [`margin`]: https://graphviz.org/docs/attrs/margin/
pub margins: Margins,
/// Minimum / initial [`width`] for nodes, defaults to `0.3`.
///
/// If `fixedsize` is true, this will be the exact / maximum width for
Expand Down Expand Up @@ -197,6 +220,37 @@ impl GraphvizAttrs {
self
}

/// Sets the default value for each node's [`margin`], defaults to
/// `0.11,0.055`.
///
/// May be a single float, or two floats separated by a comma.
///
/// [`margin`]: https://graphviz.org/docs/attrs/margin/
pub fn with_margin_cluster_default(mut self, margin_cluster_default: ClusterMargin) -> Self {
self.margin_cluster_default = margin_cluster_default;
self
}

/// Sets the default value for each cluster's [`margin`], defaults to `8.0`.
///
/// May be a single float, or two floats separated by a comma.
///
/// [`margin`]: https://graphviz.org/docs/attrs/margin/
pub fn with_margin_node_default(mut self, margin_node_default: NodeMargin) -> Self {
self.margin_node_default = margin_node_default;
self
}

/// Sets each node or cluster's [`margin`].
///
/// May be a single float, or two floats separated by a comma.
///
/// [`margin`]: https://graphviz.org/docs/attrs/margin/
pub fn with_margins(mut self, margins: Margins) -> Self {
self.margins = margins;
self
}

/// Sets the minimum / initial [`width`] for nodes, defaults to `0.3`.
///
/// If `fixedsize` is true, this will be the exact / maximum width for
Expand Down Expand Up @@ -323,6 +377,33 @@ impl GraphvizAttrs {
&self.edge_minlens
}

/// Returns the default value for each node's [`margin`].
///
/// May be a single float, or two floats separated by a comma.
///
/// [`margin`]: https://graphviz.org/docs/attrs/margin/
pub fn margin_cluster_default(&self) -> ClusterMargin {
self.margin_cluster_default
}

/// Returns the default value for each cluster's [`margin`].
///
/// May be a single float, or two floats separated by a comma.
///
/// [`margin`]: https://graphviz.org/docs/attrs/margin/
pub fn margin_node_default(&self) -> NodeMargin {
self.margin_node_default
}

/// Returns each node or cluster's [`margin`].
///
/// May be a single float, or two floats separated by a comma.
///
/// [`margin`]: https://graphviz.org/docs/attrs/margin/
pub fn margins(&self) -> &Margins {
&self.margins
}

/// Returns the minimum / initial [`width`] for nodes.
///
/// If `fixedsize` is true, this will be the exact / maximum width for
Expand Down Expand Up @@ -385,6 +466,9 @@ impl Default for GraphvizAttrs {
edge_dirs: EdgeDirs::default(),
edge_minlen_default: 2,
edge_minlens: EdgeMinlens::default(),
margin_cluster_default: ClusterMargin::default(),
margin_node_default: NodeMargin::default(),
margins: Margins::default(),
node_width_default: 0.3,
node_widths: NodeWidths::default(),
node_height_default: 0.1,
Expand Down
61 changes: 61 additions & 0 deletions crate/model/src/common/graphviz_attrs/cluster_margin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use std::fmt;

use serde::{Deserialize, Serialize};

use crate::common::graphviz_attrs::Margin;

/// A node [`margin`], which specifies the space between the nodes in the
/// cluster and the cluster bounding box.
///
/// Defaults to `Margin::Same(8.0)`; Graphviz default: `8.0`.
///
/// May be a single float, or two floats separated by a comma.
///
/// [`margin`]: https://graphviz.org/docs/attrs/margin/
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ClusterMargin(pub Margin);

impl ClusterMargin {
/// Returns a new `ClusterMargin`.
pub fn new() -> Self {
Self::default()
}

/// Returns the inner `Margin`.
pub fn into_inner(self) -> Margin {
self.0
}
}

impl Default for ClusterMargin {
fn default() -> Self {
Self(Margin::Same(8.0))
}
}

impl From<Margin> for ClusterMargin {
fn from(margin: Margin) -> Self {
Self(margin)
}
}

impl std::ops::DerefMut for ClusterMargin {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl std::ops::Deref for ClusterMargin {
type Target = Margin;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl fmt::Display for ClusterMargin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
78 changes: 78 additions & 0 deletions crate/model/src/common/graphviz_attrs/margin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use std::{fmt, fmt::Display, str::FromStr};

use serde::{de, Deserialize, Deserializer, Serialize, Serializer};

/// Node or cluster [`margin`]s.
///
/// May be a single float, or two floats separated by a comma.
///
/// [`margin`]: https://graphviz.org/docs/attrs/margin/
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Margin {
/// Margins on both left/right and top/bottom are the same.
Same(f64),
/// Margins for left/right are different to top/bottom.
Different(f64, f64),
}

impl FromStr for Margin {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.split_once(',') {
Some((margin_x, margin_y)) => {
let margin_x = margin_x.parse::<f64>().map_err(|_e| {
format!(
"Failed to parse `{margin_x}` as a margin. \
Please use a floating point number such as 0.5."
)
})?;
let margin_y = margin_y.parse::<f64>().map_err(|_e| {
format!(
"Failed to parse `{margin_y}` as a margin. \
Please use a floating point number such as 0.5."
)
})?;

Ok(Self::Different(margin_x, margin_y))
}
None => {
let margin = s.parse::<f64>().map_err(|_e| {
format!(
"Failed to parse `{s}` as a margin. \
Please use a floating point number such as 0.5."
)
})?;
Ok(Self::Same(margin))
}
}
}
}

impl Display for Margin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Margin::Same(margin) => margin.fmt(f),
Margin::Different(margin_x, margin_y) => write!(f, "{margin_x},{margin_y}"),
}
}
}

impl<'de> Deserialize<'de> for Margin {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(de::Error::custom)
}
}

impl Serialize for Margin {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
64 changes: 64 additions & 0 deletions crate/model/src/common/graphviz_attrs/margins.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use std::ops::{Deref, DerefMut};

use indexmap::IndexMap;
use serde::{Deserialize, Serialize};

use crate::common::{graphviz_attrs::Margin, NodeId};

/// GraphViz node or cluster margins. `IndexMap<NodeId, Margin>` newtype.
///
/// This is only used for GraphViz dot graphs, which sets the [`margin`]
/// attribute for the node / cluster.
///
/// If this is unset, nodes will use the default [`NodeMargin`], and clusters
/// will use the default [`ClusterMargin`].
///
/// [`margin`]: https://graphviz.org/docs/attrs/margin/
/// [`NodeMargin`]: crate::common::graphviz_attrs::NodeMargin
/// [`ClusterMargin`]: crate::common::graphviz_attrs::ClusterMargin
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
pub struct Margins(IndexMap<NodeId, Margin>);

impl Margins {
/// Returns a new `Margins` map.
pub fn new() -> Self {
Self::default()
}

/// Returns a new `Margins` map with the given preallocated
/// capacity.
pub fn with_capacity(capacity: usize) -> Self {
Self(IndexMap::with_capacity(capacity))
}

/// Returns the underlying map.
pub fn into_inner(self) -> IndexMap<NodeId, Margin> {
self.0
}
}

impl Deref for Margins {
type Target = IndexMap<NodeId, Margin>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for Margins {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl From<IndexMap<NodeId, Margin>> for Margins {
fn from(inner: IndexMap<NodeId, Margin>) -> Self {
Self(inner)
}
}

impl FromIterator<(NodeId, Margin)> for Margins {
fn from_iter<I: IntoIterator<Item = (NodeId, Margin)>>(iter: I) -> Self {
Self(IndexMap::from_iter(iter))
}
}
Loading

0 comments on commit 9ca1dec

Please sign in to comment.