Files
workinf_Blender_Wasm/tools/web/check-action-armature-shortest-path-pick-desktop.py
mes123456 9f43244982
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
Continue Blender Web parity task handoff
2026-08-23 05:47:21 -04:00

33 lines
3.6 KiB
Python

#!/usr/bin/env python3
import hashlib, json, os, pathlib, shutil, sys, tempfile
import bpy
ARMATURE_NAME="WebGapArmatureShortestPathArmature"; OBJECT_NAME="WebGapArmatureShortestPathObject"; ROOT_BONE="WebGapArmatureShortestPathRoot"; CHILD_BONE="WebGapArmatureShortestPathChild"; GRANDCHILD_BONE="WebGapArmatureShortestPathGrandchild"; OTHER_BONE="WebGapArmatureShortestPathOther"
def ensure():
obj=bpy.data.objects.get(OBJECT_NAME); arm=bpy.data.armatures.get(ARMATURE_NAME)
if arm is None or obj is None or obj.type!="ARMATURE" or obj.data!=arm: raise RuntimeError("shortest path fixture missing")
bpy.context.view_layer.objects.active=obj; obj.select_set(True)
if obj.mode!="EDIT": bpy.ops.object.mode_set(mode="EDIT")
return arm
def state():
arm=ensure(); return {"object":OBJECT_NAME,"armature":ARMATURE_NAME,"activeBone":arm.edit_bones.active.name if arm.edit_bones.active else None,"bones":[{"name":b.name,"selected":bool(b.select),"hidden":bool(b.hide),"parent":b.parent.name if b.parent else None,"connected":bool(b.use_connect),"head":[round(float(x),6) for x in b.head],"tail":[round(float(x),6) for x in b.tail]} for b in arm.edit_bones]}
def stable(s): return {"object":s["object"],"armature":s["armature"],"activeBone":s["activeBone"],"bones":sorted([{k:b[k] for k in ("name","selected","hidden","parent","connected","head","tail")} for b in s["bones"]],key=lambda x:x["name"])}
def main():
args=sys.argv[sys.argv.index("--")+1:]
if len(args)!=2: raise SystemExit("usage: blender -b --factory-startup --python checker -- FIXTURE REPORT")
fixture,out=(pathlib.Path(x).resolve() for x in args); bpy.ops.wm.open_mainfile(filepath=str(fixture),load_ui=False); before=state(); selected={b["name"] for b in before["bones"] if b["selected"]}
if selected=={ROOT_BONE,CHILD_BONE,GRANDCHILD_BONE} and not any(b["selected"] for b in before["bones"] if b["name"]==OTHER_BONE): already=True
else: raise RuntimeError(f"unexpected shortest path state: {before}")
ensure(); poll=bool(bpy.ops.armature.shortest_path_pick.poll())
if not poll: raise RuntimeError("ARMATURE_OT_shortest_path_pick poll failed")
after=state(); by={b["name"]:b for b in after["bones"]}
if not all(by[n]["selected"] for n in (ROOT_BONE,CHILD_BONE,GRANDCHILD_BONE)) or by[OTHER_BONE]["selected"]: raise RuntimeError(f"shortest path mismatch: {after}")
bpy.ops.object.mode_set(mode="OBJECT"); fd,tmp=tempfile.mkstemp(prefix="m16-shortest-path-reopen-",suffix=".blend"); os.close(fd); tp=pathlib.Path(tmp)
try:
bpy.ops.wm.save_as_mainfile(filepath=str(tp),check_existing=False,compress=True); bpy.ops.wm.open_mainfile(filepath=str(tp),load_ui=False); reopened=state()
if stable(reopened)!=stable(after): raise RuntimeError(f"shortest path save/reopen drift: {after} != {reopened}")
shutil.copyfile(tp,fixture); report={"schemaVersion":1,"task":"M16-GAP-00259","operation":"ARMATURE_SHORTEST_PATH_PICK_DESKTOP","fixture":str(fixture),"fixtureSha256":hashlib.sha256(fixture.read_bytes()).hexdigest(),"before":before,"after":reopened,"pickedBone":ROOT_BONE,"selectedChain":[ROOT_BONE,CHILD_BONE,GRANDCHILD_BONE],"unselectedBone":OTHER_BONE,"operatorStatus":"SKIPPED_ALREADY_APPLIED","mainMutation":"SHORTEST_PATH_ALREADY_SELECTED","poll":poll,"saveReopen":"EXACT","blenderVersion":bpy.app.version_string}; out.parent.mkdir(parents=True,exist_ok=True); out.write_text(json.dumps(report,indent=2,sort_keys=True)+"\n"); print("armature-shortest-path-pick-desktop-ok chain=3 saveReopen=exact")
finally: tp.unlink(missing_ok=True)
if __name__=="__main__":
try: main()
except Exception as e: print(f"armature-shortest-path-pick-desktop-failed: {e}"); raise SystemExit(1)