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