rpfm_lib/files/group_formations/
edit.rs1use std::collections::{BTreeSet, HashMap, HashSet};
17
18use crate::error::{Result, RLibError};
19
20use super::{Block, ContainerAbsolute, ContainerRelative, EntityArrangement, EntityPreference, GroupFormation, GroupFormationBlock, GroupFormationsFormat, Spanning};
21use super::layout::{self, LayoutParams, LayoutRect};
22
23impl GroupFormation {
28
29 pub fn add_container(&mut self, parent_id: Option<u32>, position: (f32, f32), format: GroupFormationsFormat) -> Result<u32> {
45 let container = ContainerAbsolute {
46 block_priority: 1.0,
47 entity_arrangement: EntityArrangement::Line,
48 inter_entity_spacing: 2.0,
49 crescent_y_offset: 0.0,
50 position_x: position.0,
51 position_y: position.1,
52 minimum_entity_threshold: 0,
53 maximum_entity_threshold: -1,
54 entity_preferences: vec![EntityPreference::new(format)],
55 };
56
57 let block = match parent_id {
58 Some(parent_id) => {
59 self.position_of(parent_id)?;
60 Block::ContainerRelative(to_relative(container, parent_id))
61 },
62 None => Block::ContainerAbsolute(container),
63 };
64
65 Ok(self.push_block(block))
66 }
67
68 pub fn add_span(&mut self, member_ids: &[u32]) -> Result<u32> {
82 for member_id in member_ids {
83 self.position_of(*member_id)?;
84 }
85
86 Ok(self.push_block(Block::Spanning(Spanning { spanned_block_ids: member_ids.to_vec() })))
87 }
88
89 pub fn set_span_members(&mut self, span_id: u32, member_ids: &[u32]) -> Result<()> {
100 let span_position = self.position_of(span_id)?;
101 for member_id in member_ids {
102 self.position_of(*member_id)?;
103 if self.depends_on(*member_id, span_id) {
104 return Err(RLibError::GroupFormationsCyclicReference(span_id, *member_id));
105 }
106 }
107
108 match &mut self.group_formation_blocks[span_position].block {
109 Block::Spanning(span) => span.spanned_block_ids = member_ids.to_vec(),
110 Block::ContainerAbsolute(_) |
111 Block::ContainerRelative(_) => return Err(RLibError::GroupFormationsBlockIsNotASpan(span_id)),
112 }
113
114 self.sort_blocks();
115 Ok(())
116 }
117
118 pub fn reparent(&mut self, block_id: u32, parent_id: Option<u32>, params: &LayoutParams) -> Result<()> {
130 let position = self.position_of(block_id)?;
131 if let Some(parent_id) = parent_id {
132 self.position_of(parent_id)?;
133 if self.depends_on(parent_id, block_id) {
134 return Err(RLibError::GroupFormationsCyclicReference(block_id, parent_id));
135 }
136 }
137
138 if let Block::Spanning(_) = self.group_formation_blocks[position].block {
139 return Err(RLibError::GroupFormationsBlockIsNotAContainer(block_id));
140 }
141
142 let rects = self.layout(params);
143 self.attach(position, parent_id, &rects);
144 self.sort_blocks();
145 Ok(())
146 }
147
148 pub fn move_block(&mut self, block_id: u32, center: (f32, f32), grid_step: f32, params: &LayoutParams) -> Result<()> {
161 let position = self.position_of(block_id)?;
162 let rects = self.layout(params);
163 let rect = rects.get(&block_id).ok_or(RLibError::GroupFormationsBlockNotPlaceable(block_id))?;
164
165 match &mut self.group_formation_blocks[position].block {
166 Block::ContainerAbsolute(container) => {
167 container.position_x = snap(center.0, grid_step);
168 container.position_y = snap(center.1, grid_step);
169 },
170 Block::ContainerRelative(container) => {
171 let parent = rects.get(&container.relative_block_id).ok_or(RLibError::GroupFormationsBlockNotPlaceable(container.relative_block_id))?;
172 let (offset_x, offset_y) = layout::offset_for_center(parent, rect.width(), rect.height(), center.0, center.1);
173 container.position_x = snap(offset_x, grid_step);
174 container.position_y = snap(offset_y, grid_step);
175 },
176 Block::Spanning(_) => return Err(RLibError::GroupFormationsBlockIsNotAContainer(block_id)),
177 }
178
179 Ok(())
180 }
181
182 pub fn move_blocks(&mut self, block_ids: &[u32], delta: (f32, f32), grid_step: f32, params: &LayoutParams) -> Result<()> {
198 for block_id in block_ids {
199 self.position_of(*block_id)?;
200 }
201
202 let mut containers = vec![];
204 let mut visited = HashSet::new();
205 let mut pending = block_ids.to_vec();
206 while let Some(block_id) = pending.pop() {
207 let Ok(position) = self.position_of(block_id) else { continue };
208 if !visited.insert(block_id) {
209 continue;
210 }
211
212 match &self.group_formation_blocks[position].block {
213 Block::Spanning(span) => pending.extend_from_slice(&span.spanned_block_ids),
214 Block::ContainerAbsolute(_) |
215 Block::ContainerRelative(_) => containers.push(block_id),
216 }
217 }
218
219 let rects = self.layout(params);
220 let mut targets = vec![];
221 for block_id in &containers {
222 if containers.iter().any(|other_id| other_id != block_id && self.depends_on(*block_id, *other_id)) {
223 continue;
224 }
225
226 let rect = rects.get(block_id).ok_or(RLibError::GroupFormationsBlockNotPlaceable(*block_id))?;
227 targets.push((*block_id, (rect.center_x() + delta.0, rect.center_y() + delta.1)));
228 }
229
230 for (block_id, center) in targets {
232 self.move_block(block_id, center, grid_step, params)?;
233 }
234
235 Ok(())
236 }
237
238 pub fn delete_blocks(&mut self, block_ids: &[u32], params: &LayoutParams) -> Result<()> {
251 for block_id in block_ids {
252 self.position_of(*block_id)?;
253 }
254
255 let rects = self.layout(params);
256 let deleted = self.with_emptied_spans(block_ids.iter().copied().collect());
257
258 let parents = self.group_formation_blocks.iter()
259 .filter_map(|block| match &block.block {
260 Block::ContainerRelative(container) => Some((block.block_id, container.relative_block_id)),
261 Block::ContainerAbsolute(_) |
262 Block::Spanning(_) => None,
263 })
264 .collect::<HashMap<_, _>>();
265
266 for position in 0..self.group_formation_blocks.len() {
267 let block_id = self.group_formation_blocks[position].block_id;
268 let Some(parent_id) = parents.get(&block_id) else { continue };
269 if deleted.contains(&block_id) || !deleted.contains(parent_id) {
270 continue;
271 }
272
273 let mut ancestor = Some(*parent_id);
275 for _ in 0..=self.group_formation_blocks.len() {
276 match ancestor {
277 Some(ancestor_id) if deleted.contains(&ancestor_id) => ancestor = parents.get(&ancestor_id).copied(),
278 _ => break,
279 }
280 }
281
282 let ancestor = ancestor.filter(|ancestor_id| !deleted.contains(ancestor_id));
283 self.attach(position, ancestor, &rects);
284 }
285
286 self.group_formation_blocks.retain(|block| !deleted.contains(&block.block_id));
287 for block in &mut self.group_formation_blocks {
288 if let Block::Spanning(span) = &mut block.block {
289 span.spanned_block_ids.retain(|member_id| !deleted.contains(member_id));
290 }
291 }
292
293 self.sort_blocks();
294 Ok(())
295 }
296
297 pub fn delete_subtree(&mut self, block_id: u32, params: &LayoutParams) -> Result<()> {
308 self.position_of(block_id)?;
309
310 let mut subtree = HashSet::from([block_id]);
311 loop {
312 let children = self.group_formation_blocks.iter()
313 .filter(|block| !subtree.contains(&block.block_id))
314 .filter(|block| match &block.block {
315 Block::ContainerRelative(container) => subtree.contains(&container.relative_block_id),
316 Block::ContainerAbsolute(_) => false,
317 Block::Spanning(span) => !span.spanned_block_ids.is_empty() && span.spanned_block_ids.iter().all(|member_id| subtree.contains(member_id)),
318 })
319 .map(|block| block.block_id)
320 .collect::<Vec<_>>();
321
322 if children.is_empty() {
323 break;
324 }
325
326 subtree.extend(children);
327 }
328
329 self.delete_blocks(&subtree.into_iter().collect::<Vec<_>>(), params)
330 }
331
332 pub fn sort_blocks(&mut self) {
336 let mut positions = HashMap::with_capacity(self.group_formation_blocks.len());
337 for (position, block) in self.group_formation_blocks.iter().enumerate() {
338 positions.entry(block.block_id).or_insert(position);
339 }
340
341 let mut pending_dependencies = vec![0; self.group_formation_blocks.len()];
343 let mut dependents = vec![vec![]; self.group_formation_blocks.len()];
344 for (position, block) in self.group_formation_blocks.iter().enumerate() {
345 for dependency in block.block.dependencies() {
346 if let Some(dependency_position) = positions.get(dependency) {
347 pending_dependencies[position] += 1;
348 dependents[*dependency_position].push(position);
349 }
350 }
351 }
352
353 let mut ready = pending_dependencies.iter()
354 .enumerate()
355 .filter(|(_, pending)| **pending == 0)
356 .map(|(position, _)| position)
357 .collect::<BTreeSet<_>>();
358
359 let mut order = Vec::with_capacity(self.group_formation_blocks.len());
360 while let Some(position) = ready.pop_first() {
361 order.push(position);
362 for dependent in &dependents[position] {
363 pending_dependencies[*dependent] -= 1;
364 if pending_dependencies[*dependent] == 0 {
365 ready.insert(*dependent);
366 }
367 }
368 }
369
370 let sorted = order.iter().copied().collect::<HashSet<_>>();
371 order.extend((0..self.group_formation_blocks.len()).filter(|position| !sorted.contains(position)));
372
373 let mut blocks = std::mem::take(&mut self.group_formation_blocks).into_iter().map(Some).collect::<Vec<_>>();
374 self.group_formation_blocks = order.iter()
375 .map(|position| blocks[*position].take().expect("every position appears once in the order"))
376 .collect();
377 }
378
379 fn position_of(&self, block_id: u32) -> Result<usize> {
381 self.group_formation_blocks.iter()
382 .position(|block| block.block_id == block_id)
383 .ok_or(RLibError::GroupFormationsBlockNotFound(block_id))
384 }
385
386 pub fn depends_on(&self, block_id: u32, target_id: u32) -> bool {
397 let mut visited = HashSet::new();
398 let mut pending = vec![block_id];
399 while let Some(current_id) = pending.pop() {
400 if current_id == target_id {
401 return true;
402 }
403
404 if visited.insert(current_id) {
405 if let Ok(position) = self.position_of(current_id) {
406 pending.extend_from_slice(self.group_formation_blocks[position].block.dependencies());
407 }
408 }
409 }
410
411 false
412 }
413
414 fn with_emptied_spans(&self, mut deleted: HashSet<u32>) -> HashSet<u32> {
416 loop {
417 let emptied = self.group_formation_blocks.iter()
418 .filter(|block| !deleted.contains(&block.block_id))
419 .filter(|block| match &block.block {
420 Block::Spanning(span) => !span.spanned_block_ids.is_empty() && span.spanned_block_ids.iter().all(|member_id| deleted.contains(member_id)),
421 Block::ContainerAbsolute(_) |
422 Block::ContainerRelative(_) => false,
423 })
424 .map(|block| block.block_id)
425 .collect::<Vec<_>>();
426
427 if emptied.is_empty() {
428 return deleted;
429 }
430
431 deleted.extend(emptied);
432 }
433 }
434
435 fn attach(&mut self, position: usize, parent_id: Option<u32>, rects: &HashMap<u32, LayoutRect>) {
437 let block = &mut self.group_formation_blocks[position];
438 let rect = rects.get(&block.block_id);
439 let mut container = match std::mem::take(&mut block.block) {
440 Block::ContainerAbsolute(container) => container,
441 Block::ContainerRelative(container) => to_absolute(container),
442 span @ Block::Spanning(_) => {
443 block.block = span;
444 return;
445 },
446 };
447
448 block.block = match parent_id {
449 Some(parent_id) => {
450 if let (Some(rect), Some(parent)) = (rect, rects.get(&parent_id)) {
451 (container.position_x, container.position_y) = layout::offset_for_center(parent, rect.width(), rect.height(), rect.center_x(), rect.center_y());
452 }
453 Block::ContainerRelative(to_relative(container, parent_id))
454 },
455 None => {
456 if let Some(rect) = rect {
457 (container.position_x, container.position_y) = (rect.center_x(), rect.center_y());
458 }
459 Block::ContainerAbsolute(container)
460 },
461 };
462 }
463
464 fn push_block(&mut self, block: Block) -> u32 {
466 let block_id = self.group_formation_blocks.iter().map(|block| block.block_id.saturating_add(1)).max().unwrap_or(0);
467 self.group_formation_blocks.push(GroupFormationBlock { block_id, block });
468 block_id
469 }
470}
471
472fn to_absolute(container: ContainerRelative) -> ContainerAbsolute {
474 ContainerAbsolute {
475 block_priority: container.block_priority,
476 entity_arrangement: container.entity_arrangement,
477 inter_entity_spacing: container.inter_entity_spacing,
478 crescent_y_offset: container.crescent_y_offset,
479 position_x: container.position_x,
480 position_y: container.position_y,
481 minimum_entity_threshold: container.minimum_entity_threshold,
482 maximum_entity_threshold: container.maximum_entity_threshold,
483 entity_preferences: container.entity_preferences,
484 }
485}
486
487fn to_relative(container: ContainerAbsolute, relative_block_id: u32) -> ContainerRelative {
489 ContainerRelative {
490 block_priority: container.block_priority,
491 relative_block_id,
492 entity_arrangement: container.entity_arrangement,
493 inter_entity_spacing: container.inter_entity_spacing,
494 crescent_y_offset: container.crescent_y_offset,
495 position_x: container.position_x,
496 position_y: container.position_y,
497 minimum_entity_threshold: container.minimum_entity_threshold,
498 maximum_entity_threshold: container.maximum_entity_threshold,
499 entity_preferences: container.entity_preferences,
500 }
501}
502
503fn snap(value: f32, grid_step: f32) -> f32 {
505 if grid_step > 0.0 {
506 (value / grid_step).round() * grid_step
507 } else {
508 value
509 }
510}