//! 网格点评估 //! //! 原始 Fortran: gridp.f //! 将曲线 y=f(x) 分成 n-1 等长段,确定新网格点 use crate::state::MDEPTH; /// 网格点评估 /// /// 将曲线 Y=f(X) 分成 N-1 个等长段, /// 每段端点的 x 坐标定义新网格点 /// /// # 参数 /// - `x`: 原始 x 坐标数组 /// - `y`: 原始 y 坐标数组 /// - `xnew`: 新 x 坐标数组 (输出) /// - `ynew`: 新 y 坐标数组 (输出) /// - `n`: 点数 /// /// # 算法 /// 1. 计算原始曲线各段长度 /// 2. 计算总长度和新段长度 /// 3. 沿曲线均匀采样新点 pub fn gridp(x: &[f64], y: &[f64], xnew: &mut [f64], ynew: &mut [f64], n: usize) { // 工作数组:存储各段长度 let mut z = vec![0.0; MDEPTH.min(n)]; // 计算原始曲线各段长度和总长度 let mut ztot = 0.0; for i in 1..n { let dx = x[i] - x[i - 1]; let dy = y[i] - y[i - 1]; z[i - 1] = (dx * dx + dy * dy).sqrt(); ztot += z[i - 1]; } // 新段长度 let z0 = ztot / (n - 1) as f64; // 初始化 let mut iseg: usize = 0; let mut xlast = x[iseg]; let mut ylast = y[iseg]; let mut zrest = z[iseg]; let mut zrem = z0; let mut ip: usize = 0; xnew[ip] = x[0]; ynew[ip] = y[0]; // 沿曲线采样新点 loop { if zrem < zrest { // 在当前段内 zrest -= zrem; xlast += zrem * (x[iseg + 1] - x[iseg]) / z[iseg]; ylast += zrem * (y[iseg + 1] - y[iseg]) / z[iseg]; ip += 1; xnew[ip] = xlast; ynew[ip] = ylast; zrem = z0; if ip >= n - 1 { break; } } else { // 跨到下一段 zrem -= zrest; iseg += 1; if iseg >= n - 1 { // 已到达最后一段 break; } xlast = x[iseg]; ylast = y[iseg]; zrest = z[iseg]; } } // 最后一个点 xnew[n - 1] = x[n - 1]; ynew[n - 1] = y[n - 1]; } #[cfg(test)] mod tests { use super::*; #[test] fn test_gridp_linear() { // 线性函数 y = x let n = 5; let x = vec![0.0, 1.0, 2.0, 3.0, 4.0]; let y = vec![0.0, 1.0, 2.0, 3.0, 4.0]; let mut xnew = vec![0.0; n]; let mut ynew = vec![0.0; n]; gridp(&x, &y, &mut xnew, &mut ynew, n); // 端点应保持不变 assert!((xnew[0] - x[0]).abs() < 1e-10); assert!((xnew[n - 1] - x[n - 1]).abs() < 1e-10); assert!((ynew[0] - y[0]).abs() < 1e-10); assert!((ynew[n - 1] - y[n - 1]).abs() < 1e-10); // 对于线性函数,新网格应与原网格相同 for i in 0..n { assert!((xnew[i] - x[i]).abs() < 1e-10); assert!((ynew[i] - y[i]).abs() < 1e-10); } } #[test] fn test_gridp_curve() { // 曲线 y = x^2 let n = 5; let x = vec![0.0, 1.0, 2.0, 3.0, 4.0]; let y = vec![0.0, 1.0, 4.0, 9.0, 16.0]; let mut xnew = vec![0.0; n]; let mut ynew = vec![0.0; n]; gridp(&x, &y, &mut xnew, &mut ynew, n); // 端点应保持不变 assert!((xnew[0] - 0.0).abs() < 1e-10); assert!((xnew[n - 1] - 4.0).abs() < 1e-10); assert!((ynew[0] - 0.0).abs() < 1e-10); assert!((ynew[n - 1] - 16.0).abs() < 1e-10); // 新网格应单调递增 for i in 1..n { assert!(xnew[i] > xnew[i - 1]); assert!(ynew[i] > ynew[i - 1]); } } #[test] fn test_gridp_uniform_segment_length() { // 验证新网格各段长度大致相等 let n = 10; let x: Vec = (0..n).map(|i| i as f64).collect(); let y: Vec = x.iter().map(|&x| x * x).collect(); let mut xnew = vec![0.0; n]; let mut ynew = vec![0.0; n]; gridp(&x, &y, &mut xnew, &mut ynew, n); // 计算各段长度 let mut lengths = Vec::new(); for i in 1..n { let dx = xnew[i] - xnew[i - 1]; let dy = ynew[i] - ynew[i - 1]; lengths.push((dx * dx + dy * dy).sqrt()); } // 验证各段长度相近 let avg_len: f64 = lengths.iter().sum::() / lengths.len() as f64; for &len in &lengths { assert!((len - avg_len).abs() / avg_len < 0.1); // 误差 < 10% } } }