diff --git a/Lottie.xcodeproj/project.pbxproj b/Lottie.xcodeproj/project.pbxproj index 36ff4d2820..9488921c3e 100644 --- a/Lottie.xcodeproj/project.pbxproj +++ b/Lottie.xcodeproj/project.pbxproj @@ -150,6 +150,8 @@ 622F77121F2BF6AA00269858 /* LOTComposition.m in Sources */ = {isa = PBXBuildFile; fileRef = 622F77101F2BF6AA00269858 /* LOTComposition.m */; }; 622F77131F2BF6AA00269858 /* LOTComposition.m in Sources */ = {isa = PBXBuildFile; fileRef = 622F77101F2BF6AA00269858 /* LOTComposition.m */; }; 622F77141F2BF6BE00269858 /* LOTComposition.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 622F770D1F2BF6A000269858 /* LOTComposition.h */; }; + 6257F31B1FFD61A100DAE7B2 /* UIBezierPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 6257F3191FFD61A100DAE7B2 /* UIBezierPath.h */; }; + 6257F31C1FFD61A100DAE7B2 /* UIBezierPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 6257F31A1FFD61A100DAE7B2 /* UIBezierPath.m */; }; 6274CEB91F157DCD00E05049 /* LOTNumberInterpolator.h in Headers */ = {isa = PBXBuildFile; fileRef = 6274CEB71F157DCD00E05049 /* LOTNumberInterpolator.h */; }; 6274CEBA1F157DCD00E05049 /* LOTNumberInterpolator.h in Headers */ = {isa = PBXBuildFile; fileRef = 6274CEB71F157DCD00E05049 /* LOTNumberInterpolator.h */; }; 6274CEBB1F157DCD00E05049 /* LOTNumberInterpolator.m in Sources */ = {isa = PBXBuildFile; fileRef = 6274CEB81F157DCD00E05049 /* LOTNumberInterpolator.m */; }; @@ -395,6 +397,8 @@ 622F766A1F2BCE1300269858 /* LOTRepeaterRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LOTRepeaterRenderer.m; path = "lottie-ios/Classes/RenderSystem/RenderNodes/LOTRepeaterRenderer.m"; sourceTree = SOURCE_ROOT; }; 622F770D1F2BF6A000269858 /* LOTComposition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LOTComposition.h; sourceTree = ""; }; 622F77101F2BF6AA00269858 /* LOTComposition.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LOTComposition.m; sourceTree = ""; }; + 6257F3191FFD61A100DAE7B2 /* UIBezierPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIBezierPath.h; sourceTree = ""; }; + 6257F31A1FFD61A100DAE7B2 /* UIBezierPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIBezierPath.m; sourceTree = ""; }; 6274CEB71F157DCD00E05049 /* LOTNumberInterpolator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LOTNumberInterpolator.h; path = "lottie-ios/Classes/RenderSystem/InterpolatorNodes/LOTNumberInterpolator.h"; sourceTree = SOURCE_ROOT; }; 6274CEB81F157DCD00E05049 /* LOTNumberInterpolator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LOTNumberInterpolator.m; path = "lottie-ios/Classes/RenderSystem/InterpolatorNodes/LOTNumberInterpolator.m"; sourceTree = SOURCE_ROOT; }; 6274CF891F16F29200E05049 /* LOTPointInterpolator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LOTPointInterpolator.h; path = "lottie-ios/Classes/RenderSystem/InterpolatorNodes/LOTPointInterpolator.h"; sourceTree = SOURCE_ROOT; }; @@ -587,6 +591,8 @@ 481A4AD71E4A78A0003CF62B /* MacCompatability */ = { isa = PBXGroup; children = ( + 6257F3191FFD61A100DAE7B2 /* UIBezierPath.h */, + 6257F31A1FFD61A100DAE7B2 /* UIBezierPath.m */, 481A4ADA1E4A78A0003CF62B /* CALayer+Compat.h */, 481A4ADB1E4A78A0003CF62B /* CALayer+Compat.m */, 481A4ADC1E4A78A0003CF62B /* LOTPlatformCompat.h */, @@ -843,6 +849,7 @@ 481A4AD61E4A7885003CF62B /* LOTAnimationView_Internal.h in Headers */, FA1F5AA11E42B52800FF36BF /* LOTAnimationView.h in Headers */, 6279981D1FE1D99F00B2DDD9 /* LOTKeypath.h in Headers */, + 6257F31B1FFD61A100DAE7B2 /* UIBezierPath.h in Headers */, 62BFC2EE1F14298D0068A342 /* LOTPathAnimator.h in Headers */, 62E27B471F31158B0098420E /* LOTAnimationCache.h in Headers */, 62BFC2DF1F14298D0068A342 /* LOTAnimatorNode.h in Headers */, @@ -1135,6 +1142,7 @@ 62BFC2F51F14298D0068A342 /* LOTRenderGroup.m in Sources */, 481A4AD41E4A7885003CF62B /* LOTAnimationView.m in Sources */, 622F762F1F2A8CBA00269858 /* LOTShapeStar.m in Sources */, + 6257F31C1FFD61A100DAE7B2 /* UIBezierPath.m in Sources */, 6274CFAB1F17E98200E05049 /* LOTColorInterpolator.m in Sources */, 6274CFA31F17E94C00E05049 /* LOTPathInterpolator.m in Sources */, 481A4AC81E4A7885003CF62B /* LOTShapeStroke.m in Sources */, diff --git a/lottie-ios/Classes/Extensions/CGGeometry+LOTAdditions.h b/lottie-ios/Classes/Extensions/CGGeometry+LOTAdditions.h index 57896f7a18..28212235c2 100644 --- a/lottie-ios/Classes/Extensions/CGGeometry+LOTAdditions.h +++ b/lottie-ios/Classes/Extensions/CGGeometry+LOTAdditions.h @@ -2,8 +2,7 @@ #import "LOTPlatformCompat.h" #import -#import -#import + // // Core Graphics Geometry Additions // @@ -82,12 +81,6 @@ CGRect LOT_RectSetHeight(CGRect rect, CGFloat height); CGFloat LOT_PointDistanceFromPoint(CGPoint point1, CGPoint point2); CGFloat LOT_DegreesToRadians(CGFloat degrees); -GLKMatrix4 LOT_GLKMatrix4FromCATransform(CATransform3D xform); - -CATransform3D LOT_CATransform3DFromGLKMatrix4(GLKMatrix4 xform); - -CATransform3D LOT_CATransform3DSlerpToTransform(CATransform3D fromXorm, CATransform3D toXform, CGFloat amount ); - CGFloat LOT_RemapValue(CGFloat value, CGFloat low1, CGFloat high1, CGFloat low2, CGFloat high2 ); CGPoint LOT_PointByLerpingPoints(CGPoint point1, CGPoint point2, CGFloat value); diff --git a/lottie-ios/Classes/Extensions/CGGeometry+LOTAdditions.m b/lottie-ios/Classes/Extensions/CGGeometry+LOTAdditions.m index 2577749966..61aa460dd5 100644 --- a/lottie-ios/Classes/Extensions/CGGeometry+LOTAdditions.m +++ b/lottie-ios/Classes/Extensions/CGGeometry+LOTAdditions.m @@ -319,55 +319,6 @@ CGFloat LOT_DegreesToRadians(CGFloat degrees) { return degrees * M_PI / 180; } -GLKMatrix4 LOT_GLKMatrix4FromCATransform(CATransform3D xform) { - return GLKMatrix4Make(xform.m11, xform.m12, xform.m13, xform.m14, - xform.m21, xform.m22, xform.m23, xform.m24, - xform.m31, xform.m32, xform.m33, xform.m34, - xform.m41, xform.m42, xform.m43, xform.m44); -} - -CATransform3D LOT_CATransform3DFromGLKMatrix4(GLKMatrix4 xform) { - CATransform3D newXform; - newXform.m11 = xform.m00; - newXform.m12 = xform.m01; - newXform.m13 = xform.m02; - newXform.m14 = xform.m03; - newXform.m21 = xform.m10; - newXform.m22 = xform.m11; - newXform.m23 = xform.m12; - newXform.m24 = xform.m13; - newXform.m31 = xform.m20; - newXform.m32 = xform.m21; - newXform.m33 = xform.m22; - newXform.m34 = xform.m23; - newXform.m41 = xform.m30; - newXform.m42 = xform.m31; - newXform.m43 = xform.m32; - newXform.m44 = xform.m33; - return newXform; -} - -CATransform3D LOT_CATransform3DSlerpToTransform(CATransform3D fromXorm, CATransform3D toXform, CGFloat amount ) { - // amount = MIN(MAX(0, amount), 1); - if (amount == 0 || amount == 1) { - return amount == 0 ? fromXorm : toXform; - } - GLKMatrix4 xform1 = LOT_GLKMatrix4FromCATransform(fromXorm); - GLKMatrix4 xform2 = LOT_GLKMatrix4FromCATransform(toXform); - GLKQuaternion q1 = GLKQuaternionMakeWithMatrix4(xform1); - GLKQuaternion q2 = GLKQuaternionMakeWithMatrix4(xform2); - GLKQuaternion r1 = GLKQuaternionSlerp(q1, q2, amount); - GLKVector4 t1 = GLKVector4Make(xform1.m30, xform1.m31, xform1.m32, xform1.m33); - GLKVector4 t2 = GLKVector4Make(xform2.m30, xform2.m31, xform2.m32, xform2.m33); - GLKVector4 r2 = GLKVector4Lerp(t1, t2, amount); - - GLKMatrix4 rX = GLKMatrix4MakeWithQuaternion(r1); - rX.m30 = r2.x; - rX.m31 = r2.y; - rX.m32 = r2.z; - return LOT_CATransform3DFromGLKMatrix4(rX); -} - CGFloat LOT_PointDistanceFromPoint(CGPoint point1, CGPoint point2) { CGFloat xDist = (point2.x - point1.x); CGFloat yDist = (point2.y - point1.y); diff --git a/lottie-ios/Classes/MacCompatability/LOTPlatformCompat.h b/lottie-ios/Classes/MacCompatability/LOTPlatformCompat.h index 6a6a1e52e2..afa09c26c0 100644 --- a/lottie-ios/Classes/MacCompatability/LOTPlatformCompat.h +++ b/lottie-ios/Classes/MacCompatability/LOTPlatformCompat.h @@ -21,6 +21,7 @@ #import "UIColor.h" #import "CALayer+Compat.h" #import "NSValue+Compat.h" +#import "UIBezierPath.h" NS_INLINE NSString *NSStringFromCGRect(CGRect rect) { return NSStringFromRect(rect); diff --git a/lottie-ios/Classes/MacCompatability/UIBezierPath.h b/lottie-ios/Classes/MacCompatability/UIBezierPath.h new file mode 100755 index 0000000000..424e5de746 --- /dev/null +++ b/lottie-ios/Classes/MacCompatability/UIBezierPath.h @@ -0,0 +1,78 @@ +// Kindly stolen from https://github.com/BigZaphod/Chameleon +/* + * Copyright (c) 2011, The Iconfactory. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of The Iconfactory nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE ICONFACTORY BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR +#import +#import + +typedef NS_OPTIONS(NSUInteger, UIRectCorner) { + UIRectCornerTopLeft = 1 << 0, + UIRectCornerTopRight = 1 << 1, + UIRectCornerBottomLeft = 1 << 2, + UIRectCornerBottomRight = 1 << 3, + UIRectCornerAllCorners = UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight +}; + +@interface UIBezierPath : NSObject + ++ (UIBezierPath *)bezierPath; ++ (UIBezierPath *)bezierPathWithRect:(CGRect)rect; ++ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect; ++ (UIBezierPath *)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius; ++ (UIBezierPath *)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii; ++ (UIBezierPath *)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise; ++ (UIBezierPath *)bezierPathWithCGPath:(CGPathRef)CGPath; + +- (void)moveToPoint:(CGPoint)point; +- (void)addLineToPoint:(CGPoint)point; +- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise; +- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2; +- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint; +- (void)closePath; +- (void)removeAllPoints; +- (void)appendPath:(UIBezierPath *)bezierPath; +- (void)setLineDash:(const CGFloat *)pattern count:(NSInteger)count phase:(CGFloat)phase; +- (void)getLineDash:(CGFloat *)pattern count:(NSInteger *)count phase:(CGFloat *)phase; +- (BOOL)containsPoint:(CGPoint)point; +- (void)applyTransform:(CGAffineTransform)transform; + +@property (nonatomic) CGPathRef CGPath; +@property (nonatomic, readonly) CGPoint currentPoint; +@property (nonatomic) CGFloat lineWidth; +@property (nonatomic) CGLineCap lineCapStyle; +@property (nonatomic) CGLineJoin lineJoinStyle; +@property (nonatomic) CGFloat miterLimit; +@property (nonatomic) CGFloat flatness; +@property (nonatomic) BOOL usesEvenOddFillRule; +@property (readonly, getter=isEmpty) BOOL empty; +@property (nonatomic, readonly) CGRect bounds; +@end + +#endif diff --git a/lottie-ios/Classes/MacCompatability/UIBezierPath.m b/lottie-ios/Classes/MacCompatability/UIBezierPath.m new file mode 100755 index 0000000000..f764d70f33 --- /dev/null +++ b/lottie-ios/Classes/MacCompatability/UIBezierPath.m @@ -0,0 +1,310 @@ +// Kindly stolen from https://github.com/BigZaphod/Chameleon +/* + * Copyright (c) 2011, The Iconfactory. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of The Iconfactory nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE ICONFACTORY BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR +#import "UIBezierPath.h" + +@implementation UIBezierPath { + CGFloat *_lineDashPattern; + NSInteger _lineDashCount; + CGFloat _lineDashPhase; +} +@synthesize CGPath = _path; + +- (id)init { + self = [super init]; + if (self) { + _path = CGPathCreateMutable(); + _lineWidth = 1; + _lineCapStyle = kCGLineCapButt; + _lineJoinStyle = kCGLineJoinMiter; + _miterLimit = 10; + _flatness = 0.6; + _usesEvenOddFillRule = NO; + _lineDashPattern = NULL; + _lineDashCount = 0; + _lineDashPhase = 0; + } + return self; +} + +- (void)dealloc { + if (_path) CGPathRelease(_path); +} + +- (id)copyWithZone:(NSZone *)zone { + UIBezierPath *copy = [[self class] new]; + + copy.CGPath = self.CGPath; + copy.lineWidth = self.lineWidth; + copy.lineCapStyle = self.lineCapStyle; + copy.lineJoinStyle = self.lineJoinStyle; + copy.miterLimit = self.miterLimit; + copy.flatness = self.flatness; + copy.usesEvenOddFillRule = self.usesEvenOddFillRule; + + NSInteger lineDashCount = 0; + [self getLineDash:NULL count:&lineDashCount phase:NULL]; + + if (lineDashCount > 0) { + CGFloat *lineDashPattern = malloc(sizeof(CGFloat) * lineDashCount); + CGFloat lineDashPhase = 0; + [self getLineDash:lineDashPattern count:NULL phase:&lineDashPhase]; + [copy setLineDash:lineDashPattern count:lineDashCount phase:lineDashPhase]; + free(lineDashPattern); + } + + return copy; +} + ++ (UIBezierPath *)bezierPathWithCGPath:(CGPathRef)CGPath { + NSAssert(CGPath != NULL, @"CGPath must not be NULL"); + UIBezierPath *bezierPath = [[self alloc] init]; + bezierPath.CGPath = CGPath; + return bezierPath; +} + ++ (UIBezierPath *)bezierPath { + UIBezierPath *bezierPath = [[self alloc] init]; + return bezierPath; +} + ++ (UIBezierPath *)bezierPathWithRect:(CGRect)rect { + CGMutablePathRef path = CGPathCreateMutable(); + CGPathAddRect(path, NULL, rect); + + UIBezierPath *bezierPath = [[self alloc] init]; + bezierPath->_path = path; + return bezierPath; +} + ++ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect { + CGMutablePathRef path = CGPathCreateMutable(); + CGPathAddEllipseInRect(path, NULL, rect); + + UIBezierPath *bezierPath = [[self alloc] init]; + bezierPath->_path = path; + return bezierPath; +} + ++ (UIBezierPath *)bezierPathWithRoundedRect:(CGRect)rect + cornerRadius:(CGFloat)cornerRadius { + return [self bezierPathWithRoundedRect:rect + byRoundingCorners:UIRectCornerAllCorners + cornerRadii:CGSizeMake(cornerRadius, cornerRadius)]; +} + ++ (UIBezierPath *)bezierPathWithRoundedRect:(CGRect)rect + byRoundingCorners:(UIRectCorner)corners + cornerRadii:(CGSize)cornerRadii { + + CGMutablePathRef path = CGPathCreateMutable(); + + const CGPoint topLeft = rect.origin; + const CGPoint topRight = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect)); + const CGPoint bottomRight = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect)); + const CGPoint bottomLeft = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect)); + + if (corners & UIRectCornerTopLeft) { + CGPathMoveToPoint(path, NULL, topLeft.x + cornerRadii.width, topLeft.y); + } else { + CGPathMoveToPoint(path, NULL, topLeft.x, topLeft.y); + } + + if (corners & UIRectCornerTopRight) { + CGPathAddLineToPoint(path, NULL, topRight.x - cornerRadii.width, topRight.y); + CGPathAddCurveToPoint(path, NULL, topRight.x, topRight.y, topRight.x, topRight.y + cornerRadii.height, topRight.x, topRight.y + cornerRadii.height); + } else { + CGPathAddLineToPoint(path, NULL, topRight.x, topRight.y); + } + + if (corners & UIRectCornerBottomRight) { + CGPathAddLineToPoint(path, NULL, bottomRight.x, bottomRight.y - cornerRadii.height); + CGPathAddCurveToPoint(path, NULL, bottomRight.x, bottomRight.y, bottomRight.x - cornerRadii.width, bottomRight.y, bottomRight.x - cornerRadii.width, bottomRight.y); + } else { + CGPathAddLineToPoint(path, NULL, bottomRight.x, bottomRight.y); + } + + if (corners & UIRectCornerBottomLeft) { + CGPathAddLineToPoint(path, NULL, bottomLeft.x + cornerRadii.width, bottomLeft.y); + CGPathAddCurveToPoint(path, NULL, bottomLeft.x, bottomLeft.y, bottomLeft.x, bottomLeft.y - cornerRadii.height, bottomLeft.x, bottomLeft.y - cornerRadii.height); + } else { + CGPathAddLineToPoint(path, NULL, bottomLeft.x, bottomLeft.y); + } + + if (corners & UIRectCornerTopLeft) { + CGPathAddLineToPoint(path, NULL, topLeft.x, topLeft.y + cornerRadii.height); + CGPathAddCurveToPoint(path, NULL, topLeft.x, topLeft.y, topLeft.x + cornerRadii.width, topLeft.y, topLeft.x + cornerRadii.width, topLeft.y); + } else { + CGPathAddLineToPoint(path, NULL, topLeft.x, topLeft.y); + } + + CGPathCloseSubpath(path); + + UIBezierPath *bezierPath = [[self alloc] init]; + bezierPath->_path = path; + return bezierPath; +} + ++ (UIBezierPath *)bezierPathWithArcCenter:(CGPoint)center + radius:(CGFloat)radius + startAngle:(CGFloat)startAngle + endAngle:(CGFloat)endAngle + clockwise:(BOOL)clockwise { + + CGMutablePathRef path = CGPathCreateMutable(); + CGPathAddArc(path, NULL, center.x, center.y, radius, startAngle, endAngle, clockwise); + + UIBezierPath *bezierPath = [[self alloc] init]; + bezierPath->_path = path; + return bezierPath; +} + +- (void)moveToPoint:(CGPoint)point { + CGMutablePathRef mutablePath = CGPathCreateMutableCopy(_path); + CGPathMoveToPoint(mutablePath, NULL, point.x, point.y); + self.CGPath = mutablePath; + CGPathRelease(mutablePath); +} + +- (void)addLineToPoint:(CGPoint)point { + CGMutablePathRef mutablePath = CGPathCreateMutableCopy(_path); + CGPathAddLineToPoint(mutablePath, NULL, point.x, point.y); + self.CGPath = mutablePath; + CGPathRelease(mutablePath); +} + +- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise { + CGMutablePathRef mutablePath = CGPathCreateMutableCopy(_path); + CGPathAddArc(mutablePath, NULL, center.x, center.y, radius, startAngle, endAngle, clockwise); + self.CGPath = mutablePath; + CGPathRelease(mutablePath); +} + +- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2 { + CGMutablePathRef mutablePath = CGPathCreateMutableCopy(_path); + CGPathAddCurveToPoint(mutablePath, NULL, controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, endPoint.x, endPoint.y); + self.CGPath = mutablePath; + CGPathRelease(mutablePath); +} + +- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint { + CGMutablePathRef mutablePath = CGPathCreateMutableCopy(_path); + CGPathAddQuadCurveToPoint(mutablePath, NULL, controlPoint.x, controlPoint.y, endPoint.x, endPoint.y); + self.CGPath = mutablePath; + CGPathRelease(mutablePath); +} + +- (void)closePath { + CGMutablePathRef mutablePath = CGPathCreateMutableCopy(_path); + CGPathCloseSubpath(mutablePath); + self.CGPath = mutablePath; + CGPathRelease(mutablePath); +} + +- (void)removeAllPoints { + CGMutablePathRef mutablePath = CGPathCreateMutable(); + self.CGPath = mutablePath; + CGPathRelease(mutablePath); +} + +- (void)appendPath:(UIBezierPath *)bezierPath { + if (bezierPath) { + CGMutablePathRef mutablePath = CGPathCreateMutableCopy(_path); + CGPathAddPath(mutablePath, NULL, bezierPath.CGPath); + self.CGPath = mutablePath; + CGPathRelease(mutablePath); + } +} + +- (void)setCGPath:(CGPathRef)path { + NSAssert(path != NULL, @"path must not be NULL"); + if (path != _path) { + if (_path) CGPathRelease(_path); + _path = CGPathCreateCopy(path); + } +} + +- (CGPoint)currentPoint { + return CGPathGetCurrentPoint(_path); +} + +- (void)setLineDash:(const CGFloat *)pattern count:(NSInteger)count phase:(CGFloat)phase { + free(_lineDashPattern); + + if (pattern && count > 0) { + const size_t size = sizeof(CGFloat) * count; + _lineDashPattern = malloc(size); + bcopy(pattern, _lineDashPattern, size); + } else { + _lineDashPattern = NULL; + } + + _lineDashCount = count; + _lineDashPhase = phase; +} + +- (void)getLineDash:(CGFloat *)pattern count:(NSInteger *)count phase:(CGFloat *)phase { + if (pattern && _lineDashPattern && _lineDashCount > 0) { + const size_t size = sizeof(CGFloat) * _lineDashCount; + bcopy(_lineDashPattern, pattern, size); + } + + if (count) { + *count = _lineDashCount; + } + + if (phase) { + *phase = _lineDashPhase; + } +} + +- (BOOL)containsPoint:(CGPoint)point { + return CGPathContainsPoint(_path, NULL, point, _usesEvenOddFillRule); +} + +- (BOOL)isEmpty { + return CGPathIsEmpty(_path); +} + +- (CGRect)bounds { + return CGPathGetBoundingBox(_path); +} + +- (void)applyTransform:(CGAffineTransform)transform { + CGMutablePathRef mutablePath = CGPathCreateMutable(); + CGPathAddPath(mutablePath, &transform, _path); + self.CGPath = mutablePath; + CGPathRelease(mutablePath); +} + +@end + +#endif