Simplified ClipSpec classes
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieClipSpec.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieClipSpec.kt
index 0aecbb1..78399dc 100644
--- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieClipSpec.kt
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieClipSpec.kt
@@ -16,131 +16,77 @@
     internal abstract fun getMaxProgress(composition: LottieComposition): Float
 
     /**
-     * Play the animation starting from this frame.
+     * Play the animation between these two frames. [maxInclusive] determines whether the animation
+     * should play the max frame or stop one frame before it.
      */
-    data class MinFrame(val minFrame: Int) : LottieClipSpec() {
+    data class Frame(
+        val min: Int? = null,
+        val max: Int? = null,
+        val maxInclusive: Boolean = true,
+    ) : LottieClipSpec() {
+
+        private val actualMaxFrame = when {
+            max == null -> null
+            maxInclusive -> max
+            else -> max - 1
+        }
+
         override fun getMinProgress(composition: LottieComposition): Float {
-            return (minFrame / composition.endFrame).coerceIn(0f, 1f)
+            return when (min) {
+                null -> 0f
+                else -> (min / composition.endFrame).coerceIn(0f, 1f)
+            }
         }
 
         override fun getMaxProgress(composition: LottieComposition): Float {
-            return 1f
+            return when (actualMaxFrame) {
+                null -> 1f
+                else -> (actualMaxFrame / composition.endFrame).coerceIn(0f, 1f)
+            }
         }
     }
 
     /**
-     * Play the animation until this frame.
+     * Play the animation between these two progress values.
      */
-    data class MaxFrame(val maxFrame: Int, val inclusive: Boolean = true) : LottieClipSpec() {
-
-        private val actualMaxFrame = if (inclusive) maxFrame else maxFrame - 1
-
+    data class Progress(
+        val min: Float = 0f,
+        val max: Float = 1f,
+    ) : LottieClipSpec() {
         override fun getMinProgress(composition: LottieComposition): Float {
-            return 0f
+            return min
         }
 
         override fun getMaxProgress(composition: LottieComposition): Float {
-            return (actualMaxFrame / composition.endFrame).coerceIn(0f, 1f)
-        }
-    }
-
-    /**
-     * Play the animation between these two frames.
-     */
-    data class MinAndMaxFrame(val minFrame: Int, val maxFrame: Int, val maxFrameInclusive: Boolean = true) : LottieClipSpec() {
-
-        private val actualMaxFrame = if (maxFrameInclusive) maxFrame else maxFrame - 1
-
-        override fun getMinProgress(composition: LottieComposition): Float {
-            return (minFrame / composition.endFrame).coerceIn(0f, 1f)
-        }
-
-        override fun getMaxProgress(composition: LottieComposition): Float {
-            return (actualMaxFrame / composition.endFrame).coerceIn(0f, 1f)
-        }
-    }
-
-    /**
-     * Play the animation from this progress.
-     */
-    data class MinProgress(val minProgress: Float) : LottieClipSpec() {
-        override fun getMinProgress(composition: LottieComposition): Float {
-            return minProgress
-        }
-
-        override fun getMaxProgress(composition: LottieComposition): Float {
-            return 1f
-        }
-    }
-
-    /**
-     * Play the animation until this progress.
-     */
-    data class MaxProgress(val maxProgress: Float) : LottieClipSpec() {
-        override fun getMinProgress(composition: LottieComposition): Float {
-            return 0f
-        }
-
-        override fun getMaxProgress(composition: LottieComposition): Float {
-            return maxProgress
-        }
-    }
-
-    /**
-     * Play the animation between these two progresses.
-     */
-    data class MinAndMaxProgress(val minProgress: Float, val maxProgress: Float) : LottieClipSpec() {
-        override fun getMinProgress(composition: LottieComposition): Float {
-            return minProgress
-        }
-
-        override fun getMaxProgress(composition: LottieComposition): Float {
-            return maxProgress
-        }
-    }
-
-    /**
-     * Play the animation starting from this marker.
-     */
-    data class MinMarker(val minMarker: String) : LottieClipSpec() {
-        override fun getMinProgress(composition: LottieComposition): Float {
-            return ((composition.getMarker(minMarker)?.startFrame ?: 0f) / composition.endFrame).coerceIn(0f, 1f)
-        }
-
-        override fun getMaxProgress(composition: LottieComposition): Float {
-            return 1f
-        }
-    }
-
-    /**
-     * Play the animation until this marker. If the marker represents the end of your animation, set
-     * [playMarkerFrame] to true. If the marker represents the beginning of the next section, set
-     * it to false. In that case, the animation will stop at the frame before the marker.
-     */
-    data class MaxMarker(val maxMarker: String, val playMarkerFrame: Boolean = true) : LottieClipSpec() {
-        override fun getMinProgress(composition: LottieComposition): Float {
-            return 0f
-        }
-
-        override fun getMaxProgress(composition: LottieComposition): Float {
-            val offset = if (playMarkerFrame) 0 else -1
-            return ((composition.getMarker(maxMarker)?.startFrame?.plus(offset) ?: 0f) / composition.endFrame).coerceIn(0f, 1f)
+            return max
         }
     }
 
     /**
      * Play the animation from minMarker until maxMarker. If maxMarker represents the end of your animation,
-     * set [playMaxMarkerStartFrame] to true. If the marker represents the beginning of the next section, set
-     * it to false. In that case, the animation will stop at the frame before maxMarker.
+     * set [maxInclusive] to true. If the marker represents the beginning of the next section, set
+     * it to false to stop the animation at the frame before maxMarker.
      */
-    data class MinAndMaxMarker(val minMarker: String, val maxMarker: String, val playMaxMarkerStartFrame: Boolean = true) : LottieClipSpec() {
+    data class Markers(
+        val min: String? = null,
+        val max: String? = null,
+        val maxInclusive: Boolean = true
+    ) : LottieClipSpec() {
         override fun getMinProgress(composition: LottieComposition): Float {
-            return ((composition.getMarker(minMarker)?.startFrame ?: 0f) / composition.endFrame).coerceIn(0f, 1f)
+            return when (min) {
+                null -> 0f
+                else -> ((composition.getMarker(min)?.startFrame ?: 0f) / composition.endFrame).coerceIn(0f, 1f)
+            }
         }
 
         override fun getMaxProgress(composition: LottieComposition): Float {
-            val offset = if (playMaxMarkerStartFrame) 0 else -1
-            return ((composition.getMarker(maxMarker)?.startFrame?.plus(offset) ?: 0f) / composition.endFrame).coerceIn(0f, 1f)
+            return when (max) {
+                null -> 1f
+                else -> {
+                    val offset = if (maxInclusive) 0 else -1
+                    return ((composition.getMarker(max)?.startFrame?.plus(offset) ?: 0f) / composition.endFrame).coerceIn(0f, 1f)
+                }
+            }
         }
     }
 
diff --git a/lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieAnimatableImplTest.kt b/lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieAnimatableImplTest.kt
index 50367ca..e207733 100644
--- a/lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieAnimatableImplTest.kt
+++ b/lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieAnimatableImplTest.kt
@@ -11,7 +11,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.robolectric.RobolectricTestRunner
-import java.lang.IllegalArgumentException
 
 @RunWith(RobolectricTestRunner::class)
 class LottieAnimatableImplTest {
@@ -111,7 +110,7 @@
 
     @Test
     fun testClipSpec() = runTest {
-        val clipSpec = LottieClipSpec.MinAndMaxProgress(0.25f, 0.75f)
+        val clipSpec = LottieClipSpec.Progress(0.25f, 0.75f)
         launch {
             anim.animate(composition, clipSpec = clipSpec)
         }
@@ -122,7 +121,7 @@
 
     @Test
     fun testClipSpecWithTwoIterations() = runTest {
-        val clipSpec = LottieClipSpec.MinAndMaxProgress(0.25f, 0.75f)
+        val clipSpec = LottieClipSpec.Progress(0.25f, 0.75f)
         launch {
             anim.animate(composition, clipSpec = clipSpec, iterations = 2)
         }
@@ -134,7 +133,7 @@
 
     @Test
     fun testNegativeSpeedWithClipSpec() = runTest {
-        val clipSpec = LottieClipSpec.MinAndMaxProgress(0.25f, 0.75f)
+        val clipSpec = LottieClipSpec.Progress(0.25f, 0.75f)
         launch {
             anim.animate(composition, clipSpec = clipSpec, speed = -1f)
         }
@@ -150,7 +149,7 @@
         }
         assertFrame(0, progress = 0f)
         assertFrame(300, progress = 0.5f)
-        val clipSpec = LottieClipSpec.MaxProgress(0.75f)
+        val clipSpec = LottieClipSpec.Progress(max = 0.75f)
         launch {
             anim.animate(composition, clipSpec = clipSpec, continueFromPreviousAnimate = false)
         }
@@ -166,7 +165,7 @@
         }
         assertFrame(0, progress = 0f, iterations = 2)
         assertFrame(300, progress = 0.5f, iterations = 2)
-        val clipSpec = LottieClipSpec.MinProgress(0.25f)
+        val clipSpec = LottieClipSpec.Progress(min = 0.25f)
         launch {
             anim.animate(composition, clipSpec = clipSpec, initialProgress = anim.progress, continueFromPreviousAnimate = true)
         }
@@ -183,7 +182,7 @@
         }
         assertFrame(0, progress = 0f)
         assertFrame(300, progress = 0.5f)
-        val clipSpec = LottieClipSpec.MaxProgress(0.75f)
+        val clipSpec = LottieClipSpec.Progress(max = 0.75f)
         launch {
             anim.animate(
                 composition,
diff --git a/lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieClipSpecTest.kt b/lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieClipSpecTest.kt
index 53f064d..8457f54 100644
--- a/lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieClipSpecTest.kt
+++ b/lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieClipSpecTest.kt
@@ -12,7 +12,7 @@
 
     @Test
     fun testMinFrame() {
-        val spec = LottieClipSpec.MinFrame(20)
+        val spec = LottieClipSpec.Frame(min = 20)
         val composition = createComposition(endFrame = 40f)
         assertEquals(0.5f, spec.getMinProgress(composition))
         assertEquals(1f, spec.getMaxProgress(composition))
@@ -20,7 +20,7 @@
 
     @Test
     fun testMaxFrame() {
-        val spec = LottieClipSpec.MaxFrame(20)
+        val spec = LottieClipSpec.Frame(max = 20)
         val composition = createComposition(endFrame = 40f)
         assertEquals(0f, spec.getMinProgress(composition))
         assertEquals(0.5f, spec.getMaxProgress(composition))
@@ -28,7 +28,7 @@
 
     @Test
     fun testMaxFrameNotInclusive() {
-        val spec = LottieClipSpec.MaxFrame(20, inclusive = false)
+        val spec = LottieClipSpec.Frame(max = 20, maxInclusive = false)
         val composition = createComposition(endFrame = 40f)
         assertEquals(0f, spec.getMinProgress(composition))
         assertEquals(0.475f, spec.getMaxProgress(composition))
@@ -36,7 +36,7 @@
 
     @Test
     fun testMinAndMaxFrame() {
-        val spec = LottieClipSpec.MinAndMaxFrame(20, 30)
+        val spec = LottieClipSpec.Frame(min = 20, max = 30)
         val composition = createComposition(endFrame = 40f)
         assertEquals(0.5f, spec.getMinProgress(composition))
         assertEquals(0.75f, spec.getMaxProgress(composition))
@@ -44,7 +44,7 @@
 
     @Test
     fun testMinAndMaxFrameNotExclusive() {
-        val spec = LottieClipSpec.MinAndMaxFrame(20, 30, maxFrameInclusive = false)
+        val spec = LottieClipSpec.Frame(min = 20, max = 30, maxInclusive = false)
         val composition = createComposition(endFrame = 40f)
         assertEquals(0.5f, spec.getMinProgress(composition))
         assertEquals(0.725f, spec.getMaxProgress(composition))
@@ -52,7 +52,7 @@
 
     @Test
     fun testMinProgress() {
-        val spec = LottieClipSpec.MinProgress(0.5f)
+        val spec = LottieClipSpec.Progress(min = 0.5f)
         val composition = createComposition(endFrame = 40f)
         assertEquals(0.5f, spec.getMinProgress(composition))
         assertEquals(1f, spec.getMaxProgress(composition))
@@ -60,7 +60,7 @@
 
     @Test
     fun testMaxProgress() {
-        val spec = LottieClipSpec.MaxProgress(0.5f)
+        val spec = LottieClipSpec.Progress(max = 0.5f)
         val composition = createComposition(endFrame = 40f)
         assertEquals(0f, spec.getMinProgress(composition))
         assertEquals(0.5f, spec.getMaxProgress(composition))
@@ -68,7 +68,7 @@
 
     @Test
     fun testMinAndMaxProgress() {
-        val spec = LottieClipSpec.MinAndMaxProgress(0.5f, 0.75f)
+        val spec = LottieClipSpec.Progress(min = 0.5f, max = 0.75f)
         val composition = createComposition(endFrame = 40f)
         assertEquals(0.5f, spec.getMinProgress(composition))
         assertEquals(0.75f, spec.getMaxProgress(composition))
@@ -76,7 +76,7 @@
 
     @Test
     fun testMinMarker() {
-        val spec = LottieClipSpec.MinMarker("start")
+        val spec = LottieClipSpec.Markers(min = "start")
         val composition = createComposition(endFrame = 40f, listOf(Marker("start", 20f, 10f)))
         assertEquals(0.5f, spec.getMinProgress(composition))
         assertEquals(1f, spec.getMaxProgress(composition))
@@ -84,7 +84,7 @@
 
     @Test
     fun testMaxMarker() {
-        val spec = LottieClipSpec.MaxMarker("end")
+        val spec = LottieClipSpec.Markers(max = "end")
         val composition = createComposition(endFrame = 40f, listOf(Marker("end", 20f, 10f)))
         assertEquals(0f, spec.getMinProgress(composition))
         assertEquals(0.5f, spec.getMaxProgress(composition))
@@ -93,7 +93,7 @@
 
     @Test
     fun testMaxMarkerExclusive() {
-        val spec = LottieClipSpec.MaxMarker("end", playMarkerFrame = false)
+        val spec = LottieClipSpec.Markers(max = "end", maxInclusive = false)
         val composition = createComposition(endFrame = 40f, listOf(Marker("end", 20f, 10f)))
         assertEquals(0f, spec.getMinProgress(composition))
         assertEquals(0.475f, spec.getMaxProgress(composition))
@@ -102,7 +102,7 @@
 
     @Test
     fun testMinAndMaxMarker() {
-        val spec = LottieClipSpec.MinAndMaxMarker("start", "end")
+        val spec = LottieClipSpec.Markers(min = "start", max = "end")
         val composition = createComposition(endFrame = 40f, listOf(Marker("start", 20f, 10f), Marker("end", 30f, 10f)))
         assertEquals(0.5f, spec.getMinProgress(composition))
         assertEquals(0.75f, spec.getMaxProgress(composition))
@@ -110,7 +110,7 @@
 
     @Test
     fun testMinAndMaxMarkerExclusive() {
-        val spec = LottieClipSpec.MinAndMaxMarker("start", "end", playMaxMarkerStartFrame = false)
+        val spec = LottieClipSpec.Markers(min = "start", max = "end", maxInclusive = false)
         val composition = createComposition(endFrame = 40f, listOf(Marker("start", 20f, 10f), Marker("end", 30f, 10f)))
         assertEquals(0.5f, spec.getMinProgress(composition))
         assertEquals(0.725f, spec.getMaxProgress(composition))
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/BasicUsageExamplesPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/BasicUsageExamplesPage.kt
index de75ff6..d3f3a1c 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/BasicUsageExamplesPage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/BasicUsageExamplesPage.kt
@@ -89,7 +89,7 @@
     LottieAnimation(
         composition,
         iterations = LottieConstants.IterateForever,
-        clipSpec = LottieClipSpec.MinAndMaxProgress(0.5f, 0.75f),
+        clipSpec = LottieClipSpec.Progress(0.5f, 0.75f),
     )
 }
 
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/TransitionsExamplesPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/TransitionsExamplesPage.kt
index 8414048..978cd7c 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/TransitionsExamplesPage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/TransitionsExamplesPage.kt
@@ -1,8 +1,6 @@
 package com.airbnb.lottie.sample.compose.examples
 
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material.Text
 import androidx.compose.material.TextButton
@@ -81,9 +79,9 @@
         composition ?: return@LaunchedEffect
         snapshotFlow { state }.collectLatest { s ->
             val clipSpec = when (s) {
-                TransitionSection.Intro -> LottieClipSpec.MinAndMaxProgress(0f, 0.301f)
-                TransitionSection.LoopMiddle -> LottieClipSpec.MinAndMaxProgress(0.301f, 2f / 3f)
-                TransitionSection.Outro -> LottieClipSpec.MinAndMaxProgress(2f / 3f, 1f)
+                TransitionSection.Intro -> LottieClipSpec.Progress(0f, 0.301f)
+                TransitionSection.LoopMiddle -> LottieClipSpec.Progress(0.301f, 2f / 3f)
+                TransitionSection.Outro -> LottieClipSpec.Progress(2f / 3f, 1f)
             }
             do {
                 animatable.animate(