use crate::coord::ranged1d::{
AsRangedCoord, KeyPointHint, NoDefaultFormatting, Ranged, ReversibleRanged, ValueFormatter,
};
use std::ops::Range;
pub trait DiscreteRanged
where
Self: Ranged,
{
fn size(&self) -> usize;
fn index_of(&self, value: &Self::ValueType) -> Option<usize>;
#[allow(clippy::wrong_self_convention)]
fn from_index(&self, index: usize) -> Option<Self::ValueType>;
fn values(&self) -> DiscreteValueIter<'_, Self>
where
Self: Sized,
{
DiscreteValueIter(self, 0, self.size())
}
fn previous(&self, value: &Self::ValueType) -> Option<Self::ValueType> {
if let Some(idx) = self.index_of(value) {
if idx > 0 {
return self.from_index(idx - 1);
}
}
None
}
fn next(&self, value: &Self::ValueType) -> Option<Self::ValueType> {
if let Some(idx) = self.index_of(value) {
if idx + 1 < self.size() {
return self.from_index(idx + 1);
}
}
None
}
}
#[derive(Clone)]
pub struct SegmentedCoord<D: DiscreteRanged>(D);
pub trait IntoSegmentedCoord: AsRangedCoord
where
Self::CoordDescType: DiscreteRanged,
{
fn into_segmented(self) -> SegmentedCoord<Self::CoordDescType> {
SegmentedCoord(self.into())
}
}
impl<R: AsRangedCoord> IntoSegmentedCoord for R where R::CoordDescType: DiscreteRanged {}
#[derive(Clone, Debug)]
pub enum SegmentValue<T> {
Exact(T),
CenterOf(T),
Last,
}
impl<T, D: DiscreteRanged + Ranged<ValueType = T>> ValueFormatter<SegmentValue<T>>
for SegmentedCoord<D>
where
D: ValueFormatter<T>,
{
fn format(value: &SegmentValue<T>) -> String {
match value {
SegmentValue::Exact(ref value) => D::format(value),
SegmentValue::CenterOf(ref value) => D::format(value),
_ => "".to_string(),
}
}
}
impl<D: DiscreteRanged> Ranged for SegmentedCoord<D> {
type FormatOption = NoDefaultFormatting;
type ValueType = SegmentValue<D::ValueType>;
fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
let margin = ((limit.1 - limit.0) as f32 / self.0.size() as f32).round() as i32;
match value {
SegmentValue::Exact(coord) => self.0.map(coord, (limit.0, limit.1 - margin)),
SegmentValue::CenterOf(coord) => {
let left = self.0.map(coord, (limit.0, limit.1 - margin));
if let Some(idx) = self.0.index_of(coord) {
if idx + 1 < self.0.size() {
let right = self.0.map(
&self.0.from_index(idx + 1).unwrap(),
(limit.0, limit.1 - margin),
);
return (left + right) / 2;
}
}
left + margin / 2
}
SegmentValue::Last => limit.1,
}
}
fn key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<Self::ValueType> {
self.0
.key_points(hint)
.into_iter()
.map(SegmentValue::CenterOf)
.collect()
}
fn range(&self) -> Range<Self::ValueType> {
let range = self.0.range();
SegmentValue::Exact(range.start)..SegmentValue::Exact(range.end)
}
}
impl<D: DiscreteRanged> DiscreteRanged for SegmentedCoord<D> {
fn size(&self) -> usize {
self.0.size() + 1
}
fn index_of(&self, value: &Self::ValueType) -> Option<usize> {
match value {
SegmentValue::Exact(value) => self.0.index_of(value),
SegmentValue::CenterOf(value) => self.0.index_of(value),
SegmentValue::Last => Some(self.0.size()),
}
}
fn from_index(&self, idx: usize) -> Option<Self::ValueType> {
match idx {
idx if idx < self.0.size() => self.0.from_index(idx).map(SegmentValue::Exact),
idx if idx == self.0.size() => Some(SegmentValue::Last),
_ => None,
}
}
}
impl<T> From<T> for SegmentValue<T> {
fn from(this: T) -> SegmentValue<T> {
SegmentValue::Exact(this)
}
}
impl<DC: DiscreteRanged> ReversibleRanged for DC {
fn unmap(&self, input: i32, limit: (i32, i32)) -> Option<Self::ValueType> {
let idx = (f64::from(input - limit.0) * (self.size() as f64) / f64::from(limit.1 - limit.0))
.floor() as usize;
self.from_index(idx)
}
}
pub struct DiscreteValueIter<'a, T: DiscreteRanged>(&'a T, usize, usize);
impl<'a, T: DiscreteRanged> Iterator for DiscreteValueIter<'a, T> {
type Item = T::ValueType;
fn next(&mut self) -> Option<T::ValueType> {
if self.1 >= self.2 {
return None;
}
let idx = self.1;
self.1 += 1;
self.0.from_index(idx)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_value_iter() {
let range: crate::coord::ranged1d::types::RangedCoordi32 = (-10..10).into();
let values: Vec<_> = range.values().collect();
assert_eq!(21, values.len());
for (expected, value) in (-10..=10).zip(values) {
assert_eq!(expected, value);
}
assert_eq!(range.next(&5), Some(6));
assert_eq!(range.next(&10), None);
assert_eq!(range.previous(&-10), None);
assert_eq!(range.previous(&10), Some(9));
}
#[test]
fn test_centric_coord() {
let coord = (0..10).into_segmented();
assert_eq!(coord.size(), 12);
for i in 0..=11 {
match coord.from_index(i as usize) {
Some(SegmentValue::Exact(value)) => assert_eq!(i, value),
Some(SegmentValue::Last) => assert_eq!(i, 11),
_ => panic!(),
}
}
for (kps, idx) in coord.key_points(20).into_iter().zip(0..) {
match kps {
SegmentValue::CenterOf(value) if value <= 10 => assert_eq!(value, idx),
_ => panic!(),
}
}
assert_eq!(coord.map(&SegmentValue::CenterOf(0), (0, 24)), 1);
assert_eq!(coord.map(&SegmentValue::Exact(0), (0, 24)), 0);
assert_eq!(coord.map(&SegmentValue::Exact(1), (0, 24)), 2);
}
}