Как я могу получить массив со всеми CGPoint
(s), содержащимися в заданном CGPath
(CGMutablePathRef
)?
Как получить CGPoint CGPath
Ответ 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
тип.