Checkpoint web parity through Chromium input tasks
This commit is contained in:
135
tools/web/generate-stl-capability-fixtures.py
Normal file
135
tools/web/generate-stl-capability-fixtures.py
Normal file
@@ -0,0 +1,135 @@
|
||||
"""Generate pinned Blender 5.2 binary and ASCII STL capability fixtures for M12-07D."""
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import struct
|
||||
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 text(value):
|
||||
return value.decode("utf-8") if isinstance(value, bytes) else value
|
||||
|
||||
|
||||
def runtime_identity():
|
||||
binary = Path(bpy.app.binary_path)
|
||||
return {
|
||||
"blenderVersion": text(bpy.app.version_string),
|
||||
"versionTuple": list(bpy.app.version),
|
||||
"buildDate": text(bpy.app.build_date),
|
||||
"buildTime": text(bpy.app.build_time),
|
||||
"buildHash": text(bpy.app.build_hash),
|
||||
"buildBranch": text(bpy.app.build_branch),
|
||||
"buildPlatform": text(bpy.app.build_platform),
|
||||
"buildType": text(bpy.app.build_type),
|
||||
"binarySha256": sha256_file(binary),
|
||||
}
|
||||
|
||||
|
||||
def create_scene():
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("M12 STL Capability Mesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2), (0, 2, 3)],
|
||||
)
|
||||
mesh.update()
|
||||
obj = bpy.data.objects.new("M12 STL Capability", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
obj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
return obj
|
||||
|
||||
|
||||
def export_stl(path, ascii_format):
|
||||
result = bpy.ops.wm.stl_export(
|
||||
filepath=str(path),
|
||||
ascii_format=ascii_format,
|
||||
export_selected_objects=True,
|
||||
apply_modifiers=False,
|
||||
evaluation_mode="DAG_EVAL_VIEWPORT",
|
||||
global_scale=1.0,
|
||||
forward_axis="NEGATIVE_Z",
|
||||
up_axis="Y",
|
||||
use_scene_unit=False,
|
||||
)
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError("Blender STL export did not finish: %s" % (result,))
|
||||
|
||||
|
||||
def binary_semantics(path):
|
||||
payload = path.read_bytes()
|
||||
if len(payload) < 84:
|
||||
raise RuntimeError("STL binary is shorter than the header")
|
||||
count = struct.unpack_from("<I", payload, 80)[0]
|
||||
expected = 84 + count * 50
|
||||
if expected != len(payload):
|
||||
raise RuntimeError("STL binary byte length does not match triangle count")
|
||||
return {"format": "STL_BINARY", "header": payload[:80].decode("ascii", errors="replace").rstrip("\x00 "), "triangleCount": count, "byteLength": len(payload)}
|
||||
|
||||
|
||||
def ascii_semantics(path):
|
||||
payload = path.read_text(encoding="utf-8")
|
||||
lines = [line.strip() for line in payload.splitlines() if line.strip()]
|
||||
facets = [line for line in lines if line.lower().startswith("facet normal")]
|
||||
vertices = [line for line in lines if line.lower().startswith("vertex ")]
|
||||
if not lines or not lines[0].lower().startswith("solid") or not lines[-1].lower() == "endsolid":
|
||||
raise RuntimeError("STL ASCII wrapper is invalid")
|
||||
return {"format": "STL_ASCII", "solid": lines[0][5:].strip(), "facetCount": len(facets), "vertexCount": len(vertices), "byteLength": path.stat().st_size}
|
||||
|
||||
|
||||
def main(output_dir, report_path):
|
||||
output_dir = Path(output_dir).resolve()
|
||||
report_path = Path(report_path).resolve()
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
report_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
create_scene()
|
||||
binary_path = output_dir / "capability-binary.stl"
|
||||
ascii_path = output_dir / "capability-ascii.stl"
|
||||
export_stl(binary_path, False)
|
||||
export_stl(ascii_path, True)
|
||||
files = []
|
||||
for item in (binary_path, ascii_path):
|
||||
files.append({"name": item.name, "byteLength": item.stat().st_size, "sha256": sha256_file(item)})
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M12-07D",
|
||||
"operation": "DESKTOP_STL_BINARY_ASCII_CAPABILITY",
|
||||
"runtime": runtime_identity(),
|
||||
"sourceAnchor": "blender-5.2.0/source/blender/io/stl",
|
||||
"operator": "wm.stl_export",
|
||||
"settings": {
|
||||
"evaluationMode": "DAG_EVAL_VIEWPORT",
|
||||
"forwardAxis": "NEGATIVE_Z",
|
||||
"upAxis": "Y",
|
||||
"globalScale": 1.0,
|
||||
"useSceneUnit": False,
|
||||
"exportSelectedObjects": True,
|
||||
},
|
||||
"variants": [
|
||||
{"id": "STL_BINARY", "asciiFormat": False, "file": binary_path.name, "semantic": binary_semantics(binary_path)},
|
||||
{"id": "STL_ASCII", "asciiFormat": True, "file": ascii_path.name, "semantic": ascii_semantics(ascii_path)},
|
||||
],
|
||||
"files": files,
|
||||
"nextTask": "M12-07E",
|
||||
}
|
||||
report_path.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("stl-capability-fixtures-generated binary=%s ascii=%s next=%s" % (report["variants"][0]["semantic"]["triangleCount"], report["variants"][1]["semantic"]["facetCount"], 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 generate-stl-capability-fixtures.py -- OUTPUT_DIR REPORT")
|
||||
main(args[0], args[1])
|
||||
Reference in New Issue
Block a user