Рисование кривой Безье между набором заданных точек

Как лучше всего нарисовать кривую Безье в приложении iOS, которая проходит через набор заданных точек


person user462455    schedule 15.10.2012    source источник


Ответы (6)


Немного более общий способ сделать это можно сделать, например, просмотрев проект BEMSimpleLineGraph GitHub (дополнительную информацию см. здесь: bemsimplelinegraph). Здесь я извлек метод рисования кривой Безье через заданный список точек.

Заголовочный файл (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

Реализация (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

Вы можете использовать его, например, создав контекст изображения с помощью UIGraphicsBeginImageContext и извлекая контекст с помощью UIGraphicsGetCurrentContext().

В противном случае вы можете изменить код и назначить полученный путь CALayer и добавить его в UIView.

Надеюсь это поможет.

person mjrehder    schedule 26.04.2015

Я знаю, что это может быть поздно, но только для тех, кто ищет правильный ответ. Вместо использования addLineToPoint для рисования прямой линии. Вы можете использовать addCurveToPoint, чтобы нарисовать кривую. например

[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];

Это действительно зависит от вас, чтобы выбрать контрольные точки кривой. Я просто использую x = last_point_x + 20; y = last_point_y для контрольной точки один и x = current_point_x - 20; у = текущая_точка_у;

и вы можете использовать другое значение вместо 20, так как у вас может быть другая ширина сегмента кривой.

person codingrhythm    schedule 09.01.2014

Вы можете легко найти в Google пример того, как создать кривую Безье в Интернете. Я нашел это короткое tut как пример.

Вы можете создать близкую кривую Безье, например. со следующим фрагментом кода:

UIBezierPath* path = [UIBezierPath bezierPath];

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

[path closePath];

Я надеюсь, что это поможет в качестве отправной точки.

person tiguero    schedule 15.10.2012

Пожалуйста, попробуйте это.

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];
person Milan Kamilya    schedule 05.11.2013
comment
Пожалуйста, поделитесь своей проблемой с переоценкой кода, прежде чем проголосовать за нее. - person Milan Kamilya; 27.11.2013
comment
бечара - хей гечило :P - person Maniac One; 09.01.2014

Вы можете быть намного эффективнее, используя метод 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];
person fawrkes    schedule 24.07.2014

person    schedule
comment
Это просто рисует прямые линии и не отвечает на вопрос. - person FractalDoctor; 02.02.2016