1 /*!
2   The one-dimensional coordinate system abstraction.
3 
4   Plotters build complex coordinate system with a combinator pattern and all the coordinate system is
5   built from the one dimensional coordinate system. This module defines the fundamental types used by
6   the one-dimensional coordinate system.
7 
8   The key trait for a one dimensional coordinate is [Ranged](trait.Ranged.html). This trait describes a
9   set of values which served as the 1D coordinate system in Plotters. In order to extend the coordinate system,
10   the new coordinate spec must implement this trait.
11 
12   The following example demonstrate how to make a customized coordinate specification
13   ```
14 use plotters::coord::ranged1d::{Ranged, DefaultFormatting, KeyPointHint};
15 use std::ops::Range;
16 
17 struct ZeroToOne;
18 
19 impl Ranged for ZeroToOne {
20     type ValueType = f64;
21     type FormatOption = DefaultFormatting;
22 
23     fn map(&self, &v: &f64, pixel_range: (i32, i32)) -> i32 {
24        let size = pixel_range.1 - pixel_range.0;
25        let v = v.min(1.0).max(0.0);
26        ((size as f64) * v).round() as i32
27     }
28 
29     fn key_points<Hint:KeyPointHint>(&self, hint: Hint) -> Vec<f64> {
30         if hint.max_num_points() < 3 {
31             vec![]
32         } else {
33             vec![0.0, 0.5, 1.0]
34         }
35     }
36 
37     fn range(&self) -> Range<f64> {
38         0.0..1.0
39     }
40 }
41 
42 use plotters::prelude::*;
43 
44 let mut buffer = vec![0; 1024 * 768 * 3];
45 let root = BitMapBackend::with_buffer(&mut buffer, (1024, 768)).into_drawing_area();
46 
47 let chart = ChartBuilder::on(&root)
48     .build_cartesian_2d(ZeroToOne, ZeroToOne)
49     .unwrap();
50 
51   ```
52 */
53 use std::fmt::Debug;
54 use std::ops::Range;
55 
56 pub(super) mod combinators;
57 pub(super) mod types;
58 
59 mod discrete;
60 pub use discrete::{DiscreteRanged, IntoSegmentedCoord, SegmentValue, SegmentedCoord};
61 
62 /// Since stable Rust doesn't have specialization, it's very hard to make our own trait that
63 /// automatically implemented the value formatter. This trait uses as a marker indicates if we
64 /// should automatically implement the default value formater based on it's `Debug` trait
65 pub trait DefaultValueFormatOption {}
66 
67 /// This makes the ranged coord uses the default `Debug` based formatting
68 pub struct DefaultFormatting;
69 impl DefaultValueFormatOption for DefaultFormatting {}
70 
71 /// This markers prevent Plotters to implement the default `Debug` based formatting
72 pub struct NoDefaultFormatting;
73 impl DefaultValueFormatOption for NoDefaultFormatting {}
74 
75 /// Determine how we can format a value in a coordinate system by default
76 pub trait ValueFormatter<V> {
77     /// Format the value
format(_value: &V) -> String78     fn format(_value: &V) -> String {
79         panic!("Unimplemented formatting method");
80     }
81     /// Determine how we can format a value in a coordinate system by default
format_ext(&self, value: &V) -> String82     fn format_ext(&self, value: &V) -> String {
83         Self::format(value)
84     }
85 }
86 
87 // By default the value is formatted by the debug trait
88 impl<R: Ranged<FormatOption = DefaultFormatting>> ValueFormatter<R::ValueType> for R
89 where
90     R::ValueType: Debug,
91 {
format(value: &R::ValueType) -> String92     fn format(value: &R::ValueType) -> String {
93         format!("{:?}", value)
94     }
95 }
96 
97 /// Specify the weight of key points.
98 pub enum KeyPointWeight {
99     /// Allows only bold key points
100     Bold,
101     /// Allows any key points
102     Any,
103 }
104 
105 impl KeyPointWeight {
106     /// Check if this key point weight setting allows light point
allow_light_points(&self) -> bool107     pub fn allow_light_points(&self) -> bool {
108         match self {
109             KeyPointWeight::Bold => false,
110             KeyPointWeight::Any => true,
111         }
112     }
113 }
114 
115 /// The trait for a hint provided to the key point algorithm used by the coordinate specs.
116 /// The most important constraint is the `max_num_points` which means the algorithm could emit no more than specific number of key points
117 /// `weight` is used to determine if this is used as a bold grid line or light grid line
118 /// `bold_points` returns the max number of coresponding bold grid lines
119 pub trait KeyPointHint {
120     /// Returns the max number of key points
max_num_points(&self) -> usize121     fn max_num_points(&self) -> usize;
122     /// Returns the weight for this hint
weight(&self) -> KeyPointWeight123     fn weight(&self) -> KeyPointWeight;
124     /// Returns the point number constraint for the bold points
bold_points(&self) -> usize125     fn bold_points(&self) -> usize {
126         self.max_num_points()
127     }
128 }
129 
130 impl KeyPointHint for usize {
max_num_points(&self) -> usize131     fn max_num_points(&self) -> usize {
132         *self
133     }
134 
weight(&self) -> KeyPointWeight135     fn weight(&self) -> KeyPointWeight {
136         KeyPointWeight::Any
137     }
138 }
139 
140 ///  The key point hint indicates we only need key point for the bold grid lines
141 pub struct BoldPoints(pub usize);
142 
143 impl KeyPointHint for BoldPoints {
max_num_points(&self) -> usize144     fn max_num_points(&self) -> usize {
145         self.0
146     }
147 
weight(&self) -> KeyPointWeight148     fn weight(&self) -> KeyPointWeight {
149         KeyPointWeight::Bold
150     }
151 }
152 
153 /// The key point hint indicates that we are using the key points for the light grid lines
154 pub struct LightPoints {
155     bold_points_num: usize,
156     light_limit: usize,
157 }
158 
159 impl LightPoints {
160     /// Create a new light key point hind
new(bold_count: usize, requested: usize) -> Self161     pub fn new(bold_count: usize, requested: usize) -> Self {
162         Self {
163             bold_points_num: bold_count,
164             light_limit: requested,
165         }
166     }
167 }
168 
169 impl KeyPointHint for LightPoints {
max_num_points(&self) -> usize170     fn max_num_points(&self) -> usize {
171         self.light_limit
172     }
173 
bold_points(&self) -> usize174     fn bold_points(&self) -> usize {
175         self.bold_points_num
176     }
177 
weight(&self) -> KeyPointWeight178     fn weight(&self) -> KeyPointWeight {
179         KeyPointWeight::Any
180     }
181 }
182 
183 /// The trait that indicates we have a ordered and ranged value
184 /// Which is used to describe any 1D axis.
185 pub trait Ranged {
186     /// This marker decides if Plotters default [ValueFormatter](trait.ValueFormatter.html) implementation should be used.
187     /// This associated type can be one of the following two types:
188     /// - [DefaultFormatting](struct.DefaultFormatting.html) will allow Plotters to automatically impl
189     /// the formatter based on `Debug` trait, if `Debug` trait is not impl for the `Self::Value`,
190     /// [ValueFormatter](trait.ValueFormatter.html) will not impl unless you impl it manually.
191     ///
192     /// - [NoDefaultFormatting](struct.NoDefaultFormatting.html) Disable the automatic `Debug`
193     /// based value formatting. Thus you have to impl the
194     /// [ValueFormatter](trait.ValueFormatter.html) manually.
195     ///
196     type FormatOption: DefaultValueFormatOption;
197 
198     /// The type of this value in this range specification
199     type ValueType;
200 
201     /// This function maps the value to i32, which is the drawing coordinate
map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32202     fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32;
203 
204     /// This function gives the key points that we can draw a grid based on this
key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<Self::ValueType>205     fn key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<Self::ValueType>;
206 
207     /// Get the range of this value
range(&self) -> Range<Self::ValueType>208     fn range(&self) -> Range<Self::ValueType>;
209 
210     /// This function provides the on-axis part of its range
211     #[allow(clippy::range_plus_one)]
axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32>212     fn axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32> {
213         if limit.0 < limit.1 {
214             limit.0..limit.1
215         } else {
216             limit.1..limit.0
217         }
218     }
219 }
220 
221 /// The trait indicates the ranged value can be map reversely, which means
222 /// an pixel-based coordinate is given, it's possible to figure out the underlying
223 /// logic value.
224 pub trait ReversibleRanged: Ranged {
225     /// Perform the reverse mapping
unmap(&self, input: i32, limit: (i32, i32)) -> Option<Self::ValueType>226     fn unmap(&self, input: i32, limit: (i32, i32)) -> Option<Self::ValueType>;
227 }
228 
229 /// The trait for the type that can be converted into a ranged coordinate axis
230 pub trait AsRangedCoord: Sized {
231     /// Type to describe a coordinate system
232     type CoordDescType: Ranged<ValueType = Self::Value> + From<Self>;
233     /// Type for values in the given coordinate system
234     type Value;
235 }
236 
237 impl<T> AsRangedCoord for T
238 where
239     T: Ranged,
240 {
241     type CoordDescType = T;
242     type Value = T::ValueType;
243 }
244