【问题标题】:CAShapeLayer: Finding the intersected area of 3 circlesCAShapeLayer:找到 3 个圆的相交区域
【发布时间】:2026-01-26 14:00:01
【问题描述】:

我有两个问题:

  1. 在 CALayer 中有什么方法可以知道两个圆是否相交?
  2. 三个圆相交,有什么办法可以突出相交的区域吗?

我正在使用 CAShapeLayer 来绘制我的圆圈:

- (CAShapeLayer *)circleForRadius:(CGFloat)iRadius withColor:(CGColorRef)iColor andDashPattern:(BOOL)isDashPattern {
CAShapeLayer *aSignalcircle = [CAShapeLayer layer];
aSignalcircle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0 * iRadius, 2.0 * iRadius) cornerRadius:iRadius].CGPath;
aSignalcircle.position = CGPointMake(0.0 - iRadius, 0.0 - iRadius);
aSignalcircle.fillColor = [UIColor clearColor].CGColor;
aSignalcircle.strokeColor = iColor;
aSignalcircle.lineWidth = kPSSignalStrokeWidth;

if (self.enableShadow) {
    aSignalcircle.shadowColor = [UIColor blackColor].CGColor;
    aSignalcircle.shadowOpacity = 0.5;
    aSignalcircle.shadowOffset = CGSizeMake(0, 0.25);
    aSignalcircle.shadowRadius = 0.5;
}

if (isDashPattern) {
    aSignalcircle.lineDashPattern = @[@1, @1];
}

return aSignalcircle;

}

我尝试使用以下代码剪切上下文路径,但输出未按预期出现:

- (void)drawRect:(CGRect)rect
{
    CGFloat iRadius = 20.0;
    // Drawing code
    CGContextRef context = UIGraphicsGetCurrentContext();
    CAShapeLayer *aSignalcircle = [CAShapeLayer layer];
    aSignalcircle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0 * iRadius, 2.0 * iRadius) cornerRadius:iRadius].CGPath;
    aSignalcircle.position = CGPointMake(10.0 , 10.0);
    aSignalcircle.fillColor = [UIColor clearColor].CGColor;
    aSignalcircle.strokeColor = [UIColor redColor].CGColor;
    aSignalcircle.lineWidth = 1.0;
    CGContextAddPath(context, aSignalcircle.path);
    CGContextClip(context);

    CAShapeLayer *aSignalcircle1 = [CAShapeLayer layer];
    aSignalcircle1.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0 * iRadius, 2.0 * iRadius) cornerRadius:iRadius].CGPath;
    aSignalcircle1.position = CGPointMake(30.0 , 10.0);
    aSignalcircle1.fillColor = [UIColor clearColor].CGColor;
    aSignalcircle1.strokeColor = [UIColor redColor].CGColor;
    aSignalcircle1.lineWidth = 1.0;
    CGContextAddPath(context, aSignalcircle1.path);
    CGContextClip(context);


    CAShapeLayer *aSignalcircle2 = [CAShapeLayer layer];
    aSignalcircle2.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0 * iRadius, 2.0 * iRadius) cornerRadius:iRadius].CGPath;
    aSignalcircle2.position = CGPointMake(25.0 , 30.0);
    aSignalcircle2.fillColor = [UIColor clearColor].CGColor;
    aSignalcircle2.strokeColor = [UIColor redColor].CGColor;
    aSignalcircle2.lineWidth = 1.0;
    CGContextAddPath(context, aSignalcircle2.path);

    [[UIColor grayColor] set];
    CGContextFillPath(context);

// Temp addition to see how the circles are looking on screen
    [self.layer addSublayer:aSignalcircle];
    [self.layer addSublayer:aSignalcircle1];
    [self.layer addSublayer:aSignalcircle2];
}

我的输出是这样的:

【问题讨论】:

    标签: iphone ios objective-c cocoa-touch calayer


    【解决方案1】:

    对于第二个问题,你可以使用下面的代码来高亮相交的区域:

    // Add first path and clip
    CGContextAddPath(context, circle1.path);
    CGContextClip(context);
    
    // Add second path and clip
    CGContextAddPath(context, circle2.path);
    CGContextClip(context);
    
    // Add third path and draw it. This means it'll be drawn within the clipping area
    CGContextAddPath(context, circle3.path);
    [[UIColor redColor] set];
    CGContextFillPath(context);
    

    【讨论】:

    • 好答案,我没想过用剪裁来绘制区域。我试图找到交点,然后将弧线绘制成路径。你的解决方案很聪明。
    • 我已经尝试剪辑上下文,但它没有按预期进行。请参阅更新的问题。请指教。
    • 目前路径的位置与其所在层的位置不同。您可以尝试注释掉更改图层位置的行并改为更改路径本身的位置,它应该可以工作。
    • 我明白了。但是当我不改变我的图层位置时,所有三个圆圈都以中心为(0,0)绘制。我应该通过 CGContextRef 来代替吗?
    • 你应该改变路径本身的位置。例如:aSignalcircle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(10.0, 10.0, 2.0 * iRadius, 2.0 * iRadius) cornerRadius:iRadius].CGPath; // aSignalcircle.position = CGPointMake(10.0 , 10.0);
    【解决方案2】:

    如果你知道两个圆的半径,以及它们的圆心,就很容易知道它们是否相交:

    // Supposing the layers have the circles at their center,
    // which reading your code, it looks like it.
    // Obtaining this values might be simpler in your code.
    CGPoint center1 = circleLayer1.position;
    CGPoint center2 = circleLayer2.position;
    CGFloat radius1 = circleLayer1.bounds.size.width / 2.f;
    CGFloat radius2 = circleLayer2.bounds.size.width / 2.f;
    
    // Squared distance between the two circle centers
    CGFloat distance2 = (center2.x - center1.x) * (center2.x - center1.x);
    distance2 += (center2.y - center2.y) * (center2.y - center2.y);
    
    // Squared sum of the two radii
    CGFloat sumRadii2 = (radius1 + radius2) * (radius1 + radius2);
    
    // The will intersect if the sum of the radii is larger than the distance)
    BOOL intersection = sumRadii2 >= distance2;
    

    关于交叉口区域……嗯,这更难。我想有必要好好复习一下你的几何书。

    更新:

    以您和 Hejazi 的解决方案为基础。

    您创建的路径坐标是相对于每个图层的,因此在与它们相交之前,您需要对其进行变换以将它们绘制在正确的位置:

    - (void)drawRect:(CGRect)rect
    {
        CGFloat iRadius = 20.0;
        // Drawing code
        CGContextRef context = UIGraphicsGetCurrentContext();
        CAShapeLayer *aSignalcircle = [CAShapeLayer layer];
        aSignalcircle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0 * iRadius, 2.0 * iRadius) cornerRadius:iRadius].CGPath;
        aSignalcircle.position = CGPointMake(10.0 , 10.0);
        aSignalcircle.fillColor = [UIColor clearColor].CGColor;
        aSignalcircle.strokeColor = [UIColor redColor].CGColor;
        aSignalcircle.lineWidth = 1.0;
    
        // Add this line
        CGPathRef translatedPath = CGPathCreateCopyByTransformingPath(aSignalcircle.path, CGAffineTransformMakeTranslation(aSignalcircle.position.x, aSignalcircle.position.y));
    
        CGContextAddPath(context, translatedPath);
        CGContextClip(context);
    
        // Release here
        CGPathRelease(translatedPath);
    
        CAShapeLayer *aSignalcircle1 = [CAShapeLayer layer];
        aSignalcircle1.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0 * iRadius, 2.0 * iRadius) cornerRadius:iRadius].CGPath;
        aSignalcircle1.position = CGPointMake(30.0 , 10.0);
        aSignalcircle1.fillColor = [UIColor clearColor].CGColor;
        aSignalcircle1.strokeColor = [UIColor redColor].CGColor;
        aSignalcircle1.lineWidth = 1.0;
    
        // Same here
        translatedPath = CGPathCreateCopyByTransformingPath(aSignalcircle1.path, CGAffineTransformMakeTranslation(aSignalcircle1.position.x, aSignalcircle1.position.y));
        CGContextAddPath(context, aSignalcircle1.path);
        CGContextClip(context);
        CGPathRelease(translatedPath);    
    
        CAShapeLayer *aSignalcircle2 = [CAShapeLayer layer];
        aSignalcircle2.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0 * iRadius, 2.0 * iRadius) cornerRadius:iRadius].CGPath;
        aSignalcircle2.position = CGPointMake(25.0 , 30.0);
        aSignalcircle2.fillColor = [UIColor clearColor].CGColor;
        aSignalcircle2.strokeColor = [UIColor redColor].CGColor;
        aSignalcircle2.lineWidth = 1.0;
    
        // And finally here
        CGPathRef translatedPath = CGPathCreateCopyByTransformingPath(aSignalcircle2.path, CGAffineTransformMakeTranslation(aSignalcircle2.position.x, aSignalcircle2.position.y));
        CGContextAddPath(context, aSignalcircle2.path);
        CGPathRelease(translatedPath);
    
        [[UIColor grayColor] set];
        CGContextFillPath(context);
    
    // Temp addition to see how the circles are looking on screen
        [self.layer addSublayer:aSignalcircle];
        [self.layer addSublayer:aSignalcircle1];
        [self.layer addSublayer:aSignalcircle2];
    }
    

    我还没有测试过,但这应该将您的路径定位在子层绘制它们的新层中。告诉我它是否有效。

    【讨论】:

    • 我已经尝试剪辑上下文,但它没有按预期进行。请参阅更新的问题。请指教。
    【解决方案3】:

    以下代码有效:

    - (void)drawRect:(CGRect)rect
    {
        CGFloat iRadius = 20.0;
        CGContextRef aContext = UIGraphicsGetCurrentContext();
    
    
        CAShapeLayer *aSignalcircle = [CAShapeLayer layer];
        aSignalcircle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(10.0, 10.0, 2.0 * iRadius, 2.0 * iRadius) cornerRadius:iRadius].CGPath;
        aSignalcircle.lineWidth = 1.0;
        CGContextAddPath(aContext, aSignalcircle.path);
        CGContextClip(aContext);
    
    
        CAShapeLayer *aSignalcircle1 = [CAShapeLayer layer];
        aSignalcircle1.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(30.0, 10.0, 2.0 * iRadius, 2.0 * iRadius) cornerRadius:iRadius].CGPath;
        aSignalcircle1.lineWidth = 1.0;
    
        CGContextAddPath(aContext, aSignalcircle1.path);
        CGContextClip(aContext);
    
    
        CAShapeLayer *aSignalcircle2 = [CAShapeLayer layer];
        aSignalcircle2.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(25.0, 30.0, 2.0 * iRadius, 2.0 * iRadius) cornerRadius:iRadius].CGPath;
        aSignalcircle2.lineWidth = 1.0;
    
        CGContextAddPath(aContext, aSignalcircle2.path);
        [[UIColor redColor] set];
        CGContextFillPath(aContext);
    }
    

    【讨论】: