steer-core 0.1.16__py3-none-any.whl → 0.1.18__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,35 +9,36 @@ import math
9
9
  from typing import List, Union, Dict
10
10
 
11
11
 
12
-
13
- def calculate_slider_steps(min_values: List[float], max_values: List[float]) -> List[float]:
12
+ def calculate_slider_steps(
13
+ min_values: List[float], max_values: List[float]
14
+ ) -> List[float]:
14
15
  """
15
16
  Calculate reasonable slider steps based on parameter ranges.
16
-
17
+
17
18
  This function analyzes the range of each parameter and suggests an appropriate
18
19
  step size that provides good granularity. The step sizes are the same as input
19
20
  steps to ensure consistent behavior between sliders and input fields:
20
21
  - Ranges < 100: step = 0.01
21
- - Ranges 100-999: step = 0.1
22
+ - Ranges 100-999: step = 0.1
22
23
  - Ranges 1000+: step = 1.0
23
-
24
+
24
25
  Args:
25
26
  min_values (List[float]): List of minimum values for each parameter
26
27
  max_values (List[float]): List of maximum values for each parameter
27
-
28
+
28
29
  Returns:
29
30
  List[float]: List of recommended step sizes for each parameter
30
-
31
+
31
32
  Raises:
32
33
  ValueError: If lists have different lengths or if any max < min
33
-
34
+
34
35
  Examples:
35
36
  >>> min_vals = [0, 0, 0, 0]
36
37
  >>> max_vals = [10, 100, 1000, 0.1]
37
38
  >>> steps = calculate_slider_steps(min_vals, max_vals)
38
39
  >>> steps
39
40
  [0.01, 0.1, 1.0, 0.01]
40
-
41
+
41
42
  >>> # Temperature range
42
43
  >>> steps = calculate_slider_steps([20], [100])
43
44
  >>> steps[0]
@@ -45,47 +46,49 @@ def calculate_slider_steps(min_values: List[float], max_values: List[float]) ->
45
46
  """
46
47
  if len(min_values) != len(max_values):
47
48
  raise ValueError("min_values and max_values must have the same length")
48
-
49
+
49
50
  steps = []
50
-
51
+
51
52
  for min_val, max_val in zip(min_values, max_values):
52
53
  if max_val < min_val:
53
- raise ValueError(f"max_value ({max_val}) cannot be less than min_value ({min_val})")
54
-
54
+ raise ValueError(
55
+ f"max_value ({max_val}) cannot be less than min_value ({min_val})"
56
+ )
57
+
55
58
  # Calculate the range
56
59
  range_val = max_val - min_val
57
-
60
+
58
61
  if range_val == 0:
59
62
  # If no range, use a very small step
60
63
  steps.append(0.001)
61
64
  continue
62
-
65
+
63
66
  # Calculate step based on range magnitude
64
67
  step = _calculate_step_for_range(range_val)
65
68
  steps.append(step)
66
-
69
+
67
70
  return steps
68
71
 
69
72
 
70
73
  def _calculate_step_for_range(range_val: float) -> float:
71
74
  """
72
75
  Calculate an appropriate step size for a given range.
73
-
76
+
74
77
  Uses the same step sizes as input steps to ensure sliders and inputs
75
78
  have consistent granularity:
76
79
  - Ranges < 100: step = 0.01
77
80
  - Ranges 100-999: step = 0.1
78
81
  - Ranges 1000+: step = 1.0
79
-
82
+
80
83
  Args:
81
84
  range_val (float): The range (max - min) for the parameter
82
-
85
+
83
86
  Returns:
84
87
  float: Recommended step size
85
88
  """
86
89
  if range_val <= 0:
87
90
  return 0.01
88
-
91
+
89
92
  # Use the same logic as input steps for consistency
90
93
  if range_val < 100:
91
94
  return 0.01
@@ -98,15 +101,15 @@ def _calculate_step_for_range(range_val: float) -> float:
98
101
  def _calculate_input_step_for_range(range_val: float) -> float:
99
102
  """
100
103
  Calculate an appropriate input step size for a given range.
101
-
104
+
102
105
  Input steps are finer than slider steps to allow more precise control:
103
106
  - Ranges < 100: input step = 0.01
104
107
  - Ranges 100-999: input step = 0.1
105
108
  - Ranges 1000+: input step = 1.0
106
-
109
+
107
110
  Args:
108
111
  range_val (float): The range (max - min) for the parameter
109
-
112
+
110
113
  Returns:
111
114
  float: Recommended input step size
112
115
  """
@@ -118,86 +121,68 @@ def _calculate_input_step_for_range(range_val: float) -> float:
118
121
  return 1.0
119
122
 
120
123
 
121
- def calculate_input_steps(min_values: List[float], max_values: List[float]) -> List[float]:
124
+ def calculate_input_steps(
125
+ min_values: List[float], max_values: List[float]
126
+ ) -> List[float]:
122
127
  """
123
128
  Calculate appropriate input step sizes based on parameter ranges.
124
-
129
+
125
130
  Input steps are typically finer than slider steps to allow precise adjustment.
126
-
131
+
127
132
  Args:
128
133
  min_values (List[float]): List of minimum values for each parameter
129
134
  max_values (List[float]): List of maximum values for each parameter
130
-
135
+
131
136
  Returns:
132
137
  List[float]: List of recommended input step sizes for each parameter
133
138
  """
134
139
  if len(min_values) != len(max_values):
135
140
  raise ValueError("min_values and max_values must have the same length")
136
-
141
+
137
142
  input_steps = []
138
-
143
+
139
144
  for min_val, max_val in zip(min_values, max_values):
140
145
  if max_val < min_val:
141
- raise ValueError(f"max_value ({max_val}) cannot be less than min_value ({min_val})")
142
-
146
+ raise ValueError(
147
+ f"max_value ({max_val}) cannot be less than min_value ({min_val})"
148
+ )
149
+
143
150
  # Calculate the range
144
151
  range_val = max_val - min_val
145
-
152
+
146
153
  # Calculate input step based on range magnitude
147
154
  input_step = _calculate_input_step_for_range(range_val)
148
155
  input_steps.append(input_step)
149
-
150
- return input_steps
151
156
 
152
-
153
- def _round_to_reasonable_precision(value: float) -> float:
154
- """
155
- Round a step value to a reasonable precision for UI display.
156
-
157
- Args:
158
- value (float): The step value to round
159
-
160
- Returns:
161
- float: Rounded step value
162
- """
163
- if value >= 1:
164
- # For values >= 1, round to reasonable whole numbers or simple decimals
165
- if value >= 10:
166
- return round(value)
167
- else:
168
- return round(value, 1)
169
- else:
170
- # For values < 1, determine appropriate decimal places
171
- magnitude = math.floor(math.log10(value))
172
- decimal_places = max(1, -magnitude + 1)
173
- return round(value, decimal_places)
157
+ return input_steps
174
158
 
175
159
 
176
- def calculate_mark_intervals(min_values: List[float], max_values: List[float],
177
- target_marks: int = 5) -> List[float]:
160
+ def calculate_mark_intervals(
161
+ min_values: List[float], max_values: List[float], target_marks: int = 5
162
+ ) -> List[float]:
178
163
  """
179
164
  Calculate mark intervals for sliders based on range magnitude.
180
-
165
+
181
166
  Creates marks at regular intervals that align with intuitive values while
182
167
  avoiding overcrowding by ensuring no more than ~5-6 marks per slider:
183
168
  - Range < 0.5: marks on every 0.1, but max 5 marks
184
169
  - Range < 1: marks on every 0.2 (interval = 0.2)
185
- - Range < 5: marks on every 1.0 (interval = 1.0)
170
+ - Range < 5: marks on every 1.0 (interval = 1.0)
186
171
  - Range < 10: marks on every 2.0 (interval = 2.0)
187
172
  - Range < 50: marks on every 10.0 (interval = 10.0)
188
173
  - Range < 100: marks on every 20.0 (interval = 20.0)
189
174
  - Range < 500: marks on every 100.0 (interval = 100.0)
190
175
  - Range < 1000: marks on every 200.0 (interval = 200.0)
191
176
  - Range >= 1000: marks on every 500.0 (interval = 500.0)
192
-
177
+
193
178
  Args:
194
179
  min_values (List[float]): List of minimum values for each parameter
195
180
  max_values (List[float]): List of maximum values for each parameter
196
181
  target_marks (int): Target number of marks to display (unused, kept for compatibility)
197
-
182
+
198
183
  Returns:
199
184
  List[float]: List of recommended mark intervals for each parameter
200
-
185
+
201
186
  Examples:
202
187
  >>> min_vals = [0, 0, 0, 0]
203
188
  >>> max_vals = [0.3, 2, 250, 2000]
@@ -207,16 +192,16 @@ def calculate_mark_intervals(min_values: List[float], max_values: List[float],
207
192
  """
208
193
  if len(min_values) != len(max_values):
209
194
  raise ValueError("min_values and max_values must have the same length")
210
-
195
+
211
196
  intervals = []
212
-
197
+
213
198
  for min_val, max_val in zip(min_values, max_values):
214
199
  range_val = max_val - min_val
215
-
200
+
216
201
  if range_val == 0:
217
202
  intervals.append(1.0)
218
203
  continue
219
-
204
+
220
205
  # Determine interval based on range magnitude to avoid overcrowding
221
206
  if range_val < 0.5:
222
207
  # Very small ranges: use 0.1 but ensure max 5 marks
@@ -240,113 +225,131 @@ def calculate_mark_intervals(min_values: List[float], max_values: List[float],
240
225
  interval = 200.0 # Every multiple of 200
241
226
  else:
242
227
  interval = 500.0 # Every multiple of 500 for very large ranges
243
-
228
+
244
229
  intervals.append(interval)
245
-
230
+
246
231
  return intervals
247
232
 
248
233
 
249
234
  def create_slider_config(
250
- min_values: List[float],
251
- max_values: List[float],
252
- property_values: List[float] = None
235
+ min_values: List[float],
236
+ max_values: List[float],
237
+ property_values: List[float] = None,
253
238
  ) -> dict:
254
239
  """
255
240
  Create complete slider configurations with automatically calculated steps and marks.
256
-
241
+
257
242
  This is a convenience function that combines step and mark interval calculations
258
243
  to create ready-to-use slider configurations. If property values are provided,
259
244
  they will be validated and adjusted to align with the calculated slider grid.
260
-
245
+
246
+ Step sizes are calculated based on the magnitude of the maximum values (0 to max)
247
+ rather than the range (min to max), providing consistent granularity regardless
248
+ of the minimum value.
249
+
261
250
  Args:
262
251
  min_values (List[float]): Minimum values for each slider
263
252
  max_values (List[float]): Maximum values for each slider
264
253
  property_values (List[float], optional): Current property values to validate
265
254
  against slider grid. If provided, values
266
255
  will be snapped to nearest valid step.
267
-
256
+
268
257
  Returns:
269
258
  dict: Configuration dictionary with keys:
270
- - 'min_vals' (List[float]): Minimum values for each slider (snapped to slider grid)
271
- - 'max_vals' (List[float]): Maximum values for each slider (snapped to slider grid)
272
- - 'step_vals' (List[float]): Calculated step values for each slider (same as input steps)
259
+ - 'min_vals' (List[float]): Minimum values for each slider (snapped to grid, >= original min)
260
+ - 'max_vals' (List[float]): Maximum values for each slider (snapped to grid, <= original max)
261
+ - 'step_vals' (List[float]): Calculated step values based on max value magnitude
273
262
  - 'input_step_vals' (List[float]): Step values for inputs (same as slider steps)
274
- - 'mark_vals' (List[dict]): Mark positions for each slider (position → None mapping)
263
+ - 'mark_vals' (List[dict]): Mark positions for each slider (string position → empty string mapping)
275
264
  - 'grid_slider_vals' (List[float], optional): Property values snapped to slider grid if provided
276
265
  - 'grid_input_vals' (List[float], optional): Property values snapped to input grid if provided
277
-
266
+
278
267
  Example:
279
268
  >>> config = create_slider_config(
280
269
  ... min_values=[0, 20, 0],
281
270
  ... max_values=[100, 80, 1000],
282
271
  ... property_values=[23.7, 45.3, 567.8]
283
272
  ... )
273
+ >>> # Step sizes based on max values: 100->0.1, 80->0.01, 1000->1.0
284
274
  >>> config['step_vals']
285
- [0.01, 0.01, 1.0]
275
+ [0.1, 0.01, 1.0]
286
276
  >>> config['input_step_vals']
287
- [0.01, 0.01, 1.0]
277
+ [0.1, 0.01, 1.0]
288
278
  >>> config['grid_vals'][0] # 23.7 snapped to grid
289
279
  23.7
290
280
  """
291
281
  # Validate inputs
292
282
  if len(min_values) != len(max_values):
293
283
  raise ValueError("min_values and max_values must have the same length")
294
-
284
+
295
285
  if property_values is not None and len(property_values) != len(min_values):
296
- raise ValueError("property_values must have the same length as min_values and max_values")
297
-
298
- # Calculate steps and mark intervals
299
- steps = calculate_slider_steps(min_values, max_values) # Use the calculated steps directly
300
-
301
- input_steps = calculate_input_steps(min_values, max_values) # Calculate input steps based on range
302
-
303
- # Snap min and max values to the slider grid
286
+ raise ValueError(
287
+ "property_values must have the same length as min_values and max_values"
288
+ )
289
+
290
+ # Calculate steps and mark intervals based on max values (0 to max) instead of range
291
+ steps = []
292
+ input_steps = []
293
+ for max_val in max_values:
294
+ # Calculate step based on maximum value magnitude (0 to max)
295
+ step = _calculate_step_for_range(max_val)
296
+ input_step = _calculate_input_step_for_range(max_val)
297
+ steps.append(step)
298
+ input_steps.append(input_step)
299
+
300
+ # Snap min and max values to the slider grid (keeping within original range)
304
301
  grid_min_values = []
305
302
  grid_max_values = []
306
303
  for i, (min_val, max_val, step) in enumerate(zip(min_values, max_values, steps)):
307
- # For min value: round down to nearest grid point (floor)
308
- grid_min = min_val - (min_val % step) if min_val % step != 0 else min_val
309
-
310
- # For max value: round up to nearest grid point (ceil)
311
- if max_val % step == 0:
312
- grid_max = max_val
304
+ # For min value: round up to nearest grid point (ceil) to stay >= min_val
305
+ if min_val % step == 0:
306
+ grid_min = min_val
313
307
  else:
314
- grid_max = max_val + (step - (max_val % step))
315
-
308
+ grid_min = min_val + (step - (min_val % step))
309
+
310
+ # For max value: round down to nearest grid point (floor) to stay <= max_val
311
+ grid_max = max_val - (max_val % step) if max_val % step != 0 else max_val
312
+
313
+ # Ensure we have a valid range (grid_min <= grid_max)
314
+ if grid_min > grid_max:
315
+ # If rounding inward creates invalid range, use original values
316
+ grid_min = min_val
317
+ grid_max = max_val
318
+
316
319
  grid_min_values.append(grid_min)
317
320
  grid_max_values.append(grid_max)
318
-
321
+
319
322
  mark_intervals = calculate_mark_intervals(grid_min_values, grid_max_values)
320
-
323
+
321
324
  # Create mark dictionaries for each slider
322
325
  mark_vals = []
323
326
  grid_slider_vals = []
324
327
  grid_input_vals = []
325
-
328
+
326
329
  for i, (min_val, max_val, step, input_step, mark_interval) in enumerate(
327
330
  zip(grid_min_values, grid_max_values, steps, input_steps, mark_intervals)
328
331
  ):
329
332
  # Generate marks dictionary for this slider (positions only, no labels)
330
333
  marks = {}
331
-
334
+
332
335
  # Find the first mark position that's a multiple of mark_interval
333
336
  # Start from the nearest multiple of mark_interval >= min_val
334
337
  if mark_interval > 0:
335
338
  first_mark = math.ceil(min_val / mark_interval) * mark_interval
336
339
  current_mark = first_mark
337
-
340
+
338
341
  while current_mark <= max_val:
339
342
  # Round to avoid floating-point precision issues
340
343
  rounded_mark = round(current_mark, 10) # Round to 10 decimal places
341
- marks[rounded_mark] = '' # No label
344
+ marks[str(rounded_mark)] = "" # Convert float key to string, no label
342
345
  current_mark += mark_interval
343
-
346
+
344
347
  # Ensure we don't exceed max_val due to floating point precision
345
348
  if current_mark > max_val + mark_interval * 0.01:
346
349
  break
347
-
350
+
348
351
  mark_vals.append(marks)
349
-
352
+
350
353
  # Handle property value if provided
351
354
  if property_values is not None:
352
355
  property_val = property_values[i]
@@ -355,35 +358,38 @@ def create_slider_config(
355
358
  input_grid_value = snap_to_grid_no_clamp(property_val, min_val, input_step)
356
359
  grid_slider_vals.append(slider_grid_value)
357
360
  grid_input_vals.append(input_grid_value)
358
-
361
+
359
362
  # Create the configuration dictionary
360
363
  config = {
361
- 'min_vals': grid_min_values,
362
- 'max_vals': grid_max_values,
363
- 'step_vals': steps,
364
- 'input_step_vals': input_steps,
365
- 'mark_vals': mark_vals
364
+ "min_vals": grid_min_values,
365
+ "max_vals": grid_max_values,
366
+ "step_vals": steps,
367
+ "input_step_vals": input_steps,
368
+ "mark_vals": mark_vals,
366
369
  }
367
-
370
+
368
371
  # Add grid values if property values were provided
369
372
  if property_values is not None:
370
- config['grid_slider_vals'] = grid_slider_vals
371
- config['grid_input_vals'] = grid_input_vals
372
-
373
+ config["grid_slider_vals"] = grid_slider_vals
374
+ config["grid_input_vals"] = grid_input_vals
375
+
373
376
  return config
374
377
 
375
378
 
376
- def create_range_slider_config(min_values: List[float], max_values: List[float],
377
- lower_values: List[float] = None,
378
- upper_values: List[float] = None) -> dict:
379
+ def create_range_slider_config(
380
+ min_values: List[float],
381
+ max_values: List[float],
382
+ lower_values: List[float] = None,
383
+ upper_values: List[float] = None,
384
+ ) -> dict:
379
385
  """
380
386
  Create complete range slider configurations with automatically calculated steps and marks.
381
-
387
+
382
388
  This is a convenience function that combines step and mark interval calculations
383
389
  to create ready-to-use range slider configurations. If lower and upper values are provided,
384
- they will be validated, adjusted to ensure proper ordering, and aligned with the
390
+ they will be validated, adjusted to ensure proper ordering, and aligned with the
385
391
  calculated slider grids.
386
-
392
+
387
393
  Args:
388
394
  min_values (List[float]): Minimum values for each range slider
389
395
  max_values (List[float]): Maximum values for each range slider
@@ -391,17 +397,17 @@ def create_range_slider_config(min_values: List[float], max_values: List[float],
391
397
  Will be snapped to grid and validated.
392
398
  upper_values (List[float], optional): Current upper bound values for each range slider.
393
399
  Will be snapped to grid and validated.
394
-
400
+
395
401
  Returns:
396
402
  dict: Complete configuration dictionary containing:
397
- - 'min_vals' (List[float]): Minimum values for each range slider (snapped to slider grid)
398
- - 'max_vals' (List[float]): Maximum values for each range slider (snapped to slider grid)
403
+ - 'min_vals' (List[float]): Minimum values for each range slider (snapped to grid, >= original min)
404
+ - 'max_vals' (List[float]): Maximum values for each range slider (snapped to grid, <= original max)
399
405
  - 'step_vals' (List[float]): Calculated step values for each range slider (same as input steps)
400
406
  - 'input_step_vals' (List[float]): Step values for inputs (same as slider steps)
401
407
  - 'mark_vals' (List[dict]): Mark positions for each range slider (position → '' mapping)
402
408
  - 'grid_slider_vals' (List[tuple], optional): Range values snapped to slider grid if provided [(lower, upper), ...]
403
409
  - 'grid_input_vals' (List[tuple], optional): Range values snapped to input grid if provided [(lower, upper), ...]
404
-
410
+
405
411
  Example:
406
412
  >>> config = create_range_slider_config(
407
413
  ... min_values=[0, 20, 0],
@@ -419,140 +425,169 @@ def create_range_slider_config(min_values: List[float], max_values: List[float],
419
425
  # Validate inputs
420
426
  if len(min_values) != len(max_values):
421
427
  raise ValueError("min_values and max_values must have the same length")
422
-
428
+
423
429
  if lower_values is not None and len(lower_values) != len(min_values):
424
- raise ValueError("lower_values must have the same length as min_values and max_values")
425
-
430
+ raise ValueError(
431
+ "lower_values must have the same length as min_values and max_values"
432
+ )
433
+
426
434
  if upper_values is not None and len(upper_values) != len(min_values):
427
- raise ValueError("upper_values must have the same length as min_values and max_values")
428
-
435
+ raise ValueError(
436
+ "upper_values must have the same length as min_values and max_values"
437
+ )
438
+
429
439
  # Both lower_values and upper_values must be provided together or not at all
430
440
  if (lower_values is None) != (upper_values is None):
431
- raise ValueError("lower_values and upper_values must both be provided or both be None")
432
-
441
+ raise ValueError(
442
+ "lower_values and upper_values must both be provided or both be None"
443
+ )
444
+
433
445
  # Calculate steps and mark intervals
434
- steps = calculate_slider_steps(min_values, max_values) # Use the calculated steps directly
435
-
436
- input_steps = calculate_input_steps(min_values, max_values) # Calculate input steps based on range
446
+ steps = calculate_slider_steps(
447
+ min_values, max_values
448
+ ) # Use the calculated steps directly
437
449
 
438
- # Snap min and max values to the slider grid
450
+ input_steps = calculate_input_steps(
451
+ min_values, max_values
452
+ ) # Calculate input steps based on range
453
+
454
+ # Snap min and max values to the slider grid (keeping within original range)
439
455
  grid_min_values = []
440
456
  grid_max_values = []
441
457
  for i, (min_val, max_val, step) in enumerate(zip(min_values, max_values, steps)):
442
- # For min value: round down to nearest grid point (floor)
443
- grid_min = min_val - (min_val % step) if min_val % step != 0 else min_val
444
-
445
- # For max value: round up to nearest grid point (ceil)
446
- remainder = max_val % step
447
- grid_max = max_val + (step - remainder) if remainder != 0 else max_val
448
-
458
+ # For min value: round up to nearest grid point (ceil) to stay >= min_val
459
+ if min_val % step == 0:
460
+ grid_min = min_val
461
+ else:
462
+ grid_min = min_val + (step - (min_val % step))
463
+
464
+ # For max value: round down to nearest grid point (floor) to stay <= max_val
465
+ grid_max = max_val - (max_val % step) if max_val % step != 0 else max_val
466
+
467
+ # Ensure we have a valid range (grid_min <= grid_max)
468
+ if grid_min > grid_max:
469
+ # If rounding inward creates invalid range, use original values
470
+ grid_min = min_val
471
+ grid_max = max_val
472
+
449
473
  grid_min_values.append(grid_min)
450
474
  grid_max_values.append(grid_max)
451
475
 
452
476
  mark_intervals = calculate_mark_intervals(grid_min_values, grid_max_values)
453
-
477
+
454
478
  # Generate marks for each slider
455
479
  mark_vals = []
456
- for min_val, max_val, interval in zip(grid_min_values, grid_max_values, mark_intervals):
480
+ for min_val, max_val, interval in zip(
481
+ grid_min_values, grid_max_values, mark_intervals
482
+ ):
457
483
  marks = {}
458
-
484
+
459
485
  # Find the first mark position that's a multiple of interval
460
486
  # Start from the nearest multiple of interval >= min_val
461
487
  if interval > 0:
462
488
  first_mark = math.ceil(min_val / interval) * interval
463
489
  current = first_mark
464
-
490
+
465
491
  while current <= max_val:
466
492
  # Round to avoid floating-point precision issues
467
493
  rounded_mark = round(current, 10) # Round to 10 decimal places
468
- marks[rounded_mark] = '' # Empty string for clean appearance
494
+ marks[str(rounded_mark)] = "" # Convert float key to string, empty string for clean appearance
469
495
  current += interval
470
-
496
+
471
497
  mark_vals.append(marks)
472
498
 
473
499
  # Process property values if provided
474
500
  grid_slider_vals = []
475
501
  grid_input_vals = []
476
-
502
+
477
503
  if lower_values is not None and upper_values is not None:
478
504
  for i, (min_val, max_val, step, input_step, lower_val, upper_val) in enumerate(
479
- zip(grid_min_values, grid_max_values, steps, input_steps, lower_values, upper_values)
505
+ zip(
506
+ grid_min_values,
507
+ grid_max_values,
508
+ steps,
509
+ input_steps,
510
+ lower_values,
511
+ upper_values,
512
+ )
480
513
  ):
481
514
  # Ensure proper ordering of lower/upper values
482
515
  actual_lower = min(lower_val, upper_val)
483
516
  actual_upper = max(lower_val, upper_val)
484
-
517
+
485
518
  # Snap to grids
486
519
  lower_slider_grid = snap_to_grid_no_clamp(actual_lower, min_val, step)
487
520
  upper_slider_grid = snap_to_grid_no_clamp(actual_upper, min_val, step)
488
521
  lower_input_grid = snap_to_grid_no_clamp(actual_lower, min_val, input_step)
489
522
  upper_input_grid = snap_to_grid_no_clamp(actual_upper, min_val, input_step)
490
-
523
+
491
524
  # Store as tuples
492
525
  grid_slider_vals.append((lower_slider_grid, upper_slider_grid))
493
526
  grid_input_vals.append((lower_input_grid, upper_input_grid))
494
527
 
495
528
  # Create the configuration dictionary
496
529
  config = {
497
- 'min_vals': grid_min_values,
498
- 'max_vals': grid_max_values,
499
- 'step_vals': steps,
500
- 'input_step_vals': input_steps,
501
- 'mark_vals': mark_vals
530
+ "min_vals": grid_min_values,
531
+ "max_vals": grid_max_values,
532
+ "step_vals": steps,
533
+ "input_step_vals": input_steps,
534
+ "mark_vals": mark_vals,
502
535
  }
503
-
536
+
504
537
  # Add grid values if property values were provided
505
538
  if lower_values is not None and upper_values is not None:
506
- config['grid_slider_vals'] = grid_slider_vals
507
- config['grid_input_vals'] = grid_input_vals
508
-
539
+ config["grid_slider_vals"] = grid_slider_vals
540
+ config["grid_input_vals"] = grid_input_vals
541
+
509
542
  return config
510
543
 
511
544
 
512
- def snap_to_slider_grid(value: float, min_val: float, max_val: float, step: float) -> float:
545
+ def snap_to_slider_grid(
546
+ value: float, min_val: float, max_val: float, step: float
547
+ ) -> float:
513
548
  """
514
549
  Snap a value to the nearest valid position on a slider grid.
515
-
550
+
516
551
  This ensures that property values align with the discrete steps of a slider,
517
552
  preventing issues with values that fall between valid slider positions.
518
-
553
+
519
554
  Args:
520
555
  value (float): The value to snap to the grid
521
556
  min_val (float): Minimum value of the slider range
522
557
  max_val (float): Maximum value of the slider range
523
558
  step (float): Step size of the slider
524
-
559
+
525
560
  Returns:
526
561
  float: Value snapped to the nearest valid slider position
527
-
562
+
528
563
  Examples:
529
564
  >>> # Snap 23.7 to a slider with step 0.1
530
565
  >>> snap_to_slider_grid(23.7, 0, 100, 0.1)
531
566
  23.7
532
-
533
- >>> # Snap 23.75 to a slider with step 0.1
567
+
568
+ >>> # Snap 23.75 to a slider with step 0.1
534
569
  >>> snap_to_slider_grid(23.75, 0, 100, 0.1)
535
570
  23.8
536
-
571
+
537
572
  >>> # Clamp value outside range
538
573
  >>> snap_to_slider_grid(150, 0, 100, 1.0)
539
574
  100.0
540
575
  """
541
576
  # First clamp to range
542
577
  clamped_value = max(min_val, min(max_val, value))
543
-
578
+
544
579
  # Calculate offset from minimum
545
580
  offset = clamped_value - min_val
546
-
581
+
547
582
  # Round to nearest step
548
583
  num_steps = round(offset / step)
549
-
584
+
550
585
  # Calculate snapped value
551
586
  snapped_value = min_val + (num_steps * step)
552
-
587
+
553
588
  # Ensure we stay within bounds (floating point precision issues)
554
589
  snapped_value = max(min_val, min(max_val, snapped_value))
555
-
590
+
556
591
  # Round to appropriate precision based on step size
557
592
  if step >= 1:
558
593
  return round(snapped_value)
@@ -564,42 +599,42 @@ def snap_to_slider_grid(value: float, min_val: float, max_val: float, step: floa
564
599
  def snap_to_grid_no_clamp(value: float, min_val: float, step: float) -> float:
565
600
  """
566
601
  Snap a value to the nearest grid position without clamping to range.
567
-
602
+
568
603
  This function snaps values to the grid defined by min_val and step,
569
604
  but does NOT clamp values to stay within a min/max range. This allows
570
605
  property values outside the slider range to maintain their relative
571
606
  position on the extended grid.
572
-
607
+
573
608
  Args:
574
609
  value (float): The value to snap to the grid
575
610
  min_val (float): Minimum value of the slider range (grid reference point)
576
611
  step (float): Step size of the grid
577
-
612
+
578
613
  Returns:
579
614
  float: Value snapped to the nearest valid grid position
580
-
615
+
581
616
  Examples:
582
617
  >>> # Snap value within range
583
618
  >>> snap_to_grid_no_clamp(23.75, 0, 0.1)
584
619
  23.8
585
-
620
+
586
621
  >>> # Snap value outside range (not clamped)
587
622
  >>> snap_to_grid_no_clamp(150.3, 0, 0.1)
588
623
  150.3
589
-
624
+
590
625
  >>> # Snap value below range (not clamped)
591
626
  >>> snap_to_grid_no_clamp(-5.67, 0, 0.1)
592
627
  -5.7
593
628
  """
594
629
  # Calculate offset from minimum (no clamping)
595
630
  offset = value - min_val
596
-
631
+
597
632
  # Round to nearest step
598
633
  num_steps = round(offset / step)
599
-
634
+
600
635
  # Calculate snapped value
601
636
  snapped_value = min_val + (num_steps * step)
602
-
637
+
603
638
  # Round to appropriate precision based on step size
604
639
  if step >= 1:
605
640
  return round(snapped_value)
@@ -611,17 +646,17 @@ def snap_to_grid_no_clamp(value: float, min_val: float, step: float) -> float:
611
646
  def format_slider_value(value: float, step: float) -> str:
612
647
  """
613
648
  Format a slider value for display based on the step size.
614
-
649
+
615
650
  Automatically determines appropriate decimal places based on the step size
616
651
  to avoid displaying unnecessary precision.
617
-
652
+
618
653
  Args:
619
654
  value (float): The value to format
620
655
  step (float): The step size of the slider
621
-
656
+
622
657
  Returns:
623
658
  str: Formatted value string
624
-
659
+
625
660
  Examples:
626
661
  >>> format_slider_value(123.456, 1.0)
627
662
  '123'
@@ -639,38 +674,35 @@ def format_slider_value(value: float, step: float) -> str:
639
674
 
640
675
 
641
676
  def are_slider_input_values_incompatible(
642
- slider_value: float,
643
- input_value: float,
644
- slider_step: float,
645
- input_step: float
677
+ slider_value: float, input_value: float, slider_step: float, input_step: float
646
678
  ) -> bool:
647
679
  """
648
680
  Check if slider and input values are incompatible with each other.
649
-
681
+
650
682
  Two values are considered incompatible if there is no single original value
651
683
  that would snap to both the given slider grid value AND the given input grid value.
652
684
  This typically happens when the slider value and input value represent different
653
685
  original values that have been snapped to their respective grids.
654
-
686
+
655
687
  Args:
656
688
  slider_value (float): The value from the slider grid
657
- input_value (float): The value from the input grid
689
+ input_value (float): The value from the input grid
658
690
  slider_step (float): Step size for the slider grid
659
691
  input_step (float): Step size for the input grid
660
-
692
+
661
693
  Returns:
662
694
  bool: True if the values are incompatible, False if they could represent
663
695
  the same original value snapped to different grids
664
-
696
+
665
697
  Examples:
666
698
  >>> # Compatible values: both could come from original value 12.36
667
699
  >>> are_slider_input_values_incompatible(12.4, 12.36, 0, 0.1, 0.01)
668
700
  False
669
-
701
+
670
702
  >>> # Incompatible values: no single value could snap to both
671
- >>> are_slider_input_values_incompatible(12.3, 12.36, 0, 0.1, 0.01)
703
+ >>> are_slider_input_values_incompatible(12.3, 12.36, 0, 0.1, 0.01)
672
704
  True
673
-
705
+
674
706
  >>> # Compatible values: both could come from original value 12.35
675
707
  >>> are_slider_input_values_incompatible(12.4, 12.35, 0, 0.1, 0.01)
676
708
  False
@@ -679,20 +711,18 @@ def are_slider_input_values_incompatible(
679
711
  slider_half_step = slider_step / 2.0
680
712
  slider_min_original = slider_value - slider_half_step
681
713
  slider_max_original = slider_value + slider_half_step
682
-
683
- # Calculate the range of original values that would snap to the input_value
714
+
715
+ # Calculate the range of original values that would snap to the input_value
684
716
  input_half_step = input_step / 2.0
685
717
  input_min_original = input_value - input_half_step
686
718
  input_max_original = input_value + input_half_step
687
-
719
+
688
720
  # Check if the ranges overlap
689
721
  # If they overlap, there exists at least one original value that would snap to both
690
722
  overlap_start = max(slider_min_original, input_min_original)
691
723
  overlap_end = min(slider_max_original, input_max_original)
692
-
724
+
693
725
  # If overlap_start > overlap_end, there's no overlap (incompatible)
694
726
  # Use a small tolerance for floating-point comparison
695
727
  tolerance = 1e-12
696
728
  return overlap_start > overlap_end + tolerance
697
-
698
-