blast_stress_solver/rapier/
split_migrator.rs1use std::collections::{HashMap, HashSet};
2
3use rapier3d::prelude::RigidBodyHandle;
4
5use crate::types::SplitChild;
6
7pub struct ExistingBodyState {
9 pub handle: RigidBodyHandle,
10 pub node_indices: HashSet<u32>,
11 pub is_fixed: bool,
12}
13
14pub struct SplitMigrationPlan {
16 pub reuse: Vec<ReuseEntry>,
18 pub create: Vec<CreateEntry>,
20}
21
22pub struct ReuseEntry {
23 pub child_index: usize,
24 pub body_handle: RigidBodyHandle,
25}
26
27pub struct CreateEntry {
28 pub child_index: usize,
29}
30
31#[derive(Clone, Copy, Debug, Default)]
32pub struct PlannerChildSupport {
33 pub is_support: bool,
34}
35
36pub fn plan_split_migration(
43 bodies: &[ExistingBodyState],
44 children: &[SplitChild],
45) -> SplitMigrationPlan {
46 plan_split_migration_with_support(
47 bodies,
48 children,
49 &vec![PlannerChildSupport::default(); children.len()],
50 )
51}
52
53pub fn plan_split_migration_with_support(
54 bodies: &[ExistingBodyState],
55 children: &[SplitChild],
56 _child_support: &[PlannerChildSupport],
57) -> SplitMigrationPlan {
58 if bodies.is_empty() || children.is_empty() {
59 return SplitMigrationPlan {
60 reuse: Vec::new(),
61 create: (0..children.len())
62 .map(|i| CreateEntry { child_index: i })
63 .collect(),
64 };
65 }
66
67 let mut body_hashes: HashMap<u64, Vec<usize>> = HashMap::new();
69 for (i, body) in bodies.iter().enumerate() {
70 let hash = hash_node_set(&body.node_indices);
71 body_hashes.entry(hash).or_default().push(i);
72 }
73
74 let mut reuse = Vec::new();
75 let mut assigned_bodies = HashSet::new();
76 let mut unmatched_children = Vec::new();
77
78 for (ci, child) in children.iter().enumerate() {
79 let child_set: HashSet<u32> = child.nodes.iter().copied().collect();
80 let hash = hash_node_set(&child_set);
81
82 let mut matched = false;
83 if let Some(candidates) = body_hashes.get(&hash) {
84 for &bi in candidates {
85 if assigned_bodies.contains(&bi) {
86 continue;
87 }
88 if bodies[bi].node_indices == child_set {
89 reuse.push(ReuseEntry {
90 child_index: ci,
91 body_handle: bodies[bi].handle,
92 });
93 assigned_bodies.insert(bi);
94 matched = true;
95 break;
96 }
97 }
98 }
99 if !matched {
100 unmatched_children.push(ci);
101 }
102 }
103
104 let unmatched_bodies: Vec<usize> = bodies
105 .iter()
106 .enumerate()
107 .filter_map(|(idx, _)| (!assigned_bodies.contains(&idx)).then_some(idx))
108 .collect();
109
110 if !unmatched_bodies.is_empty() && !unmatched_children.is_empty() {
111 let overlap =
112 build_overlap_matrix(bodies, &unmatched_bodies, children, &unmatched_children);
113 let assignments = hungarian_max(&overlap);
114 let mut reused_children = HashSet::new();
115
116 for (row_idx, assignment) in assignments.into_iter().enumerate() {
117 let Some(col_idx) = assignment else {
118 continue;
119 };
120 let overlap_score = overlap
121 .get(row_idx)
122 .and_then(|row| row.get(col_idx))
123 .copied()
124 .unwrap_or(0);
125 if overlap_score == 0 {
126 continue;
127 }
128
129 let body_idx = unmatched_bodies[row_idx];
130 let child_index = unmatched_children[col_idx];
131 reuse.push(ReuseEntry {
132 child_index,
133 body_handle: bodies[body_idx].handle,
134 });
135 reused_children.insert(child_index);
136 }
137
138 let create = unmatched_children
139 .into_iter()
140 .filter(|child_index| !reused_children.contains(child_index))
141 .map(|child_index| CreateEntry { child_index })
142 .collect();
143 return SplitMigrationPlan { reuse, create };
144 }
145
146 let create = unmatched_children
147 .into_iter()
148 .map(|child_index| CreateEntry { child_index })
149 .collect();
150 SplitMigrationPlan { reuse, create }
151}
152
153fn hash_node_set(nodes: &HashSet<u32>) -> u64 {
154 let mut sorted: Vec<u32> = nodes.iter().copied().collect();
155 sorted.sort_unstable();
156 let mut hash = 0u64;
157 for n in sorted {
158 hash ^= n as u64;
160 hash = hash.wrapping_mul(0x100000001b3);
161 }
162 hash
163}
164
165fn build_overlap_matrix(
166 bodies: &[ExistingBodyState],
167 unmatched_bodies: &[usize],
168 children: &[SplitChild],
169 unmatched_children: &[usize],
170) -> Vec<Vec<usize>> {
171 unmatched_bodies
172 .iter()
173 .map(|&body_idx| {
174 let body = &bodies[body_idx];
175 unmatched_children
176 .iter()
177 .map(|&child_idx| {
178 children[child_idx]
179 .nodes
180 .iter()
181 .filter(|node| body.node_indices.contains(node))
182 .count()
183 })
184 .collect()
185 })
186 .collect()
187}
188
189fn hungarian_max(matrix: &[Vec<usize>]) -> Vec<Option<usize>> {
190 let rows = matrix.len();
191 let cols = matrix.first().map(Vec::len).unwrap_or(0);
192 if rows == 0 || cols == 0 {
193 return vec![None; rows];
194 }
195
196 let size = rows.max(cols);
197 let max_val = matrix
198 .iter()
199 .flat_map(|row| row.iter())
200 .copied()
201 .max()
202 .unwrap_or(0) as i64;
203 let mut cost = vec![vec![max_val; size]; size];
204 for (i, row) in matrix.iter().enumerate() {
205 for (j, value) in row.iter().enumerate() {
206 cost[i][j] = max_val - (*value as i64);
207 }
208 }
209
210 let assignments = hungarian(&cost);
211 assignments
212 .into_iter()
213 .take(rows)
214 .map(|col| (col < cols).then_some(col))
215 .collect()
216}
217
218fn hungarian(cost: &[Vec<i64>]) -> Vec<usize> {
219 let size = cost.len();
220 let mut u = vec![0i64; size + 1];
221 let mut v = vec![0i64; size + 1];
222 let mut p = vec![0usize; size + 1];
223 let mut way = vec![0usize; size + 1];
224
225 for i in 1..=size {
226 p[0] = i;
227 let mut minv = vec![i64::MAX; size + 1];
228 let mut used = vec![false; size + 1];
229 let mut j0 = 0usize;
230 loop {
231 used[j0] = true;
232 let i0 = p[j0];
233 let mut delta = i64::MAX;
234 let mut j1 = 0usize;
235 for j in 1..=size {
236 if used[j] {
237 continue;
238 }
239 let cur = cost[i0 - 1][j - 1] - u[i0] - v[j];
240 if cur < minv[j] {
241 minv[j] = cur;
242 way[j] = j0;
243 }
244 if minv[j] < delta {
245 delta = minv[j];
246 j1 = j;
247 }
248 }
249 for j in 0..=size {
250 if used[j] {
251 u[p[j]] += delta;
252 v[j] -= delta;
253 } else if minv[j] != i64::MAX {
254 minv[j] -= delta;
255 }
256 }
257 j0 = j1;
258 if p[j0] == 0 {
259 break;
260 }
261 }
262 loop {
263 let j1 = way[j0];
264 p[j0] = p[j1];
265 j0 = j1;
266 if j0 == 0 {
267 break;
268 }
269 }
270 }
271
272 let mut result = vec![usize::MAX; size];
273 for j in 1..=size {
274 if p[j] > 0 {
275 result[p[j] - 1] = j - 1;
276 }
277 }
278 result
279}