gpt4 book ai didi

ios - 用户可读格式的 CGAffineTransform 矩阵

转载 作者:可可西里 更新时间:2023-11-01 04:08:50 24 4
gpt4 key购买 nike

我应该如何将 CoreGraphics CGAffineTransform 解释为人类可读、有意义的格式?

我正在寻找类似的东西:

NSLog(@"naturalSize %@, appliedSize %@, transformationMatrix %@",
NSStringFromCGSize(clipVideoTrack.naturalSize),
NSStringFromCGSize(CGSizeApplyAffineTransform(clipVideoTrack.naturalSize, clipVideoTrack.preferredTransform)),
NSStringFromCGAffineTransform(clipVideoTrack.preferredTransform));

naturalSize {1920, 1080}, appliedSize {-1080, 1920}, transformationMatrix [0, 1, -1, 0, 1080, 0]

上述矩阵变换的最终结果是取这个 Landscape Right Video并转变为这个Portrait Up Video

我很高兴能够将其分解为步骤,以人类可读的形式,以便人们可以查看和理解转换的实际作用。

类似于(不确定我的步骤是否正确):

 0. Will use upper left corner for video export of width 1080, height 1920.
1. Will move the video -1080(left) on the x axis
2. Will move the video 1920(down) on the y axis
3. Will rotate 90deg clockwise from bottom right corner

如果能指出执行此操作的代码、实现或解释,我将不胜感激。我试图学习和理解 AVFoundation 上下文中的转换矩阵究竟是如何运作的。

最佳答案

事实证明,在大多数情况下,您可以对仿射变换进行适当的描述,因为它非常受限。对 3D 变换做同样的事情更难:(

请注意,我仍然无法告诉您串联的不同变换矩阵,只能告诉您最终结果。我也忽略了剪切,因为没有提供用于创建此类转换的函数。


我编写了一个函数,可以很好地理解仿射变换的作用。

你会在很多地方看到我写的if (fabs(foo - bar) < FLT_EPSILON)而不仅仅是 if (foo == bar) .这是为了保护自己在比较中的浮点 (im) 精度。

另一个需要指出的值得注意的事情是我计算旋转角度的方式。对于纯旋转,我可以使用 asin(b) ,但如果变换也被缩放,那么结果将是不正确的。相反,我将 b 除以 a 并使用 arctan 来计算角度。

代码中有大量注释,因此您应该能够跟进,主要是通过阅读代码。

NSString *affineTransformDescription(CGAffineTransform transform)
{
// check if it's simply the identity matrix
if (CGAffineTransformIsIdentity(transform)) {
return @"Is the identity transform";
}
// the above does't catch things like a 720° rotation so also check it manually
if (fabs(transform.a - 1.0) < FLT_EPSILON &&
fabs(transform.b - 0.0) < FLT_EPSILON &&
fabs(transform.c - 0.0) < FLT_EPSILON &&
fabs(transform.d - 1.0) < FLT_EPSILON &&
fabs(transform.tx - 0.0) < FLT_EPSILON &&
fabs(transform.ty - 0.0) < FLT_EPSILON) {
return @"Is the identity transform";
}

// The affine transforms is built up like this:

// a b tx
// c d ty
// 0 0 1

// An array to hold all the different descirptions, charasteristics of the transform.
NSMutableArray *descriptions = [NSMutableArray array];

// Checking for a translation
if (fabs(transform.tx) > FLT_EPSILON) { // translation along X
[descriptions addObject:[NSString stringWithFormat:@"Will move %.2f along the X axis",
transform.tx]];
}
if (fabs(transform.ty) > FLT_EPSILON) { // translation along Y
[descriptions addObject:[NSString stringWithFormat:@"Will move %.2f along the Y axis",
transform.ty]];
}


// Checking for a rotation
CGFloat angle = atan2(transform.b, transform.a); // get the angle of the rotation. Note this assumes no shearing!
if (fabs(angle) < FLT_EPSILON || fabs(angle - M_PI) < FLT_EPSILON) {
// there is a change that there is a 180° rotation, in that case, A and D will and be negative.
BOOL bothAreNegative = transform.a < 0.0 && transform.d < 0.0;

if (bothAreNegative) {
angle = M_PI;
} else {
angle = 0.0; // this is not considered a rotation, but a negative scale along one axis.
}
}

// add the rotation description if there was an angle
if (fabs(angle) > FLT_EPSILON) {
[descriptions addObject:[NSString stringWithFormat:@"Will rotate %.1f° degrees",
angle*180.0/M_PI]];
}


// Checking for a scale (and account for the possible rotation as well)
CGFloat scaleX = transform.a/cos(angle);
CGFloat scaleY = transform.d/cos(angle);


if (fabs(scaleX - scaleY) < FLT_EPSILON && fabs(scaleX - 1.0) > FLT_EPSILON) {
// if both are the same then we can format things a little bit nicer
[descriptions addObject:[NSString stringWithFormat:@"Will scale by %.2f along both X and Y",
scaleX]];
} else {
// otherwise we look at X and Y scale separately
if (fabs(scaleX - 1.0) > FLT_EPSILON) { // scale along X
[descriptions addObject:[NSString stringWithFormat:@"Will scale by %.2f along the X axis",
scaleX]];
}

if (fabs(scaleY - 1.0) > FLT_EPSILON) { // scale along Y
[descriptions addObject:[NSString stringWithFormat:@"Will scale by %.2f along the Y axis",
scaleY]];
}
}

// Return something else when there is nothing to say about the transform matrix
if (descriptions.count == 0) {
return @"Can't easilly be described.";
}

// join all the descriptions on their own line
return [descriptions componentsJoinedByString:@",\n"];
}

为了尝试,我测试了多种不同转换的输出。这是我用来测试它的代码:

// identity
CGAffineTransform t = CGAffineTransformIdentity;
NSLog(@"identity: \n%@", affineTransformDescription(t));


// translation
t = CGAffineTransformMakeTranslation(10, 0);
NSLog(@"translate(10, 0): \n%@", affineTransformDescription(t));

t = CGAffineTransformMakeTranslation(0, 20);
NSLog(@"translate(0, 20): \n%@", affineTransformDescription(t));

t = CGAffineTransformMakeTranslation(2, -3);
NSLog(@"translate(2, -3): \n%@", affineTransformDescription(t));


// scale
t = CGAffineTransformMakeScale(2, 2);
NSLog(@"scale(2, 2): \n%@", affineTransformDescription(t));


t = CGAffineTransformMakeScale(-1, 3);
NSLog(@"scale(-1, 3): \n%@", affineTransformDescription(t));



// rotation
t = CGAffineTransformMakeRotation(M_PI/3.0);
NSLog(@"rotate(60 deg): \n%@", affineTransformDescription(t));

t = CGAffineTransformMakeRotation(M_PI);
NSLog(@"rotate(180 deg): \n%@", affineTransformDescription(t));

t = CGAffineTransformMakeRotation(4.0*M_PI);
NSLog(@"rotate(720 deg): \n%@", affineTransformDescription(t));

t = CGAffineTransformMakeRotation(3.0*M_PI);
NSLog(@"rotate(540 deg): \n%@", affineTransformDescription(t));



// concatenated transforms
// rotate & translate
t = CGAffineTransformMakeRotation(M_PI/3.0);
t = CGAffineTransformTranslate(t, 10, 20);
NSLog(@"rotate(60 deg), translate(10, 20): \n%@", affineTransformDescription(t));

t = CGAffineTransformMakeTranslation(10, 20);
t = CGAffineTransformRotate(t, M_PI/3.0);
NSLog(@"translate(10, 20), rotate(60 deg): \n%@", affineTransformDescription(t));

// rotate & scale
t = CGAffineTransformMakeRotation(M_PI/3.0);
t = CGAffineTransformScale(t, 2, 2);
NSLog(@"rotate(60 deg), scale(2, 2): \n%@", affineTransformDescription(t));

t = CGAffineTransformMakeScale(2, 2);
t = CGAffineTransformRotate(t, M_PI/3.0);
NSLog(@"scale(2, 2), rotate(60 deg): \n%@", affineTransformDescription(t));

// translate & scale
t = CGAffineTransformMakeTranslation(10, 20);
t = CGAffineTransformScale(t, 2, 2);
NSLog(@"translate(10, 20), scale(2, 2): \n%@", affineTransformDescription(t));

t = CGAffineTransformMakeScale(2, 2);
t = CGAffineTransformTranslate(t, 10, 20);
NSLog(@"scale(2, 2), translate(10, 20): \n%@", affineTransformDescription(t));

以及该测试的输出:

identity: 
Is the identity transform
translate(10, 0):
Will move 10.00 along the X axis
translate(0, 20):
Will move 20.00 along the Y axis
translate(2, -3):
Will move 2.00 along the X axis,
Will move -3.00 along the Y axis
scale(2, 2):
Will scale by 2.00 along both X and Y
scale(-1, 3):
Will scale by -1.00 along the X axis,
Will scale by 3.00 along the Y axis
rotate(60 deg):
Will rotate 60.0° degrees
rotate(180 deg):
Will rotate 180.0° degrees
rotate(720 deg):
Is the identity transform
rotate(540 deg):
Will rotate 180.0° degrees
rotate(60 deg), translate(10, 20):
Will move -12.32 along the X axis,
Will move 18.66 along the Y axis,
Will rotate 60.0° degrees
translate(10, 20), rotate(60 deg):
Will move 10.00 along the X axis,
Will move 20.00 along the Y axis,
Will rotate 60.0° degrees
rotate(60 deg), scale(2, 2):
Will rotate 60.0° degrees,
Will scale by 2.00 along both X and Y
scale(2, 2), rotate(60 deg):
Will rotate 60.0° degrees,
Will scale by 2.00 along both X and Y
translate(10, 20), scale(2, 2):
Will move 10.00 along the X axis,
Will move 20.00 along the Y axis,
Will scale by 2.00 along both X and Y
scale(2, 2), translate(10, 20):
Will move 20.00 along the X axis,
Will move 40.00 along the Y axis,
Will scale by 2.00 along both X and Y

关于ios - 用户可读格式的 CGAffineTransform 矩阵,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23265519/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com