Один шаг аффинного преобразования для вращения вокруг точки?

Как сделать аффинное преобразование Core Graphics для вращения вокруг точки x,y угла a, используя только один вызов функций триггера CGAffineTransformMake() плюс math.h, таких как sin(), cos() и т. Д., И никаких других звонков CG.

Другие ответы здесь, по-видимому, касаются использования нескольких стековых преобразований или многошаговых преобразований для перемещения, вращения и перемещения с использованием нескольких вызовов Core Graphics. Эти ответы не соответствуют моим конкретным требованиям.

6 ответов

Решение

Поворот на угол вокруг точки (x,y) соответствует аффинному преобразованию:

CGAffineTransform transform = CGAffineTransformMake(cos(a),sin(a),-sin(a),cos(a),x-x*cos(a)+y*sin(a),y-x*sin(a)-y*cos(a));

Возможно, вам придется подключить -a вместо зависимости от того, хотите ли вы вращение по часовой стрелке или против часовой стрелки. Кроме того, вам может понадобиться подключить -y вместо y в зависимости от того, перевернута ли ваша система координат.

Кроме того, вы можете выполнить одно и то же в трех строках кода, используя:

CGAffineTransform transform = CGAffineTransformMakeTranslation(x, y);
transform = CGAffineTransformRotate(transform, a);
transform = CGAffineTransformTranslate(transform,-x,-y);

Если вы применяете это к представлению, вы также можете просто использовать преобразование вращения через CGAffineTransformMakeRotation(a), при условии, что вы установили свойство anchorPoint слоя представления, чтобы отразить точку, вокруг которой вы хотите вращаться. Однако звучит так, будто вы не заинтересованы в применении этого к представлению.

Наконец, если вы применяете это к неевклидовому двумерному пространству, вам может вообще не потребоваться аффинное преобразование. Аффинные преобразования являются изометриями евклидова пространства, что означает, что они сохраняют стандартное евклидово расстояние, а также углы. Если ваше пространство не является евклидовым, то преобразование, которое вы хотите, может на самом деле не быть аффинным, или если оно аффинно, матрица вращения может быть не такой простой, как то, что я написал выше с помощью sin и cos. Например, если вы находитесь в гиперболическом пространстве, вам может понадобиться использовать гиперболические тригонометрические функции sinh и cosh, а также различные знаки + и - в формуле.

PS Я также хотел напомнить всем, кто читает это далеко, что "аффинность" произносится с коротким "а", как в "спросить", а не с длинным "а", как в "состоянии". Я даже слышал, как сотрудники Apple неправильно произносили это в своих выступлениях на WWDC.

Для Swift 4

print(x, y) // where x,y is the point to rotate around
let degrees = 45.0
let transform = CGAffineTransform(translationX: x, y: y)
    .rotated(by: degrees * .pi / 180)
    .translatedBy(x: -x, y: -y)

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

Хитрость здесь в том, чтобы перевести контрольную точку перед любым изменением формы (как масштаб, так и вращение). После этого вам нужно объединить два преобразования, чтобы получить полное аффинное преобразование.

Я упаковал все решение в подкласс CIFilter, который вы можете найти здесь.

После соответствующей части кода:

CGFloat a = _inputDegree.floatValue;
CGFloat x = _inputImage.extent.size.width/2.0;
CGFloat y = _inputImage.extent.size.height/2.0;

CGFloat scale = [self calculateScaleForAngle:GLKMathRadiansToDegrees(a)];

CGAffineTransform transform = CGAffineTransformMakeTranslation(x, y);
transform = CGAffineTransformRotate(transform, a);
transform = CGAffineTransformTranslate(transform,-x,-y);


CGAffineTransform transform2 = CGAffineTransformMakeTranslation(x, y);
transform2 = CGAffineTransformScale(transform2, scale, scale);
transform2 = CGAffineTransformTranslate(transform2,-x,-y);

CGAffineTransform concate   = CGAffineTransformConcat(transform2, transform);

Вот несколько удобных способов вращения вокруг точки привязки:

      extension CGAffineTransform {
    
    init(rotationAngle: CGFloat, anchor: CGPoint) {
        self.init(
            a: cos(rotationAngle),
            b: sin(rotationAngle),
            c: -sin(rotationAngle),
            d: cos(rotationAngle),
            tx: anchor.x - anchor.x * cos(rotationAngle) + anchor.y * sin(rotationAngle),
            ty: anchor.y - anchor.x * sin(rotationAngle) - anchor.y * cos(rotationAngle)
        )
    }

    func rotated(by angle: CGFloat, anchor: CGPoint) -> Self {
        let transform = Self(rotationAngle: angle, anchor: anchor)
        return self.concatenating(transform)
    }

}

Проверьте эту ссылку: вращение iPhone вокруг точки. Это может помочь вам. В нижней части статьи есть раздел под названием "Изменить опорную точку". Это, вероятно, больше всего подходит для ваших нужд, хотя есть некоторая компенсация, которая должна быть сделана, чтобы не компенсировать вид.

Используйте слой вида и опорную точку. например

view.layer.anchorPoint = CGPoint(x:0,y:1.0)
Другие вопросы по тегам