From cc83ab86f77bcdda04793d227017b42a3e29f87f Mon Sep 17 00:00:00 2001 From: Brandon Withrow Date: Tue, 12 Jul 2016 16:41:50 -0700 Subject: [PATCH] Updated Animated properties. Started removing mantle --- LotteAnimator.xcodeproj/project.pbxproj | 42 ++-- .../UserInterfaceState.xcuserstate | Bin 38614 -> 43006 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 16 -- LotteAnimator/LAAnimatableColorValue.h | 11 +- LotteAnimator/LAAnimatableColorValue.m | 41 ++-- LotteAnimator/LAAnimatableNumberValue.h | 12 +- LotteAnimator/LAAnimatableNumberValue.m | 36 ++-- LotteAnimator/LAAnimatablePointValue.h | 12 +- LotteAnimator/LAAnimatablePointValue.m | 35 +--- LotteAnimator/LAAnimatableRectValue.h | 23 ++ LotteAnimator/LAAnimatableRectValue.m | 21 ++ LotteAnimator/LAAnimatableScaleValue.h | 15 ++ LotteAnimator/LAAnimatableScaleValue.m | 21 ++ LotteAnimator/LAAnimatableShapeValue.h | 14 +- LotteAnimator/LAAnimatableShapeValue.m | 61 ++---- LotteAnimator/LAAnimatableValue.h | 16 -- LotteAnimator/LACompView.h | 4 +- LotteAnimator/LACompView.m | 4 +- LotteAnimator/LAComposition.h | 18 +- LotteAnimator/LAComposition.m | 41 +++- LotteAnimator/LALayer.h | 45 ++-- LotteAnimator/LALayer.m | 198 ++++++------------ LotteAnimator/LAMask.h | 24 ++- LotteAnimator/LAMask.m | 51 +++-- LotteAnimator/LAModels.h | 4 +- LotteAnimator/LAPath.h | 19 -- LotteAnimator/LAPath.m | 54 ----- LotteAnimator/LAShape.h | 21 -- LotteAnimator/LAShape.m | 85 -------- LotteAnimator/LAShapeCircle.h | 10 +- LotteAnimator/LAShapeCircle.m | 28 +-- LotteAnimator/LAShapeFill.h | 17 +- LotteAnimator/LAShapeFill.m | 41 ++-- LotteAnimator/LAShapeGroup.h | 17 ++ LotteAnimator/LAShapeGroup.m | 59 ++++++ LotteAnimator/LAShapeItem.h | 28 --- LotteAnimator/LAShapeItem.m | 54 ----- LotteAnimator/LAShapePath.h | 13 +- LotteAnimator/LAShapePath.m | 28 +-- LotteAnimator/LAShapeRectangle.h | 11 +- LotteAnimator/LAShapeRectangle.m | 36 +--- LotteAnimator/LAShapeStroke.h | 18 +- LotteAnimator/LAShapeStroke.m | 47 +++-- LotteAnimator/LAShapeTransform.h | 24 +-- LotteAnimator/LAShapeTransform.m | 74 +++---- LotteAnimator/ViewController.m | 2 +- 46 files changed, 601 insertions(+), 850 deletions(-) create mode 100644 LotteAnimator/LAAnimatableRectValue.h create mode 100644 LotteAnimator/LAAnimatableRectValue.m create mode 100644 LotteAnimator/LAAnimatableScaleValue.h create mode 100644 LotteAnimator/LAAnimatableScaleValue.m delete mode 100644 LotteAnimator/LAAnimatableValue.h delete mode 100644 LotteAnimator/LAPath.h delete mode 100644 LotteAnimator/LAPath.m delete mode 100644 LotteAnimator/LAShape.h delete mode 100644 LotteAnimator/LAShape.m create mode 100644 LotteAnimator/LAShapeGroup.h create mode 100644 LotteAnimator/LAShapeGroup.m delete mode 100644 LotteAnimator/LAShapeItem.h delete mode 100644 LotteAnimator/LAShapeItem.m diff --git a/LotteAnimator.xcodeproj/project.pbxproj b/LotteAnimator.xcodeproj/project.pbxproj index 512322fbfa..f81ee8bb5a 100644 --- a/LotteAnimator.xcodeproj/project.pbxproj +++ b/LotteAnimator.xcodeproj/project.pbxproj @@ -17,10 +17,8 @@ 4804B3191C1F6DEA00DA8AF7 /* LAComposition.m in Sources */ = {isa = PBXBuildFile; fileRef = 4804B3181C1F6DEA00DA8AF7 /* LAComposition.m */; }; 4804B31E1C1F757600DA8AF7 /* UIColor+Expanded.m in Sources */ = {isa = PBXBuildFile; fileRef = 4804B31D1C1F757600DA8AF7 /* UIColor+Expanded.m */; }; 4804B3211C1F761800DA8AF7 /* BWMath.m in Sources */ = {isa = PBXBuildFile; fileRef = 4804B3201C1F761800DA8AF7 /* BWMath.m */; }; - 48372A471C1F8B4C00AD0293 /* LAPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 48372A461C1F8B4C00AD0293 /* LAPath.m */; }; 48372A4A1C1F8D7D00AD0293 /* LAMask.m in Sources */ = {isa = PBXBuildFile; fileRef = 48372A491C1F8D7D00AD0293 /* LAMask.m */; }; - 48372A4F1C1F99C600AD0293 /* LAShape.m in Sources */ = {isa = PBXBuildFile; fileRef = 48372A4E1C1F99C600AD0293 /* LAShape.m */; }; - 48372A521C20973300AD0293 /* LAShapeItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 48372A511C20973300AD0293 /* LAShapeItem.m */; }; + 48372A4F1C1F99C600AD0293 /* LAShapeGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 48372A4E1C1F99C600AD0293 /* LAShapeGroup.m */; }; 48372A551C209A5F00AD0293 /* LAShapePath.m in Sources */ = {isa = PBXBuildFile; fileRef = 48372A541C209A5F00AD0293 /* LAShapePath.m */; }; 48372A581C209A6C00AD0293 /* LAShapeStroke.m in Sources */ = {isa = PBXBuildFile; fileRef = 48372A571C209A6C00AD0293 /* LAShapeStroke.m */; }; 48372A5B1C209A7A00AD0293 /* LAShapeFill.m in Sources */ = {isa = PBXBuildFile; fileRef = 48372A5A1C209A7A00AD0293 /* LAShapeFill.m */; }; @@ -76,6 +74,10 @@ 620A565F1D1C81750030EBFB /* LAAnimatablePointValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 620A565E1D1C81750030EBFB /* LAAnimatablePointValue.m */; }; 620A56621D1C81850030EBFB /* LAAnimatableNumberValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 620A56611D1C81850030EBFB /* LAAnimatableNumberValue.m */; }; 620A56651D1C81930030EBFB /* LAAnimatableShapeValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 620A56641D1C81930030EBFB /* LAAnimatableShapeValue.m */; }; + 620CD7CD1D3415F000055AD1 /* LAAnimatableRectValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 620CD7CC1D3415F000055AD1 /* LAAnimatableRectValue.m */; }; + 620CD7D01D343A2500055AD1 /* LAAnimatableScaleValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 620CD7CF1D343A2500055AD1 /* LAAnimatableScaleValue.m */; }; + 620CD7D11D35AB9C00055AD1 /* LALayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4804B32B1C1F835F00DA8AF7 /* LALayerView.m */; }; + 620CD7D21D35AB9F00055AD1 /* LACompView.m in Sources */ = {isa = PBXBuildFile; fileRef = 48372A431C1F84D700AD0293 /* LACompView.m */; }; 629737DF1D25D7FC007B4AC9 /* allHold_KeyTest.json in Resources */ = {isa = PBXBuildFile; fileRef = 629737DB1D25D7FC007B4AC9 /* allHold_KeyTest.json */; }; 629737E01D25D7FC007B4AC9 /* firstHold_KeyTest.json in Resources */ = {isa = PBXBuildFile; fileRef = 629737DC1D25D7FC007B4AC9 /* firstHold_KeyTest.json */; }; 629737E11D25D7FC007B4AC9 /* lastHold_KeyTest.json in Resources */ = {isa = PBXBuildFile; fileRef = 629737DD1D25D7FC007B4AC9 /* lastHold_KeyTest.json */; }; @@ -109,14 +111,10 @@ 4804B32B1C1F835F00DA8AF7 /* LALayerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LALayerView.m; sourceTree = ""; }; 48372A421C1F84D700AD0293 /* LACompView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LACompView.h; sourceTree = ""; }; 48372A431C1F84D700AD0293 /* LACompView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LACompView.m; sourceTree = ""; }; - 48372A451C1F8B4C00AD0293 /* LAPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LAPath.h; sourceTree = ""; }; - 48372A461C1F8B4C00AD0293 /* LAPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LAPath.m; sourceTree = ""; }; 48372A481C1F8D7D00AD0293 /* LAMask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LAMask.h; sourceTree = ""; }; 48372A491C1F8D7D00AD0293 /* LAMask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LAMask.m; sourceTree = ""; }; - 48372A4D1C1F99C600AD0293 /* LAShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LAShape.h; sourceTree = ""; }; - 48372A4E1C1F99C600AD0293 /* LAShape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LAShape.m; sourceTree = ""; }; - 48372A501C20973300AD0293 /* LAShapeItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LAShapeItem.h; sourceTree = ""; }; - 48372A511C20973300AD0293 /* LAShapeItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LAShapeItem.m; sourceTree = ""; }; + 48372A4D1C1F99C600AD0293 /* LAShapeGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LAShapeGroup.h; sourceTree = ""; }; + 48372A4E1C1F99C600AD0293 /* LAShapeGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LAShapeGroup.m; sourceTree = ""; }; 48372A531C209A5F00AD0293 /* LAShapePath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LAShapePath.h; sourceTree = ""; }; 48372A541C209A5F00AD0293 /* LAShapePath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LAShapePath.m; sourceTree = ""; }; 48372A561C209A6C00AD0293 /* LAShapeStroke.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LAShapeStroke.h; sourceTree = ""; }; @@ -176,7 +174,6 @@ 4872BEDB1D146D850028E0B3 /* animatedTransformHold.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = animatedTransformHold.json; sourceTree = ""; }; 4872BEDD1D146D850028E0B3 /* animatedTransform.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = animatedTransform.json; sourceTree = ""; }; 488990EC1D08A2A400425189 /* curve.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = curve.json; sourceTree = ""; }; - 620A56451D1B2D3D0030EBFB /* LAAnimatableValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = LAAnimatableValue.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 620A56481D1B3B150030EBFB /* FullLayerModel_Animation.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = FullLayerModel_Animation.json; sourceTree = ""; }; 620A565A1D1C81610030EBFB /* LAAnimatableColorValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LAAnimatableColorValue.h; sourceTree = ""; }; 620A565B1D1C81610030EBFB /* LAAnimatableColorValue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LAAnimatableColorValue.m; sourceTree = ""; }; @@ -186,6 +183,10 @@ 620A56611D1C81850030EBFB /* LAAnimatableNumberValue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LAAnimatableNumberValue.m; sourceTree = ""; }; 620A56631D1C81930030EBFB /* LAAnimatableShapeValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LAAnimatableShapeValue.h; sourceTree = ""; }; 620A56641D1C81930030EBFB /* LAAnimatableShapeValue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LAAnimatableShapeValue.m; sourceTree = ""; }; + 620CD7CB1D3415F000055AD1 /* LAAnimatableRectValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LAAnimatableRectValue.h; sourceTree = ""; }; + 620CD7CC1D3415F000055AD1 /* LAAnimatableRectValue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LAAnimatableRectValue.m; sourceTree = ""; }; + 620CD7CE1D343A2500055AD1 /* LAAnimatableScaleValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LAAnimatableScaleValue.h; sourceTree = ""; }; + 620CD7CF1D343A2500055AD1 /* LAAnimatableScaleValue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LAAnimatableScaleValue.m; sourceTree = ""; }; 629737DB1D25D7FC007B4AC9 /* allHold_KeyTest.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = allHold_KeyTest.json; sourceTree = ""; }; 629737DC1D25D7FC007B4AC9 /* firstHold_KeyTest.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = firstHold_KeyTest.json; sourceTree = ""; }; 629737DD1D25D7FC007B4AC9 /* lastHold_KeyTest.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = lastHold_KeyTest.json; sourceTree = ""; }; @@ -281,14 +282,10 @@ 4804B3181C1F6DEA00DA8AF7 /* LAComposition.m */, 4804B3141C1F5B2000DA8AF7 /* LALayer.h */, 4804B3151C1F5B2000DA8AF7 /* LALayer.m */, - 48372A451C1F8B4C00AD0293 /* LAPath.h */, - 48372A461C1F8B4C00AD0293 /* LAPath.m */, 48372A481C1F8D7D00AD0293 /* LAMask.h */, 48372A491C1F8D7D00AD0293 /* LAMask.m */, - 48372A4D1C1F99C600AD0293 /* LAShape.h */, - 48372A4E1C1F99C600AD0293 /* LAShape.m */, - 48372A501C20973300AD0293 /* LAShapeItem.h */, - 48372A511C20973300AD0293 /* LAShapeItem.m */, + 48372A4D1C1F99C600AD0293 /* LAShapeGroup.h */, + 48372A4E1C1F99C600AD0293 /* LAShapeGroup.m */, 48372A561C209A6C00AD0293 /* LAShapeStroke.h */, 48372A571C209A6C00AD0293 /* LAShapeStroke.m */, 48372A591C209A7A00AD0293 /* LAShapeFill.h */, @@ -371,7 +368,6 @@ 62F683871D26EF420088C015 /* AnimatableProperties */ = { isa = PBXGroup; children = ( - 620A56451D1B2D3D0030EBFB /* LAAnimatableValue.h */, 620A565A1D1C81610030EBFB /* LAAnimatableColorValue.h */, 620A565B1D1C81610030EBFB /* LAAnimatableColorValue.m */, 620A565D1D1C81750030EBFB /* LAAnimatablePointValue.h */, @@ -380,6 +376,10 @@ 620A56611D1C81850030EBFB /* LAAnimatableNumberValue.m */, 620A56631D1C81930030EBFB /* LAAnimatableShapeValue.h */, 620A56641D1C81930030EBFB /* LAAnimatableShapeValue.m */, + 620CD7CB1D3415F000055AD1 /* LAAnimatableRectValue.h */, + 620CD7CC1D3415F000055AD1 /* LAAnimatableRectValue.m */, + 620CD7CE1D343A2500055AD1 /* LAAnimatableScaleValue.h */, + 620CD7CF1D343A2500055AD1 /* LAAnimatableScaleValue.m */, ); name = AnimatableProperties; sourceTree = ""; @@ -563,28 +563,30 @@ buildActionMask = 2147483647; files = ( 4804B31E1C1F757600DA8AF7 /* UIColor+Expanded.m in Sources */, + 620CD7D21D35AB9F00055AD1 /* LACompView.m in Sources */, 4804B2FF1C1F55E600DA8AF7 /* ViewController.m in Sources */, 48372A641C20A91C00AD0293 /* LAJSONExplorerViewController.m in Sources */, 620A56651D1C81930030EBFB /* LAAnimatableShapeValue.m in Sources */, 4804B3191C1F6DEA00DA8AF7 /* LAComposition.m in Sources */, 4804B2FC1C1F55E600DA8AF7 /* AppDelegate.m in Sources */, 4804B3211C1F761800DA8AF7 /* BWMath.m in Sources */, - 48372A471C1F8B4C00AD0293 /* LAPath.m in Sources */, 48372AB41C20C13700AD0293 /* CGGeometryAdditions.m in Sources */, 620A565F1D1C81750030EBFB /* LAAnimatablePointValue.m in Sources */, 48372A4A1C1F8D7D00AD0293 /* LAMask.m in Sources */, 4804B3161C1F5B2000DA8AF7 /* LALayer.m in Sources */, - 48372A521C20973300AD0293 /* LAShapeItem.m in Sources */, - 48372A4F1C1F99C600AD0293 /* LAShape.m in Sources */, + 48372A4F1C1F99C600AD0293 /* LAShapeGroup.m in Sources */, 48372A581C209A6C00AD0293 /* LAShapeStroke.m in Sources */, + 620CD7D11D35AB9C00055AD1 /* LALayerView.m in Sources */, 48372ABA1C20D98200AD0293 /* LAShapeRectangle.m in Sources */, 48372A5B1C209A7A00AD0293 /* LAShapeFill.m in Sources */, 4804B2F91C1F55E600DA8AF7 /* main.m in Sources */, 620A56621D1C81850030EBFB /* LAAnimatableNumberValue.m in Sources */, 48372A551C209A5F00AD0293 /* LAShapePath.m in Sources */, 48372AB71C20D97200AD0293 /* LAShapeCircle.m in Sources */, + 620CD7CD1D3415F000055AD1 /* LAAnimatableRectValue.m in Sources */, 620A565C1D1C81610030EBFB /* LAAnimatableColorValue.m in Sources */, 48372A5E1C209A8900AD0293 /* LAShapeTransform.m in Sources */, + 620CD7D01D343A2500055AD1 /* LAAnimatableScaleValue.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/LotteAnimator.xcworkspace/xcuserdata/brandon_withrow.xcuserdatad/UserInterfaceState.xcuserstate b/LotteAnimator.xcworkspace/xcuserdata/brandon_withrow.xcuserdatad/UserInterfaceState.xcuserstate index e89383cce6702dd97eba434055983fad24b5730c..b42005273b0618e8afb3e395e75a6d36a905d22d 100644 GIT binary patch literal 43006 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 literal 38614 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 diff --git a/LotteAnimator.xcworkspace/xcuserdata/brandon_withrow.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/LotteAnimator.xcworkspace/xcuserdata/brandon_withrow.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index b6306184fd..2123de9320 100644 --- a/LotteAnimator.xcworkspace/xcuserdata/brandon_withrow.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/LotteAnimator.xcworkspace/xcuserdata/brandon_withrow.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -29,22 +29,6 @@ landmarkType = "5"> - - - - @interface LAAnimatableColorValue : NSObject - (instancetype)initWithColorValues:(NSDictionary *)colorValues; -- (void)prepareAnimationsForKeyPath:(NSString *)keyPath frameRate:(NSNumber *)frameRate; - @property (nonatomic, readonly) UIColor *initialColor; -@property (nonatomic, readonly) NSString *keyPath; -@property (nonatomic, readonly) CAKeyframeAnimation *animation; + +@property (nonatomic, readonly) NSArray *colorKeyframes; +@property (nonatomic, readonly) NSArray *keyTimes; +@property (nonatomic, readonly) NSArray *timingFunctions; +@property (nonatomic, readonly) NSNumber *startFrame; +@property (nonatomic, readonly) NSNumber *durationFrames; @end diff --git a/LotteAnimator/LAAnimatableColorValue.m b/LotteAnimator/LAAnimatableColorValue.m index 5acc3f6434..5e3edce884 100644 --- a/LotteAnimator/LAAnimatableColorValue.m +++ b/LotteAnimator/LAAnimatableColorValue.m @@ -9,21 +9,16 @@ #import "LAAnimatableColorValue.h" @implementation LAAnimatableColorValue -@synthesize animation = _animation; -@synthesize keyPath = _keyPath; -- (instancetype)initWithColorValues:(NSDictionary *)colorValues - keyPath:(NSString *)keyPath - frameRate:(NSNumber *)frameRate { +- (instancetype)initWithColorValues:(NSDictionary *)colorValues { self = [super init]; if (self) { - _keyPath = [keyPath copy]; NSArray *value = colorValues[@"k"]; if ([value isKindOfClass:[NSArray class]] && [[(NSArray *)value firstObject] isKindOfClass:[NSDictionary class]] && [(NSDictionary *)[(NSArray *)value firstObject] objectForKey:@"t"]) { //Keframes - [self _buildAnimationForKeyframes:value keyPath:keyPath frameRate:frameRate]; + [self _buildAnimationForKeyframes:value]; } else { //Single Value, no animation _initialColor = [[self _colorValueFromArray:value] copy]; @@ -32,23 +27,19 @@ return self; } -- (void)_buildAnimationForKeyframes:(NSArray *)keyframes - keyPath:(NSString *)keyPath - frameRate:(NSNumber *)frameRate { +- (void)_buildAnimationForKeyframes:(NSArray *)keyframes { NSMutableArray *keyTimes = [NSMutableArray array]; NSMutableArray *timingFunctions = [NSMutableArray array]; NSMutableArray *colorValues = [NSMutableArray array]; - NSNumber *startFrame = keyframes.firstObject[@"t"]; + _startFrame = keyframes.firstObject[@"t"]; NSNumber *endFrame = keyframes.lastObject[@"t"]; - NSAssert((startFrame && endFrame && startFrame.integerValue < endFrame.integerValue), + NSAssert((_startFrame && endFrame && _startFrame.integerValue < endFrame.integerValue), @"Lotte: Keyframe animation has incorrect time values or invalid number of keyframes"); - // Calculate time bounds - NSTimeInterval beginTime = startFrame.floatValue / frameRate.floatValue; - NSNumber *durationFrames = @(endFrame.floatValue - startFrame.floatValue); - NSTimeInterval durationTime = durationFrames.floatValue / frameRate.floatValue; + // Calculate time duration + _durationFrames = @(endFrame.floatValue - _startFrame.floatValue); BOOL addStartValue = YES; BOOL addTimePadding = NO; @@ -59,11 +50,11 @@ NSNumber *frame = keyframe[@"t"]; // Calculate percentage value for keyframe. //CA Animations accept time values of 0-1 as a percentage of animation completed. - NSNumber *timePercentage = @((frame.floatValue - startFrame.floatValue) / durationFrames.floatValue); + NSNumber *timePercentage = @((frame.floatValue - _startFrame.floatValue) / _durationFrames.floatValue); if (outColor) { //add out value - [colorValues addObject:(id)[[outColor copy] CGColor]]; + [colorValues addObject:[outColor copy]]; [timingFunctions addObject:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]]; outColor = nil; } @@ -75,7 +66,7 @@ if (keyframe == keyframes.firstObject) { _initialColor = startColor; } - [colorValues addObject:(id)[[startColor copy] CGColor]]; + [colorValues addObject:[startColor copy]]; if (timingFunctions.count) { [timingFunctions addObject:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]]; } @@ -93,7 +84,7 @@ // add end value if present for keyframe UIColor *endColor = [self _colorValueFromArray:keyframe[@"e"]]; if (endColor) { - [colorValues addObject:(id)[[endColor copy] CGColor]]; + [colorValues addObject:[endColor copy]]; /* * Timing Function for time interpolations between keyframes * Should be n-1 where n is the number of keyframes @@ -126,13 +117,9 @@ } } - _animation = [CAKeyframeAnimation animationWithKeyPath:keyPath]; - _animation.values = colorValues; - _animation.keyTimes = keyTimes; - _animation.timingFunctions = timingFunctions; - _animation.beginTime = beginTime; - _animation.duration = durationTime; - _animation.fillMode = kCAFillModeForwards; + _colorKeyframes = colorValues; + _keyTimes = keyTimes; + _timingFunctions = timingFunctions; } - (UIColor *)_colorValueFromArray:(NSArray *)colorArray { diff --git a/LotteAnimator/LAAnimatableNumberValue.h b/LotteAnimator/LAAnimatableNumberValue.h index 5c380870f3..da28fff1d6 100644 --- a/LotteAnimator/LAAnimatableNumberValue.h +++ b/LotteAnimator/LAAnimatableNumberValue.h @@ -6,17 +6,17 @@ // Copyright © 2016 Brandon Withrow. All rights reserved. // -#import "LAAnimatableValue.h" #import @interface LAAnimatableNumberValue : NSObject -- (instancetype)initWithNumberValues:(NSDictionary *)numberValues - keyPath:(NSString *)keyPath - frameRate:(NSNumber *)frameRate; +- (instancetype)initWithNumberValues:(NSDictionary *)numberValues; @property (nonatomic, readonly) NSNumber *initialValue; -@property (nonatomic, readonly) NSString *keyPath; -@property (nonatomic, readonly) CAKeyframeAnimation *animation; +@property (nonatomic, readonly) NSArray *valueKeyframes; +@property (nonatomic, readonly) NSArray *keyTimes; +@property (nonatomic, readonly) NSArray *timingFunctions; +@property (nonatomic, readonly) NSNumber *startFrame; +@property (nonatomic, readonly) NSNumber *durationFrames; @end diff --git a/LotteAnimator/LAAnimatableNumberValue.m b/LotteAnimator/LAAnimatableNumberValue.m index 220528c4a6..921c490a84 100644 --- a/LotteAnimator/LAAnimatableNumberValue.m +++ b/LotteAnimator/LAAnimatableNumberValue.m @@ -10,18 +10,15 @@ @implementation LAAnimatableNumberValue -- (instancetype)initWithNumberValues:(NSDictionary *)numberValues - keyPath:(NSString *)keyPath - frameRate:(NSNumber *)frameRate { +- (instancetype)initWithNumberValues:(NSDictionary *)numberValues { self = [super init]; if (self) { - _keyPath = [keyPath copy]; id value = numberValues[@"k"]; if ([value isKindOfClass:[NSArray class]] && [[(NSArray *)value firstObject] isKindOfClass:[NSDictionary class]] && [(NSDictionary *)[(NSArray *)value firstObject] objectForKey:@"t"]) { //Keframes - [self _buildAnimationForKeyframes:value keyPath:keyPath frameRate:frameRate]; + [self _buildAnimationForKeyframes:value]; } else { //Single Value, no animation _initialValue = [[self _numberValueFromObject:value] copy]; @@ -30,23 +27,21 @@ return self; } -- (void)_buildAnimationForKeyframes:(NSArray *)keyframes - keyPath:(NSString *)keyPath - frameRate:(NSNumber *)frameRate { +- (void)_buildAnimationForKeyframes:(NSArray *)keyframes { + NSMutableArray *keyTimes = [NSMutableArray array]; NSMutableArray *timingFunctions = [NSMutableArray array]; NSMutableArray *numberValues = [NSMutableArray array]; - NSNumber *startFrame = keyframes.firstObject[@"t"]; + _startFrame = keyframes.firstObject[@"t"]; NSNumber *endFrame = keyframes.lastObject[@"t"]; - NSAssert((startFrame && endFrame && startFrame.integerValue < endFrame.integerValue), + NSAssert((_startFrame && endFrame && _startFrame.integerValue < endFrame.integerValue), @"Lotte: Keyframe animation has incorrect time values or invalid number of keyframes"); - // Calculate time bounds in second-time. - NSTimeInterval beginTime = startFrame.floatValue / frameRate.floatValue; - NSNumber *durationFrames = @(endFrame.floatValue - startFrame.floatValue); - NSTimeInterval durationTime = durationFrames.floatValue / frameRate.floatValue; + // Calculate time duration + _durationFrames = @(endFrame.floatValue - _startFrame.floatValue); + BOOL addStartValue = YES; BOOL addTimePadding = NO; @@ -57,7 +52,7 @@ NSNumber *frame = keyframe[@"t"]; // Calculate percentage value for keyframe. //CA Animations accept time values of 0-1 as a percentage of animation completed. - NSNumber *timePercentage = @((frame.floatValue - startFrame.floatValue) / durationFrames.floatValue); + NSNumber *timePercentage = @((frame.floatValue - _startFrame.floatValue) / _durationFrames.floatValue); if (outValue) { //add out value @@ -123,14 +118,9 @@ addTimePadding = YES; } } - - _animation = [CAKeyframeAnimation animationWithKeyPath:keyPath]; - _animation.values = numberValues; - _animation.keyTimes = keyTimes; - _animation.timingFunctions = timingFunctions; - _animation.beginTime = beginTime; - _animation.duration = durationTime; - _animation.fillMode = kCAFillModeForwards; + _valueKeyframes = numberValues; + _keyTimes = keyTimes; + _timingFunctions = timingFunctions; } - (NSNumber *)_numberValueFromObject:(id)valueObject { diff --git a/LotteAnimator/LAAnimatablePointValue.h b/LotteAnimator/LAAnimatablePointValue.h index 843af062b5..b560c8b4fb 100644 --- a/LotteAnimator/LAAnimatablePointValue.h +++ b/LotteAnimator/LAAnimatablePointValue.h @@ -6,17 +6,17 @@ // Copyright © 2016 Brandon Withrow. All rights reserved. // -#import "LAAnimatableValue.h" #import @interface LAAnimatablePointValue : NSObject -- (instancetype)initWithPointValues:(NSDictionary *)pointValues - keyPath:(NSString *)keyPath - frameRate:(NSNumber *)frameRate; +- (instancetype)initWithPointValues:(NSDictionary *)pointValues; @property (nonatomic, readonly) CGPoint initialPoint; -@property (nonatomic, readonly) NSString *keyPath; -@property (nonatomic, readonly) CAKeyframeAnimation *animation; +@property (nonatomic, readonly) UIBezierPath *animationPath; +@property (nonatomic, readonly) NSArray *keyTimes; +@property (nonatomic, readonly) NSArray *timingFunctions; +@property (nonatomic, readonly) NSNumber *startFrame; +@property (nonatomic, readonly) NSNumber *durationFrames; @end diff --git a/LotteAnimator/LAAnimatablePointValue.m b/LotteAnimator/LAAnimatablePointValue.m index 5b2c75bdb4..ed8ac4bbf0 100644 --- a/LotteAnimator/LAAnimatablePointValue.m +++ b/LotteAnimator/LAAnimatablePointValue.m @@ -9,21 +9,16 @@ #import "LAAnimatablePointValue.h" @implementation LAAnimatablePointValue -@synthesize animation = _animation; -@synthesize keyPath = _keyPath; -- (instancetype)initWithPointValues:(NSDictionary *)pointValues - keyPath:(NSString *)keyPath - frameRate:(NSNumber *)frameRate { +- (instancetype)initWithPointValues:(NSDictionary *)pointValues { self = [super init]; if (self) { - _keyPath = [keyPath copy]; NSArray *value = pointValues[@"k"]; if ([value isKindOfClass:[NSArray class]] && [[(NSArray *)value firstObject] isKindOfClass:[NSDictionary class]] && [(NSDictionary *)[(NSArray *)value firstObject] objectForKey:@"t"]) { //Keframes - [self _buildAnimationForKeyframes:value keyPath:keyPath frameRate:frameRate]; + [self _buildAnimationForKeyframes:value]; } else { //Single Value, no animation _initialPoint = [self _pointFromValueArray:value]; @@ -32,23 +27,19 @@ return self; } -- (void)_buildAnimationForKeyframes:(NSArray *)keyframes - keyPath:(NSString *)keyPath - frameRate:(NSNumber *)frameRate { +- (void)_buildAnimationForKeyframes:(NSArray *)keyframes { NSMutableArray *keyTimes = [NSMutableArray array]; NSMutableArray *timingFunctions = [NSMutableArray array]; UIBezierPath *motionPath = [UIBezierPath new]; - NSNumber *startFrame = keyframes.firstObject[@"t"]; + _startFrame = keyframes.firstObject[@"t"]; NSNumber *endFrame = keyframes.lastObject[@"t"]; - NSAssert((startFrame && endFrame && startFrame.integerValue < endFrame.integerValue), + NSAssert((_startFrame && endFrame && _startFrame.integerValue < endFrame.integerValue), @"Lotte: Keyframe animation has incorrect time values or invalid number of keyframes"); - // Calculate time bounds - NSTimeInterval beginTime = startFrame.floatValue / frameRate.floatValue; - NSNumber *durationFrames = @(endFrame.floatValue - startFrame.floatValue); - NSTimeInterval durationTime = durationFrames.floatValue / frameRate.floatValue; + // Calculate time duration + _durationFrames = @(endFrame.floatValue - _startFrame.floatValue); BOOL addStartValue = YES; BOOL addTimePadding = NO; @@ -59,7 +50,7 @@ NSNumber *frame = keyframe[@"t"]; // Calculate percentage value for keyframe. //CA Animations accept time values of 0-1 as a percentage of animation completed. - NSNumber *timePercentage = @((frame.floatValue - startFrame.floatValue) / durationFrames.floatValue); + NSNumber *timePercentage = @((frame.floatValue - _startFrame.floatValue) / _durationFrames.floatValue); if (outPoint) { //add out value @@ -138,13 +129,9 @@ } } - _animation = [CAKeyframeAnimation animationWithKeyPath:keyPath]; - _animation.path = motionPath.CGPath; - _animation.keyTimes = keyTimes; - _animation.timingFunctions = timingFunctions; - _animation.beginTime = beginTime; - _animation.duration = durationTime; - _animation.fillMode = kCAFillModeForwards; + _animationPath = motionPath; + _keyTimes = keyTimes; + _timingFunctions = timingFunctions; } - (CGPoint)_pointFromValueArray:(NSArray *)values { diff --git a/LotteAnimator/LAAnimatableRectValue.h b/LotteAnimator/LAAnimatableRectValue.h new file mode 100644 index 0000000000..41cd79a07d --- /dev/null +++ b/LotteAnimator/LAAnimatableRectValue.h @@ -0,0 +1,23 @@ +// +// LAAnimatableSizeValue.h +// LotteAnimator +// +// Created by brandon_withrow on 7/11/16. +// Copyright © 2016 Brandon Withrow. All rights reserved. +// + +#import + +@interface LAAnimatableRectValue : NSObject + +- (instancetype)initWithRectValues:(NSDictionary *)rectValues; + +@property (nonatomic, readonly) CGRect initialRect; +@property (nonatomic, readonly) NSArray *rectKeyframes; +@property (nonatomic, readonly) NSArray *keyTimes; +@property (nonatomic, readonly) NSArray *timingFunctions; +@property (nonatomic, readonly) NSNumber *startFrame; +@property (nonatomic, readonly) NSNumber *durationFrames; + + +@end diff --git a/LotteAnimator/LAAnimatableRectValue.m b/LotteAnimator/LAAnimatableRectValue.m new file mode 100644 index 0000000000..573aaa65d4 --- /dev/null +++ b/LotteAnimator/LAAnimatableRectValue.m @@ -0,0 +1,21 @@ +// +// LAAnimatableSizeValue.m +// LotteAnimator +// +// Created by brandon_withrow on 7/11/16. +// Copyright © 2016 Brandon Withrow. All rights reserved. +// + +#import "LAAnimatableRectValue.h" + +@implementation LAAnimatableRectValue + +- (instancetype)initWithRectValues:(NSDictionary *)rectValues { + self = [super init]; + if (self) { + + } + return self; +} + +@end diff --git a/LotteAnimator/LAAnimatableScaleValue.h b/LotteAnimator/LAAnimatableScaleValue.h new file mode 100644 index 0000000000..2e3b916da3 --- /dev/null +++ b/LotteAnimator/LAAnimatableScaleValue.h @@ -0,0 +1,15 @@ +// +// LAAnimatableScaleValue.h +// LotteAnimator +// +// Created by brandon_withrow on 7/11/16. +// Copyright © 2016 Brandon Withrow. All rights reserved. +// + +#import + +@interface LAAnimatableScaleValue : NSObject + +- (instancetype)initWithScaleValues:(NSDictionary *)scaleValues; + +@end diff --git a/LotteAnimator/LAAnimatableScaleValue.m b/LotteAnimator/LAAnimatableScaleValue.m new file mode 100644 index 0000000000..4139f3f717 --- /dev/null +++ b/LotteAnimator/LAAnimatableScaleValue.m @@ -0,0 +1,21 @@ +// +// LAAnimatableScaleValue.m +// LotteAnimator +// +// Created by brandon_withrow on 7/11/16. +// Copyright © 2016 Brandon Withrow. All rights reserved. +// + +#import "LAAnimatableScaleValue.h" + +@implementation LAAnimatableScaleValue + +- (instancetype)initWithScaleValues:(NSDictionary *)scaleValues { + self = [super init]; + if (self) { + + } + return self; +} + +@end diff --git a/LotteAnimator/LAAnimatableShapeValue.h b/LotteAnimator/LAAnimatableShapeValue.h index 06e2e9ff9c..f97d1951ef 100644 --- a/LotteAnimator/LAAnimatableShapeValue.h +++ b/LotteAnimator/LAAnimatableShapeValue.h @@ -6,18 +6,18 @@ // Copyright © 2016 Brandon Withrow. All rights reserved. // -#import "LAAnimatableValue.h" #import @interface LAAnimatableShapeValue : NSObject -- (instancetype)initWithShapeValues:(NSDictionary *)shapeValues - keyPath:(NSString *)keyPath - frameRate:(NSNumber *)frameRate - closedPath:(BOOL)closedPath; +- (instancetype)initWithShapeValues:(NSDictionary *)shapeValues closed:(BOOL)closed; @property (nonatomic, readonly) UIBezierPath *initialShape; -@property (nonatomic, readonly) NSString *keyPath; -@property (nonatomic, readonly) CAKeyframeAnimation *animation; + +@property (nonatomic, readonly) NSArray *shapeKeyframes; +@property (nonatomic, readonly) NSArray *keyTimes; +@property (nonatomic, readonly) NSArray *timingFunctions; +@property (nonatomic, readonly) NSNumber *startFrame; +@property (nonatomic, readonly) NSNumber *durationFrames; @end diff --git a/LotteAnimator/LAAnimatableShapeValue.m b/LotteAnimator/LAAnimatableShapeValue.m index bcecf37dad..21c6204512 100644 --- a/LotteAnimator/LAAnimatableShapeValue.m +++ b/LotteAnimator/LAAnimatableShapeValue.m @@ -9,76 +9,65 @@ #import "LAAnimatableShapeValue.h" @implementation LAAnimatableShapeValue -@synthesize animation = _animation; -@synthesize keyPath = _keyPath; -- (instancetype)initWithShapeValues:(NSDictionary *)shapeValues - keyPath:(NSString *)keyPath - frameRate:(NSNumber *)frameRate - closedPath:(BOOL)closedPath { +- (instancetype)initWithShapeValues:(NSDictionary *)shapeValues closed:(BOOL)closed { self = [super init]; if (self) { - _keyPath = [keyPath copy]; id value = shapeValues[@"k"]; if ([value isKindOfClass:[NSArray class]] && [[(NSArray *)value firstObject] isKindOfClass:[NSDictionary class]] && [(NSDictionary *)[(NSArray *)value firstObject] objectForKey:@"t"]) { //Keframes - [self _buildAnimationForKeyframes:value keyPath:keyPath frameRate:frameRate closedPath:closedPath]; - } else { + [self _buildAnimationForKeyframes:value closed:closed]; + } else if ([value isKindOfClass:[NSDictionary class]]) { //Single Value, no animation - _initialShape = [self _bezierShapeFromValue:value closedPath:closedPath]; + _initialShape = [self _bezierShapeFromValue:value closed:closed]; } } return self; } -- (void)_buildAnimationForKeyframes:(NSArray *)keyframes - keyPath:(NSString *)keyPath - frameRate:(NSNumber *)frameRate - closedPath:(BOOL)closedPath { +- (void)_buildAnimationForKeyframes:(NSArray *)keyframes closed:(BOOL)closed { NSMutableArray *keyTimes = [NSMutableArray array]; NSMutableArray *timingFunctions = [NSMutableArray array]; - NSMutableArray *shapeValues = [NSMutableArray array]; + NSMutableArray *shapeValues = [NSMutableArray array]; - NSNumber *startFrame = keyframes.firstObject[@"t"]; + _startFrame = keyframes.firstObject[@"t"]; NSNumber *endFrame = keyframes.lastObject[@"t"]; - NSAssert((startFrame && endFrame && startFrame.integerValue < endFrame.integerValue), + NSAssert((_startFrame && endFrame && _startFrame.integerValue < endFrame.integerValue), @"Lotte: Keyframe animation has incorrect time values or invalid number of keyframes"); - // Calculate time bounds - NSTimeInterval beginTime = startFrame.floatValue / frameRate.floatValue; - NSNumber *durationFrames = @(endFrame.floatValue - startFrame.floatValue); - NSTimeInterval durationTime = durationFrames.floatValue / frameRate.floatValue; + // Calculate time duration + _durationFrames = @(endFrame.floatValue - _startFrame.floatValue); BOOL addStartValue = YES; BOOL addTimePadding = NO; - UIBezierPath *outShape = nil; + NSDictionary *outShape = nil; for (NSDictionary *keyframe in keyframes) { // Get keyframe time value NSNumber *frame = keyframe[@"t"]; // Calculate percentage value for keyframe. //CA Animations accept time values of 0-1 as a percentage of animation completed. - NSNumber *timePercentage = @((frame.floatValue - startFrame.floatValue) / durationFrames.floatValue); + NSNumber *timePercentage = @((frame.floatValue - _startFrame.floatValue) / _durationFrames.floatValue); if (outShape) { //add out value - [shapeValues addObject:(id)[[outShape copy] CGPath]]; + [shapeValues addObject:[self _bezierShapeFromValue:outShape closed:closed]]; [timingFunctions addObject:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]]; outShape = nil; } - UIBezierPath *startShape = [self _bezierShapeFromValue:keyframe[@"s"] closedPath:closedPath]; + NSDictionary *startShape = keyframe[@"s"]; if (addStartValue) { // Add start value if (startShape) { if (keyframe == keyframes.firstObject) { - _initialShape = startShape; + _initialShape = [self _bezierShapeFromValue:startShape closed:closed]; } - [shapeValues addObject:(id)[[startShape copy] CGPath]]; + [shapeValues addObject:[self _bezierShapeFromValue:startShape closed:closed]]; if (timingFunctions.count) { [timingFunctions addObject:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]]; } @@ -94,9 +83,9 @@ } // add end value if present for keyframe - UIBezierPath *endShape = [self _bezierShapeFromValue:keyframe[@"e"] closedPath:closedPath]; + NSDictionary *endShape = keyframe[@"e"]; if (endShape) { - [shapeValues addObject:(id)[[endShape copy] CGColor]]; + [shapeValues addObject:[self _bezierShapeFromValue:endShape closed:closed]]; /* * Timing Function for time interpolations between keyframes * Should be n-1 where n is the number of keyframes @@ -128,17 +117,13 @@ addTimePadding = YES; } } - - _animation = [CAKeyframeAnimation animationWithKeyPath:keyPath]; - _animation.values = shapeValues; - _animation.keyTimes = keyTimes; - _animation.timingFunctions = timingFunctions; - _animation.beginTime = beginTime; - _animation.duration = durationTime; - _animation.fillMode = kCAFillModeForwards; + + _shapeKeyframes = shapeValues; + _keyTimes = keyTimes; + _timingFunctions = timingFunctions; } -- (UIBezierPath *)_bezierShapeFromValue:(id)value closedPath:(BOOL)closedPath { +- (UIBezierPath *)_bezierShapeFromValue:(id)value closed:(BOOL)closedPath { NSDictionary *pointsData = nil; if ([value isKindOfClass:[NSArray class]] && [[(NSArray *)value firstObject] isKindOfClass:[NSDictionary class]] && diff --git a/LotteAnimator/LAAnimatableValue.h b/LotteAnimator/LAAnimatableValue.h deleted file mode 100644 index f0f6f91419..0000000000 --- a/LotteAnimator/LAAnimatableValue.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// LAAnimatableProperty.h -// LotteAnimator -// -// Created by brandon_withrow on 6/22/16. -// Copyright © 2016 Brandon Withrow. All rights reserved. -// - -#import - -@protocol LAAnimatableValue - -@property (nonatomic, readonly) NSString *keyPath; -@property (nonatomic, readonly) CAKeyframeAnimation *animation; - -@end diff --git a/LotteAnimator/LACompView.h b/LotteAnimator/LACompView.h index 29bf703d68..0f2ab8ee49 100644 --- a/LotteAnimator/LACompView.h +++ b/LotteAnimator/LACompView.h @@ -10,9 +10,9 @@ @interface LACompView : UIView -- (instancetype)initWithModel:(LAScene *)model; +- (instancetype)initWithModel:(LAComposition *)model; -@property (nonatomic, strong) LAScene *sceneModel; +@property (nonatomic, strong) LAComposition *sceneModel; @property (nonatomic, assign) BOOL debugModeOn; @end diff --git a/LotteAnimator/LACompView.m b/LotteAnimator/LACompView.m index 4ed7166818..1cc054f04c 100644 --- a/LotteAnimator/LACompView.m +++ b/LotteAnimator/LACompView.m @@ -10,8 +10,8 @@ @implementation LACompView -- (instancetype)initWithModel:(LAScene *)model { - self = [super initWithFrame:CGRectMake(0, 0, model.compWidth.floatValue, model.compHeight.floatValue)]; +- (instancetype)initWithModel:(LAComposition *)model { + self = [super initWithFrame:model.compBounds]; if (self) { _sceneModel = model; for (LALayer *layer in model.layers) { diff --git a/LotteAnimator/LAComposition.h b/LotteAnimator/LAComposition.h index b38f05df14..67690dcc92 100644 --- a/LotteAnimator/LAComposition.h +++ b/LotteAnimator/LAComposition.h @@ -6,16 +6,18 @@ // Copyright © 2015 Brandon Withrow. All rights reserved. // -#import "MTLModel.h" +#import @class LALayer; -@interface LAComposition : MTLModel +@interface LAComposition : NSObject -@property (nonatomic, strong) NSArray *layers; -@property (nonatomic, strong) NSNumber *compWidth; -@property (nonatomic, strong) NSNumber *compHeight; -@property (nonatomic, strong) NSNumber *startFrame; -@property (nonatomic, strong) NSNumber *endFrame; -@property (nonatomic, strong) NSNumber *framerate; +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary; + +@property (nonatomic, readonly) NSArray *layers; +@property (nonatomic, readonly) CGRect compBounds; +@property (nonatomic, readonly) NSNumber *startFrame; +@property (nonatomic, readonly) NSNumber *endFrame; +@property (nonatomic, readonly) NSNumber *framerate; +@property (nonatomic, readonly) NSTimeInterval timeDuration; @end diff --git a/LotteAnimator/LAComposition.m b/LotteAnimator/LAComposition.m index d49ff86c4a..a806da7b39 100644 --- a/LotteAnimator/LAComposition.m +++ b/LotteAnimator/LAComposition.m @@ -11,18 +11,39 @@ @implementation LAComposition -+ (NSDictionary *)JSONKeyPathsByPropertyKey { - return @{@"compWidth" : @"w", - @"compHeight" : @"h", - @"framerate" : @"fr", - @"startFrame" : @"ip", - @"endFrame" : @"op", - @"layers" : @"layers"}; +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary { + self = [super init]; + if (self) { + [self _mapFromJSON:jsonDictionary]; + } + return self; } -+ (NSValueTransformer *)layersJSONTransformer { - // tell Mantle to populate diaAttributes property with an array of MDAttribute objects - return [MTLJSONAdapter arrayTransformerWithModelClass:[LALayer class]]; +- (void)_mapFromJSON:(NSDictionary *)jsonDictionary { + NSNumber *width = jsonDictionary[@"w"]; + NSNumber *height = jsonDictionary[@"h"]; + if (width && height) { + CGRect bounds = CGRectMake(0, 0, width.floatValue, height.floatValue); + _compBounds = bounds; + } + + _startFrame = [jsonDictionary[@"ip"] copy]; + _endFrame = [jsonDictionary[@"op"] copy]; + _framerate = [jsonDictionary[@"fr"] copy]; + + if (_startFrame && _endFrame && _framerate) { + NSInteger frameDuration = _endFrame.integerValue - _startFrame.integerValue; + NSTimeInterval timeDuration = frameDuration / _framerate.floatValue; + _timeDuration = timeDuration; + } + + NSArray *layersJSON = jsonDictionary[@"layers"]; + NSMutableArray *layers = [NSMutableArray array]; + for (NSDictionary *layerJSON in layersJSON) { + LALayer *layer = [[LALayer alloc] initWithJSON:layerJSON frameRate:_framerate]; + [layers addObject:layer]; + } + _layers = layers; } @end diff --git a/LotteAnimator/LALayer.h b/LotteAnimator/LALayer.h index c1a67a16b2..8d981c7d58 100644 --- a/LotteAnimator/LALayer.h +++ b/LotteAnimator/LALayer.h @@ -6,17 +6,18 @@ // Copyright © 2015 Brandon Withrow. All rights reserved. // -#import "MTLModel.h" +#import #import -@class LALayerView; -@class LAShape; +@class LAShapeGroup; @class LAMask; @class LAAnimatableColorValue; @class LAAnimatablePointValue; @class LAAnimatableNumberValue; +@class LAAnimatableRectValue; +@class LAAnimatableScaleValue; -typedef enum : NSUInteger { +typedef enum : NSInteger { LALayerTypeNone, LALayerTypeSolid, LALayerTypeUnknown, @@ -24,25 +25,27 @@ typedef enum : NSUInteger { LALayerTypeShape } LALayerType; -@interface LALayer : MTLModel +@interface LALayer : NSObject -@property (nonatomic, copy) NSString *layerName; -@property (nonatomic, copy) NSNumber *layerID; -@property (nonatomic, assign) LALayerType layerType; -@property (nonatomic, copy) NSNumber *parentID; -@property (nonatomic, copy) NSNumber *inPoint; -@property (nonatomic, copy) NSNumber *outPoint; +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate; -@property (nonatomic, copy) NSArray *shapes; -@property (nonatomic, copy) NSArray *masks; -@property (nonatomic, copy) LAAnimatableNumberValue *solidWidth; -@property (nonatomic, copy) LAAnimatableNumberValue *solidHeight; -@property (nonatomic, copy) LAAnimatableColorValue *solidColor; +@property (nonatomic, readonly) NSString *layerName; +@property (nonatomic, readonly) NSNumber *layerID; +@property (nonatomic, readonly) LALayerType layerType; +@property (nonatomic, readonly) NSNumber *parentID; +@property (nonatomic, readonly) NSNumber *inFrame; +@property (nonatomic, readonly) NSNumber *outFrame; -@property (nonatomic, copy) LAAnimatableNumberValue *opacity; -@property (nonatomic, copy) LAAnimatableNumberValue *rotation; -@property (nonatomic, copy) LAAnimatablePointValue *position; -@property (nonatomic, copy) LAAnimatablePointValue *anchor; -//@property (nonatomic, copy) LAAnimatableProperty *scale; //TODO Make This +@property (nonatomic, readonly) NSArray *shapes; +@property (nonatomic, readonly) NSArray *masks; + +@property (nonatomic, readonly) LAAnimatableRectValue *solidBounds; +@property (nonatomic, readonly) LAAnimatableColorValue *solidColor; + +@property (nonatomic, readonly) LAAnimatableNumberValue *opacity; +@property (nonatomic, readonly) LAAnimatableNumberValue *rotation; +@property (nonatomic, readonly) LAAnimatablePointValue *position; +@property (nonatomic, readonly) LAAnimatablePointValue *anchor; +@property (nonatomic, readonly) LAAnimatableScaleValue *scale; @end diff --git a/LotteAnimator/LALayer.m b/LotteAnimator/LALayer.m index 526d4cf218..25214a6e3b 100644 --- a/LotteAnimator/LALayer.m +++ b/LotteAnimator/LALayer.m @@ -10,143 +10,77 @@ #import "LAAnimatableColorValue.h" #import "LAAnimatablePointValue.h" #import "LAAnimatableNumberValue.h" +#import "LAAnimatableScaleValue.h" +#import "LAShapeGroup.h" @implementation LALayer -+ (NSDictionary *)JSONKeyPathsByPropertyKey -{ - // model_property_name : json_field_name - return @{ - @"layerName" : @"layerName", - @"layerID" : @"ind", - @"layerType" : @"ty", - @"parentID" : @"parent", - @"inPoint" : @"ip", - @"outPoint" : @"op", - - @"rotation" : @"ks.r", - @"position" : @"ks.p", - @"anchor" : @"ks.a", -// @"scale" : @"ks.s", - @"opacity" : @"ks.o", - - @"solidWidth" : @"sw", - @"solidHeight" : @"sh", - @"solidColor" : @"sc", -// @"masks" : @"masksProperties", -// @"shapes" : @"shapes" - }; +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + self = [super init]; + if (self) { + [self _mapFromJSON:jsonDictionary frameRate:frameRate]; + } + return self; } -+ (NSValueTransformer *)shapesJSONTransformer { - return [MTLJSONAdapter arrayTransformerWithModelClass:[LAShape class]]; +- (void)_mapFromJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + _layerName = [jsonDictionary[@"nm"] copy]; + _layerID = [jsonDictionary[@"ind"] copy]; + + NSNumber *layerType = jsonDictionary[@"ty"]; + if (layerType.integerValue <= LALayerTypeShape) { + _layerType = layerType.integerValue; + } else { + _layerType = LALayerTypeUnknown; + } + + _parentID = [jsonDictionary[@"parent"] copy]; + _inFrame = [jsonDictionary[@"ip"] copy]; + _outFrame = [jsonDictionary[@"op"] copy]; + + if (_layerType == LALayerTypeSolid) { + // TODO Solids. + + } + + NSDictionary *opacity = jsonDictionary[@"ks.o"]; + if (opacity) { + _opacity = [[LAAnimatableNumberValue alloc] initWithNumberValues:opacity]; + } + + NSDictionary *rotation = jsonDictionary[@"ks.r"]; + if (rotation) { + _rotation = [[LAAnimatableNumberValue alloc] initWithNumberValues:rotation]; + } + + NSDictionary *position = jsonDictionary[@"ks.p"]; + if (position) { + _position = [[LAAnimatablePointValue alloc] initWithPointValues:position]; + } + + NSDictionary *anchor = jsonDictionary[@"ks.a"]; + if (anchor) { + _anchor = [[LAAnimatablePointValue alloc] initWithPointValues:anchor]; + } + + NSDictionary *scale = jsonDictionary[@"ks.s"]; + if (scale) { + _scale = [[LAAnimatableScaleValue alloc] initWithScaleValues:scale]; + } + + NSMutableArray *masks = [NSMutableArray array]; + for (NSDictionary *maskJSON in jsonDictionary[@"masksProperties"]) { + LAMask *mask = [[LAMask alloc] initWithJSON:maskJSON frameRate:frameRate]; + [masks addObject:mask]; + } + _masks = masks; + + NSMutableArray *shapes = [NSMutableArray array]; + for (NSDictionary *shapeJSON in jsonDictionary[@"shapes"]) { + LAShapeGroup *group = [[LAShapeGroup alloc] initWithJSON:shapeJSON frameRate:frameRate]; + [shapes addObject:group]; + } + _shapes = shapes; } -+ (NSValueTransformer *)masksJSONTransformer { - return [MTLJSONAdapter arrayTransformerWithModelClass:[LAMask class]]; -} - -+ (NSValueTransformer *)rotationJSONTransformer { - return [MTLValueTransformer transformerUsingForwardBlock:^id(NSDictionary *value, BOOL *success, NSError *__autoreleasing *error) { - if (value == nil) { - return nil; - } - if (![value isKindOfClass:NSDictionary.class]) { - if (error != NULL) { - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: NSLocalizedString(@"Could not convert JSON dictionary to model object", @""), - NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected an NSDictionary, got: %@", @""), value], - MTLTransformerErrorHandlingInputValueErrorKey : value - }; - - *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; - } - *success = NO; - return nil; - } - LAAnimatableNumberValue *numvalue = [[LAAnimatableNumberValue alloc] initWithNumberValues:<#(NSDictionary *)#> keyPath:<#(NSString *)#> frameRate:<#(NSNumber *)#>] - }]; -} - -//+ (NSValueTransformer *)rotationJSONTransformer { -// return [LAAnimatableValue animatablePropertyValueTransformerForKey:@"rotation"]; -//} -// -//+ (NSValueTransformer *)positionJSONTransformer { -// return [LAAnimatableValue animatablePropertyValueTransformerForKey:@"position"]; -//} -// -//+ (NSValueTransformer *)anchorJSONTransformer { -// return [LAAnimatableValue animatablePropertyValueTransformerForKey:@"anchor"]; -//} -// -//+ (NSValueTransformer *)scaleJSONTransformer { -// return [LAAnimatableValue animatablePropertyValueTransformerForKey:@"scale"]; -//} -// -//+ (NSValueTransformer *)opacityJSONTransformer { -// return [LAAnimatableValue animatablePropertyValueTransformerForKey:@"opacity"]; -//} - -//- (UIColor *)bgColor { -// if (!self.color) { -// return [UIColor clearColor]; -// } -// NSString *hexString = [self.color copy]; -// hexString = [hexString stringByReplacingOccurrencesOfString:@"#" withString:@""]; -// -// return [UIColor colorWithHexString:hexString]; -//} -// -//- (CGPoint)position { -// if (!self.positionArray) { -// return CGPointZero; -// } -// CGPoint aePosition = CGPointMake([self.positionArray[0] floatValue], [self.positionArray[1] floatValue]); -// if (self.anchorPointArray) { -// aePosition.x -= [self.anchorPointArray[0] floatValue]; -// aePosition.y -= [self.anchorPointArray[1] floatValue]; -// } -// return aePosition; -//} -// -//- (CGPoint)anchorPoint { -// if (!self.anchorPointArray) { -// return CGPointZero; -// } -// CGPoint aeAnchorPoint = CGPointMake([self.anchorPointArray[0] floatValue], [self.anchorPointArray[1] floatValue]); -// CGPoint uikitAnchorPoint = CGPointMake(aeAnchorPoint.x / self.size.width, -// aeAnchorPoint.y / self.size.height); -// return uikitAnchorPoint; -//} -// -//- (CGSize)size { -// if (!self.width && !self.height) { -// return CGSizeZero; -// } -// return CGSizeMake(self.width.floatValue, self.height.floatValue); -//} -// -//- (CGSize)scale { -// if (!self.scaleArray) { -// return CGSizeZero; -// } -// return CGSizeMake([self.scaleArray[0] floatValue] / 100.f, [self.scaleArray[1] floatValue] / 100.f); -//} -// -//- (CGFloat)alpha { -// if (!self.opacity) { -// return 1; -// } -// return self.opacity.floatValue / 100.f; -//} -// -//- (CGRect)frameRect { -// return CGRectMake(self.position.x, self.position.y, self.size.width, self.size.height); -//} -// -//- (CGAffineTransform)transform { -// return CGAffineTransformRotate(CGAffineTransformMakeScale(self.scale.width, self.scale.height), DegreesToRadians(self.rotation ? self.rotation.floatValue : 0.f)); -//} - @end diff --git a/LotteAnimator/LAMask.h b/LotteAnimator/LAMask.h index 3354a82ec7..ccb82a31ee 100644 --- a/LotteAnimator/LAMask.h +++ b/LotteAnimator/LAMask.h @@ -6,13 +6,25 @@ // Copyright © 2015 Brandon Withrow. All rights reserved. // -#import "MTLModel.h" +#import +@class LAAnimatableShapeValue; +@class LAAnimatableNumberValue; -@interface LAMask : MTLModel +typedef enum : NSUInteger { + LAMaskModeAdd, + LAMaskModeSubtract, + LAMaskModeIntersect, + LAMaskModeUnknown +} LAMaskMode; -@property (nonatomic, getter=isClosed) BOOL closed; -@property (nonatomic, getter=isInverted) BOOL inverted; -@property (nonatomic, strong) LAPath *maskPath; -@property (nonatomic, copy) NSNumber *opacity; +@interface LAMask : NSObject + +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate; + +@property (nonatomic, readonly) BOOL closed; +@property (nonatomic, readonly) BOOL inverted; +@property (nonatomic, readonly) LAMaskMode maskMode; +@property (nonatomic, readonly) LAAnimatableShapeValue *maskPath; +@property (nonatomic, readonly) LAAnimatableNumberValue *opacity; @end diff --git a/LotteAnimator/LAMask.m b/LotteAnimator/LAMask.m index 73b74d5143..3d3dc38c56 100644 --- a/LotteAnimator/LAMask.m +++ b/LotteAnimator/LAMask.m @@ -7,27 +7,46 @@ // #import "LAMask.h" +#import "LAAnimatableShapeValue.h" +#import "LAAnimatableNumberValue.h" @implementation LAMask -+ (NSDictionary *)JSONKeyPathsByPropertyKey { - return @{@"closed" : @"cl", - @"inverted" : @"inv", - @"maskPath" : @"pt", - @"opacity" : @"o"}; +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + self = [super init]; + if (self) { + [self _mapFromJSON:jsonDictionary frameRate:frameRate]; + } + return self; } -+ (NSValueTransformer *)maskPathJSONTransformer { - // tell Mantle to populate diaAttributes property with an array of MDAttribute objects - return [MTLJSONAdapter dictionaryTransformerWithModelClass:[LAPath class]]; -} - -+ (NSValueTransformer *)closedJSONTransformer { - return [NSValueTransformer valueTransformerForName:MTLBooleanValueTransformerName]; -} - -+ (NSValueTransformer *)invertedJSONTransformer { - return [NSValueTransformer valueTransformerForName:MTLBooleanValueTransformerName]; +- (void)_mapFromJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + NSNumber *closed = jsonDictionary[@"cl"]; + _closed = closed.boolValue; + + NSNumber *inverted = jsonDictionary[@"inv"]; + _inverted = inverted.boolValue; + + NSString *mode = jsonDictionary[@"mode"]; + if ([mode isEqualToString:@"a"]) { + _maskMode = LAMaskModeAdd; + } else if ([mode isEqualToString:@"s"]) { + _maskMode = LAMaskModeSubtract; + } else if ([mode isEqualToString:@"i"]) { + _maskMode = LAMaskModeIntersect; + } else { + _maskMode = LAMaskModeUnknown; + } + + NSDictionary *maskshape = jsonDictionary[@"pt"]; + if (maskshape) { + _maskPath = [[LAAnimatableShapeValue alloc] initWithShapeValues:maskshape closed:_closed]; + } + + NSDictionary *opacity = jsonDictionary[@"o"]; + if (opacity) { + _opacity = [[LAAnimatableNumberValue alloc] initWithNumberValues:opacity]; + } } @end diff --git a/LotteAnimator/LAModels.h b/LotteAnimator/LAModels.h index 9b6c1408b3..b6741fb122 100644 --- a/LotteAnimator/LAModels.h +++ b/LotteAnimator/LAModels.h @@ -9,12 +9,10 @@ #ifndef LAModels_h #define LAModels_h -#import "LAPath.h" #import "LAMask.h" #import "LAComposition.h" #import "LALayer.h" -#import "LAShape.h" -#import "LAShapeItem.h" +#import "LAShapeGroup.h" #import "LAShapePath.h" #import "LAShapeStroke.h" #import "LAShapeFill.h" diff --git a/LotteAnimator/LAPath.h b/LotteAnimator/LAPath.h deleted file mode 100644 index 31724fb897..0000000000 --- a/LotteAnimator/LAPath.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// LABezierPath.h -// LotteAnimator -// -// Created by Brandon Withrow on 12/14/15. -// Copyright © 2015 Brandon Withrow. All rights reserved. -// - -#import "MTLModel.h" - -@interface LAPath : MTLModel - -@property (nonatomic, strong) NSArray *points; -@property (nonatomic, strong) NSArray *inTangents; -@property (nonatomic, strong) NSArray *outTangents; - -- (UIBezierPath *)bezierPath:(BOOL)closedPath; - -@end diff --git a/LotteAnimator/LAPath.m b/LotteAnimator/LAPath.m deleted file mode 100644 index 435ee56710..0000000000 --- a/LotteAnimator/LAPath.m +++ /dev/null @@ -1,54 +0,0 @@ -// -// LABezierPath.m -// LotteAnimator -// -// Created by Brandon Withrow on 12/14/15. -// Copyright © 2015 Brandon Withrow. All rights reserved. -// - -#import "LAPath.h" - -@implementation LAPath - -+ (NSDictionary *)JSONKeyPathsByPropertyKey { - return @{@"points" : @"v", - @"inTangents" : @"i", - @"outTangents" : @"o"}; -} - -- (UIBezierPath *)bezierPath:(BOOL)closedPath { - if (!self.points.count) { - return nil; - } - UIBezierPath *path = [UIBezierPath bezierPath]; - - [path moveToPoint:[self _vertexAtIndex:0]]; - for (int i = 1; i < self.points.count; i ++) { - [path addCurveToPoint:[self _vertexAtIndex:i] controlPoint1:[self _outTangentAtIndex:i - 1] controlPoint2:[self _inTangentAtIndex:i]]; - } - - if (closedPath) { - [path addCurveToPoint:[self _vertexAtIndex:0] controlPoint1:[self _outTangentAtIndex:self.points.count - 1] controlPoint2:[self _inTangentAtIndex:0]]; - [path closePath]; - } - - return path; -} - -- (CGPoint)_vertexAtIndex:(NSInteger)idx { - NSArray *pointArray = self.points[idx]; - return CGPointMake([pointArray[0] floatValue], [pointArray[1] floatValue]); -} - -- (CGPoint)_outTangentAtIndex:(NSInteger)idx { - NSArray *outArray = self.outTangents[idx]; - CGPoint vertex = [self _vertexAtIndex:idx]; - return CGPointMake([outArray[0] floatValue] + vertex.x, [outArray[1] floatValue] + vertex.y); -} - -- (CGPoint)_inTangentAtIndex:(NSInteger)idx { - NSArray *inArray = self.inTangents[idx]; - CGPoint vertex = [self _vertexAtIndex:idx]; - return CGPointMake([inArray[0] floatValue] + vertex.x, [inArray[1] floatValue] + vertex.y); -} -@end diff --git a/LotteAnimator/LAShape.h b/LotteAnimator/LAShape.h deleted file mode 100644 index 3b91fd6a4b..0000000000 --- a/LotteAnimator/LAShape.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// LAShape.h -// LotteAnimator -// -// Created by Brandon Withrow on 12/14/15. -// Copyright © 2015 Brandon Withrow. All rights reserved. -// - -#import "MTLModel.h" - -@interface LAShape : MTLModel - -@property (nonatomic, copy) NSArray *shapeItems; -// TODO Map these items more efficiently for use in LALayerView -// This is extremely inefficient and unsafe. -@property (nonatomic, readonly) NSArray *paths; -@property (nonatomic, readonly) NSArray *strokes; -@property (nonatomic, readonly) NSArray *fills; -@property (nonatomic, readonly) NSArray *transforms; - -@end diff --git a/LotteAnimator/LAShape.m b/LotteAnimator/LAShape.m deleted file mode 100644 index 956c71b98d..0000000000 --- a/LotteAnimator/LAShape.m +++ /dev/null @@ -1,85 +0,0 @@ -// -// LAShape.m -// LotteAnimator -// -// Created by Brandon Withrow on 12/14/15. -// Copyright © 2015 Brandon Withrow. All rights reserved. -// - -#import "LAShape.h" -#import "LAShapeItem.h" - -@implementation LAShape - -+ (NSDictionary *)JSONKeyPathsByPropertyKey { - return @{@"shapeItems" : @"it"}; -} - -+ (NSValueTransformer *)shapeItemsJSONTransformer { - // tell Mantle to populate diaAttributes property with an array of MDAttribute objects - return [MTLJSONAdapter arrayTransformerWithModelClass:[LAShapeItem class]]; -} - -- (NSArray *)paths { - if (!self.shapeItems) { - return @[]; - } - NSMutableArray *paths = [NSMutableArray array]; - - for (LAShapeItem *item in self.shapeItems) { - if ([item.itemType isEqualToString:LAShapeItemType.Path] || - [item.itemType isEqualToString:LAShapeItemType.Circle] || - [item.itemType isEqualToString:LAShapeItemType.Rectangle]) { - [paths addObject:item]; - } - } - - return paths; -} - -- (NSArray *)strokes { - if (!self.shapeItems) { - return @[]; - } - NSMutableArray *strokes = [NSMutableArray array]; - - for (LAShapeItem *item in self.shapeItems) { - if ([item.itemType isEqualToString:LAShapeItemType.Stroke]) { - [strokes addObject:item]; - } - } - - return strokes; -} - -- (NSArray *)fills { - if (!self.shapeItems) { - return @[]; - } - NSMutableArray *fills = [NSMutableArray array]; - - for (LAShapeItem *item in self.shapeItems) { - if ([item.itemType isEqualToString:LAShapeItemType.Fill]) { - [fills addObject:item]; - } - } - - return fills; -} - -- (NSArray *)transforms { - if (!self.shapeItems) { - return @[]; - } - NSMutableArray *transforms = [NSMutableArray array]; - - for (LAShapeItem *item in self.shapeItems) { - if ([item.itemType isEqualToString:LAShapeItemType.Transform]) { - [transforms addObject:item]; - } - } - - return transforms; -} - -@end diff --git a/LotteAnimator/LAShapeCircle.h b/LotteAnimator/LAShapeCircle.h index 0dfe616a54..8421f50b6d 100644 --- a/LotteAnimator/LAShapeCircle.h +++ b/LotteAnimator/LAShapeCircle.h @@ -6,14 +6,10 @@ // Copyright © 2015 Brandon Withrow. All rights reserved. // -#import "LAShapeItem.h" +#import -@interface LAShapeCircle : LAShapeItem +@interface LAShapeCircle : NSObject -@property (nonatomic, copy) NSArray *positionArray; -@property (nonatomic, copy) NSArray *sizeArray; - -@property (nonatomic, readonly) CGPoint position; -@property (nonatomic, readonly) CGSize size; +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate; @end diff --git a/LotteAnimator/LAShapeCircle.m b/LotteAnimator/LAShapeCircle.m index 4672c34b2b..ef4ec4168f 100644 --- a/LotteAnimator/LAShapeCircle.m +++ b/LotteAnimator/LAShapeCircle.m @@ -10,30 +10,16 @@ @implementation LAShapeCircle -+ (NSDictionary *)JSONKeyPathsByPropertyKey { - return @{@"itemType" : @"ty", - @"positionArray" : @"p", - @"sizeArray" : @"s"}; -} - -- (CGPoint)position { - if (!self.positionArray) { - return CGPointZero; +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + self = [super init]; + if (self) { + [self _mapFromJSON:jsonDictionary frameRate:frameRate]; } - CGPoint aePosition = CGPointMake([self.positionArray[0] floatValue], [self.positionArray[1] floatValue]); - - return aePosition; + return self; } -- (CGSize)size { - if (!self.sizeArray) { - return CGSizeZero; - } - return CGSizeMake([self.sizeArray[0] floatValue], [self.sizeArray[1] floatValue]); +- (void)_mapFromJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + } -- (UIBezierPath *)path { - CGRect circleBounds = CGRectMake(self.size.width * -0.5, self.size.height * -0.5, self.size.width, self.size.height); - return [UIBezierPath bezierPathWithOvalInRect:circleBounds]; -} @end diff --git a/LotteAnimator/LAShapeFill.h b/LotteAnimator/LAShapeFill.h index 72cb5d0fd4..e72eacd4c6 100644 --- a/LotteAnimator/LAShapeFill.h +++ b/LotteAnimator/LAShapeFill.h @@ -6,14 +6,17 @@ // Copyright © 2015 Brandon Withrow. All rights reserved. // -#import "LAShapeItem.h" +#import -@interface LAShapeFill : LAShapeItem +@class LAAnimatableColorValue; +@class LAAnimatableNumberValue; -@property (nonatomic, getter=isFillEnabled) BOOL fillEnabled; -@property (nonatomic, copy) NSArray *colorElements; -@property (nonatomic, copy) NSNumber *opacity; +@interface LAShapeFill : NSObject + +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate; + +@property (nonatomic, readonly) BOOL fillEnabled; +@property (nonatomic, readonly) LAAnimatableColorValue *color; +@property (nonatomic, readonly) LAAnimatableNumberValue *opacity; -@property (nonatomic, readonly) UIColor *color; -@property (nonatomic, readonly) CGFloat alpha; @end diff --git a/LotteAnimator/LAShapeFill.m b/LotteAnimator/LAShapeFill.m index e779f5086f..212d80b97b 100644 --- a/LotteAnimator/LAShapeFill.m +++ b/LotteAnimator/LAShapeFill.m @@ -7,35 +7,32 @@ // #import "LAShapeFill.h" +#import "LAAnimatableNumberValue.h" +#import "LAAnimatableColorValue.h" @implementation LAShapeFill -+ (NSDictionary *)JSONKeyPathsByPropertyKey { - return @{@"itemType" : @"ty", - @"fillEnabled" : @"fillEnabled", - @"colorElements" : @"c", - @"opacity" : @"o"}; -} - -+ (NSValueTransformer *)fillEnabledJSONTransformer { - return [NSValueTransformer valueTransformerForName:MTLBooleanValueTransformerName]; -} - -- (CGFloat)alpha { - if (!self.opacity) { - return 1; +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + self = [super init]; + if (self) { + [self _mapFromJSON:jsonDictionary frameRate:frameRate]; } - return self.opacity.floatValue / 100.f; + return self; } -- (UIColor *)color { - if (!self.colorElements) { - return [UIColor clearColor]; +- (void)_mapFromJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + NSDictionary *color = jsonDictionary[@"c"]; + if (color) { + _color = [[LAAnimatableColorValue alloc] initWithColorValues:color]; } - return [UIColor colorWithRed:([self.colorElements[0] floatValue]/255.f) - green:([self.colorElements[1] floatValue]/255.f) - blue:([self.colorElements[2] floatValue]/255.f) - alpha:([self.colorElements[3] floatValue]/255.f)]; + + NSDictionary *opacity = jsonDictionary[@"o"]; + if (opacity) { + _opacity = [[LAAnimatableNumberValue alloc] initWithNumberValues:opacity]; + } + + NSNumber *fillEnabled = jsonDictionary[@"fillEnabled"]; + _fillEnabled = fillEnabled.boolValue; } @end diff --git a/LotteAnimator/LAShapeGroup.h b/LotteAnimator/LAShapeGroup.h new file mode 100644 index 0000000000..aa7eb4418f --- /dev/null +++ b/LotteAnimator/LAShapeGroup.h @@ -0,0 +1,17 @@ +// +// LAShape.h +// LotteAnimator +// +// Created by Brandon Withrow on 12/14/15. +// Copyright © 2015 Brandon Withrow. All rights reserved. +// + +#import + +@interface LAShapeGroup : NSObject + +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate; + +@property (nonatomic, readonly) NSArray *items; + +@end diff --git a/LotteAnimator/LAShapeGroup.m b/LotteAnimator/LAShapeGroup.m new file mode 100644 index 0000000000..305f5ec73e --- /dev/null +++ b/LotteAnimator/LAShapeGroup.m @@ -0,0 +1,59 @@ +// +// LAShape.m +// LotteAnimator +// +// Created by Brandon Withrow on 12/14/15. +// Copyright © 2015 Brandon Withrow. All rights reserved. +// + +#import "LAShapeGroup.h" +#import "LAShapeFill.h" +#import "LAShapePath.h" +#import "LAShapeCircle.h" +#import "LAShapeStroke.h" +#import "LAShapeTransform.h" +#import "LAShapeRectangle.h" + +@implementation LAShapeGroup + +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + self = [super init]; + if (self) { + [self _mapFromJSON:jsonDictionary frameRate:frameRate]; + } + return self; +} + +- (void)_mapFromJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + NSArray *itemsJSON = jsonDictionary[@"it"]; + NSMutableArray *items = [NSMutableArray array]; + for (NSDictionary *itemJSON in itemsJSON) { + NSString *type = itemJSON[@"ty"]; + + if ([type isEqualToString:@"gr"]) { + LAShapeGroup *group = [[LAShapeGroup alloc] initWithJSON:itemJSON frameRate:frameRate]; + [items addObject:group]; + } else if ([type isEqualToString:@"st"]) { + LAShapeStroke *stroke = [[LAShapeStroke alloc] initWithJSON:itemJSON frameRate:frameRate]; + [items addObject:stroke]; + } else if ([type isEqualToString:@"fl"]) { + LAShapeFill *fill = [[LAShapeFill alloc] initWithJSON:itemJSON frameRate:frameRate]; + [items addObject:fill]; + } else if ([type isEqualToString:@"tr"]) { + LAShapeTransform *transform = [[LAShapeTransform alloc] initWithJSON:itemJSON frameRate:frameRate]; + [items addObject:transform]; + } else if ([type isEqualToString:@"sh"]) { + LAShapePath *path = [[LAShapePath alloc] initWithJSON:itemJSON frameRate:frameRate]; + [items addObject:path]; + } else if ([type isEqualToString:@"el"]) { + LAShapeCircle *circle = [[LAShapeCircle alloc] initWithJSON:itemJSON frameRate:frameRate]; + [items addObject:circle]; + } else if ([type isEqualToString:@"rc"]) { + LAShapeRectangle *rectangle = [[LAShapeRectangle alloc] initWithJSON:itemJSON frameRate:frameRate]; + [items addObject:rectangle]; + } + } + _items = items; +} + +@end diff --git a/LotteAnimator/LAShapeItem.h b/LotteAnimator/LAShapeItem.h deleted file mode 100644 index 338391e04a..0000000000 --- a/LotteAnimator/LAShapeItem.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// LAShapeItem.h -// LotteAnimator -// -// Created by Brandon Withrow on 12/15/15. -// Copyright © 2015 Brandon Withrow. All rights reserved. -// - -#import "MTLModel.h" - -struct LAShapeItemType { - __unsafe_unretained NSString * const Path; - __unsafe_unretained NSString * const Stroke; - __unsafe_unretained NSString * const Fill; - __unsafe_unretained NSString * const Transform; - __unsafe_unretained NSString * const Circle; - __unsafe_unretained NSString * const Rectangle; -}; - -extern const struct LAShapeItemType LAShapeItemType; - -@interface LAShapeItem : MTLModel - -@property (nonatomic, copy) NSString *itemType; - -@property (nonatomic, readonly) UIBezierPath *path; - -@end diff --git a/LotteAnimator/LAShapeItem.m b/LotteAnimator/LAShapeItem.m deleted file mode 100644 index 7728aa76a9..0000000000 --- a/LotteAnimator/LAShapeItem.m +++ /dev/null @@ -1,54 +0,0 @@ -// -// LAShapeItem.m -// LotteAnimator -// -// Created by Brandon Withrow on 12/15/15. -// Copyright © 2015 Brandon Withrow. All rights reserved. -// - -#import "LAShapeItem.h" - -const struct LAShapeItemType LAShapeItemType = { - .Path = @"sh", - .Stroke = @"st", - .Fill = @"fl", - .Transform = @"tr", - .Circle = @"el", - .Rectangle = @"rc" -}; - -@implementation LAShapeItem - -+ (NSDictionary *)JSONKeyPathsByPropertyKey { - return @{@"itemType" : @"ty"}; -} - -+ (Class)classForParsingJSONDictionary:(NSDictionary *)JSONDictionary { - if ([JSONDictionary[@"ty"] isEqual:LAShapeItemType.Path]) { - return LAShapePath.class; - } - - if ([JSONDictionary[@"ty"] isEqual:LAShapeItemType.Stroke]) { - return LAShapeStroke.class; - } - - if ([JSONDictionary[@"ty"] isEqual:LAShapeItemType.Fill]) { - return LAShapeFill.class; - } - - if ([JSONDictionary[@"ty"] isEqual:LAShapeItemType.Transform]) { - return LAShapeTransform.class; - } - - if ([JSONDictionary[@"ty"] isEqual:LAShapeItemType.Circle]) { - return LAShapeCircle.class; - } - - if ([JSONDictionary[@"ty"] isEqual:LAShapeItemType.Rectangle]) { - return LAShapeRectangle.class; - } - - return self; -} - -@end diff --git a/LotteAnimator/LAShapePath.h b/LotteAnimator/LAShapePath.h index 5410460dfe..5d8e8cf77a 100644 --- a/LotteAnimator/LAShapePath.h +++ b/LotteAnimator/LAShapePath.h @@ -6,11 +6,16 @@ // Copyright © 2015 Brandon Withrow. All rights reserved. // -#import "LAShapeItem.h" +#import -@interface LAShapePath : LAShapeItem +@class LAAnimatableShapeValue; -@property (nonatomic, getter=isClosed) BOOL closed; -@property (nonatomic, strong) LAPath *shapePath; +@interface LAShapePath : NSObject + +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate; + +@property (nonatomic, readonly) BOOL closed; +@property (nonatomic, readonly) NSNumber *index; +@property (nonatomic, readonly) LAAnimatableShapeValue *shapePath; @end diff --git a/LotteAnimator/LAShapePath.m b/LotteAnimator/LAShapePath.m index 8b811ff2c5..942fb69ae5 100644 --- a/LotteAnimator/LAShapePath.m +++ b/LotteAnimator/LAShapePath.m @@ -7,25 +7,25 @@ // #import "LAShapePath.h" +#import "LAAnimatableShapeValue.h" @implementation LAShapePath -+ (NSDictionary *)JSONKeyPathsByPropertyKey { - return @{@"itemType" : @"ty", - @"closed" : @"closed", - @"shapePath" : @"ks"}; +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + self = [super init]; + if (self) { + [self _mapFromJSON:jsonDictionary frameRate:frameRate]; + } + return self; } -+ (NSValueTransformer *)shapePathJSONTransformer { - // tell Mantle to populate diaAttributes property with an array of MDAttribute objects - return [MTLJSONAdapter dictionaryTransformerWithModelClass:[LAPath class]]; +- (void)_mapFromJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + _index = jsonDictionary[@"ind"]; + _closed = [jsonDictionary[@"closed"] boolValue]; + NSDictionary *shape = jsonDictionary[@"ks"]; + if (shape) { + _shapePath = [[LAAnimatableShapeValue alloc] initWithShapeValues:shape closed:_closed]; + } } -+ (NSValueTransformer *)closedJSONTransformer { - return [NSValueTransformer valueTransformerForName:MTLBooleanValueTransformerName]; -} - -- (UIBezierPath *)path { - return [self.shapePath bezierPath:self.isClosed]; -} @end diff --git a/LotteAnimator/LAShapeRectangle.h b/LotteAnimator/LAShapeRectangle.h index 1c4d69bfd4..2298e9f667 100644 --- a/LotteAnimator/LAShapeRectangle.h +++ b/LotteAnimator/LAShapeRectangle.h @@ -6,15 +6,10 @@ // Copyright © 2015 Brandon Withrow. All rights reserved. // -#import "LAShapeItem.h" +#import -@interface LAShapeRectangle : LAShapeItem +@interface LAShapeRectangle : NSObject -@property (nonatomic, copy) NSArray *positionArray; -@property (nonatomic, copy) NSArray *sizeArray; -@property (nonatomic, copy) NSNumber *cornerRadius; - -@property (nonatomic, readonly) CGPoint position; -@property (nonatomic, readonly) CGSize size; +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate; @end diff --git a/LotteAnimator/LAShapeRectangle.m b/LotteAnimator/LAShapeRectangle.m index 14723985a5..4ac342e836 100644 --- a/LotteAnimator/LAShapeRectangle.m +++ b/LotteAnimator/LAShapeRectangle.m @@ -10,38 +10,16 @@ @implementation LAShapeRectangle -+ (NSDictionary *)JSONKeyPathsByPropertyKey { - return @{@"itemType" : @"ty", - @"positionArray" : @"p", - @"sizeArray" : @"s", - @"cornerRadius" : @"r"}; +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + self = [super init]; + if (self) { + [self _mapFromJSON:jsonDictionary frameRate:frameRate]; + } + return self; } -- (CGPoint)position { - if (!self.positionArray) { - return CGPointZero; - } - CGPoint aePosition = CGPointMake([self.positionArray[0] floatValue], [self.positionArray[1] floatValue]); +- (void)_mapFromJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { - return aePosition; -} - -- (CGSize)size { - if (!self.sizeArray) { - return CGSizeZero; - } - return CGSizeMake([self.sizeArray[0] floatValue], [self.sizeArray[1] floatValue]); -} - -- (UIBezierPath *)path { - CGRect rectBounds = CGRectMake(self.size.width * -0.5, self.size.height * -0.5, self.size.width, self.size.height); - UIBezierPath *path; - if (self.cornerRadius && self.cornerRadius.floatValue > 0.0) { - path = [UIBezierPath bezierPathWithRoundedRect:rectBounds cornerRadius:self.cornerRadius.floatValue]; - } else { - path = [UIBezierPath bezierPathWithRect:rectBounds]; - } - return path; } @end diff --git a/LotteAnimator/LAShapeStroke.h b/LotteAnimator/LAShapeStroke.h index 49d01f4de7..f795df4cae 100644 --- a/LotteAnimator/LAShapeStroke.h +++ b/LotteAnimator/LAShapeStroke.h @@ -6,16 +6,18 @@ // Copyright © 2015 Brandon Withrow. All rights reserved. // -#import "LAShapeItem.h" +#import -@interface LAShapeStroke : LAShapeItem +@class LAAnimatableColorValue; +@class LAAnimatableNumberValue; -@property (nonatomic, getter=isFillEnabled) BOOL fillEnabled; -@property (nonatomic, copy) NSArray *colorElements; -@property (nonatomic, copy) NSNumber *opacity; -@property (nonatomic, copy) NSNumber *width; +@interface LAShapeStroke : NSObject -@property (nonatomic, readonly) UIColor *color; -@property (nonatomic, readonly) CGFloat alpha; +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate; + +@property (nonatomic, readonly) BOOL fillEnabled; +@property (nonatomic, readonly) LAAnimatableColorValue *color; +@property (nonatomic, readonly) LAAnimatableNumberValue *opacity; +@property (nonatomic, readonly) LAAnimatableNumberValue *width; @end diff --git a/LotteAnimator/LAShapeStroke.m b/LotteAnimator/LAShapeStroke.m index d0ddb31c87..30a8815e53 100644 --- a/LotteAnimator/LAShapeStroke.m +++ b/LotteAnimator/LAShapeStroke.m @@ -7,36 +7,37 @@ // #import "LAShapeStroke.h" +#import "LAAnimatableNumberValue.h" +#import "LAAnimatableColorValue.h" @implementation LAShapeStroke -+ (NSDictionary *)JSONKeyPathsByPropertyKey { - return @{@"itemType" : @"ty", - @"fillEnabled" : @"fillEnabled", - @"colorElements" : @"c", - @"opacity" : @"o", - @"width" : @"w"}; -} - -+ (NSValueTransformer *)fillEnabledJSONTransformer { - return [NSValueTransformer valueTransformerForName:MTLBooleanValueTransformerName]; -} - -- (CGFloat)alpha { - if (!self.opacity) { - return 1; +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + self = [super init]; + if (self) { + [self _mapFromJSON:jsonDictionary frameRate:frameRate]; } - return self.opacity.floatValue / 100.f; + return self; } -- (UIColor *)color { - if (!self.colorElements) { - return [UIColor clearColor]; +- (void)_mapFromJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + NSDictionary *color = jsonDictionary[@"c"]; + if (color) { + _color = [[LAAnimatableColorValue alloc] initWithColorValues:color]; } - return [UIColor colorWithRed:([self.colorElements[0] floatValue]/255.f) - green:([self.colorElements[1] floatValue]/255.f) - blue:([self.colorElements[2] floatValue]/255.f) - alpha:([self.colorElements[3] floatValue]/255.f)]; + + NSDictionary *width = jsonDictionary[@"w"]; + if (width) { + _width = [[LAAnimatableNumberValue alloc] initWithNumberValues:width]; + } + + NSDictionary *opacity = jsonDictionary[@"o"]; + if (opacity) { + _opacity = [[LAAnimatableNumberValue alloc] initWithNumberValues:opacity]; + } + + NSNumber *fillEnabled = jsonDictionary[@"fillEnabled"]; + _fillEnabled = fillEnabled.boolValue; } @end diff --git a/LotteAnimator/LAShapeTransform.h b/LotteAnimator/LAShapeTransform.h index 60a5393220..b217aaaff6 100644 --- a/LotteAnimator/LAShapeTransform.h +++ b/LotteAnimator/LAShapeTransform.h @@ -6,20 +6,20 @@ // Copyright © 2015 Brandon Withrow. All rights reserved. // -#import "LAShapeItem.h" +#import -@interface LAShapeTransform : LAShapeItem +@class LAAnimatableNumberValue; +@class LAAnimatablePointValue; +@class LAAnimatableScaleValue; -@property (nonatomic, copy) NSArray *positionArray; -@property (nonatomic, copy) NSArray *anchorPointArray; -@property (nonatomic, copy) NSArray *scaleArray; -@property (nonatomic, copy) NSNumber *rotation; -@property (nonatomic, copy) NSNumber *opacity; +@interface LAShapeTransform : NSObject -@property (nonatomic, readonly) CGPoint position; -@property (nonatomic, readonly) CGPoint anchorPoint; -@property (nonatomic, readonly) CGSize scale; -@property (nonatomic, readonly) CGFloat alpha; -@property (nonatomic, readonly) CGAffineTransform transform; +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate; + +@property (nonatomic, readonly) LAAnimatablePointValue *position; +@property (nonatomic, readonly) LAAnimatablePointValue *anchor; +@property (nonatomic, readonly) LAAnimatableScaleValue *scale; +@property (nonatomic, readonly) LAAnimatableNumberValue *rotation; +@property (nonatomic, readonly) LAAnimatableNumberValue *opacity; @end diff --git a/LotteAnimator/LAShapeTransform.m b/LotteAnimator/LAShapeTransform.m index ad347f2cc3..b0f33c549a 100644 --- a/LotteAnimator/LAShapeTransform.m +++ b/LotteAnimator/LAShapeTransform.m @@ -7,61 +7,45 @@ // #import "LAShapeTransform.h" +#import "LAAnimatableNumberValue.h" +#import "LAAnimatablePointValue.h" +#import "LAAnimatableScaleValue.h" @implementation LAShapeTransform -+ (NSDictionary *)JSONKeyPathsByPropertyKey { - return @{@"itemType" : @"ty", - @"positionArray" : @"p", - @"anchorPointArray" : @"a", - @"scaleArray" : @"s", - @"rotation" : @"r", - @"opacity" : @"o"}; +- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + self = [super init]; + if (self) { + [self _mapFromJSON:jsonDictionary frameRate:frameRate]; + } + return self; } -- (CGPoint)position { - if (!self.positionArray) { - return CGPointZero; +- (void)_mapFromJSON:(NSDictionary *)jsonDictionary frameRate:(NSNumber *)frameRate { + NSDictionary *position = jsonDictionary[@"p"]; + if (position) { + _position = [[LAAnimatablePointValue alloc] initWithPointValues:position]; } - CGPoint aePosition = CGPointMake([self.positionArray[0] floatValue], [self.positionArray[1] floatValue]); - if (self.anchorPointArray) { - aePosition.x -= [self.anchorPointArray[0] floatValue]; - aePosition.y -= [self.anchorPointArray[1] floatValue]; + + NSDictionary *anchor = jsonDictionary[@"a"]; + if (anchor) { + _anchor = [[LAAnimatablePointValue alloc] initWithPointValues:anchor]; } - return aePosition; -} - -- (CGPoint)anchorPoint { - if (!self.anchorPointArray) { - return CGPointZero; + + NSDictionary *scale = jsonDictionary[@"s"]; + if (scale) { + _scale = [[LAAnimatableScaleValue alloc] initWithScaleValues:scale]; } - CGPoint aeAnchorPoint = CGPointMake([self.anchorPointArray[0] floatValue], [self.anchorPointArray[1] floatValue]); -// CGPoint uikitAnchorPoint = CGPointMake(aeAnchorPoint.x / self.size.width, -// aeAnchorPoint.y / self.size.height); - // TODO Figure out this crazy thing - return aeAnchorPoint; -} - -// TODO Permanently Unwrap these arrays for efficency -- (CGSize)scale { - if (!self.scaleArray) { - return CGSizeZero; + + NSDictionary *rotation = jsonDictionary[@"r"]; + if (rotation) { + _rotation = [[LAAnimatableNumberValue alloc] initWithNumberValues:rotation]; } - return CGSizeMake([self.scaleArray[0] floatValue] / 100.f, [self.scaleArray[1] floatValue] / 100.f); -} - -- (CGFloat)alpha { - if (!self.opacity) { - return 1; + + NSDictionary *opacity = jsonDictionary[@"o"]; + if (opacity) { + _opacity = [[LAAnimatableNumberValue alloc] initWithNumberValues:opacity]; } - return self.opacity.floatValue / 100.f; -} - -- (CGAffineTransform)transform { - CGAffineTransform translate = CGAffineTransformMakeTranslation(self.position.x, self.position.y); - CGAffineTransform scale = CGAffineTransformScale(translate, self.scale.width, self.scale.height); - CGAffineTransform rotate = CGAffineTransformRotate(scale, DegreesToRadians(self.rotation ? self.rotation.floatValue : 0.f)); - return rotate; } @end diff --git a/LotteAnimator/ViewController.m b/LotteAnimator/ViewController.m index b91dd7c35c..f34ceb1831 100644 --- a/LotteAnimator/ViewController.m +++ b/LotteAnimator/ViewController.m @@ -33,7 +33,7 @@ NSData *jsonData = [[NSData alloc] initWithContentsOfFile:filePath]; NSDictionary *JSONObject = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error]; - LAComposition *laScene = [MTLJSONAdapter modelOfClass:[LAComposition class] fromJSONDictionary:JSONObject error:&error]; + LAComposition *laScene = [[LAComposition alloc] initWithJSON:JSONObject]; }