Skip to main content

radiantkit_image/
image_node.rs

1use epaint::{
2    pos2, ClippedPrimitive, ClippedShape, Color32, Rect, TessellationOptions, TextureHandle,
3};
4use radiantkit_core::{
5    ColorComponent, RadiantComponent, RadiantComponentProvider, RadiantNode, RadiantTessellatable,
6    RadiantTransformable, ScreenDescriptor, SelectionComponent, TransformComponent,
7};
8use serde::{Deserialize, Serialize};
9use std::{
10    any::{Any, TypeId},
11    fmt::Debug,
12};
13
14#[derive(Serialize, Deserialize, Clone)]
15pub struct RadiantImageNode {
16    pub id: u64,
17    pub transform: TransformComponent,
18    pub selection: SelectionComponent,
19    pub tint: ColorComponent,
20    #[serde(skip)]
21    pub texture_handle: Option<TextureHandle>,
22    #[serde(skip)]
23    pub primitives: Vec<ClippedPrimitive>,
24    #[serde(skip)]
25    pub selection_primitives: Vec<ClippedPrimitive>,
26    #[serde(skip)]
27    pub needs_tessellation: bool,
28    #[serde(skip)]
29    pub bounding_rect: [f32; 4],
30}
31
32impl Debug for RadiantImageNode {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        f.debug_struct("RadiantImageNode")
35            .field("id", &self.id)
36            .field("transform", &self.transform)
37            .field("selection", &self.selection)
38            .field("needs_tessellation", &self.needs_tessellation)
39            .field("bounding_rect", &self.bounding_rect)
40            .finish()
41    }
42}
43
44impl RadiantImageNode {
45    pub fn new(
46        id: u64,
47        position: [f32; 2],
48        scale: [f32; 2],
49        texture_handle: TextureHandle,
50    ) -> Self {
51        let mut transform = TransformComponent::new();
52        transform.set_xy(&position);
53        transform.set_scale(&scale);
54
55        let selection = SelectionComponent::new();
56        let mut tint = ColorComponent::new();
57        tint.set_fill_color(Color32::WHITE);
58
59        Self {
60            id,
61            transform,
62            selection,
63            tint,
64            texture_handle: Some(texture_handle),
65            primitives: Vec::new(),
66            selection_primitives: Vec::new(),
67            needs_tessellation: true,
68            bounding_rect: [0.0, 0.0, 0.0, 0.0],
69        }
70    }
71
72    fn tessellate(&mut self, screen_descriptor: &ScreenDescriptor) {
73        if !self.needs_tessellation {
74            return;
75        }
76        self.needs_tessellation = false;
77
78        let pixels_per_point = screen_descriptor.pixels_per_point;
79        let position = self.transform.get_xy();
80        let scale = self.transform.get_scale();
81
82        let rect = epaint::Rect::from_two_pos(
83            epaint::Pos2::new(
84                position[0] / pixels_per_point,
85                position[1] / pixels_per_point,
86            ),
87            epaint::Pos2::new(
88                (position[0] + scale[0]) / pixels_per_point,
89                (position[1] + scale[1]) / pixels_per_point,
90            ),
91        );
92        let rounding = epaint::Rounding::default();
93
94        let mut mesh = epaint::Mesh::with_texture(self.texture_handle.clone().unwrap().id());
95        let uv = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
96        mesh.add_rect_with_uv(rect, uv, self.tint.fill_color());
97        let shapes = vec![ClippedShape(Rect::EVERYTHING, epaint::Shape::Mesh(mesh))];
98        self.primitives = epaint::tessellator::tessellate_shapes(
99            pixels_per_point,
100            TessellationOptions::default(),
101            [1, 1],
102            vec![],
103            shapes,
104        );
105
106        let color = epaint::Color32::from_rgb(
107            (self.id + 1 >> 0) as u8 & 0xFF,
108            (self.id + 1 >> 8) as u8 & 0xFF,
109            (self.id + 1 >> 16) as u8 & 0xFF,
110        );
111        let rect_shape = epaint::RectShape::filled(rect, rounding, color);
112        let shapes = vec![ClippedShape(
113            Rect::EVERYTHING,
114            epaint::Shape::Rect(rect_shape),
115        )];
116        self.selection_primitives = epaint::tessellator::tessellate_shapes(
117            pixels_per_point,
118            TessellationOptions::default(),
119            [1, 1],
120            vec![],
121            shapes,
122        );
123    }
124}
125
126impl RadiantTessellatable for RadiantImageNode {
127    fn attach(&mut self, screen_descriptor: &ScreenDescriptor) {
128        self.tessellate(screen_descriptor);
129    }
130
131    fn detach(&mut self) {
132        self.primitives.clear();
133        self.selection_primitives.clear();
134    }
135
136    fn set_needs_tessellation(&mut self) {
137        let position = self.transform.get_xy();
138        let scale = self.transform.get_scale();
139
140        let rect = epaint::Rect::from_min_max(
141            epaint::Pos2::new(position[0], position[1]),
142            epaint::Pos2::new(position[0] + scale[0], position[1] + scale[1]),
143        );
144        self.bounding_rect = [
145            rect.left_top().x,
146            rect.left_top().y,
147            rect.right_bottom().x,
148            rect.right_bottom().y,
149        ];
150
151        self.needs_tessellation = true;
152    }
153
154    fn tessellate(
155        &mut self,
156        selection: bool,
157        screen_descriptor: &ScreenDescriptor,
158        _fonts_manager: &epaint::text::Fonts,
159    ) -> Vec<ClippedPrimitive> {
160        self.tessellate(screen_descriptor);
161        if selection {
162            self.selection_primitives.clone()
163        } else {
164            self.primitives.clone()
165        }
166    }
167}
168
169impl RadiantNode for RadiantImageNode {
170    fn get_id(&self) -> u64 {
171        return self.id;
172    }
173
174    fn set_id(&mut self, id: u64) {
175        self.id = id;
176    }
177
178    fn get_bounding_rect(&self) -> [f32; 4] {
179        self.bounding_rect
180    }
181}
182
183impl RadiantComponentProvider for RadiantImageNode {
184    fn get_component<T: RadiantComponent + 'static>(&self) -> Option<&T> {
185        if TypeId::of::<T>() == TypeId::of::<SelectionComponent>() {
186            unsafe { Some(&*(&self.selection as *const dyn Any as *const T)) }
187        } else if TypeId::of::<T>() == TypeId::of::<TransformComponent>() {
188            unsafe { Some(&*(&self.transform as *const dyn Any as *const T)) }
189        } else if TypeId::of::<T>() == TypeId::of::<ColorComponent>() {
190            unsafe { Some(&*(&self.tint as *const dyn Any as *const T)) }
191        } else {
192            None
193        }
194    }
195
196    fn get_component_mut<T: RadiantComponent + 'static>(&mut self) -> Option<&mut T> {
197        if TypeId::of::<T>() == TypeId::of::<SelectionComponent>() {
198            unsafe { Some(&mut *(&mut self.selection as *mut dyn Any as *mut T)) }
199        } else if TypeId::of::<T>() == TypeId::of::<TransformComponent>() {
200            unsafe { Some(&mut *(&mut self.transform as *mut dyn Any as *mut T)) }
201        } else if TypeId::of::<T>() == TypeId::of::<ColorComponent>() {
202            unsafe { Some(&mut *(&mut self.tint as *mut dyn Any as *mut T)) }
203        } else {
204            None
205        }
206    }
207}