1#[derive(Debug, Clone, PartialEq)]
11pub struct ScalarField2 {
12 pub nx: usize,
13 pub ny: usize,
14 pub dx: f64,
15 pub data: Vec<f64>,
16}
17
18impl ScalarField2 {
19 #[must_use]
21 pub fn new(nx: usize, ny: usize, dx: f64) -> Self {
22 Self { nx, ny, dx, data: vec![0.0; nx * ny] }
23 }
24
25 #[must_use]
27 pub fn from_fn(nx: usize, ny: usize, dx: f64, f: impl Fn(f64, f64) -> f64) -> Self {
28 let mut data = Vec::with_capacity(nx * ny);
29 for j in 0..ny {
30 for i in 0..nx {
31 data.push(f(i as f64 * dx, j as f64 * dx));
32 }
33 }
34 Self { nx, ny, dx, data }
35 }
36
37 #[must_use]
39 pub fn get(&self, i: usize, j: usize) -> f64 {
40 self.data[j * self.nx + i]
41 }
42
43 pub fn set(&mut self, i: usize, j: usize, v: f64) {
45 self.data[j * self.nx + i] = v;
46 }
47
48 #[must_use]
50 pub fn sample(&self, x: f64, y: f64) -> f64 {
51 let fx = (x / self.dx).clamp(0.0, (self.nx - 1) as f64);
52 let fy = (y / self.dx).clamp(0.0, (self.ny - 1) as f64);
53 let i0 = fx.floor() as usize;
54 let j0 = fy.floor() as usize;
55 let i1 = (i0 + 1).min(self.nx - 1);
56 let j1 = (j0 + 1).min(self.ny - 1);
57 let tx = fx - i0 as f64;
58 let ty = fy - j0 as f64;
59 self.get(i0, j0) * (1.0 - tx) * (1.0 - ty)
60 + self.get(i1, j0) * tx * (1.0 - ty)
61 + self.get(i0, j1) * (1.0 - tx) * ty
62 + self.get(i1, j1) * tx * ty
63 }
64
65 #[must_use]
67 pub fn min_max(&self) -> (f64, f64) {
68 let mut lo = f64::INFINITY;
69 let mut hi = f64::NEG_INFINITY;
70 for &v in &self.data {
71 lo = lo.min(v);
72 hi = hi.max(v);
73 }
74 (lo, hi)
75 }
76}
77
78#[derive(Debug, Clone, PartialEq)]
81pub struct ScalarField3 {
82 pub nx: usize,
83 pub ny: usize,
84 pub nz: usize,
85 pub dx: f64,
86 pub data: Vec<f64>,
87}
88
89impl ScalarField3 {
90 #[must_use]
92 pub fn new(nx: usize, ny: usize, nz: usize, dx: f64) -> Self {
93 Self { nx, ny, nz, dx, data: vec![0.0; nx * ny * nz] }
94 }
95
96 #[must_use]
98 pub fn get(&self, i: usize, j: usize, k: usize) -> f64 {
99 self.data[(k * self.ny + j) * self.nx + i]
100 }
101
102 pub fn set(&mut self, i: usize, j: usize, k: usize, v: f64) {
104 self.data[(k * self.ny + j) * self.nx + i] = v;
105 }
106
107 #[must_use]
109 pub fn sample(&self, x: f64, y: f64, z: f64) -> f64 {
110 let fx = (x / self.dx).clamp(0.0, (self.nx - 1) as f64);
111 let fy = (y / self.dx).clamp(0.0, (self.ny - 1) as f64);
112 let fz = (z / self.dx).clamp(0.0, (self.nz - 1) as f64);
113 let i0 = fx.floor() as usize;
114 let j0 = fy.floor() as usize;
115 let k0 = fz.floor() as usize;
116 let i1 = (i0 + 1).min(self.nx - 1);
117 let j1 = (j0 + 1).min(self.ny - 1);
118 let k1 = (k0 + 1).min(self.nz - 1);
119 let tx = fx - i0 as f64;
120 let ty = fy - j0 as f64;
121 let tz = fz - k0 as f64;
122 let c00 = self.get(i0, j0, k0) * (1.0 - tx) + self.get(i1, j0, k0) * tx;
123 let c10 = self.get(i0, j1, k0) * (1.0 - tx) + self.get(i1, j1, k0) * tx;
124 let c01 = self.get(i0, j0, k1) * (1.0 - tx) + self.get(i1, j0, k1) * tx;
125 let c11 = self.get(i0, j1, k1) * (1.0 - tx) + self.get(i1, j1, k1) * tx;
126 let c0 = c00 * (1.0 - ty) + c10 * ty;
127 let c1 = c01 * (1.0 - ty) + c11 * ty;
128 c0 * (1.0 - tz) + c1 * tz
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn test_scalar_field2_sampling() {
138 let f = ScalarField2::from_fn(11, 11, 0.1, |x, y| 2.0 * x + 3.0 * y);
139 assert!((f.get(5, 5) - 2.5).abs() < 1e-12);
140 assert!((f.sample(0.33, 0.47) - (0.66 + 1.41)).abs() < 1e-12);
142 let (lo, hi) = f.min_max();
143 assert!((lo - 0.0).abs() < 1e-12 && (hi - 5.0).abs() < 1e-12);
144 }
145
146 #[test]
147 fn test_scalar_field2_new_is_zero_and_set_writes_one_node() {
148 let mut f = ScalarField2::new(5, 4, 0.25);
149 assert_eq!(f.nx, 5);
150 assert_eq!(f.ny, 4);
151 assert_eq!(f.dx, 0.25);
152 assert_eq!(f.data.len(), 20);
153 assert!(f.data.iter().all(|&v| v == 0.0), "new() is zero-filled");
154 assert_eq!(f.min_max(), (0.0, 0.0));
155 f.set(3, 2, -4.5);
157 assert_eq!(f.get(3, 2), -4.5);
158 assert_eq!(f.data[2 * 5 + 3], -4.5, "row-major index j*nx + i");
159 assert_eq!(f.data.iter().filter(|&&v| v != 0.0).count(), 1);
160 assert_eq!(f.min_max(), (-4.5, 0.0));
161 assert!((f.sample(3.0 * 0.25, 2.0 * 0.25) - (-4.5)).abs() < 1e-12);
164 assert!((f.sample(3.5 * 0.25, 2.0 * 0.25) - (-2.25)).abs() < 1e-12);
165 }
166
167 #[test]
168 fn test_scalar_field2_set_linear_field_has_constant_gradient() {
169 let (nx, ny, dx) = (9usize, 7usize, 0.5_f64);
173 let mut f = ScalarField2::new(nx, ny, dx);
174 for j in 0..ny {
175 for i in 0..nx {
176 f.set(i, j, 3.0 * (i as f64 * dx) - 2.0 * (j as f64 * dx));
177 }
178 }
179 let reference = ScalarField2::from_fn(nx, ny, dx, |x, y| 3.0 * x - 2.0 * y);
181 assert_eq!(f, reference);
182 for j in 1..ny - 1 {
183 for i in 1..nx - 1 {
184 let gx = (f.get(i + 1, j) - f.get(i - 1, j)) / (2.0 * dx);
185 let gy = (f.get(i, j + 1) - f.get(i, j - 1)) / (2.0 * dx);
186 assert!((gx - 3.0).abs() < 1e-12, "d/dx at ({i}, {j}) = {gx}");
187 assert!((gy + 2.0).abs() < 1e-12, "d/dy at ({i}, {j}) = {gy}");
188 let lap = (f.get(i + 1, j) + f.get(i - 1, j) + f.get(i, j + 1)
190 + f.get(i, j - 1)
191 - 4.0 * f.get(i, j))
192 / (dx * dx);
193 assert!(lap.abs() < 1e-11, "laplacian at ({i}, {j}) = {lap}");
194 }
195 }
196 let (lo, hi) = f.min_max();
197 assert!((lo - (-2.0 * (ny - 1) as f64 * dx)).abs() < 1e-12);
198 assert!((hi - 3.0 * (nx - 1) as f64 * dx).abs() < 1e-12);
199 }
200
201 #[test]
202 fn test_scalar_field3_sampling() {
203 let mut f = ScalarField3::new(4, 4, 4, 1.0);
204 f.set(1, 2, 3, 7.0);
205 assert_eq!(f.get(1, 2, 3), 7.0);
206 assert!((f.sample(1.0, 2.0, 3.0) - 7.0).abs() < 1e-12);
208 assert!((f.sample(1.5, 2.0, 3.0) - 3.5).abs() < 1e-12);
210 }
211}