12

を回転させたいのですがCGPath、次のコードを使用しています。

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

このコードは正常に機能しますが、パスの位置が変わります。パスを回転前と同じ位置に維持したい。

4

4 に答える 4

63

パスには「位置」がありません。パスは、ポイントのセットです(線分と曲線セグメントによって定義されます)。すべてのポイントには独自の位置があります。

おそらく、原点の周りではなく、特定のポイントの周りでパスを回転させたいと思うでしょう。秘訣は、3つの個別の変換を組み合わせた複合変換を作成することです。

  1. 原点を回転点に移動します。
  2. 回転します。
  3. 手順1の翻訳を逆にします。

たとえば、パスを取得して、バウンディングボックスの中心を中心に回転した元のパスである新しいパスを返す関数を次に示します。

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

この関数は、+1の保持カウントを持つ新しいパスを返すことに注意してください。このパスは、使い終わったときに解放する責任があります。たとえば、シェイプレイヤーのパスを回転させようとしている場合:

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

アップデート

これは、Swiftプレイグラウンドを使用したデモンストレーションです。パスを表示し、十字線で原点をマークするヘルパー関数から始めます。

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

次に、テストパスは次のとおりです。

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)

元のパス

(十字線は原点であり、変換するパスの一部ではないことを忘れないでください。)

中心を取得し、原点を中心とするようにパスを変換します。

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)

原点に変換されたパス

次に、それを回転させます。すべての回転は原点を中心に行われます。

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

パスが回転しました

最後に、元の翻訳を正確に反転して、元に戻します。

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

原点から変換されたパス

元の翻訳を反転する必要があることに注意してください。新しいバウンディングボックスの中心では翻訳しません。(この例では)元の中心は(1050,1100)にあることを思い出してください。ただし、原点に変換して回転させた後、新しいバウンディングボックスの中心は(-25,0)になります。パスを(-25,0)で変換しても、元の位置に近づくことはありません。

于 2012-12-06T07:08:47.077 に答える
9

Swift 5バージョン:

func rotate(path: UIBezierPath, degree: CGFloat) {
    let bounds: CGRect = path.cgPath.boundingBox
    let center = CGPoint(x: bounds.midX, y: bounds.midY)

    let radians = degree / 180.0 * .pi
    var transform: CGAffineTransform = .identity
    transform = transform.translatedBy(x: center.x, y: center.y)
    transform = transform.rotated(by: radians)
    transform = transform.translatedBy(x: -center.x, y: -center.y)
    path.apply(transform)
}
于 2019-11-11T17:56:44.810 に答える
1

長方形をその中心の周りの所定の位置に回転させるSwift3の方法:

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
}

これをパスとそのバウンディングボックスに適用するためのrobmayoffのコードはすべて引き続き適用されるため、それらを繰り返す理由はわかりませんでした。

于 2017-02-02T22:27:04.060 に答える
1

UIBezierPathを回転および/またはスケーリングする場合、またはUIBezierPathの中心を維持したままCAShapeLayerをスケーリングする場合。ここにSwift4/5の関数があります。これは、robmayoffからの回答に基づいています。

extension UIBezierPath {
 
/// perform scale on UIBezierPath and keep the position in the path's center
  func scale(scale:CGFloat) {
    let bounds = self.cgPath.boundingBox
    
    let center = CGPoint(x:bounds.midX,y:bounds.midY)
    var transform = CGAffineTransform.identity.translatedBy(x: center.x, y: center.y)
    transform = transform.scaledBy(x: scale, y: scale)
    transform = transform.translatedBy(x: -center.x, y: -center.y);
    
    self.apply(transform)
  }


  /// perform rotate on UIBezierPath and keep the center unchanged
  func rotate(radians:CGFloat) {
    let bounds = self.cgPath.boundingBox
    
    let center = CGPoint(x:bounds.midX,y:bounds.midY)
    var transform = CGAffineTransform.identity.translatedBy(x: center.x, y: center.y)
    transform = transform.rotated(by: radians)
    transform = transform.translatedBy(x: -center.x, y: -center.y);
    
    self.apply(transform)
  }
}
于 2021-06-20T09:42:52.100 に答える