1 /*
<lambda>null2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.compose.foundation.layout
18 
19 import androidx.compose.foundation.layout.internal.JvmDefaultWithCompatibility
20 import androidx.compose.runtime.Immutable
21 import androidx.compose.runtime.Stable
22 import androidx.compose.ui.Alignment
23 import androidx.compose.ui.unit.Density
24 import androidx.compose.ui.unit.Dp
25 import androidx.compose.ui.unit.LayoutDirection
26 import androidx.compose.ui.unit.dp
27 import androidx.compose.ui.util.fastRoundToInt
28 import kotlin.math.min
29 
30 /**
31  * Used to specify the arrangement of the layout's children in layouts like [Row] or [Column] in the
32  * main axis direction (horizontal and vertical, respectively).
33  *
34  * Below is an illustration of different horizontal arrangements in [Row]s: ![Row
35  * arrangements](https://developer.android.com/images/reference/androidx/compose/foundation/layout/row_arrangement_visualization.gif)
36  *
37  * Different vertical arrangements in [Column]s: ![Column
38  * arrangements](https://developer.android.com/images/reference/androidx/compose/foundation/layout/column_arrangement_visualization.gif)
39  */
40 @Immutable
41 object Arrangement {
42     /**
43      * Used to specify the horizontal arrangement of the layout's children in layouts like [Row].
44      */
45     @Stable
46     @JvmDefaultWithCompatibility
47     interface Horizontal {
48         /** Spacing that should be added between any two adjacent layout children. */
49         val spacing
50             get() = 0.dp
51 
52         /**
53          * Horizontally places the layout children.
54          *
55          * @param totalSize Available space that can be occupied by the children, in pixels.
56          * @param sizes An array of sizes of all children, in pixels.
57          * @param layoutDirection A layout direction, left-to-right or right-to-left, of the parent
58          *   layout that should be taken into account when determining positions of the children.
59          * @param outPositions An array of the size of [sizes] that returns the calculated positions
60          *   relative to the left, in pixels.
61          */
62         fun Density.arrange(
63             totalSize: Int,
64             sizes: IntArray,
65             layoutDirection: LayoutDirection,
66             outPositions: IntArray
67         )
68     }
69 
70     /**
71      * Used to specify the vertical arrangement of the layout's children in layouts like [Column].
72      */
73     @Stable
74     @JvmDefaultWithCompatibility
75     interface Vertical {
76         /** Spacing that should be added between any two adjacent layout children. */
77         val spacing
78             get() = 0.dp
79 
80         /**
81          * Vertically places the layout children.
82          *
83          * @param totalSize Available space that can be occupied by the children, in pixels.
84          * @param sizes An array of sizes of all children, in pixels.
85          * @param outPositions An array of the size of [sizes] that returns the calculated positions
86          *   relative to the top, in pixels.
87          */
88         fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray)
89     }
90 
91     /**
92      * Used to specify the horizontal arrangement of the layout's children in horizontal layouts
93      * like [Row], or the vertical arrangement of the layout's children in vertical layouts like
94      * [Column].
95      */
96     @Stable
97     @JvmDefaultWithCompatibility
98     interface HorizontalOrVertical : Horizontal, Vertical {
99         /** Spacing that should be added between any two adjacent layout children. */
100         override val spacing: Dp
101             get() = 0.dp
102     }
103 
104     /**
105      * Place children horizontally such that they are as close as possible to the beginning of the
106      * horizontal axis (left if the layout direction is LTR, right otherwise). Visually: 123#### for
107      * LTR and ####321.
108      */
109     @Stable
110     val Start =
111         object : Horizontal {
112             override fun Density.arrange(
113                 totalSize: Int,
114                 sizes: IntArray,
115                 layoutDirection: LayoutDirection,
116                 outPositions: IntArray
117             ) =
118                 if (layoutDirection == LayoutDirection.Ltr) {
119                     placeLeftOrTop(sizes, outPositions, reverseInput = false)
120                 } else {
121                     placeRightOrBottom(totalSize, sizes, outPositions, reverseInput = true)
122                 }
123 
124             override fun toString() = "Arrangement#Start"
125         }
126 
127     /**
128      * Place children horizontally such that they are as close as possible to the end of the main
129      * axis. Visually: ####123 for LTR and 321#### for RTL.
130      */
131     @Stable
132     val End =
133         object : Horizontal {
134             override fun Density.arrange(
135                 totalSize: Int,
136                 sizes: IntArray,
137                 layoutDirection: LayoutDirection,
138                 outPositions: IntArray
139             ) =
140                 if (layoutDirection == LayoutDirection.Ltr) {
141                     placeRightOrBottom(totalSize, sizes, outPositions, reverseInput = false)
142                 } else {
143                     placeLeftOrTop(sizes, outPositions, reverseInput = true)
144                 }
145 
146             override fun toString() = "Arrangement#End"
147         }
148 
149     /**
150      * Place children vertically such that they are as close as possible to the top of the main
151      * axis. Visually: (top) 123#### (bottom)
152      */
153     @Stable
154     val Top =
155         object : Vertical {
156             override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) =
157                 placeLeftOrTop(sizes, outPositions, reverseInput = false)
158 
159             override fun toString() = "Arrangement#Top"
160         }
161 
162     /**
163      * Place children vertically such that they are as close as possible to the bottom of the main
164      * axis. Visually: (top) ####123 (bottom)
165      */
166     @Stable
167     val Bottom =
168         object : Vertical {
169             override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) =
170                 placeRightOrBottom(totalSize, sizes, outPositions, reverseInput = false)
171 
172             override fun toString() = "Arrangement#Bottom"
173         }
174 
175     /**
176      * Place children such that they are as close as possible to the middle of the main axis.
177      * Visually: ##123## for LTR and ##321## for RTL.
178      */
179     @Stable
180     val Center =
181         object : HorizontalOrVertical {
182             override val spacing = 0.dp
183 
184             override fun Density.arrange(
185                 totalSize: Int,
186                 sizes: IntArray,
187                 layoutDirection: LayoutDirection,
188                 outPositions: IntArray
189             ) =
190                 if (layoutDirection == LayoutDirection.Ltr) {
191                     placeCenter(totalSize, sizes, outPositions, reverseInput = false)
192                 } else {
193                     placeCenter(totalSize, sizes, outPositions, reverseInput = true)
194                 }
195 
196             override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) =
197                 placeCenter(totalSize, sizes, outPositions, reverseInput = false)
198 
199             override fun toString() = "Arrangement#Center"
200         }
201 
202     /**
203      * Place children such that they are spaced evenly across the main axis, including free space
204      * before the first child and after the last child. Visually: #1#2#3# for LTR and #3#2#1# for
205      * RTL.
206      */
207     @Stable
208     val SpaceEvenly =
209         object : HorizontalOrVertical {
210             override val spacing = 0.dp
211 
212             override fun Density.arrange(
213                 totalSize: Int,
214                 sizes: IntArray,
215                 layoutDirection: LayoutDirection,
216                 outPositions: IntArray
217             ) =
218                 if (layoutDirection == LayoutDirection.Ltr) {
219                     placeSpaceEvenly(totalSize, sizes, outPositions, reverseInput = false)
220                 } else {
221                     placeSpaceEvenly(totalSize, sizes, outPositions, reverseInput = true)
222                 }
223 
224             override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) =
225                 placeSpaceEvenly(totalSize, sizes, outPositions, reverseInput = false)
226 
227             override fun toString() = "Arrangement#SpaceEvenly"
228         }
229 
230     /**
231      * Place children such that they are spaced evenly across the main axis, without free space
232      * before the first child or after the last child. Visually: 1##2##3 for LTR or 3##2##1 for RTL.
233      */
234     @Stable
235     val SpaceBetween =
236         object : HorizontalOrVertical {
237             override val spacing = 0.dp
238 
239             override fun Density.arrange(
240                 totalSize: Int,
241                 sizes: IntArray,
242                 layoutDirection: LayoutDirection,
243                 outPositions: IntArray
244             ) =
245                 if (layoutDirection == LayoutDirection.Ltr) {
246                     placeSpaceBetween(totalSize, sizes, outPositions, reverseInput = false)
247                 } else {
248                     placeSpaceBetween(totalSize, sizes, outPositions, reverseInput = true)
249                 }
250 
251             override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) =
252                 placeSpaceBetween(totalSize, sizes, outPositions, reverseInput = false)
253 
254             override fun toString() = "Arrangement#SpaceBetween"
255         }
256 
257     /**
258      * Place children such that they are spaced evenly across the main axis, including free space
259      * before the first child and after the last child, but half the amount of space existing
260      * otherwise between two consecutive children. Visually: #1##2##3# for LTR and #3##2##1# for RTL
261      */
262     @Stable
263     val SpaceAround =
264         object : HorizontalOrVertical {
265             override val spacing = 0.dp
266 
267             override fun Density.arrange(
268                 totalSize: Int,
269                 sizes: IntArray,
270                 layoutDirection: LayoutDirection,
271                 outPositions: IntArray
272             ) =
273                 if (layoutDirection == LayoutDirection.Ltr) {
274                     placeSpaceAround(totalSize, sizes, outPositions, reverseInput = false)
275                 } else {
276                     placeSpaceAround(totalSize, sizes, outPositions, reverseInput = true)
277                 }
278 
279             override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) =
280                 placeSpaceAround(totalSize, sizes, outPositions, reverseInput = false)
281 
282             override fun toString() = "Arrangement#SpaceAround"
283         }
284 
285     /**
286      * Place children such that each two adjacent ones are spaced by a fixed [space] distance across
287      * the main axis. The spacing will be subtracted from the available space that the children can
288      * occupy. The [space] can be negative, in which case children will overlap.
289      *
290      * To change alignment of the spaced children horizontally or vertically, use [spacedBy]
291      * overloads with `alignment` parameter.
292      *
293      * @param space The space between adjacent children.
294      */
295     @Stable
296     fun spacedBy(space: Dp): HorizontalOrVertical =
297         SpacedAligned(space, true) { size, layoutDirection ->
298             Alignment.Start.align(0, size, layoutDirection)
299         }
300 
301     /**
302      * Place children horizontally such that each two adjacent ones are spaced by a fixed [space]
303      * distance. The spacing will be subtracted from the available width that the children can
304      * occupy. An [alignment] can be specified to align the spaced children horizontally inside the
305      * parent, in case there is empty width remaining. The [space] can be negative, in which case
306      * children will overlap.
307      *
308      * @param space The space between adjacent children.
309      * @param alignment The alignment of the spaced children inside the parent.
310      */
311     @Stable
312     fun spacedBy(space: Dp, alignment: Alignment.Horizontal): Horizontal =
313         SpacedAligned(space, true) { size, layoutDirection ->
314             alignment.align(0, size, layoutDirection)
315         }
316 
317     /**
318      * Place children vertically such that each two adjacent ones are spaced by a fixed [space]
319      * distance. The spacing will be subtracted from the available height that the children can
320      * occupy. An [alignment] can be specified to align the spaced children vertically inside the
321      * parent, in case there is empty height remaining. The [space] can be negative, in which case
322      * children will overlap.
323      *
324      * @param space The space between adjacent children.
325      * @param alignment The alignment of the spaced children inside the parent.
326      */
327     @Stable
328     fun spacedBy(space: Dp, alignment: Alignment.Vertical): Vertical =
329         SpacedAligned(space, false) { size, _ -> alignment.align(0, size) }
330 
331     /**
332      * Place children horizontally one next to the other and align the obtained group according to
333      * an [alignment].
334      *
335      * @param alignment The alignment of the children inside the parent.
336      */
337     @Stable
338     fun aligned(alignment: Alignment.Horizontal): Horizontal =
339         SpacedAligned(0.dp, true) { size, layoutDirection ->
340             alignment.align(0, size, layoutDirection)
341         }
342 
343     /**
344      * Place children vertically one next to the other and align the obtained group according to an
345      * [alignment].
346      *
347      * @param alignment The alignment of the children inside the parent.
348      */
349     @Stable
350     fun aligned(alignment: Alignment.Vertical): Vertical =
351         SpacedAligned(0.dp, false) { size, _ -> alignment.align(0, size) }
352 
353     @Immutable
354     object Absolute {
355         /**
356          * Place children horizontally such that they are as close as possible to the left edge of
357          * the [Row].
358          *
359          * Unlike [Arrangement.Start], when the layout direction is RTL, the children will not be
360          * mirrored and as such children will appear in the order they are composed inside the
361          * [Row].
362          *
363          * Visually: 123####
364          */
365         @Stable
366         val Left =
367             object : Horizontal {
368                 override fun Density.arrange(
369                     totalSize: Int,
370                     sizes: IntArray,
371                     layoutDirection: LayoutDirection,
372                     outPositions: IntArray
373                 ) = placeLeftOrTop(sizes, outPositions, reverseInput = false)
374 
375                 override fun toString() = "AbsoluteArrangement#Left"
376             }
377 
378         /**
379          * Place children such that they are as close as possible to the middle of the [Row].
380          *
381          * Unlike [Arrangement.Center], when the layout direction is RTL, the children will not be
382          * mirrored and as such children will appear in the order they are composed inside the
383          * [Row].
384          *
385          * Visually: ##123##
386          */
387         @Stable
388         val Center =
389             object : Horizontal {
390                 override fun Density.arrange(
391                     totalSize: Int,
392                     sizes: IntArray,
393                     layoutDirection: LayoutDirection,
394                     outPositions: IntArray
395                 ) = placeCenter(totalSize, sizes, outPositions, reverseInput = false)
396 
397                 override fun toString() = "AbsoluteArrangement#Center"
398             }
399 
400         /**
401          * Place children horizontally such that they are as close as possible to the right edge of
402          * the [Row].
403          *
404          * Unlike [Arrangement.End], when the layout direction is RTL, the children will not be
405          * mirrored and as such children will appear in the order they are composed inside the
406          * [Row].
407          *
408          * Visually: ####123
409          */
410         @Stable
411         val Right =
412             object : Horizontal {
413                 override fun Density.arrange(
414                     totalSize: Int,
415                     sizes: IntArray,
416                     layoutDirection: LayoutDirection,
417                     outPositions: IntArray
418                 ) = placeRightOrBottom(totalSize, sizes, outPositions, reverseInput = false)
419 
420                 override fun toString() = "AbsoluteArrangement#Right"
421             }
422 
423         /**
424          * Place children such that they are spaced evenly across the main axis, without free space
425          * before the first child or after the last child.
426          *
427          * Unlike [Arrangement.SpaceBetween], when the layout direction is RTL, the children will
428          * not be mirrored and as such children will appear in the order they are composed inside
429          * the [Row].
430          *
431          * Visually: 1##2##3
432          */
433         @Stable
434         val SpaceBetween =
435             object : Horizontal {
436                 override fun Density.arrange(
437                     totalSize: Int,
438                     sizes: IntArray,
439                     layoutDirection: LayoutDirection,
440                     outPositions: IntArray
441                 ) = placeSpaceBetween(totalSize, sizes, outPositions, reverseInput = false)
442 
443                 override fun toString() = "AbsoluteArrangement#SpaceBetween"
444             }
445 
446         /**
447          * Place children such that they are spaced evenly across the main axis, including free
448          * space before the first child and after the last child.
449          *
450          * Unlike [Arrangement.SpaceEvenly], when the layout direction is RTL, the children will not
451          * be mirrored and as such children will appear in the order they are composed inside the
452          * [Row].
453          *
454          * Visually: #1#2#3#
455          */
456         @Stable
457         val SpaceEvenly =
458             object : Horizontal {
459                 override fun Density.arrange(
460                     totalSize: Int,
461                     sizes: IntArray,
462                     layoutDirection: LayoutDirection,
463                     outPositions: IntArray
464                 ) = placeSpaceEvenly(totalSize, sizes, outPositions, reverseInput = false)
465 
466                 override fun toString() = "AbsoluteArrangement#SpaceEvenly"
467             }
468 
469         /**
470          * Place children such that they are spaced evenly horizontally, including free space before
471          * the first child and after the last child, but half the amount of space existing otherwise
472          * between two consecutive children.
473          *
474          * Unlike [Arrangement.SpaceAround], when the layout direction is RTL, the children will not
475          * be mirrored and as such children will appear in the order they are composed inside the
476          * [Row].
477          *
478          * Visually: #1##2##3##4#
479          */
480         @Stable
481         val SpaceAround =
482             object : Horizontal {
483                 override fun Density.arrange(
484                     totalSize: Int,
485                     sizes: IntArray,
486                     layoutDirection: LayoutDirection,
487                     outPositions: IntArray
488                 ) = placeSpaceAround(totalSize, sizes, outPositions, reverseInput = false)
489 
490                 override fun toString() = "AbsoluteArrangement#SpaceAround"
491             }
492 
493         /**
494          * Place children such that each two adjacent ones are spaced by a fixed [space] distance
495          * across the main axis. The spacing will be subtracted from the available space that the
496          * children can occupy.
497          *
498          * Unlike [Arrangement.spacedBy], when the layout direction is RTL, the children will not be
499          * mirrored and as such children will appear in the order they are composed inside the
500          * [Row].
501          *
502          * @param space The space between adjacent children.
503          */
504         @Stable fun spacedBy(space: Dp): HorizontalOrVertical = SpacedAligned(space, false, null)
505 
506         /**
507          * Place children horizontally such that each two adjacent ones are spaced by a fixed
508          * [space] distance. The spacing will be subtracted from the available width that the
509          * children can occupy. An [alignment] can be specified to align the spaced children
510          * horizontally inside the parent, in case there is empty width remaining.
511          *
512          * Unlike [Arrangement.spacedBy], when the layout direction is RTL, the children will not be
513          * mirrored and as such children will appear in the order they are composed inside the
514          * [Row].
515          *
516          * @param space The space between adjacent children.
517          * @param alignment The alignment of the spaced children inside the parent.
518          */
519         @Stable
520         fun spacedBy(space: Dp, alignment: Alignment.Horizontal): Horizontal =
521             SpacedAligned(space, false) { size, layoutDirection ->
522                 alignment.align(0, size, layoutDirection)
523             }
524 
525         /**
526          * Place children vertically such that each two adjacent ones are spaced by a fixed [space]
527          * distance. The spacing will be subtracted from the available height that the children can
528          * occupy. An [alignment] can be specified to align the spaced children vertically inside
529          * the parent, in case there is empty height remaining.
530          *
531          * Unlike [Arrangement.spacedBy], when the layout direction is RTL, the children will not be
532          * mirrored and as such children will appear in the order they are composed inside the
533          * [Row].
534          *
535          * @param space The space between adjacent children.
536          * @param alignment The alignment of the spaced children inside the parent.
537          */
538         @Stable
539         fun spacedBy(space: Dp, alignment: Alignment.Vertical): Vertical =
540             SpacedAligned(space, false) { size, _ -> alignment.align(0, size) }
541 
542         /**
543          * Place children horizontally one next to the other and align the obtained group according
544          * to an [alignment].
545          *
546          * Unlike [Arrangement.aligned], when the layout direction is RTL, the children will not be
547          * mirrored and as such children will appear in the order they are composed inside the
548          * [Row].
549          *
550          * @param alignment The alignment of the children inside the parent.
551          */
552         @Stable
553         fun aligned(alignment: Alignment.Horizontal): Horizontal =
554             SpacedAligned(0.dp, false) { size, layoutDirection ->
555                 alignment.align(0, size, layoutDirection)
556             }
557     }
558 
559     /**
560      * Arrangement with spacing between adjacent children and alignment for the spaced group. Should
561      * not be instantiated directly, use [spacedBy] instead.
562      */
563     @Immutable
564     internal data class SpacedAligned(
565         val space: Dp,
566         val rtlMirror: Boolean,
567         val alignment: ((Int, LayoutDirection) -> Int)?
568     ) : HorizontalOrVertical {
569 
570         override val spacing = space
571 
572         override fun Density.arrange(
573             totalSize: Int,
574             sizes: IntArray,
575             layoutDirection: LayoutDirection,
576             outPositions: IntArray
577         ) {
578             if (sizes.isEmpty()) return
579             val spacePx = space.roundToPx()
580 
581             var occupied = 0
582             var lastSpace = 0
583             val reversed = rtlMirror && layoutDirection == LayoutDirection.Rtl
584             sizes.forEachIndexed(reversed) { index, it ->
585                 outPositions[index] = min(occupied, totalSize - it)
586                 lastSpace = min(spacePx, totalSize - outPositions[index] - it)
587                 occupied = outPositions[index] + it + lastSpace
588             }
589             occupied -= lastSpace
590 
591             if (alignment != null && occupied < totalSize) {
592                 val groupPosition = alignment.invoke(totalSize - occupied, layoutDirection)
593                 for (index in outPositions.indices) {
594                     outPositions[index] += groupPosition
595                 }
596             }
597         }
598 
599         override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) =
600             arrange(totalSize, sizes, LayoutDirection.Ltr, outPositions)
601 
602         override fun toString() =
603             "${if (rtlMirror) "" else "Absolute"}Arrangement#spacedAligned($space, $alignment)"
604     }
605 
606     internal fun placeRightOrBottom(
607         totalSize: Int,
608         size: IntArray,
609         outPosition: IntArray,
610         reverseInput: Boolean
611     ) {
612         val consumedSize = size.fold(0) { a, b -> a + b }
613         var current = totalSize - consumedSize
614         size.forEachIndexed(reverseInput) { index, it ->
615             outPosition[index] = current
616             current += it
617         }
618     }
619 
620     internal fun placeLeftOrTop(size: IntArray, outPosition: IntArray, reverseInput: Boolean) {
621         var current = 0
622         size.forEachIndexed(reverseInput) { index, it ->
623             outPosition[index] = current
624             current += it
625         }
626     }
627 
628     internal fun placeCenter(
629         totalSize: Int,
630         size: IntArray,
631         outPosition: IntArray,
632         reverseInput: Boolean
633     ) {
634         val consumedSize = size.fold(0) { a, b -> a + b }
635         var current = (totalSize - consumedSize).toFloat() / 2
636         size.forEachIndexed(reverseInput) { index, it ->
637             outPosition[index] = current.fastRoundToInt()
638             current += it.toFloat()
639         }
640     }
641 
642     internal fun placeSpaceEvenly(
643         totalSize: Int,
644         size: IntArray,
645         outPosition: IntArray,
646         reverseInput: Boolean
647     ) {
648         val consumedSize = size.fold(0) { a, b -> a + b }
649         val gapSize = (totalSize - consumedSize).toFloat() / (size.size + 1)
650         var current = gapSize
651         size.forEachIndexed(reverseInput) { index, it ->
652             outPosition[index] = current.fastRoundToInt()
653             current += it.toFloat() + gapSize
654         }
655     }
656 
657     internal fun placeSpaceBetween(
658         totalSize: Int,
659         size: IntArray,
660         outPosition: IntArray,
661         reverseInput: Boolean
662     ) {
663         if (size.isEmpty()) return
664 
665         val consumedSize = size.fold(0) { a, b -> a + b }
666         val noOfGaps = maxOf(size.lastIndex, 1)
667         val gapSize = (totalSize - consumedSize).toFloat() / noOfGaps
668 
669         var current = 0f
670         if (reverseInput && size.size == 1) {
671             // If the layout direction is right-to-left and there is only one gap,
672             // we start current with the gap size. That forces the single item to be right-aligned.
673             current = gapSize
674         }
675         size.forEachIndexed(reverseInput) { index, it ->
676             outPosition[index] = current.fastRoundToInt()
677             current += it.toFloat() + gapSize
678         }
679     }
680 
681     internal fun placeSpaceAround(
682         totalSize: Int,
683         size: IntArray,
684         outPosition: IntArray,
685         reverseInput: Boolean
686     ) {
687         val consumedSize = size.fold(0) { a, b -> a + b }
688         val gapSize =
689             if (size.isNotEmpty()) {
690                 (totalSize - consumedSize).toFloat() / size.size
691             } else {
692                 0f
693             }
694         var current = gapSize / 2
695         size.forEachIndexed(reverseInput) { index, it ->
696             outPosition[index] = current.fastRoundToInt()
697             current += it.toFloat() + gapSize
698         }
699     }
700 
701     private inline fun IntArray.forEachIndexed(reversed: Boolean, action: (Int, Int) -> Unit) {
702         if (!reversed) {
703             forEachIndexed(action)
704         } else {
705             for (i in (size - 1) downTo 0) {
706                 action(i, get(i))
707             }
708         }
709     }
710 }
711