1pub use bevy_gizmos_macros::GizmoConfigGroup;
4
5#[cfg(all(
6    feature = "bevy_render",
7    any(feature = "bevy_pbr", feature = "bevy_sprite_render")
8))]
9use {crate::GizmoAsset, bevy_asset::Handle, bevy_ecs::component::Component};
10
11use bevy_ecs::{reflect::ReflectResource, resource::Resource};
12use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath};
13use bevy_utils::TypeIdMap;
14use core::{
15    any::TypeId,
16    hash::Hash,
17    ops::{Deref, DerefMut},
18    panic,
19};
20
21#[derive(Debug, Default, Copy, Clone, Reflect, PartialEq, Eq, Hash)]
23#[reflect(Default, PartialEq, Hash, Clone)]
24pub enum GizmoLineJoint {
25    #[default]
27    None,
28    Miter,
30    Round(u32),
35    Bevel,
37}
38
39#[derive(Copy, Clone, Debug, Default, PartialEq, Reflect)]
41#[reflect(Default, PartialEq, Hash, Clone)]
42#[non_exhaustive]
43pub enum GizmoLineStyle {
44    #[default]
46    Solid,
47    Dotted,
49    Dashed {
51        gap_scale: f32,
53        line_scale: f32,
55    },
56}
57
58impl Eq for GizmoLineStyle {}
59
60impl Hash for GizmoLineStyle {
61    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
62        match self {
63            Self::Solid => {
64                0u64.hash(state);
65            }
66            Self::Dotted => 1u64.hash(state),
67            Self::Dashed {
68                gap_scale,
69                line_scale,
70            } => {
71                2u64.hash(state);
72                gap_scale.to_bits().hash(state);
73                line_scale.to_bits().hash(state);
74            }
75        }
76    }
77}
78
79pub trait GizmoConfigGroup: Reflect + TypePath + Default {}
85
86#[derive(Default, Reflect, GizmoConfigGroup)]
88#[reflect(Default)]
89pub struct DefaultGizmoConfigGroup;
90
91#[derive(Default, Reflect, GizmoConfigGroup, Debug, Clone)]
94#[reflect(Default, Clone)]
95pub struct ErasedGizmoConfigGroup;
96
97#[derive(Reflect, Resource, Default)]
101#[reflect(Resource, Default)]
102pub struct GizmoConfigStore {
103    #[reflect(ignore)]
105    store: TypeIdMap<(GizmoConfig, Box<dyn Reflect>)>,
106}
107
108impl GizmoConfigStore {
109    pub fn get_config_dyn(&self, config_type_id: &TypeId) -> Option<(&GizmoConfig, &dyn Reflect)> {
111        let (config, ext) = self.store.get(config_type_id)?;
112        Some((config, ext.deref()))
113    }
114
115    pub fn config<T: GizmoConfigGroup>(&self) -> (&GizmoConfig, &T) {
117        let Some((config, ext)) = self.get_config_dyn(&TypeId::of::<T>()) else {
118            panic!("Requested config {} does not exist in `GizmoConfigStore`! Did you forget to add it using `app.init_gizmo_group<T>()`?", T::type_path());
119        };
120        let ext = ext.as_any().downcast_ref().unwrap();
122        (config, ext)
123    }
124
125    pub fn get_config_mut_dyn(
127        &mut self,
128        config_type_id: &TypeId,
129    ) -> Option<(&mut GizmoConfig, &mut dyn Reflect)> {
130        let (config, ext) = self.store.get_mut(config_type_id)?;
131        Some((config, ext.deref_mut()))
132    }
133
134    pub fn config_mut<T: GizmoConfigGroup>(&mut self) -> (&mut GizmoConfig, &mut T) {
136        let Some((config, ext)) = self.get_config_mut_dyn(&TypeId::of::<T>()) else {
137            panic!("Requested config {} does not exist in `GizmoConfigStore`! Did you forget to add it using `app.init_gizmo_group<T>()`?", T::type_path());
138        };
139        let ext = ext.as_any_mut().downcast_mut().unwrap();
141        (config, ext)
142    }
143
144    pub fn iter(&self) -> impl Iterator<Item = (&TypeId, &GizmoConfig, &dyn Reflect)> + '_ {
146        self.store
147            .iter()
148            .map(|(id, (config, ext))| (id, config, ext.deref()))
149    }
150
151    pub fn iter_mut(
153        &mut self,
154    ) -> impl Iterator<Item = (&TypeId, &mut GizmoConfig, &mut dyn Reflect)> + '_ {
155        self.store
156            .iter_mut()
157            .map(|(id, (config, ext))| (id, config, ext.deref_mut()))
158    }
159
160    pub fn insert<T: GizmoConfigGroup>(&mut self, config: GizmoConfig, ext_config: T) {
162        self.store
164            .insert(TypeId::of::<T>(), (config, Box::new(ext_config)));
165    }
166
167    pub(crate) fn register<T: GizmoConfigGroup>(&mut self) {
168        self.insert(GizmoConfig::default(), T::default());
169    }
170}
171
172#[derive(Clone, Reflect, Debug)]
174#[reflect(Clone, Default)]
175pub struct GizmoConfig {
176    pub enabled: bool,
180    pub line: GizmoLineConfig,
182    pub depth_bias: f32,
195    #[cfg(feature = "bevy_render")]
199    pub render_layers: bevy_camera::visibility::RenderLayers,
200}
201
202impl Default for GizmoConfig {
203    fn default() -> Self {
204        Self {
205            enabled: true,
206            line: Default::default(),
207            depth_bias: 0.,
208            #[cfg(feature = "bevy_render")]
209            render_layers: Default::default(),
210        }
211    }
212}
213
214#[derive(Clone, Reflect, Debug)]
216#[reflect(Clone, Default)]
217pub struct GizmoLineConfig {
218    pub width: f32,
224    pub perspective: bool,
230    pub style: GizmoLineStyle,
232    pub joints: GizmoLineJoint,
234}
235
236impl Default for GizmoLineConfig {
237    fn default() -> Self {
238        Self {
239            width: 2.,
240            perspective: false,
241            style: GizmoLineStyle::Solid,
242            joints: GizmoLineJoint::None,
243        }
244    }
245}
246
247#[cfg(all(
248    feature = "bevy_render",
249    any(feature = "bevy_pbr", feature = "bevy_sprite_render")
250))]
251#[derive(Component)]
252pub(crate) struct GizmoMeshConfig {
253    pub line_perspective: bool,
254    pub line_style: GizmoLineStyle,
255    pub line_joints: GizmoLineJoint,
256    pub render_layers: bevy_camera::visibility::RenderLayers,
257    pub handle: Handle<GizmoAsset>,
258}