@@ -166,11 +166,68 @@ class AnimatedTexture(defaultChannel: AnimationChannel) : Texture(defaultChannel
166166 }
167167 .duration(Duration .seconds(animationChannel.frameDuration * animationChannel.sequence.size))
168168 .interpolator(interpolator)
169- .animate(AnimatedValue (0 , animationChannel.sequence.size - 1 ))
169+ .animate(PreciseAnimatedIntValue (0 , animationChannel.sequence.size - 1 ))
170170 .onProgress { frameNum ->
171171 currentFrame = min(frameNum, animationChannel.sequence.size - 1 )
172172 updateImage()
173173 }
174174 .build()
175175 }
176+ }
177+
178+ /* *
179+ * This animated value provides higher accuracy for mapping progress to an int value.
180+ *
181+ * For example, the default AnimatedValue results for [0,3] (non-uniform!):
182+ 0.00: 0
183+ 0.05: 0
184+ 0.10: 0
185+ 0.15: 0
186+
187+ 0.20: 1
188+ 0.25: 1
189+ 0.30: 1
190+ 0.35: 1
191+ 0.40: 1
192+ 0.45: 1
193+
194+ 0.50: 2
195+ 0.55: 2
196+ 0.60: 2
197+ 0.65: 2
198+ 0.70: 2
199+ 0.75: 2
200+ 0.80: 2
201+
202+ 0.85: 3
203+ 0.90: 3
204+ 0.95: 3
205+ 1.00: 3
206+ */
207+ private class PreciseAnimatedIntValue (start : Int , end : Int ) : AnimatedValue<Int>(start, end) {
208+
209+ private val mapping = linkedMapOf<ClosedFloatingPointRange <Double >, Int > ()
210+
211+ init {
212+ val numSegments = end - start + 1
213+ val progressPerSegment = 1.0 / numSegments
214+
215+ var progress = 0.0
216+
217+ for (i in start.. end) {
218+ val progressEnd = if (i == end) 1.0 else (progress + progressPerSegment)
219+
220+ mapping[progress.. progressEnd] = i
221+
222+ progress + = progressPerSegment
223+ }
224+ }
225+
226+ override fun animate (val1 : Int , val2 : Int , progress : Double , interpolator : Interpolator ): Int {
227+ val p = interpolator.interpolate(0.0 , 1.0 , progress)
228+
229+ return mapping.filterKeys { p in it }
230+ .values
231+ .firstOrNull() ? : val1
232+ }
176233}
0 commit comments