use super::{utils::slice_view1, Base, ChannelAxis, Error, Numeric, NumericAxis, OrderedNumeric};
use ndarray::{Array1, SliceInfoElem};
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct LogarithmicAxis {
pub extents: [f64; 2],
pub size: usize,
pub name: String,
pub units: Option<String>,
}
impl Base for LogarithmicAxis {
fn size(&self) -> Result<usize, Error> {
Ok(self.size)
}
fn slice(&self, slice_info: &SliceInfoElem) -> Result<ChannelAxis, Error> {
if matches!(slice_info, SliceInfoElem::NewAxis) {
return Err("SliceInfoElem::NewAxis variant is not supported".into());
}
let coordinates = self.coordinates()?;
let slice = slice_view1(coordinates.view(), slice_info)?;
if slice.is_empty() {
return Err("LogarithmicAxis cannot be empty".into());
}
Ok(Self {
extents: [*slice.first().unwrap(), *slice.last().unwrap()],
size: slice.len(),
name: self.name.clone(),
units: self.units.clone(),
}
.into())
}
}
impl Numeric for LogarithmicAxis {
fn name(&self) -> &str {
&self.name
}
fn units(&self) -> &Option<String> {
&self.units
}
fn coordinates(&self) -> Result<Array1<f64>, Error> {
if self.size()? == 0 {
return Err("LogarithmicAxis cannot be empty".into());
}
if self.extents[0] <= 0f64 || self.extents[1] <= 0f64 {
return Err("LogarithmicAxis extents must be non-negative".into());
}
if self.size()? == 1 {
if self.extents[0] == self.extents[1] {
return Ok(Array1::from_vec(vec![self.extents[0]]));
} else {
return Err("LogarithmicAxis of size 1 must have identical extents".into());
}
}
let base = (self.extents[1] / self.extents[0]).powf(1f64 / ((self.size()? as f64) - 1f64));
let coordinates = self.extents()[0]
* Array1::<f64>::logspace(base, 0f64, self.size()? as f64 - 1f64, self.size()?);
Ok(coordinates)
}
fn make_monotonic(&self) -> Result<Vec<NumericAxis>, Error> {
todo!()
}
}
impl OrderedNumeric for LogarithmicAxis {
fn extents(&self) -> [f64; 2] {
self.extents
}
}
#[cfg(test)]
mod tests {
use super::{LogarithmicAxis, Numeric};
#[test]
fn coordinates() {
let coordinates = LogarithmicAxis {
name: "".into(),
units: None,
extents: [3.14f64, 3140f64],
size: 4,
}
.coordinates()
.unwrap();
let eps = 1e-6;
for (ours, gt) in coordinates.iter().zip(vec![3.14, 31.4, 314f64, 3140f64]) {
assert!((ours - gt).abs() < eps);
}
}
}