从CATransform3D获取缩放,平移和旋转

时间:2018-08-06 11:01:43

标签: swift 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做类似的事情? (我正在寻找不使用键路径的解决方案。)

(您可以自行决定实施或仅数学答案)

2 个答案:

答案 0 :(得分:3)

如果从正确的仿射矩阵开始,该仿射矩阵可以正确地(如果不是毫不含糊地)分解成比例尺序列,则旋转,平移,此方法将分解为代表平移,旋转(欧拉角)和比例分量:

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之一。

注意事项:当然,某些方法的数值不稳定。我还没有对它进行足够广泛的测试,以了解这些陷阱在哪里,所以 caveat emptor

答案 1 :(得分:0)

为了与我的问题中的CGAffineTransform扩展名对称,以下是CATransform3D扩展名,它基于沃伦的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))
    }
}