Skip to content

Commit fc75f1a

Browse files
authored
Merge pull request #9 from hacknus/fix_panic_for_single_val_range
implement fix for range panic
2 parents 539ed44 + 358e9e8 commit fc75f1a

File tree

5 files changed

+88
-24
lines changed

5 files changed

+88
-24
lines changed

CHANGELOG.MD

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ All notable changes to the `egui double slider` crate will be documented in this
88

99
* ...
1010

11+
# 0.7.1 - 23.5.2025
12+
13+
### Added:
14+
15+
* Fix for the case where range is specified as `0.0..=0.0` or any other single valued range.
16+
1117
# 0.7.0 - 24.3.2025
1218

1319
### Added:

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "egui_double_slider"
3-
version = "0.7.0"
3+
version = "0.7.1"
44
edition = "2021"
55
authors = ["Linus Leo Stöckli"]
66
repository = "https://github.com/hacknus/egui_double_slider"

README.MD

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Add double slider along with `eframe` to your `cargo.toml`.
1212

1313
```toml
1414
eframe = "0.31"
15-
egui_double_slider = "0.7.0"
15+
egui_double_slider = "0.7.2"
1616
```
1717

1818
To run the example:

examples/example.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ impl App for MyApp {
5252
10.0..=300.0,
5353
)
5454
.width(width)
55-
.separation_distance(10.0),
55+
.separation_distance(0.0),
5656
);
5757
ui.label(format!("Lower Bound: {:.2}", self.slider_f32_low));
5858
ui.label(format!("Upper Bound: {:.2}", self.slider_f32_high));

src/double_slider.rs

Lines changed: 79 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,14 @@ impl<'a, T: Numeric> DoubleSlider<'a, T> {
137137
/// Default is false.
138138
#[inline]
139139
pub fn logarithmic(mut self, logarithmic: bool) -> Self {
140-
let range_f64 = self.range_f64();
141-
assert!(
142-
*range_f64.start() > 0.0 && range_f64.end().is_finite(),
143-
"Logarithmic scale can only be used with finite, strictly positive values"
144-
);
140+
if logarithmic {
141+
let range_f64 = self.range_f64();
142+
assert!(
143+
*range_f64.start() > 0.0 && range_f64.start().is_finite() &&
144+
*range_f64.end() > 0.0 && range_f64.end().is_finite(),
145+
"Logarithmic scale can only be used with a range of finite, strictly positive values (both start and end)"
146+
);
147+
}
145148
self.logarithmic = logarithmic;
146149
self
147150
}
@@ -156,31 +159,86 @@ impl<'a, T: Numeric> DoubleSlider<'a, T> {
156159

157160
fn val_to_x(&self, val: T) -> f32 {
158161
let offset = self.control_point_radius + OFFSET;
159-
let slider_width = self.width - 2.0 * offset;
160-
let mut range_f64 = self.range_f64();
161-
let mut val = val.to_f64();
162+
// Calculate usable visual width of the slider track, ensuring it's not negative
163+
let visual_slider_width = (self.width - 2.0 * offset).max(0.0);
164+
165+
let mut current_val_f64 = val.to_f64();
166+
let mut range_min_f64 = self.range.start().to_f64();
167+
let mut range_max_f64 = self.range.end().to_f64();
168+
162169
if self.logarithmic {
163-
range_f64 = range_f64.start().log10()..=range_f64.end().log10();
164-
val = val.log10();
170+
// Values are asserted to be > 0 in the logarithmic() setter.
171+
// If range_min_f64 or range_max_f64 were <=0, log10 would produce NaN or Inf.
172+
// current_val_f64 should also be > 0.
173+
if current_val_f64 <= 0.0 || range_min_f64 <= 0.0 || range_max_f64 <= 0.0 {
174+
// This case should ideally be prevented by assertions or clamping
175+
// For safety, if inputs are invalid for log, default to a non-NaN behavior
176+
// though this indicates a deeper issue if reached.
177+
// Given the problem context (1..=1), this branch isn't the primary issue.
178+
return offset; // Fallback to avoid NaN from log
179+
}
180+
current_val_f64 = current_val_f64.log10();
181+
range_min_f64 = range_min_f64.log10();
182+
range_max_f64 = range_max_f64.log10();
165183
}
166-
let ratio = ((val - range_f64.start()) / (range_f64.end() - range_f64.start())) as f32;
167184

168-
ratio * slider_width + offset
185+
let range_span_f64 = range_max_f64 - range_min_f64;
186+
187+
let ratio = if range_span_f64 == 0.0 {
188+
// If the range is a single point (e.g., 1..=1, or log(1)..=log(1)),
189+
// the value is conceptually at that point.
190+
// Map this to the start (0.0) of the visual slider part.
191+
0.0
192+
} else {
193+
// Normalize current_val_f64 to a [0, 1] ratio within the range.
194+
let normalized_val = (current_val_f64 - range_min_f64) / range_span_f64;
195+
normalized_val.clamp(0.0, 1.0) // Clamp to handle potential floating point inaccuracies.
196+
};
197+
198+
// Map the ratio to the screen coordinate.
199+
(ratio as f32 * visual_slider_width) + offset
169200
}
170201

171-
fn x_to_val(&self, x: f32) -> T {
202+
fn x_to_val(&self, x_in_widget: f32) -> T {
172203
let offset = self.control_point_radius + OFFSET;
173-
let slider_width = (self.width - 2.0 * offset) as f64;
174-
let ratio = (x - offset) as f64 / slider_width;
175-
let range_f64 = self.range_f64();
176-
let val = if self.logarithmic {
177-
let (start, end) = (range_f64.start().log10(), range_f64.end().log10());
178-
10.0f64.powf(start + (end - start) * ratio)
204+
// Calculate usable visual width of the slider track, ensuring it's not negative
205+
let visual_slider_width = (self.width - 2.0 * offset).max(0.0) as f64;
206+
207+
let range_min_f64 = self.range.start().to_f64();
208+
let range_max_f64 = self.range.end().to_f64();
209+
210+
let value_f64 = if range_min_f64 == range_max_f64 {
211+
// If the range is a single point, any x position maps to this single value.
212+
range_min_f64
179213
} else {
180-
range_f64.start() + (*range_f64.end() - *range_f64.start()) * ratio
214+
// Position of x relative to the start of the slider track
215+
let x_on_track = (x_in_widget - offset) as f64;
216+
217+
let ratio = if visual_slider_width == 0.0 {
218+
// If visual width is zero (e.g. self.width is too small),
219+
// effectively all points map to the start of the range.
220+
0.0
221+
} else {
222+
(x_on_track / visual_slider_width).clamp(0.0, 1.0)
223+
};
224+
225+
if self.logarithmic {
226+
// Values are asserted to be > 0 in the logarithmic() setter.
227+
if range_min_f64 <= 0.0 || range_max_f64 <= 0.0 {
228+
// Fallback, though assertions should prevent this.
229+
return self.f64_to_val(self.range.start().to_f64());
230+
}
231+
let log_min = range_min_f64.log10();
232+
let log_max = range_max_f64.log10();
233+
// This path is taken only if range_min_f64 != range_max_f64.
234+
// If they are different and positive, their logs will also be different.
235+
10.0f64.powf(log_min + (log_max - log_min) * ratio)
236+
} else {
237+
range_min_f64 + (range_max_f64 - range_min_f64) * ratio
238+
}
181239
};
182240

183-
self.f64_to_val(val)
241+
self.f64_to_val(value_f64)
184242
}
185243

186244
fn left_slider_f64(&self) -> f64 {

0 commit comments

Comments
 (0)