#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SelectionMode {
Face,
Vertex,
Edge,
Object,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct SelectionState {
pub mode: SelectionMode,
selected: Vec<bool>,
pub item_count: usize,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct SelectionRendererConfig {
pub outline_color: [f32; 4],
pub outline_width: f32,
pub fill_selected: bool,
pub fill_color: [f32; 4],
pub default_mode: SelectionMode,
}
#[allow(dead_code)]
pub fn default_selection_config() -> SelectionRendererConfig {
SelectionRendererConfig {
outline_color: [1.0, 0.6, 0.0, 1.0],
outline_width: 2.0,
fill_selected: true,
fill_color: [1.0, 0.6, 0.0, 0.2],
default_mode: SelectionMode::Vertex,
}
}
#[allow(dead_code)]
pub fn new_selection_state(item_count: usize, mode: SelectionMode) -> SelectionState {
SelectionState {
mode,
selected: vec![false; item_count],
item_count,
}
}
#[allow(dead_code)]
pub fn select_vertices(state: &mut SelectionState, indices: &[usize]) {
for &i in indices {
if i < state.item_count {
state.selected[i] = true;
}
}
}
#[allow(dead_code)]
pub fn deselect_vertices(state: &mut SelectionState, indices: &[usize]) {
for &i in indices {
if i < state.item_count {
state.selected[i] = false;
}
}
}
#[allow(dead_code)]
pub fn toggle_vertex_selection(state: &mut SelectionState, indices: &[usize]) {
for &i in indices {
if i < state.item_count {
state.selected[i] = !state.selected[i];
}
}
}
#[allow(dead_code)]
pub fn select_all(state: &mut SelectionState) {
state.selected.iter_mut().for_each(|s| *s = true);
}
#[allow(dead_code)]
pub fn deselect_all(state: &mut SelectionState) {
state.selected.iter_mut().for_each(|s| *s = false);
}
#[allow(dead_code)]
pub fn invert_selection(state: &mut SelectionState) {
state.selected.iter_mut().for_each(|s| *s = !*s);
}
#[allow(dead_code)]
pub fn selected_vertex_count(state: &SelectionState) -> usize {
state.selected.iter().filter(|&&s| s).count()
}
#[allow(dead_code)]
pub fn is_vertex_selected(state: &SelectionState, index: usize) -> bool {
state.selected.get(index).copied().unwrap_or(false)
}
#[allow(dead_code)]
pub fn selection_to_index_list(state: &SelectionState) -> Vec<usize> {
state
.selected
.iter()
.enumerate()
.filter_map(|(i, &s)| if s { Some(i) } else { None })
.collect()
}
pub type SelectionAabb = [f32; 6];
#[allow(dead_code)]
pub fn selection_bounding_box(
state: &SelectionState,
positions: &[[f32; 3]],
) -> Option<SelectionAabb> {
let mut min = [f32::MAX; 3];
let mut max = [f32::MIN; 3];
let mut found = false;
for (i, &sel) in state.selected.iter().enumerate() {
if !sel {
continue;
}
let p = positions.get(i)?;
for k in 0..3 {
if p[k] < min[k] {
min[k] = p[k];
}
if p[k] > max[k] {
max[k] = p[k];
}
}
found = true;
}
if found {
Some([min[0], min[1], min[2], max[0], max[1], max[2]])
} else {
None
}
}
#[allow(dead_code)]
pub fn selection_to_json(state: &SelectionState) -> String {
let mode = match state.mode {
SelectionMode::Face => "face",
SelectionMode::Vertex => "vertex",
SelectionMode::Edge => "edge",
SelectionMode::Object => "object",
};
let indices = selection_to_index_list(state);
let idx_json: Vec<String> = indices.iter().map(|i| i.to_string()).collect();
format!(
"{{\"mode\":\"{}\",\"count\":{},\"indices\":[{}]}}",
mode,
indices.len(),
idx_json.join(",")
)
}
#[allow(dead_code)]
pub fn grow_selection(state: &mut SelectionState, adjacency: &[Vec<usize>]) {
let to_add: Vec<usize> = state
.selected
.iter()
.enumerate()
.filter(|(_, &s)| s)
.flat_map(|(i, _)| {
adjacency
.get(i)
.map(|v| v.as_slice())
.unwrap_or(&[])
.to_vec()
})
.collect();
for i in to_add {
if i < state.item_count {
state.selected[i] = true;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_state(n: usize) -> SelectionState {
new_selection_state(n, SelectionMode::Vertex)
}
#[test]
fn test_default_config_outline_width() {
let cfg = default_selection_config();
assert!((cfg.outline_width - 2.0).abs() < 1e-6);
}
#[test]
fn test_default_config_fill_selected() {
let cfg = default_selection_config();
assert!(cfg.fill_selected);
}
#[test]
fn test_new_state_all_unselected() {
let s = make_state(5);
assert_eq!(selected_vertex_count(&s), 0);
}
#[test]
fn test_new_state_item_count() {
let s = make_state(10);
assert_eq!(s.item_count, 10);
}
#[test]
fn test_select_vertices() {
let mut s = make_state(5);
select_vertices(&mut s, &[0, 2, 4]);
assert_eq!(selected_vertex_count(&s), 3);
}
#[test]
fn test_select_out_of_range_ignored() {
let mut s = make_state(3);
select_vertices(&mut s, &[100]);
assert_eq!(selected_vertex_count(&s), 0);
}
#[test]
fn test_deselect_vertices() {
let mut s = make_state(5);
select_all(&mut s);
deselect_vertices(&mut s, &[1, 3]);
assert_eq!(selected_vertex_count(&s), 3);
}
#[test]
fn test_toggle_vertex_selection() {
let mut s = make_state(4);
select_vertices(&mut s, &[0, 1]);
toggle_vertex_selection(&mut s, &[1, 2]);
assert!(is_vertex_selected(&s, 0));
assert!(!is_vertex_selected(&s, 1));
assert!(is_vertex_selected(&s, 2));
assert!(!is_vertex_selected(&s, 3));
}
#[test]
fn test_select_all() {
let mut s = make_state(4);
select_all(&mut s);
assert_eq!(selected_vertex_count(&s), 4);
}
#[test]
fn test_deselect_all() {
let mut s = make_state(4);
select_all(&mut s);
deselect_all(&mut s);
assert_eq!(selected_vertex_count(&s), 0);
}
#[test]
fn test_invert_selection() {
let mut s = make_state(4);
select_vertices(&mut s, &[0, 2]);
invert_selection(&mut s);
assert!(is_vertex_selected(&s, 1));
assert!(is_vertex_selected(&s, 3));
assert!(!is_vertex_selected(&s, 0));
assert!(!is_vertex_selected(&s, 2));
}
#[test]
fn test_is_vertex_selected_in_range() {
let mut s = make_state(3);
select_vertices(&mut s, &[1]);
assert!(is_vertex_selected(&s, 1));
assert!(!is_vertex_selected(&s, 0));
}
#[test]
fn test_is_vertex_selected_out_of_range() {
let s = make_state(3);
assert!(!is_vertex_selected(&s, 99));
}
#[test]
fn test_selection_to_index_list() {
let mut s = make_state(5);
select_vertices(&mut s, &[1, 3]);
let list = selection_to_index_list(&s);
assert_eq!(list, vec![1, 3]);
}
#[test]
fn test_selection_bounding_box() {
let mut s = make_state(3);
select_vertices(&mut s, &[0, 2]);
let positions = vec![[0.0f32, 0.0, 0.0], [5.0, 5.0, 5.0], [2.0, 3.0, 1.0]];
let aabb = selection_bounding_box(&s, &positions).expect("should succeed");
assert!((aabb[0] - 0.0).abs() < 1e-6); assert!((aabb[3] - 2.0).abs() < 1e-6); }
#[test]
fn test_selection_bounding_box_empty() {
let s = make_state(3);
let positions = vec![[0.0f32; 3]; 3];
assert!(selection_bounding_box(&s, &positions).is_none());
}
#[test]
fn test_selection_to_json_format() {
let mut s = make_state(4);
select_vertices(&mut s, &[0, 3]);
let json = selection_to_json(&s);
assert!(json.contains("\"mode\":\"vertex\""));
assert!(json.contains("\"count\":2"));
}
#[test]
fn test_grow_selection() {
let mut s = make_state(4);
select_vertices(&mut s, &[0]);
let adj: Vec<Vec<usize>> = vec![vec![1, 2], vec![3], vec![], vec![]];
grow_selection(&mut s, &adj);
assert!(is_vertex_selected(&s, 1));
assert!(is_vertex_selected(&s, 2));
assert!(!is_vertex_selected(&s, 3)); }
}