Skip to main content

jellyflow_layout/
preset.rs

1use jellyflow_core::{CanvasSize, NodeId};
2use serde::{Deserialize, Serialize};
3
4use crate::engine::{
5    LayoutDirection, LayoutEngineId, LayoutEngineRequest, LayoutOptions, LayoutRequest,
6    LayoutScope, LayoutSpacing,
7};
8
9/// Builder for common layout presets.
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11pub struct LayoutPresetBuilder {
12    request: LayoutEngineRequest,
13}
14
15impl Default for LayoutPresetBuilder {
16    fn default() -> Self {
17        Self::workflow()
18    }
19}
20
21impl LayoutPresetBuilder {
22    /// Returns a layered workflow preset.
23    pub fn workflow() -> Self {
24        Self::new(LayoutEngineId::dugong(), LayoutRequest::all())
25    }
26
27    /// Returns a tree-shaped layered preset.
28    pub fn tree() -> Self {
29        Self::new(LayoutEngineId::tidy_tree(), LayoutRequest::all()).with_options(LayoutOptions {
30            direction: LayoutDirection::TopToBottom,
31            spacing: LayoutSpacing {
32                nodesep: 32.0,
33                ranksep: 72.0,
34                edgesep: 16.0,
35            },
36            ..LayoutOptions::default()
37        })
38    }
39
40    /// Returns a radial mind-map preset.
41    pub fn mind_map() -> Self {
42        Self::new(LayoutEngineId::mind_map_radial(), LayoutRequest::all())
43    }
44
45    /// Returns a freeform mind-map preset.
46    pub fn freeform() -> Self {
47        Self::new(LayoutEngineId::mind_map_freeform(), LayoutRequest::all()).with_options(
48            LayoutOptions {
49                spacing: LayoutSpacing {
50                    nodesep: 24.0,
51                    ranksep: 24.0,
52                    edgesep: 24.0,
53                },
54                ..LayoutOptions::default()
55            },
56        )
57    }
58
59    /// Creates a preset builder for a specific engine.
60    pub fn new(engine: impl Into<LayoutEngineId>, layout: LayoutRequest) -> Self {
61        Self {
62            request: LayoutEngineRequest::new(engine, layout),
63        }
64    }
65
66    /// Uses a different engine.
67    pub fn with_engine(mut self, engine: impl Into<LayoutEngineId>) -> Self {
68        self.request.engine = engine.into();
69        self
70    }
71
72    /// Uses a different layout request.
73    pub fn with_layout(mut self, layout: LayoutRequest) -> Self {
74        self.request.layout = layout;
75        self
76    }
77
78    /// Sets layout options.
79    pub fn with_options(mut self, options: LayoutOptions) -> Self {
80        self.request.layout.options = options;
81        self
82    }
83
84    /// Uses a different layered layout direction.
85    pub fn with_direction(mut self, direction: LayoutDirection) -> Self {
86        self.request.layout.options.direction = direction;
87        self
88    }
89
90    /// Uses a different layered spacing profile.
91    pub fn with_spacing(mut self, spacing: LayoutSpacing) -> Self {
92        self.request.layout.options.spacing = spacing;
93        self
94    }
95
96    /// Uses a different margin.
97    pub fn with_margin(mut self, margin: CanvasSize) -> Self {
98        self.request.layout.options.margin = margin;
99        self
100    }
101
102    /// Uses a different fallback node size.
103    pub fn with_default_node_size(mut self, size: CanvasSize) -> Self {
104        self.request.layout.options.default_node_size = size;
105        self
106    }
107
108    /// Uses a different fallback node origin.
109    pub fn with_node_origin(mut self, node_origin: (f32, f32)) -> Self {
110        self.request.layout.options.node_origin = node_origin;
111        self
112    }
113
114    /// Targets all visible nodes.
115    pub fn all(mut self) -> Self {
116        self.request.layout.scope = LayoutScope::All;
117        self
118    }
119
120    /// Targets a selected set of nodes.
121    pub fn nodes(mut self, nodes: impl IntoIterator<Item = NodeId>) -> Self {
122        self.request.layout.scope = LayoutScope::Nodes {
123            nodes: nodes.into_iter().collect(),
124        };
125        self
126    }
127
128    /// Uses a different request scope.
129    pub fn with_scope(mut self, scope: LayoutScope) -> Self {
130        self.request.layout.scope = scope;
131        self
132    }
133
134    /// Adds request-local measured node sizes.
135    pub fn with_measured_node_sizes(
136        mut self,
137        sizes: impl IntoIterator<Item = (NodeId, CanvasSize)>,
138    ) -> Self {
139        self.request.layout.measured_node_sizes.extend(sizes);
140        self
141    }
142
143    /// Builds a layout engine request.
144    pub fn build(self) -> LayoutEngineRequest {
145        self.request
146    }
147
148    /// Returns the layout request without consuming the builder.
149    pub fn layout_request(&self) -> LayoutRequest {
150        self.request.layout.clone()
151    }
152
153    /// Returns the engine request without consuming the builder.
154    pub fn engine_request(&self) -> LayoutEngineRequest {
155        self.request.clone()
156    }
157}
158
159impl From<LayoutPresetBuilder> for LayoutEngineRequest {
160    fn from(value: LayoutPresetBuilder) -> Self {
161        value.build()
162    }
163}