1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
use std::{cmp::Ordering, fmt::Display};
use serde::{Deserialize, Serialize};
use crate::{EdgeStyle, NodeId, Port, SubgraphId, TYPST_PREFIX};
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize)]
pub enum ArrowKind {
#[default]
Single,
DoubleEnded,
None,
}
impl ArrowKind {
pub fn is_double_ended(&self) -> bool {
matches!(self, ArrowKind::DoubleEnded)
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Edge {
from: NodeId,
to: NodeId,
label: Option<String>,
object: Option<String>,
weight: Option<f64>,
options: EdgeOptions,
style: Option<EdgeStyle>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize)]
pub struct EdgeOptions {
arrowkind: ArrowKind,
dashed: bool,
floating: bool,
headport: Option<Port>,
tailport: Option<Port>,
from_subgraph: Option<SubgraphId>,
to_subgraph: Option<SubgraphId>,
}
impl Edge {
/// Creates a new [`Edge`] from a source [`Node`](crate::Node) to a target [`Node`](crate::Node).
pub fn from(from: NodeId, to: NodeId) -> Self {
Self {
from,
to,
..Default::default()
}
}
}
impl Edge {
// -- builders
/// Sets the Label for the [`Edge`].
pub fn label<L: Display>(mut self, label: L) -> Self {
self.label = Some(label.to_string());
self
}
/// Adds an Object (SVG) to the [`Edge`]. SVG-element must be without surroundings <svg>-tags, only the element-tag
/// like <rect>, <group>, <text> etc.
///
/// # Example
/// ```
/// let edge = Edge::from(source, target)
/// .label("Hi")
/// .object("<rect width='40' height='20' fill='black'></rect>");
/// ```
pub fn object<SVG: Display>(mut self, object: SVG) -> Self {
self.object = Some(object.to_string());
self
}
/// Adds [typst](https://typst.app) as the content of the [`Edge`]. Typst [symbols](https://typst.app/docs/reference/symbols/sym/).
///
/// # Example
/// ```
/// let edge = Edge::from(a, b).typst("$ (partial g)/(partial x) = 0 $");
/// ```
pub fn typst<T: Display>(mut self, typst: T) -> Self {
self.object = Some(format!("{TYPST_PREFIX}\n{typst}"));
self
}
/// Makes the Edge dashed.
pub fn dashed(mut self) -> Self {
self.options.dashed = true;
self
}
/// Makes the [`Edge`] a double-ended arrow.
pub fn double_ended(mut self) -> Self {
self.options.arrowkind = ArrowKind::DoubleEnded;
self
}
/// Makes the [`Edge`] have no arrow.
pub fn no_arrow(mut self) -> Self {
self.options.arrowkind = ArrowKind::None;
self
}
/// Whether or not Edge-label should be "floating" above the line, meaning no Node or Edge should care about the
/// Edge-label in the overall layout.
pub fn floating(mut self) -> Self {
self.options.floating = true;
self
}
/// Sets a port for head-Node.
pub fn headport(mut self, port: Port) -> Self {
self.options.headport = Some(port);
self
}
/// Sets a port for tail-Node.
pub fn tailport(mut self, port: Port) -> Self {
self.options.tailport = Some(port);
self
}
/// Sets a Subgraph as the source.
pub fn from_subgraph(mut self, subgraph: SubgraphId) -> Self {
self.options.from_subgraph = Some(subgraph);
self
}
/// Sets a Subgraph as the target.
pub fn to_subgraph(mut self, subgraph: SubgraphId) -> Self {
self.options.to_subgraph = Some(subgraph);
self
}
/// Set custom style for [`Edge`].
pub fn style(mut self, edgestyle: EdgeStyle) -> Self {
self.style = Some(edgestyle);
self
}
/// Adds a weight to the [`Edge`].
pub fn weight<W: Into<f64>>(mut self, weight: W) -> Self {
self.weight = Some(weight.into());
self
}
// -- getters
/// Returns the ID of the source Node.
pub fn get_from(&self) -> NodeId {
self.from
}
/// Returns the ID of the target Node.
pub fn get_to(&self) -> NodeId {
self.to
}
/// Returns a reference to the Label of the Edge, if any.
pub fn get_label(&self) -> Option<&str> {
self.label.as_deref()
}
/// Returns a reference to the Object (SVG) of the Edge, if any.
pub fn get_object(&self) -> Option<&str> {
self.object.as_deref()
}
/// Returns a reference to custom style for the Edge, if any.
pub fn get_style(&self) -> Option<&EdgeStyle> {
self.style.as_ref()
}
/// Returns typst content, if any.
pub fn get_typst(&self) -> Option<String> {
if let Some(content) = &self.object {
if content.starts_with(TYPST_PREFIX) {
return Some(content.replace(TYPST_PREFIX, ""));
}
}
None
}
/// Returns the weight of the Edge, if any.
pub fn get_weight(&self) -> Option<f64> {
self.weight
}
// -- getters: EdgeOptions
/// Returns the kind of arrow for the Edge.
pub fn get_arrowkind(&self) -> ArrowKind {
self.options.arrowkind
}
/// Returns wether the Edge is dashed.
pub fn get_dashed(&self) -> bool {
self.options.dashed
}
/// Return whether Edge-label is "floating".
pub fn get_floating(&self) -> bool {
self.options.floating
}
/// Returns the port of the head-Node.
pub fn get_headport(&self) -> Option<Port> {
self.options.headport
}
/// Returns the port of the tail-Node.
pub fn get_tailport(&self) -> Option<Port> {
self.options.tailport
}
/// Returns the ID of the source Subgraph.
pub fn get_from_subgraph(&self) -> Option<SubgraphId> {
self.options.from_subgraph
}
/// Returns the ID of the target Subgraph.
pub fn get_to_subgraph(&self) -> Option<SubgraphId> {
self.options.to_subgraph
}
}
// -- traits: custom defined Eq, PartialEq, Ord and PartialOrd
impl PartialEq for Edge {
fn eq(&self, other: &Self) -> bool {
self.from == other.from && self.to == other.to && self.label == other.label && self.object == other.object && self.weight == other.weight
}
}
impl PartialOrd for Edge {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
// Sammenlign 'from'-feltet først
match self.from.partial_cmp(&other.from) {
// Hvis de er like, fortsett til neste felt
Some(Ordering::Equal) => {}
// Hvis de IKKE er like, har vi resultatet vårt. Returner det.
result => return result,
}
// Sammenlign 'to'-feltet
match self.to.partial_cmp(&other.to) {
Some(Ordering::Equal) => {}
result => return result,
}
// ...og så videre for de andre feltene...
match self.label.partial_cmp(&other.label) {
Some(Ordering::Equal) => {}
result => return result,
}
// Her bruker vi partial_cmp for vekten!
match self.weight.partial_cmp(&other.weight) {
Some(Ordering::Equal) => {}
result => return result,
}
// Hvis alt annet var likt, sammenlign det siste feltet
self.object.partial_cmp(&other.object)
}
}