mirror of
https://github.com/OpenVGLab/OmniLottie.git
synced 2026-09-16 23:26:26 +00:00
98 lines
2.8 KiB
Python
98 lines
2.8 KiB
Python
from..nvector import NVector
|
|
|
|
|
|
# FABRIK
|
|
class Chain:
|
|
def __init__(self, tail, fixed_tail=True, tolerance=0.5, max_iter=8):
|
|
self.joints = [tail.clone()]
|
|
self.fixed_tail = fixed_tail
|
|
self.lengths = []
|
|
self.total_length = 0
|
|
self.tolerance = tolerance
|
|
self.max_iter = max_iter
|
|
|
|
def add_joint(self, point):
|
|
length = (point - self.joints[-1]).length
|
|
self.lengths.append(length)
|
|
self.total_length += length
|
|
self.joints.append(point.clone())
|
|
|
|
def add_joints(self, head, n):
|
|
delta = head - self.joints[-1]
|
|
self.total_length += delta.length
|
|
segment = delta / n
|
|
seglen = segment.length
|
|
for i in range(n):
|
|
self.lengths.append(seglen)
|
|
self.joints.append(self.joints[-1] + segment)
|
|
|
|
def backward(self, target):
|
|
"""!
|
|
target -> -> start
|
|
"""
|
|
self.joints[-1] = target
|
|
for i in range(len(self.joints)-2, -1, -1):
|
|
r = self.joints[i+1] - self.joints[i]
|
|
l = self.lengths[i] / r.length
|
|
self.joints[i] = self.joints[i+1].lerp(self.joints[i], l)
|
|
|
|
def forward(self, target):
|
|
"""!
|
|
start -> -> tail
|
|
"""
|
|
self.joints[0] = target
|
|
for i in range(0, len(self.joints)-1):
|
|
r = self.joints[i+1] - self.joints[i]
|
|
l = self.lengths[i] / r.length
|
|
self.joints[i+1] = self.joints[i].lerp(self.joints[i+1], l)
|
|
|
|
def reach(self, target):
|
|
if not self.fixed_tail:
|
|
self.backward(target)
|
|
return
|
|
|
|
distance = (target - self.joints[0]).length
|
|
if distance >= self.total_length:
|
|
for i in range(len(self.joints)-1):
|
|
r = target - self.joints[i]
|
|
l = self.lengths[i] / r.length
|
|
self.joints[i+1] = self.joints[i].lerp(target, l)
|
|
return
|
|
|
|
base = self.joints[0]
|
|
|
|
distance = (target - self.joints[-1]).length
|
|
n_it = 0
|
|
while distance > self.tolerance and n_it < self.max_iter:
|
|
self.backward(target)
|
|
self.forward(base)
|
|
distance = (target - self.joints[-1]).length
|
|
n_it += 1
|
|
|
|
|
|
class Octopus:
|
|
def __init__(self, master):
|
|
self.chains = {"master": master}
|
|
self.master = master
|
|
|
|
@property
|
|
def base(self):
|
|
return self.master.joints[-1]
|
|
|
|
def add_chain(self, name):
|
|
ch = Chain(self.base)
|
|
self.chains[name] = ch
|
|
return ch
|
|
|
|
def reach(self, target_map):
|
|
centroid = NVector(0, 0)
|
|
for chain, target in target_map.items():
|
|
self.chains[chain].backward(target)
|
|
centroid += self.chains[chain].joints[0]
|
|
centroid /= len(target_map)
|
|
|
|
self.master.reach(centroid)
|
|
|
|
for chain in target_map.keys():
|
|
self.chains[chain].forward(self.base)
|