101 lines
2.4 KiB
Rust
101 lines
2.4 KiB
Rust
/// Normalizes a gesture by removing translation and scale
|
|
///
|
|
/// # Arguments
|
|
/// * `points` - Slice of Point structs representing the gesture
|
|
///
|
|
/// # Returns
|
|
/// Vector of normalized points centered at origin with unit scale
|
|
///
|
|
/// # Example
|
|
/// ```
|
|
/// use redoal::point::Point;
|
|
/// use redoal::normalize;
|
|
///
|
|
/// let points = vec![
|
|
/// Point::new(1.0, 2.0),
|
|
/// Point::new(3.0, 4.0),
|
|
/// Point::new(5.0, 6.0),
|
|
/// ];
|
|
/// let normalized = normalize(&points);
|
|
/// ```
|
|
use crate::Point;
|
|
|
|
pub fn normalize(points: &[Point]) -> Vec<Point> {
|
|
let n = points.len() as f64;
|
|
|
|
// Calculate centroid
|
|
let cx = points.iter().map(|p| p.x).sum::<f64>() / n;
|
|
let cy = points.iter().map(|p| p.y).sum::<f64>() / n;
|
|
|
|
// Center points at origin
|
|
let mut out: Vec<Point> = points.iter().map(|p| {
|
|
Point {
|
|
x: p.x - cx,
|
|
y: p.y - cy,
|
|
}
|
|
}).collect();
|
|
|
|
// Find maximum radius
|
|
let mut max_r = 0.0;
|
|
for p in &out {
|
|
let r = (p.x * p.x + p.y * p.y).sqrt();
|
|
if r > max_r {
|
|
max_r = r;
|
|
}
|
|
}
|
|
|
|
// Scale to unit size
|
|
if max_r > 0.0 {
|
|
for p in &mut out {
|
|
p.x /= max_r;
|
|
p.y /= max_r;
|
|
}
|
|
}
|
|
|
|
out
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::point::Point;
|
|
|
|
#[test]
|
|
fn test_normalize_centers_points() {
|
|
let points = vec![
|
|
Point::new(1.0, 1.0),
|
|
Point::new(2.0, 2.0),
|
|
Point::new(3.0, 3.0),
|
|
];
|
|
|
|
let normalized = normalize(&points);
|
|
let sum_x: f64 = normalized.iter().map(|p| p.x).sum();
|
|
let sum_y: f64 = normalized.iter().map(|p| p.y).sum();
|
|
|
|
// Centroid should be at origin
|
|
assert!( (sum_x.abs() < 1e-10) );
|
|
assert!( (sum_y.abs() < 1e-10) );
|
|
}
|
|
|
|
#[test]
|
|
fn test_normalize_scales_points() {
|
|
let points = vec![
|
|
Point::new(0.0, 0.0),
|
|
Point::new(10.0, 0.0),
|
|
Point::new(5.0, 5.0),
|
|
];
|
|
|
|
let normalized = normalize(&points);
|
|
let max_r = normalized.iter().map(|p| (p.x * p.x + p.y * p.y).sqrt()).fold(0.0, |a: f64, b| a.max(b));
|
|
|
|
// All points should be within unit circle
|
|
assert!( (max_r - 1.0).abs() < 1e-10 );
|
|
}
|
|
|
|
#[test]
|
|
fn test_normalize_empty() {
|
|
let points: Vec<Point> = vec![];
|
|
let normalized = normalize(&points);
|
|
assert_eq!(normalized.len(), 0);
|
|
}
|
|
} |