You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何填充未闭合贝塞尔路径的环路部分?

如何仅填充NSBezierPath的环路部分(而非整个未闭合路径的覆盖区域)

嘿,我明白你遇到的问题了——用未闭合的NSBezierPath填充时,Core Graphics默认会自动把路径的起点和终点连接起来,形成一个大的闭合区域,导致填充范围和你想要的环路部分完全不符。别担心,这里有几个实用的解决方案,你可以根据自己的路径结构来选择:

方案一:调整填充的缠绕规则(最适合交叉路径)

Core Graphics的填充行为由缠绕规则决定,默认的non-zero winding rule会把所有被路径包围的区域都填充,而切换到even-odd winding rule后,只会填充被路径奇数次包围的区域,刚好适合处理交叉形成的环路。

你只需要在填充路径时修改规则即可,修改后的代码片段如下:

UIGraphicsBeginImageContext(board.size)
if let context = UIGraphicsGetCurrentContext() {
    if let path = snake.path {
        context.setShouldAntialias(false)
        let transformedPath = CGMutablePath()
        transformedPath.addPath(path, transform: transform)
        context.addPath(transformedPath)
        
        // 设置填充颜色(根据你的需求调整)
        context.setFillColor(UIColor.yourFillColor.cgColor)
        // 关键:使用even-odd缠绕规则填充
        context.fillPath(using: .evenOdd)
    }
}
UIGraphicsEndImageContext()

方案二:手动提取闭合子路径(适合明确包含环路的路径)

如果你的路径是由多个线段组成,其中明确包含闭合的环路子路径,你可以遍历路径的元素,把闭合的子路径单独提取出来填充,这样就能精准控制只填充环路部分。

示例代码如下:

UIGraphicsBeginImageContext(board.size)
if let context = UIGraphicsGetCurrentContext(), let cgPath = snake.path?.cgPath {
    context.setShouldAntialias(false)
    context.setFillColor(UIColor.yourFillColor.cgColor)
    
    var isSubpathClosed = false
    var currentSubpath = CGMutablePath()
    
    // 遍历CGPath的每个元素
    cgPath.applyWithBlock { element in
        switch element.type {
        case .moveToPoint:
            // 如果之前的子路径是闭合的,先填充它
            if isSubpathClosed {
                context.addPath(currentSubpath)
                context.fillPath()
                currentSubpath = CGMutablePath()
                isSubpathClosed = false
            }
            currentSubpath.move(to: element.points.pointee)
        case .addLineToPoint:
            currentSubpath.addLine(to: element.points.pointee)
        case .addQuadCurveToPoint:
            let points = element.points
            currentSubpath.addQuadCurve(to: points[1], control: points[0])
        case .addCurveToPoint:
            let points = element.points
            currentSubpath.addCurve(to: points[2], control1: points[0], control2: points[1])
        case .closeSubpath:
            currentSubpath.closeSubpath()
            isSubpathClosed = true
        @unknown default:
            break
        }
    }
    
    // 处理最后一个闭合的子路径
    if isSubpathClosed {
        context.addPath(currentSubpath)
        context.fillPath()
    }
}
UIGraphicsEndImageContext()

方案三:描边路径后提取环路(适合线条交叉形成的环路)

如果你的未闭合路径是通过线条交叉形成的环路,可以先把原路径描边成一个闭合的区域,再用even-odd规则填充,这样就能得到环路的填充效果。

示例代码如下:

UIGraphicsBeginImageContext(board.size)
if let context = UIGraphicsGetCurrentContext() {
    if let path = snake.path {
        context.setShouldAntialias(false)
        let transformedPath = CGMutablePath()
        transformedPath.addPath(path, transform: transform)
        
        // 将原路径描边成闭合区域(宽度根据你的线条粗细调整)
        let strokedPath = transformedPath.copy(
            strokingWithWidth: 2.0,
            lineCap: .butt,
            lineJoin: .miter,
            miterLimit: 10
        )
        
        context.addPath(strokedPath)
        context.setFillColor(UIColor.yourFillColor.cgColor)
        context.fillPath(using: .evenOdd)
    }
}
UIGraphicsEndImageContext()

总结

  • 如果你的路径是交叉形成的环路,优先尝试方案一,最简单高效;
  • 如果路径里有明确的闭合子路径,方案二能精准控制填充区域;
  • 如果是线条交叉形成的环路,方案三可以把线条转化为闭合区域再填充。

内容的提问来源于stack exchange,提问作者Connor Yass

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:16:49