Skip to content

Commit 747ff73

Browse files
committed
implement SensitivityDistribution & its builder
1 parent e35455b commit 747ff73

File tree

1 file changed

+85
-0
lines changed

1 file changed

+85
-0
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package io.monstarlab.mosaic.slider
2+
3+
public class SensitivityDistribution private constructor(
4+
equationMap: Map<Float, LinearEquation>,
5+
) {
6+
private var _equationList: MutableList<RangedLinearEquation> =
7+
mutableListOf<RangedLinearEquation>()
8+
public val equationList: List<RangedLinearEquation> get() = _equationList.toList()
9+
10+
init {
11+
var previous: Pair<Float, LinearEquation>? = null
12+
// get the max range start value to help us identify the last equation
13+
val maxRangeStart = equationMap.maxOf { it.key }
14+
equationMap.forEach { pair ->
15+
previous?.let {
16+
addEquation(it.second, it.first, pair.key)
17+
}
18+
// add the last equation in the range and make sure the range for it ends at 1f
19+
if (pair.key == maxRangeStart) {
20+
addEquation(pair.value, pair.key, 1f)
21+
}
22+
previous = pair.toPair()
23+
}
24+
}
25+
26+
private fun addEquation(equation: LinearEquation, rangeStart: Float, rangeEnd: Float) {
27+
val offsetRange = rangeStart..rangeEnd
28+
val valueRange =
29+
equation.valueFromOffset(rangeStart)..equation.valueFromOffset(rangeEnd)
30+
_equationList.add(
31+
RangedLinearEquation(
32+
equation = equation,
33+
offsetRange = offsetRange,
34+
valueRange = valueRange,
35+
),
36+
)
37+
}
38+
39+
public fun valueFromOffset(offset: Float): Float? =
40+
_equationList.firstOrNull { offset in it.offsetRange }?.equation?.valueFromOffset(offset)
41+
42+
public fun offsetFromValue(value: Float): Float? =
43+
_equationList.firstOrNull { value in it.valueRange }?.equation?.offsetFromValue(value)
44+
45+
public class Builder {
46+
private var equationRangeStartMap = mutableMapOf<Float, LinearEquation>()
47+
private var maxRangeValue: Float = -1f
48+
private var lastEquation: LinearEquation? = null
49+
50+
public fun add(sensitivity: Float, rangeStart: Float): Builder = apply {
51+
if (equationRangeStartMap.isEmpty() && rangeStart != 0f) {
52+
throw FirstValueNotZeroException()
53+
}
54+
if (rangeStart !in 0.0f..1f) throw OutOfRangeException()
55+
if (rangeStart < maxRangeValue) throw OverlappingRangeException()
56+
57+
val equationRangeStartPair = calcNextEquation(sensitivity, rangeStart, lastEquation)
58+
equationRangeStartMap[equationRangeStartPair.first] = equationRangeStartPair.second
59+
lastEquation = equationRangeStartPair.second
60+
maxRangeValue = rangeStart
61+
}
62+
63+
private fun calcNextEquation(
64+
slope: Float,
65+
rangeStart: Float,
66+
previousEquation: LinearEquation?,
67+
): Pair<Float, LinearEquation> {
68+
val y0 = previousEquation?.valueFromOffset(rangeStart) ?: 0f
69+
val c = y0 - slope * rangeStart
70+
return Pair(rangeStart, LinearEquation(slope, c))
71+
}
72+
73+
public fun build(): SensitivityDistribution {
74+
return SensitivityDistribution(equationRangeStartMap)
75+
}
76+
}
77+
78+
public class FirstValueNotZeroException :
79+
Exception("The range of the first value added should start from zero")
80+
81+
public class OutOfRangeException : Exception("The range value should be between 0 and 1")
82+
83+
public class OverlappingRangeException :
84+
Exception("can't add overlapping ranges with different sensitivity")
85+
}

0 commit comments

Comments
 (0)