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, RouteId, 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, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
40pub enum Deletable {
41 Shape(ShapeId),
42 Route(RouteId),
43 Shapes(Vec<ShapeId>),
44 Pins(Vec<PinId>),
45}
46
47impl Deletable {
48 pub fn shapes(&self) -> Option<Vec<ShapeId>> {
52 match self {
53 Deletable::Shape(id) => Some(vec![*id]),
54 Deletable::Shapes(ids) => Some(ids.clone()),
55 Deletable::Route(_) | Deletable::Pins(_) => None,
56 }
57 }
58
59 pub fn count(&self) -> usize {
63 match self {
64 Deletable::Shape(_) | Deletable::Route(_) => 1,
65 Deletable::Shapes(ids) => ids.len(),
66 Deletable::Pins(pins) => pins.len(),
67 }
68 }
69}
70
71#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
74pub enum RoleTarget {
75 Block(BlockId),
76 Port(PinId),
77 Route(RouteId),
78 Area(AreaId),
79 Text(TextId),
80}
81
82#[derive(
87 Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, serde::Serialize, serde::Deserialize,
88)]
89pub enum ShapeId {
90 Rect(BlockId),
91 Port(PinId),
92 Text(TextId),
93 Area(AreaId),
94 Image(ImageId),
95 Icon(BlockId),
97}
98
99impl ShapeId {
100 pub fn block(self) -> Option<BlockId> {
104 match self {
105 Self::Rect(id) => Some(id),
106 Self::Port(_) | Self::Text(_) | Self::Area(_) | Self::Image(_) | Self::Icon(_) => None,
107 }
108 }
109
110 pub fn is_block(self) -> bool {
111 self.block().is_some()
112 }
113
114 pub fn affects_routing(self) -> bool {
119 matches!(self, Self::Rect(_) | Self::Port(_))
120 }
121
122 pub fn is_artwork(self) -> bool {
127 matches!(self, Self::Image(_) | Self::Icon(_))
128 }
129}
130
131#[derive(Copy, Clone, Debug)]
132pub struct PinLocation {
133 pub side: PinSide,
134 pub offset: f32,
135}
136
137impl From<(PinSide, f32)> for PinLocation {
138 fn from(value: (PinSide, f32)) -> Self {
139 PinLocation {
140 side: value.0,
141 offset: value.1,
142 }
143 }
144}
145
146#[derive(Clone, Copy, Debug, Default, PartialEq)]
154pub struct ShapeLabel<'a> {
155 pub name: &'a str,
156 pub hidden: bool,
157 pub side: LabelSide,
158 pub offset: f32,
159}
160
161pub trait BaseShape {
166 fn title(&self) -> Option<ShapeLabel<'_>> {
167 None
168 }
169 fn type_label(&self) -> Option<ShapeLabel<'_>> {
170 None
171 }
172 fn gui_rect(&self) -> Rect;
173 fn constrain_resize_delta(&self, delta: Vec2) -> Vec2 {
174 delta
175 }
176 fn pin(&self, id: PinId) -> Option<&Pin> {
177 let _ = id;
178 None
179 }
180 fn anchor_point_with_rect(&self, rect: Rect, id: PinId) -> Option<Pos2> {
181 let _ = (rect, id);
182 None
183 }
184 fn pin_position(&self, location: PinLocation) -> Option<Pos2> {
185 let _ = location;
186 None
187 }
188 fn pin_drop_candidate(&self, pin_id: PinId, side: PinSide, raw_offset_px: f32) -> Option<u32> {
193 let _ = (pin_id, side, raw_offset_px);
194 None
195 }
196 fn pin_text_rect<R: Renderer>(&self, id: PinId, painter: &Style<'_, R>) -> Option<Rect> {
197 let _ = (id, painter);
198 None
199 }
200 fn pin_type_rect<R: Renderer>(&self, id: PinId, painter: &Style<'_, R>) -> Option<Rect> {
204 let _ = (id, painter);
205 None
206 }
207 fn tag_text_rect_for<R: Renderer>(
211 &self,
212 id: PinId,
213 text: &str,
214 painter: &Style<'_, R>,
215 ) -> Option<Rect> {
216 let _ = (id, text, painter);
217 None
218 }
219 fn pin_stub_rect(&self, id: PinId) -> Option<Rect> {
221 let _ = id;
222 None
223 }
224 fn render_ng<R: Renderer>(&self, mode: RenderMode, painter: &mut Style<'_, R>) {
227 let _ = (mode, painter);
228 }
229 fn new_pin_locations(&self) -> Vec<PinLocation> {
230 Vec::new()
231 }
232 fn title_anchor(&self) -> Option<Pos2> {
233 None
234 }
235 fn type_anchor(&self) -> Option<Pos2> {
236 None
237 }
238 fn resizable(&self) -> bool {
239 false
240 }
241}
242
243pub enum ShapeRef<'a> {
249 Block(BlockShape<'a>),
250 Port(PortShape<'a>),
251 Text(TextShape<'a>),
252 Area(&'a Area),
253 Image(Artwork<'a>),
254}
255
256impl<'a> ShapeRef<'a> {
257 pub fn text(text: &'a Text, extent: Option<GridSize>) -> Self {
260 Self::Text(TextShape { text, extent })
261 }
262
263 pub fn artwork(rect: Rect, asset: Option<&'a Asset>) -> Self {
267 Self::Image(Artwork { rect, asset })
268 }
269
270 pub fn gui_rect(&self) -> Rect {
271 match self {
272 Self::Block(b) => b.gui_rect(),
273 Self::Port(p) => p.gui_rect(),
274 Self::Text(t) => t.gui_rect(),
275 Self::Area(c) => c.gui_rect(),
276 Self::Image(s) => s.gui_rect(),
277 }
278 }
279 pub fn render_ng<R: Renderer>(
283 &self,
284 accents: crate::presentation::ShapeAccents<'_>,
285 mode: RenderMode,
286 painter: &mut Style<'_, R>,
287 ) {
288 match self {
289 Self::Block(b) => b.render_ng(mode, painter),
290 Self::Port(p) => p.render_ng(accents, mode, painter),
291 Self::Text(t) => t.render_ng(mode, painter),
292 Self::Area(c) => c.render_ng(mode, painter),
293 Self::Image(s) => s.render_ng(mode, painter),
294 }
295 }
296 pub fn render_pins_ng<R: Renderer>(
300 &self,
301 accents: crate::presentation::ShapeAccents<'_>,
302 mode: RenderMode,
303 painter: &mut Style<'_, R>,
304 ) {
305 match self {
306 Self::Block(b) => b.render_pins_ng(accents, mode, painter),
307 Self::Port(_) | Self::Text(_) | Self::Area(_) | Self::Image(_) => {}
308 }
309 }
310 pub fn with_pins(&self, mut f: impl FnMut(PinId, &'a Pin)) {
311 match self {
312 Self::Block(b) => b.pins.iter().for_each(|&(id, pin)| f(id, pin)),
313 Self::Port(p) => f(p.id, p.pin),
314 Self::Text(_) | Self::Area(_) | Self::Image(_) => {}
316 }
317 }
318 pub fn pin(&self, id: PinId) -> Option<&Pin> {
319 match self {
320 Self::Block(b) => b.pin(id),
321 Self::Port(p) => p.pin(id),
322 Self::Text(t) => t.pin(id),
323 Self::Area(c) => c.pin(id),
324 Self::Image(s) => s.pin(id),
325 }
326 }
327 pub fn pin_text_rect<R: Renderer>(&self, id: PinId, painter: &Style<'_, R>) -> Option<Rect> {
328 match self {
329 Self::Block(b) => b.pin_text_rect(id, painter),
330 Self::Port(p) => p.pin_text_rect(id, painter),
331 Self::Text(t) => t.pin_text_rect(id, painter),
332 Self::Area(c) => c.pin_text_rect(id, painter),
333 Self::Image(s) => s.pin_text_rect(id, painter),
334 }
335 }
336 pub fn pin_type_rect<R: Renderer>(&self, id: PinId, painter: &Style<'_, R>) -> Option<Rect> {
337 match self {
338 Self::Block(b) => b.pin_type_rect(id, painter),
339 Self::Port(p) => p.pin_type_rect(id, painter),
340 Self::Text(t) => t.pin_type_rect(id, painter),
341 Self::Area(c) => c.pin_type_rect(id, painter),
342 Self::Image(s) => s.pin_type_rect(id, painter),
343 }
344 }
345 pub fn tag_text_rect_for<R: Renderer>(
346 &self,
347 id: PinId,
348 text: &str,
349 painter: &Style<'_, R>,
350 ) -> Option<Rect> {
351 match self {
352 Self::Block(b) => b.tag_text_rect_for(id, text, painter),
353 Self::Port(p) => p.tag_text_rect_for(id, text, painter),
354 Self::Text(t) => t.tag_text_rect_for(id, text, painter),
355 Self::Area(c) => c.tag_text_rect_for(id, text, painter),
356 Self::Image(s) => s.tag_text_rect_for(id, text, painter),
357 }
358 }
359 pub fn pin_stub_rect(&self, id: PinId) -> Option<Rect> {
360 match self {
361 Self::Block(b) => b.pin_stub_rect(id),
362 Self::Port(p) => p.pin_stub_rect(id),
363 Self::Text(t) => t.pin_stub_rect(id),
364 Self::Area(c) => c.pin_stub_rect(id),
365 Self::Image(s) => s.pin_stub_rect(id),
366 }
367 }
368 pub fn anchor_point_with_rect(&self, rect: Rect, id: PinId) -> Option<Pos2> {
369 match self {
370 Self::Block(b) => b.anchor_point_with_rect(rect, id),
371 Self::Port(p) => p.anchor_point_with_rect(rect, id),
372 Self::Text(t) => t.anchor_point_with_rect(rect, id),
373 Self::Area(c) => c.anchor_point_with_rect(rect, id),
374 Self::Image(s) => s.anchor_point_with_rect(rect, id),
375 }
376 }
377 pub fn pin_drop_candidate(
378 &self,
379 pin_id: PinId,
380 side: PinSide,
381 raw_offset_px: f32,
382 ) -> Option<u32> {
383 match self {
384 Self::Block(b) => b.pin_drop_candidate(pin_id, side, raw_offset_px),
385 Self::Port(p) => p.pin_drop_candidate(pin_id, side, raw_offset_px),
386 Self::Text(t) => t.pin_drop_candidate(pin_id, side, raw_offset_px),
387 Self::Area(c) => c.pin_drop_candidate(pin_id, side, raw_offset_px),
388 Self::Image(s) => s.pin_drop_candidate(pin_id, side, raw_offset_px),
389 }
390 }
391 pub fn title(&self) -> Option<ShapeLabel<'_>> {
392 match self {
393 Self::Block(b) => b.title(),
394 Self::Port(p) => p.title(),
395 Self::Text(t) => t.title(),
396 Self::Area(c) => c.title(),
397 Self::Image(s) => s.title(),
398 }
399 }
400 pub fn type_label(&self) -> Option<ShapeLabel<'_>> {
401 match self {
402 Self::Block(b) => b.type_label(),
403 Self::Port(p) => p.type_label(),
404 Self::Text(t) => t.type_label(),
405 Self::Area(c) => c.type_label(),
406 Self::Image(s) => s.type_label(),
407 }
408 }
409 pub fn title_anchor(&self) -> Option<Pos2> {
410 match self {
411 Self::Block(b) => b.title_anchor(),
412 Self::Port(p) => p.title_anchor(),
413 Self::Text(t) => t.title_anchor(),
414 Self::Area(c) => c.title_anchor(),
415 Self::Image(s) => s.title_anchor(),
416 }
417 }
418 pub fn resizable(&self) -> bool {
419 match self {
420 Self::Block(b) => b.resizable(),
421 Self::Port(p) => p.resizable(),
422 Self::Text(t) => t.resizable(),
423 Self::Area(c) => c.resizable(),
424 Self::Image(s) => s.resizable(),
425 }
426 }
427 pub fn constrain_resize_delta(&self, delta: Vec2) -> Vec2 {
428 match self {
429 Self::Block(b) => b.constrain_resize_delta(delta),
430 Self::Port(p) => p.constrain_resize_delta(delta),
431 Self::Text(t) => t.constrain_resize_delta(delta),
432 Self::Area(c) => c.constrain_resize_delta(delta),
433 Self::Image(s) => s.constrain_resize_delta(delta),
434 }
435 }
436}