162 lines
4.3 KiB
Rust
162 lines
4.3 KiB
Rust
//! 网格点评估
|
|
//!
|
|
//! 原始 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<f64> = (0..n).map(|i| i as f64).collect();
|
|
let y: Vec<f64> = 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::<f64>() / lengths.len() as f64;
|
|
for &len in &lengths {
|
|
assert!((len - avg_len).abs() / avg_len < 0.1); // 误差 < 10%
|
|
}
|
|
}
|
|
}
|