steel_utils/value_providers/
float.rs1use serde::{Deserialize, Deserializer, de::Error as _};
2
3use crate::random::Random;
4
5#[derive(Debug, Clone, Copy)]
10pub enum FloatProvider {
11 Constant(f32),
13 Uniform {
15 min_inclusive: f32,
17 max_exclusive: f32,
19 },
20 Trapezoid {
23 min: f32,
25 max: f32,
27 plateau: f32,
29 },
30 ClampedNormal {
32 mean: f32,
34 deviation: f32,
36 min: f32,
38 max: f32,
40 },
41}
42
43impl FloatProvider {
44 pub fn sample<R: Random + ?Sized>(self, random: &mut R) -> f32 {
49 match self {
50 Self::Constant(v) => v,
51 Self::Uniform {
52 min_inclusive,
53 max_exclusive,
54 } => random.next_f32() * (max_exclusive - min_inclusive) + min_inclusive,
55 Self::Trapezoid { min, max, plateau } => {
56 let range = max - min;
57 let plateau_start = (range - plateau) / 2.0;
58 let plateau_end = range - plateau_start;
59 min + random.next_f32() * plateau_end + random.next_f32() * plateau_start
60 }
61 Self::ClampedNormal {
62 mean,
63 deviation,
64 min,
65 max,
66 } => {
67 let sample = mean + deviation * random.next_gaussian() as f32;
69 sample.clamp(min, max)
70 }
71 }
72 }
73
74 #[must_use]
76 pub const fn min(self) -> f32 {
77 match self {
78 Self::Constant(v) => v,
79 Self::Uniform { min_inclusive, .. } => min_inclusive,
80 Self::Trapezoid { min, .. } | Self::ClampedNormal { min, .. } => min,
81 }
82 }
83
84 #[must_use]
86 pub const fn max(self) -> f32 {
87 match self {
88 Self::Constant(v) => v,
89 Self::Uniform { max_exclusive, .. } => max_exclusive,
90 Self::Trapezoid { max, .. } | Self::ClampedNormal { max, .. } => max,
91 }
92 }
93}
94
95impl<'de> Deserialize<'de> for FloatProvider {
96 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
97 #[derive(Deserialize)]
98 #[serde(tag = "type", deny_unknown_fields)]
99 enum Tagged {
100 #[serde(rename = "minecraft:constant")]
101 Constant { value: f32 },
102 #[serde(rename = "minecraft:uniform")]
103 Uniform {
104 min_inclusive: f32,
105 max_exclusive: f32,
106 },
107 #[serde(rename = "minecraft:trapezoid")]
108 Trapezoid { min: f32, max: f32, plateau: f32 },
109 #[serde(rename = "minecraft:clamped_normal")]
110 ClampedNormal {
111 mean: f32,
112 deviation: f32,
113 min: f32,
114 max: f32,
115 },
116 }
117
118 let value = serde_json::Value::deserialize(d)?;
119 if value.is_number() {
120 return Ok(Self::Constant(
121 f32::deserialize(value).map_err(D::Error::custom)?,
122 ));
123 }
124
125 Ok(
126 match serde_json::from_value(value).map_err(D::Error::custom)? {
127 Tagged::Constant { value: v } => Self::Constant(v),
128 Tagged::Uniform {
129 min_inclusive,
130 max_exclusive,
131 } => Self::Uniform {
132 min_inclusive,
133 max_exclusive,
134 },
135 Tagged::Trapezoid { min, max, plateau } => Self::Trapezoid { min, max, plateau },
136 Tagged::ClampedNormal {
137 mean,
138 deviation,
139 min,
140 max,
141 } => Self::ClampedNormal {
142 mean,
143 deviation,
144 min,
145 max,
146 },
147 },
148 )
149 }
150}