aboutsummaryrefslogtreecommitdiff
path: root/src/coord/logarithmic.rs
diff options
context:
space:
mode:
authorJakub Kotur <qtr@google.com>2021-03-16 20:53:15 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2021-03-16 20:53:15 +0000
commit59b25e0dc5d4ca639c8d46b53a90417f37976169 (patch)
treea2b9db8395d7a587819e58039f5a575290b1a427 /src/coord/logarithmic.rs
parent97853433079c3108a7c92b68a671575f945dd5a9 (diff)
parent15d57486a2b5313937d3dda5f0b74f3d7c4675b7 (diff)
downloadplotters-59b25e0dc5d4ca639c8d46b53a90417f37976169.tar.gz
Initial import of plotters-0.2.15. am: 00e4d270b2 am: fa31b59d73 am: 92238a6cd1 am: 15d57486a2
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/plotters/+/1621407 Change-Id: I1c0edc0a5d6d263892cf85d4ff5adf9d484fe22b
Diffstat (limited to 'src/coord/logarithmic.rs')
-rw-r--r--src/coord/logarithmic.rs148
1 files changed, 148 insertions, 0 deletions
diff --git a/src/coord/logarithmic.rs b/src/coord/logarithmic.rs
new file mode 100644
index 0000000..a651013
--- /dev/null
+++ b/src/coord/logarithmic.rs
@@ -0,0 +1,148 @@
+use super::{AsRangedCoord, Ranged, RangedCoordf64};
+use std::marker::PhantomData;
+use std::ops::Range;
+
+/// The trait for the type that is able to be presented in the log scale
+pub trait LogScalable: Clone {
+ /// Make the conversion from the type to the floating point number
+ fn as_f64(&self) -> f64;
+ /// Convert a floating point number to the scale
+ fn from_f64(f: f64) -> Self;
+}
+
+macro_rules! impl_log_scalable {
+ (i, $t:ty) => {
+ impl LogScalable for $t {
+ fn as_f64(&self) -> f64 {
+ if *self != 0 {
+ return *self as f64;
+ }
+ // If this is an integer, we should allow zero point to be shown
+ // on the chart, thus we can't map the zero point to inf.
+ // So we just assigning a value smaller than 1 as the alternative
+ // of the zero point.
+ return 0.5;
+ }
+ fn from_f64(f: f64) -> $t {
+ f.round() as $t
+ }
+ }
+ };
+ (f, $t:ty) => {
+ impl LogScalable for $t {
+ fn as_f64(&self) -> f64 {
+ *self as f64
+ }
+ fn from_f64(f: f64) -> $t {
+ f as $t
+ }
+ }
+ };
+}
+
+impl_log_scalable!(i, u8);
+impl_log_scalable!(i, u16);
+impl_log_scalable!(i, u32);
+impl_log_scalable!(i, u64);
+impl_log_scalable!(f, f32);
+impl_log_scalable!(f, f64);
+
+/// The decorator type for a range of a log-scaled value
+pub struct LogRange<V: LogScalable>(pub Range<V>);
+
+impl<V: LogScalable + Clone> Clone for LogRange<V> {
+ fn clone(&self) -> Self {
+ Self(self.0.clone())
+ }
+}
+
+impl<V: LogScalable> From<LogRange<V>> for LogCoord<V> {
+ fn from(range: LogRange<V>) -> LogCoord<V> {
+ LogCoord {
+ linear: (range.0.start.as_f64().ln()..range.0.end.as_f64().ln()).into(),
+ logic: range.0,
+ marker: PhantomData,
+ }
+ }
+}
+
+impl<V: LogScalable> AsRangedCoord for LogRange<V> {
+ type CoordDescType = LogCoord<V>;
+ type Value = V;
+}
+
+/// A log scaled coordinate axis
+pub struct LogCoord<V: LogScalable> {
+ linear: RangedCoordf64,
+ logic: Range<V>,
+ marker: PhantomData<V>,
+}
+
+impl<V: LogScalable> Ranged for LogCoord<V> {
+ type ValueType = V;
+
+ fn map(&self, value: &V, limit: (i32, i32)) -> i32 {
+ let value = value.as_f64();
+ let value = value.max(self.logic.start.as_f64()).ln();
+ self.linear.map(&value, limit)
+ }
+
+ fn key_points(&self, max_points: usize) -> Vec<Self::ValueType> {
+ let tier_1 = (self.logic.end.as_f64() / self.logic.start.as_f64())
+ .log10()
+ .abs()
+ .floor()
+ .max(1.0) as usize;
+
+ let tier_2_density = if max_points < tier_1 {
+ 0
+ } else {
+ let density = 1 + (max_points - tier_1) / tier_1;
+ let mut exp = 1;
+ while exp * 10 <= density {
+ exp *= 10;
+ }
+ exp - 1
+ };
+
+ let mut multiplier = 10.0;
+ let mut cnt = 1;
+ while max_points < tier_1 / cnt {
+ multiplier *= 10.0;
+ cnt += 1;
+ }
+
+ let mut ret = vec![];
+ let mut val = (10f64).powf(self.logic.start.as_f64().log10().ceil());
+
+ while val <= self.logic.end.as_f64() {
+ ret.push(V::from_f64(val));
+ for i in 1..=tier_2_density {
+ let v = val
+ * (1.0
+ + multiplier / f64::from(tier_2_density as u32 + 1) * f64::from(i as u32));
+ if v > self.logic.end.as_f64() {
+ break;
+ }
+ ret.push(V::from_f64(v));
+ }
+ val *= multiplier;
+ }
+
+ ret
+ }
+
+ fn range(&self) -> Range<V> {
+ self.logic.clone()
+ }
+}
+#[cfg(test)]
+mod test {
+ use super::*;
+ #[test]
+ fn regression_test_issue_143() {
+ let range: LogCoord<f64> = LogRange(1.0..5.0).into();
+
+ range.key_points(100);
+ }
+}