use std::f64::consts::PI;
use plotlars_core::components::{Rgb, Shape};
use plotters::style::RGBColor;
pub(crate) const DEFAULT_COLORS: [RGBColor; 10] = [
RGBColor(99, 110, 250), RGBColor(239, 85, 59), RGBColor(0, 204, 150), RGBColor(171, 99, 250), RGBColor(255, 161, 90), RGBColor(25, 211, 243), RGBColor(255, 102, 146), RGBColor(182, 232, 128), RGBColor(255, 151, 255), RGBColor(254, 203, 82), ];
pub(crate) fn convert_rgb(rgb: &Rgb) -> RGBColor {
RGBColor(rgb.0, rgb.1, rgb.2)
}
pub(crate) fn default_color(index: usize) -> RGBColor {
DEFAULT_COLORS[index % DEFAULT_COLORS.len()]
}
pub(crate) fn resolve_trace_color(
marker: &Option<plotlars_core::ir::marker::MarkerIR>,
trace_idx: usize,
) -> RGBColor {
marker
.as_ref()
.and_then(|m| m.color.as_ref())
.map(convert_rgb)
.unwrap_or_else(|| default_color(trace_idx))
}
#[derive(Clone, Copy)]
pub(crate) enum BaseShape {
Circle,
Square,
Diamond,
TriangleUp,
TriangleDown,
TriangleLeft,
TriangleRight,
Cross,
X,
Pentagon,
Hexagon,
Octagon,
Star,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum FillMode {
Filled,
Open,
}
pub(crate) fn resolve_shape(shape: &Shape) -> (BaseShape, FillMode) {
use Shape::*;
match shape {
Circle | CircleDot | CircleCross | CircleCrossOpen | CircleX | CircleXOpen => {
(BaseShape::Circle, FillMode::Filled)
}
CircleOpen | CircleOpenDot => (BaseShape::Circle, FillMode::Open),
Square | SquareDot | SquareCross | SquareCrossOpen | SquareX | SquareXOpen => {
(BaseShape::Square, FillMode::Filled)
}
SquareOpen | SquareOpenDot => (BaseShape::Square, FillMode::Open),
Diamond | DiamondDot | DiamondTall | DiamondTallDot | DiamondWide | DiamondWideDot
| DiamondCross | DiamondCrossOpen | DiamondX | DiamondXOpen => {
(BaseShape::Diamond, FillMode::Filled)
}
DiamondOpen | DiamondOpenDot | DiamondTallOpen | DiamondTallOpenDot | DiamondWideOpen
| DiamondWideOpenDot => (BaseShape::Diamond, FillMode::Open),
Cross | CrossDot | CrossThin | CrossThinOpen | CrossOpen | CrossOpenDot => {
(BaseShape::Cross, FillMode::Filled)
}
X | XDot | XThin | XThinOpen => (BaseShape::X, FillMode::Filled),
XOpen | XOpenDot => (BaseShape::X, FillMode::Open),
TriangleUp | TriangleUpDot => (BaseShape::TriangleUp, FillMode::Filled),
TriangleUpOpen | TriangleUpOpenDot => (BaseShape::TriangleUp, FillMode::Open),
TriangleDown | TriangleDownDot => (BaseShape::TriangleDown, FillMode::Filled),
TriangleDownOpen | TriangleDownOpenDot => (BaseShape::TriangleDown, FillMode::Open),
TriangleLeft | TriangleLeftDot => (BaseShape::TriangleLeft, FillMode::Filled),
TriangleLeftOpen | TriangleLeftOpenDot => (BaseShape::TriangleLeft, FillMode::Open),
TriangleRight | TriangleRightDot => (BaseShape::TriangleRight, FillMode::Filled),
TriangleRightOpen | TriangleRightOpenDot => (BaseShape::TriangleRight, FillMode::Open),
TriangleNE | TriangleNEDot => (BaseShape::TriangleUp, FillMode::Filled),
TriangleNEOpen | TriangleNEOpenDot => (BaseShape::TriangleUp, FillMode::Open),
TriangleSE | TriangleSEDot => (BaseShape::TriangleDown, FillMode::Filled),
TriangleSEOpen | TriangleSEOpenDot => (BaseShape::TriangleDown, FillMode::Open),
TriangleSW | TriangleSWDot => (BaseShape::TriangleDown, FillMode::Filled),
TriangleSWOpen | TriangleSWOpenDot => (BaseShape::TriangleDown, FillMode::Open),
TriangleNW | TriangleNWDot => (BaseShape::TriangleUp, FillMode::Filled),
TriangleNWOpen | TriangleNWOpenDot => (BaseShape::TriangleUp, FillMode::Open),
Pentagon | PentagonDot => (BaseShape::Pentagon, FillMode::Filled),
PentagonOpen | PentagonOpenDot => (BaseShape::Pentagon, FillMode::Open),
Hexagon | HexagonDot | Hexagon2 | Hexagon2Dot => (BaseShape::Hexagon, FillMode::Filled),
HexagonOpen | HexagonOpenDot | Hexagon2Open | Hexagon2OpenDot => {
(BaseShape::Hexagon, FillMode::Open)
}
Octagon | OctagonDot => (BaseShape::Octagon, FillMode::Filled),
OctagonOpen | OctagonOpenDot => (BaseShape::Octagon, FillMode::Open),
Star | StarDot | StarTriangleUp | StarTriangleUpDot | StarTriangleDown
| StarTriangleDownDot | StarSquare | StarSquareDot | StarDiamond | StarDiamondDot
| Hexagram | HexagramDot => (BaseShape::Star, FillMode::Filled),
StarOpen
| StarOpenDot
| StarTriangleUpOpen
| StarTriangleUpOpenDot
| StarTriangleDownOpen
| StarTriangleDownOpenDot
| StarSquareOpen
| StarSquareOpenDot
| StarDiamondOpen
| StarDiamondOpenDot
| HexagramOpen
| HexagramOpenDot => (BaseShape::Star, FillMode::Open),
Hourglass | HourglassOpen | BowTie | BowTieOpen => (BaseShape::Diamond, FillMode::Filled),
Asterisk | AsteriskOpen => (BaseShape::Star, FillMode::Filled),
Hash | HashDot | HashOpen | HashOpenDot => (BaseShape::Cross, FillMode::Filled),
YUp | YUpOpen | YDown | YDownOpen | YLeft | YLeftOpen | YRight | YRightOpen => {
(BaseShape::Cross, FillMode::Filled)
}
LineEW | LineEWOpen | LineNS | LineNSOpen | LineNE | LineNEOpen | LineNW | LineNWOpen => {
(BaseShape::Cross, FillMode::Filled)
}
}
}
pub(crate) fn resolve_trace_shape(
marker: &Option<plotlars_core::ir::marker::MarkerIR>,
) -> (BaseShape, FillMode) {
marker
.as_ref()
.and_then(|m| m.shape.as_ref())
.map(resolve_shape)
.unwrap_or((BaseShape::Circle, FillMode::Filled))
}
pub(crate) fn square_vertices(cx: i32, cy: i32, r: i32) -> Vec<(i32, i32)> {
vec![
(cx - r, cy - r),
(cx + r, cy - r),
(cx + r, cy + r),
(cx - r, cy + r),
]
}
pub(crate) fn diamond_vertices(cx: i32, cy: i32, r: i32) -> Vec<(i32, i32)> {
vec![(cx, cy - r), (cx + r, cy), (cx, cy + r), (cx - r, cy)]
}
pub(crate) fn triangle_down_vertices(cx: i32, cy: i32, r: i32) -> Vec<(i32, i32)> {
vec![(cx, cy + r), (cx + r, cy - r), (cx - r, cy - r)]
}
pub(crate) fn triangle_left_vertices(cx: i32, cy: i32, r: i32) -> Vec<(i32, i32)> {
vec![(cx - r, cy), (cx + r, cy - r), (cx + r, cy + r)]
}
pub(crate) fn triangle_right_vertices(cx: i32, cy: i32, r: i32) -> Vec<(i32, i32)> {
vec![(cx + r, cy), (cx - r, cy - r), (cx - r, cy + r)]
}
pub(crate) fn regular_polygon_vertices(cx: i32, cy: i32, r: i32, n: usize) -> Vec<(i32, i32)> {
let offset = -PI / 2.0;
(0..n)
.map(|i| {
let angle = offset + 2.0 * PI * (i as f64) / (n as f64);
let x = cx as f64 + r as f64 * angle.cos();
let y = cy as f64 + r as f64 * angle.sin();
(x.round() as i32, y.round() as i32)
})
.collect()
}
pub(crate) fn star_vertices(cx: i32, cy: i32, r: i32) -> Vec<(i32, i32)> {
let n = 5;
let inner_r = (r as f64 * 0.38).round() as i32;
let offset = -PI / 2.0;
(0..n * 2)
.map(|i| {
let angle = offset + PI * (i as f64) / (n as f64);
let radius = if i % 2 == 0 { r } else { inner_r };
let x = cx as f64 + radius as f64 * angle.cos();
let y = cy as f64 + radius as f64 * angle.sin();
(x.round() as i32, y.round() as i32)
})
.collect()
}