Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get random CGPoint on CGPath

I have a CGPath that represents an ellipse. I'd like to place a sprite node at a random point on that path. How can I get a random CGPoint on that CGPath?

like image 768
JuJoDi Avatar asked Apr 10 '26 19:04

JuJoDi


1 Answers

Speaking about a closed CGPath and thinking to a fast method to obtain a random point between infinite points inside a CGPath, first of all I've translated to Swift this:

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
    }

    func spriteKitCenter() ->CGPoint {
        let shape = SKShapeNode(path: self)
        return CGPointMake(CGRectGetMidX(shape.frame),CGRectGetMidY(shape.frame))
    }

    func uiKitCenter() ->CGPoint {
        let layer = CAShapeLayer()
        layer.path = self
        return CGPointMake(CGRectGetMidX(layer.frame),CGRectGetMidY(layer.frame))
    }
}

I used the follow method with my irregulars polygon, it's based on triangles so if you use arcs some curve parts will be losts (please if you have any advice comment it, I'd be grateful):

  • get all CGPath border points (element that compose my path)
  • get the center of my CGPath
  • obtain a list of triangles between my points and the center
  • choose a random triangle from the triangles list
  • choose a random point inside the triangle

This is the triangle method:

func getRandomPointInTriangle (aPoint:CGPoint,bPoint:CGPoint,cPoint:CGPoint)->CGPoint {

    var randomA = CGFloat(Float(arc4random()) / Float(UINT32_MAX))
    var randomB = CGFloat(Float(arc4random()) / Float(UINT32_MAX))

    if (randomA + randomB > 1) {
        randomA = 1-randomA
        randomB = 1-randomB
    }

    let randomC = 1-randomA-randomB

    let rndX = (randomA*aPoint.x)+(randomB*bPoint.x)+(randomC*cPoint.x)
    let rndY = (randomA*aPoint.y)+(randomB*bPoint.y)+(randomC*cPoint.y)

    return CGPointMake(rndX,rndY)
}

This is the main method:

func getRandomPoint()->CGPoint {
        let points = self.path!.getPathElementsPoints()
        let center = self.path!.spriteKitCenter()
        // divide the cgpath in triangles
        var triangles = Array<Array<CGPoint>>()
        for i in 0..<points.count-1 {
            let triangle = [center,points[i],points[i+1]]
            triangles.append(triangle)
        }
        let chooseTriangleNum = Int(arc4random()) % triangles.count
        let chooseTriangle = triangles[chooseTriangleNum]
        return getRandomPointInTriangle(chooseTriangle[0],bPoint: chooseTriangle[1],cPoint: chooseTriangle[2])
}
like image 168
Alessandro Ornano Avatar answered Apr 17 '26 17:04

Alessandro Ornano



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!