Files
v60/tools/verify_v2_outputs.py
T

83 lines
3.2 KiB
Python

#!/usr/bin/env python3
"""Verify the complete SOFT FACET V2 PDF/SVG/JSON output matrix."""
from __future__ import annotations
import argparse
import json
import math
import xml.etree.ElementTree as ET
from pathlib import Path
from pypdf import PdfReader
from generate_a3_templates import SURFACE_STYLES
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--preset", default="v60_02")
parser.add_argument("--output-root", type=Path, default=Path(__file__).resolve().parents[1] / "output")
return parser.parse_args()
def require(condition: bool, message: str) -> None:
if not condition:
raise AssertionError(message)
def verify_style(root: Path, preset: str, style: str) -> None:
pdf_path = root / "pdf" / f"{preset}_{style}_clay_templates_a3.pdf"
data_path = root / "data" / f"{preset}_{style}_clay_templates.json"
svg_dir = root / "svg" / style
require(pdf_path.is_file(), f"missing PDF: {pdf_path}")
require(data_path.is_file(), f"missing JSON: {data_path}")
reader = PdfReader(str(pdf_path))
require(len(reader.pages) == 3, f"{style}: expected 3 PDF pages")
for page_number, page in enumerate(reader.pages, start=1):
width_mm = float(page.mediabox.width) * 25.4 / 72.0
height_mm = float(page.mediabox.height) * 25.4 / 72.0
require(math.isclose(width_mm, 420.0, abs_tol=0.01), f"{style}: page {page_number} width")
require(math.isclose(height_mm, 297.0, abs_tol=0.01), f"{style}: page {page_number} height")
text = page.extract_text() or ""
require(style in text, f"{style}: style label missing on page {page_number}")
require("50 x 50" in text, f"{style}: calibration square label missing on page {page_number}")
data = json.loads(data_path.read_text(encoding="utf-8"))
require(data["model_version"] == "2.0", f"{style}: wrong model version")
require(data["surface_style"] == style, f"{style}: JSON style mismatch")
require(0 < data["release_scale"] <= 1, f"{style}: invalid release scale")
require(data["minimum_release_draft_angle_deg"] >= 5, f"{style}: release angle below production minimum")
for field in (
"master_top_radius_mm",
"master_bottom_radius_mm",
"master_height_mm",
"average_meridian_length_mm",
"single_piece_outer_radius_mm",
"single_piece_inner_radius_mm",
"support_max_span_mm",
"support_hole_diameter_mm",
):
require(data[field] > 0, f"{style}: non-positive {field}")
for field in ("gore_top_widths_mm", "gore_bottom_widths_mm", "gore_heights_mm"):
require(len(data[field]) == 4, f"{style}: {field} must contain four values")
require(all(value > 0 for value in data[field]), f"{style}: invalid {field}")
svg_paths = sorted(svg_dir.glob(f"{preset}_{style}_*.svg"))
require(len(svg_paths) == 6, f"{style}: expected 6 SVG files, found {len(svg_paths)}")
for svg_path in svg_paths:
ET.parse(svg_path)
def main() -> None:
args = parse_args()
for style in SURFACE_STYLES:
verify_style(args.output_root, args.preset, style)
print(f"ok: {style}")
print(f"verified {len(SURFACE_STYLES)} styles")
if __name__ == "__main__":
main()