mod force;
mod sugiyama;
pub(crate) use force::ForceDirectedLayout;
pub(crate) use sugiyama::SugiyamaLayout;
use super::graph::IndexedGraph;
use super::types::{DiagramCluster, DiagramEdge, DiagramNode, Orientation};
use super::viewport::BoundingBox;
#[derive(Clone, Debug, PartialEq)]
pub struct NodePosition {
id: String,
x: f64,
y: f64,
width: f64,
height: f64,
}
impl NodePosition {
pub fn new(id: String, x: f64, y: f64, width: f64, height: f64) -> Self {
Self {
id,
x,
y,
width,
height,
}
}
pub fn id(&self) -> &str {
&self.id
}
pub fn x(&self) -> f64 {
self.x
}
pub fn y(&self) -> f64 {
self.y
}
pub fn width(&self) -> f64 {
self.width
}
pub fn height(&self) -> f64 {
self.height
}
pub(crate) fn center_x(&self) -> f64 {
self.x + self.width / 2.0
}
pub(crate) fn center_y(&self) -> f64 {
self.y + self.height / 2.0
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum PathSegment {
MoveTo(f64, f64),
LineTo(f64, f64),
}
#[derive(Clone, Debug, PartialEq)]
pub struct EdgePath {
from_id: String,
to_id: String,
segments: Vec<PathSegment>,
}
impl EdgePath {
pub fn new(from_id: String, to_id: String, segments: Vec<PathSegment>) -> Self {
Self {
from_id,
to_id,
segments,
}
}
pub fn from_id(&self) -> &str {
&self.from_id
}
pub fn to_id(&self) -> &str {
&self.to_id
}
pub fn segments(&self) -> &[PathSegment] {
&self.segments
}
pub(crate) fn segments_mut(&mut self) -> &mut Vec<PathSegment> {
&mut self.segments
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct LayoutResult {
pub(crate) node_positions: Vec<NodePosition>,
pub(crate) edge_paths: Vec<EdgePath>,
pub(crate) bounding_box: BoundingBox,
}
impl LayoutResult {
pub fn node_positions(&self) -> &[NodePosition] {
&self.node_positions
}
pub fn edge_paths(&self) -> &[EdgePath] {
&self.edge_paths
}
pub fn bounding_box(&self) -> &BoundingBox {
&self.bounding_box
}
pub fn empty() -> Self {
Self {
node_positions: Vec::new(),
edge_paths: Vec::new(),
bounding_box: BoundingBox::default(),
}
}
}
pub(crate) struct LayoutHints<'a> {
pub(crate) orientation: Orientation,
pub(crate) node_spacing: f64,
pub(crate) layer_spacing: f64,
#[allow(dead_code)] pub(crate) previous_layout: Option<&'a LayoutResult>,
}
impl Default for LayoutHints<'_> {
fn default() -> Self {
Self {
orientation: Orientation::default(),
node_spacing: 4.0,
layer_spacing: 8.0,
previous_layout: None,
}
}
}
pub(crate) trait LayoutAlgorithm {
fn compute(
&self,
graph: &IndexedGraph,
nodes: &[DiagramNode],
edges: &[DiagramEdge],
clusters: &[DiagramCluster],
hints: &LayoutHints<'_>,
) -> LayoutResult;
}