1use crate::core::base::Orientation;
4use crate::core::format::{ClipHandle, RenderBackend, Renderable, Visible};
5use crate::core::geometry::*;
6use crate::core::style::{LineStyleKind, StyleAttr};
7use crate::std_shapes::shapes::*;
8
9fn get_record_size(
12 rec: &RecordDef,
13 dir: Orientation,
14 font_size: usize,
15) -> Point {
16 match rec {
17 RecordDef::Text(label, _) => pad_shape_scalar(
18 get_size_for_str(label, font_size),
19 BOX_SHAPE_PADDING,
20 ),
21 RecordDef::Array(arr) => {
22 let mut x: f64 = 0.;
23 let mut y: f64 = 0.;
24 for elem in arr {
25 let ret = get_record_size(elem, dir.flip(), font_size);
26 if dir.is_left_right() {
27 x += ret.x;
28 y = y.max(ret.y);
29 } else {
30 x = x.max(ret.x);
31 y += ret.y;
32 }
33 }
34 Point::new(x, y)
35 }
36 }
37}
38
39const BOX_SHAPE_PADDING: f64 = 10.;
40const CIRCLE_SHAPE_PADDING: f64 = 20.;
41
42pub fn get_shape_size(
47 dir: Orientation,
48 s: &ShapeKind,
49 font: usize,
50 make_xy_same: bool,
51) -> Point {
52 let mut res = match s {
53 ShapeKind::Box(text) => {
54 pad_shape_scalar(get_size_for_str(text, font), BOX_SHAPE_PADDING)
55 }
56 ShapeKind::Circle(text) => {
57 pad_shape_scalar(get_size_for_str(text, font), CIRCLE_SHAPE_PADDING)
58 }
59 ShapeKind::DoubleCircle(text) => {
60 pad_shape_scalar(get_size_for_str(text, font), CIRCLE_SHAPE_PADDING)
61 }
62 ShapeKind::Record(sr) => {
63 pad_shape_scalar(get_record_size(sr, dir, font), BOX_SHAPE_PADDING)
64 }
65 ShapeKind::Connector(text) => {
66 if let Option::Some(text) = text {
67 pad_shape_scalar(
68 get_size_for_str(text, font),
69 BOX_SHAPE_PADDING,
70 )
71 } else {
72 Point::new(1., 1.)
73 }
74 }
75 _ => Point::new(1., 1.),
76 };
77 if make_xy_same {
78 res = make_size_square(res);
79 }
80 res
81}
82
83fn get_record_port_location(
86 rec: &RecordDef,
87 dir: Orientation,
88 loc: Point,
89 size: Point,
90 look: &StyleAttr,
91 port_name: &str,
92) -> (Point, Point) {
93 struct Locator {
94 port_name: String,
95 loc: Point,
96 size: Point,
97 }
98
99 impl RecordVisitor for Locator {
100 fn handle_box(&mut self, _loc: Point, _size: Point) {}
101 fn handle_text(
102 &mut self,
103 loc: Point,
104 size: Point,
105 _label: &str,
106 port: &Option<String>,
107 ) {
108 if let Option::Some(port_name) = port {
109 if *port_name == self.port_name {
110 self.loc = loc;
111 self.size = size;
112 }
113 }
114 }
115 }
116
117 let mut visitor = Locator {
118 port_name: port_name.to_string(),
119 loc,
120 size,
121 };
122 visit_record(rec, dir, loc, size, look, &mut visitor);
123 (visitor.loc, visitor.size)
124}
125
126fn render_record(
127 rec: &RecordDef,
128 dir: Orientation,
129 loc: Point,
130 size: Point,
131 look: &StyleAttr,
132 canvas: &mut dyn RenderBackend,
133) {
134 struct Renderer<'a> {
135 look: StyleAttr,
136 clip_handle: Option<ClipHandle>,
137 canvas: &'a mut dyn RenderBackend,
138 }
139
140 let mut clip_handle: Option<ClipHandle> = Option::None;
142
143 if look.rounded > 0 {
144 let xy = Point::new(loc.x - size.x / 2., loc.y - size.y / 2.);
145 let ch = canvas.create_clip(xy, size, 15);
146 clip_handle = Option::Some(ch);
147 }
148
149 impl<'a> RecordVisitor for Renderer<'a> {
150 fn handle_box(&mut self, loc: Point, size: Point) {
151 self.canvas.draw_rect(
152 Point::new(loc.x - size.x / 2., loc.y - size.y / 2.),
153 Point::new(size.x, size.y),
154 &self.look,
155 Option::None,
156 self.clip_handle,
157 );
158 }
159 fn handle_text(
160 &mut self,
161 loc: Point,
162 _size: Point,
163 label: &str,
164 _port: &Option<String>,
165 ) {
166 self.canvas.draw_text(loc, label, &self.look);
167 }
168 }
169
170 let mut visitor = Renderer {
171 look: look.clone(),
172 clip_handle,
173 canvas,
174 };
175 visitor.look.rounded = 0;
177 visit_record(rec, dir, loc, size, look, &mut visitor);
178
179 let mut look = look.clone();
180 look.fill_color = Option::None;
181 canvas.draw_rect(
182 Point::new(loc.x - size.x / 2., loc.y - size.y / 2.),
183 Point::new(size.x, size.y),
184 &look,
185 Option::None,
186 Option::None,
187 );
188}
189
190pub trait RecordVisitor {
191 fn handle_box(&mut self, loc: Point, size: Point);
192 fn handle_text(
193 &mut self,
194 loc: Point,
195 size: Point,
196 label: &str,
197 port: &Option<String>,
198 );
199}
200
201fn visit_record(
202 rec: &RecordDef,
203 dir: Orientation,
204 loc: Point,
205 size: Point,
206 look: &StyleAttr,
207 visitor: &mut dyn RecordVisitor,
208) {
209 visitor.handle_box(loc, size);
210 match rec {
211 RecordDef::Text(text, port) => {
212 visitor.handle_text(loc, size, text, port);
213 }
214 RecordDef::Array(arr) => {
215 let mut sizes: Vec<Point> = Vec::new();
216 let mut sum = Point::zero();
217 let mut mx = Point::zero();
218 for elem in arr {
221 let sz = get_record_size(elem, dir, look.font_size);
222 sizes.push(sz);
223 sum = Point::new(sum.x + sz.x, sum.y + sz.y);
224 mx = Point::new(mx.x.max(sz.x), mx.y.max(sz.y));
225 }
226 for sz in &mut sizes {
229 if dir.is_left_right() {
230 *sz = Point::new(size.x * sz.x / sum.x, size.y);
231 } else {
232 *sz = Point::new(size.x, size.y * sz.y / sum.y);
233 }
234 }
235
236 if dir.is_left_right() {
237 let mut startx = loc.x - size.x / 2.;
240 for i in 0..sizes.len() {
241 let element = &arr[i];
242 let loc2 = Point::new(startx + sizes[i].x / 2., loc.y);
243 visit_record(
244 element,
245 dir.flip(),
246 loc2,
247 sizes[i],
248 look,
249 visitor,
250 );
251 startx += sizes[i].x;
252 }
253 } else {
254 let mut starty = loc.y - size.y / 2.;
257 for i in 0..sizes.len() {
258 let element = &arr[i];
259 let loc2 = Point::new(loc.x, starty + sizes[i].y / 2.);
260 visit_record(
261 element,
262 dir.flip(),
263 loc2,
264 sizes[i],
265 look,
266 visitor,
267 );
268 starty += sizes[i].y;
269 }
270 }
271 }
272 }
273}
274
275impl Renderable for Element {
276 fn render(&self, debug: bool, canvas: &mut dyn RenderBackend) {
277 if debug {
278 let debug_look = StyleAttr::debug0();
280 let bb = self.pos.bbox(true);
281 canvas.draw_rect(
282 bb.0,
283 self.pos.size(true),
284 &debug_look,
285 self.properties.clone(),
286 Option::None,
287 );
288 }
289
290 match &self.shape {
291 ShapeKind::None => {}
292 ShapeKind::Record(rec) => {
293 render_record(
294 rec,
295 self.orientation,
296 self.pos.center(),
297 self.pos.size(false),
298 &self.look,
299 canvas,
300 );
301 }
302 ShapeKind::Box(text) => {
303 canvas.draw_rect(
304 self.pos.bbox(false).0,
305 self.pos.size(false),
306 &self.look,
307 self.properties.clone(),
308 Option::None,
309 );
310 canvas.draw_text(self.pos.center(), text.as_str(), &self.look);
311 }
312 ShapeKind::Circle(text) => {
313 canvas.draw_circle(
314 self.pos.center(),
315 self.pos.size(false),
316 &self.look,
317 self.properties.clone(),
318 );
319 canvas.draw_text(self.pos.center(), text.as_str(), &self.look);
320 }
321 ShapeKind::DoubleCircle(text) => {
322 canvas.draw_circle(
323 self.pos.center(),
324 self.pos.size(false),
325 &self.look,
326 self.properties.clone(),
327 );
328 let outer_circle_style = {
329 let mut x = self.look.clone();
330 x.fill_color = None;
331 x
332 };
333 canvas.draw_circle(
334 self.pos.center(),
335 self.pos.size(false).add(Point::splat(8.)),
336 &outer_circle_style,
337 None,
338 );
339 canvas.draw_text(self.pos.center(), text.as_str(), &self.look);
340 }
341 ShapeKind::Connector(label) => {
342 if debug {
343 canvas.draw_rect(
344 self.pos.bbox(true).0,
345 self.pos.size(true),
346 &StyleAttr::debug0(),
347 Option::None,
348 Option::None,
349 );
350
351 canvas.draw_rect(
352 self.pos.bbox(false).0,
353 self.pos.size(false),
354 &StyleAttr::debug1(),
355 Option::None,
356 Option::None,
357 );
358 }
359 if let Option::Some(label) = label {
360 canvas.draw_text(self.pos.middle(), label, &self.look);
361 }
362 }
363 }
364 if debug {
365 canvas.draw_circle(
366 self.pos.center(),
367 Point::new(6., 6.),
368 &StyleAttr::debug2(),
369 Option::None,
370 );
371 }
372 }
373
374 fn get_connector_location(
375 &self,
376 from: Point,
377 force: f64,
378 port: &Option<String>,
379 ) -> (Point, Point) {
380 match &self.shape {
381 ShapeKind::None => (Point::zero(), Point::zero()),
382 ShapeKind::Record(rec) => {
383 let mut loc = self.pos.center();
384 let mut size = self.pos.size(false);
385 if let Option::Some(port_name) = port {
387 let r = get_record_port_location(
388 rec,
389 self.orientation,
390 loc,
391 size,
392 &self.look,
393 port_name,
394 );
395 loc = r.0;
396 size = r.1;
397 }
398
399 get_connection_point_for_box(loc, size, from, force)
400 }
401 ShapeKind::Box(_) => {
402 let loc = self.pos.center();
403 let size = self.pos.size(false);
404 get_connection_point_for_box(loc, size, from, force)
405 }
406 ShapeKind::Circle(_) => {
407 let loc = self.pos.center();
408 let size = self.pos.size(false);
409 get_connection_point_for_circle(loc, size, from, force)
410 }
411 ShapeKind::DoubleCircle(_) => {
412 let loc = self.pos.center();
413 let size = self.pos.size(false);
414 get_connection_point_for_circle(loc, size, from, force)
415 }
416 _ => {
417 unreachable!();
418 }
419 }
420 }
421
422 fn get_passthrough_path(
423 &self,
424 _from: Point,
425 _to: Point,
426 _force: f64,
427 ) -> (Point, Point) {
428 let loc = self.pos.center();
429 let size = self.pos.size(false);
430 if let ShapeKind::Connector(_) = self.shape {
431 get_passthrough_path_invisible(size, loc, _from, _to, _force)
432 } else {
433 panic!("We don't pass edges through this kind of shape");
434 }
435 }
436}
437
438pub fn generate_curve_for_elements(
439 elements: &[Element],
440 arrow: &Arrow,
441 force: f64,
442) -> Vec<(Point, Point)> {
443 let mut path: Vec<(Point, Point)> = Vec::new();
444 let to_loc = elements[1].position().center();
445 let from_con =
446 elements[0].get_connector_location(to_loc, force, &arrow.src_port);
447
448 let mut prev_exit_loc = from_con.0;
449
450 path.push((from_con.0, from_con.1));
451
452 for i in 1..elements.len() {
453 let to_con;
454 let is_last: bool = i == elements.len() - 1;
455
456 if is_last {
457 let to = &elements[i];
458 to_con = to.get_connector_location(
459 prev_exit_loc,
460 force,
461 &arrow.dst_port,
462 );
463 prev_exit_loc = to_con.0;
464 } else {
465 let center = &elements[i];
466 let to = &elements[i + 1];
467 let to_loc = to.position().center();
468 to_con = center.get_passthrough_path(prev_exit_loc, to_loc, force);
469 prev_exit_loc = to_con.0;
470 }
471
472 path.push((to_con.1, to_con.0));
473 }
474
475 path
476}
477
478pub fn render_arrow(
479 canvas: &mut dyn RenderBackend,
480 debug: bool,
481 elements: &[Element],
482 arrow: &Arrow,
483) {
484 let path = generate_curve_for_elements(elements, arrow, 30.);
485
486 if debug {
487 for seg in &path {
488 canvas.draw_line(seg.0, seg.1, &StyleAttr::debug2(), Option::None);
489 canvas.draw_circle(
490 seg.0,
491 Point::new(6., 6.),
492 &StyleAttr::debug1(),
493 Option::None,
494 );
495 canvas.draw_circle(
496 seg.1,
497 Point::new(6., 6.),
498 &StyleAttr::debug1(),
499 Option::None,
500 );
501 }
502 }
503
504 let dash = match arrow.line_style {
505 LineStyleKind::None => {
506 return;
507 }
508 LineStyleKind::Normal => false,
509 LineStyleKind::Dashed => true,
510 LineStyleKind::Dotted => true,
511 };
512
513 let start = matches!(arrow.start, LineEndKind::Arrow);
514 let end = matches!(arrow.end, LineEndKind::Arrow);
515
516 canvas.draw_arrow(
517 &path,
518 dash,
519 (start, end),
520 &arrow.look,
521 arrow.properties.clone(),
522 &arrow.text,
523 );
524}