Подтвердить что ты не робот

Как получить CGPoint CGPath

Как я могу получить массив со всеми CGPoint (s), содержащимися в заданном CGPath (CGMutablePathRef)?

4b9b3361

Ответ 1

Вы можете использовать CGPathApply() для повторения каждого сегмента в пути и запуска пользовательской функции с этим сегментом. Это даст вам всю информацию, имеющую путь.

Однако, если "все CGPoint (ы)", вы имели в виду каждую точку, которая имеет пиксель, предоставленный ему, это набор бесконечного размера. Хотя вы, безусловно, можете использовать функцию apply для получения каждого сегмента, а затем для каждого сегмента без перемещения, оцените свою собственную математику с помощью контрольных точек сегмента, чтобы получить список точек в любой плотности, которую вы хотите.

Ответ 2

Использование Swift 2.x (для Swift 3.x, Swift 4.x и Swift 5.x читайте здесь ниже).), я нашел эту фантастическую статью о C Callbacks в Swift.

Попытка получить "все CGPoint (и)", как объясняет Лили Баллард, может быть плохой идеей, как она сказала.

Поэтому я думаю, что, возможно, лучший способ - получить точки элементов пути, используемые для создания определенного CGPath:

//MARK: - CGPath extensions
extension CGPath {
    func forEach(@noescape body: @convention(block) (CGPathElement) -> Void) {
        typealias Body = @convention(block) (CGPathElement) -> Void
        func callback(info: UnsafeMutablePointer<Void>, element: UnsafePointer<CGPathElement>) {
            let body = unsafeBitCast(info, Body.self)
            body(element.memory)
        }
        print(sizeofValue(body))
        let unsafeBody = unsafeBitCast(body, UnsafeMutablePointer<Void>.self)
        CGPathApply(self, unsafeBody, callback)
    }

    func getPathElementsPoints() -> [CGPoint] {
        var arrayPoints : [CGPoint]! = [CGPoint]()
        self.forEach { element in
            switch (element.type) {
            case CGPathElementType.MoveToPoint:
                arrayPoints.append(element.points[0])
            case .AddLineToPoint:
                arrayPoints.append(element.points[0])
            case .AddQuadCurveToPoint:
                arrayPoints.append(element.points[0])
                arrayPoints.append(element.points[1])
            case .AddCurveToPoint:
                arrayPoints.append(element.points[0])
                arrayPoints.append(element.points[1])
                arrayPoints.append(element.points[2])
            default: break
            }
        }
        return arrayPoints
    }
}

С этим расширением вы можете сделать, например:

var bezier = UIBezierPath(ovalInRect: CGRectMake(0, 0, 400, 300))
let myOval = bezier.CGPath
let junctionPoints = myOval.getPathElementsPoints()
print("junction points are: \(junctionPoints)")

Swift 3.x и Swift 4.1 (см. ниже Swift 4.2 или более позднюю версию..)

(есть некоторые исправления из-за повторного введения синтаксиса @convention(c)):

extension CGPath {

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


    func getPathElementsPoints() -> [CGPoint] {
        var arrayPoints : [CGPoint]! = [CGPoint]()
        self.forEach { element in
            switch (element.type) {
            case CGPathElementType.moveToPoint:
                arrayPoints.append(element.points[0])
            case .addLineToPoint:
                arrayPoints.append(element.points[0])
            case .addQuadCurveToPoint:
                arrayPoints.append(element.points[0])
                arrayPoints.append(element.points[1])
            case .addCurveToPoint:
                arrayPoints.append(element.points[0])
                arrayPoints.append(element.points[1])
                arrayPoints.append(element.points[2])
            default: break
            }
        }
        return arrayPoints
    }

    func getPathElementsPointsAndTypes() -> ([CGPoint],[CGPathElementType]) {
        var arrayPoints : [CGPoint]! = [CGPoint]()
        var arrayTypes : [CGPathElementType]! = [CGPathElementType]()
        self.forEach { element in
            switch (element.type) {
            case CGPathElementType.moveToPoint:
                arrayPoints.append(element.points[0])
                arrayTypes.append(element.type)
            case .addLineToPoint:
                arrayPoints.append(element.points[0])
                arrayTypes.append(element.type)
            case .addQuadCurveToPoint:
                arrayPoints.append(element.points[0])
                arrayPoints.append(element.points[1])
                arrayTypes.append(element.type)
                arrayTypes.append(element.type)
            case .addCurveToPoint:
                arrayPoints.append(element.points[0])
                arrayPoints.append(element.points[1])
                arrayPoints.append(element.points[2])
                arrayTypes.append(element.type)
                arrayTypes.append(element.type)
                arrayTypes.append(element.type)
            default: break
            }
        }
        return (arrayPoints,arrayTypes)
    }
}

Swift> 4.1 (также Swift 5.x) и iOS 9.x и> совместимые

extension CGPath {
    func forEach( body: @escaping @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))
    }
    func getPathElementsPoints() -> [CGPoint] {
        var arrayPoints : [CGPoint]! = [CGPoint]()
        self.forEach { element in
            switch (element.type) {
            case CGPathElementType.moveToPoint:
                arrayPoints.append(element.points[0])
            case .addLineToPoint:
                arrayPoints.append(element.points[0])
            case .addQuadCurveToPoint:
                arrayPoints.append(element.points[0])
                arrayPoints.append(element.points[1])
            case .addCurveToPoint:
                arrayPoints.append(element.points[0])
                arrayPoints.append(element.points[1])
                arrayPoints.append(element.points[2])
            default: break
            }
        }
        return arrayPoints
    }
    func getPathElementsPointsAndTypes() -> ([CGPoint],[CGPathElementType]) {
        var arrayPoints : [CGPoint]! = [CGPoint]()
        var arrayTypes : [CGPathElementType]! = [CGPathElementType]()
        self.forEach { element in
            switch (element.type) {
            case CGPathElementType.moveToPoint:
                arrayPoints.append(element.points[0])
                arrayTypes.append(element.type)
            case .addLineToPoint:
                arrayPoints.append(element.points[0])
                arrayTypes.append(element.type)
            case .addQuadCurveToPoint:
                arrayPoints.append(element.points[0])
                arrayPoints.append(element.points[1])
                arrayTypes.append(element.type)
                arrayTypes.append(element.type)
            case .addCurveToPoint:
                arrayPoints.append(element.points[0])
                arrayPoints.append(element.points[1])
                arrayPoints.append(element.points[2])
                arrayTypes.append(element.type)
                arrayTypes.append(element.type)
                arrayTypes.append(element.type)
            default: break
            }
        }
        return (arrayPoints,arrayTypes)
    }
}

Ответ 3

Apple добавила метод экземпляра CGPath.applyWithBlock, доступный для iOS11. 0+ и macOS10. 13+

Вы все еще можете исследовать каждый элемент пути с помощью CGPath.apply, как описано в предыдущих ответах. Но если вы хотите избежать использования указателей в стиле C и unsafeBitCast, вам следует использовать гораздо более удобный метод экземпляра applyWithBlock. Например, если вы хотите получить массив CGPoints CGPath, вы можете добавить расширение к CGPath, в этом примере вычисляемое свойство (points), которое собирает CGPoints CGPath:

extension CGPath {

  ///< this is a computed property
  var points: [CGPoint] {

     ///< this is a local container where we will store our CGPoints
     var arrPoints: [CGPoint] = []

     ///< applyWithBlock lets us examine each element of the CGPath, and decide what to do
     self.applyWithBlock { element in

        switch element.pointee.type
        {
        case .moveToPoint, .addLineToPoint:
          arrPoints.append(element.pointee.points.pointee)

        case .addQuadCurveToPoint:
          arrPoints.append(element.pointee.points.pointee)
          arrPoints.append(element.pointee.points.advance(by: 1).pointee))

        case .addCurveToPoint:
          arrPoints.append(element.pointee.points.pointee)
          arrPoints.append(element.pointee.points.advance(by: 1).pointee))
          arrPoints.append(element.pointee.points.advance(by: 2).pointee))

        default:
          break
        }
     }

    ///< We are now done collecting our CGPoints and so we can return the result
    return arrPoints

  }
}

Ответ 4

CGPath является непрозрачным типом данных и не обязательно хранит все используемые точки. В дополнение к этому, путь может не отображать все точки, используемые в качестве входных данных (например, рассмотрим контрольные точки Безье).

Единственными двумя задокументированными способами получения информации из пути является использование CGPathGetBoundingBox для получения ограничивающего прямоугольника или более сложный метод использования CGPathApply для вызова функции обратного вызова, которая даст вам последовательность, если CGPathElement тип.