1 /* 2 * Copyright (C) 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.constraintlayout.core.motion.utils; 18 19 import java.util.Arrays; 20 21 /** 22 * Provide the engine for cubic spline easing 23 * 24 * 25 */ 26 public class Easing { 27 static Easing sDefault = new Easing(); 28 String mStr = "identity"; 29 private static final String STANDARD = "cubic(0.4, 0.0, 0.2, 1)"; 30 private static final String ACCELERATE = "cubic(0.4, 0.05, 0.8, 0.7)"; 31 private static final String DECELERATE = "cubic(0.0, 0.0, 0.2, 0.95)"; 32 private static final String LINEAR = "cubic(1, 1, 0, 0)"; 33 private static final String ANTICIPATE = "cubic(0.36, 0, 0.66, -0.56)"; 34 private static final String OVERSHOOT = "cubic(0.34, 1.56, 0.64, 1)"; 35 36 private static final String DECELERATE_NAME = "decelerate"; 37 private static final String ACCELERATE_NAME = "accelerate"; 38 private static final String STANDARD_NAME = "standard"; 39 private static final String LINEAR_NAME = "linear"; 40 private static final String ANTICIPATE_NAME = "anticipate"; 41 private static final String OVERSHOOT_NAME = "overshoot"; 42 43 public static String[] NAMED_EASING = 44 {STANDARD_NAME, ACCELERATE_NAME, DECELERATE_NAME, LINEAR_NAME}; 45 46 // @TODO: add description getInterpolator(String configString)47 public static Easing getInterpolator(String configString) { 48 if (configString == null) { 49 return null; 50 } 51 if (configString.startsWith("cubic")) { 52 return new CubicEasing(configString); 53 } else if (configString.startsWith("spline")) { 54 return new StepCurve(configString); 55 } else if (configString.startsWith("Schlick")) { 56 return new Schlick(configString); 57 } else { 58 switch (configString) { 59 case STANDARD_NAME: 60 return new CubicEasing(STANDARD); 61 case ACCELERATE_NAME: 62 return new CubicEasing(ACCELERATE); 63 case DECELERATE_NAME: 64 return new CubicEasing(DECELERATE); 65 case LINEAR_NAME: 66 return new CubicEasing(LINEAR); 67 case ANTICIPATE_NAME: 68 return new CubicEasing(ANTICIPATE); 69 case OVERSHOOT_NAME: 70 return new CubicEasing(OVERSHOOT); 71 default: 72 System.err.println("transitionEasing syntax error syntax:" 73 + "transitionEasing=\"cubic(1.0,0.5,0.0,0.6)\" or " 74 + Arrays.toString(NAMED_EASING)); 75 } 76 77 } 78 return sDefault; 79 } 80 81 // @TODO: add description get(double x)82 public double get(double x) { 83 return x; 84 } 85 86 // @TODO: add description 87 @Override toString()88 public String toString() { 89 return mStr; 90 } 91 92 // @TODO: add description getDiff(double x)93 public double getDiff(double x) { 94 return 1; 95 } 96 97 static class CubicEasing extends Easing { 98 99 private static double sError = 0.01; 100 private static double sDError = 0.0001; 101 double mX1, mY1, mX2, mY2; 102 CubicEasing(String configString)103 CubicEasing(String configString) { 104 // done this way for efficiency 105 mStr = configString; 106 int start = configString.indexOf('('); 107 int off1 = configString.indexOf(',', start); 108 mX1 = Double.parseDouble(configString.substring(start + 1, off1).trim()); 109 int off2 = configString.indexOf(',', off1 + 1); 110 mY1 = Double.parseDouble(configString.substring(off1 + 1, off2).trim()); 111 int off3 = configString.indexOf(',', off2 + 1); 112 mX2 = Double.parseDouble(configString.substring(off2 + 1, off3).trim()); 113 int end = configString.indexOf(')', off3 + 1); 114 mY2 = Double.parseDouble(configString.substring(off3 + 1, end).trim()); 115 } 116 CubicEasing(double x1, double y1, double x2, double y2)117 CubicEasing(double x1, double y1, double x2, double y2) { 118 setup(x1, y1, x2, y2); 119 } 120 setup(double x1, double y1, double x2, double y2)121 void setup(double x1, double y1, double x2, double y2) { 122 this.mX1 = x1; 123 this.mY1 = y1; 124 this.mX2 = x2; 125 this.mY2 = y2; 126 } 127 getX(double t)128 private double getX(double t) { 129 double t1 = 1 - t; 130 // no need for because start at 0,0 double f0 = (1 - t) * (1 - t) * (1 - t); 131 double f1 = 3 * t1 * t1 * t; 132 double f2 = 3 * t1 * t * t; 133 double f3 = t * t * t; 134 return mX1 * f1 + mX2 * f2 + f3; 135 } 136 getY(double t)137 private double getY(double t) { 138 double t1 = 1 - t; 139 // no need for because start at 0,0 double f0 = (1 - t) * (1 - t) * (1 - t); 140 double f1 = 3 * t1 * t1 * t; 141 double f2 = 3 * t1 * t * t; 142 double f3 = t * t * t; 143 return mY1 * f1 + mY2 * f2 + f3; 144 } 145 146 @SuppressWarnings("unused") getDiffX(double t)147 private double getDiffX(double t) { 148 double t1 = 1 - t; 149 return 3 * t1 * t1 * mX1 + 6 * t1 * t * (mX2 - mX1) + 3 * t * t * (1 - mX2); 150 } 151 152 @SuppressWarnings("unused") getDiffY(double t)153 private double getDiffY(double t) { 154 double t1 = 1 - t; 155 return 3 * t1 * t1 * mY1 + 6 * t1 * t * (mY2 - mY1) + 3 * t * t * (1 - mY2); 156 } 157 158 /** 159 * binary search for the region 160 * and linear interpolate the answer 161 */ 162 @Override getDiff(double x)163 public double getDiff(double x) { 164 double t = 0.5; 165 double range = 0.5; 166 while (range > sDError) { 167 double tx = getX(t); 168 range *= 0.5; 169 if (tx < x) { 170 t += range; 171 } else { 172 t -= range; 173 } 174 } 175 176 double x1 = getX(t - range); 177 double x2 = getX(t + range); 178 double y1 = getY(t - range); 179 double y2 = getY(t + range); 180 181 return (y2 - y1) / (x2 - x1); 182 } 183 184 /** 185 * binary search for the region 186 * and linear interpolate the answer 187 */ 188 @Override get(double x)189 public double get(double x) { 190 if (x <= 0.0) { 191 return 0; 192 } 193 if (x >= 1.0) { 194 return 1.0; 195 } 196 double t = 0.5; 197 double range = 0.5; 198 while (range > sError) { 199 double tx = getX(t); 200 range *= 0.5; 201 if (tx < x) { 202 t += range; 203 } else { 204 t -= range; 205 } 206 } 207 208 double x1 = getX(t - range); 209 double x2 = getX(t + range); 210 double y1 = getY(t - range); 211 double y2 = getY(t + range); 212 213 return (y2 - y1) * (x - x1) / (x2 - x1) + y1; 214 } 215 } 216 } 217