use std::ops::RangeInclusive;
use crate::scales::value::{DataColumn, Value};
use super::{Scale, ScaleTypeKind, TemporalUnit};
pub fn continuous<T>(domain: RangeInclusive<T>) -> Scale
where
T: Into<Value> + Copy,
{
Scale::new(ScaleTypeKind::Continuous).domain_continuous(*domain.start(), *domain.end())
}
pub fn discrete(domain: impl IntoIterator<Item = Value>) -> Scale {
Scale::new(ScaleTypeKind::Discrete).domain_discrete(domain)
}
pub fn binned<T>(domain: RangeInclusive<T>, edges: Vec<f64>) -> Scale
where
T: Into<Value> + Copy,
{
Scale::new(ScaleTypeKind::Binned)
.domain_continuous(*domain.start(), *domain.end())
.with_bins(edges)
}
pub fn identity() -> Scale {
Scale::new(ScaleTypeKind::Identity)
}
pub fn temporal<T>(domain: RangeInclusive<T>) -> Scale
where
T: Into<Value> + Copy,
{
let start: Value = (*domain.start()).into();
let unit = match &start {
Value::Date(_) => TemporalUnit::Date,
Value::DateTime(_) => TemporalUnit::DateTime,
Value::Time(_) => TemporalUnit::Time,
Value::Duration(_) => TemporalUnit::Duration,
other => panic!(
"scale::temporal: expected a temporal value (Date / DateTime / Time / Duration), got {other:?}"
),
};
Scale::new(ScaleTypeKind::Temporal(unit)).domain_continuous(*domain.start(), *domain.end())
}
pub fn ordinal(domain: impl IntoIterator<Item = impl Into<Value>>) -> Scale {
Scale::new(ScaleTypeKind::Ordinal).domain_discrete(domain.into_iter().map(Into::into))
}
pub fn continuous_from_data(col: &DataColumn) -> Scale {
let scale = Scale::new(ScaleTypeKind::Continuous);
if col.is_empty() {
return scale;
}
let mut lo = f64::INFINITY;
let mut hi = f64::NEG_INFINITY;
for i in 0..col.len() {
if let Some(n) = col.get(i).as_number() {
if n.is_finite() {
lo = lo.min(n);
hi = hi.max(n);
}
}
}
if lo.is_finite() && hi.is_finite() && lo <= hi {
scale.domain_continuous(lo, hi)
} else {
scale
}
}