Получить случайный CGPoint на CGPath

У меня есть CGPath, который представляет собой эллипс. Я хотел бы поместить узел спрайта в случайную точку на этом пути. Как я могу получить случайный CGPoint на этом CGPath?


person JuJoDi    schedule 15.06.2014    source источник
comment
Единственное решение, которое я когда-либо видел, которое не включало суперматематику, было то, где путь был обведен пунктирной линией 1, а затем точки были экстраполированы из этого обведенного пути. Не знаю, так ли это или все еще актуально.   -  person Cooper Buckingham    schedule 16.06.2014
comment
Вы когда-нибудь решали это? У меня просто есть некоторая расплывчатая стратегия: если эллипс идеально круглый, как часы, мы могли бы легко изменить начальную точку, просто используя преобразование с поворотом при создании эллипса. Теперь, в моем текущем случае, мой эллипс НЕ идеально круглый, но, возможно, с некоторой настройкой он все еще может работать... хм...   -  person Jonny    schedule 31.03.2015
comment
@Jonny Джонни, я не решил эту проблему, но, как и в вашей идее, вы можете создать круг, повернуть его случайным образом между 0 и 2pi, а затем преобразовать его в эллипс, и начальная точка эллипса будет случайной? Прошло некоторое время с тех пор, как я посещал эту проблему, поэтому я не помню, нужно ли преобразовать ее в эллипс.   -  person JuJoDi    schedule 31.03.2015
comment
Я решил это другим способом, который кто-то указал; мы можем нарисовать пунктирную линию по пути, а затем использовать CGPathApply (я думаю), чтобы перебрать все действия/элементы рисования, используемые для рисования этого пунктирного пути. Это немного сложно начать, но определенно работает. Мы можем использовать точки этих действий как точки на пути эллипса. Это потребует некоторой настройки. Вы, вероятно, не получите бесконечные случайные точки эллипса, возможно, только начальные точки этих точек, или как бы вы их ни настроили.   -  person Jonny    schedule 01.04.2015
comment
Добрый день, кто-нибудь решил проблему. У меня также есть один и тот же термин с разными формами, такими как квадрат, ромб, ромб и т. Д., И я хочу работать с такими же ... Пожалуйста, напишите ответ или несколько полезных ссылок. Спасибо всем.   -  person Chetan Prajapati    schedule 08.02.2016


Ответы (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])
}
person Alessandro Ornano    schedule 12.06.2016

Извините за воскресение :)

Возьмите ограничивающую рамку и выберите две случайные точки на двух противоположных сторонах (это должно гарантировать линию, пересекающую некоторую точку пути.

Пройдите по линии, используя path.contains(), чтобы найти первую точку на пути.

Это, конечно, не очень случайно, поскольку игнорирует «внутренние» циклы, поэтому, если вы хотите больше случайности, пройдите всю линию и запишите все пересечения, а затем выберите случайное одно из пересечений.

Это все еще не очень случайно, так как предпочтение отдается более «центральным» точкам по периметру.

Следующая оптимизация, возможно, вам нужно немного быстрее, и path.contains() не сокращает ее.

Нарисуйте свой путь к закадровому растровому изображению (обводка, без заливки), затем, чтобы проверить, находится ли точка на пути, вам нужно только получить цвет в этой точке. Это должно быть намного быстрее, чем path.contains().

person Gordon Dove    schedule 29.10.2016
comment
Можете ли вы предоставить код, соответствующий этому ответу? - person JuJoDi; 30.10.2016