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