1use tessera_ui::{Color, DrawCommand, PxPosition, PxSize};
2
3use super::{ShapeUniforms, ShapeVertex};
4
5#[derive(Debug, Clone)]
7pub enum ShapeCommand {
8 Rect {
10 color: Color,
12 corner_radius: f32,
14 g2_k_value: f32,
17 shadow: Option<ShadowProps>,
19 },
20 OutlinedRect {
22 color: Color,
24 corner_radius: f32,
26 g2_k_value: f32,
29 shadow: Option<ShadowProps>,
31 border_width: f32,
33 },
34 RippleRect {
36 color: Color,
38 corner_radius: f32,
40 g2_k_value: f32,
43 shadow: Option<ShadowProps>,
45 ripple: RippleProps,
47 },
48 RippleOutlinedRect {
50 color: Color,
52 corner_radius: f32,
54 g2_k_value: f32,
57 shadow: Option<ShadowProps>,
59 border_width: f32,
61 ripple: RippleProps,
63 },
64 Ellipse {
66 color: Color,
68 shadow: Option<ShadowProps>,
70 },
71 OutlinedEllipse {
73 color: Color,
75 shadow: Option<ShadowProps>,
77 border_width: f32,
79 },
80}
81
82impl DrawCommand for ShapeCommand {
83 fn barrier(&self) -> Option<tessera_ui::BarrierRequirement> {
84 None
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq)]
91pub struct ShadowProps {
92 pub color: Color,
94 pub offset: [f32; 2],
96 pub smoothness: f32,
98}
99
100impl Default for ShadowProps {
101 fn default() -> Self {
102 Self {
103 color: Color::BLACK.with_alpha(0.25),
104 offset: [0.0, 2.0],
105 smoothness: 4.0,
106 }
107 }
108}
109
110#[derive(Debug, Clone, Copy, PartialEq)]
112pub struct RippleProps {
113 pub center: [f32; 2],
115 pub radius: f32,
117 pub alpha: f32,
119 pub color: Color,
121}
122
123impl Default for RippleProps {
124 fn default() -> Self {
125 Self {
126 center: [0.0, 0.0],
127 radius: 0.0,
128 alpha: 0.0,
129 color: Color::WHITE,
130 }
131 }
132}
133
134pub struct ShapeCommandComputed {
135 pub(crate) vertices: Vec<ShapeVertex>,
136 pub(crate) uniforms: ShapeUniforms,
137}
138
139impl ShapeCommandComputed {
140 pub fn from_command(command: ShapeCommand, size: PxSize, position: PxPosition) -> Self {
141 match command {
142 ShapeCommand::Rect {
143 color,
144 corner_radius,
145 g2_k_value,
146 shadow,
147 } => rect_to_computed_draw_command(
148 size,
149 position,
150 color, corner_radius,
152 g2_k_value,
153 shadow,
154 0.0, 0.0, ),
157 ShapeCommand::OutlinedRect {
158 color,
159 corner_radius,
160 g2_k_value,
161 shadow,
162 border_width,
163 } => rect_to_computed_draw_command(
164 size,
165 position,
166 color, corner_radius,
168 g2_k_value,
169 shadow,
170 border_width,
171 1.0, ),
173 ShapeCommand::RippleRect {
174 color,
175 corner_radius,
176 g2_k_value,
177 shadow,
178 ripple,
179 } => ripple_rect_to_computed_draw_command(
180 size,
181 position,
182 color,
183 corner_radius,
184 g2_k_value,
185 shadow,
186 0.0, 0.0, ripple,
189 ),
190 ShapeCommand::RippleOutlinedRect {
191 color,
192 corner_radius,
193 g2_k_value,
194 shadow,
195 border_width,
196 ripple,
197 } => ripple_rect_to_computed_draw_command(
198 size,
199 position,
200 color,
201 corner_radius,
202 g2_k_value,
203 shadow,
204 border_width,
205 1.0, ripple,
207 ),
208 ShapeCommand::Ellipse { color, shadow } => rect_to_computed_draw_command(
209 size, position, color,
210 -1.0, 0.0, shadow, 0.0, 0.0, ),
214 ShapeCommand::OutlinedEllipse {
215 color,
216 shadow,
217 border_width,
218 } => rect_to_computed_draw_command(
219 size,
220 position,
221 color,
222 -1.0, 0.0,
224 shadow,
225 border_width,
226 1.0, ),
228 }
229 }
230}
231
232fn rect_to_computed_draw_command(
234 size: PxSize,
235 position: PxPosition,
236 primary_color_rgba: Color,
237 corner_radius: f32,
238 g2_k_value: f32,
239 shadow: Option<ShadowProps>,
240 border_width: f32,
241 render_mode: f32,
242) -> ShapeCommandComputed {
243 let width = size.width;
244 let height = size.height;
245
246 let rect_local_pos = [
247 [-0.5, -0.5], [0.5, -0.5], [0.5, 0.5], [-0.5, 0.5], ];
252
253 let vertex_color_placeholder_rgb = [0.0, 0.0, 0.0];
254 let top_left = position.to_f32_arr3();
255 let top_right = [top_left[0] + width.to_f32(), top_left[1], top_left[2]];
256 let bottom_right = [
257 top_left[0] + width.to_f32(),
258 top_left[1] + height.to_f32(),
259 top_left[2],
260 ];
261 let bottom_left = [top_left[0], top_left[1] + height.to_f32(), top_left[2]];
262
263 let vertices = vec![
264 ShapeVertex {
265 position: top_left,
266 color: vertex_color_placeholder_rgb,
267 local_pos: rect_local_pos[0],
268 },
269 ShapeVertex {
270 position: top_right,
271 color: vertex_color_placeholder_rgb,
272 local_pos: rect_local_pos[1],
273 },
274 ShapeVertex {
275 position: bottom_right,
276 color: vertex_color_placeholder_rgb,
277 local_pos: rect_local_pos[2],
278 },
279 ShapeVertex {
280 position: bottom_left,
281 color: vertex_color_placeholder_rgb,
282 local_pos: rect_local_pos[3],
283 },
284 ];
285
286 let (shadow_rgba_color, shadow_offset_vec, shadow_smooth_val) = if let Some(s_props) = shadow {
287 (s_props.color, s_props.offset, s_props.smoothness)
288 } else {
289 (Color::TRANSPARENT, [0.0, 0.0], 0.0)
290 };
291
292 let uniforms = ShapeUniforms {
293 size_cr_border_width: [width.to_f32(), height.to_f32(), corner_radius, border_width].into(),
294 primary_color: primary_color_rgba.to_array().into(),
295 shadow_color: shadow_rgba_color.to_array().into(),
296 render_params: [
297 shadow_offset_vec[0],
298 shadow_offset_vec[1],
299 shadow_smooth_val,
300 render_mode,
301 ]
302 .into(),
303 ripple_params: [0.0, 0.0, 0.0, 0.0].into(),
304 ripple_color: [0.0, 0.0, 0.0, 0.0].into(),
305 g2_k_value,
306 };
307
308 ShapeCommandComputed { vertices, uniforms }
309}
310
311fn ripple_rect_to_computed_draw_command(
313 size: PxSize,
314 position: PxPosition,
315 primary_color_rgba: Color,
316 corner_radius: f32,
317 g2_k_value: f32,
318 shadow: Option<ShadowProps>,
319 border_width: f32,
320 render_mode: f32,
321 ripple: RippleProps,
322) -> ShapeCommandComputed {
323 let width = size.width;
324 let height = size.height;
325
326 let rect_local_pos = [
327 [-0.5, -0.5], [0.5, -0.5], [0.5, 0.5], [-0.5, 0.5], ];
332
333 let vertex_color_placeholder_rgb = [0.0, 0.0, 0.0];
334 let top_left = position.to_f32_arr3();
335 let top_right = [top_left[0] + width.to_f32(), top_left[1], top_left[2]];
336 let bottom_right = [
337 top_left[0] + width.to_f32(),
338 top_left[1] + height.to_f32(),
339 top_left[2],
340 ];
341 let bottom_left = [top_left[0], top_left[1] + height.to_f32(), top_left[2]];
342
343 let vertices = vec![
344 ShapeVertex {
345 position: top_left,
346 color: vertex_color_placeholder_rgb,
347 local_pos: rect_local_pos[0],
348 },
349 ShapeVertex {
350 position: top_right,
351 color: vertex_color_placeholder_rgb,
352 local_pos: rect_local_pos[1],
353 },
354 ShapeVertex {
355 position: bottom_right,
356 color: vertex_color_placeholder_rgb,
357 local_pos: rect_local_pos[2],
358 },
359 ShapeVertex {
360 position: bottom_left,
361 color: vertex_color_placeholder_rgb,
362 local_pos: rect_local_pos[3],
363 },
364 ];
365
366 let (shadow_rgba_color, shadow_offset_vec, shadow_smooth_val) = if let Some(s_props) = shadow {
367 (s_props.color.into(), s_props.offset, s_props.smoothness)
368 } else {
369 ([0.0, 0.0, 0.0, 0.0], [0.0, 0.0], 0.0)
370 };
371
372 let ripple_render_mode = if render_mode == 0.0 { 3.0 } else { 4.0 };
373
374 let uniforms = ShapeUniforms {
375 size_cr_border_width: [width.to_f32(), height.to_f32(), corner_radius, border_width].into(),
376 primary_color: primary_color_rgba.to_array().into(),
377 shadow_color: shadow_rgba_color.into(),
378 render_params: [
379 shadow_offset_vec[0],
380 shadow_offset_vec[1],
381 shadow_smooth_val,
382 ripple_render_mode,
383 ]
384 .into(),
385 ripple_params: [
386 ripple.center[0],
387 ripple.center[1],
388 ripple.radius,
389 ripple.alpha,
390 ]
391 .into(),
392 ripple_color: [ripple.color.r, ripple.color.g, ripple.color.b, 0.0].into(),
393 g2_k_value,
394 };
395
396 ShapeCommandComputed { vertices, uniforms }
397}