def scale(cls, x, y, z, dtype=FLOAT) -> 'Transform': m = np.eye(4, 4, dtype=dtype) m_inv = np.eye(4, 4, dtype=dtype) m[0][0] = x m[1][1] = y m[2][2] = z m_inv[0][0] = 1. / x m_inv[1][1] = 1. / y m_inv[2][2] = 1. / z return cls(m, m_inv, dtype)
def translate(cls, delta: 'geo.Vector', dtype=FLOAT) -> 'Transform': m = np.eye(4, 4, dtype=dtype) m_inv = np.eye(4, 4, dtype=dtype) m[0][3] = delta.x m[1][3] = delta.y m[2][3] = delta.z m_inv[0][3] = -delta.x m_inv[1][3] = -delta.y m_inv[2][3] = -delta.z return cls(m, m_inv, dtype)
def look_at(cls, pos: 'geo.Point', look: 'geo.Point', up: 'geo.Vector', dtype=FLOAT) -> 'Transform': """ look_at Look-at transformation, from camera to world """ w2c = np.eye(4, 4, dtype=dtype) c2w = np.eye(4, 4, dtype=dtype) zc = geo.normalize(look - pos) xc = geo.normalize(geo.normalize(up).cross(zc)) yc = zc.cross(xc) # orthogonality # c2w translation c2w[0][3] = pos.x c2w[1][3] = pos.y c2w[2][3] = pos.z # c2w rotation c2w[0][0] = xc.x c2w[0][1] = xc.y c2w[0][2] = xc.z c2w[1][0] = yc.x c2w[1][1] = yc.y c2w[1][2] = yc.z c2w[2][0] = zc.x c2w[2][1] = zc.y c2w[2][2] = zc.z # w2c rotation # in effect as camera extrinsic w2c[0][0] = xc.x w2c[0][1] = yc.x w2c[0][2] = zc.x w2c[1][0] = xc.y w2c[1][1] = yc.y w2c[1][2] = zc.y w2c[2][0] = xc.z w2c[2][1] = yc.z w2c[2][2] = zc.z # w2c translation w2c[0][3] = -(pos.x * xc.x + pos.y * yc.x + pos.z * zc.x) w2c[1][3] = -(pos.x * xc.y + pos.y * yc.y + pos.z * zc.y) w2c[2][3] = -(pos.x * xc.z + pos.y * yc.z + pos.z * zc.z) return cls(c2w, w2c, dtype)
def __init__(self, m=None, m_inv=None, dtype=FLOAT): if m is None: self.__m = np.eye(4, 4, dtype=dtype) self.__m_inv = np.eye(4, 4, dtype=dtype) elif m is not None and m_inv is not None: self.__m = m.copy() self.__m_inv = m_inv.copy() else: if not np.shape(m) == (4, 4): raise TypeError('Transform matrix must be 4x4') self.__m = m.copy() self.__m_inv = np.linalg.inv(m) self.__m.flags.writeable = False self.__m_inv.flags.writeable = False
def rotate_z(cls, angle, dtype=FLOAT) -> 'Transform': m = np.eye(4, 4, dtype=dtype) sin_t = np.sin(np.deg2rad(angle)) cos_t = np.cos(np.deg2rad(angle)) m[0][0] = cos_t m[0][1] = -sin_t m[1][0] = sin_t m[1][1] = cos_t return cls(m, m.T, dtype)
def perspective(cls, fov: FLOAT, n: FLOAT, f: FLOAT, dtype=FLOAT): # projective along z m = np.eye(4, dtype=dtype) m[2, 2] = f / (f - n) m[2, 3] = -f * n / (f - n) m[3, 2] = 1. m[3, 3] = 0. # scale to viewing volume tan_inv = 1. / np.tan(np.deg2rad(fov) / 2.) return cls.scale(tan_inv, tan_inv, 1.) * cls(m)
def rotate(cls, angle, axis: 'geo.Vector', dtype=FLOAT) -> 'Transform': a = geo.normalize(axis) s = np.sin(np.deg2rad(angle)) c = np.cos(np.deg2rad(angle)) m = np.eye(4, 4, dtype=dtype) m[0][0] = a.x * a.x + (1. - a.x * a.x) * c m[0][1] = a.x * a.y * (1. - c) - a.z * s m[0][2] = a.x * a.z * (1. - c) + a.y * s m[1][0] = a.x * a.y * (1. - c) + a.z * s m[1][1] = a.y * a.y + (1. - a.y * a.y) * c m[1][2] = a.y * a.z * (1. - c) - a.x * s m[2][0] = a.x * a.z * (1. - c) - a.y * s m[2][1] = a.y * a.z * (1. - c) + a.x * s m[2][2] = a.z * a.z + (1. - a.z * a.z) * c return cls(m, m.T, dtype)
def is_identity(self) -> bool: return np.array_equal(self.m, np.eye(4, 4))