4

Is there any workaround to know where a CGpath (created using an array of co-ordinates) is closed.

I was not able to find any suitable method in the header CGPath.

I wish to track the path traced by user .If its a closed loop, then i wish to extract that part from the context. Something like masking or clipping the context but it should be user tracked clipping.

Thanks!

Abhishek Bedi
  • 5,205
  • 2
  • 36
  • 62
  • [CGPathContainsPoint](https://developer.apple.com/library/ios/documentation/graphicsimaging/Reference/CGPath/Reference/reference.html#//apple_ref/c/func/CGPathContainsPoint) may help you! , [A sample project that could help you](https://github.com/ole/CGPathHitTesting) and [a discussion about it](http://oleb.net/blog/2012/02/cgpath-hit-testing/) – Bala Dec 07 '12 at 15:41
  • Dear Bala, I appreciate your thought but that's not exactly what i was looking for.Also the link is useful, still it doesn't serves the purpose. Thanks anyways for your time. – Abhishek Bedi Dec 10 '12 at 07:29
  • it could be more helpful if you update and explain your question with more specific about what you are looking for..? – Bala Dec 10 '12 at 13:49
  • @Bala : I wish to track the path traced by user .If its a closed loop, then i wish to extract that part from the context.Something like masking or clipping context but it should be user tracked clipping. – Abhishek Bedi Dec 11 '12 at 06:49

2 Answers2

2

In fact, a path can consist of multiple subpaths. A new subpath is created when you move the path's current point without connecting the two points. For the path to be closed, all its subpaths must in fact be closed.

extension CGPath {

    /// Note that adding a line or curve to the start point of the current
    /// subpath does not close it. This can be visualized by stroking such a
    /// path with a line cap of `.butt`.
    public var isClosed: Bool {
        var completedSubpathsWereClosed = true
        var currentSubpathIsClosed = true

        self.applyWithBlock({ pointer in
            let element = pointer.pointee

            switch element.type {
            case .moveToPoint:
                if !currentSubpathIsClosed {
                    completedSubpathsWereClosed = false
                }

                currentSubpathIsClosed = true
            case .addLineToPoint, .addQuadCurveToPoint, .addCurveToPoint:
                currentSubpathIsClosed = false
            case .closeSubpath:
                currentSubpathIsClosed = true
            }
        })

        return completedSubpathsWereClosed && currentSubpathIsClosed
    }
}
Christian Schnorr
  • 10,768
  • 8
  • 48
  • 83
1

Just in case you've been stuck on this for the last 4 1/2 years, here is how to do it in Swift 3. I have borrowed heavily from this answer. I really just added the isClosed() function.

extension CGPath {

func isClosed() -> Bool {
    var isClosed = false
    forEach { element in
        if element.type == .closeSubpath { isClosed = true }
    }
    return isClosed
}

func forEach( body: @convention(block) (CGPathElement) -> Void) {
    typealias Body = @convention(block) (CGPathElement) -> Void
    let callback: @convention(c) (UnsafeMutableRawPointer, UnsafePointer<CGPathElement>) -> Void = { (info, element) in
        let body = unsafeBitCast(info, to: Body.self)
        body(element.pointee)
    }
    print(MemoryLayout.size(ofValue: body))
    let unsafeBody = unsafeBitCast(body, to: UnsafeMutableRawPointer.self)
    self.apply(info: unsafeBody, function: unsafeBitCast(callback, to: CGPathApplierFunction.self))
}
}

And assuming path is a CGPath or CGMutablePath, here is how you use it:

path.isClosed()
mogelbuster
  • 1,066
  • 9
  • 19
  • Just because a subpath is closed doesn't mean the path as a whole is closed, see my answer for a correct implementation when the path consists of multiple subpaths. – Christian Schnorr Jan 06 '19 at 16:59