Skip to main content

proof_engine/geometry/
deformation.rs

1//! Real-time surface deformation driven by force fields and math functions.
2
3use glam::Vec3;
4use super::GeoMesh;
5use crate::math::{MathFunction, ForceField};
6
7/// A deformation operation to apply to mesh vertices.
8#[derive(Debug, Clone)]
9pub enum DeformField {
10    /// Displace each vertex along its normal by a scalar function of position.
11    NormalDisplace { amplitude: f32, func: MathFunction },
12    /// Radial displacement from a center point.
13    Radial { center: Vec3, amplitude: f32, falloff: f32 },
14    /// Twist around an axis.
15    Twist { axis: Vec3, center: Vec3, angle_per_unit: f32 },
16    /// Bend around an axis.
17    Bend { axis: Vec3, center: Vec3, angle: f32, region_size: f32 },
18    /// Noise-based displacement.
19    Noise { amplitude: f32, frequency: f32, time: f32 },
20    /// Sine wave along an axis.
21    Wave { direction: Vec3, amplitude: f32, frequency: f32, phase: f32 },
22    /// Pinch/inflate around a point.
23    Pinch { center: Vec3, radius: f32, strength: f32 },
24    /// Spherize: push vertices toward a sphere.
25    Spherize { center: Vec3, radius: f32, strength: f32 },
26    /// Taper: scale vertices based on distance along an axis.
27    Taper { axis: Vec3, center: Vec3, start_scale: f32, end_scale: f32, length: f32 },
28    /// Apply a force field's force as displacement.
29    ForceFieldDisplace { field: ForceField, scale: f32, time: f32 },
30}
31
32/// Applies deformations to meshes.
33pub struct Deformer;
34
35impl Deformer {
36    /// Apply a deformation field to a mesh in-place.
37    pub fn apply(mesh: &mut GeoMesh, deform: &DeformField) {
38        match deform {
39            DeformField::NormalDisplace { amplitude, func } => {
40                for i in 0..mesh.vertices.len() {
41                    let p = mesh.vertices[i];
42                    let n = mesh.normals[i];
43                    let scalar = func.evaluate(p.x + p.y + p.z, p.length());
44                    mesh.vertices[i] = p + n * scalar * *amplitude;
45                }
46            }
47            DeformField::Radial { center, amplitude, falloff } => {
48                for v in &mut mesh.vertices {
49                    let dir = *v - *center;
50                    let dist = dir.length();
51                    let weight = (-dist * falloff).exp();
52                    *v += dir.normalize_or_zero() * weight * *amplitude;
53                }
54            }
55            DeformField::Twist { axis, center, angle_per_unit } => {
56                let axis_n = axis.normalize_or_zero();
57                for v in &mut mesh.vertices {
58                    let d = (*v - *center).dot(axis_n);
59                    let angle = d * angle_per_unit;
60                    let (s, c) = angle.sin_cos();
61                    let local = *v - *center;
62                    let proj = axis_n * local.dot(axis_n);
63                    let perp = local - proj;
64                    if perp.length_squared() < 1e-10 { continue; }
65                    let t1 = perp.normalize();
66                    let t2 = axis_n.cross(t1);
67                    let r = perp.length();
68                    *v = *center + proj + (t1 * c + t2 * s) * r;
69                }
70            }
71            DeformField::Wave { direction, amplitude, frequency, phase } => {
72                let dir_n = direction.normalize_or_zero();
73                for v in &mut mesh.vertices {
74                    let d = v.dot(dir_n);
75                    let offset = (d * frequency + phase).sin() * amplitude;
76                    *v += Vec3::Y * offset; // displace vertically
77                }
78            }
79            DeformField::Noise { amplitude, frequency, time } => {
80                for v in &mut mesh.vertices {
81                    let nx = simple_hash(v.x * frequency + time) * amplitude;
82                    let ny = simple_hash(v.y * frequency + time * 1.3) * amplitude;
83                    let nz = simple_hash(v.z * frequency + time * 0.7) * amplitude;
84                    *v += Vec3::new(nx, ny, nz);
85                }
86            }
87            DeformField::Pinch { center, radius, strength } => {
88                for v in &mut mesh.vertices {
89                    let dir = *v - *center;
90                    let dist = dir.length();
91                    if dist < *radius && dist > 1e-6 {
92                        let t = 1.0 - dist / radius;
93                        *v = *center + dir * (1.0 - t * strength);
94                    }
95                }
96            }
97            DeformField::Spherize { center, radius, strength } => {
98                for v in &mut mesh.vertices {
99                    let dir = *v - *center;
100                    let dist = dir.length();
101                    if dist > 1e-6 {
102                        let sphere_pos = *center + dir.normalize() * *radius;
103                        *v = v.lerp(sphere_pos, *strength);
104                    }
105                }
106            }
107            DeformField::Taper { axis, center, start_scale, end_scale, length } => {
108                let axis_n = axis.normalize_or_zero();
109                for v in &mut mesh.vertices {
110                    let d = (*v - *center).dot(axis_n);
111                    let t = (d / length.max(1e-6)).clamp(0.0, 1.0);
112                    let scale = start_scale + (end_scale - start_scale) * t;
113                    let proj = axis_n * d;
114                    let perp = *v - *center - proj;
115                    *v = *center + proj + perp * scale;
116                }
117            }
118            DeformField::Bend { axis, center, angle, region_size } => {
119                let axis_n = axis.normalize_or_zero();
120                for v in &mut mesh.vertices {
121                    let d = (*v - *center).dot(axis_n);
122                    let t = (d / region_size.max(1e-6)).clamp(-1.0, 1.0);
123                    let bend_angle = t * angle;
124                    let (s, c) = bend_angle.sin_cos();
125                    let local = *v - *center;
126                    let proj = axis_n * local.dot(axis_n);
127                    let perp = local - proj;
128                    if perp.length_squared() < 1e-10 { continue; }
129                    let t1 = perp.normalize();
130                    let t2 = axis_n.cross(t1);
131                    let r = perp.length();
132                    *v = *center + proj + (t1 * c + t2 * s) * r;
133                }
134            }
135            DeformField::ForceFieldDisplace { field, scale, time } => {
136                for v in &mut mesh.vertices {
137                    let force = field.force_at(*v, 1.0, 0.0, *time);
138                    *v += force * *scale;
139                }
140            }
141        }
142    }
143
144    /// Apply multiple deformations in sequence.
145    pub fn apply_chain(mesh: &mut GeoMesh, deforms: &[DeformField]) {
146        for d in deforms {
147            Self::apply(mesh, d);
148        }
149        mesh.recompute_normals();
150    }
151}
152
153fn simple_hash(x: f32) -> f32 {
154    let x = (x * 12.9898).sin() * 43758.5453;
155    x.fract() * 2.0 - 1.0
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161    use glam::Vec2;
162
163    fn flat_grid() -> GeoMesh {
164        let mut mesh = GeoMesh::new();
165        for z in 0..4 {
166            for x in 0..4 {
167                mesh.add_vertex(Vec3::new(x as f32, 0.0, z as f32), Vec3::Y, Vec2::ZERO);
168            }
169        }
170        for z in 0..3 {
171            for x in 0..3 {
172                let i = (z * 4 + x) as u32;
173                mesh.add_triangle(i, i + 1, i + 5);
174                mesh.add_triangle(i, i + 5, i + 4);
175            }
176        }
177        mesh
178    }
179
180    #[test]
181    fn wave_deforms_vertically() {
182        let mut mesh = flat_grid();
183        let orig_y: Vec<f32> = mesh.vertices.iter().map(|v| v.y).collect();
184        Deformer::apply(&mut mesh, &DeformField::Wave {
185            direction: Vec3::X, amplitude: 1.0, frequency: 1.0, phase: 0.0,
186        });
187        let changed = mesh.vertices.iter().zip(orig_y.iter()).any(|(v, &oy)| (v.y - oy).abs() > 0.01);
188        assert!(changed, "Wave should displace vertices");
189    }
190
191    #[test]
192    fn twist_preserves_on_axis() {
193        let mut mesh = GeoMesh::new();
194        // Point on the twist axis should not move
195        mesh.add_vertex(Vec3::new(0.0, 1.0, 0.0), Vec3::Y, Vec2::ZERO);
196        mesh.add_vertex(Vec3::new(0.0, 2.0, 0.0), Vec3::Y, Vec2::ZERO);
197        mesh.add_vertex(Vec3::new(1.0, 1.0, 0.0), Vec3::Y, Vec2::ZERO);
198        mesh.add_triangle(0, 1, 2);
199
200        let orig = mesh.vertices[0];
201        Deformer::apply(&mut mesh, &DeformField::Twist {
202            axis: Vec3::Y, center: Vec3::ZERO, angle_per_unit: 1.0,
203        });
204        assert!((mesh.vertices[0] - orig).length() < 0.01, "Point on axis shouldn't move");
205    }
206}