1use crate::theme::Style;
13use blockworx_geom::{Pos2, Rect, Vec2};
14use blockworx_paint::Renderer;
15
16use blockworx_doc::{
17 block_model::{Area, Asset, Pin, Text},
18 geometry::GridSize,
19 id::{AreaId, BlockId, ImageId, PinId, TextId},
20 values::LabelSide,
21};
22
23use crate::{shape::pin::PinSide, state::RenderMode};
24
25pub mod area;
26pub mod block;
27pub mod image;
28pub mod pin;
29pub mod port;
30pub mod text_box;
31
32pub use block::BlockShape;
33pub use image::Artwork;
34pub use port::PortShape;
35pub use text_box::TextShape;
36
37#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
42pub enum ShapeId {
43 Rect(BlockId),
44 Port(PinId),
45 Text(TextId),
46 Area(AreaId),
47 Image(ImageId),
48 Icon(BlockId),
50}
51
52impl ShapeId {
53 pub fn block(self) -> Option<BlockId> {
57 match self {
58 Self::Rect(id) => Some(id),
59 Self::Port(_) | Self::Text(_) | Self::Area(_) | Self::Image(_) | Self::Icon(_) => None,
60 }
61 }
62
63 pub fn is_block(self) -> bool {
64 self.block().is_some()
65 }
66
67 pub fn affects_routing(self) -> bool {
72 matches!(self, Self::Rect(_) | Self::Port(_))
73 }
74
75 pub fn is_artwork(self) -> bool {
80 matches!(self, Self::Image(_) | Self::Icon(_))
81 }
82}
83
84#[derive(Copy, Clone, Debug)]
85pub struct PinLocation {
86 pub side: PinSide,
87 pub offset: f32,
88}
89
90impl From<(PinSide, f32)> for PinLocation {
91 fn from(value: (PinSide, f32)) -> Self {
92 PinLocation {
93 side: value.0,
94 offset: value.1,
95 }
96 }
97}
98
99#[derive(Clone, Copy, Debug, Default, PartialEq)]
107pub struct ShapeLabel<'a> {
108 pub name: &'a str,
109 pub hidden: bool,
110 pub side: LabelSide,
111 pub offset: f32,
112}
113
114pub trait BaseShape {
119 fn title(&self) -> Option<ShapeLabel<'_>> {
120 None
121 }
122 fn type_label(&self) -> Option<ShapeLabel<'_>> {
123 None
124 }
125 fn gui_rect(&self) -> Rect;
126 fn constrain_resize_delta(&self, delta: Vec2) -> Vec2 {
127 delta
128 }
129 fn pin(&self, id: PinId) -> Option<&Pin> {
130 let _ = id;
131 None
132 }
133 fn anchor_point_with_rect(&self, rect: Rect, id: PinId) -> Option<Pos2> {
134 let _ = (rect, id);
135 None
136 }
137 fn pin_position(&self, location: PinLocation) -> Option<Pos2> {
138 let _ = location;
139 None
140 }
141 fn pin_drop_candidate(&self, pin_id: PinId, side: PinSide, raw_offset_px: f32) -> Option<u32> {
146 let _ = (pin_id, side, raw_offset_px);
147 None
148 }
149 fn pin_text_rect<R: Renderer>(&self, id: PinId, painter: &Style<'_, R>) -> Option<Rect> {
150 let _ = (id, painter);
151 None
152 }
153 fn pin_type_rect<R: Renderer>(&self, id: PinId, painter: &Style<'_, R>) -> Option<Rect> {
157 let _ = (id, painter);
158 None
159 }
160 fn tag_text_rect_for<R: Renderer>(
164 &self,
165 id: PinId,
166 text: &str,
167 painter: &Style<'_, R>,
168 ) -> Option<Rect> {
169 let _ = (id, text, painter);
170 None
171 }
172 fn pin_stub_rect(&self, id: PinId) -> Option<Rect> {
174 let _ = id;
175 None
176 }
177 fn render_ng<R: Renderer>(&self, mode: RenderMode, painter: &mut Style<'_, R>) {
180 let _ = (mode, painter);
181 }
182 fn new_pin_locations(&self) -> Vec<PinLocation> {
183 Vec::new()
184 }
185 fn title_anchor(&self) -> Option<Pos2> {
186 None
187 }
188 fn type_anchor(&self) -> Option<Pos2> {
189 None
190 }
191 fn resizable(&self) -> bool {
192 false
193 }
194}
195
196pub enum ShapeRef<'a> {
202 Block(BlockShape<'a>),
203 Port(PortShape<'a>),
204 Text(TextShape<'a>),
205 Area(&'a Area),
206 Image(Artwork<'a>),
207}
208
209impl<'a> ShapeRef<'a> {
210 pub fn text(text: &'a Text, extent: Option<GridSize>) -> Self {
213 Self::Text(TextShape { text, extent })
214 }
215
216 pub fn artwork(rect: Rect, asset: Option<&'a Asset>) -> Self {
220 Self::Image(Artwork { rect, asset })
221 }
222
223 pub fn gui_rect(&self) -> Rect {
224 match self {
225 Self::Block(b) => b.gui_rect(),
226 Self::Port(p) => p.gui_rect(),
227 Self::Text(t) => t.gui_rect(),
228 Self::Area(c) => c.gui_rect(),
229 Self::Image(s) => s.gui_rect(),
230 }
231 }
232 pub fn render_ng<R: Renderer>(
236 &self,
237 accents: crate::presentation::ShapeAccents<'_>,
238 mode: RenderMode,
239 painter: &mut Style<'_, R>,
240 ) {
241 match self {
242 Self::Block(b) => b.render_ng(mode, painter),
243 Self::Port(p) => p.render_ng(accents, mode, painter),
244 Self::Text(t) => t.render_ng(mode, painter),
245 Self::Area(c) => c.render_ng(mode, painter),
246 Self::Image(s) => s.render_ng(mode, painter),
247 }
248 }
249 pub fn render_pins_ng<R: Renderer>(
253 &self,
254 accents: crate::presentation::ShapeAccents<'_>,
255 mode: RenderMode,
256 painter: &mut Style<'_, R>,
257 ) {
258 match self {
259 Self::Block(b) => b.render_pins_ng(accents, mode, painter),
260 Self::Port(_) | Self::Text(_) | Self::Area(_) | Self::Image(_) => {}
261 }
262 }
263 pub fn with_pins(&self, mut f: impl FnMut(PinId, &'a Pin)) {
264 match self {
265 Self::Block(b) => b.pins.iter().for_each(|&(id, pin)| f(id, pin)),
266 Self::Port(p) => f(p.id, p.pin),
267 Self::Text(_) | Self::Area(_) | Self::Image(_) => {}
269 }
270 }
271 pub fn pin(&self, id: PinId) -> Option<&Pin> {
272 match self {
273 Self::Block(b) => b.pin(id),
274 Self::Port(p) => p.pin(id),
275 Self::Text(t) => t.pin(id),
276 Self::Area(c) => c.pin(id),
277 Self::Image(s) => s.pin(id),
278 }
279 }
280 pub fn pin_text_rect<R: Renderer>(&self, id: PinId, painter: &Style<'_, R>) -> Option<Rect> {
281 match self {
282 Self::Block(b) => b.pin_text_rect(id, painter),
283 Self::Port(p) => p.pin_text_rect(id, painter),
284 Self::Text(t) => t.pin_text_rect(id, painter),
285 Self::Area(c) => c.pin_text_rect(id, painter),
286 Self::Image(s) => s.pin_text_rect(id, painter),
287 }
288 }
289 pub fn pin_type_rect<R: Renderer>(&self, id: PinId, painter: &Style<'_, R>) -> Option<Rect> {
290 match self {
291 Self::Block(b) => b.pin_type_rect(id, painter),
292 Self::Port(p) => p.pin_type_rect(id, painter),
293 Self::Text(t) => t.pin_type_rect(id, painter),
294 Self::Area(c) => c.pin_type_rect(id, painter),
295 Self::Image(s) => s.pin_type_rect(id, painter),
296 }
297 }
298 pub fn tag_text_rect_for<R: Renderer>(
299 &self,
300 id: PinId,
301 text: &str,
302 painter: &Style<'_, R>,
303 ) -> Option<Rect> {
304 match self {
305 Self::Block(b) => b.tag_text_rect_for(id, text, painter),
306 Self::Port(p) => p.tag_text_rect_for(id, text, painter),
307 Self::Text(t) => t.tag_text_rect_for(id, text, painter),
308 Self::Area(c) => c.tag_text_rect_for(id, text, painter),
309 Self::Image(s) => s.tag_text_rect_for(id, text, painter),
310 }
311 }
312 pub fn pin_stub_rect(&self, id: PinId) -> Option<Rect> {
313 match self {
314 Self::Block(b) => b.pin_stub_rect(id),
315 Self::Port(p) => p.pin_stub_rect(id),
316 Self::Text(t) => t.pin_stub_rect(id),
317 Self::Area(c) => c.pin_stub_rect(id),
318 Self::Image(s) => s.pin_stub_rect(id),
319 }
320 }
321 pub fn anchor_point_with_rect(&self, rect: Rect, id: PinId) -> Option<Pos2> {
322 match self {
323 Self::Block(b) => b.anchor_point_with_rect(rect, id),
324 Self::Port(p) => p.anchor_point_with_rect(rect, id),
325 Self::Text(t) => t.anchor_point_with_rect(rect, id),
326 Self::Area(c) => c.anchor_point_with_rect(rect, id),
327 Self::Image(s) => s.anchor_point_with_rect(rect, id),
328 }
329 }
330 pub fn pin_drop_candidate(
331 &self,
332 pin_id: PinId,
333 side: PinSide,
334 raw_offset_px: f32,
335 ) -> Option<u32> {
336 match self {
337 Self::Block(b) => b.pin_drop_candidate(pin_id, side, raw_offset_px),
338 Self::Port(p) => p.pin_drop_candidate(pin_id, side, raw_offset_px),
339 Self::Text(t) => t.pin_drop_candidate(pin_id, side, raw_offset_px),
340 Self::Area(c) => c.pin_drop_candidate(pin_id, side, raw_offset_px),
341 Self::Image(s) => s.pin_drop_candidate(pin_id, side, raw_offset_px),
342 }
343 }
344 pub fn title(&self) -> Option<ShapeLabel<'_>> {
345 match self {
346 Self::Block(b) => b.title(),
347 Self::Port(p) => p.title(),
348 Self::Text(t) => t.title(),
349 Self::Area(c) => c.title(),
350 Self::Image(s) => s.title(),
351 }
352 }
353 pub fn type_label(&self) -> Option<ShapeLabel<'_>> {
354 match self {
355 Self::Block(b) => b.type_label(),
356 Self::Port(p) => p.type_label(),
357 Self::Text(t) => t.type_label(),
358 Self::Area(c) => c.type_label(),
359 Self::Image(s) => s.type_label(),
360 }
361 }
362 pub fn title_anchor(&self) -> Option<Pos2> {
363 match self {
364 Self::Block(b) => b.title_anchor(),
365 Self::Port(p) => p.title_anchor(),
366 Self::Text(t) => t.title_anchor(),
367 Self::Area(c) => c.title_anchor(),
368 Self::Image(s) => s.title_anchor(),
369 }
370 }
371 pub fn resizable(&self) -> bool {
372 match self {
373 Self::Block(b) => b.resizable(),
374 Self::Port(p) => p.resizable(),
375 Self::Text(t) => t.resizable(),
376 Self::Area(c) => c.resizable(),
377 Self::Image(s) => s.resizable(),
378 }
379 }
380 pub fn constrain_resize_delta(&self, delta: Vec2) -> Vec2 {
381 match self {
382 Self::Block(b) => b.constrain_resize_delta(delta),
383 Self::Port(p) => p.constrain_resize_delta(delta),
384 Self::Text(t) => t.constrain_resize_delta(delta),
385 Self::Area(c) => c.constrain_resize_delta(delta),
386 Self::Image(s) => s.constrain_resize_delta(delta),
387 }
388 }
389}