Continue Blender Web parity task handoff
This commit is contained in:
41
tools/web/check-action-armature-subdivide-desktop.py
Normal file
41
tools/web/check-action-armature-subdivide-desktop.py
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib, json, os, pathlib, shutil, sys, tempfile
|
||||
import bpy
|
||||
|
||||
ARMATURE_NAME="WebGapArmatureSubdivideArmature"; OBJECT_NAME="WebGapArmatureSubdivideObject"; SOURCE_BONE="WebGapArmatureSubdivideSource"; OTHER_BONE="WebGapArmatureSubdivideOther"
|
||||
|
||||
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("subdivide 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 bone_report(b):
|
||||
return {"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]}
|
||||
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":[bone_report(b) for b in arm.edit_bones]}
|
||||
def stable(s): return {"object":s["object"],"armature":s["armature"],"activeBone":s["activeBone"],"bones":sorted(s["bones"],key=lambda b:b["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(v).resolve() for v in args); bpy.ops.wm.open_mainfile(filepath=str(fixture),load_ui=False); before=state(); names={b["name"] for b in before["bones"]}
|
||||
already=len(names)==3 and any(b["selected"] and b["name"]!=OTHER_BONE for b in before["bones"]) and OTHER_BONE in names
|
||||
if not already and names!={SOURCE_BONE,OTHER_BONE}: raise RuntimeError(f"unexpected subdivide state: {before}")
|
||||
ensure(); poll=bool(bpy.ops.armature.subdivide.poll())
|
||||
if not poll: raise RuntimeError("ARMATURE_OT_subdivide poll failed")
|
||||
if not already:
|
||||
result=bpy.ops.armature.subdivide(number_cuts=1)
|
||||
if result!={"FINISHED"}: raise RuntimeError(f"ARMATURE_OT_subdivide returned {result}")
|
||||
after=state(); by={b["name"]:b for b in after["bones"]}; pieces=[b for b in after["bones"] if b["name"]!=OTHER_BONE]
|
||||
if len(pieces)!=2 or len(after["bones"])!=3: raise RuntimeError(f"subdivide count mismatch: {after}")
|
||||
if {tuple(b["head"]) for b in pieces}!={(0.0,0.0,0.0),(0.0,0.5,0.0)} or {tuple(b["tail"]) for b in pieces}!={(0.0,0.5,0.0),(0.0,1.0,0.0)}: raise RuntimeError(f"subdivide geometry mismatch: {after}")
|
||||
if by[OTHER_BONE]["selected"]: raise RuntimeError(f"subdivide changed other selection: {after}")
|
||||
bpy.ops.object.mode_set(mode="OBJECT"); fd,tmp=tempfile.mkstemp(prefix="m16-subdivide-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"subdivide save/reopen drift: {after} != {reopened}")
|
||||
shutil.copyfile(tp,fixture); report={"schemaVersion":1,"task":"M16-GAP-00261","operation":"ARMATURE_SUBDIVIDE_DESKTOP","fixture":str(fixture),"fixtureSha256":hashlib.sha256(fixture.read_bytes()).hexdigest(),"before":before,"after":reopened,"sourceBone":SOURCE_BONE,"otherBone":OTHER_BONE,"numberCuts":1,"operatorStatus":"FINISHED" if not already else "SKIPPED_ALREADY_APPLIED","mainMutation":"BONE_SUBDIVIDED_ONCE" if not already else "BONE_ALREADY_SUBDIVIDED_ONCE","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-subdivide-desktop-ok cuts=1 pieces=2 saveReopen=exact")
|
||||
finally: tp.unlink(missing_ok=True)
|
||||
if __name__=="__main__":
|
||||
try: main()
|
||||
except Exception as e: print(f"armature-subdivide-desktop-failed: {e}"); raise SystemExit(1)
|
||||
Reference in New Issue
Block a user