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