У меня есть CGPath, который представляет собой эллипс. Я хотел бы поместить узел спрайта в случайную точку на этом пути. Как я могу получить случайный CGPoint на этом CGPath?
Получить случайный CGPoint на CGPath
Ответы (2)
Говоря о закрытом CGPath
и думая о быстром методе получения случайной точки между бесконечными точками внутри CGPath
, прежде всего я перевел на Swift это:
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))
}
}
Я использовал следующий метод с моим неправильным многоугольником, он основан на треугольниках, поэтому, если вы используете дуги, некоторые части кривой будут потеряны (пожалуйста, если у вас есть какой-либо совет, прокомментируйте его, я был бы признателен):
- получить все
CGPath
граничные точки (элемент, из которых состоит мой путь) - получить центр моего
CGPath
- получить список треугольников между моими точками и центром
- выбрать случайный треугольник из списка треугольников
- выбрать случайную точку внутри треугольника
Это метод треугольника:
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)
}
Это основной метод:
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])
}
Извините за воскресение :)
Возьмите ограничивающую рамку и выберите две случайные точки на двух противоположных сторонах (это должно гарантировать линию, пересекающую некоторую точку пути.
Пройдите по линии, используя path.contains(), чтобы найти первую точку на пути.
Это, конечно, не очень случайно, поскольку игнорирует «внутренние» циклы, поэтому, если вы хотите больше случайности, пройдите всю линию и запишите все пересечения, а затем выберите случайное одно из пересечений.
Это все еще не очень случайно, так как предпочтение отдается более «центральным» точкам по периметру.
Следующая оптимизация, возможно, вам нужно немного быстрее, и path.contains() не сокращает ее.
Нарисуйте свой путь к закадровому растровому изображению (обводка, без заливки), затем, чтобы проверить, находится ли точка на пути, вам нужно только получить цвет в этой точке. Это должно быть намного быстрее, чем path.contains().