/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.animation.core
import androidx.compose.ui.util.identityHashCode
/**
* [AnimationVector] class that is the base class of [AnimationVector1D], [AnimationVector2D],
* [AnimationVector3D] and [AnimationVector4D]. In order to animate any arbitrary type, it is
* required to provide a [TwoWayConverter] that defines how to convert that arbitrary type T to an
* [AnimationVector], and vice versa. Depending on how many dimensions this type T has, it may need
* to be converted to any of the subclasses of [AnimationVector]. For example, a position based
* object should be converted to [AnimationVector2D], whereas an object that describes rectangle
* bounds should convert to [AnimationVector4D].
*/
sealed class AnimationVector {
internal abstract fun reset()
internal abstract fun newVector(): AnimationVector
internal abstract operator fun get(index: Int): Float
internal abstract operator fun set(index: Int, value: Float)
internal abstract val size: Int
}
/**
* Factory method to create an [AnimationVector1D]
*
* @param v1 value to set on the value field of [AnimationVector1D]
*/
fun AnimationVector(v1: Float) = AnimationVector1D(v1)
/**
* Factory method to create an [AnimationVector2D]
*
* @param v1 value to set on the first dimension
* @param v2 value to set on the second dimension
*/
fun AnimationVector(v1: Float, v2: Float) = AnimationVector2D(v1, v2)
/**
* Factory method to create an [AnimationVector3D]
*
* @param v1 value to set on the first dimension
* @param v2 value to set on the second dimension
* @param v3 value to set on the third dimension
*/
fun AnimationVector(v1: Float, v2: Float, v3: Float) = AnimationVector3D(v1, v2, v3)
/**
* Factory method to create an [AnimationVector4D]
*
* @param v1 value to set on the first dimension
* @param v2 value to set on the second dimension
* @param v3 value to set on the third dimension
* @param v4 value to set on the fourth dimension
*/
fun AnimationVector(
v1: Float,
v2: Float,
v3: Float,
v4: Float
) = AnimationVector4D(v1, v2, v3, v4)
internal fun <T : AnimationVector> T.newInstance(): T {
@Suppress("UNCHECKED_CAST")
return this.newVector() as T
}
/**
* This class defines a 1D vector. It contains only one Float value that is initialized in the
* constructor.
*
* @param initVal initial value to set the [value] field to.
*/
class AnimationVector1D(initVal: Float) : AnimationVector() {
/**
* This field holds the only Float value in this [AnimationVector1D] object.
*/
var value: Float = initVal
internal set
// internal
override fun reset() {
value = 0f
}
override fun newVector(): AnimationVector1D = AnimationVector1D(0f)
override fun get(index: Int): Float {
if (index == 0) {
return value
} else {
return 0f
}
}
override fun set(index: Int, value: Float) {
if (index == 0) {
this.value = value
}
}
override val size: Int = 1
override fun toString(): String {
return "AnimationVector1D: value = $value"
}
override fun equals(other: Any?): Boolean =
other is AnimationVector1D && other.value == value
override fun hashCode(): Int = identityHashCode()
}
/**
* This class defines a 2D vector that contains two Float values for the two dimensions.
*
* @param v1 initial value to set on the first dimension
* @param v2 initial value to set on the second dimension
*/
class AnimationVector2D(v1: Float, v2: Float) : AnimationVector() {
/**
* Float value field for the first dimension of the 2D vector.
*/
var v1: Float = v1
internal set
/**
* Float value field for the second dimension of the 2D vector.
*/
var v2: Float = v2
internal set
// internal
override fun reset() {
v1 = 0f
v2 = 0f
}
override fun newVector(): AnimationVector2D = AnimationVector2D(0f, 0f)
override fun get(index: Int): Float {
return when (index) {
0 -> v1
1 -> v2
else -> 0f
}
}
override fun set(index: Int, value: Float) {
when (index) {
0 -> v1 = value
1 -> v2 = value
}
}
override val size: Int = 2
override fun toString(): String {
return "AnimationVector2D: v1 = $v1, v2 = $v2"
}
override fun equals(other: Any?): Boolean =
other is AnimationVector2D && other.v1 == v1 && other.v2 == v2
override fun hashCode(): Int = identityHashCode()
}
/**
* This class defines a 3D vector that contains three Float value fields for the three dimensions.
*
* @param v1 initial value to set on the first dimension
* @param v2 initial value to set on the second dimension
* @param v3 initial value to set on the third dimension
*/
class AnimationVector3D(v1: Float, v2: Float, v3: Float) : AnimationVector() {
// Internally mutable, so we don't have to create a number of small objects per anim frame
/**
* Float value field for the first dimension of the 3D vector.
*/
var v1: Float = v1
internal set
/**
* Float value field for the second dimension of the 3D vector.
*/
var v2: Float = v2
internal set
/**
* Float value field for the third dimension of the 3D vector.
*/
var v3: Float = v3
internal set
// internal
override fun reset() {
v1 = 0f
v2 = 0f
v3 = 0f
}
override fun newVector(): AnimationVector3D = AnimationVector3D(0f, 0f, 0f)
override fun get(index: Int): Float {
return when (index) {
0 -> v1
1 -> v2
2 -> v3
else -> 0f
}
}
override fun set(index: Int, value: Float) {
when (index) {
0 -> v1 = value
1 -> v2 = value
2 -> v3 = value
}
}
override val size: Int = 3
override fun toString(): String {
return "AnimationVector3D: v1 = $v1, v2 = $v2, v3 = $v3"
}
override fun equals(other: Any?): Boolean =
other is AnimationVector3D && other.v1 == v1 && other.v2 == v2 && other.v3 == v3
override fun hashCode(): Int = identityHashCode()
}
/**
* This class defines a 4D vector that contains four Float fields for its four dimensions.
*
* @param v1 initial value to set on the first dimension
* @param v2 initial value to set on the second dimension
* @param v3 initial value to set on the third dimension
* @param v4 initial value to set on the fourth dimension
*/
class AnimationVector4D(v1: Float, v2: Float, v3: Float, v4: Float) : AnimationVector() {
// Internally mutable, so we don't have to create a number of small objects per anim frame
/**
* Float value field for the first dimension of the 4D vector.
*/
var v1: Float = v1
internal set
/**
* Float value field for the second dimension of the 4D vector.
*/
var v2: Float = v2
internal set
/**
* Float value field for the third dimension of the 4D vector.
*/
var v3: Float = v3
internal set
/**
* Float value field for the fourth dimension of the 4D vector.
*/
var v4: Float = v4
internal set
override fun reset() {
v1 = 0f
v2 = 0f
v3 = 0f
v4 = 0f
}
override fun newVector(): AnimationVector4D = AnimationVector4D(0f, 0f, 0f, 0f)
override fun get(index: Int): Float {
return when (index) {
0 -> v1
1 -> v2
2 -> v3
3 -> v4
else -> 0f
}
}
override fun set(index: Int, value: Float) {
when (index) {
0 -> v1 = value
1 -> v2 = value
2 -> v3 = value
3 -> v4 = value
}
}
override val size: Int = 4
override fun toString(): String {
return "AnimationVector4D: v1 = $v1, v2 = $v2, v3 = $v3, v4 = $v4"
}
override fun equals(other: Any?): Boolean =
other is AnimationVector4D &&
other.v1 == v1 &&
other.v2 == v2 &&
other.v3 == v3 &&
other.v4 == v4
override fun hashCode(): Int = identityHashCode()
}