Contents
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
use super::palette::Palette;
use super::ShapeStyle;
use plotters_backend::{BackendColor, BackendStyle};
use std::marker::PhantomData;
/// Any color representation
pub trait Color {
/// Normalize this color representation to the backend color
fn to_backend_color(&self) -> BackendColor;
/// Convert the RGB representation to the standard RGB tuple
#[inline(always)]
fn rgb(&self) -> (u8, u8, u8) {
self.to_backend_color().rgb
}
/// Get the alpha channel of the color
#[inline(always)]
fn alpha(&self) -> f64 {
self.to_backend_color().alpha
}
/// Mix the color with given opacity
fn mix(&self, value: f64) -> RGBAColor {
let (r, g, b) = self.rgb();
let a = self.alpha() * value;
RGBAColor(r, g, b, a)
}
/// Convert the color into the RGBA color which is internally used by Plotters
fn to_rgba(&self) -> RGBAColor {
let (r, g, b) = self.rgb();
let a = self.alpha();
RGBAColor(r, g, b, a)
}
/// Make a filled style form the color
fn filled(&self) -> ShapeStyle
where
Self: Sized,
{
Into::<ShapeStyle>::into(self).filled()
}
/// Make a shape style with stroke width from a color
fn stroke_width(&self, width: u32) -> ShapeStyle
where
Self: Sized,
{
Into::<ShapeStyle>::into(self).stroke_width(width)
}
}
impl<T: Color> Color for &'_ T {
fn to_backend_color(&self) -> BackendColor {
<T as Color>::to_backend_color(*self)
}
}
/// The RGBA representation of the color, Plotters use RGBA as the internal representation
/// of color
///
/// If you want to directly create a RGB color with transparency use [RGBColor::mix]
#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct RGBAColor(pub u8, pub u8, pub u8, pub f64);
impl Color for RGBAColor {
#[inline(always)]
fn to_backend_color(&self) -> BackendColor {
BackendColor {
rgb: (self.0, self.1, self.2),
alpha: self.3,
}
}
}
/// A color in the given palette
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct PaletteColor<P: Palette>(usize, PhantomData<P>);
impl<P: Palette> PaletteColor<P> {
/// Pick a color from the palette
pub fn pick(idx: usize) -> PaletteColor<P> {
PaletteColor(idx % P::COLORS.len(), PhantomData)
}
}
impl<P: Palette> Color for PaletteColor<P> {
#[inline(always)]
fn to_backend_color(&self) -> BackendColor {
BackendColor {
rgb: P::COLORS[self.0],
alpha: 1.0,
}
}
}
/// The color described by its RGB value
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct RGBColor(pub u8, pub u8, pub u8);
impl BackendStyle for RGBAColor {
fn color(&self) -> BackendColor {
self.to_backend_color()
}
}
impl Color for RGBColor {
#[inline(always)]
fn to_backend_color(&self) -> BackendColor {
BackendColor {
rgb: (self.0, self.1, self.2),
alpha: 1.0,
}
}
}
impl BackendStyle for RGBColor {
fn color(&self) -> BackendColor {
self.to_backend_color()
}
}
/// The color described by HSL color space
#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct HSLColor(pub f64, pub f64, pub f64);
impl Color for HSLColor {
#[inline(always)]
#[allow(clippy::many_single_char_names)]
fn to_backend_color(&self) -> BackendColor {
let (h, s, l) = (
self.0.min(1.0).max(0.0),
self.1.min(1.0).max(0.0),
self.2.min(1.0).max(0.0),
);
if s == 0.0 {
let value = (l * 255.0).round() as u8;
return BackendColor {
rgb: (value, value, value),
alpha: 1.0,
};
}
let q = if l < 0.5 {
l * (1.0 + s)
} else {
l + s - l * s
};
let p = 2.0 * l - q;
let cvt = |mut t| {
if t < 0.0 {
t += 1.0;
}
if t > 1.0 {
t -= 1.0;
}
let value = if t < 1.0 / 6.0 {
p + (q - p) * 6.0 * t
} else if t < 1.0 / 2.0 {
q
} else if t < 2.0 / 3.0 {
p + (q - p) * (2.0 / 3.0 - t) * 6.0
} else {
p
};
(value * 255.0).round() as u8
};
BackendColor {
rgb: (cvt(h + 1.0 / 3.0), cvt(h), cvt(h - 1.0 / 3.0)),
alpha: 1.0,
}
}
}