在点之间画线(多个)

时间:2014-02-05 20:12:39

标签: iphone objective-c ipad

在帮助下我可以使用协调绘制圆圈:

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 5.0);
    CGContextSetStrokeColorWithColor(context,[UIColor grayColor].CGColor);
    /**** Get values from reponse and fill it in this array.   ******/
    NSArray *objectCoords = [NSArray arrayWithObjects: @"{{20,80},{5,5}}", @"{{120,60},{5,5}}", @"{{60,84},{5,5}}", @"{{80,88},{5,5}}", @"{{100,93},{5,5}}", @"{{20,20},{5,5}}", @"{{160,70},{5,5}}", @"{{128,68},{5,5}}", @"{{90,60},{5,5}}", @"{{110,80},{5,5}}", nil];
    for (NSString* objectCoord in objectCoords) {
        CGRect coord = CGRectFromString(objectCoord);
        // Draw using your coord
        CGContextAddEllipseInRect(context, coord);
    }

现在我想要实现的是在点(圆圈)之间绘制线条,如附图中所示。我知道我们可以在2个点之间绘制一条线,但在这种情况下,需要绘制线条在一个到多个点/圆之间。请建议我实现这个结果。 expected output

2 个答案:

答案 0 :(得分:2)

你可以画多行。首先,提出一个模型来表示您想要绘制线条的点。例如,你可以有一个行数组,其中每一行本身定义为一个索引为两点的数组,即起点和终点。

例如,如果要绘制从第1点到第3点,第4点和第5点以及第3点到第4点和第5点以及第4点和第5点之间的行,您可以执行以下操作:

CGContextSetLineWidth(context, 1.0);

NSArray *lines = @[@[@(1), @(3)],
                   @[@(1), @(4)],
                   @[@(1), @(5)],
                   @[@(3), @(4)],
                   @[@(3), @(5)],
                   @[@(4), @(5)]];

for (NSArray *points in lines) {
    NSInteger startIndex = [points[0] integerValue];
    NSInteger endIndex   = [points[1] integerValue];

    CGRect startRect = CGRectFromString(objectCoords[startIndex]);
    CGRect endRect   = CGRectFromString(objectCoords[endIndex]);

    CGContextMoveToPoint(context, CGRectGetMidX(startRect), CGRectGetMidY(startRect));
    CGContextAddLineToPoint(context, CGRectGetMidX(endRect), CGRectGetMidY(endRect));
}

CGContextDrawPath(context, kCGPathStroke);

有很多不同的方法可以做到这一点,但只是想出一些代表你想要绘制线条的模型,然后遍历该模型来绘制所有单独的线条。


如果您希望每个点都画线到最近的三个点(这不是您的图片所做的,但这是您在后续评论中所要求的),您可以:

  • indices数组中构建索引数组objectCoords;和

  • 现在遍历objectCoords中的每个点:

    • 构建新的索引数组sortedIndices,按照该索引所代表的点距objectCoords中当前对象的距离排序;以及

    • 画出最近的三条线。

因此:

// build array of indices (0, 1, 2, ...)

NSMutableArray *indices = [NSMutableArray array];
[objectCoords enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    [indices addObject:@(idx)];
}];

// now go through all of the points in objectCoords

[objectCoords enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) {

    // build new array of indices sorted by distance from the current point

    NSArray *sortedIndices = [indices sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        CGFloat distance1 = [self distanceFromPointAtIndex:idx
                                            toPointAtIndex:[obj1 integerValue]
                                         usingObjectCoords:objectCoords];

        CGFloat distance2 = [self distanceFromPointAtIndex:idx
                                            toPointAtIndex:[obj2 integerValue]
                                         usingObjectCoords:objectCoords];

        if (distance1 < distance2)
            return NSOrderedAscending;
        else if (distance1 > distance2)
            return NSOrderedDescending;
        return NSOrderedSame;
    }];

    // now draw lines to the three closest indices
    // (skipping 0, because that's probably the current point)

    for (NSInteger i = 1; i < 4 && i < [sortedIndices count]; i++) {

        NSInteger index = [sortedIndices[i] integerValue];

        CGRect startRect = CGRectFromString(objectCoords[idx]);
        CGRect endRect   = CGRectFromString(objectCoords[index]);

        CGContextMoveToPoint(context,    CGRectGetMidX(startRect), CGRectGetMidY(startRect));
        CGContextAddLineToPoint(context, CGRectGetMidX(endRect),   CGRectGetMidY(endRect));
    }
}];

CGContextSetLineWidth(context, 1);
CGContextDrawPath(context, kCGPathStroke);

这使用以下方法计算两点之间的距离:

- (CGFloat)distanceFromPointAtIndex:(NSInteger)index1 toPointAtIndex:(NSInteger)index2 usingObjectCoords:(NSArray *)objectCoords
{
    CGRect rect1 = CGRectFromString(objectCoords[index1]);
    CGRect rect2 = CGRectFromString(objectCoords[index2]);

    return hypotf(CGRectGetMidX(rect1) - CGRectGetMidX(rect2), CGRectGetMidY(rect1) - CGRectGetMidY(rect2));
}

使用原始示例中的objectCoords,产生:

points with lines b/w three closest for each

答案 1 :(得分:1)

这与你原来的问题有点无关,而不是一串字符串,如下所示:

NSArray *objectCoords = @[@"{{20,80},{5,5}}",
                          @"{{120,60},{5,5}}",
                          @"{{60,84},{5,5}}",
                          @"{{80,88},{5,5}}",
                          @"{{100,93},{5,5}}",
                          @"{{20,20},{5,5}}",
                          @"{{160,70},{5,5}}",
                          @"{{128,68},{5,5}}",
                          @"{{90,60},{5,5}}",
                          @"{{110,80},{5,5}}"];

我可能会建议使用一组NSValue个对象:

NSArray *objectCoords = @[[NSValue valueWithCGRect:CGRectMake(20,80,5,5)],
                          [NSValue valueWithCGRect:CGRectMake(120,60,5,5)],
                          [NSValue valueWithCGRect:CGRectMake(60,84,5,5)],
                          [NSValue valueWithCGRect:CGRectMake(80,88,5,5)],
                          [NSValue valueWithCGRect:CGRectMake(100,93,5,5)],
                          [NSValue valueWithCGRect:CGRectMake(20,20,5,5)],
                          [NSValue valueWithCGRect:CGRectMake(160,70,5,5)],
                          [NSValue valueWithCGRect:CGRectMake(128,68,5,5)],
                          [NSValue valueWithCGRect:CGRectMake(90,60,5,5)],
                          [NSValue valueWithCGRect:CGRectMake(110,80,5,5)]];

然后,当您提取CGRect值时,而不是:

CGRect rect = CGRectFromString(objectCoords[index]);

你会这样做:

CGRect rect = [objectCoords[index] CGRectValue];

我知道这看起来更麻烦,但使用NSValue会更有效率(这在做很多或重复计算距离时很有用)。


更好的是,您可能希望定义自己的模型对象,更直观地定义要绘制图表的点,例如:

@interface ChartPoint : NSObject

@property (nonatomic) CGPoint center;
@property (nonatomic) CGFloat radius;

@end

@implementation ChartPoint

+ (instancetype) chartPointWithCenter:(CGPoint)center radius:(CGFloat)radius
{
    ChartPoint *chartPoint = [[ChartPoint alloc] init];
    chartPoint.center = center;
    chartPoint.radius = radius;

    return chartPoint;
}

- (CGFloat)distanceToPoint:(ChartPoint *)otherPoint
{
    return hypotf(self.center.x - otherPoint.center.x, self.center.y - otherPoint.center.y);
}

@end

然后你可以像这样创建一个数组:

NSArray *objectCoords = @[[ChartPoint chartPointWithCenter:CGPointMake(20,80)  radius:5],
                          [ChartPoint chartPointWithCenter:CGPointMake(120,60) radius:5],
                          [ChartPoint chartPointWithCenter:CGPointMake(60,84)  radius:5],
                          [ChartPoint chartPointWithCenter:CGPointMake(80,88)  radius:5],
                          [ChartPoint chartPointWithCenter:CGPointMake(100,93) radius:5],
                          [ChartPoint chartPointWithCenter:CGPointMake(20,20)  radius:5],
                          [ChartPoint chartPointWithCenter:CGPointMake(160,70) radius:5],
                          [ChartPoint chartPointWithCenter:CGPointMake(128,68) radius:5],
                          [ChartPoint chartPointWithCenter:CGPointMake(90,60)  radius:5],
                          [ChartPoint chartPointWithCenter:CGPointMake(110,80) radius:5]];

但是这个

  • 避免效率低CGRectFromString;

  • 避免需要执行重复CGRectGetMidXCGRectGetMidY次调用以确定CGRect的中心;最重要的是,

  • 更准确地表示您的对象实际上是什么。

显然,当你想要得出你的分数而不是做:

NSString *string = objectCoords[idx];
CGRect   *rect = CGRectFromString(string);
CGContextAddEllipseInRect(context, rect);

你会这样做:

ChartPoint *point = objectCoords[idx];
CGContextAddArc(context, point.center.x, point.center.y, point.radius, 0, M_PI * 2.0, YES);