#![allow(dead_code)]
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct VertexWeight {
pub vertex_index: usize,
pub weight: f32,
}
#[derive(Debug, Clone)]
pub struct VertexWeightGroup {
pub name: String,
pub entries: Vec<VertexWeight>,
}
impl VertexWeightGroup {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
entries: Vec::new(),
}
}
pub fn set_weight(&mut self, vertex: usize, weight: f32) {
if let Some(e) = self.entries.iter_mut().find(|e| e.vertex_index == vertex) {
e.weight = weight.clamp(0.0, 1.0);
} else {
self.entries.push(VertexWeight {
vertex_index: vertex,
weight: weight.clamp(0.0, 1.0),
});
}
}
pub fn get_weight(&self, vertex: usize) -> f32 {
self.entries
.iter()
.find(|e| e.vertex_index == vertex)
.map_or(0.0, |e| e.weight)
}
pub fn entry_count(&self) -> usize {
self.entries.len()
}
}
#[derive(Debug, Clone)]
pub struct VertexWeightGroupSet {
pub groups: HashMap<String, VertexWeightGroup>,
}
impl VertexWeightGroupSet {
pub fn new() -> Self {
Self {
groups: HashMap::new(),
}
}
pub fn add_group(&mut self, group: VertexWeightGroup) {
self.groups.insert(group.name.clone(), group);
}
pub fn group_count(&self) -> usize {
self.groups.len()
}
pub fn get_group_mut(&mut self, name: &str) -> Option<&mut VertexWeightGroup> {
self.groups.get_mut(name)
}
}
impl Default for VertexWeightGroupSet {
fn default() -> Self {
Self::new()
}
}
pub fn normalize_across_groups(set: &mut VertexWeightGroupSet, vertex: usize) {
let sum: f32 = set.groups.values().map(|g| g.get_weight(vertex)).sum();
if sum > 1e-6 {
for g in set.groups.values_mut() {
if let Some(e) = g.entries.iter_mut().find(|e| e.vertex_index == vertex) {
e.weight /= sum;
}
}
}
}
pub fn dominant_group(set: &VertexWeightGroupSet, vertex: usize) -> Option<&str> {
set.groups
.values()
.max_by(|a, b| {
a.get_weight(vertex)
.partial_cmp(&b.get_weight(vertex))
.unwrap_or(std::cmp::Ordering::Equal)
})
.filter(|g| g.get_weight(vertex) > 0.0)
.map(|g| g.name.as_str())
}
#[cfg(test)]
mod tests {
use super::*;
fn build_set() -> VertexWeightGroupSet {
let mut set = VertexWeightGroupSet::new();
let mut g1 = VertexWeightGroup::new("bones");
g1.set_weight(0, 0.8);
g1.set_weight(1, 0.5);
let mut g2 = VertexWeightGroup::new("muscles");
g2.set_weight(0, 0.3);
set.add_group(g1);
set.add_group(g2);
set
}
#[test]
fn test_group_count() {
let s = build_set();
assert_eq!(s.group_count(), 2);
}
#[test]
fn test_get_weight() {
let s = build_set();
let g = s.groups.get("bones").expect("should succeed");
assert!((g.get_weight(0) - 0.8).abs() < 1e-6);
}
#[test]
fn test_set_weight_clamps() {
let mut g = VertexWeightGroup::new("test");
g.set_weight(0, 2.5);
assert!((g.get_weight(0) - 1.0).abs() < 1e-6);
}
#[test]
fn test_set_weight_updates_existing() {
let mut g = VertexWeightGroup::new("test");
g.set_weight(0, 0.5);
g.set_weight(0, 0.9);
assert_eq!(g.entry_count(), 1);
assert!((g.get_weight(0) - 0.9).abs() < 1e-6);
}
#[test]
fn test_get_weight_missing_vertex_zero() {
let g = VertexWeightGroup::new("empty");
assert_eq!(g.get_weight(5), 0.0);
}
#[test]
fn test_normalize_across_groups() {
let mut s = build_set();
normalize_across_groups(&mut s, 0);
let total: f32 = s.groups.values().map(|g| g.get_weight(0)).sum();
assert!((total - 1.0).abs() < 1e-5);
}
#[test]
fn test_dominant_group() {
let s = build_set();
let dom = dominant_group(&s, 0);
assert!(dom == Some("bones"));
}
#[test]
fn test_dominant_group_zero_weight() {
let s = VertexWeightGroupSet::new();
assert!(dominant_group(&s, 0).is_none());
}
}