Permalink
Browse files

Add support for extrapolation

Summary:
Adds support for the `extrapolate` parameter on the native interpolation node. This is pretty much a 1 to 1 port of the JS implementation.

**Test plan**
Tested by adding the `extrapolate` parameter in the native animated UIExplorer example.
Closes #9366

Differential Revision: D3824154

fbshipit-source-id: 2ef593af827a8bd3d7b8ab2d53abbdc9516c6022
  • Loading branch information...
1 parent fab0ff3 commit 6d978c3c8b1e3929e59439db0c0a34dd094253cf @janicduplessis janicduplessis committed with Facebook Github Bot 5 Sep 6, 2016
@@ -1071,11 +1071,16 @@ class AnimatedInterpolation extends AnimatedWithChildren {
}
__getNativeConfig(): any {
- NativeAnimatedHelper.validateInterpolation(this._config);
+ if (__DEV__) {
+ NativeAnimatedHelper.validateInterpolation(this._config);
+ }
+
return {
- ...this._config,
+ inputRange: this._config.inputRange,
// Only the `outputRange` can contain strings so we don't need to tranform `inputRange` here
outputRange: this.__transformDataType(this._config.outputRange),
+ extrapolateLeft: this._config.extrapolateLeft || this._config.extrapolate || 'extend',
+ extrapolateRight: this._config.extrapolateRight || this._config.extrapolate || 'extend',
type: 'interpolation',
};
}
@@ -135,6 +135,9 @@ function validateInterpolation(config: Object): void {
var SUPPORTED_INTERPOLATION_PARAMS = {
inputRange: true,
outputRange: true,
+ extrapolate: true,
+ extrapolateRight: true,
+ extrapolateLeft: true,
};
for (var key in config) {
if (!SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(key)) {
@@ -1,5 +1,6 @@
package com.facebook.react.animated;
+import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
@@ -12,6 +13,10 @@
*/
/*package*/ class InterpolationAnimatedNode extends ValueAnimatedNode {
+ public static final String EXTRAPOLATE_TYPE_IDENTITY = "identity";
+ public static final String EXTRAPOLATE_TYPE_CLAMP = "clamp";
+ public static final String EXTRAPOLATE_TYPE_EXTEND = "extend";
+
private static double[] fromDoubleArray(ReadableArray ary) {
double[] res = new double[ary.size()];
for (int i = 0; i < res.length; i++) {
@@ -25,19 +30,62 @@ private static double interpolate(
double inputMin,
double inputMax,
double outputMin,
- double outputMax) {
+ double outputMax,
+ String extrapolateLeft,
+ String extrapolateRight) {
+ double result = value;
+
+ // Extrapolate
+ if (result < inputMin) {
+ switch (extrapolateLeft) {
+ case EXTRAPOLATE_TYPE_IDENTITY:
+ return result;
+ case EXTRAPOLATE_TYPE_CLAMP:
+ result = inputMin;
+ break;
+ case EXTRAPOLATE_TYPE_EXTEND:
+ break;
+ default:
+ throw new JSApplicationIllegalArgumentException(
+ "Invalid extrapolation type " + extrapolateLeft + "for left extrapolation");
+ }
+ }
+
+ if (result > inputMax) {
+ switch (extrapolateRight) {
+ case EXTRAPOLATE_TYPE_IDENTITY:
+ return result;
+ case EXTRAPOLATE_TYPE_CLAMP:
+ result = inputMax;
+ break;
+ case EXTRAPOLATE_TYPE_EXTEND:
+ break;
+ default:
+ throw new JSApplicationIllegalArgumentException(
+ "Invalid extrapolation type " + extrapolateRight + "for right extrapolation");
+ }
+ }
+
return outputMin + (outputMax - outputMin) *
- (value - inputMin) / (inputMax - inputMin);
+ (result - inputMin) / (inputMax - inputMin);
}
- /*package*/ static double interpolate(double value, double[] inputRange, double[] outputRange) {
+ /*package*/ static double interpolate(
+ double value,
+ double[] inputRange,
+ double[] outputRange,
+ String extrapolateLeft,
+ String extrapolateRight
+ ) {
int rangeIndex = findRangeIndex(value, inputRange);
return interpolate(
value,
inputRange[rangeIndex],
inputRange[rangeIndex + 1],
outputRange[rangeIndex],
- outputRange[rangeIndex + 1]);
+ outputRange[rangeIndex + 1],
+ extrapolateLeft,
+ extrapolateRight);
}
private static int findRangeIndex(double value, double[] ranges) {
@@ -52,11 +100,15 @@ private static int findRangeIndex(double value, double[] ranges) {
private final double mInputRange[];
private final double mOutputRange[];
+ private final String mExtrapolateLeft;
+ private final String mExtrapolateRight;
private @Nullable ValueAnimatedNode mParent;
public InterpolationAnimatedNode(ReadableMap config) {
mInputRange = fromDoubleArray(config.getArray("inputRange"));
mOutputRange = fromDoubleArray(config.getArray("outputRange"));
+ mExtrapolateLeft = config.getString("extrapolateLeft");
+ mExtrapolateRight = config.getString("extrapolateRight");
}
@Override
@@ -84,6 +136,6 @@ public void update() {
throw new IllegalStateException("Trying to update interpolation node that has not been " +
"attached to the parent");
}
- mValue = interpolate(mParent.mValue, mInputRange, mOutputRange);
+ mValue = interpolate(mParent.mValue, mInputRange, mOutputRange, mExtrapolateLeft, mExtrapolateRight);
}
}
@@ -12,53 +12,102 @@
@RunWith(RobolectricTestRunner.class)
public class NativeAnimatedInterpolationTest {
+ private double simpleInterpolation(double value, double[] input, double[] output) {
+ return InterpolationAnimatedNode.interpolate(
+ value,
+ input,
+ output,
+ InterpolationAnimatedNode.EXTRAPOLATE_TYPE_EXTEND,
+ InterpolationAnimatedNode.EXTRAPOLATE_TYPE_EXTEND
+ );
+ }
+
@Test
public void testSimpleOneToOneMapping() {
double[] input = new double[] {0d, 1d};
double[] output = new double[] {0d, 1d};
- assertThat(InterpolationAnimatedNode.interpolate(0, input, output)).isEqualTo(0);
- assertThat(InterpolationAnimatedNode.interpolate(0.5, input, output)).isEqualTo(0.5);
- assertThat(InterpolationAnimatedNode.interpolate(0.8, input, output)).isEqualTo(0.8);
- assertThat(InterpolationAnimatedNode.interpolate(1, input, output)).isEqualTo(1);
+ assertThat(simpleInterpolation(0, input, output)).isEqualTo(0);
+ assertThat(simpleInterpolation(0.5, input, output)).isEqualTo(0.5);
+ assertThat(simpleInterpolation(0.8, input, output)).isEqualTo(0.8);
+ assertThat(simpleInterpolation(1, input, output)).isEqualTo(1);
}
@Test
public void testWiderOutputRange() {
double[] input = new double[] {0d, 1d};
double[] output = new double[] {100d, 200d};
- assertThat(InterpolationAnimatedNode.interpolate(0, input, output)).isEqualTo(100);
- assertThat(InterpolationAnimatedNode.interpolate(0.5, input, output)).isEqualTo(150);
- assertThat(InterpolationAnimatedNode.interpolate(0.8, input, output)).isEqualTo(180);
- assertThat(InterpolationAnimatedNode.interpolate(1, input, output)).isEqualTo(200);
+ assertThat(simpleInterpolation(0, input, output)).isEqualTo(100);
+ assertThat(simpleInterpolation(0.5, input, output)).isEqualTo(150);
+ assertThat(simpleInterpolation(0.8, input, output)).isEqualTo(180);
+ assertThat(simpleInterpolation(1, input, output)).isEqualTo(200);
}
@Test
public void testWiderInputRange() {
double[] input = new double[] {2000d, 3000d};
double[] output = new double[] {1d, 2d};
- assertThat(InterpolationAnimatedNode.interpolate(2000, input, output)).isEqualTo(1);
- assertThat(InterpolationAnimatedNode.interpolate(2250, input, output)).isEqualTo(1.25);
- assertThat(InterpolationAnimatedNode.interpolate(2800, input, output)).isEqualTo(1.8);
- assertThat(InterpolationAnimatedNode.interpolate(3000, input, output)).isEqualTo(2);
+ assertThat(simpleInterpolation(2000, input, output)).isEqualTo(1);
+ assertThat(simpleInterpolation(2250, input, output)).isEqualTo(1.25);
+ assertThat(simpleInterpolation(2800, input, output)).isEqualTo(1.8);
+ assertThat(simpleInterpolation(3000, input, output)).isEqualTo(2);
}
@Test
public void testManySegments() {
double[] input = new double[] {-1d, 1d, 5d};
double[] output = new double[] {0, 10d, 20d};
- assertThat(InterpolationAnimatedNode.interpolate(-1, input, output)).isEqualTo(0);
- assertThat(InterpolationAnimatedNode.interpolate(0, input, output)).isEqualTo(5);
- assertThat(InterpolationAnimatedNode.interpolate(1, input, output)).isEqualTo(10);
- assertThat(InterpolationAnimatedNode.interpolate(2, input, output)).isEqualTo(12.5);
- assertThat(InterpolationAnimatedNode.interpolate(5, input, output)).isEqualTo(20);
+ assertThat(simpleInterpolation(-1, input, output)).isEqualTo(0);
+ assertThat(simpleInterpolation(0, input, output)).isEqualTo(5);
+ assertThat(simpleInterpolation(1, input, output)).isEqualTo(10);
+ assertThat(simpleInterpolation(2, input, output)).isEqualTo(12.5);
+ assertThat(simpleInterpolation(5, input, output)).isEqualTo(20);
}
@Test
- public void testExtrapolate() {
+ public void testExtendExtrapolate() {
double[] input = new double[] {10d, 20d};
double[] output = new double[] {0d, 1d};
- assertThat(InterpolationAnimatedNode.interpolate(30d, input, output)).isEqualTo(2);
- assertThat(InterpolationAnimatedNode.interpolate(5d, input, output)).isEqualTo(-0.5);
+ assertThat(simpleInterpolation(30d, input, output)).isEqualTo(2);
+ assertThat(simpleInterpolation(5d, input, output)).isEqualTo(-0.5);
}
+ @Test
+ public void testClampExtrapolate() {
+ double[] input = new double[] {10d, 20d};
+ double[] output = new double[] {0d, 1d};
+ assertThat(InterpolationAnimatedNode.interpolate(
+ 30d,
+ input,
+ output,
+ InterpolationAnimatedNode.EXTRAPOLATE_TYPE_CLAMP,
+ InterpolationAnimatedNode.EXTRAPOLATE_TYPE_CLAMP
+ )).isEqualTo(1);
+ assertThat(InterpolationAnimatedNode.interpolate(
+ 5d,
+ input,
+ output,
+ InterpolationAnimatedNode.EXTRAPOLATE_TYPE_CLAMP,
+ InterpolationAnimatedNode.EXTRAPOLATE_TYPE_CLAMP
+ )).isEqualTo(0);
+ }
+
+ @Test
+ public void testIdentityExtrapolate() {
+ double[] input = new double[] {10d, 20d};
+ double[] output = new double[] {0d, 1d};
+ assertThat(InterpolationAnimatedNode.interpolate(
+ 30d,
+ input,
+ output,
+ InterpolationAnimatedNode.EXTRAPOLATE_TYPE_IDENTITY,
+ InterpolationAnimatedNode.EXTRAPOLATE_TYPE_IDENTITY
+ )).isEqualTo(30);
+ assertThat(InterpolationAnimatedNode.interpolate(
+ 5d,
+ input,
+ output,
+ InterpolationAnimatedNode.EXTRAPOLATE_TYPE_IDENTITY,
+ InterpolationAnimatedNode.EXTRAPOLATE_TYPE_IDENTITY
+ )).isEqualTo(5);
+ }
}
@@ -652,7 +652,11 @@ public void testInterpolationNode() {
"inputRange",
JavaOnlyArray.of(10d, 20d),
"outputRange",
- JavaOnlyArray.of(0d, 1d)));
+ JavaOnlyArray.of(0d, 1d),
+ "extrapolateLeft",
+ "extend",
+ "extrapolateRight",
+ "extend"));
mNativeAnimatedNodesManager.createAnimatedNode(
3,

0 comments on commit 6d978c3

Please sign in to comment.