# Edge-stretch metric: how much mesh edges change length during a clip (0 = perfectly rigid).
# blender -b -P stretch.py -- anim.glb "label:cond;cond|label:cond" [nframes]
import bpy, sys, mathutils
a = sys.argv[sys.argv.index("--") + 1:]
src, spec = a[0], a[1]; nf = int(a[2]) if len(a) > 2 else 12
bpy.ops.wm.read_factory_settings(use_empty=True); bpy.ops.import_scene.gltf(filepath=src)
for o in list(bpy.context.scene.objects):
    if o.type == "MESH" and o.name.startswith("Ico"): bpy.data.objects.remove(o)
arm = [o for o in bpy.context.scene.objects if o.type == "ARMATURE"][0]
me = [o for o in bpy.context.scene.objects if o.type == "MESH"][0]
act = arm.animation_data.action if arm.animation_data and arm.animation_data.action else bpy.data.actions[0]
f0, f1 = act.frame_range
def coords():
    dg = bpy.context.evaluated_depsgraph_get(); e = me.evaluated_get(dg); m = e.to_mesh()
    c = [e.matrix_world @ v.co for v in m.vertices]; e.to_mesh_clear(); return c
arm.data.pose_position = "REST"; bpy.context.view_layer.update(); rest = coords()
arm.data.pose_position = "POSE"
edges = [tuple(e.vertices) for e in me.data.edges]
regions = []
for part in spec.split("|"):
    label, conds = part.split(":", 1); conds = conds.split(";")
    sel = [all(eval(c, {"x": p.x, "y": p.y, "z": p.z, "abs": abs}) for c in conds) for p in rest]
    es = [(i, j, (rest[i] - rest[j]).length) for i, j in edges if sel[i] and sel[j]]
    es = [(i, j, l) for i, j, l in es if l > 1e-5]
    regions.append((label, es, []))
for k in range(nf):
    f = f0 + (f1 - f0) * k / max(1, nf - 1)
    bpy.context.scene.frame_set(int(round(f)))
    c = coords()
    for label, es, acc in regions:
        acc.extend(abs((c[i] - c[j]).length - l) / l for i, j, l in es)
for label, es, acc in regions:
    acc.sort(); n = len(acc)
    if not n: print("STRETCH %-10s no edges" % label); continue
    q = lambda t: acc[min(n - 1, int(t * n))]
    print("STRETCH %-10s edges=%6d  median=%5.1f%%  p90=%5.1f%%  p99=%5.1f%%  >10%%:%5.1f%% of samples"
          % (label, len(es), 100 * q(.5), 100 * q(.9), 100 * q(.99), 100 * sum(1 for x in acc if x > .1) / n))
