Pytanie Obróć CGPath bez zmiany jego położenia


Chcę obrócić a CGPathUżywam następującego kodu:

CGAffineTransform transform = CGAffineTransformMakeRotation(angleInRadians);
CGPathRef rotatedPath = CGPathCreateCopyByTransformingPath(myPath, &transform);

Ten kod działa poprawnie, ale zmienia pozycję ścieżki! Chcę, aby ścieżka pozostała w tej samej pozycji, w jakiej znajdowała się przed obrotem.


10
2017-12-06 06:48


pochodzenie


Ścieżka będzie się obracać wokół pewnego punktu. Musi. Jak myślisz, w jakim punkcie powinien się obracać? Z twojego pytania nie wynika jednoznacznie. - David Rönnqvist
wokół punktu centralnego - Asif Mujteba
Wierzę, że ścieżka obróci się (0, 0). Możesz dokonać przekształcenia, przetłumaczyć środek na (0,0), a następnie obrócić, a następnie przekształcić z powrotem. - David Rönnqvist


Odpowiedzi:


Ścieżka nie ma "pozycji". Ścieżka to zestaw punktów (zdefiniowanych przez segmenty linii i łuków). Każdy punkt ma swoją własną pozycję.

Być może chcesz obrócić ścieżkę wokół określonego punktu, zamiast wokół źródła. Sztuką jest stworzenie złożonej transformacji, która łączy w sobie trzy indywidualne transformacje:

  1. Przetłumacz pochodzenie na punkt obrotu.
  2. Obracać się.
  3. Odwróć tłumaczenie z kroku 1.

Na przykład tutaj jest funkcja, która pobiera ścieżkę i zwraca nową ścieżkę, która jest oryginalną ścieżką obróconą wokół środka obwiedni:

static CGPathRef createPathRotatedAroundBoundingBoxCenter(CGPathRef path, CGFloat radians) {
    CGRect bounds = CGPathGetBoundingBox(path); // might want to use CGPathGetPathBoundingBox
    CGPoint center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
    CGAffineTransform transform = CGAffineTransformIdentity;
    transform = CGAffineTransformTranslate(transform, center.x, center.y);
    transform = CGAffineTransformRotate(transform, radians);
    transform = CGAffineTransformTranslate(transform, -center.x, -center.y);
    return CGPathCreateCopyByTransformingPath(path, &transform);
}

Zwróć uwagę, że ta funkcja zwraca nową ścieżkę z liczbą zatrzymań +1, którą jesteś odpowiedzialny za zwolnienie, gdy skończysz. Na przykład, jeśli próbujesz obrócić ścieżkę warstwy kształtu:

- (IBAction)rotateButtonWasTapped:(id)sender {
    CGPathRef path = createPathRotatedAroundBoundingBoxCenter(shapeLayer_.path, M_PI / 8);
    shapeLayer_.path  = path;
    CGPathRelease(path);
}

AKTUALIZACJA

Oto demonstracja z użyciem placu zabaw Swift. Zaczniemy od funkcji pomocnika wyświetlającej ścieżkę i oznaczającej punkt początkowy za pomocą krzyżyka:

import UIKit
import XCPlayground

func showPath(label: String, path: UIBezierPath) {
    let graph = UIBezierPath()
    let r = 40
    graph.moveToPoint(CGPoint(x:0,y:r))
    graph.addLineToPoint(CGPoint(x:0,y:-r))
    graph.moveToPoint(CGPoint(x:-r,y:0))
    graph.addLineToPoint(CGPoint(x:r,y:0))
    graph.appendPath(path)
    XCPCaptureValue(label, graph)
}

Dalej, oto nasza ścieżka testowa:

var path = UIBezierPath()
path.moveToPoint(CGPoint(x:1000,y:1000))
path.addLineToPoint(CGPoint(x:1000,y:1200))
path.addLineToPoint(CGPoint(x:1100,y:1200))
showPath("original", path)

original path

(Pamiętaj, że celownik jest źródłem i nie jest częścią ścieżki, którą przekształcamy.)

Dostajemy centrum i przekształcamy ścieżkę tak, aby była skoncentrowana na punkcie początkowym:

let bounds = CGPathGetBoundingBox(path.CGPath)
let center = CGPoint(x:CGRectGetMidX(bounds), y:CGRectGetMidY(bounds))

let toOrigin = CGAffineTransformMakeTranslation(-center.x, -center.y)
path.applyTransform(toOrigin)
showPath("translated center to origin", path)

path translated to origin

Następnie obracamy go. Wszystkie rotacje zachodzą wokół miejsca pochodzenia:

let rotation = CGAffineTransformMakeRotation(CGFloat(M_PI / 3.0))
path.applyTransform(rotation)
showPath("rotated", path)

path rotated

Na koniec tłumaczymy to, dokładnie odwracając oryginalne tłumaczenie:

let fromOrigin = CGAffineTransformMakeTranslation(center.x, center.y)
path.applyTransform(fromOrigin)
showPath("translated back to original center", path)

path translated from origin

Zauważ, że musimy odwróć oryginalne tłumaczenie. Nie tłumaczymy przez środek nowego obramowania. Przypomnij sobie (w tym przykładzie), że oryginalne centrum znajduje się na (1050,1100). Ale po przetłumaczeniu go na pochodzenie i obróceniu, nowy środek obwiedni jest w (-25,0). Tłumaczenie ścieżki przez (-25,0) nie spowoduje jej nigdzie w pobliżu pierwotnej pozycji!


55
2017-12-06 07:08



Znakomita odpowiedź, dzięki. - PKCLsoft
Obwiednia zmieni się podczas obrotu, powiedzmy na 45 stopni, a skończy się ścieżką o różnych wymiarach beziera. Aby cofnąć tłumaczenie, prawdopodobnie musiałbyś otrzymać nowe pole obwiedni i odwrócić używając nowego początku. Odwracanie ze starym środkiem nie będzie działać, ponieważ po obrocie ścieżki mogą rosnąć. - highmaintenance
Cofanie tłumaczenia oznacza wykonanie dokładnie odwrotnego od pierwotnego tłumaczenia. Nie chcesz patrzeć na nowe obramowanie. Musisz przekształcić ścieżkę przez odwrotność oryginalnej transformacji tłumaczenia. - rob mayoff
@Andy Zaktualizowałem swoją odpowiedź, aby pokazać proces krok po kroku. - rob mayoff
@robmayoff wspaniały post, bardzo dziękuję za tę aktualizację z Playground. Prawdopodobnie mieszałem tutaj różne rzeczy. Próbowałem obrócić ścieżkę Beziera i wyrenderować ją na obrazie. W tym celu musiałem zresetować tłumaczenie, aby upewnić się, że ścieżka Beziera pasuje do granic obrazu. Zasadniczo użyłem (x, y) z bezierPath.bounds, aby cofnąć tłumaczenie. - highmaintenance


Metoda Swift 3 do obracania prostokąta w miejscu wokół jego środka:

func createRotatedCGRect(rect: CGRect, radians: CGFloat) -> CGPath {
    let center = CGPoint(x: rect.midX, y: rect.midY)
    let path = UIBezierPath(rect: rect)
    path.apply(CGAffineTransform(translationX: center.x, y: center.y).inverted())
    path.apply(CGAffineTransform(rotationAngle: radians))
    path.apply(CGAffineTransform(translationX: center.x, y: center.y))
    return path.cgPath
}

Cały kod roboffa mayoffa do zastosowania go do ścieżki i jej ramki granicznej nadal będzie obowiązywał, więc nie widziałem powodu, aby je powtarzać.


1
2018-02-02 22:27