use crate::data::bounds::DataBounds;
use crate::data::point::DataPoint;
use crate::error::{DataError, DataResult};
use heapless::Vec;
pub struct StaticDataSeriesIter<T, const N: usize> {
data: heapless::Vec<T, N>,
index: usize,
}
impl<T: Clone, const N: usize> Iterator for StaticDataSeriesIter<T, N> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.data.len() {
let item = self.data.get(self.index)?.clone();
self.index += 1;
Some(item)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.data.len() - self.index;
(remaining, Some(remaining))
}
}
impl<T: Clone, const N: usize> ExactSizeIterator for StaticDataSeriesIter<T, N> {}
pub struct StaticDataSeriesRefIter<'a, T> {
data: &'a [T],
index: usize,
}
impl<'a, T> Iterator for StaticDataSeriesRefIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.data.len() {
let item = &self.data[self.index];
self.index += 1;
Some(item)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.data.len() - self.index;
(remaining, Some(remaining))
}
}
impl<'a, T> ExactSizeIterator for StaticDataSeriesRefIter<'a, T> {}
pub trait DataSeries {
type Item: DataPoint;
type Iter: Iterator<Item = Self::Item>;
fn iter(&self) -> Self::Iter;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn calculate_bounds(&self) -> DataResult<()> {
Ok(())
}
fn get(&self, index: usize) -> Option<Self::Item>;
}
#[derive(Debug, Clone)]
pub struct StaticDataSeries<T, const N: usize>
where
T: DataPoint,
{
data: Vec<T, N>,
label: Option<heapless::String<32>>,
}
impl<T, const N: usize> StaticDataSeries<T, N>
where
T: DataPoint,
{
pub fn new() -> Self {
Self {
data: Vec::new(),
label: None,
}
}
pub fn with_label(label: &str) -> Self {
let mut series = Self::new();
series.set_label(label);
series
}
pub fn set_label(&mut self, label: &str) {
let mut string = heapless::String::new();
if string.push_str(label).is_ok() {
self.label = Some(string);
}
}
pub fn label(&self) -> Option<&str> {
self.label.as_ref().map(|s| s.as_str())
}
pub fn push(&mut self, point: T) -> DataResult<()> {
self.data
.push(point)
.map_err(|_| DataError::buffer_full("push data point", N))
}
pub fn extend<I>(&mut self, points: I) -> DataResult<()>
where
I: IntoIterator<Item = T>,
{
for point in points {
self.push(point)?;
}
Ok(())
}
pub fn from_tuples(tuples: &[(T::X, T::Y)]) -> DataResult<Self>
where
T: DataPoint,
{
let mut series = Self::new();
for &(x, y) in tuples {
series.push(T::new(x, y))?;
}
Ok(series)
}
pub fn clear(&mut self) {
self.data.clear();
}
pub fn capacity(&self) -> usize {
N
}
pub fn remaining_capacity(&self) -> usize {
N - self.data.len()
}
pub fn is_full(&self) -> bool {
self.data.len() == N
}
pub fn as_slice(&self) -> &[T] {
&self.data
}
pub fn sort_by_x(&mut self)
where
T::X: Ord,
T: Clone,
{
if self.data.len() <= 16 {
self.insertion_sort_by_x();
} else {
self.merge_sort_by_x();
}
}
fn insertion_sort_by_x(&mut self)
where
T::X: Ord,
T: Clone,
{
for i in 1..self.data.len() {
let key = self.data[i];
let mut j = i;
while j > 0 && self.data[j - 1].x() > key.x() {
self.data[j] = self.data[j - 1];
j -= 1;
}
self.data[j] = key;
}
}
fn merge_sort_by_x(&mut self)
where
T::X: Ord,
T: Clone,
{
let len = self.data.len();
if len <= 1 {
return;
}
let mut temp = heapless::Vec::<T, N>::new();
for _ in 0..len {
if temp.push(self.data[0]).is_err() {
self.insertion_sort_by_x();
return;
}
}
self.merge_sort_recursive(0, len, &mut temp);
}
fn merge_sort_recursive(&mut self, start: usize, end: usize, temp: &mut heapless::Vec<T, N>)
where
T::X: Ord,
T: Clone,
{
if end - start <= 1 {
return;
}
let mid = start + (end - start) / 2;
self.merge_sort_recursive(start, mid, temp);
self.merge_sort_recursive(mid, end, temp);
let mut i = start;
let mut j = mid;
let mut k = start;
for idx in start..end {
temp[idx] = self.data[idx];
}
while i < mid && j < end {
if temp[i].x() <= temp[j].x() {
self.data[k] = temp[i];
i += 1;
} else {
self.data[k] = temp[j];
j += 1;
}
k += 1;
}
while i < mid {
self.data[k] = temp[i];
i += 1;
k += 1;
}
while j < end {
self.data[k] = temp[j];
j += 1;
k += 1;
}
}
pub fn sort_by_y(&mut self)
where
T::Y: Ord,
T: Clone,
{
if self.data.len() <= 16 {
self.insertion_sort_by_y();
} else {
self.merge_sort_by_y();
}
}
fn insertion_sort_by_y(&mut self)
where
T::Y: Ord,
T: Clone,
{
for i in 1..self.data.len() {
let key = self.data[i];
let mut j = i;
while j > 0 && self.data[j - 1].y() > key.y() {
self.data[j] = self.data[j - 1];
j -= 1;
}
self.data[j] = key;
}
}
fn merge_sort_by_y(&mut self)
where
T::Y: Ord,
T: Clone,
{
let len = self.data.len();
if len <= 1 {
return;
}
let mut temp = heapless::Vec::<T, N>::new();
for _ in 0..len {
if temp.push(self.data[0]).is_err() {
self.insertion_sort_by_y();
return;
}
}
self.merge_sort_by_y_recursive(0, len, &mut temp);
}
fn merge_sort_by_y_recursive(
&mut self,
start: usize,
end: usize,
temp: &mut heapless::Vec<T, N>,
) where
T::Y: Ord,
T: Clone,
{
if end - start <= 1 {
return;
}
let mid = start + (end - start) / 2;
self.merge_sort_by_y_recursive(start, mid, temp);
self.merge_sort_by_y_recursive(mid, end, temp);
let mut i = start;
let mut j = mid;
let mut k = start;
for idx in start..end {
temp[idx] = self.data[idx];
}
while i < mid && j < end {
if temp[i].y() <= temp[j].y() {
self.data[k] = temp[i];
i += 1;
} else {
self.data[k] = temp[j];
j += 1;
}
k += 1;
}
while i < mid {
self.data[k] = temp[i];
i += 1;
k += 1;
}
while j < end {
self.data[k] = temp[j];
j += 1;
k += 1;
}
}
}
impl<T, const N: usize> Default for StaticDataSeries<T, N>
where
T: DataPoint,
{
fn default() -> Self {
Self::new()
}
}
impl<T, const N: usize> StaticDataSeries<T, N>
where
T: DataPoint + Clone,
{
pub fn iter_ref(&self) -> StaticDataSeriesRefIter<'_, T> {
StaticDataSeriesRefIter {
data: self.data.as_slice(),
index: 0,
}
}
pub fn data(&self) -> &[T] {
self.data.as_slice()
}
}
impl<T, const N: usize> DataSeries for StaticDataSeries<T, N>
where
T: DataPoint + Clone,
{
type Item = T;
type Iter = StaticDataSeriesIter<T, N>;
fn iter(&self) -> Self::Iter {
StaticDataSeriesIter {
data: self.data.clone(),
index: 0,
}
}
fn len(&self) -> usize {
self.data.len()
}
fn get(&self, index: usize) -> Option<Self::Item> {
self.data.get(index).copied()
}
}
impl<T, const N: usize> StaticDataSeries<T, N>
where
T: DataPoint + Clone,
T::X: PartialOrd + Copy,
T::Y: PartialOrd + Copy,
{
pub fn bounds(&self) -> DataResult<crate::data::bounds::DataBounds<T::X, T::Y>> {
use crate::data::bounds::calculate_bounds;
calculate_bounds(self.iter())
}
}
#[derive(Debug, Clone)]
pub struct MultiSeries<T, const SERIES: usize, const POINTS: usize>
where
T: DataPoint,
{
series: Vec<StaticDataSeries<T, POINTS>, SERIES>,
}
impl<T, const SERIES: usize, const POINTS: usize> MultiSeries<T, SERIES, POINTS>
where
T: DataPoint,
{
pub fn new() -> Self {
Self { series: Vec::new() }
}
pub fn add_series(&mut self, series: StaticDataSeries<T, POINTS>) -> DataResult<usize> {
let index = self.series.len();
self.series
.push(series)
.map_err(|_| DataError::buffer_full("add data series", SERIES))?;
Ok(index)
}
pub fn get_series(&self, index: usize) -> Option<&StaticDataSeries<T, POINTS>> {
self.series.get(index)
}
pub fn get_series_mut(&mut self, index: usize) -> Option<&mut StaticDataSeries<T, POINTS>> {
self.series.get_mut(index)
}
pub fn series_count(&self) -> usize {
self.series.len()
}
pub fn is_empty(&self) -> bool {
self.series.is_empty()
}
pub fn iter_series(&self) -> core::slice::Iter<StaticDataSeries<T, POINTS>> {
self.series.iter()
}
pub fn combined_bounds(&self) -> DataResult<DataBounds<T::X, T::Y>>
where
T: DataPoint + Clone,
T::X: PartialOrd + Copy,
T::Y: PartialOrd + Copy,
{
if self.series.is_empty() {
return Err(DataError::insufficient_data(
"calculate combined bounds",
1,
0,
));
}
let mut combined_bounds = self.series[0].bounds()?;
for series in self.series.iter().skip(1) {
let series_bounds = series.bounds()?;
combined_bounds = combined_bounds.merge(&series_bounds);
}
Ok(combined_bounds)
}
pub fn clear(&mut self) {
self.series.clear();
}
}
impl<T, const SERIES: usize, const POINTS: usize> Default for MultiSeries<T, SERIES, POINTS>
where
T: DataPoint,
{
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "animations")]
#[derive(Debug, Clone)]
pub struct SlidingWindowSeries<T, const N: usize>
where
T: DataPoint + Copy,
{
buffer: [Option<T>; N],
head: usize,
count: usize,
full: bool,
label: Option<heapless::String<32>>,
}
#[cfg(feature = "animations")]
impl<T, const N: usize> SlidingWindowSeries<T, N>
where
T: DataPoint + Copy,
{
pub fn new() -> Self {
Self {
buffer: [None; N],
head: 0,
count: 0,
full: false,
label: None,
}
}
pub fn with_label(label: &str) -> Self {
let mut series = Self::new();
series.set_label(label);
series
}
pub fn set_label(&mut self, label: &str) {
let mut string = heapless::String::new();
if string.push_str(label).is_ok() {
self.label = Some(string);
}
}
pub fn label(&self) -> Option<&str> {
self.label.as_ref().map(|s| s.as_str())
}
pub fn push(&mut self, point: T) {
self.buffer[self.head] = Some(point);
self.head = (self.head + 1) % N;
if self.full {
} else {
self.count += 1;
if self.count == N {
self.full = true;
}
}
}
pub fn current_len(&self) -> usize {
self.count
}
pub fn is_full(&self) -> bool {
self.full
}
pub fn capacity(&self) -> usize {
N
}
pub fn clear(&mut self) {
self.buffer = [None; N];
self.head = 0;
self.count = 0;
self.full = false;
}
pub fn iter_chronological(&self) -> impl Iterator<Item = T> + '_ {
let start_idx = if self.full { self.head } else { 0 };
let len = if self.full { N } else { self.count };
(0..len).filter_map(move |i| {
let idx = (start_idx + i) % N;
self.buffer[idx]
})
}
}
#[cfg(feature = "animations")]
impl<T, const N: usize> Default for SlidingWindowSeries<T, N>
where
T: DataPoint + Copy,
{
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "animations")]
impl<T, const N: usize> DataSeries for SlidingWindowSeries<T, N>
where
T: DataPoint + Copy,
{
type Item = T;
type Iter = <heapless::Vec<T, N> as IntoIterator>::IntoIter;
fn iter(&self) -> Self::Iter {
let mut vec = heapless::Vec::new();
for point in self.iter_chronological() {
let _ = vec.push(point);
}
vec.into_iter()
}
fn len(&self) -> usize {
self.current_len()
}
fn get(&self, index: usize) -> Option<Self::Item> {
if index >= self.current_len() {
return None;
}
let start_idx = if self.full { self.head } else { 0 };
let actual_idx = (start_idx + index) % N;
self.buffer[actual_idx]
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::data::point::Point2D;
#[test]
fn test_static_series_creation() {
let series: StaticDataSeries<Point2D, 10> = StaticDataSeries::new();
assert_eq!(series.len(), 0);
assert!(series.is_empty());
assert_eq!(series.capacity(), 10);
}
#[test]
fn test_static_series_push() {
let mut series: StaticDataSeries<Point2D, 10> = StaticDataSeries::new();
let point = Point2D::new(1.0, 2.0);
series.push(point).unwrap();
assert_eq!(series.len(), 1);
assert_eq!(series.get(0), Some(point));
}
#[test]
fn test_static_series_from_tuples() {
let tuples = [(1.0, 2.0), (3.0, 4.0), (5.0, 6.0)];
let series: StaticDataSeries<Point2D, 10> = StaticDataSeries::from_tuples(&tuples).unwrap();
assert_eq!(series.len(), 3);
assert_eq!(series.get(0), Some(Point2D::new(1.0, 2.0)));
assert_eq!(series.get(1), Some(Point2D::new(3.0, 4.0)));
assert_eq!(series.get(2), Some(Point2D::new(5.0, 6.0)));
}
#[test]
fn test_multi_series() {
let mut multi: MultiSeries<Point2D, 5, 10> = MultiSeries::new();
let mut series1 = StaticDataSeries::with_label("Series 1");
series1.push(Point2D::new(1.0, 2.0)).unwrap();
let index = multi.add_series(series1).unwrap();
assert_eq!(index, 0);
assert_eq!(multi.series_count(), 1);
let retrieved_series = multi.get_series(0).unwrap();
assert_eq!(retrieved_series.label(), Some("Series 1"));
assert_eq!(retrieved_series.len(), 1);
}
#[cfg(feature = "animations")]
#[test]
fn test_sliding_window_series() {
let mut series: SlidingWindowSeries<Point2D, 3> = SlidingWindowSeries::new();
series.push(Point2D::new(1.0, 1.0));
series.push(Point2D::new(2.0, 2.0));
series.push(Point2D::new(3.0, 3.0));
assert_eq!(series.current_len(), 3);
assert!(series.is_full());
series.push(Point2D::new(4.0, 4.0));
assert_eq!(series.current_len(), 3);
let points: Vec<Point2D, 3> = series.iter().collect();
assert_eq!(points.len(), 3);
assert_eq!(points[0], Point2D::new(2.0, 2.0));
assert_eq!(points[1], Point2D::new(3.0, 3.0));
assert_eq!(points[2], Point2D::new(4.0, 4.0));
}
}