#[cfg(feature = "python")]
use pyo3::prelude::*;
use super::*;
use crate::{calc_config, calc_input_names, calc_output_names, py_json_methods};
#[cfg_attr(feature = "python", pyclass)]
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct InverseAffine {
input_name: String,
slope: f64,
offset: f64,
save_outputs: bool,
#[serde(default)]
output_unit: Option<String>,
#[serde(skip)]
input_index: usize,
#[serde(skip)]
output_index: usize,
}
impl InverseAffine {
pub fn new(input_name: String, slope: f64, offset: f64, save_outputs: bool) -> Box<Self> {
let input_index = usize::MAX;
let output_index = usize::MAX;
Box::new(Self {
input_name,
slope,
offset,
save_outputs,
output_unit: None,
input_index,
output_index,
})
}
pub fn with_output_unit(mut self: Box<Self>, unit: impl Into<String>) -> Box<Self> {
self.output_unit = Some(unit.into());
self
}
}
py_json_methods!(
InverseAffine,
Calc,
#[new]
#[pyo3(signature = (input_name, slope, offset, save_outputs, output_unit = None))]
fn py_new(
input_name: String,
slope: f64,
offset: f64,
save_outputs: bool,
output_unit: Option<String>,
) -> Self {
let mut calc = Self::new(input_name, slope, offset, save_outputs);
calc.output_unit = output_unit;
*calc
}
);
#[typetag::serde]
impl Calc for InverseAffine {
fn init(
&mut self,
_: ControllerCtx,
input_indices: Vec<usize>,
output_range: Range<usize>,
) -> Result<(), String> {
self.input_index = input_indices[0];
self.output_index = output_range.clone().next().unwrap();
Ok(())
}
fn terminate(&mut self) -> Result<(), String> {
self.input_index = usize::MAX;
self.output_index = usize::MAX;
Ok(())
}
fn eval(&mut self, tape: &mut [f64]) -> Result<(), String> {
let x = tape[self.input_index];
let y = (x - self.offset) / self.slope;
tape[self.output_index] = y;
Ok(())
}
fn get_input_map(&self) -> BTreeMap<CalcInputName, FieldName> {
let mut map = BTreeMap::new();
map.insert("x".to_owned(), self.input_name.clone());
map
}
fn update_input_map(&mut self, field: &str, source: &str) -> Result<(), String> {
if field == "x" {
self.input_name = source.to_owned();
} else {
return Err(format!("Unrecognized field {field}"));
}
Ok(())
}
fn get_output_units(&self) -> Vec<Option<String>> {
vec![self.output_unit.clone()]
}
calc_config!(slope, offset);
calc_input_names!(x);
calc_output_names!(y);
}