1use crate::{
2 edit::naming::TagVisibility,
3 grid::{GRID_SIZE, PORT_RENDER_HEIGHT},
4 shape::pin::{PinDir, PinSide, flipped_dir},
5 theme::{Role, RoleStroke, Style},
6};
7use blockworx_geom::{Align, Pos2, Rect, pos2, vec2};
8use blockworx_paint::Renderer;
9
10use super::{draw_pin_labels, draw_pin_stub, pin_tag_location, port_text_center};
11
12fn rounded_pentagon(
13 vertices: &[Pos2],
14 radius: f32,
15 fill: Role,
16 stroke: RoleStroke,
17 painter: &mut Style<'_, impl Renderer>,
18) {
19 let n = vertices.len();
20 let mut points = Vec::with_capacity(n * 5);
21 for i in 0..n {
22 let prev = vertices[(i + n - 1) % n];
23 let curr = vertices[i];
24 let next = vertices[(i + 1) % n];
25 let dir_in = (curr - prev).normalized();
26 let dir_out = (next - curr).normalized();
27 let r = radius
28 .min((curr - prev).length() * 0.5)
29 .min((next - curr).length() * 0.5);
30 let p0 = curr - dir_in * r;
31 let p2 = curr + dir_out * r;
32 points.push(p0);
33 for s in 1..=4_u8 {
34 let t = s as f32 / 4.0;
35 let it = 1.0 - t;
36 points.push(pos2(
37 it * it * p0.x + 2.0 * it * t * curr.x + t * t * p2.x,
38 it * it * p0.y + 2.0 * it * t * curr.y + t * t * p2.y,
39 ));
40 }
41 }
42 painter.add_convex_polygon(points, fill, stroke);
43}
44
45pub fn draw_port_outline(
47 bbox: Rect,
48 side: PinSide,
49 fill: Role,
50 stroke: impl Into<RoleStroke>,
51 painter: &mut Style<'_, impl Renderer>,
52) {
53 let render_rect = Rect::from_center_size(bbox.center(), vec2(bbox.width(), PORT_RENDER_HEIGHT));
54 let center_y = render_rect.center().y;
55 let vertices = match side {
56 PinSide::East => [
57 render_rect.left_top(),
58 render_rect.right_top() - vec2(GRID_SIZE, 0.0),
59 pos2(render_rect.right(), center_y),
60 render_rect.right_bottom() - vec2(GRID_SIZE, 0.0),
61 render_rect.left_bottom(),
62 ],
63 PinSide::West => [
64 render_rect.right_top(),
65 render_rect.left_top() + vec2(GRID_SIZE, 0.0),
66 pos2(render_rect.left(), center_y),
67 render_rect.left_bottom() + vec2(GRID_SIZE, 0.0),
68 render_rect.right_bottom(),
69 ],
70 };
71 rounded_pentagon(&vertices, 3.0, fill, stroke.into(), painter);
72}
73
74#[allow(clippy::too_many_arguments)]
75pub fn draw_port(
76 bbox: Rect,
77 side: PinSide,
78 text: &str,
79 type_label: &str,
80 tag: &str,
81 tag_visibility: TagVisibility,
82 kind: PinDir,
83 fill: Role,
84 stroke: impl Into<RoleStroke>,
85 painter: &mut Style<'_, impl Renderer>,
86 pin_role: Role,
87) {
88 draw_port_outline(bbox, side, fill, stroke, painter);
89 let center_y = bbox.center().y;
90 let (stub_a, stub_b) = match side {
91 PinSide::East => (
92 pos2(bbox.right(), center_y),
93 pos2(bbox.right() + GRID_SIZE, center_y),
94 ),
95 PinSide::West => (
96 pos2(bbox.left(), center_y),
97 pos2(bbox.left() - GRID_SIZE, center_y),
98 ),
99 };
100 draw_pin_stub(stub_a, stub_b, flipped_dir(kind), (1.7, pin_role), painter);
102 draw_pin_labels(
105 port_text_center(bbox, side),
106 Align::Center,
107 text,
108 type_label,
109 painter,
110 );
111 if !tag_visibility.is_hidden() && !tag.is_empty() {
112 let (tag_pos, tag_align) = pin_tag_location(bbox.left(), bbox.right(), side, center_y);
113 painter.text(
114 tag_pos,
115 tag_align,
116 tag,
117 &painter.theme().tag_font,
118 Role::PinTag,
119 );
120 }
121}