layout/std_shapes/
shapes.rs

1//! The shapes that are defined in this module are used to describe and render the
2//! shapes that appear on the screen. Things like 'circle' and 'edge' go in here.
3//! Shapes need to contain all of the information that they need to be rendered.
4//! This includes things like font size, and color.
5
6use crate::core::base::Orientation;
7use crate::core::format::Visible;
8use crate::core::geometry::{Point, Position};
9use crate::core::style::{LineStyleKind, StyleAttr};
10use crate::std_shapes::render::get_shape_size;
11
12const PADDING: f64 = 60.;
13const CONN_PADDING: f64 = 10.;
14
15#[derive(Debug, Copy, Clone)]
16pub enum LineEndKind {
17    None,
18    Arrow,
19}
20
21#[derive(Debug, Clone)]
22pub enum RecordDef {
23    // Label, port:
24    Text(String, Option<String>),
25    Array(Vec<RecordDef>),
26}
27
28impl RecordDef {
29    pub fn new_text(s: &str) -> Self {
30        RecordDef::Text(s.to_string(), None)
31    }
32
33    pub fn new_text_with_port(s: &str, p: &str) -> Self {
34        RecordDef::Text(s.to_string(), Some(p.to_string()))
35    }
36}
37
38#[derive(Debug, Clone)]
39pub enum ShapeKind {
40    None,
41    Box(String),
42    Circle(String),
43    DoubleCircle(String),
44    Record(RecordDef),
45    Connector(Option<String>),
46}
47
48impl ShapeKind {
49    pub fn new_box(s: &str) -> Self {
50        ShapeKind::Box(s.to_string())
51    }
52    pub fn new_circle(s: &str) -> Self {
53        ShapeKind::Circle(s.to_string())
54    }
55    pub fn new_double_circle(s: &str) -> Self {
56        ShapeKind::DoubleCircle(s.to_string())
57    }
58    pub fn new_record(r: &RecordDef) -> Self {
59        ShapeKind::Record(r.clone())
60    }
61    pub fn new_connector(s: &str) -> Self {
62        if s.is_empty() {
63            return ShapeKind::Connector(None);
64        }
65        ShapeKind::Connector(Some(s.to_string()))
66    }
67}
68
69#[derive(Clone, Debug)]
70pub struct Element {
71    pub shape: ShapeKind,
72    pub pos: Position,
73    pub look: StyleAttr,
74    pub orientation: Orientation,
75    pub properties: Option<String>,
76}
77
78impl Element {
79    pub fn create(
80        shape: ShapeKind,
81        look: StyleAttr,
82        orientation: Orientation,
83        size: Point,
84    ) -> Element {
85        Element {
86            shape,
87            look,
88            orientation,
89            pos: Position::new(
90                Point::zero(),
91                size,
92                Point::zero(),
93                Point::splat(PADDING),
94            ),
95            properties: Option::None,
96        }
97    }
98
99    pub fn create_with_properties(
100        shape: ShapeKind,
101        look: StyleAttr,
102        orientation: Orientation,
103        size: Point,
104        properties: impl Into<String>,
105    ) -> Element {
106        let mut elem = Element::create(shape, look, orientation, size);
107        elem.properties = Option::Some(properties.into());
108        elem
109    }
110    pub fn create_connector(
111        label: &str,
112        look: &StyleAttr,
113        dir: Orientation,
114    ) -> Element {
115        Element {
116            shape: ShapeKind::new_connector(label),
117            look: look.clone(),
118            orientation: dir,
119            pos: Position::new(
120                Point::zero(),
121                Point::zero(),
122                Point::zero(),
123                Point::splat(CONN_PADDING),
124            ),
125            properties: Option::None,
126        }
127    }
128
129    pub fn empty_connector(dir: Orientation) -> Element {
130        Self::create_connector("", &StyleAttr::simple(), dir)
131    }
132
133    // Make the center of the shape point to \p to.
134    pub fn move_to(&mut self, to: Point) {
135        self.pos.move_to(to)
136    }
137}
138
139#[derive(Debug, Clone)]
140pub struct Arrow {
141    pub start: LineEndKind,
142    pub end: LineEndKind,
143    pub line_style: LineStyleKind,
144    pub text: String,
145    pub look: StyleAttr,
146    pub properties: Option<String>,
147    pub src_port: Option<String>,
148    pub dst_port: Option<String>,
149}
150
151impl Default for Arrow {
152    fn default() -> Arrow {
153        Arrow {
154            start: LineEndKind::None,
155            end: LineEndKind::Arrow,
156            line_style: LineStyleKind::Normal,
157            text: String::new(),
158            look: StyleAttr::simple(),
159            properties: Option::None,
160            src_port: Option::None,
161            dst_port: Option::None,
162        }
163    }
164}
165
166impl Arrow {
167    pub fn reverse(&self) -> Arrow {
168        Arrow {
169            start: self.end,
170            end: self.start,
171            line_style: self.line_style,
172            text: self.text.clone(),
173            look: self.look.clone(),
174            properties: self.properties.clone(),
175            src_port: self.dst_port.clone(),
176            dst_port: self.src_port.clone(),
177        }
178    }
179
180    pub fn new(
181        start: LineEndKind,
182        end: LineEndKind,
183        line_style: LineStyleKind,
184        text: &str,
185        look: &StyleAttr,
186        src_port: &Option<String>,
187        dst_port: &Option<String>,
188    ) -> Arrow {
189        Arrow {
190            start,
191            end,
192            line_style,
193            text: String::from(text),
194            look: look.clone(),
195            properties: Option::None,
196            src_port: src_port.clone(),
197            dst_port: dst_port.clone(),
198        }
199    }
200
201    pub fn with_properties(
202        start: LineEndKind,
203        end: LineEndKind,
204        line_style: LineStyleKind,
205        text: &str,
206        look: &StyleAttr,
207        properties: impl Into<String>,
208        src_port: &Option<String>,
209        dst_port: &Option<String>,
210    ) -> Arrow {
211        Arrow {
212            start,
213            end,
214            line_style,
215            text: String::from(text),
216            look: look.clone(),
217            properties: Option::Some(properties.into()),
218            src_port: src_port.clone(),
219            dst_port: dst_port.clone(),
220        }
221    }
222
223    pub fn simple(text: &str) -> Arrow {
224        Arrow::new(
225            LineEndKind::None,
226            LineEndKind::Arrow,
227            LineStyleKind::Normal,
228            text,
229            &StyleAttr::simple(),
230            &None,
231            &None,
232        )
233    }
234
235    pub fn simple_with_properties(
236        text: &str,
237        properties: impl Into<String>,
238    ) -> Arrow {
239        let mut arrow = Arrow::simple(text);
240        arrow.properties = Some(properties.into());
241        arrow
242    }
243
244    pub fn invisible() -> Arrow {
245        Arrow::new(
246            LineEndKind::None,
247            LineEndKind::None,
248            LineStyleKind::None,
249            "",
250            &StyleAttr::simple(),
251            &None,
252            &None,
253        )
254    }
255}
256
257impl Visible for Element {
258    fn position(&self) -> Position {
259        self.pos
260    }
261    fn position_mut(&mut self) -> &mut Position {
262        &mut self.pos
263    }
264
265    fn is_connector(&self) -> bool {
266        matches!(self.shape, ShapeKind::Connector(_))
267    }
268
269    fn transpose(&mut self) {
270        self.orientation = self.orientation.flip();
271        self.pos.transpose();
272    }
273
274    fn resize(&mut self) {
275        if let ShapeKind::Connector(_) = self.shape.clone() {
276            let size = get_shape_size(
277                self.orientation,
278                &self.shape,
279                self.look.font_size,
280                false,
281            );
282            self.pos.set_size(size);
283            match self.orientation {
284                Orientation::TopToBottom => {
285                    self.pos.set_new_center_point(Point::new(0., size.y / 2.));
286                }
287                Orientation::LeftToRight => {
288                    self.pos.set_new_center_point(Point::new(size.x / 2., 0.));
289                }
290            }
291        }
292    }
293}