#![cfg(feature = "sf")]
use ggplot_rs::data::Value;
use ggplot_rs::prelude::*;
use ggplot_rs::scale::ScaleSet;
use ggplot_rs::stat::binhex::{HexLattice, StatBinHex};
use ggplot_rs::stat::Stat;
fn grid_points() -> Vec<(String, Vec<Value>)> {
let mut xs = Vec::new();
let mut ys = Vec::new();
for i in 0..20 {
for j in 0..20 {
xs.push(Value::Float(i as f64 * 0.37));
ys.push(Value::Float(j as f64 * 0.41));
}
}
vec![("x".into(), xs), ("y".into(), ys)]
}
#[test]
fn hexbin_renders_with_no_nan_and_is_deterministic_over_5_runs() {
let plot = || {
GGPlot::new(grid_points())
.aes(Aes::new().x("x").y("y"))
.geom_hex()
};
let first = plot().render_svg_native_with_size(400, 400).unwrap();
assert!(!first.contains("NaN"));
assert!(first.contains("<polygon"));
for _ in 0..5 {
let again = plot().render_svg_native_with_size(400, 400).unwrap();
assert_eq!(first, again);
}
}
#[test]
fn total_point_count_is_conserved() {
let mut data = grid_points();
let n = data[0].1.len();
let stat = StatBinHex::new().bins_xy(10, 10);
let scales = ScaleSet::default();
let mut df = ggplot_rs::data::DataFrame::new();
for (name, col) in data.drain(..) {
df.add_column(name, col);
}
let result = stat.compute_group(&df, &scales);
let total: f64 = result
.column("count")
.unwrap()
.iter()
.map(|v| v.as_f64().unwrap())
.sum();
assert_eq!(total, n as f64);
}
#[test]
fn hex_edges_are_equal_under_coord_sf() {
let svg = GGPlot::new(vec![
("x".into(), vec![Value::Float(0.0)]),
("y".into(), vec![Value::Float(0.0)]),
])
.aes(Aes::new().x("x").y("y"))
.geom_hex_with(GeomHex::default())
.stat(StatBinHex::new().binwidth(1.0, 1.0).origin(0.0, 0.0))
.coord_sf()
.render_svg_native_with_size(400, 400)
.unwrap();
assert!(!svg.contains("NaN"));
let poly = svg.split("<polygon").nth(1).expect("a hex polygon");
let points_attr = poly
.split("points=\"")
.nth(1)
.unwrap()
.split('"')
.next()
.unwrap();
let pts: Vec<(f64, f64)> = points_attr
.split(' ')
.map(|p| {
let mut it = p.split(',');
let x: f64 = it.next().unwrap().parse().unwrap();
let y: f64 = it.next().unwrap().parse().unwrap();
(x, y)
})
.collect();
assert_eq!(pts.len(), 6);
let edge = |a: (f64, f64), b: (f64, f64)| ((a.0 - b.0).powi(2) + (a.1 - b.1).powi(2)).sqrt();
let edges: Vec<f64> = (0..6).map(|i| edge(pts[i], pts[(i + 1) % 6])).collect();
let first = edges[0];
for e in &edges {
assert!((e - first).abs() < 0.05, "edges should be equal: {edges:?}");
}
}
#[test]
fn locate_agrees_with_compute_group_on_random_points() {
let mut state: u64 = 7;
let mut next = move || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
(state % 20000) as f64 / 100.0 - 100.0
};
let xs: Vec<f64> = (0..500).map(|_| next()).collect();
let ys: Vec<f64> = (0..500).map(|_| next()).collect();
let mut df = ggplot_rs::data::DataFrame::new();
df.add_column("x".into(), xs.iter().map(|&v| Value::Float(v)).collect());
df.add_column("y".into(), ys.iter().map(|&v| Value::Float(v)).collect());
let stat = StatBinHex::new().bins_xy(20, 20);
let scales = ScaleSet::default();
let lattice: HexLattice = stat.lattice(&xs, &ys);
let result = stat.compute_group(&df, &scales);
let produced: std::collections::HashSet<(i64, i64)> = result
.column("q")
.unwrap()
.iter()
.zip(result.column("r").unwrap().iter())
.map(|(q, r)| (q.as_f64().unwrap() as i64, r.as_f64().unwrap() as i64))
.collect();
for i in 0..xs.len() {
let qr = StatBinHex::locate(xs[i], ys[i], &lattice);
assert!(produced.contains(&qr));
}
}