use colorgrad::Gradient;
use plotters::{
prelude::*,
style::colors::colormaps::{BlackWhite, Bone, MandelbrotHSL, ViridisRGB, VulcanoHSL},
};
pub enum ColorMaps {
Viridis,
Plasma,
Inferno,
Magma,
Turbo,
Cividis,
Warm,
Cool,
CubehelixDefault,
Rainbow,
Jet,
Spectral,
Bone(Bone),
Mandelbrot(MandelbrotHSL),
BlackWhite(BlackWhite),
Volcano(VulcanoHSL),
#[deprecated(note = "Use ColorMaps::Viridis instead")]
ViridisRGB(ViridisRGB),
}
impl Clone for ColorMaps {
fn clone(&self) -> Self {
match self {
ColorMaps::Viridis => ColorMaps::Viridis,
ColorMaps::Plasma => ColorMaps::Plasma,
ColorMaps::Inferno => ColorMaps::Inferno,
ColorMaps::Magma => ColorMaps::Magma,
ColorMaps::Turbo => ColorMaps::Turbo,
ColorMaps::Cividis => ColorMaps::Cividis,
ColorMaps::Warm => ColorMaps::Warm,
ColorMaps::Cool => ColorMaps::Cool,
ColorMaps::CubehelixDefault => ColorMaps::CubehelixDefault,
ColorMaps::Rainbow => ColorMaps::Rainbow,
ColorMaps::Jet => ColorMaps::Jet,
ColorMaps::Spectral => ColorMaps::Spectral,
ColorMaps::Bone(_) => ColorMaps::Bone(Bone),
ColorMaps::Mandelbrot(_) => ColorMaps::Mandelbrot(MandelbrotHSL),
ColorMaps::BlackWhite(_) => ColorMaps::BlackWhite(BlackWhite),
ColorMaps::Volcano(_) => ColorMaps::Volcano(VulcanoHSL),
#[allow(deprecated)]
ColorMaps::ViridisRGB(_) => ColorMaps::Viridis,
}
}
}
impl ColorMaps {
pub fn map(&self, value: f32) -> RGBColor {
let clamped_value = value.max(0.0).min(1.0);
match self {
ColorMaps::Viridis => {
let grad = colorgrad::preset::viridis();
let color = grad.at(clamped_value);
RGBColor(
(color.r * 255.0) as u8,
(color.g * 255.0) as u8,
(color.b * 255.0) as u8,
)
}
ColorMaps::Plasma => {
let grad = colorgrad::preset::plasma();
let color = grad.at(clamped_value);
RGBColor(
(color.r * 255.0) as u8,
(color.g * 255.0) as u8,
(color.b * 255.0) as u8,
)
}
ColorMaps::Inferno => {
let grad = colorgrad::preset::inferno();
let color = grad.at(clamped_value);
RGBColor(
(color.r * 255.0) as u8,
(color.g * 255.0) as u8,
(color.b * 255.0) as u8,
)
}
ColorMaps::Magma => {
let grad = colorgrad::preset::magma();
let color = grad.at(clamped_value);
RGBColor(
(color.r * 255.0) as u8,
(color.g * 255.0) as u8,
(color.b * 255.0) as u8,
)
}
ColorMaps::Turbo => {
let grad = colorgrad::preset::turbo();
let color = grad.at(clamped_value);
RGBColor(
(color.r * 255.0) as u8,
(color.g * 255.0) as u8,
(color.b * 255.0) as u8,
)
}
ColorMaps::Cividis => {
let grad = colorgrad::preset::cividis();
let color = grad.at(clamped_value);
RGBColor(
(color.r * 255.0) as u8,
(color.g * 255.0) as u8,
(color.b * 255.0) as u8,
)
}
ColorMaps::Warm => {
let grad = colorgrad::preset::warm();
let color = grad.at(clamped_value);
RGBColor(
(color.r * 255.0) as u8,
(color.g * 255.0) as u8,
(color.b * 255.0) as u8,
)
}
ColorMaps::Cool => {
let grad = colorgrad::preset::cool();
let color = grad.at(clamped_value);
RGBColor(
(color.r * 255.0) as u8,
(color.g * 255.0) as u8,
(color.b * 255.0) as u8,
)
}
ColorMaps::CubehelixDefault => {
let grad = colorgrad::preset::cubehelix_default();
let color = grad.at(clamped_value);
RGBColor(
(color.r * 255.0) as u8,
(color.g * 255.0) as u8,
(color.b * 255.0) as u8,
)
}
ColorMaps::Rainbow => {
let grad = colorgrad::preset::rainbow();
let color = grad.at(clamped_value);
RGBColor(
(color.r * 255.0) as u8,
(color.g * 255.0) as u8,
(color.b * 255.0) as u8,
)
}
ColorMaps::Jet => {
let grad = colorgrad::preset::sinebow();
let color = grad.at(clamped_value);
RGBColor(
(color.r * 255.0) as u8,
(color.g * 255.0) as u8,
(color.b * 255.0) as u8,
)
}
ColorMaps::Spectral => {
let grad = colorgrad::preset::spectral();
let color = grad.at(clamped_value);
RGBColor(
(color.r * 255.0) as u8,
(color.g * 255.0) as u8,
(color.b * 255.0) as u8,
)
}
ColorMaps::Bone(c) => c.get_color(clamped_value),
ColorMaps::Mandelbrot(c) => convert_hsl_to_rgb(c.get_color(clamped_value)),
ColorMaps::BlackWhite(c) => c.get_color(clamped_value),
ColorMaps::Volcano(c) => convert_hsl_to_rgb(c.get_color(clamped_value)),
#[allow(deprecated)]
ColorMaps::ViridisRGB(c) => c.get_color(clamped_value),
}
}
}
impl std::fmt::Debug for ColorMaps {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ColorMaps::Viridis => write!(f, "Viridis"),
ColorMaps::Plasma => write!(f, "Plasma"),
ColorMaps::Inferno => write!(f, "Inferno"),
ColorMaps::Magma => write!(f, "Magma"),
ColorMaps::Turbo => write!(f, "Turbo"),
ColorMaps::Cividis => write!(f, "Cividis"),
ColorMaps::Warm => write!(f, "Warm"),
ColorMaps::Cool => write!(f, "Cool"),
ColorMaps::CubehelixDefault => write!(f, "CubehelixDefault"),
ColorMaps::Rainbow => write!(f, "Rainbow"),
ColorMaps::Jet => write!(f, "Jet"),
ColorMaps::Spectral => write!(f, "Spectral"),
ColorMaps::Bone(_) => write!(f, "Bone"),
ColorMaps::Mandelbrot(_) => write!(f, "Mandelbrot"),
ColorMaps::BlackWhite(_) => write!(f, "BlackWhite"),
ColorMaps::Volcano(_) => write!(f, "Volcano"),
#[allow(deprecated)]
ColorMaps::ViridisRGB(_) => write!(f, "ViridisRGB"),
}
}
}
impl Default for ColorMaps {
fn default() -> Self {
ColorMaps::Viridis
}
}
fn convert_hsl_to_rgb(hsl: HSLColor) -> RGBColor {
let (r, g, b) = hsl.rgb();
RGBColor(r, g, b)
}
pub struct CustomColorMap;
macro_rules! def_linear_colormap{
($color_scale_name:ident, $color_type:ident, $doc:expr, $(($($color_value:expr),+)),*) => {
#[doc = $doc]
pub struct $color_scale_name;
impl $color_scale_name {
const COLORS: [$color_type; $crate::count!($(($($color_value:expr),+))*)] = $crate::define_colors_from_list_of_values_or_directly!{$color_type, $(($($color_value),+)),*};
}
$crate::implement_linear_interpolation_color_map!{$color_scale_name, $color_type}
};
($color_scale_name:ident, $color_type:ident, $doc:expr, $($color_complete:tt),+) => {
#[doc = $doc]
pub struct $color_scale_name;
impl $color_scale_name {
const COLORS: [$color_type; $crate::count!($($color_complete)*)] = $crate::define_colors_from_list_of_values_or_directly!{$($color_complete),+};
}
$crate::implement_linear_interpolation_color_map!{$color_scale_name, $color_type}
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! implement_linear_interpolation_color_map {
($color_scale_name:ident, $color_type:ident) => {
impl<FloatType: std::fmt::Debug + num_traits::Float + num_traits::FromPrimitive + num_traits::ToPrimitive>
ColorMap<$color_type, FloatType> for $color_scale_name
{
fn get_color_normalized(
&self,
h: FloatType,
min: FloatType,
max: FloatType,
) -> $color_type {
let (
relative_difference,
index_lower,
index_upper
) = calculate_relative_difference_index_lower_upper(
h,
min,
max,
Self::COLORS.len()
);
$crate::calculate_new_color_value!(
relative_difference,
Self::COLORS,
index_upper,
index_lower,
$color_type
)
}
}
impl $color_scale_name {
#[doc = "Get color value from `"]
#[doc = stringify!($color_scale_name)]
#[doc = "` by supplying a parameter 0.0 <= h <= 1.0"]
pub fn get_color<FloatType: std::fmt::Debug + num_traits::Float + num_traits::FromPrimitive + num_traits::ToPrimitive>(
h: FloatType,
) -> $color_type {
let color_scale = $color_scale_name {};
color_scale.get_color(h)
}
#[doc = "Get color value from `"]
#[doc = stringify!($color_scale_name)]
#[doc = "` by supplying lower and upper bounds min, max and a parameter h where min <= h <= max"]
pub fn get_color_normalized<
FloatType: std::fmt::Debug + num_traits::Float + num_traits::FromPrimitive + num_traits::ToPrimitive,
>(
h: FloatType,
min: FloatType,
max: FloatType,
) -> $color_type {
let color_scale = $color_scale_name {};
color_scale.get_color_normalized(h, min, max)
}
}
};
}
pub fn calculate_relative_difference_index_lower_upper<
FloatType: num_traits::Float + num_traits::FromPrimitive + num_traits::ToPrimitive,
>(
h: FloatType,
min: FloatType,
max: FloatType,
n_steps: usize,
) -> (FloatType, usize, usize) {
let h = num_traits::clamp(h, min, max);
let t = (h - min) / (max - min);
let approximate_index = t
* (FloatType::from_usize(n_steps).expect("should be able to get a float type from usize")
- FloatType::one())
.max(FloatType::zero());
let index_lower = approximate_index
.floor()
.to_usize()
.expect("should be able to get the lower index");
let index_upper = approximate_index
.ceil()
.to_usize()
.expect("should be able to get the upper index");
let relative_difference = approximate_index.ceil() - approximate_index;
(relative_difference, index_lower, index_upper)
}
macro_rules! define_colors_from_list_of_values_or_directly{
($color_type:ident, $(($($color_value:expr),+)),+) => {
[$($color_type($($color_value),+)),+]
};
($($color_complete:tt),+) => {
[$($color_complete),+]
};
}