#![cfg(feature = "plotting")]
use eunoia::geometry::primitives::Point;
use eunoia::geometry::shapes::{Circle, Ellipse, Square};
use eunoia::geometry::traits::Polygonize;
use eunoia::plotting::{classify_into_pieces, polygon_clip, ClipOperation, PlotData, PlotOptions};
use eunoia::spec::{Combination, DiagramSpecBuilder, InputType};
use eunoia::{DiagramError, Fitter};
#[test]
fn binding_author_walkthrough() {
assert!(Circle::try_new(Point::new(0.0, 0.0), 1.0).is_ok());
assert!(Ellipse::try_new(Point::new(0.0, 0.0), 4.0, 3.0, 0.0).is_ok());
assert!(Square::try_new(Point::new(0.0, 0.0), 2.0).is_ok());
let err = Ellipse::try_new(Point::new(0.0, 0.0), 4.0, -1.0, 0.0).unwrap_err();
match err {
DiagramError::InvalidShapeParameter {
shape,
param,
value,
} => {
assert_eq!(shape, "Ellipse");
assert_eq!(param, "semi_minor");
assert!(value < 0.0);
}
other => panic!("expected InvalidShapeParameter, got {:?}", other),
}
let spec = DiagramSpecBuilder::new()
.set("A", 10.0)
.set("B", 8.0)
.set("C", 4.0)
.intersection(&["A", "B"], 3.0)
.intersection(&["A", "C"], 2.0)
.intersection(&["B", "C"], 2.0)
.intersection(&["A", "B", "C"], 1.0)
.input_type(InputType::Exclusive)
.build()
.unwrap();
let layout = Fitter::<Circle>::new(&spec).seed(42).fit().unwrap();
let plot = layout.plot_data(&spec, PlotOptions::default());
for combo_str in plot.region_anchors.keys() {
assert!(plot.region_areas.contains_key(combo_str));
}
for name in spec.set_names() {
assert!(plot.shape_outlines.contains_key(name));
assert!(plot.set_anchors.contains_key(name));
}
for combo_str in plot.region_anchors.keys() {
let combo: Combination = combo_str.parse().unwrap();
let pieces_via_combo = plot.regions.get(&combo).expect("region must exist");
let pieces_via_str = plot.pieces_for(combo_str).expect("region must exist");
assert_eq!(pieces_via_combo.len(), pieces_via_str.len());
assert_eq!(combo.to_string(), combo_str.as_str());
}
let combo: Combination = " A & B ".parse().unwrap();
assert_eq!(combo, Combination::new(&["A", "B"]));
let order: Vec<String> = plot
.regions
.iter_in_input_order(spec.set_names())
.map(|(combo, _)| combo.to_string())
.collect();
let expected_order = ["A", "B", "C", "A&B", "A&C", "B&C", "A&B&C"];
let mut expected_present: Vec<&str> = expected_order
.iter()
.copied()
.filter(|s| plot.region_anchors.contains_key(*s))
.collect();
expected_present.retain(|s| order.iter().any(|o| o == s));
assert_eq!(order, expected_present);
let _canonical: Vec<&Combination> =
plot.regions.iter_sorted().map(|(combo, _)| combo).collect();
let outer_outline = plot.shape_outlines.get("A").cloned().unwrap();
let inner_outline = plot.shape_outlines.get("B").cloned().unwrap();
let raw_rings = polygon_clip(&outer_outline, &inner_outline, ClipOperation::Difference);
let pieces = classify_into_pieces(raw_rings);
for piece in &pieces {
assert!(piece.area() > 0.0);
for hole in &piece.holes {
assert!(hole.vertices().len() >= 3);
}
}
let circle = layout.shape_for_set("A").unwrap();
let smooth = circle.polygonize(512);
assert_eq!(smooth.vertices().len(), 512);
let stored_shapes: Vec<Circle> = spec
.set_names()
.iter()
.map(|n| *layout.shape_for_set(n).unwrap())
.collect();
let replot = PlotData::from_shapes(
&stored_shapes,
&spec,
layout.container(),
PlotOptions {
n_vertices: 32,
..PlotOptions::default()
},
);
assert_eq!(replot.regions.len(), plot.regions.len());
let custom_names = ["A", "B", "C"];
let _ad_hoc_order: Vec<_> = plot
.regions
.iter_in_input_order(&custom_names)
.map(|(combo, _)| combo.to_string())
.collect();
}