1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
use std::fmt;
use std::ops::Range;
use std::rc::Rc;
use super::{AsRangedCoord, Ranged};
/// The category coordinate
pub struct Category<T: PartialEq> {
name: String,
elements: Rc<Vec<T>>,
// i32 type is required for the empty ref (having -1 value)
idx: i32,
}
impl<T: PartialEq> Clone for Category<T> {
fn clone(&self) -> Self {
Category {
name: self.name.clone(),
elements: Rc::clone(&self.elements),
idx: self.idx,
}
}
}
impl<T: PartialEq + fmt::Display> fmt::Debug for Category<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let element = &self.elements[self.idx as usize];
write!(f, "{}", element)
}
}
impl<T: PartialEq> Category<T> {
/// Create a new category coordinate.
///
/// - `name`: The name of the category
/// - `elements`: The vector of category elements
/// - **returns** The newly created category coordinate
///
/// ```rust
/// use plotters::prelude::*;
///
/// let category = Category::new("color", vec!["red", "green", "blue"]);
/// ```
pub fn new<S: Into<String>>(name: S, elements: Vec<T>) -> Self {
Self {
name: name.into(),
elements: Rc::new(elements),
idx: -1,
}
}
/// Get an element reference (tick) by its value.
///
/// - `val`: The value of the element
/// - **returns** The optional reference
///
/// ```rust
/// use plotters::prelude::*;
///
/// let category = Category::new("color", vec!["red", "green", "blue"]);
/// let red = category.get(&"red");
/// assert!(red.is_some());
/// let unknown = category.get(&"unknown");
/// assert!(unknown.is_none());
/// ```
pub fn get(&self, val: &T) -> Option<Category<T>> {
match self.elements.iter().position(|x| x == val) {
Some(pos) => {
let element_ref = Category {
name: self.name.clone(),
elements: Rc::clone(&self.elements),
idx: pos as i32,
};
Some(element_ref)
}
_ => None,
}
}
/// Create a full range over the category elements.
///
/// - **returns** The range including all category elements
///
/// ```rust
/// use plotters::prelude::*;
///
/// let category = Category::new("color", vec!["red", "green", "blue"]);
/// let range = category.range();
/// ```
pub fn range(&self) -> Self {
self.clone()
}
/// Get the number of elements in the category.
///
/// - **returns** The number of elements
///
/// ```rust
/// use plotters::prelude::*;
///
/// let category = Category::new("color", vec!["red", "green", "blue"]);
/// assert_eq!(category.len(), 3);
/// ```
pub fn len(&self) -> usize {
self.elements.len()
}
/// Returns `true` if the category contains no elements.
///
/// - **returns** `true` is no elements, otherwise - `false`
///
/// ```rust
/// use plotters::prelude::*;
///
/// let category = Category::new("color", vec!["red", "green", "blue"]);
/// assert_eq!(category.is_empty(), false);
///
/// let category = Category::new("empty", Vec::<&str>::new());
/// assert_eq!(category.is_empty(), true);
/// ```
pub fn is_empty(&self) -> bool {
self.elements.is_empty()
}
/// Get the category name.
///
/// - **returns** The name of the category
///
/// ```rust
/// use plotters::prelude::*;
///
/// let category = Category::new("color", vec!["red", "green", "blue"]);
/// assert_eq!(category.name(), "color");
/// ```
pub fn name(&self) -> String {
self.name.clone()
}
}
impl<T: PartialEq> Ranged for Category<T> {
type ValueType = Category<T>;
fn range(&self) -> Range<Category<T>> {
let mut left = self.clone();
let mut right = self.clone();
left.idx = 0;
right.idx = right.len() as i32 - 1;
left..right
}
fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
// Add margins to spans as edge values are not applicable to category
let total_span = (self.len() + 1) as f64;
let value_span = f64::from(value.idx + 1);
(f64::from(limit.1 - limit.0) * value_span / total_span) as i32 + limit.0
}
fn key_points(&self, max_points: usize) -> Vec<Self::ValueType> {
let mut ret = vec![];
let intervals = (self.len() - 1) as f64;
let elements = &self.elements;
let name = &self.name;
let step = (intervals / max_points as f64 + 1.0) as usize;
for idx in (0..self.len()).step_by(step) {
ret.push(Category {
name: name.clone(),
elements: Rc::clone(&elements),
idx: idx as i32,
});
}
ret
}
}
impl<T: PartialEq> AsRangedCoord for Category<T> {
type CoordDescType = Self;
type Value = Category<T>;
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_clone_trait() {
let category = Category::new("color", vec!["red", "green", "blue"]);
let red = category.get(&"red").unwrap();
assert_eq!(red.idx, 0);
let clone = red.clone();
assert_eq!(clone.idx, 0);
}
#[test]
fn test_debug_trait() {
let category = Category::new("color", vec!["red", "green", "blue"]);
let red = category.get(&"red").unwrap();
assert_eq!(format!("{:?}", red), "red");
}
#[test]
fn test_ranged_trait() {
let category = Category::new("color", vec!["red", "green", "blue"]);
assert_eq!(category.map(&category.get(&"red").unwrap(), (0, 8)), 2);
assert_eq!(category.map(&category.get(&"green").unwrap(), (0, 8)), 4);
assert_eq!(category.map(&category.get(&"blue").unwrap(), (0, 8)), 6);
assert_eq!(category.key_points(3).len(), 3);
assert_eq!(category.key_points(5).len(), 3);
}
}