LOTPolygonAnimator.m 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. //
  2. // LOTPolygonAnimator.m
  3. // Lottie
  4. //
  5. // Created by brandon_withrow on 7/27/17.
  6. // Copyright © 2017 Airbnb. All rights reserved.
  7. //
  8. #import "LOTPolygonAnimator.h"
  9. #import "LOTKeyframe.h"
  10. #import "LOTPointInterpolator.h"
  11. #import "LOTNumberInterpolator.h"
  12. #import "LOTBezierPath.h"
  13. #import "CGGeometry+LOTAdditions.h"
  14. const CGFloat kPOLYGON_MAGIC_NUMBER = .25f;
  15. @implementation LOTPolygonAnimator {
  16. LOTNumberInterpolator *_outerRadiusInterpolator;
  17. LOTNumberInterpolator *_outerRoundnessInterpolator;
  18. LOTPointInterpolator *_positionInterpolator;
  19. LOTNumberInterpolator *_pointsInterpolator;
  20. LOTNumberInterpolator *_rotationInterpolator;
  21. }
  22. - (instancetype _Nonnull)initWithInputNode:(LOTAnimatorNode *_Nullable)inputNode
  23. shapePolygon:(LOTShapeStar *_Nonnull)shapeStar {
  24. self = [super initWithInputNode:inputNode keyName:shapeStar.keyname];
  25. if (self) {
  26. _outerRadiusInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:shapeStar.outerRadius.keyframes];
  27. _outerRoundnessInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:shapeStar.outerRoundness.keyframes];
  28. _pointsInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:shapeStar.numberOfPoints.keyframes];
  29. _rotationInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:shapeStar.rotation.keyframes];
  30. _positionInterpolator = [[LOTPointInterpolator alloc] initWithKeyframes:shapeStar.position.keyframes];
  31. }
  32. return self;
  33. }
  34. - (NSDictionary *)valueInterpolators {
  35. return @{@"Points" : _pointsInterpolator,
  36. @"Position" : _positionInterpolator,
  37. @"Rotation" : _rotationInterpolator,
  38. @"Outer Radius" : _outerRadiusInterpolator,
  39. @"Outer Roundness" : _outerRoundnessInterpolator};
  40. }
  41. - (BOOL)needsUpdateForFrame:(NSNumber *)frame {
  42. return ([_outerRadiusInterpolator hasUpdateForFrame:frame] ||
  43. [_outerRoundnessInterpolator hasUpdateForFrame:frame] ||
  44. [_pointsInterpolator hasUpdateForFrame:frame] ||
  45. [_rotationInterpolator hasUpdateForFrame:frame] ||
  46. [_positionInterpolator hasUpdateForFrame:frame]);
  47. }
  48. - (void)performLocalUpdate {
  49. CGFloat outerRadius = [_outerRadiusInterpolator floatValueForFrame:self.currentFrame];
  50. CGFloat outerRoundness = [_outerRoundnessInterpolator floatValueForFrame:self.currentFrame] / 100.f;
  51. CGFloat points = [_pointsInterpolator floatValueForFrame:self.currentFrame];
  52. CGFloat rotation = [_rotationInterpolator floatValueForFrame:self.currentFrame];
  53. CGPoint position = [_positionInterpolator pointValueForFrame:self.currentFrame];
  54. LOTBezierPath *path = [[LOTBezierPath alloc] init];
  55. path.cacheLengths = self.pathShouldCacheLengths;
  56. CGFloat currentAngle = LOT_DegreesToRadians(rotation - 90);
  57. CGFloat anglePerPoint = (CGFloat)((2 * M_PI) / points);
  58. CGFloat x;
  59. CGFloat y;
  60. CGFloat previousX;
  61. CGFloat previousY;
  62. x = (CGFloat) (outerRadius * cosf(currentAngle));
  63. y = (CGFloat) (outerRadius * sinf(currentAngle));
  64. [path LOT_moveToPoint:CGPointMake(x, y)];
  65. currentAngle += anglePerPoint;
  66. double numPoints = ceil(points);
  67. for (int i = 0; i < numPoints; i++) {
  68. previousX = x;
  69. previousY = y;
  70. x = (CGFloat) (outerRadius * cosf(currentAngle));
  71. y = (CGFloat) (outerRadius * sinf(currentAngle));
  72. if (outerRoundness != 0) {
  73. CGFloat cp1Theta = (CGFloat) (atan2(previousY, previousX) - M_PI / 2.f);
  74. CGFloat cp1Dx = (CGFloat) cosf(cp1Theta);
  75. CGFloat cp1Dy = (CGFloat) sinf(cp1Theta);
  76. CGFloat cp2Theta = (CGFloat) (atan2(y, x) - M_PI / 2.f);
  77. CGFloat cp2Dx = (CGFloat) cosf(cp2Theta);
  78. CGFloat cp2Dy = (CGFloat) sinf(cp2Theta);
  79. CGFloat cp1x = outerRadius * outerRoundness * kPOLYGON_MAGIC_NUMBER * cp1Dx;
  80. CGFloat cp1y = outerRadius * outerRoundness * kPOLYGON_MAGIC_NUMBER * cp1Dy;
  81. CGFloat cp2x = outerRadius * outerRoundness * kPOLYGON_MAGIC_NUMBER * cp2Dx;
  82. CGFloat cp2y = outerRadius * outerRoundness * kPOLYGON_MAGIC_NUMBER * cp2Dy;
  83. [path LOT_addCurveToPoint:CGPointMake(x, y)
  84. controlPoint1:CGPointMake(previousX - cp1x, previousY - cp1y)
  85. controlPoint2:CGPointMake(x + cp2x, y + cp2y)];
  86. } else {
  87. [path LOT_addLineToPoint:CGPointMake(x, y)];
  88. }
  89. currentAngle += anglePerPoint;
  90. }
  91. [path LOT_closePath];
  92. [path LOT_applyTransform:CGAffineTransformMakeTranslation(position.x, position.y)];
  93. self.localPath = path;
  94. }
  95. @end