Solution for iOS12.4 / XCode 10.3 / Swift 5:
First, add this extension into you code, to extract the CGPoints of the path into an array:
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)
}
}
source: How to get the CGPoint(s) of a CGPath (answer from user Alessandro Ornano; swift 5.x Version)
Second, my example code is:
let tmp_size = 100
var path = CGMutablePath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 0, y: tmp_size / 2 ))
path.addLine(to: CGPoint(x: tmp_size / 2, y: tmp_size / 2))
path.addLine(to: CGPoint(x: tmp_size / 2, y: tmp_size ))
path.addLine(to: CGPoint(x: tmp_size , y: tmp_size ))
path.addLine(to: CGPoint(x: tmp_size , y: 0 ))
path.closeSubpath()
var pb = SKPhysicsBody(polygonFrom: path)
print("area1: \(getAreaFromPolygon(path: path))")
path = CGMutablePath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 0, y: tmp_size))
path.addLine(to: CGPoint(x: tmp_size, y: tmp_size))
path.addLine(to: CGPoint(x: tmp_size, y: 0))
path.closeSubpath()
pb = SKPhysicsBody(polygonFrom: path)
print("area2: \(getAreaFromPolygon(path: path))")
And last, my function to calculate the area of the polygon (in CGPoints):
func getAreaFromPolygon (path: CGMutablePath) -> Double {
// get an array of the CGPoints of this path
let PathArray = path.getPathElementsPoints()
// store all CGPoint.x into an array
var xArray = [Int]()
for PosX in PathArray {
xArray.append(Int(PosX.x))
}
// store all CGPoints.y into an array
var yArray = [Int]()
for PosY in PathArray {
yArray.append(Int(PosY.y))
}
// calculate the area
var area = 0.0
for i in 0..<PathArray.count {
let firstPart = Double((yArray[i] + yArray[(i+1) % PathArray.count]))
let secondPart = (xArray[i] - xArray[(i+1) % PathArray.count])
area += Double(firstPart) * Double(secondPart)
}
area = abs(area) / 2.0
return area
}
source: https://de.wikipedia.org/wiki/Gaußsche_Trapezformel#Programmcode
(was in Java, but i translated it into Swift 5)
note: i divided the formula into two sections (fristPart and secondPart) since it was too complex for Xcode to check
btw, my output is:
area1: 7500.0
area2: 10000.0
which is correct, since area1 ist 75% from the area2.
P.S.: i only have polygons of rectangles, but it should also work with any polygons (except maybe polygons with intersecting lines)