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