From 9968afbe4810665c622664baaa6f76e43890607a Mon Sep 17 00:00:00 2001 From: sacha-renault Date: Sat, 5 Jul 2025 21:45:57 +0200 Subject: [PATCH] feat: Add with_sweep_range() method for control over knob start position and rotation range. Maintains backward compatibility --- examples/example_knob.rs | 6 ++++-- src/lib.rs | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/examples/example_knob.rs b/examples/example_knob.rs index f438ad2..11a0690 100644 --- a/examples/example_knob.rs +++ b/examples/example_knob.rs @@ -117,7 +117,8 @@ impl eframe::App for KnobExample { egui::Color32::from_rgb(100, 255, 100), ) .with_size(50.0) - .with_label_format(|v| format!("{:.2}%", v)), + .with_label_format(|v| format!("{:.2}%", v)) + .with_sweep_range(1. / 8., 0.75), ); ui.add( @@ -128,7 +129,8 @@ impl eframe::App for KnobExample { egui::Color32::from_rgb(50, 50, 220), egui::Color32::from_rgb(100, 100, 255), ) - .with_size(50.0), + .with_size(50.0) + .with_sweep_range(0., 2.), ); }); }); diff --git a/src/lib.rs b/src/lib.rs index c1b687b..5828620 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +use core::f32; + use egui::{Align2, Color32, Rect, Response, Sense, Stroke, Ui, Vec2, Widget}; /// Position of the label relative to the knob @@ -42,6 +44,8 @@ pub struct Knob<'a> { label_offset: f32, label_format: Box String>, step: Option, + min_angle: f32, + max_angle: f32, } impl<'a> Knob<'a> { @@ -69,9 +73,37 @@ impl<'a> Knob<'a> { label_offset: 1.0, label_format: Box::new(|v| format!("{:.2}", v)), step: None, + min_angle: -std::f32::consts::PI, + max_angle: std::f32::consts::PI * 0.5, } } + /// Sets the angular sweep range of the knob + /// + /// This controls where the knob starts and how far it can rotate. By default, + /// knobs start at the bottom-left (135°) and sweep 270° clockwise to bottom-right. + /// + /// # Arguments + /// * `start_angle_normalized` - Starting position as fraction of full circle: + /// - `0.0` = bottom (6 o'clock) + /// - `0.25` = left (9 o'clock) + /// - `0.5` = top (12 o'clock) + /// - `0.75` = right (3 o'clock) + /// * `range` - How far the knob can sweep as fraction of full circle: + /// - `0.25` = quarter turn (90°) + /// - `0.5` = half turn (180°) + /// - `0.75` = three-quarter turn (270°) + /// - `1.0` = full turn (360°) + /// - Values > 1.0 create multi-turn knobs + pub fn with_sweep_range(mut self, start_angle_normalized: f32, range: f32) -> Self { + self.min_angle = + start_angle_normalized.rem_euclid(1.) * f32::consts::PI * 2. + f32::consts::PI / 2.; + + // A range of 1. represent a full turn + self.max_angle = self.min_angle + range.max(0.) * 2. * f32::consts::PI; + self + } + /// Sets the size of the knob pub fn with_size(mut self, size: f32) -> Self { self.size = size; @@ -210,8 +242,8 @@ impl Widget for Knob<'_> { let center = knob_rect.center(); let radius = knob_size.x / 2.0; - let angle = (*self.value - self.min) / (self.max - self.min) * std::f32::consts::PI * 1.5 - - std::f32::consts::PI; + let angle = self.min_angle + + (*self.value - self.min) / (self.max - self.min) * (self.max_angle - self.min_angle); painter.circle_stroke( center,