Skip to main content

radiantkit_path/
path_node.rs

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}