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
//! vizkit is a rendering-agnostic kit for data visualization.
//!
//! It aims to provide basic functionalities for making easier data visualization in GUI such as
//! [iced](https://iced.rs/) or [egui](https://www.egui.rs/) or more specific use cases such as
//! creating your own SVG.
//!
//! # Features
//!
//! Optional features:
//!
//! - `time`: Enable time operations and scales with a temporal domain using
//! [chrono](https://docs.rs/chrono/latest/chrono/).
//!
//! # Overview
//!
//! Most of the time, you want to draw basic elements (circles, rectangles, lines, ...) in a
//! well-defined region with a `width` and `height` (and sometimes margins such as `margin_top`,
//! `margin_right`, `margin_bottom`, `margin_left`).
//!
//! For that, let's imagine the following values:
//! ```
//! let width = 960.;
//! let height = 400.;
//!
//! let margin_top = 10.;
//! let margin_left = 50.;
//! let margin_right = 10.;
//! let margin_bottom = 40.;
//! ```
//!
//! In order to visualize your data, we assume you have processed data ready to be used as a slice
//! `&[T]`. Let's say `T` is the following structure:
//!
//! ```
//! struct Row {
//! location: String, // discrete values
//! hour: u8, // discrete values {0, 1, 2, ..., 21, 22, 23}
//! vehicles: u32, // continuous values between [0, 10_000]
//! }
//! ```
//!
//! For this specific case, we want to draw a _heatmap_ with:
//!
//! - an x-axis where `hour` values represent the ticks.
//! - an y-axis where `location` values represent the ticks.
//! - rectangles positioned at `[hour, location]` coordinates and filled with a color based on the
//! row's associated `vehicles` value.
//!
//! We are going to use different scalers (see [`scale`][`crate::scale`] for more information):
//!
//! - a first [`ScaleDiscrete`][`crate::scale::ScaleDiscrete`] for mapping `hour` values to a range defined
//! by the region's width.
//! - a second [`ScaleDiscrete`][`crate::scale::ScaleDiscrete`] for mapping `location` values to a range
//! defined by the region's height.
//! - a [`ScaleColor`][`crate::scale::ScaleColor`] for mapping `vehicles` values to a range of
//! colors.
//!
//! ```
//! use std::collections::HashSet;
//! use vizkit::{
//! chromatic::{ColorMap, Turbo},
//! draw::{AxisOptions, ShapeAttrs, axis_bottom_iter, axis_left_iter, rect_iter},
//! scale::{Axis, ScaleDiscrete, ScaleColor},
//! };
//!
//! let width = 960.;
//! let height = 400.;
//!
//! let margin_top = 10.;
//! let margin_left = 50.;
//! let margin_right = 10.;
//! let margin_bottom = 40.;
//!
//! struct Row {
//! location: String,
//! hour: u8,
//! vehicles: u32,
//! }
//!
//! let data = vec![
//! Row {
//! location: "Hasborn".to_string(),
//! hour: 19,
//! vehicles: 929,
//! },
//! Row {
//! location: "Köln-Nord".to_string(),
//! hour: 7,
//! vehicles: 6882,
//! },
//! // ...
//! ];
//!
//! let hours: Vec<u8> = (0..24).collect();
//! let x_scale = ScaleDiscrete::band()
//! .domain(&hours)
//! .range([margin_left, width - margin_right]);
//!
//! let locations: HashSet<&str> = HashSet::from_iter(
//! data.iter().map(|row| row.location.as_str())
//! );
//! let y_scale = ScaleDiscrete::band()
//! .domain(&Vec::from_iter(locations))
//! .range([height - margin_bottom, margin_top]);
//!
//! let rect_color = ScaleColor::linear(Turbo::default()).domain([0.0, 10_000.0]);
//!
//! let axis_options = AxisOptions::default();
//!
//! let x_axis = axis_bottom_iter(
//! &x_scale,
//! height - margin_bottom,
//! |tick| tick.to_string(),
//! &axis_options,
//! );
//!
//! let y_axis = axis_left_iter(
//! &y_scale,
//! margin_left,
//! |tick| tick.to_string(),
//! &axis_options,
//! );
//!
//! let rects = rect_iter(
//! &data,
//! |d| [
//! x_scale.scale(d.hour).unwrap_or_default(),
//! y_scale.scale(&d.location).unwrap_or_default()
//! ],
//! |_| [20., 20.],
//! None, // corner radius
//! |d| ShapeAttrs {
//! fill_color: Some(rect_color.scale(d.vehicles as f32)),
//! ..Default::default()
//! }
//! );
//! ```
//!
//! ## Notes
//!
//! Every function in the [`draw`][`crate::draw`] module shares the same API: they return an
//! iterator of type `impl Iterator<Item = T>`. For basic shapes such as circles, rectangles, text,
//! and lines, `T` is a structure containing all properties for each element. For specific curves
//! like paths, areas, or arrows, `T` represents a sequence of
//! [`PathCommand`][`crate::draw::PathCommand`] used to draw the curve.