Diff of /app/RotationUtil.py [000000] .. [4de1c7]

Switch to unified view

a b/app/RotationUtil.py
1
import numpy as np
2
import math
3
4
from resources.LeapSDK.v53_python39 import Leap
5
6
7
def get_order():
8
    ##############
9
    #  i, j, k, n
10
    #  n = parity of axis permutation (even=False, odd=True)
11
    #  from https://github.com/dfelinto/blender/blob/master/source/blender/blenlib/intern/math_rotation.c
12
    ##############
13
14
    return [0, 1, 2, False]  # XYZ
15
    # return [0, 2, 1, True]   # XZY
16
    # return [1, 0, 2, True]   # YXZ
17
    # return [1, 2, 0, False]  # YZX
18
    # return [2, 0, 1, False]  # ZXY
19
    # return [2, 1, 0, True]   # ZYX
20
21
22
def _rot2eulsimple(rotmat):
23
    eul = np.zeros(3)
24
    if -1 < rotmat[1, 1] < 1:
25
        eul[1] = math.acos(rotmat[1, 1])
26
    if rotmat[1, 1] >= 1.0:
27
        eul[1] = 0
28
    if rotmat[1, 1] <= -1.0:
29
        eul[1] = np.pi
30
31
    if rotmat[0, 0] < 1 and rotmat[1, 1] > -1:
32
        eul[0] = math.acos(rotmat[0, 0])
33
    if rotmat[1, 1] >= 1.0:
34
        eul[0] = 0
35
    if rotmat[1, 1] <= -1.0:
36
        eul[0] = np.pi
37
38
    eul[2] = 0
39
    return eul
40
41
42
def _rot2eul(rotmat):
43
    ##############
44
    #  i, j, k, n
45
    #  n = parity of axis permutation (even=False, odd=True)
46
    #  from https://github.com/dfelinto/blender/blob/master/source/blender/blenlib/intern/math_rotation.c
47
    ##############
48
49
    # order = [0, 1, 2, False] # XYZ
50
    # order = [0, 2, 1, True] # XZY
51
    # order = [1, 0, 2, True] # YXZ
52
    # order = [1, 2, 0, False] # YZX
53
    # order = [2, 0, 1, False]  # ZXY
54
    # order = [2, 1, 0, True] # ZYX
55
56
    order = get_order()
57
58
    i = int(order[2])
59
    j = int(order[1])
60
    k = int(order[0])
61
    parity = order[3]
62
63
    eul1 = np.zeros(3)
64
    eul2 = np.zeros(3)
65
66
    cy = np.hypot(rotmat[i, i], rotmat[i, j])
67
68
    if cy > Leap.EPSILON:
69
        eul1[i] = math.atan2(rotmat[j, k], rotmat[k, k])
70
        eul1[j] = math.atan2(-rotmat[i, k], cy)
71
        eul1[k] = math.atan2(rotmat[i, j], rotmat[i, i])
72
73
        eul2[i] = math.atan2(-rotmat[j, k], -rotmat[k, k])
74
        eul2[j] = math.atan2(-rotmat[i, k], -cy)
75
        eul2[k] = math.atan2(-rotmat[i, j], -rotmat[i, i])
76
77
    else:
78
        eul1[i] = math.atan2(-rotmat[k, j], rotmat[j, j])
79
        eul1[j] = math.atan2(-rotmat[i, k], cy)
80
        eul1[k] = 0.0
81
82
        eul2 = eul1
83
84
    #  parity of axis permutation (even=False, odd=True)
85
    if not parity:
86
        eul1 = np.negative(eul1)
87
        eul2 = np.negative(eul2)
88
89
    # return best, which is just the one with lowest values in it
90
    if np.sum(np.absolute(eul1)) > np.sum(np.absolute(eul2)):
91
        return eul2
92
    return eul1
93
94
95
def quat2mat(quat):
96
    q0 = math.sqrt(2) * quat[0]
97
    q1 = math.sqrt(2) * quat[1]
98
    q2 = math.sqrt(2) * quat[2]
99
    q3 = math.sqrt(2) * quat[3]
100
101
    qda = q0 * q1
102
    qdb = q0 * q2
103
    qdc = q0 * q3
104
    qaa = q1 * q1
105
    qab = q1 * q2
106
    qac = q1 * q3
107
    qbb = q2 * q2
108
    qbc = q2 * q3
109
    qcc = q3 * q3
110
111
    mat = np.zeros((3, 3))
112
    mat[0, 0] = 1.0 - qbb - qcc
113
    mat[1, 0] = qdc + qab
114
    mat[2, 0] = -qdb + qac
115
116
    mat[0, 1] = -qdc + qab
117
    mat[1, 1] = 1.0 - qaa - qcc
118
    mat[2, 1] = qda + qbc
119
120
    mat[0, 2] = qdb + qac
121
    mat[1, 2] = -qda + qbc
122
    mat[2, 2] = 1.0 - qaa - qbb
123
124
    return mat
125
126
127
def vec2quat(v1, v2):
128
    a = np.divide(v1, np.linalg.norm(v1))
129
    b = np.divide(v2, np.linalg.norm(v2))
130
131
    xUnitVec = np.array([1, 0, 0])
132
    yUnitVec = np.array([0, 1, 0])
133
134
    dot_p = np.dot(a, b)
135
136
    if dot_p < -1 + Leap.EPSILON:
137
        tmpvec3 = np.cross(xUnitVec, a)
138
        if np.linalg.norm(tmpvec3) < Leap.EPSILON:
139
            tmpvec3 = np.cross(yUnitVec, a)
140
        tmpvec3 = np.divide(tmpvec3, np.linalg.norm(tmpvec3))
141
142
        t = np.pi * 0.5
143
        s = np.sin(t)
144
        q = np.zeros(4)
145
        q[0] = np.cos(t)
146
        q[1] = tmpvec3[0]*s
147
        q[2] = tmpvec3[1]*s
148
        q[3] = tmpvec3[2]*s
149
        return q
150
151
    if dot_p > 1 - Leap.EPSILON:
152
        return np.array([0, 0, 0, 1])
153
154
    tmpvec3 = np.cross(a, b)
155
    q = np.append(1 + dot_p, tmpvec3)
156
    return np.divide(q, np.linalg.norm(q))
157
158
159
def rot2quat(rotmat):
160
    q = np.zeros(4)
161
162
    tr = 0.25 * (1 + np.trace(rotmat))
163
    if tr > Leap.EPSILON:
164
        s = np.sqrt(tr)
165
        q[0] = s
166
        s = 1.0 / (4.0 * s)
167
        q[1] = (rotmat[1, 2] - rotmat[2, 1]) * s
168
        q[2] = (rotmat[2, 0] - rotmat[0, 2]) * s
169
        q[3] = (rotmat[0, 1] - rotmat[1, 0]) * s
170
        return normalize_quat(q)
171
172
    if rotmat[0, 0] > rotmat[1, 1] and rotmat[0, 0] > rotmat[2, 2]:
173
        s = 2.0 * np.sqrt(1.0 + rotmat[0, 0] - rotmat[1, 1] - rotmat[2, 2])
174
        q[1] = 0.25 * s
175
        s = 1.0 / s
176
        q[0] = (rotmat[1, 2] - rotmat[2, 1]) * s
177
        q[2] = (rotmat[1, 0] - rotmat[0, 1]) * s
178
        q[3] = (rotmat[2, 0] - rotmat[0, 2]) * s
179
        return normalize_quat(q)
180
181
    if rotmat[1, 1] > rotmat[2, 2]:
182
        s = 2.0 * np.sqrt(1.0 + rotmat[1, 1] - rotmat[0, 0] - rotmat[2, 2])
183
        q[2] = 0.25 * s
184
        s = 1.0 / s
185
        q[0] = (rotmat[2, 0] - rotmat[0, 2]) * s
186
        q[1] = (rotmat[1, 0] - rotmat[0, 1]) * s
187
        q[3] = (rotmat[2, 1] - rotmat[1, 2]) * s
188
        return normalize_quat(q)
189
190
    s = 2.0 * np.sqrt(1.0 + rotmat[2, 2] - rotmat[0, 0] - rotmat[1, 1])
191
    q[3] = 0.25 * s
192
    s = 1.0 / s
193
    q[0] = (rotmat[0, 1] - rotmat[1, 0]) * s
194
    q[1] = (rotmat[2, 0] - rotmat[0, 2]) * s
195
    q[2] = (rotmat[2, 1] - rotmat[1, 2]) * s
196
    return normalize_quat(q)
197
198
199
def normalize_quat(q):
200
    scal = np.sqrt(np.dot(q, q))
201
    if scal != 0.0:
202
        return np.kron(q, 1.0 / scal)
203
204
    q[1] = 1.0
205
    q[0] = q[2] = q[3] = 0.0
206
    return q
207
208
209
def conjugate_quat(q):
210
    q[1] = -q[1]
211
    q[2] = -q[2]
212
    q[3] = -q[4]
213
    return q
214
215
216
def multiply_quat(q1, q2):
217
    q = np.zeros(4)
218
    t0 = q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3]
219
    t1 = q1[0] * q2[1] + q1[1] * q2[0] + q1[2] * q2[3] - q1[3] * q2[2]
220
    t2 = q1[0] * q2[2] + q1[2] * q2[0] + q1[3] * q2[1] - q1[1] * q2[3]
221
    q[3] = q1[0] * q2[3] + q1[3] * q2[0] + q1[1] * q2[2] - q1[2] * q2[1]
222
    q[0] = t0
223
    q[1] = t1
224
    q[2] = t2
225
    return q
226
227
228
def vec2eul(v1, v2):
229
    quaternions = vec2quat(v1, v2)
230
    rotmat = quat2mat(quaternions)
231
    euler = _rot2eul(rotmat)
232
    return euler[0], euler[1], euler[2]
233
234
235
def rot2eul(rotmat):
236
    euler = _rot2eul(rotmat)
237
    return euler[0], euler[1], euler[2]
238
239
240
def quat_diff(q_prev, q_next):
241
    q_prev = conjugate_quat(q_prev)
242
    q_prev = np.kron(q_prev, 1.0 / np.dot(q_prev, q_prev))
243
    return multiply_quat(q_prev, q_next)
244
245
246
def quat_between_rot(rotmat_prev, rotmat_next):
247
    # TODO: normalize rotation matrix
248
    return quat_diff(rot2quat(rotmat_prev), rot2quat(rotmat_next))