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 /// Whether to draw grid lines.
30 fn gridlines(&self) -> bool {
31 true
32 }
33
34 /// Whether this coordinate system flips X and Y.
35 fn is_flipped(&self) -> bool {
36 false
37 }
38
39 /// Whether this is a non-linear polar system (bars become radial sectors,
40 /// so geoms that draw rectangles must tessellate their edges into an arc).
41 fn is_polar(&self) -> bool {
42 false
43 }
44
45 /// For a polar system, whether the *x* aesthetic maps to angle (`theta="x"`).
46 /// When false (`theta="y"`, pies), the *y* aesthetic is the angle, so sector
47 /// tessellation must sweep the y edge instead of the x edge.
48 fn polar_theta_is_x(&self) -> bool {
49 true
50 }
51
52 /// Zoom limits for x-axis (data coordinates). Clips viewport without filtering data.
53 fn zoom_x(&self) -> Option<(f64, f64)> {
54 None
55 }
56
57 /// Zoom limits for y-axis (data coordinates). Clips viewport without filtering data.
58 fn zoom_y(&self) -> Option<(f64, f64)> {
59 None
60 }
61
62 /// Supply the trained x/y axis spans after scale training. Coordinate systems
63 /// that warp the axis (e.g. `coord_trans`) need this to map a normalized
64 /// position back to a data value. Default is a no-op.
65 fn set_domains(&mut self, _x: Option<AxisSpan>, _y: Option<AxisSpan>) {}
66
67 /// Radar coordinates: straight segments between spokes, closed paths, and
68 /// ring/spoke guides instead of cartesian axes. Default false.
69 fn is_radar(&self) -> bool {
70 false
71 }
72
73 /// Inspect / adjust the trained scales once training is complete (e.g.
74 /// `coord_radar` anchors the radius at 0 and counts its spokes). Default:
75 /// no-op.
76 fn train_scales(&mut self, _scales: &mut crate::scale::ScaleSet) {}
77}