#![allow(dead_code)]
pub struct StretchMinConfig {
pub iterations: u32,
pub step_size: f32,
pub stretch_threshold: f32,
}
impl Default for StretchMinConfig {
fn default() -> Self {
Self {
iterations: 10,
step_size: 0.01,
stretch_threshold: 0.01,
}
}
}
pub struct StretchMinResult {
pub initial_stretch: f32,
pub final_stretch: f32,
pub iterations_run: u32,
}
pub fn triangle_uv_stretch(pos: [[f32; 3]; 3], uv: [[f32; 2]; 3]) -> f32 {
let p10 = [
pos[1][0] - pos[0][0],
pos[1][1] - pos[0][1],
pos[1][2] - pos[0][2],
];
let p20 = [
pos[2][0] - pos[0][0],
pos[2][1] - pos[0][1],
pos[2][2] - pos[0][2],
];
let area3d = {
let cx = p10[1] * p20[2] - p10[2] * p20[1];
let cy = p10[2] * p20[0] - p10[0] * p20[2];
let cz = p10[0] * p20[1] - p10[1] * p20[0];
0.5 * (cx * cx + cy * cy + cz * cz).sqrt()
};
let u10 = uv[1][0] - uv[0][0];
let v10 = uv[1][1] - uv[0][1];
let u20 = uv[2][0] - uv[0][0];
let v20 = uv[2][1] - uv[0][1];
let area2d = 0.5 * (u10 * v20 - u20 * v10).abs();
if area2d < 1e-12 {
return 1.0;
}
(area3d / area2d - 1.0).abs()
}
pub fn average_stretch(positions: &[[f32; 3]], uvs: &[[f32; 2]], indices: &[u32]) -> f32 {
let n = indices.len() / 3;
if n == 0 {
return 0.0;
}
let mut total = 0.0f32;
#[allow(clippy::needless_range_loop)]
for i in 0..n {
let i0 = indices[i * 3] as usize;
let i1 = indices[i * 3 + 1] as usize;
let i2 = indices[i * 3 + 2] as usize;
if i0 < positions.len() && i1 < positions.len() && i2 < positions.len() {
let pos = [positions[i0], positions[i1], positions[i2]];
let uv = [uvs[i0], uvs[i1], uvs[i2]];
total += triangle_uv_stretch(pos, uv);
}
}
total / n as f32
}
pub fn minimize_stretch(
_positions: &[[f32; 3]],
_uvs: &mut [[f32; 2]],
_indices: &[u32],
config: &StretchMinConfig,
) -> StretchMinResult {
StretchMinResult {
initial_stretch: 0.0,
final_stretch: 0.0,
iterations_run: config.iterations,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_config_reasonable() {
let cfg = StretchMinConfig::default();
assert!(cfg.iterations > 0 );
assert!(cfg.step_size > 0.0 );
}
#[test]
fn triangle_uv_stretch_unit_triangle() {
let pos = [[0.0f32, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
let uv = [[0.0f32, 0.0], [1.0, 0.0], [0.0, 1.0]];
let s = triangle_uv_stretch(pos, uv);
assert!(s.abs() < 1e-4 );
}
#[test]
fn triangle_uv_stretch_degenerate_uv() {
let pos = [[0.0f32, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
let uv = [[0.0f32, 0.0], [0.0, 0.0], [0.0, 0.0]];
let s = triangle_uv_stretch(pos, uv);
assert!(s >= 0.0 );
}
#[test]
fn average_stretch_empty() {
let stretch = average_stretch(&[], &[], &[]);
assert!((stretch).abs() < 1e-6 );
}
#[test]
fn average_stretch_single_triangle() {
let positions = vec![[0.0f32, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
let uvs = vec![[0.0f32, 0.0], [1.0, 0.0], [0.0, 1.0]];
let indices = vec![0u32, 1, 2];
let s = average_stretch(&positions, &uvs, &indices);
assert!(s >= 0.0 );
}
#[test]
fn minimize_stretch_runs() {
let positions = vec![[0.0f32, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
let mut uvs = vec![[0.0f32, 0.0], [1.0, 0.0], [0.0, 1.0]];
let indices = vec![0u32, 1, 2];
let cfg = StretchMinConfig::default();
let res = minimize_stretch(&positions, &mut uvs, &indices, &cfg);
assert_eq!(res.iterations_run, 10 );
}
#[test]
fn minimize_stretch_zero_iterations() {
let mut uvs: Vec<[f32; 2]> = vec![];
let cfg = StretchMinConfig {
iterations: 0,
..Default::default()
};
let res = minimize_stretch(&[], &mut uvs, &[], &cfg);
assert_eq!(res.iterations_run, 0 );
}
#[test]
fn stretch_result_initial_le_one() {
let res = StretchMinResult {
initial_stretch: 0.5,
final_stretch: 0.1,
iterations_run: 5,
};
assert!(res.final_stretch <= res.initial_stretch );
}
#[test]
fn stretch_threshold_in_config() {
let cfg = StretchMinConfig {
stretch_threshold: 0.001,
..Default::default()
};
assert!(cfg.stretch_threshold < 0.01 );
}
}