Drawing a bezier curve between a set of given points

后端 未结 6 1656
春和景丽
春和景丽 2021-02-13 13:40

What is the best way to draw a bezier curve, in iOS application, that passes through a set of given points

相关标签:
6条回答
  • 2021-02-13 14:11

    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];
    
    0 讨论(0)
  • 2021-02-13 14:21
    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];
    
    0 讨论(0)
  • 2021-02-13 14:24

    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. You can use addCurveToPoint to draw the curve. e.g.

    [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; y = last_point_y for control point one, and x = current_point_x - 20; 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.

    0 讨论(0)
  • 2021-02-13 14:25

    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). Here I extracted a method to draw a bezier curve through a given list of points.

    The header file (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):

    #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().

    Otherwise you may want to change the code and assign the resulting Path to a CALayer and add that to an UIView.

    Hope this helps.

    0 讨论(0)
  • 2021-02-13 14:32

    You can be much more efficient by using the CGPointFromString method:

     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];
    
    0 讨论(0)
  • 2021-02-13 14:35

    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 e.g. 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.

    0 讨论(0)
提交回复
热议问题