def jacobian(self, x, fp, fr, ts, dsc, tdsc): """ Find parent rotations """ prs = fr[:, self.animation.parents] prs[:, 0] = Quaternions.id((1)) """ Find global positions of target joints """ tps = fp[:, np.array(list(ts.keys()))] """ Get partial rotations """ qys = Quaternions.from_angle_axis(x[:, 1:prs.shape[1] * 3:3], np.array([[[0, 1, 0]]])) qzs = Quaternions.from_angle_axis(x[:, 2:prs.shape[1] * 3:3], np.array([[[0, 0, 1]]])) """ Find axis of rotations """ es = np.empty((len(x), fr.shape[1] * 3, 3)) es[:, 0::3] = ((prs * qzs) * qys) * np.array([[[1, 0, 0]]]) es[:, 1::3] = ((prs * qzs) * np.array([[[0, 1, 0]]])) es[:, 2::3] = ((prs * np.array([[[0, 0, 1]]]))) """ Construct Jacobian """ j = fp.repeat(3, axis=1) j = dsc[np.newaxis, :, :, np.newaxis] * (tps[:, np.newaxis, :] - j[:, :, np.newaxis]) j = self.cross(es[:, :, np.newaxis, :], j) j = np.swapaxes(j.reshape((len(x), fr.shape[1] * 3, len(ts) * 3)), 1, 2) if self.translate: es = np.empty((len(x), fr.shape[1] * 3, 3)) es[:, 0::3] = prs * np.array([[[1, 0, 0]]]) es[:, 1::3] = prs * np.array([[[0, 1, 0]]]) es[:, 2::3] = prs * np.array([[[0, 0, 1]]]) jt = tdsc[np.newaxis, :, :, np.newaxis] * es[:, :, np.newaxis, :].repeat( tps.shape[1], axis=2) jt = np.swapaxes( jt.reshape((len(x), fr.shape[1] * 3, len(ts) * 3)), 1, 2) j = np.concatenate([j, jt], axis=-1) return j