field.rs

2.5 kB · rust · 83 lines

1use crate::core::error::{shape_error, Result};2use serde::{Deserialize, Serialize};34/// A square grid of f32 samples.5#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]6pub struct Field {7    /// The samples in row-major order.8    pub data: Vec<f32>,9    /// The side length in samples.10    pub size: usize,11}1213impl Field {14    /// Builds a zeroed field of the given side.15    pub fn new(size: usize) -> Field {16        Field {17            data: vec![0.0; size * size],18            size,19        }20    }21    /// Wraps row-major samples of the given side.22    ///23    /// # Errors24    ///25    /// Errors when the count is not the side squared.26    pub fn from_data(data: Vec<f32>, size: usize) -> Result<Field> {27        if data.len() != size * size {28            return shape_error(format!(29                "a field of side {size} needs {} samples, got {}.",30                size * size,31                data.len()32            ));33        }34        Ok(Field { data, size })35    }36    /// Returns the smallest sample.37    pub fn min(&self) -> f32 {38        self.data.iter().cloned().fold(f32::INFINITY, f32::min)39    }40    /// Returns the largest sample.41    pub fn max(&self) -> f32 {42        self.data.iter().cloned().fold(f32::NEG_INFINITY, f32::max)43    }44    /// Returns the mean sample, or zero for an empty field.45    pub fn mean(&self) -> f64 {46        if self.data.is_empty() {47            return 0.0;48        }49        self.data.iter().map(|&v| v as f64).sum::<f64>() / self.data.len() as f6450    }51    /// Returns the samples widened to f64.52    pub fn as_f64(&self) -> Vec<f64> {53        self.data.iter().map(|&v| v as f64).collect()54    }55    /// Returns the samples scaled into 0..1, symmetric about zero on request.56    pub fn normalized(&self, symmetric: bool) -> Vec<f32> {57        if symmetric {58            let m = self59                .data60                .iter()61                .fold(0.0f32, |acc, &v| acc.max(v.abs()))62                .max(f32::EPSILON);63            self.data.iter().map(|&v| (v / m + 1.0) / 2.0).collect()64        } else {65            let lo = self.min();66            let hi = self.max();67            let span = (hi - lo).max(f32::EPSILON);68            self.data.iter().map(|&v| (v - lo) / span).collect()69        }70    }71}7273#[cfg(test)]74mod tests {75    use super::*;7677    #[test]78    fn refuses_a_count_that_is_not_the_side_squared() {79        assert!(Field::from_data(vec![0.0; 3], 2).is_err());80        assert!(Field::from_data(vec![0.0; 5], 2).is_err());81        assert!(Field::from_data(vec![0.0; 4], 2).is_ok());82    }83}