1#![doc = include_str!("../README.md")]
2pub mod interval_property;
3pub mod prop_rand;
4pub mod prop_range;
5pub mod variable_property;
6
7use bevy_math::*;
8use bevy_reflect::{Reflect, TypePath};
9use rand::{seq::SliceRandom, thread_rng};
10
11use std::ops::{Range, RangeInclusive};
12
13use crate::prop_rand::PropRand;
14use crate::prop_range::PropRange;
15
16use crate::variable_property::VariableProperty;
17
18#[derive(Reflect, Clone)]
23pub enum Property<T> {
24 Static(T),
26
27 RandomRange(PropRange<T>),
29
30 RandomChoice(Vec<T>),
32
33 Random,
35}
36
37impl<T> VariableProperty for Property<T>
38where
39 T: PropRand + Clone + TypePath,
40{
41 type Output = T;
42 fn get_value(&self) -> T {
45 match self {
46 Property::Static(v) => v.clone(),
47 Property::RandomRange(range) => {
48 <T as PropRand>::gen_range(&mut thread_rng(), range.clone())
49 }
50 Property::RandomChoice(choices) => choices.choose(&mut thread_rng()).unwrap().clone(),
51 Property::Random => T::gen(&mut thread_rng()),
52 }
53 }
54}
55
56impl<T: Default> Default for Property<T> {
58 fn default() -> Self {
59 Property::Static(T::default())
60 }
61}
62
63macro_rules! prop_from_impl {
72 ($from_type:tt) => {
73 prop_from_impl!($from_type, $from_type);
74 };
75
76 ($from_type:tt, $into_prop_type:tt) => {
77 impl From<$from_type> for Property<$into_prop_type> {
78 fn from(v: $from_type) -> Self {
79 Property::Static(v.into())
80 }
81 }
82
83 impl From<Range<$from_type>> for Property<$into_prop_type> {
84 fn from(v: Range<$from_type>) -> Self {
85 Property::RandomRange(PropRange {
86 start: v.start.into(),
87 end: v.end.into(),
88 inclusive: false,
89 })
90 }
91 }
92
93 impl From<RangeInclusive<$from_type>> for Property<$into_prop_type> {
94 fn from(v: RangeInclusive<$from_type>) -> Self {
95 Property::RandomRange(PropRange {
96 start: v.start().clone().into(),
97 end: v.end().clone().into(),
98 inclusive: true,
99 })
100 }
101 }
102
103 impl From<Vec<$from_type>> for Property<$into_prop_type> {
104 fn from(v: Vec<$from_type>) -> Self {
105 Property::RandomChoice(v.into_iter().map(|x| x.into()).collect())
106 }
107 }
108
109 impl From<&[$from_type]> for Property<$into_prop_type> {
110 fn from(v: &[$from_type]) -> Self {
111 Property::RandomChoice(v.into_iter().cloned().map(|x| x.into()).collect())
112 }
113 }
114
115 impl<const N: usize> From<[$from_type; N]> for Property<$into_prop_type> {
116 fn from(v: [$from_type; N]) -> Self {
117 Property::RandomChoice(v.into())
118 }
119 }
120 };
121}
122
123macro_rules! prop_from_impl_many {
130 ($($type:tt,)+) => {
131 $(
132 prop_from_impl!($type, $type);
133 )+
134 }
135}
136
137prop_from_impl_many!(
138 usize, isize, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, f32, f64, Vec2, Vec3, Vec4,
139 UVec2, UVec3, UVec4, IVec2, IVec3, IVec4, DVec2, DVec3, DVec4,
140);
141
142impl<T, const N: usize> From<Range<[T; N]>> for Property<[T; N]> {
143 fn from(v: Range<[T; N]>) -> Self {
144 Self::RandomRange(PropRange {
145 start: v.start.into(),
146 end: v.end.into(),
147 inclusive: false,
148 })
149 }
150}
151
152impl<T: Clone, const N: usize> From<RangeInclusive<[T; N]>> for Property<[T; N]> {
153 fn from(v: RangeInclusive<[T; N]>) -> Self {
154 Self::RandomRange(PropRange {
155 start: v.start().clone(),
156 end: v.end().clone(),
157 inclusive: true,
158 })
159 }
160}
161
162impl<T, const N: usize> From<Vec<[T; N]>> for Property<[T; N]> {
163 fn from(v: Vec<[T; N]>) -> Self {
164 Property::RandomChoice(v.into_iter().map(|x| x.into()).collect())
165 }
166}
167
168impl<T: Clone, const N: usize> From<&[[T; N]]> for Property<[T; N]> {
169 fn from(v: &[[T; N]]) -> Self {
170 Property::RandomChoice(v.into_iter().cloned().collect())
171 }
172}
173
174impl<T, const N: usize, const M: usize> From<[[T; N]; M]> for Property<[T; N]> {
175 fn from(v: [[T; N]; M]) -> Self {
176 Property::RandomChoice(v.into_iter().collect())
177 }
178}
179
180pub mod prelude {
181 pub use crate::{
182 interval_property::IntervalProperty, prop_range::PropRange,
183 variable_property::VariableProperty, Property,
184 };
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190 #[test]
191 fn range_generation() {
192 let ranges = (2.5..5.0, -10.0..0.0, 0.0..1.0);
193 let vec3_generator: (Property<f32>, Property<f32>, Property<f32>) = (
194 ranges.0.clone().into(),
195 ranges.1.clone().into(),
196 ranges.2.clone().into(),
197 );
198 let (x, y, z) = vec3_generator.get_value().into();
199 assert!(
200 ranges.0.contains(&x),
201 "{} was not in the range of ({}..{})",
202 x,
203 ranges.0.start,
204 ranges.0.end
205 );
206 assert!(
207 ranges.1.contains(&y),
208 "{} was not in the range of ({}..{})",
209 y,
210 ranges.1.start,
211 ranges.1.end
212 );
213 assert!(
214 ranges.2.contains(&z),
215 "{} was not in the range of ({}..{})",
216 z,
217 ranges.2.start,
218 ranges.2.end
219 );
220 }
221
222 #[test]
223 fn array_range_generation() {
224 let (start, end) = ([0usize, 25], [10, 50]);
225 let array_prop: Property<[usize; 2]> = (start..=end).into();
226 let [x, y] = array_prop.get_value();
227 assert!(
228 (start[0]..=end[0]).contains(&x),
229 "{} was not in the range of ({}..{})",
230 x,
231 start[0],
232 end[0]
233 );
234 assert!(
235 (start[1]..=end[1]).contains(&y),
236 "{} was not in the range of ({}..{})",
237 y,
238 start[1],
239 end[1]
240 );
241 }
242
243 #[test]
244 #[should_panic]
245 fn bad_range() {
246 let p = Property::from(10.0..1.0);
247 p.get_value();
248 }
249
250 #[test]
251 #[should_panic]
252 fn bad_array_range() {
253 let p = Property::from([0.0, 10.0]..[1.0, 5.0]);
254 p.get_value();
255 }
256
257 #[test]
258 fn tuples() {
259 let p = Property::Static((1.0, 5.0));
260 p.get_value();
261 }
262}