jagua_rs/util/
assertions.rs1use crate::collision_detection::CDEngine;
2use crate::collision_detection::hazards::Hazard;
3use crate::collision_detection::hazards::{BasicHazardEntity, HazardEntity};
4use crate::entities::Layout;
5use crate::entities::LayoutSnapshot;
6use crate::geometry::fail_fast::SPSurrogate;
7use crate::geometry::geo_enums::GeoPosition;
8use itertools::Itertools;
9use std::collections::HashSet;
10#[must_use]
14pub fn snapshot_matches_layout(layout: &Layout, layout_snapshot: &LayoutSnapshot) -> bool {
15 if !std::sync::Arc::ptr_eq(
16 &layout.container.base_cde,
17 &layout_snapshot.container.base_cde,
18 ) {
19 return false;
20 }
21 for placed_item in layout_snapshot.placed_items.values() {
22 if !layout
23 .placed_items
24 .values()
25 .any(|pi| pi.item.idx == placed_item.item.idx && pi.d_transf == placed_item.d_transf)
26 {
27 return false;
28 }
29 }
30 true
31}
32
33#[must_use]
36pub fn index_matches_hazards<E: HazardEntity>(cde: &CDEngine<E>) -> bool {
37 let indexed = cde.index.iter().collect_vec();
38 let sorted = indexed.is_sorted_by(|(_, a), (_, b)| a.x_min <= b.x_min);
39 let chunk_bounds = indexed
40 .chunks(64)
41 .map(|chunk| {
42 chunk
43 .iter()
44 .map(|(_, bbox)| bbox.x_max)
45 .fold(f32::NEG_INFINITY, f32::max)
46 })
47 .eq(cde.index.chunk_x_max().iter().copied());
48 let interior = cde
49 .hazards_map
50 .iter()
51 .filter(|(_, h)| h.entity().scope() == GeoPosition::Interior)
52 .map(|(k, h)| (k, h.shape().bbox))
53 .sorted_by_key(|(k, _)| *k)
54 .collect_vec();
55 let by_key = indexed.into_iter().sorted_by_key(|(k, _)| *k).collect_vec();
56 sorted && chunk_bounds && by_key == interior
57}
58
59#[must_use]
61pub fn layout_cde_matches_fresh(layout: &Layout) -> bool {
62 let hazards = layout
63 .placed_items
64 .iter()
65 .map(|(pk, pi)| Hazard::new(BasicHazardEntity::from((pk, pi)), pi.shape.clone()));
66 cde_matches_fresh(layout.cde(), &layout.container.base_cde, hazards)
67}
68
69#[must_use]
72pub fn cde_matches_fresh<E: HazardEntity>(
73 cde: &CDEngine<E>,
74 base: &CDEngine<E>,
75 dynamic_hazards: impl IntoIterator<Item = Hazard<E>>,
76) -> bool {
77 let mut fresh_cde = base.clone();
78 for hazard in dynamic_hazards {
79 fresh_cde.register_hazard(hazard);
80 }
81 index_matches_hazards(cde) && hazards_match(cde.hazards(), fresh_cde.hazards())
82}
83
84fn hazards_match<'a, E: HazardEntity + 'a>(
85 chv1: impl Iterator<Item = &'a Hazard<E>>,
86 chv2: impl Iterator<Item = &'a Hazard<E>>,
87) -> bool {
88 let chv1_active_hazards = chv1.map(Hazard::entity).collect::<HashSet<_>>();
89
90 let chv2_active_hazards = chv2.map(Hazard::entity).collect::<HashSet<_>>();
91
92 if chv1_active_hazards != chv2_active_hazards {
93 println!("Hazard vecs don't match");
94 return false;
95 }
96 true
97}
98
99pub fn print_layout(layout: &Layout) {
101 println!("let mut layout = Layout::new(container.clone());");
102 println!();
103
104 for pi in layout.placed_items.values() {
105 let transformation_str = {
106 let t_decomp = &pi.d_transf;
107 let (tr, (tx, ty)) = (t_decomp.rotation(), t_decomp.translation());
108 format!(
109 "&DTransformation::new({tr:.6},({tx:.6},{ty:.6})).with_reflection({})",
110 t_decomp.reflected
111 )
112 };
113
114 println!(
115 "layout.place_item(instance.item({}), {});",
116 pi.item.idx, transformation_str
117 );
118 }
119}
120
121#[must_use]
123pub fn hull_indices_match_reference(
124 surrogate: &SPSurrogate,
125 reference: &SPSurrogate,
126 reflected: bool,
127 n_vertices: usize,
128) -> bool {
129 let ref_indices = &reference.convex_hull_indices;
130 let expected: Vec<usize> = if reflected {
131 ref_indices
132 .iter()
133 .rev()
134 .map(|&i| n_vertices - 1 - i)
135 .collect()
136 } else {
137 ref_indices.clone()
138 };
139 surrogate.convex_hull_indices == expected
140}