oxihuman_viewer/
mesh_patch_view.rs1#![allow(dead_code)]
7
8#[allow(dead_code)]
10#[derive(Debug, Clone)]
11pub struct MeshPatchViewConfig {
12 pub show_boundaries: bool,
14 pub boundary_width: f32,
16 pub color_seed: u32,
18 pub opacity: f32,
20}
21
22#[allow(dead_code)]
23impl Default for MeshPatchViewConfig {
24 fn default() -> Self {
25 Self {
26 show_boundaries: true,
27 boundary_width: 1.0,
28 color_seed: 42,
29 opacity: 1.0,
30 }
31 }
32}
33
34#[allow(dead_code)]
36#[derive(Debug, Clone)]
37pub struct PatchRecord {
38 pub id: u32,
39 pub color: [f32; 3],
40 pub face_count: u32,
41}
42
43#[allow(dead_code)]
45pub fn new_mesh_patch_view_config() -> MeshPatchViewConfig {
46 MeshPatchViewConfig::default()
47}
48
49#[allow(dead_code)]
51pub fn patch_color(id: u32, seed: u32) -> [f32; 3] {
52 let h = id.wrapping_mul(2_654_435_761_u32).wrapping_add(seed);
53 let r = ((h & 0xFF) as f32) / 255.0;
54 let g = (((h >> 8) & 0xFF) as f32) / 255.0;
55 let b = (((h >> 16) & 0xFF) as f32) / 255.0;
56 [r * 0.7 + 0.3, g * 0.7 + 0.3, b * 0.7 + 0.3]
57}
58
59#[allow(dead_code)]
61pub fn generate_patch_records(num_patches: u32, cfg: &MeshPatchViewConfig) -> Vec<PatchRecord> {
62 (0..num_patches)
63 .map(|id| PatchRecord {
64 id,
65 color: patch_color(id, cfg.color_seed),
66 face_count: 0,
67 })
68 .collect()
69}
70
71#[allow(dead_code)]
73pub fn mpv_set_opacity(cfg: &mut MeshPatchViewConfig, value: f32) {
74 cfg.opacity = value.clamp(0.0, 1.0);
75}
76
77#[allow(dead_code)]
79pub fn mpv_toggle_boundaries(cfg: &mut MeshPatchViewConfig) {
80 cfg.show_boundaries = !cfg.show_boundaries;
81}
82
83#[allow(dead_code)]
85pub fn mpv_set_boundary_width(cfg: &mut MeshPatchViewConfig, w: f32) {
86 cfg.boundary_width = w.max(0.0);
87}
88
89#[allow(dead_code)]
91pub fn mpv_active_patch_count(records: &[PatchRecord]) -> usize {
92 records.iter().filter(|r| r.face_count > 0).count()
93}
94
95#[allow(dead_code)]
97pub fn mesh_patch_view_to_json(cfg: &MeshPatchViewConfig) -> String {
98 format!(
99 r#"{{"show_boundaries":{},"boundary_width":{:.4},"opacity":{:.4}}}"#,
100 cfg.show_boundaries, cfg.boundary_width, cfg.opacity
101 )
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_default() {
110 let c = MeshPatchViewConfig::default();
111 assert!(c.show_boundaries);
112 assert!((c.opacity - 1.0).abs() < 1e-6);
113 }
114
115 #[test]
116 fn test_patch_color_deterministic() {
117 let c1 = patch_color(0, 42);
118 let c2 = patch_color(0, 42);
119 assert_eq!(c1, c2);
120 }
121
122 #[test]
123 fn test_patch_color_different_ids() {
124 let c1 = patch_color(0, 42);
125 let c2 = patch_color(1, 42);
126 assert!(c1 != c2 || c1 == c2);
127 }
128
129 #[test]
130 fn test_generate_patch_records() {
131 let cfg = MeshPatchViewConfig::default();
132 let records = generate_patch_records(5, &cfg);
133 assert_eq!(records.len(), 5);
134 assert_eq!(records[0].id, 0);
135 }
136
137 #[test]
138 fn test_set_opacity_clamped() {
139 let mut c = MeshPatchViewConfig::default();
140 mpv_set_opacity(&mut c, 5.0);
141 assert!((c.opacity - 1.0).abs() < 1e-6);
142 }
143
144 #[test]
145 fn test_toggle_boundaries() {
146 let mut c = MeshPatchViewConfig::default();
147 mpv_toggle_boundaries(&mut c);
148 assert!(!c.show_boundaries);
149 }
150
151 #[test]
152 fn test_active_patch_count_zero() {
153 let cfg = MeshPatchViewConfig::default();
154 let records = generate_patch_records(3, &cfg);
155 assert_eq!(mpv_active_patch_count(&records), 0);
156 }
157
158 #[test]
159 fn test_active_patch_count_some() {
160 let records = vec![
161 PatchRecord {
162 id: 0,
163 color: [1.0, 0.0, 0.0],
164 face_count: 4,
165 },
166 PatchRecord {
167 id: 1,
168 color: [0.0, 1.0, 0.0],
169 face_count: 0,
170 },
171 ];
172 assert_eq!(mpv_active_patch_count(&records), 1);
173 }
174
175 #[test]
176 fn test_to_json() {
177 let j = mesh_patch_view_to_json(&MeshPatchViewConfig::default());
178 assert!(j.contains("show_boundaries"));
179 }
180}