#[derive(Clone, Debug, Default)]
pub struct GuideLegend {
pub title: Option<String>,
pub ncol: Option<usize>,
pub nrow: Option<usize>,
pub reverse: bool,
pub byrow: bool,
pub max_rows: Option<usize>,
pub hidden: Vec<crate::aes::Aesthetic>,
pub titles: Vec<(crate::aes::Aesthetic, String)>,
pub overrides: Vec<(crate::aes::Aesthetic, KeyOverride)>,
pub reversed: Vec<crate::aes::Aesthetic>,
pub colourbars: Vec<(crate::aes::Aesthetic, GuideColourbar)>,
pub bins: Vec<(crate::aes::Aesthetic, GuideBins)>,
pub positions: Vec<(crate::aes::Aesthetic, crate::theme::LegendPosition)>,
pub custom: Vec<GuideCustom>,
pub plain_keys: Vec<crate::aes::Aesthetic>,
#[doc(hidden)]
pub layer_keys: Vec<crate::guide::key::LayerKey>,
}
impl GuideLegend {
pub fn new() -> Self {
Self::default()
}
pub(crate) fn colourbar_for(&self, aes: &crate::aes::Aesthetic) -> GuideColourbar {
self.colourbars
.iter()
.find(|(a, _)| a == aes)
.map(|(_, c)| c.clone())
.unwrap_or_default()
}
pub(crate) fn bins_for(
&self,
aes: &crate::aes::Aesthetic,
scale: &dyn crate::scale::Scale,
) -> Option<GuideBins> {
scale.bin_edge_labels()?;
if self.plain_keys.contains(aes) {
return None;
}
Some(
self.bins
.iter()
.find(|(a, _)| a == aes)
.map(|(_, b)| b.clone())
.unwrap_or_default(),
)
}
pub(crate) fn position_for<'a>(
&'a self,
aes: &crate::aes::Aesthetic,
main: &'a crate::theme::LegendPosition,
) -> &'a crate::theme::LegendPosition {
self.positions
.iter()
.find(|(a, _)| a == aes)
.map_or(main, |(_, p)| p)
}
pub(crate) fn is_reversed(&self, aes: &crate::aes::Aesthetic) -> bool {
self.reverse != self.reversed.contains(aes)
}
pub fn with_title(mut self, title: &str) -> Self {
self.title = Some(title.to_string());
self
}
pub fn with_ncol(mut self, ncol: usize) -> Self {
self.ncol = Some(ncol);
self
}
pub fn with_nrow(mut self, nrow: usize) -> Self {
self.nrow = Some(nrow);
self
}
pub fn reverse(mut self) -> Self {
self.reverse = true;
self
}
pub fn with_byrow(mut self, byrow: bool) -> Self {
self.byrow = byrow;
self
}
pub fn with_max_rows(mut self, max_rows: usize) -> Self {
self.max_rows = Some(max_rows);
self
}
pub(crate) fn validated(&self, n: usize, warnings: &mut Vec<String>) -> GuideLegend {
let mut out = self.clone();
if out.nrow == Some(0) {
warnings.push("guide_legend: nrow(0) is invalid; using the default".to_string());
out.nrow = None;
}
if out.ncol == Some(0) {
warnings.push("guide_legend: ncol(0) is invalid; using the default".to_string());
out.ncol = None;
}
if let (Some(nrow), Some(ncol)) = (out.nrow, out.ncol) {
if nrow.saturating_mul(ncol) < n {
warnings.push(format!(
"guide_legend: nrow({nrow}) x ncol({ncol}) = {} is smaller than {n} keys; dropping ncol",
nrow.saturating_mul(ncol)
));
out.ncol = None;
}
}
if out.max_rows == Some(0) {
warnings.push("guide_legend: max_rows(0) is invalid; using 1".to_string());
out.max_rows = Some(1);
}
out
}
}
pub type CustomGuideDraw = std::sync::Arc<
dyn Fn(
&mut dyn crate::render::backend::DrawBackend,
&crate::render::Rect,
) -> Result<(), crate::render::RenderError>
+ Send
+ Sync,
>;
#[derive(Clone)]
pub struct GuideCustom {
pub title: Option<String>,
pub width: f64,
pub height: f64,
pub draw: CustomGuideDraw,
}
impl GuideCustom {
pub fn new<F>(width: f64, height: f64, draw: F) -> Self
where
F: Fn(
&mut dyn crate::render::backend::DrawBackend,
&crate::render::Rect,
) -> Result<(), crate::render::RenderError>
+ Send
+ Sync
+ 'static,
{
GuideCustom {
title: None,
width: width.max(0.0),
height: height.max(0.0),
draw: std::sync::Arc::new(draw),
}
}
pub fn with_title(mut self, title: &str) -> Self {
self.title = Some(title.to_string());
self
}
pub(crate) fn title_offset(&self, theme: &crate::theme::Theme) -> f64 {
match &self.title {
Some(t) if !t.is_empty() => theme.legend_title.size + 4.0,
_ => 0.0,
}
}
}
impl std::fmt::Debug for GuideCustom {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GuideCustom")
.field("title", &self.title)
.field("width", &self.width)
.field("height", &self.height)
.finish_non_exhaustive()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct GuideBins {
pub show_limits: bool,
pub axis: bool,
}
impl Default for GuideBins {
fn default() -> Self {
GuideBins {
show_limits: false,
axis: true,
}
}
}
impl GuideBins {
pub fn new() -> Self {
Self::default()
}
pub fn with_show_limits(mut self, on: bool) -> Self {
self.show_limits = on;
self
}
pub fn with_axis(mut self, on: bool) -> Self {
self.axis = on;
self
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ColourbarDisplay {
#[default]
Gradient,
Rectangles,
}
#[derive(Clone, Debug, PartialEq)]
pub struct GuideColourbar {
pub bar_width: Option<f64>,
pub bar_height: Option<f64>,
pub nbin: usize,
pub display: ColourbarDisplay,
pub frame: Option<bool>,
pub ticks: bool,
pub steps: bool,
pub even_steps: bool,
pub show_limits: bool,
}
impl Default for GuideColourbar {
fn default() -> Self {
GuideColourbar {
bar_width: None,
bar_height: None,
nbin: 50,
display: ColourbarDisplay::Gradient,
frame: None,
ticks: true,
steps: false,
even_steps: true,
show_limits: false,
}
}
}
impl GuideColourbar {
pub fn new() -> Self {
Self::default()
}
pub fn steps() -> Self {
GuideColourbar {
steps: true,
ticks: false,
..Self::default()
}
}
pub fn with_bar_width(mut self, px: f64) -> Self {
self.bar_width = Some(px);
self
}
pub fn with_bar_height(mut self, px: f64) -> Self {
self.bar_height = Some(px);
self
}
pub fn with_nbin(mut self, nbin: usize) -> Self {
self.nbin = nbin.max(1);
self
}
pub fn with_display(mut self, display: ColourbarDisplay) -> Self {
self.display = display;
self
}
pub fn with_frame(mut self, frame: bool) -> Self {
self.frame = Some(frame);
self
}
pub fn with_ticks(mut self, ticks: bool) -> Self {
self.ticks = ticks;
self
}
pub fn with_even_steps(mut self, even: bool) -> Self {
self.even_steps = even;
self
}
pub fn with_show_limits(mut self, show: bool) -> Self {
self.show_limits = show;
self
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct KeyOverride {
pub color: Option<(u8, u8, u8)>,
pub fill: Option<(u8, u8, u8)>,
pub shape: Option<crate::render::backend::PointShape>,
pub linetype: Option<crate::render::backend::Linetype>,
pub size: Option<f64>,
pub alpha: Option<f64>,
pub linewidth: Option<f64>,
}
impl KeyOverride {
pub fn new() -> Self {
Self::default()
}
pub fn color(mut self, c: (u8, u8, u8)) -> Self {
self.color = Some(c);
self
}
pub fn fill(mut self, c: (u8, u8, u8)) -> Self {
self.fill = Some(c);
self
}
pub fn shape(mut self, s: crate::render::backend::PointShape) -> Self {
self.shape = Some(s);
self
}
pub fn linetype(mut self, l: crate::render::backend::Linetype) -> Self {
self.linetype = Some(l);
self
}
pub fn size(mut self, v: f64) -> Self {
self.size = Some(v);
self
}
pub fn alpha(mut self, v: f64) -> Self {
self.alpha = Some(v.clamp(0.0, 1.0));
self
}
pub fn linewidth(mut self, v: f64) -> Self {
self.linewidth = Some(v);
self
}
}