// // UIBezierPath+ThroughPointsBezier.m // ZHLineChart // // Created by 周亚楠 on 2020/3/1. // Copyright © 2020 Zhou. All rights reserved. // #import "UIBezierPath+ThroughPointsBezier.h" #import @implementation UIBezierPath (ThroughPointsBezier) - (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) { return 0.7; } return [contractionFactorAssociatedObject floatValue]; } /** * 正常折线绘制 * 必须将CGPoint结构体包装成NSValue对象并且至少一个点来画折线。 */ - (void)addNormalBezierThroughPoints:(NSArray *)pointArray { for (int i = 0; i < pointArray.count; i++) { NSValue * pointIValue = pointArray[i]; CGPoint pointI = [pointIValue CGPointValue]; [self addLineToPoint:pointI]; } } - (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