git.lucas.co / cce-gallery
widget gallery and compositor test bench

commit79a35c1f50abfa71177120c7f83928bfee3e7c1b
parent9deb7120be
authorLucas Galante <[email protected]>
date2026-07-04 22:04
Support line type configuration loading, saving, and hot reloading in test interface and render loops

 src/main.rs | 79 ++++++++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 54 insertions(+), 25 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 787b028..92e9e98 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -98,24 +98,27 @@ struct State {
     child_type: Option<String>,
     ui_context: cce_ui::context::UiContext,
     bevel_ramp: Vec<RampKey>,
+    bevel_ramp_line_type: String,
     last_ramp_mod: Option<std::time::SystemTime>,
 }
 
-fn save_bevel_ramp(keys: &[RampKey]) {
+fn save_bevel_ramp(keys: &[RampKey], line_type: &str) {
     let mut s = String::new();
     s.push_str("keys {\n");
     for k in keys {
         s.push_str(&format!("    key pos={} val={}\n", k.pos, k.value));
     }
     s.push_str("}\n");
+    s.push_str(&format!("line_type \"{}\"\n", line_type));
     let path = cce_ui::config::get_config_path().parent().unwrap().join("bevel_ramp.kdl");
     let _ = std::fs::write(path, s);
 }
 
-fn load_bevel_ramp() -> Vec<RampKey> {
+fn load_bevel_ramp() -> (Vec<RampKey>, String) {
     let path = cce_ui::config::get_config_path().parent().unwrap().join("bevel_ramp.kdl");
+    let mut line_type = "linear".to_string();
+    let mut keys = Vec::new();
     if let Ok(content) = std::fs::read_to_string(path) {
-        let mut keys = Vec::new();
         for line in content.lines() {
             let line = line.trim();
             if line.starts_with("key ") {
@@ -129,23 +132,30 @@ fn load_bevel_ramp() -> Vec<RampKey> {
                     }
                 }
                 keys.push(RampKey { pos, value: val });
+            } else if line.starts_with("line_type ") {
+                if let Some(val_str) = line.split_whitespace().nth(1) {
+                    line_type = val_str.trim_matches('"').to_string();
+                }
             }
         }
         if !keys.is_empty() {
             keys.sort_by(|a, b| a.pos.partial_cmp(&b.pos).unwrap());
-            return keys;
+            return (keys, line_type);
         }
     }
     // Default keys: raised bevel profile (starts/ends at backplate level 0.5, peaks at 1.0)
-    vec![
-        RampKey { pos: 0.0, value: 0.5 },
-        RampKey { pos: 0.2, value: 1.0 },
-        RampKey { pos: 0.8, value: 1.0 },
-        RampKey { pos: 1.0, value: 0.5 },
-    ]
+    (
+        vec![
+            RampKey { pos: 0.0, value: 0.5 },
+            RampKey { pos: 0.2, value: 1.0 },
+            RampKey { pos: 0.8, value: 1.0 },
+            RampKey { pos: 1.0, value: 0.5 },
+        ],
+        "linear".to_string()
+    )
 }
 
-fn interpolate_ramp_value(keys: &[RampKey], u: f32) -> f32 {
+fn interpolate_ramp_value(keys: &[RampKey], u: f32, line_type: &str) -> f32 {
     if keys.is_empty() {
         return 0.5;
     }
@@ -164,7 +174,12 @@ fn interpolate_ramp_value(keys: &[RampKey], u: f32) -> f32 {
                 return k1.value;
             }
             let w = (u - k1.pos) / range;
-            return k1.value * (1.0 - w) + k2.value * w;
+            if line_type == "bezier" {
+                let w_smooth = w * w * (3.0 - 2.0 * w);
+                return k1.value * (1.0 - w_smooth) + k2.value * w_smooth;
+            } else {
+                return k1.value * (1.0 - w) + k2.value * w;
+            }
         }
     }
     keys[0].value
@@ -402,7 +417,12 @@ impl State {
                     bg,                      // 0
                     Box::new({
                         let mut ramp = Ramp::new();
-                        ramp.keys = load_bevel_ramp();
+                        let (loaded_keys, loaded_type) = load_bevel_ramp();
+                        ramp.keys = loaded_keys;
+                        ramp.line_type_dropdown.selected = match loaded_type.as_str() {
+                            "bezier" => 1,
+                            _ => 0,
+                        };
                         ramp
                     }), // 1
                     Box::new(Button::new(0.0, 0.0, 100.0, 35.0).with_label("Close")), // 2
@@ -542,6 +562,8 @@ cascades in cce."
             mapped_at_creation: false,
         });
 
+        let (loaded_keys, loaded_type) = load_bevel_ramp();
+
         let mut state = Self {
             surface,
             device,
@@ -581,7 +603,8 @@ cascades in cce."
             border_bevel,
             child_type: child_type.clone(),
             ui_context: cce_ui::context::UiContext::new(),
-            bevel_ramp: load_bevel_ramp(),
+            bevel_ramp: loaded_keys,
+            bevel_ramp_line_type: loaded_type,
             last_ramp_mod: None,
         };
 
@@ -768,8 +791,8 @@ cascades in cce."
                                 let u_curr = idx as f32 / slices as f32;
                                 let u_next = (idx + 1) as f32 / slices as f32;
                                 
-                                let h_outer = if idx == 0 { 0.5 } else { interpolate_ramp_value(&self.bevel_ramp, u_curr) };
-                                let h_inner = if idx == slices - 1 { 0.5 } else { interpolate_ramp_value(&self.bevel_ramp, u_next) };
+                                let h_outer = if idx == 0 { 0.5 } else { interpolate_ramp_value(&self.bevel_ramp, u_curr, &self.bevel_ramp_line_type) };
+                                let h_inner = if idx == slices - 1 { 0.5 } else { interpolate_ramp_value(&self.bevel_ramp, u_next, &self.bevel_ramp_line_type) };
                                 
                                 let d_h = h_inner - h_outer;
                                 let color_offset = d_h * 0.4;
@@ -817,8 +840,8 @@ cascades in cce."
                                 let u_curr = idx as f32 / slices as f32;
                                 let u_next = (idx + 1) as f32 / slices as f32;
                                 
-                                let h_outer = if idx == 0 { 0.5 } else { interpolate_ramp_value(&self.bevel_ramp, u_curr) };
-                                let h_inner = if idx == slices - 1 { 0.5 } else { interpolate_ramp_value(&self.bevel_ramp, u_next) };
+                                let h_outer = if idx == 0 { 0.5 } else { interpolate_ramp_value(&self.bevel_ramp, u_curr, &self.bevel_ramp_line_type) };
+                                let h_inner = if idx == slices - 1 { 0.5 } else { interpolate_ramp_value(&self.bevel_ramp, u_next, &self.bevel_ramp_line_type) };
                                 
                                 let d_h = h_inner - h_outer;
                                 let color_offset = d_h * 0.4;
@@ -952,8 +975,8 @@ cascades in cce."
                             let u_curr = i as f32 / slices as f32;
                             let u_next = (i + 1) as f32 / slices as f32;
                             
-                            let h_outer = if i == 0 { 0.5 } else { interpolate_ramp_value(&self.bevel_ramp, u_curr) };
-                            let h_inner = if i == slices - 1 { 0.5 } else { interpolate_ramp_value(&self.bevel_ramp, u_next) };
+                            let h_outer = if i == 0 { 0.5 } else { interpolate_ramp_value(&self.bevel_ramp, u_curr, &self.bevel_ramp_line_type) };
+                            let h_inner = if i == slices - 1 { 0.5 } else { interpolate_ramp_value(&self.bevel_ramp, u_next, &self.bevel_ramp_line_type) };
                             
                             let d_h = h_inner - h_outer;
                             let color_offset = d_h * 0.4;
@@ -1001,8 +1024,8 @@ cascades in cce."
                             let u_curr = i as f32 / slices as f32;
                             let u_next = (i + 1) as f32 / slices as f32;
                             
-                            let h_outer = if i == 0 { 0.5 } else { interpolate_ramp_value(&self.bevel_ramp, u_curr) };
-                            let h_inner = if i == slices - 1 { 0.5 } else { interpolate_ramp_value(&self.bevel_ramp, u_next) };
+                            let h_outer = if i == 0 { 0.5 } else { interpolate_ramp_value(&self.bevel_ramp, u_curr, &self.bevel_ramp_line_type) };
+                            let h_inner = if i == slices - 1 { 0.5 } else { interpolate_ramp_value(&self.bevel_ramp, u_next, &self.bevel_ramp_line_type) };
                             
                             let d_h = h_inner - h_outer;
                             let color_offset = d_h * 0.4;
@@ -1399,9 +1422,11 @@ cascades in cce."
             let path = cce_ui::config::get_config_path().parent().unwrap().join("bevel_ramp.kdl");
             if let Ok(metadata) = std::fs::metadata(&path) {
                 if let Ok(mod_time) = metadata.modified() {
-                    if Some(mod_time) != self.last_ramp_mod {
+                     if Some(mod_time) != self.last_ramp_mod {
                         self.last_ramp_mod = Some(mod_time);
-                        self.bevel_ramp = load_bevel_ramp();
+                        let (keys, line_type) = load_bevel_ramp();
+                        self.bevel_ramp = keys;
+                        self.bevel_ramp_line_type = line_type;
                         changed = true;
                     }
                 }
@@ -1441,7 +1466,11 @@ cascades in cce."
                     changed = true;
                     if self.is_child && self.child_type.as_deref() == Some("Ramp") && i == 1 {
                         if let Some(ramp) = w.as_any().downcast_ref::<Ramp>() {
-                            save_bevel_ramp(&ramp.keys);
+                            let line_type_str = match ramp.line_type_dropdown.selected {
+                                1 => "bezier",
+                                _ => "linear",
+                            };
+                            save_bevel_ramp(&ramp.keys, line_type_str);
                         }
                     }
                 }