| 1 | #![warn (missing_docs)] |
| 2 | #![allow (clippy::type_complexity)] |
| 3 | #![cfg_attr (doc_cfg, feature(doc_cfg))] |
| 4 | /*! |
| 5 | |
| 6 | # Plotters - A Rust drawing library focusing on data plotting for both WASM and native applications 🦀📈🚀 |
| 7 | |
| 8 | <a href="https://crates.io/crates/plotters"> |
| 9 | <img style="display: inline!important" src="https://img.shields.io/crates/v/plotters.svg"></img> |
| 10 | </a> |
| 11 | <a href="https://docs.rs/plotters"> |
| 12 | <img style="display: inline!important" src="https://docs.rs/plotters/badge.svg"></img> |
| 13 | </a> |
| 14 | <a href="https://docs.rs/plotters"> |
| 15 | <img style="display: inline!important" src="https://img.shields.io/crates/d/plotters"></img> |
| 16 | </a> |
| 17 | <a href="https://plotters-rs.github.io/rustdoc/plotters/"> |
| 18 | <img style="display: inline! important" src="https://img.shields.io/badge/docs-development-lightgrey.svg"></img> |
| 19 | </a> |
| 20 | |
| 21 | Plotters is a drawing library designed for rendering figures, plots, and charts, in pure Rust. Plotters supports various types of back-ends, |
| 22 | including bitmap, vector graph, piston window, GTK/Cairo and WebAssembly. |
| 23 | |
| 24 | - A new Plotters Developer's Guide is a work in progress. The preview version is available [here](https://plotters-rs.github.io/book). |
| 25 | - Try Plotters with an interactive Jupyter notebook, or view [here](https://plotters-rs.github.io/plotters-doc-data/evcxr-jupyter-integration.html) for the static HTML version. |
| 26 | - To view the WASM example, go to this [link](https://plotters-rs.github.io/wasm-demo/www/index.html) |
| 27 | - Currently we have all the internal code ready for console plotting, but a console based backend is still not ready. See [this example](https://github.com/plotters-rs/plotters/blob/master/plotters/examples/console.rs) for how to plot on console with a customized backend. |
| 28 | - Plotters has moved all backend code to separate repositories, check [FAQ list](#faq-list) for details |
| 29 | - Some interesting [demo projects](#demo-projects) are available, feel free to try them out. |
| 30 | |
| 31 | ## Gallery |
| 32 | |
| 33 | <div class="galleryItem"> |
| 34 | <a href="https://plotters-rs.github.io/plotters-doc-data/sample.png"> |
| 35 | <img src="https://plotters-rs.github.io/plotters-doc-data/sample.png" class="galleryItem"></img> |
| 36 | </a> |
| 37 | <div class="galleryText"> |
| 38 | Multiple Plot |
| 39 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/chart.rs">[code]</a> |
| 40 | </div> |
| 41 | </div> |
| 42 | |
| 43 | <div class="galleryItem"> |
| 44 | <a href="https://plotters-rs.github.io/plotters-doc-data/stock.png"> |
| 45 | <img src="https://plotters-rs.github.io/plotters-doc-data/stock.png" class="galleryItem"></img> |
| 46 | </a> |
| 47 | <div class="galleryText"> |
| 48 | Candlestick Plot |
| 49 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/stock.rs">[code]</a> |
| 50 | </div> |
| 51 | </div> |
| 52 | |
| 53 | <div class="galleryItem"> |
| 54 | <a href="https://plotters-rs.github.io/plotters-doc-data/histogram.png"> |
| 55 | <img src="https://plotters-rs.github.io/plotters-doc-data/histogram.png" class="galleryItem"></img> |
| 56 | </a> |
| 57 | <div class="galleryText"> |
| 58 | Histogram |
| 59 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/histogram.rs">[code]</a> |
| 60 | </div> |
| 61 | </div> |
| 62 | |
| 63 | <div class="galleryItem"> |
| 64 | <a href="https://plotters-rs.github.io/plotters-doc-data/0.png"> |
| 65 | <img src="https://plotters-rs.github.io/plotters-doc-data/0.png" class="galleryItem"></img> |
| 66 | </a> |
| 67 | <div class="galleryText"> |
| 68 | Simple Chart |
| 69 | </div> |
| 70 | </div> |
| 71 | |
| 72 | <div class="galleryItem"> |
| 73 | <a href="https://plotters-rs.github.io/plotters-doc-data/console-2.png"> |
| 74 | <img src="https://plotters-rs.github.io/plotters-doc-data/console-2.png" class="galleryItem"></img> |
| 75 | </a> |
| 76 | <div class="galleryText"> |
| 77 | Plotting the Console |
| 78 | </div> |
| 79 | </div> |
| 80 | |
| 81 | <div class="galleryItem"> |
| 82 | <a href="https://plotters-rs.github.io/plotters-doc-data/mandelbrot.png"> |
| 83 | <img src="https://plotters-rs.github.io/plotters-doc-data/mandelbrot.png" class="galleryItem"></img> |
| 84 | </a> |
| 85 | <div class="galleryText"> |
| 86 | Mandelbrot set |
| 87 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/mandelbrot.rs">[code]</a> |
| 88 | </div> |
| 89 | </div> |
| 90 | |
| 91 | |
| 92 | <div class="galleryItem"> |
| 93 | <a href="https://plotters-rs.github.io/plotters-doc-data/evcxr_animation.gif"> |
| 94 | <img src="https://plotters-rs.github.io/plotters-doc-data/evcxr_animation.gif" class="galleryItem"></img> |
| 95 | </a> |
| 96 | <div class="galleryText"> |
| 97 | Jupyter Support |
| 98 | </div> |
| 99 | </div> |
| 100 | |
| 101 | <div class="galleryItem"> |
| 102 | <a href="https://plotters-rs.github.io/plotters-doc-data/plotters-piston.gif"> |
| 103 | <img src="https://plotters-rs.github.io/plotters-doc-data/plotters-piston.gif" class="galleryItem"></img> |
| 104 | </a> |
| 105 | <div class="galleryText"> |
| 106 | Real-time Rendering |
| 107 | <a href="https://github.com/plotters-rs/plotters-piston/blob/master/examples/cpustat.rs">[code]</a> |
| 108 | </div> |
| 109 | </div> |
| 110 | |
| 111 | <div class="galleryItem"> |
| 112 | <a href="https://plotters-rs.github.io/plotters-doc-data/normal-dist.png"> |
| 113 | <img src="https://plotters-rs.github.io/plotters-doc-data/normal-dist.png" class="galleryItem"></img> |
| 114 | </a> |
| 115 | <div class="galleryText"> |
| 116 | Histogram with Scatter |
| 117 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/normal-dist.rs">[code]</a> |
| 118 | </div> |
| 119 | </div> |
| 120 | |
| 121 | <div class="galleryItem"> |
| 122 | <a href="https://plotters-rs.github.io/plotters-doc-data/twoscale.png"> |
| 123 | <img src="https://plotters-rs.github.io/plotters-doc-data/twoscale.png" class="galleryItem"></img> |
| 124 | </a> |
| 125 | <div class="galleryText"> |
| 126 | Dual Y-Axis Example |
| 127 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/two-scales.rs">[code]</a> |
| 128 | </div> |
| 129 | </div> |
| 130 | |
| 131 | <div class="galleryItem"> |
| 132 | <a href="https://plotters-rs.github.io/plotters-doc-data/matshow.png"> |
| 133 | <img src="https://plotters-rs.github.io/plotters-doc-data/matshow.png" class="galleryItem"></img> |
| 134 | </a> |
| 135 | <div class="galleryText"> |
| 136 | The Matplotlib Matshow Example |
| 137 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/matshow.rs">[code]</a> |
| 138 | </div> |
| 139 | </div> |
| 140 | |
| 141 | <div class="galleryItem"> |
| 142 | <a href="https://plotters-rs.github.io/plotters-doc-data/sierpinski.png"> |
| 143 | <img src="https://plotters-rs.github.io/plotters-doc-data/sierpinski.png" class="galleryItem"></img> |
| 144 | </a> |
| 145 | <div class="galleryText"> |
| 146 | The Sierpinski Carpet |
| 147 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/sierpinski.rs">[code]</a> |
| 148 | </div> |
| 149 | </div> |
| 150 | |
| 151 | <div class="galleryItem"> |
| 152 | <a href="https://plotters-rs.github.io/plotters-doc-data/normal-dist2.png"> |
| 153 | <img src="https://plotters-rs.github.io/plotters-doc-data/normal-dist2.png" class="galleryItem"></img> |
| 154 | </a> |
| 155 | <div class="galleryText"> |
| 156 | The 1D Gaussian Distribution |
| 157 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/normal-dist2.rs">[code]</a> |
| 158 | </div> |
| 159 | </div> |
| 160 | |
| 161 | <div class="galleryItem"> |
| 162 | <a href="https://plotters-rs.github.io/plotters-doc-data/errorbar.png"> |
| 163 | <img src="https://plotters-rs.github.io/plotters-doc-data/errorbar.png" class="galleryItem"></img> |
| 164 | </a> |
| 165 | <div class="galleryText"> |
| 166 | The 1D Gaussian Distribution |
| 167 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/errorbar.rs">[code]</a> |
| 168 | </div> |
| 169 | </div> |
| 170 | |
| 171 | <div class="galleryItem"> |
| 172 | <a href="https://plotters-rs.github.io/plotters-doc-data/slc-temp.png"> |
| 173 | <img src="https://plotters-rs.github.io/plotters-doc-data/slc-temp.png" class="galleryItem"></img> |
| 174 | </a> |
| 175 | <div class="galleryText"> |
| 176 | Monthly Time Coordinate |
| 177 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/slc-temp.rs">[code]</a> |
| 178 | </div> |
| 179 | </div> |
| 180 | |
| 181 | <div class="galleryItem"> |
| 182 | <a href="https://plotters-rs.github.io/plotters-doc-data/area-chart.png"> |
| 183 | <img src="https://plotters-rs.github.io/plotters-doc-data/area-chart.png" class="galleryItem"></img> |
| 184 | </a> |
| 185 | <div class="galleryText"> |
| 186 | Monthly Time Coordinate |
| 187 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/area-chart.rs">[code]</a> |
| 188 | </div> |
| 189 | </div> |
| 190 | |
| 191 | <div class="galleryItem"> |
| 192 | <a href="https://plotters-rs.github.io/plotters-doc-data/snowflake.png"> |
| 193 | <img src="https://plotters-rs.github.io/plotters-doc-data/snowflake.png" class="galleryItem"></img> |
| 194 | </a> |
| 195 | <div class="galleryText"> |
| 196 | Koch Snowflake |
| 197 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/snowflake.rs">[code]</a> |
| 198 | </div> |
| 199 | </div> |
| 200 | |
| 201 | |
| 202 | <div class="galleryItem"> |
| 203 | <a href="https://plotters-rs.github.io/plotters-doc-data/animation.gif"> |
| 204 | <img src="https://plotters-rs.github.io/plotters-doc-data/animation.gif" class="galleryItem"></img> |
| 205 | </a> |
| 206 | <div class="galleryText"> |
| 207 | Koch Snowflake Animation |
| 208 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/animation.rs">[code]</a> |
| 209 | </div> |
| 210 | </div> |
| 211 | |
| 212 | |
| 213 | <div class="galleryItem"> |
| 214 | <a href="https://plotters-rs.github.io/plotters-doc-data/console.png"> |
| 215 | <img src="https://plotters-rs.github.io/plotters-doc-data/console.png" class="galleryItem"></img> |
| 216 | </a> |
| 217 | <div class="galleryText"> |
| 218 | Drawing on a Console |
| 219 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/console.rs">[code]</a> |
| 220 | </div> |
| 221 | </div> |
| 222 | |
| 223 | <div class="galleryItem"> |
| 224 | <a href="https://plotters-rs.github.io/plotters-doc-data/blit-bitmap.png"> |
| 225 | <img src="https://plotters-rs.github.io/plotters-doc-data/blit-bitmap.png" class="galleryItem"></img> |
| 226 | </a> |
| 227 | <div class="galleryText"> |
| 228 | Drawing bitmap on chart |
| 229 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/blit-bitmap.rs">[code]</a> |
| 230 | </div> |
| 231 | </div> |
| 232 | |
| 233 | <div class="galleryItem"> |
| 234 | <a href="https://plotters-rs.github.io/plotters-doc-data/boxplot.svg"> |
| 235 | <img src="https://plotters-rs.github.io/plotters-doc-data/boxplot.svg" class="galleryItem"></img> |
| 236 | </a> |
| 237 | <div class="galleryText"> |
| 238 | The boxplot demo |
| 239 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/boxplot.rs">[code]</a> |
| 240 | </div> |
| 241 | </div> |
| 242 | |
| 243 | <div class="galleryItem"> |
| 244 | <a href="https://plotters-rs.github.io/plotters-doc-data/3d-plot.svg"> |
| 245 | <img src="https://plotters-rs.github.io/plotters-doc-data/3d-plot.svg" class="galleryItem"></img> |
| 246 | </a> |
| 247 | <div class="galleryText"> |
| 248 | 3D plot rendering |
| 249 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/3d-plot.rs">[code]</a> |
| 250 | </div> |
| 251 | </div> |
| 252 | |
| 253 | <div class="galleryItem"> |
| 254 | <a href="https://plotters-rs.github.io/plotters-doc-data/3d-plot2.gif"> |
| 255 | <img src="https://plotters-rs.github.io/plotters-doc-data/3d-plot2.gif" class="galleryItem"></img> |
| 256 | </a> |
| 257 | <div class="galleryText"> |
| 258 | 2-Var Gussian Distribution PDF |
| 259 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/3d-plot2.rs">[code]</a> |
| 260 | </div> |
| 261 | </div> |
| 262 | |
| 263 | <div class="galleryItem"> |
| 264 | <a href="https://plotters-rs.github.io/plotters-doc-data/tick_control.svg"> |
| 265 | <img src="https://plotters-rs.github.io/plotters-doc-data/tick_control.svg" class="galleryItem"></img> |
| 266 | </a> |
| 267 | <div class="galleryText"> |
| 268 | COVID-19 Visualization |
| 269 | <a href="https://github.com/plotters-rs/plotters/blob/master/plotters/examples/tick_control.rs">[code]</a> |
| 270 | </div> |
| 271 | </div> |
| 272 | |
| 273 | |
| 274 | ## Table of Contents |
| 275 | * [Gallery](#gallery) |
| 276 | * [Dependencies](#dependencies) |
| 277 | + [Ubuntu Linux](#ubuntu-linux) |
| 278 | * [Quick Start](#quick-start) |
| 279 | * [Demo Projects](#demo-projects) |
| 280 | * [Trying with Jupyter evcxr Kernel Interactively](#trying-with-jupyter-evcxr-kernel-interactively) |
| 281 | * [Interactive Tutorial with Jupyter Notebook](#interactive-tutorial-with-jupyter-notebook) |
| 282 | * [Plotting in Rust](#plotting-in-rust) |
| 283 | * [Plotting on HTML5 canvas with WASM Backend](#plotting-on-html5-canvas-with-wasm-backend) |
| 284 | * [What types of figure are supported?](#what-types-of-figure-are-supported) |
| 285 | * [Concepts by example](#concepts-by-example) |
| 286 | + [Drawing Backends](#drawing-backends) |
| 287 | + [Drawing Area](#drawing-area) |
| 288 | + [Elements](#elements) |
| 289 | + [Composable Elements](#composable-elements) |
| 290 | + [Chart Context](#chart-context) |
| 291 | * [Misc](#misc) |
| 292 | + [Development Version](#development-version) |
| 293 | + [Reducing Depending Libraries && Turning Off Backends](#reducing-depending-libraries--turning-off-backends) |
| 294 | + [List of Features](#list-of-features) |
| 295 | * [FAQ List](#faq-list) |
| 296 | |
| 297 | ## Dependencies |
| 298 | |
| 299 | ### Ubuntu Linux |
| 300 | |
| 301 | ```sudo apt install pkg-config libfreetype6-dev libfontconfig1-dev``` |
| 302 | |
| 303 | ## Quick Start |
| 304 | |
| 305 | To use Plotters, you can simply add Plotters into your `Cargo.toml` |
| 306 | ```toml |
| 307 | [dependencies] |
| 308 | plotters = "0.3.3" |
| 309 | ``` |
| 310 | |
| 311 | And the following code draws a quadratic function. `src/main.rs`, |
| 312 | |
| 313 | ```rust |
| 314 | use plotters::prelude::*; |
| 315 | fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 316 | let root = BitMapBackend::new("plotters-doc-data/0.png", (640, 480)).into_drawing_area(); |
| 317 | root.fill(&WHITE)?; |
| 318 | let mut chart = ChartBuilder::on(&root) |
| 319 | .caption("y=x^2", ("sans-serif", 50).into_font()) |
| 320 | .margin(5) |
| 321 | .x_label_area_size(30) |
| 322 | .y_label_area_size(30) |
| 323 | .build_cartesian_2d(-1f32..1f32, -0.1f32..1f32)?; |
| 324 | |
| 325 | chart.configure_mesh().draw()?; |
| 326 | |
| 327 | chart |
| 328 | .draw_series(LineSeries::new( |
| 329 | (-50..=50).map(|x| x as f32 / 50.0).map(|x| (x, x * x)), |
| 330 | &RED, |
| 331 | ))? |
| 332 | .label("y = x^2") |
| 333 | .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &RED)); |
| 334 | |
| 335 | chart |
| 336 | .configure_series_labels() |
| 337 | .background_style(&WHITE.mix(0.8)) |
| 338 | .border_style(&BLACK) |
| 339 | .draw()?; |
| 340 | |
| 341 | root.present()?; |
| 342 | |
| 343 | Ok(()) |
| 344 | } |
| 345 | ``` |
| 346 | |
| 347 |  |
| 348 | |
| 349 | ## Demo Projects |
| 350 | |
| 351 | To learn how to use Plotters in different scenarios, check out the following demo projects: |
| 352 | |
| 353 | - WebAssembly + Plotters: [plotters-wasm-demo](https://github.com/plotters-rs/plotters-wasm-demo) |
| 354 | - minifb + Plotters: [plotters-minifb-demo](https://github.com/plotters-rs/plotters-minifb-demo) |
| 355 | - GTK + Plotters: [plotters-gtk-demo](https://github.com/plotters-rs/plotters-gtk-demo) |
| 356 | |
| 357 | |
| 358 | ## Trying with Jupyter evcxr Kernel Interactively |
| 359 | |
| 360 | Plotters now supports integration with `evcxr` and is able to interactively draw plots in Jupyter Notebook. |
| 361 | The feature `evcxr` should be enabled when including Plotters to Jupyter Notebook. |
| 362 | |
| 363 | The following code shows a minimal example of this. |
| 364 | |
| 365 | ```text |
| 366 | :dep plotters = { version = "^0.3.6", default-features = false, features = ["evcxr", "all_series", "all_elements"] } |
| 367 | extern crate plotters; |
| 368 | use plotters::prelude::*; |
| 369 | |
| 370 | let figure = evcxr_figure((640, 480), |root| { |
| 371 | root.fill(&WHITE)?; |
| 372 | let mut chart = ChartBuilder::on(&root) |
| 373 | .caption("y=x^2", ("Arial", 50).into_font()) |
| 374 | .margin(5) |
| 375 | .x_label_area_size(30) |
| 376 | .y_label_area_size(30) |
| 377 | .build_cartesian_2d(-1f32..1f32, -0.1f32..1f32)?; |
| 378 | |
| 379 | chart.configure_mesh().draw()?; |
| 380 | |
| 381 | chart.draw_series(LineSeries::new( |
| 382 | (-50..=50).map(|x| x as f32 / 50.0).map(|x| (x, x * x)), |
| 383 | &RED, |
| 384 | )).unwrap() |
| 385 | .label("y = x^2") |
| 386 | .legend(|(x,y)| PathElement::new(vec![(x,y), (x + 20,y)], &RED)); |
| 387 | |
| 388 | chart.configure_series_labels() |
| 389 | .background_style(&WHITE.mix(0.8)) |
| 390 | .border_style(&BLACK) |
| 391 | .draw()?; |
| 392 | Ok(()) |
| 393 | }); |
| 394 | figure |
| 395 | ``` |
| 396 | |
| 397 | <img src="https://plotters-rs.github.io/plotters-doc-data/evcxr_animation.gif" width="450px" ></img> |
| 398 | |
| 399 | ## Interactive Tutorial with Jupyter Notebook |
| 400 | |
| 401 | *This tutorial is a work in progress and isn't complete* |
| 402 | |
| 403 | Thanks to the evcxr, now we have an interactive tutorial for Plotters! |
| 404 | To use the interactive notebook, you must have Jupyter and evcxr installed on your computer. |
| 405 | Follow the instruction on [this page](https://github.com/google/evcxr/tree/master/evcxr_jupyter) below to install it. |
| 406 | |
| 407 | After that, you should be able to start your Jupyter server locally and load the tutorial! |
| 408 | |
| 409 | ```bash |
| 410 | git clone https://github.com/38/plotters-doc-data |
| 411 | cd plotters-doc-data |
| 412 | jupyter notebook |
| 413 | ``` |
| 414 | |
| 415 | And select the notebook called `evcxr-jupyter-integration.ipynb`. |
| 416 | |
| 417 | Also, there's a static HTML version of this notebook available at [this location](https://plotters-rs.github.io/plotters-doc-data/evcxr-jupyter-integration.html) |
| 418 | |
| 419 | ## Plotting in Rust |
| 420 | |
| 421 | Rust is a perfect language for data visualization. Although there are many mature visualization libraries in many different languages, Rust is one of the best languages that fits the need. |
| 422 | |
| 423 | * **Easy to use** Rust has a very good iterator system built into the standard library. With the help of iterators, |
| 424 | plotting in Rust can be as easy as most of the high-level programming languages. The Rust based plotting library |
| 425 | can be very easy to use. |
| 426 | |
| 427 | * **Fast** If you need to render a figure with trillions of data points, |
| 428 | Rust is a good choice. Rust's performance allows you to combine the data processing step |
| 429 | and rendering step into a single application. When plotting in high-level programming languages, |
| 430 | e.g. Javascript or Python, data points must be down-sampled before feeding into the plotting |
| 431 | program because of the performance considerations. Rust is fast enough to do the data processing and visualization |
| 432 | within a single program. You can also integrate the |
| 433 | figure rendering code into your application to handle a huge amount of data and visualize it in real-time. |
| 434 | |
| 435 | * **WebAssembly Support** Rust is one of the languages with the best WASM support. Plotting in Rust could be |
| 436 | very useful for visualization on a web page and would have a huge performance improvement comparing to Javascript. |
| 437 | |
| 438 | ## Plotting on HTML5 canvas with WASM Backend |
| 439 | |
| 440 | Plotters currently supports a backend that uses the HTML5 canvas. To use WASM, you can simply use |
| 441 | `CanvasBackend` instead of other backend and all other API remains the same! |
| 442 | |
| 443 | There's a small demo for Plotters + WASM available at [here](https://github.com/plotters-rs/plotters-wasm-demo). |
| 444 | To play with the deployed version, follow this [link](https://plotters-rs.github.io/wasm-demo/www/index.html). |
| 445 | |
| 446 | ## What types of figure are supported? |
| 447 | |
| 448 | Plotters is not limited to any specific type of figure. |
| 449 | You can create your own types of figures easily with the Plotters API. |
| 450 | |
| 451 | Plotters does provide some built-in figure types for convenience. |
| 452 | Currently, we support line series, point series, candlestick series, and histogram. |
| 453 | And the library is designed to be able to render multiple figure into a single image. |
| 454 | But Plotter is aimed to be a platform that is fully extendable to support any other types of figure. |
| 455 | |
| 456 | ## Concepts by example |
| 457 | |
| 458 | ### Drawing Backends |
| 459 | Plotters can use different drawing backends, including SVG, BitMap, and even real-time rendering. For example, a bitmap drawing backend. |
| 460 | |
| 461 | ```rust |
| 462 | use plotters::prelude::*; |
| 463 | fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 464 | // Create a 800*600 bitmap and start drawing |
| 465 | let mut backend = BitMapBackend::new("plotters-doc-data/1.png", (300, 200)); |
| 466 | // And if we want SVG backend |
| 467 | // let backend = SVGBackend::new("output.svg", (800, 600)); |
| 468 | backend.draw_rect((50, 50), (200, 150), &RED, true)?; |
| 469 | backend.present()?; |
| 470 | Ok(()) |
| 471 | } |
| 472 | ``` |
| 473 | |
| 474 |  |
| 475 | |
| 476 | ### Drawing Area |
| 477 | Plotters uses a concept called drawing area for layout purpose. |
| 478 | Plotters supports integrating multiple figures into a single image. |
| 479 | This is done by creating sub-drawing-areas. |
| 480 | |
| 481 | Besides that, the drawing area also allows for a customized coordinate system, by doing so, the coordinate mapping is done by the drawing area automatically. |
| 482 | |
| 483 | ```rust |
| 484 | use plotters::prelude::*; |
| 485 | fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 486 | let root_drawing_area = |
| 487 | BitMapBackend::new("plotters-doc-data/2.png", (300, 200)).into_drawing_area(); |
| 488 | // And we can split the drawing area into 3x3 grid |
| 489 | let child_drawing_areas = root_drawing_area.split_evenly((3, 3)); |
| 490 | // Then we fill the drawing area with different color |
| 491 | for (area, color) in child_drawing_areas.into_iter().zip(0..) { |
| 492 | area.fill(&Palette99::pick(color))?; |
| 493 | } |
| 494 | root_drawing_area.present()?; |
| 495 | Ok(()) |
| 496 | } |
| 497 | ``` |
| 498 | |
| 499 |  |
| 500 | |
| 501 | ### Elements |
| 502 | |
| 503 | In Plotters, elements are the building blocks of figures. All elements are able to be drawn on a drawing area. |
| 504 | There are different types of built-in elements, like lines, texts, circles, etc. |
| 505 | You can also define your own element in the application code. |
| 506 | |
| 507 | You may also combine existing elements to build a complex element. |
| 508 | |
| 509 | To learn more about the element system, please read the [element module documentation](./element/index.html). |
| 510 | |
| 511 | ```rust |
| 512 | use plotters::prelude::*; |
| 513 | fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 514 | let root = BitMapBackend::new("plotters-doc-data/3.png", (300, 200)).into_drawing_area(); |
| 515 | root.fill(&WHITE)?; |
| 516 | // Draw an circle on the drawing area |
| 517 | root.draw(&Circle::new( |
| 518 | (100, 100), |
| 519 | 50, |
| 520 | Into::<ShapeStyle>::into(&GREEN).filled(), |
| 521 | ))?; |
| 522 | root.present()?; |
| 523 | Ok(()) |
| 524 | } |
| 525 | ``` |
| 526 | |
| 527 |  |
| 528 | |
| 529 | ### Composable Elements |
| 530 | |
| 531 | Besides the built-in elements, elements can be composed into a logical group we called composed elements. |
| 532 | When composing new elements, the upper-left corner is given in the target coordinate, and a new pixel-based |
| 533 | coordinate which has the upper-left corner defined as `(0,0)` is used for further element composition. |
| 534 | |
| 535 | For example, we can have an element which includes a dot and its coordinate. |
| 536 | |
| 537 | ```rust |
| 538 | use plotters::prelude::*; |
| 539 | use plotters::coord::types::RangedCoordf32; |
| 540 | |
| 541 | fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 542 | let root = BitMapBackend::new("plotters-doc-data/4.png", (640, 480)).into_drawing_area(); |
| 543 | |
| 544 | root.fill(&RGBColor(240, 200, 200))?; |
| 545 | |
| 546 | let root = root.apply_coord_spec(Cartesian2d::<RangedCoordf32, RangedCoordf32>::new( |
| 547 | 0f32..1f32, |
| 548 | 0f32..1f32, |
| 549 | (0..640, 0..480), |
| 550 | )); |
| 551 | |
| 552 | let dot_and_label = |x: f32, y: f32| { |
| 553 | return EmptyElement::at((x, y)) |
| 554 | + Circle::new((0, 0), 3, ShapeStyle::from(&BLACK).filled()) |
| 555 | + Text::new( |
| 556 | format!("({:.2},{:.2})", x, y), |
| 557 | (10, 0), |
| 558 | ("sans-serif", 15.0).into_font(), |
| 559 | ); |
| 560 | }; |
| 561 | |
| 562 | root.draw(&dot_and_label(0.5, 0.6))?; |
| 563 | root.draw(&dot_and_label(0.25, 0.33))?; |
| 564 | root.draw(&dot_and_label(0.8, 0.8))?; |
| 565 | root.present()?; |
| 566 | Ok(()) |
| 567 | } |
| 568 | ``` |
| 569 | |
| 570 |  |
| 571 | |
| 572 | ### Chart Context |
| 573 | |
| 574 | In order to draw a chart, Plotters needs a data object built on top of the drawing area called `ChartContext`. |
| 575 | The chart context defines even higher level constructs compare to the drawing area. |
| 576 | For example, you can define the label areas, meshes, and put a data series onto the drawing area with the help |
| 577 | of the chart context object. |
| 578 | |
| 579 | ```rust |
| 580 | use plotters::prelude::*; |
| 581 | fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 582 | let root = BitMapBackend::new("plotters-doc-data/5.png", (640, 480)).into_drawing_area(); |
| 583 | root.fill(&WHITE); |
| 584 | let root = root.margin(10, 10, 10, 10); |
| 585 | // After this point, we should be able to construct a chart context |
| 586 | let mut chart = ChartBuilder::on(&root) |
| 587 | // Set the caption of the chart |
| 588 | .caption("This is our first plot", ("sans-serif", 40).into_font()) |
| 589 | // Set the size of the label region |
| 590 | .x_label_area_size(20) |
| 591 | .y_label_area_size(40) |
| 592 | // Finally attach a coordinate on the drawing area and make a chart context |
| 593 | .build_cartesian_2d(0f32..10f32, 0f32..10f32)?; |
| 594 | |
| 595 | // Then we can draw a mesh |
| 596 | chart |
| 597 | .configure_mesh() |
| 598 | // We can customize the maximum number of labels allowed for each axis |
| 599 | .x_labels(5) |
| 600 | .y_labels(5) |
| 601 | // We can also change the format of the label text |
| 602 | .y_label_formatter(&|x| format!("{:.3}", x)) |
| 603 | .draw()?; |
| 604 | |
| 605 | // And we can draw something in the drawing area |
| 606 | chart.draw_series(LineSeries::new( |
| 607 | vec![(0.0, 0.0), (5.0, 5.0), (8.0, 7.0)], |
| 608 | &RED, |
| 609 | ))?; |
| 610 | // Similarly, we can draw point series |
| 611 | chart.draw_series(PointSeries::of_element( |
| 612 | vec![(0.0, 0.0), (5.0, 5.0), (8.0, 7.0)], |
| 613 | 5, |
| 614 | &RED, |
| 615 | &|c, s, st| { |
| 616 | return EmptyElement::at(c) // We want to construct a composed element on-the-fly |
| 617 | + Circle::new((0,0),s,st.filled()) // At this point, the new pixel coordinate is established |
| 618 | + Text::new(format!("{:?}", c), (10, 0), ("sans-serif", 10).into_font()); |
| 619 | }, |
| 620 | ))?; |
| 621 | root.present()?; |
| 622 | Ok(()) |
| 623 | } |
| 624 | ``` |
| 625 | |
| 626 |  |
| 627 | |
| 628 | ## Misc |
| 629 | |
| 630 | ### Development Version |
| 631 | |
| 632 | Find the latest development version of Plotters on [GitHub](https://github.com/plotters-rs/plotters.git). |
| 633 | Clone the repository and learn more about the Plotters API and ways to contribute. Your help is needed! |
| 634 | |
| 635 | If you want to add the development version of Plotters to your project, add the following to your `Cargo.toml`: |
| 636 | |
| 637 | ```toml |
| 638 | [dependencies] |
| 639 | plotters = { git = "https://github.com/plotters-rs/plotters.git" } |
| 640 | ``` |
| 641 | |
| 642 | ### Reducing Depending Libraries && Turning Off Backends |
| 643 | Plotters now supports use features to control the backend dependencies. By default, `BitMapBackend` and `SVGBackend` are supported, |
| 644 | use `default-features = false` in the dependency description in `Cargo.toml` and you can cherry-pick the backend implementations. |
| 645 | |
| 646 | - `svg` Enable the `SVGBackend` |
| 647 | - `bitmap` Enable the `BitMapBackend` |
| 648 | |
| 649 | For example, the following dependency description would avoid compiling with bitmap support: |
| 650 | |
| 651 | ```toml |
| 652 | [dependencies] |
| 653 | plotters = { git = "https://github.com/plotters-rs/plotters.git", default-features = false, features = ["svg"] } |
| 654 | ``` |
| 655 | |
| 656 | The library also allows consumers to make use of the [`Palette`](https://crates.io/crates/palette/) crate's color types by default. |
| 657 | This behavior can also be turned off by setting `default-features = false`. |
| 658 | |
| 659 | ### List of Features |
| 660 | |
| 661 | This is the full list of features that is defined by `Plotters` crate. |
| 662 | Use `default-features = false` to disable those default enabled features, |
| 663 | and then you should be able to cherry-pick what features you want to include into `Plotters` crate. |
| 664 | By doing so, you can minimize the number of dependencies down to only `itertools` and compile time is less than 6s. |
| 665 | |
| 666 | The following list is a complete list of features that can be opted in or out. |
| 667 | |
| 668 | - Tier 1 drawing backends |
| 669 | |
| 670 | | Name | Description | Additional Dependency |Default?| |
| 671 | |---------|--------------|--------|------------| |
| 672 | | bitmap\_encoder | Allow `BitMapBackend` to save the result to bitmap files | image, rusttype, font-kit | Yes | |
| 673 | | svg\_backend | Enable `SVGBackend` Support | None | Yes | |
| 674 | | bitmap\_gif| Opt-in GIF animation Rendering support for `BitMapBackend`, implies `bitmap` enabled | gif | Yes | |
| 675 | |
| 676 | - Font manipulation features |
| 677 | |
| 678 | | Name | Description | Additional Dependency | Default? | |
| 679 | |----------|------------------------------------------|-----------------------|----------| |
| 680 | | ttf | Allows TrueType font support | font-kit | Yes | |
| 681 | | ab_glyph | Skips loading system fonts, unlike `ttf` | ab_glyph | No | |
| 682 | |
| 683 | `ab_glyph` supports TrueType and OpenType fonts, but does not attempt to |
| 684 | load fonts provided by the system on which it is running. |
| 685 | It is pure Rust, and easier to cross compile. |
| 686 | To use this, you *must* call `plotters::style::register_font` before |
| 687 | using any `plotters` functions which require the ability to render text. |
| 688 | This function only exists when the `ab_glyph` feature is enabled. |
| 689 | ```rust,ignore |
| 690 | /// Register a font in the fonts table. |
| 691 | /// |
| 692 | /// The `name` parameter gives the name this font shall be referred to |
| 693 | /// in the other APIs, like `"sans-serif"`. |
| 694 | /// |
| 695 | /// Unprovided font styles for a given name will fallback to `FontStyle::Normal` |
| 696 | /// if that is available for that name, when other functions lookup fonts which |
| 697 | /// are registered with this function. |
| 698 | /// |
| 699 | /// The `bytes` parameter should be the complete contents |
| 700 | /// of an OpenType font file, like: |
| 701 | /// ```ignore |
| 702 | /// include_bytes!("FiraGO-Regular.otf") |
| 703 | /// ``` |
| 704 | pub fn register_font( |
| 705 | name: &str, |
| 706 | style: FontStyle, |
| 707 | bytes: &'static [u8], |
| 708 | ) -> Result<(), InvalidFont> |
| 709 | ``` |
| 710 | |
| 711 | - Coordinate features |
| 712 | |
| 713 | | Name | Description | Additional Dependency |Default?| |
| 714 | |---------|--------------|--------|------------| |
| 715 | | datetime | Enable the date and time coordinate support | chrono | Yes | |
| 716 | |
| 717 | - Element, series and util functions |
| 718 | |
| 719 | | Name | Description | Additional Dependency |Default?| |
| 720 | |---------|--------------|--------|------------| |
| 721 | | errorbar | The errorbar element support | None | Yes | |
| 722 | | candlestick | The candlestick element support | None | Yes | |
| 723 | | boxplot | The boxplot element support | None | Yes | |
| 724 | | area\_series | The area series support | None | Yes | |
| 725 | | line\_series | The line series support | None | Yes | |
| 726 | | histogram | The histogram series support | None | Yes | |
| 727 | | point\_series| The point series support | None | Yes | |
| 728 | |
| 729 | - Misc |
| 730 | |
| 731 | | Name | Description | Additional Dependency |Default?| |
| 732 | |---------|--------------|--------|------------| |
| 733 | | deprecated\_items | This feature allows use of deprecated items which is going to be removed in the future | None | Yes | |
| 734 | | debug | Enable the code used for debugging | None | No | |
| 735 | |
| 736 | |
| 737 | ## FAQ List |
| 738 | |
| 739 | * Why does the WASM example break on my machine ? |
| 740 | |
| 741 | The WASM example requires using `wasm32` target to build. Using `cargo build` is likely to use the default target |
| 742 | which in most of the case is any of the x86 target. Thus you need add `--target=wasm32-unknown-unknown` in the cargo |
| 743 | parameter list to build it. |
| 744 | |
| 745 | * How to draw text/circle/point/rectangle/... on the top of chart ? |
| 746 | |
| 747 | As you may have realized, Plotters is a drawing library rather than a traditional data plotting library, |
| 748 | you have the freedom to draw anything you want on the drawing area. |
| 749 | Use `DrawingArea::draw` to draw any element on the drawing area. |
| 750 | |
| 751 | * Where can I find the backend code ? |
| 752 | |
| 753 | Since Plotters 0.3, all drawing backends are independent crate from the main Plotters crate. |
| 754 | Use the following link to find the backend code: |
| 755 | |
| 756 | - [Bitmap Backend](https://github.com/plotters-rs/plotters-bitmap.git) |
| 757 | - [SVG Backend](https://github.com/plotters-rs/plotters-svg.git) |
| 758 | - [HTML5 Canvas Backend](https://github.com/plotters-rs/plotters-canvas.git) |
| 759 | - [GTK/Cairo Backend](https://github.com/plotters-rs/plotters-cairo.git) |
| 760 | |
| 761 | * How to check if a backend writes to a file successfully ? |
| 762 | |
| 763 | The behavior of Plotters backend is consistent with the standard library. |
| 764 | When the backend instance is dropped, [`crate::drawing::DrawingArea::present()`] or `Backend::present()` is called automatically |
| 765 | whenever is needed. When the `present()` method is called from `drop`, any error will be silently ignored. |
| 766 | |
| 767 | In the case that error handling is important, you need manually call the `present()` method before the backend gets dropped. |
| 768 | For more information, please see the examples. |
| 769 | |
| 770 | |
| 771 | <style> |
| 772 | img { |
| 773 | display: block; |
| 774 | margin: 0 auto; |
| 775 | max-width: 500px; |
| 776 | } |
| 777 | .galleryItem { |
| 778 | width: 250px; |
| 779 | display: inline-block; |
| 780 | } |
| 781 | .galleryImg { |
| 782 | max-width: 100%; |
| 783 | } |
| 784 | .galleryText { |
| 785 | text-align: center; |
| 786 | } |
| 787 | </style> |
| 788 | |
| 789 | |
| 790 | */ |
| 791 | pub mod chart; |
| 792 | pub mod coord; |
| 793 | pub mod data; |
| 794 | pub mod drawing; |
| 795 | pub mod element; |
| 796 | pub mod series; |
| 797 | pub mod style; |
| 798 | |
| 799 | /// Evaluation Context for Rust. See [the evcxr crate](https://crates.io/crates/evcxr) for more information. |
| 800 | #[cfg (feature = "evcxr" )] |
| 801 | #[cfg_attr (doc_cfg, doc(cfg(feature = "evcxr" )))] |
| 802 | pub mod evcxr; |
| 803 | |
| 804 | #[cfg (test)] |
| 805 | pub use crate::drawing::{check_color, create_mocked_drawing_area}; |
| 806 | |
| 807 | /// The module imports the most commonly used types and modules in Plotters |
| 808 | pub mod prelude { |
| 809 | // Chart related types |
| 810 | pub use crate::chart::{ChartBuilder, ChartContext, LabelAreaPosition, SeriesLabelPosition}; |
| 811 | |
| 812 | // Coordinates |
| 813 | pub use crate::coord::{ |
| 814 | cartesian::Cartesian2d, |
| 815 | combinators::{ |
| 816 | make_partial_axis, BindKeyPointMethod, BindKeyPoints, BuildNestedCoord, GroupBy, |
| 817 | IntoLinspace, IntoLogRange, IntoPartialAxis, Linspace, LogCoord, LogScalable, |
| 818 | NestedRange, NestedValue, ToGroupByRange, |
| 819 | }, |
| 820 | ranged1d::{DiscreteRanged, IntoSegmentedCoord, Ranged, SegmentValue}, |
| 821 | CoordTranslate, |
| 822 | }; |
| 823 | |
| 824 | #[allow (deprecated)] |
| 825 | pub use crate::coord::combinators::LogRange; |
| 826 | |
| 827 | #[cfg (feature = "chrono" )] |
| 828 | #[cfg_attr (doc_cfg, doc(cfg(feature = "chrono" )))] |
| 829 | pub use crate::coord::types::{ |
| 830 | IntoMonthly, IntoYearly, RangedDate, RangedDateTime, RangedDuration, |
| 831 | }; |
| 832 | |
| 833 | // Re-export the backend for backward compatibility |
| 834 | pub use plotters_backend::DrawingBackend; |
| 835 | |
| 836 | pub use crate::drawing::*; |
| 837 | |
| 838 | // Series helpers |
| 839 | #[cfg (feature = "area_series" )] |
| 840 | #[cfg_attr (doc_cfg, doc(cfg(feature = "area_series" )))] |
| 841 | pub use crate::series::AreaSeries; |
| 842 | #[cfg (feature = "histogram" )] |
| 843 | #[cfg_attr (doc_cfg, doc(cfg(feature = "histogram" )))] |
| 844 | pub use crate::series::Histogram; |
| 845 | #[cfg (feature = "point_series" )] |
| 846 | #[cfg_attr (doc_cfg, doc(cfg(feature = "point_series" )))] |
| 847 | pub use crate::series::PointSeries; |
| 848 | #[cfg (feature = "surface_series" )] |
| 849 | #[cfg_attr (doc_cfg, doc(cfg(feature = "surface_series" )))] |
| 850 | pub use crate::series::SurfaceSeries; |
| 851 | #[cfg (feature = "line_series" )] |
| 852 | #[cfg_attr (doc_cfg, doc(cfg(feature = "line_series" )))] |
| 853 | pub use crate::series::{DashedLineSeries, DottedLineSeries, LineSeries}; |
| 854 | |
| 855 | // Styles |
| 856 | pub use crate::style::{BLACK, BLUE, CYAN, GREEN, MAGENTA, RED, TRANSPARENT, WHITE, YELLOW}; |
| 857 | |
| 858 | #[cfg (feature = "full_palette" )] |
| 859 | #[cfg_attr (doc_cfg, doc(cfg(feature = "full_palette" )))] |
| 860 | pub use crate::style::full_palette; |
| 861 | |
| 862 | #[cfg (feature = "colormaps" )] |
| 863 | #[cfg_attr (doc_cfg, doc(cfg(feature = "colormaps" )))] |
| 864 | pub use crate::style::colors::colormaps::*; |
| 865 | |
| 866 | pub use crate::style::{ |
| 867 | AsRelative, Color, FontDesc, FontFamily, FontStyle, FontTransform, HSLColor, IntoFont, |
| 868 | IntoTextStyle, Palette, Palette100, Palette99, Palette9999, PaletteColor, RGBAColor, |
| 869 | RGBColor, ShapeStyle, TextStyle, |
| 870 | }; |
| 871 | |
| 872 | // Elements |
| 873 | pub use crate::element::{ |
| 874 | Circle, Cross, Cubiod, DynElement, EmptyElement, IntoDynElement, MultiLineText, |
| 875 | PathElement, Pie, Pixel, Polygon, Rectangle, Text, TriangleMarker, |
| 876 | }; |
| 877 | |
| 878 | #[cfg (feature = "boxplot" )] |
| 879 | #[cfg_attr (doc_cfg, doc(cfg(feature = "boxplot" )))] |
| 880 | pub use crate::element::Boxplot; |
| 881 | #[cfg (feature = "candlestick" )] |
| 882 | #[cfg_attr (doc_cfg, doc(cfg(feature = "candlestick" )))] |
| 883 | pub use crate::element::CandleStick; |
| 884 | #[cfg (feature = "errorbar" )] |
| 885 | #[cfg_attr (doc_cfg, doc(cfg(feature = "errorbar" )))] |
| 886 | pub use crate::element::ErrorBar; |
| 887 | |
| 888 | #[cfg (feature = "bitmap_backend" )] |
| 889 | #[cfg_attr (doc_cfg, doc(cfg(feature = "bitmap_backend" )))] |
| 890 | pub use crate::element::BitMapElement; |
| 891 | |
| 892 | // Data |
| 893 | pub use crate::data::Quartiles; |
| 894 | |
| 895 | // TODO: This should be deprecated and completely removed |
| 896 | #[cfg (feature = "deprecated_items" )] |
| 897 | #[cfg_attr (doc_cfg, doc(cfg(feature = "deprecated_items" )))] |
| 898 | #[allow (deprecated)] |
| 899 | pub use crate::element::Path; |
| 900 | |
| 901 | #[allow (type_alias_bounds)] |
| 902 | /// The type used to returns a drawing operation that can be failed |
| 903 | /// - `T`: The return type |
| 904 | /// - `D`: The drawing backend type |
| 905 | pub type DrawResult<T, D: DrawingBackend> = |
| 906 | Result<T, crate::drawing::DrawingAreaErrorKind<D::ErrorType>>; |
| 907 | |
| 908 | #[cfg (feature = "evcxr" )] |
| 909 | #[cfg_attr (doc_cfg, doc(cfg(feature = "evcxr" )))] |
| 910 | pub use crate::evcxr::evcxr_figure; |
| 911 | |
| 912 | // Re-export tier 1 backends for backward compatibility |
| 913 | #[cfg (feature = "bitmap_backend" )] |
| 914 | #[cfg_attr (doc_cfg, doc(cfg(feature = "bitmap_backend" )))] |
| 915 | pub use plotters_bitmap::BitMapBackend; |
| 916 | |
| 917 | #[cfg (feature = "svg_backend" )] |
| 918 | #[cfg_attr (doc_cfg, doc(cfg(feature = "svg_backend" )))] |
| 919 | pub use plotters_svg::SVGBackend; |
| 920 | } |
| 921 | |
| 922 | /// This module contains some useful re-export of backend related types. |
| 923 | pub mod backend { |
| 924 | pub use plotters_backend::DrawingBackend; |
| 925 | #[cfg (feature = "bitmap_backend" )] |
| 926 | #[cfg_attr (doc_cfg, doc(cfg(feature = "bitmap_backend" )))] |
| 927 | pub use plotters_bitmap::{ |
| 928 | bitmap_pixel::{BGRXPixel, PixelFormat, RGBPixel}, |
| 929 | BitMapBackend, |
| 930 | }; |
| 931 | #[cfg (feature = "svg_backend" )] |
| 932 | #[cfg_attr (doc_cfg, doc(cfg(feature = "svg_backend" )))] |
| 933 | pub use plotters_svg::SVGBackend; |
| 934 | } |
| 935 | |
| 936 | #[cfg (test)] |
| 937 | mod test; |
| 938 | |