ccf0f5f940
按最终矢量页面汇总每个相机单元的笔画包络,并标出低于 0.2 mm 的数量。 已核对 SVG、清单哈希和 Blender 导出,175 项相关测试通过。
129 lines
5.3 KiB
Python
129 lines
5.3 KiB
Python
"""The stroke CLI uses the texture geometry job and verifies its outputs."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from orisys_sensor_design import cli
|
|
from orisys_sensor_design.manifest import RevisionPaths, sha256_file
|
|
|
|
REVISION = Path(__file__).resolve().parents[1] / "sensors" / "finger-xynova" / "r01"
|
|
|
|
|
|
def _production_report(output_dir: Path, ids: tuple[str, ...]) -> None:
|
|
views = []
|
|
for view_id in ids:
|
|
svg = output_dir / f"stroke_texture_{view_id}.svg"
|
|
views.append({
|
|
"id": view_id, "stroke_count": 3, "width_mm": {"min": 0.1, "max": 0.3},
|
|
"length_mm": {"min": 0.2, "max": 0.5}, "width_below_target_count": 1,
|
|
"svg": {"sha256": sha256_file(svg)},
|
|
})
|
|
(output_dir / "stroke_texture_production_report.json").write_text(json.dumps({
|
|
"schema": "orisys-sensor-design/stroke-production-report-v1",
|
|
"width_target_mm": 0.2, "views": views,
|
|
}), encoding="utf-8")
|
|
|
|
|
|
def _args(job: Path, output_dir: Path, views=False) -> argparse.Namespace:
|
|
return argparse.Namespace(
|
|
views=views, source_job=job, output_dir=output_dir,
|
|
width_ratio=0.4, length_ratio=0.8, orientation_mode=None,
|
|
proposals=None, seed=13,
|
|
)
|
|
|
|
|
|
def _job(tmp_path: Path) -> Path:
|
|
paths = RevisionPaths.from_revision_dir(REVISION)
|
|
job = tmp_path / "_texture_job.json"
|
|
job.write_text(json.dumps({
|
|
"schema": "orisys-sensor-design/texture-job-v3",
|
|
"revision_dir": str(paths.root), "source_blend": str(paths.source_blend),
|
|
"settings": {"nx": 80, "ny": 80},
|
|
"objects": {"mesh": "sensing_surface", "camera": "cam_sensor"},
|
|
"outputs": [{"uv_map": "compute"}],
|
|
}), encoding="utf-8")
|
|
return job
|
|
|
|
|
|
def test_exports_from_geometry_job(tmp_path, monkeypatch):
|
|
job = _job(tmp_path)
|
|
output_dir = tmp_path / "strokes"
|
|
calls = []
|
|
|
|
def run_blender(script, blend, args):
|
|
calls.append((script, blend, args))
|
|
for fmt in ("png", "svg", "pdf"):
|
|
(output_dir / f"stroke_texture_compute.{fmt}").write_bytes(fmt.encode())
|
|
_production_report(output_dir, ("compute",))
|
|
return 0
|
|
|
|
monkeypatch.setattr(cli, "_run_blender_script", run_blender)
|
|
assert cli.cmd_stroke_texture(REVISION, _args(job, output_dir)) == 0
|
|
assert calls[0][0] == "camera_grid_stroke.py"
|
|
params_path = Path(calls[0][2][calls[0][2].index("--params") + 1])
|
|
params = json.loads(params_path.read_text(encoding="utf-8"))
|
|
assert params["seed"] == 13
|
|
assert params["width_ratio"] == 0.4
|
|
manifest = json.loads((output_dir / "stroke_texture_manifest.json").read_text())
|
|
assert manifest["schema"] == "orisys-sensor-design/stroke-texture-manifest-v2"
|
|
assert manifest["source_job"]["sha256"] == sha256_file(job)
|
|
assert set(manifest["outputs"][0]["artifacts"]) == {"png", "svg", "pdf"}
|
|
assert "stroke_texture_production_report.json" in manifest["auxiliary"]
|
|
assert cli.cmd_stroke_texture(REVISION, _args(job, output_dir)) == 2
|
|
assert len(calls) == 1
|
|
|
|
|
|
def test_multiview_exports_owned_pairs_without_colour_artwork(tmp_path, monkeypatch):
|
|
paths = RevisionPaths.from_revision_dir(REVISION)
|
|
job = tmp_path / "_texture_views_job.json"
|
|
job.write_text(json.dumps({
|
|
"schema": "orisys-sensor-design/texture-job-multiview-v1",
|
|
"revision_dir": str(paths.root), "source_blend": str(paths.source_blend),
|
|
"settings": {"nx": 80, "ny": 80}, "objects": {"mesh": "sensing_surface"},
|
|
"pairs": [{"id": "uv_1"}, {"id": "uv_2"}],
|
|
}), encoding="utf-8")
|
|
output_dir = tmp_path / "strokes"
|
|
calls = []
|
|
|
|
def run_blender(script, blend, args):
|
|
calls.append((script, blend, args))
|
|
for pair in ("uv_1", "uv_2"):
|
|
for fmt in ("png", "svg", "pdf"):
|
|
(output_dir / f"stroke_texture_{pair}.{fmt}").write_bytes(pair.encode())
|
|
for name in ("_multiview_ground_truth.json", "_print_views.json"):
|
|
(output_dir / name).write_text("{}", encoding="utf-8")
|
|
_production_report(output_dir, ("uv_1", "uv_2"))
|
|
return 0
|
|
|
|
monkeypatch.setattr(cli, "_run_blender_script", run_blender)
|
|
assert cli.cmd_stroke_texture(REVISION, _args(job, output_dir, views=True)) == 0
|
|
assert calls[0][0] == "random_patch_multiview.py"
|
|
assert "--stroke-params" in calls[0][2]
|
|
manifest = json.loads((output_dir / "stroke_texture_manifest.json").read_text())
|
|
assert manifest["views"] is True
|
|
assert [entry["id"] for entry in manifest["outputs"]] == ["uv_1", "uv_2"]
|
|
assert set(manifest["auxiliary"]) == {
|
|
"_multiview_ground_truth.json", "_print_views.json",
|
|
"stroke_texture_production_report.json",
|
|
}
|
|
assert len(calls) == 1
|
|
|
|
|
|
def test_rejects_report_for_a_different_svg(tmp_path, monkeypatch):
|
|
job = _job(tmp_path)
|
|
output_dir = tmp_path / "strokes"
|
|
|
|
def run_blender(_script, _blend, _args):
|
|
for fmt in ("png", "svg", "pdf"):
|
|
(output_dir / f"stroke_texture_compute.{fmt}").write_bytes(fmt.encode())
|
|
_production_report(output_dir, ("compute",))
|
|
(output_dir / "stroke_texture_compute.svg").write_bytes(b"changed")
|
|
return 0
|
|
|
|
monkeypatch.setattr(cli, "_run_blender_script", run_blender)
|
|
assert cli.cmd_stroke_texture(REVISION, _args(job, output_dir)) == 3
|
|
assert not (output_dir / "stroke_texture_manifest.json").exists()
|