makepad_widgets/
rotated_image.rs

1use crate::{image_cache::*, makepad_draw::*, widget::*};
2use crate::makepad_derive_widget::*;
3
4live_design! {
5    link widgets
6    use link::shaders::*;
7    
8    pub RotatedImageBase = {{RotatedImage}} {}
9    pub RotatedImage = <RotatedImageBase> {
10        width: Fit
11        height: Fit
12                
13        draw_bg: {
14            texture image: texture2d
15                        
16            instance rotation: 0.0
17            instance opacity: 1.0
18            instance scale: 1.0
19                        
20            fn rotation_vertex_expansion(rotation: float, w: float, h: float) -> vec2 {
21                let horizontal_expansion = (abs(cos(rotation)) * w + abs(sin(rotation)) * h) / w - 1.0;
22                let vertical_expansion = (abs(sin(rotation)) * w + abs(cos(rotation)) * h) / h - 1.0;
23                                
24                return vec2(horizontal_expansion, vertical_expansion);
25            }
26                        
27            fn rotate_2d_from_center(coord: vec2, a: float, size: vec2) -> vec2 {
28                let cos_a = cos(-a);
29                let sin_a = sin(-a);
30                                
31                let centered_coord = coord - vec2(0.5, 0.5);
32                                
33                // Denormalize the coordinates to use original proportions (between height and width)
34                let denorm_coord = vec2(centered_coord.x, centered_coord.y * size.y / size.x);
35                let demorm_rotated = vec2(denorm_coord.x * cos_a - denorm_coord.y * sin_a, denorm_coord.x * sin_a + denorm_coord.y * cos_a);
36                                
37                // Restore the coordinates to use the texture coordinates proportions (between 0 and 1 in both axis)
38                let rotated = vec2(demorm_rotated.x, demorm_rotated.y * size.x / size.y);
39                                
40                return rotated + vec2(0.5, 0.5);
41            }
42                        
43            fn get_color(self) -> vec4 {
44                let rot_padding = rotation_vertex_expansion(self.rotation, self.rect_size.x, self.rect_size.y) / 2.0;
45                                
46                // Current position is a traslated one, so let's get the original position
47                let current_pos = self.pos.xy - rot_padding;
48                let original_pos = rotate_2d_from_center(current_pos, self.rotation, self.rect_size);
49                                
50                // Scale the current position by the scale factor
51                let scaled_pos = original_pos / self.scale;
52                                
53                // Take pixel color from the original image
54                let color = sample2d(self.image, scaled_pos).xyzw;
55                                
56                let faded_color = color * vec4(1.0, 1.0, 1.0, self.opacity);
57                return faded_color;
58            }
59                        
60            fn pixel(self) -> vec4 {
61                let rot_expansion = rotation_vertex_expansion(self.rotation, self.rect_size.x, self.rect_size.y);
62                                
63                // Debug
64                // let line_width = 0.01;
65                // if self.pos.x < line_width || self.pos.x > (self.scale + rot_expansion.x - line_width) || self.pos.y < line_width || self.pos.y > (self.scale + rot_expansion.y - line_width) {
66                    //     return #c86;
67                    // }
68                                    
69                    let sdf = Sdf2d::viewport(self.pos * self.rect_size);
70                                    
71                    let translation_offset = vec2(self.rect_size.x * rot_expansion.x / 2.0, self.rect_size.y * self.scale * rot_expansion.y / 2.0);
72                    sdf.translate(translation_offset.x, translation_offset.y);
73                                    
74                    let center = self.rect_size * 0.5;
75                    sdf.rotate(self.rotation, center.x, center.y);
76                                    
77                    let scaled_size = self.rect_size * self.scale;
78                    sdf.box(0.0, 0.0, scaled_size.x, scaled_size.y, 1);
79                                    
80                    sdf.fill_premul(Pal::premul(self.get_color()));
81                    return sdf.result
82                }
83                            
84                fn vertex(self) -> vec4 {
85                    let rot_expansion = rotation_vertex_expansion(self.rotation, self.rect_size.x, self.rect_size.y);
86                    let adjusted_pos = vec2(
87                        self.rect_pos.x - self.rect_size.x * rot_expansion.x / 2.0,
88                        self.rect_pos.y - self.rect_size.y * rot_expansion.y / 2.0
89                    );
90                                    
91                    let expanded_size = vec2(self.rect_size.x * (self.scale + rot_expansion.x), self.rect_size.y * (self.scale + rot_expansion.y));
92                    let clipped: vec2 = clamp(
93                        self.geom_pos * expanded_size + adjusted_pos,
94                        self.draw_clip.xy,
95                        self.draw_clip.zw
96                    );
97                                    
98                    self.pos = (clipped - adjusted_pos) / self.rect_size;
99                    return self.camera_projection * (self.camera_view * (
100                        self.view_transform * vec4(clipped.x, clipped.y, self.draw_depth + self.draw_zbias, 1.)
101                    ));
102                }
103                            
104                shape: Solid,
105                fill: Image
106            }
107        }
108        
109}
110
111#[derive(Live, Widget)]
112pub struct RotatedImage {
113    #[walk] walk: Walk,
114    #[layout] layout: Layout,
115    #[redraw] #[live] draw_bg: DrawColor,
116
117    #[live] source: LiveDependency,
118    #[rust(Texture::new(cx))] texture: Option<Texture>,
119    #[live] scale: f64,
120}
121
122impl ImageCacheImpl for RotatedImage {
123    fn get_texture(&self, _id:usize) -> &Option<Texture> {
124        &self.texture
125    }
126
127    fn set_texture(&mut self, texture: Option<Texture>, _id:usize) {
128        self.texture = texture;
129    }
130}
131
132impl LiveHook for RotatedImage {
133
134    fn after_apply(&mut self, cx: &mut Cx, _applyl: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
135        self.lazy_create_image_cache(cx);
136        let source = self.source.clone();
137        if source.as_str().len()>0{
138            let _ = self.load_image_dep_by_path(cx, source.as_str(), 0);
139        }
140    }
141}
142
143impl Widget for RotatedImage {
144    fn draw_walk(&mut self, cx: &mut Cx2d, _scope:&mut Scope, walk: Walk) -> DrawStep {
145        self.draw_walk_rotated_image(cx, walk)
146    }
147}
148
149impl RotatedImage {
150    pub fn draw_walk_rotated_image(&mut self, cx: &mut Cx2d, walk: Walk) -> DrawStep {
151        if let Some(image_texture) = &self.texture {
152            self.draw_bg.draw_vars.set_texture(0, image_texture);
153        }
154        self.draw_bg.draw_walk(cx, walk);
155
156        DrawStep::done()
157    }
158}