Spaces:
Running
Running
import numpy as np | |
# import scipy.sparse as sparse | |
import visualization.Animation as Animation | |
""" Family Functions """ | |
def joints(parents): | |
""" | |
Parameters | |
---------- | |
parents : (J) ndarray | |
parents array | |
Returns | |
------- | |
joints : (J) ndarray | |
Array of joint indices | |
""" | |
return np.arange(len(parents), dtype=int) | |
def joints_list(parents): | |
""" | |
Parameters | |
---------- | |
parents : (J) ndarray | |
parents array | |
Returns | |
------- | |
joints : [ndarray] | |
List of arrays of joint idices for | |
each joint | |
""" | |
return list(joints(parents)[:, np.newaxis]) | |
def parents_list(parents): | |
""" | |
Parameters | |
---------- | |
parents : (J) ndarray | |
parents array | |
Returns | |
------- | |
parents : [ndarray] | |
List of arrays of joint idices for | |
the parents of each joint | |
""" | |
return list(parents[:, np.newaxis]) | |
def children_list(parents): | |
""" | |
Parameters | |
---------- | |
parents : (J) ndarray | |
parents array | |
Returns | |
------- | |
children : [ndarray] | |
List of arrays of joint indices for | |
the children of each joint | |
""" | |
def joint_children(i): | |
return [j for j, p in enumerate(parents) if p == i] | |
return list(map(lambda j: np.array(joint_children(j)), joints(parents))) | |
def descendants_list(parents): | |
""" | |
Parameters | |
---------- | |
parents : (J) ndarray | |
parents array | |
Returns | |
------- | |
descendants : [ndarray] | |
List of arrays of joint idices for | |
the descendants of each joint | |
""" | |
children = children_list(parents) | |
def joint_descendants(i): | |
return sum([joint_descendants(j) for j in children[i]], list(children[i])) | |
return list(map(lambda j: np.array(joint_descendants(j)), joints(parents))) | |
def ancestors_list(parents): | |
""" | |
Parameters | |
---------- | |
parents : (J) ndarray | |
parents array | |
Returns | |
------- | |
ancestors : [ndarray] | |
List of arrays of joint idices for | |
the ancestors of each joint | |
""" | |
decendants = descendants_list(parents) | |
def joint_ancestors(i): | |
return [j for j in joints(parents) if i in decendants[j]] | |
return list(map(lambda j: np.array(joint_ancestors(j)), joints(parents))) | |
""" Mask Functions """ | |
def mask(parents, filter): | |
""" | |
Constructs a Mask for a give filter | |
A mask is a (J, J) ndarray truth table for a given | |
condition over J joints. For example there | |
may be a mask specifying if a joint N is a | |
child of another joint M. | |
This could be constructed into a mask using | |
`m = mask(parents, children_list)` and the condition | |
of childhood tested using `m[N, M]`. | |
Parameters | |
---------- | |
parents : (J) ndarray | |
parents array | |
filter : (J) ndarray -> [ndarray] | |
function that outputs a list of arrays | |
of joint indices for some condition | |
Returns | |
------- | |
mask : (N, N) ndarray | |
boolean truth table of given condition | |
""" | |
m = np.zeros((len(parents), len(parents))).astype(bool) | |
jnts = joints(parents) | |
fltr = filter(parents) | |
for i, f in enumerate(fltr): m[i, :] = np.any(jnts[:, np.newaxis] == f[np.newaxis, :], axis=1) | |
return m | |
def joints_mask(parents): return np.eye(len(parents)).astype(bool) | |
def children_mask(parents): return mask(parents, children_list) | |
def parents_mask(parents): return mask(parents, parents_list) | |
def descendants_mask(parents): return mask(parents, descendants_list) | |
def ancestors_mask(parents): return mask(parents, ancestors_list) | |
""" Search Functions """ | |
def joint_chain_ascend(parents, start, end): | |
chain = [] | |
while start != end: | |
chain.append(start) | |
start = parents[start] | |
chain.append(end) | |
return np.array(chain, dtype=int) | |
""" Constraints """ | |
def constraints(anim, **kwargs): | |
""" | |
Constraint list for Animation | |
This constraint list can be used in the | |
VerletParticle solver to constrain | |
a animation global joint positions. | |
Parameters | |
---------- | |
anim : Animation | |
Input animation | |
masses : (F, J) ndarray | |
Optional list of masses | |
for joints J across frames F | |
defaults to weighting by | |
vertical height | |
Returns | |
------- | |
constraints : [(int, int, (F, J) ndarray, (F, J) ndarray, (F, J) ndarray)] | |
A list of constraints in the format: | |
(Joint1, Joint2, Masses1, Masses2, Lengths) | |
""" | |
masses = kwargs.pop('masses', None) | |
children = children_list(anim.parents) | |
constraints = [] | |
points_offsets = Animation.offsets_global(anim) | |
points = Animation.positions_global(anim) | |
if masses is None: | |
masses = 1.0 / (0.1 + np.absolute(points_offsets[:, 1])) | |
masses = masses[np.newaxis].repeat(len(anim), axis=0) | |
for j in range(anim.shape[1]): | |
""" Add constraints between all joints and their children """ | |
for c0 in children[j]: | |
dists = np.sum((points[:, c0] - points[:, j]) ** 2.0, axis=1) ** 0.5 | |
constraints.append((c0, j, masses[:, c0], masses[:, j], dists)) | |
""" Add constraints between all children of joint """ | |
for c1 in children[j]: | |
if c0 == c1: continue | |
dists = np.sum((points[:, c0] - points[:, c1]) ** 2.0, axis=1) ** 0.5 | |
constraints.append((c0, c1, masses[:, c0], masses[:, c1], dists)) | |
return constraints | |
""" Graph Functions """ | |
def graph(anim): | |
""" | |
Generates a weighted adjacency matrix | |
using local joint distances along | |
the skeletal structure. | |
Joints which are not connected | |
are assigned the weight `0`. | |
Joints which actually have zero distance | |
between them, but are still connected, are | |
perturbed by some minimal amount. | |
The output of this routine can be used | |
with the `scipy.sparse.csgraph` | |
routines for graph analysis. | |
Parameters | |
---------- | |
anim : Animation | |
input animation | |
Returns | |
------- | |
graph : (N, N) ndarray | |
weight adjacency matrix using | |
local distances along the | |
skeletal structure from joint | |
N to joint M. If joints are not | |
directly connected are assigned | |
the weight `0`. | |
""" | |
graph = np.zeros(anim.shape[1], anim.shape[1]) | |
lengths = np.sum(anim.offsets ** 2.0, axis=1) ** 0.5 + 0.001 | |
for i, p in enumerate(anim.parents): | |
if p == -1: continue | |
graph[i, p] = lengths[p] | |
graph[p, i] = lengths[p] | |
return graph | |
def distances(anim): | |
""" | |
Generates a distance matrix for | |
pairwise joint distances along | |
the skeletal structure | |
Parameters | |
---------- | |
anim : Animation | |
input animation | |
Returns | |
------- | |
distances : (N, N) ndarray | |
array of pairwise distances | |
along skeletal structure | |
from some joint N to some | |
joint M | |
""" | |
distances = np.zeros((anim.shape[1], anim.shape[1])) | |
generated = distances.copy().astype(bool) | |
joint_lengths = np.sum(anim.offsets ** 2.0, axis=1) ** 0.5 | |
joint_children = children_list(anim) | |
joint_parents = parents_list(anim) | |
def find_distance(distances, generated, prev, i, j): | |
""" If root, identity, or already generated, return """ | |
if j == -1: return (0.0, True) | |
if j == i: return (0.0, True) | |
if generated[i, j]: return (distances[i, j], True) | |
""" Find best distances along parents and children """ | |
par_dists = [(joint_lengths[j], find_distance(distances, generated, j, i, p)) for p in joint_parents[j] if | |
p != prev] | |
out_dists = [(joint_lengths[c], find_distance(distances, generated, j, i, c)) for c in joint_children[j] if | |
c != prev] | |
""" Check valid distance and not dead end """ | |
par_dists = [a + d for (a, (d, f)) in par_dists if f] | |
out_dists = [a + d for (a, (d, f)) in out_dists if f] | |
""" All dead ends """ | |
if (out_dists + par_dists) == []: return (0.0, False) | |
""" Get minimum path """ | |
dist = min(out_dists + par_dists) | |
distances[i, j] = dist; | |
distances[j, i] = dist | |
generated[i, j] = True; | |
generated[j, i] = True | |
for i in range(anim.shape[1]): | |
for j in range(anim.shape[1]): | |
find_distance(distances, generated, -1, i, j) | |
return distances | |
def edges(parents): | |
""" | |
Animation structure edges | |
Parameters | |
---------- | |
parents : (J) ndarray | |
parents array | |
Returns | |
------- | |
edges : (M, 2) ndarray | |
array of pairs where each | |
pair contains two indices of a joints | |
which corrisponds to an edge in the | |
joint structure going from parent to child. | |
""" | |
return np.array(list(zip(parents, joints(parents)))[1:]) | |
def incidence(parents): | |
""" | |
Incidence Matrix | |
Parameters | |
---------- | |
parents : (J) ndarray | |
parents array | |
Returns | |
------- | |
incidence : (N, M) ndarray | |
Matrix of N joint positions by | |
M edges which each entry is either | |
1 or -1 and multiplication by the | |
joint positions returns the an | |
array of vectors along each edge | |
of the structure | |
""" | |
es = edges(parents) | |
inc = np.zeros((len(parents) - 1, len(parents))).astype(np.int) | |
for i, e in enumerate(es): | |
inc[i, e[0]] = 1 | |
inc[i, e[1]] = -1 | |
return inc.T | |