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}