【问题标题】:Draw lines between points(multiple)在点之间画线(多条)
【发布时间】:2023-03-23 06:08:01
【问题描述】:

在我的帮助下,我可以用协调的方式画圆:

    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 个点之间画一条线,但在这种情况下,需要画线在一个到多个点/圆之间。请建议我达到这个结果。

【问题讨论】:

    标签: iphone objective-c ipad


    【解决方案1】:

    你可以只画多条线。首先,想出一个模型来表示要在哪些点之间画线。例如,您可以有一个线数组,其中每条线本身都被定义为一个包含两个点(起点和终点)索引的数组。

    例如,如果您想从点 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);
    

    有很多不同的方法可以做到这一点,但只需提出一些模型来表示您要在哪里绘制线条,然后遍历该模型以绘制所有单独的线条。


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

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

    • 现在遍历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,产生:

    【讨论】:

    • 谢谢 Rob,这看起来像在赛道上。就像你有一个 NSArray 的线,给出值来绘制例如:1-3,在我的数组中,我无法定义。我 m试图根据最近的圆找到起点和终点。有什么办法吗?
    • @iscavengers 我不完全确定我是否遵循“基于最近的圆圈”的意思。从每个圆圈中,找到最近的邻居并画一条线?这不是你的图表正在做的事情,但是,当然,你可以做到这一点。上面的代码示例说明了所有基本组件(例如,使用CGRectGetMidXCGRectGetMidY 获取中心;如何绘制单独的线等)。要计算点之间的距离,您可以使用hypotf(point1.x - point2.x, point1.y - point2.y)。所以只需遍历点,计算距离,然后找到最近的。
    • 感谢@Rob 的回复。在你的前任中:@(1)、@(3)、(1)、@(4) 画线从 (1) 到 3,4 我怎样才能使它动态,从每个单独的点,它应该至少画线附近 3-4 点?
    • @iscavengers 我已经添加了执行此操作的代码示例,但不可否认,这不是您的图片所采用的逻辑。那里使用什么逻辑来确定绘制什么线并不完全清楚。
    • 这太酷了。这是最好的方法之一。感谢您让它如此易于理解。感谢它。
    【解决方案2】:

    这与您的原始问题有点无关,而是一个字符串数组,如下所示:

    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);
    

    【讨论】:

    • 对,使用 CGRectFromString 可能是合适的。再次感谢 Rob 的帮助。
    • 还有一件事 Rob,我正在尝试延迟绘制线条,例如先显示点,然后在 3 秒后显示连接线。我把点代码放在drawRect中,你的画线代码放在另一个方法(callDrawlines)中,当我做[self callDrawline]时,它被调用,但是当我使用选择器调用它时,通过给予3秒的延迟,它不是画线。有什么想法吗?
    • @iscavengers 您不能在drawRect 的上下文之外调用这些核心图形调用。因此,您需要配置您的drawRect,以便它可以仅绘制点或同时绘制点和线,这取决于一些布尔变量的设置。然后加载视图(默认情况下应该只绘制点),三秒钟后设置布尔值,然后调用 setNeedsDisplay(这将导致再次调用 drawRect)。
    • @iscavengers 当然,工作示例见github.com/robertmryan/PointsAndLines
    • @iscavengers 谢谢,但这真的没有必要。编码愉快。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 2016-08-25
    • 1970-01-01
    相关资源
    最近更新 更多