2017-08-01 14:44:03 -07:00

111 lines
4.4 KiB
Objective-C

//
// LOTPolygonAnimator.m
// Lottie
//
// Created by brandon_withrow on 7/27/17.
// Copyright © 2017 Airbnb. All rights reserved.
//
#import "LOTPolygonAnimator.h"
#import "LOTKeyframe.h"
#import "LOTPointInterpolator.h"
#import "LOTNumberInterpolator.h"
#import "LOTBezierPath.h"
#import "CGGeometry+LOTAdditions.h"
const CGFloat kPOLYGON_MAGIC_NUMBER = .25f;
@implementation LOTPolygonAnimator {
LOTNumberInterpolator *_outerRadiusInterpolator;
LOTNumberInterpolator *_outerRoundnessInterpolator;
LOTPointInterpolator *_positionInterpolator;
LOTNumberInterpolator *_pointsInterpolator;
LOTNumberInterpolator *_rotationInterpolator;
}
- (instancetype _Nonnull )initWithInputNode:(LOTAnimatorNode *_Nullable)inputNode
shapePolygon:(LOTShapeStar *_Nonnull)shapeStar {
self = [super initWithInputNode:inputNode keyName:shapeStar.keyname];
if (self) {
_outerRadiusInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:shapeStar.outerRadius.keyframes];
_outerRoundnessInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:shapeStar.outerRoundness.keyframes];
_pointsInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:shapeStar.numberOfPoints.keyframes];
_rotationInterpolator = [[LOTNumberInterpolator alloc] initWithKeyframes:shapeStar.rotation.keyframes];
_positionInterpolator = [[LOTPointInterpolator alloc] initWithKeyframes:shapeStar.position.keyframes];
}
return self;
}
- (NSDictionary *)valueInterpolators {
return @{@"Points" : _pointsInterpolator,
@"Position" : _positionInterpolator,
@"Rotation" : _rotationInterpolator,
@"Outer Radius" : _outerRadiusInterpolator,
@"Outer Roundness" : _outerRoundnessInterpolator};
}
- (BOOL)needsUpdateForFrame:(NSNumber *)frame {
return ([_outerRadiusInterpolator hasUpdateForFrame:frame] ||
[_outerRoundnessInterpolator hasUpdateForFrame:frame] ||
[_pointsInterpolator hasUpdateForFrame:frame] ||
[_rotationInterpolator hasUpdateForFrame:frame] ||
[_positionInterpolator hasUpdateForFrame:frame]);
}
- (void)performLocalUpdate {
CGFloat outerRadius = [_outerRadiusInterpolator floatValueForFrame:self.currentFrame];
CGFloat outerRoundness = [_outerRoundnessInterpolator floatValueForFrame:self.currentFrame] / 100.f;
CGFloat points = [_pointsInterpolator floatValueForFrame:self.currentFrame];
CGFloat rotation = [_rotationInterpolator floatValueForFrame:self.currentFrame];
CGPoint position = [_positionInterpolator pointValueForFrame:self.currentFrame];
LOTBezierPath *path = [[LOTBezierPath alloc] init];
path.cacheLengths = self.pathShouldCacheLengths;
CGFloat currentAngle = LOT_DegreesToRadians(rotation - 90);
CGFloat anglePerPoint = (CGFloat)((2 * M_PI) / points);
CGFloat x;
CGFloat y;
CGFloat previousX;
CGFloat previousY;
x = (CGFloat) (outerRadius * cosf(currentAngle));
y = (CGFloat) (outerRadius * sinf(currentAngle));
[path LOT_moveToPoint:CGPointMake(x, y)];
currentAngle += anglePerPoint;
double numPoints = ceil(points);
for (int i = 0; i < numPoints; i++) {
previousX = x;
previousY = y;
x = (CGFloat) (outerRadius * cosf(currentAngle));
y = (CGFloat) (outerRadius * sinf(currentAngle));
if (outerRoundness != 0) {
CGFloat cp1Theta = (CGFloat) (atan2(previousY, previousX) - M_PI / 2.f);
CGFloat cp1Dx = (CGFloat) cosf(cp1Theta);
CGFloat cp1Dy = (CGFloat) sinf(cp1Theta);
CGFloat cp2Theta = (CGFloat) (atan2(y, x) - M_PI / 2.f);
CGFloat cp2Dx = (CGFloat) cosf(cp2Theta);
CGFloat cp2Dy = (CGFloat) sinf(cp2Theta);
CGFloat cp1x = outerRadius * outerRoundness * kPOLYGON_MAGIC_NUMBER * cp1Dx;
CGFloat cp1y = outerRadius * outerRoundness * kPOLYGON_MAGIC_NUMBER * cp1Dy;
CGFloat cp2x = outerRadius * outerRoundness * kPOLYGON_MAGIC_NUMBER * cp2Dx;
CGFloat cp2y = outerRadius * outerRoundness * kPOLYGON_MAGIC_NUMBER * cp2Dy;
[path LOT_addCurveToPoint:CGPointMake(x, y)
controlPoint1:CGPointMake(previousX - cp1x, previousY - cp1y)
controlPoint2:CGPointMake(x + cp2x, y + cp2y)];
} else {
[path LOT_addLineToPoint:CGPointMake(x, y)];
}
currentAngle += anglePerPoint;
}
[path LOT_closePath];
[path LOT_applyTransform:CGAffineTransformMakeTranslation(position.x, position.y)];
self.localPath = path;
}
@end