skelform-python 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- skelform_python-0.2.0/.gitignore +1 -0
- {skelform_python-0.1.0 → skelform_python-0.2.0}/PKG-INFO +1 -1
- {skelform_python-0.1.0 → skelform_python-0.2.0}/pyproject.toml +1 -1
- skelform_python-0.2.0/skelform_python/__init__.py +343 -0
- skelform_python-0.2.0/skelform_python/tests.py +143 -0
- skelform_python-0.2.0/uv.lock +8 -0
- skelform_python-0.1.0/skelform_python/__init__.py +0 -102
- {skelform_python-0.1.0 → skelform_python-0.2.0}/README.md +0 -0
- {skelform_python-0.1.0 → skelform_python-0.2.0}/readme.md +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/dist
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import copy
|
|
3
|
+
import zipfile
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Vec2:
|
|
10
|
+
x: float
|
|
11
|
+
y: float
|
|
12
|
+
|
|
13
|
+
def __sub__(self, other):
|
|
14
|
+
return Vec2(self.x - other.x, self.y - other.y)
|
|
15
|
+
|
|
16
|
+
def __add__(self, other):
|
|
17
|
+
return Vec2(self.x + other.x, self.y + other.y)
|
|
18
|
+
|
|
19
|
+
def __mul__(self, other):
|
|
20
|
+
return Vec2(self.x * other.x, self.y * other.y)
|
|
21
|
+
|
|
22
|
+
def __isub__(self, other):
|
|
23
|
+
return self.__sub__(other)
|
|
24
|
+
|
|
25
|
+
def __iadd__(self, other):
|
|
26
|
+
return self.__add__(other)
|
|
27
|
+
|
|
28
|
+
def __imul__(self, other):
|
|
29
|
+
return self.__mul__(other)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class Bone:
|
|
34
|
+
name: str
|
|
35
|
+
id: int
|
|
36
|
+
parent_id: int
|
|
37
|
+
style_ids: Optional[list[int]]
|
|
38
|
+
tex: Optional[str]
|
|
39
|
+
rot: float
|
|
40
|
+
scale: Vec2
|
|
41
|
+
pos: Vec2
|
|
42
|
+
ik_bone_ids: Optional[list[int]]
|
|
43
|
+
ik_mode: Optional[int]
|
|
44
|
+
ik_constraint_str: Optional[str]
|
|
45
|
+
ik_constraint: Optional[int]
|
|
46
|
+
ik_family_id: Optional[int]
|
|
47
|
+
ik_target_id: Optional[int]
|
|
48
|
+
init_rot: float
|
|
49
|
+
init_scale: Vec2
|
|
50
|
+
init_pos: Vec2
|
|
51
|
+
init_ik_constraint: Optional[int]
|
|
52
|
+
zindex: Optional[int] = 0
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class Keyframe:
|
|
57
|
+
frame: int
|
|
58
|
+
bone_id: int
|
|
59
|
+
element: int
|
|
60
|
+
value: float
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class Animation:
|
|
65
|
+
name: str
|
|
66
|
+
keyframes: list[Keyframe]
|
|
67
|
+
fps: int
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass
|
|
71
|
+
class Texture:
|
|
72
|
+
name: str
|
|
73
|
+
offset: Vec2
|
|
74
|
+
size: Vec2
|
|
75
|
+
atlas_idx: int
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass
|
|
79
|
+
class Style:
|
|
80
|
+
name: str
|
|
81
|
+
textures: list[Texture]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass
|
|
85
|
+
class Atlas:
|
|
86
|
+
filename: str
|
|
87
|
+
size: Vec2
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@dataclass
|
|
91
|
+
class Armature:
|
|
92
|
+
bones: list[Bone]
|
|
93
|
+
ik_root_ids: list[int]
|
|
94
|
+
animations: Optional[list[Animation]]
|
|
95
|
+
atlases: list[Atlas]
|
|
96
|
+
styles: list[Style]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def animate(
|
|
100
|
+
armature: Armature, animations: [Animation], frames: [int], blend_frames: [int]
|
|
101
|
+
):
|
|
102
|
+
bones = []
|
|
103
|
+
for a in range(len(animations)):
|
|
104
|
+
kf = animations[a].keyframes
|
|
105
|
+
bf = blend_frames[a]
|
|
106
|
+
ikf = interpolate_keyframes
|
|
107
|
+
|
|
108
|
+
for bone in armature.bones:
|
|
109
|
+
bone = copy.deepcopy(bone)
|
|
110
|
+
bones.append(bone)
|
|
111
|
+
id = bone.id
|
|
112
|
+
# yapf: disable
|
|
113
|
+
bone.pos.x = ikf(0, bone.pos.x, bone.init_pos.x, kf, frames[a], id, bf)
|
|
114
|
+
bone.pos.y = ikf(1, bone.pos.y, bone.init_pos.y, kf, frames[a], id, bf)
|
|
115
|
+
bone.rot = ikf(2, bone.rot, bone.init_rot, kf, frames[a], id, bf)
|
|
116
|
+
bone.scale.x = ikf(3, bone.scale.x, bone.init_scale.x, kf, frames[a], id, bf)
|
|
117
|
+
bone.scale.y = ikf(4, bone.scale.y, bone.init_scale.y, kf, frames[a], id, bf)
|
|
118
|
+
|
|
119
|
+
return bones
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def rotate(point: Vec2, rot: float):
|
|
123
|
+
return Vec2(
|
|
124
|
+
point.x * math.cos(rot) - point.y * math.sin(rot),
|
|
125
|
+
point.x * math.sin(rot) + point.y * math.cos(rot),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def inheritance(bones, ik_rots):
|
|
130
|
+
for bone in bones:
|
|
131
|
+
if bone.parent_id != -1:
|
|
132
|
+
# inherit parent
|
|
133
|
+
parent = bones[bone.parent_id]
|
|
134
|
+
|
|
135
|
+
bone.rot += parent.rot
|
|
136
|
+
bone.scale *= parent.scale
|
|
137
|
+
bone.pos *= parent.scale
|
|
138
|
+
|
|
139
|
+
bone.pos = rotate(bone.pos, parent.rot)
|
|
140
|
+
|
|
141
|
+
bone.pos += parent.pos
|
|
142
|
+
|
|
143
|
+
if bone.id in ik_rots:
|
|
144
|
+
bone.rot = ik_rots[bone.id]
|
|
145
|
+
|
|
146
|
+
return bones
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def magnitude(vec):
|
|
150
|
+
return math.sqrt(vec.x * vec.x + vec.y * vec.y)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def normalize(vec):
|
|
154
|
+
mag = magnitude(vec)
|
|
155
|
+
if mag == 0:
|
|
156
|
+
return Vec2(0, 0)
|
|
157
|
+
return Vec2(vec.x / mag, vec.y / mag)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def construct(armature: Armature):
|
|
161
|
+
inh_props = copy.deepcopy(armature.bones)
|
|
162
|
+
|
|
163
|
+
inh_props = inheritance(inh_props, {})
|
|
164
|
+
ik_rots = inverse_kinematics(inh_props, armature.ik_root_ids)
|
|
165
|
+
|
|
166
|
+
final_bones = copy.deepcopy(armature.bones)
|
|
167
|
+
final_bones = inheritance(final_bones, ik_rots)
|
|
168
|
+
|
|
169
|
+
return final_bones
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def inverse_kinematics(bones: list[Bone], ik_root_ids: list[int]):
|
|
173
|
+
ik_rots = {}
|
|
174
|
+
|
|
175
|
+
for root_id in ik_root_ids:
|
|
176
|
+
family = bones[root_id]
|
|
177
|
+
if (
|
|
178
|
+
family.ik_target_id == -1
|
|
179
|
+
or not family.ik_bone_ids
|
|
180
|
+
or not family.ik_target_id
|
|
181
|
+
):
|
|
182
|
+
continue
|
|
183
|
+
|
|
184
|
+
root = copy.deepcopy(bones[family.ik_bone_ids[0]].pos)
|
|
185
|
+
target = copy.deepcopy(bones[family.ik_target_id].pos)
|
|
186
|
+
|
|
187
|
+
for i in range(10):
|
|
188
|
+
fabrik(family, bones, root, target)
|
|
189
|
+
|
|
190
|
+
# setting bone rotations
|
|
191
|
+
end_bone = bones[family.ik_bone_ids[-1]].pos
|
|
192
|
+
tip_pos = end_bone
|
|
193
|
+
for i in range(len(family.ik_bone_ids) - 1, -1, -1):
|
|
194
|
+
dir = tip_pos - bones[family.ik_bone_ids[i]].pos
|
|
195
|
+
tip_pos = bones[family.ik_bone_ids[i]].pos
|
|
196
|
+
bones[family.ik_bone_ids[i]].rot = math.atan2(dir.y, dir.x)
|
|
197
|
+
|
|
198
|
+
# applying constraint
|
|
199
|
+
joint_dir = normalize(bones[family.ik_bone_ids[1]].pos - root)
|
|
200
|
+
base_dir = normalize(target - root)
|
|
201
|
+
dir = joint_dir.x * base_dir.y - base_dir.x * joint_dir.y
|
|
202
|
+
base_angle = math.atan2(base_dir.y, base_dir.x)
|
|
203
|
+
cw = family.ik_constraint == 1 and dir > 0
|
|
204
|
+
ccw = family.ik_constraint == 2 and dir < 0
|
|
205
|
+
if ccw or cw:
|
|
206
|
+
for i in family.ik_bone_ids:
|
|
207
|
+
bones[i].rot = -bones[i].rot + base_angle * 2
|
|
208
|
+
|
|
209
|
+
# saving rotations to map
|
|
210
|
+
for i in range(len(family.ik_bone_ids) - 1):
|
|
211
|
+
ik_rots[family.ik_bone_ids[i]] = bones[family.ik_bone_ids[i]].rot
|
|
212
|
+
|
|
213
|
+
return ik_rots
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def fabrik(family, bones, root, target):
|
|
217
|
+
# forward reaching
|
|
218
|
+
next_pos = bones[family.ik_target_id].pos
|
|
219
|
+
next_length = 0
|
|
220
|
+
for i in range(len(family.ik_bone_ids) - 1, -1, -1):
|
|
221
|
+
length = Vec2(0, 0)
|
|
222
|
+
if i != len(family.ik_bone_ids) - 1:
|
|
223
|
+
length = normalize(next_pos - bones[family.ik_bone_ids[i]].pos)
|
|
224
|
+
length.x *= next_length
|
|
225
|
+
length.y *= next_length
|
|
226
|
+
|
|
227
|
+
if i != 0:
|
|
228
|
+
next_bone = bones[family.ik_bone_ids[i - 1]]
|
|
229
|
+
bone_pos = bones[family.ik_bone_ids[i]].pos
|
|
230
|
+
next_length = magnitude(bone_pos - next_bone.pos)
|
|
231
|
+
|
|
232
|
+
bones[family.ik_bone_ids[i]].pos = next_pos - length
|
|
233
|
+
next_pos = bones[family.ik_bone_ids[i]].pos
|
|
234
|
+
|
|
235
|
+
# backward reaching
|
|
236
|
+
prev_pos = root
|
|
237
|
+
prev_length = 0
|
|
238
|
+
for i in range(len(family.ik_bone_ids)):
|
|
239
|
+
length = Vec2(0, 0)
|
|
240
|
+
if i != 0:
|
|
241
|
+
length = normalize(prev_pos - bones[family.ik_bone_ids[i]].pos)
|
|
242
|
+
length.x *= prev_length
|
|
243
|
+
length.y *= prev_length
|
|
244
|
+
|
|
245
|
+
if i != len(family.ik_bone_ids) - 1:
|
|
246
|
+
prev_bone = bones[family.ik_bone_ids[i + 1]]
|
|
247
|
+
bone_pos = bones[family.ik_bone_ids[i]].pos
|
|
248
|
+
prev_length = magnitude(bone_pos - prev_bone.pos)
|
|
249
|
+
|
|
250
|
+
bones[family.ik_bone_ids[i]].pos = prev_pos - length
|
|
251
|
+
prev_pos = bones[family.ik_bone_ids[i]].pos
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
# Flips bone's rotation if either axis of provided scale is negative.
|
|
255
|
+
# Returns new bone rotations
|
|
256
|
+
def check_bone_flip(bone_rot: float, scale: Vec2):
|
|
257
|
+
either = scale.x < 0 or scale.y < 0
|
|
258
|
+
both = scale.x < 0 and scale.y < 0
|
|
259
|
+
if either and not both:
|
|
260
|
+
bone_rot = -bone_rot
|
|
261
|
+
return bone_rot
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
# Returns a (bone.id, Texture) map of textures to draw bones with.
|
|
265
|
+
def setup_bone_textures(bones: [Bone], styles: [Style]):
|
|
266
|
+
final_textures = {}
|
|
267
|
+
for bone in bones:
|
|
268
|
+
for style in styles:
|
|
269
|
+
if bone.tex is None:
|
|
270
|
+
continue
|
|
271
|
+
final_tex = {}
|
|
272
|
+
has_final = False
|
|
273
|
+
for tex in style.textures:
|
|
274
|
+
if tex.name == bone.tex:
|
|
275
|
+
final_tex = tex
|
|
276
|
+
has_final = True
|
|
277
|
+
break
|
|
278
|
+
if has_final:
|
|
279
|
+
final_textures[bone.id] = final_tex
|
|
280
|
+
|
|
281
|
+
return final_textures
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def interpolate_keyframes(
|
|
285
|
+
element, field, default, keyframes, frame, bone_id, blend_frames
|
|
286
|
+
):
|
|
287
|
+
prev_kf = {}
|
|
288
|
+
next_kf = {}
|
|
289
|
+
|
|
290
|
+
for kf in keyframes:
|
|
291
|
+
if kf.frame < frame and kf.bone_id == bone_id and kf.element == element:
|
|
292
|
+
prev_kf = kf
|
|
293
|
+
|
|
294
|
+
for kf in keyframes:
|
|
295
|
+
if kf.frame >= frame and kf.bone_id == bone_id and kf.element == element:
|
|
296
|
+
next_kf = kf
|
|
297
|
+
break
|
|
298
|
+
|
|
299
|
+
if prev_kf == {}:
|
|
300
|
+
prev_kf = next_kf
|
|
301
|
+
elif next_kf == {}:
|
|
302
|
+
next_kf = prev_kf
|
|
303
|
+
|
|
304
|
+
if prev_kf == {} and next_kf == {}:
|
|
305
|
+
return interpolate(frame, blend_frames, field, default)
|
|
306
|
+
|
|
307
|
+
total_frames = next_kf.frame - prev_kf.frame
|
|
308
|
+
current_frame = frame - prev_kf.frame
|
|
309
|
+
|
|
310
|
+
result = interpolate(current_frame, total_frames, prev_kf.value, next_kf.value)
|
|
311
|
+
blend = interpolate(current_frame, blend_frames, field, result)
|
|
312
|
+
|
|
313
|
+
return blend
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def interpolate(current, max, start_val, end_val):
|
|
317
|
+
if current > max or max == 0:
|
|
318
|
+
return end_val
|
|
319
|
+
interp = current / max
|
|
320
|
+
end = end_val - start_val
|
|
321
|
+
return start_val + (end * interp)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def format_frame(frame, animation: Animation, reverse, loop):
|
|
325
|
+
last_kf = len(animation.keyframes) - 1
|
|
326
|
+
last_frame = animation.keyframes[last_kf].frame
|
|
327
|
+
|
|
328
|
+
if loop:
|
|
329
|
+
frame %= last_frame + 1
|
|
330
|
+
|
|
331
|
+
if reverse:
|
|
332
|
+
frame = last_frame - frame
|
|
333
|
+
|
|
334
|
+
return int(frame)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def time_frame(time, animation, reverse, loop):
|
|
338
|
+
frametime = 1 / animation.fps
|
|
339
|
+
frame = time / frametime
|
|
340
|
+
|
|
341
|
+
frame = format_frame(frame, animation, reverse, loop)
|
|
342
|
+
|
|
343
|
+
return int(frame)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import zipfile
|
|
2
|
+
import json
|
|
3
|
+
import sys
|
|
4
|
+
from typing import List
|
|
5
|
+
from types import SimpleNamespace
|
|
6
|
+
import math
|
|
7
|
+
|
|
8
|
+
sys.path.append("../../skelform_python")
|
|
9
|
+
|
|
10
|
+
import skelform_python
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def new_bone(id, x, y):
|
|
14
|
+
return SimpleNamespace(id=id, pos=SimpleNamespace(x=x, y=y))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def setup_armature():
|
|
18
|
+
armature = SimpleNamespace(bones=[], ik_families=[])
|
|
19
|
+
|
|
20
|
+
armature.bones.append(new_bone(0, 0, 150))
|
|
21
|
+
armature.bones.append(new_bone(1, 0, 0))
|
|
22
|
+
armature.bones.append(new_bone(2, 50, 0))
|
|
23
|
+
armature.bones.append(new_bone(3, 100, 0))
|
|
24
|
+
|
|
25
|
+
armature.ik_families.append(
|
|
26
|
+
SimpleNamespace(target_id=0, constraint="Clockwise", bone_ids=[1, 2, 3])
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
return armature
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def forward_reaching(bones, ik_families):
|
|
33
|
+
for family in ik_families:
|
|
34
|
+
if family.target_id == -1:
|
|
35
|
+
continue
|
|
36
|
+
next_pos = bones[family.target_id].pos
|
|
37
|
+
next_length = 0
|
|
38
|
+
for i in range(len(family.bone_ids) - 1, -1, -1):
|
|
39
|
+
length = skelform_python.Vec2(0, 0)
|
|
40
|
+
if i != len(family.bone_ids) - 1:
|
|
41
|
+
length = skelform_python.normalize(
|
|
42
|
+
skelform_python.vec_sub(next_pos, bones[family.bone_ids[i]].pos)
|
|
43
|
+
)
|
|
44
|
+
length.x *= next_length
|
|
45
|
+
length.y *= next_length
|
|
46
|
+
|
|
47
|
+
if i != 0:
|
|
48
|
+
next_bone = bones[family.bone_ids[i - 1]]
|
|
49
|
+
next_length = skelform_python.magnitude(
|
|
50
|
+
skelform_python.vec_sub(
|
|
51
|
+
bones[family.bone_ids[i]].pos, next_bone.pos
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
bones[family.bone_ids[i]].pos = skelform_python.vec_sub(next_pos, length)
|
|
56
|
+
next_pos = bones[family.bone_ids[i]].pos
|
|
57
|
+
print(f"{next_pos.x:.2f}", f"{next_pos.y:.2f}")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def backward_reaching(bones, ik_families, root):
|
|
61
|
+
for family in ik_families:
|
|
62
|
+
base_line = skelform_python.normalize(
|
|
63
|
+
skelform_python.vec_sub(bones[family.target_id].pos, root)
|
|
64
|
+
)
|
|
65
|
+
base_angle = math.atan2(base_line.y, base_line.x)
|
|
66
|
+
if family.target_id == -1:
|
|
67
|
+
continue
|
|
68
|
+
next_pos = root
|
|
69
|
+
next_length = 0
|
|
70
|
+
for i in range(len(family.bone_ids)):
|
|
71
|
+
length = skelform_python.Vec2(0, 0)
|
|
72
|
+
if i != 0:
|
|
73
|
+
length = skelform_python.normalize(
|
|
74
|
+
skelform_python.vec_sub(next_pos, bones[family.bone_ids[i]].pos)
|
|
75
|
+
)
|
|
76
|
+
length.x *= next_length
|
|
77
|
+
length.y *= next_length
|
|
78
|
+
|
|
79
|
+
if i != len(family.bone_ids) - 1:
|
|
80
|
+
next_bone = bones[family.bone_ids[i + 1]]
|
|
81
|
+
next_length = skelform_python.magnitude(
|
|
82
|
+
skelform_python.vec_sub(
|
|
83
|
+
bones[family.bone_ids[i]].pos, next_bone.pos
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
bones[family.bone_ids[i]].pos = skelform_python.vec_sub(next_pos, length)
|
|
88
|
+
|
|
89
|
+
if i != 0 and i != len(family.bone_ids) - 1 and family.constraint != "None":
|
|
90
|
+
joint_line = skelform_python.normalize(
|
|
91
|
+
skelform_python.vec_sub(next_pos, bones[family.bone_ids[i]].pos)
|
|
92
|
+
)
|
|
93
|
+
joint_angle = math.atan2(joint_line.y, joint_line.x) - base_angle
|
|
94
|
+
|
|
95
|
+
constraint_min = 0
|
|
96
|
+
constraint_max = 0
|
|
97
|
+
if family.constraint == "Clockwise":
|
|
98
|
+
constraint_min = -3.14
|
|
99
|
+
else:
|
|
100
|
+
constraint_max = 3.14
|
|
101
|
+
|
|
102
|
+
if joint_angle > constraint_max or joint_angle < constraint_min:
|
|
103
|
+
push_angle = -joint_angle * 2
|
|
104
|
+
new_point = skelform_python.rotate(
|
|
105
|
+
skelform_python.vec_sub(
|
|
106
|
+
bones[family.bone_ids[i]].pos, next_pos
|
|
107
|
+
),
|
|
108
|
+
push_angle,
|
|
109
|
+
)
|
|
110
|
+
bones[family.bone_ids[i]].pos = skelform_python.vec_add(
|
|
111
|
+
new_point, next_pos
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
next_pos = bones[family.bone_ids[i]].pos
|
|
115
|
+
print(f"{next_pos.x:.2f}", f"{next_pos.y:.2f}")
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def rotations(bones, ik_families):
|
|
119
|
+
for family in ik_families:
|
|
120
|
+
end_bone = bones[family.bone_ids[-1]].pos
|
|
121
|
+
tip_pos = end_bone
|
|
122
|
+
for i in range(len(family.bone_ids) - 1, -1, -1):
|
|
123
|
+
dir = skelform_python.vec_sub(tip_pos, bones[family.bone_ids[i]].pos)
|
|
124
|
+
tip_pos = bones[family.bone_ids[i]].pos
|
|
125
|
+
angle = math.atan2(dir.y, dir.x)
|
|
126
|
+
print(f"{angle:.2f}", f"{angle * 180 / 3.14:.2f}")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
armature = setup_armature()
|
|
130
|
+
|
|
131
|
+
root = armature.bones[armature.ik_families[0].bone_ids[0]].pos
|
|
132
|
+
|
|
133
|
+
print()
|
|
134
|
+
print("forward reaching:")
|
|
135
|
+
forward_reaching(armature.bones, armature.ik_families)
|
|
136
|
+
print()
|
|
137
|
+
|
|
138
|
+
print("backward reaching:")
|
|
139
|
+
backward_reaching(armature.bones, armature.ik_families, root)
|
|
140
|
+
print()
|
|
141
|
+
|
|
142
|
+
print("rotations:")
|
|
143
|
+
rotations(armature.bones, armature.ik_families)
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import math
|
|
2
|
-
import copy
|
|
3
|
-
import pytweening
|
|
4
|
-
import zipfile
|
|
5
|
-
|
|
6
|
-
def get_frame_by_time(armature, anim_idx, elapsed, reverse):
|
|
7
|
-
anim = armature["animations"][anim_idx]
|
|
8
|
-
last_frame = anim["keyframes"][-1]["frame"]
|
|
9
|
-
|
|
10
|
-
frametime = 1 / anim["fps"]
|
|
11
|
-
frame = elapsed / frametime
|
|
12
|
-
|
|
13
|
-
if reverse:
|
|
14
|
-
frame = last_frame - frame
|
|
15
|
-
|
|
16
|
-
return frame
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def animate(armature, anim_idx, frame, after_animate=None):
|
|
20
|
-
props = []
|
|
21
|
-
keyframes = armature["animations"][anim_idx]["keyframes"]
|
|
22
|
-
|
|
23
|
-
frame %= keyframes[-1]["frame"]
|
|
24
|
-
|
|
25
|
-
for bone in armature["bones"]:
|
|
26
|
-
prop = copy.deepcopy(bone)
|
|
27
|
-
props.append(prop)
|
|
28
|
-
|
|
29
|
-
# interpolate
|
|
30
|
-
# yapf: disable
|
|
31
|
-
prop["rot"] += animate_float(keyframes, frame, prop["id"], "Rotation", 0)
|
|
32
|
-
prop["pos"]["x"] += animate_float(keyframes, frame, prop["id"], "PositionX", 0)
|
|
33
|
-
prop["pos"]["y"] += animate_float(keyframes, frame, prop["id"], "PositionY", 0)
|
|
34
|
-
prop["scale"]["x"] *= animate_float(keyframes, frame, prop["id"], "ScaleX", 1)
|
|
35
|
-
prop["scale"]["y"] *= animate_float(keyframes, frame, prop["id"], "ScaleY", 1)
|
|
36
|
-
|
|
37
|
-
try:
|
|
38
|
-
after_animate(props, prop)
|
|
39
|
-
except:
|
|
40
|
-
pass
|
|
41
|
-
|
|
42
|
-
if prop["parent_id"] == -1:
|
|
43
|
-
continue
|
|
44
|
-
|
|
45
|
-
# inherit parent
|
|
46
|
-
parent = [prop for prop in props if prop["id"] == props[-1]["parent_id"]][0]
|
|
47
|
-
|
|
48
|
-
prop["rot"] += parent["rot"]
|
|
49
|
-
prop["scale"]["x"] *= parent["scale"]["x"]
|
|
50
|
-
prop["scale"]["y"] *= parent["scale"]["y"]
|
|
51
|
-
prop["pos"]["x"] *= parent["scale"]["x"]
|
|
52
|
-
prop["pos"]["y"] *= parent["scale"]["y"]
|
|
53
|
-
|
|
54
|
-
x = copy.deepcopy(prop["pos"]["x"])
|
|
55
|
-
y = copy.deepcopy(prop["pos"]["y"])
|
|
56
|
-
prop["pos"]["x"] = x * math.cos(parent["rot"]) - y * math.sin(parent["rot"])
|
|
57
|
-
prop["pos"]["y"] = x * math.sin(parent["rot"]) + y * math.cos(parent["rot"])
|
|
58
|
-
|
|
59
|
-
prop["pos"]["x"] += parent["pos"]["x"]
|
|
60
|
-
prop["pos"]["y"] += parent["pos"]["y"]
|
|
61
|
-
|
|
62
|
-
return props
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def animate_float(keyframes, frame, bone_id, element, default):
|
|
66
|
-
prev_kf = {}
|
|
67
|
-
next_kf = {}
|
|
68
|
-
|
|
69
|
-
for kf in keyframes:
|
|
70
|
-
if kf["frame"] > frame:
|
|
71
|
-
break
|
|
72
|
-
elif kf["bone_id"] == bone_id and kf["element"] == element:
|
|
73
|
-
prev_kf = kf
|
|
74
|
-
|
|
75
|
-
for kf in keyframes:
|
|
76
|
-
if (
|
|
77
|
-
kf["frame"] >= frame
|
|
78
|
-
and kf["bone_id"] == bone_id
|
|
79
|
-
and kf["element"] == element
|
|
80
|
-
):
|
|
81
|
-
next_kf = kf
|
|
82
|
-
break
|
|
83
|
-
|
|
84
|
-
if prev_kf == {}:
|
|
85
|
-
prev_kf = next_kf
|
|
86
|
-
elif next_kf == {}:
|
|
87
|
-
next_kf = prev_kf
|
|
88
|
-
|
|
89
|
-
if prev_kf == {} and next_kf == {}:
|
|
90
|
-
return default
|
|
91
|
-
|
|
92
|
-
total_frames = next_kf["frame"] - prev_kf["frame"]
|
|
93
|
-
current_frame = frame - prev_kf["frame"]
|
|
94
|
-
|
|
95
|
-
if total_frames == 0:
|
|
96
|
-
return prev_kf["value"]
|
|
97
|
-
|
|
98
|
-
interp = current_frame / total_frames
|
|
99
|
-
start = prev_kf["value"]
|
|
100
|
-
end = next_kf["value"] - prev_kf["value"]
|
|
101
|
-
result = start + (end * interp)
|
|
102
|
-
return result
|
|
File without changes
|
|
File without changes
|