Skip to content

Commit

Permalink
feat: add min max support
Browse files Browse the repository at this point in the history
feat: add support for different index calculation
  • Loading branch information
ekroon committed Dec 29, 2022
1 parent 2aa7381 commit 4dab12e
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 17 deletions.
10 changes: 10 additions & 0 deletions src/indexer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pub mod algorithmic;

pub trait Indexer<T, S> {
fn index(&self, v: T) -> S;
}

pub trait BuildIndexer<T, S> {
type Indexer: Indexer<T, S>;
fn build_indexer<C>(&self, min: T, max: T, ticks: &[C]) -> Self::Indexer;
}
28 changes: 28 additions & 0 deletions src/indexer/algorithmic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::{BuildIndexer, Indexer};

#[derive(Default)]
pub struct BuildAlgorithmicIndexer;

pub struct AlgorithmicIndexer {
min: f64,
max: f64,
idx_per_step: f64,
}

impl Indexer<f64, usize> for AlgorithmicIndexer {
fn index(&self, v: f64) -> usize {
let v = v.clamp(self.min, self.max);
((v - self.min) * self.idx_per_step).round() as usize
}
}

impl BuildIndexer<f64, usize> for BuildAlgorithmicIndexer {
type Indexer = AlgorithmicIndexer;
fn build_indexer<C>(&self, min: f64, max: f64, ticks: &[C]) -> Self::Indexer {
AlgorithmicIndexer {
min,
max,
idx_per_step: (ticks.len() - 1) as f64 / (max - min),
}
}
}
71 changes: 54 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
//! The sparklines crate provides a simple way to generate sparklines.
use crate::indexer::algorithmic::BuildAlgorithmicIndexer;
use crate::indexer::{BuildIndexer, Indexer};

mod indexer;

/// Default ticks for create a string sparkline.
/// ```
/// assert_eq!(sparklines::TICKS, ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█']);
/// ```
pub const TICKS: [char; 8] = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];

/// `StringSparkline` is a struct that can be used to create a string sparkline.
pub struct StringSpark<'a> {
pub struct StringSpark<'a, I = BuildAlgorithmicIndexer>
where
I: BuildIndexer<f64, usize>,
{
min: Option<f64>,
max: Option<f64>,
ticks: &'a [char],
middle_idx: usize,
build_indexer: I,
}

impl<'a> StringSpark<'a> {
Expand All @@ -27,8 +38,33 @@ impl<'a> StringSpark<'a> {
/// ```
pub fn new(ticks: &'a [char]) -> Self {
Self {
ticks: &ticks,
min: None,
max: None,
ticks,
middle_idx: ticks.len() / 2,
build_indexer: Default::default(),
}
}

/// Create a new `SparkLines` instance.
///
/// # Examples
/// ```
/// # use sparklines::TICKS;
///
/// let spark = sparklines::StringSpark::new_with_min_max(&TICKS, 2.0, 3.0);
/// assert_eq!(spark.spark(&[1.0,2.0,3.0,4.0]), "▁▁██");
///
/// let spark = sparklines::StringSpark::new_with_min_max(&TICKS, 1.0, 3.0);
/// assert_eq!(spark.spark(&[0.0,2.0,300.0]), "▁▅█");
/// ```
pub fn new_with_min_max(ticks: &'a [char], min: f64, max: f64) -> Self {
Self {
min: Some(min),
max: Some(max),
ticks,
middle_idx: ticks.len() / 2,
build_indexer: Default::default(),
}
}

Expand All @@ -40,22 +76,24 @@ impl<'a> StringSpark<'a> {
/// ```
pub fn spark(&self, data: &[f64]) -> String {
let mut result = String::with_capacity(data.len() * 4);
let mut min = None;
let mut max = None;
for v in data {
if let Some(m) = min {
if v < m {
let mut min: Option<&f64> = self.min.as_ref();
let mut max: Option<&f64> = self.max.as_ref();
if min.is_none() || max.is_none() {
for v in data {
if let Some(m) = min {
if v < m {
min = Some(v);
}
} else {
min = Some(v);
}
} else {
min = Some(v);
}
if let Some(m) = max {
if v > m {
if let Some(m) = max {
if v > m {
max = Some(v);
}
} else {
max = Some(v);
}
} else {
max = Some(v);
}
}
if let (Some(min), Some(max)) = (min, max) {
Expand All @@ -64,10 +102,9 @@ impl<'a> StringSpark<'a> {
result.push(self.ticks[self.middle_idx]);
})
} else {
let idx_per_step = (self.ticks.len() - 1) as f64 / (max - min);
let indexer = self.build_indexer.build_indexer(*min, *max, self.ticks);
data.iter().for_each(|v| {
let idx = ((v - min) * idx_per_step).round() as usize;
result.push(self.ticks[idx]);
result.push(self.ticks[indexer.index(*v)]);
});
}
}
Expand Down

0 comments on commit 4dab12e

Please sign in to comment.