///|
/// RUI multi-thumb normalization adapted for native touch input (MIT).
pub struct SliderSpec {
min : Int
max : Int
step : Int
min_steps_between : Int
} derive(Eq, Debug)
///|
pub fn slider_spec(
min? : Int = 0,
max? : Int = 100,
step? : Int = 1,
min_steps_between? : Int = 0,
) -> SliderSpec {
guard min >= -100000000 &&
max <= 100000000 &&
max > min &&
step > 0 &&
step <= max - min &&
min_steps_between >= 0 &&
min_steps_between <= (max - min) / step else {
abort("invalid slider range, step or separation")
}
{ min, max, step, min_steps_between, }
}
///|
pub fn SliderSpec::snap(self : SliderSpec, value : Double) -> Int {
let value = if value != value {
self.min.to_double()
} else {
value.clamp(min=self.min.to_double(), max=self.max.to_double())
}
let steps = ((value - self.min.to_double()) / self.step.to_double() + 0.5).to_int()
(self.min + steps * self.step).clamp(
min=self.min,
max=self.min + (self.max - self.min) / self.step * self.step,
)
}
///|
pub fn SliderSpec::normalize(
self : SliderSpec,
values : Array[Int],
) -> Array[Int] {
let values = if values.is_empty() {
[self.min]
} else {
values.map(value => self.snap(value.to_double()))
}
let gap = self.min_steps_between * self.step
guard gap == 0 || values.length() - 1 <= (self.max - self.min) / gap else {
abort("slider has too many thumbs for its minimum separation")
}
values.sort()
for i = 1; i < values.length(); i = i + 1 {
values[i] = values[i].max(values[i - 1] + gap)
}
let last = values.length() - 1
values[last] = values[last].min(self.snap(self.max.to_double()))
for i = last - 1; i >= 0; i = i - 1 {
values[i] = values[i].min(values[i + 1] - gap)
}
values
}
///|
pub fn SliderSpec::update(
self : SliderSpec,
values : Array[Int],
index : Int,
value : Double,
) -> Array[Int] {
let result = self.normalize(values)
guard index >= 0 && index < result.length() else { return result }
let gap = self.min_steps_between * self.step
let low = if index == 0 { self.min } else { result[index - 1] + gap }
let high = if index == result.length() - 1 {
self.snap(self.max.to_double())
} else {
result[index + 1] - gap
}
result[index] = self.snap(value).clamp(min=low, max=high)
result
}
///|
pub fn SliderSpec::fraction(self : SliderSpec, value : Int) -> Double {
((value - self.min).to_double() / (self.max - self.min).to_double()).clamp(
min=0.0,
max=1.0,
)
}
///|
pub fn SliderSpec::nearest(
self : SliderSpec,
values : Array[Int],
value : Double,
) -> Int {
let values = self.normalize(values)
let mut nearest = 0
let mut distance = (values[0].to_double() - value).abs()
for index, current in values {
let next = (current.to_double() - value).abs()
if next < distance {
nearest = index
distance = next
}
}
nearest
}