简体   繁体   English

在一组给定点之间绘制贝塞尔曲线

[英]Drawing a bezier curve between a set of given points

在iOS应用程序中绘制贝塞尔曲线的最佳方法是什么,它通过一组给定的点

A little more generic way to do it can be achieved by, for example, looking at the BEMSimpleLineGraph GitHub Project (see here for more info: ). 例如,通过查看BEMSimpleLineGraph GitHub项目可以实现更通用的方法(请参阅此处了解更多信息: )。 Here I extracted a method to draw a bezier curve through a given list of points. 在这里,我提取了一种方法,通过给定的点列表绘制贝塞尔曲线。

The header file (BezierLine.h): 头文件(BezierLine.h):

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <CoreGraphics/CoreGraphics.h>

@interface BezierLine : NSObject

/*
 Draws a bezier curved line on the given context
 with points: Array of CGPoint values
 */
-(void) drawBezierCurveInContext:(CGContextRef)context withPoints:(NSArray*)points lineColor:(UIColor*)color lineWidth:(CGFloat)lineWidth;

@end

The implementation (BezierLine.m): 实施(BezierLine.m):

#import "BezierLine.h"

@implementation BezierLine

-(void) drawBezierCurveInContext:(CGContextRef)context withPoints:(NSArray*)points lineColor:(UIColor*)color lineWidth:(CGFloat)lineWidth {
    if (points.count < 2) return;

    CGPoint CP1;
    CGPoint CP2;

    // LINE
    UIBezierPath *line = [UIBezierPath bezierPath];

    CGPoint p0;
    CGPoint p1;
    CGPoint p2;
    CGPoint p3;
    CGFloat tensionBezier1 = 0.3;
    CGFloat tensionBezier2 = 0.3;

    CGPoint previousPoint1;
    CGPoint previousPoint2;

    [line moveToPoint:[[points objectAtIndex:0] CGPointValue]];

    for (int i = 0; i < points.count - 1; i++) {
        p1 = [[points objectAtIndex:i] CGPointValue];
        p2 = [[points objectAtIndex:i + 1] CGPointValue];

        const CGFloat maxTension = 1.0f / 3.0f;
        tensionBezier1 = maxTension;
        tensionBezier2 = maxTension;

        if (i > 0) { // Exception for first line because there is no previous point
            p0 = previousPoint1;
            if (p2.y - p1.y == p1.y - p0.y) tensionBezier1 = 0;
        } else {
            tensionBezier1 = 0;
            p0 = p1;
        }

        if (i < points.count - 2) { // Exception for last line because there is no next point
            p3 = [[points objectAtIndex:i + 2] CGPointValue];
            if (p3.y - p2.y == p2.y - p1.y) tensionBezier2 = 0;
        } else {
            p3 = p2;
            tensionBezier2 = 0;
        }

        // The tension should never exceed 0.3
        if (tensionBezier1 > maxTension) tensionBezier1 = maxTension;
        if (tensionBezier2 > maxTension) tensionBezier2 = maxTension;

        // First control point
        CP1 = CGPointMake(p1.x + (p2.x - p1.x)/3,
                          p1.y - (p1.y - p2.y)/3 - (p0.y - p1.y)*tensionBezier1);

        // Second control point
        CP2 = CGPointMake(p1.x + 2*(p2.x - p1.x)/3,
                          (p1.y - 2*(p1.y - p2.y)/3) + (p2.y - p3.y)*tensionBezier2);


        [line addCurveToPoint:p2 controlPoint1:CP1 controlPoint2:CP2];

        previousPoint1 = p1;
        previousPoint2 = p2;
    }

    CGContextSetAllowsAntialiasing(context, YES);
    CGContextSetStrokeColorWithColor(context, color.CGColor);
    CGContextSetLineWidth(context, lineWidth);
    CGContextAddPath(context, line.CGPath);
    CGContextDrawPath(context, kCGPathStroke);
}


@end

You can use it by for example creating an image context using UIGraphicsBeginImageContext and retrieving the context with UIGraphicsGetCurrentContext(). 您可以通过使用UIGraphicsBeginImageContext创建图像上下文并使用UIGraphicsGetCurrentContext()检索上下文来使用它。

Otherwise you may want to change the code and assign the resulting Path to a CALayer and add that to an UIView. 否则,您可能需要更改代码并将结果路径分配给CALayer并将其添加到UIView。

Hope this helps. 希望这可以帮助。

I know this might be late, but just for anyone who is looking for the right answer. 我知道这可能会迟到,但对于那些正在寻找正确答案的人来说。 Instead of using addLineToPoint to draw the straight line. 而不是使用addLineToPoint绘制直线。 You can use addCurveToPoint to draw the curve. 您可以使用addCurveToPoint绘制曲线。 eg 例如

[bezierPath moveToPoint:CGPointMake(0, 0)];
[bezierPath addCurveToPoint:CGPointMake(40, 100) 
              controlPoint1:CGPointMake(20, 0) 
              controlPoint2:CGPointMake(20, 100)];
[bezierPath addCurveToPoint:CGPointMake(80, 50) 
              controlPoint1:CGPointMake(60, 100) 
              controlPoint2:CGPointMake(60, 50)];

// and you may don't want to close the path
// [bezierPath closePath];

It's really up to you to choose the control points of the curve. 选择曲线的控制点真的取决于你。 I just use the x = last_point_x + 20; 我只使用x = last_point_x + 20; y = last_point_y for control point one, and x = current_point_x - 20; 对于控制点1,y = last_point_y,x = current_point_x - 20; y = current_point_y; y = current_point_y;

and you may want to use other value instead of the 20 as you may have different segment width of the curve. 并且您可能希望使用其他值而不是20,因为您可能具有不同的曲线段宽度。

You can easily google some example of how to create bezier curve on the web. 您可以轻松地谷歌一些如何在网络上创建贝塞尔曲线的示例。 I found this short tut as an example. 我发现这个短暂的啧啧是一个例子。

You can create a close bezier curve for eg with the following code snippet: 您可以创建一个紧密的贝塞尔曲线,例如使用以下代码片段:

UIBezierPath* path = [UIBezierPath bezierPath];

[path moveToPoint:pt1];
[path addLineToPoint:pt2];
[path addLineToPoint:pt3];

[path closePath];

I hope it will help as a starting point. 我希望它有助于作为一个起点。

Please try this. 请试试这个。

UIImageView *waterLevel = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,200,200)];
UIGraphicsBeginImageContext(waterLevel.frame.size);
[waterLevel.image drawAtPoint:CGPointZero];
//define BezierPath
UIBezierPath *bezierPath = [UIBezierPath bezierPath];


// Set the starting point of the shape.
[bezierPath moveToPoint:CGPointMake(0, 0)];

[bezierPath addLineToPoint:CGPointMake(waterLevel.frame.size.width, 0)];
[bezierPath addLineToPoint:CGPointMake(waterLevel.frame.size.width, waterLevel.frame.size.height)];
[bezierPath addLineToPoint:CGPointMake(0, waterLevel.frame.size.height)];
[bezierPath closePath];

bezierPath.lineWidth = 15;
//set the stoke color
[[UIColor blackColor] setStroke];
//draw the path
[bezierPath stroke];

// Add to the current Graphic context
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextAddPath(context,bezierPath.CGPath);
waterLevel.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

[self.view addSubview:waterLevel];

You can be much more efficient by using the CGPointFromString method: 使用CGPointFromString方法可以提高效率:

 NSArray *pointArray = @[@"{3.0,2.5}",@"{100.0,30.2}", @"{100.0,200.0}", @"{3.0,200.0}"];

// draw the path
UIBezierPath *aPath = [UIBezierPath bezierPath];
for (NSString *pointString in pointArray) {
    if ([pointArray indexOfObject:pointString] == 0)
        [aPath moveToPoint:CGPointFromString(pointString)];
    else
        [aPath addLineToPoint:CGPointFromString(pointString)];
}
[aPath closePath];
UIBezierPath *aPath = [UIBezierPath bezierPath];

// Set the starting point of the shape.
[aPath moveToPoint:CGPointMake(100.0, 0.0)];

// Draw the lines.
[aPath addLineToPoint:CGPointMake(200.0, 40.0)];
[aPath addLineToPoint:CGPointMake(160, 140)];
[aPath addLineToPoint:CGPointMake(40.0, 140)];
[aPath addLineToPoint:CGPointMake(0.0, 40.0)];
[aPath closePath];

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM