"""Probe STL normal/unit/edge behavior in pinned Blender 5.2 for M12-07E.""" import hashlib import json import sys from pathlib import Path import bpy def sha256_file(path): digest = hashlib.sha256() with open(path, "rb") as handle: for chunk in iter(lambda: handle.read(1024 * 1024), b""): digest.update(chunk) return digest.hexdigest() def import_case(path, global_scale): bpy.ops.wm.read_factory_settings(use_empty=True) result = bpy.ops.wm.stl_import( filepath=str(path), directory=str(path.parent), forward_axis="NEGATIVE_Z", up_axis="Y", global_scale=global_scale, use_scene_unit=False, use_facet_normal=True, use_mesh_validate=True, ) objects = [obj for obj in bpy.context.scene.objects if obj.type == "MESH"] if "FINISHED" not in result or not objects: return {"status": "BLOCKED", "objectCount": len(objects)} mesh = objects[0].data mesh.calc_loop_triangles() coordinates = [[float(value) for value in (objects[0].matrix_world @ vertex.co)] for vertex in mesh.vertices] bounds = { "min": [min(values) for values in zip(*coordinates)], "max": [max(values) for values in zip(*coordinates)], } if coordinates else {"min": [0.0, 0.0, 0.0], "max": [0.0, 0.0, 0.0]} return { "status": "ACCEPTED", "objectCount": len(objects), "vertexCount": len(mesh.vertices), "polygonCount": len(mesh.polygons), "triangleCount": len(mesh.loop_triangles), "bounds": bounds, "polygonNormals": [[float(value) for value in polygon.normal] for polygon in mesh.polygons], } def main(fixture_root, report_path): fixture_root = Path(fixture_root).resolve() report_path = Path(report_path).resolve() cases = [] for case_id, name, scale in ( ("BINARY_UNIT_1", "capability-binary.stl", 1.0), ("BINARY_UNIT_001", "capability-binary.stl", 0.001), ("DEGENERATE_TRIANGLE", "degenerate-binary.stl", 1.0), ("TRAILING_BYTES", "trailing-binary.stl", 1.0), ): path = fixture_root / name cases.append({"id": case_id, "file": name, "globalScale": scale, "byteLength": path.stat().st_size, "sha256": sha256_file(path), "result": import_case(path, scale)}) report = {"schemaVersion": 1, "task": "M12-07E", "operation": "BLENDER_STL_EDGE_PROBE", "cases": cases, "nextTask": "M12-07F"} report_path.parent.mkdir(parents=True, exist_ok=True) report_path.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8") print("stl-edge-probe-complete cases=%s next=%s" % (len(cases), report["nextTask"])) if __name__ == "__main__": args = sys.argv[sys.argv.index("--") + 1 :] if "--" in sys.argv else [] if len(args) != 2: raise SystemExit("usage: blender --background --python probe-stl-edge-fixtures.py -- FIXTURE_ROOT REPORT") main(args[0], args[1])