Skip to main content

ggplot_rs/coord/
mod.rs

1pub mod cartesian;
2pub mod fixed;
3pub mod flip;
4pub mod map;
5pub mod polar;
6pub mod radar;
7#[cfg(feature = "sf")]
8pub mod sf;
9pub mod trans;
10
11use crate::render::Rect;
12
13/// A trained axis domain together with the normalized panel positions of its
14/// endpoints. `pmin`/`pmax` are `scale.map(min)`/`scale.map(max)`, so a coord can
15/// invert the (linear, expanded) scale mapping exactly: given a normalized
16/// position `n`, the data value is `min + (n - pmin)/(pmax - pmin) * (max - min)`.
17#[derive(Clone, Copy, Debug)]
18pub struct AxisSpan {
19    pub min: f64,
20    pub max: f64,
21    pub pmin: f64,
22    pub pmax: f64,
23}
24
25/// Trait for coordinate systems.
26pub trait Coord: Send + Sync {
27    /// Transform normalized (0..1, 0..1) coordinates to pixel coordinates.
28    fn transform(&self, point: (f64, f64), plot_area: &Rect) -> (f64, f64);
29
30    /// The panel aspect (height / width) this coord letterboxes to, if any.
31    /// `facet_wrap` sizes its panels to it (so every map panel is filled
32    /// instead of letterboxed in an equal, arbitrary cell). Default `None`.
33    fn fixed_aspect(&self) -> Option<f64> {
34        None
35    }
36
37    /// The rect inside `plot_area` the normalized data window maps onto,
38    /// for a coord that letterboxes (`coord_sf`); `None` when it is the
39    /// whole plot area or not a rectangle. Default `None`.
40    fn data_rect(&self, _plot_area: &Rect) -> Option<Rect> {
41        None
42    }
43
44    /// Whether to draw grid lines.
45    fn gridlines(&self) -> bool {
46        true
47    }
48
49    /// Whether this coordinate system flips X and Y.
50    fn is_flipped(&self) -> bool {
51        false
52    }
53
54    /// Whether this is a non-linear polar system (bars become radial sectors,
55    /// so geoms that draw rectangles must tessellate their edges into an arc).
56    fn is_polar(&self) -> bool {
57        false
58    }
59
60    /// For a polar system, whether the *x* aesthetic maps to angle (`theta="x"`).
61    /// When false (`theta="y"`, pies), the *y* aesthetic is the angle, so sector
62    /// tessellation must sweep the y edge instead of the x edge.
63    fn polar_theta_is_x(&self) -> bool {
64        true
65    }
66
67    /// A polar coord's guide geometry (#411): `None` for every other coord.
68    fn polar_guide(&self, _plot_area: &crate::render::Rect) -> Option<PolarGuide> {
69        None
70    }
71
72    /// Zoom limits for x-axis (data coordinates). Clips viewport without filtering data.
73    fn zoom_x(&self) -> Option<(f64, f64)> {
74        None
75    }
76
77    /// Zoom limits for y-axis (data coordinates). Clips viewport without filtering data.
78    fn zoom_y(&self) -> Option<(f64, f64)> {
79        None
80    }
81
82    /// Supply the trained x/y axis spans after scale training. Coordinate systems
83    /// that warp the axis (e.g. `coord_trans`) need this to map a normalized
84    /// position back to a data value. Default is a no-op.
85    fn set_domains(&mut self, _x: Option<AxisSpan>, _y: Option<AxisSpan>) {}
86
87    /// Radar coordinates: straight segments between spokes, closed paths, and
88    /// ring/spoke guides instead of cartesian axes. Default false.
89    fn is_radar(&self) -> bool {
90        false
91    }
92
93    /// Inspect / adjust the trained scales once training is complete (e.g.
94    /// `coord_radar` anchors the radius at 0 and counts its spokes). Default:
95    /// no-op.
96    fn train_scales(&mut self, _scales: &mut crate::scale::ScaleSet) {}
97
98    /// Whether bars on a discrete axis keep the band padding (10 % of the
99    /// panel split across the ends). `coord_polar().expand(false)` turns it
100    /// off so a full-width pie closes at the centre. Default true.
101    fn discrete_expand(&self) -> bool {
102        true
103    }
104
105    /// Whether `Theme::axis_text_x_fit`'s `Reserve` mode may grow the panel
106    /// margin for this coordinate system (B3-1). `transform` isn't a simple
107    /// linear per-axis map for polar/radar (angle) or sf (letterboxed,
108    /// non-degree units), so reserving a left/right margin from a label
109    /// bbox would be meaningless there; those coords still get the
110    /// draw-time `Shift` fallback in `draw_x_axis`. Default true.
111    fn supports_axis_reserve(&self) -> bool {
112        true
113    }
114}
115
116/// What [`draw_polar_guides`](crate::guide::polar::draw_polar_guides) needs
117/// from a polar coord (#411).
118#[derive(Clone, Copy, Debug)]
119pub struct PolarGuide {
120    /// The pole, in pixels.
121    pub centre: (f64, f64),
122    /// The swept angle covers the whole circle.
123    pub full_circle: bool,
124    /// Put the r axis along the start spoke instead of the panel's left
125    /// edge (`r.axis.inside`).
126    pub r_axis_inside: bool,
127    /// Turn theta labels to follow the circle (`rotate.angle`).
128    pub rotate_labels: bool,
129}