1use crate::mesh_edit::EditableMesh;
20use crate::noise::{Noise, NoiseKind};
21use glam::{Mat4, Quat, Vec3};
22
23impl EditableMesh {
24 pub fn vertex_count(&self) -> usize {
26 self.vertices.len()
27 }
28
29 pub fn position(&self, i: usize) -> Option<Vec3> {
31 self.vertices.get(i).map(|v| Vec3::from(v.pos))
32 }
33
34 pub fn set_position(&mut self, i: usize, p: Vec3) -> bool {
36 match self.vertices.get_mut(i) {
37 Some(v) if p.is_finite() => {
38 v.pos = p.to_array();
39 self.changed();
40 true
41 }
42 _ => false,
43 }
44 }
45
46 pub fn bounds(&self) -> (Vec3, Vec3) {
48 let mut it = self.vertices.iter().map(|v| Vec3::from(v.pos));
49 let Some(first) = it.next() else { return (Vec3::ZERO, Vec3::ZERO) };
50 it.fold((first, first), |(lo, hi), p| (lo.min(p), hi.max(p)))
51 }
52
53 pub fn map_positions(&mut self, mut f: impl FnMut(usize, Vec3, Vec3) -> Vec3) {
55 for (i, v) in self.vertices.iter_mut().enumerate() {
56 let p = f(i, Vec3::from(v.pos), Vec3::from(v.normal));
57 if p.is_finite() {
58 v.pos = p.to_array();
59 }
60 }
61 self.changed();
62 }
63
64 pub fn select_sphere(&self, center: Vec3, radius: f32) -> Vec<usize> {
66 let r2 = radius * radius;
67 self.vertices.iter().enumerate().filter(|(_, v)| Vec3::from(v.pos).distance_squared(center) <= r2).map(|(i, _)| i).collect()
68 }
69
70 pub fn select_half_space(&self, point: Vec3, normal: Vec3) -> Vec<usize> {
72 let n = normal.normalize_or_zero();
73 self.vertices.iter().enumerate().filter(|(_, v)| (Vec3::from(v.pos) - point).dot(n) >= 0.0).map(|(i, _)| i).collect()
74 }
75
76 pub fn translate_vertices(&mut self, indices: &[usize], delta: Vec3) {
78 for &i in indices {
79 if let Some(v) = self.vertices.get_mut(i) {
80 v.pos = (Vec3::from(v.pos) + delta).to_array();
81 }
82 }
83 self.changed();
84 }
85
86 pub fn translate(&mut self, delta: Vec3) {
88 self.map_positions(|_, p, _| p + delta);
89 }
90
91 pub fn scale_about(&mut self, center: Vec3, scale: Vec3) {
93 self.map_positions(|_, p, _| center + (p - center) * scale);
94 }
95
96 pub fn transform(&mut self, m: Mat4) {
98 let (_, rot, _) = m.to_scale_rotation_translation();
99 for v in &mut self.vertices {
100 v.pos = m.transform_point3(Vec3::from(v.pos)).to_array();
101 v.normal = (rot * Vec3::from(v.normal)).normalize_or_zero().to_array();
102 }
103 self.changed();
104 }
105
106 pub fn inflate(&mut self, amount: f32) {
108 self.map_positions(|_, p, n| p + n * amount);
109 }
110
111 pub fn twist(&mut self, axis: Vec3, degrees_per_metre: f32) {
113 let axis = axis.normalize_or_zero();
114 if axis == Vec3::ZERO {
115 return;
116 }
117 self.map_positions(|_, p, _| Quat::from_axis_angle(axis, (p.dot(axis) * degrees_per_metre).to_radians()) * p);
118 }
119
120 pub fn taper(&mut self, axis: Vec3, start: f32, end: f32) {
122 let axis = axis.normalize_or_zero();
123 if axis == Vec3::ZERO {
124 return;
125 }
126 let (lo, hi) = self.bounds();
127 let (a, b) = (lo.dot(axis).min(hi.dot(axis)), lo.dot(axis).max(hi.dot(axis)));
128 self.map_positions(|_, p, _| {
129 let t = if b - a > 1e-6 { (p.dot(axis) - a) / (b - a) } else { 0.0 };
130 let along = axis * p.dot(axis);
131 along + (p - along) * (start + (end - start) * t)
132 });
133 }
134
135 pub fn bend(&mut self, along: Vec3, bend_axis: Vec3, degrees_per_metre: f32) {
138 let (along, axis) = (along.normalize_or_zero(), bend_axis.normalize_or_zero());
139 if along == Vec3::ZERO || axis == Vec3::ZERO {
140 return;
141 }
142 self.map_positions(|_, p, _| Quat::from_axis_angle(axis, (p.dot(along) * degrees_per_metre).to_radians()) * p);
143 }
144
145 pub fn wave(&mut self, direction: Vec3, displace: Vec3, amplitude: f32, wavelength: f32, phase: f32) {
148 let direction = direction.normalize_or_zero();
149 let wavelength = wavelength.max(1e-4);
150 self.map_positions(|_, p, _| p + displace * amplitude * ((p.dot(direction) / wavelength + phase) * std::f32::consts::TAU).sin());
151 }
152
153 pub fn noise_displace(&mut self, noise: &Noise, amplitude: f32, time: f32) {
156 self.map_positions(|_, p, n| p + n * amplitude * noise.sample3(p.x, p.y, p.z + time));
157 }
158
159 pub fn smooth(&mut self, iterations: u32, factor: f32) {
162 let key = |p: [f32; 3]| p.map(|v| (v as f64 * 10_000.0).round() as i64);
163 for _ in 0..iterations.min(32) {
164 let mut sum = std::collections::HashMap::<[i64; 3], (Vec3, f32)>::new();
165 for t in self.indices.chunks_exact(3) {
166 for k in 0..3 {
167 let (a, b) = (t[k] as usize, t[(k + 1) % 3] as usize);
168 let (pa, pb) = (self.vertices[a].pos, self.vertices[b].pos);
169 let e = sum.entry(key(pa)).or_default();
170 e.0 += Vec3::from(pb);
171 e.1 += 1.0;
172 let e = sum.entry(key(pb)).or_default();
173 e.0 += Vec3::from(pa);
174 e.1 += 1.0;
175 }
176 }
177 for v in &mut self.vertices {
178 if let Some((s, n)) = sum.get(&key(v.pos)) {
179 if *n > 0.0 {
180 let p = Vec3::from(v.pos);
181 v.pos = p.lerp(*s / *n, factor.clamp(0.0, 1.0)).to_array();
182 }
183 }
184 }
185 }
186 self.changed();
187 }
188
189 pub fn default_noise(frequency: f32, seed: u32) -> Noise {
191 Noise::new(NoiseKind::Perlin).with_seed(seed).with_frequency(frequency)
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198 use crate::mesh::MeshData;
199
200 fn cube() -> EditableMesh {
201 EditableMesh::from_mesh(&MeshData::primitive("cube").unwrap()).unwrap()
202 }
203
204 #[test]
205 fn deformers_change_geometry_and_bump_the_revision() {
206 let mut m = cube();
207 let rev = m.revision;
208 let before = m.bounds();
209 m.taper(Vec3::Y, 1.0, 0.0);
210 assert!(m.revision > rev);
211 let top: Vec<_> = m.vertices.iter().filter(|v| v.pos[1] > 0.49).collect();
213 assert!(top.iter().all(|v| v.pos[0].abs() < 1e-4 && v.pos[2].abs() < 1e-4));
214 assert_eq!(m.bounds().1.y, before.1.y);
215 let mut m = cube();
216 m.bend(Vec3::Y, Vec3::Z, 30.0);
217 assert!(m.bounds() != before);
218 let mut m = cube();
219 m.noise_displace(&EditableMesh::default_noise(2.0, 1), 0.2, 0.0);
220 m.smooth(2, 0.5);
221 m.recalculate_normals().unwrap();
222 assert!(m.vertices.iter().all(|v| Vec3::from(v.pos).is_finite()));
223 }
224
225 #[test]
226 fn selection_and_access_are_checked() {
227 let mut m = cube();
228 assert_eq!(m.select_sphere(Vec3::ZERO, 10.0).len(), m.vertex_count());
229 assert!(m.select_half_space(Vec3::ZERO, Vec3::Y).len() < m.vertex_count());
230 assert!(!m.set_position(9999, Vec3::ZERO));
231 assert!(!m.set_position(0, Vec3::NAN));
232 assert!(m.set_position(0, Vec3::ONE));
233 let sel = m.select_half_space(Vec3::ZERO, Vec3::Y);
234 let y0 = m.position(sel[0]).unwrap().y;
235 m.translate_vertices(&sel, Vec3::Y);
236 assert!((m.position(sel[0]).unwrap().y - y0 - 1.0).abs() < 1e-5);
237 }
238}