Skip to content

Commit

Permalink
UIBezierPath+LxThroughPointsBezier
Browse files Browse the repository at this point in the history
  • Loading branch information
Jakey committed Aug 24, 2015
1 parent 75e13e5 commit 93f5767
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 2 deletions.
25 changes: 25 additions & 0 deletions Categories/UIKit/UIBezierPath/UIBezierPath+LxThroughPointsBezier.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// UIBezierPath+LxThroughPointsBezier.h
// LxThroughPointsBezierDemo
//
// A funny iOS library. Draw a smooth bezier through several points you designated. The curve‘s bend level is adjustable.

//https://github.com/DeveloperLx/LxThroughPointsBezier

#import <UIKit/UIKit.h>

@interface UIBezierPath (LxThroughPointsBezier)

/**
* The curve‘s bend level. The good value is about 0.6 ~ 0.8. The default and recommended value is 0.7.
*/
@property (nonatomic) CGFloat contractionFactor;

/**
* You must wrap CGPoint struct to NSValue object.
*
* @param pointArray Points you want to through. You must give at least 1 point for drawing curve.
*/
- (void)addBezierThroughPoints:(NSArray *)pointArray;

@end
148 changes: 148 additions & 0 deletions Categories/UIKit/UIBezierPath/UIBezierPath+LxThroughPointsBezier.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@

//
// UIBezierPath+LxThroughPointsBezier.m
// LxThroughPointsBezierDemo
//

#import "UIBezierPath+LxThroughPointsBezier.h"
#import <objc/runtime.h>

@implementation UIBezierPath (LxThroughPointsBezier)

- (void)setContractionFactor:(CGFloat)contractionFactor
{
objc_setAssociatedObject(self, @selector(contractionFactor), @(contractionFactor), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (CGFloat)contractionFactor
{
id contractionFactorAssociatedObject = objc_getAssociatedObject(self, @selector(contractionFactor));
if (contractionFactorAssociatedObject == nil) {
return 0.7;
}
return [contractionFactorAssociatedObject floatValue];
}

- (void)addBezierThroughPoints:(NSArray *)pointArray
{
NSAssert(pointArray.count > 0, @"You must give at least 1 point for drawing the curve.");

if (pointArray.count < 3) {
switch (pointArray.count) {
case 1:
{
NSValue * point0Value = pointArray[0];
CGPoint point0 = [point0Value CGPointValue];
[self addLineToPoint:point0];
}
break;
case 2:
{
NSValue * point0Value = pointArray[0];
CGPoint point0 = [point0Value CGPointValue];
NSValue * point1Value = pointArray[1];
CGPoint point1 = [point1Value CGPointValue];
[self addQuadCurveToPoint:point1 controlPoint:ControlPointForTheBezierCanThrough3Point(self.currentPoint, point0, point1)];
}
break;
default:
break;
}
}

CGPoint previousPoint = CGPointZero;

CGPoint previousCenterPoint = CGPointZero;
CGPoint centerPoint = CGPointZero;
CGFloat centerPointDistance = 0;

CGFloat obliqueAngle = 0;

CGPoint previousControlPoint1 = CGPointZero;
CGPoint previousControlPoint2 = CGPointZero;
CGPoint controlPoint1 = CGPointZero;

previousPoint = self.currentPoint;

for (int i = 0; i < pointArray.count; i++) {

NSValue * pointIValue = pointArray[i];
CGPoint pointI = [pointIValue CGPointValue];

if (i > 0) {

previousCenterPoint = CenterPointOf(self.currentPoint, previousPoint);
centerPoint = CenterPointOf(previousPoint, pointI);

centerPointDistance = DistanceBetweenPoint(previousCenterPoint, centerPoint);

obliqueAngle = ObliqueAngleOfStraightThrough(centerPoint, previousCenterPoint);

previousControlPoint2 = CGPointMake(previousPoint.x - 0.5 * self.contractionFactor * centerPointDistance * cos(obliqueAngle), previousPoint.y - 0.5 * self.contractionFactor * centerPointDistance * sin(obliqueAngle));
controlPoint1 = CGPointMake(previousPoint.x + 0.5 * self.contractionFactor * centerPointDistance * cos(obliqueAngle), previousPoint.y + 0.5 * self.contractionFactor * centerPointDistance * sin(obliqueAngle));
}

if (i == 1) {

[self addQuadCurveToPoint:previousPoint controlPoint:previousControlPoint2];
}
else if (i > 1 && i < pointArray.count - 1) {

[self addCurveToPoint:previousPoint controlPoint1:previousControlPoint1 controlPoint2:previousControlPoint2];
}
else if (i == pointArray.count - 1) {

[self addCurveToPoint:previousPoint controlPoint1:previousControlPoint1 controlPoint2:previousControlPoint2];
[self addQuadCurveToPoint:pointI controlPoint:controlPoint1];
}
else {

}

previousControlPoint1 = controlPoint1;
previousPoint = pointI;
}
}

CGFloat ObliqueAngleOfStraightThrough(CGPoint point1, CGPoint point2) // [-π/2, 3π/2)
{
CGFloat obliqueRatio = 0;
CGFloat obliqueAngle = 0;

if (point1.x > point2.x) {

obliqueRatio = (point2.y - point1.y) / (point2.x - point1.x);
obliqueAngle = atan(obliqueRatio);
}
else if (point1.x < point2.x) {

obliqueRatio = (point2.y - point1.y) / (point2.x - point1.x);
obliqueAngle = M_PI + atan(obliqueRatio);
}
else if (point2.y - point1.y >= 0) {

obliqueAngle = M_PI/2;
}
else {
obliqueAngle = -M_PI/2;
}

return obliqueAngle;
}

CGPoint ControlPointForTheBezierCanThrough3Point(CGPoint point1, CGPoint point2, CGPoint point3)
{
return CGPointMake(2 * point2.x - (point1.x + point3.x) / 2, 2 * point2.y - (point1.y + point3.y) / 2);
}

CGFloat DistanceBetweenPoint(CGPoint point1, CGPoint point2)
{
return sqrt((point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y));
}

CGPoint CenterPointOf(CGPoint point1, CGPoint point2)
{
return CGPointMake((point1.x + point2.x) / 2, (point1.y + point2.y) / 2);
}

@end
2 changes: 1 addition & 1 deletion Categories/UIKit/UIScrollView/UIScrollView+Addition.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ typedef NS_ENUM(NSInteger, ScrollDirection) {
- (CGPoint)leftContentOffset;
- (CGPoint)rightContentOffset;

- (ScrollDirection)scrollDirection;
- (ScrollDirection)ScrollDirection;

- (BOOL)isScrolledToTop;
- (BOOL)isScrolledToBottom;
Expand Down
2 changes: 1 addition & 1 deletion Categories/UIKit/UIScrollView/UIScrollView+Addition.m
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ - (CGPoint)rightContentOffset
{
return CGPointMake(self.contentSize.width + self.contentInset.right - self.bounds.size.width, 0.0f);
}
- (ScrollDirection)scrollDirection
- (ScrollDirection)ScrollDirection
{
ScrollDirection direction;

Expand Down
6 changes: 6 additions & 0 deletions IOS-Categories.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@
A2EA48F21B34FBB400A4B66F /* CALayer+UIColor.m in Sources */ = {isa = PBXBuildFile; fileRef = A2EA48F11B34FBB400A4B66F /* CALayer+UIColor.m */; };
A2EC52E71A8769D40059D53B /* NSArray+SafeAccess.m in Sources */ = {isa = PBXBuildFile; fileRef = A2EC52E61A8769D40059D53B /* NSArray+SafeAccess.m */; };
A2EC52EA1A876E7C0059D53B /* NSString+Base64.m in Sources */ = {isa = PBXBuildFile; fileRef = A2EC52E91A876E7C0059D53B /* NSString+Base64.m */; };
A2EE52B51B89FD920000E7B3 /* UIBezierPath+LxThroughPointsBezier.m in Sources */ = {isa = PBXBuildFile; fileRef = A2EE52B41B89FD920000E7B3 /* UIBezierPath+LxThroughPointsBezier.m */; };
A2EF56AF1B21C2C30005F730 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2EF56AE1B21C2C30005F730 /* CoreData.framework */; };
A2EF56B11B21C2C80005F730 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2EF56B01B21C2C80005F730 /* CoreLocation.framework */; };
A2EF56B31B21C2D00005F730 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2EF56B21B21C2D00005F730 /* UIKit.framework */; };
Expand Down Expand Up @@ -885,6 +886,8 @@
A2EC52E61A8769D40059D53B /* NSArray+SafeAccess.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = "NSArray+SafeAccess.m"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
A2EC52E81A876E7C0059D53B /* NSString+Base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Base64.h"; sourceTree = "<group>"; };
A2EC52E91A876E7C0059D53B /* NSString+Base64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Base64.m"; sourceTree = "<group>"; };
A2EE52B31B89FD920000E7B3 /* UIBezierPath+LxThroughPointsBezier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIBezierPath+LxThroughPointsBezier.h"; sourceTree = "<group>"; };
A2EE52B41B89FD920000E7B3 /* UIBezierPath+LxThroughPointsBezier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIBezierPath+LxThroughPointsBezier.m"; sourceTree = "<group>"; };
A2EF56AE1B21C2C30005F730 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
A2EF56B01B21C2C80005F730 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; };
A2EF56B21B21C2D00005F730 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
Expand Down Expand Up @@ -1506,6 +1509,8 @@
A284C0FD1AEB908100D90ED5 /* UIBezierPath+BasicShapes.m */,
A280E5461A52480300E6ACA2 /* UIBezierPath+SVG.h */,
A280E5471A52480300E6ACA2 /* UIBezierPath+SVG.m */,
A2EE52B31B89FD920000E7B3 /* UIBezierPath+LxThroughPointsBezier.h */,
A2EE52B41B89FD920000E7B3 /* UIBezierPath+LxThroughPointsBezier.m */,
);
path = UIBezierPath;
sourceTree = "<group>";
Expand Down Expand Up @@ -3094,6 +3099,7 @@
A284C1251AEBC19C00D90ED5 /* UINavigationBarDemoViewController.m in Sources */,
A219B8B71A615CA100135327 /* UIView+Screenshot.m in Sources */,
A284C13F1AEBCF9600D90ED5 /* NSDateFormatterDemoViewController.m in Sources */,
A2EE52B51B89FD920000E7B3 /* UIBezierPath+LxThroughPointsBezier.m in Sources */,
A2958D3C1B35A2E900D7AA0F /* UIWebVIew+SwipeGesture.m in Sources */,
A28BE3421A3EA20B005C4AC6 /* UIColor+Random.m in Sources */,
A2FD5B631A52718200555EA2 /* NSDate+Utilities.m in Sources */,
Expand Down

0 comments on commit 93f5767

Please sign in to comment.