use gdk4::RGBA;
use cairo::Context;
use super::super::context_mapper::ContextMapper;
use std::collections::HashMap;
use super::*;
use super::super::MappingProperty;
use std::borrow::Borrow;
#[derive(Debug, Clone)]
pub struct BarMapping {
color : RGBA,
center_anchor : bool,
x : Vec<f64>,
y : Vec<f64>,
h : Vec<f64>,
w : Vec<f64>,
col_names : [String; 4],
bar_width : f64,
origin : (f64, f64),
bar_spacing : f64,
horizontal : bool,
source : String
}
impl Default for BarMapping {
fn default() -> Self {
let mut bar = Self {
color : RGBA::BLACK,
x : Vec::new(),
y : Vec::new(),
h : Vec::new(),
w : Vec::new(),
col_names : [String::new(), String::new(), String::new(), String::new()],
bar_width : 100.0,
origin : (0.0, 0.0),
bar_spacing : 1.0,
horizontal : false,
center_anchor : false,
source : String::new()
};
bar.adjust_bar();
bar
}
}
impl BarMapping {
pub fn color(mut self, color : String) -> Self {
self.color = color.parse().unwrap();
self.adjust_bar();
self
}
pub fn center_anchor(mut self, center_anchor : bool) -> Self {
self.center_anchor = center_anchor;
self.adjust_bar();
self
}
pub fn width(mut self, w : f64) -> Self {
self.bar_width = w;
self.adjust_bar();
self
}
pub fn origin(mut self, origin : (f64, f64)) -> Self {
self.origin = origin;
self.adjust_bar();
self
}
pub fn bar_spacing(mut self, bar_spacing : f64) -> Self {
self.bar_spacing = bar_spacing;
self.adjust_bar();
self
}
pub fn horizontal(mut self, horizontal : bool) -> Self {
self.horizontal = horizontal;
self.adjust_bar();
self
}
pub fn map<D>(ext : impl IntoIterator<Item=D>) -> Self
where
D : Borrow<f64>
{
let mut bar : BarMapping = Default::default();
let extension : Vec<_> = ext.into_iter().map(|d| *d.borrow() ).collect();
bar.update_data(vec![extension]);
bar
}
fn adjust_bar(&mut self) {
if self.horizontal {
let n = self.w.len();
self.y = (0..n).map(|i| self.origin.1 + self.bar_spacing * i as f64 ).collect();
self.x = (0..n).map(|_| self.origin.0 ).collect();
if self.center_anchor {
let spacing = self.bar_spacing;
self.y.iter_mut().for_each(|y| *y -= spacing / 2. );
}
} else {
let n = self.h.len();
self.y = (0..n).map(|_| self.origin.1 ).collect();
self.x = (0..n).map(|i| self.origin.0 + self.bar_spacing * i as f64 ).collect();
if self.center_anchor {
let spacing = self.bar_spacing;
self.x.iter_mut().for_each(|x| *x -= spacing / 2. );
}
}
if self.horizontal {
self.h = (0..self.w.len()).map(|_| self.bar_spacing * self.bar_width ).collect();
} else {
self.w = (0..self.h.len()).map(|_| self.bar_spacing * self.bar_width ).collect();
}
}
}
impl Mapping for BarMapping {
fn clone_boxed(&self) -> Box<dyn Mapping> {
Box::new(self.clone())
}
fn update(&mut self, prop : MappingProperty) -> bool {
match prop {
MappingProperty::Line(_line) => {
unimplemented!()
},
_ => false
}
}
fn update_from_json(&mut self, rep : crate::model::Mapping) {
if let Some(w) = rep.width {
self.bar_width = w;
}
if let Some(v) = rep.vertical {
self.horizontal = !v;
}
if let Some(s) = rep.spacing {
self.bar_spacing = s;
}
if let Some(origin) = rep.origin {
if self.horizontal {
self.origin = (0., origin);
} else {
self.origin = (origin, 0.);
}
}
if let Some(c) = rep.center {
self.center_anchor = c;
}
if let Some(color) = rep.color.clone() {
self.color = color.parse().unwrap();
}
let mut new_data = Vec::new();
super::update_single_data_from_json(&mut new_data, rep);
self.update_data(vec![new_data]);
}
fn draw(&self, mapper : &ContextMapper, ctx : &Context) -> Result<(), Box<dyn Error>> {
ctx.save()?;
ctx.set_source_rgb(self.color.red().into(), self.color.green().into(), self.color.blue().into());
let r_iter = self.x.iter().zip(self.y.iter()
.zip(self.w.iter()
.zip(self.h.iter()))
);
for (x, (y, (w, h))) in r_iter {
let tl_ok = mapper.check_bounds(*x, y + h);
let tr_ok = mapper.check_bounds(x + w, y + h);
let bl_ok = mapper.check_bounds(*x, *y);
let br_ok = mapper.check_bounds(x + *w, *y);
if tl_ok && tr_ok && bl_ok && br_ok {
let bottom_left = mapper.map(*x, *y);
let bottom_right = mapper.map(x + *w, *y);
let top_left = mapper.map(*x, *y + *h);
let coord_w = bottom_left.distance(bottom_right);
let coord_h = bottom_left.distance(top_left);
ctx.rectangle(top_left.x, top_left.y, coord_w, coord_h);
ctx.fill()?;
ctx.stroke()?;
} else {
}
}
ctx.restore()?;
Ok(())
}
fn update_data(&mut self, mut values : Vec<Vec<f64>>) {
if self.horizontal {
self.w = values.remove(0);
} else {
self.h = values.remove(0);
}
assert!(values.len() == 0);
self.adjust_bar();
}
fn update_extra_data(&mut self, _values : Vec<Vec<String>>) {
}
fn data_limits(&self) -> Option<((f64, f64), (f64, f64))> {
let mut xmin = self.origin.0;
let mut ymin = self.origin.1;
if self.center_anchor && self.horizontal {
ymin -= self.bar_spacing / 2.0
}
if self.center_anchor && !self.horizontal {
xmin -= self.bar_spacing / 2.0
}
let xmax = if self.horizontal {
let max_w = self.w.iter().max_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal))?;
xmin + *max_w
} else {
let n = self.x.len();
xmin + n as f64 * self.bar_spacing
};
let ymax = if self.horizontal {
let n = self.y.len();
ymin + n as f64 * self.bar_spacing
} else {
let max_h = self.h.iter().max_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal))?;
ymin + *max_h
};
Some(((xmin, xmax), (ymin, ymax)))
}
fn mapping_type(&self) -> String {
"bar".into()
}
fn get_col_name(&self, col : &str) -> String {
match col {
"x" => self.col_names[0].clone(),
"y" => self.col_names[1].clone(),
"width" => self.col_names[2].clone(),
"height" => self.col_names[3].clone(),
_ => String::new()
}
}
fn get_ordered_col_names(&self) -> Vec<(String, String)> {
vec![
(String::from("x"), self.get_col_name("x")),
(String::from("y"), self.get_col_name("y")),
(String::from("width"), self.get_col_name("width")),
(String::from("height"), self.get_col_name("height"))
]
}
fn get_hash_col_names(&self) -> HashMap<String, String> {
let mut cols = HashMap::new();
cols.insert("x".into(), self.col_names[0].clone());
cols.insert("y".into(), self.col_names[1].clone());
cols.insert("width".into(), self.col_names[2].clone());
cols.insert("height".into(), self.col_names[3].clone());
cols
}
fn set_col_name(&mut self, col : &str, name : &str) {
match col {
"x" => { self.col_names[0] = name.into(); },
"y" => { self.col_names[1] = name.into(); },
"width" => { self.col_names[2] = name.into(); },
"height" => { self.col_names[3] = name.into(); },
_ => { }
}
}
fn set_col_names(&mut self, cols : Vec<String>) -> Result<(), &'static str> {
if cols.len() != 1 {
Err("Wrong number of columns.")
} else {
self.set_col_name("height", &cols[0]);
Ok(())
}
}
fn set_source(&mut self, source : String) {
self.source = source;
}
fn get_source(&self) -> String {
self.source.clone()
}
}