1use blockworx_doc::{
9 block_model::{Asset, Label},
10 geometry::{GridRect, GridSize},
11 values::Role,
12};
13use blockworx_geom::Pos2;
14
15use crate::grid::{GRID_SIZE, grid_rect, max_pin_slot, snap_block_height_cells};
16use crate::shape::ShapeLabel;
17
18pub fn slot_capacity(height: u32) -> u32 {
21 max_pin_slot(height as f32 * GRID_SIZE)
22}
23
24pub fn block_rect(start: Pos2, end: Pos2) -> GridRect {
29 let rect = grid_rect(start, end);
30 GridRect {
31 size: GridSize {
32 h: snap_block_height_cells(rect.size.h),
33 ..rect.size
34 },
35 ..rect
36 }
37}
38
39pub fn role_from_accent(accent: Option<u8>) -> Role {
44 match accent {
45 None => Role::Accent0,
46 Some(0) => Role::Accent1,
47 Some(1) => Role::Accent2,
48 Some(2) => Role::Accent3,
49 Some(3) => Role::Accent4,
50 Some(4) => Role::Accent5,
51 Some(5) => Role::Accent6,
52 Some(6) => Role::Accent7,
53 Some(_) => Role::Accent8,
54 }
55}
56
57pub fn shape_label(label: &Label) -> ShapeLabel<'_> {
63 ShapeLabel {
64 name: &label.name,
65 hidden: label.hidden,
66 side: label.side,
67 offset: (label.offset).into(),
68 }
69}
70
71pub fn asset_within_limit(asset: &Asset, name: &str) -> bool {
76 if asset.within_limit() {
77 return true;
78 }
79 tracing::error!(
80 "{name} is {} bytes; artwork is limited to {} bytes because it rides the commit log",
81 asset.bytes().len(),
82 blockworx_doc::block_model::ASSET_LIMIT,
83 );
84 false
85}
86
87pub fn accent_from_role(role: Role) -> Option<u8> {
90 match role {
91 Role::Accent0 => None,
92 Role::Accent1 => Some(0),
93 Role::Accent2 => Some(1),
94 Role::Accent3 => Some(2),
95 Role::Accent4 => Some(3),
96 Role::Accent5 => Some(4),
97 Role::Accent6 => Some(5),
98 Role::Accent7 => Some(6),
99 Role::Accent8 => Some(7),
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use blockworx_doc::geometry::GridPoint;
106 use blockworx_geom::pos2;
107
108 use super::*;
109 use crate::grid::{GRID_SIZE, max_pin_slot};
110
111 #[test]
112 fn accent_and_role_are_an_involution_over_the_picker_range() {
113 for accent in std::iter::once(None).chain((0..=7).map(Some)) {
114 assert_eq!(accent_from_role(role_from_accent(accent)), accent);
115 }
116 }
117
118 #[test]
119 fn a_block_rect_settles_on_the_height_ladder_keeping_its_top_edge() {
120 let cell = |x: f32, y: f32| pos2(x * GRID_SIZE, y * GRID_SIZE);
121 let rect = block_rect(cell(1.0, 2.0), cell(9.0, 7.0));
122 assert_eq!(
123 grid_rect(cell(1.0, 2.0), cell(9.0, 7.0)).size.h,
124 5,
125 "precondition: the drag's own height is off the ladder"
126 );
127 assert_eq!(rect.top_left, GridPoint { x: 1, y: 2 });
128 assert_eq!(rect.size, GridSize { w: 8, h: 4 });
129
130 for (drag, ladder) in [(1, 4), (5, 4), (6, 7), (9, 10)] {
132 let rect = block_rect(cell(0.0, 0.0), cell(3.0, drag as f32));
133 assert_eq!(rect.size.h, ladder);
134 assert_eq!(
135 max_pin_slot(ladder as f32 * GRID_SIZE),
136 (ladder - 4) / 3,
137 "a valid height's capacity is a whole number of slots"
138 );
139 }
140 }
141}