1 package com.airbnb.lottie.model.content; 2 3 import android.graphics.PointF; 4 import androidx.annotation.FloatRange; 5 6 import com.airbnb.lottie.model.CubicCurveData; 7 import com.airbnb.lottie.utils.Logger; 8 import com.airbnb.lottie.utils.MiscUtils; 9 10 import java.util.ArrayList; 11 import java.util.List; 12 13 public class ShapeData { 14 private final List<CubicCurveData> curves; 15 private PointF initialPoint; 16 private boolean closed; 17 ShapeData(PointF initialPoint, boolean closed, List<CubicCurveData> curves)18 public ShapeData(PointF initialPoint, boolean closed, List<CubicCurveData> curves) { 19 this.initialPoint = initialPoint; 20 this.closed = closed; 21 this.curves = new ArrayList<>(curves); 22 } 23 ShapeData()24 public ShapeData() { 25 curves = new ArrayList<>(); 26 } 27 setInitialPoint(float x, float y)28 private void setInitialPoint(float x, float y) { 29 if (initialPoint == null) { 30 initialPoint = new PointF(); 31 } 32 initialPoint.set(x, y); 33 } 34 getInitialPoint()35 public PointF getInitialPoint() { 36 return initialPoint; 37 } 38 isClosed()39 public boolean isClosed() { 40 return closed; 41 } 42 getCurves()43 public List<CubicCurveData> getCurves() { 44 return curves; 45 } 46 interpolateBetween(ShapeData shapeData1, ShapeData shapeData2, @FloatRange(from = 0f, to = 1f) float percentage)47 public void interpolateBetween(ShapeData shapeData1, ShapeData shapeData2, 48 @FloatRange(from = 0f, to = 1f) float percentage) { 49 if (initialPoint == null) { 50 initialPoint = new PointF(); 51 } 52 closed = shapeData1.isClosed() || shapeData2.isClosed(); 53 54 55 if (shapeData1.getCurves().size() != shapeData2.getCurves().size()) { 56 Logger.warning("Curves must have the same number of control points. Shape 1: " + 57 shapeData1.getCurves().size() + "\tShape 2: " + shapeData2.getCurves().size()); 58 } 59 60 int points = Math.min(shapeData1.getCurves().size(), shapeData2.getCurves().size()); 61 if (curves.size() < points) { 62 for (int i = curves.size(); i < points; i++) { 63 curves.add(new CubicCurveData()); 64 } 65 } else if (curves.size() > points) { 66 for (int i = curves.size() - 1; i >= points; i--) { 67 curves.remove(curves.size() - 1); 68 } 69 } 70 71 PointF initialPoint1 = shapeData1.getInitialPoint(); 72 PointF initialPoint2 = shapeData2.getInitialPoint(); 73 74 setInitialPoint(MiscUtils.lerp(initialPoint1.x, initialPoint2.x, percentage), 75 MiscUtils.lerp(initialPoint1.y, initialPoint2.y, percentage)); 76 77 for (int i = curves.size() - 1; i >= 0; i--) { 78 CubicCurveData curve1 = shapeData1.getCurves().get(i); 79 CubicCurveData curve2 = shapeData2.getCurves().get(i); 80 81 PointF cp11 = curve1.getControlPoint1(); 82 PointF cp21 = curve1.getControlPoint2(); 83 PointF vertex1 = curve1.getVertex(); 84 85 PointF cp12 = curve2.getControlPoint1(); 86 PointF cp22 = curve2.getControlPoint2(); 87 PointF vertex2 = curve2.getVertex(); 88 89 curves.get(i).setControlPoint1( 90 MiscUtils.lerp(cp11.x, cp12.x, percentage), MiscUtils.lerp(cp11.y, cp12.y, 91 percentage)); 92 curves.get(i).setControlPoint2( 93 MiscUtils.lerp(cp21.x, cp22.x, percentage), MiscUtils.lerp(cp21.y, cp22.y, 94 percentage)); 95 curves.get(i).setVertex( 96 MiscUtils.lerp(vertex1.x, vertex2.x, percentage), MiscUtils.lerp(vertex1.y, vertex2.y, 97 percentage)); 98 } 99 } 100 toString()101 @Override public String toString() { 102 return "ShapeData{" + "numCurves=" + curves.size() + 103 "closed=" + closed + 104 '}'; 105 } 106 } 107