bevy_single_variable_function_mesh/
lib.rs1use bevy::math::Vec3;
2use bevy::render::mesh::{Indices, Mesh, PrimitiveTopology};
3use bevy::render::render_asset::RenderAssetUsages;
4
5#[derive(Debug, Clone, Copy)]
8pub struct SingleVariableFunctionMesh {
9 pub f1: fn(f32) -> f32,
14 pub f1_x_start: f32,
17 pub f1_x_end: f32,
20 pub f1_vertices: usize,
24 pub f2: fn(f32) -> f32,
25 pub f2_x_start: f32,
26 pub f2_x_end: f32,
27 pub f2_vertices: usize,
28}
29
30impl Default for SingleVariableFunctionMesh {
31 fn default() -> Self {
32 SingleVariableFunctionMesh {
33 f1: |_x: f32| -> f32 { 1.0 },
34 f1_x_start: -1.0,
35 f1_x_end: 1.0,
36 f1_vertices: 18,
37 f2: |_x: f32| -> f32 { 1.0 },
38 f2_x_start: -1.0,
39 f2_x_end: 1.0,
40 f2_vertices: 18,
41 }
42 }
43}
44
45impl From<SingleVariableFunctionMesh> for Mesh {
46 fn from(mathfunction: SingleVariableFunctionMesh) -> Self {
47 debug_assert!(mathfunction.f1_x_start <= mathfunction.f1_x_end);
48 debug_assert!(mathfunction.f2_x_start <= mathfunction.f2_x_end);
49 let (ring_horizontal, maximum1) = calculate_ring_of_vertices(
50 mathfunction.f1,
51 mathfunction.f1_x_start,
52 mathfunction.f1_x_end,
53 mathfunction.f1_vertices,
54 true,
55 );
56 let (ring_vertical, _) = calculate_ring_of_vertices(
57 mathfunction.f2,
58 mathfunction.f2_x_start,
59 mathfunction.f2_x_end,
60 mathfunction.f2_vertices,
61 false,
62 );
63 let width_maximum = mathfunction.f1_x_end - mathfunction.f1_x_start;
64 let amount = ring_horizontal.len();
65 let mut amount_layers = mathfunction.f2_vertices - 1;
66 if mathfunction.f2_x_start == mathfunction.f2_x_end {
67 amount_layers = 1;
68 }
69
70 let mut vertices: Vec<([f32; 3], [f32; 3], [f32; 2])> =
71 Vec::with_capacity(amount * amount_layers + 2);
72 vertices.push((
73 [0.0, mathfunction.f2_x_start, 0.0],
74 [0.0, -1.0, 0.0],
75 [0.5, 0.5],
76 ));
77 for i in ring_vertical.iter().take(amount_layers) {
78 for (k, j) in ring_horizontal.iter().enumerate().take(amount) {
79 let (mut x, mut z) = (j.x, j.y);
81 if amount_layers > 1 {
82 (x, z) = (x.signum() * (x.abs() * i.y), z.signum() * (z.abs() * i.y));
83 }
84 let y = i.x;
85
86 let mut normal_horizontally =
88 Vec3::new(-j.slope_in_percentage.tan(), 0.0, 1.0).normalize();
89
90 if k >= amount / 2 {
91 normal_horizontally[2] = -normal_horizontally[2];
92 }
93 let normal_vertical = Vec3::new(1.0, -i.slope_in_percentage.tan(), 1.0).normalize();
94 let mut normals = [
95 normal_horizontally[0] / 3.0 * 2.0,
96 normal_vertical[1],
97 normal_horizontally[2] / 3.0 * 2.0,
98 ];
99 if amount_layers == 1 {
100 normals = [0.0, 1.0, 0.0];
101 }
102 let uv_x = (x + mathfunction.f1_x_start.abs()) / width_maximum;
103 let uv_y = (z + maximum1) / (maximum1 * 2.0);
104 vertices.push(([x, y, z], normals, [uv_x, uv_y]));
105 }
106 }
107 vertices.push((
108 [0.0, mathfunction.f2_x_end, 0.0],
109 [0.0, 1.0, 0.0],
110 [0.5, 0.5],
111 ));
112
113 let mut indeces: Vec<u32> = Vec::with_capacity((amount * amount_layers + 2) * 3);
115 for i in 0..amount {
116 if amount_layers > 1 {
117 indeces.append(&mut vec![
118 ((i + 1) % amount + 1).try_into().unwrap(),
119 (i + 1).try_into().unwrap(),
120 0,
121 ]);
122 }
123 indeces.append(&mut vec![
124 ((amount_layers - 1) * amount + i + 1).try_into().unwrap(),
125 ((amount_layers - 1) * amount + (i + 1) % amount + 1)
126 .try_into()
127 .unwrap(),
128 (amount_layers * amount + 1).try_into().unwrap(),
129 ]);
130 }
131 for segment in 1..amount_layers {
132 for i in 0..amount {
133 let tl = (segment * amount + i + 1) as u32;
134 let mut tr = (segment * amount + i + 2) as u32;
135 let bl = ((segment - 1) * amount + i + 1) as u32;
136 let mut br = ((segment - 1) * amount + i + 2) as u32;
137 if i == amount - 1 {
138 tr = (segment * amount + 1) as u32;
139 br = ((segment - 1) * amount + 1) as u32;
140 }
141 indeces.append(&mut vec![br, tr, tl]);
142 indeces.append(&mut vec![bl, br, tl]);
143 }
144 }
145
146 let positions: Vec<_> = vertices.iter().map(|(p, _, _)| *p).collect();
147 let normals: Vec<_> = vertices.iter().map(|(_, n, _)| *n).collect();
148 let uvs: Vec<_> = vertices.iter().map(|(_, _, uv)| *uv).collect();
149 let mut mesh = Mesh::new(
150 PrimitiveTopology::TriangleList,
151 RenderAssetUsages::default(),
152 );
153 mesh.insert_indices(Indices::U32(indeces));
154 mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
155 mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
156 mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
157 mesh
158 }
159}
160
161#[derive(Copy, Clone, Debug)]
162struct Position {
163 x: f32,
164 y: f32,
165 slope_in_percentage: f32,
166}
167
168fn calculate_ring_of_vertices(
169 f: fn(f32) -> f32,
170 x_start: f32,
171 x_end: f32,
172 vertices: usize,
173 generate_lower_half: bool,
174) -> (Vec<Position>, f32) {
175 let delta = 0.000001;
176 let start = Position {
177 x: x_start,
178 y: f(x_start),
179 slope_in_percentage: ((f(x_start + delta) - f(x_start)) / (delta)).atan(),
180 };
181 let end = Position {
182 x: x_end,
183 y: f(x_end),
184 slope_in_percentage: ((f(x_end) - f(x_end - delta)) / (delta)).atan(),
185 };
186 let mut vec: Vec<Position> = Vec::with_capacity(vertices);
187 let mut maximum = 0.0;
188 vec.push(start);
189 vec.push(end);
190 for _ in 2..vertices {
191 let (mut index, mut max_slope_difference, mut max_x_difference) = (1, 0.0, 0.0);
192 for j in 1..vec.len() {
193 let new_x = vec[j - 1].x + (vec[j].x - vec[j - 1].x) / 2.0;
194 let new_m = ((f(new_x + delta) - f(new_x)) / (delta)).atan();
195 let x_difference = vec[j].x - vec[j - 1].x;
196 let slope_difference = (new_m - vec[j].slope_in_percentage).abs()
197 + (new_m - vec[j - 1].slope_in_percentage).abs();
198 if slope_difference > max_slope_difference
199 || (slope_difference == max_slope_difference && x_difference > max_x_difference)
200 {
201 (index, max_slope_difference, max_x_difference) =
202 (j, slope_difference, x_difference);
203 }
204 }
205 let new_x = vec[index - 1].x + (vec[index].x - vec[index - 1].x) / 2.0;
206 vec.insert(
207 index,
208 Position {
209 x: new_x,
210 y: f(new_x),
211 slope_in_percentage: ((f(new_x + delta) - f(new_x)) / (delta)).atan(),
212 },
213 );
214 if f(new_x) > maximum {
215 maximum = f(new_x);
216 }
217 }
218 if !generate_lower_half {
219 return (vec, maximum);
220 }
221 let mut lower_half = vec.clone();
222 if f(lower_half[0].x) != 0.0 {
223 lower_half.remove(0);
224 }
225 lower_half.reverse();
226 if f(lower_half[0].x) != 0.0 {
227 lower_half.remove(0);
228 }
229 for vertex in &lower_half {
230 vec.push(Position {
231 x: vertex.x,
232 y: -vertex.y,
233 slope_in_percentage: vertex.slope_in_percentage,
234 });
235 }
236 (vec, maximum)
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242
243 fn square(_x: f32) -> f32 {
244 1.0
245 }
246
247 #[test]
248 fn test_amount_of_vertices() {
249 let square_2d_mesh: Mesh = SingleVariableFunctionMesh {
250 f1: square,
251 f1_x_start: -1.0,
252 f1_x_end: 1.0,
253 f1_vertices: 20,
254 f2: square,
255 f2_x_start: 0.0,
256 f2_x_end: 0.0,
257 f2_vertices: 20,
258 }
259 .into();
260 let circle_2d_mesh: Mesh = SingleVariableFunctionMesh {
261 f1: circle,
262 f1_x_start: -1.0,
263 f1_x_end: 1.0,
264 f1_vertices: 20,
265 f2: circle,
266 f2_x_start: 0.0,
267 f2_x_end: 0.0,
268 f2_vertices: 20,
269 }
270 .into();
271 assert_eq!(
272 square_2d_mesh.count_vertices(),
273 circle_2d_mesh.count_vertices() - 2
274 );
275 }
276}