oxihuman_viewer/
pixel_sort_effect.rs1#![allow(dead_code)]
3
4#[allow(dead_code)]
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
9pub enum SortDirection {
10 #[default]
11 Horizontal,
12 Vertical,
13 Diagonal,
14}
15
16#[allow(dead_code)]
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
19pub enum SortKey {
20 #[default]
21 Luminance,
22 Hue,
23 Saturation,
24 Red,
25 Green,
26 Blue,
27}
28
29#[allow(dead_code)]
31#[derive(Debug, Clone, PartialEq)]
32pub struct PixelSortEffectConfig {
33 pub direction: SortDirection,
34 pub sort_key: SortKey,
35 pub threshold_low: f32,
37 pub threshold_high: f32,
39 pub intensity: f32,
41 pub seed: u64,
43 pub enabled: bool,
44}
45
46impl Default for PixelSortEffectConfig {
47 fn default() -> Self {
48 Self {
49 direction: SortDirection::Horizontal,
50 sort_key: SortKey::Luminance,
51 threshold_low: 0.2,
52 threshold_high: 0.8,
53 intensity: 0.7,
54 seed: 12345,
55 enabled: true,
56 }
57 }
58}
59
60#[allow(dead_code)]
61pub fn new_pixel_sort_effect_config() -> PixelSortEffectConfig {
62 PixelSortEffectConfig::default()
63}
64
65#[allow(dead_code)]
66pub fn ps_set_threshold_low(cfg: &mut PixelSortEffectConfig, v: f32) {
67 cfg.threshold_low = v.clamp(0.0, 1.0);
68}
69
70#[allow(dead_code)]
71pub fn ps_set_threshold_high(cfg: &mut PixelSortEffectConfig, v: f32) {
72 cfg.threshold_high = v.clamp(0.0, 1.0);
73}
74
75#[allow(dead_code)]
76pub fn ps_set_intensity(cfg: &mut PixelSortEffectConfig, v: f32) {
77 cfg.intensity = v.clamp(0.0, 1.0);
78}
79
80#[allow(dead_code)]
82pub fn ps_sort_range(cfg: &PixelSortEffectConfig) -> f32 {
83 (cfg.threshold_high - cfg.threshold_low).max(0.0)
84}
85
86#[allow(dead_code)]
88pub fn ps_should_sort(luminance: f32, cfg: &PixelSortEffectConfig) -> bool {
89 luminance >= cfg.threshold_low && luminance <= cfg.threshold_high
90}
91
92#[allow(dead_code)]
94pub fn ps_sort_row(row: &mut [f32], cfg: &PixelSortEffectConfig) {
95 if !cfg.enabled {
96 return;
97 }
98 let n = row.len();
100 let mut i = 0;
101 while i < n {
102 if ps_should_sort(row[i], cfg) {
103 let start = i;
104 while i < n && ps_should_sort(row[i], cfg) {
105 i += 1;
106 }
107 row[start..i].sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
108 } else {
109 i += 1;
110 }
111 }
112}
113
114#[allow(dead_code)]
115pub fn ps_to_json(cfg: &PixelSortEffectConfig) -> String {
116 format!(
117 "{{\"threshold_low\":{:.3},\"threshold_high\":{:.3},\"intensity\":{:.3},\"enabled\":{}}}",
118 cfg.threshold_low, cfg.threshold_high, cfg.intensity, cfg.enabled
119 )
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn should_sort_in_range() {
128 let cfg = new_pixel_sort_effect_config();
129 assert!(ps_should_sort(0.5, &cfg));
130 }
131
132 #[test]
133 fn should_not_sort_below() {
134 let cfg = new_pixel_sort_effect_config();
135 assert!(!ps_should_sort(0.1, &cfg));
136 }
137
138 #[test]
139 fn should_not_sort_above() {
140 let cfg = new_pixel_sort_effect_config();
141 assert!(!ps_should_sort(0.9, &cfg));
142 }
143
144 #[test]
145 fn sort_range_positive() {
146 let cfg = new_pixel_sort_effect_config();
147 assert!(ps_sort_range(&cfg) > 0.0);
148 }
149
150 #[test]
151 fn sort_row_ascending() {
152 let cfg = new_pixel_sort_effect_config();
153 let mut row = vec![0.7f32, 0.3, 0.5, 0.6, 0.4];
154 ps_sort_row(&mut row, &cfg);
155 for i in 0..row.len() - 1 {
156 assert!(row[i] <= row[i + 1] || !ps_should_sort(row[i], &cfg));
157 }
158 }
159
160 #[test]
161 fn disabled_does_not_sort() {
162 let mut cfg = new_pixel_sort_effect_config();
163 cfg.enabled = false;
164 let mut row = vec![0.7f32, 0.3, 0.5];
165 let orig = row.clone();
166 ps_sort_row(&mut row, &cfg);
167 assert_eq!(row, orig);
168 }
169
170 #[test]
171 fn threshold_low_clamps() {
172 let mut cfg = new_pixel_sort_effect_config();
173 ps_set_threshold_low(&mut cfg, -1.0);
174 assert!(cfg.threshold_low < 1e-6);
175 }
176
177 #[test]
178 fn threshold_high_clamps() {
179 let mut cfg = new_pixel_sort_effect_config();
180 ps_set_threshold_high(&mut cfg, 5.0);
181 assert!((cfg.threshold_high - 1.0).abs() < 1e-6);
182 }
183
184 #[test]
185 fn intensity_clamps() {
186 let mut cfg = new_pixel_sort_effect_config();
187 ps_set_intensity(&mut cfg, 2.0);
188 assert!((cfg.intensity - 1.0).abs() < 1e-6);
189 }
190
191 #[test]
192 fn json_has_keys() {
193 let j = ps_to_json(&new_pixel_sort_effect_config());
194 assert!(j.contains("threshold_low") && j.contains("enabled"));
195 }
196}