CGPath交集(_:using:)和好友(iOS 16新功能)坏了?

kq0g1dla  于 2023-01-03  发布在  iOS
关注(0)|答案(1)|浏览(92)

iOS 16中有CGPath(核心图形)的新功能,更准确地说,有一个函数交集(_:using:)用于确定两个CGPath的交集(请参见https://developer.apple.com/documentation/coregraphics/cgpath/3994964-intersection),以及几个相关函数用于联合、归一化、将路径分离为其组件等。
我测试了其中的几个,但似乎都不起作用。事实上,任何随机组合的非平凡示例都会给我错误的结果。例如,下面的代码给了我下面的第一张图片:紫色区域是正确的交叉点。

var pathRedFill = CGMutablePath(rect: CGRect(origin: CGPoint(x: 10, y: 10),
                                                  size: CGSize(width: 400, height: 700)),
                                     transform: nil)
           pathRedFill.addQuadCurve(to: CGPoint(x: 267, y: 121), control: CGPoint(x: 395, y: 735))
           pathRedFill.closeSubpath()
           
   var pathBlueFill = CGMutablePath(rect: CGRect(origin: CGPoint(x: 120, y: 120),
                                                  size: CGSize(width: 200, height: 200)), transform: nil)
   pathBlueFill.addQuadCurve(to: CGPoint(x: 637, y: 176), control: CGPoint(x: 343, y: 451))
           pathBlueFill.closeSubpath()
           
           context.setBlendMode(.normal)
           
           context.setFillColor(red: 1, green: 0, blue: 0, alpha: 0.5)
           context.addPath(pathRedFill)
           context.drawPath(using: .eoFill)
           
           context.setFillColor(red: 0, green: 0, blue: 1, alpha: 0.5)
           context.addPath(pathBlueFill)
           context.drawPath(using: .eoFill)

如果我添加下面的代码来计算相交路径,我得到了第二张图片。绿色线应该精确地环绕紫色区域。但它没有。

context.setLineWidth(4)
      context.setStrokeColor(red: 0, green: 1, blue: 0, alpha: 1)
      let intersectionPath = pathRedFill.intersection(pathBlueFill, using: .evenOdd)
      context.addPath(intersectionPath)
      context.drawPath(using: .stroke)

我的问题:
1.是不是我做错了什么?(文档没有对我相交的两个路径提出任何要求,所以我的路径不是太复杂,我应该认为...)
1.有人遇到过类似的错误吗?
非常感谢您的帮助!

编辑:我对iOS16中添加的所有10个函数做了更多的测试(根据DonMag纠正了我的路径公式中的错误),并试图找到我认为出错的最简单的情况。所以这(所有其他代码保持不变,只有路径发生了变化)

let pathRedFill = CGPath(rect: CGRect(origin: CGPoint(x: 200, y: 300),
                                             size: CGSize(width: 200, height: 200)),
                                transform: nil)
       
       var pathBlueFill = CGMutablePath()
       pathBlueFill.move(to: CGPoint(x: 302.854156, y: 369.438843))
       pathBlueFill.addQuadCurve(to: CGPoint(x: 296.932526, y: 386.031342), control: CGPoint(x: 218.5, y: 376.0))
       pathBlueFill.closeSubpath()

给出了结果:

绿线应该环绕紫色,但没有。
这第一个是一个例子的一部分,其中红色和蓝色路径都有很多自相交,我认为路径的自相交是问题所在(因为我有一些非常复杂的例子没有自相交,但是有洞,看起来一切都很好)但是当我完成了最小化的剥离,结果发现还有一些不一致的地方,因为在两条路径中没有更多的自相交...当四舍五入蓝色路径的数字时,误差消失,所以这可能是关于根的这种退化角情况...而且我的蓝色曲线是二次曲线(然后是闭合路径的线),而结果是交叉点,用绿色描边,是三次贝塞尔曲线我不确定我是不是还做错了什么。

8zzbczxx

8zzbczxx1#

几种可能性...
1.新的路径交叉函数(也许还有其他的)有点"bug"
1.它工作正常,但我们对路径如何构建的理解有点模糊

  1. 1和2的组合
    经过一些实验,我得到了这个与您的原始代码在iOS上(不知道为什么它是垂直翻转-您运行这个在MacOS应用程序?):

现在,据我所知,这句台词:

var pathRedFill = CGMutablePath(rect: CGRect(origin: CGPoint(x: 10, y: 10),
                                                 size: CGSize(width: 400, height: 700)),
                                    transform: nil)

等价于:

var pathRedFill = CGMutablePath()
    pathRedFill.move(to: CGPoint(x: 10, y: 10))
    pathRedFill.addLine(to: CGPoint(x: 410, y: 10))
    pathRedFill.addLine(to: CGPoint(x: 410, y: 710))
    pathRedFill.addLine(to: CGPoint(x: 10, y: 710))
    pathRedFill.closeSubpath()

接下来我们要做的是:

pathRedFill.addQuadCurve(to: CGPoint(x: 267, y: 121), control: CGPoint(x: 395, y: 735))
    pathRedFill.closeSubpath()

我们得到了预期的输出...但是,当我们完成其余的代码时,我们得到了(似乎是)一个不正确的交集。
我的猜测是,在关闭第一个子路径之后调用.addQuadCurve时,会出现某种"断开连接"。
如果我们进行此更改:

var pathRedFill = CGMutablePath(rect: CGRect(origin: CGPoint(x: 10, y: 10),
                                                 size: CGSize(width: 400, height: 700)),
                                    transform: nil)
    
    // add this line:
    pathRedFill.move(to: pathRedFill.currentPoint)
    
    pathRedFill.addQuadCurve(to: CGPoint(x: 267, y: 121), control: CGPoint(x: 395, y: 735))
    pathRedFill.closeSubpath()
    
    var pathBlueFill = CGMutablePath(rect: CGRect(origin: CGPoint(x: 120, y: 120),
                                                  size: CGSize(width: 200, height: 200)), transform: nil)

    // add this line:
    pathBlueFill.move(to: pathBlueFill.currentPoint)

    pathBlueFill.addQuadCurve(to: CGPoint(x: 637, y: 176), control: CGPoint(x: 343, y: 451))
    pathBlueFill.closeSubpath()

下面是输出:

因此,调用.moveTo可能 *"显式地启动一个新的子路径"。
然后,在内部,路径/子路径更...完整?

    • 编辑**

你问题的第二部分...
我认为路径可能比表面上看起来要复杂得多。
对于您的具体示例:

let pathRedFill = CGPath(rect: CGRect(origin: CGPoint(x: 200, y: 300),
                                      size: CGSize(width: 200, height: 200)),
                         transform: nil)

var pathBlueFill = CGMutablePath()
pathBlueFill.move(to: CGPoint(x: 302.854156, y: 369.438843))
pathBlueFill.addQuadCurve(to: CGPoint(x: 296.932526, y: 386.031342), control: CGPoint(x: 218.5, y: 376.0))
pathBlueFill.closeSubpath()

pathRedFill顺时针路径,而pathBlueFill逆时针路径。
要查看差异,请尝试更改此代码中的clockwise变量...当设置为true时,我们会得到正确的intersectionPath

override func draw(_ rect: CGRect) {
    
    guard let context = UIGraphicsGetCurrentContext()
    else { return }
    
    var pathRedFill = CGMutablePath()
    var pathBlueFill = CGMutablePath()

    // 200 x 300 rect path
    pathRedFill = CGMutablePath(rect: CGRect(origin: CGPoint(x: 200, y: 300),
                                             size: CGSize(width: 200, height: 200)),
                                transform: nil)

    // switch between true and false to see the difference
    let clockwise: Bool = false
    
    var y1: CGFloat = 369.438843
    var y2: CGFloat = 386.031342
    
    if clockwise {
        let t = y1
        y1 = y2
        y2 = t
    }
    
    pathBlueFill = CGMutablePath()
    
    pathBlueFill.move(to: CGPoint(x: 302.854156, y: y1))
    pathBlueFill.addQuadCurve(to: CGPoint(x: 296.932526, y: y2), control: CGPoint(x: 218.5, y: 376.0))
    
    pathBlueFill.closeSubpath()
    
    
    context.setBlendMode(.normal)
    
    context.setFillColor(red: 1, green: 0, blue: 0, alpha: 0.5)
    context.addPath(pathRedFill)
    context.drawPath(using: .eoFill)
    
    context.setFillColor(red: 0, green: 0, blue: 1, alpha: 0.5)
    context.addPath(pathBlueFill)
    context.drawPath(using: .eoFill)
    
    context.setLineWidth(1)
    context.setStrokeColor(red: 0, green: 1, blue: 0, alpha: 1)
    let intersectionPath = pathRedFill.intersection(pathBlueFill, using: .evenOdd)
    context.addPath(intersectionPath)
    context.drawPath(using: .stroke)

}

相关问题