从 CATransform3D 获取缩放、平移和旋转
Get scale, translation and rotation from CATransform3D
给定一个 CATransform3D
变换,我想将缩放、平移和旋转提取为单独的变换。通过一些挖掘,我能够在 Swift 中为 CGAffineTransform
完成此操作,如下所示:
extension CGAffineTransform {
var scaleDelta:CGAffineTransform {
let xScale = sqrt(a * a + c * c)
let yScale = sqrt(b * b + d * d)
return CGAffineTransform(scaleX: xScale, y: yScale)
}
var rotationDelta:CGAffineTransform {
let rotation = CGFloat(atan2f(Float(b), Float(a)))
return CGAffineTransform(rotationAngle: rotation)
}
var translationDelta:CGAffineTransform {
return CGAffineTransform(translationX: tx, y: ty)
}
}
如何使用数学为 CATransform3D
做类似的事情? (我正在寻找不使用键路径的解决方案。)
(由您自行决定实施或纯数学答案)
如果您从一个适当的仿射矩阵开始,该仿射矩阵可以正确地(如果不是明确地)分解为一系列缩放、旋转、平移,则此方法将分解为表示平移的向量元组,旋转(欧拉角)和缩放分量:
extension CATransform3D {
func decomposeTRS() -> (float3, float3, float3) {
let m0 = float3(Float(self.m11), Float(self.m12), Float(self.m13))
let m1 = float3(Float(self.m21), Float(self.m22), Float(self.m23))
let m2 = float3(Float(self.m31), Float(self.m32), Float(self.m33))
let m3 = float3(Float(self.m41), Float(self.m42), Float(self.m43))
let t = m3
let sx = length(m0)
let sy = length(m1)
let sz = length(m2)
let s = float3(sx, sy, sz)
let rx = m0 / sx
let ry = m1 / sy
let rz = m2 / sz
let pitch = atan2(ry.z, rz.z)
let yaw = atan2(-rx.z, hypot(ry.z, rz.z))
let roll = atan2(rx.y, rx.x)
let r = float3(pitch, yaw, roll)
return (t, r, s)
}
}
为了证明此例程正确提取了各种组件,构建一个转换并确保它按预期分解:
let rotationX = CATransform3DMakeRotation(.pi / 2, 1, 0, 0)
let rotationY = CATransform3DMakeRotation(.pi / 3, 0, 1, 0)
let rotationZ = CATransform3DMakeRotation(.pi / 4, 0, 0, 1)
let translation = CATransform3DMakeTranslation(1, 2, 3)
let scale = CATransform3DMakeScale(0.1, 0.2, 0.3)
let transform = CATransform3DConcat(CATransform3DConcat(CATransform3DConcat(CATransform3DConcat(scale, rotationX), rotationY), rotationZ), translation)
let (T, R, S) = transform.decomposeTRS()
print("\(T), \(R), \(S))")
这会产生:
float3(1.0, 2.0, 3.0), float3(1.5708, 1.0472, 0.785398), float3(0.1, 0.2, 0.3))
请注意,此分解假定 XYZ 的欧拉乘法阶数,它只是 several possible orderings 之一。
警告:对于某些值,此方法在数值上不稳定。我还没有对它进行足够广泛的测试以了解这些陷阱在哪里,所以买者自负。
为了与我的问题中的 CGAffineTransform
扩展对称,这里是 CATransform3D
扩展,根据 Warren 的 decomposeTRS
,我已将其标记为已接受的答案。
extension CATransform3D {
var scaleDelta:CATransform3D {
let s = decomposeTRS().2
return CATransform3DMakeScale(CGFloat(s.x), CGFloat(s.y), CGFloat(s.z))
}
var rotationDelta:CATransform3D {
let r = decomposeTRS().1
let rx = CATransform3DMakeRotation(CGFloat(r.x), 1, 0, 0)
let ry = CATransform3DMakeRotation(CGFloat(r.y), 0, 1, 0)
let rz = CATransform3DMakeRotation(CGFloat(r.z), 0, 0, 1)
return CATransform3DConcat(CATransform3DConcat(rx, ry), rz)
}
var translationDelta:CATransform3D {
let t = decomposeTRS().0
return CATransform3DMakeTranslation(CGFloat(t.x), CGFloat(t.y), CGFloat(t.z))
}
}
给定一个 CATransform3D
变换,我想将缩放、平移和旋转提取为单独的变换。通过一些挖掘,我能够在 Swift 中为 CGAffineTransform
完成此操作,如下所示:
extension CGAffineTransform {
var scaleDelta:CGAffineTransform {
let xScale = sqrt(a * a + c * c)
let yScale = sqrt(b * b + d * d)
return CGAffineTransform(scaleX: xScale, y: yScale)
}
var rotationDelta:CGAffineTransform {
let rotation = CGFloat(atan2f(Float(b), Float(a)))
return CGAffineTransform(rotationAngle: rotation)
}
var translationDelta:CGAffineTransform {
return CGAffineTransform(translationX: tx, y: ty)
}
}
如何使用数学为 CATransform3D
做类似的事情? (我正在寻找不使用键路径的解决方案。)
(由您自行决定实施或纯数学答案)
如果您从一个适当的仿射矩阵开始,该仿射矩阵可以正确地(如果不是明确地)分解为一系列缩放、旋转、平移,则此方法将分解为表示平移的向量元组,旋转(欧拉角)和缩放分量:
extension CATransform3D {
func decomposeTRS() -> (float3, float3, float3) {
let m0 = float3(Float(self.m11), Float(self.m12), Float(self.m13))
let m1 = float3(Float(self.m21), Float(self.m22), Float(self.m23))
let m2 = float3(Float(self.m31), Float(self.m32), Float(self.m33))
let m3 = float3(Float(self.m41), Float(self.m42), Float(self.m43))
let t = m3
let sx = length(m0)
let sy = length(m1)
let sz = length(m2)
let s = float3(sx, sy, sz)
let rx = m0 / sx
let ry = m1 / sy
let rz = m2 / sz
let pitch = atan2(ry.z, rz.z)
let yaw = atan2(-rx.z, hypot(ry.z, rz.z))
let roll = atan2(rx.y, rx.x)
let r = float3(pitch, yaw, roll)
return (t, r, s)
}
}
为了证明此例程正确提取了各种组件,构建一个转换并确保它按预期分解:
let rotationX = CATransform3DMakeRotation(.pi / 2, 1, 0, 0)
let rotationY = CATransform3DMakeRotation(.pi / 3, 0, 1, 0)
let rotationZ = CATransform3DMakeRotation(.pi / 4, 0, 0, 1)
let translation = CATransform3DMakeTranslation(1, 2, 3)
let scale = CATransform3DMakeScale(0.1, 0.2, 0.3)
let transform = CATransform3DConcat(CATransform3DConcat(CATransform3DConcat(CATransform3DConcat(scale, rotationX), rotationY), rotationZ), translation)
let (T, R, S) = transform.decomposeTRS()
print("\(T), \(R), \(S))")
这会产生:
float3(1.0, 2.0, 3.0), float3(1.5708, 1.0472, 0.785398), float3(0.1, 0.2, 0.3))
请注意,此分解假定 XYZ 的欧拉乘法阶数,它只是 several possible orderings 之一。
警告:对于某些值,此方法在数值上不稳定。我还没有对它进行足够广泛的测试以了解这些陷阱在哪里,所以买者自负。
为了与我的问题中的 CGAffineTransform
扩展对称,这里是 CATransform3D
扩展,根据 Warren 的 decomposeTRS
,我已将其标记为已接受的答案。
extension CATransform3D {
var scaleDelta:CATransform3D {
let s = decomposeTRS().2
return CATransform3DMakeScale(CGFloat(s.x), CGFloat(s.y), CGFloat(s.z))
}
var rotationDelta:CATransform3D {
let r = decomposeTRS().1
let rx = CATransform3DMakeRotation(CGFloat(r.x), 1, 0, 0)
let ry = CATransform3DMakeRotation(CGFloat(r.y), 0, 1, 0)
let rz = CATransform3DMakeRotation(CGFloat(r.z), 0, 0, 1)
return CATransform3DConcat(CATransform3DConcat(rx, ry), rz)
}
var translationDelta:CATransform3D {
let t = decomposeTRS().0
return CATransform3DMakeTranslation(CGFloat(t.x), CGFloat(t.y), CGFloat(t.z))
}
}