git.lucas.co / cce-designer
graphic design tool
git clone https://git.lucas.co/cce-designer.git

commitf4d9192713d80c85b0108a3fcac1960cd8e288d0
parent4e92084472
authorLucas Galante <lsgalante12@gmail.com>
date2026-09-29 18:03
feat: the Camera Pivot Size slider runs 0 to 1

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

 CLAUDE.md     |  3 ++-
 src/app.rs    | 21 ++++++++++++---------
 src/dialog.rs | 19 -------------------
 src/main.rs   | 16 ++++++++--------
 4 files changed, 22 insertions(+), 37 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index 221c5a4..541cbe0 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -2181,7 +2181,8 @@ The viewport's right-click menu has two SUBMENUS (since 2026-09-29;
 until then it was one list of some twenty rows). The menu itself holds
 what is done — Frame All, View 1:1 — the guides (Show Grid, Show Origin,
 and since 2026-09-29 Show Camera Pivot with a Camera Pivot Size slider
-under it, 0.1–5 by a tenth, which until then were in the Guides
+under it, 0–1 by a twentieth (the palette row is the coarse one, whole
+tenths up to 5), which until then were in the Guides
 menubar and the palette only;
 the reference CUBE guide was removed on 2026-09-25 — its command, mesh,
 RT-scene copy, settings field and menubar item, with the Guides menubar
diff --git a/src/app.rs b/src/app.rs
index c097fb0..bfaba50 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -480,8 +480,9 @@ pub enum ViewportMenuAction {
     /// Pull Arrow Scale, the pull arrows' length as a multiple of the true
     /// displacement: the palette row's 0.25–10.
     PullArrowScaleSlider,
-    /// Camera Pivot Size, the pivot marker's scale, under its switch: the
-    /// palette row's 0.1–5 by a tenth.
+    /// Camera Pivot Size, the pivot marker's scale, under its switch: 0–1
+    /// by a twentieth. The palette row is the coarse one, in tenths up
+    /// to 5.
     CameraPivotSizeSlider,
     /// A "-" row: engraved, inert.
     Separator,
@@ -5154,11 +5155,11 @@ impl State {
             // may not carry and which a fallback glyph would then
             // under-measure.
             ViewportMenuAction::CameraPivotSizeSlider => MenuSlider {
-                value: self.camera_pivot_size_shown().clamp(0.1, 5.0),
-                min: 0.1,
-                max: 5.0,
-                step: 0.1,
-                decimals: 1,
+                value: self.camera_pivot_size.clamp(0.0, 1.0),
+                min: 0.0,
+                max: 1.0,
+                step: 0.05,
+                decimals: 2,
                 suffix: "x",
             },
             ViewportMenuAction::PullArrowScaleSlider => MenuSlider {
@@ -5183,8 +5184,10 @@ impl State {
     fn land_viewport_menu_slider(&mut self, action: ViewportMenuAction, v: f32) {
         // The menu's opacity rows are in percent; the field is a fraction.
         if action == ViewportMenuAction::CameraPivotSizeSlider {
-            // Owned by the active camera, as the palette's row is.
-            self.set_camera_pivot_size(v.clamp(0.1, 5.0));
+            // The live field itself: the palette row writes whole tenths,
+            // which is coarser than this slider's step.
+            self.camera_pivot_size = v.clamp(0.0, 1.0);
+            self.update_pivot_geometry();
             self.viewport_dirty = true;
             return;
         }
diff --git a/src/dialog.rs b/src/dialog.rs
index 559241b..9d55981 100644
--- a/src/dialog.rs
+++ b/src/dialog.rs
@@ -1860,25 +1860,6 @@ impl State {
         }
     }
 
-    /// The pivot marker's scale as the Camera Pivot Size row reads it: the
-    /// active camera's param where it has one, else the live field.
-    pub(crate) fn camera_pivot_size_shown(&self) -> f32 {
-        SETTINGS
-            .iter()
-            .find(|s| s.label == "Camera Pivot Size")
-            .and_then(|s| self.setting_value(s).parse::<f32>().ok())
-            .map_or(self.camera_pivot_size, |tenths| tenths / 10.0)
-    }
-
-    /// Set it, through the row's own write, and re-bake the marker. The
-    /// viewport menu's slider lands here; nothing is evaluated.
-    pub(crate) fn set_camera_pivot_size(&mut self, v: f32) {
-        if let Some(s) = SETTINGS.iter().find(|s| s.label == "Camera Pivot Size") {
-            self.setting_write(s, &((v * 10.0).round() as i64).to_string());
-            self.update_pivot_geometry();
-        }
-    }
-
     /// Write one setting's value to whatever owns it.
     fn setting_write(&mut self, s: &Setting, value: &str) {
         match s.owner {
diff --git a/src/main.rs b/src/main.rs
index 834fa6d..3c4d8d8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2359,16 +2359,16 @@ mod tests {
         assert!(crate::app::DesignSettings::from_kdl_str(&kdl).viewport.show_camera_pivot_enabled, "persisted");
     }
 
-    /// Camera Pivot Size is a slider under Show Camera Pivot, over the
-    /// palette row's 0.1–5: the wheel steps a tenth, re-bakes the marker
-    /// and nothing else, and saves.
+    /// Camera Pivot Size is a slider under Show Camera Pivot, over 0–1:
+    /// the wheel steps a twentieth, re-bakes the marker and nothing else,
+    /// and saves.
     #[test]
     fn the_viewport_menu_sets_the_camera_pivot_size() {
         use crate::app::ViewportMenuAction as A;
         use crate::window::WindowEvent;
         use cce_ui::widget::{context_menu, MouseScrollDelta};
         let mut state = State::new(false);
-        state.camera_pivot_size = 1.0;
+        state.camera_pivot_size = 0.5;
         state.pending_pivot = None;
         let version = state.rt_geometry_version;
         state.cursor_x = 300.0;
@@ -2378,19 +2378,19 @@ mod tests {
         let i = actions.iter().position(|a| *a == A::CameraPivotSizeSlider).expect("a Camera Pivot Size row");
         assert_eq!(actions[i - 1], A::Command("toggle_camera_pivot"), "it sits under its switch");
         let sl = context_menu::slider(i).expect("a slider");
-        assert_eq!((sl.min, sl.max, sl.step, sl.decimals), (0.1, 5.0, 0.1, 1));
-        assert!((sl.value - 1.0).abs() < 1e-6);
+        assert_eq!((sl.min, sl.max, sl.step, sl.decimals), (0.0, 1.0, 0.05, 2));
+        assert!((sl.value - 0.5).abs() < 1e-6);
 
         state.cursor_x = context_menu::x() + 20.0;
         state.cursor_y = context_menu::row_y(i) + context_menu::ROW_H * 0.5;
         state.handle_event(&WindowEvent::MouseWheel { delta: MouseScrollDelta::LineDelta(0.0, 5.0) });
-        assert!((state.camera_pivot_size - 1.5).abs() < 1e-5, "{}", state.camera_pivot_size);
+        assert!((state.camera_pivot_size - 0.75).abs() < 1e-5, "{}", state.camera_pivot_size);
         assert!(state.pending_pivot.is_some(), "the marker re-bakes");
         assert_eq!(state.rt_geometry_version, version, "and the graph is not evaluated");
         assert!(context_menu::is_visible(), "a slider row keeps the menu up");
         let kdl = fs::read_to_string(crate::app::DesignSettings::file_path()).expect("saved");
         let saved = crate::app::DesignSettings::from_kdl_str(&kdl).viewport.camera_pivot_size;
-        assert!((saved - 1.5).abs() < 1e-5, "persisted: {saved}");
+        assert!((saved - 0.75).abs() < 1e-5, "persisted: {saved}");
         context_menu::hide();
     }