4

As UIBeizerPath supports for fill() to fill the enclosed area. My requirement is how do I get the enclosed area from UIBeizerPath() same as fill() method is using. It would be very thankful if I could get help in finding area and perimeter of the beizerpath.

Suppose I want to get the enclosed area in letter 'A', I can get all the beizerPath CGPoints but can't define subPaths as evenOdd rule use to get the required field properties and finally get area. I am very curious to know how fill() is able to get the enclosed area.

I got beizerPath for font as in this solution and want get area for any fonts drawn from this.

How could I achieve this.

Community
  • 1
  • 1
rjndra
  • 121
  • 1
  • 9
  • @matt the fillable area. as we know 'fill()' method fills the area with color we specify to fill with. the blue color area used by P see the [link](https://i.stack.imgur.com/9Cyqw.png) – rjndra Nov 24 '16 at 05:00
  • actually i am drawing a beizer from the font so I have irregular ploygon with any cgpoints used to make the beizer for fonts. can see what I am doing is [like](http://stackoverflow.com/questions/11172207/get-path-to-trace-out-a-character-in-an-ios-uifont) and getting area for those to compare with gesture drawn character – rjndra Nov 24 '16 at 05:14
  • It would be nice of anybody would provide hint on how `UIbeizer` has achieved this math section regardless of the shape drawn. Reference to any programming language will be accepted as answer. – rjndra Nov 25 '16 at 05:41

1 Answers1

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


extension UIBezierPath{
    var length: CGFloat{
        var pathLength:CGFloat = 0.0
        var current =  CGPoint.zero
        var first   =   CGPoint.zero

        self.cgPath.forEach{ element in
            pathLength += element.distance(to: current, startPoint: first)

            if element.type == .moveToPoint{
                first = element.point
            }
            if element.type != .closeSubpath{
                current = element.point
            }
        }
        return pathLength
    }
}


extension CGPathElement{

    var point: CGPoint{
        switch type {
        case .moveToPoint, .addLineToPoint:
            return self.points[0]
        case .addQuadCurveToPoint:
            return self.points[1]
        case .addCurveToPoint:
            return self.points[2]
        case .closeSubpath:
            return CGRect.null.origin
        }
    }

    func distance(to point: CGPoint, startPoint: CGPoint ) -> CGFloat{
        switch type {
        case .moveToPoint:
            return 0.0
        case .closeSubpath:
            return point.distance(to:startPoint)
        case .addLineToPoint:
            return point.distance(to:self.points[0])
        case .addCurveToPoint:
            return BezierCurveLength(p0: point, c1: self.points[0], c2: self.points[1], p1: self.points[2])
        case .addQuadCurveToPoint:
            return BezierCurveLength(p0: point, c1: self.points[0], p1: self.points[1])
        }
    }
}

extension CGPoint{
    func distance(to:CGPoint) -> CGFloat{
        let dx = pow(to.x - self.x,2)
        let dy = pow(to.y - self.y,2)
        return sqrt(dx+dy)
    }
}



// Helper Functions

func CubicBezierCurveFactors(t:CGFloat) -> (CGFloat,CGFloat,CGFloat,CGFloat){
    let t1 = pow(1.0-t, 3.0)
    let t2 = 3.0*pow(1.0-t,2.0)*t
    let t3 = 3.0*(1.0-t)*pow(t,2.0)
    let t4 = pow(t, 3.0)

    return  (t1,t2,t3,t4)
}


func QuadBezierCurveFactors(t:CGFloat) -> (CGFloat,CGFloat,CGFloat){
    let t1 = pow(1.0-t,2.0)
    let t2 = 2.0*(1-t)*t
    let t3 = pow(t, 2.0)

    return (t1,t2,t3)
}

// Quadratic Bezier Curve

func BezierCurve(t:CGFloat,p0:CGFloat,c1:CGFloat,p1:CGFloat) -> CGFloat{
    let factors = QuadBezierCurveFactors(t: t)
    return (factors.0*p0) + (factors.1*c1) + (factors.2*p1)
}


// Quadratic Bezier Curve

func BezierCurve(t:CGFloat,p0:CGPoint,c1:CGPoint,p1:CGPoint) -> CGPoint{
    let x = BezierCurve(t: t, p0: p0.x, c1: c1.x, p1: p1.x)
    let y = BezierCurve(t: t, p0: p0.y, c1: c1.y, p1: p1.y)
    return CGPoint(x: x, y: y)
}



// Cubic Bezier Curve

func BezierCurve(t:CGFloat,p0:CGFloat, c1:CGFloat, c2:CGFloat, p1:CGFloat) -> CGFloat{
    let factors = CubicBezierCurveFactors(t: t)
    return (factors.0*p0) + (factors.1*c1) + (factors.2*c2) + (factors.3*p1)
}


// Cubic Bezier Curve

func BezierCurve(t: CGFloat, p0:CGPoint, c1:CGPoint, c2: CGPoint, p1: CGPoint) -> CGPoint{
    let x = BezierCurve(t: t, p0: p0.x, c1: c1.x, c2: c2.x, p1: p1.x)
    let y = BezierCurve(t: t, p0: p0.y, c1: c1.y, c2: c2.y, p1: p1.y)
    return CGPoint(x: x, y: y)
}


// Cubic Bezier Curve Length

func BezierCurveLength(p0:CGPoint,c1:CGPoint, c2:CGPoint, p1:CGPoint) -> CGFloat{
    let steps = 12
    var current  = p0
    var previous = p0
    var length:CGFloat = 0.0

    for i in 1...steps{
        let t = CGFloat(i) / CGFloat(steps)
        current = BezierCurve(t: t, p0: p0, c1: c1, c2: c2, p1: p1)
        length += previous.distance(to: current)
        previous = current
    }

    return length
}


// Quadratic Bezier Curve Length

func BezierCurveLength(p0:CGPoint,c1:CGPoint, p1:CGPoint) -> CGFloat{
    let steps = 12

    var current  = p0
    var previous = p0
    var length:CGFloat = 0.0

    for i in 1...steps{
        let t = CGFloat(i) / CGFloat(steps)
        current = BezierCurve(t: t, p0: p0, c1: c1, p1: p1)
        length += previous.distance(to: current)
        previous = current
    }
    return length
}
Samip Shah
  • 854
  • 7
  • 11
  • This actually worked for me getting the perimeter. Please is there any solution regarding area. Thanks @Samip – rjndra Nov 25 '16 at 06:33
  • 1
    For the area I got a solution myself my obtaining all the `cgpoints` in the `beizerPath` within the bounds of the path. Area actually is the unit number of cgpoints contained in the bounds. – rjndra Nov 25 '16 at 12:11