Repository: plotters-rs/plotters (language: rust)

Issue:
Refactor the DiscreteRanged trait
# Background

Now the discrete range trait doesn't seem clean because of the following issue:

* No bound check, the `next_value` and `previous_value` requires always returns a value 
* Unknown number of distinct values
* Linspace coord should be a discrete range, however this can't be used in `Histogram` since the hash table requires `Equal` trait but `f64` doesn't impl the trait. 

However, all the discrete range is equivalent to a integer range, thus we doesn't actually need put any restriction on the traits for the actual values. (Later we may use an array as an discrete coordinate and lifting the trait bound will make this possible)

# Drawbacks

In order to know the number of values and handle the bound correctly, we have to make most of the `RangeParam` not an unit type, and this will makes `Histogram::new` unsable to applied to most of the coord types. Thus this should be considered as an breaking change. 



Discussion / hints:

Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index 45dd2391..f6febe33 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -79,7 +79,6 @@ rand_distr = "0.2.2"
 criterion = "0.3.0"
 rayon = "1.2.0"
 rand_xorshift = "0.2.0"
-cpuprofiler = "*"
 
 [target.'cfg(target_arch = "wasm32")'.dev-dependencies]
 wasm-bindgen-test = "0.3.4"
diff --git a/examples/histogram.rs b/examples/histogram.rs
index c33363f8..7e1d31f1 100644
--- a/examples/histogram.rs
+++ b/examples/histogram.rs
@@ -10,13 +10,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
         .y_label_area_size(40)
         .margin(5)
         .caption("Histogram Test", ("sans-serif", 50.0).into_font())
-        .build_ranged(0u32..10u32, 0u32..10u32)?;
+        .build_ranged((0u32..10u32).into_centric(), 0u32..10u32)?;
 
     chart
         .configure_mesh()
         .disable_x_mesh()
         .line_style_1(&WHITE.mix(0.3))
-        .x_label_offset(30)
         .y_desc("Count")
         .x_desc("Bucket")
         .axis_desc_style(("sans-serif", 15).into_font())
diff --git a/examples/normal-dist2.rs b/examples/normal-dist2.rs
index 6155ea65..eaebfdc5 100644
--- a/examples/normal-dist2.rs
+++ b/examples/normal-dist2.rs
@@ -28,7 +28,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
         .set_label_area_size(LabelAreaPosition::Bottom, 60)
         .set_label_area_size(LabelAreaPosition::Right, 60)
         .build_ranged(-4f64..4f64, 0f64..0.1)?
-        .set_secondary_coord((-40i32..40i32).into_centric(), 0u32..500u32);
+        .set_secondary_coord(-40i32..40i32, 0u32..500u32);
 
     chart
         .configure_mesh()
diff --git a/src/coord/category.rs b/src/coord/category.rs
index 805bad2c..f6dc6864 100644
--- a/src/coord/category.rs
+++ b/src/coord/category.rs
@@ -2,7 +2,7 @@ use std::fmt;
 use std::ops::Range;
 use std::rc::Rc;
 
-use super::{AsRangedCoord, Ranged};
+use super::Ranged;
 
 /// The category coordinate
 pub struct Category<T: PartialEq> {
@@ -172,11 +172,6 @@ impl<T: PartialEq> Ranged for Category<T> {
     }
 }
 
-impl<T: PartialEq> AsRangedCoord for Category<T> {
-    type CoordDescType = Self;
-    type Value = Category<T>;
-}
-
 #[cfg(test)]
 mod test {
     use super::*;
diff --git a/src/coord/datetime.rs b/src/coord/datetime.rs
index cb96f937..be4e75dc 100644
--- a/src/coord/datetime.rs
+++ b/src/coord/datetime.rs
@@ -135,14 +135,20 @@ impl<Z: TimeZone> Ranged for RangedDate<Z> {
 }
 
 impl<Z: TimeZone> DiscreteRanged for RangedDate<Z> {
-    type RangeParameter = ();
-    fn get_range_parameter(&self) {}
-    fn next_value(this: &Date<Z>, _: &()) -> Date<Z> {
-        this.clone() + Duration::days(1)
+    fn size(&self) -> usize {
+        ((self.1.clone() - self.0.clone()).num_days().max(0) + 1) as usize
     }
 
-    fn previous_value(this: &Date<Z>, _: &()) -> Date<Z> {
-        this.clone() - Duration::days(1)
+    fn index_of(&self, value: &Date<Z>) -> Option<usize> {
+        let ret = (value.clone() - self.0.clone()).num_days();
+        if ret < 0 {
+            return None;
+        }
+        Some(ret as usize)
+    }
+
+    fn from_index(&self, index: usize) -> Option<Date<Z>> {
+        Some(self.0.clone() + Duration::days(index as i64))
     }
 }
 
@@ -159,11 +165,6 @@ impl<Z: TimeZone> AsRangedCoord for Range<Date<Z>> {
 #[derive(Clone)]
 pub struct Monthly<T: TimeValue>(Range<T>);
 
-impl<T: TimeValue + Clone> AsRangedCoord for Monthly<T> {
-    type CoordDescType = Monthly<T>;
-    type Value = T;
-}
-
 impl<T: TimeValue + Clone> Ranged for Monthly<T> {
     type ValueType = T;
 
@@ -267,28 +268,50 @@ impl<T: TimeValue + Clone> Ranged for Monthly<T> {
 }
 
 impl<T: TimeValue + Clone> DiscreteRanged for Monthly<T> {
-    type RangeParameter = ();
-    fn get_range_parameter(&self) {}
-    fn next_value(this: &T, _: &()) -> T {
-        let mut year = this.date_ceil().year();
-        let mut month = this.date_ceil().month();
-        month += 1;
-        if month == 13 {
-            month = 1;
-            year += 1;
+    fn size(&self) -> usize {
+        let (start_year, start_month) = {
+            let ceil = self.0.start.date_ceil();
+            (ceil.year(), ceil.month())
+        };
+        let (end_year, end_month) = {
+            let floor = self.0.end.date_floor();
+            (floor.year(), floor.month())
+        };
+        ((end_year - start_year).max(0) * 12
+            + (1 - start_month as i32)
+            + (end_month as i32 - 1)
+            + 1)
+        .max(0) as usize
+    }
+
+    fn index_of(&self, value: &T) -> Option<usize> {
+        let this_year = value.date_floor().year();
+        let this_month = value.date_floor().month();
+
+        let start_year = self.0.start.date_ceil().year();
+        let start_month = self.0.start.date_ceil().month();
+
+        let ret = (this_year - start_year).max(0) * 12
+            + (1 - start_month as i32)
+            + (this_month as i32 - 1);
+        if ret >= 0 {
+            return Some(ret as usize);
         }
-        T::earliest_after_date(this.timezone().ymd(year, month, this.date_ceil().day()))
+        None
     }
 
-    fn previous_value(this: &T, _: &()) -> T {
-        let mut year = this.clone().date_floor().year();
-        let mut month = this.clone().date_floor().month();
-        month -= 1;
-        if month == 0 {
-            month = 12;
-            year -= 1;
+    fn from_index(&self, index: usize) -> Option<T> {
+        if index == 0 {
+            return Some(T::earliest_after_date(self.0.start.date_ceil()));
         }
-        T::earliest_after_date(this.timezone().ymd(year, month, this.date_floor().day()))
+        let index_from_start_year = index + (self.0.start.date_ceil().month() - 1) as usize;
+        let year = self.0.start.date_ceil().year() + index_from_start_year as i32 / 12;
+        let month = index_from_start_year % 12;
+        Some(T::earliest_after_date(self.0.start.timezone().ymd(
+            year,
+            month as u32 + 1,
+            1,
+        )))
     }
 }
 
@@ -296,11 +319,6 @@ impl<T: TimeValue + Clone> DiscreteRanged for Monthly<T> {
 #[derive(Clone)]
 pub struct Yearly<T: TimeValue>(Range<T>);
 
-impl<T: TimeValue + Clone> AsRangedCoord for Yearly<T> {
-    type CoordDescType = Yearly<T>;
-    type Value = T;
-}
-
 fn generate_yearly_keypoints<T: TimeValue>(
     max_points: usize,
     mut start_year: i32,
@@ -380,14 +398,29 @@ impl<T: TimeValue + Clone> Ranged for Yearly<T> {
 }
 
 impl<T: TimeValue + Clone> DiscreteRanged for Yearly<T> {
-    type RangeParameter = ();
-    fn get_range_parameter(&self) {}
-    fn next_value(this: &T, _: &()) -> T {
-        T::earliest_after_date(this.timezone().ymd(this.date_floor().year() + 1, 1, 1))
+    fn size(&self) -> usize {
+        let year_start = self.0.start.date_ceil().year();
+        let year_end = self.0.end.date_floor().year();
+        (year_end - year_start + 1) as usize
+    }
+
+    fn index_of(&self, value: &T) -> Option<usize> {
+        let year_start = self.0.start.date_ceil().year();
+        let year_value = value.date_floor().year();
+        let ret = year_value - year_start;
+        if ret < 0 {
+            return None;
+        }
+        Some(ret as usize)
     }
 
-    fn previous_value(this: &T, _: &()) -> T {
-        T::earliest_after_date(this.timezone().ymd(this.date_ceil().year() - 1, 1, 1))
+    fn from_index(&self, index: usize) -> Option<T> {
+        let year = self.0.start.date_ceil().year() + index as i32;
+        let ret = T::earliest_after_date(self.0.start.timezone().ymd(year, 1, 1));
+        if ret.date_ceil() <= self.0.start.date_floor() {
+            return Some(self.0.start.clone());
+        }
+        Some(ret)
     }
 }
 
@@ -951,4 +984,39 @@ mod test {
         assert!(max == min);
         assert_eq!(max, 3600 * 2);
     }
+
+    #[test]
+    fn test_date_discrete() {
+        let coord: RangedDate<Utc> = (Utc.ymd(2019, 1, 1)..Utc.ymd(2019, 12, 31)).into();
+        assert_eq!(coord.size(), 365);
+        assert_eq!(coord.index_of(&Utc.ymd(2019, 2, 28)), Some(31 + 28 - 1));
+        assert_eq!(coord.from_index(364), Some(Utc.ymd(2019, 12, 31)));
+    }
+
+    #[test]
+    fn test_monthly_discrete() {
+        let coord1 = (Utc.ymd(2019, 1, 10)..Utc.ymd(2019, 12, 31)).monthly();
+        let coord2 = (Utc.ymd(2019, 1, 10)..Utc.ymd(2020, 1, 1)).monthly();
+        assert_eq!(coord1.size(), 12);
+        assert_eq!(coord2.size(), 13);
+
+        for i in 1..=12 {
+            assert_eq!(coord1.from_index(i - 1).unwrap().month(), i as u32);
+            assert_eq!(
+                coord1.index_of(&coord1.from_index(i - 1).unwrap()).unwrap(),
+                i - 1
+            );
+        }
+    }
+
+    #[test]
+    fn test_yearly_discrete() {
+        let coord1 = (Utc.ymd(2000, 1, 10)..Utc.ymd(2019, 12, 31)).yearly();
+        assert_eq!(coord1.size(), 20);
+
+        for i in 0..20 {
+            assert_eq!(coord1.from_index(i).unwrap().year(), 2000 + i as i32);
+            assert_eq!(coord1.index_of(&coord1.from_index(i).unwrap()).unwrap(), i);
+        }
+    }
 }
diff --git a/src/coord/discrete.rs b/src/coord/discrete.rs
new file mode 100644
index 00000000..c63a3f40
--- /dev/null
+++ b/src/coord/discrete.rs
@@ -0,0 +1,254 @@
+use super::{AsRangedCoord, Ranged};
+use std::fmt::{Debug, Formatter, Result as FmtResult};
+use std::ops::Range;
+
+/// The trait indicates the coordinate is discrete
+/// This means we can bidirectionally map the range value to 0 to N
+/// in which N is the number of distinct values of the range.
+///
+/// This is useful since for a histgoram, this is an abstraction of bucket.
+pub trait DiscreteRanged
+where
+    Self: Ranged,
+{
+    /// Get the number of element in the range
+    /// Note: we assume that all the ranged discrete coordinate has finite value
+    ///
+    /// - **returns** The number of values in the range
+    fn size(&self) -> usize;
+
+    /// Map a value to the index
+    ///
+    /// Note: This function doesn't guareentee return None when the value is out of range.
+    /// The only way to confirm the value is in the range is to examing the return value isn't
+    /// larger than self.size.
+    ///
+    /// - `value`: The value to map
+    /// - **returns** The index of the value
+    fn index_of(&self, value: &Self::ValueType) -> Option<usize>;
+
+    /// Reverse map the index to the value
+    ///
+    /// Note: This function doesn't guareentee returning None when the index is out of range.
+    ///
+    /// - `value`: The index to map
+    /// - **returns** The value
+    fn from_index(&self, index: usize) -> Option<Self::ValueType>;
+
+    /// Return a iterator that iterates over the all possible values
+    ///
+    /// - **returns** The value iterator
+    fn values(&self) -> DiscreteValueIter<'_, Self>
+    where
+        Self: Sized,
+    {
+        DiscreteValueIter(self, 0, self.size())
+    }
+
+    /// Returns the previous value in this range
+    ///
+    /// Normally, it's based on the `from_index` and `index_of` function. But for
+    /// some of the coord spec, it's possible that we value faster implementation.
+    /// If this is the case, we can impelemnet the type specific impl for the `previous`
+    /// and `next`.
+    ///
+    /// - `value`: The current value
+    /// - **returns**: The value piror to current value
+    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
+    }
+
+    /// Returns the next value in this range
+    ///
+    /// Normally, it's based on the `from_index` and `index_of` function. But for
+    /// some of the coord spec, it's possible that we value faster implementation.
+    /// If this is the case, we can impelemnet the type specific impl for the `previous`
+    /// and `next`.
+    ///
+    /// - `value`: The current value
+    /// - **returns**: The value next to current value
+    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
+    }
+}
+
+/// The axis decorator that makes key-point in the center of the value range
+/// This is useful when we draw a histogram, since we want the axis value label
+/// to be shown in the middle of the range rather than exactly the location where
+/// the value mapped to.
+pub struct CentricDiscreteRange<D: DiscreteRanged>(D);
+
+impl<D: DiscreteRanged + Clone> Clone for CentricDiscreteRange<D> {
+    fn clone(&self) -> Self {
+        CentricDiscreteRange(self.0.clone())
+    }
+}
+
+/// The trait for types that can decorated by `CentricDiscreteRange` decorator
+pub trait IntoCentric: AsRangedCoord
+where
+    Self::CoordDescType: DiscreteRanged,
+{
+    /// Convert current ranged value into a centric ranged value
+    fn into_centric(self) -> CentricDiscreteRange<Self::CoordDescType> {
+        CentricDiscreteRange(self.into())
+    }
+}
+
+impl<R: AsRangedCoord> IntoCentric for R where R::CoordDescType: DiscreteRanged {}
+
+/// The value that used by the centric coordinate
+pub enum CentricValues<T> {
+    Exact(T),
+    CenterOf(T),
+    Last,
+}
+
+impl<D: Debug> Debug for CentricValues<D> {
+    fn fmt(&self, formatter: &mut Formatter) -> FmtResult {
+        match self {
+            CentricValues::Exact(value) => write!(formatter, "{:?}", value),
+            CentricValues::CenterOf(value) => write!(formatter, "{:?}", value),
+            CentricValues::Last => Ok(()),
+        }
+    }
+}
+
+impl<D: DiscreteRanged> Ranged for CentricDiscreteRange<D> {
+    type ValueType = CentricValues<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 {
+            CentricValues::Exact(coord) => self.0.map(coord, (limit.0, limit.1 - margin)),
+            CentricValues::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
+            }
+            CentricValues::Last => limit.1,
+        }
+    }
+
+    fn key_points(&self, max_points: usize) -> Vec<Self::ValueType> {
+        self.0
+            .key_points(max_points)
+            .into_iter()
+            .map(CentricValues::CenterOf)
+            .collect()
+    }
+
+    fn range(&self) -> Range<Self::ValueType> {
+        let range = self.0.range();
+        CentricValues::Exact(range.start)..CentricValues::Exact(range.end)
+    }
+}
+
+impl<D: DiscreteRanged> DiscreteRanged for CentricDiscreteRange<D> {
+    fn size(&self) -> usize {
+        self.0.size() + 1
+    }
+
+    fn index_of(&self, value: &Self::ValueType) -> Option<usize> {
+        match value {
+            CentricValues::Exact(value) => self.0.index_of(value),
+            CentricValues::CenterOf(value) => self.0.index_of(value),
+            CentricValues::Last => Some(self.0.size()),
+        }
+    }
+
+    fn from_index(&self, idx: usize) -> Option<Self::ValueType> {
+        if idx < self.0.size() {
+            self.0.from_index(idx).map(|x| CentricValues::Exact(x))
+        } else if idx == self.0.size() {
+            Some(CentricValues::Last)
+        } else {
+            None
+        }
+    }
+}
+
+impl<T> From<T> for CentricValues<T> {
+    fn from(this: T) -> CentricValues<T> {
+        CentricValues::Exact(this)
+    }
+}
+
+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::numeric::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_centric();
+
+        assert_eq!(coord.size(), 12);
+        for i in 0..=11 {
+            match coord.from_index(i as usize) {
+                Some(CentricValues::Exact(value)) => assert_eq!(i, value),
+                Some(CentricValues::Last) => assert_eq!(i, 11),
+                _ => panic!(),
+            }
+        }
+
+        for (kps, idx) in coord.key_points(20).into_iter().zip(0..) {
+            match kps {
+                CentricValues::CenterOf(value) if value <= 10 => assert_eq!(value, idx),
+                _ => panic!(),
+            }
+        }
+
+        assert_eq!(coord.map(&CentricValues::CenterOf(0), (0, 24)), 1);
+        assert_eq!(coord.map(&CentricValues::Exact(0), (0, 24)), 0);
+        assert_eq!(coord.map(&CentricValues::Exact(1), (0, 24)), 2);
+    }
+}
diff --git a/src/coord/group_by.rs b/src/coord/group_by.rs
new file mode 100644
index 00000000..5cda0fb8
--- /dev/null
+++ b/src/coord/group_by.rs
@@ -0,0 +1,72 @@
+use super::numeric::RangedCoordusize;
+use super::{AsRangedCoord, DiscreteRanged, Ranged};
+use std::ops::Range;
+
+/// The ranged value spec that needs to be grouped.
+/// This is useful, for example, when we have an X axis is a integer and denotes days.
+/// And we are expecting the tick mark denotes weeks, in this way we can make the range
+/// spec grouping by 7 elements.
+pub struct GroupBy<T: DiscreteRanged>(T, usize);
+
+/// The trait that provides method `Self::group_by` function which creates a
+/// `GroupBy` decorated ranged value.
+pub trait ToGroupByRange: AsRangedCoord + Sized
+where
+    Self::CoordDescType: DiscreteRanged,
+{
+    /// Make a grouping ranged value, see the documentation for `GroupBy` for details.
+    ///
+    /// - `value`: The number of values we want to group it
+    /// - **return**: The newly created grouping range specification
+    fn group_by(self, value: usize) -> GroupBy<<Self as AsRangedCoord>::CoordDescType> {
+        GroupBy(self.into(), value)
+    }
+}
+
+impl<T: AsRangedCoord + Sized> ToGroupByRange for T where T::CoordDescType: DiscreteRanged {}
+
+impl<T: DiscreteRanged> DiscreteRanged for GroupBy<T> {
+    fn size(&self) -> usize {
+        (self.0.size() + self.1 - 1) / self.1
+    }
+    fn index_of(&self, value: &Self::ValueType) -> Option<usize> {
+        self.0.index_of(value).map(|idx| idx / self.1)
+    }
+    fn from_index(&self, index: usize) -> Option<Self::ValueType> {
+        self.0.from_index(index * self.1)
+    }
+}
+
+impl<T: DiscreteRanged> Ranged for GroupBy<T> {
+    type ValueType = T::ValueType;
+    fn map(&self, value: &T::ValueType, limit: (i32, i32)) -> i32 {
+        self.0.map(value, limit)
+    }
+    fn range(&self) -> Range<T::ValueType> {
+        self.0.range()
+    }
+    fn key_points(&self, max_points: usize) -> Vec<T::ValueType> {
+        let range = 0..(self.0.size() + self.1 - 1) / self.1;
+        let logic_range: RangedCoordusize = range.into();
+
+        logic_range
+            .key_points(max_points)
+            .into_iter()
+            .map(|x| self.0.from_index(x * self.1).unwrap())
+            .collect()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    #[test]
+    fn test_group_by() {
+        let coord = (0..100).group_by(10);
+        assert_eq!(coord.size(), 11);
+        for (idx, val) in (0..).zip(coord.values()) {
+            assert_eq!(val, idx * 10);
+            assert_eq!(coord.from_index(idx as usize), Some(val));
+        }
+    }
+}
diff --git a/src/coord/mod.rs b/src/coord/mod.rs
index 0afafa32..f66321f8 100644
--- a/src/coord/mod.rs
+++ b/src/coord/mod.rs
@@ -25,8 +25,11 @@ use crate::drawing::backend::BackendCoord;
 mod category;
 #[cfg(feature = "chrono")]
 mod datetime;
+mod discrete;
+mod group_by;
 mod logarithmic;
 mod numeric;
+mod partial_axis;
 mod ranged;
 
 #[cfg(feature = "chrono")]
@@ -35,16 +38,15 @@ pub use numeric::{
     RangedCoordf32, RangedCoordf64, RangedCoordi128, RangedCoordi32, RangedCoordi64,
     RangedCoordu128, RangedCoordu32, RangedCoordu64,
 };
-pub use ranged::{
-    AsRangedCoord, DiscreteRanged, IntoCentric, IntoPartialAxis, MeshLine, Ranged, RangedCoord,
-    ReversibleRanged,
-};
+pub use ranged::{AsRangedCoord, MeshLine, Ranged, RangedCoord, ReversibleRanged};
+
+pub use partial_axis::{make_partial_axis, IntoPartialAxis};
 
-pub use ranged::make_partial_axis;
+pub use discrete::{DiscreteRanged, IntoCentric};
 
 pub use logarithmic::{LogCoord, LogRange, LogScalable};
 
-pub use numeric::group_integer_by::{GroupBy, ToGroupByRange};
+pub use group_by::{GroupBy, ToGroupByRange};
 use std::rc::Rc;
 use std::sync::Arc;
 
diff --git a/src/coord/numeric.rs b/src/coord/numeric.rs
index 6a9f72d2..e1f5cd8f 100644
--- a/src/coord/numeric.rs
+++ b/src/coord/numeric.rs
@@ -1,3 +1,4 @@
+use std::convert::TryFrom;
 use std::ops::Range;
 
 use super::{AsRangedCoord, DiscreteRanged, Ranged, ReversibleRanged};
@@ -5,13 +6,27 @@ use super::{AsRangedCoord, DiscreteRanged, Ranged, ReversibleRanged};
 macro_rules! impl_discrete_trait {
     ($name:ident) => {
         impl DiscreteRanged for $name {
-            type RangeParameter = ();
-            fn get_range_parameter(&self) -> () {}
-            fn next_value(this: &Self::ValueType, _: &()) -> Self::ValueType {
-                return *this + 1;
+            fn size(&self) -> usize {
+                if &self.1 < &self.0 {
+                    return 0;
+                }
+                let values = self.1 - self.0;
+                (values + 1) as usize
+            }
+
+            fn index_of(&self, value: &Self::ValueType) -> Option<usize> {
+                if value < &self.0 {
+                    return None;
+                }
+                let ret = value - self.0;
+                Some(ret as usize)
             }
-            fn previous_value(this: &Self::ValueType, _: &()) -> Self::ValueType {
-                return *this - 1;
+
+            fn from_index(&self, index: usize) -> Option<Self::ValueType> {
+                if let Ok(index) = Self::ValueType::try_from(index) {
+                    return Some(self.0 + index);
+                }
+                None
             }
         }
     };
@@ -256,107 +271,6 @@ impl_ranged_type_trait!(u128, RangedCoordu128);
 impl_ranged_type_trait!(isize, RangedCoordisize);
 impl_ranged_type_trait!(usize, RangedCoordusize);
 
-// TODO: Think about how to re-organize this part
-pub mod group_integer_by {
-    use super::Ranged;
-    use super::{AsRangedCoord, DiscreteRanged};
-    use num_traits::{FromPrimitive, PrimInt, ToPrimitive};
-    use std::ops::{Mul, Range};
-
-    /// The ranged value spec that needs to be grouped.
-    /// This is useful, for example, when we have an X axis is a integer and denotes days.
-    /// And we are expecting the tick mark denotes weeks, in this way we can make the range
-    /// spec grouping by 7 elements.
-    pub struct GroupBy<T>(T, T::ValueType)
-    where
-        T::ValueType: PrimInt + ToPrimitive + FromPrimitive + Mul,
-        T: Ranged;
-
-    /// The trait that provides method `Self::group_by` function which creates a
-    /// `GroupBy` decorated ranged value.
-    pub trait ToGroupByRange
-    where
-        Self: AsRangedCoord,
-        <Self as AsRangedCoord>::Value: PrimInt + ToPrimitive + FromPrimitive + Mul,
-        <<Self as AsRangedCoord>::CoordDescType as Ranged>::ValueType:
-            PrimInt + ToPrimitive + FromPrimitive + Mul,
-    {
-        /// Make a grouping ranged value, see the documentation for `GroupBy` for details.
-        ///
-        /// - `value`: The number of values we want to group it
-        /// - **return**: The newly created grouping range sepcification
-        fn group_by(
-            self,
-            value: <<Self as AsRangedCoord>::CoordDescType as Ranged>::ValueType,
-        ) -> GroupBy<<Self as AsRangedCoord>::CoordDescType> {
-            GroupBy(self.into(), value)
-        }
-    }
-
-    impl<T> ToGroupByRange for T
-    where
-        Self: AsRangedCoord,
-        <Self as AsRangedCoord>::Value: PrimInt + FromPrimitive + ToPrimitive + Mul,
-        <<Self as AsRangedCoord>::CoordDescType as Ranged>::ValueType:
-            PrimInt + FromPrimitive + ToPrimitive + Mul,
-    {
-    }
-
-    impl<T> AsRangedCoord for GroupBy<T>
-    where
-        T::ValueType: PrimInt + ToPrimitive + FromPrimitive + Mul,
-        T: Ranged,
-    {
-        type Value = T::ValueType;
-        type CoordDescType = Self;
-    }
-
-    impl<T> DiscreteRanged for GroupBy<T>
-    where
-        T::ValueType: PrimInt + ToPrimitive + FromPrimitive + Mul,
-        T: Ranged + DiscreteRanged,
-    {
-        type RangeParameter = <T as DiscreteRanged>::RangeParameter;
-        fn get_range_parameter(&self) -> Self::RangeParameter {
-            self.0.get_range_parameter()
-        }
-        fn previous_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType {
-            <T as DiscreteRanged>::previous_value(this, param)
-        }
-        fn next_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType {
-            <T as DiscreteRanged>::next_value(this, param)
-        }
-    }
-
-    impl<T> Ranged for GroupBy<T>
-    where
-        T::ValueType: PrimInt + ToPrimitive + FromPrimitive + Mul,
-        T: Ranged,
-    {
-        type ValueType = T::ValueType;
-        fn map(&self, value: &T::ValueType, limit: (i32, i32)) -> i32 {
-            self.0.map(value, limit)
-        }
-        fn range(&self) -> Range<T::ValueType> {
-            self.0.range()
-        }
-        fn key_points(&self, max_points: usize) -> Vec<T::ValueType> {
-            let actual_range = self.0.range();
-            let from = ((actual_range.start + self.1 - T::ValueType::from_u8(1).unwrap()) / self.1)
-                .to_isize()
-                .unwrap();
-            let to = (actual_range.end / self.1).to_isize().unwrap();
-            let logic_range: super::RangedCoordisize = (from..to).into();
-
-            logic_range
-                .key_points(max_points)
-                .into_iter()
-                .map(|x| T::ValueType::from_isize(x).unwrap() * self.1)
-                .collect()
-        }
-    }
-}
-
 #[cfg(test)]
 mod test {
     use super::*;
@@ -392,4 +306,12 @@ mod test {
         let _coord =
             RangedCoord::<RangedCoordu32, RangedCoordu32>::new(0..10, 0..10, (0..1024, 0..768));
     }
+
+    #[test]
+    fn test_coord_unmap() {
+        let coord: RangedCoordu32 = (0..20).into();
+        let pos = coord.map(&5, (1000, 2000));
+        let value = coord.unmap(pos, (1000, 2000));
+        assert_eq!(value, Some(5));
+    }
 }
diff --git a/src/coord/partial_axis.rs b/src/coord/partial_axis.rs
new file mode 100644
index 00000000..e3e3a518
--- /dev/null
+++ b/src/coord/partial_axis.rs
@@ -0,0 +1,118 @@
+use super::{AsRangedCoord, DiscreteRanged, Ranged};
+use std::ops::Range;
+
+/// This axis decorator will make the axis partially display on the axis.
+/// At some time, we want the axis only covers some part of the value.
+/// This decorator will have an additional display range defined.
+pub struct PartialAxis<R: Ranged>(R, Range<R::ValueType>);
+
+/// The trait for the types that can be converted into a partial axis
+pub trait IntoPartialAxis: AsRangedCoord {
+    /// Make the partial axis
+    ///
+    /// - `axis_range`: The range of the axis to be displayed
+    /// - **returns**: The converted range specification
+    fn partial_axis(
+        self,
+        axis_range: Range<<Self::CoordDescType as Ranged>::ValueType>,
+    ) -> PartialAxis<Self::CoordDescType> {
+        PartialAxis(self.into(), axis_range)
+    }
+}
+
+impl<R: AsRangedCoord> IntoPartialAxis for R {}
+
+impl<R: Ranged + Clone> Clone for PartialAxis<R>
+where
+    <R as Ranged>::ValueType: Clone,
+{
+    fn clone(&self) -> Self {
+        PartialAxis(self.0.clone(), self.1.clone())
+    }
+}
+
+impl<R: Ranged> Ranged for PartialAxis<R>
+where
+    R::ValueType: Clone,
+{
+    type ValueType = R::ValueType;
+
+    fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
+        self.0.map(value, limit)
+    }
+
+    fn key_points(&self, max_points: usize) -> Vec<Self::ValueType> {
+        self.0.key_points(max_points)
+    }
+
+    fn range(&self) -> Range<Self::ValueType> {
+        self.0.range()
+    }
+
+    fn axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32> {
+        let left = self.map(&self.1.start, limit);
+        let right = self.map(&self.1.end, limit);
+
+        left.min(right)..left.max(right)
+    }
+}
+
+impl<R: DiscreteRanged> DiscreteRanged for PartialAxis<R>
+where
+    R: Ranged,
+    <R as Ranged>::ValueType: Eq + Clone,
+{
+    fn size(&self) -> usize {
+        self.0.size()
+    }
+
+    fn index_of(&self, value: &R::ValueType) -> Option<usize> {
+        self.0.index_of(value)
+    }
+
+    fn from_index(&self, index: usize) -> Option<Self::ValueType> {
+        self.0.from_index(index)
+    }
+}
+
+/// Make a partial axis based on the percentage of visible portion.
+/// We can use `into_partial_axis` to create a partial axis range specification.
+/// But sometimes, we want to directly specify the percentage visible to the user.
+///
+/// - `axis_range`: The range specification
+/// - `part`: The visible part of the axis. Each value is from [0.0, 1.0]
+/// - **returns**: The partial axis created from the input, or `None` when not possible
+pub fn make_partial_axis<T>(
+    axis_range: Range<T>,
+    part: Range<f64>,
+) -> Option<PartialAxis<<Range<T> as AsRangedCoord>::CoordDescType>>
+where
+    Range<T>: AsRangedCoord,
+    T: num_traits::NumCast + Clone,
+{
+    let left: f64 = num_traits::cast(axis_range.start.clone())?;
+    let right: f64 = num_traits::cast(axis_range.end.clone())?;
+
+    let full_range_size = (right - left) / (part.end - part.start);
+
+    let full_left = left - full_range_size * part.start;
+    let full_right = right + full_range_size * (1.0 - part.end);
+
+    let full_range: Range<T> = num_traits::cast(full_left)?..num_traits::cast(full_right)?;
+
+    let axis_range: <Range<T> as AsRangedCoord>::CoordDescType = axis_range.into();
+
+    Some(PartialAxis(full_range.into(), axis_range.range()))
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    #[test]
+    fn test_make_partial_axis() {
+        let r = make_partial_axis(20..80, 0.2..0.8).unwrap();
+        assert_eq!(r.size(), 101);
+        assert_eq!(r.range(), 0..100);
+        assert_eq!(r.axis_pixel_range((0, 100)), 20..80);
+    }
+}
diff --git a/src/coord/ranged.rs b/src/coord/ranged.rs
index b7f3ffd7..a8a34339 100644
--- a/src/coord/ranged.rs
+++ b/src/coord/ranged.rs
@@ -172,22 +172,6 @@ impl<'a, X: Ranged, Y: Ranged> MeshLine<'a, X, Y> {
     }
 }
 
-/// The trait indicates the coordinate is discrete, so that we can draw histogram on it
-pub trait DiscreteRanged
-where
-    Self: Ranged,
-{
-    type RangeParameter;
-
-    fn get_range_parameter(&self) -> Self::RangeParameter;
-
-    /// Get the smallest value that is larger than the `this` value
-    fn next_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType;
-
-    /// Get the largest value that is smaller than `this` value
-    fn previous_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType;
-}
-
 /// The trait for the type that can be converted into a ranged coordinate axis
 pub trait AsRangedCoord: Sized {
     type CoordDescType: Ranged<ValueType = Self::Value> + From<Self>;
@@ -197,201 +181,7 @@ pub trait AsRangedCoord: Sized {
 impl<T> AsRangedCoord for T
 where
     T: Ranged,
-    Range<T::ValueType>: Into<T>,
 {
     type CoordDescType = T;
     type Value = T::ValueType;
 }
-
-/// The axis decorator that makes key-point in the center of the value range
-/// This is useful when we draw a histogram, since we want the axis value label
-/// to be shown in the middle of the range rather than exactly the location where
-/// the value mapped to.
-pub struct CentricDiscreteRange<D: DiscreteRanged>(D)
-where
-    <D as Ranged>::ValueType: Eq;
-
-/// The trait for types that can decorated by `CentricDiscreteRange` decorator
-pub trait IntoCentric: AsRangedCoord
-where
-    Self::CoordDescType: DiscreteRanged,
-    <Self::CoordDescType as Ranged>::ValueType: Eq,
-{
-    /// Convert current ranged value into a centric ranged value
-    fn into_centric(self) -> CentricDiscreteRange<Self::CoordDescType> {
-        CentricDiscreteRange(self.into())
-    }
-}
-
-impl<T: AsRangedCoord> IntoCentric for T
-where
-    T::CoordDescType: DiscreteRanged,
-    <Self::CoordDescType as Ranged>::ValueType: Eq,
-{
-}
-
-impl<D: DiscreteRanged + Clone> Clone for CentricDiscreteRange<D>
-where
-    <D as Ranged>::ValueType: Eq,
-{
-    fn clone(&self) -> Self {
-        Self(self.0.clone())
-    }
-}
-
-impl<D: DiscreteRanged> Ranged for CentricDiscreteRange<D>
-where
-    <D as Ranged>::ValueType: Eq,
-{
-    type ValueType = <D as Ranged>::ValueType;
-
-    fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
-        let prev = <D as DiscreteRanged>::previous_value(&value, &self.0.get_range_parameter());
-        (self.0.map(&prev, limit) + self.0.map(value, limit)) / 2
-    }
-
-    fn key_points(&self, max_points: usize) -> Vec<Self::ValueType> {
-        self.0.key_points(max_points)
-    }
-
-    fn range(&self) -> Range<Self::ValueType> {
-        self.0.range()
-    }
-}
-
-impl<D: DiscreteRanged> DiscreteRanged for CentricDiscreteRange<D>
-where
-    <D as Ranged>::ValueType: Eq,
-{
-    type RangeParameter = <D as DiscreteRanged>::RangeParameter;
-    fn get_range_parameter(&self) -> Self::RangeParameter {
-        self.0.get_range_parameter()
-    }
-    fn next_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType {
-        <D as DiscreteRanged>::next_value(this, param)
-    }
-
-    fn previous_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType {
-        <D as DiscreteRanged>::previous_value(this, param)
-    }
-}
-
-impl<D: DiscreteRanged> AsRangedCoord for CentricDiscreteRange<D>
-where
-    <D as Ranged>::ValueType: Eq,
-{
-    type CoordDescType = Self;
-    type Value = <Self as Ranged>::ValueType;
-}
-
-/// This axis decorator will make the axis partially display on the axis.
-/// At some time, we want the axis only covers some part of the value.
-/// This decorator will have an additional display range defined.
-pub struct PartialAxis<R: Ranged>(R, Range<R::ValueType>);
-
-/// The trait for the types that can be converted into a partial axis
-pub trait IntoPartialAxis: AsRangedCoord {
-    /// Make the partial axis
-    ///
-    /// - `axis_range`: The range of the axis to be displayed
-    /// - **returns**: The converted range specification
-    fn partial_axis(
-        self,
-        axis_range: Range<<Self::CoordDescType as Ranged>::ValueType>,
-    ) -> PartialAxis<Self::CoordDescType> {
-        PartialAxis(self.into(), axis_range)
-    }
-}
-
-impl<R: AsRangedCoord> IntoPartialAxis for R {}
-
-impl<R: Ranged + Clone> Clone for PartialAxis<R>
-where
-    <R as Ranged>::ValueType: Clone,
-{
-    fn clone(&self) -> Self {
-        PartialAxis(self.0.clone(), self.1.clone())
-    }
-}
-
-impl<R: Ranged> Ranged for PartialAxis<R>
-where
-    R::ValueType: Clone,
-{
-    type ValueType = R::ValueType;
-
-    fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
-        self.0.map(value, limit)
-    }
-
-    fn key_points(&self, max_points: usize) -> Vec<Self::ValueType> {
-        self.0.key_points(max_points)
-    }
-
-    fn range(&self) -> Range<Self::ValueType> {
-        self.0.range()
-    }
-
-    fn axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32> {
-        let left = self.map(&self.1.start, limit);
-        let right = self.map(&self.1.end, limit);
-
-        left.min(right)..left.max(right)
-    }
-}
-
-impl<R: DiscreteRanged> DiscreteRanged for PartialAxis<R>
-where
-    R: Ranged,
-    <R as Ranged>::ValueType: Eq + Clone,
-{
-    type RangeParameter = <R as DiscreteRanged>::RangeParameter;
-    fn get_range_parameter(&self) -> Self::RangeParameter {
-        self.0.get_range_parameter()
-    }
-    fn next_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType {
-        <R as DiscreteRanged>::next_value(this, param)
-    }
-
-    fn previous_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType {
-        <R as DiscreteRanged>::previous_value(this, param)
-    }
-}
-
-impl<R: Ranged> AsRangedCoord for PartialAxis<R>
-where
-    <R as Ranged>::ValueType: Clone,
-{
-    type CoordDescType = Self;
-    type Value = <Self as Ranged>::ValueType;
-}
-
-/// Make a partial axis based on the percentage of visible portion.
-/// We can use `into_partial_axis` to create a partial axis range specification.
-/// But sometimes, we want to directly specify the percentage visible to the user.
-///
-/// - `axis_range`: The range specification
-/// - `part`: The visible part of the axis. Each value is from [0.0, 1.0]
-/// - **returns**: The partial axis created from the input, or `None` when not possible
-pub fn make_partial_axis<T>(
-    axis_range: Range<T>,
-    part: Range<f64>,
-) -> Option<PartialAxis<<Range<T> as AsRangedCoord>::CoordDescType>>
-where
-    Range<T>: AsRangedCoord,
-    T: num_traits::NumCast + Clone,
-{
-    let left: f64 = num_traits::cast(axis_range.start.clone())?;
-    let right: f64 = num_traits::cast(axis_range.end.clone())?;
-
-    let full_range_size = (right - left) / (part.end - part.start);
-
-    let full_left = left - full_range_size * part.start;
-    let full_right = right + full_range_size * (1.0 - part.end);
-
-    let full_range: Range<T> = num_traits::cast(full_left)?..num_traits::cast(full_right)?;
-
-    let axis_range: <Range<T> as AsRangedCoord>::CoordDescType = axis_range.into();
-
-    Some(PartialAxis(full_range.into(), axis_range.range()))
-}
diff --git a/src/series/histogram.rs b/src/series/histogram.rs
index 75c2fb2a..d4220e0b 100644
--- a/src/series/histogram.rs
+++ b/src/series/histogram.rs
@@ -1,5 +1,4 @@
 use std::collections::{hash_map::IntoIter as HashMapIter, HashMap};
-use std::hash::Hash;
 use std::marker::PhantomData;
 use std::ops::AddAssign;
 
@@ -20,32 +19,30 @@ impl HistogramType for Horizontal {}
 pub struct Histogram<'a, BR, A, Tag = Vertical>
 where
     BR: DiscreteRanged,
-    BR::ValueType: Eq + Hash,
     A: AddAssign<A> + Default,
     Tag: HistogramType,
 {
     style: Box<dyn Fn(&BR::ValueType, &A) -> ShapeStyle + 'a>,
     margin: u32,
-    iter: HashMapIter<BR::ValueType, A>,
-    baseline: Box<dyn Fn(BR::ValueType) -> A + 'a>,
-    br_param: BR::RangeParameter,
-    _p: PhantomData<(BR, Tag)>,
+    iter: HashMapIter<usize, A>,
+    baseline: Box<dyn Fn(&BR::ValueType) -> A + 'a>,
+    br: BR,
+    _p: PhantomData<Tag>,
 }
 
 impl<'a, BR, A, Tag> Histogram<'a, BR, A, Tag>
 where
-    BR: DiscreteRanged,
-    BR::ValueType: Eq + Hash,
+    BR: DiscreteRanged + Clone,
     A: AddAssign<A> + Default + 'a,
     Tag: HistogramType,
 {
-    fn empty(br_param: BR::RangeParameter) -> Self {
+    fn empty(br: &BR) -> Self {
         Self {
             style: Box::new(|_, _| GREEN.filled()),
             margin: 5,
             iter: HashMap::new().into_iter(),
             baseline: Box::new(|_| A::default()),
-            br_param,
+            br: br.clone(),
             _p: PhantomData,
         }
     }
@@ -75,7 +72,7 @@ where
     }
 
     /// Set a function that defines variant baseline
-    pub fn baseline_func(mut self, func: impl Fn(BR::ValueType) -> A + 'a) -> Self {
+    pub fn baseline_func(mut self, func: impl Fn(&BR::ValueType) -> A + 'a) -> Self {
         self.baseline = Box::new(func);
         self
     }
@@ -87,68 +84,33 @@ where
     }
 
     /// Set the data iterator
-    pub fn data<I: IntoIterator<Item = (BR::ValueType, A)>>(mut self, iter: I) -> Self {
-        let mut buffer = HashMap::<BR::ValueType, A>::new();
+    pub fn data<TB: Into<BR::ValueType>, I: IntoIterator<Item = (TB, A)>>(
+        mut self,
+        iter: I,
+    ) -> Self {
+        let mut buffer = HashMap::<usize, A>::new();
         for (x, y) in iter.into_iter() {
-            *buffer.entry(x).or_insert_with(Default::default) += y;
+            if let Some(x) = self.br.index_of(&x.into()) {
+                *buffer.entry(x).or_insert_with(Default::default) += y;
+            }
         }
         self.iter = buffer.into_iter();
         self
     }
 }
 
-pub trait UseDefaultParameter: Default {
-    fn new() -> Self {
-        Default::default()
-    }
-}
-
-impl UseDefaultParameter for () {}
-
 impl<'a, BR, A> Histogram<'a, BR, A, Vertical>
 where
-    BR: DiscreteRanged,
-    BR::ValueType: Eq + Hash,
+    BR: DiscreteRanged + Clone,
     A: AddAssign<A> + Default + 'a,
 {
-    /// Create a new histogram series.
-    ///
-    /// - `iter`: The data iterator
-    /// - `margin`: The margin between bars
-    /// - `style`: The style of bars
-    ///
-    /// Returns the newly created histogram series
-    #[allow(clippy::redundant_closure)]
-    pub fn new<S: Into<ShapeStyle>, I: IntoIterator<Item = (BR::ValueType, A)>>(
-        iter: I,
-        margin: u32,
-        style: S,
-    ) -> Self
-    where
-        BR::RangeParameter: UseDefaultParameter,
-    {
-        let mut buffer = HashMap::<BR::ValueType, A>::new();
-        for (x, y) in iter.into_iter() {
-            *buffer.entry(x).or_insert_with(Default::default) += y;
-        }
-        let style = style.into();
-        Self {
-            style: Box::new(move |_, _| style.clone()),
-            margin,
-            iter: buffer.into_iter(),
-            baseline: Box::new(|_| A::default()),
-            br_param: BR::RangeParameter::new(),
-            _p: PhantomData,
-        }
-    }
-
     pub fn vertical<ACoord, DB: DrawingBackend + 'a>(
         parent: &ChartContext<DB, RangedCoord<BR, ACoord>>,
     ) -> Self
     where
         ACoord: Ranged<ValueType = A>,
     {
-        let dp = parent.as_coord_spec().x_spec().get_range_parameter();
+        let dp = parent.as_coord_spec().x_spec();
 
         Self::empty(dp)
     }
@@ -156,8 +118,7 @@ where
 
 impl<'a, BR, A> Histogram<'a, BR, A, Horizontal>
 where
-    BR: DiscreteRanged,
-    BR::ValueType: Eq + Hash,
+    BR: DiscreteRanged + Clone,
     A: AddAssign<A> + Default + 'a,
 {
     pub fn horizontal<ACoord, DB: DrawingBackend>(
@@ -166,7 +127,7 @@ where
     where
         ACoord: Ranged<ValueType = A>,
     {
-        let dp = parent.as_coord_spec().y_spec().get_range_parameter();
+        let dp = parent.as_coord_spec().y_spec();
         Self::empty(dp)
     }
 }
@@ -174,18 +135,22 @@ where
 impl<'a, BR, A> Iterator for Histogram<'a, BR, A, Vertical>
 where
     BR: DiscreteRanged,
-    BR::ValueType: Eq + Hash,
     A: AddAssign<A> + Default,
 {
     type Item = Rectangle<(BR::ValueType, A)>;
     fn next(&mut self) -> Option<Self::Item> {
-        if let Some((x, y)) = self.iter.next() {
-            let nx = BR::next_value(&x, &self.br_param);
-            let base = (self.baseline)(BR::previous_value(&nx, &self.br_param));
-            let style = (self.style)(&x, &y);
-            let mut rect = Rectangle::new([(x, y), (nx, base)], style);
-            rect.set_margin(0, 0, self.margin, self.margin);
-            return Some(rect);
+        while let Some((x, y)) = self.iter.next() {
+            if let Some((x, Some(nx))) = self
+                .br
+                .from_index(x)
+                .map(|v| (v, self.br.from_index(x + 1)))
+            {
+                let base = (self.baseline)(&x);
+                let style = (self.style)(&x, &y);
+                let mut rect = Rectangle::new([(x, y), (nx, base)], style);
+                rect.set_margin(0, 0, self.margin, self.margin);
+                return Some(rect);
+            }
         }
         None
     }
@@ -194,19 +159,22 @@ where
 impl<'a, BR, A> Iterator for Histogram<'a, BR, A, Horizontal>
 where
     BR: DiscreteRanged,
-    BR::ValueType: Eq + Hash,
     A: AddAssign<A> + Default,
 {
     type Item = Rectangle<(A, BR::ValueType)>;
     fn next(&mut self) -> Option<Self::Item> {
-        if let Some((y, x)) = self.iter.next() {
-            let ny = BR::next_value(&y, &self.br_param);
-            // With this trick we can avoid the clone trait bound
-            let base = (self.baseline)(BR::previous_value(&ny, &self.br_param));
-            let style = (self.style)(&y, &x);
-            let mut rect = Rectangle::new([(x, y), (base, ny)], style);
-            rect.set_margin(self.margin, self.margin, 0, 0);
-            return Some(rect);
+        while let Some((y, x)) = self.iter.next() {
+            if let Some((y, Some(ny))) = self
+                .br
+                .from_index(y)
+                .map(|v| (v, self.br.from_index(y + 1)))
+            {
+                let base = (self.baseline)(&y);
+                let style = (self.style)(&y, &x);
+                let mut rect = Rectangle::new([(x, y), (base, ny)], style);
+                rect.set_margin(0, 0, self.margin, self.margin);
+                return Some(rect);
+            }
         }
         None
     }


Test patch: