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 self.clip_handle,
156 );
157 }
158 fn handle_text(
159 &mut self,
160 loc: Point,
161 _size: Point,
162 label: &str,
163 _port: &Option<String>,
164 ) {
165 self.canvas.draw_text(loc, label, &self.look);
166 }
167 }
168
169 let mut visitor = Renderer {
170 look: look.clone(),
171 clip_handle,
172 canvas,
173 };
174 visitor.look.rounded = 0;
176 visit_record(rec, dir, loc, size, look, &mut visitor);
177
178 let mut look = look.clone();
179 look.fill_color = Option::None;
180 canvas.draw_rect(
181 Point::new(loc.x - size.x / 2., loc.y - size.y / 2.),
182 Point::new(size.x, size.y),
183 &look,
184 Option::None,
185 );
186}
187
188pub trait RecordVisitor {
189 fn handle_box(&mut self, loc: Point, size: Point);
190 fn handle_text(
191 &mut self,
192 loc: Point,
193 size: Point,
194 label: &str,
195 port: &Option<String>,
196 );
197}
198
199fn visit_record(
200 rec: &RecordDef,
201 dir: Orientation,
202 loc: Point,
203 size: Point,
204 look: &StyleAttr,
205 visitor: &mut dyn RecordVisitor,
206) {
207 visitor.handle_box(loc, size);
208 match rec {
209 RecordDef::Text(text, port) => {
210 visitor.handle_text(loc, size, text, port);
211 }
212 RecordDef::Array(arr) => {
213 let mut sizes: Vec<Point> = Vec::new();
214 let mut sum = Point::zero();
215 let mut mx = Point::zero();
216 for elem in arr {
219 let sz = get_record_size(elem, dir, look.font_size);
220 sizes.push(sz);
221 sum = Point::new(sum.x + sz.x, sum.y + sz.y);
222 mx = Point::new(mx.x.max(sz.x), mx.y.max(sz.y));
223 }
224 for sz in &mut sizes {
227 if dir.is_left_right() {
228 *sz = Point::new(size.x * sz.x / sum.x, size.y);
229 } else {
230 *sz = Point::new(size.x, size.y * sz.y / sum.y);
231 }
232 }
233
234 if dir.is_left_right() {
235 let mut startx = loc.x - size.x / 2.;
238 for i in 0..sizes.len() {
239 let element = &arr[i];
240 let loc2 = Point::new(startx + sizes[i].x / 2., loc.y);
241 visit_record(
242 element,
243 dir.flip(),
244 loc2,
245 sizes[i],
246 look,
247 visitor,
248 );
249 startx += sizes[i].x;
250 }
251 } else {
252 let mut starty = loc.y - size.y / 2.;
255 for i in 0..sizes.len() {
256 let element = &arr[i];
257 let loc2 = Point::new(loc.x, starty + sizes[i].y / 2.);
258 visit_record(
259 element,
260 dir.flip(),
261 loc2,
262 sizes[i],
263 look,
264 visitor,
265 );
266 starty += sizes[i].y;
267 }
268 }
269 }
270 }
271}
272
273impl Renderable for Element {
274 fn render(&self, debug: bool, canvas: &mut dyn RenderBackend) {
275 if debug {
276 let debug_look = StyleAttr::debug0();
278 let bb = self.pos.bbox(true);
279 canvas.draw_rect(
280 bb.0,
281 self.pos.size(true),
282 &debug_look,
283 Option::None,
284 );
285 }
286
287 match &self.shape {
288 ShapeKind::None => {}
289 ShapeKind::Record(rec) => {
290 render_record(
291 rec,
292 self.orientation,
293 self.pos.center(),
294 self.pos.size(false),
295 &self.look,
296 canvas,
297 );
298 }
299 ShapeKind::Box(text) => {
300 canvas.draw_rect(
301 self.pos.bbox(false).0,
302 self.pos.size(false),
303 &self.look,
304 Option::None,
305 );
306 canvas.draw_text(self.pos.center(), text.as_str(), &self.look);
307 }
308 ShapeKind::Circle(text) => {
309 canvas.draw_circle(
310 self.pos.center(),
311 self.pos.size(false),
312 &self.look,
313 );
314 canvas.draw_text(self.pos.center(), text.as_str(), &self.look);
315 }
316 ShapeKind::DoubleCircle(text) => {
317 canvas.draw_circle(
318 self.pos.center(),
319 self.pos.size(false),
320 &self.look,
321 );
322 canvas.draw_circle(
323 self.pos.center(),
324 self.pos.size(false).sub(Point::splat(15.)),
325 &self.look,
326 );
327 canvas.draw_text(self.pos.center(), text.as_str(), &self.look);
328 }
329 ShapeKind::Connector(label) => {
330 if debug {
331 canvas.draw_rect(
332 self.pos.bbox(true).0,
333 self.pos.size(true),
334 &StyleAttr::debug0(),
335 Option::None,
336 );
337
338 canvas.draw_rect(
339 self.pos.bbox(false).0,
340 self.pos.size(false),
341 &StyleAttr::debug1(),
342 Option::None,
343 );
344 }
345 if let Option::Some(label) = label {
346 canvas.draw_text(self.pos.middle(), label, &self.look);
347 }
348 }
349 }
350 if debug {
351 canvas.draw_circle(
352 self.pos.center(),
353 Point::new(6., 6.),
354 &StyleAttr::debug2(),
355 );
356 }
357 }
358
359 fn get_connector_location(
360 &self,
361 from: Point,
362 force: f64,
363 port: &Option<String>,
364 ) -> (Point, Point) {
365 match &self.shape {
366 ShapeKind::None => (Point::zero(), Point::zero()),
367 ShapeKind::Record(rec) => {
368 let mut loc = self.pos.center();
369 let mut size = self.pos.size(false);
370 if let Option::Some(port_name) = port {
372 let r = get_record_port_location(
373 rec,
374 self.orientation,
375 loc,
376 size,
377 &self.look,
378 port_name,
379 );
380 loc = r.0;
381 size = r.1;
382 }
383
384 get_connection_point_for_box(loc, size, from, force)
385 }
386 ShapeKind::Box(_) => {
387 let loc = self.pos.center();
388 let size = self.pos.size(false);
389 get_connection_point_for_box(loc, size, from, force)
390 }
391 ShapeKind::Circle(_) => {
392 let loc = self.pos.center();
393 let size = self.pos.size(false);
394 get_connection_point_for_circle(loc, size, from, force)
395 }
396 ShapeKind::DoubleCircle(_) => {
397 let loc = self.pos.center();
398 let size = self.pos.size(false);
399 get_connection_point_for_circle(loc, size, from, force)
400 }
401 _ => {
402 unreachable!();
403 }
404 }
405 }
406
407 fn get_passthrough_path(
408 &self,
409 _from: Point,
410 _to: Point,
411 _force: f64,
412 ) -> (Point, Point) {
413 let loc = self.pos.center();
414 let size = self.pos.size(false);
415 if let ShapeKind::Connector(_) = self.shape {
416 get_passthrough_path_invisible(size, loc, _from, _to, _force)
417 } else {
418 panic!("We don't pass edges through this kind of shape");
419 }
420 }
421}
422
423pub fn generate_curve_for_elements(
424 elements: &[Element],
425 arrow: &Arrow,
426 force: f64,
427) -> Vec<(Point, Point)> {
428 let mut path: Vec<(Point, Point)> = Vec::new();
429 let to_loc = elements[1].position().center();
430 let from_con =
431 elements[0].get_connector_location(to_loc, force, &arrow.src_port);
432
433 let mut prev_exit_loc = from_con.0;
434
435 path.push((from_con.0, from_con.1));
436
437 for i in 1..elements.len() {
438 let to_con;
439 let is_last: bool = i == elements.len() - 1;
440
441 if is_last {
442 let to = &elements[i];
443 to_con = to.get_connector_location(
444 prev_exit_loc,
445 force,
446 &arrow.dst_port,
447 );
448 prev_exit_loc = to_con.0;
449 } else {
450 let center = &elements[i];
451 let to = &elements[i + 1];
452 let to_loc = to.position().center();
453 to_con = center.get_passthrough_path(prev_exit_loc, to_loc, force);
454 prev_exit_loc = to_con.0;
455 }
456
457 path.push((to_con.1, to_con.0));
458 }
459
460 path
461}
462
463pub fn render_arrow(
464 canvas: &mut dyn RenderBackend,
465 debug: bool,
466 elements: &[Element],
467 arrow: &Arrow,
468) {
469 let path = generate_curve_for_elements(elements, arrow, 30.);
470
471 if debug {
472 for seg in &path {
473 canvas.draw_line(seg.0, seg.1, &StyleAttr::debug2());
474 canvas.draw_circle(seg.0, Point::new(6., 6.), &StyleAttr::debug1());
475 canvas.draw_circle(seg.1, Point::new(6., 6.), &StyleAttr::debug1());
476 }
477 }
478
479 let dash = match arrow.line_style {
480 LineStyleKind::None => {
481 return;
482 }
483 LineStyleKind::Normal => false,
484 LineStyleKind::Dashed => true,
485 LineStyleKind::Dotted => true,
486 };
487
488 let start = matches!(arrow.start, LineEndKind::Arrow);
489 let end = matches!(arrow.end, LineEndKind::Arrow);
490
491 canvas.draw_arrow(&path, dash, (start, end), &arrow.look, &arrow.text);
492}