Skip to main content

ironlab_ir/
axes.rs

1//! Axes, their coordinate axes, projection and legend.
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use crate::artist::Artist;
7use crate::ids::NodeId;
8use crate::text::Text;
9
10/// A plotting region placed in one or more cells of the figure's tile layout.
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
12pub struct Axes {
13    /// The node identifier of the axes, unique within the figure.
14    pub id: NodeId,
15    /// The cells of the figure's tile layout that the axes occupies.
16    pub cell: Cell,
17    /// Whether the axes is two- or three-dimensional, with its 3D view.
18    pub projection: Projection,
19    /// The title drawn above the axes.
20    pub title: Option<Text>,
21    /// The horizontal axis in 2D, or the first horizontal axis in 3D.
22    pub x: Axis,
23    /// The vertical axis in 2D, or the second horizontal axis in 3D.
24    pub y: Axis,
25    /// The vertical axis in 3D; ignored by 2D axes.
26    pub z: Axis,
27    /// Whether the full outline of the plot box is drawn, rather than only the
28    /// edges that carry tick labels.
29    #[serde(rename = "box")]
30    pub box_: bool,
31    /// The colormap used by colormapped artists in this axes.
32    pub colormap: ColormapName,
33    /// The data values mapped to the first and last colours of the colormap.
34    pub clim: Limits,
35    /// The legend, or `null` when no legend is shown.
36    pub legend: Option<Legend>,
37    /// The artists drawn in this axes, in drawing order.
38    pub artists: Vec<Artist>,
39}
40
41impl Default for Axes {
42    fn default() -> Self {
43        Self {
44            id: NodeId::default(),
45            cell: Cell::default(),
46            projection: Projection::TwoD,
47            title: None,
48            x: Axis::default(),
49            y: Axis::default(),
50            z: Axis::default(),
51            box_: true,
52            colormap: ColormapName::Viridis,
53            clim: Limits::Auto,
54            legend: None,
55            artists: Vec::new(),
56        }
57    }
58}
59
60/// A rectangular block of cells in the figure's tile layout.
61///
62/// Rows and columns are numbered from zero, starting at the top-left cell.
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
64pub struct Cell {
65    /// The zero-based index of the top row occupied.
66    pub row: u32,
67    /// The zero-based index of the leftmost column occupied.
68    pub col: u32,
69    /// The number of rows occupied; at least one.
70    pub row_span: u32,
71    /// The number of columns occupied; at least one.
72    pub col_span: u32,
73}
74
75impl Default for Cell {
76    fn default() -> Self {
77        Self {
78            row: 0,
79            col: 0,
80            row_span: 1,
81            col_span: 1,
82        }
83    }
84}
85
86/// The projection of an axes.
87#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
88#[serde(tag = "type", rename_all = "snake_case")]
89pub enum Projection {
90    /// A two-dimensional Cartesian axes.
91    #[default]
92    TwoD,
93    /// A three-dimensional Cartesian axes viewed through an orthographic camera.
94    ThreeD {
95        /// The camera view.
96        view3d: View3d,
97    },
98}
99
100/// The orthographic camera view of a three-dimensional axes.
101#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
102pub struct View3d {
103    /// The rotation about the vertical axis in degrees, measured counterclockwise
104    /// from the negative y axis when viewed from above.
105    pub azimuth_deg: f64,
106    /// The angle of the view direction above the x-y plane in degrees, from -90 to 90.
107    pub elevation_deg: f64,
108    /// The magnification of the projected box, where 1 fits the box to the plot area.
109    pub zoom: f64,
110    /// The horizontal offset of the projected box as a fraction of the plot area's
111    /// width, increasing to the right.
112    pub pan_x: f64,
113    /// The vertical offset of the projected box as a fraction of the plot area's
114    /// height, increasing downwards.
115    pub pan_y: f64,
116}
117
118impl Default for View3d {
119    fn default() -> Self {
120        Self {
121            azimuth_deg: -37.5,
122            elevation_deg: 30.0,
123            zoom: 1.0,
124            pan_x: 0.0,
125            pan_y: 0.0,
126        }
127    }
128}
129
130/// One coordinate axis of an axes.
131#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
132pub struct Axis {
133    /// The axis label.
134    pub label: Option<Text>,
135    /// The mapping from data values to positions along the axis.
136    pub scale: Scale,
137    /// The range of data values shown along the axis.
138    pub limits: Limits,
139    /// Whether grid lines are drawn at the major ticks of this axis.
140    pub grid: bool,
141}
142
143/// The mapping from data values to positions along an axis.
144#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
145#[serde(rename_all = "snake_case")]
146pub enum Scale {
147    /// Positions are proportional to values.
148    #[default]
149    Linear,
150    /// Positions are proportional to the base-10 logarithm of values; non-positive
151    /// values are not drawn.
152    Log,
153}
154
155/// A range of data values.
156#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
157#[serde(tag = "type", rename_all = "snake_case")]
158pub enum Limits {
159    /// The range is computed from the data. Axis limits are rounded outwards to ticks, except where only the grid of a
160    /// contour or surface sets the x or y range, which then ends exactly at the grid.
161    #[default]
162    Auto,
163    /// The range is fixed.
164    Manual {
165        /// The lower bound of the range.
166        min: f64,
167        /// The upper bound of the range.
168        max: f64,
169    },
170}
171
172/// The name of a colormap.
173#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
174#[serde(rename_all = "snake_case")]
175pub enum ColormapName {
176    /// The perceptually uniform blue–green–yellow colormap.
177    #[default]
178    Viridis,
179    /// The perceptually uniform blue–yellow colormap designed for colour-vision
180    /// deficiency.
181    Cividis,
182    /// The perceptually uniform black–purple–cream colormap.
183    Magma,
184    /// The perceptually uniform black–red–yellow colormap.
185    Inferno,
186    /// The perceptually uniform blue–purple–yellow colormap.
187    Plasma,
188    /// The diverging blue–white–red colormap.
189    Coolwarm,
190    /// The linear black–white colormap.
191    Gray,
192}
193
194/// A legend listing the artists of an axes that have a display name.
195#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
196pub struct Legend {
197    /// Where the legend is placed inside the plot area.
198    pub location: LegendLocation,
199    /// Whether the legend is drawn with a background and outline.
200    pub boxed: bool,
201}
202
203impl Default for Legend {
204    fn default() -> Self {
205        Self {
206            location: LegendLocation::NorthEast,
207            boxed: true,
208        }
209    }
210}
211
212/// The placement of a legend inside the plot area.
213#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
214#[serde(rename_all = "snake_case")]
215pub enum LegendLocation {
216    /// The top-right corner.
217    #[default]
218    NorthEast,
219    /// The top-left corner.
220    NorthWest,
221    /// The bottom-right corner.
222    SouthEast,
223    /// The bottom-left corner.
224    SouthWest,
225    /// The centre of the top edge.
226    North,
227    /// The centre of the bottom edge.
228    South,
229    /// The centre of the right edge.
230    East,
231    /// The centre of the left edge.
232    West,
233    /// The corner that overlaps the least data.
234    Best,
235}