or .
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..d7ee7c8a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2024 Plotly, Inc
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/LICENSE-MIT b/LICENSE-MIT
deleted file mode 100644
index 3893863b..00000000
--- a/LICENSE-MIT
+++ /dev/null
@@ -1,25 +0,0 @@
-Copyright (c) 2020-2022 Ioannis Giagkiozis
-
-Permission is hereby granted, free of charge, to any
-person obtaining a copy of this software and associated
-documentation files (the "Software"), to deal in the
-Software without restriction, including without
-limitation the rights to use, copy, modify, merge,
-publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software
-is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice
-shall be included in all copies or substantial portions
-of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
-ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
-TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
-PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
-SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
-IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
index 76ea74f9..8407d6ba 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
Plotly.rs
Plotly for Rust
-
-
+
+
@@ -14,21 +14,27 @@
-
+
+
+
# Table of Contents
* [Introduction](#introduction)
@@ -44,10 +50,10 @@
A plotting library for Rust powered by [Plotly.js](https://plot.ly/javascript/).
-Documentation and numerous interactive examples are available in the [Plotly.rs Book](https://igiagkiozis.github.io/plotly/content/getting_started.html), the [examples/](https://github.com/igiagkiozis/plotly/tree/master/examples) directory and [docs.rs](https://docs.rs/crate/plotly).
+Documentation and numerous interactive examples are available in the [Plotly.rs Book](https://plotly.github.io/plotly.rs/content/getting_started.html), the [examples/](https://github.com/plotly/plotly.rs/tree/main/examples) directory and [docs.rs](https://docs.rs/crate/plotly).
-For changes since the last version, please consult the [changelog](https://github.com/igiagkiozis/plotly/blob/master/CHANGELOG.md).
+For changes since the last version, please consult the [changelog](https://github.com/plotly/plotly.rs/tree/main/CHANGELOG.md).
# Basic Usage
@@ -55,7 +61,7 @@ Add this to your `Cargo.toml`:
```toml
[dependencies]
-plotly = "0.8.4"
+plotly = "0.9.0"
```
## Exporting an Interactive Plot
@@ -97,7 +103,7 @@ To save a plot as a static image, the `kaleido` feature is required:
# Cargo.toml
[dependencies]
-plotly = { version = "0.8.4", features = ["kaleido"] }
+plotly = { version = "0.9.0", features = ["kaleido"] }
```
With this feature enabled, plots can be saved as any of `png`, `jpeg`, `webp`, `svg`, `pdf` and `eps`. Note that the plot will be a static image, i.e. they will be non-interactive.
@@ -124,7 +130,7 @@ Using `Plotly.rs` in a Wasm-based frontend framework is possible by enabling the
# Cargo.toml
[dependencies]
-plotly = { version = "0.8.4", features = ["wasm"] }
+plotly = { version = "0.9.0", features = ["wasm"] }
```
First, make sure that you have the Plotly JavaScript library in your base HTML template:
@@ -163,13 +169,13 @@ pub fn plot_component() -> Html {
}
});
-
+
use_effect_with_deps(move |_| {
p.run();
|| ()
}, (),
);
-
+
html! {
@@ -177,7 +183,7 @@ pub fn plot_component() -> Html {
}
```
-More detailed standalone examples can be found in the [examples/](https://github.com/igiagkiozis/plotly/tree/master/examples) directory.
+More detailed standalone examples can be found in the [examples/](https://github.com/plotly/plotly.rs/tree/main/examples) directory.
# Crate Feature Flags
@@ -201,12 +207,12 @@ Enables compilation for the `wasm32-unknown-unknown` target and provides access
# Contributing
-* If you've spotted a bug or would like to see a new feature, please submit an issue on the [issue tracker](https://github.com/igiagkiozis/plotly/issues).
+* If you've spotted a bug or would like to see a new feature, please submit an issue on the [issue tracker](https://github.com/plotly/plotly.rs/issues).
-* Pull requests are welcome, see the [contributing guide](https://github.com/igiagkiozis/plotly/blob/master/CONTRIBUTING.md) for more information.
+* Pull requests are welcome, see the [contributing guide](https://github.com/plotly/plotly.rs/tree/main/CONTRIBUTING.md) for more information.
# License
`Plotly.rs` is distributed under the terms of the MIT license.
-See [LICENSE-MIT](https://github.com/igiagkiozis/plotly/blob/master/LICENSE-MIT), and [COPYRIGHT](https://github.com/igiagkiozis/plotly/blob/master/COPYRIGHT) for details.
+See [LICENSE-MIT](https://github.com/plotly/plotly.rs/tree/main/LICENSE-MIT), and [COPYRIGHT](https://github.com/plotly/plotly.rs/tree/main/COPYRIGHT) for details.
diff --git a/docs/book/src/fundamentals.md b/docs/book/src/fundamentals.md
index 6ef36d87..9fea76a1 100644
--- a/docs/book/src/fundamentals.md
+++ b/docs/book/src/fundamentals.md
@@ -1,9 +1,9 @@
-
-
+
+
-
-
+
+
@@ -18,4 +18,4 @@
# Fundamentals
-Functionality that applies to the library as a whole is described in the next sections.
\ No newline at end of file
+Functionality that applies to the library as a whole is described in the next sections.
\ No newline at end of file
diff --git a/docs/book/src/fundamentals/jupyter_support.md b/docs/book/src/fundamentals/jupyter_support.md
index 2576aa91..b329ef30 100644
--- a/docs/book/src/fundamentals/jupyter_support.md
+++ b/docs/book/src/fundamentals/jupyter_support.md
@@ -1,12 +1,12 @@
# Jupyter Support
-As of version `0.7.0`, [Plotly.rs](https://github.com/igiagkiozis/plotly) has native support for the [EvCxR Jupyter Kernel](https://github.com/google/evcxr/tree/master/evcxr_jupyter).
+As of version `0.7.0`, [Plotly.rs](https://github.com/plotly/plotly.rs) has native support for the [EvCxR Jupyter Kernel](https://github.com/google/evcxr/tree/master/evcxr_jupyter).
Once you've installed the required packages you'll be able to run all the examples shown here as well as all [the recipes](../recipes.md) in Jupyter Lab!
## Installation
-It is assumed that an installation of the [Anaconda](https://www.anaconda.com/products/individual) Python distribution is already present in the system. If that is not the case you can follow these [instructions](https://www.anaconda.com/products/individual) to get up and running with `Anaconda`.
+It is assumed that an installation of the [Anaconda](https://www.anaconda.com/products/individual) Python distribution is already present in the system. If that is not the case you can follow these [instructions](https://www.anaconda.com/products/individual) to get up and running with `Anaconda`.
```shell script
conda install -c plotly plotly=4.9.0
@@ -20,17 +20,17 @@ conda install notebook
Although there are alternative methods to enable support for the [EvCxR Jupyter Kernel](https://github.com/google/evcxr/tree/master/evcxr_jupyter), we have elected to keep the requirements consistent with what those of other languages, e.g. Julia, Python and R. This way users know what to expect; and also the folks at [Plotly](https://plotly.com/python/getting-started/#jupyter-notebook-support) have done already most of the heavy lifting to create an extension for Jupyter Lab that works very well.
-Run the following to install the Plotly Jupyter Lab extension:
+Run the following to install the Plotly Jupyter Lab extension:
```shell script
jupyter labextension install jupyterlab-plotly@4.9.0
```
-Once this step is complete to make sure the installation so far was successful, run the following command:
+Once this step is complete to make sure the installation so far was successful, run the following command:
```shell script
jupyter lab
```
-Open a `Python 3` kernel copy/paste the following code in a cell and run it:
+Open a `Python 3` kernel copy/paste the following code in a cell and run it:
```python
import plotly.graph_objects as go
fig = go.Figure(data=go.Bar(x=['a', 'b', 'c'], y=[11, 22, 33]))
@@ -62,7 +62,7 @@ If you're not familiar with the EvCxR kernel it would be good that you at least
## Usage
-Launch Jupyter Lab:
+Launch Jupyter Lab:
```shell script
jupyter lab
```
@@ -104,6 +104,6 @@ plot.set_layout(layout);
plot.lab_display();
format!("EVCXR_BEGIN_CONTENT application/vnd.plotly.v1+json\n{}\nEVCXR_END_CONTENT", plot.to_json())
```
-For Jupyter Lab there are two ways to display a plot in the `EvCxR` kernel, either have the plot object be in the last line without a semicolon or directly invoke the `Plot::lab_display` method on it; both have the same result. You can also find an example notebook [here](https://github.com/igiagkiozis/plotly/blob/master/examples/jupyter/jupyter_lab.ipynb) that will periodically be updated with examples.
+For Jupyter Lab there are two ways to display a plot in the `EvCxR` kernel, either have the plot object be in the last line without a semicolon or directly invoke the `Plot::lab_display` method on it; both have the same result. You can also find an example notebook [here](https://github.com/plotly/plotly.rs/tree/main/examples/jupyter/jupyter_lab.ipynb) that will periodically be updated with examples.
-The process for Jupyter Notebook is very much the same with one exception; the `Plot::notebook_display` method must be used to display the plot. You can find an example notebook [here](https://github.com/igiagkiozis/plotly/blob/master/examples/jupyter/jupyter_notebook.ipynb)
+The process for Jupyter Notebook is very much the same with one exception; the `Plot::notebook_display` method must be used to display the plot. You can find an example notebook [here](https://github.com/plotly/plotly.rs/tree/main/examples/jupyter/jupyter_notebook.ipynb)
diff --git a/docs/book/src/fundamentals/ndarray_support.md b/docs/book/src/fundamentals/ndarray_support.md
index b1048dda..c708a635 100644
--- a/docs/book/src/fundamentals/ndarray_support.md
+++ b/docs/book/src/fundamentals/ndarray_support.md
@@ -1,16 +1,16 @@
-# `ndarray` Support
+# `ndarray` Support
-To enable [ndarray](https://github.com/rust-ndarray/ndarray) support in [Plotly.rs](https://github.com/igiagkiozis/plotly) add the following feature to your `Cargo.toml` file:
+To enable [ndarray](https://github.com/rust-ndarray/ndarray) support in [Plotly.rs](https://github.com/plotly/plotly.rs) add the following feature to your `Cargo.toml` file:
```toml
[dependencies]
plotly = { version = ">=0.7.0", features = ["plotly_ndarray"] }
```
-This extends the [Plotly.rs](https://github.com/igiagkiozis/plotly) API in two ways:
+This extends the [Plotly.rs](https://github.com/plotly/plotly.rs) API in two ways:
* `Scatter` traces can now be created using the `Scatter::from_ndarray` constructor,
* and also multiple traces can be created with the `Scatter::to_traces` method.
-The full source code for the examples below can be found [here](https://github.com/igiagkiozis/plotly/blob/master/plotly/examples/ndarray_support.rs).
+The full source code for the examples below can be found [here](https://github.com/plotly/plotly.rs/tree/main/examples/ndarray_support).
## `ndarray` Traces
@@ -55,7 +55,7 @@ var layout = {};
### Multiple Traces
-To display a `2D` array (`Array<_, Ix2>`) you can use the `Scatter::to_traces` method. The first argument of the method represents the common axis for the traces (`x` axis) whilst the second argument contains a collection of traces. At this point it should be noted that there is some ambiguity when passing a `2D` array; namely are the traces arranged along the columns or the rows of the matrix? This ambiguity is resolved by the third argument of the `Scatter::to_traces` method. If that argument is set to `ArrayTraces::OverColumns` then the library assumes that every column represents an individual trace, alternatively if this is set to `ArrayTraces::OverRows` the assumption is that every row represents a trace.
+To display a `2D` array (`Array<_, Ix2>`) you can use the `Scatter::to_traces` method. The first argument of the method represents the common axis for the traces (`x` axis) whilst the second argument contains a collection of traces. At this point it should be noted that there is some ambiguity when passing a `2D` array; namely are the traces arranged along the columns or the rows of the matrix? This ambiguity is resolved by the third argument of the `Scatter::to_traces` method. If that argument is set to `ArrayTraces::OverColumns` then the library assumes that every column represents an individual trace, alternatively if this is set to `ArrayTraces::OverRows` the assumption is that every row represents a trace.
To illustrate this distinction consider the following examples:
```rust
diff --git a/docs/book/src/getting_started.md b/docs/book/src/getting_started.md
index 47dc5cc5..ee3acc79 100644
--- a/docs/book/src/getting_started.md
+++ b/docs/book/src/getting_started.md
@@ -1,9 +1,9 @@
-
-
+
+
-
-
+
+
@@ -18,26 +18,26 @@
# Getting Started
-To start using [plotly.rs](https://github.com/igiagkiozis/plotly) in your project add the following to your `Cargo.toml`:
+To start using [plotly.rs](https://github.com/plotly/plotly.rs) in your project add the following to your `Cargo.toml`:
```toml
[dependencies]
-plotly = "0.8.4"
+plotly = "0.9.0"
```
-[Plotly.rs](https://github.com/igiagkiozis/plotly) is ultimately a thin wrapper around the `plotly.js` library. The main job of this library is to provide `structs` and `enums` which get serialized to `json` and passed to the `plotly.js` library to actually do the heavy lifting. As such, if you are familiar with `plotly.js` or its derivatives (e.g. the equivalent Python library), then you should find [`plotly.rs`](https://github.com/igiagkiozis/plotly) intuitive to use.
+[Plotly.rs](https://github.com/plotly/plotly.rs) is ultimately a thin wrapper around the `plotly.js` library. The main job of this library is to provide `structs` and `enums` which get serialized to `json` and passed to the `plotly.js` library to actually do the heavy lifting. As such, if you are familiar with `plotly.js` or its derivatives (e.g. the equivalent Python library), then you should find [`plotly.rs`](https://github.com/plotly/plotly.rs) intuitive to use.
A `Plot` struct contains one or more `Trace` objects which describe the structure of data to be displayed. Optional `Layout` and `Configuration` structs can be used to specify the layout and config of the plot, respectively.
The builder pattern is used extensively throughout the library, which means you only need to specify the attributes and details you desire. Any attributes that are not set will fall back to the default value used by `plotly.js`.
-All available traces (e.g. `Scatter`, `Bar`, `Histogram`, etc), the `Layout`, `Configuration` and `Plot` have been hoisted in the `plotly` namespace so that they can be imported simply using the following:
+All available traces (e.g. `Scatter`, `Bar`, `Histogram`, etc), the `Layout`, `Configuration` and `Plot` have been hoisted in the `plotly` namespace so that they can be imported simply using the following:
```rust
use plotly::{Plot, Layout, Scatter};
```
-The aforementioned components can be combined to produce as simple plot as follows:
+The aforementioned components can be combined to produce as simple plot as follows:
```rust
use plotly::common::Mode;
@@ -65,17 +65,17 @@ fn main() -> std::io::Result<()> {
}
```
-which results in the following figure (displayed here as a static png file):
+which results in the following figure (displayed here as a static png file):

-The above code will generate an interactive `html` page of the `Plot` and display it in the default browser. The `html` for the plot is stored in the platform specific temporary directory. To save the `html` result, you can do so quite simply:
+The above code will generate an interactive `html` page of the `Plot` and display it in the default browser. The `html` for the plot is stored in the platform specific temporary directory. To save the `html` result, you can do so quite simply:
```rust
plot.write_html("/home/user/line_and_scatter_plot.html");
```
-It is often the case that plots are produced to be included in a document and a different format for the plot is desirable (e.g. png, jpeg, etc). Given that the `html` version of the plot is composed of vector graphics, the display when converted to a non-vector format (e.g. png) is not guaranteed to be identical to the one displayed in `html`. This means that some fine tuning may be required to get to the desired output. To support that iterative workflow, `Plot` has a `show_image()` method which will display the rasterised output to the target format, for example:
+It is often the case that plots are produced to be included in a document and a different format for the plot is desirable (e.g. png, jpeg, etc). Given that the `html` version of the plot is composed of vector graphics, the display when converted to a non-vector format (e.g. png) is not guaranteed to be identical to the one displayed in `html`. This means that some fine tuning may be required to get to the desired output. To support that iterative workflow, `Plot` has a `show_image()` method which will display the rasterised output to the target format, for example:
```rust
plot.show_image(ImageFormat::PNG, 1280, 900);
@@ -83,7 +83,7 @@ plot.show_image(ImageFormat::PNG, 1280, 900);
will display in the browser the rasterised plot; 1280 pixels wide and 900 pixels tall, in png format.
-Once a satisfactory result is achieved, and assuming the [`kaleido`](getting_started#saving-plots) feature is enabled, the plot can be saved using the following:
+Once a satisfactory result is achieved, and assuming the [`kaleido`](getting_started#saving-plots) feature is enabled, the plot can be saved using the following:
```rust
plot.write_image("/home/user/plot_name.ext", ImageFormat::PNG, 1280, 900, 1.0);
@@ -93,16 +93,16 @@ The extension in the file-name path is optional as the appropriate extension (`I
## Saving Plots
-To add the ability to save plots in the following formats: png, jpeg, webp, svg, pdf and eps, you can use the `kaleido` feature. This feature depends on [plotly/Kaleido](https://github.com/plotly/Kaleido): a cross-platform open source library for generating static images. All the necessary binaries have been included with `plotly_kaleido` for `Linux`, `Windows` and `MacOS`. Previous versions of [plotly.rs](https://github.com/igiagkiozis/plotly) used the `orca` feature, however, this has been deprecated as it provided the same functionality but required additional installation steps. To enable the `kaleido` feature add the following to your `Cargo.toml`:
+To add the ability to save plots in the following formats: png, jpeg, webp, svg, pdf and eps, you can use the `kaleido` feature. This feature depends on [plotly/Kaleido](https://github.com/plotly/Kaleido): a cross-platform open source library for generating static images. All the necessary binaries have been included with `plotly_kaleido` for `Linux`, `Windows` and `MacOS`. Previous versions of [plotly.rs](https://github.com/plotly/plotly.rs) used the `orca` feature, however, this has been deprecated as it provided the same functionality but required additional installation steps. To enable the `kaleido` feature add the following to your `Cargo.toml`:
```toml
[dependencies]
-plotly = { version = "0.8.4", features = ["kaleido"] }
+plotly = { version = "0.9.0", features = ["kaleido"] }
```
## WebAssembly Support
-As of v0.8.0, [plotly.rs](https://github.com/igiagkiozis/plotly) can now be used in a `Wasm` environment by enabling the `wasm` feature in your `Cargo.toml`:
+As of v0.8.0, [plotly.rs](https://github.com/plotly/plotly.rs) can now be used in a `Wasm` environment by enabling the `wasm` feature in your `Cargo.toml`:
```toml
[dependencies]
diff --git a/docs/book/src/plotly_rs.md b/docs/book/src/plotly_rs.md
index 19af18d6..24bdf51c 100644
--- a/docs/book/src/plotly_rs.md
+++ b/docs/book/src/plotly_rs.md
@@ -1,9 +1,9 @@
-
-
+
+
-
-
+
+
@@ -20,17 +20,17 @@
Plotly.rs is a plotting library powered by [Plotly.js](https://plot.ly/javascript/). The aim is to bring over to Rust all the functionality that `Python` users have come to rely on with the added benefit of type safety and speed.
-Plotly.rs is free and open source. You can find the source on [GitHub](https://github.com/igiagkiozis/plotly). Issues and feature requests can be posted on the [issue tracker](https://github.com/igiagkiozis/plotly/issues).
+Plotly.rs is free and open source. You can find the source on [GitHub](https://github.com/plotly/plotly.rs). Issues and feature requests can be posted on the [issue tracker](https://github.com/plotly/plotly.rs/issues).
## API Docs
This book is intended to be a recipe index, which closely follows the [plotly.js examples](https://plotly.com/javascript/), and is complemented by the [API documentation](https://docs.rs/plotly).
## Contributing
-Contributions are always welcomed, no matter how large or small. Refer to the [contributing guidelines](https://github.com/igiagkiozis/plotly/blob/master/CONTRIBUTING.md) for further pointers, and, if in doubt, [open an issue](https://github.com/igiagkiozis/plotly/issues).
+Contributions are always welcomed, no matter how large or small. Refer to the [contributing guidelines](https://github.com/plotly/plotly.rs/tree/main/CONTRIBUTING.md) for further pointers, and, if in doubt, [open an issue](https://github.com/plotly/plotly.rs/issues).
## License
Plotly.rs is distributed under the terms of the MIT license.
-See [LICENSE-MIT](https://github.com/igiagkiozis/plotly/blob/master/LICENSE-MIT), and [COPYRIGHT](https://github.com/igiagkiozis/plotly/blob/master/COPYRIGHT) for details.
\ No newline at end of file
+See [LICENSE-MIT](https://github.com/plotly/plotly.rs/tree/main/LICENSE-MIT), and [COPYRIGHT](https://github.com/plotly/plotly.rs/tree/main/COPYRIGHT) for details.
\ No newline at end of file
diff --git a/docs/book/src/recipes.md b/docs/book/src/recipes.md
index 870a45a2..5c27ecf2 100644
--- a/docs/book/src/recipes.md
+++ b/docs/book/src/recipes.md
@@ -1,9 +1,9 @@
-
-
+
+
-
-
+
+
@@ -18,4 +18,4 @@
# Recipes
-Most of the recipes presented here have been adapted from the official documentation for [plotly.js](https://plotly.com/javascript/) and [plotly.py](https://plotly.com/python/). Contributions of interesting plots that showcase the capabilities of the library are most welcome. For more information on the process please see [the contributing guidelines](https://github.com/igiagkiozis/plotly/blob/master/CONTRIBUTING.md).
+Most of the recipes presented here have been adapted from the official documentation for [plotly.js](https://plotly.com/javascript/) and [plotly.py](https://plotly.com/python/). Contributions of interesting plots that showcase the capabilities of the library are most welcome. For more information on the process please see [the contributing guidelines](https://github.com/plotly/plotly.rs/tree/main/CONTRIBUTING.md).
diff --git a/docs/book/src/recipes/3dcharts.md b/docs/book/src/recipes/3dcharts.md
index 35731c94..852337bb 100644
--- a/docs/book/src/recipes/3dcharts.md
+++ b/docs/book/src/recipes/3dcharts.md
@@ -1,6 +1,6 @@
# 3D Charts
-The complete source code for the following examples can also be found [here](https://github.com/igiagkiozis/plotly/blob/master/plotly/examples/plot3d.rs).
+The complete source code for the following examples can also be found [here](https://github.com/plotly/plotly.rs/tree/main/examples/plot3d).
Kind | Link
:---|:----:
diff --git a/docs/book/src/recipes/basic_charts.md b/docs/book/src/recipes/basic_charts.md
index 24e0e5df..c8e3a77f 100644
--- a/docs/book/src/recipes/basic_charts.md
+++ b/docs/book/src/recipes/basic_charts.md
@@ -1,6 +1,6 @@
# Basic Charts
-The source code for the following examples can also be found [here](https://github.com/igiagkiozis/plotly/blob/master/plotly/examples/basic_charts.rs).
+The source code for the following examples can also be found [here](https://github.com/plotly/plotly.rs/tree/main/examples/basic_charts).
Kind | Link
:---|:----:
diff --git a/docs/book/src/recipes/financial_charts.md b/docs/book/src/recipes/financial_charts.md
index 4cca705a..6a811d9a 100644
--- a/docs/book/src/recipes/financial_charts.md
+++ b/docs/book/src/recipes/financial_charts.md
@@ -1,6 +1,6 @@
# Financial Charts
-The source code for the following examples can also be found [here](https://github.com/igiagkiozis/plotly/blob/master/plotly/examples/financial_charts.rs).
+The source code for the following examples can also be found [here](https://github.com/plotly/plotly.rs/tree/main/examples/financial_charts).
Kind | Link
:---|:----:
diff --git a/docs/book/src/recipes/scientific_charts.md b/docs/book/src/recipes/scientific_charts.md
index 2b18ea47..36549951 100644
--- a/docs/book/src/recipes/scientific_charts.md
+++ b/docs/book/src/recipes/scientific_charts.md
@@ -1,6 +1,6 @@
# Scientific Charts
-The source code for the following examples can also be found [here](https://github.com/igiagkiozis/plotly/blob/master/plotly/examples/scientific_charts.rs).
+The source code for the following examples can also be found [here](https://github.com/plotly/plotly.rs/tree/main/examples/scientific_charts).
Kind | Link
:---|:----:
diff --git a/docs/book/src/recipes/statistical_charts.md b/docs/book/src/recipes/statistical_charts.md
index adef8b98..4f446d1d 100644
--- a/docs/book/src/recipes/statistical_charts.md
+++ b/docs/book/src/recipes/statistical_charts.md
@@ -1,6 +1,6 @@
# Statistical Charts
-The complete source code for the following examples can also be found [here](https://github.com/igiagkiozis/plotly/blob/master/plotly/examples/statistical_charts.rs).
+The complete source code for the following examples can also be found [here](https://github.com/plotly/plotly.rs/tree/main/examples/statistical_charts).
Kind | Link
:---|:----:
diff --git a/docs/book/src/recipes/subplots.md b/docs/book/src/recipes/subplots.md
index 0f40e075..490ead55 100644
--- a/docs/book/src/recipes/subplots.md
+++ b/docs/book/src/recipes/subplots.md
@@ -1,6 +1,6 @@
# Subplots
-The complete source code for the following examples can also be found [here](https://github.com/igiagkiozis/plotly/blob/master/plotly/examples/subplots.rs).
+The complete source code for the following examples can also be found [here](https://github.com/plotly/plotly.rs/tree/main/examples/subplots).
Kind | Link
:---|:----:
diff --git a/examples/3d_charts/Cargo.toml b/examples/3d_charts/Cargo.toml
index 21a4f1ce..48058077 100644
--- a/examples/3d_charts/Cargo.toml
+++ b/examples/3d_charts/Cargo.toml
@@ -6,4 +6,5 @@ edition = "2021"
[dependencies]
ndarray = "0.15.6"
+rand = "0.8.5"
plotly = { path = "../../plotly" }
diff --git a/examples/3d_charts/src/main.rs b/examples/3d_charts/src/main.rs
index d5bf785a..ed484f73 100644
--- a/examples/3d_charts/src/main.rs
+++ b/examples/3d_charts/src/main.rs
@@ -2,10 +2,12 @@
use ndarray::Array;
use plotly::{
- common::{ColorScale, ColorScalePalette, Marker, MarkerSymbol, Mode, Title},
- layout::{Axis, Layout},
+ color::Rgb,
+ common::{ColorBar, ColorScale, ColorScalePalette, Font, Marker, MarkerSymbol, Mode},
+ layout::{Axis, Camera, Layout, LayoutScene, Legend, Margin, ProjectionType},
Mesh3D, Plot, Scatter3D, Surface,
};
+use rand::Rng;
// 3D Scatter Plots
fn simple_scatter3d_plot() {
@@ -29,6 +31,7 @@ fn customized_scatter3d_plot() {
let sizelookup = z.clone();
let trace = Scatter3D::new(t.clone(), y.clone(), z.iter().map(|i| -i).collect())
+ .name("Helix 1")
.mode(Mode::Markers)
.marker(
Marker::new()
@@ -39,28 +42,70 @@ fn customized_scatter3d_plot() {
.map(|i| (i.abs() * 25f64) as usize)
.collect(),
)
- .color_scale(ColorScale::Palette(ColorScalePalette::Viridis)),
+ .color_scale(ColorScale::Palette(ColorScalePalette::Viridis))
+ .color_array(z.clone()),
);
- let trace2 = Scatter3D::new(t, z, y).mode(Mode::Markers).marker(
- Marker::new()
- .size_array(
- sizelookup
- .iter()
- .map(|i| (i.abs() * 25f64) as usize)
- .collect(),
- )
- .color_scale(ColorScale::Palette(ColorScalePalette::Viridis)),
- );
+ let trace2 = Scatter3D::new(t, z.clone(), y)
+ .name("Helix 2")
+ .mode(Mode::Markers)
+ .marker(
+ Marker::new()
+ .size_array(
+ sizelookup
+ .iter()
+ .map(|i| (i.abs() * 25f64) as usize)
+ .collect(),
+ )
+ .color_scale(ColorScale::Palette(ColorScalePalette::Viridis))
+ .color_array(z),
+ );
let mut plot = Plot::new();
plot.add_trace(trace);
plot.add_trace(trace2);
+
+ let background_color: Rgb = Rgb::new(70, 70, 70);
+ let front_color: Rgb = Rgb::new(255, 255, 255);
+
let layout = Layout::new()
- .title("Helix".into())
- .x_axis(Axis::new().title("x (A meaningful axis name goes here)".into()))
- .y_axis(Axis::new().title(Title::new("This is the label of the Y axis")))
- .z_axis(Axis::new().title("z Axis".into()));
+ .title("Helix")
+ .legend(Legend::new().x(0.9).y(0.9))
+ .font(Font::new().color(front_color))
+ .paper_background_color(background_color)
+ .scene(
+ LayoutScene::new()
+ .x_axis(
+ Axis::new()
+ .title("x (A meaningful axis name goes here)")
+ .tick_angle(0f64)
+ .grid_color(front_color)
+ .color(front_color),
+ )
+ .y_axis(
+ Axis::new()
+ .title("This is the label of the Y axis")
+ .tick_format(".1f")
+ .grid_color(front_color)
+ .color(front_color),
+ )
+ .z_axis(Axis::new().title("").tick_values(vec![]))
+ .aspect_mode(plotly::layout::AspectMode::Manual)
+ .aspect_ratio((3.0, 1.0, 1.0).into())
+ .camera(
+ Camera::new()
+ .projection(ProjectionType::Orthographic.into())
+ .eye((0.0, 0.0, 1.0).into())
+ .up((0.0, 1.0, 0.0).into())
+ .center((0.0, 0.0, 0.0).into()),
+ )
+ .drag_mode(plotly::layout::DragMode3D::Pan)
+ .hover_mode(plotly::layout::HoverMode::Closest)
+ .background_color(background_color),
+ )
+ .margin(Margin::new().left(0).right(0).top(50).bottom(0))
+ .width(1000)
+ .height(500);
plot.set_layout(layout);
plot.show();
@@ -119,6 +164,71 @@ fn mesh_3d_plot() {
plot.show();
}
+fn colorscale_plot() {
+ let mut plot = Plot::new();
+
+ let x = (0..100)
+ .map(|x| ((x - 50) as f64) / 100f64)
+ .collect::>();
+
+ let y = x.clone();
+
+ let iproduct = |x: &[f64], y: &[f64]| -> Vec<(f64, f64)> {
+ let mut result = Vec::new();
+ for x in x {
+ for y in y {
+ result.push((*x, *y));
+ }
+ }
+ result
+ };
+
+ let ((x, y), z): ((Vec, Vec), Vec) = iproduct(&x, &y)
+ .into_iter()
+ .map(|(x, y)| ((x, y), -(x.powi(2) + y.powi(2)) + 0.5))
+ .unzip();
+
+ let color: Vec = z.clone().into_iter().rev().map(|x| x as f32).collect();
+ let _color: Vec = (0..z.len()).collect();
+ let _color: Vec = (0..z.len()).map(|x| x as u8).collect();
+ let _color: Vec = {
+ let mut rng = rand::thread_rng();
+ (0..z.len()).map(|_| rng.gen_range(0..100)).collect()
+ };
+
+ let color_max = color.iter().fold(f64::MIN, |acc, x| acc.max(*x as f64));
+
+ let colorscale = ColorScalePalette::YlGnBu;
+
+ let marker = Marker::new()
+ .color_array(color)
+ .color_scale(plotly::common::ColorScale::Palette(colorscale.clone()))
+ .cauto(false)
+ .cmax(color_max * 1.5)
+ .color_bar(ColorBar::new());
+
+ let scatter = Scatter3D::new(x, y, z).mode(Mode::Markers).marker(marker);
+
+ plot.add_trace(scatter);
+
+ let layout = Layout::new()
+ .font(Font::new().size(18).family("Palatino-Linotype"))
+ .title(format!("Colorscale: {colorscale:?}"))
+ .width(1200)
+ .height(1000)
+ .scene(
+ LayoutScene::new()
+ .aspect_mode(plotly::layout::AspectMode::Data)
+ .x_axis(Axis::new().tick_format(".1f"))
+ .y_axis(Axis::new().tick_format(".1f"))
+ .z_axis(Axis::new().tick_format(".1f")),
+ );
+
+ plot.set_layout(layout);
+
+ plot.show();
+}
+
fn main() {
// Uncomment any of these lines to display the example.
@@ -126,6 +236,7 @@ fn main() {
// simple_scatter3d_plot();
// simple_line3d_plot();
// customized_scatter3d_plot();
+ // colorscale_plot();
// Surface Plots
// surface_plot();
diff --git a/examples/basic_charts/src/main.rs b/examples/basic_charts/src/main.rs
index 65518e2f..14f31adb 100644
--- a/examples/basic_charts/src/main.rs
+++ b/examples/basic_charts/src/main.rs
@@ -5,11 +5,12 @@ use plotly::{
color::{NamedColor, Rgb, Rgba},
common::{
ColorScale, ColorScalePalette, DashType, Fill, Font, Line, LineShape, Marker, Mode,
- Orientation, Title,
+ Orientation,
},
layout::{Axis, BarMode, Layout, Legend, TicksDirection, TraceOrder},
sankey::{Line as SankeyLine, Link, Node},
- Bar, Plot, Sankey, Scatter, ScatterPolar,
+ traces::table::{Cells, Header},
+ Bar, Plot, Sankey, Scatter, ScatterPolar, Table,
};
use rand_distr::{Distribution, Normal, Uniform};
@@ -117,9 +118,9 @@ fn data_labels_hover() {
plot.add_trace(trace2);
let layout = Layout::new()
- .title("Data Labels Hover".into())
- .x_axis(Axis::new().title("x".into()).range(vec![0.75, 5.25]))
- .y_axis(Axis::new().title("y".into()).range(vec![0., 8.]));
+ .title("Data Labels Hover")
+ .x_axis(Axis::new().title("x").range(vec![0.75, 5.25]))
+ .y_axis(Axis::new().title("y").range(vec![0., 8.]));
plot.set_layout(layout);
plot.show();
@@ -142,7 +143,7 @@ fn data_labels_on_the_plot() {
plot.add_trace(trace2);
let layout = Layout::new()
- .title("Data Labels on the Plot".into())
+ .title("Data Labels on the Plot")
.x_axis(Axis::new().range(vec![0.75, 5.25]))
.y_axis(Axis::new().range(vec![0., 8.]));
plot.set_layout(layout);
@@ -215,14 +216,14 @@ fn colored_and_styled_scatter_plot() {
.marker(Marker::new().color(Rgb::new(142, 124, 195)).size(12));
let layout = Layout::new()
- .title(Title::new("Quarter 1 Growth"))
+ .title("Quarter 1 Growth")
.x_axis(
Axis::new()
- .title(Title::new("GDP per Capita"))
+ .title("GDP per Capita")
.show_grid(false)
.zero_line(false),
)
- .y_axis(Axis::new().title(Title::new("Percent")).show_line(false));
+ .y_axis(Axis::new().title("Percent").show_line(false));
let mut plot = Plot::new();
plot.add_trace(trace1);
plot.add_trace(trace2);
@@ -279,7 +280,7 @@ fn adding_names_to_line_and_scatter_plot() {
.mode(Mode::LinesMarkers)
.name("Scatter + Lines");
- let layout = Layout::new().title(Title::new("Adding Names to Line and Scatter Plot"));
+ let layout = Layout::new().title("Adding Names to Line and Scatter Plot");
let mut plot = Plot::new();
plot.add_trace(trace1);
plot.add_trace(trace2);
@@ -304,7 +305,7 @@ fn line_and_scatter_styling() {
.marker(Marker::new().color(Rgb::new(128, 0, 128)).size(12))
.line(Line::new().color(Rgb::new(128, 0, 128)).width(1.0));
- let layout = Layout::new().title(Title::new("Line and Scatter Styling"));
+ let layout = Layout::new().title("Line and Scatter Styling");
let mut plot = Plot::new();
plot.add_trace(trace1);
plot.add_trace(trace2);
@@ -325,7 +326,7 @@ fn styling_line_plot() {
.line(Line::new().color(Rgb::new(55, 128, 191)).width(1.0));
let layout = Layout::new()
- .title(Title::new("Styling Line Plot"))
+ .title("Styling Line Plot")
.width(500)
.height(500);
let mut plot = Plot::new();
@@ -594,7 +595,7 @@ fn basic_sankey_diagram() {
);
let layout = Layout::new()
- .title("Basic Sankey".into())
+ .title("Basic Sankey")
.font(Font::new().size(10));
let mut plot = Plot::new();
@@ -604,6 +605,16 @@ fn basic_sankey_diagram() {
plot.show();
}
+fn table_chart() {
+ let trace = Table::new(
+ Header::new(vec![String::from("col1"), String::from("col2")]),
+ Cells::new(vec![vec![1, 2], vec![2, 3]]),
+ );
+ let mut plot = Plot::new();
+ plot.add_trace(trace);
+ plot.show();
+}
+
fn main() {
// Uncomment any of these lines to display the example.
@@ -629,6 +640,7 @@ fn main() {
// basic_bar_chart();
// grouped_bar_chart();
// stacked_bar_chart();
+ // table_chart();
// Sankey Diagrams
// basic_sankey_diagram();
diff --git a/examples/financial_charts/src/main.rs b/examples/financial_charts/src/main.rs
index 2805482b..1473699e 100644
--- a/examples/financial_charts/src/main.rs
+++ b/examples/financial_charts/src/main.rs
@@ -3,7 +3,7 @@
use std::env;
use std::path::PathBuf;
-use plotly::common::{TickFormatStop, Title};
+use plotly::common::TickFormatStop;
use plotly::layout::{Axis, RangeSelector, RangeSlider, SelectorButton, SelectorStep, StepMode};
use plotly::{Candlestick, Layout, Ohlc, Plot, Scatter};
use serde::Deserialize;
@@ -50,7 +50,7 @@ fn time_series_plot_with_custom_date_range() {
let layout = Layout::new()
.x_axis(Axis::new().range(vec!["2016-07-01", "2016-12-31"]))
- .title(Title::new("Manually Set Date Range"));
+ .title("Manually Set Date Range");
plot.set_layout(layout);
plot.show();
@@ -68,7 +68,7 @@ fn time_series_with_range_slider() {
let layout = Layout::new()
.x_axis(Axis::new().range_slider(RangeSlider::new().visible(true)))
- .title(Title::new("Manually Set Date Range"));
+ .title("Manually Set Date Range");
plot.set_layout(layout);
plot.show();
diff --git a/examples/images/Cargo.toml b/examples/images/Cargo.toml
index 144ac7a4..59a3bc34 100644
--- a/examples/images/Cargo.toml
+++ b/examples/images/Cargo.toml
@@ -5,6 +5,6 @@ authors = ["Michael Freeborn "]
edition = "2021"
[dependencies]
-image = "0.24.4"
+image = "0.25"
ndarray = "0.15.6"
plotly = { path = "../../plotly", features = ["plotly_image", "plotly_ndarray"] }
diff --git a/examples/maps/src/main.rs b/examples/maps/src/main.rs
index 10312ced..1258e6ae 100644
--- a/examples/maps/src/main.rs
+++ b/examples/maps/src/main.rs
@@ -3,7 +3,7 @@
use plotly::{
common::Marker,
layout::{Center, DragMode, Mapbox, MapboxStyle, Margin},
- Layout, Plot, ScatterMapbox,
+ DensityMapbox, Layout, Plot, ScatterMapbox,
};
fn scatter_mapbox() {
@@ -27,8 +27,29 @@ fn scatter_mapbox() {
plot.show();
}
+fn density_mapbox() {
+ let trace = DensityMapbox::new(vec![45.5017], vec![-73.5673], vec![0.75]).zauto(true);
+
+ let layout = Layout::new()
+ .drag_mode(DragMode::Zoom)
+ .margin(Margin::new().top(0).left(0).bottom(0).right(0))
+ .mapbox(
+ Mapbox::new()
+ .style(MapboxStyle::OpenStreetMap)
+ .center(Center::new(45.5017, -73.5673))
+ .zoom(5),
+ );
+
+ let mut plot = Plot::new();
+ plot.add_trace(trace);
+ plot.set_layout(layout);
+
+ plot.show();
+}
+
fn main() {
// Uncomment any of these lines to display the example.
// scatter_mapbox();
+ // density_mapbox();
}
diff --git a/examples/scientific_charts/src/main.rs b/examples/scientific_charts/src/main.rs
index 20d98a94..bea229e2 100644
--- a/examples/scientific_charts/src/main.rs
+++ b/examples/scientific_charts/src/main.rs
@@ -2,7 +2,7 @@
use std::f64::consts::PI;
-use plotly::common::{ColorScale, ColorScalePalette, Title};
+use plotly::common::{ColorScale, ColorScalePalette, Font};
use plotly::contour::Contours;
use plotly::{Contour, HeatMap, Layout, Plot};
@@ -47,7 +47,7 @@ fn colorscale_for_contour_plot() {
];
let trace = Contour::new_z(z).color_scale(ColorScale::Palette(ColorScalePalette::Jet));
- let layout = Layout::new().title(Title::new("Colorscale for Contour Plot"));
+ let layout = Layout::new().title("Colorscale for Contour Plot");
let mut plot = Plot::new();
plot.set_layout(layout);
plot.add_trace(trace);
@@ -68,7 +68,7 @@ fn customizing_size_and_range_of_a_contour_plots_contours() {
.auto_contour(false)
.contours(Contours::new().start(0.0).end(8.0).size(2));
- let layout = Layout::new().title(Title::new("Customizing Size and Range of Contours"));
+ let layout = Layout::new().title("Customizing Size and Range of Contours");
let mut plot = Plot::new();
plot.set_layout(layout);
plot.add_trace(trace);
@@ -91,7 +91,7 @@ fn customizing_spacing_between_x_and_y_ticks() {
.dy(10.0)
.y0(10.0);
- let layout = Layout::new().title(Title::new("Customizing Size and Range of Contours"));
+ let layout = Layout::new().title("Customizing Size and Range of Contours");
let mut plot = Plot::new();
plot.set_layout(layout);
plot.add_trace(trace);
@@ -102,22 +102,60 @@ fn customizing_spacing_between_x_and_y_ticks() {
// Heatmaps
fn basic_heat_map() {
let z = vec![vec![1, 20, 30], vec![20, 1, 60], vec![30, 60, 1]];
- let trace = HeatMap::new_z(z);
+ let trace = HeatMap::new_z(z).zmin(1.0).zmax(60.0);
let mut plot = Plot::new();
plot.add_trace(trace);
plot.show();
}
+fn customized_heat_map() {
+ let x = (0..100).map(|x| x as f64).collect::>();
+ let y = (0..100).map(|y| y as f64).collect::>();
+ let z: Vec> = x
+ .iter()
+ .map(|x| {
+ y.iter()
+ .map(|y| (x / 5.0).powf(2.0) + (y / 5.0).powf(2.0))
+ .collect::>()
+ })
+ .collect::>>();
+
+ let (_z_min, z_max) = z
+ .iter()
+ .flatten()
+ .fold((f64::MAX, f64::MIN), |(min, max), &x| {
+ (min.min(x), max.max(x))
+ });
+
+ let colorscale = ColorScalePalette::Jet;
+
+ let trace = HeatMap::new(x, y, z)
+ .zmin(z_max * 0.4)
+ .zmax(z_max * 0.5)
+ .color_scale(colorscale.into());
+
+ let layout = Layout::new()
+ .title("Customized Heatmap")
+ .font(Font::new().size(32));
+
+ let mut plot = Plot::new();
+ plot.set_layout(layout);
+ plot.add_trace(trace);
+
+ plot.show();
+}
+
fn main() {
// Uncomment any of these lines to display the example.
// Contour Plots
- simple_contour_plot();
- colorscale_for_contour_plot();
- customizing_size_and_range_of_a_contour_plots_contours();
- customizing_spacing_between_x_and_y_ticks();
+ // simple_contour_plot();
+ // colorscale_for_contour_plot();
+ // customizing_size_and_range_of_a_contour_plots_contours();
+ // customizing_spacing_between_x_and_y_ticks();
// Heatmaps
- basic_heat_map();
+ // basic_heat_map();
+ // customized_heat_map();
}
diff --git a/examples/shapes/src/main.rs b/examples/shapes/src/main.rs
index 871986d1..84eaafc0 100644
--- a/examples/shapes/src/main.rs
+++ b/examples/shapes/src/main.rs
@@ -138,7 +138,7 @@ fn creating_tangent_lines_with_shapes() {
plot.add_trace(trace);
let mut layout =
- Layout::new().title("$f(x)=x\\sin(x^2)+1\\\\ f\'(x)=\\sin(x^2)+2x^2\\cos(x^2)$".into());
+ Layout::new().title("$f(x)=x\\sin(x^2)+1\\\\ f\'(x)=\\sin(x^2)+2x^2\\cos(x^2)$");
layout.add_shape(
Shape::new()
diff --git a/examples/statistical_charts/src/main.rs b/examples/statistical_charts/src/main.rs
index 5d2a7190..f1c00633 100644
--- a/examples/statistical_charts/src/main.rs
+++ b/examples/statistical_charts/src/main.rs
@@ -4,7 +4,7 @@ use ndarray::Array;
use plotly::{
box_plot::{BoxMean, BoxPoints},
color::{NamedColor, Rgb, Rgba},
- common::{ErrorData, ErrorType, Line, Marker, Mode, Orientation, Title},
+ common::{ErrorData, ErrorType, Line, Marker, Mode, Orientation},
histogram::{Bins, Cumulative, HistFunc, HistNorm},
layout::{Axis, BarMode, BoxMode, Layout, Margin},
Bar, BoxPlot, Histogram, Plot, Scatter,
@@ -203,11 +203,7 @@ fn grouped_box_plot() {
plot.add_trace(trace3);
let layout = Layout::new()
- .y_axis(
- Axis::new()
- .title(Title::new("normalized moisture"))
- .zero_line(false),
- )
+ .y_axis(Axis::new().title("normalized moisture").zero_line(false))
.box_mode(BoxMode::Group);
plot.set_layout(layout);
@@ -248,7 +244,7 @@ fn box_plot_styling_outliers() {
.marker(Marker::new().color(Rgb::new(107, 174, 214)))
.box_points(BoxPoints::Outliers);
- let layout = Layout::new().title(Title::new("Box Plot Styling Outliers"));
+ let layout = Layout::new().title("Box Plot Styling Outliers");
let mut plot = Plot::new();
plot.set_layout(layout);
@@ -274,7 +270,7 @@ fn box_plot_styling_mean_and_standard_deviation() {
.name("Mean and Standard Deviation")
.marker(Marker::new().color(Rgb::new(8, 81, 156)))
.box_mean(BoxMean::StandardDeviation);
- let layout = Layout::new().title(Title::new("Box Plot Styling Mean and Standard Deviation"));
+ let layout = Layout::new().title("Box Plot Styling Mean and Standard Deviation");
let mut plot = Plot::new();
plot.set_layout(layout);
@@ -321,12 +317,8 @@ fn grouped_horizontal_box_plot() {
plot.add_trace(trace3);
let layout = Layout::new()
- .title(Title::new("Grouped Horizontal Box Plot"))
- .x_axis(
- Axis::new()
- .title(Title::new("normalized moisture"))
- .zero_line(false),
- )
+ .title("Grouped Horizontal Box Plot")
+ .x_axis(Axis::new().title("normalized moisture").zero_line(false))
.box_mode(BoxMode::Group);
plot.set_layout(layout);
@@ -345,7 +337,7 @@ fn fully_styled_box_plot() {
v
};
- let x_data = vec![
+ let x_data = [
"Carmelo
Anthony",
"Dwyane
Wade",
"Deron
Williams",
@@ -370,9 +362,7 @@ fn fully_styled_box_plot() {
let mut plot = Plot::new();
let layout = Layout::new()
- .title(Title::new(
- "Points Scored by the Top 9 Scoring NBA Players in 2012",
- ))
+ .title("Points Scored by the Top 9 Scoring NBA Players in 2012")
.y_axis(
Axis::new()
.auto_range(true)
@@ -522,9 +512,9 @@ fn colored_and_styled_histograms() {
.auto_bin_x(false)
.x_bins(Bins::new(-3.2, 4.0, 0.06));
let layout = Layout::new()
- .title(Title::new("Colored and Styled Histograms"))
- .x_axis(Axis::new().title(Title::new("Value")))
- .y_axis(Axis::new().title(Title::new("Count")))
+ .title("Colored and Styled Histograms")
+ .x_axis(Axis::new().title("Value"))
+ .y_axis(Axis::new().title("Count"))
.bar_mode(BarMode::Overlay)
.bar_gap(0.05)
.bar_group_gap(0.2);
diff --git a/examples/subplots/src/main.rs b/examples/subplots/src/main.rs
index 88af0dcb..c78bde5a 100644
--- a/examples/subplots/src/main.rs
+++ b/examples/subplots/src/main.rs
@@ -1,9 +1,11 @@
#![allow(dead_code)]
-use plotly::common::{AxisSide, Font, Title};
-use plotly::layout::{Axis, GridPattern, Layout, LayoutGrid, Legend, RowOrder, TraceOrder};
+use plotly::common::{Anchor, AxisSide, Font, Title};
+use plotly::layout::{
+ Annotation, Axis, GridPattern, Layout, LayoutGrid, Legend, RowOrder, TraceOrder,
+};
+use plotly::Configuration;
use plotly::{color::Rgb, Plot, Scatter};
-
// Subplots
fn simple_subplot() {
let trace1 = Scatter::new(vec![1, 2, 3], vec![4, 5, 6]).name("trace1");
@@ -27,6 +29,50 @@ fn simple_subplot() {
plot.show();
}
+fn simple_subplot_matches_x_axis() {
+ let trace1 = Scatter::new(vec![1, 2, 3], vec![4, 5, 6]).name("trace1");
+ let trace2 = Scatter::new(vec![20, 30, 40], vec![50, 60, 70])
+ .name("trace2")
+ .x_axis("x2")
+ .y_axis("y2");
+
+ let mut plot = Plot::new();
+ plot.add_trace(trace1);
+ plot.add_trace(trace2);
+
+ let layout = Layout::new().x_axis(Axis::new().matches("x2")).grid(
+ LayoutGrid::new()
+ .rows(1)
+ .columns(2)
+ .pattern(GridPattern::Independent),
+ );
+ plot.set_layout(layout);
+
+ plot.show();
+}
+
+fn simple_subplot_matches_y_axis() {
+ let trace1 = Scatter::new(vec![1, 2, 3], vec![4, 5, 6]).name("trace1");
+ let trace2 = Scatter::new(vec![20, 30, 40], vec![50, 60, 70])
+ .name("trace2")
+ .x_axis("x2")
+ .y_axis("y2");
+
+ let mut plot = Plot::new();
+ plot.add_trace(trace1);
+ plot.add_trace(trace2);
+
+ let layout = Layout::new().y_axis(Axis::new().matches("y2")).grid(
+ LayoutGrid::new()
+ .rows(1)
+ .columns(2)
+ .pattern(GridPattern::Independent),
+ );
+ plot.set_layout(layout);
+
+ plot.show();
+}
+
fn custom_sized_subplot() {
let trace1 = Scatter::new(vec![1, 2, 3], vec![4, 5, 6]).name("trace1");
let trace2 = Scatter::new(vec![20, 30, 40], vec![50, 60, 70])
@@ -148,7 +194,7 @@ fn multiple_custom_sized_subplots() {
plot.add_trace(trace4);
let layout = Layout::new()
- .title(Title::new("Multiple Custom Sized Subplots"))
+ .title("Multiple Custom Sized Subplots")
.x_axis(Axis::new().domain(&[0., 0.45]).anchor("y1"))
.y_axis(Axis::new().domain(&[0.5, 1.]).anchor("x1"))
.x_axis2(Axis::new().domain(&[0.55, 1.]).anchor("y2"))
@@ -174,11 +220,11 @@ fn two_y_axes() {
plot.add_trace(trace2);
let layout = Layout::new()
- .title(Title::new("Double Y Axis Example"))
- .y_axis(Axis::new().title(Title::new("yaxis title")))
+ .title("Double Y Axis Example")
+ .y_axis(Axis::new().title("yaxis title"))
.y_axis2(
Axis::new()
- .title(Title::new("yaxis2 title").font(Font::new().color(Rgb::new(148, 103, 189))))
+ .title(Title::from("yaxis2 title").font(Font::new().color(Rgb::new(148, 103, 189))))
.tick_font(Font::new().color(Rgb::new(148, 103, 189)))
.overlaying("y")
.side(AxisSide::Right),
@@ -203,17 +249,17 @@ fn multiple_axes() {
plot.add_trace(trace4);
let layout = Layout::new()
- .title(Title::new("multiple y-axes example"))
+ .title("multiple y-axes example")
.width(800)
.x_axis(Axis::new().domain(&[0.3, 0.7]))
.y_axis(
Axis::new()
- .title(Title::new("yaxis title").font(Font::new().color("#1f77b4")))
+ .title(Title::from("yaxis title").font(Font::new().color("#1f77b4")))
.tick_font(Font::new().color("#1f77b4")),
)
.y_axis2(
Axis::new()
- .title(Title::new("yaxis2 title").font(Font::new().color("#ff7f0e")))
+ .title(Title::from("yaxis2 title").font(Font::new().color("#ff7f0e")))
.tick_font(Font::new().color("#ff7f0e"))
.anchor("free")
.overlaying("y")
@@ -222,7 +268,7 @@ fn multiple_axes() {
)
.y_axis3(
Axis::new()
- .title(Title::new("yaxis3 title").font(Font::new().color("#d62728")))
+ .title(Title::from("yaxis3 title").font(Font::new().color("#d62728")))
.tick_font(Font::new().color("#d62728"))
.anchor("x")
.overlaying("y")
@@ -230,7 +276,7 @@ fn multiple_axes() {
)
.y_axis4(
Axis::new()
- .title(Title::new("yaxis4 title").font(Font::new().color("#9467bd")))
+ .title(Title::from("yaxis4 title").font(Font::new().color("#9467bd")))
.tick_font(Font::new().color("#9467bd"))
.anchor("free")
.overlaying("y")
@@ -242,16 +288,59 @@ fn multiple_axes() {
plot.show();
}
+fn many_subplots_with_titles() {
+ let trace1 = Scatter::new(vec![1, 2], vec![4, 5]);
+
+ let number_of_plots = 10;
+
+ let mut plot = Plot::new();
+ let mut layout = Layout::new()
+ .grid(
+ LayoutGrid::new()
+ .rows(number_of_plots / 2)
+ .columns(2)
+ .pattern(GridPattern::Independent),
+ )
+ .height(number_of_plots * 200);
+
+ for i in 1..number_of_plots + 1 {
+ plot.add_trace(
+ trace1
+ .clone()
+ .y_axis(format!("y{}", i))
+ .x_axis(format!("x{}", i)),
+ );
+ layout.add_annotation(
+ Annotation::new()
+ .y_ref(format!("y{} domain", i))
+ .y_anchor(Anchor::Bottom)
+ .y(1)
+ .text(format!("Title {}", i))
+ .x_ref(format!("x{} domain", i))
+ .x_anchor(Anchor::Center)
+ .x(0.5)
+ .show_arrow(false),
+ )
+ }
+
+ plot.set_layout(layout);
+ plot.set_configuration(Configuration::new().responsive(true));
+ plot.show();
+}
+
fn main() {
// Uncomment any of these lines to display the example.
// Subplots
// simple_subplot();
+ // simple_subplot_matches_x_axis();
+ // simple_subplot_matches_y_axis();
// custom_sized_subplot();
// multiple_subplots();
// stacked_subplots();
// stacked_subplots_with_shared_x_axis();
// multiple_custom_sized_subplots();
+ // many_subplots_with_titles();
// Multiple Axes
// two_y_axes();
diff --git a/examples/wasm-yew-minimal/Cargo.toml b/examples/wasm-yew-minimal/Cargo.toml
index ea8d220d..cef3d180 100644
--- a/examples/wasm-yew-minimal/Cargo.toml
+++ b/examples/wasm-yew-minimal/Cargo.toml
@@ -1,12 +1,12 @@
[package]
name = "wasm-yew-minimal"
version = "0.1.0"
-authors = ["Michael Freeborn "]
+authors = ["Michael Freeborn ", "Yuichi Nakamura "]
edition = "2021"
[dependencies]
plotly = { path = "../../plotly", features = ["wasm"] }
-yew = "0.19.0"
-yew-hooks = "0.1.56"
+yew = "0.21.0"
+yew-hooks = "0.3.2"
log = "0.4.6"
-wasm-logger = "0.2"
\ No newline at end of file
+wasm-logger = "0.2"
diff --git a/examples/wasm-yew-minimal/src/main.rs b/examples/wasm-yew-minimal/src/main.rs
index 8649c03c..cb0fb2bc 100644
--- a/examples/wasm-yew-minimal/src/main.rs
+++ b/examples/wasm-yew-minimal/src/main.rs
@@ -9,8 +9,7 @@ pub fn plot_component() -> Html {
let trace = Scatter::new(vec![0, 1, 2], vec![2, 1, 0]);
plot.add_trace(trace);
- let layout =
- plotly::Layout::new().title(plotly::common::Title::new("Displaying a Chart in Yew"));
+ let layout = plotly::Layout::new().title("Displaying a Chart in Yew");
plot.set_layout(layout);
async move {
@@ -18,14 +17,10 @@ pub fn plot_component() -> Html {
Ok(())
}
});
-
- use_effect_with_deps(
- move |_| {
- p.run();
- || ()
- },
- (),
- );
+ // Only on first render
+ use_effect_with((), move |_| {
+ p.run();
+ });
html! {
@@ -34,5 +29,5 @@ pub fn plot_component() -> Html {
fn main() {
wasm_logger::init(wasm_logger::Config::default());
- yew::start_app::();
+ yew::Renderer::::new().render();
}
diff --git a/plotly/.gitignore b/plotly/.gitignore
index 68f62957..108a47cd 100644
--- a/plotly/.gitignore
+++ b/plotly/.gitignore
@@ -2,3 +2,4 @@ target/
.idea
Cargo.lock
gh-pages/
+.vscode
diff --git a/plotly/Cargo.toml b/plotly/Cargo.toml
index bd6ba120..ff9b07aa 100644
--- a/plotly/Cargo.toml
+++ b/plotly/Cargo.toml
@@ -1,13 +1,13 @@
[package]
name = "plotly"
-version = "0.8.4"
+version = "0.9.0"
description = "A plotting library powered by Plotly.js"
authors = ["Ioannis Giagkiozis "]
license = "MIT"
readme = "../README.md"
-homepage = "https://github.com/igiagkiozis/plotly"
+homepage = "https://github.com/plotly/plotly.rs"
documentation = "https://docs.rs/plotly"
-repository = "https://github.com/igiagkiozis/plotly"
+repository = "https://github.com/plotly/plotly.rs"
edition = "2018"
keywords = ["plot", "chart", "plotly"]
@@ -18,31 +18,33 @@ kaleido = ["plotly_kaleido"]
plotly_ndarray = ["ndarray"]
plotly_image = ["image"]
wasm = ["getrandom", "js-sys", "wasm-bindgen", "wasm-bindgen-futures"]
+with-axum = ["askama/with-axum", "askama_axum"]
[dependencies]
askama = { version = ">=0.11.0, <0.13.0", features = ["serde-json"] }
+askama_axum = { version = "0.4.0", optional = true }
dyn-clone = "1"
-erased-serde = "0.3"
+erased-serde = "0.4"
getrandom = { version = "0.2", features = ["js"], optional = true }
-image = { version = "0.24.2", optional = true }
+image = { version = "0.25", optional = true }
js-sys = { version = "0.3", optional = true }
-plotly_derive = { version = "0.8.4", path = "../plotly_derive" }
-plotly_kaleido = { version = "0.8.4", path = "../plotly_kaleido", optional = true }
+plotly_derive = { version = "0.9.0", path = "../plotly_derive" }
+plotly_kaleido = { version = "0.9.0", path = "../plotly_kaleido", optional = true }
ndarray = { version = "0.15.4", optional = true }
once_cell = "1"
serde = { version = "1.0.132", features = ["derive"] }
serde_json = "1.0.73"
serde_repr = "0.1"
-serde_with = "2"
+serde_with = ">=2, <4"
rand = "0.8"
wasm-bindgen = { version = "0.2", optional = true }
wasm-bindgen-futures = { version = "0.4", optional = true }
[dev-dependencies]
csv = "1.1.6"
-image = "0.24.4"
-itertools = "0.10.3"
+image = "0.25"
+itertools = ">=0.10, <0.14"
itertools-num = "0.1.3"
ndarray = "0.15.4"
-plotly_kaleido = { version = "0.8.4", path = "../plotly_kaleido" }
+plotly_kaleido = { version = "0.9.0", path = "../plotly_kaleido" }
rand_distr = "0.4"
diff --git a/plotly/src/common/color.rs b/plotly/src/common/color.rs
index 06121ef8..1d7a03af 100644
--- a/plotly/src/common/color.rs
+++ b/plotly/src/common/color.rs
@@ -33,6 +33,17 @@ impl Color for &'static str {}
impl Color for String {}
impl Color for Rgb {}
impl Color for Rgba {}
+impl Color for f64 {}
+impl Color for f32 {}
+impl Color for u64 {}
+impl Color for u32 {}
+impl Color for u16 {}
+impl Color for u8 {}
+impl Color for i64 {}
+impl Color for i32 {}
+impl Color for i16 {}
+impl Color for i8 {}
+impl Color for usize {}
/// ColorArray is only used internally to provide a helper method for converting
/// Vec to Vec>, as we would otherwise fall foul of
@@ -290,6 +301,20 @@ mod tests {
assert_eq!(to_value(color).unwrap(), json!("any_arbitrary_string"));
}
+ #[test]
+ fn test_serialize_numbers() {
+ assert_eq!(to_value(1f64).unwrap(), json!(1f64));
+ assert_eq!(to_value(1f32).unwrap(), json!(1f32));
+ assert_eq!(to_value(1i64).unwrap(), json!(1i64));
+ assert_eq!(to_value(1i32).unwrap(), json!(1i32));
+ assert_eq!(to_value(1i16).unwrap(), json!(1i16));
+ assert_eq!(to_value(1i8).unwrap(), json!(1i8));
+ assert_eq!(to_value(1u64).unwrap(), json!(1u64));
+ assert_eq!(to_value(1u32).unwrap(), json!(1u32));
+ assert_eq!(to_value(1u16).unwrap(), json!(1u16));
+ assert_eq!(to_value(1u8).unwrap(), json!(1u8));
+ }
+
#[test]
#[rustfmt::skip]
fn test_serialize_named_color() {
diff --git a/plotly/src/common/mod.rs b/plotly/src/common/mod.rs
index fc98e6e9..74956fa2 100644
--- a/plotly/src/common/mod.rs
+++ b/plotly/src/common/mod.rs
@@ -58,14 +58,36 @@ pub enum HoverInfo {
#[serde_with::skip_serializing_none]
#[derive(Serialize, Clone, Debug, Default)]
pub struct LegendGroupTitle {
- text: String,
+ text: Option,
font: Option,
}
+impl From<&str> for LegendGroupTitle {
+ fn from(title: &str) -> Self {
+ LegendGroupTitle::with_text(title)
+ }
+}
+
+impl From for LegendGroupTitle {
+ fn from(value: String) -> Self {
+ LegendGroupTitle::with_text(value)
+ }
+}
+
+impl From<&String> for LegendGroupTitle {
+ fn from(value: &String) -> Self {
+ LegendGroupTitle::with_text(value)
+ }
+}
+
impl LegendGroupTitle {
- pub fn new(text: &str) -> Self {
- Self {
- text: text.to_string(),
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ pub fn with_text>(text: S) -> Self {
+ LegendGroupTitle {
+ text: Some(text.into()),
..Default::default()
}
}
@@ -201,6 +223,8 @@ pub enum PlotType {
Ohlc,
Sankey,
Surface,
+ DensityMapbox,
+ Table,
}
#[derive(Serialize, Clone, Debug)]
@@ -750,6 +774,7 @@ pub struct ColorBar {
len_mode: Option,
#[serde(rename = "nticks")]
n_ticks: Option,
+ orientation: Option,
#[serde(rename = "outlinecolor")]
outline_color: Option>,
#[serde(rename = "outlinewidth")]
@@ -852,6 +877,11 @@ impl ColorBar {
self
}
+ pub fn orientation(mut self, orientation: Orientation) -> Self {
+ self.orientation = Some(orientation);
+ self
+ }
+
pub fn outline_color(mut self, outline_color: C) -> Self {
self.outline_color = Some(Box::new(outline_color));
self
@@ -968,8 +998,8 @@ impl ColorBar {
self
}
- pub fn title(mut self, title: Title) -> Self {
- self.title = Some(title);
+ pub fn title>(mut self, title: T) -> Self {
+ self.title = Some(title.into());
self
}
@@ -1227,7 +1257,7 @@ impl Pad {
#[serde_with::skip_serializing_none]
#[derive(Serialize, Clone, Debug, Default)]
pub struct Title {
- text: String,
+ text: Option,
font: Option,
side: Option,
#[serde(rename = "xref")]
@@ -1245,14 +1275,30 @@ pub struct Title {
impl From<&str> for Title {
fn from(title: &str) -> Self {
- Title::new(title)
+ Title::with_text(title)
+ }
+}
+
+impl From for Title {
+ fn from(value: String) -> Self {
+ Title::with_text(value)
+ }
+}
+
+impl From<&String> for Title {
+ fn from(value: &String) -> Self {
+ Title::with_text(value)
}
}
impl Title {
- pub fn new(text: &str) -> Self {
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ pub fn with_text>(text: S) -> Self {
Title {
- text: text.to_owned(),
+ text: Some(text.into()),
..Default::default()
}
}
@@ -1662,6 +1708,7 @@ mod tests {
.len(99)
.len_mode(ThicknessMode::Pixels)
.n_ticks(500)
+ .orientation(Orientation::Horizontal)
.outline_color("#789456")
.outline_width(7)
.separate_thousands(true)
@@ -1685,7 +1732,7 @@ mod tests {
.tick_width(55)
.tick0(0.0)
.ticks(Ticks::Outside)
- .title(Title::new("title"))
+ .title(Title::new())
.x(5.0)
.x_anchor(Anchor::Bottom)
.x_pad(2.2)
@@ -1702,6 +1749,7 @@ mod tests {
"len": 99,
"lenmode": "pixels",
"nticks": 500,
+ "orientation": "h",
"outlinecolor": "#789456",
"outlinewidth": 7,
"separatethousands": true,
@@ -1725,7 +1773,7 @@ mod tests {
"tickwidth": 55,
"tick0": 0.0,
"ticks": "outside",
- "title": {"text": "title"},
+ "title": {},
"x": 5.0,
"xanchor": "bottom",
"xpad": 2.2,
@@ -2162,6 +2210,15 @@ mod tests {
assert_eq!(to_value(Reference::Paper).unwrap(), json!("paper"));
}
+ #[test]
+ #[rustfmt::skip]
+ fn test_serialize_legend_group_title() {
+ assert_eq!(to_value(LegendGroupTitle::new()).unwrap(), json!({}));
+ assert_eq!(to_value(LegendGroupTitle::with_text("title_str").font(Font::default())).unwrap(), json!({"font": {}, "text": "title_str"}));
+ assert_eq!(to_value(LegendGroupTitle::from(String::from("title_string"))).unwrap(), json!({"text" : "title_string"}));
+ assert_eq!(to_value(LegendGroupTitle::from(&String::from("title_string"))).unwrap(), json!({"text" : "title_string"}));
+ }
+
#[test]
fn test_serialize_pad() {
let pad = Pad::new(1, 2, 3);
@@ -2176,7 +2233,7 @@ mod tests {
#[test]
fn test_serialize_title() {
- let title = Title::new("title")
+ let title = Title::with_text("title")
.font(Font::new())
.side(Side::Top)
.x_ref(Reference::Paper)
@@ -2297,4 +2354,13 @@ mod tests {
assert_eq!(to_value(HoverOn::PointsAndFills).unwrap(), json!("points+fills"));
}
+
+ #[test]
+ #[allow(clippy::needless_borrows_for_generic_args)]
+ fn test_title_method_can_take_string() {
+ ColorBar::new().title("Title");
+ ColorBar::new().title(String::from("Title"));
+ ColorBar::new().title(&String::from("Title"));
+ ColorBar::new().title(Title::with_text("Title"));
+ }
}
diff --git a/plotly/src/layout/mod.rs b/plotly/src/layout/mod.rs
index 2f77871b..7a70c23e 100644
--- a/plotly/src/layout/mod.rs
+++ b/plotly/src/layout/mod.rs
@@ -7,6 +7,7 @@ use plotly_derive::FieldSetter;
use serde::{Serialize, Serializer};
use update_menu::UpdateMenu;
+use crate::common::Domain;
use crate::{
color::Color,
common::{
@@ -440,6 +441,9 @@ pub struct Axis {
#[serde(rename = "nticks")]
n_ticks: Option,
+ #[serde(rename = "scaleanchor")]
+ scale_anchor: Option,
+
tick0: Option,
dtick: Option,
@@ -542,10 +546,8 @@ impl Axis {
Default::default()
}
- pub fn matches(mut self, matches: bool) -> Self {
- if matches {
- self.matches = Some(String::from("x"));
- }
+ pub fn matches(mut self, matches: &str) -> Self {
+ self.matches = Some(matches.to_string());
self
}
@@ -1283,6 +1285,31 @@ impl Serialize for DragMode {
}
}
+#[derive(Debug, Clone)]
+/// Determines the mode of drag interactions.
+pub enum DragMode3D {
+ Zoom,
+ Pan,
+ Turntable,
+ Orbit,
+ False,
+}
+
+impl Serialize for DragMode3D {
+ fn serialize(&self, serializer: S) -> Result
+ where
+ S: serde::Serializer,
+ {
+ match *self {
+ Self::Zoom => serializer.serialize_str("zoom"),
+ Self::Pan => serializer.serialize_str("pan"),
+ Self::Turntable => serializer.serialize_str("turntable"),
+ Self::Orbit => serializer.serialize_str("orbit"),
+ Self::False => serializer.serialize_bool(false),
+ }
+ }
+}
+
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum SelectDirection {
@@ -1345,6 +1372,8 @@ pub struct Mapbox {
bearing: Option,
/// Sets the latitude and longitude of the center of the map.
center: Option,
+ /// Sets the domain within which the mapbox will be drawn.
+ domain: Option,
/// Sets the pitch angle of the map in degrees, where `0` means
/// perpendicular to the surface of the map.
pitch: Option,
@@ -1360,6 +1389,236 @@ impl Mapbox {
}
}
+#[derive(Serialize, Debug, Clone)]
+/// If "cube", this scene's axes are drawn as a cube, regardless of the axes'
+/// ranges. If "data", this scene's axes are drawn in proportion with the axes'
+/// ranges. If "manual", this scene's axes are drawn in proportion with the
+/// input of "aspectratio" (the default behavior if "aspectratio" is provided).
+/// If "auto", this scene's axes are drawn using the results of "data" except
+/// when one axis is more than four times the size of the two others, where in
+/// that case the results of "cube" are used.
+/// Default: "auto"
+#[derive(Default)]
+pub enum AspectMode {
+ #[serde(rename = "auto")]
+ #[default]
+ Auto,
+ #[serde(rename = "cube")]
+ Cube,
+ #[serde(rename = "data")]
+ Data,
+ #[serde(rename = "manual")]
+ Manual,
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Debug, Clone, FieldSetter)]
+/// Sets the (x, y, z) components of the 'eye' camera vector. This vector
+/// determines the view point about the origin of this scene.
+/// Default: {x: 1.25, y: 1.25, z: 1.25}
+pub struct Eye {
+ x: Option,
+ y: Option,
+ z: Option,
+}
+
+impl Eye {
+ pub fn new() -> Self {
+ Eye {
+ x: Some(1.25),
+ y: Some(1.25),
+ z: Some(1.25),
+ }
+ }
+}
+
+impl From<(f64, f64, f64)> for Eye {
+ fn from((x, y, z): (f64, f64, f64)) -> Self {
+ Eye {
+ x: Some(x),
+ y: Some(y),
+ z: Some(z),
+ }
+ }
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Debug, Clone, FieldSetter)]
+/// Sets the (x, y, z) components of the 'up' camera vector. This vector
+/// determines the up direction of this scene with respect to the page. The
+/// Default: {x: 0, y: 0, z: 1} which means that the z axis points up.
+pub struct Up {
+ x: Option,
+ y: Option,
+ z: Option,
+}
+
+impl Up {
+ pub fn new() -> Self {
+ Up {
+ x: Some(0.0),
+ y: Some(0.0),
+ z: Some(1.0),
+ }
+ }
+}
+
+impl From<(f64, f64, f64)> for Up {
+ fn from((x, y, z): (f64, f64, f64)) -> Self {
+ Up {
+ x: Some(x),
+ y: Some(y),
+ z: Some(z),
+ }
+ }
+}
+
+#[derive(Default, Serialize, Debug, Clone)]
+/// Sets the projection type. The projection type could be either "perspective"
+/// or "orthographic".
+/// Default: "perspective"
+pub enum ProjectionType {
+ #[default]
+ #[serde(rename = "perspective")]
+ Perspective,
+ #[serde(rename = "orthographic")]
+ Orthographic,
+}
+
+impl From for Projection {
+ fn from(projection_type: ProjectionType) -> Self {
+ Projection {
+ projection_type: Some(projection_type),
+ }
+ }
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Debug, Clone, FieldSetter)]
+/// Container for Projection options.
+pub struct Projection {
+ #[serde(rename = "type")]
+ projection_type: Option,
+}
+
+impl Projection {
+ pub fn new() -> Self {
+ Default::default()
+ }
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Debug, Clone, FieldSetter)]
+/// Sets the (x, y, z) components of the 'center' camera vector. This vector
+/// determines the translation (x, y, z) space about the center of this scene.
+/// Default: {x: 0, y: 0, z: 0} which means that the center of the scene is at
+/// the origin.
+pub struct CameraCenter {
+ x: Option,
+ y: Option,
+ z: Option,
+}
+
+impl CameraCenter {
+ pub fn new() -> Self {
+ CameraCenter {
+ x: Some(0.0),
+ y: Some(0.0),
+ z: Some(0.0),
+ }
+ }
+}
+
+impl From<(f64, f64, f64)> for CameraCenter {
+ fn from((x, y, z): (f64, f64, f64)) -> Self {
+ CameraCenter {
+ x: Some(x),
+ y: Some(y),
+ z: Some(z),
+ }
+ }
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Debug, Clone, FieldSetter)]
+/// Container for CameraCenter, Eye, Up, and Projection objects. The camera of a
+/// 3D scene.
+pub struct Camera {
+ center: Option,
+ eye: Option,
+ up: Option,
+ projection: Option,
+}
+
+impl Camera {
+ pub fn new() -> Self {
+ Default::default()
+ }
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Debug, Clone, FieldSetter)]
+/// Sets this scene's axis aspectratio.
+/// x, y, z must be positive.
+/// Default: {x: 1, y: 1, z: 1}
+pub struct AspectRatio {
+ x: Option,
+ y: Option,
+ z: Option,
+}
+
+impl AspectRatio {
+ pub fn new() -> Self {
+ AspectRatio {
+ x: Some(1.0),
+ y: Some(1.0),
+ z: Some(1.0),
+ }
+ }
+}
+
+impl From<(f64, f64, f64)> for AspectRatio {
+ fn from((x, y, z): (f64, f64, f64)) -> Self {
+ AspectRatio {
+ x: Some(x),
+ y: Some(y),
+ z: Some(z),
+ }
+ }
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Debug, Clone, FieldSetter)]
+/// 3D scene layout
+pub struct LayoutScene {
+ #[serde(rename = "bgcolor")]
+ background_color: Option>,
+ camera: Option,
+ #[serde(rename = "aspectmode")]
+ aspect_mode: Option,
+ #[serde(rename = "aspectratio")]
+ aspect_ratio: Option,
+ #[serde(rename = "xaxis")]
+ x_axis: Option,
+ #[serde(rename = "yaxis")]
+ y_axis: Option,
+ #[serde(rename = "zaxis")]
+ z_axis: Option,
+ #[serde(rename = "dragmode")]
+ drag_mode: Option,
+ #[serde(rename = "hovermode")]
+ hover_mode: Option,
+ annotations: Option>,
+ // domain: Domain,
+ // uirevision: Uirevision,
+}
+
+impl LayoutScene {
+ pub fn new() -> Self {
+ Default::default()
+ }
+}
+
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct Template {
@@ -1481,7 +1740,7 @@ pub struct LayoutTemplate {
y_axis8: Option>,
// ternary: Option,
- // scene: Option,
+ scene: Option,
// polar: Option,
annotations: Option>,
shapes: Option>,
@@ -1665,7 +1924,7 @@ pub struct Layout {
z_axis8: Option>,
// ternary: Option,
- // scene: Option,
+ scene: Option,
// polar: Option,
annotations: Option>,
shapes: Option>,
@@ -1921,7 +2180,7 @@ mod tests {
.y(2.0)
.y_anchor(Anchor::Left)
.valign(VAlign::Middle)
- .title(Title::new("title"))
+ .title("title")
.group_click(GroupClick::ToggleItem)
.item_width(50);
@@ -2171,7 +2430,7 @@ mod tests {
let axis = Axis::new()
.visible(false)
.color("#678123")
- .title(Title::new("title"))
+ .title(Title::with_text("title"))
.type_(AxisType::Date)
.auto_range(false)
.range_mode(RangeMode::NonNegative)
@@ -2183,7 +2442,7 @@ mod tests {
.n_ticks(600)
.tick0(5.0)
.dtick(10.0)
- .matches(true)
+ .matches("x")
.tick_values(vec![1.0, 2.0])
.tick_text(vec!["one".to_string(), "two".to_string()])
.ticks(TicksDirection::Inside)
@@ -2703,7 +2962,7 @@ mod tests {
#[test]
fn test_serialize_layout_template() {
let layout_template = LayoutTemplate::new()
- .title("Title".into())
+ .title("Title")
.show_legend(false)
.legend(Legend::new())
.margin(Margin::new())
@@ -2845,7 +3104,9 @@ mod tests {
#[test]
fn test_serialize_layout() {
let layout = Layout::new()
- .title("Title".into())
+ .title("Title")
+ .title(String::from("Title"))
+ .title(Title::with_text("Title"))
.show_legend(false)
.legend(Legend::new())
.margin(Margin::new())
@@ -2908,7 +3169,8 @@ mod tests {
.extend_pie_colors(true)
.sunburst_colorway(vec!["#654654"])
.extend_sunburst_colors(false)
- .z_axis(Axis::new());
+ .z_axis(Axis::new())
+ .scene(LayoutScene::new());
let expected = json!({
"title": {"text": "Title"},
@@ -2975,8 +3237,188 @@ mod tests {
"sunburstcolorway": ["#654654"],
"extendsunburstcolors": false,
"zaxis": {},
+ "scene": {}
+ });
+
+ assert_eq!(to_value(layout).unwrap(), expected);
+ }
+
+ #[test]
+ fn test_serialize_layout_scene() {
+ let layout = Layout::new().scene(
+ LayoutScene::new()
+ .x_axis(Axis::new())
+ .y_axis(Axis::new())
+ .z_axis(Axis::new())
+ .camera(Camera::new())
+ .aspect_mode(AspectMode::Auto)
+ .hover_mode(HoverMode::Closest)
+ .drag_mode(DragMode3D::Turntable)
+ .background_color("#FFFFFF")
+ .annotations(vec![Annotation::new()]),
+ );
+
+ let expected = json!({
+ "scene": {
+ "xaxis": {},
+ "yaxis": {},
+ "zaxis": {},
+ "camera": {},
+ "aspectmode": "auto",
+ "hovermode": "closest",
+ "dragmode": "turntable",
+ "bgcolor": "#FFFFFF",
+ "annotations": [{}],
+ }
});
assert_eq!(to_value(layout).unwrap(), expected);
}
+
+ #[test]
+ fn test_serialize_eye() {
+ let eye = Eye::new();
+
+ assert_eq!(
+ to_value(eye).unwrap(),
+ json!({
+ "x": 1.25,
+ "y": 1.25,
+ "z": 1.25,
+ })
+ );
+
+ let eye = Eye::new().x(1f64).y(2f64).z(3f64);
+
+ let expected = json!({
+ "x": 1.0,
+ "y": 2.0,
+ "z": 3.0,
+ });
+
+ assert_eq!(to_value(eye).unwrap(), expected);
+
+ let eye: Eye = (1f64, 2f64, 3f64).into();
+
+ assert_eq!(to_value(eye).unwrap(), expected);
+ }
+
+ #[test]
+ fn test_serialize_projection() {
+ let projection = Projection::new().projection_type(ProjectionType::default());
+
+ let expected = json!({
+ "type": "perspective",
+ });
+
+ assert_eq!(to_value(projection).unwrap(), expected);
+
+ let projection = Projection::new().projection_type(ProjectionType::Orthographic);
+
+ let expected = json!({
+ "type": "orthographic",
+ });
+
+ assert_eq!(to_value(projection).unwrap(), expected);
+
+ let projection: Projection = ProjectionType::Orthographic.into();
+
+ assert_eq!(to_value(projection).unwrap(), expected);
+ }
+
+ #[test]
+ fn test_serialize_camera_center() {
+ let camera_center = CameraCenter::new();
+
+ let expected = json!({
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0,
+ });
+
+ assert_eq!(to_value(camera_center).unwrap(), expected);
+
+ let camera_center = CameraCenter::new().x(1f64).y(2f64).z(3f64);
+
+ let expected = json!({
+ "x": 1.0,
+ "y": 2.0,
+ "z": 3.0,
+ });
+
+ assert_eq!(to_value(camera_center).unwrap(), expected);
+
+ let camera_center: CameraCenter = (1f64, 2f64, 3f64).into();
+
+ assert_eq!(to_value(camera_center).unwrap(), expected);
+ }
+
+ #[test]
+ fn test_serialize_aspect_ratio() {
+ let aspect_ratio = AspectRatio::new();
+
+ let expected = json!({
+ "x": 1.0,
+ "y": 1.0,
+ "z": 1.0,
+ });
+
+ assert_eq!(to_value(aspect_ratio).unwrap(), expected);
+
+ let aspect_ratio = AspectRatio::new().x(1f64).y(2f64).z(3f64);
+
+ let expected = json!({
+ "x": 1.0,
+ "y": 2.0,
+ "z": 3.0,
+ });
+
+ assert_eq!(to_value(aspect_ratio).unwrap(), expected);
+
+ let aspect_ratio: AspectRatio = (1f64, 2f64, 3f64).into();
+
+ assert_eq!(to_value(aspect_ratio).unwrap(), expected);
+ }
+
+ #[test]
+ fn test_serialize_aspect_mode() {
+ let aspect_mode = AspectMode::default();
+
+ assert_eq!(to_value(aspect_mode).unwrap(), json!("auto"));
+
+ let aspect_mode = AspectMode::Data;
+
+ assert_eq!(to_value(aspect_mode).unwrap(), json!("data"));
+
+ let aspect_mode = AspectMode::Cube;
+
+ assert_eq!(to_value(aspect_mode).unwrap(), json!("cube"));
+ }
+
+ #[test]
+ fn test_serialize_up() {
+ let up = Up::new();
+
+ let expected = json!({
+ "x": 0.0,
+ "y": 0.0,
+ "z": 1.0,
+ });
+
+ assert_eq!(to_value(up).unwrap(), expected);
+
+ let up = Up::new().x(1f64).y(2f64).z(3f64);
+
+ let expected = json!({
+ "x": 1.0,
+ "y": 2.0,
+ "z": 3.0,
+ });
+
+ assert_eq!(to_value(up).unwrap(), expected);
+
+ let up: Up = (1f64, 2f64, 3f64).into();
+
+ assert_eq!(to_value(up).unwrap(), expected);
+ }
}
diff --git a/plotly/src/layout/themes.rs b/plotly/src/layout/themes.rs
index a4fb2774..a687caa7 100644
--- a/plotly/src/layout/themes.rs
+++ b/plotly/src/layout/themes.rs
@@ -62,7 +62,7 @@ pub static PLOTLY_WHITE: Lazy = Lazy::new(|| {
.hover_mode(HoverMode::Closest)
.paper_background_color("#ffffff")
.plot_background_color("#ffffff")
- .title(Title::new("").x(0.05))
+ .title(Title::new().x(0.05))
.x_axis(
Axis::new()
.auto_margin(true)
@@ -138,7 +138,7 @@ pub static PLOTLY_DARK: Lazy = Lazy::new(|| {
.hover_mode(HoverMode::Closest)
.paper_background_color("#111111")
.plot_background_color("#111111")
- .title(Title::new("").x(0.05))
+ .title(Title::new().x(0.05))
.x_axis(
Axis::new()
.auto_margin(true)
@@ -165,6 +165,31 @@ mod tests {
use super::*;
use crate::*;
+ #[test]
+ fn test_plotly_default() {
+ let template = &*DEFAULT;
+ let layout = Layout::new().template(template);
+ let mut plot = Plot::new();
+ plot.set_layout(layout);
+ plot.add_trace(Bar::new(vec![0], vec![1]));
+
+ let expected = r##"{"template":{"layout":{}}}"##; // etc...
+ assert!(plot.to_json().contains(expected));
+ }
+
+ #[test]
+ fn test_plotly_white() {
+ let template = &*PLOTLY_WHITE;
+ let layout = Layout::new().template(template);
+ let mut plot = Plot::new();
+ plot.set_layout(layout);
+ plot.add_trace(Bar::new(vec![0], vec![1]));
+ dbg!(plot.to_json());
+
+ let expected = r##"{"template":{"layout":{"title":{"x":0.05},"font":{"color":"#2a3f5f"}"##; // etc...
+ assert!(plot.to_json().contains(expected));
+ }
+
#[test]
fn test_plotly_dark() {
let template = &*PLOTLY_DARK;
@@ -173,8 +198,7 @@ mod tests {
plot.set_layout(layout);
plot.add_trace(Bar::new(vec![0], vec![1]));
- let expected =
- r##"{"template":{"layout":{"title":{"text":"","x":0.05},"font":{"color":"#f2f5fa"}"##; // etc...
+ let expected = r##"{"template":{"layout":{"title":{"x":0.05},"font":{"color":"#f2f5fa"}"##; // etc...
assert!(plot.to_json().contains(expected));
}
}
diff --git a/plotly/src/layout/update_menu.rs b/plotly/src/layout/update_menu.rs
index 4823385e..f662a7f2 100644
--- a/plotly/src/layout/update_menu.rs
+++ b/plotly/src/layout/update_menu.rs
@@ -100,7 +100,7 @@ impl ButtonBuilder {
pub fn new() -> Self {
Default::default()
}
- pub fn push_restyle(mut self, restyle: impl Restyle + Serialize) -> Self {
+ pub fn push_restyle(mut self, restyle: impl Restyle) -> Self {
let restyle = serde_json::to_value(&restyle).unwrap();
for (k, v) in restyle.as_object().unwrap() {
self.restyles.insert(k.clone(), v.clone());
@@ -246,10 +246,7 @@ mod tests {
use serde_json::{json, to_value};
use super::*;
- use crate::{
- common::{Title, Visible},
- Layout,
- };
+ use crate::{common::Visible, Layout};
#[test]
fn test_serialize_button_method() {
@@ -315,7 +312,7 @@ mod tests {
Visible::True,
Visible::False,
]))
- .push_relayout(Layout::modify_title(Title::new("Hello")))
+ .push_relayout(Layout::modify_title("Hello"))
.push_relayout(Layout::modify_width(20))
.build();
diff --git a/plotly/src/lib.rs b/plotly/src/lib.rs
index 2d0a1e15..c5337680 100644
--- a/plotly/src/lib.rs
+++ b/plotly/src/lib.rs
@@ -30,11 +30,13 @@ pub use configuration::Configuration;
pub use layout::Layout;
pub use plot::{ImageFormat, Plot, Trace};
// Also provide easy access to modules which contain additional trace-specific types
-pub use traces::{box_plot, contour, histogram, image, mesh3d, sankey, scatter_mapbox, surface};
+pub use traces::{
+ box_plot, contour, heat_map, histogram, image, mesh3d, sankey, scatter_mapbox, surface,
+};
// Bring the different trace types into the top-level scope
pub use traces::{
- Bar, BoxPlot, Candlestick, Contour, HeatMap, Histogram, Image, Mesh3D, Ohlc, Sankey, Scatter,
- Scatter3D, ScatterMapbox, ScatterPolar, Surface,
+ Bar, BoxPlot, Candlestick, Contour, DensityMapbox, HeatMap, Histogram, Image, Mesh3D, Ohlc,
+ Sankey, Scatter, Scatter3D, ScatterMapbox, ScatterPolar, Surface, Table,
};
pub trait Restyle: serde::Serialize {}
diff --git a/plotly/src/plot.rs b/plotly/src/plot.rs
index ac80af65..d02fe92d 100644
--- a/plotly/src/plot.rs
+++ b/plotly/src/plot.rs
@@ -20,6 +20,7 @@ struct PlotTemplate<'a> {
#[derive(Template)]
#[template(path = "static_plot.html", escape = "none")]
+#[cfg(not(target_family = "wasm"))]
struct StaticPlotTemplate<'a> {
plot: &'a Plot,
format: ImageFormat,
@@ -61,7 +62,7 @@ let height = 680;
let scale = 1.0;
plot.write_image("filename", ImageFormat::PNG, width, height, scale);
-See https://igiagkiozis.github.io/plotly/content/getting_started.html for further details.
+See https://plotly.github.io/plotly.rs/content/getting_started.html for further details.
"#;
/// Image format for static image export.
@@ -160,9 +161,9 @@ impl Traces {
/// plot.add_trace(trace2);
/// plot.add_trace(trace3);
///
-/// let layout = Layout::new().title("Line and Scatter Plot".into());
+/// let layout = Layout::new().title("Line and Scatter Plot");
/// plot.set_layout(layout);
-///
+///
/// # if false { // We don't actually want to try and display the plot in a browser when running a doctest.
/// plot.show();
/// # }
@@ -462,7 +463,7 @@ impl Plot {
fn show_with_default_app(temp_path: &str) {
use std::process::Command;
Command::new("open")
- .args(&[temp_path])
+ .args([temp_path])
.output()
.expect(DEFAULT_HTML_APP_NOT_FOUND);
}
@@ -547,7 +548,7 @@ mod tests {
#[test]
fn test_plot_serialize_with_layout() {
let mut plot = create_test_plot();
- let layout = Layout::new().title("Title".into());
+ let layout = Layout::new().title("Title");
plot.set_layout(layout);
let expected = json!({
@@ -596,7 +597,7 @@ mod tests {
#[test]
fn test_layout_to_json() {
let mut plot = create_test_plot();
- let layout = Layout::new().title("TestTitle".into());
+ let layout = Layout::new().title("TestTitle");
plot.set_layout(layout);
let expected = json!({
@@ -650,6 +651,7 @@ mod tests {
assert!(!dst.exists());
}
+ #[cfg(target_os = "linux")]
#[test]
#[cfg(feature = "kaleido")]
fn test_save_to_png() {
@@ -661,6 +663,7 @@ mod tests {
assert!(!dst.exists());
}
+ #[cfg(target_os = "linux")]
#[test]
#[cfg(feature = "kaleido")]
fn test_save_to_jpeg() {
@@ -672,6 +675,7 @@ mod tests {
assert!(!dst.exists());
}
+ #[cfg(target_os = "linux")]
#[test]
#[cfg(feature = "kaleido")]
fn test_save_to_svg() {
@@ -695,6 +699,7 @@ mod tests {
assert!(!dst.exists());
}
+ #[cfg(target_os = "linux")]
#[test]
#[cfg(feature = "kaleido")]
fn test_save_to_pdf() {
@@ -706,6 +711,7 @@ mod tests {
assert!(!dst.exists());
}
+ #[cfg(target_os = "linux")]
#[test]
#[cfg(feature = "kaleido")]
fn test_save_to_webp() {
diff --git a/plotly/src/traces/bar.rs b/plotly/src/traces/bar.rs
index 16300ae6..0acaed0d 100644
--- a/plotly/src/traces/bar.rs
+++ b/plotly/src/traces/bar.rs
@@ -159,7 +159,7 @@ mod tests {
.inside_text_anchor(TextAnchor::End)
.inside_text_font(Font::new())
.legend_group("legend-group")
- .legend_group_title(LegendGroupTitle::new("legend-group-title"))
+ .legend_group_title("legend-group-title")
.marker(Marker::new())
.name("Bar")
.offset(5)
diff --git a/plotly/src/traces/box_plot.rs b/plotly/src/traces/box_plot.rs
index 5b8e9c91..8903c480 100644
--- a/plotly/src/traces/box_plot.rs
+++ b/plotly/src/traces/box_plot.rs
@@ -283,7 +283,7 @@ mod tests {
.jitter(0.5)
.line(Line::new())
.legend_group("one")
- .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
+ .legend_group_title("Legend Group Title")
.lower_fence(vec![0., 1.])
.marker(Marker::new())
.mean(vec![12., 13.])
diff --git a/plotly/src/traces/candlestick.rs b/plotly/src/traces/candlestick.rs
index 2fe6a197..64b25a5b 100644
--- a/plotly/src/traces/candlestick.rs
+++ b/plotly/src/traces/candlestick.rs
@@ -144,7 +144,7 @@ mod tests {
.visible(Visible::True)
.show_legend(false)
.legend_group("group_1")
- .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
+ .legend_group_title("Legend Group Title")
.opacity(0.3)
.text_array(vec!["text", "here"])
.text("text here")
diff --git a/plotly/src/traces/contour.rs b/plotly/src/traces/contour.rs
index 844f55c2..576e58f1 100644
--- a/plotly/src/traces/contour.rs
+++ b/plotly/src/traces/contour.rs
@@ -339,8 +339,11 @@ where
Box::new(self)
}
- pub fn legend_group_title(mut self, legend_group_title: LegendGroupTitle) -> Box {
- self.legend_group_title = Some(legend_group_title);
+ pub fn legend_group_title(
+ mut self,
+ legend_group_title: impl Into,
+ ) -> Box {
+ self.legend_group_title = Some(legend_group_title.into());
Box::new(self)
}
@@ -583,7 +586,7 @@ mod tests {
.hover_template_array(vec!["ok {1}", "ok {2}"])
.hover_text(vec!["p3", "p4"])
.legend_group("group_1")
- .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
+ .legend_group_title("Legend Group Title")
.line(Line::new())
.n_contours(5)
.name("contour trace")
diff --git a/plotly/src/traces/density_mapbox.rs b/plotly/src/traces/density_mapbox.rs
new file mode 100644
index 00000000..dd66ce67
--- /dev/null
+++ b/plotly/src/traces/density_mapbox.rs
@@ -0,0 +1,146 @@
+//! Density mapbox scatter plot
+
+use plotly_derive::FieldSetter;
+use serde::Serialize;
+
+use crate::common::{LegendGroupTitle, Line, PlotType, Visible};
+use crate::Trace;
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Clone, Debug, FieldSetter)]
+#[field_setter(box_self, kind = "trace")]
+pub struct DensityMapbox
+where
+ Lat: Serialize + Clone,
+ Lon: Serialize + Clone,
+ Z: Serialize + Clone,
+{
+ #[field_setter(default = "PlotType::DensityMapbox")]
+ r#type: PlotType,
+ /// Sets the trace name. The trace name appear as the legend item and on
+ /// hover.
+ name: Option,
+ /// Determines whether or not this trace is visible. If
+ /// `Visible::LegendOnly`, the trace is not drawn, but can appear as a
+ /// legend item (provided that the legend itself is visible).
+ visible: Option,
+
+ /// Determines whether or not an item corresponding to this trace is shown
+ /// in the legend.
+ #[serde(rename = "showlegend")]
+ show_legend: Option,
+
+ /// Sets the legend rank for this trace. Items and groups with smaller ranks
+ /// are presented on top/left side while with `"reversed"
+ /// `legend.trace_order` they are on bottom/right side. The default
+ /// legendrank is 1000, so that you can use ranks less than 1000 to
+ /// place certain items before all unranked items, and ranks greater
+ /// than 1000 to go after all unranked items.
+ #[serde(rename = "legendrank")]
+ legend_rank: Option,
+ /// Sets the legend group for this trace. Traces part of the same legend
+ /// group show/hide at the same time when toggling legend items.
+ #[serde(rename = "legendgroup")]
+ legend_group: Option,
+ /// Set and style the title to appear for the legend group.
+ #[serde(rename = "legendgrouptitle")]
+ legend_group_title: Option,
+
+ /// Line display properties.
+ line: Option,
+
+ lat: Option>,
+ lon: Option>,
+ z: Option>,
+
+ /// Sets the opacity of the trace.
+ opacity: Option,
+
+ /// Sets a reference between this trace's data coordinates and a mapbox
+ /// subplot. If "mapbox" (the default value), the data refer to
+ /// `layout.mapbox`. If "mapbox2", the data refer to `layout.mapbox2`, and
+ /// so on.
+ subplot: Option,
+
+ /// Determines whether or not the color domain is computed
+ /// with respect to the input data (here in `z`) or the bounds set
+ /// in `zmin` and `zmax`. Defaults to false when `zmin` and `zmax` are
+ /// set by the user.
+ zauto: Option,
+
+ /// Sets the upper bound of the color domain. Value should have the
+ /// same units as in `z` and if set, `zmin` must be set as well.
+ zmax: Option,
+
+ zmid: Option,
+
+ zmin: Option,
+
+ zoom: Option,
+
+ radius: Option,
+ //color_continuous_scale: Option>,
+ //color_continuous_midpoint: Option,
+}
+
+impl DensityMapbox
+where
+ Lat: Serialize + Clone + std::default::Default, // TODO why is "+ Default" necessary?
+ Lon: Serialize + Clone + std::default::Default,
+ Z: Serialize + Clone + std::default::Default,
+{
+ pub fn new(lat: Vec, lon: Vec, z: Vec) -> Box {
+ Box::new(Self {
+ lat: Some(lat),
+ lon: Some(lon),
+ z: Some(z),
+ ..Default::default()
+ })
+ }
+}
+
+impl Trace for DensityMapbox
+where
+ Lat: Serialize + Clone,
+ Lon: Serialize + Clone,
+ Z: Serialize + Clone,
+{
+ fn to_json(&self) -> String {
+ serde_json::to_string(&self).unwrap()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use serde_json::{json, to_value};
+
+ use super::*;
+
+ #[test]
+ fn test_serialize_density_mapbox() {
+ let density_mapbox = DensityMapbox::new(vec![45.5017], vec![-73.5673], vec![1.0])
+ .name("name")
+ .visible(Visible::True)
+ .show_legend(true)
+ .legend_rank(1000)
+ .legend_group("legend group")
+ .zoom(5)
+ .radius(20)
+ .opacity(0.5);
+ let expected = json!({
+ "type": "densitymapbox",
+ "lat": [45.5017],
+ "lon": [-73.5673],
+ "z": [1.0],
+ "name": "name",
+ "visible": true,
+ "showlegend": true,
+ "legendrank": 1000,
+ "legendgroup": "legend group",
+ "opacity": 0.5,
+ "zoom": 5,
+ "radius": 20,
+ });
+ assert_eq!(to_value(density_mapbox.clone()).unwrap(), expected);
+ }
+}
diff --git a/plotly/src/traces/heat_map.rs b/plotly/src/traces/heat_map.rs
index 151b6941..811dcfb6 100644
--- a/plotly/src/traces/heat_map.rs
+++ b/plotly/src/traces/heat_map.rs
@@ -110,9 +110,9 @@ where
zauto: Option,
#[serde(rename = "zhoverformat")]
zhover_format: Option,
- zmax: Option,
- zmid: Option,
- zmin: Option,
+ zmax: Option,
+ zmid: Option,
+ zmin: Option,
zsmooth: Option,
}
@@ -179,10 +179,10 @@ mod tests {
#[test]
fn test_serialize_heat_map_z() {
- let trace = HeatMap::new_z(vec![1.0]);
+ let trace = HeatMap::new_z(vec![vec![1.0]]);
let expected = json!({
"type": "heatmap",
- "z": [1.0],
+ "z": [[1.0]],
});
assert_eq!(to_value(trace).unwrap(), expected);
@@ -190,37 +190,41 @@ mod tests {
#[test]
fn test_serialize_heat_map() {
- let trace = HeatMap::new(vec![0.0, 1.0], vec![2.0, 3.0], vec![4.0, 5.0])
- .auto_color_scale(true)
- .color_bar(ColorBar::new())
- .color_scale(ColorScale::Palette(ColorScalePalette::Picnic))
- .connect_gaps(false)
- .hover_info(HoverInfo::None)
- .hover_label(Label::new())
- .hover_on_gaps(true)
- .hover_template("tmpl")
- .hover_template_array(vec!["tmpl1", "tmpl2"])
- .hover_text(vec!["hov", "er"])
- .legend_group("1")
- .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
- .name("name")
- .opacity(0.99)
- .reverse_scale(false)
- .show_legend(true)
- .show_scale(false)
- .text(vec!["te", "xt"])
- .transpose(true)
- .visible(Visible::LegendOnly)
- .x_axis("x")
- .x_calendar(Calendar::Hebrew)
- .y_axis("y")
- .y_calendar(Calendar::Islamic)
- .zauto(true)
- .zhover_format("fmt")
- .zmax(10.0)
- .zmid(5.0)
- .zmin(0.0)
- .zsmooth(Smoothing::Fast);
+ let trace = HeatMap::new(
+ vec![0.0, 1.0],
+ vec![2.0, 3.0],
+ vec![vec![4.0, 5.0], vec![6.0, 7.0]],
+ )
+ .auto_color_scale(true)
+ .color_bar(ColorBar::new())
+ .color_scale(ColorScale::Palette(ColorScalePalette::Picnic))
+ .connect_gaps(false)
+ .hover_info(HoverInfo::None)
+ .hover_label(Label::new())
+ .hover_on_gaps(true)
+ .hover_template("tmpl")
+ .hover_template_array(vec!["tmpl1", "tmpl2"])
+ .hover_text(vec!["hov", "er"])
+ .legend_group("1")
+ .legend_group_title("Legend Group Title")
+ .name("name")
+ .opacity(0.99)
+ .reverse_scale(false)
+ .show_legend(true)
+ .show_scale(false)
+ .text(vec!["te", "xt"])
+ .transpose(true)
+ .visible(Visible::LegendOnly)
+ .x_axis("x")
+ .x_calendar(Calendar::Hebrew)
+ .y_axis("y")
+ .y_calendar(Calendar::Islamic)
+ .zauto(true)
+ .zhover_format("fmt")
+ .zmax(10.0)
+ .zmid(5.0)
+ .zmin(0.0)
+ .zsmooth(Smoothing::Fast);
let expected = json!({
"type": "heatmap",
@@ -249,7 +253,7 @@ mod tests {
"y": [2.0, 3.0],
"yaxis": "y",
"ycalendar": "islamic",
- "z": [4.0, 5.0],
+ "z": [[4.0, 5.0], [6.0, 7.0]],
"zauto": true,
"zhoverformat": "fmt",
"zmax": 10.0,
diff --git a/plotly/src/traces/histogram.rs b/plotly/src/traces/histogram.rs
index 7ae6dd59..b4b9b97f 100644
--- a/plotly/src/traces/histogram.rs
+++ b/plotly/src/traces/histogram.rs
@@ -410,7 +410,7 @@ mod tests {
.hover_text("hover_text")
.hover_text_array(vec!["hover_text_1", "hover_text_2"])
.legend_group("legendgroup")
- .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
+ .legend_group_title("Legend Group Title")
.marker(Marker::new())
.n_bins_x(5)
.n_bins_y(10)
diff --git a/plotly/src/traces/image.rs b/plotly/src/traces/image.rs
index 4efbf0c9..d081f9b4 100644
--- a/plotly/src/traces/image.rs
+++ b/plotly/src/traces/image.rs
@@ -408,7 +408,7 @@ mod tests {
.name("image name")
.visible(Visible::True)
.legend_rank(1000)
- .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
+ .legend_group_title("Legend Group Title")
.opacity(0.5)
.ids(vec!["one"])
.x0(0.0)
diff --git a/plotly/src/traces/mesh3d.rs b/plotly/src/traces/mesh3d.rs
index fae98c9a..fc45d781 100644
--- a/plotly/src/traces/mesh3d.rs
+++ b/plotly/src/traces/mesh3d.rs
@@ -493,7 +493,7 @@ mod tests {
.show_legend(true)
.legend_rank(1000)
.legend_group("legend_group")
- .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
+ .legend_group_title("Legend Group Title")
.opacity(0.5)
.ids(vec!["one"])
.face_color(vec!["#ff00ff"])
diff --git a/plotly/src/traces/mod.rs b/plotly/src/traces/mod.rs
index 28f6079e..f12305c3 100644
--- a/plotly/src/traces/mod.rs
+++ b/plotly/src/traces/mod.rs
@@ -4,7 +4,8 @@ pub mod bar;
pub mod box_plot;
mod candlestick;
pub mod contour;
-mod heat_map;
+mod density_mapbox;
+pub mod heat_map;
pub mod histogram;
pub mod image;
pub mod mesh3d;
@@ -15,11 +16,13 @@ mod scatter3d;
pub mod scatter_mapbox;
mod scatter_polar;
pub mod surface;
+pub mod table;
pub use bar::Bar;
pub use box_plot::BoxPlot;
pub use candlestick::Candlestick;
pub use contour::Contour;
+pub use density_mapbox::DensityMapbox;
pub use heat_map::HeatMap;
pub use histogram::Histogram;
pub use mesh3d::Mesh3D;
@@ -30,5 +33,6 @@ pub use scatter3d::Scatter3D;
pub use scatter_mapbox::ScatterMapbox;
pub use scatter_polar::ScatterPolar;
pub use surface::Surface;
+pub use table::Table;
pub use self::image::Image;
diff --git a/plotly/src/traces/ohlc.rs b/plotly/src/traces/ohlc.rs
index cf1b4e50..7067514f 100644
--- a/plotly/src/traces/ohlc.rs
+++ b/plotly/src/traces/ohlc.rs
@@ -28,7 +28,7 @@ use crate::{
/// let expected = serde_json::json!({
/// "type": "ohlc",
/// "x": ["2022-08-22", "2022-08-23"],
-/// "open": [5, 6],
+/// "open": [5, 6],
/// "high": [8, 10],
/// "low": [2, 4],
/// "close": [6, 7]
@@ -133,7 +133,7 @@ mod test {
.hover_text("1")
.increasing(Direction::Increasing { line: Line::new() })
.legend_group("legendgroup")
- .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
+ .legend_group_title("Legend Group Title")
.line(Line::new())
.name("ohlc_trace")
.opacity(0.4)
diff --git a/plotly/src/traces/sankey.rs b/plotly/src/traces/sankey.rs
index 1a53d2c8..be96afa2 100644
--- a/plotly/src/traces/sankey.rs
+++ b/plotly/src/traces/sankey.rs
@@ -436,7 +436,7 @@ mod tests {
.name("sankey")
.visible(true)
.legend_rank(1000)
- .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
+ .legend_group_title("Legend Group Title")
.ids(vec!["one"])
.hover_info(HoverInfo::All)
.hover_label(Label::new())
diff --git a/plotly/src/traces/scatter.rs b/plotly/src/traces/scatter.rs
index caa2a076..fe9f5b9c 100644
--- a/plotly/src/traces/scatter.rs
+++ b/plotly/src/traces/scatter.rs
@@ -456,7 +456,7 @@ mod tests {
.hover_template_array(vec!["hover_template"])
.ids(vec!["1"])
.legend_group("legend_group")
- .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
+ .legend_group_title("Legend Group Title")
.line(Line::new())
.marker(Marker::new())
.meta("meta")
diff --git a/plotly/src/traces/scatter3d.rs b/plotly/src/traces/scatter3d.rs
index 66c72c03..412ba1f8 100644
--- a/plotly/src/traces/scatter3d.rs
+++ b/plotly/src/traces/scatter3d.rs
@@ -367,7 +367,7 @@ mod tests {
.ids(vec!["1"])
.legend_group("legend_group")
.legend_rank(1000)
- .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
+ .legend_group_title("Legend Group Title")
.line(Line::new())
.marker(Marker::new())
.meta("meta")
diff --git a/plotly/src/traces/scatter_mapbox.rs b/plotly/src/traces/scatter_mapbox.rs
index 1ee3c164..76348e6a 100644
--- a/plotly/src/traces/scatter_mapbox.rs
+++ b/plotly/src/traces/scatter_mapbox.rs
@@ -311,7 +311,7 @@ mod tests {
.show_legend(true)
.legend_rank(1000)
.legend_group("legend group")
- .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
+ .legend_group_title("Legend Group Title")
.opacity(0.5)
.mode(Mode::LinesText)
.ids(vec!["one"])
diff --git a/plotly/src/traces/scatter_polar.rs b/plotly/src/traces/scatter_polar.rs
index ae3ed244..a9101607 100644
--- a/plotly/src/traces/scatter_polar.rs
+++ b/plotly/src/traces/scatter_polar.rs
@@ -368,7 +368,7 @@ mod tests {
.hover_text_array(vec!["hover_text"])
.ids(vec!["1"])
.legend_group("legend_group")
- .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
+ .legend_group_title("Legend Group Title")
.line(Line::new())
.marker(Marker::new())
.meta("meta")
diff --git a/plotly/src/traces/surface.rs b/plotly/src/traces/surface.rs
index b4110d3b..3f692543 100644
--- a/plotly/src/traces/surface.rs
+++ b/plotly/src/traces/surface.rs
@@ -328,7 +328,7 @@ mod tests {
.hover_text("hover_text")
.hover_text_array(vec!["hover_text_1"])
.legend_group("legend_group")
- .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
+ .legend_group_title("Legend Group Title")
.lighting(Lighting::new())
.light_position(Position::new(0, 0, 0))
.name("surface_trace")
diff --git a/plotly/src/traces/table.rs b/plotly/src/traces/table.rs
new file mode 100644
index 00000000..58545a57
--- /dev/null
+++ b/plotly/src/traces/table.rs
@@ -0,0 +1,177 @@
+//! Table trace
+
+use plotly_derive::FieldSetter;
+use serde::Serialize;
+
+use crate::{
+ color::Color,
+ common::{Font, Line, PlotType, Visible},
+ Trace,
+};
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Clone, Debug, FieldSetter)]
+#[field_setter(box_self, kind = "trace")]
+pub struct Table
+where
+ T: Serialize + Clone + 'static,
+ N: Serialize + Clone + 'static,
+{
+ #[field_setter(default = "PlotType::Table")]
+ r#type: PlotType,
+ /// Sets the trace name. The trace name appear as the legend item and on
+ /// hover.
+ name: Option,
+ #[serde(rename = "columnorder")]
+ /// Determines whether or not this trace is visible. If
+ /// `Visible::LegendOnly`, the trace is not drawn, but can appear as a
+ /// legend item (provided that the legend itself is visible).
+ visible: Option,
+ ///Specifies the rendered order of the data columns; for example, a value
+ /// `2` at position `0`, means that column index `0` in the data will be
+ /// rendered as the, third column, as columns have an index base of
+ /// zero.
+ column_order: Option>,
+ #[serde(rename = "columnwidth")]
+ ///The width of columns expressed as a ratio. Columns fill the available
+ /// width, in proportion of their specified column widths.
+ column_width: Option,
+ ///Header cell values. `values[m][n]` represents the value of the `n`th
+ /// point in column `m`,, therefore the `values[m]` vector length for
+ /// all columns must be the same (longer vectors, will be truncated).
+ /// Each value must be a finite number or a string.
+ header: Option>,
+ ///Cell values. `values[m][n]` represents the value of the `n`th point in
+ /// column `m`,, therefore the `values[m]` vector length for all columns
+ /// must be the same (longer vectors, will be truncated). Each value
+ /// must be a finite number or a string.
+ cells: Option>,
+}
+
+impl Table
+where
+ T: Serialize + Clone + Default + 'static,
+ N: Serialize + Clone + Default + 'static,
+{
+ pub fn new(header: Header, cells: Cells) -> Box {
+ Box::new(Table {
+ header: Some(header),
+ cells: Some(cells),
+ ..Default::default()
+ })
+ }
+}
+
+impl Trace for Table
+where
+ T: Serialize + Clone + 'static,
+ N: Serialize + Clone + 'static,
+{
+ fn to_json(&self) -> String {
+ serde_json::to_string(self).unwrap()
+ }
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Clone, Debug, FieldSetter)]
+pub struct Cells {
+ ///Cell values. `values[m][n]` represents the value of the `n`th point in
+ /// column `m`, therefore the `values[m]` vector length for all columns
+ /// must be the same (longer vectors, will be truncated). Each value
+ /// must be a finite number or a string
+ values: Option>>,
+ ///Prefix for cell values.
+ prefix: Option,
+ ///Suffix for cell values.
+ suffix: Option,
+ height: Option,
+ align: Option,
+ line: Option,
+ ///Sets the cell fill color. It accepts either a specific color,
+ ///or an array of colors or a 2D array of colors
+ fill: Option,
+ font: Option,
+}
+
+impl Cells
+where
+ N: Serialize + Clone + Default + 'static,
+{
+ pub fn new(values: Vec>) -> Self {
+ Cells {
+ values: Some(values),
+ ..Default::default()
+ }
+ }
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Clone, Debug, FieldSetter)]
+pub struct Header {
+ ///Header cell values. `values[m][n]` represents the value of the `n`th
+ /// point in column `m`, therefore the `values[m]` vector length for all
+ /// columns must be the same (longer vectors, will be truncated). Each
+ /// value must be a finite number or a string.
+ values: Option>,
+ ///Prefix for cell values.
+ prefix: Option,
+ ///Suffix for cell values.
+ suffix: Option,
+ height: Option,
+ align: Option,
+ line: Option,
+ ///Sets the cell fill color. It accepts either a specific color,
+ ///or an array of colors or a 2D array of colors
+ fill: Option,
+ font: Option,
+}
+
+impl Header
+where
+ T: Serialize + Clone + Default + 'static,
+{
+ pub fn new(values: Vec) -> Self {
+ Header {
+ values: Some(values),
+ ..Default::default()
+ }
+ }
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Clone, Debug, FieldSetter)]
+pub struct Fill {
+ color: Option>,
+}
+
+impl Fill {
+ pub fn new() -> Self {
+ Default::default()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use serde_json::{json, to_value};
+
+ use super::*;
+
+ #[test]
+ fn test_serialize_table() {
+ let columns = Header::new(vec![String::from("col1"), String::from("col2")]);
+ let values = Cells::new(vec![vec![1, 2], vec![2, 3]]);
+ let trace = Table::new(columns, values);
+
+ let expected = json!({
+ "type": "table",
+ "cells": {
+ "values": [[1, 2], [2, 3]],
+ },
+ "header": {
+ "values": ["col1", "col2"],
+ },
+ });
+
+ assert_eq!(to_value(trace).unwrap(), expected);
+ }
+}
diff --git a/plotly_derive/Cargo.toml b/plotly_derive/Cargo.toml
index 34be9693..b818e8de 100644
--- a/plotly_derive/Cargo.toml
+++ b/plotly_derive/Cargo.toml
@@ -1,20 +1,20 @@
[package]
name = "plotly_derive"
-version = "0.8.4"
+version = "0.9.0"
description = "Internal proc macro crate for Plotly-rs."
authors = ["Ioannis Giagkiozis "]
license = "MIT"
-homepage = "https://github.com/igiagkiozis/plotly"
+homepage = "https://github.com/plotly/plotly.rs"
documentation = "https://docs.rs/plotly"
-repository = "https://github.com/igiagkiozis/plotly"
+repository = "https://github.com/plotly/plotly.rs"
edition = "2018"
keywords = ["plot", "chart", "plotly"]
[dependencies]
-quote = "1.0"
-syn = "1.0"
-proc-macro2 = "1.0"
-darling = "0.14.1"
+quote = "1"
+syn = "2"
+proc-macro2 = "1"
+darling = "0.20"
[lib]
proc-macro = true
diff --git a/plotly_derive/README.md b/plotly_derive/README.md
index 868ba5c3..6c2b83d6 100644
--- a/plotly_derive/README.md
+++ b/plotly_derive/README.md
@@ -1,3 +1,3 @@
# plotly_derive
-This is an internal crate defining procedural macros for [Plotly.rs](https://github.com/igiagkiozis/plotly).
\ No newline at end of file
+This is an internal crate defining procedural macros for [Plotly.rs](https://github.com/plotly/plotly.rs).
\ No newline at end of file
diff --git a/plotly_derive/src/field_setter.rs b/plotly_derive/src/field_setter.rs
index a1642989..ca13d1d3 100644
--- a/plotly_derive/src/field_setter.rs
+++ b/plotly_derive/src/field_setter.rs
@@ -140,6 +140,8 @@ enum FieldType {
OptionString,
OptionNumOrString,
OptionNumOrStringCollection,
+ OptionTitle,
+ OptionLegendGroupTitle,
OptionOther(syn::Type),
}
@@ -202,6 +204,8 @@ impl FieldType {
FieldType::OptionNumOrStringCollection => quote![crate::private::NumOrStringCollection],
FieldType::OptionOther(inner) => quote![#inner],
FieldType::OptionBoxOther(inner) => quote![Box<#inner>],
+ FieldType::OptionTitle => quote![Title],
+ FieldType::OptionLegendGroupTitle => quote![LegendGroupTitle],
}
}
@@ -225,6 +229,8 @@ impl FieldType {
["Box", "Color"] => FieldType::OptionBoxColor,
["Box", ..] => FieldType::OptionBoxOther(types.get(2).cloned().unwrap()),
["Vec", "Box", "Color"] => FieldType::OptionVecBoxColor,
+ ["Title"] => FieldType::OptionTitle,
+ ["LegendGroupTitle"] => FieldType::OptionLegendGroupTitle,
_ => FieldType::OptionOther(types.get(1).cloned().unwrap()),
}
}
@@ -344,6 +350,12 @@ impl FieldReceiver {
quote![value.into()],
quote![],
),
+ FieldType::OptionTitle => (quote![impl Into], quote![value.into()], quote![]),
+ FieldType::OptionLegendGroupTitle => (
+ quote![impl Into],
+ quote![value.into()],
+ quote![],
+ ),
};
struct ModifyEnum {
@@ -492,7 +504,7 @@ impl FieldReceiver {
self.attrs
.iter()
.filter(|attr| {
- attr.path
+ attr.path()
.segments
.first()
.map_or(false, |p| p.ident == name)
diff --git a/plotly_kaleido/Cargo.toml b/plotly_kaleido/Cargo.toml
index c05b3aa5..ed7403a8 100644
--- a/plotly_kaleido/Cargo.toml
+++ b/plotly_kaleido/Cargo.toml
@@ -1,14 +1,14 @@
[package]
name = "plotly_kaleido"
-version = "0.8.4"
+version = "0.9.0"
description = "Additional output format support for plotly using Kaleido"
authors = ["Ioannis Giagkiozis "]
license = "MIT"
readme = "README.md"
workspace = ".."
-homepage = "https://github.com/igiagkiozis/plotly"
+homepage = "https://github.com/plotly/plotly.rs"
documentation = "https://docs.rs/plotly_kaleido"
-repository = "https://github.com/igiagkiozis/plotly"
+repository = "https://github.com/plotly/plotly.rs"
edition = "2018"
keywords = ["plot", "chart", "plotly", "ndarray"]
@@ -17,14 +17,10 @@ exclude = ["target/*", "kaleido/*", "examples/*"]
[dependencies]
serde = { version = "1.0.132", features = ["derive"] }
serde_json = "1.0.73"
-base64 = "0.13.0"
+base64 = "0.22"
dunce = "1.0.2"
-directories = "4.0.1"
-
-[dev-dependencies]
-zip = "0.5.13"
+directories = ">=4, <6"
[build-dependencies]
-zip = "0.5.13"
-directories = "4.0.1"
-
+zip = "2.1"
+directories = ">=4, <6"
diff --git a/plotly_kaleido/README.md b/plotly_kaleido/README.md
index f38830d4..0d0eae99 100644
--- a/plotly_kaleido/README.md
+++ b/plotly_kaleido/README.md
@@ -1,7 +1,7 @@
# plotly_kaleido
-This is an internal crate which implements the `kaleido` feature for [Plotly.rs](https://github.com/igiagkiozis/plotly).
-
-The `kaleido` feature enables `Plot` conversion to the following output formats: `png`, `jpeg`, `webp`, `svg`, `pdf` and `eps`.
+This is an internal crate which implements the `kaleido` feature for [Plotly.rs](https://github.com/plotly/plotly.rs).
-See [examples/](https://github.com/igiagkiozis/plotly/tree/master/examples/kaleido) for usage demonstrations.
\ No newline at end of file
+The `kaleido` feature enables `Plot` conversion to the following output formats: `png`, `jpeg`, `webp`, `svg`, `pdf` and `eps`.
+
+See [examples/](https://github.com/plotly/plotly.rs/tree/main/examples/kaleido) for usage demonstrations.
\ No newline at end of file
diff --git a/plotly_kaleido/build.rs b/plotly_kaleido/build.rs
index 5a4342c5..cf47e82d 100644
--- a/plotly_kaleido/build.rs
+++ b/plotly_kaleido/build.rs
@@ -10,10 +10,14 @@ use std::process::Command;
use directories::ProjectDirs;
-#[cfg(target_os = "linux")]
+#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
const KALEIDO_URL: &str =
"https://github.com/plotly/Kaleido/releases/download/v0.2.1/kaleido_linux_x64.zip";
+#[cfg(all(target_arch = "aarch64", target_os = "linux"))]
+const KALEIDO_URL: &str =
+ "https://github.com/plotly/Kaleido/releases/download/v0.2.1/kaleido_linux_arm64.zip";
+
#[cfg(target_os = "windows")]
const KALEIDO_URL: &str =
"https://github.com/plotly/Kaleido/releases/download/v0.2.1/kaleido_win_x64.zip";
diff --git a/plotly_kaleido/src/lib.rs b/plotly_kaleido/src/lib.rs
index ce0d67cc..f6641c25 100644
--- a/plotly_kaleido/src/lib.rs
+++ b/plotly_kaleido/src/lib.rs
@@ -1,5 +1,5 @@
//! # Plotly Kaleido
-//! Plotly Kaleido implements the `kaleido` feature for [Plotly.rs](https://github.com/igiagkiozis/plotly)
+//! Plotly Kaleido implements the `kaleido` feature for [Plotly.rs](https://github.com/plotly/plotly.rs)
//!
//! The `kaleido` feature enables `Plot` conversion to the following output
//! formats: png, jpeg, webp, svg, pdf and eps. It has the added benefit over
@@ -16,6 +16,7 @@ use std::io::BufReader;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
+use base64::{engine::general_purpose, Engine as _};
use directories::ProjectDirs;
use serde::{Deserialize, Serialize};
use serde_json::Value;
@@ -137,7 +138,7 @@ impl Kaleido {
let p = p.to_str().unwrap();
let p = String::from(p);
- let process = Command::new(p.as_str())
+ let mut process = Command::new(p.as_str())
.current_dir(self.cmd_path.parent().unwrap())
.args([
"plotly",
@@ -145,6 +146,7 @@ impl Kaleido {
"--allow-file-access-from-files",
"--disable-breakpad",
"--disable-dev-shm-usage",
+ "--disable-software-rasterizer",
"--single-process",
])
.stdin(Stdio::piped())
@@ -155,20 +157,20 @@ impl Kaleido {
{
let plot_data = PlotData::new(plotly_data, format, width, height, scale).to_json();
- let mut process_stdin = process.stdin.unwrap();
+ let mut process_stdin = process.stdin.take().unwrap();
process_stdin
.write_all(plot_data.as_bytes())
.expect("couldn't write to Kaleido stdin");
process_stdin.flush()?;
}
- let output_lines = BufReader::new(process.stdout.unwrap()).lines();
- for line in output_lines.flatten() {
+ let output_lines = BufReader::new(process.stdout.take().unwrap()).lines();
+ for line in output_lines.map_while(Result::ok) {
let res = KaleidoResult::from(line.as_str());
if let Some(image_data) = res.result {
let data: Vec = match format {
"svg" | "eps" => image_data.as_bytes().to_vec(),
- _ => base64::decode(image_data).unwrap(),
+ _ => general_purpose::STANDARD.decode(image_data).unwrap(),
};
let mut file = File::create(dst.as_path())?;
file.write_all(&data)?;
@@ -237,6 +239,8 @@ mod tests {
assert_eq!(to_value(kaleido_data).unwrap(), expected);
}
+ // This seems to fail unpredictably on MacOs.
+ #[cfg(target_os = "linux")]
#[test]
fn test_save_png() {
let test_plot = create_test_plot();
@@ -247,6 +251,8 @@ mod tests {
assert!(std::fs::remove_file(dst.as_path()).is_ok());
}
+ // This seems to fail unpredictably on MacOs.
+ #[cfg(target_os = "linux")]
#[test]
fn test_save_jpeg() {
let test_plot = create_test_plot();
@@ -257,6 +263,8 @@ mod tests {
assert!(std::fs::remove_file(dst.as_path()).is_ok());
}
+ // This seems to fail unpredictably on MacOs.
+ #[cfg(target_os = "linux")]
#[test]
fn test_save_webp() {
let test_plot = create_test_plot();
@@ -267,6 +275,8 @@ mod tests {
assert!(std::fs::remove_file(dst.as_path()).is_ok());
}
+ // This seems to fail unpredictably on MacOs.
+ #[cfg(target_os = "linux")]
#[test]
fn test_save_svg() {
let test_plot = create_test_plot();
@@ -277,6 +287,8 @@ mod tests {
assert!(std::fs::remove_file(dst.as_path()).is_ok());
}
+ // This seems to fail unpredictably on MacOs.
+ #[cfg(target_os = "linux")]
#[test]
fn test_save_pdf() {
let test_plot = create_test_plot();
@@ -287,6 +299,7 @@ mod tests {
assert!(std::fs::remove_file(dst.as_path()).is_ok());
}
+ // This doesn't work for some reason
#[test]
#[ignore]
fn test_save_eps() {